18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Technologic Systems TS-5500 Single Board Computer support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013-2014 Savoir-faire Linux Inc. 68c2ecf20Sopenharmony_ci * Vivien Didelot <vivien.didelot@savoirfairelinux.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This driver registers the Technologic Systems TS-5500 Single Board Computer 98c2ecf20Sopenharmony_ci * (SBC) and its devices, and exposes information to userspace such as jumpers' 108c2ecf20Sopenharmony_ci * state or available options. For further information about sysfs entries, see 118c2ecf20Sopenharmony_ci * Documentation/ABI/testing/sysfs-platform-ts5500. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This code may be extended to support similar x86-based platforms. 148c2ecf20Sopenharmony_ci * Actually, the TS-5500 and TS-5400 are supported. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/leds.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_data/max197.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Product code register */ 278c2ecf20Sopenharmony_ci#define TS5500_PRODUCT_CODE_ADDR 0x74 288c2ecf20Sopenharmony_ci#define TS5500_PRODUCT_CODE 0x60 /* TS-5500 product code */ 298c2ecf20Sopenharmony_ci#define TS5400_PRODUCT_CODE 0x40 /* TS-5400 product code */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* SRAM/RS-485/ADC options, and RS-485 RTS/Automatic RS-485 flags register */ 328c2ecf20Sopenharmony_ci#define TS5500_SRAM_RS485_ADC_ADDR 0x75 338c2ecf20Sopenharmony_ci#define TS5500_SRAM BIT(0) /* SRAM option */ 348c2ecf20Sopenharmony_ci#define TS5500_RS485 BIT(1) /* RS-485 option */ 358c2ecf20Sopenharmony_ci#define TS5500_ADC BIT(2) /* A/D converter option */ 368c2ecf20Sopenharmony_ci#define TS5500_RS485_RTS BIT(6) /* RTS for RS-485 */ 378c2ecf20Sopenharmony_ci#define TS5500_RS485_AUTO BIT(7) /* Automatic RS-485 */ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* External Reset/Industrial Temperature Range options register */ 408c2ecf20Sopenharmony_ci#define TS5500_ERESET_ITR_ADDR 0x76 418c2ecf20Sopenharmony_ci#define TS5500_ERESET BIT(0) /* External Reset option */ 428c2ecf20Sopenharmony_ci#define TS5500_ITR BIT(1) /* Indust. Temp. Range option */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* LED/Jumpers register */ 458c2ecf20Sopenharmony_ci#define TS5500_LED_JP_ADDR 0x77 468c2ecf20Sopenharmony_ci#define TS5500_LED BIT(0) /* LED flag */ 478c2ecf20Sopenharmony_ci#define TS5500_JP1 BIT(1) /* Automatic CMOS */ 488c2ecf20Sopenharmony_ci#define TS5500_JP2 BIT(2) /* Enable Serial Console */ 498c2ecf20Sopenharmony_ci#define TS5500_JP3 BIT(3) /* Write Enable Drive A */ 508c2ecf20Sopenharmony_ci#define TS5500_JP4 BIT(4) /* Fast Console (115K baud) */ 518c2ecf20Sopenharmony_ci#define TS5500_JP5 BIT(5) /* User Jumper */ 528c2ecf20Sopenharmony_ci#define TS5500_JP6 BIT(6) /* Console on COM1 (req. JP2) */ 538c2ecf20Sopenharmony_ci#define TS5500_JP7 BIT(7) /* Undocumented (Unused) */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* A/D Converter registers */ 568c2ecf20Sopenharmony_ci#define TS5500_ADC_CONV_BUSY_ADDR 0x195 /* Conversion state register */ 578c2ecf20Sopenharmony_ci#define TS5500_ADC_CONV_BUSY BIT(0) 588c2ecf20Sopenharmony_ci#define TS5500_ADC_CONV_INIT_LSB_ADDR 0x196 /* Start conv. / LSB register */ 598c2ecf20Sopenharmony_ci#define TS5500_ADC_CONV_MSB_ADDR 0x197 /* MSB register */ 608c2ecf20Sopenharmony_ci#define TS5500_ADC_CONV_DELAY 12 /* usec */ 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/** 638c2ecf20Sopenharmony_ci * struct ts5500_sbc - TS-5500 board description 648c2ecf20Sopenharmony_ci * @name: Board model name. 658c2ecf20Sopenharmony_ci * @id: Board product ID. 668c2ecf20Sopenharmony_ci * @sram: Flag for SRAM option. 678c2ecf20Sopenharmony_ci * @rs485: Flag for RS-485 option. 688c2ecf20Sopenharmony_ci * @adc: Flag for Analog/Digital converter option. 698c2ecf20Sopenharmony_ci * @ereset: Flag for External Reset option. 708c2ecf20Sopenharmony_ci * @itr: Flag for Industrial Temperature Range option. 718c2ecf20Sopenharmony_ci * @jumpers: Bitfield for jumpers' state. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_cistruct ts5500_sbc { 748c2ecf20Sopenharmony_ci const char *name; 758c2ecf20Sopenharmony_ci int id; 768c2ecf20Sopenharmony_ci bool sram; 778c2ecf20Sopenharmony_ci bool rs485; 788c2ecf20Sopenharmony_ci bool adc; 798c2ecf20Sopenharmony_ci bool ereset; 808c2ecf20Sopenharmony_ci bool itr; 818c2ecf20Sopenharmony_ci u8 jumpers; 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* Board signatures in BIOS shadow RAM */ 858c2ecf20Sopenharmony_cistatic const struct { 868c2ecf20Sopenharmony_ci const char * const string; 878c2ecf20Sopenharmony_ci const ssize_t offset; 888c2ecf20Sopenharmony_ci} ts5500_signatures[] __initconst = { 898c2ecf20Sopenharmony_ci { "TS-5x00 AMD Elan", 0xb14 }, 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int __init ts5500_check_signature(void) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci void __iomem *bios; 958c2ecf20Sopenharmony_ci int i, ret = -ENODEV; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci bios = ioremap(0xf0000, 0x10000); 988c2ecf20Sopenharmony_ci if (!bios) 998c2ecf20Sopenharmony_ci return -ENOMEM; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ts5500_signatures); i++) { 1028c2ecf20Sopenharmony_ci if (check_signature(bios + ts5500_signatures[i].offset, 1038c2ecf20Sopenharmony_ci ts5500_signatures[i].string, 1048c2ecf20Sopenharmony_ci strlen(ts5500_signatures[i].string))) { 1058c2ecf20Sopenharmony_ci ret = 0; 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci iounmap(bios); 1118c2ecf20Sopenharmony_ci return ret; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int __init ts5500_detect_config(struct ts5500_sbc *sbc) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci u8 tmp; 1178c2ecf20Sopenharmony_ci int ret = 0; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (!request_region(TS5500_PRODUCT_CODE_ADDR, 4, "ts5500")) 1208c2ecf20Sopenharmony_ci return -EBUSY; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci sbc->id = inb(TS5500_PRODUCT_CODE_ADDR); 1238c2ecf20Sopenharmony_ci if (sbc->id == TS5500_PRODUCT_CODE) { 1248c2ecf20Sopenharmony_ci sbc->name = "TS-5500"; 1258c2ecf20Sopenharmony_ci } else if (sbc->id == TS5400_PRODUCT_CODE) { 1268c2ecf20Sopenharmony_ci sbc->name = "TS-5400"; 1278c2ecf20Sopenharmony_ci } else { 1288c2ecf20Sopenharmony_ci pr_err("ts5500: unknown product code 0x%x\n", sbc->id); 1298c2ecf20Sopenharmony_ci ret = -ENODEV; 1308c2ecf20Sopenharmony_ci goto cleanup; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci tmp = inb(TS5500_SRAM_RS485_ADC_ADDR); 1348c2ecf20Sopenharmony_ci sbc->sram = tmp & TS5500_SRAM; 1358c2ecf20Sopenharmony_ci sbc->rs485 = tmp & TS5500_RS485; 1368c2ecf20Sopenharmony_ci sbc->adc = tmp & TS5500_ADC; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci tmp = inb(TS5500_ERESET_ITR_ADDR); 1398c2ecf20Sopenharmony_ci sbc->ereset = tmp & TS5500_ERESET; 1408c2ecf20Sopenharmony_ci sbc->itr = tmp & TS5500_ITR; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci tmp = inb(TS5500_LED_JP_ADDR); 1438c2ecf20Sopenharmony_ci sbc->jumpers = tmp & ~TS5500_LED; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cicleanup: 1468c2ecf20Sopenharmony_ci release_region(TS5500_PRODUCT_CODE_ADDR, 4); 1478c2ecf20Sopenharmony_ci return ret; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic ssize_t name_show(struct device *dev, struct device_attribute *attr, 1518c2ecf20Sopenharmony_ci char *buf) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct ts5500_sbc *sbc = dev_get_drvdata(dev); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", sbc->name); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic ssize_t id_show(struct device *dev, struct device_attribute *attr, 1608c2ecf20Sopenharmony_ci char *buf) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct ts5500_sbc *sbc = dev_get_drvdata(dev); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return sprintf(buf, "0x%.2x\n", sbc->id); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(id); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic ssize_t jumpers_show(struct device *dev, struct device_attribute *attr, 1698c2ecf20Sopenharmony_ci char *buf) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct ts5500_sbc *sbc = dev_get_drvdata(dev); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return sprintf(buf, "0x%.2x\n", sbc->jumpers >> 1); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(jumpers); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci#define TS5500_ATTR_BOOL(_field) \ 1788c2ecf20Sopenharmony_ci static ssize_t _field##_show(struct device *dev, \ 1798c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) \ 1808c2ecf20Sopenharmony_ci { \ 1818c2ecf20Sopenharmony_ci struct ts5500_sbc *sbc = dev_get_drvdata(dev); \ 1828c2ecf20Sopenharmony_ci \ 1838c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", sbc->_field); \ 1848c2ecf20Sopenharmony_ci } \ 1858c2ecf20Sopenharmony_ci static DEVICE_ATTR_RO(_field) 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ciTS5500_ATTR_BOOL(sram); 1888c2ecf20Sopenharmony_ciTS5500_ATTR_BOOL(rs485); 1898c2ecf20Sopenharmony_ciTS5500_ATTR_BOOL(adc); 1908c2ecf20Sopenharmony_ciTS5500_ATTR_BOOL(ereset); 1918c2ecf20Sopenharmony_ciTS5500_ATTR_BOOL(itr); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic struct attribute *ts5500_attributes[] = { 1948c2ecf20Sopenharmony_ci &dev_attr_id.attr, 1958c2ecf20Sopenharmony_ci &dev_attr_name.attr, 1968c2ecf20Sopenharmony_ci &dev_attr_jumpers.attr, 1978c2ecf20Sopenharmony_ci &dev_attr_sram.attr, 1988c2ecf20Sopenharmony_ci &dev_attr_rs485.attr, 1998c2ecf20Sopenharmony_ci &dev_attr_adc.attr, 2008c2ecf20Sopenharmony_ci &dev_attr_ereset.attr, 2018c2ecf20Sopenharmony_ci &dev_attr_itr.attr, 2028c2ecf20Sopenharmony_ci NULL 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic const struct attribute_group ts5500_attr_group = { 2068c2ecf20Sopenharmony_ci .attrs = ts5500_attributes, 2078c2ecf20Sopenharmony_ci}; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic struct resource ts5500_dio1_resource[] = { 2108c2ecf20Sopenharmony_ci DEFINE_RES_IRQ_NAMED(7, "DIO1 interrupt"), 2118c2ecf20Sopenharmony_ci}; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic struct platform_device ts5500_dio1_pdev = { 2148c2ecf20Sopenharmony_ci .name = "ts5500-dio1", 2158c2ecf20Sopenharmony_ci .id = -1, 2168c2ecf20Sopenharmony_ci .resource = ts5500_dio1_resource, 2178c2ecf20Sopenharmony_ci .num_resources = 1, 2188c2ecf20Sopenharmony_ci}; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic struct resource ts5500_dio2_resource[] = { 2218c2ecf20Sopenharmony_ci DEFINE_RES_IRQ_NAMED(6, "DIO2 interrupt"), 2228c2ecf20Sopenharmony_ci}; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic struct platform_device ts5500_dio2_pdev = { 2258c2ecf20Sopenharmony_ci .name = "ts5500-dio2", 2268c2ecf20Sopenharmony_ci .id = -1, 2278c2ecf20Sopenharmony_ci .resource = ts5500_dio2_resource, 2288c2ecf20Sopenharmony_ci .num_resources = 1, 2298c2ecf20Sopenharmony_ci}; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic void ts5500_led_set(struct led_classdev *led_cdev, 2328c2ecf20Sopenharmony_ci enum led_brightness brightness) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci outb(!!brightness, TS5500_LED_JP_ADDR); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic enum led_brightness ts5500_led_get(struct led_classdev *led_cdev) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci return (inb(TS5500_LED_JP_ADDR) & TS5500_LED) ? LED_FULL : LED_OFF; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic struct led_classdev ts5500_led_cdev = { 2438c2ecf20Sopenharmony_ci .name = "ts5500:green:", 2448c2ecf20Sopenharmony_ci .brightness_set = ts5500_led_set, 2458c2ecf20Sopenharmony_ci .brightness_get = ts5500_led_get, 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int ts5500_adc_convert(u8 ctrl) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci u8 lsb, msb; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* Start conversion (ensure the 3 MSB are set to 0) */ 2538c2ecf20Sopenharmony_ci outb(ctrl & 0x1f, TS5500_ADC_CONV_INIT_LSB_ADDR); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* 2568c2ecf20Sopenharmony_ci * The platform has CPLD logic driving the A/D converter. 2578c2ecf20Sopenharmony_ci * The conversion must complete within 11 microseconds, 2588c2ecf20Sopenharmony_ci * otherwise we have to re-initiate a conversion. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci udelay(TS5500_ADC_CONV_DELAY); 2618c2ecf20Sopenharmony_ci if (inb(TS5500_ADC_CONV_BUSY_ADDR) & TS5500_ADC_CONV_BUSY) 2628c2ecf20Sopenharmony_ci return -EBUSY; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Read the raw data */ 2658c2ecf20Sopenharmony_ci lsb = inb(TS5500_ADC_CONV_INIT_LSB_ADDR); 2668c2ecf20Sopenharmony_ci msb = inb(TS5500_ADC_CONV_MSB_ADDR); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return (msb << 8) | lsb; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic struct max197_platform_data ts5500_adc_pdata = { 2728c2ecf20Sopenharmony_ci .convert = ts5500_adc_convert, 2738c2ecf20Sopenharmony_ci}; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic struct platform_device ts5500_adc_pdev = { 2768c2ecf20Sopenharmony_ci .name = "max197", 2778c2ecf20Sopenharmony_ci .id = -1, 2788c2ecf20Sopenharmony_ci .dev = { 2798c2ecf20Sopenharmony_ci .platform_data = &ts5500_adc_pdata, 2808c2ecf20Sopenharmony_ci }, 2818c2ecf20Sopenharmony_ci}; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int __init ts5500_init(void) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct platform_device *pdev; 2868c2ecf20Sopenharmony_ci struct ts5500_sbc *sbc; 2878c2ecf20Sopenharmony_ci int err; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* 2908c2ecf20Sopenharmony_ci * There is no DMI available or PCI bridge subvendor info, 2918c2ecf20Sopenharmony_ci * only the BIOS provides a 16-bit identification call. 2928c2ecf20Sopenharmony_ci * It is safer to find a signature in the BIOS shadow RAM. 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_ci err = ts5500_check_signature(); 2958c2ecf20Sopenharmony_ci if (err) 2968c2ecf20Sopenharmony_ci return err; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci pdev = platform_device_register_simple("ts5500", -1, NULL, 0); 2998c2ecf20Sopenharmony_ci if (IS_ERR(pdev)) 3008c2ecf20Sopenharmony_ci return PTR_ERR(pdev); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci sbc = devm_kzalloc(&pdev->dev, sizeof(struct ts5500_sbc), GFP_KERNEL); 3038c2ecf20Sopenharmony_ci if (!sbc) { 3048c2ecf20Sopenharmony_ci err = -ENOMEM; 3058c2ecf20Sopenharmony_ci goto error; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci err = ts5500_detect_config(sbc); 3098c2ecf20Sopenharmony_ci if (err) 3108c2ecf20Sopenharmony_ci goto error; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, sbc); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci err = sysfs_create_group(&pdev->dev.kobj, &ts5500_attr_group); 3158c2ecf20Sopenharmony_ci if (err) 3168c2ecf20Sopenharmony_ci goto error; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (sbc->id == TS5500_PRODUCT_CODE) { 3198c2ecf20Sopenharmony_ci ts5500_dio1_pdev.dev.parent = &pdev->dev; 3208c2ecf20Sopenharmony_ci if (platform_device_register(&ts5500_dio1_pdev)) 3218c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "DIO1 block registration failed\n"); 3228c2ecf20Sopenharmony_ci ts5500_dio2_pdev.dev.parent = &pdev->dev; 3238c2ecf20Sopenharmony_ci if (platform_device_register(&ts5500_dio2_pdev)) 3248c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "DIO2 block registration failed\n"); 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (led_classdev_register(&pdev->dev, &ts5500_led_cdev)) 3288c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "LED registration failed\n"); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (sbc->adc) { 3318c2ecf20Sopenharmony_ci ts5500_adc_pdev.dev.parent = &pdev->dev; 3328c2ecf20Sopenharmony_ci if (platform_device_register(&ts5500_adc_pdev)) 3338c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "ADC registration failed\n"); 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return 0; 3378c2ecf20Sopenharmony_cierror: 3388c2ecf20Sopenharmony_ci platform_device_unregister(pdev); 3398c2ecf20Sopenharmony_ci return err; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_cidevice_initcall(ts5500_init); 342