aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/can
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/can')
-rw-r--r--drivers/net/can/sja1000/Kconfig12
-rw-r--r--drivers/net/can/sja1000/Makefile1
-rw-r--r--drivers/net/can/sja1000/tscan1.c216
3 files changed, 229 insertions, 0 deletions
diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig
index ae3505afd68..6fdc031daaa 100644
--- a/drivers/net/can/sja1000/Kconfig
+++ b/drivers/net/can/sja1000/Kconfig
@@ -58,4 +58,16 @@ config CAN_PLX_PCI
58 - esd CAN-PCIe/2000 58 - esd CAN-PCIe/2000
59 - Marathon CAN-bus-PCI card (http://www.marathon.ru/) 59 - Marathon CAN-bus-PCI card (http://www.marathon.ru/)
60 - TEWS TECHNOLOGIES TPMC810 card (http://www.tews.com/) 60 - TEWS TECHNOLOGIES TPMC810 card (http://www.tews.com/)
61
62config CAN_TSCAN1
63 tristate "TS-CAN1 PC104 boards"
64 depends on ISA
65 help
66 This driver is for Technologic Systems' TSCAN-1 PC104 boards.
67 http://www.embeddedarm.com/products/board-detail.php?product=TS-CAN1
68 The driver supports multiple boards and automatically configures them:
69 PLD IO base addresses are read from jumpers JP1 and JP2,
70 IRQ numbers are read from jumpers JP4 and JP5,
71 SJA1000 IO base addresses are chosen heuristically (first that works).
72
61endif 73endif
diff --git a/drivers/net/can/sja1000/Makefile b/drivers/net/can/sja1000/Makefile
index ce924553995..2c591eb321c 100644
--- a/drivers/net/can/sja1000/Makefile
+++ b/drivers/net/can/sja1000/Makefile
@@ -9,5 +9,6 @@ obj-$(CONFIG_CAN_SJA1000_OF_PLATFORM) += sja1000_of_platform.o
9obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o 9obj-$(CONFIG_CAN_EMS_PCI) += ems_pci.o
10obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o 10obj-$(CONFIG_CAN_KVASER_PCI) += kvaser_pci.o
11obj-$(CONFIG_CAN_PLX_PCI) += plx_pci.o 11obj-$(CONFIG_CAN_PLX_PCI) += plx_pci.o
12obj-$(CONFIG_CAN_TSCAN1) += tscan1.o
12 13
13ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG 14ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/sja1000/tscan1.c b/drivers/net/can/sja1000/tscan1.c
new file mode 100644
index 00000000000..9756099a883
--- /dev/null
+++ b/drivers/net/can/sja1000/tscan1.c
@@ -0,0 +1,216 @@
1/*
2 * tscan1.c: driver for Technologic Systems TS-CAN1 PC104 boards
3 *
4 * Copyright 2010 Andre B. Oliveira
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/*
21 * References:
22 * - Getting started with TS-CAN1, Technologic Systems, Jun 2009
23 * http://www.embeddedarm.com/documentation/ts-can1-manual.pdf
24 */
25
26#include <linux/init.h>
27#include <linux/io.h>
28#include <linux/ioport.h>
29#include <linux/isa.h>
30#include <linux/module.h>
31#include <linux/netdevice.h>
32#include "sja1000.h"
33
34MODULE_DESCRIPTION("Driver for Technologic Systems TS-CAN1 PC104 boards");
35MODULE_AUTHOR("Andre B. Oliveira <anbadeol@gmail.com>");
36MODULE_LICENSE("GPL");
37
38/* Maximum number of boards (one in each JP1:JP2 setting of IO address) */
39#define TSCAN1_MAXDEV 4
40
41/* PLD registers address offsets */
42#define TSCAN1_ID1 0
43#define TSCAN1_ID2 1
44#define TSCAN1_VERSION 2
45#define TSCAN1_LED 3
46#define TSCAN1_PAGE 4
47#define TSCAN1_MODE 5
48#define TSCAN1_JUMPERS 6
49
50/* PLD board identifier registers magic values */
51#define TSCAN1_ID1_VALUE 0xf6
52#define TSCAN1_ID2_VALUE 0xb9
53
54/* PLD mode register SJA1000 IO enable bit */
55#define TSCAN1_MODE_ENABLE 0x40
56
57/* PLD jumpers register bits */
58#define TSCAN1_JP4 0x10
59#define TSCAN1_JP5 0x20
60
61/* PLD IO base addresses start */
62#define TSCAN1_PLD_ADDRESS 0x150
63
64/* PLD register space size */
65#define TSCAN1_PLD_SIZE 8
66
67/* SJA1000 register space size */
68#define TSCAN1_SJA1000_SIZE 32
69
70/* SJA1000 crystal frequency (16MHz) */
71#define TSCAN1_SJA1000_XTAL 16000000
72
73/* SJA1000 IO base addresses */
74static const unsigned short tscan1_sja1000_addresses[] __devinitconst = {
75 0x100, 0x120, 0x180, 0x1a0, 0x200, 0x240, 0x280, 0x320
76};
77
78/* Read SJA1000 register */
79static u8 tscan1_read(const struct sja1000_priv *priv, int reg)
80{
81 return inb((unsigned long)priv->reg_base + reg);
82}
83
84/* Write SJA1000 register */
85static void tscan1_write(const struct sja1000_priv *priv, int reg, u8 val)
86{
87 outb(val, (unsigned long)priv->reg_base + reg);
88}
89
90/* Probe for a TS-CAN1 board with JP2:JP1 jumper setting ID */
91static int __devinit tscan1_probe(struct device *dev, unsigned id)
92{
93 struct net_device *netdev;
94 struct sja1000_priv *priv;
95 unsigned long pld_base, sja1000_base;
96 int irq, i;
97
98 pld_base = TSCAN1_PLD_ADDRESS + id * TSCAN1_PLD_SIZE;
99 if (!request_region(pld_base, TSCAN1_PLD_SIZE, dev_name(dev)))
100 return -EBUSY;
101
102 if (inb(pld_base + TSCAN1_ID1) != TSCAN1_ID1_VALUE ||
103 inb(pld_base + TSCAN1_ID2) != TSCAN1_ID2_VALUE) {
104 release_region(pld_base, TSCAN1_PLD_SIZE);
105 return -ENODEV;
106 }
107
108 switch (inb(pld_base + TSCAN1_JUMPERS) & (TSCAN1_JP4 | TSCAN1_JP5)) {
109 case TSCAN1_JP4:
110 irq = 6;
111 break;
112 case TSCAN1_JP5:
113 irq = 7;
114 break;
115 case TSCAN1_JP4 | TSCAN1_JP5:
116 irq = 5;
117 break;
118 default:
119 dev_err(dev, "invalid JP4:JP5 setting (no IRQ)\n");
120 release_region(pld_base, TSCAN1_PLD_SIZE);
121 return -EINVAL;
122 }
123
124 netdev = alloc_sja1000dev(0);
125 if (!netdev) {
126 release_region(pld_base, TSCAN1_PLD_SIZE);
127 return -ENOMEM;
128 }
129
130 dev_set_drvdata(dev, netdev);
131 SET_NETDEV_DEV(netdev, dev);
132
133 netdev->base_addr = pld_base;
134 netdev->irq = irq;
135
136 priv = netdev_priv(netdev);
137 priv->read_reg = tscan1_read;
138 priv->write_reg = tscan1_write;
139 priv->can.clock.freq = TSCAN1_SJA1000_XTAL / 2;
140 priv->cdr = CDR_CBP | CDR_CLK_OFF;
141 priv->ocr = OCR_TX0_PUSHPULL;
142
143 /* Select the first SJA1000 IO address that is free and that works */
144 for (i = 0; i < ARRAY_SIZE(tscan1_sja1000_addresses); i++) {
145 sja1000_base = tscan1_sja1000_addresses[i];
146 if (!request_region(sja1000_base, TSCAN1_SJA1000_SIZE,
147 dev_name(dev)))
148 continue;
149
150 /* Set SJA1000 IO base address and enable it */
151 outb(TSCAN1_MODE_ENABLE | i, pld_base + TSCAN1_MODE);
152
153 priv->reg_base = (void __iomem *)sja1000_base;
154 if (!register_sja1000dev(netdev)) {
155 /* SJA1000 probe succeeded; turn LED off and return */
156 outb(0, pld_base + TSCAN1_LED);
157 netdev_info(netdev, "TS-CAN1 at 0x%lx 0x%lx irq %d\n",
158 pld_base, sja1000_base, irq);
159 return 0;
160 }
161
162 /* SJA1000 probe failed; release and try next address */
163 outb(0, pld_base + TSCAN1_MODE);
164 release_region(sja1000_base, TSCAN1_SJA1000_SIZE);
165 }
166
167 dev_err(dev, "failed to assign SJA1000 IO address\n");
168 dev_set_drvdata(dev, NULL);
169 free_sja1000dev(netdev);
170 release_region(pld_base, TSCAN1_PLD_SIZE);
171 return -ENXIO;
172}
173
174static int __devexit tscan1_remove(struct device *dev, unsigned id /*unused*/)
175{
176 struct net_device *netdev;
177 struct sja1000_priv *priv;
178 unsigned long pld_base, sja1000_base;
179
180 netdev = dev_get_drvdata(dev);
181 unregister_sja1000dev(netdev);
182 dev_set_drvdata(dev, NULL);
183
184 priv = netdev_priv(netdev);
185 pld_base = netdev->base_addr;
186 sja1000_base = (unsigned long)priv->reg_base;
187
188 outb(0, pld_base + TSCAN1_MODE); /* disable SJA1000 IO space */
189
190 release_region(sja1000_base, TSCAN1_SJA1000_SIZE);
191 release_region(pld_base, TSCAN1_PLD_SIZE);
192
193 free_sja1000dev(netdev);
194
195 return 0;
196}
197
198static struct isa_driver tscan1_isa_driver = {
199 .probe = tscan1_probe,
200 .remove = __devexit_p(tscan1_remove),
201 .driver = {
202 .name = "tscan1",
203 },
204};
205
206static int __init tscan1_init(void)
207{
208 return isa_register_driver(&tscan1_isa_driver, TSCAN1_MAXDEV);
209}
210module_init(tscan1_init);
211
212static void __exit tscan1_exit(void)
213{
214 isa_unregister_driver(&tscan1_isa_driver);
215}
216module_exit(tscan1_exit);