summaryrefslogtreecommitdiff
path: root/drivers/net/can/old/ccan/h7202_can.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/can/old/ccan/h7202_can.c')
-rw-r--r--drivers/net/can/old/ccan/h7202_can.c199
1 files changed, 199 insertions, 0 deletions
diff --git a/drivers/net/can/old/ccan/h7202_can.c b/drivers/net/can/old/ccan/h7202_can.c
new file mode 100644
index 000000000000..7dcc3e749248
--- /dev/null
+++ b/drivers/net/can/old/ccan/h7202_can.c
@@ -0,0 +1,199 @@
+/*
+ * drivers/can/h7202_can.c
+ *
+ * Copyright (C) 2007
+ *
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ *
+ * 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/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/io.h>
+#include <asm/hardware.h>
+
+#include "ccan.h"
+
+#define DRV_NAME "h7202can"
+#define DELAY 5
+#define CAN_ENABLE 0x0e
+
+static u16 h7202can_read_reg(struct net_device *dev, enum c_regs reg)
+{
+ u16 val;
+ volatile int i;
+
+ /* The big kernel lock is used to prevent any other AMBA devices from
+ * interfering with the current register read operation. The register
+ * is read twice because of braindamaged hynix cpu.
+ */
+ lock_kernel();
+ val = inw(dev->base_addr + (reg<<1));
+ for (i = 0; i < DELAY; i++);
+ val = inw(dev->base_addr + (reg<<1));
+ for (i = 0; i < DELAY; i++);
+ unlock_kernel();
+
+ return val;
+}
+
+static void h7202can_write_reg(struct net_device *dev, enum c_regs reg, u16 val)
+{
+ volatile int i;
+
+ lock_kernel();
+ outw(val, dev->base_addr + (reg<<1));
+ for (i = 0; i < DELAY; i++);
+ unlock_kernel();
+}
+
+static int h7202can_drv_probe(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct ccan_priv *priv;
+ struct resource *mem;
+ u32 mem_size;
+ int ret = -ENODEV;
+
+ dev = alloc_ccandev(sizeof(struct ccan_priv));
+ if (!dev)
+ return -ENOMEM;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dev->irq = platform_get_irq(pdev, 0);
+ if (!mem || !dev->irq)
+ goto req_error;
+
+ mem_size = mem->end - mem->start + 1;
+ if (!request_mem_region(mem->start, mem_size, pdev->dev.driver->name)) {
+ dev_err(&pdev->dev, "resource unavailable\n");
+ goto req_error;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ dev->base_addr = (unsigned long)ioremap_nocache(mem->start, mem_size);
+
+ if (!dev->base_addr) {
+ dev_err(&pdev->dev, "failed to map can port\n");
+ ret = -ENOMEM;
+ goto fail_map;
+ }
+
+ priv = netdev_priv(dev);
+ priv->can.can_sys_clock = 8000000;
+ priv->read_reg = h7202can_read_reg;
+ priv->write_reg = h7202can_write_reg;
+
+ platform_set_drvdata(pdev, dev);
+
+ /* configure ports */
+ switch (mem->start) {
+ case CAN0_PHYS:
+ CPU_REG(GPIO_C_VIRT, GPIO_EN) &= ~(3<<1);
+ CPU_REG(GPIO_C_VIRT, GPIO_DIR) &= ~(1<<1);
+ CPU_REG(GPIO_C_VIRT, GPIO_DIR) |= (1<<2);
+ break;
+ case CAN1_PHYS:
+ CPU_REG(GPIO_E_VIRT, GPIO_EN) &= ~(3<<16);
+ CPU_REG(GPIO_E_VIRT, GPIO_DIR) |= (1<<16);
+ CPU_REG(GPIO_E_VIRT, GPIO_DIR) &= ~(1<<17);
+ break;
+ }
+
+ /* enable can */
+ h7202can_write_reg(dev, CAN_ENABLE, 1);
+
+ ret = register_ccandev(dev);
+ if (ret >= 0) {
+ dev_info(&pdev->dev, "probe for a port 0x%lX done\n",
+ dev->base_addr);
+ return ret;
+ }
+
+ iounmap((unsigned long *)dev->base_addr);
+fail_map:
+ release_mem_region(mem->start, mem_size);
+req_error:
+ free_ccandev(dev);
+ dev_err(&pdev->dev, "probe failed\n");
+ return ret;
+}
+
+static int h7202can_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ platform_set_drvdata(pdev, NULL);
+ unregister_ccandev(dev);
+
+ iounmap((volatile void __iomem *)(dev->base_addr));
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, mem->end - mem->start + 1);
+ free_ccandev(dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int h7202can_drv_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return 0;
+}
+
+static int h7202can_drv_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static struct platform_driver h7202can_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = h7202can_drv_probe,
+ .remove = h7202can_drv_remove,
+#ifdef CONFIG_PM
+ .suspend = h7202can_drv_suspend,
+ .resume = h7202can_drv_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init h7202can_init(void)
+{
+ printk(KERN_INFO "%s initializing\n", h7202can_driver.driver.name);
+ return platform_driver_register(&h7202can_driver);
+}
+
+static void __exit h7202can_cleanup(void)
+{
+ platform_driver_unregister(&h7202can_driver);
+ printk(KERN_INFO "%s unloaded\n", h7202can_driver.driver.name);
+}
+
+module_init(h7202can_init);
+module_exit(h7202can_cleanup);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_AUTHOR("Simon Kallweit <simon.kallweit@intefo.ch>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CAN port driver Hynix H7202 processor");