aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/can/cc770/cc770_isa.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/can/cc770/cc770_isa.c')
-rw-r--r--drivers/net/can/cc770/cc770_isa.c337
1 files changed, 337 insertions, 0 deletions
diff --git a/drivers/net/can/cc770/cc770_isa.c b/drivers/net/can/cc770/cc770_isa.c
new file mode 100644
index 000000000000..455d79f534d0
--- /dev/null
+++ b/drivers/net/can/cc770/cc770_isa.c
@@ -0,0 +1,337 @@
1/*
2 * Copyright (C) 2009, 2011 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/version.h>
20#include <linux/module.h>
21#include <linux/platform_device.h>
22#include <linux/interrupt.h>
23#include <linux/netdevice.h>
24#include <linux/delay.h>
25#include <linux/irq.h>
26#include <linux/io.h>
27#include <linux/can.h>
28#include <linux/can/dev.h>
29
30#include "cc770.h"
31
32#define DRV_NAME "cc770_isa"
33
34#define MAXDEV 8
35
36MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
37MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the ISA bus");
38MODULE_LICENSE("GPL v2");
39
40#define CLK_DEFAULT 16000000 /* 16 MHz */
41#define BCR_DEFAULT 0x00
42#define COR_DEFAULT 0x00
43
44static unsigned long port[MAXDEV];
45static unsigned long mem[MAXDEV];
46static int __devinitdata irq[MAXDEV];
47static int __devinitdata clk[MAXDEV];
48static u8 __devinitdata cir[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
49static u8 __devinitdata bcr[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
50static u8 __devinitdata cor[MAXDEV] = {[0 ... (MAXDEV - 1)] = 0xff};
51static int __devinitdata indirect[MAXDEV] = {[0 ... (MAXDEV - 1)] = -1};
52
53module_param_array(port, ulong, NULL, S_IRUGO);
54MODULE_PARM_DESC(port, "I/O port number");
55
56module_param_array(mem, ulong, NULL, S_IRUGO);
57MODULE_PARM_DESC(mem, "I/O memory address");
58
59module_param_array(indirect, int, NULL, S_IRUGO);
60MODULE_PARM_DESC(indirect, "Indirect access via address and data port");
61
62module_param_array(irq, int, NULL, S_IRUGO);
63MODULE_PARM_DESC(irq, "IRQ number");
64
65module_param_array(clk, int, NULL, S_IRUGO);
66MODULE_PARM_DESC(clk, "External oscillator clock frequency "
67 "(default=16000000 [16 MHz])");
68
69module_param_array(cir, byte, NULL, S_IRUGO);
70MODULE_PARM_DESC(cir, "CPU interface register (default=0x40 [CPU_DSC])");
71
72module_param_array(bcr, byte, NULL, S_IRUGO);
73MODULE_PARM_DESC(ocr, "Bus configuration register (default=0x00)");
74
75module_param_array(cor, byte, NULL, S_IRUGO);
76MODULE_PARM_DESC(cor, "Clockout register (default=0x00)");
77
78#define CC770_IOSIZE 0x20
79#define CC770_IOSIZE_INDIRECT 0x02
80
81static struct platform_device *cc770_isa_devs[MAXDEV];
82
83static u8 cc770_isa_mem_read_reg(const struct cc770_priv *priv, int reg)
84{
85 return readb(priv->reg_base + reg);
86}
87
88static void cc770_isa_mem_write_reg(const struct cc770_priv *priv,
89 int reg, u8 val)
90{
91 writeb(val, priv->reg_base + reg);
92}
93
94static u8 cc770_isa_port_read_reg(const struct cc770_priv *priv, int reg)
95{
96 return inb((unsigned long)priv->reg_base + reg);
97}
98
99static void cc770_isa_port_write_reg(const struct cc770_priv *priv,
100 int reg, u8 val)
101{
102 outb(val, (unsigned long)priv->reg_base + reg);
103}
104
105static u8 cc770_isa_port_read_reg_indirect(const struct cc770_priv *priv,
106 int reg)
107{
108 unsigned long base = (unsigned long)priv->reg_base;
109
110 outb(reg, base);
111 return inb(base + 1);
112}
113
114static void cc770_isa_port_write_reg_indirect(const struct cc770_priv *priv,
115 int reg, u8 val)
116{
117 unsigned long base = (unsigned long)priv->reg_base;
118
119 outb(reg, base);
120 outb(val, base + 1);
121}
122
123static int __devinit cc770_isa_probe(struct platform_device *pdev)
124{
125 struct net_device *dev;
126 struct cc770_priv *priv;
127 void __iomem *base = NULL;
128 int iosize = CC770_IOSIZE;
129 int idx = pdev->id;
130 int err;
131 u32 clktmp;
132
133 dev_dbg(&pdev->dev, "probing idx=%d: port=%#lx, mem=%#lx, irq=%d\n",
134 idx, port[idx], mem[idx], irq[idx]);
135 if (mem[idx]) {
136 if (!request_mem_region(mem[idx], iosize, DRV_NAME)) {
137 err = -EBUSY;
138 goto exit;
139 }
140 base = ioremap_nocache(mem[idx], iosize);
141 if (!base) {
142 err = -ENOMEM;
143 goto exit_release;
144 }
145 } else {
146 if (indirect[idx] > 0 ||
147 (indirect[idx] == -1 && indirect[0] > 0))
148 iosize = CC770_IOSIZE_INDIRECT;
149 if (!request_region(port[idx], iosize, DRV_NAME)) {
150 err = -EBUSY;
151 goto exit;
152 }
153 }
154
155 dev = alloc_cc770dev(0);
156 if (!dev) {
157 err = -ENOMEM;
158 goto exit_unmap;
159 }
160 priv = netdev_priv(dev);
161
162 dev->irq = irq[idx];
163 priv->irq_flags = IRQF_SHARED;
164 if (mem[idx]) {
165 priv->reg_base = base;
166 dev->base_addr = mem[idx];
167 priv->read_reg = cc770_isa_mem_read_reg;
168 priv->write_reg = cc770_isa_mem_write_reg;
169 } else {
170 priv->reg_base = (void __iomem *)port[idx];
171 dev->base_addr = port[idx];
172
173 if (iosize == CC770_IOSIZE_INDIRECT) {
174 priv->read_reg = cc770_isa_port_read_reg_indirect;
175 priv->write_reg = cc770_isa_port_write_reg_indirect;
176 } else {
177 priv->read_reg = cc770_isa_port_read_reg;
178 priv->write_reg = cc770_isa_port_write_reg;
179 }
180 }
181
182 if (clk[idx])
183 clktmp = clk[idx];
184 else if (clk[0])
185 clktmp = clk[0];
186 else
187 clktmp = CLK_DEFAULT;
188 priv->can.clock.freq = clktmp;
189
190 if (cir[idx] != 0xff) {
191 priv->cpu_interface = cir[idx] & 0xff;
192 } else if (cir[0] != 0xff) {
193 priv->cpu_interface = cir[0] & 0xff;
194 } else {
195 /* The system clock may not exceed 10 MHz */
196 if (clktmp > 10000000) {
197 priv->cpu_interface |= CPUIF_DSC;
198 clktmp /= 2;
199 }
200 /* The memory clock may not exceed 8 MHz */
201 if (clktmp > 8000000)
202 priv->cpu_interface |= CPUIF_DMC;
203 }
204
205 if (priv->cpu_interface & CPUIF_DSC)
206 priv->can.clock.freq /= 2;
207
208 if (bcr[idx] != 0xff)
209 priv->bus_config = bcr[idx] & 0xff;
210 else if (bcr[0] != 0xff)
211 priv->bus_config = bcr[0] & 0xff;
212 else
213 priv->bus_config = BCR_DEFAULT;
214
215 if (cor[idx] != 0xff)
216 priv->clkout = cor[idx];
217 else if (cor[0] != 0xff)
218 priv->clkout = cor[0] & 0xff;
219 else
220 priv->clkout = COR_DEFAULT;
221
222 dev_set_drvdata(&pdev->dev, dev);
223 SET_NETDEV_DEV(dev, &pdev->dev);
224
225 err = register_cc770dev(dev);
226 if (err) {
227 dev_err(&pdev->dev, "registering %s failed (err=%d)\n",
228 DRV_NAME, err);
229 goto exit_unmap;
230 }
231
232 dev_info(&pdev->dev, "%s device registered (reg_base=0x%p, irq=%d)\n",
233 DRV_NAME, priv->reg_base, dev->irq);
234 return 0;
235
236 exit_unmap:
237 if (mem[idx])
238 iounmap(base);
239 exit_release:
240 if (mem[idx])
241 release_mem_region(mem[idx], iosize);
242 else
243 release_region(port[idx], iosize);
244 exit:
245 return err;
246}
247
248static int __devexit cc770_isa_remove(struct platform_device *pdev)
249{
250 struct net_device *dev = dev_get_drvdata(&pdev->dev);
251 struct cc770_priv *priv = netdev_priv(dev);
252 int idx = pdev->id;
253
254 unregister_cc770dev(dev);
255 dev_set_drvdata(&pdev->dev, NULL);
256
257 if (mem[idx]) {
258 iounmap(priv->reg_base);
259 release_mem_region(mem[idx], CC770_IOSIZE);
260 } else {
261 if (priv->read_reg == cc770_isa_port_read_reg_indirect)
262 release_region(port[idx], CC770_IOSIZE_INDIRECT);
263 else
264 release_region(port[idx], CC770_IOSIZE);
265 }
266 free_cc770dev(dev);
267
268 return 0;
269}
270
271static struct platform_driver cc770_isa_driver = {
272 .probe = cc770_isa_probe,
273 .remove = __devexit_p(cc770_isa_remove),
274 .driver = {
275 .name = DRV_NAME,
276 .owner = THIS_MODULE,
277 },
278};
279
280static int __init cc770_isa_init(void)
281{
282 int idx, err;
283
284 for (idx = 0; idx < MAXDEV; idx++) {
285 if ((port[idx] || mem[idx]) && irq[idx]) {
286 cc770_isa_devs[idx] =
287 platform_device_alloc(DRV_NAME, idx);
288 if (!cc770_isa_devs[idx]) {
289 err = -ENOMEM;
290 goto exit_free_devices;
291 }
292 err = platform_device_add(cc770_isa_devs[idx]);
293 if (err) {
294 platform_device_put(cc770_isa_devs[idx]);
295 goto exit_free_devices;
296 }
297 pr_debug("%s: platform device %d: port=%#lx, mem=%#lx, "
298 "irq=%d\n",
299 DRV_NAME, idx, port[idx], mem[idx], irq[idx]);
300 } else if (idx == 0 || port[idx] || mem[idx]) {
301 pr_err("%s: insufficient parameters supplied\n",
302 DRV_NAME);
303 err = -EINVAL;
304 goto exit_free_devices;
305 }
306 }
307
308 err = platform_driver_register(&cc770_isa_driver);
309 if (err)
310 goto exit_free_devices;
311
312 pr_info("Legacy %s driver for max. %d devices registered\n",
313 DRV_NAME, MAXDEV);
314
315 return 0;
316
317exit_free_devices:
318 while (--idx >= 0) {
319 if (cc770_isa_devs[idx])
320 platform_device_unregister(cc770_isa_devs[idx]);
321 }
322
323 return err;
324}
325module_init(cc770_isa_init);
326
327static void __exit cc770_isa_exit(void)
328{
329 int idx;
330
331 platform_driver_unregister(&cc770_isa_driver);
332 for (idx = 0; idx < MAXDEV; idx++) {
333 if (cc770_isa_devs[idx])
334 platform_device_unregister(cc770_isa_devs[idx]);
335 }
336}
337module_exit(cc770_isa_exit);