diff options
Diffstat (limited to 'drivers/net/can/softing/softing_fw.c')
| -rw-r--r-- | drivers/net/can/softing/softing_fw.c | 691 | 
1 files changed, 691 insertions, 0 deletions
diff --git a/drivers/net/can/softing/softing_fw.c b/drivers/net/can/softing/softing_fw.c new file mode 100644 index 00000000000..b520784fb19 --- /dev/null +++ b/drivers/net/can/softing/softing_fw.c @@ -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; +	} +}  |