2 * Technologic Systems TS-5500 Single Board Computer support
4 * Copyright (C) 2013-2014 Savoir-faire Linux Inc.
5 * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
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
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.
18 * This code may be extended to support similar x86-based platforms.
19 * Actually, the TS-5500 and TS-5400 are supported.
22 #include <linux/delay.h>
24 #include <linux/kernel.h>
25 #include <linux/leds.h>
26 #include <linux/init.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>
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 */
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 */
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 */
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) */
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 */
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.
90 /* Board signatures in BIOS shadow RAM */
92 const char * const string
;
94 } ts5500_signatures
[] __initconst
= {
95 { "TS-5x00 AMD Elan", 0xb14 },
98 static int __init
ts5500_check_signature(void)
101 int i
, ret
= -ENODEV
;
103 bios
= ioremap(0xf0000, 0x10000);
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
))) {
120 static int __init
ts5500_detect_config(struct ts5500_sbc
*sbc
)
125 if (!request_region(TS5500_PRODUCT_CODE_ADDR
, 4, "ts5500"))
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";
134 pr_err("ts5500: unknown product code 0x%x\n", sbc
->id
);
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
;
144 tmp
= inb(TS5500_ERESET_ITR_ADDR
);
145 sbc
->ereset
= tmp
& TS5500_ERESET
;
146 sbc
->itr
= tmp
& TS5500_ITR
;
148 tmp
= inb(TS5500_LED_JP_ADDR
);
149 sbc
->jumpers
= tmp
& ~TS5500_LED
;
152 release_region(TS5500_PRODUCT_CODE_ADDR
, 4);
156 static ssize_t
name_show(struct device
*dev
, struct device_attribute
*attr
,
159 struct ts5500_sbc
*sbc
= dev_get_drvdata(dev
);
161 return sprintf(buf
, "%s\n", sbc
->name
);
163 static DEVICE_ATTR_RO(name
);
165 static ssize_t
id_show(struct device
*dev
, struct device_attribute
*attr
,
168 struct ts5500_sbc
*sbc
= dev_get_drvdata(dev
);
170 return sprintf(buf
, "0x%.2x\n", sbc
->id
);
172 static DEVICE_ATTR_RO(id
);
174 static ssize_t
jumpers_show(struct device
*dev
, struct device_attribute
*attr
,
177 struct ts5500_sbc
*sbc
= dev_get_drvdata(dev
);
179 return sprintf(buf
, "0x%.2x\n", sbc
->jumpers
>> 1);
181 static DEVICE_ATTR_RO(jumpers
);
183 #define TS5500_ATTR_BOOL(_field) \
184 static ssize_t _field##_show(struct device *dev, \
185 struct device_attribute *attr, char *buf) \
187 struct ts5500_sbc *sbc = dev_get_drvdata(dev); \
189 return sprintf(buf, "%d\n", sbc->_field); \
191 static DEVICE_ATTR_RO(_field)
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
);
199 static struct attribute
*ts5500_attributes
[] = {
202 &dev_attr_jumpers
.attr
,
204 &dev_attr_rs485
.attr
,
206 &dev_attr_ereset
.attr
,
211 static const struct attribute_group ts5500_attr_group
= {
212 .attrs
= ts5500_attributes
,
215 static struct resource ts5500_dio1_resource
[] = {
216 DEFINE_RES_IRQ_NAMED(7, "DIO1 interrupt"),
219 static struct platform_device ts5500_dio1_pdev
= {
220 .name
= "ts5500-dio1",
222 .resource
= ts5500_dio1_resource
,
226 static struct resource ts5500_dio2_resource
[] = {
227 DEFINE_RES_IRQ_NAMED(6, "DIO2 interrupt"),
230 static struct platform_device ts5500_dio2_pdev
= {
231 .name
= "ts5500-dio2",
233 .resource
= ts5500_dio2_resource
,
237 static void ts5500_led_set(struct led_classdev
*led_cdev
,
238 enum led_brightness brightness
)
240 outb(!!brightness
, TS5500_LED_JP_ADDR
);
243 static enum led_brightness
ts5500_led_get(struct led_classdev
*led_cdev
)
245 return (inb(TS5500_LED_JP_ADDR
) & TS5500_LED
) ? LED_FULL
: LED_OFF
;
248 static struct led_classdev ts5500_led_cdev
= {
249 .name
= "ts5500:green:",
250 .brightness_set
= ts5500_led_set
,
251 .brightness_get
= ts5500_led_get
,
254 static int ts5500_adc_convert(u8 ctrl
)
258 /* Start conversion (ensure the 3 MSB are set to 0) */
259 outb(ctrl
& 0x1f, TS5500_ADC_CONV_INIT_LSB_ADDR
);
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.
266 udelay(TS5500_ADC_CONV_DELAY
);
267 if (inb(TS5500_ADC_CONV_BUSY_ADDR
) & TS5500_ADC_CONV_BUSY
)
270 /* Read the raw data */
271 lsb
= inb(TS5500_ADC_CONV_INIT_LSB_ADDR
);
272 msb
= inb(TS5500_ADC_CONV_MSB_ADDR
);
274 return (msb
<< 8) | lsb
;
277 static struct max197_platform_data ts5500_adc_pdata
= {
278 .convert
= ts5500_adc_convert
,
281 static struct platform_device ts5500_adc_pdev
= {
285 .platform_data
= &ts5500_adc_pdata
,
289 static int __init
ts5500_init(void)
291 struct platform_device
*pdev
;
292 struct ts5500_sbc
*sbc
;
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.
300 err
= ts5500_check_signature();
304 pdev
= platform_device_register_simple("ts5500", -1, NULL
, 0);
306 return PTR_ERR(pdev
);
308 sbc
= devm_kzalloc(&pdev
->dev
, sizeof(struct ts5500_sbc
), GFP_KERNEL
);
314 err
= ts5500_detect_config(sbc
);
318 platform_set_drvdata(pdev
, sbc
);
320 err
= sysfs_create_group(&pdev
->dev
.kobj
, &ts5500_attr_group
);
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");
333 if (led_classdev_register(&pdev
->dev
, &ts5500_led_cdev
))
334 dev_warn(&pdev
->dev
, "LED registration failed\n");
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");
344 platform_device_unregister(pdev
);
347 device_initcall(ts5500_init
);