ts5500.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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. #include <linux/delay.h>
  22. #include <linux/io.h>
  23. #include <linux/kernel.h>
  24. #include <linux/leds.h>
  25. #include <linux/module.h>
  26. #include <linux/platform_data/gpio-ts5500.h>
  27. #include <linux/platform_data/max197.h>
  28. #include <linux/platform_device.h>
  29. #include <linux/slab.h>
  30. /* Product code register */
  31. #define TS5500_PRODUCT_CODE_ADDR 0x74
  32. #define TS5500_PRODUCT_CODE 0x60 /* TS-5500 product code */
  33. #define TS5400_PRODUCT_CODE 0x40 /* TS-5400 product code */
  34. /* SRAM/RS-485/ADC options, and RS-485 RTS/Automatic RS-485 flags register */
  35. #define TS5500_SRAM_RS485_ADC_ADDR 0x75
  36. #define TS5500_SRAM BIT(0) /* SRAM option */
  37. #define TS5500_RS485 BIT(1) /* RS-485 option */
  38. #define TS5500_ADC BIT(2) /* A/D converter option */
  39. #define TS5500_RS485_RTS BIT(6) /* RTS for RS-485 */
  40. #define TS5500_RS485_AUTO BIT(7) /* Automatic RS-485 */
  41. /* External Reset/Industrial Temperature Range options register */
  42. #define TS5500_ERESET_ITR_ADDR 0x76
  43. #define TS5500_ERESET BIT(0) /* External Reset option */
  44. #define TS5500_ITR BIT(1) /* Indust. Temp. Range option */
  45. /* LED/Jumpers register */
  46. #define TS5500_LED_JP_ADDR 0x77
  47. #define TS5500_LED BIT(0) /* LED flag */
  48. #define TS5500_JP1 BIT(1) /* Automatic CMOS */
  49. #define TS5500_JP2 BIT(2) /* Enable Serial Console */
  50. #define TS5500_JP3 BIT(3) /* Write Enable Drive A */
  51. #define TS5500_JP4 BIT(4) /* Fast Console (115K baud) */
  52. #define TS5500_JP5 BIT(5) /* User Jumper */
  53. #define TS5500_JP6 BIT(6) /* Console on COM1 (req. JP2) */
  54. #define TS5500_JP7 BIT(7) /* Undocumented (Unused) */
  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. * struct ts5500_sbc - TS-5500 board description
  63. * @name: Board model name.
  64. * @id: Board product ID.
  65. * @sram: Flag for SRAM option.
  66. * @rs485: Flag for RS-485 option.
  67. * @adc: Flag for Analog/Digital converter option.
  68. * @ereset: Flag for External Reset option.
  69. * @itr: Flag for Industrial Temperature Range option.
  70. * @jumpers: Bitfield for jumpers' state.
  71. */
  72. struct ts5500_sbc {
  73. const char *name;
  74. int id;
  75. bool sram;
  76. bool rs485;
  77. bool adc;
  78. bool ereset;
  79. bool itr;
  80. u8 jumpers;
  81. };
  82. /* Board signatures in BIOS shadow RAM */
  83. static const struct {
  84. const char * const string;
  85. const ssize_t offset;
  86. } ts5500_signatures[] __initconst = {
  87. { "TS-5x00 AMD Elan", 0xb14 },
  88. };
  89. static int __init ts5500_check_signature(void)
  90. {
  91. void __iomem *bios;
  92. int i, ret = -ENODEV;
  93. bios = ioremap(0xf0000, 0x10000);
  94. if (!bios)
  95. return -ENOMEM;
  96. for (i = 0; i < ARRAY_SIZE(ts5500_signatures); i++) {
  97. if (check_signature(bios + ts5500_signatures[i].offset,
  98. ts5500_signatures[i].string,
  99. strlen(ts5500_signatures[i].string))) {
  100. ret = 0;
  101. break;
  102. }
  103. }
  104. iounmap(bios);
  105. return ret;
  106. }
  107. static int __init ts5500_detect_config(struct ts5500_sbc *sbc)
  108. {
  109. u8 tmp;
  110. int ret = 0;
  111. if (!request_region(TS5500_PRODUCT_CODE_ADDR, 4, "ts5500"))
  112. return -EBUSY;
  113. sbc->id = inb(TS5500_PRODUCT_CODE_ADDR);
  114. if (sbc->id == TS5500_PRODUCT_CODE) {
  115. sbc->name = "TS-5500";
  116. } else if (sbc->id == TS5400_PRODUCT_CODE) {
  117. sbc->name = "TS-5400";
  118. } else {
  119. pr_err("ts5500: unknown product code 0x%x\n", sbc->id);
  120. ret = -ENODEV;
  121. goto cleanup;
  122. }
  123. tmp = inb(TS5500_SRAM_RS485_ADC_ADDR);
  124. sbc->sram = tmp & TS5500_SRAM;
  125. sbc->rs485 = tmp & TS5500_RS485;
  126. sbc->adc = tmp & TS5500_ADC;
  127. tmp = inb(TS5500_ERESET_ITR_ADDR);
  128. sbc->ereset = tmp & TS5500_ERESET;
  129. sbc->itr = tmp & TS5500_ITR;
  130. tmp = inb(TS5500_LED_JP_ADDR);
  131. sbc->jumpers = tmp & ~TS5500_LED;
  132. cleanup:
  133. release_region(TS5500_PRODUCT_CODE_ADDR, 4);
  134. return ret;
  135. }
  136. static ssize_t name_show(struct device *dev, struct device_attribute *attr,
  137. char *buf)
  138. {
  139. struct ts5500_sbc *sbc = dev_get_drvdata(dev);
  140. return sprintf(buf, "%s\n", sbc->name);
  141. }
  142. static DEVICE_ATTR_RO(name);
  143. static ssize_t id_show(struct device *dev, struct device_attribute *attr,
  144. char *buf)
  145. {
  146. struct ts5500_sbc *sbc = dev_get_drvdata(dev);
  147. return sprintf(buf, "0x%.2x\n", sbc->id);
  148. }
  149. static DEVICE_ATTR_RO(id);
  150. static ssize_t jumpers_show(struct device *dev, struct device_attribute *attr,
  151. char *buf)
  152. {
  153. struct ts5500_sbc *sbc = dev_get_drvdata(dev);
  154. return sprintf(buf, "0x%.2x\n", sbc->jumpers >> 1);
  155. }
  156. static DEVICE_ATTR_RO(jumpers);
  157. #define TS5500_ATTR_BOOL(_field) \
  158. static ssize_t _field##_show(struct device *dev, \
  159. struct device_attribute *attr, char *buf) \
  160. { \
  161. struct ts5500_sbc *sbc = dev_get_drvdata(dev); \
  162. \
  163. return sprintf(buf, "%d\n", sbc->_field); \
  164. } \
  165. static DEVICE_ATTR_RO(_field)
  166. TS5500_ATTR_BOOL(sram);
  167. TS5500_ATTR_BOOL(rs485);
  168. TS5500_ATTR_BOOL(adc);
  169. TS5500_ATTR_BOOL(ereset);
  170. TS5500_ATTR_BOOL(itr);
  171. static struct attribute *ts5500_attributes[] = {
  172. &dev_attr_id.attr,
  173. &dev_attr_name.attr,
  174. &dev_attr_jumpers.attr,
  175. &dev_attr_sram.attr,
  176. &dev_attr_rs485.attr,
  177. &dev_attr_adc.attr,
  178. &dev_attr_ereset.attr,
  179. &dev_attr_itr.attr,
  180. NULL
  181. };
  182. static const struct attribute_group ts5500_attr_group = {
  183. .attrs = ts5500_attributes,
  184. };
  185. static struct resource ts5500_dio1_resource[] = {
  186. DEFINE_RES_IRQ_NAMED(7, "DIO1 interrupt"),
  187. };
  188. static struct platform_device ts5500_dio1_pdev = {
  189. .name = "ts5500-dio1",
  190. .id = -1,
  191. .resource = ts5500_dio1_resource,
  192. .num_resources = 1,
  193. };
  194. static struct resource ts5500_dio2_resource[] = {
  195. DEFINE_RES_IRQ_NAMED(6, "DIO2 interrupt"),
  196. };
  197. static struct platform_device ts5500_dio2_pdev = {
  198. .name = "ts5500-dio2",
  199. .id = -1,
  200. .resource = ts5500_dio2_resource,
  201. .num_resources = 1,
  202. };
  203. static void ts5500_led_set(struct led_classdev *led_cdev,
  204. enum led_brightness brightness)
  205. {
  206. outb(!!brightness, TS5500_LED_JP_ADDR);
  207. }
  208. static enum led_brightness ts5500_led_get(struct led_classdev *led_cdev)
  209. {
  210. return (inb(TS5500_LED_JP_ADDR) & TS5500_LED) ? LED_FULL : LED_OFF;
  211. }
  212. static struct led_classdev ts5500_led_cdev = {
  213. .name = "ts5500:green:",
  214. .brightness_set = ts5500_led_set,
  215. .brightness_get = ts5500_led_get,
  216. };
  217. static int ts5500_adc_convert(u8 ctrl)
  218. {
  219. u8 lsb, msb;
  220. /* Start conversion (ensure the 3 MSB are set to 0) */
  221. outb(ctrl & 0x1f, TS5500_ADC_CONV_INIT_LSB_ADDR);
  222. /*
  223. * The platform has CPLD logic driving the A/D converter.
  224. * The conversion must complete within 11 microseconds,
  225. * otherwise we have to re-initiate a conversion.
  226. */
  227. udelay(TS5500_ADC_CONV_DELAY);
  228. if (inb(TS5500_ADC_CONV_BUSY_ADDR) & TS5500_ADC_CONV_BUSY)
  229. return -EBUSY;
  230. /* Read the raw data */
  231. lsb = inb(TS5500_ADC_CONV_INIT_LSB_ADDR);
  232. msb = inb(TS5500_ADC_CONV_MSB_ADDR);
  233. return (msb << 8) | lsb;
  234. }
  235. static struct max197_platform_data ts5500_adc_pdata = {
  236. .convert = ts5500_adc_convert,
  237. };
  238. static struct platform_device ts5500_adc_pdev = {
  239. .name = "max197",
  240. .id = -1,
  241. .dev = {
  242. .platform_data = &ts5500_adc_pdata,
  243. },
  244. };
  245. static int __init ts5500_init(void)
  246. {
  247. struct platform_device *pdev;
  248. struct ts5500_sbc *sbc;
  249. int err;
  250. /*
  251. * There is no DMI available or PCI bridge subvendor info,
  252. * only the BIOS provides a 16-bit identification call.
  253. * It is safer to find a signature in the BIOS shadow RAM.
  254. */
  255. err = ts5500_check_signature();
  256. if (err)
  257. return err;
  258. pdev = platform_device_register_simple("ts5500", -1, NULL, 0);
  259. if (IS_ERR(pdev))
  260. return PTR_ERR(pdev);
  261. sbc = devm_kzalloc(&pdev->dev, sizeof(struct ts5500_sbc), GFP_KERNEL);
  262. if (!sbc) {
  263. err = -ENOMEM;
  264. goto error;
  265. }
  266. err = ts5500_detect_config(sbc);
  267. if (err)
  268. goto error;
  269. platform_set_drvdata(pdev, sbc);
  270. err = sysfs_create_group(&pdev->dev.kobj, &ts5500_attr_group);
  271. if (err)
  272. goto error;
  273. if (sbc->id == TS5500_PRODUCT_CODE) {
  274. ts5500_dio1_pdev.dev.parent = &pdev->dev;
  275. if (platform_device_register(&ts5500_dio1_pdev))
  276. dev_warn(&pdev->dev, "DIO1 block registration failed\n");
  277. ts5500_dio2_pdev.dev.parent = &pdev->dev;
  278. if (platform_device_register(&ts5500_dio2_pdev))
  279. dev_warn(&pdev->dev, "DIO2 block registration failed\n");
  280. }
  281. if (led_classdev_register(&pdev->dev, &ts5500_led_cdev))
  282. dev_warn(&pdev->dev, "LED registration failed\n");
  283. if (sbc->adc) {
  284. ts5500_adc_pdev.dev.parent = &pdev->dev;
  285. if (platform_device_register(&ts5500_adc_pdev))
  286. dev_warn(&pdev->dev, "ADC registration failed\n");
  287. }
  288. return 0;
  289. error:
  290. platform_device_unregister(pdev);
  291. return err;
  292. }
  293. device_initcall(ts5500_init);
  294. MODULE_LICENSE("GPL");
  295. MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
  296. MODULE_DESCRIPTION("Technologic Systems TS-5500 platform driver");