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