linux/drivers/net/can/softing/softing_fw.c
Alexey Dobriyan b7f080cfe2 net: remove mm.h inclusion from netdevice.h
Remove linux/mm.h inclusion from netdevice.h -- it's unused (I've checked manually).

To prevent mm.h inclusion via other channels also extract "enum dma_data_direction"
definition into separate header. This tiny piece is what gluing netdevice.h with mm.h
via "netdevice.h => dmaengine.h => dma-mapping.h => scatterlist.h => mm.h".
Removal of mm.h from scatterlist.h was tried and was found not feasible
on most archs, so the link was cutoff earlier.

Hope people are OK with tiny include file.

Note, that mm_types.h is still dragged in, but it is a separate story.

Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2011-06-21 19:17:20 -07:00

693 lines
18 KiB
C

/*
* 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 <asm/io.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;
}
}