diff options
author | Wolfgang Grandegger <wg@grandegger.com> | 2009-09-01 01:37:33 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-09-04 05:16:16 -0400 |
commit | 2a6ba39ad6a277595ef9a0187a44f09e062dd3b2 (patch) | |
tree | e4036840743f2f618df8626815edd5a9ab2cd5b2 /drivers | |
parent | 8935f57e68c40c6469efe03f996bfb6150e18c6b (diff) |
can: sja1000: legacy SJA1000 ISA bus driver
This patch adds support for legacy SJA1000 CAN controllers on the ISA
or PC-104 bus. The I/O port or memory address and the IRQ number must
be specified via module parameters:
insmod sja1000_isa.ko port=0x310,0x380 irq=7,11
for ISA devices using I/O ports or:
insmod sja1000_isa.ko mem=0xd1000,0xd1000 irq=7,11
for memory mapped ISA devices.
Indirect access via address and data port is supported as well:
insmod sja1000_isa.ko port=0x310,0x380 indirect=1 irq=7,11
Here is a full list of the supported module parameters:
port:I/O port number (array of ulong)
mem:I/O memory address (array of ulong)
indirect:Indirect access via address and data port (array of byte)
irq:IRQ number (array of int)
clk:External oscillator clock frequency (default=16000000 [16 MHz])
(array of int)
cdr:Clock divider register (default=0x48 [CDR_CBP | CDR_CLK_OFF])
(array of byte)
ocr:Output clock register (default=0x18 [OCR_TX0_PUSHPULL])
(array of byte)
Note: for clk, cdr, ocr, the first argument re-defines the default
for all other devices, e.g.:
insmod sja1000_isa.ko mem=0xd1000,0xd1000 irq=7,11 clk=24000000
is equivalent to
insmod sja1000_isa.ko mem=0xd1000,0xd1000 irq=7,11 \
clk=24000000,24000000
Signed-off-by: Wolfgang Grandegger <wg@grandegger.com>
Tested-by: Oliver Hartkopp <oliver@hartkopp.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/can/Kconfig | 7 | ||||
-rw-r--r-- | drivers/net/can/sja1000/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/can/sja1000/sja1000_isa.c | 281 |
3 files changed, 289 insertions, 0 deletions
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 30ae55d71678..090074372462 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig | |||
@@ -41,6 +41,13 @@ config CAN_SJA1000 | |||
41 | ---help--- | 41 | ---help--- |
42 | Driver for the SJA1000 CAN controllers from Philips or NXP | 42 | Driver for the SJA1000 CAN controllers from Philips or NXP |
43 | 43 | ||
44 | config CAN_SJA1000_ISA | ||
45 | depends on CAN_SJA1000 && ISA | ||
46 | tristate "ISA Bus based legacy SJA1000 driver" | ||
47 | ---help--- | ||
48 | This driver adds legacy support for SJA1000 chips connected to | ||
49 | the ISA bus using I/O port, memory mapped or indirect access. | ||
50 | |||
44 | config CAN_SJA1000_PLATFORM | 51 | config CAN_SJA1000_PLATFORM |
45 | depends on CAN_SJA1000 | 52 | depends on CAN_SJA1000 |
46 | tristate "Generic Platform Bus based SJA1000 driver" | 53 | tristate "Generic Platform Bus based SJA1000 driver" |
diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile index 9d0c08da273c..9d245ac03965 100644 --- a/drivers/net/can/sja1000/Makefile +++ b/drivers/net/can/sja1000/Makefile | |||
@@ -3,6 +3,7 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | obj-$(CONFIG_CAN_SJA1000) += sja1000.o | 5 | obj-$(CONFIG_CAN_SJA1000) += sja1000.o |
6 | obj-$(CONFIG_CAN_SJA1000_ISA) += sja1000_isa.o | ||
6 | obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o | 7 | obj-$(CONFIG_CAN_SJA1000_PLATFORM) += sja1000_platform.o |
7 | obj-$(CONFIG_CAN_SJA1000_OF_PLATFORM) += sja1000_of_platform.o | 8 | obj-$(CONFIG_CAN_SJA1000_OF_PLATFORM) += sja1000_of_platform.o |
8 | obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o | 9 | obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o |
diff --git a/drivers/net/can/sja1000/sja1000_isa.c b/drivers/net/can/sja1000/sja1000_isa.c new file mode 100644 index 000000000000..a6a51f155962 --- /dev/null +++ b/drivers/net/can/sja1000/sja1000_isa.c | |||
@@ -0,0 +1,281 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009 Wolfgang Grandegger <wg@grandegger.com> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the version 2 of the GNU General Public License | ||
6 | * as published by the Free Software Foundation | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program; if not, write to the Free Software | ||
15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
16 | */ | ||
17 | |||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/isa.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/netdevice.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/irq.h> | ||
25 | #include <linux/io.h> | ||
26 | #include <linux/can.h> | ||
27 | #include <linux/can/dev.h> | ||
28 | #include <linux/can/platform/sja1000.h> | ||
29 | |||
30 | #include "sja1000.h" | ||
31 | |||
32 | #define DRV_NAME "sja1000_isa" | ||
33 | |||
34 | #define MAXDEV 8 | ||
35 | |||
36 | MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); | ||
37 | MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the ISA bus"); | ||
38 | MODULE_LICENSE("GPL v2"); | ||
39 | |||
40 | #define CLK_DEFAULT 16000000 /* 16 MHz */ | ||
41 | #define CDR_DEFAULT (CDR_CBP | CDR_CLK_OFF) | ||
42 | #define OCR_DEFAULT OCR_TX0_PUSHPULL | ||
43 | |||
44 | static unsigned long port[MAXDEV]; | ||
45 | static unsigned long mem[MAXDEV]; | ||
46 | static int __devinitdata irq[MAXDEV]; | ||
47 | static int __devinitdata clk[MAXDEV]; | ||
48 | static char __devinitdata cdr[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1}; | ||
49 | static char __devinitdata ocr[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1}; | ||
50 | static char __devinitdata indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1}; | ||
51 | |||
52 | module_param_array(port, ulong, NULL, S_IRUGO); | ||
53 | MODULE_PARM_DESC(port, "I/O port number"); | ||
54 | |||
55 | module_param_array(mem, ulong, NULL, S_IRUGO); | ||
56 | MODULE_PARM_DESC(mem, "I/O memory address"); | ||
57 | |||
58 | module_param_array(indirect, byte, NULL, S_IRUGO); | ||
59 | MODULE_PARM_DESC(indirect, "Indirect access via address and data port"); | ||
60 | |||
61 | module_param_array(irq, int, NULL, S_IRUGO); | ||
62 | MODULE_PARM_DESC(irq, "IRQ number"); | ||
63 | |||
64 | module_param_array(clk, int, NULL, S_IRUGO); | ||
65 | MODULE_PARM_DESC(clk, "External oscillator clock frequency " | ||
66 | "(default=16000000 [16 MHz])"); | ||
67 | |||
68 | module_param_array(cdr, byte, NULL, S_IRUGO); | ||
69 | MODULE_PARM_DESC(cdr, "Clock divider register " | ||
70 | "(default=0x48 [CDR_CBP | CDR_CLK_OFF])"); | ||
71 | |||
72 | module_param_array(ocr, byte, NULL, S_IRUGO); | ||
73 | MODULE_PARM_DESC(ocr, "Output control register " | ||
74 | "(default=0x18 [OCR_TX0_PUSHPULL])"); | ||
75 | |||
76 | #define SJA1000_IOSIZE 0x20 | ||
77 | #define SJA1000_IOSIZE_INDIRECT 0x02 | ||
78 | |||
79 | static u8 sja1000_isa_mem_read_reg(const struct sja1000_priv *priv, int reg) | ||
80 | { | ||
81 | return readb(priv->reg_base + reg); | ||
82 | } | ||
83 | |||
84 | static void sja1000_isa_mem_write_reg(const struct sja1000_priv *priv, | ||
85 | int reg, u8 val) | ||
86 | { | ||
87 | writeb(val, priv->reg_base + reg); | ||
88 | } | ||
89 | |||
90 | static u8 sja1000_isa_port_read_reg(const struct sja1000_priv *priv, int reg) | ||
91 | { | ||
92 | return inb((unsigned long)priv->reg_base + reg); | ||
93 | } | ||
94 | |||
95 | static void sja1000_isa_port_write_reg(const struct sja1000_priv *priv, | ||
96 | int reg, u8 val) | ||
97 | { | ||
98 | outb(val, (unsigned long)priv->reg_base + reg); | ||
99 | } | ||
100 | |||
101 | static u8 sja1000_isa_port_read_reg_indirect(const struct sja1000_priv *priv, | ||
102 | int reg) | ||
103 | { | ||
104 | unsigned long base = (unsigned long)priv->reg_base; | ||
105 | |||
106 | outb(reg, base); | ||
107 | return inb(base + 1); | ||
108 | } | ||
109 | |||
110 | static void sja1000_isa_port_write_reg_indirect(const struct sja1000_priv *priv, | ||
111 | int reg, u8 val) | ||
112 | { | ||
113 | unsigned long base = (unsigned long)priv->reg_base; | ||
114 | |||
115 | outb(reg, base); | ||
116 | outb(val, base + 1); | ||
117 | } | ||
118 | |||
119 | static int __devinit sja1000_isa_match(struct device *pdev, unsigned int idx) | ||
120 | { | ||
121 | if (port[idx] || mem[idx]) { | ||
122 | if (irq[idx]) | ||
123 | return 1; | ||
124 | } else if (idx) | ||
125 | return 0; | ||
126 | |||
127 | dev_err(pdev, "insufficient parameters supplied\n"); | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static int __devinit sja1000_isa_probe(struct device *pdev, unsigned int idx) | ||
132 | { | ||
133 | struct net_device *dev; | ||
134 | struct sja1000_priv *priv; | ||
135 | void __iomem *base = NULL; | ||
136 | int iosize = SJA1000_IOSIZE; | ||
137 | int err; | ||
138 | |||
139 | if (mem[idx]) { | ||
140 | if (!request_mem_region(mem[idx], iosize, DRV_NAME)) { | ||
141 | err = -EBUSY; | ||
142 | goto exit; | ||
143 | } | ||
144 | base = ioremap_nocache(mem[idx], iosize); | ||
145 | if (!base) { | ||
146 | err = -ENOMEM; | ||
147 | goto exit_release; | ||
148 | } | ||
149 | } else { | ||
150 | if (indirect[idx] > 0 || | ||
151 | (indirect[idx] == -1 && indirect[0] > 0)) | ||
152 | iosize = SJA1000_IOSIZE_INDIRECT; | ||
153 | if (!request_region(port[idx], iosize, DRV_NAME)) { | ||
154 | err = -EBUSY; | ||
155 | goto exit; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | dev = alloc_sja1000dev(0); | ||
160 | if (!dev) { | ||
161 | err = -ENOMEM; | ||
162 | goto exit_unmap; | ||
163 | } | ||
164 | priv = netdev_priv(dev); | ||
165 | |||
166 | dev->irq = irq[idx]; | ||
167 | priv->irq_flags = IRQF_SHARED; | ||
168 | if (mem[idx]) { | ||
169 | priv->reg_base = base; | ||
170 | dev->base_addr = mem[idx]; | ||
171 | priv->read_reg = sja1000_isa_mem_read_reg; | ||
172 | priv->write_reg = sja1000_isa_mem_write_reg; | ||
173 | } else { | ||
174 | priv->reg_base = (void __iomem *)port[idx]; | ||
175 | dev->base_addr = port[idx]; | ||
176 | |||
177 | if (iosize == SJA1000_IOSIZE_INDIRECT) { | ||
178 | priv->read_reg = sja1000_isa_port_read_reg_indirect; | ||
179 | priv->write_reg = sja1000_isa_port_write_reg_indirect; | ||
180 | } else { | ||
181 | priv->read_reg = sja1000_isa_port_read_reg; | ||
182 | priv->write_reg = sja1000_isa_port_write_reg; | ||
183 | } | ||
184 | } | ||
185 | |||
186 | if (clk[idx]) | ||
187 | priv->can.clock.freq = clk[idx] / 2; | ||
188 | else if (clk[0]) | ||
189 | priv->can.clock.freq = clk[0] / 2; | ||
190 | else | ||
191 | priv->can.clock.freq = CLK_DEFAULT / 2; | ||
192 | |||
193 | if (ocr[idx] != -1) | ||
194 | priv->ocr = ocr[idx] & 0xff; | ||
195 | else if (ocr[0] != -1) | ||
196 | priv->ocr = ocr[0] & 0xff; | ||
197 | else | ||
198 | priv->ocr = OCR_DEFAULT; | ||
199 | |||
200 | if (cdr[idx] != -1) | ||
201 | priv->cdr = cdr[idx] & 0xff; | ||
202 | else if (cdr[0] != -1) | ||
203 | priv->cdr = cdr[0] & 0xff; | ||
204 | else | ||
205 | priv->cdr = CDR_DEFAULT; | ||
206 | |||
207 | dev_set_drvdata(pdev, dev); | ||
208 | SET_NETDEV_DEV(dev, pdev); | ||
209 | |||
210 | err = register_sja1000dev(dev); | ||
211 | if (err) { | ||
212 | dev_err(pdev, "registering %s failed (err=%d)\n", | ||
213 | DRV_NAME, err); | ||
214 | goto exit_unmap; | ||
215 | } | ||
216 | |||
217 | dev_info(pdev, "%s device registered (reg_base=0x%p, irq=%d)\n", | ||
218 | DRV_NAME, priv->reg_base, dev->irq); | ||
219 | return 0; | ||
220 | |||
221 | exit_unmap: | ||
222 | if (mem[idx]) | ||
223 | iounmap(base); | ||
224 | exit_release: | ||
225 | if (mem[idx]) | ||
226 | release_mem_region(mem[idx], iosize); | ||
227 | else | ||
228 | release_region(port[idx], iosize); | ||
229 | exit: | ||
230 | return err; | ||
231 | } | ||
232 | |||
233 | static int __devexit sja1000_isa_remove(struct device *pdev, unsigned int idx) | ||
234 | { | ||
235 | struct net_device *dev = dev_get_drvdata(pdev); | ||
236 | struct sja1000_priv *priv = netdev_priv(dev); | ||
237 | |||
238 | unregister_sja1000dev(dev); | ||
239 | dev_set_drvdata(pdev, NULL); | ||
240 | |||
241 | if (mem[idx]) { | ||
242 | iounmap(priv->reg_base); | ||
243 | release_mem_region(mem[idx], SJA1000_IOSIZE); | ||
244 | } else { | ||
245 | if (priv->read_reg == sja1000_isa_port_read_reg_indirect) | ||
246 | release_region(port[idx], SJA1000_IOSIZE_INDIRECT); | ||
247 | else | ||
248 | release_region(port[idx], SJA1000_IOSIZE); | ||
249 | } | ||
250 | free_sja1000dev(dev); | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static struct isa_driver sja1000_isa_driver = { | ||
256 | .match = sja1000_isa_match, | ||
257 | .probe = sja1000_isa_probe, | ||
258 | .remove = __devexit_p(sja1000_isa_remove), | ||
259 | .driver = { | ||
260 | .name = DRV_NAME, | ||
261 | }, | ||
262 | }; | ||
263 | |||
264 | static int __init sja1000_isa_init(void) | ||
265 | { | ||
266 | int err = isa_register_driver(&sja1000_isa_driver, MAXDEV); | ||
267 | |||
268 | if (!err) | ||
269 | printk(KERN_INFO | ||
270 | "Legacy %s driver for max. %d devices registered\n", | ||
271 | DRV_NAME, MAXDEV); | ||
272 | return err; | ||
273 | } | ||
274 | |||
275 | static void __exit sja1000_isa_exit(void) | ||
276 | { | ||
277 | isa_unregister_driver(&sja1000_isa_driver); | ||
278 | } | ||
279 | |||
280 | module_init(sja1000_isa_init); | ||
281 | module_exit(sja1000_isa_exit); | ||