/* * Janz ICAN3 CAN Inquiry Message Types * * NOTE: there appears to be a firmware bug here. You must send * NOTE: INQUIRY_STATUS and expect to receive an INQUIRY_EXTENDED * NOTE: response. The controller never responds to a message with * NOTE: the INQUIRY_EXTENDED subspec :(
*/ #define INQUIRY_STATUS 0x00 #define INQUIRY_TERMINATION 0x01 #define INQUIRY_EXTENDED 0x04
/* Janz ICAN3 CAN Set Acceptance Filter Mask Message Types */ #define SETAFILMASK_REJECT 0x00 #define SETAFILMASK_FASTIF 0x02
/* base address of registers and IRQ */ struct janz_cmodio_onboard_regs __iomem *ctrl; struct ican3_dpm_control __iomem *dpmctrl; void __iomem *dpm; int irq;
/* CAN bus termination status */ struct completion termination_comp; bool termination_enabled;
/* CAN bus error status registers */ struct completion buserror_comp; struct can_berr_counter bec;
/* firmware type */ enum ican3_fwtype fwtype; char fwinfo[32];
/* old and new style host interface */ unsignedint iftype;
/* queue for echo packets */ struct sk_buff_head echoq;
/* * Any function which changes the current DPM page must hold this * lock while it is performing data accesses. This ensures that the * function will not be preempted and end up reading data from a * different DPM page than it expects.
*/
spinlock_t lock;
/* * Send a message through the "old-style" firmware interface * * LOCKING: must hold mod->lock * * returns 0 on success, -ENOMEM when no free space exists
*/ staticint ican3_old_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
{ unsignedint mbox, mbox_page;
u8 locl, peer, xord;
/* switch to the fromhost mid queue, and read the buffer descriptor */
ican3_set_page(mod, QUEUE_FROMHOST_MID);
memcpy_fromio(&desc, desc_addr, sizeof(desc));
if (!(desc.control & DESC_VALID)) {
netdev_dbg(mod->ndev, "%s: no free buffers\n", __func__); return -ENOMEM;
}
/* switch to the data page, copy the data */
ican3_set_page(mod, desc.pointer);
memcpy_toio(mod->dpm, msg, sizeof(*msg));
/* switch back to the descriptor, set the valid bit, write it back */
ican3_set_page(mod, QUEUE_FROMHOST_MID);
desc.control ^= DESC_VALID;
memcpy_toio(desc_addr, &desc, sizeof(desc));
/* update the tx number */
mod->tx_num = (desc.control & DESC_WRAP) ? 0 : (mod->tx_num + 1); return 0;
}
/* switch to the tohost queue, and read the buffer descriptor */
ican3_set_page(mod, QUEUE_TOHOST);
memcpy_fromio(&desc, desc_addr, sizeof(desc));
if (!(desc.control & DESC_VALID)) {
netdev_dbg(mod->ndev, "%s: no buffers to recv\n", __func__); return -ENOMEM;
}
/* switch to the data page, copy the data */
ican3_set_page(mod, desc.pointer);
memcpy_fromio(msg, mod->dpm, sizeof(*msg));
/* switch back to the descriptor, toggle the valid bit, write it back */
ican3_set_page(mod, QUEUE_TOHOST);
desc.control ^= DESC_VALID;
memcpy_toio(desc_addr, &desc, sizeof(desc));
/* update the rx number */
mod->rx_num = (desc.control & DESC_WRAP) ? 0 : (mod->rx_num + 1); return 0;
}
/* If we're not using the new interface yet, we cannot do this */
WARN_ON(mod->iftype != 1);
return ican3_send_msg(mod, &msg);
}
/* * Setup the CAN filter to either accept or reject all * messages from the CAN bus.
*/ staticint ican3_set_id_filter(struct ican3_dev *mod, bool accept)
{ struct ican3_msg msg; int ret;
staticvoid can_frame_to_ican3(struct ican3_dev *mod, struct can_frame *cf, struct ican3_fast_desc *desc)
{ /* clear out any stale data in the descriptor */
memset(desc->data, 0, sizeof(desc->data));
/* we always use the extended format, with the ECHO flag set */
desc->command = ICAN3_CAN_TYPE_EFF;
desc->data[0] |= cf->len;
desc->data[1] |= ICAN3_ECHO;
/* support single transmission (no retries) mode */ if (mod->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
desc->data[1] |= ICAN3_SNGL;
if (cf->can_id & CAN_RTR_FLAG)
desc->data[0] |= ICAN3_EFF_RTR;
/* copy the data bits into the descriptor */
memcpy(&desc->data[6], cf->data, cf->len);
}
/* * Interrupt Handling
*/
/* * Handle an ID + Version message response from the firmware. We never generate * this message in production code, but it is very useful when debugging to be * able to display this message.
*/ staticvoid ican3_handle_idvers(struct ican3_dev *mod, struct ican3_msg *msg)
{
netdev_dbg(mod->ndev, "IDVERS response: %s\n", msg->data);
}
/* * Report that communication messages with the microcontroller firmware * are being lost. These are never CAN frames, so we do not generate an * error frame for userspace
*/ if (msg->spec == MSG_MSGLOST) {
netdev_err(mod->ndev, "lost %d control messages\n", msg->data[0]); return;
}
/* * Oops, this indicates that we have lost messages in the fast queue, * which are exclusively CAN messages. Our driver isn't reading CAN * frames fast enough. * * We'll pretend that the SJA1000 told us that it ran out of buffer * space, because there is not a better message for this.
*/
skb = alloc_can_err_skb(dev, &cf); if (skb) {
cf->can_id |= CAN_ERR_CRTL;
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
stats->rx_over_errors++;
stats->rx_errors++;
netif_rx(skb);
}
}
/* * Handle CAN Event Indication Messages from the firmware * * The ICAN3 firmware provides the values of some SJA1000 registers when it * generates this message. The code below is largely copied from the * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary
*/ staticint ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
{ struct net_device *dev = mod->ndev; struct net_device_stats *stats = &dev->stats; enum can_state state = mod->can.state;
u8 isrc, ecc, status, rxerr, txerr; struct can_frame *cf; struct sk_buff *skb;
/* we can only handle the SJA1000 part */ if (msg->data[1] != CEVTIND_CHIP_SJA1000) {
netdev_err(mod->ndev, "unable to handle errors on non-SJA1000\n"); return -ENODEV;
}
/* check the message length for sanity */ if (le16_to_cpu(msg->len) < 6) {
netdev_err(mod->ndev, "error message too short\n"); return -EINVAL;
}
/* * This hardware lacks any support other than bus error messages to * determine if packet transmission has failed. * * When TX errors happen, one echo skb needs to be dropped from the * front of the queue. * * A small bit of code is duplicated here and below, to avoid error * skb allocation when it will just be freed immediately.
*/ if (isrc == CEVTIND_BEI) { int ret;
netdev_dbg(mod->ndev, "bus error interrupt\n");
/* * The controller automatically disables bus-error interrupts * and therefore we must re-enable them.
*/
ret = ican3_set_buserror(mod, 1); if (ret) {
netdev_err(mod->ndev, "unable to re-enable bus-error\n"); return ret;
}
/* bus error reporting is off, return immediately */ if (!(mod->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) return 0;
}
skb = alloc_can_err_skb(dev, &cf); if (skb == NULL) return -ENOMEM;
subspec = msg->data[0] + msg->data[1] * 0x100; if (subspec == NMTS_SLAVE_EVENT_IND) { switch (msg->data[2]) { case NE_LOCAL_OCCURRED: case NE_LOCAL_RESOLVED: /* now follows the same message as Raw ICANOS CEVTIND * shift the data at the same place and call this method
*/
le16_add_cpu(&msg->len, -3);
memmove(msg->data, msg->data + 3, le16_to_cpu(msg->len));
ican3_handle_cevtind(mod, msg); break; case NE_REMOTE_OCCURRED: case NE_REMOTE_RESOLVED: /* should not occurre, ignore */ break; default:
netdev_warn(mod->ndev, "unknown NMTS event indication %x\n",
msg->data[2]); break;
}
} elseif (subspec == NMTS_SLAVE_STATE_IND) { /* ignore state indications */
} else {
netdev_warn(mod->ndev, "unhandled NMTS indication %x\n",
subspec); return;
}
}
/* * Handle a control message from the firmware
*/ staticvoid ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg)
{
netdev_dbg(mod->ndev, "%s: modno %d spec 0x%.2x len %d bytes\n", __func__,
mod->num, msg->spec, le16_to_cpu(msg->len));
switch (msg->spec) { case MSG_IDVERS:
ican3_handle_idvers(mod, msg); break; case MSG_MSGLOST: case MSG_FMSGLOST:
ican3_handle_msglost(mod, msg); break; case MSG_CEVTIND:
ican3_handle_cevtind(mod, msg); break; case MSG_INQUIRY:
ican3_handle_inquiry(mod, msg); break; case MSG_NMTS:
ican3_handle_nmtsind(mod, msg); break; default:
ican3_handle_unknown_message(mod, msg); break;
}
}
/* * The ican3 needs to store all echo skbs, and therefore cannot * use the generic infrastructure for this.
*/ staticvoid ican3_put_echo_skb(struct ican3_dev *mod, struct sk_buff *skb)
{
skb = can_create_echo_skb(skb); if (!skb) return;
skb_tx_timestamp(skb);
/* save this skb for tx interrupt echo handling */
skb_queue_tail(&mod->echoq, skb);
}
/* * Compare an skb with an existing echo skb * * This function will be used on devices which have a hardware loopback. * On these devices, this function can be used to compare a received skb * with the saved echo skbs so that the hardware echo skb can be dropped. * * Returns true if the skb's are identical, false otherwise.
*/ staticbool ican3_echo_skb_matches(struct ican3_dev *mod, struct sk_buff *skb)
{ struct can_frame *cf = (struct can_frame *)skb->data; struct sk_buff *echo_skb = skb_peek(&mod->echoq); struct can_frame *echo_cf;
if (!echo_skb) returnfalse;
echo_cf = (struct can_frame *)echo_skb->data; if (cf->can_id != echo_cf->can_id) returnfalse;
/* * Check that there is room in the TX ring to transmit another skb * * LOCKING: must hold mod->lock
*/ staticbool ican3_txok(struct ican3_dev *mod)
{ struct ican3_fast_desc __iomem *desc;
u8 control;
/* check that we have echo queue space */ if (skb_queue_len(&mod->echoq) >= ICAN3_TX_BUFFERS) returnfalse;
/* copy the control bits of the descriptor */
ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
desc = mod->dpm + ((mod->fasttx_num % 16) * sizeof(*desc));
control = ioread8(&desc->control);
/* if the control bits are not valid, then we have no more space */ if (!(control & DESC_VALID)) returnfalse;
returntrue;
}
/* * Receive one CAN frame from the hardware * * CONTEXT: must be called from user context
*/ staticint ican3_recv_skb(struct ican3_dev *mod)
{ struct net_device *ndev = mod->ndev; struct net_device_stats *stats = &ndev->stats; struct ican3_fast_desc desc; void __iomem *desc_addr; struct can_frame *cf; struct sk_buff *skb; unsignedlong flags;
/* check that we actually have a CAN frame */ if (!(desc.control & DESC_VALID)) return -ENOBUFS;
/* allocate an skb */
skb = alloc_can_skb(ndev, &cf); if (unlikely(skb == NULL)) {
stats->rx_dropped++; goto err_noalloc;
}
/* convert the ICAN3 frame into Linux CAN format */
ican3_to_can_frame(mod, &desc, cf);
/* * If this is an ECHO frame received from the hardware loopback * feature, use the skb saved in the ECHO stack instead. This allows * the Linux CAN core to support CAN_RAW_RECV_OWN_MSGS correctly. * * Since this is a confirmation of a successfully transmitted packet * sent from this host, update the transmit statistics. * * Also, the netdevice queue needs to be allowed to send packets again.
*/ if (ican3_echo_skb_matches(mod, skb)) {
stats->tx_packets++;
stats->tx_bytes += ican3_get_echo_skb(mod);
kfree_skb(skb); goto err_noalloc;
}
/* update statistics, receive the skb */
stats->rx_packets++; if (!(cf->can_id & CAN_RTR_FLAG))
stats->rx_bytes += cf->len;
netif_receive_skb(skb);
err_noalloc: /* toggle the valid bit and return the descriptor to the ring */
desc.control ^= DESC_VALID;
/* update the next buffer pointer */
mod->fastrx_num = (desc.control & DESC_WRAP) ? 0
: (mod->fastrx_num + 1);
/* there are still more buffers to process */
spin_unlock_irqrestore(&mod->lock, flags); return 0;
}
staticint ican3_napi(struct napi_struct *napi, int budget)
{ struct ican3_dev *mod = container_of(napi, struct ican3_dev, napi); unsignedlong flags; int received = 0; int ret;
/* process all communication messages */ while (true) { struct ican3_msg msg;
ret = ican3_recv_msg(mod, &msg); if (ret) break;
ican3_handle_message(mod, &msg);
}
/* process all CAN frames from the fast interface */ while (received < budget) {
ret = ican3_recv_skb(mod); if (ret) break;
received++;
}
/* We have processed all packets that the adapter had, but it
* was less than our budget, stop polling */ if (received < budget)
napi_complete_done(napi, received);
spin_lock_irqsave(&mod->lock, flags);
/* Wake up the transmit queue if necessary */ if (netif_queue_stopped(mod->ndev) && ican3_txok(mod))
netif_wake_queue(mod->ndev);
/* * The interrupt status register on this device reports interrupts * as zeroes instead of using ones like most other devices
*/
stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num); if (stat == (1 << mod->num)) return IRQ_NONE;
/* clear the MODULbus interrupt from the microcontroller */
ioread8(&mod->dpmctrl->interrupt);
/* disable interrupt generation, schedule the NAPI poller */
iowrite8(1 << mod->num, &mod->ctrl->int_disable);
napi_schedule(&mod->napi); return IRQ_HANDLED;
}
/* * Firmware reset, startup, and shutdown
*/
/* * Reset an ICAN module to its power-on state * * CONTEXT: no network device registered
*/ staticint ican3_reset_module(struct ican3_dev *mod)
{ unsignedlong start;
u8 runold, runnew;
/* disable interrupts so no more work is scheduled */
iowrite8(1 << mod->num, &mod->ctrl->int_disable);
/* the first unallocated page in the DPM is #9 */
mod->free_page = DPM_FREE_START;
/* reset the module */
iowrite8(0x00, &mod->dpmctrl->hwreset);
/* wait until the module has finished resetting and is running */
start = jiffies; do {
ican3_set_page(mod, QUEUE_OLD_CONTROL);
runnew = ioread8(mod->dpm + TARGET_RUNNING); if (runnew == (runold ^ 0xff)) return 0;
msleep(10);
} while (time_before(jiffies, start + HZ / 2));
netdev_err(mod->ndev, "failed to reset CAN module\n"); return -ETIMEDOUT;
}
/* re-enable interrupts so we can send messages */
iowrite8(1 << mod->num, &mod->ctrl->int_enable);
ret = ican3_msg_connect(mod); if (ret) {
netdev_err(mod->ndev, "unable to connect to module\n"); return ret;
}
ican3_init_new_host_interface(mod);
ret = ican3_msg_newhostif(mod); if (ret) {
netdev_err(mod->ndev, "unable to switch to new-style interface\n"); return ret;
}
/* default to "termination on" */
ret = ican3_set_termination(mod, true); if (ret) {
netdev_err(mod->ndev, "unable to enable termination\n"); return ret;
}
/* default to "bus errors enabled" */
ret = ican3_set_buserror(mod, 1); if (ret) {
netdev_err(mod->ndev, "unable to set bus-error\n"); return ret;
}
ican3_init_fast_host_interface(mod);
ret = ican3_msg_fasthostif(mod); if (ret) {
netdev_err(mod->ndev, "unable to switch to fast host interface\n"); return ret;
}
ret = ican3_set_id_filter(mod, true); if (ret) {
netdev_err(mod->ndev, "unable to set acceptance filter\n"); return ret;
}
/* open the CAN layer */
ret = open_candev(ndev); if (ret) {
netdev_err(mod->ndev, "unable to start CAN layer\n"); return ret;
}
/* bring the bus online */
ret = ican3_set_bus_state(mod, true); if (ret) {
netdev_err(mod->ndev, "unable to set bus-on\n");
close_candev(ndev); return ret;
}
/* start up the network device */
mod->can.state = CAN_STATE_ERROR_ACTIVE;
netif_start_queue(ndev);
/* bring the bus offline, stop receiving packets */
ret = ican3_set_bus_state(mod, false); if (ret) {
netdev_err(mod->ndev, "unable to set bus-off\n"); return ret;
}
/* drop all outstanding echo skbs */
skb_queue_purge(&mod->echoq);
/* close the CAN layer */
close_candev(ndev); return 0;
}
if (can_dev_dropped_skb(ndev, skb)) return NETDEV_TX_OK;
spin_lock_irqsave(&mod->lock, flags);
/* check that we can actually transmit */ if (!ican3_txok(mod)) {
netdev_err(mod->ndev, "BUG: no free descriptors\n");
spin_unlock_irqrestore(&mod->lock, flags); return NETDEV_TX_BUSY;
}
/* copy the control bits of the descriptor */
ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
desc_addr = mod->dpm + ((mod->fasttx_num % 16) * sizeof(desc));
memset(&desc, 0, sizeof(desc));
memcpy_fromio(&desc, desc_addr, 1);
/* convert the Linux CAN frame into ICAN3 format */
can_frame_to_ican3(mod, cf, &desc);
/* * This hardware doesn't have TX-done notifications, so we'll try and * emulate it the best we can using ECHO skbs. Add the skb to the ECHO * stack. Upon packet reception, check if the ECHO skb and received * skb match, and use that to wake the queue.
*/
ican3_put_echo_skb(mod, skb);
/* * the programming manual says that you must set the IVALID bit, then * interrupt, then set the valid bit. Quite weird, but it seems to be * required for this to work
*/
desc.control |= DESC_IVALID;
memcpy_toio(desc_addr, &desc, sizeof(desc));
/* generate a MODULbus interrupt to the microcontroller */
iowrite8(0x01, &mod->dpmctrl->interrupt);
pdata = dev_get_platdata(&pdev->dev); if (!pdata) return -ENXIO;
dev_dbg(&pdev->dev, "probe: module number %d\n", pdata->modno);
/* save the struct device for printing */
dev = &pdev->dev;
/* allocate the CAN device and private data */
ndev = alloc_candev(sizeof(*mod), 0); if (!ndev) {
dev_err(dev, "unable to allocate CANdev\n");
ret = -ENOMEM; goto out_return;
}
/* find our IRQ number */
mod->irq = platform_get_irq(pdev, 0); if (mod->irq < 0) {
ret = -ENODEV; goto out_free_ndev;
}
ndev->irq = mod->irq;
/* get access to the MODULbus registers for this module */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) {
dev_err(dev, "MODULbus registers not found\n");
ret = -ENODEV; goto out_free_ndev;
}
mod->dpm = ioremap(res->start, resource_size(res)); if (!mod->dpm) {
dev_err(dev, "MODULbus registers not ioremap\n");
ret = -ENOMEM; goto out_free_ndev;
}
mod->dpmctrl = mod->dpm + DPM_PAGE_SIZE;
/* get access to the control registers for this module */
res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) {
dev_err(dev, "CONTROL registers not found\n");
ret = -ENODEV; goto out_iounmap_dpm;
}
mod->ctrl = ioremap(res->start, resource_size(res)); if (!mod->ctrl) {
dev_err(dev, "CONTROL registers not ioremap\n");
ret = -ENOMEM; goto out_iounmap_dpm;
}
/* disable our IRQ, then hookup the IRQ handler */
iowrite8(1 << mod->num, &mod->ctrl->int_disable);
ret = request_irq(mod->irq, ican3_irq, IRQF_SHARED, DRV_NAME, mod); if (ret) {
dev_err(dev, "unable to request IRQ\n"); goto out_iounmap_ctrl;
}
/* reset and initialize the CAN controller into fast mode */
napi_enable(&mod->napi);
ret = ican3_startup_module(mod); if (ret) {
dev_err(dev, "%s: unable to start CANdev\n", __func__); goto out_free_irq;
}
/* register with the Linux CAN layer */
ret = register_candev(ndev); if (ret) {
dev_err(dev, "%s: unable to register CANdev\n", __func__); goto out_free_irq;
}
netdev_info(mod->ndev, "module %d: registered CAN device\n", pdata->modno); return 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.