ts5500.c 8.8 KB

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