On 07/19/2011 02:03 PM, Wolfgang Grandegger wrote:
Mike, could you please post your version of flexcan.c. It's not the one
from mainline I suspect.

Thanks,

Wolfgang.
$ cat ./build/linux-2.6.35.13/drivers/net/can/flexcan.c
/*
 * flexcan.c - FLEXCAN CAN controller driver
 *
 * Copyright (c) 2005-2006 Varma Electronics Oy
 * Copyright (c) 2009 Sascha Hauer, Pengutronix
 * Copyright (c) 2010 Marc Kleine-Budde, Pengutronix
 * Copyright 2011 Freescale Semiconductor, Inc.
 *
 * Modifier: Bhaskar upadhaya <[email protected]>
 * Based on code originally by Andrey Volkov <[email protected]>
 *
 * LICENCE:
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation version 2.
 *
 * 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.
 *
 */

#include <linux/can/platform/flexcan.h>

static struct can_bittiming_const flexcan_bittiming_const = {
    .name = DRV_NAME,
    .tseg1_min = 4,
    .tseg1_max = 16,
    .tseg2_min = 2,
    .tseg2_max = 8,
    .sjw_max = 4,
    .brp_min = 1,
    .brp_max = 256,
    .brp_inc = 1,
};

/*
 * Swtich transceiver on or off
 */
static void flexcan_transceiver_switch(const struct flexcan_priv *priv, int on)
{
    if (priv->pdata && priv->pdata->transceiver_switch)
        priv->pdata->transceiver_switch(on);
}

static inline int flexcan_has_and_handle_berr(const struct flexcan_priv *priv,
                          u32 reg_esr)
{
    return (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) &&
            (reg_esr & FLEXCAN_ESR_ERR_BUS);
}

static inline void flexcan_chip_enable(struct flexcan_priv *priv)
{
    struct flexcan_regs __iomem *regs = priv->base;
    u32 reg;
    struct flexcan_interface *flexcan_cb = priv->flexcan_cb;

    reg = flexcan_cb->read(&regs->mcr);

    reg &= ~FLEXCAN_MCR_MDIS;
    flexcan_cb->write(reg, &regs->mcr);

    udelay(10);
}

static inline void flexcan_chip_disable(struct flexcan_priv *priv)
{
    struct flexcan_regs __iomem *regs = priv->base;
    u32 reg;
    struct flexcan_interface *flexcan_cb = priv->flexcan_cb;

    reg = flexcan_cb->read(&regs->mcr);

    reg |= FLEXCAN_MCR_MDIS;
    flexcan_cb->write(reg, &regs->mcr);
}

static int flexcan_get_berr_counter(const struct net_device *dev,
                    struct can_berr_counter *bec)
{
    const struct flexcan_priv *priv = netdev_priv(dev);
    struct flexcan_regs __iomem *regs = priv->base;
    struct flexcan_interface *flexcan_cb = priv->flexcan_cb;
    u32 reg = flexcan_cb->read(&regs->ecr);
    bec->txerr = reg & 0xff;
    bec->rxerr = (reg >> 8) & 0xff;

    return 0;
}

static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    const struct flexcan_priv *priv = netdev_priv(dev);
    struct net_device_stats *stats = &dev->stats;
    struct flexcan_regs __iomem *regs = priv->base;
    struct can_frame *cf = (struct can_frame *)skb->data;
    u32 can_id;
    u32 ctrl = FLEXCAN_MB_CNT_CODE(0xc) | (cf->can_dlc << 16);
    struct flexcan_interface *flexcan_cb = priv->flexcan_cb;

#ifdef CONFIG_CAN_DEBUG_DEVICES
    flexcan_reg_dump(dev);
#endif
    if (can_dropped_invalid_skb(dev, skb))
        return NETDEV_TX_OK;

    netif_stop_queue(dev);

    if (cf->can_id & CAN_EFF_FLAG) {
        can_id = cf->can_id & CAN_EFF_MASK;
        ctrl |= FLEXCAN_MB_CNT_IDE | FLEXCAN_MB_CNT_SRR;
    } else {
        can_id = (cf->can_id & CAN_SFF_MASK) << 18;
    }

    if (cf->can_id & CAN_RTR_FLAG)
        ctrl |= FLEXCAN_MB_CNT_RTR;

    if (cf->can_dlc > 0) {
        u32 data = be32_to_cpup((__be32 *) &cf->data[0]);
        flexcan_cb->write(data, \
&regs->cantxfg[FLEXCAN_TX_BUF_ID].data[0]);
    }
    if (cf->can_dlc > 3) {
        u32 data = be32_to_cpup((__be32 *) &cf->data[4]);
        flexcan_cb->write(data, \
&regs->cantxfg[FLEXCAN_TX_BUF_ID].data[1]);
    }

    flexcan_cb->write(can_id, &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_id);
    flexcan_cb->write(ctrl, &regs->cantxfg[FLEXCAN_TX_BUF_ID].can_ctrl);

    kfree_skb(skb);

    /* tx_packets is incremented in flexcan_irq */
    stats->tx_bytes += cf->can_dlc;

#ifdef CONFIG_CAN_DEBUG_DEVICES
    flexcan_reg_dump(dev);
#endif
    return NETDEV_TX_OK;
}

static void do_bus_err(struct net_device *dev,
               struct can_frame *cf, u32 reg_esr)
{
    struct flexcan_priv *priv = netdev_priv(dev);
    int rx_errors = 0, tx_errors = 0;

    cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;

    if (reg_esr & FLEXCAN_ESR_BIT1_ERR) {
        dev_dbg(dev->dev.parent, "BIT1_ERR irq\n");
        cf->data[2] |= CAN_ERR_PROT_BIT1;
        tx_errors = 1;
    }
    if (reg_esr & FLEXCAN_ESR_BIT0_ERR) {
        dev_dbg(dev->dev.parent, "BIT0_ERR irq\n");
        cf->data[2] |= CAN_ERR_PROT_BIT0;
        tx_errors = 1;
    }
    if (reg_esr & FLEXCAN_ESR_ACK_ERR) {
        dev_dbg(dev->dev.parent, "ACK_ERR irq\n");
        cf->can_id |= CAN_ERR_ACK;
        cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
        tx_errors = 1;
    }
    if (reg_esr & FLEXCAN_ESR_CRC_ERR) {
        dev_dbg(dev->dev.parent, "CRC_ERR irq\n");
        cf->data[2] |= CAN_ERR_PROT_BIT;
        cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
        rx_errors = 1;
    }
    if (reg_esr & FLEXCAN_ESR_FRM_ERR) {
        dev_dbg(dev->dev.parent, "FRM_ERR irq\n");
        cf->data[2] |= CAN_ERR_PROT_FORM;
        rx_errors = 1;
    }
    if (reg_esr & FLEXCAN_ESR_STF_ERR) {
        dev_dbg(dev->dev.parent, "STF_ERR irq\n");
        cf->data[2] |= CAN_ERR_PROT_STUFF;
        rx_errors = 1;
    }

    priv->can.can_stats.bus_error++;
    if (rx_errors)
        dev->stats.rx_errors++;
    if (tx_errors)
        dev->stats.tx_errors++;
}

static int flexcan_poll_bus_err(struct net_device *dev, u32 reg_esr)
{
    struct sk_buff *skb;
    struct can_frame *cf;

    skb = alloc_can_err_skb(dev, &cf);
    if (unlikely(!skb))
        return 0;

    do_bus_err(dev, cf, reg_esr);
    netif_receive_skb(skb);

    dev->stats.rx_packets++;
    dev->stats.rx_bytes += cf->can_dlc;

    return 1;
}

static void do_state(struct net_device *dev,
             struct can_frame *cf, enum can_state new_state)
{
    struct flexcan_priv *priv = netdev_priv(dev);
    struct can_berr_counter bec;

    flexcan_get_berr_counter(dev, &bec);

    switch (priv->can.state) {
    case CAN_STATE_ERROR_ACTIVE:
        /*
         * from: ERROR_ACTIVE
         * to  : ERROR_WARNING, ERROR_PASSIVE, BUS_OFF
         * =>  : there was a warning int
         */
        if (new_state >= CAN_STATE_ERROR_WARNING &&
            new_state <= CAN_STATE_BUS_OFF) {
            dev_dbg(dev->dev.parent, "Error Warning IRQ\n");
            priv->can.can_stats.error_warning++;

            cf->can_id |= CAN_ERR_CRTL;
            cf->data[1] = (bec.txerr > bec.rxerr) ?
                CAN_ERR_CRTL_TX_WARNING : CAN_ERR_CRTL_RX_WARNING;
        }
        /* fallthrough */
    case CAN_STATE_ERROR_WARNING:
        /*
         * from: ERROR_ACTIVE, ERROR_WARNING
         * to  : ERROR_PASSIVE, BUS_OFF
         * =>  : error passive int
         */
        if (new_state >= CAN_STATE_ERROR_PASSIVE &&
            new_state <= CAN_STATE_BUS_OFF) {
            dev_dbg(dev->dev.parent, "Error Passive IRQ\n");
            priv->can.can_stats.error_passive++;

            cf->can_id |= CAN_ERR_CRTL;
            cf->data[1] = (bec.txerr > bec.rxerr) ?
                CAN_ERR_CRTL_TX_PASSIVE : CAN_ERR_CRTL_RX_PASSIVE;
        }
        break;
    case CAN_STATE_BUS_OFF:
        dev_err(dev->dev.parent,
            "BUG! hardware recovered automatically from BUS_OFF\n");
        break;
    default:
        break;
    }

    /* process state changes depending on the new state */
    switch (new_state) {
    case CAN_STATE_ERROR_ACTIVE:
        dev_dbg(dev->dev.parent, "Error Active\n");
        cf->can_id |= CAN_ERR_PROT;
        cf->data[2] = CAN_ERR_PROT_ACTIVE;
        break;
    case CAN_STATE_BUS_OFF:
        cf->can_id |= CAN_ERR_BUSOFF;
        can_bus_off(dev);
        break;
    default:
        break;
    }
}

static int flexcan_poll_state(struct net_device *dev, u32 reg_esr)
{
    struct flexcan_priv *priv = netdev_priv(dev);
    struct sk_buff *skb;
    struct can_frame *cf;
    enum can_state new_state;
    int flt;

    flt = reg_esr & FLEXCAN_ESR_FLT_CONF_MASK;
    if (likely(flt == FLEXCAN_ESR_FLT_CONF_ACTIVE)) {
        if (likely(!(reg_esr & (FLEXCAN_ESR_TX_WRN |
                    FLEXCAN_ESR_RX_WRN))))
            new_state = CAN_STATE_ERROR_ACTIVE;
        else
            new_state = CAN_STATE_ERROR_WARNING;
    } else if (unlikely(flt == FLEXCAN_ESR_FLT_CONF_PASSIVE))
        new_state = CAN_STATE_ERROR_PASSIVE;
    else
        new_state = CAN_STATE_BUS_OFF;

    /* state hasn't changed */
    if (likely(new_state == priv->can.state))
        return 0;

    skb = alloc_can_err_skb(dev, &cf);
    if (unlikely(!skb))
        return 0;

    do_state(dev, cf, new_state);
    priv->can.state = new_state;
    netif_receive_skb(skb);

    dev->stats.rx_packets++;
    dev->stats.rx_bytes += cf->can_dlc;

    return 1;
}

static void flexcan_read_fifo(const struct net_device *dev,
                  struct can_frame *cf)
{
    const struct flexcan_priv *priv = netdev_priv(dev);
    struct flexcan_regs __iomem *regs = priv->base;
    struct flexcan_mb __iomem *mb = &regs->cantxfg[0];
    u32 reg_ctrl, reg_id;
    struct flexcan_interface *flexcan_cb = priv->flexcan_cb;

    reg_ctrl = flexcan_cb->read(&mb->can_ctrl);
    reg_id = flexcan_cb->read(&mb->can_id);
    if (reg_ctrl & FLEXCAN_MB_CNT_IDE)
        cf->can_id = ((reg_id >> 0) & CAN_EFF_MASK) | CAN_EFF_FLAG;
    else
        cf->can_id = (reg_id >> 18) & CAN_SFF_MASK;

    if (reg_ctrl & FLEXCAN_MB_CNT_RTR)
        cf->can_id |= CAN_RTR_FLAG;
    cf->can_dlc = get_can_dlc((reg_ctrl >> 16) & 0xf);

    *(__be32 *) (cf->data + 0) =
        cpu_to_be32(flexcan_cb->read(&mb->data[0]));
    *(__be32 *) (cf->data + 4) =
        cpu_to_be32(flexcan_cb->read(&mb->data[1]));

    /* mark as read */
    flexcan_cb->write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, &regs->iflag1);
}

static int flexcan_read_frame(struct net_device *dev)
{
    struct net_device_stats *stats = &dev->stats;
    struct can_frame *cf;
    struct sk_buff *skb;

    skb = alloc_can_skb(dev, &cf);
    if (unlikely(!skb)) {
        stats->rx_dropped++;
        return 0;
    }

    flexcan_read_fifo(dev, cf);
    netif_receive_skb(skb);

    stats->rx_packets++;
    stats->rx_bytes += cf->can_dlc;

    return 1;
}

static int flexcan_poll(struct napi_struct *napi, int quota)
{
    struct net_device *dev = napi->dev;
    const struct flexcan_priv *priv = netdev_priv(dev);
    struct flexcan_regs __iomem *regs = priv->base;
    u32 reg_iflag1, reg_esr;
    int work_done = 0;
    struct flexcan_interface *flexcan_cb = priv->flexcan_cb;

    /*
     * The error bits are cleared on read,
     * use saved value from irq handler.
     */
    reg_esr = flexcan_cb->read(&regs->esr) | priv->reg_esr;

    /* handle state changes */
    work_done += flexcan_poll_state(dev, reg_esr);

    /* handle RX-FIFO */
    reg_iflag1 = flexcan_cb->read(&regs->iflag1);
    while ((reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) &&
           (work_done < quota)) {
        work_done += flexcan_read_frame(dev);
        reg_iflag1 = flexcan_cb->read(&regs->iflag1);
    }

    /* report bus errors */
    if (flexcan_has_and_handle_berr(priv, reg_esr) && work_done < quota)
        work_done += flexcan_poll_bus_err(dev, reg_esr);

    if (work_done < quota) {
        napi_complete(napi);
        /* enable IRQs */
        flexcan_cb->write(FLEXCAN_IFLAG_DEFAULT, &regs->imask1);
        flexcan_cb->write(priv->reg_ctrl_default, &regs->ctrl);
    }

    return work_done;
}

static irqreturn_t flexcan_irq(int irq, void *dev_id)
{
    struct net_device *dev = dev_id;
    struct net_device_stats *stats = &dev->stats;
    struct flexcan_priv *priv = netdev_priv(dev);
    struct flexcan_regs __iomem *regs = priv->base;
    u32 reg_iflag1, reg_esr;
    struct flexcan_interface *flexcan_cb = priv->flexcan_cb;

#ifdef CONFIG_CAN_DEBUG_DEVICES
    flexcan_reg_dump(dev);
#endif
    reg_iflag1 = flexcan_cb->read(&regs->iflag1);
    reg_esr = flexcan_cb->read(&regs->esr);
    flexcan_cb->write(FLEXCAN_ESR_ERR_INT, &regs->esr);/* ACK err IRQ */

    /*
     * schedule NAPI in case of:
     * - rx IRQ
     * - state change IRQ
     * - bus error IRQ and bus error reporting is activated
     */
    if ((reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) ||
        (reg_esr & FLEXCAN_ESR_ERR_STATE) ||
        flexcan_has_and_handle_berr(priv, reg_esr)) {
        /*
         * The error bits are cleared on read,
         * save them for later use.
         */
        priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS;
        flexcan_cb->write(FLEXCAN_IFLAG_DEFAULT & \
            ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, &regs->imask1);
        flexcan_cb->write(priv->reg_ctrl_default & \
            ~FLEXCAN_CTRL_ERR_ALL, &regs->ctrl);
        napi_schedule(&priv->napi);
    }

    /* FIFO overflow */
    if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) {
        flexcan_cb->write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, \
&regs->iflag1);
        dev->stats.rx_over_errors++;
        dev->stats.rx_errors++;
    }

    /* transmission complete interrupt */
    if (reg_iflag1 & (1 << FLEXCAN_TX_BUF_ID)) {
        /* tx_bytes is incremented in flexcan_start_xmit */
        stats->tx_packets++;
        flexcan_cb->write((1 << FLEXCAN_TX_BUF_ID), &regs->iflag1);
        netif_wake_queue(dev);
    }

#ifdef CONFIG_CAN_DEBUG_DEVICES
    flexcan_reg_dump(dev);
#endif
    return IRQ_HANDLED;
}

static void flexcan_set_bittiming(struct net_device *dev)
{
    const struct flexcan_priv *priv = netdev_priv(dev);
    const struct can_bittiming *bt = &priv->can.bittiming;
    struct flexcan_regs __iomem *regs = priv->base;
    u32 reg;
    struct flexcan_interface *flexcan_cb = priv->flexcan_cb;

    reg = flexcan_cb->read(&regs->ctrl);
    reg &= ~(FLEXCAN_CTRL_PRESDIV(0xff) |
         FLEXCAN_CTRL_RJW(0x3) |
         FLEXCAN_CTRL_PSEG1(0x7) |
         FLEXCAN_CTRL_PSEG2(0x7) |
         FLEXCAN_CTRL_PROPSEG(0x7) |
         FLEXCAN_CTRL_SMP | FLEXCAN_CTRL_LOM);

    reg |= FLEXCAN_CTRL_PRESDIV(bt->brp - 1) |
        FLEXCAN_CTRL_PSEG1(bt->phase_seg1 - 1) |
        FLEXCAN_CTRL_PSEG2(bt->phase_seg2 - 1) |
        FLEXCAN_CTRL_RJW(bt->sjw - 1) |
        FLEXCAN_CTRL_PROPSEG(bt->prop_seg - 1);

    if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
        reg |= FLEXCAN_CTRL_LOM;
    if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
        reg |= FLEXCAN_CTRL_SMP;

    dev_info(dev->dev.parent, "writing ctrl=0x%08x\n", reg);
    flexcan_cb->write(reg, &regs->ctrl);

    /* print chip status */
    dev_dbg(dev->dev.parent, "%s: mcr=0x%08x ctrl=0x%08x\n", __func__,
        flexcan_cb->read(&regs->mcr), flexcan_cb->read(&regs->ctrl));
}

/*
 * flexcan_chip_start
 *
 * this function is entered with clocks enabled
 *
 */
static int flexcan_chip_start(struct net_device *dev)
{
    struct flexcan_priv *priv = netdev_priv(dev);
    struct flexcan_regs __iomem *regs = priv->base;
    unsigned int i;
    int err;
    u32 reg_mcr, reg_ctrl;
    struct flexcan_interface *flexcan_cb = priv->flexcan_cb;

    /* enable module */
    flexcan_chip_enable(priv);

    /* soft reset */
    flexcan_cb->write(FLEXCAN_MCR_SOFTRST, &regs->mcr);
    udelay(10);

    reg_mcr = flexcan_cb->read(&regs->mcr);
    if (reg_mcr & FLEXCAN_MCR_SOFTRST) {
        dev_err(dev->dev.parent,
            "Failed to softreset can module (mcr=0x%08x)\n",
            reg_mcr);
        err = -ENODEV;
        goto out;
    }

    flexcan_set_bittiming(dev);

    /*
     * MCR
     *
     * enable freeze
     * enable fifo
     * halt now
     * only supervisor access
     * enable warning int
     * choose format C
     *
     */
    reg_mcr = flexcan_cb->read(&regs->mcr);
    reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT |
        FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_IDAM_C |
        FLEXCAN_MCR_SRX_DIS;
    dev_dbg(dev->dev.parent, "%s: writing mcr=0x%08x", __func__, reg_mcr);
    flexcan_cb->write(reg_mcr, &regs->mcr);

    /*
     * CTRL
     *
     * disable timer sync feature
     *
     * disable auto busoff recovery
     * transmit lowest buffer first
     *
     * enable tx and rx warning interrupt
     * enable bus off interrupt
     * (== FLEXCAN_CTRL_ERR_STATE)
     *
     * _note_: we enable the "error interrupt"
     * (FLEXCAN_CTRL_ERR_MSK), too. Otherwise we don't get any
     * warning or bus passive interrupts.
     */
    reg_ctrl = flexcan_cb->read(&regs->ctrl);
    reg_ctrl &= ~FLEXCAN_CTRL_TSYN;
    reg_ctrl |= FLEXCAN_CTRL_BOFF_REC | FLEXCAN_CTRL_LBUF |
        FLEXCAN_CTRL_ERR_STATE | FLEXCAN_CTRL_ERR_MSK;

    /* save for later use */
    priv->reg_ctrl_default = reg_ctrl;
dev_dbg(dev->dev.parent, "%s: writing ctrl=0x%08x", __func__, reg_ctrl);
    flexcan_cb->write(reg_ctrl, &regs->ctrl);

    for (i = 0; i < ARRAY_SIZE(regs->cantxfg); i++) {
        flexcan_cb->write(0, &regs->cantxfg[i].can_ctrl);
        flexcan_cb->write(0, &regs->cantxfg[i].can_id);
        flexcan_cb->write(0, &regs->cantxfg[i].data[0]);
        flexcan_cb->write(0, &regs->cantxfg[i].data[1]);

        /* put MB into rx queue */
        flexcan_cb->write(FLEXCAN_MB_CNT_CODE(0x4), \
&regs->cantxfg[i].can_ctrl);
    }

    /* acceptance mask/acceptance code (accept everything) */
    flexcan_cb->write(0x0, &regs->rxgmask);
    flexcan_cb->write(0x0, &regs->rx14mask);
    flexcan_cb->write(0x0, &regs->rx15mask);

    flexcan_transceiver_switch(priv, 1);

    /* synchronize with the can bus */
    reg_mcr = flexcan_cb->read(&regs->mcr);
    reg_mcr &= ~FLEXCAN_MCR_HALT;
    flexcan_cb->write(reg_mcr, &regs->mcr);

    priv->can.state = CAN_STATE_ERROR_ACTIVE;

    /* enable FIFO interrupts */
    flexcan_cb->write(FLEXCAN_IFLAG_DEFAULT, &regs->imask1);

    /* print chip status */
    dev_dbg(dev->dev.parent, "%s: reading mcr=0x%08x ctrl=0x%08x\n",
        __func__, flexcan_cb->read(&regs->mcr), \
            flexcan_cb->read(&regs->ctrl));

    return 0;

 out:
    flexcan_chip_disable(priv);
    return err;
}

/*
 * flexcan_chip_stop
 *
 * this functions is entered with clocks enabled
 *
 */
static void flexcan_chip_stop(struct net_device *dev)
{
    struct flexcan_priv *priv = netdev_priv(dev);
    struct flexcan_regs __iomem *regs = priv->base;
    u32 reg;
    struct flexcan_interface *flexcan_cb = priv->flexcan_cb;

    /* Disable all interrupts */
    flexcan_cb->write(0, &regs->imask1);

    /* Disable + halt module */
    reg = flexcan_cb->read(&regs->mcr);
    reg |= FLEXCAN_MCR_MDIS | FLEXCAN_MCR_HALT;
    flexcan_cb->write(reg, &regs->mcr);

    flexcan_transceiver_switch(priv, 0);
    priv->can.state = CAN_STATE_STOPPED;

    return;
}

static int flexcan_open(struct net_device *dev)
{
    struct flexcan_priv *priv = netdev_priv(dev);
    int err;

    clk_enable(priv->clk);

    err = open_candev(dev);
    if (err)
        goto out;

    err = request_irq(dev->irq, flexcan_irq, IRQF_SHARED, dev->name, dev);
    if (err)
        goto out_close;

    /* start chip and queuing */
    err = flexcan_chip_start(dev);
    if (err)
        goto out_close;
    napi_enable(&priv->napi);
    netif_start_queue(dev);

    return 0;

 out_close:
    close_candev(dev);
 out:
    clk_disable(priv->clk);

    return err;
}

static int flexcan_close(struct net_device *dev)
{
    struct flexcan_priv *priv = netdev_priv(dev);

    netif_stop_queue(dev);
    napi_disable(&priv->napi);
    flexcan_chip_stop(dev);

    free_irq(dev->irq, dev);
    clk_disable(priv->clk);

    close_candev(dev);

    return 0;
}

static int flexcan_set_mode(struct net_device *dev, enum can_mode mode)
{
    int err;

    switch (mode) {
    case CAN_MODE_START:
        err = flexcan_chip_start(dev);
        if (err)
            return err;

        netif_wake_queue(dev);
        break;

    default:
#ifdef CONFIG_CAN_DEBUG_DEVICES
        DBG(KERN_INFO "Setting flexcan mode(%d) in func %s in line \
                    %d \r\n", mode, __func__, __LINE__);
#endif
        return -EOPNOTSUPP;
    }

    return 0;
}

static const struct net_device_ops flexcan_netdev_ops = {
    .ndo_open = flexcan_open,
    .ndo_stop = flexcan_close,
    .ndo_start_xmit = flexcan_start_xmit,
};

static int __devinit register_flexcandev(struct net_device *dev)
{
    struct flexcan_priv *priv = netdev_priv(dev);
    struct flexcan_regs __iomem *regs = priv->base;
    u32 reg, err;
    struct flexcan_interface *flexcan_cb = priv->flexcan_cb;

    clk_enable(priv->clk);

    /* select "bus clock", chip must be disabled */
    flexcan_chip_disable(priv);
    reg = flexcan_cb->read(&regs->ctrl);
    reg |= FLEXCAN_CTRL_CLK_SRC;
    flexcan_cb->write(reg, &regs->ctrl);

    flexcan_chip_enable(priv);

    /* set freeze, halt and activate FIFO, restrict register access */
    reg = flexcan_cb->read(&regs->mcr);
    reg |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT |
        FLEXCAN_MCR_FEN | FLEXCAN_MCR_SUPV;
    flexcan_cb->write(reg, &regs->mcr);

    /*
     * Currently we only support newer versions of this core
     * featuring a RX FIFO. Older cores found on some Coldfire
     * derivates are not yet supported.
     */
    reg = flexcan_cb->read(&regs->mcr);
    if (!(reg & FLEXCAN_MCR_FEN)) {
        dev_err(dev->dev.parent,
            "Could not enable RX FIFO, unsupported core\n");
        err = -ENODEV;
        goto out;
    }

    err = register_candev(dev);

    return err;
 out:
    /* disable core and turn off clocks */
    flexcan_chip_disable(priv);
    clk_disable(priv->clk);

    return err;
}

void __devexit unregister_flexcandev(struct net_device *dev)
{
    unregister_candev(dev);
}

int flexcan_core(struct device *pdev, struct flexcan_resource flexcan_res,
         struct flexcan_interface *flexcan_cb)
{
    struct net_device *dev;
    struct flexcan_priv *priv;
    struct clk *clk;
    void __iomem *base;
    int err;

    clk = flexcan_cb->clk_get(pdev, NULL);
    if (IS_ERR(clk)) {
        dev_err(pdev, "no clock defined\n");
        err = PTR_ERR(clk);
        goto failed_clock;
    }

    if (!request_mem_region
        (flexcan_res.addr, flexcan_res.size, flexcan_res.drv_name)) {
        err = -EBUSY;
        goto failed_req;
    }

    base = ioremap(flexcan_res.addr, flexcan_res.size);
    if (!base) {
        err = -ENOMEM;
        goto failed_map;
    }

    dev = alloc_candev(sizeof(struct flexcan_priv), 0);
    if (!dev) {
        err = -ENOMEM;
        goto failed_alloc;
    }

    dev->netdev_ops = &flexcan_netdev_ops;
    dev->irq = flexcan_res.irq;
    //dev->flags |= IFF_ECHO;    /* we support local echo in hardware */
    dev->flags &= ~IFF_ECHO;

    priv = netdev_priv(dev);
    priv->can.clock.freq = flexcan_cb->clk_get_rate(clk);
    priv->can.bittiming_const = &flexcan_bittiming_const;
    priv->can.do_set_mode = flexcan_set_mode;
    priv->can.do_get_berr_counter = flexcan_get_berr_counter;
    priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
                    CAN_CTRLMODE_LISTENONLY |
                    CAN_CTRLMODE_3_SAMPLES |
                    CAN_CTRLMODE_BERR_REPORTING;
    priv->base = base;
    priv->dev = dev;
    priv->clk = clk;
    priv->pdata = pdev->platform_data;
    priv->flexcan_cb = flexcan_cb;

    netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT);

    dev_set_drvdata(pdev, dev);
    SET_NETDEV_DEV(dev, pdev);

    err = register_flexcandev(dev);
    if (err) {
        dev_err(pdev, "registering netdev failed\n");
        goto failed_register;
    }

    dev_info(pdev, "device registered (reg_base=%p, irq=%d, flags=%X)\n",
         priv->base, dev->irq, dev->flags);

    return 0;

 failed_register:
    free_candev(dev);
 failed_alloc:
    iounmap(base);
 failed_map:
    release_mem_region(flexcan_res.addr, flexcan_res.size);
 failed_req:
    clk_put(clk);
 failed_clock:
    return err;
}
#ifdef CONFIG_CAN_DEBUG_DEVICES
void flexcan_reg_dump(struct net_device *dev)
{
    const struct flexcan_priv *priv = netdev_priv(dev);
    struct flexcan_regs __iomem *regs = priv->base;
    struct flexcan_interface *flexcan_cb = priv->flexcan_cb;

    DBG(KERN_INFO "can-mcr 0x%x \r\n can-ctrl 0x%x \r\n  \
            can-ecr 0x%x \r\n can-esr 0x%x \r\n \
            can-rxgmask 0x%x \r\n can-rx14mask 0x%x \r\n \
            can-rx15mask 0x%x \r\n can-imask1 0x%x \r\n \
            can-iflag1 0x%x \r\n \
            in  func <%s> line <%d> \r\n", \
            flexcan_cb->read(&regs->mcr), \
            flexcan_cb->read(&regs->ctrl), \
            flexcan_cb->read(&regs->ecr),\
            flexcan_cb->read(&regs->esr), \
            flexcan_cb->read(&regs->rxgmask),\
            flexcan_cb->read(&regs->rx14mask), \
            flexcan_cb->read(&regs->rx15mask), \
            flexcan_cb->read(&regs->imask1), \
            flexcan_cb->read(&regs->iflag1), __func__, __LINE__);
}
#endif


--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Mike Brown                                      Email: [email protected]
Engineering Software and Diagnostics
SGI                                             Phone: (715)726-7613
1168 Technology Way
Chippewa Falls, WI 54729                        Fax:   (715)726-4345
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

_______________________________________________
Socketcan-users mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/socketcan-users

Reply via email to