12a6ba39aSWolfgang Grandegger /* 22a6ba39aSWolfgang Grandegger * Copyright (C) 2009 Wolfgang Grandegger <wg@grandegger.com> 32a6ba39aSWolfgang Grandegger * 42a6ba39aSWolfgang Grandegger * This program is free software; you can redistribute it and/or modify 52a6ba39aSWolfgang Grandegger * it under the terms of the version 2 of the GNU General Public License 62a6ba39aSWolfgang Grandegger * as published by the Free Software Foundation 72a6ba39aSWolfgang Grandegger * 82a6ba39aSWolfgang Grandegger * This program is distributed in the hope that it will be useful, 92a6ba39aSWolfgang Grandegger * but WITHOUT ANY WARRANTY; without even the implied warranty of 102a6ba39aSWolfgang Grandegger * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 112a6ba39aSWolfgang Grandegger * GNU General Public License for more details. 122a6ba39aSWolfgang Grandegger * 132a6ba39aSWolfgang Grandegger * You should have received a copy of the GNU General Public License 142a6ba39aSWolfgang Grandegger * along with this program; if not, write to the Free Software 152a6ba39aSWolfgang Grandegger * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 162a6ba39aSWolfgang Grandegger */ 172a6ba39aSWolfgang Grandegger 182a6ba39aSWolfgang Grandegger #include <linux/kernel.h> 192a6ba39aSWolfgang Grandegger #include <linux/module.h> 201839a6c6SWolfgang Grandegger #include <linux/platform_device.h> 212a6ba39aSWolfgang Grandegger #include <linux/interrupt.h> 222a6ba39aSWolfgang Grandegger #include <linux/netdevice.h> 232a6ba39aSWolfgang Grandegger #include <linux/delay.h> 242a6ba39aSWolfgang Grandegger #include <linux/irq.h> 252a6ba39aSWolfgang Grandegger #include <linux/io.h> 262a6ba39aSWolfgang Grandegger #include <linux/can/dev.h> 272a6ba39aSWolfgang Grandegger #include <linux/can/platform/sja1000.h> 282a6ba39aSWolfgang Grandegger 292a6ba39aSWolfgang Grandegger #include "sja1000.h" 302a6ba39aSWolfgang Grandegger 312a6ba39aSWolfgang Grandegger #define DRV_NAME "sja1000_isa" 322a6ba39aSWolfgang Grandegger 332a6ba39aSWolfgang Grandegger #define MAXDEV 8 342a6ba39aSWolfgang Grandegger 352a6ba39aSWolfgang Grandegger MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); 362a6ba39aSWolfgang Grandegger MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the ISA bus"); 372a6ba39aSWolfgang Grandegger MODULE_LICENSE("GPL v2"); 382a6ba39aSWolfgang Grandegger 392a6ba39aSWolfgang Grandegger #define CLK_DEFAULT 16000000 /* 16 MHz */ 402a6ba39aSWolfgang Grandegger #define CDR_DEFAULT (CDR_CBP | CDR_CLK_OFF) 412a6ba39aSWolfgang Grandegger #define OCR_DEFAULT OCR_TX0_PUSHPULL 422a6ba39aSWolfgang Grandegger 432a6ba39aSWolfgang Grandegger static unsigned long port[MAXDEV]; 442a6ba39aSWolfgang Grandegger static unsigned long mem[MAXDEV]; 45*3c8ac0f2SBill Pemberton static int irq[MAXDEV]; 46*3c8ac0f2SBill Pemberton static int clk[MAXDEV]; 47*3c8ac0f2SBill Pemberton static unsigned char cdr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; 48*3c8ac0f2SBill Pemberton static unsigned char ocr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff}; 49*3c8ac0f2SBill Pemberton static int indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1}; 502a6ba39aSWolfgang Grandegger 512a6ba39aSWolfgang Grandegger module_param_array(port, ulong, NULL, S_IRUGO); 522a6ba39aSWolfgang Grandegger MODULE_PARM_DESC(port, "I/O port number"); 532a6ba39aSWolfgang Grandegger 542a6ba39aSWolfgang Grandegger module_param_array(mem, ulong, NULL, S_IRUGO); 552a6ba39aSWolfgang Grandegger MODULE_PARM_DESC(mem, "I/O memory address"); 562a6ba39aSWolfgang Grandegger 57115d2a3dSWolfgang Grandegger module_param_array(indirect, int, NULL, S_IRUGO); 582a6ba39aSWolfgang Grandegger MODULE_PARM_DESC(indirect, "Indirect access via address and data port"); 592a6ba39aSWolfgang Grandegger 602a6ba39aSWolfgang Grandegger module_param_array(irq, int, NULL, S_IRUGO); 612a6ba39aSWolfgang Grandegger MODULE_PARM_DESC(irq, "IRQ number"); 622a6ba39aSWolfgang Grandegger 632a6ba39aSWolfgang Grandegger module_param_array(clk, int, NULL, S_IRUGO); 642a6ba39aSWolfgang Grandegger MODULE_PARM_DESC(clk, "External oscillator clock frequency " 652a6ba39aSWolfgang Grandegger "(default=16000000 [16 MHz])"); 662a6ba39aSWolfgang Grandegger 672a6ba39aSWolfgang Grandegger module_param_array(cdr, byte, NULL, S_IRUGO); 682a6ba39aSWolfgang Grandegger MODULE_PARM_DESC(cdr, "Clock divider register " 692a6ba39aSWolfgang Grandegger "(default=0x48 [CDR_CBP | CDR_CLK_OFF])"); 702a6ba39aSWolfgang Grandegger 712a6ba39aSWolfgang Grandegger module_param_array(ocr, byte, NULL, S_IRUGO); 722a6ba39aSWolfgang Grandegger MODULE_PARM_DESC(ocr, "Output control register " 732a6ba39aSWolfgang Grandegger "(default=0x18 [OCR_TX0_PUSHPULL])"); 742a6ba39aSWolfgang Grandegger 752a6ba39aSWolfgang Grandegger #define SJA1000_IOSIZE 0x20 762a6ba39aSWolfgang Grandegger #define SJA1000_IOSIZE_INDIRECT 0x02 772a6ba39aSWolfgang Grandegger 781839a6c6SWolfgang Grandegger static struct platform_device *sja1000_isa_devs[MAXDEV]; 791839a6c6SWolfgang Grandegger 802a6ba39aSWolfgang Grandegger static u8 sja1000_isa_mem_read_reg(const struct sja1000_priv *priv, int reg) 812a6ba39aSWolfgang Grandegger { 822a6ba39aSWolfgang Grandegger return readb(priv->reg_base + reg); 832a6ba39aSWolfgang Grandegger } 842a6ba39aSWolfgang Grandegger 852a6ba39aSWolfgang Grandegger static void sja1000_isa_mem_write_reg(const struct sja1000_priv *priv, 862a6ba39aSWolfgang Grandegger int reg, u8 val) 872a6ba39aSWolfgang Grandegger { 882a6ba39aSWolfgang Grandegger writeb(val, priv->reg_base + reg); 892a6ba39aSWolfgang Grandegger } 902a6ba39aSWolfgang Grandegger 912a6ba39aSWolfgang Grandegger static u8 sja1000_isa_port_read_reg(const struct sja1000_priv *priv, int reg) 922a6ba39aSWolfgang Grandegger { 932a6ba39aSWolfgang Grandegger return inb((unsigned long)priv->reg_base + reg); 942a6ba39aSWolfgang Grandegger } 952a6ba39aSWolfgang Grandegger 962a6ba39aSWolfgang Grandegger static void sja1000_isa_port_write_reg(const struct sja1000_priv *priv, 972a6ba39aSWolfgang Grandegger int reg, u8 val) 982a6ba39aSWolfgang Grandegger { 992a6ba39aSWolfgang Grandegger outb(val, (unsigned long)priv->reg_base + reg); 1002a6ba39aSWolfgang Grandegger } 1012a6ba39aSWolfgang Grandegger 1022a6ba39aSWolfgang Grandegger static u8 sja1000_isa_port_read_reg_indirect(const struct sja1000_priv *priv, 1032a6ba39aSWolfgang Grandegger int reg) 1042a6ba39aSWolfgang Grandegger { 1052a6ba39aSWolfgang Grandegger unsigned long base = (unsigned long)priv->reg_base; 1062a6ba39aSWolfgang Grandegger 1072a6ba39aSWolfgang Grandegger outb(reg, base); 1082a6ba39aSWolfgang Grandegger return inb(base + 1); 1092a6ba39aSWolfgang Grandegger } 1102a6ba39aSWolfgang Grandegger 1112a6ba39aSWolfgang Grandegger static void sja1000_isa_port_write_reg_indirect(const struct sja1000_priv *priv, 1122a6ba39aSWolfgang Grandegger int reg, u8 val) 1132a6ba39aSWolfgang Grandegger { 1142a6ba39aSWolfgang Grandegger unsigned long base = (unsigned long)priv->reg_base; 1152a6ba39aSWolfgang Grandegger 1162a6ba39aSWolfgang Grandegger outb(reg, base); 1172a6ba39aSWolfgang Grandegger outb(val, base + 1); 1182a6ba39aSWolfgang Grandegger } 1192a6ba39aSWolfgang Grandegger 120*3c8ac0f2SBill Pemberton static int sja1000_isa_probe(struct platform_device *pdev) 1212a6ba39aSWolfgang Grandegger { 1222a6ba39aSWolfgang Grandegger struct net_device *dev; 1232a6ba39aSWolfgang Grandegger struct sja1000_priv *priv; 1242a6ba39aSWolfgang Grandegger void __iomem *base = NULL; 1252a6ba39aSWolfgang Grandegger int iosize = SJA1000_IOSIZE; 1261839a6c6SWolfgang Grandegger int idx = pdev->id; 1272a6ba39aSWolfgang Grandegger int err; 1282a6ba39aSWolfgang Grandegger 1291839a6c6SWolfgang Grandegger dev_dbg(&pdev->dev, "probing idx=%d: port=%#lx, mem=%#lx, irq=%d\n", 1301839a6c6SWolfgang Grandegger idx, port[idx], mem[idx], irq[idx]); 1311839a6c6SWolfgang Grandegger 1322a6ba39aSWolfgang Grandegger if (mem[idx]) { 1332a6ba39aSWolfgang Grandegger if (!request_mem_region(mem[idx], iosize, DRV_NAME)) { 1342a6ba39aSWolfgang Grandegger err = -EBUSY; 1352a6ba39aSWolfgang Grandegger goto exit; 1362a6ba39aSWolfgang Grandegger } 1372a6ba39aSWolfgang Grandegger base = ioremap_nocache(mem[idx], iosize); 1382a6ba39aSWolfgang Grandegger if (!base) { 1392a6ba39aSWolfgang Grandegger err = -ENOMEM; 1402a6ba39aSWolfgang Grandegger goto exit_release; 1412a6ba39aSWolfgang Grandegger } 1422a6ba39aSWolfgang Grandegger } else { 1432a6ba39aSWolfgang Grandegger if (indirect[idx] > 0 || 1442a6ba39aSWolfgang Grandegger (indirect[idx] == -1 && indirect[0] > 0)) 1452a6ba39aSWolfgang Grandegger iosize = SJA1000_IOSIZE_INDIRECT; 1462a6ba39aSWolfgang Grandegger if (!request_region(port[idx], iosize, DRV_NAME)) { 1472a6ba39aSWolfgang Grandegger err = -EBUSY; 1482a6ba39aSWolfgang Grandegger goto exit; 1492a6ba39aSWolfgang Grandegger } 1502a6ba39aSWolfgang Grandegger } 1512a6ba39aSWolfgang Grandegger 1522a6ba39aSWolfgang Grandegger dev = alloc_sja1000dev(0); 1532a6ba39aSWolfgang Grandegger if (!dev) { 1542a6ba39aSWolfgang Grandegger err = -ENOMEM; 1552a6ba39aSWolfgang Grandegger goto exit_unmap; 1562a6ba39aSWolfgang Grandegger } 1572a6ba39aSWolfgang Grandegger priv = netdev_priv(dev); 1582a6ba39aSWolfgang Grandegger 1592a6ba39aSWolfgang Grandegger dev->irq = irq[idx]; 1602a6ba39aSWolfgang Grandegger priv->irq_flags = IRQF_SHARED; 1612a6ba39aSWolfgang Grandegger if (mem[idx]) { 1622a6ba39aSWolfgang Grandegger priv->reg_base = base; 1632a6ba39aSWolfgang Grandegger dev->base_addr = mem[idx]; 1642a6ba39aSWolfgang Grandegger priv->read_reg = sja1000_isa_mem_read_reg; 1652a6ba39aSWolfgang Grandegger priv->write_reg = sja1000_isa_mem_write_reg; 1662a6ba39aSWolfgang Grandegger } else { 1672a6ba39aSWolfgang Grandegger priv->reg_base = (void __iomem *)port[idx]; 1682a6ba39aSWolfgang Grandegger dev->base_addr = port[idx]; 1692a6ba39aSWolfgang Grandegger 1702a6ba39aSWolfgang Grandegger if (iosize == SJA1000_IOSIZE_INDIRECT) { 1712a6ba39aSWolfgang Grandegger priv->read_reg = sja1000_isa_port_read_reg_indirect; 1722a6ba39aSWolfgang Grandegger priv->write_reg = sja1000_isa_port_write_reg_indirect; 1732a6ba39aSWolfgang Grandegger } else { 1742a6ba39aSWolfgang Grandegger priv->read_reg = sja1000_isa_port_read_reg; 1752a6ba39aSWolfgang Grandegger priv->write_reg = sja1000_isa_port_write_reg; 1762a6ba39aSWolfgang Grandegger } 1772a6ba39aSWolfgang Grandegger } 1782a6ba39aSWolfgang Grandegger 1792a6ba39aSWolfgang Grandegger if (clk[idx]) 1802a6ba39aSWolfgang Grandegger priv->can.clock.freq = clk[idx] / 2; 1812a6ba39aSWolfgang Grandegger else if (clk[0]) 1822a6ba39aSWolfgang Grandegger priv->can.clock.freq = clk[0] / 2; 1832a6ba39aSWolfgang Grandegger else 1842a6ba39aSWolfgang Grandegger priv->can.clock.freq = CLK_DEFAULT / 2; 1852a6ba39aSWolfgang Grandegger 186115d2a3dSWolfgang Grandegger if (ocr[idx] != 0xff) 187115d2a3dSWolfgang Grandegger priv->ocr = ocr[idx]; 188115d2a3dSWolfgang Grandegger else if (ocr[0] != 0xff) 189115d2a3dSWolfgang Grandegger priv->ocr = ocr[0]; 1902a6ba39aSWolfgang Grandegger else 1912a6ba39aSWolfgang Grandegger priv->ocr = OCR_DEFAULT; 1922a6ba39aSWolfgang Grandegger 193115d2a3dSWolfgang Grandegger if (cdr[idx] != 0xff) 194115d2a3dSWolfgang Grandegger priv->cdr = cdr[idx]; 195115d2a3dSWolfgang Grandegger else if (cdr[0] != 0xff) 196115d2a3dSWolfgang Grandegger priv->cdr = cdr[0]; 1972a6ba39aSWolfgang Grandegger else 1982a6ba39aSWolfgang Grandegger priv->cdr = CDR_DEFAULT; 1992a6ba39aSWolfgang Grandegger 2001839a6c6SWolfgang Grandegger dev_set_drvdata(&pdev->dev, dev); 2011839a6c6SWolfgang Grandegger SET_NETDEV_DEV(dev, &pdev->dev); 2022a6ba39aSWolfgang Grandegger 2032a6ba39aSWolfgang Grandegger err = register_sja1000dev(dev); 2042a6ba39aSWolfgang Grandegger if (err) { 2051839a6c6SWolfgang Grandegger dev_err(&pdev->dev, "registering %s failed (err=%d)\n", 2062a6ba39aSWolfgang Grandegger DRV_NAME, err); 2072a6ba39aSWolfgang Grandegger goto exit_unmap; 2082a6ba39aSWolfgang Grandegger } 2092a6ba39aSWolfgang Grandegger 2101839a6c6SWolfgang Grandegger dev_info(&pdev->dev, "%s device registered (reg_base=0x%p, irq=%d)\n", 2112a6ba39aSWolfgang Grandegger DRV_NAME, priv->reg_base, dev->irq); 2122a6ba39aSWolfgang Grandegger return 0; 2132a6ba39aSWolfgang Grandegger 2142a6ba39aSWolfgang Grandegger exit_unmap: 2152a6ba39aSWolfgang Grandegger if (mem[idx]) 2162a6ba39aSWolfgang Grandegger iounmap(base); 2172a6ba39aSWolfgang Grandegger exit_release: 2182a6ba39aSWolfgang Grandegger if (mem[idx]) 2192a6ba39aSWolfgang Grandegger release_mem_region(mem[idx], iosize); 2202a6ba39aSWolfgang Grandegger else 2212a6ba39aSWolfgang Grandegger release_region(port[idx], iosize); 2222a6ba39aSWolfgang Grandegger exit: 2232a6ba39aSWolfgang Grandegger return err; 2242a6ba39aSWolfgang Grandegger } 2252a6ba39aSWolfgang Grandegger 226*3c8ac0f2SBill Pemberton static int sja1000_isa_remove(struct platform_device *pdev) 2272a6ba39aSWolfgang Grandegger { 2281839a6c6SWolfgang Grandegger struct net_device *dev = dev_get_drvdata(&pdev->dev); 2292a6ba39aSWolfgang Grandegger struct sja1000_priv *priv = netdev_priv(dev); 2301839a6c6SWolfgang Grandegger int idx = pdev->id; 2312a6ba39aSWolfgang Grandegger 2322a6ba39aSWolfgang Grandegger unregister_sja1000dev(dev); 2331839a6c6SWolfgang Grandegger dev_set_drvdata(&pdev->dev, NULL); 2342a6ba39aSWolfgang Grandegger 2352a6ba39aSWolfgang Grandegger if (mem[idx]) { 2362a6ba39aSWolfgang Grandegger iounmap(priv->reg_base); 2372a6ba39aSWolfgang Grandegger release_mem_region(mem[idx], SJA1000_IOSIZE); 2382a6ba39aSWolfgang Grandegger } else { 2392a6ba39aSWolfgang Grandegger if (priv->read_reg == sja1000_isa_port_read_reg_indirect) 2402a6ba39aSWolfgang Grandegger release_region(port[idx], SJA1000_IOSIZE_INDIRECT); 2412a6ba39aSWolfgang Grandegger else 2422a6ba39aSWolfgang Grandegger release_region(port[idx], SJA1000_IOSIZE); 2432a6ba39aSWolfgang Grandegger } 2442a6ba39aSWolfgang Grandegger free_sja1000dev(dev); 2452a6ba39aSWolfgang Grandegger 2462a6ba39aSWolfgang Grandegger return 0; 2472a6ba39aSWolfgang Grandegger } 2482a6ba39aSWolfgang Grandegger 2491839a6c6SWolfgang Grandegger static struct platform_driver sja1000_isa_driver = { 2502a6ba39aSWolfgang Grandegger .probe = sja1000_isa_probe, 251*3c8ac0f2SBill Pemberton .remove = sja1000_isa_remove, 2522a6ba39aSWolfgang Grandegger .driver = { 2532a6ba39aSWolfgang Grandegger .name = DRV_NAME, 2541839a6c6SWolfgang Grandegger .owner = THIS_MODULE, 2552a6ba39aSWolfgang Grandegger }, 2562a6ba39aSWolfgang Grandegger }; 2572a6ba39aSWolfgang Grandegger 2582a6ba39aSWolfgang Grandegger static int __init sja1000_isa_init(void) 2592a6ba39aSWolfgang Grandegger { 2601839a6c6SWolfgang Grandegger int idx, err; 2612a6ba39aSWolfgang Grandegger 2621839a6c6SWolfgang Grandegger for (idx = 0; idx < MAXDEV; idx++) { 2631839a6c6SWolfgang Grandegger if ((port[idx] || mem[idx]) && irq[idx]) { 2641839a6c6SWolfgang Grandegger sja1000_isa_devs[idx] = 2651839a6c6SWolfgang Grandegger platform_device_alloc(DRV_NAME, idx); 2661839a6c6SWolfgang Grandegger if (!sja1000_isa_devs[idx]) { 2671839a6c6SWolfgang Grandegger err = -ENOMEM; 2681839a6c6SWolfgang Grandegger goto exit_free_devices; 2691839a6c6SWolfgang Grandegger } 2701839a6c6SWolfgang Grandegger err = platform_device_add(sja1000_isa_devs[idx]); 2711839a6c6SWolfgang Grandegger if (err) { 2721839a6c6SWolfgang Grandegger platform_device_put(sja1000_isa_devs[idx]); 2731839a6c6SWolfgang Grandegger goto exit_free_devices; 2741839a6c6SWolfgang Grandegger } 2751839a6c6SWolfgang Grandegger pr_debug("%s: platform device %d: port=%#lx, mem=%#lx, " 2761839a6c6SWolfgang Grandegger "irq=%d\n", 2771839a6c6SWolfgang Grandegger DRV_NAME, idx, port[idx], mem[idx], irq[idx]); 2781839a6c6SWolfgang Grandegger } else if (idx == 0 || port[idx] || mem[idx]) { 2791839a6c6SWolfgang Grandegger pr_err("%s: insufficient parameters supplied\n", 2801839a6c6SWolfgang Grandegger DRV_NAME); 2811839a6c6SWolfgang Grandegger err = -EINVAL; 2821839a6c6SWolfgang Grandegger goto exit_free_devices; 2831839a6c6SWolfgang Grandegger } 2841839a6c6SWolfgang Grandegger } 2851839a6c6SWolfgang Grandegger 2861839a6c6SWolfgang Grandegger err = platform_driver_register(&sja1000_isa_driver); 2871839a6c6SWolfgang Grandegger if (err) 2881839a6c6SWolfgang Grandegger goto exit_free_devices; 2891839a6c6SWolfgang Grandegger 2901839a6c6SWolfgang Grandegger pr_info("Legacy %s driver for max. %d devices registered\n", 2912a6ba39aSWolfgang Grandegger DRV_NAME, MAXDEV); 2921839a6c6SWolfgang Grandegger 2931839a6c6SWolfgang Grandegger return 0; 2941839a6c6SWolfgang Grandegger 2951839a6c6SWolfgang Grandegger exit_free_devices: 2961839a6c6SWolfgang Grandegger while (--idx >= 0) { 2971839a6c6SWolfgang Grandegger if (sja1000_isa_devs[idx]) 2981839a6c6SWolfgang Grandegger platform_device_unregister(sja1000_isa_devs[idx]); 2991839a6c6SWolfgang Grandegger } 3001839a6c6SWolfgang Grandegger 3012a6ba39aSWolfgang Grandegger return err; 3022a6ba39aSWolfgang Grandegger } 3032a6ba39aSWolfgang Grandegger 3042a6ba39aSWolfgang Grandegger static void __exit sja1000_isa_exit(void) 3052a6ba39aSWolfgang Grandegger { 3061839a6c6SWolfgang Grandegger int idx; 3071839a6c6SWolfgang Grandegger 3081839a6c6SWolfgang Grandegger platform_driver_unregister(&sja1000_isa_driver); 3091839a6c6SWolfgang Grandegger for (idx = 0; idx < MAXDEV; idx++) { 3101839a6c6SWolfgang Grandegger if (sja1000_isa_devs[idx]) 3111839a6c6SWolfgang Grandegger platform_device_unregister(sja1000_isa_devs[idx]); 3121839a6c6SWolfgang Grandegger } 3132a6ba39aSWolfgang Grandegger } 3142a6ba39aSWolfgang Grandegger 3152a6ba39aSWolfgang Grandegger module_init(sja1000_isa_init); 3162a6ba39aSWolfgang Grandegger module_exit(sja1000_isa_exit); 317