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