ts5500.c 9.1 KB

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