]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/net/can/softing/softing_fw.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / net / can / softing / softing_fw.c
diff --git a/drivers/net/can/softing/softing_fw.c b/drivers/net/can/softing/softing_fw.c
new file mode 100644 (file)
index 0000000..b520784
--- /dev/null
@@ -0,0 +1,691 @@
+/*
+ * Copyright (C) 2008-2010
+ *
+ * - Kurt Van Dijck, EIA Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <asm/div64.h>
+
+#include "softing.h"
+
+/*
+ * low level DPRAM command.
+ * Make sure that card->dpram[DPRAM_FCT_HOST] is preset
+ */
+static int _softing_fct_cmd(struct softing *card, int16_t cmd, uint16_t vector,
+               const char *msg)
+{
+       int ret;
+       unsigned long stamp;
+
+       iowrite16(cmd, &card->dpram[DPRAM_FCT_PARAM]);
+       iowrite8(vector >> 8, &card->dpram[DPRAM_FCT_HOST + 1]);
+       iowrite8(vector, &card->dpram[DPRAM_FCT_HOST]);
+       /* be sure to flush this to the card */
+       wmb();
+       stamp = jiffies + 1 * HZ;
+       /* wait for card */
+       do {
+               /* DPRAM_FCT_HOST is _not_ aligned */
+               ret = ioread8(&card->dpram[DPRAM_FCT_HOST]) +
+                       (ioread8(&card->dpram[DPRAM_FCT_HOST + 1]) << 8);
+               /* don't have any cached variables */
+               rmb();
+               if (ret == RES_OK)
+                       /* read return-value now */
+                       return ioread16(&card->dpram[DPRAM_FCT_RESULT]);
+
+               if ((ret != vector) || time_after(jiffies, stamp))
+                       break;
+               /* process context => relax */
+               usleep_range(500, 10000);
+       } while (1);
+
+       ret = (ret == RES_NONE) ? -ETIMEDOUT : -ECANCELED;
+       dev_alert(&card->pdev->dev, "firmware %s failed (%i)\n", msg, ret);
+       return ret;
+}
+
+static int softing_fct_cmd(struct softing *card, int16_t cmd, const char *msg)
+{
+       int ret;
+
+       ret = _softing_fct_cmd(card, cmd, 0, msg);
+       if (ret > 0) {
+               dev_alert(&card->pdev->dev, "%s returned %u\n", msg, ret);
+               ret = -EIO;
+       }
+       return ret;
+}
+
+int softing_bootloader_command(struct softing *card, int16_t cmd,
+               const char *msg)
+{
+       int ret;
+       unsigned long stamp;
+
+       iowrite16(RES_NONE, &card->dpram[DPRAM_RECEIPT]);
+       iowrite16(cmd, &card->dpram[DPRAM_COMMAND]);
+       /* be sure to flush this to the card */
+       wmb();
+       stamp = jiffies + 3 * HZ;
+       /* wait for card */
+       do {
+               ret = ioread16(&card->dpram[DPRAM_RECEIPT]);
+               /* don't have any cached variables */
+               rmb();
+               if (ret == RES_OK)
+                       return 0;
+               if (time_after(jiffies, stamp))
+                       break;
+               /* process context => relax */
+               usleep_range(500, 10000);
+       } while (!signal_pending(current));
+
+       ret = (ret == RES_NONE) ? -ETIMEDOUT : -ECANCELED;
+       dev_alert(&card->pdev->dev, "bootloader %s failed (%i)\n", msg, ret);
+       return ret;
+}
+
+static int fw_parse(const uint8_t **pmem, uint16_t *ptype, uint32_t *paddr,
+               uint16_t *plen, const uint8_t **pdat)
+{
+       uint16_t checksum[2];
+       const uint8_t *mem;
+       const uint8_t *end;
+
+       /*
+        * firmware records are a binary, unaligned stream composed of:
+        * uint16_t type;
+        * uint32_t addr;
+        * uint16_t len;
+        * uint8_t dat[len];
+        * uint16_t checksum;
+        * all values in little endian.
+        * We could define a struct for this, with __attribute__((packed)),
+        * but would that solve the alignment in _all_ cases (cfr. the
+        * struct itself may be an odd address)?
+        *
+        * I chose to use leXX_to_cpup() since this solves both
+        * endianness & alignment.
+        */
+       mem = *pmem;
+       *ptype = le16_to_cpup((void *)&mem[0]);
+       *paddr = le32_to_cpup((void *)&mem[2]);
+       *plen = le16_to_cpup((void *)&mem[6]);
+       *pdat = &mem[8];
+       /* verify checksum */
+       end = &mem[8 + *plen];
+       checksum[0] = le16_to_cpup((void *)end);
+       for (checksum[1] = 0; mem < end; ++mem)
+               checksum[1] += *mem;
+       if (checksum[0] != checksum[1])
+               return -EINVAL;
+       /* increment */
+       *pmem += 10 + *plen;
+       return 0;
+}
+
+int softing_load_fw(const char *file, struct softing *card,
+               __iomem uint8_t *dpram, unsigned int size, int offset)
+{
+       const struct firmware *fw;
+       int ret;
+       const uint8_t *mem, *end, *dat;
+       uint16_t type, len;
+       uint32_t addr;
+       uint8_t *buf = NULL;
+       int buflen = 0;
+       int8_t type_end = 0;
+
+       ret = request_firmware(&fw, file, &card->pdev->dev);
+       if (ret < 0)
+               return ret;
+       dev_dbg(&card->pdev->dev, "%s, firmware(%s) got %u bytes"
+               ", offset %c0x%04x\n",
+               card->pdat->name, file, (unsigned int)fw->size,
+               (offset >= 0) ? '+' : '-', (unsigned int)abs(offset));
+       /* parse the firmware */
+       mem = fw->data;
+       end = &mem[fw->size];
+       /* look for header record */
+       ret = fw_parse(&mem, &type, &addr, &len, &dat);
+       if (ret < 0)
+               goto failed;
+       if (type != 0xffff)
+               goto failed;
+       if (strncmp("Structured Binary Format, Softing GmbH" , dat, len)) {
+               ret = -EINVAL;
+               goto failed;
+       }
+       /* ok, we had a header */
+       while (mem < end) {
+               ret = fw_parse(&mem, &type, &addr, &len, &dat);
+               if (ret < 0)
+                       goto failed;
+               if (type == 3) {
+                       /* start address, not used here */
+                       continue;
+               } else if (type == 1) {
+                       /* eof */
+                       type_end = 1;
+                       break;
+               } else if (type != 0) {
+                       ret = -EINVAL;
+                       goto failed;
+               }
+
+               if ((addr + len + offset) > size)
+                       goto failed;
+               memcpy_toio(&dpram[addr + offset], dat, len);
+               /* be sure to flush caches from IO space */
+               mb();
+               if (len > buflen) {
+                       /* align buflen */
+                       buflen = (len + (1024-1)) & ~(1024-1);
+                       buf = krealloc(buf, buflen, GFP_KERNEL);
+                       if (!buf) {
+                               ret = -ENOMEM;
+                               goto failed;
+                       }
+               }
+               /* verify record data */
+               memcpy_fromio(buf, &dpram[addr + offset], len);
+               if (memcmp(buf, dat, len)) {
+                       /* is not ok */
+                       dev_alert(&card->pdev->dev, "DPRAM readback failed\n");
+                       ret = -EIO;
+                       goto failed;
+               }
+       }
+       if (!type_end)
+               /* no end record seen */
+               goto failed;
+       ret = 0;
+failed:
+       kfree(buf);
+       release_firmware(fw);
+       if (ret < 0)
+               dev_info(&card->pdev->dev, "firmware %s failed\n", file);
+       return ret;
+}
+
+int softing_load_app_fw(const char *file, struct softing *card)
+{
+       const struct firmware *fw;
+       const uint8_t *mem, *end, *dat;
+       int ret, j;
+       uint16_t type, len;
+       uint32_t addr, start_addr = 0;
+       unsigned int sum, rx_sum;
+       int8_t type_end = 0, type_entrypoint = 0;
+
+       ret = request_firmware(&fw, file, &card->pdev->dev);
+       if (ret) {
+               dev_alert(&card->pdev->dev, "request_firmware(%s) got %i\n",
+                       file, ret);
+               return ret;
+       }
+       dev_dbg(&card->pdev->dev, "firmware(%s) got %lu bytes\n",
+               file, (unsigned long)fw->size);
+       /* parse the firmware */
+       mem = fw->data;
+       end = &mem[fw->size];
+       /* look for header record */
+       ret = fw_parse(&mem, &type, &addr, &len, &dat);
+       if (ret)
+               goto failed;
+       ret = -EINVAL;
+       if (type != 0xffff) {
+               dev_alert(&card->pdev->dev, "firmware starts with type 0x%x\n",
+                       type);
+               goto failed;
+       }
+       if (strncmp("Structured Binary Format, Softing GmbH", dat, len)) {
+               dev_alert(&card->pdev->dev, "firmware string '%.*s' fault\n",
+                               len, dat);
+               goto failed;
+       }
+       /* ok, we had a header */
+       while (mem < end) {
+               ret = fw_parse(&mem, &type, &addr, &len, &dat);
+               if (ret)
+                       goto failed;
+
+               if (type == 3) {
+                       /* start address */
+                       start_addr = addr;
+                       type_entrypoint = 1;
+                       continue;
+               } else if (type == 1) {
+                       /* eof */
+                       type_end = 1;
+                       break;
+               } else if (type != 0) {
+                       dev_alert(&card->pdev->dev,
+                                       "unknown record type 0x%04x\n", type);
+                       ret = -EINVAL;
+                       goto failed;
+               }
+
+               /* regualar data */
+               for (sum = 0, j = 0; j < len; ++j)
+                       sum += dat[j];
+               /* work in 16bit (target) */
+               sum &= 0xffff;
+
+               memcpy_toio(&card->dpram[card->pdat->app.offs], dat, len);
+               iowrite32(card->pdat->app.offs + card->pdat->app.addr,
+                               &card->dpram[DPRAM_COMMAND + 2]);
+               iowrite32(addr, &card->dpram[DPRAM_COMMAND + 6]);
+               iowrite16(len, &card->dpram[DPRAM_COMMAND + 10]);
+               iowrite8(1, &card->dpram[DPRAM_COMMAND + 12]);
+               ret = softing_bootloader_command(card, 1, "loading app.");
+               if (ret < 0)
+                       goto failed;
+               /* verify checksum */
+               rx_sum = ioread16(&card->dpram[DPRAM_RECEIPT + 2]);
+               if (rx_sum != sum) {
+                       dev_alert(&card->pdev->dev, "SRAM seems to be damaged"
+                               ", wanted 0x%04x, got 0x%04x\n", sum, rx_sum);
+                       ret = -EIO;
+                       goto failed;
+               }
+       }
+       if (!type_end || !type_entrypoint)
+               goto failed;
+       /* start application in card */
+       iowrite32(start_addr, &card->dpram[DPRAM_COMMAND + 2]);
+       iowrite8(1, &card->dpram[DPRAM_COMMAND + 6]);
+       ret = softing_bootloader_command(card, 3, "start app.");
+       if (ret < 0)
+               goto failed;
+       ret = 0;
+failed:
+       release_firmware(fw);
+       if (ret < 0)
+               dev_info(&card->pdev->dev, "firmware %s failed\n", file);
+       return ret;
+}
+
+static int softing_reset_chip(struct softing *card)
+{
+       int ret;
+
+       do {
+               /* reset chip */
+               iowrite8(0, &card->dpram[DPRAM_RESET_RX_FIFO]);
+               iowrite8(0, &card->dpram[DPRAM_RESET_RX_FIFO+1]);
+               iowrite8(1, &card->dpram[DPRAM_RESET]);
+               iowrite8(0, &card->dpram[DPRAM_RESET+1]);
+
+               ret = softing_fct_cmd(card, 0, "reset_can");
+               if (!ret)
+                       break;
+               if (signal_pending(current))
+                       /* don't wait any longer */
+                       break;
+       } while (1);
+       card->tx.pending = 0;
+       return ret;
+}
+
+int softing_chip_poweron(struct softing *card)
+{
+       int ret;
+       /* sync */
+       ret = _softing_fct_cmd(card, 99, 0x55, "sync-a");
+       if (ret < 0)
+               goto failed;
+
+       ret = _softing_fct_cmd(card, 99, 0xaa, "sync-b");
+       if (ret < 0)
+               goto failed;
+
+       ret = softing_reset_chip(card);
+       if (ret < 0)
+               goto failed;
+       /* get_serial */
+       ret = softing_fct_cmd(card, 43, "get_serial_number");
+       if (ret < 0)
+               goto failed;
+       card->id.serial = ioread32(&card->dpram[DPRAM_FCT_PARAM]);
+       /* get_version */
+       ret = softing_fct_cmd(card, 12, "get_version");
+       if (ret < 0)
+               goto failed;
+       card->id.fw_version = ioread16(&card->dpram[DPRAM_FCT_PARAM + 2]);
+       card->id.hw_version = ioread16(&card->dpram[DPRAM_FCT_PARAM + 4]);
+       card->id.license = ioread16(&card->dpram[DPRAM_FCT_PARAM + 6]);
+       card->id.chip[0] = ioread16(&card->dpram[DPRAM_FCT_PARAM + 8]);
+       card->id.chip[1] = ioread16(&card->dpram[DPRAM_FCT_PARAM + 10]);
+       return 0;
+failed:
+       return ret;
+}
+
+static void softing_initialize_timestamp(struct softing *card)
+{
+       uint64_t ovf;
+
+       card->ts_ref = ktime_get();
+
+       /* 16MHz is the reference */
+       ovf = 0x100000000ULL * 16;
+       do_div(ovf, card->pdat->freq ?: 16);
+
+       card->ts_overflow = ktime_add_us(ktime_set(0, 0), ovf);
+}
+
+ktime_t softing_raw2ktime(struct softing *card, u32 raw)
+{
+       uint64_t rawl;
+       ktime_t now, real_offset;
+       ktime_t target;
+       ktime_t tmp;
+
+       now = ktime_get();
+       real_offset = ktime_sub(ktime_get_real(), now);
+
+       /* find nsec from card */
+       rawl = raw * 16;
+       do_div(rawl, card->pdat->freq ?: 16);
+       target = ktime_add_us(card->ts_ref, rawl);
+       /* test for overflows */
+       tmp = ktime_add(target, card->ts_overflow);
+       while (unlikely(ktime_to_ns(tmp) > ktime_to_ns(now))) {
+               card->ts_ref = ktime_add(card->ts_ref, card->ts_overflow);
+               target = tmp;
+               tmp = ktime_add(target, card->ts_overflow);
+       }
+       return ktime_add(target, real_offset);
+}
+
+static inline int softing_error_reporting(struct net_device *netdev)
+{
+       struct softing_priv *priv = netdev_priv(netdev);
+
+       return (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
+               ? 1 : 0;
+}
+
+int softing_startstop(struct net_device *dev, int up)
+{
+       int ret;
+       struct softing *card;
+       struct softing_priv *priv;
+       struct net_device *netdev;
+       int bus_bitmask_start;
+       int j, error_reporting;
+       struct can_frame msg;
+       const struct can_bittiming *bt;
+
+       priv = netdev_priv(dev);
+       card = priv->card;
+
+       if (!card->fw.up)
+               return -EIO;
+
+       ret = mutex_lock_interruptible(&card->fw.lock);
+       if (ret)
+               return ret;
+
+       bus_bitmask_start = 0;
+       if (dev && up)
+               /* prepare to start this bus as well */
+               bus_bitmask_start |= (1 << priv->index);
+       /* bring netdevs down */
+       for (j = 0; j < ARRAY_SIZE(card->net); ++j) {
+               netdev = card->net[j];
+               if (!netdev)
+                       continue;
+               priv = netdev_priv(netdev);
+
+               if (dev != netdev)
+                       netif_stop_queue(netdev);
+
+               if (netif_running(netdev)) {
+                       if (dev != netdev)
+                               bus_bitmask_start |= (1 << j);
+                       priv->tx.pending = 0;
+                       priv->tx.echo_put = 0;
+                       priv->tx.echo_get = 0;
+                       /*
+                        * this bus' may just have called open_candev()
+                        * which is rather stupid to call close_candev()
+                        * already
+                        * but we may come here from busoff recovery too
+                        * in which case the echo_skb _needs_ flushing too.
+                        * just be sure to call open_candev() again
+                        */
+                       close_candev(netdev);
+               }
+               priv->can.state = CAN_STATE_STOPPED;
+       }
+       card->tx.pending = 0;
+
+       softing_enable_irq(card, 0);
+       ret = softing_reset_chip(card);
+       if (ret)
+               goto failed;
+       if (!bus_bitmask_start)
+               /* no busses to be brought up */
+               goto card_done;
+
+       if ((bus_bitmask_start & 1) && (bus_bitmask_start & 2)
+                       && (softing_error_reporting(card->net[0])
+                               != softing_error_reporting(card->net[1]))) {
+               dev_alert(&card->pdev->dev,
+                               "err_reporting flag differs for busses\n");
+               goto invalid;
+       }
+       error_reporting = 0;
+       if (bus_bitmask_start & 1) {
+               netdev = card->net[0];
+               priv = netdev_priv(netdev);
+               error_reporting += softing_error_reporting(netdev);
+               /* init chip 1 */
+               bt = &priv->can.bittiming;
+               iowrite16(bt->brp, &card->dpram[DPRAM_FCT_PARAM + 2]);
+               iowrite16(bt->sjw, &card->dpram[DPRAM_FCT_PARAM + 4]);
+               iowrite16(bt->phase_seg1 + bt->prop_seg,
+                               &card->dpram[DPRAM_FCT_PARAM + 6]);
+               iowrite16(bt->phase_seg2, &card->dpram[DPRAM_FCT_PARAM + 8]);
+               iowrite16((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 1 : 0,
+                               &card->dpram[DPRAM_FCT_PARAM + 10]);
+               ret = softing_fct_cmd(card, 1, "initialize_chip[0]");
+               if (ret < 0)
+                       goto failed;
+               /* set mode */
+               iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 2]);
+               iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 4]);
+               ret = softing_fct_cmd(card, 3, "set_mode[0]");
+               if (ret < 0)
+                       goto failed;
+               /* set filter */
+               /* 11bit id & mask */
+               iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 2]);
+               iowrite16(0x07ff, &card->dpram[DPRAM_FCT_PARAM + 4]);
+               /* 29bit id.lo & mask.lo & id.hi & mask.hi */
+               iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 6]);
+               iowrite16(0xffff, &card->dpram[DPRAM_FCT_PARAM + 8]);
+               iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 10]);
+               iowrite16(0x1fff, &card->dpram[DPRAM_FCT_PARAM + 12]);
+               ret = softing_fct_cmd(card, 7, "set_filter[0]");
+               if (ret < 0)
+                       goto failed;
+               /* set output control */
+               iowrite16(priv->output, &card->dpram[DPRAM_FCT_PARAM + 2]);
+               ret = softing_fct_cmd(card, 5, "set_output[0]");
+               if (ret < 0)
+                       goto failed;
+       }
+       if (bus_bitmask_start & 2) {
+               netdev = card->net[1];
+               priv = netdev_priv(netdev);
+               error_reporting += softing_error_reporting(netdev);
+               /* init chip2 */
+               bt = &priv->can.bittiming;
+               iowrite16(bt->brp, &card->dpram[DPRAM_FCT_PARAM + 2]);
+               iowrite16(bt->sjw, &card->dpram[DPRAM_FCT_PARAM + 4]);
+               iowrite16(bt->phase_seg1 + bt->prop_seg,
+                               &card->dpram[DPRAM_FCT_PARAM + 6]);
+               iowrite16(bt->phase_seg2, &card->dpram[DPRAM_FCT_PARAM + 8]);
+               iowrite16((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 1 : 0,
+                               &card->dpram[DPRAM_FCT_PARAM + 10]);
+               ret = softing_fct_cmd(card, 2, "initialize_chip[1]");
+               if (ret < 0)
+                       goto failed;
+               /* set mode2 */
+               iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 2]);
+               iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 4]);
+               ret = softing_fct_cmd(card, 4, "set_mode[1]");
+               if (ret < 0)
+                       goto failed;
+               /* set filter2 */
+               /* 11bit id & mask */
+               iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 2]);
+               iowrite16(0x07ff, &card->dpram[DPRAM_FCT_PARAM + 4]);
+               /* 29bit id.lo & mask.lo & id.hi & mask.hi */
+               iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 6]);
+               iowrite16(0xffff, &card->dpram[DPRAM_FCT_PARAM + 8]);
+               iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 10]);
+               iowrite16(0x1fff, &card->dpram[DPRAM_FCT_PARAM + 12]);
+               ret = softing_fct_cmd(card, 8, "set_filter[1]");
+               if (ret < 0)
+                       goto failed;
+               /* set output control2 */
+               iowrite16(priv->output, &card->dpram[DPRAM_FCT_PARAM + 2]);
+               ret = softing_fct_cmd(card, 6, "set_output[1]");
+               if (ret < 0)
+                       goto failed;
+       }
+       /* enable_error_frame */
+       /*
+        * Error reporting is switched off at the moment since
+        * the receiving of them is not yet 100% verified
+        * This should be enabled sooner or later
+        *
+       if (error_reporting) {
+               ret = softing_fct_cmd(card, 51, "enable_error_frame");
+               if (ret < 0)
+                       goto failed;
+       }
+       */
+       /* initialize interface */
+       iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 2]);
+       iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 4]);
+       iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 6]);
+       iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 8]);
+       iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 10]);
+       iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 12]);
+       iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 14]);
+       iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 16]);
+       iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 18]);
+       iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 20]);
+       ret = softing_fct_cmd(card, 17, "initialize_interface");
+       if (ret < 0)
+               goto failed;
+       /* enable_fifo */
+       ret = softing_fct_cmd(card, 36, "enable_fifo");
+       if (ret < 0)
+               goto failed;
+       /* enable fifo tx ack */
+       ret = softing_fct_cmd(card, 13, "fifo_tx_ack[0]");
+       if (ret < 0)
+               goto failed;
+       /* enable fifo tx ack2 */
+       ret = softing_fct_cmd(card, 14, "fifo_tx_ack[1]");
+       if (ret < 0)
+               goto failed;
+       /* start_chip */
+       ret = softing_fct_cmd(card, 11, "start_chip");
+       if (ret < 0)
+               goto failed;
+       iowrite8(0, &card->dpram[DPRAM_INFO_BUSSTATE]);
+       iowrite8(0, &card->dpram[DPRAM_INFO_BUSSTATE2]);
+       if (card->pdat->generation < 2) {
+               iowrite8(0, &card->dpram[DPRAM_V2_IRQ_TOHOST]);
+               /* flush the DPRAM caches */
+               wmb();
+       }
+
+       softing_initialize_timestamp(card);
+
+       /*
+        * do socketcan notifications/status changes
+        * from here, no errors should occur, or the failed: part
+        * must be reviewed
+        */
+       memset(&msg, 0, sizeof(msg));
+       msg.can_id = CAN_ERR_FLAG | CAN_ERR_RESTARTED;
+       msg.can_dlc = CAN_ERR_DLC;
+       for (j = 0; j < ARRAY_SIZE(card->net); ++j) {
+               if (!(bus_bitmask_start & (1 << j)))
+                       continue;
+               netdev = card->net[j];
+               if (!netdev)
+                       continue;
+               priv = netdev_priv(netdev);
+               priv->can.state = CAN_STATE_ERROR_ACTIVE;
+               open_candev(netdev);
+               if (dev != netdev) {
+                       /* notify other busses on the restart */
+                       softing_netdev_rx(netdev, &msg, ktime_set(0, 0));
+                       ++priv->can.can_stats.restarts;
+               }
+               netif_wake_queue(netdev);
+       }
+
+       /* enable interrupts */
+       ret = softing_enable_irq(card, 1);
+       if (ret)
+               goto failed;
+card_done:
+       mutex_unlock(&card->fw.lock);
+       return 0;
+invalid:
+       ret = -EINVAL;
+failed:
+       softing_enable_irq(card, 0);
+       softing_reset_chip(card);
+       mutex_unlock(&card->fw.lock);
+       /* bring all other interfaces down */
+       for (j = 0; j < ARRAY_SIZE(card->net); ++j) {
+               netdev = card->net[j];
+               if (!netdev)
+                       continue;
+               dev_close(netdev);
+       }
+       return ret;
+}
+
+int softing_default_output(struct net_device *netdev)
+{
+       struct softing_priv *priv = netdev_priv(netdev);
+       struct softing *card = priv->card;
+
+       switch (priv->chip) {
+       case 1000:
+               return (card->pdat->generation < 2) ? 0xfb : 0xfa;
+       case 5:
+               return 0x60;
+       default:
+               return 0x40;
+       }
+}