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 22 #include <linux/delay.h> 23 #include <linux/io.h> 24 #include <linux/kernel.h> 25 #include <linux/leds.h> 26 #include <linux/init.h> 27 #include <linux/platform_data/max197.h> 28 #include <linux/platform_device.h> 29 #include <linux/slab.h> 30 31 /* Product code register */ 32 #define TS5500_PRODUCT_CODE_ADDR 0x74 33 #define TS5500_PRODUCT_CODE 0x60 /* TS-5500 product code */ 34 #define TS5400_PRODUCT_CODE 0x40 /* TS-5400 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 * @name: Board model name. 70 * @id: Board product ID. 71 * @sram: Flag for SRAM option. 72 * @rs485: Flag for RS-485 option. 73 * @adc: Flag for Analog/Digital converter option. 74 * @ereset: Flag for External Reset option. 75 * @itr: Flag for Industrial Temperature Range option. 76 * @jumpers: Bitfield for jumpers' state. 77 */ 78 struct ts5500_sbc { 79 const char *name; 80 int id; 81 bool sram; 82 bool rs485; 83 bool adc; 84 bool ereset; 85 bool itr; 86 u8 jumpers; 87 }; 88 89 /* Board signatures in BIOS shadow RAM */ 90 static const struct { 91 const char * const string; 92 const ssize_t offset; 93 } ts5500_signatures[] __initconst = { 94 { "TS-5x00 AMD Elan", 0xb14 }, 95 }; 96 97 static int __init ts5500_check_signature(void) 98 { 99 void __iomem *bios; 100 int i, ret = -ENODEV; 101 102 bios = ioremap(0xf0000, 0x10000); 103 if (!bios) 104 return -ENOMEM; 105 106 for (i = 0; i < ARRAY_SIZE(ts5500_signatures); i++) { 107 if (check_signature(bios + ts5500_signatures[i].offset, 108 ts5500_signatures[i].string, 109 strlen(ts5500_signatures[i].string))) { 110 ret = 0; 111 break; 112 } 113 } 114 115 iounmap(bios); 116 return ret; 117 } 118 119 static int __init ts5500_detect_config(struct ts5500_sbc *sbc) 120 { 121 u8 tmp; 122 int ret = 0; 123 124 if (!request_region(TS5500_PRODUCT_CODE_ADDR, 4, "ts5500")) 125 return -EBUSY; 126 127 sbc->id = inb(TS5500_PRODUCT_CODE_ADDR); 128 if (sbc->id == TS5500_PRODUCT_CODE) { 129 sbc->name = "TS-5500"; 130 } else if (sbc->id == TS5400_PRODUCT_CODE) { 131 sbc->name = "TS-5400"; 132 } else { 133 pr_err("ts5500: unknown product code 0x%x\n", sbc->id); 134 ret = -ENODEV; 135 goto cleanup; 136 } 137 138 tmp = inb(TS5500_SRAM_RS485_ADC_ADDR); 139 sbc->sram = tmp & TS5500_SRAM; 140 sbc->rs485 = tmp & TS5500_RS485; 141 sbc->adc = tmp & TS5500_ADC; 142 143 tmp = inb(TS5500_ERESET_ITR_ADDR); 144 sbc->ereset = tmp & TS5500_ERESET; 145 sbc->itr = tmp & TS5500_ITR; 146 147 tmp = inb(TS5500_LED_JP_ADDR); 148 sbc->jumpers = tmp & ~TS5500_LED; 149 150 cleanup: 151 release_region(TS5500_PRODUCT_CODE_ADDR, 4); 152 return ret; 153 } 154 155 static ssize_t name_show(struct device *dev, struct device_attribute *attr, 156 char *buf) 157 { 158 struct ts5500_sbc *sbc = dev_get_drvdata(dev); 159 160 return sprintf(buf, "%s\n", sbc->name); 161 } 162 static DEVICE_ATTR_RO(name); 163 164 static ssize_t id_show(struct device *dev, struct device_attribute *attr, 165 char *buf) 166 { 167 struct ts5500_sbc *sbc = dev_get_drvdata(dev); 168 169 return sprintf(buf, "0x%.2x\n", sbc->id); 170 } 171 static DEVICE_ATTR_RO(id); 172 173 static ssize_t jumpers_show(struct device *dev, struct device_attribute *attr, 174 char *buf) 175 { 176 struct ts5500_sbc *sbc = dev_get_drvdata(dev); 177 178 return sprintf(buf, "0x%.2x\n", sbc->jumpers >> 1); 179 } 180 static DEVICE_ATTR_RO(jumpers); 181 182 #define TS5500_ATTR_BOOL(_field) \ 183 static ssize_t _field##_show(struct device *dev, \ 184 struct device_attribute *attr, char *buf) \ 185 { \ 186 struct ts5500_sbc *sbc = dev_get_drvdata(dev); \ 187 \ 188 return sprintf(buf, "%d\n", sbc->_field); \ 189 } \ 190 static DEVICE_ATTR_RO(_field) 191 192 TS5500_ATTR_BOOL(sram); 193 TS5500_ATTR_BOOL(rs485); 194 TS5500_ATTR_BOOL(adc); 195 TS5500_ATTR_BOOL(ereset); 196 TS5500_ATTR_BOOL(itr); 197 198 static struct attribute *ts5500_attributes[] = { 199 &dev_attr_id.attr, 200 &dev_attr_name.attr, 201 &dev_attr_jumpers.attr, 202 &dev_attr_sram.attr, 203 &dev_attr_rs485.attr, 204 &dev_attr_adc.attr, 205 &dev_attr_ereset.attr, 206 &dev_attr_itr.attr, 207 NULL 208 }; 209 210 static const struct attribute_group ts5500_attr_group = { 211 .attrs = ts5500_attributes, 212 }; 213 214 static struct resource ts5500_dio1_resource[] = { 215 DEFINE_RES_IRQ_NAMED(7, "DIO1 interrupt"), 216 }; 217 218 static struct platform_device ts5500_dio1_pdev = { 219 .name = "ts5500-dio1", 220 .id = -1, 221 .resource = ts5500_dio1_resource, 222 .num_resources = 1, 223 }; 224 225 static struct resource ts5500_dio2_resource[] = { 226 DEFINE_RES_IRQ_NAMED(6, "DIO2 interrupt"), 227 }; 228 229 static struct platform_device ts5500_dio2_pdev = { 230 .name = "ts5500-dio2", 231 .id = -1, 232 .resource = ts5500_dio2_resource, 233 .num_resources = 1, 234 }; 235 236 static void ts5500_led_set(struct led_classdev *led_cdev, 237 enum led_brightness brightness) 238 { 239 outb(!!brightness, TS5500_LED_JP_ADDR); 240 } 241 242 static enum led_brightness ts5500_led_get(struct led_classdev *led_cdev) 243 { 244 return (inb(TS5500_LED_JP_ADDR) & TS5500_LED) ? LED_FULL : LED_OFF; 245 } 246 247 static struct led_classdev ts5500_led_cdev = { 248 .name = "ts5500:green:", 249 .brightness_set = ts5500_led_set, 250 .brightness_get = ts5500_led_get, 251 }; 252 253 static int ts5500_adc_convert(u8 ctrl) 254 { 255 u8 lsb, msb; 256 257 /* Start conversion (ensure the 3 MSB are set to 0) */ 258 outb(ctrl & 0x1f, TS5500_ADC_CONV_INIT_LSB_ADDR); 259 260 /* 261 * The platform has CPLD logic driving the A/D converter. 262 * The conversion must complete within 11 microseconds, 263 * otherwise we have to re-initiate a conversion. 264 */ 265 udelay(TS5500_ADC_CONV_DELAY); 266 if (inb(TS5500_ADC_CONV_BUSY_ADDR) & TS5500_ADC_CONV_BUSY) 267 return -EBUSY; 268 269 /* Read the raw data */ 270 lsb = inb(TS5500_ADC_CONV_INIT_LSB_ADDR); 271 msb = inb(TS5500_ADC_CONV_MSB_ADDR); 272 273 return (msb << 8) | lsb; 274 } 275 276 static struct max197_platform_data ts5500_adc_pdata = { 277 .convert = ts5500_adc_convert, 278 }; 279 280 static struct platform_device ts5500_adc_pdev = { 281 .name = "max197", 282 .id = -1, 283 .dev = { 284 .platform_data = &ts5500_adc_pdata, 285 }, 286 }; 287 288 static int __init ts5500_init(void) 289 { 290 struct platform_device *pdev; 291 struct ts5500_sbc *sbc; 292 int err; 293 294 /* 295 * There is no DMI available or PCI bridge subvendor info, 296 * only the BIOS provides a 16-bit identification call. 297 * It is safer to find a signature in the BIOS shadow RAM. 298 */ 299 err = ts5500_check_signature(); 300 if (err) 301 return err; 302 303 pdev = platform_device_register_simple("ts5500", -1, NULL, 0); 304 if (IS_ERR(pdev)) 305 return PTR_ERR(pdev); 306 307 sbc = devm_kzalloc(&pdev->dev, sizeof(struct ts5500_sbc), GFP_KERNEL); 308 if (!sbc) { 309 err = -ENOMEM; 310 goto error; 311 } 312 313 err = ts5500_detect_config(sbc); 314 if (err) 315 goto error; 316 317 platform_set_drvdata(pdev, sbc); 318 319 err = sysfs_create_group(&pdev->dev.kobj, &ts5500_attr_group); 320 if (err) 321 goto error; 322 323 if (sbc->id == TS5500_PRODUCT_CODE) { 324 ts5500_dio1_pdev.dev.parent = &pdev->dev; 325 if (platform_device_register(&ts5500_dio1_pdev)) 326 dev_warn(&pdev->dev, "DIO1 block registration failed\n"); 327 ts5500_dio2_pdev.dev.parent = &pdev->dev; 328 if (platform_device_register(&ts5500_dio2_pdev)) 329 dev_warn(&pdev->dev, "DIO2 block registration failed\n"); 330 } 331 332 if (led_classdev_register(&pdev->dev, &ts5500_led_cdev)) 333 dev_warn(&pdev->dev, "LED registration failed\n"); 334 335 if (sbc->adc) { 336 ts5500_adc_pdev.dev.parent = &pdev->dev; 337 if (platform_device_register(&ts5500_adc_pdev)) 338 dev_warn(&pdev->dev, "ADC registration failed\n"); 339 } 340 341 return 0; 342 error: 343 platform_device_unregister(pdev); 344 return err; 345 } 346 device_initcall(ts5500_init); 347