xref: /linux/arch/x86/platform/ts5500/ts5500.c (revision 6eb2fb3170549737207974c2c6ad34bcc2f3025e)
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");
340