diff options
author | Wolfgang Grandegger <wg@grandegger.com> | 2009-05-15 23:39:31 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-05-18 15:41:42 -0700 |
commit | f534e52f091a7b9f51fb6726710bdf731b663e94 (patch) | |
tree | f1faad4e595abf44eae5a4942401f1fac14f1494 | |
parent | 429da1cc841bc9f2e762fd7272fc2b80314b890a (diff) |
can: SJA1000 generic platform bus driver
This driver adds support for the SJA1000 chips connected to the
"platform bus", which can be found on various embedded systems.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Wolfgang Grandegger <wg@grandegger.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/can/Kconfig | 10 | ||||
-rw-r--r-- | drivers/net/can/sja1000/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/can/sja1000/sja1000_platform.c | 164 | ||||
-rw-r--r-- | include/linux/can/platform/sja1000.h | 32 |
4 files changed, 207 insertions, 0 deletions
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index c7e5ce339ea..8fe79741d3c 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -41,6 +41,16 @@ config CAN_SJA1000 ---help--- Driver for the SJA1000 CAN controllers from Philips or NXP +config CAN_SJA1000_PLATFORM + depends on CAN_SJA1000 + tristate "Generic Platform Bus based SJA1000 driver" + ---help--- + This driver adds support for the SJA1000 chips connected to + the "platform bus" (Linux abstraction for directly to the + processor attached devices). Which can be found on various + boards from Phytec (http://www.phytec.de) like the PCM027, + PCM038. + config CAN_DEBUG_DEVICES bool "CAN devices debugging messages" depends on CAN diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile index 893ab2cfe24..5115cc90ab5 100644 --- a/drivers/net/can/sja1000/Makefile +++ b/drivers/net/can/sja1000/Makefile @@ -3,5 +3,6 @@ # obj-$(CONFIG_CAN_SJA1000) += sja1000.o +obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/sja1000/sja1000_platform.c b/drivers/net/can/sja1000/sja1000_platform.c new file mode 100644 index 00000000000..8017229d6fd --- /dev/null +++ b/drivers/net/can/sja1000/sja1000_platform.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2005 Sascha Hauer, Pengutronix + * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.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 + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/irq.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/platform/sja1000.h> +#include <linux/io.h> + +#include "sja1000.h" + +#define DRV_NAME "sja1000_platform" + +MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); +MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the platform bus"); +MODULE_LICENSE("GPL v2"); + +static u8 sp_read_reg(const struct net_device *dev, int reg) +{ + return ioread8((void __iomem *)(dev->base_addr + reg)); +} + +static void sp_write_reg(const struct net_device *dev, int reg, u8 val) +{ + iowrite8(val, (void __iomem *)(dev->base_addr + reg)); +} + +static int sp_probe(struct platform_device *pdev) +{ + int err; + void __iomem *addr; + struct net_device *dev; + struct sja1000_priv *priv; + struct resource *res_mem, *res_irq; + struct sja1000_platform_data *pdata; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data provided!\n"); + err = -ENODEV; + goto exit; + } + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res_mem || !res_irq) { + err = -ENODEV; + goto exit; + } + + if (!request_mem_region(res_mem->start, resource_size(res_mem), + DRV_NAME)) { + err = -EBUSY; + goto exit; + } + + addr = ioremap_nocache(res_mem->start, resource_size(res_mem)); + if (!addr) { + err = -ENOMEM; + goto exit_release; + } + + dev = alloc_sja1000dev(0); + if (!dev) { + err = -ENOMEM; + goto exit_iounmap; + } + priv = netdev_priv(dev); + + dev->base_addr = (unsigned long)addr; + dev->irq = res_irq->start; + priv->irq_flags = res_irq->flags & IRQF_TRIGGER_MASK; + priv->read_reg = sp_read_reg; + priv->write_reg = sp_write_reg; + priv->can.clock.freq = pdata->clock; + priv->ocr = pdata->ocr; + priv->cdr = pdata->cdr; + + dev_set_drvdata(&pdev->dev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + err = register_sja1000dev(dev); + if (err) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto exit_free; + } + + dev_info(&pdev->dev, "%s device registered (base_addr=%#lx, irq=%d)\n", + DRV_NAME, dev->base_addr, dev->irq); + return 0; + + exit_free: + free_sja1000dev(dev); + exit_iounmap: + iounmap(addr); + exit_release: + release_mem_region(res_mem->start, resource_size(res_mem)); + exit: + return err; +} + +static int sp_remove(struct platform_device *pdev) +{ + struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct resource *res; + + unregister_sja1000dev(dev); + dev_set_drvdata(&pdev->dev, NULL); + + if (dev->base_addr) + iounmap((void __iomem *)dev->base_addr); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + free_sja1000dev(dev); + + return 0; +} + +static struct platform_driver sp_driver = { + .probe = sp_probe, + .remove = sp_remove, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init sp_init(void) +{ + return platform_driver_register(&sp_driver); +} + +static void __exit sp_exit(void) +{ + platform_driver_unregister(&sp_driver); +} + +module_init(sp_init); +module_exit(sp_exit); diff --git a/include/linux/can/platform/sja1000.h b/include/linux/can/platform/sja1000.h new file mode 100644 index 00000000000..37966e630ff --- /dev/null +++ b/include/linux/can/platform/sja1000.h @@ -0,0 +1,32 @@ +#ifndef _CAN_PLATFORM_SJA1000_H_ +#define _CAN_PLATFORM_SJA1000_H_ + +/* clock divider register */ +#define CDR_CLKOUT_MASK 0x07 +#define CDR_CLK_OFF 0x08 /* Clock off (CLKOUT pin) */ +#define CDR_RXINPEN 0x20 /* TX1 output is RX irq output */ +#define CDR_CBP 0x40 /* CAN input comparator bypass */ +#define CDR_PELICAN 0x80 /* PeliCAN mode */ + +/* output control register */ +#define OCR_MODE_BIPHASE 0x00 +#define OCR_MODE_TEST 0x01 +#define OCR_MODE_NORMAL 0x02 +#define OCR_MODE_CLOCK 0x03 +#define OCR_TX0_INVERT 0x04 +#define OCR_TX0_PULLDOWN 0x08 +#define OCR_TX0_PULLUP 0x10 +#define OCR_TX0_PUSHPULL 0x18 +#define OCR_TX1_INVERT 0x20 +#define OCR_TX1_PULLDOWN 0x40 +#define OCR_TX1_PULLUP 0x80 +#define OCR_TX1_PUSHPULL 0xc0 + +struct sja1000_platform_data { + u32 clock; /* CAN bus oscillator frequency in Hz */ + + u8 ocr; /* output control register */ + u8 cdr; /* clock divider register */ +}; + +#endif /* !_CAN_PLATFORM_SJA1000_H_ */ |