Logo Search packages:      
Sourcecode: linux-fsl-imx51 version File versions  Download package

dev.c

Go to the documentation of this file.
/*
 * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

/*!
 * @file dev.c
 *
 * @brief Driver for Freescale CAN Controller FlexCAN.
 *
 * @ingroup can
 */
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/spinlock.h>
#include <linux/device.h>

#include <linux/module.h>
#include <mach/hardware.h>
#include "flexcan.h"

enum {
      FLEXCAN_ATTR_STATE = 0,
      FLEXCAN_ATTR_BITRATE,
      FLEXCAN_ATTR_BR_PRESDIV,
      FLEXCAN_ATTR_BR_RJW,
      FLEXCAN_ATTR_BR_PROPSEG,
      FLEXCAN_ATTR_BR_PSEG1,
      FLEXCAN_ATTR_BR_PSEG2,
      FLEXCAN_ATTR_BR_CLKSRC,
      FLEXCAN_ATTR_MAXMB,
      FLEXCAN_ATTR_XMIT_MAXMB,
      FLEXCAN_ATTR_FIFO,
      FLEXCAN_ATTR_WAKEUP,
      FLEXCAN_ATTR_SRX_DIS,
      FLEXCAN_ATTR_WAK_SRC,
      FLEXCAN_ATTR_BCC,
      FLEXCAN_ATTR_LOCAL_PRIORITY,
      FLEXCAN_ATTR_ABORT,
      FLEXCAN_ATTR_LOOPBACK,
      FLEXCAN_ATTR_SMP,
      FLEXCAN_ATTR_BOFF_REC,
      FLEXCAN_ATTR_TSYN,
      FLEXCAN_ATTR_LISTEN,
      FLEXCAN_ATTR_EXTEND_MSG,
      FLEXCAN_ATTR_STANDARD_MSG,
#ifdef CONFIG_CAN_DEBUG_DEVICES
      FLEXCAN_ATTR_DUMP_REG,
      FLEXCAN_ATTR_DUMP_XMIT_MB,
      FLEXCAN_ATTR_DUMP_RX_MB,
#endif
      FLEXCAN_ATTR_MAX
};

static ssize_t flexcan_show_attr(struct device *dev,
                         struct device_attribute *attr, char *buf);
static ssize_t flexcan_set_attr(struct device *dev,
                        struct device_attribute *attr, const char *buf,
                        size_t count);

static struct device_attribute flexcan_dev_attr[FLEXCAN_ATTR_MAX] = {
      [FLEXCAN_ATTR_STATE] = __ATTR(state, 0444, flexcan_show_attr, NULL),
      [FLEXCAN_ATTR_BITRATE] =
          __ATTR(bitrate, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_BR_PRESDIV] =
          __ATTR(br_presdiv, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_BR_RJW] =
          __ATTR(br_rjw, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_BR_PROPSEG] =
          __ATTR(br_propseg, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_BR_PSEG1] =
          __ATTR(br_pseg1, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_BR_PSEG2] =
          __ATTR(br_pseg2, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_BR_CLKSRC] =
          __ATTR(br_clksrc, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_MAXMB] =
          __ATTR(maxmb, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_XMIT_MAXMB] =
          __ATTR(xmit_maxmb, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_FIFO] =
          __ATTR(fifo, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_WAKEUP] =
          __ATTR(wakeup, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_SRX_DIS] =
          __ATTR(srx_dis, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_WAK_SRC] =
          __ATTR(wak_src, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_BCC] =
          __ATTR(bcc, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_LOCAL_PRIORITY] =
          __ATTR(local_priority, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_ABORT] =
          __ATTR(abort, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_LOOPBACK] =
          __ATTR(loopback, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_SMP] =
          __ATTR(smp, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_BOFF_REC] =
          __ATTR(boff_rec, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_TSYN] =
          __ATTR(tsyn, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_LISTEN] =
          __ATTR(listen, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_EXTEND_MSG] =
          __ATTR(ext_msg, 0644, flexcan_show_attr, flexcan_set_attr),
      [FLEXCAN_ATTR_STANDARD_MSG] =
          __ATTR(std_msg, 0644, flexcan_show_attr, flexcan_set_attr),
#ifdef CONFIG_CAN_DEBUG_DEVICES
      [FLEXCAN_ATTR_DUMP_REG] =
          __ATTR(dump_reg, 0444, flexcan_show_attr, NULL),
      [FLEXCAN_ATTR_DUMP_XMIT_MB] =
          __ATTR(dump_xmit_mb, 0444, flexcan_show_attr, NULL),
      [FLEXCAN_ATTR_DUMP_RX_MB] =
          __ATTR(dump_rx_mb, 0444, flexcan_show_attr, NULL),
#endif
};

static void flexcan_set_bitrate(struct flexcan_device *flexcan, int bitrate)
{
      /* TODO:: implement in future
       * based on the bitrate to get the timing of
       * presdiv, pseg1, pseg2, propseg
       */
}

static void flexcan_update_bitrate(struct flexcan_device *flexcan)
{
      int rate, div;

      if (flexcan->br_clksrc)
            rate = clk_get_rate(flexcan->clk);
      else {
            struct clk *clk;
            clk = clk_get(NULL, "ckih");
            if (!clk)
                  return;
            rate = clk_get_rate(clk);
            clk_put(clk);
      }
      if (!rate)
            return;

      div = (flexcan->br_presdiv + 1);
      div *=
          (flexcan->br_propseg + flexcan->br_pseg1 + flexcan->br_pseg2 + 4);
      flexcan->bitrate = (rate + div - 1) / div;
}

#ifdef CONFIG_CAN_DEBUG_DEVICES
static int flexcan_dump_reg(struct flexcan_device *flexcan, char *buf)
{
      int ret = 0;
      unsigned int reg;
      reg = __raw_readl(flexcan->io_base + CAN_HW_REG_MCR);
      ret += sprintf(buf + ret, "MCR::0x%x\n", reg);
      reg = __raw_readl(flexcan->io_base + CAN_HW_REG_CTRL);
      ret += sprintf(buf + ret, "CTRL::0x%x\n", reg);
      reg = __raw_readl(flexcan->io_base + CAN_HW_REG_RXGMASK);
      ret += sprintf(buf + ret, "RXGMASK::0x%x\n", reg);
      reg = __raw_readl(flexcan->io_base + CAN_HW_REG_RX14MASK);
      ret += sprintf(buf + ret, "RX14MASK::0x%x\n", reg);
      reg = __raw_readl(flexcan->io_base + CAN_HW_REG_RX15MASK);
      ret += sprintf(buf + ret, "RX15MASK::0x%x\n", reg);
      reg = __raw_readl(flexcan->io_base + CAN_HW_REG_ECR);
      ret += sprintf(buf + ret, "ECR::0x%x\n", reg);
      reg = __raw_readl(flexcan->io_base + CAN_HW_REG_ESR);
      ret += sprintf(buf + ret, "ESR::0x%x\n", reg);
      reg = __raw_readl(flexcan->io_base + CAN_HW_REG_IMASK2);
      ret += sprintf(buf + ret, "IMASK2::0x%x\n", reg);
      reg = __raw_readl(flexcan->io_base + CAN_HW_REG_IMASK1);
      ret += sprintf(buf + ret, "IMASK1::0x%x\n", reg);
      reg = __raw_readl(flexcan->io_base + CAN_HW_REG_IFLAG2);
      ret += sprintf(buf + ret, "IFLAG2::0x%x\n", reg);
      reg = __raw_readl(flexcan->io_base + CAN_HW_REG_IFLAG1);
      ret += sprintf(buf + ret, "IFLAG1::0x%x\n", reg);
      return ret;
}

static int flexcan_dump_xmit_mb(struct flexcan_device *flexcan, char *buf)
{
      int ret = 0, i;
      i = flexcan->xmit_maxmb + 1;
      for (; i <= flexcan->maxmb; i++)
            ret +=
                sprintf(buf + ret,
                      "mb[%d]::CS:0x%x ID:0x%x DATA[1~2]:0x%02x,0x%02x\n",
                      i, flexcan->hwmb[i].mb_cs.data,
                      flexcan->hwmb[i].mb_id, flexcan->hwmb[i].mb_data[1],
                      flexcan->hwmb[i].mb_data[2]);
      return ret;
}

static int flexcan_dump_rx_mb(struct flexcan_device *flexcan, char *buf)
{
      int ret = 0, i;
      for (i = 0; i <= flexcan->xmit_maxmb; i++)
            ret +=
                sprintf(buf + ret,
                      "mb[%d]::CS:0x%x ID:0x%x DATA[1~2]:0x%02x,0x%02x\n",
                      i, flexcan->hwmb[i].mb_cs.data,
                      flexcan->hwmb[i].mb_id, flexcan->hwmb[i].mb_data[1],
                      flexcan->hwmb[i].mb_data[2]);
      return ret;
}
#endif

static ssize_t flexcan_show_state(struct net_device *net, char *buf)
{
      int ret, esr;
      struct flexcan_device *flexcan = netdev_priv(net);
      ret = sprintf(buf, "%s::", netif_running(net) ? "Start" : "Stop");
      if (netif_carrier_ok(net)) {
            esr = __raw_readl(flexcan->io_base + CAN_HW_REG_ESR);
            switch ((esr & __ESR_FLT_CONF_MASK) >> __ESR_FLT_CONF_OFF) {
            case 0:
                  ret += sprintf(buf + ret, "normal\n");
                  break;
            case 1:
                  ret += sprintf(buf + ret, "error passive\n");
                  break;
            default:
                  ret += sprintf(buf + ret, "bus off\n");
            }
      } else
            ret += sprintf(buf + ret, "bus off\n");
      return ret;
}

static ssize_t flexcan_show_attr(struct device *dev,
                         struct device_attribute *attr, char *buf)
{
      int attr_id;
      struct net_device *net;
      struct flexcan_device *flexcan;

      net = dev_get_drvdata(dev);
      BUG_ON(!net);
      flexcan = netdev_priv(net);
      BUG_ON(!flexcan);

      attr_id = attr - flexcan_dev_attr;
      switch (attr_id) {
      case FLEXCAN_ATTR_STATE:
            return flexcan_show_state(net, buf);
      case FLEXCAN_ATTR_BITRATE:
            return sprintf(buf, "%d\n", flexcan->bitrate);
      case FLEXCAN_ATTR_BR_PRESDIV:
            return sprintf(buf, "%d\n", flexcan->br_presdiv + 1);
      case FLEXCAN_ATTR_BR_RJW:
            return sprintf(buf, "%d\n", flexcan->br_rjw);
      case FLEXCAN_ATTR_BR_PROPSEG:
            return sprintf(buf, "%d\n", flexcan->br_propseg + 1);
      case FLEXCAN_ATTR_BR_PSEG1:
            return sprintf(buf, "%d\n", flexcan->br_pseg1 + 1);
      case FLEXCAN_ATTR_BR_PSEG2:
            return sprintf(buf, "%d\n", flexcan->br_pseg2 + 1);
      case FLEXCAN_ATTR_BR_CLKSRC:
            return sprintf(buf, "%s\n", flexcan->br_clksrc ? "bus" : "osc");
      case FLEXCAN_ATTR_MAXMB:
            return sprintf(buf, "%d\n", flexcan->maxmb + 1);
      case FLEXCAN_ATTR_XMIT_MAXMB:
            return sprintf(buf, "%d\n", flexcan->xmit_maxmb + 1);
      case FLEXCAN_ATTR_FIFO:
            return sprintf(buf, "%d\n", flexcan->fifo);
      case FLEXCAN_ATTR_WAKEUP:
            return sprintf(buf, "%d\n", flexcan->wakeup);
      case FLEXCAN_ATTR_SRX_DIS:
            return sprintf(buf, "%d\n", flexcan->srx_dis);
      case FLEXCAN_ATTR_WAK_SRC:
            return sprintf(buf, "%d\n", flexcan->wak_src);
      case FLEXCAN_ATTR_BCC:
            return sprintf(buf, "%d\n", flexcan->bcc);
      case FLEXCAN_ATTR_LOCAL_PRIORITY:
            return sprintf(buf, "%d\n", flexcan->lprio);
      case FLEXCAN_ATTR_ABORT:
            return sprintf(buf, "%d\n", flexcan->abort);
      case FLEXCAN_ATTR_LOOPBACK:
            return sprintf(buf, "%d\n", flexcan->loopback);
      case FLEXCAN_ATTR_SMP:
            return sprintf(buf, "%d\n", flexcan->smp);
      case FLEXCAN_ATTR_BOFF_REC:
            return sprintf(buf, "%d\n", flexcan->boff_rec);
      case FLEXCAN_ATTR_TSYN:
            return sprintf(buf, "%d\n", flexcan->tsyn);
      case FLEXCAN_ATTR_LISTEN:
            return sprintf(buf, "%d\n", flexcan->listen);
      case FLEXCAN_ATTR_EXTEND_MSG:
            return sprintf(buf, "%d\n", flexcan->ext_msg);
      case FLEXCAN_ATTR_STANDARD_MSG:
            return sprintf(buf, "%d\n", flexcan->std_msg);
#ifdef CONFIG_CAN_DEBUG_DEVICES
      case FLEXCAN_ATTR_DUMP_REG:
            return flexcan_dump_reg(flexcan, buf);
      case FLEXCAN_ATTR_DUMP_XMIT_MB:
            return flexcan_dump_xmit_mb(flexcan, buf);
      case FLEXCAN_ATTR_DUMP_RX_MB:
            return flexcan_dump_rx_mb(flexcan, buf);
#endif
      default:
            return sprintf(buf, "%s:%p->%p\n", __func__, flexcan_dev_attr,
                         attr);
      }
}

static ssize_t flexcan_set_attr(struct device *dev,
                        struct device_attribute *attr, const char *buf,
                        size_t count)
{
      int attr_id, tmp;
      struct net_device *net;
      struct flexcan_device *flexcan;

      net = dev_get_drvdata(dev);
      BUG_ON(!net);
      flexcan = netdev_priv(net);
      BUG_ON(!flexcan);

      attr_id = attr - flexcan_dev_attr;

      if (mutex_lock_interruptible(&flexcan->mutex))
            return count;

      if (netif_running(net))
            goto set_finish;

      if (attr_id == FLEXCAN_ATTR_BR_CLKSRC) {
            if (!strcasecmp(buf, "bus"))
                  flexcan->br_clksrc = 1;
            else if (!strcasecmp(buf, "osc"))
                  flexcan->br_clksrc = 0;
            goto set_finish;
      }

      tmp = simple_strtoul(buf, NULL, 0);
      switch (attr_id) {
      case FLEXCAN_ATTR_BITRATE:
            flexcan_set_bitrate(flexcan, tmp);
            break;
      case FLEXCAN_ATTR_BR_PRESDIV:
            if ((tmp > 0) && (tmp <= FLEXCAN_MAX_PRESDIV)) {
                  flexcan->br_presdiv = tmp - 1;
                  flexcan_update_bitrate(flexcan);
            }
            break;
      case FLEXCAN_ATTR_BR_RJW:
            if ((tmp > 0) && (tmp <= FLEXCAN_MAX_RJW))
                  flexcan->br_rjw = tmp - 1;
            break;
      case FLEXCAN_ATTR_BR_PROPSEG:
            if ((tmp > 0) && (tmp <= FLEXCAN_MAX_PROPSEG)) {
                  flexcan->br_propseg = tmp - 1;
                  flexcan_update_bitrate(flexcan);
            }
            break;
      case FLEXCAN_ATTR_BR_PSEG1:
            if ((tmp > 0) && (tmp <= FLEXCAN_MAX_PSEG1)) {
                  flexcan->br_pseg1 = tmp - 1;
                  flexcan_update_bitrate(flexcan);
            }
            break;
      case FLEXCAN_ATTR_BR_PSEG2:
            if ((tmp > 0) && (tmp <= FLEXCAN_MAX_PSEG2)) {
                  flexcan->br_pseg2 = tmp - 1;
                  flexcan_update_bitrate(flexcan);
            }
            break;
      case FLEXCAN_ATTR_MAXMB:
            if ((tmp > 0) && (tmp <= FLEXCAN_MAX_MB)) {
                  if (flexcan->maxmb != (tmp - 1)) {
                        flexcan->maxmb = tmp - 1;
                        if (flexcan->xmit_maxmb < flexcan->maxmb)
                              flexcan->xmit_maxmb = flexcan->maxmb;
                  }
            }
            break;
      case FLEXCAN_ATTR_XMIT_MAXMB:
            if ((tmp > 0) && (tmp <= (flexcan->maxmb + 1))) {
                  if (flexcan->xmit_maxmb != (tmp - 1))
                        flexcan->xmit_maxmb = tmp - 1;
            }
            break;
      case FLEXCAN_ATTR_FIFO:
            flexcan->fifo = tmp ? 1 : 0;
            break;
      case FLEXCAN_ATTR_WAKEUP:
            flexcan->wakeup = tmp ? 1 : 0;
            break;
      case FLEXCAN_ATTR_SRX_DIS:
            flexcan->srx_dis = tmp ? 1 : 0;
            break;
      case FLEXCAN_ATTR_WAK_SRC:
            flexcan->wak_src = tmp ? 1 : 0;
            break;
      case FLEXCAN_ATTR_BCC:
            flexcan->bcc = tmp ? 1 : 0;
            break;
      case FLEXCAN_ATTR_LOCAL_PRIORITY:
            flexcan->lprio = tmp ? 1 : 0;
            break;
      case FLEXCAN_ATTR_ABORT:
            flexcan->abort = tmp ? 1 : 0;
            break;
      case FLEXCAN_ATTR_LOOPBACK:
            flexcan->loopback = tmp ? 1 : 0;
            break;
      case FLEXCAN_ATTR_SMP:
            flexcan->smp = tmp ? 1 : 0;
            break;
      case FLEXCAN_ATTR_BOFF_REC:
            flexcan->boff_rec = tmp ? 1 : 0;
            break;
      case FLEXCAN_ATTR_TSYN:
            flexcan->tsyn = tmp ? 1 : 0;
            break;
      case FLEXCAN_ATTR_LISTEN:
            flexcan->listen = tmp ? 1 : 0;
            break;
      case FLEXCAN_ATTR_EXTEND_MSG:
            flexcan->ext_msg = tmp ? 1 : 0;
            break;
      case FLEXCAN_ATTR_STANDARD_MSG:
            flexcan->std_msg = tmp ? 1 : 0;
            break;
      }
      set_finish:
      mutex_unlock(&flexcan->mutex);
      return count;
}

static void flexcan_device_default(struct flexcan_device *dev)
{
      dev->br_clksrc = 1;
      dev->br_rjw = 2;
      dev->br_presdiv = 6;
      dev->br_propseg = 4;
      dev->br_pseg1 = 4;
      dev->br_pseg2 = 7;

      dev->bcc = 1;
      dev->srx_dis = 1;
      dev->smp = 1;
      dev->boff_rec = 1;

      dev->maxmb = FLEXCAN_MAX_MB - 1;
      dev->xmit_maxmb = (FLEXCAN_MAX_MB >> 1) - 1;
      dev->xmit_mb = dev->maxmb - dev->xmit_maxmb;

      dev->ext_msg = 1;
      dev->std_msg = 1;
}

static int flexcan_device_attach(struct flexcan_device *flexcan)
{
      int ret;
      struct resource *res;
      struct platform_device *pdev = flexcan->dev;
      struct flexcan_platform_data *plat_data = (pdev->dev).platform_data;

      res = platform_get_resource(flexcan->dev, IORESOURCE_MEM, 0);
      if (!res)
            return -ENODEV;

      flexcan->io_base = ioremap(res->start, res->end - res->start + 1);
      if (!flexcan->io_base)
            return -ENOMEM;

      flexcan->irq = platform_get_irq(flexcan->dev, 0);
      if (!flexcan->irq) {
            ret = -ENODEV;
            goto no_irq_err;
      }

      ret = -EINVAL;
      if (plat_data) {
            if (plat_data->core_reg) {
                  flexcan->core_reg = regulator_get(&pdev->dev,
                                            plat_data->core_reg);
                  if (!flexcan->core_reg)
                        goto plat_err;
            }

            if (plat_data->io_reg) {
                  flexcan->io_reg = regulator_get(&pdev->dev,
                                          plat_data->io_reg);
                  if (!flexcan->io_reg)
                        goto plat_err;
            }
      }
      flexcan->clk = clk_get(&(flexcan->dev)->dev, "can_clk");
      flexcan->hwmb = (struct can_hw_mb *)(flexcan->io_base + CAN_MB_BASE);
      flexcan->rx_mask = (unsigned int *)(flexcan->io_base + CAN_RXMASK_BASE);

      return 0;
      plat_err:
      if (flexcan->core_reg) {
            regulator_put(flexcan->core_reg);
            flexcan->core_reg = NULL;
      }
      no_irq_err:
      if (flexcan->io_base)
            iounmap(flexcan->io_base);
      return ret;
}

static void flexcan_device_detach(struct flexcan_device *flexcan)
{
      if (flexcan->clk) {
            clk_put(flexcan->clk);
            flexcan->clk = NULL;
      }

      if (flexcan->io_reg) {
            regulator_put(flexcan->io_reg);
            flexcan->io_reg = NULL;
      }

      if (flexcan->core_reg) {
            regulator_put(flexcan->core_reg);
            flexcan->core_reg = NULL;
      }

      if (flexcan->io_base)
            iounmap(flexcan->io_base);
}

/*!
 * @brief The function allocates can device.
 *
 * @param pdev    the pointer of platform device.
 * @param setup   the initial function pointer of network device.
 *
 * @return none
 */
00548 struct net_device *flexcan_device_alloc(struct platform_device *pdev,
                              void (*setup) (struct net_device *dev))
{
      struct flexcan_device *flexcan;
      struct net_device *net;
      int i, num;

      net = alloc_netdev(sizeof(*flexcan), "can%d", setup);
      if (net == NULL) {
            printk(KERN_ERR "Allocate netdevice for FlexCAN fail!\n");
            return NULL;
      }
      flexcan = netdev_priv(net);
      memset(flexcan, 0, sizeof(*flexcan));

      mutex_init(&flexcan->mutex);
      init_timer(&flexcan->timer);

      flexcan->dev = pdev;
      if (flexcan_device_attach(flexcan)) {
            printk(KERN_ERR "Attach FlexCAN fail!\n");
            free_netdev(net);
            return NULL;
      }
      flexcan_device_default(flexcan);
      flexcan_update_bitrate(flexcan);

      num = ARRAY_SIZE(flexcan_dev_attr);

      for (i = 0; i < num; i++) {
            if (device_create_file(&pdev->dev, flexcan_dev_attr + i)) {
                  printk(KERN_ERR "Create attribute file fail!\n");
                  break;
            }
      }

      if (i != num) {
            for (; i >= 0; i--)
                  device_remove_file(&pdev->dev, flexcan_dev_attr + i);
            free_netdev(net);
            return NULL;
      }
      dev_set_drvdata(&pdev->dev, net);
      return net;
}

/*!
 * @brief The function frees can device.
 *
 * @param pdev    the pointer of platform device.
 *
 * @return none
 */
00601 void flexcan_device_free(struct platform_device *pdev)
{
      struct net_device *net;
      struct flexcan_device *flexcan;
      int i, num;
      net = (struct net_device *)dev_get_drvdata(&pdev->dev);

      unregister_netdev(net);
      flexcan = netdev_priv(net);
      del_timer(&flexcan->timer);

      num = ARRAY_SIZE(flexcan_dev_attr);

      for (i = 0; i < num; i++)
            device_remove_file(&pdev->dev, flexcan_dev_attr + i);

      flexcan_device_detach(netdev_priv(net));
      free_netdev(net);
}

Generated by  Doxygen 1.6.0   Back to index