diff options
Diffstat (limited to 'drivers/net/can/sja1000/pipcan.c')
-rw-r--r-- | drivers/net/can/sja1000/pipcan.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/drivers/net/can/sja1000/pipcan.c b/drivers/net/can/sja1000/pipcan.c new file mode 100644 index 000000000000..b7414b218697 --- /dev/null +++ b/drivers/net/can/sja1000/pipcan.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2008 David Müller, <d.mueller@elsoft.ch> + * + * 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/platform_device.h> +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/io.h> + +#include "sja1000.h" + +#define DRV_NAME "pipcan" + +MODULE_AUTHOR("David Müller <d.mueller@elsoft.ch>"); +MODULE_DESCRIPTION("Socket-CAN driver for MPL PIPCAN module"); +MODULE_SUPPORTED_DEVICE("MPL PIPCAN module"); +MODULE_LICENSE("GPL v2"); + +#define PIPCAN_CAN_CLOCK (16000000 / 2) + +#define PIPCAN_OCR (OCR_TX1_PUSHPULL) +#define PIPCAN_CDR (CDR_CBP | CDR_CLK_OFF) + +#define PIPCAN_IOSIZE (0x100) + +#define PIPCAN_RES (0x804) +#define PIPCAN_RST (0x805) + +static u8 pc_read_reg(struct net_device *dev, int reg) +{ + return inb(dev->base_addr + reg); +} + +static void pc_write_reg(struct net_device *dev, int reg, u8 val) +{ + outb(val, dev->base_addr + reg); +} + +static int __init pc_probe(struct platform_device *pdev) +{ + struct net_device *dev; + struct sja1000_priv *priv; + struct resource *res; + int rc, irq; + + rc = -ENODEV; + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + irq = platform_get_irq(pdev, 0); + if (!res || !irq) + goto exit; + + rc = -EBUSY; + if (!request_region(res->start, res->end - res->start + 1, DRV_NAME)) + goto exit; + + rc = -ENOMEM; + dev = alloc_sja1000dev(0); + if (!dev) + goto exit_release; + + priv = netdev_priv(dev); + + priv->read_reg = pc_read_reg; + priv->write_reg = pc_write_reg; + priv->can.bittiming.clock = PIPCAN_CAN_CLOCK; + priv->ocr = PIPCAN_OCR; + priv->cdr = PIPCAN_CDR; + + dev->irq = irq; + dev->base_addr = res->start; + + dev_set_drvdata(&pdev->dev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + /* deactivate RST */ + outb(inb(PIPCAN_RST) & ~0x01, PIPCAN_RST); + + rc = register_sja1000dev(dev); + if (rc) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, rc); + goto exit_free; + } + + dev_info(&pdev->dev, "device registered (base_addr=%#lx, irq=%d)\n", + dev->base_addr, dev->irq); + return 0; + +exit_free: + free_sja1000dev(dev); + +exit_release: + release_region(res->start, res->end - res->start + 1); + +exit: + return rc; +} + +static int __exit pc_remove(struct platform_device *pdev) +{ + struct net_device *dev = dev_get_drvdata(&pdev->dev); + struct resource *res; + + dev_set_drvdata(&pdev->dev, NULL); + unregister_sja1000dev(dev); + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + + free_sja1000dev(dev); + + release_region(res->start, res->end - res->start + 1); + + /* activate RST */ + outb(inb(PIPCAN_RST) | 0x01, PIPCAN_RST); + + return 0; +} + +static struct platform_driver pc_driver = { + .remove = __exit_p(pc_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static struct platform_device *pc_pdev; +static const u16 pipcan_ioport[] = {0x1000, 0x8000, 0xE000}; + +static int __init pc_init(void) +{ + struct resource r[2]; + int rc, addr, irq, idx; + u8 pc_res; + + /* get PIPCAN resources from EPLD */ + pc_res = inb(PIPCAN_RES); + + idx = (pc_res & 0x0F); + if ((idx <= 0) || (idx > ARRAY_SIZE(pipcan_ioport))) { + printk(KERN_ERR DRV_NAME " invalid base address\n"); + return -EINVAL; + } + addr = pipcan_ioport[idx-1]; + + irq = (pc_res >> 4) & 0x0F; + if ((irq < 3) || (irq == 8) || (irq == 13)) { + printk(KERN_ERR DRV_NAME " invalid IRQ\n"); + return -EINVAL; + } + + /* fill in resources */ + memset(&r, 0, sizeof(r)); + r[0].start = addr; + r[0].end = addr + PIPCAN_IOSIZE - 1; + r[0].name = DRV_NAME; + r[0].flags = IORESOURCE_IO; + r[1].start = r[1].end = irq; + r[1].name = DRV_NAME; + r[1].flags = IORESOURCE_IRQ; + + pc_pdev = platform_device_register_simple(DRV_NAME, 0, r, + ARRAY_SIZE(r)); + if (IS_ERR(pc_pdev)) + return PTR_ERR(pc_pdev); + + rc = platform_driver_probe(&pc_driver, pc_probe); + if (rc) { + platform_device_unregister(pc_pdev); + printk(KERN_ERR DRV_NAME + " platform_driver_probe() failed (%d)\n", rc); + } + + return rc; +} + +static void __exit pc_exit(void) +{ + platform_driver_unregister(&pc_driver); + platform_device_unregister(pc_pdev); +} + +module_init(pc_init); +module_exit(pc_exit); |