1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#include <linux/delay.h>
23#include <linux/io.h>
24#include <linux/kernel.h>
25#include <linux/leds.h>
26#include <linux/module.h>
27#include <linux/platform_data/gpio-ts5500.h>
28#include <linux/platform_data/max197.h>
29#include <linux/platform_device.h>
30#include <linux/slab.h>
31
32
33#define TS5500_PRODUCT_CODE_ADDR 0x74
34#define TS5500_PRODUCT_CODE 0x60
35#define TS5400_PRODUCT_CODE 0x40
36
37
38#define TS5500_SRAM_RS485_ADC_ADDR 0x75
39#define TS5500_SRAM BIT(0)
40#define TS5500_RS485 BIT(1)
41#define TS5500_ADC BIT(2)
42#define TS5500_RS485_RTS BIT(6)
43#define TS5500_RS485_AUTO BIT(7)
44
45
46#define TS5500_ERESET_ITR_ADDR 0x76
47#define TS5500_ERESET BIT(0)
48#define TS5500_ITR BIT(1)
49
50
51#define TS5500_LED_JP_ADDR 0x77
52#define TS5500_LED BIT(0)
53#define TS5500_JP1 BIT(1)
54#define TS5500_JP2 BIT(2)
55#define TS5500_JP3 BIT(3)
56#define TS5500_JP4 BIT(4)
57#define TS5500_JP5 BIT(5)
58#define TS5500_JP6 BIT(6)
59#define TS5500_JP7 BIT(7)
60
61
62#define TS5500_ADC_CONV_BUSY_ADDR 0x195
63#define TS5500_ADC_CONV_BUSY BIT(0)
64#define TS5500_ADC_CONV_INIT_LSB_ADDR 0x196
65#define TS5500_ADC_CONV_MSB_ADDR 0x197
66#define TS5500_ADC_CONV_DELAY 12
67
68
69
70
71
72
73
74
75
76
77
78
79struct ts5500_sbc {
80 const char *name;
81 int id;
82 bool sram;
83 bool rs485;
84 bool adc;
85 bool ereset;
86 bool itr;
87 u8 jumpers;
88};
89
90
91static const struct {
92 const char * const string;
93 const ssize_t offset;
94} ts5500_signatures[] __initconst = {
95 { "TS-5x00 AMD Elan", 0xb14 },
96};
97
98static int __init ts5500_check_signature(void)
99{
100 void __iomem *bios;
101 int i, ret = -ENODEV;
102
103 bios = ioremap(0xf0000, 0x10000);
104 if (!bios)
105 return -ENOMEM;
106
107 for (i = 0; i < ARRAY_SIZE(ts5500_signatures); i++) {
108 if (check_signature(bios + ts5500_signatures[i].offset,
109 ts5500_signatures[i].string,
110 strlen(ts5500_signatures[i].string))) {
111 ret = 0;
112 break;
113 }
114 }
115
116 iounmap(bios);
117 return ret;
118}
119
120static int __init ts5500_detect_config(struct ts5500_sbc *sbc)
121{
122 u8 tmp;
123 int ret = 0;
124
125 if (!request_region(TS5500_PRODUCT_CODE_ADDR, 4, "ts5500"))
126 return -EBUSY;
127
128 sbc->id = inb(TS5500_PRODUCT_CODE_ADDR);
129 if (sbc->id == TS5500_PRODUCT_CODE) {
130 sbc->name = "TS-5500";
131 } else if (sbc->id == TS5400_PRODUCT_CODE) {
132 sbc->name = "TS-5400";
133 } else {
134 pr_err("ts5500: unknown product code 0x%x\n", sbc->id);
135 ret = -ENODEV;
136 goto cleanup;
137 }
138
139 tmp = inb(TS5500_SRAM_RS485_ADC_ADDR);
140 sbc->sram = tmp & TS5500_SRAM;
141 sbc->rs485 = tmp & TS5500_RS485;
142 sbc->adc = tmp & TS5500_ADC;
143
144 tmp = inb(TS5500_ERESET_ITR_ADDR);
145 sbc->ereset = tmp & TS5500_ERESET;
146 sbc->itr = tmp & TS5500_ITR;
147
148 tmp = inb(TS5500_LED_JP_ADDR);
149 sbc->jumpers = tmp & ~TS5500_LED;
150
151cleanup:
152 release_region(TS5500_PRODUCT_CODE_ADDR, 4);
153 return ret;
154}
155
156static ssize_t name_show(struct device *dev, struct device_attribute *attr,
157 char *buf)
158{
159 struct ts5500_sbc *sbc = dev_get_drvdata(dev);
160
161 return sprintf(buf, "%s\n", sbc->name);
162}
163static DEVICE_ATTR_RO(name);
164
165static ssize_t id_show(struct device *dev, struct device_attribute *attr,
166 char *buf)
167{
168 struct ts5500_sbc *sbc = dev_get_drvdata(dev);
169
170 return sprintf(buf, "0x%.2x\n", sbc->id);
171}
172static DEVICE_ATTR_RO(id);
173
174static ssize_t jumpers_show(struct device *dev, struct device_attribute *attr,
175 char *buf)
176{
177 struct ts5500_sbc *sbc = dev_get_drvdata(dev);
178
179 return sprintf(buf, "0x%.2x\n", sbc->jumpers >> 1);
180}
181static DEVICE_ATTR_RO(jumpers);
182
183#define TS5500_ATTR_BOOL(_field) \
184 static ssize_t _field##_show(struct device *dev, \
185 struct device_attribute *attr, char *buf) \
186 { \
187 struct ts5500_sbc *sbc = dev_get_drvdata(dev); \
188 \
189 return sprintf(buf, "%d\n", sbc->_field); \
190 } \
191 static DEVICE_ATTR_RO(_field)
192
193TS5500_ATTR_BOOL(sram);
194TS5500_ATTR_BOOL(rs485);
195TS5500_ATTR_BOOL(adc);
196TS5500_ATTR_BOOL(ereset);
197TS5500_ATTR_BOOL(itr);
198
199static struct attribute *ts5500_attributes[] = {
200 &dev_attr_id.attr,
201 &dev_attr_name.attr,
202 &dev_attr_jumpers.attr,
203 &dev_attr_sram.attr,
204 &dev_attr_rs485.attr,
205 &dev_attr_adc.attr,
206 &dev_attr_ereset.attr,
207 &dev_attr_itr.attr,
208 NULL
209};
210
211static const struct attribute_group ts5500_attr_group = {
212 .attrs = ts5500_attributes,
213};
214
215static struct resource ts5500_dio1_resource[] = {
216 DEFINE_RES_IRQ_NAMED(7, "DIO1 interrupt"),
217};
218
219static struct platform_device ts5500_dio1_pdev = {
220 .name = "ts5500-dio1",
221 .id = -1,
222 .resource = ts5500_dio1_resource,
223 .num_resources = 1,
224};
225
226static struct resource ts5500_dio2_resource[] = {
227 DEFINE_RES_IRQ_NAMED(6, "DIO2 interrupt"),
228};
229
230static struct platform_device ts5500_dio2_pdev = {
231 .name = "ts5500-dio2",
232 .id = -1,
233 .resource = ts5500_dio2_resource,
234 .num_resources = 1,
235};
236
237static void ts5500_led_set(struct led_classdev *led_cdev,
238 enum led_brightness brightness)
239{
240 outb(!!brightness, TS5500_LED_JP_ADDR);
241}
242
243static enum led_brightness ts5500_led_get(struct led_classdev *led_cdev)
244{
245 return (inb(TS5500_LED_JP_ADDR) & TS5500_LED) ? LED_FULL : LED_OFF;
246}
247
248static struct led_classdev ts5500_led_cdev = {
249 .name = "ts5500:green:",
250 .brightness_set = ts5500_led_set,
251 .brightness_get = ts5500_led_get,
252};
253
254static int ts5500_adc_convert(u8 ctrl)
255{
256 u8 lsb, msb;
257
258
259 outb(ctrl & 0x1f, TS5500_ADC_CONV_INIT_LSB_ADDR);
260
261
262
263
264
265
266 udelay(TS5500_ADC_CONV_DELAY);
267 if (inb(TS5500_ADC_CONV_BUSY_ADDR) & TS5500_ADC_CONV_BUSY)
268 return -EBUSY;
269
270
271 lsb = inb(TS5500_ADC_CONV_INIT_LSB_ADDR);
272 msb = inb(TS5500_ADC_CONV_MSB_ADDR);
273
274 return (msb << 8) | lsb;
275}
276
277static struct max197_platform_data ts5500_adc_pdata = {
278 .convert = ts5500_adc_convert,
279};
280
281static struct platform_device ts5500_adc_pdev = {
282 .name = "max197",
283 .id = -1,
284 .dev = {
285 .platform_data = &ts5500_adc_pdata,
286 },
287};
288
289static int __init ts5500_init(void)
290{
291 struct platform_device *pdev;
292 struct ts5500_sbc *sbc;
293 int err;
294
295
296
297
298
299
300 err = ts5500_check_signature();
301 if (err)
302 return err;
303
304 pdev = platform_device_register_simple("ts5500", -1, NULL, 0);
305 if (IS_ERR(pdev))
306 return PTR_ERR(pdev);
307
308 sbc = devm_kzalloc(&pdev->dev, sizeof(struct ts5500_sbc), GFP_KERNEL);
309 if (!sbc) {
310 err = -ENOMEM;
311 goto error;
312 }
313
314 err = ts5500_detect_config(sbc);
315 if (err)
316 goto error;
317
318 platform_set_drvdata(pdev, sbc);
319
320 err = sysfs_create_group(&pdev->dev.kobj, &ts5500_attr_group);
321 if (err)
322 goto error;
323
324 if (sbc->id == TS5500_PRODUCT_CODE) {
325 ts5500_dio1_pdev.dev.parent = &pdev->dev;
326 if (platform_device_register(&ts5500_dio1_pdev))
327 dev_warn(&pdev->dev, "DIO1 block registration failed\n");
328 ts5500_dio2_pdev.dev.parent = &pdev->dev;
329 if (platform_device_register(&ts5500_dio2_pdev))
330 dev_warn(&pdev->dev, "DIO2 block registration failed\n");
331 }
332
333 if (led_classdev_register(&pdev->dev, &ts5500_led_cdev))
334 dev_warn(&pdev->dev, "LED registration failed\n");
335
336 if (sbc->adc) {
337 ts5500_adc_pdev.dev.parent = &pdev->dev;
338 if (platform_device_register(&ts5500_adc_pdev))
339 dev_warn(&pdev->dev, "ADC registration failed\n");
340 }
341
342 return 0;
343error:
344 platform_device_unregister(pdev);
345 return err;
346}
347device_initcall(ts5500_init);
348
349MODULE_LICENSE("GPL");
350MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
351MODULE_DESCRIPTION("Technologic Systems TS-5500 platform driver");
352