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[] __initconst = { 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 id_show(struct device *dev, struct device_attribute *attr, 151 char *buf) 152 { 153 struct ts5500_sbc *sbc = dev_get_drvdata(dev); 154 155 return sprintf(buf, "0x%.2x\n", sbc->id); 156 } 157 static DEVICE_ATTR_RO(id); 158 159 static ssize_t jumpers_show(struct device *dev, 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 static DEVICE_ATTR_RO(jumpers); 167 168 #define TS5500_ATTR_BOOL(_field) \ 169 static ssize_t _field##_show(struct device *dev, \ 170 struct device_attribute *attr, char *buf) \ 171 { \ 172 struct ts5500_sbc *sbc = dev_get_drvdata(dev); \ 173 \ 174 return sprintf(buf, "%d\n", sbc->_field); \ 175 } \ 176 static DEVICE_ATTR_RO(_field) 177 178 TS5500_ATTR_BOOL(sram); 179 TS5500_ATTR_BOOL(rs485); 180 TS5500_ATTR_BOOL(adc); 181 TS5500_ATTR_BOOL(ereset); 182 TS5500_ATTR_BOOL(itr); 183 184 static struct attribute *ts5500_attributes[] = { 185 &dev_attr_id.attr, 186 &dev_attr_jumpers.attr, 187 &dev_attr_sram.attr, 188 &dev_attr_rs485.attr, 189 &dev_attr_adc.attr, 190 &dev_attr_ereset.attr, 191 &dev_attr_itr.attr, 192 NULL 193 }; 194 195 static const struct attribute_group ts5500_attr_group = { 196 .attrs = ts5500_attributes, 197 }; 198 199 static struct resource ts5500_dio1_resource[] = { 200 DEFINE_RES_IRQ_NAMED(7, "DIO1 interrupt"), 201 }; 202 203 static struct platform_device ts5500_dio1_pdev = { 204 .name = "ts5500-dio1", 205 .id = -1, 206 .resource = ts5500_dio1_resource, 207 .num_resources = 1, 208 }; 209 210 static struct resource ts5500_dio2_resource[] = { 211 DEFINE_RES_IRQ_NAMED(6, "DIO2 interrupt"), 212 }; 213 214 static struct platform_device ts5500_dio2_pdev = { 215 .name = "ts5500-dio2", 216 .id = -1, 217 .resource = ts5500_dio2_resource, 218 .num_resources = 1, 219 }; 220 221 static void ts5500_led_set(struct led_classdev *led_cdev, 222 enum led_brightness brightness) 223 { 224 outb(!!brightness, TS5500_LED_JP_ADDR); 225 } 226 227 static enum led_brightness ts5500_led_get(struct led_classdev *led_cdev) 228 { 229 return (inb(TS5500_LED_JP_ADDR) & TS5500_LED) ? LED_FULL : LED_OFF; 230 } 231 232 static struct led_classdev ts5500_led_cdev = { 233 .name = "ts5500:green:", 234 .brightness_set = ts5500_led_set, 235 .brightness_get = ts5500_led_get, 236 }; 237 238 static int ts5500_adc_convert(u8 ctrl) 239 { 240 u8 lsb, msb; 241 242 /* Start conversion (ensure the 3 MSB are set to 0) */ 243 outb(ctrl & 0x1f, TS5500_ADC_CONV_INIT_LSB_ADDR); 244 245 /* 246 * The platform has CPLD logic driving the A/D converter. 247 * The conversion must complete within 11 microseconds, 248 * otherwise we have to re-initiate a conversion. 249 */ 250 udelay(TS5500_ADC_CONV_DELAY); 251 if (inb(TS5500_ADC_CONV_BUSY_ADDR) & TS5500_ADC_CONV_BUSY) 252 return -EBUSY; 253 254 /* Read the raw data */ 255 lsb = inb(TS5500_ADC_CONV_INIT_LSB_ADDR); 256 msb = inb(TS5500_ADC_CONV_MSB_ADDR); 257 258 return (msb << 8) | lsb; 259 } 260 261 static struct max197_platform_data ts5500_adc_pdata = { 262 .convert = ts5500_adc_convert, 263 }; 264 265 static struct platform_device ts5500_adc_pdev = { 266 .name = "max197", 267 .id = -1, 268 .dev = { 269 .platform_data = &ts5500_adc_pdata, 270 }, 271 }; 272 273 static int __init ts5500_init(void) 274 { 275 struct platform_device *pdev; 276 struct ts5500_sbc *sbc; 277 int err; 278 279 /* 280 * There is no DMI available or PCI bridge subvendor info, 281 * only the BIOS provides a 16-bit identification call. 282 * It is safer to find a signature in the BIOS shadow RAM. 283 */ 284 err = ts5500_check_signature(); 285 if (err) 286 return err; 287 288 pdev = platform_device_register_simple("ts5500", -1, NULL, 0); 289 if (IS_ERR(pdev)) 290 return PTR_ERR(pdev); 291 292 sbc = devm_kzalloc(&pdev->dev, sizeof(struct ts5500_sbc), GFP_KERNEL); 293 if (!sbc) { 294 err = -ENOMEM; 295 goto error; 296 } 297 298 err = ts5500_detect_config(sbc); 299 if (err) 300 goto error; 301 302 platform_set_drvdata(pdev, sbc); 303 304 err = sysfs_create_group(&pdev->dev.kobj, &ts5500_attr_group); 305 if (err) 306 goto error; 307 308 ts5500_dio1_pdev.dev.parent = &pdev->dev; 309 if (platform_device_register(&ts5500_dio1_pdev)) 310 dev_warn(&pdev->dev, "DIO1 block registration failed\n"); 311 ts5500_dio2_pdev.dev.parent = &pdev->dev; 312 if (platform_device_register(&ts5500_dio2_pdev)) 313 dev_warn(&pdev->dev, "DIO2 block registration failed\n"); 314 315 if (led_classdev_register(&pdev->dev, &ts5500_led_cdev)) 316 dev_warn(&pdev->dev, "LED registration failed\n"); 317 318 if (sbc->adc) { 319 ts5500_adc_pdev.dev.parent = &pdev->dev; 320 if (platform_device_register(&ts5500_adc_pdev)) 321 dev_warn(&pdev->dev, "ADC registration failed\n"); 322 } 323 324 return 0; 325 error: 326 platform_device_unregister(pdev); 327 return err; 328 } 329 device_initcall(ts5500_init); 330 331 MODULE_LICENSE("GPL"); 332 MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>"); 333 MODULE_DESCRIPTION("Technologic Systems TS-5500 platform driver"); 334