]> rtime.felk.cvut.cz Git - socketcan-devel.git/blobdiff - kernel/2.6/drivers/net/can/mscan/mpc52xx_can.c
MSCAN: further cleanup, remove volatile and whitespace
[socketcan-devel.git] / kernel / 2.6 / drivers / net / can / mscan / mpc52xx_can.c
index 99b4aab48122ae45fbf76f1abe1a1cbd30bed17e..21cef77aa6199483be9aa0fb2492a3abf75afe4c 100644 (file)
@@ -1,69 +1,74 @@
 /*
- * $Id$
+ * CAN bus driver for the Freescale MPC52xx embedded CPU.
  *
- * DESCRIPTION:
- *  CAN bus driver for the Freescale MPC52xx embedded CPU.
+ * Copyright (C) 2004-2005 Andrey Volkov <avolkov@varma-el.com>,
+ *                         Varma Electronics Oy
+ * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
  *
- * AUTHOR:
- *  Andrey Volkov <avolkov@varma-el.com>
+ * 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
  *
- * COPYRIGHT:
- *  2004-2005, Varma Electronics Oy
- *
- * 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; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  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
- *
- * HISTORY:
- *      2005-02-03 created
+ * 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/config.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <linux/netdevice.h>
-#include <linux/can/can.h>
+#include <linux/can.h>
 #include <linux/can/dev.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
+#include <linux/of_platform.h>
+#include <sysdev/fsl_soc.h>
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#include <linux/io.h>
+#else
 #include <asm/io.h>
+#endif
 #include <asm/mpc52xx.h>
 
 #include "mscan.h"
 
+#include <linux/can/version.h> /* for RCSID. Removed by mkpatch script */
+
+RCSID("$Id$");
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+
+#define PDEV_MAX 2
+
+struct platform_device *pdev[PDEV_MAX];
+
 static int __devinit mpc52xx_can_probe(struct platform_device *pdev)
 {
-       struct can_device *can;
        struct resource *mem;
-       struct net_device *ndev;
+       struct net_device *dev;
        struct mscan_platform_data *pdata = pdev->dev.platform_data;
+       struct can_priv *priv;
        u32 mem_size;
        int ret = -ENODEV;
 
-       if(!pdata)
+       if (!pdata)
                return ret;
 
-       can = alloc_mscandev();
-       if (!can)
+       dev = alloc_mscandev();
+       if (!dev)
                return -ENOMEM;
-
-       ndev = CAN2ND(can);
+       priv = netdev_priv(dev);
 
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       ndev->irq = platform_get_irq(pdev, 0);
-       if (!mem || !ndev->irq)
+       dev->irq = platform_get_irq(pdev, 0);
+       if (!mem || !dev->irq)
                goto req_error;
 
        mem_size = mem->end - mem->start + 1;
@@ -72,79 +77,438 @@ static int __devinit mpc52xx_can_probe(struct platform_device *pdev)
                goto req_error;
        }
 
-       SET_NETDEV_DEV(ndev, &pdev->dev);
-       SET_MODULE_OWNER(THIS_MODULE);
+       SET_NETDEV_DEV(dev, &pdev->dev);
 
-       ndev->base_addr = (unsigned long)ioremap_nocache(mem->start, mem_size);
+       dev->base_addr = (unsigned long)ioremap_nocache(mem->start, mem_size);
 
-       if (!ndev->base_addr) {
+       if (!dev->base_addr) {
                dev_err(&pdev->dev, "failed to map can port\n");
                ret = -ENOMEM;
                goto fail_map;
        }
 
-       can->can_sys_clock = pdata->clock_frq;
+       priv->bittiming.clock = pdata->clock_frq;
 
-       platform_set_drvdata(pdev, can);
+       platform_set_drvdata(pdev, dev);
 
-       ret = mscan_register(can, pdata->clock_src);
+       ret = register_mscandev(dev, pdata->clock_src);
        if (ret >= 0) {
-               dev_info(&pdev->dev, "probe for a port 0x%lX done\n", ndev->base_addr);
+               dev_info(&pdev->dev, "probe for port 0x%lX done (irq=%d)\n",
+                        dev->base_addr, dev->irq);
                return ret;
        }
 
-       iounmap((unsigned long *)ndev->base_addr);
+       iounmap((unsigned long *)dev->base_addr);
+
 fail_map:
        release_mem_region(mem->start, mem_size);
+
 req_error:
-       free_candev(can);
+       free_candev(dev);
        dev_err(&pdev->dev, "probe failed\n");
        return ret;
 }
 
 static int __devexit mpc52xx_can_remove(struct platform_device *pdev)
 {
-       struct can_device *can = platform_get_drvdata(pdev);
+       struct net_device *dev = platform_get_drvdata(pdev);
        struct resource *mem;
 
        platform_set_drvdata(pdev, NULL);
-       mscan_unregister(can);
+       unregister_mscandev(dev);
 
-       iounmap((volatile void __iomem *)(CAN2ND(can)->base_addr));
+       iounmap((volatile void __iomem *)dev->base_addr);
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       release_mem_region(mem->start, mem->start - mem->end + 1);
-       free_candev(can);
+       release_mem_region(mem->start, mem->end - mem->start + 1);
+       free_candev(dev);
        return 0;
 }
 
+#ifdef CONFIG_PM
+static struct mscan_regs saved_regs;
+static int mpc52xx_can_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+       struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+       _memcpy_fromio(&saved_regs, regs, sizeof(*regs));
+
+       return 0;
+}
+
+static int mpc52xx_can_resume(struct platform_device *pdev)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+       struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+       regs->canctl0 |= MSCAN_INITRQ;
+       while ((regs->canctl1 & MSCAN_INITAK) == 0)
+               udelay(10);
+
+       regs->canctl1 = saved_regs.canctl1;
+       regs->canbtr0 = saved_regs.canbtr0;
+       regs->canbtr1 = saved_regs.canbtr1;
+       regs->canidac = saved_regs.canidac;
+
+       /* restore masks, buffers etc. */
+       _memcpy_toio(&regs->canidar1_0, (void *)&saved_regs.canidar1_0,
+                    sizeof(*regs) - offsetof(struct mscan_regs, canidar1_0));
+
+       regs->canctl0 &= ~MSCAN_INITRQ;
+       regs->cantbsel = saved_regs.cantbsel;
+       regs->canrier = saved_regs.canrier;
+       regs->cantier = saved_regs.cantier;
+       regs->canctl0 = saved_regs.canctl0;
+
+       return 0;
+}
+#endif
+
 static struct platform_driver mpc52xx_can_driver = {
-       .driver         = {
-               .name     = "mpc52xx-mscan",
-       },
-       .probe    = mpc52xx_can_probe,
-       .remove   = __devexit_p(mpc52xx_can_remove)
+       .driver = {
+                  .name = "mpc52xx-mscan",
+                  },
+       .probe = mpc52xx_can_probe,
+       .remove = __devexit_p(mpc52xx_can_remove),
 #ifdef CONFIG_PM
-/*     .suspend        = mpc52xx_can_suspend,  TODO */
-/*     .resume         = mpc52xx_can_resume,   TODO */
+       .suspend = mpc52xx_can_suspend,
+       .resume = mpc52xx_can_resume,
 #endif
 };
 
+#ifdef CONFIG_PPC_MERGE
+static int __init mpc52xx_of_to_pdev(void)
+{
+       struct device_node *np = NULL;
+       unsigned int i;
+       int err = -ENODEV;
+
+       for (i = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+            (np = of_find_compatible_node(np, "mscan", "mpc5200-mscan"));
+#else
+            (np = of_find_compatible_node(np, NULL, "fsl,mpc5200-mscan"));
+#endif
+            i++) {
+               struct resource r[2] = { };
+               struct mscan_platform_data pdata;
+
+               if (i >= PDEV_MAX) {
+                       printk(KERN_WARNING "%s: increase PDEV_MAX for more "
+                              "than %i devices\n", __func__, PDEV_MAX);
+                       break;
+               }
+
+               err = of_address_to_resource(np, 0, &r[0]);
+               if (err)
+                       break;
+
+               of_irq_to_resource(np, 0, &r[1]);
+
+               pdev[i] =
+                   platform_device_register_simple("mpc52xx-mscan", i, r, 2);
+               if (IS_ERR(pdev[i])) {
+                       err = PTR_ERR(pdev[i]);
+                       break;
+               }
+
+               pdata.clock_src = MSCAN_CLKSRC_BUS;
+               pdata.clock_frq = mpc52xx_find_ipb_freq(np);
+               err = platform_device_add_data(pdev[i], &pdata, sizeof(pdata));
+               if (err)
+                       break;
+       }
+       return err;
+}
+#endif
+
 int __init mpc52xx_can_init(void)
 {
-       printk(KERN_INFO "%s initializing\n", mpc52xx_can_driver.driver.name);
+#ifdef CONFIG_PPC_MERGE
+       int err = mpc52xx_of_to_pdev();
+
+       if (err) {
+               printk(KERN_ERR "%s init failed with err=%d\n",
+                      mpc52xx_can_driver.driver.name, err);
+               return err;
+       }
+#endif
        return platform_driver_register(&mpc52xx_can_driver);
 }
 
 void __exit mpc52xx_can_exit(void)
 {
+       int i;
        platform_driver_unregister(&mpc52xx_can_driver);
+       for (i = 0; i < PDEV_MAX; i++)
+               platform_device_unregister(pdev[i]);
        printk(KERN_INFO "%s unloaded\n", mpc52xx_can_driver.driver.name);
 }
 
-
 module_init(mpc52xx_can_init);
 module_exit(mpc52xx_can_exit);
 
 MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) */
+#define DRV_NAME "mpc52xx_can"
+
+static struct of_device_id mpc52xx_cdm_ids[] __devinitdata = {
+       { .compatible = "fsl,mpc5200-cdm", },
+       { .compatible = "fsl,mpc5200b-cdm", },
+       {}
+};
+
+/*
+ * Get the frequency of the external oscillator clock connected
+ * to the SYS_XTAL_IN pin, or retrun 0 if it cannot be determined.
+ */
+static unsigned int  __devinit mpc52xx_can_xtal_freq(struct device_node *np)
+{
+       struct mpc52xx_cdm  __iomem *cdm;
+       struct device_node *np_cdm;
+       unsigned int freq;
+       u32 val;
+
+       freq = mpc52xx_find_ipb_freq(np);
+       if (!freq)
+               return 0;
+
+       /*
+        * Detemine SYS_XTAL_IN frequency from the clock domain settings
+        */
+       np_cdm = of_find_matching_node(NULL, mpc52xx_cdm_ids);
+       cdm = of_iomap(np_cdm, 0);
+       of_node_put(np_cdm);
+       if (!np_cdm) {
+               printk(KERN_ERR "%s() failed abnormally\n", __func__);
+               return 0;
+       }
+
+       if (in_8(&cdm->ipb_clk_sel) & 0x1)
+               freq *= 2;
+       val  = in_be32(&cdm->rstcfg);
+       if (val & (1 << 5))
+               freq *= 8;
+       else
+               freq *= 4;
+       if (val & (1 << 6))
+               freq /= 12;
+       else
+               freq /= 16;
+
+       iounmap(cdm);
+
+       return freq;
+}
+
+/*
+ * Get frequency of the MSCAN clock source
+ *
+ * Either the oscillator clock (SYS_XTAL_IN) or the IP bus clock (IP_CLK)
+ * can be selected. According to the MPC5200 user's manual, the oscillator
+ * clock is the better choice as it has less jitter but due to a hardware
+ * bug, it can not be selected for the old MPC5200 Rev. A chips.
+ */
+
+static unsigned int  __devinit mpc52xx_can_clock_freq(struct device_node *np,
+                                                     int clock_src)
+{
+       unsigned int pvr;
+
+       pvr = mfspr(SPRN_PVR);
+
+       if (clock_src == MSCAN_CLKSRC_BUS || pvr == 0x80822011)
+               return mpc52xx_find_ipb_freq(np);
+
+       return mpc52xx_can_xtal_freq(np);
+}
+
+static int __devinit mpc52xx_can_probe(struct of_device *ofdev,
+                                      const struct of_device_id *id)
+{
+       struct device_node *np = ofdev->node;
+       struct net_device *dev;
+       struct can_priv *priv;
+       struct resource res;
+       void __iomem *base;
+       int err, irq, res_size, clock_src;
+
+       err = of_address_to_resource(np, 0, &res);
+       if (err) {
+               dev_err(&ofdev->dev, "invalid address\n");
+               return err;
+       }
+
+       res_size = res.end - res.start + 1;
+
+       if (!request_mem_region(res.start, res_size, DRV_NAME)) {
+               dev_err(&ofdev->dev, "couldn't request %#x..%#x\n",
+                       res.start, res.end);
+               return -EBUSY;
+       }
+
+       base = ioremap_nocache(res.start, res_size);
+       if (!base) {
+               dev_err(&ofdev->dev, "couldn't ioremap %#x..%#x\n",
+                       res.start, res.end);
+               err = -ENOMEM;
+               goto exit_release_mem;
+       }
+
+       irq = irq_of_parse_and_map(np, 0);
+       if (irq == NO_IRQ) {
+               dev_err(&ofdev->dev, "no irq found\n");
+               err = -ENODEV;
+               goto exit_unmap_mem;
+       }
+
+       dev = alloc_mscandev();
+       if (!dev) {
+               err = -ENOMEM;
+               goto exit_dispose_irq;
+       }
+
+       dev->base_addr = (unsigned long)base;
+       dev->irq = irq;
+
+       priv = netdev_priv(dev);
+
+       /*
+        * Either the oscillator clock (SYS_XTAL_IN) or the IP bus clock
+        * (IP_CLK) can be selected as MSCAN clock source. According to
+        * the MPC5200 user's manual, the oscillator clock is the better
+        * choice as it has less jitter. For this reason, it is selected
+        * by default.
+        */
+       if (of_get_property(np, "clock-ipb", NULL))
+               clock_src = MSCAN_CLKSRC_BUS;
+       else
+               clock_src = MSCAN_CLKSRC_XTAL;
+       priv->bittiming.clock = mpc52xx_can_clock_freq(np, clock_src);
+       if (!priv->bittiming.clock) {
+               dev_err(&ofdev->dev, "couldn't get MSCAN clock frequency\n");
+               err = -ENODEV;
+               goto exit_free_mscan;
+       }
+
+       SET_NETDEV_DEV(dev, &ofdev->dev);
+
+       err = register_mscandev(dev, clock_src);
+       if (err) {
+               dev_err(&ofdev->dev, "registering %s failed (err=%d)\n",
+                       DRV_NAME, err);
+               goto exit_free_mscan;
+       }
+
+       dev_set_drvdata(&ofdev->dev, dev);
+
+       dev_info(&ofdev->dev, "MSCAN at 0x%lx, irq %d, clock %dHZ\n",
+                dev->base_addr, dev->irq, priv->bittiming.clock);
+
+       return 0;
+
+exit_free_mscan:
+       free_candev(dev);
+exit_dispose_irq:
+       irq_dispose_mapping(irq);
+exit_unmap_mem:
+       iounmap(base);
+exit_release_mem:
+       release_mem_region(res.start, res_size);
+
+       return err;
+}
+
+static int __devexit mpc52xx_can_remove(struct of_device *ofdev)
+{
+       struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+       struct device_node *np = ofdev->node;
+       struct resource res;
+
+       dev_set_drvdata(&ofdev->dev, NULL);
+
+       unregister_mscandev(dev);
+       iounmap((void __iomem *)dev->base_addr);
+       irq_dispose_mapping(dev->irq);
+       free_candev(dev);
+
+       of_address_to_resource(np, 0, &res);
+       release_mem_region(res.start, res.end - res.start + 1);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static struct mscan_regs saved_regs;
+static int mpc52xx_can_suspend(struct of_device *ofdev, pm_message_t state)
+{
+       struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+       struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+       _memcpy_fromio(&saved_regs, regs, sizeof(*regs));
+
+       return 0;
+}
+
+static int mpc52xx_can_resume(struct of_device *ofdev)
+{
+       struct net_device *dev = dev_get_drvdata(&ofdev->dev);
+       struct mscan_regs *regs = (struct mscan_regs *)dev->base_addr;
+
+       regs->canctl0 |= MSCAN_INITRQ;
+       while ((regs->canctl1 & MSCAN_INITAK) == 0)
+               udelay(10);
+
+       regs->canctl1 = saved_regs.canctl1;
+       regs->canbtr0 = saved_regs.canbtr0;
+       regs->canbtr1 = saved_regs.canbtr1;
+       regs->canidac = saved_regs.canidac;
+
+       /* restore masks, buffers etc. */
+       _memcpy_toio(&regs->canidar1_0, (void *)&saved_regs.canidar1_0,
+                    sizeof(*regs) - offsetof(struct mscan_regs, canidar1_0));
+
+       regs->canctl0 &= ~MSCAN_INITRQ;
+       regs->cantbsel = saved_regs.cantbsel;
+       regs->canrier = saved_regs.canrier;
+       regs->cantier = saved_regs.cantier;
+       regs->canctl0 = saved_regs.canctl0;
+
+       return 0;
+}
+#endif
+
+static struct of_device_id __devinitdata mpc52xx_can_table[] = {
+       {.compatible = "fsl,mpc5200-mscan"},
+       {.compatible = "fsl,mpc5200b-mscan"},
+       {},
+};
+
+static struct of_platform_driver mpc52xx_can_driver = {
+       .owner = THIS_MODULE,
+       .name = "mpc52xx_can",
+       .probe = mpc52xx_can_probe,
+       .remove = __devexit_p(mpc52xx_can_remove),
+#ifdef CONFIG_PM
+       .suspend = mpc52xx_can_suspend,
+       .resume = mpc52xx_can_resume,
+#endif
+       .match_table = mpc52xx_can_table,
+};
+
+static int __init mpc52xx_can_init(void)
+{
+       return of_register_platform_driver(&mpc52xx_can_driver);
+}
+module_init(mpc52xx_can_init);
+
+static void __exit mpc52xx_can_exit(void)
+{
+       return of_unregister_platform_driver(&mpc52xx_can_driver);
+};
+module_exit(mpc52xx_can_exit);
+
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) */
 MODULE_DESCRIPTION("Freescale MPC5200 CAN driver");
-MODULE_LICENSE("GPLv2");
+MODULE_LICENSE("GPL v2");