]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blob - arch/x86/platform/ts5500/ts5500.c
x86/platform/ts5500: Add a 'name' sysfs attribute
[mirror_ubuntu-zesty-kernel.git] / 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 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 * @name: Board model name.
70 * @id: Board product ID.
71 * @sram: Flag for SRAM option.
72 * @rs485: Flag for RS-485 option.
73 * @adc: Flag for Analog/Digital converter option.
74 * @ereset: Flag for External Reset option.
75 * @itr: Flag for Industrial Temperature Range option.
76 * @jumpers: Bitfield for jumpers' state.
77 */
78 struct ts5500_sbc {
79 const char *name;
80 int id;
81 bool sram;
82 bool rs485;
83 bool adc;
84 bool ereset;
85 bool itr;
86 u8 jumpers;
87 };
88
89 /* Board signatures in BIOS shadow RAM */
90 static const struct {
91 const char * const string;
92 const ssize_t offset;
93 } ts5500_signatures[] __initconst = {
94 { "TS-5x00 AMD Elan", 0xb14 },
95 };
96
97 static int __init ts5500_check_signature(void)
98 {
99 void __iomem *bios;
100 int i, ret = -ENODEV;
101
102 bios = ioremap(0xf0000, 0x10000);
103 if (!bios)
104 return -ENOMEM;
105
106 for (i = 0; i < ARRAY_SIZE(ts5500_signatures); i++) {
107 if (check_signature(bios + ts5500_signatures[i].offset,
108 ts5500_signatures[i].string,
109 strlen(ts5500_signatures[i].string))) {
110 ret = 0;
111 break;
112 }
113 }
114
115 iounmap(bios);
116 return ret;
117 }
118
119 static int __init ts5500_detect_config(struct ts5500_sbc *sbc)
120 {
121 u8 tmp;
122 int ret = 0;
123
124 if (!request_region(TS5500_PRODUCT_CODE_ADDR, 4, "ts5500"))
125 return -EBUSY;
126
127 sbc->id = inb(TS5500_PRODUCT_CODE_ADDR);
128 if (sbc->id == TS5500_PRODUCT_CODE) {
129 sbc->name = "TS-5500";
130 } else {
131 pr_err("ts5500: unknown product code 0x%x\n", sbc->id);
132 ret = -ENODEV;
133 goto cleanup;
134 }
135
136 tmp = inb(TS5500_SRAM_RS485_ADC_ADDR);
137 sbc->sram = tmp & TS5500_SRAM;
138 sbc->rs485 = tmp & TS5500_RS485;
139 sbc->adc = tmp & TS5500_ADC;
140
141 tmp = inb(TS5500_ERESET_ITR_ADDR);
142 sbc->ereset = tmp & TS5500_ERESET;
143 sbc->itr = tmp & TS5500_ITR;
144
145 tmp = inb(TS5500_LED_JP_ADDR);
146 sbc->jumpers = tmp & ~TS5500_LED;
147
148 cleanup:
149 release_region(TS5500_PRODUCT_CODE_ADDR, 4);
150 return ret;
151 }
152
153 static ssize_t name_show(struct device *dev, struct device_attribute *attr,
154 char *buf)
155 {
156 struct ts5500_sbc *sbc = dev_get_drvdata(dev);
157
158 return sprintf(buf, "%s\n", sbc->name);
159 }
160 static DEVICE_ATTR_RO(name);
161
162 static ssize_t id_show(struct device *dev, struct device_attribute *attr,
163 char *buf)
164 {
165 struct ts5500_sbc *sbc = dev_get_drvdata(dev);
166
167 return sprintf(buf, "0x%.2x\n", sbc->id);
168 }
169 static DEVICE_ATTR_RO(id);
170
171 static ssize_t jumpers_show(struct device *dev, struct device_attribute *attr,
172 char *buf)
173 {
174 struct ts5500_sbc *sbc = dev_get_drvdata(dev);
175
176 return sprintf(buf, "0x%.2x\n", sbc->jumpers >> 1);
177 }
178 static DEVICE_ATTR_RO(jumpers);
179
180 #define TS5500_ATTR_BOOL(_field) \
181 static ssize_t _field##_show(struct device *dev, \
182 struct device_attribute *attr, char *buf) \
183 { \
184 struct ts5500_sbc *sbc = dev_get_drvdata(dev); \
185 \
186 return sprintf(buf, "%d\n", sbc->_field); \
187 } \
188 static DEVICE_ATTR_RO(_field)
189
190 TS5500_ATTR_BOOL(sram);
191 TS5500_ATTR_BOOL(rs485);
192 TS5500_ATTR_BOOL(adc);
193 TS5500_ATTR_BOOL(ereset);
194 TS5500_ATTR_BOOL(itr);
195
196 static struct attribute *ts5500_attributes[] = {
197 &dev_attr_id.attr,
198 &dev_attr_name.attr,
199 &dev_attr_jumpers.attr,
200 &dev_attr_sram.attr,
201 &dev_attr_rs485.attr,
202 &dev_attr_adc.attr,
203 &dev_attr_ereset.attr,
204 &dev_attr_itr.attr,
205 NULL
206 };
207
208 static const struct attribute_group ts5500_attr_group = {
209 .attrs = ts5500_attributes,
210 };
211
212 static struct resource ts5500_dio1_resource[] = {
213 DEFINE_RES_IRQ_NAMED(7, "DIO1 interrupt"),
214 };
215
216 static struct platform_device ts5500_dio1_pdev = {
217 .name = "ts5500-dio1",
218 .id = -1,
219 .resource = ts5500_dio1_resource,
220 .num_resources = 1,
221 };
222
223 static struct resource ts5500_dio2_resource[] = {
224 DEFINE_RES_IRQ_NAMED(6, "DIO2 interrupt"),
225 };
226
227 static struct platform_device ts5500_dio2_pdev = {
228 .name = "ts5500-dio2",
229 .id = -1,
230 .resource = ts5500_dio2_resource,
231 .num_resources = 1,
232 };
233
234 static void ts5500_led_set(struct led_classdev *led_cdev,
235 enum led_brightness brightness)
236 {
237 outb(!!brightness, TS5500_LED_JP_ADDR);
238 }
239
240 static enum led_brightness ts5500_led_get(struct led_classdev *led_cdev)
241 {
242 return (inb(TS5500_LED_JP_ADDR) & TS5500_LED) ? LED_FULL : LED_OFF;
243 }
244
245 static struct led_classdev ts5500_led_cdev = {
246 .name = "ts5500:green:",
247 .brightness_set = ts5500_led_set,
248 .brightness_get = ts5500_led_get,
249 };
250
251 static int ts5500_adc_convert(u8 ctrl)
252 {
253 u8 lsb, msb;
254
255 /* Start conversion (ensure the 3 MSB are set to 0) */
256 outb(ctrl & 0x1f, TS5500_ADC_CONV_INIT_LSB_ADDR);
257
258 /*
259 * The platform has CPLD logic driving the A/D converter.
260 * The conversion must complete within 11 microseconds,
261 * otherwise we have to re-initiate a conversion.
262 */
263 udelay(TS5500_ADC_CONV_DELAY);
264 if (inb(TS5500_ADC_CONV_BUSY_ADDR) & TS5500_ADC_CONV_BUSY)
265 return -EBUSY;
266
267 /* Read the raw data */
268 lsb = inb(TS5500_ADC_CONV_INIT_LSB_ADDR);
269 msb = inb(TS5500_ADC_CONV_MSB_ADDR);
270
271 return (msb << 8) | lsb;
272 }
273
274 static struct max197_platform_data ts5500_adc_pdata = {
275 .convert = ts5500_adc_convert,
276 };
277
278 static struct platform_device ts5500_adc_pdev = {
279 .name = "max197",
280 .id = -1,
281 .dev = {
282 .platform_data = &ts5500_adc_pdata,
283 },
284 };
285
286 static int __init ts5500_init(void)
287 {
288 struct platform_device *pdev;
289 struct ts5500_sbc *sbc;
290 int err;
291
292 /*
293 * There is no DMI available or PCI bridge subvendor info,
294 * only the BIOS provides a 16-bit identification call.
295 * It is safer to find a signature in the BIOS shadow RAM.
296 */
297 err = ts5500_check_signature();
298 if (err)
299 return err;
300
301 pdev = platform_device_register_simple("ts5500", -1, NULL, 0);
302 if (IS_ERR(pdev))
303 return PTR_ERR(pdev);
304
305 sbc = devm_kzalloc(&pdev->dev, sizeof(struct ts5500_sbc), GFP_KERNEL);
306 if (!sbc) {
307 err = -ENOMEM;
308 goto error;
309 }
310
311 err = ts5500_detect_config(sbc);
312 if (err)
313 goto error;
314
315 platform_set_drvdata(pdev, sbc);
316
317 err = sysfs_create_group(&pdev->dev.kobj, &ts5500_attr_group);
318 if (err)
319 goto error;
320
321 ts5500_dio1_pdev.dev.parent = &pdev->dev;
322 if (platform_device_register(&ts5500_dio1_pdev))
323 dev_warn(&pdev->dev, "DIO1 block registration failed\n");
324 ts5500_dio2_pdev.dev.parent = &pdev->dev;
325 if (platform_device_register(&ts5500_dio2_pdev))
326 dev_warn(&pdev->dev, "DIO2 block registration failed\n");
327
328 if (led_classdev_register(&pdev->dev, &ts5500_led_cdev))
329 dev_warn(&pdev->dev, "LED registration failed\n");
330
331 if (sbc->adc) {
332 ts5500_adc_pdev.dev.parent = &pdev->dev;
333 if (platform_device_register(&ts5500_adc_pdev))
334 dev_warn(&pdev->dev, "ADC registration failed\n");
335 }
336
337 return 0;
338 error:
339 platform_device_unregister(pdev);
340 return err;
341 }
342 device_initcall(ts5500_init);
343
344 MODULE_LICENSE("GPL");
345 MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
346 MODULE_DESCRIPTION("Technologic Systems TS-5500 platform driver");