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