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