Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
[pandora-kernel.git] / arch / x86 / platform / ts5500 / ts5500.c
1 /*
2  * Technologic Systems TS-5500 Single Board Computer support
3  *
4  * Copyright (C) 2013 Savoir-faire Linux Inc.
5  *      Vivien Didelot <vivien.didelot@savoirfairelinux.com>
6  *
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option) any later
10  * version.
11  *
12  *
13  * This driver registers the Technologic Systems TS-5500 Single Board Computer
14  * (SBC) and its devices, and exposes information to userspace such as jumpers'
15  * state or available options. For further information about sysfs entries, see
16  * Documentation/ABI/testing/sysfs-platform-ts5500.
17  *
18  * This code actually supports the TS-5500 platform, but it may be extended to
19  * support similar Technologic Systems x86-based platforms, such as the TS-5600.
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 /* Product code register */
33 #define TS5500_PRODUCT_CODE_ADDR        0x74
34 #define TS5500_PRODUCT_CODE             0x60    /* TS-5500 product code */
35
36 /* SRAM/RS-485/ADC options, and RS-485 RTS/Automatic RS-485 flags register */
37 #define TS5500_SRAM_RS485_ADC_ADDR      0x75
38 #define TS5500_SRAM                     BIT(0)  /* SRAM option */
39 #define TS5500_RS485                    BIT(1)  /* RS-485 option */
40 #define TS5500_ADC                      BIT(2)  /* A/D converter option */
41 #define TS5500_RS485_RTS                BIT(6)  /* RTS for RS-485 */
42 #define TS5500_RS485_AUTO               BIT(7)  /* Automatic RS-485 */
43
44 /* External Reset/Industrial Temperature Range options register */
45 #define TS5500_ERESET_ITR_ADDR          0x76
46 #define TS5500_ERESET                   BIT(0)  /* External Reset option */
47 #define TS5500_ITR                      BIT(1)  /* Indust. Temp. Range option */
48
49 /* LED/Jumpers register */
50 #define TS5500_LED_JP_ADDR              0x77
51 #define TS5500_LED                      BIT(0)  /* LED flag */
52 #define TS5500_JP1                      BIT(1)  /* Automatic CMOS */
53 #define TS5500_JP2                      BIT(2)  /* Enable Serial Console */
54 #define TS5500_JP3                      BIT(3)  /* Write Enable Drive A */
55 #define TS5500_JP4                      BIT(4)  /* Fast Console (115K baud) */
56 #define TS5500_JP5                      BIT(5)  /* User Jumper */
57 #define TS5500_JP6                      BIT(6)  /* Console on COM1 (req. JP2) */
58 #define TS5500_JP7                      BIT(7)  /* Undocumented (Unused) */
59
60 /* A/D Converter registers */
61 #define TS5500_ADC_CONV_BUSY_ADDR       0x195   /* Conversion state register */
62 #define TS5500_ADC_CONV_BUSY            BIT(0)
63 #define TS5500_ADC_CONV_INIT_LSB_ADDR   0x196   /* Start conv. / LSB register */
64 #define TS5500_ADC_CONV_MSB_ADDR        0x197   /* MSB register */
65 #define TS5500_ADC_CONV_DELAY           12      /* usec */
66
67 /**
68  * struct ts5500_sbc - TS-5500 board description
69  * @id:         Board product ID.
70  * @sram:       Flag for SRAM option.
71  * @rs485:      Flag for RS-485 option.
72  * @adc:        Flag for Analog/Digital converter option.
73  * @ereset:     Flag for External Reset option.
74  * @itr:        Flag for Industrial Temperature Range option.
75  * @jumpers:    Bitfield for jumpers' state.
76  */
77 struct ts5500_sbc {
78         int     id;
79         bool    sram;
80         bool    rs485;
81         bool    adc;
82         bool    ereset;
83         bool    itr;
84         u8      jumpers;
85 };
86
87 /* Board signatures in BIOS shadow RAM */
88 static const struct {
89         const char * const string;
90         const ssize_t offset;
91 } ts5500_signatures[] __initdata = {
92         { "TS-5x00 AMD Elan", 0xb14 },
93 };
94
95 static int __init ts5500_check_signature(void)
96 {
97         void __iomem *bios;
98         int i, ret = -ENODEV;
99
100         bios = ioremap(0xf0000, 0x10000);
101         if (!bios)
102                 return -ENOMEM;
103
104         for (i = 0; i < ARRAY_SIZE(ts5500_signatures); i++) {
105                 if (check_signature(bios + ts5500_signatures[i].offset,
106                                     ts5500_signatures[i].string,
107                                     strlen(ts5500_signatures[i].string))) {
108                         ret = 0;
109                         break;
110                 }
111         }
112
113         iounmap(bios);
114         return ret;
115 }
116
117 static int __init ts5500_detect_config(struct ts5500_sbc *sbc)
118 {
119         u8 tmp;
120         int ret = 0;
121
122         if (!request_region(TS5500_PRODUCT_CODE_ADDR, 4, "ts5500"))
123                 return -EBUSY;
124
125         tmp = inb(TS5500_PRODUCT_CODE_ADDR);
126         if (tmp != TS5500_PRODUCT_CODE) {
127                 pr_err("This platform is not a TS-5500 (found ID 0x%x)\n", tmp);
128                 ret = -ENODEV;
129                 goto cleanup;
130         }
131         sbc->id = tmp;
132
133         tmp = inb(TS5500_SRAM_RS485_ADC_ADDR);
134         sbc->sram = tmp & TS5500_SRAM;
135         sbc->rs485 = tmp & TS5500_RS485;
136         sbc->adc = tmp & TS5500_ADC;
137
138         tmp = inb(TS5500_ERESET_ITR_ADDR);
139         sbc->ereset = tmp & TS5500_ERESET;
140         sbc->itr = tmp & TS5500_ITR;
141
142         tmp = inb(TS5500_LED_JP_ADDR);
143         sbc->jumpers = tmp & ~TS5500_LED;
144
145 cleanup:
146         release_region(TS5500_PRODUCT_CODE_ADDR, 4);
147         return ret;
148 }
149
150 static ssize_t ts5500_show_id(struct device *dev,
151                               struct device_attribute *attr, char *buf)
152 {
153         struct ts5500_sbc *sbc = dev_get_drvdata(dev);
154
155         return sprintf(buf, "0x%.2x\n", sbc->id);
156 }
157
158 static ssize_t ts5500_show_jumpers(struct device *dev,
159                                    struct device_attribute *attr,
160                                    char *buf)
161 {
162         struct ts5500_sbc *sbc = dev_get_drvdata(dev);
163
164         return sprintf(buf, "0x%.2x\n", sbc->jumpers >> 1);
165 }
166
167 #define TS5500_SHOW(field)                                      \
168         static ssize_t ts5500_show_##field(struct device *dev,  \
169                         struct device_attribute *attr,          \
170                         char *buf)                              \
171         {                                                       \
172                 struct ts5500_sbc *sbc = dev_get_drvdata(dev);  \
173                 return sprintf(buf, "%d\n", sbc->field);        \
174         }
175
176 TS5500_SHOW(sram)
177 TS5500_SHOW(rs485)
178 TS5500_SHOW(adc)
179 TS5500_SHOW(ereset)
180 TS5500_SHOW(itr)
181
182 static DEVICE_ATTR(id, S_IRUGO, ts5500_show_id, NULL);
183 static DEVICE_ATTR(jumpers, S_IRUGO, ts5500_show_jumpers, NULL);
184 static DEVICE_ATTR(sram, S_IRUGO, ts5500_show_sram, NULL);
185 static DEVICE_ATTR(rs485, S_IRUGO, ts5500_show_rs485, NULL);
186 static DEVICE_ATTR(adc, S_IRUGO, ts5500_show_adc, NULL);
187 static DEVICE_ATTR(ereset, S_IRUGO, ts5500_show_ereset, NULL);
188 static DEVICE_ATTR(itr, S_IRUGO, ts5500_show_itr, NULL);
189
190 static struct attribute *ts5500_attributes[] = {
191         &dev_attr_id.attr,
192         &dev_attr_jumpers.attr,
193         &dev_attr_sram.attr,
194         &dev_attr_rs485.attr,
195         &dev_attr_adc.attr,
196         &dev_attr_ereset.attr,
197         &dev_attr_itr.attr,
198         NULL
199 };
200
201 static const struct attribute_group ts5500_attr_group = {
202         .attrs = ts5500_attributes,
203 };
204
205 static struct resource ts5500_dio1_resource[] = {
206         DEFINE_RES_IRQ_NAMED(7, "DIO1 interrupt"),
207 };
208
209 static struct platform_device ts5500_dio1_pdev = {
210         .name = "ts5500-dio1",
211         .id = -1,
212         .resource = ts5500_dio1_resource,
213         .num_resources = 1,
214 };
215
216 static struct resource ts5500_dio2_resource[] = {
217         DEFINE_RES_IRQ_NAMED(6, "DIO2 interrupt"),
218 };
219
220 static struct platform_device ts5500_dio2_pdev = {
221         .name = "ts5500-dio2",
222         .id = -1,
223         .resource = ts5500_dio2_resource,
224         .num_resources = 1,
225 };
226
227 static void ts5500_led_set(struct led_classdev *led_cdev,
228                            enum led_brightness brightness)
229 {
230         outb(!!brightness, TS5500_LED_JP_ADDR);
231 }
232
233 static enum led_brightness ts5500_led_get(struct led_classdev *led_cdev)
234 {
235         return (inb(TS5500_LED_JP_ADDR) & TS5500_LED) ? LED_FULL : LED_OFF;
236 }
237
238 static struct led_classdev ts5500_led_cdev = {
239         .name = "ts5500:green:",
240         .brightness_set = ts5500_led_set,
241         .brightness_get = ts5500_led_get,
242 };
243
244 static int ts5500_adc_convert(u8 ctrl)
245 {
246         u8 lsb, msb;
247
248         /* Start conversion (ensure the 3 MSB are set to 0) */
249         outb(ctrl & 0x1f, TS5500_ADC_CONV_INIT_LSB_ADDR);
250
251         /*
252          * The platform has CPLD logic driving the A/D converter.
253          * The conversion must complete within 11 microseconds,
254          * otherwise we have to re-initiate a conversion.
255          */
256         udelay(TS5500_ADC_CONV_DELAY);
257         if (inb(TS5500_ADC_CONV_BUSY_ADDR) & TS5500_ADC_CONV_BUSY)
258                 return -EBUSY;
259
260         /* Read the raw data */
261         lsb = inb(TS5500_ADC_CONV_INIT_LSB_ADDR);
262         msb = inb(TS5500_ADC_CONV_MSB_ADDR);
263
264         return (msb << 8) | lsb;
265 }
266
267 static struct max197_platform_data ts5500_adc_pdata = {
268         .convert = ts5500_adc_convert,
269 };
270
271 static struct platform_device ts5500_adc_pdev = {
272         .name = "max197",
273         .id = -1,
274         .dev = {
275                 .platform_data = &ts5500_adc_pdata,
276         },
277 };
278
279 static int __init ts5500_init(void)
280 {
281         struct platform_device *pdev;
282         struct ts5500_sbc *sbc;
283         int err;
284
285         /*
286          * There is no DMI available or PCI bridge subvendor info,
287          * only the BIOS provides a 16-bit identification call.
288          * It is safer to find a signature in the BIOS shadow RAM.
289          */
290         err = ts5500_check_signature();
291         if (err)
292                 return err;
293
294         pdev = platform_device_register_simple("ts5500", -1, NULL, 0);
295         if (IS_ERR(pdev))
296                 return PTR_ERR(pdev);
297
298         sbc = devm_kzalloc(&pdev->dev, sizeof(struct ts5500_sbc), GFP_KERNEL);
299         if (!sbc) {
300                 err = -ENOMEM;
301                 goto error;
302         }
303
304         err = ts5500_detect_config(sbc);
305         if (err)
306                 goto error;
307
308         platform_set_drvdata(pdev, sbc);
309
310         err = sysfs_create_group(&pdev->dev.kobj, &ts5500_attr_group);
311         if (err)
312                 goto error;
313
314         ts5500_dio1_pdev.dev.parent = &pdev->dev;
315         if (platform_device_register(&ts5500_dio1_pdev))
316                 dev_warn(&pdev->dev, "DIO1 block registration failed\n");
317         ts5500_dio2_pdev.dev.parent = &pdev->dev;
318         if (platform_device_register(&ts5500_dio2_pdev))
319                 dev_warn(&pdev->dev, "DIO2 block registration failed\n");
320
321         if (led_classdev_register(&pdev->dev, &ts5500_led_cdev))
322                 dev_warn(&pdev->dev, "LED registration failed\n");
323
324         if (sbc->adc) {
325                 ts5500_adc_pdev.dev.parent = &pdev->dev;
326                 if (platform_device_register(&ts5500_adc_pdev))
327                         dev_warn(&pdev->dev, "ADC registration failed\n");
328         }
329
330         return 0;
331 error:
332         platform_device_unregister(pdev);
333         return err;
334 }
335 device_initcall(ts5500_init);
336
337 MODULE_LICENSE("GPL");
338 MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
339 MODULE_DESCRIPTION("Technologic Systems TS-5500 platform driver");