Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / arch / x86 / platform / ts5500 / ts5500.c
1 /*
2  * Technologic Systems TS-5500 Single Board Computer support
3  *
4  * Copyright (C) 2013-2014 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 may be extended to support similar x86-based platforms.
19  * Actually, the TS-5500 and TS-5400 are supported.
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 #define TS5400_PRODUCT_CODE             0x40    /* TS-5400 product code */
36
37 /* SRAM/RS-485/ADC options, and RS-485 RTS/Automatic RS-485 flags register */
38 #define TS5500_SRAM_RS485_ADC_ADDR      0x75
39 #define TS5500_SRAM                     BIT(0)  /* SRAM option */
40 #define TS5500_RS485                    BIT(1)  /* RS-485 option */
41 #define TS5500_ADC                      BIT(2)  /* A/D converter option */
42 #define TS5500_RS485_RTS                BIT(6)  /* RTS for RS-485 */
43 #define TS5500_RS485_AUTO               BIT(7)  /* Automatic RS-485 */
44
45 /* External Reset/Industrial Temperature Range options register */
46 #define TS5500_ERESET_ITR_ADDR          0x76
47 #define TS5500_ERESET                   BIT(0)  /* External Reset option */
48 #define TS5500_ITR                      BIT(1)  /* Indust. Temp. Range option */
49
50 /* LED/Jumpers register */
51 #define TS5500_LED_JP_ADDR              0x77
52 #define TS5500_LED                      BIT(0)  /* LED flag */
53 #define TS5500_JP1                      BIT(1)  /* Automatic CMOS */
54 #define TS5500_JP2                      BIT(2)  /* Enable Serial Console */
55 #define TS5500_JP3                      BIT(3)  /* Write Enable Drive A */
56 #define TS5500_JP4                      BIT(4)  /* Fast Console (115K baud) */
57 #define TS5500_JP5                      BIT(5)  /* User Jumper */
58 #define TS5500_JP6                      BIT(6)  /* Console on COM1 (req. JP2) */
59 #define TS5500_JP7                      BIT(7)  /* Undocumented (Unused) */
60
61 /* A/D Converter registers */
62 #define TS5500_ADC_CONV_BUSY_ADDR       0x195   /* Conversion state register */
63 #define TS5500_ADC_CONV_BUSY            BIT(0)
64 #define TS5500_ADC_CONV_INIT_LSB_ADDR   0x196   /* Start conv. / LSB register */
65 #define TS5500_ADC_CONV_MSB_ADDR        0x197   /* MSB register */
66 #define TS5500_ADC_CONV_DELAY           12      /* usec */
67
68 /**
69  * struct ts5500_sbc - TS-5500 board description
70  * @name:       Board model name.
71  * @id:         Board product ID.
72  * @sram:       Flag for SRAM option.
73  * @rs485:      Flag for RS-485 option.
74  * @adc:        Flag for Analog/Digital converter option.
75  * @ereset:     Flag for External Reset option.
76  * @itr:        Flag for Industrial Temperature Range option.
77  * @jumpers:    Bitfield for jumpers' state.
78  */
79 struct 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 /* Board signatures in BIOS shadow RAM */
91 static const struct {
92         const char * const string;
93         const ssize_t offset;
94 } ts5500_signatures[] __initconst = {
95         { "TS-5x00 AMD Elan", 0xb14 },
96 };
97
98 static 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
120 static 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
151 cleanup:
152         release_region(TS5500_PRODUCT_CODE_ADDR, 4);
153         return ret;
154 }
155
156 static 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 }
163 static DEVICE_ATTR_RO(name);
164
165 static 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 }
172 static DEVICE_ATTR_RO(id);
173
174 static 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 }
181 static 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
193 TS5500_ATTR_BOOL(sram);
194 TS5500_ATTR_BOOL(rs485);
195 TS5500_ATTR_BOOL(adc);
196 TS5500_ATTR_BOOL(ereset);
197 TS5500_ATTR_BOOL(itr);
198
199 static 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
211 static const struct attribute_group ts5500_attr_group = {
212         .attrs = ts5500_attributes,
213 };
214
215 static struct resource ts5500_dio1_resource[] = {
216         DEFINE_RES_IRQ_NAMED(7, "DIO1 interrupt"),
217 };
218
219 static struct platform_device ts5500_dio1_pdev = {
220         .name = "ts5500-dio1",
221         .id = -1,
222         .resource = ts5500_dio1_resource,
223         .num_resources = 1,
224 };
225
226 static struct resource ts5500_dio2_resource[] = {
227         DEFINE_RES_IRQ_NAMED(6, "DIO2 interrupt"),
228 };
229
230 static struct platform_device ts5500_dio2_pdev = {
231         .name = "ts5500-dio2",
232         .id = -1,
233         .resource = ts5500_dio2_resource,
234         .num_resources = 1,
235 };
236
237 static void ts5500_led_set(struct led_classdev *led_cdev,
238                            enum led_brightness brightness)
239 {
240         outb(!!brightness, TS5500_LED_JP_ADDR);
241 }
242
243 static 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
248 static struct led_classdev ts5500_led_cdev = {
249         .name = "ts5500:green:",
250         .brightness_set = ts5500_led_set,
251         .brightness_get = ts5500_led_get,
252 };
253
254 static int ts5500_adc_convert(u8 ctrl)
255 {
256         u8 lsb, msb;
257
258         /* Start conversion (ensure the 3 MSB are set to 0) */
259         outb(ctrl & 0x1f, TS5500_ADC_CONV_INIT_LSB_ADDR);
260
261         /*
262          * The platform has CPLD logic driving the A/D converter.
263          * The conversion must complete within 11 microseconds,
264          * otherwise we have to re-initiate a conversion.
265          */
266         udelay(TS5500_ADC_CONV_DELAY);
267         if (inb(TS5500_ADC_CONV_BUSY_ADDR) & TS5500_ADC_CONV_BUSY)
268                 return -EBUSY;
269
270         /* Read the raw data */
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
277 static struct max197_platform_data ts5500_adc_pdata = {
278         .convert = ts5500_adc_convert,
279 };
280
281 static struct platform_device ts5500_adc_pdev = {
282         .name = "max197",
283         .id = -1,
284         .dev = {
285                 .platform_data = &ts5500_adc_pdata,
286         },
287 };
288
289 static int __init ts5500_init(void)
290 {
291         struct platform_device *pdev;
292         struct ts5500_sbc *sbc;
293         int err;
294
295         /*
296          * There is no DMI available or PCI bridge subvendor info,
297          * only the BIOS provides a 16-bit identification call.
298          * It is safer to find a signature in the BIOS shadow RAM.
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;
343 error:
344         platform_device_unregister(pdev);
345         return err;
346 }
347 device_initcall(ts5500_init);
348
349 MODULE_LICENSE("GPL");
350 MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
351 MODULE_DESCRIPTION("Technologic Systems TS-5500 platform driver");