18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson SA 2010
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson
68c2ecf20Sopenharmony_ci * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
118c2ecf20Sopenharmony_ci#include <linux/irq.h>
128c2ecf20Sopenharmony_ci#include <linux/irqdomain.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/i2c.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/of_device.h>
178c2ecf20Sopenharmony_ci#include <linux/mfd/core.h>
188c2ecf20Sopenharmony_ci#include <linux/mfd/tc3589x.h>
198c2ecf20Sopenharmony_ci#include <linux/err.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * enum tc3589x_version - indicates the TC3589x version
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_cienum tc3589x_version {
258c2ecf20Sopenharmony_ci	TC3589X_TC35890,
268c2ecf20Sopenharmony_ci	TC3589X_TC35892,
278c2ecf20Sopenharmony_ci	TC3589X_TC35893,
288c2ecf20Sopenharmony_ci	TC3589X_TC35894,
298c2ecf20Sopenharmony_ci	TC3589X_TC35895,
308c2ecf20Sopenharmony_ci	TC3589X_TC35896,
318c2ecf20Sopenharmony_ci	TC3589X_UNKNOWN,
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define TC3589x_CLKMODE_MODCTL_SLEEP		0x0
358c2ecf20Sopenharmony_ci#define TC3589x_CLKMODE_MODCTL_OPERATION	(1 << 0)
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/**
388c2ecf20Sopenharmony_ci * tc3589x_reg_read() - read a single TC3589x register
398c2ecf20Sopenharmony_ci * @tc3589x:	Device to read from
408c2ecf20Sopenharmony_ci * @reg:	Register to read
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_ciint tc3589x_reg_read(struct tc3589x *tc3589x, u8 reg)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	int ret;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_byte_data(tc3589x->i2c, reg);
478c2ecf20Sopenharmony_ci	if (ret < 0)
488c2ecf20Sopenharmony_ci		dev_err(tc3589x->dev, "failed to read reg %#x: %d\n",
498c2ecf20Sopenharmony_ci			reg, ret);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	return ret;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tc3589x_reg_read);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/**
568c2ecf20Sopenharmony_ci * tc3589x_reg_write() - write a single TC3589x register
578c2ecf20Sopenharmony_ci * @tc3589x:	Device to write to
588c2ecf20Sopenharmony_ci * @reg:	Register to read
598c2ecf20Sopenharmony_ci * @data:	Value to write
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_ciint tc3589x_reg_write(struct tc3589x *tc3589x, u8 reg, u8 data)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	int ret;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(tc3589x->i2c, reg, data);
668c2ecf20Sopenharmony_ci	if (ret < 0)
678c2ecf20Sopenharmony_ci		dev_err(tc3589x->dev, "failed to write reg %#x: %d\n",
688c2ecf20Sopenharmony_ci			reg, ret);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	return ret;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tc3589x_reg_write);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/**
758c2ecf20Sopenharmony_ci * tc3589x_block_read() - read multiple TC3589x registers
768c2ecf20Sopenharmony_ci * @tc3589x:	Device to read from
778c2ecf20Sopenharmony_ci * @reg:	First register
788c2ecf20Sopenharmony_ci * @length:	Number of registers
798c2ecf20Sopenharmony_ci * @values:	Buffer to write to
808c2ecf20Sopenharmony_ci */
818c2ecf20Sopenharmony_ciint tc3589x_block_read(struct tc3589x *tc3589x, u8 reg, u8 length, u8 *values)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	int ret;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_i2c_block_data(tc3589x->i2c, reg, length, values);
868c2ecf20Sopenharmony_ci	if (ret < 0)
878c2ecf20Sopenharmony_ci		dev_err(tc3589x->dev, "failed to read regs %#x: %d\n",
888c2ecf20Sopenharmony_ci			reg, ret);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	return ret;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tc3589x_block_read);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/**
958c2ecf20Sopenharmony_ci * tc3589x_block_write() - write multiple TC3589x registers
968c2ecf20Sopenharmony_ci * @tc3589x:	Device to write to
978c2ecf20Sopenharmony_ci * @reg:	First register
988c2ecf20Sopenharmony_ci * @length:	Number of registers
998c2ecf20Sopenharmony_ci * @values:	Values to write
1008c2ecf20Sopenharmony_ci */
1018c2ecf20Sopenharmony_ciint tc3589x_block_write(struct tc3589x *tc3589x, u8 reg, u8 length,
1028c2ecf20Sopenharmony_ci			const u8 *values)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	int ret;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_i2c_block_data(tc3589x->i2c, reg, length,
1078c2ecf20Sopenharmony_ci					     values);
1088c2ecf20Sopenharmony_ci	if (ret < 0)
1098c2ecf20Sopenharmony_ci		dev_err(tc3589x->dev, "failed to write regs %#x: %d\n",
1108c2ecf20Sopenharmony_ci			reg, ret);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return ret;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tc3589x_block_write);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/**
1178c2ecf20Sopenharmony_ci * tc3589x_set_bits() - set the value of a bitfield in a TC3589x register
1188c2ecf20Sopenharmony_ci * @tc3589x:	Device to write to
1198c2ecf20Sopenharmony_ci * @reg:	Register to write
1208c2ecf20Sopenharmony_ci * @mask:	Mask of bits to set
1218c2ecf20Sopenharmony_ci * @val:	Value to set
1228c2ecf20Sopenharmony_ci */
1238c2ecf20Sopenharmony_ciint tc3589x_set_bits(struct tc3589x *tc3589x, u8 reg, u8 mask, u8 val)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	int ret;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	mutex_lock(&tc3589x->lock);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	ret = tc3589x_reg_read(tc3589x, reg);
1308c2ecf20Sopenharmony_ci	if (ret < 0)
1318c2ecf20Sopenharmony_ci		goto out;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	ret &= ~mask;
1348c2ecf20Sopenharmony_ci	ret |= val;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	ret = tc3589x_reg_write(tc3589x, reg, ret);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ciout:
1398c2ecf20Sopenharmony_ci	mutex_unlock(&tc3589x->lock);
1408c2ecf20Sopenharmony_ci	return ret;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tc3589x_set_bits);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic struct resource gpio_resources[] = {
1458c2ecf20Sopenharmony_ci	{
1468c2ecf20Sopenharmony_ci		.start	= TC3589x_INT_GPIIRQ,
1478c2ecf20Sopenharmony_ci		.end	= TC3589x_INT_GPIIRQ,
1488c2ecf20Sopenharmony_ci		.flags	= IORESOURCE_IRQ,
1498c2ecf20Sopenharmony_ci	},
1508c2ecf20Sopenharmony_ci};
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic struct resource keypad_resources[] = {
1538c2ecf20Sopenharmony_ci	{
1548c2ecf20Sopenharmony_ci		.start  = TC3589x_INT_KBDIRQ,
1558c2ecf20Sopenharmony_ci		.end    = TC3589x_INT_KBDIRQ,
1568c2ecf20Sopenharmony_ci		.flags  = IORESOURCE_IRQ,
1578c2ecf20Sopenharmony_ci	},
1588c2ecf20Sopenharmony_ci};
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic const struct mfd_cell tc3589x_dev_gpio[] = {
1618c2ecf20Sopenharmony_ci	{
1628c2ecf20Sopenharmony_ci		.name		= "tc3589x-gpio",
1638c2ecf20Sopenharmony_ci		.num_resources	= ARRAY_SIZE(gpio_resources),
1648c2ecf20Sopenharmony_ci		.resources	= &gpio_resources[0],
1658c2ecf20Sopenharmony_ci		.of_compatible	= "toshiba,tc3589x-gpio",
1668c2ecf20Sopenharmony_ci	},
1678c2ecf20Sopenharmony_ci};
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic const struct mfd_cell tc3589x_dev_keypad[] = {
1708c2ecf20Sopenharmony_ci	{
1718c2ecf20Sopenharmony_ci		.name           = "tc3589x-keypad",
1728c2ecf20Sopenharmony_ci		.num_resources  = ARRAY_SIZE(keypad_resources),
1738c2ecf20Sopenharmony_ci		.resources      = &keypad_resources[0],
1748c2ecf20Sopenharmony_ci		.of_compatible	= "toshiba,tc3589x-keypad",
1758c2ecf20Sopenharmony_ci	},
1768c2ecf20Sopenharmony_ci};
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic irqreturn_t tc3589x_irq(int irq, void *data)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct tc3589x *tc3589x = data;
1818c2ecf20Sopenharmony_ci	int status;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ciagain:
1848c2ecf20Sopenharmony_ci	status = tc3589x_reg_read(tc3589x, TC3589x_IRQST);
1858c2ecf20Sopenharmony_ci	if (status < 0)
1868c2ecf20Sopenharmony_ci		return IRQ_NONE;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	while (status) {
1898c2ecf20Sopenharmony_ci		int bit = __ffs(status);
1908c2ecf20Sopenharmony_ci		int virq = irq_find_mapping(tc3589x->domain, bit);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		handle_nested_irq(virq);
1938c2ecf20Sopenharmony_ci		status &= ~(1 << bit);
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/*
1978c2ecf20Sopenharmony_ci	 * A dummy read or write (to any register) appears to be necessary to
1988c2ecf20Sopenharmony_ci	 * have the last interrupt clear (for example, GPIO IC write) take
1998c2ecf20Sopenharmony_ci	 * effect. In such a case, recheck for any interrupt which is still
2008c2ecf20Sopenharmony_ci	 * pending.
2018c2ecf20Sopenharmony_ci	 */
2028c2ecf20Sopenharmony_ci	status = tc3589x_reg_read(tc3589x, TC3589x_IRQST);
2038c2ecf20Sopenharmony_ci	if (status)
2048c2ecf20Sopenharmony_ci		goto again;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic int tc3589x_irq_map(struct irq_domain *d, unsigned int virq,
2108c2ecf20Sopenharmony_ci				irq_hw_number_t hwirq)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct tc3589x *tc3589x = d->host_data;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	irq_set_chip_data(virq, tc3589x);
2158c2ecf20Sopenharmony_ci	irq_set_chip_and_handler(virq, &dummy_irq_chip,
2168c2ecf20Sopenharmony_ci				handle_edge_irq);
2178c2ecf20Sopenharmony_ci	irq_set_nested_thread(virq, 1);
2188c2ecf20Sopenharmony_ci	irq_set_noprobe(virq);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	return 0;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic void tc3589x_irq_unmap(struct irq_domain *d, unsigned int virq)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	irq_set_chip_and_handler(virq, NULL, NULL);
2268c2ecf20Sopenharmony_ci	irq_set_chip_data(virq, NULL);
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic const struct irq_domain_ops tc3589x_irq_ops = {
2308c2ecf20Sopenharmony_ci	.map    = tc3589x_irq_map,
2318c2ecf20Sopenharmony_ci	.unmap  = tc3589x_irq_unmap,
2328c2ecf20Sopenharmony_ci	.xlate  = irq_domain_xlate_onecell,
2338c2ecf20Sopenharmony_ci};
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic int tc3589x_irq_init(struct tc3589x *tc3589x, struct device_node *np)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	tc3589x->domain = irq_domain_add_simple(
2388c2ecf20Sopenharmony_ci		np, TC3589x_NR_INTERNAL_IRQS, 0,
2398c2ecf20Sopenharmony_ci		&tc3589x_irq_ops, tc3589x);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	if (!tc3589x->domain) {
2428c2ecf20Sopenharmony_ci		dev_err(tc3589x->dev, "Failed to create irqdomain\n");
2438c2ecf20Sopenharmony_ci		return -ENOSYS;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	return 0;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic int tc3589x_chip_init(struct tc3589x *tc3589x)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	int manf, ver, ret;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	manf = tc3589x_reg_read(tc3589x, TC3589x_MANFCODE);
2548c2ecf20Sopenharmony_ci	if (manf < 0)
2558c2ecf20Sopenharmony_ci		return manf;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	ver = tc3589x_reg_read(tc3589x, TC3589x_VERSION);
2588c2ecf20Sopenharmony_ci	if (ver < 0)
2598c2ecf20Sopenharmony_ci		return ver;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (manf != TC3589x_MANFCODE_MAGIC) {
2628c2ecf20Sopenharmony_ci		dev_err(tc3589x->dev, "unknown manufacturer: %#x\n", manf);
2638c2ecf20Sopenharmony_ci		return -EINVAL;
2648c2ecf20Sopenharmony_ci	}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	dev_info(tc3589x->dev, "manufacturer: %#x, version: %#x\n", manf, ver);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	/*
2698c2ecf20Sopenharmony_ci	 * Put everything except the IRQ module into reset;
2708c2ecf20Sopenharmony_ci	 * also spare the GPIO module for any pin initialization
2718c2ecf20Sopenharmony_ci	 * done during pre-kernel boot
2728c2ecf20Sopenharmony_ci	 */
2738c2ecf20Sopenharmony_ci	ret = tc3589x_reg_write(tc3589x, TC3589x_RSTCTRL,
2748c2ecf20Sopenharmony_ci				TC3589x_RSTCTRL_TIMRST
2758c2ecf20Sopenharmony_ci				| TC3589x_RSTCTRL_ROTRST
2768c2ecf20Sopenharmony_ci				| TC3589x_RSTCTRL_KBDRST);
2778c2ecf20Sopenharmony_ci	if (ret < 0)
2788c2ecf20Sopenharmony_ci		return ret;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	/* Clear the reset interrupt. */
2818c2ecf20Sopenharmony_ci	return tc3589x_reg_write(tc3589x, TC3589x_RSTINTCLR, 0x1);
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic int tc3589x_device_init(struct tc3589x *tc3589x)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	int ret = 0;
2878c2ecf20Sopenharmony_ci	unsigned int blocks = tc3589x->pdata->block;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (blocks & TC3589x_BLOCK_GPIO) {
2908c2ecf20Sopenharmony_ci		ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_gpio,
2918c2ecf20Sopenharmony_ci				      ARRAY_SIZE(tc3589x_dev_gpio), NULL,
2928c2ecf20Sopenharmony_ci				      0, tc3589x->domain);
2938c2ecf20Sopenharmony_ci		if (ret) {
2948c2ecf20Sopenharmony_ci			dev_err(tc3589x->dev, "failed to add gpio child\n");
2958c2ecf20Sopenharmony_ci			return ret;
2968c2ecf20Sopenharmony_ci		}
2978c2ecf20Sopenharmony_ci		dev_info(tc3589x->dev, "added gpio block\n");
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (blocks & TC3589x_BLOCK_KEYPAD) {
3018c2ecf20Sopenharmony_ci		ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_keypad,
3028c2ecf20Sopenharmony_ci				      ARRAY_SIZE(tc3589x_dev_keypad), NULL,
3038c2ecf20Sopenharmony_ci				      0, tc3589x->domain);
3048c2ecf20Sopenharmony_ci		if (ret) {
3058c2ecf20Sopenharmony_ci			dev_err(tc3589x->dev, "failed to keypad child\n");
3068c2ecf20Sopenharmony_ci			return ret;
3078c2ecf20Sopenharmony_ci		}
3088c2ecf20Sopenharmony_ci		dev_info(tc3589x->dev, "added keypad block\n");
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	return ret;
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic const struct of_device_id tc3589x_match[] = {
3158c2ecf20Sopenharmony_ci	/* Legacy compatible string */
3168c2ecf20Sopenharmony_ci	{ .compatible = "tc3589x", .data = (void *) TC3589X_UNKNOWN },
3178c2ecf20Sopenharmony_ci	{ .compatible = "toshiba,tc35890", .data = (void *) TC3589X_TC35890 },
3188c2ecf20Sopenharmony_ci	{ .compatible = "toshiba,tc35892", .data = (void *) TC3589X_TC35892 },
3198c2ecf20Sopenharmony_ci	{ .compatible = "toshiba,tc35893", .data = (void *) TC3589X_TC35893 },
3208c2ecf20Sopenharmony_ci	{ .compatible = "toshiba,tc35894", .data = (void *) TC3589X_TC35894 },
3218c2ecf20Sopenharmony_ci	{ .compatible = "toshiba,tc35895", .data = (void *) TC3589X_TC35895 },
3228c2ecf20Sopenharmony_ci	{ .compatible = "toshiba,tc35896", .data = (void *) TC3589X_TC35896 },
3238c2ecf20Sopenharmony_ci	{ }
3248c2ecf20Sopenharmony_ci};
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tc3589x_match);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic struct tc3589x_platform_data *
3298c2ecf20Sopenharmony_citc3589x_of_probe(struct device *dev, enum tc3589x_version *version)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
3328c2ecf20Sopenharmony_ci	struct tc3589x_platform_data *pdata;
3338c2ecf20Sopenharmony_ci	struct device_node *child;
3348c2ecf20Sopenharmony_ci	const struct of_device_id *of_id;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
3378c2ecf20Sopenharmony_ci	if (!pdata)
3388c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	of_id = of_match_device(tc3589x_match, dev);
3418c2ecf20Sopenharmony_ci	if (!of_id)
3428c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
3438c2ecf20Sopenharmony_ci	*version = (enum tc3589x_version) of_id->data;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	for_each_child_of_node(np, child) {
3468c2ecf20Sopenharmony_ci		if (of_device_is_compatible(child, "toshiba,tc3589x-gpio"))
3478c2ecf20Sopenharmony_ci			pdata->block |= TC3589x_BLOCK_GPIO;
3488c2ecf20Sopenharmony_ci		if (of_device_is_compatible(child, "toshiba,tc3589x-keypad"))
3498c2ecf20Sopenharmony_ci			pdata->block |= TC3589x_BLOCK_KEYPAD;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	return pdata;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic int tc3589x_probe(struct i2c_client *i2c,
3568c2ecf20Sopenharmony_ci				   const struct i2c_device_id *id)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	struct device_node *np = i2c->dev.of_node;
3598c2ecf20Sopenharmony_ci	struct tc3589x_platform_data *pdata = dev_get_platdata(&i2c->dev);
3608c2ecf20Sopenharmony_ci	struct tc3589x *tc3589x;
3618c2ecf20Sopenharmony_ci	enum tc3589x_version version;
3628c2ecf20Sopenharmony_ci	int ret;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (!pdata) {
3658c2ecf20Sopenharmony_ci		pdata = tc3589x_of_probe(&i2c->dev, &version);
3668c2ecf20Sopenharmony_ci		if (IS_ERR(pdata)) {
3678c2ecf20Sopenharmony_ci			dev_err(&i2c->dev, "No platform data or DT found\n");
3688c2ecf20Sopenharmony_ci			return PTR_ERR(pdata);
3698c2ecf20Sopenharmony_ci		}
3708c2ecf20Sopenharmony_ci	} else {
3718c2ecf20Sopenharmony_ci		/* When not probing from device tree we have this ID */
3728c2ecf20Sopenharmony_ci		version = id->driver_data;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA
3768c2ecf20Sopenharmony_ci				     | I2C_FUNC_SMBUS_I2C_BLOCK))
3778c2ecf20Sopenharmony_ci		return -EIO;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	tc3589x = devm_kzalloc(&i2c->dev, sizeof(struct tc3589x),
3808c2ecf20Sopenharmony_ci				GFP_KERNEL);
3818c2ecf20Sopenharmony_ci	if (!tc3589x)
3828c2ecf20Sopenharmony_ci		return -ENOMEM;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	mutex_init(&tc3589x->lock);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	tc3589x->dev = &i2c->dev;
3878c2ecf20Sopenharmony_ci	tc3589x->i2c = i2c;
3888c2ecf20Sopenharmony_ci	tc3589x->pdata = pdata;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	switch (version) {
3918c2ecf20Sopenharmony_ci	case TC3589X_TC35893:
3928c2ecf20Sopenharmony_ci	case TC3589X_TC35895:
3938c2ecf20Sopenharmony_ci	case TC3589X_TC35896:
3948c2ecf20Sopenharmony_ci		tc3589x->num_gpio = 20;
3958c2ecf20Sopenharmony_ci		break;
3968c2ecf20Sopenharmony_ci	case TC3589X_TC35890:
3978c2ecf20Sopenharmony_ci	case TC3589X_TC35892:
3988c2ecf20Sopenharmony_ci	case TC3589X_TC35894:
3998c2ecf20Sopenharmony_ci	case TC3589X_UNKNOWN:
4008c2ecf20Sopenharmony_ci	default:
4018c2ecf20Sopenharmony_ci		tc3589x->num_gpio = 24;
4028c2ecf20Sopenharmony_ci		break;
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	i2c_set_clientdata(i2c, tc3589x);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	ret = tc3589x_chip_init(tc3589x);
4088c2ecf20Sopenharmony_ci	if (ret)
4098c2ecf20Sopenharmony_ci		return ret;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	ret = tc3589x_irq_init(tc3589x, np);
4128c2ecf20Sopenharmony_ci	if (ret)
4138c2ecf20Sopenharmony_ci		return ret;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	ret = request_threaded_irq(tc3589x->i2c->irq, NULL, tc3589x_irq,
4168c2ecf20Sopenharmony_ci				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
4178c2ecf20Sopenharmony_ci				   "tc3589x", tc3589x);
4188c2ecf20Sopenharmony_ci	if (ret) {
4198c2ecf20Sopenharmony_ci		dev_err(tc3589x->dev, "failed to request IRQ: %d\n", ret);
4208c2ecf20Sopenharmony_ci		return ret;
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	ret = tc3589x_device_init(tc3589x);
4248c2ecf20Sopenharmony_ci	if (ret) {
4258c2ecf20Sopenharmony_ci		dev_err(tc3589x->dev, "failed to add child devices\n");
4268c2ecf20Sopenharmony_ci		return ret;
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	return 0;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic int tc3589x_remove(struct i2c_client *client)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	struct tc3589x *tc3589x = i2c_get_clientdata(client);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	mfd_remove_devices(tc3589x->dev);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	return 0;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
4428c2ecf20Sopenharmony_cistatic int tc3589x_suspend(struct device *dev)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct tc3589x *tc3589x = dev_get_drvdata(dev);
4458c2ecf20Sopenharmony_ci	struct i2c_client *client = tc3589x->i2c;
4468c2ecf20Sopenharmony_ci	int ret = 0;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	/* put the system to sleep mode */
4498c2ecf20Sopenharmony_ci	if (!device_may_wakeup(&client->dev))
4508c2ecf20Sopenharmony_ci		ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE,
4518c2ecf20Sopenharmony_ci				TC3589x_CLKMODE_MODCTL_SLEEP);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	return ret;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic int tc3589x_resume(struct device *dev)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	struct tc3589x *tc3589x = dev_get_drvdata(dev);
4598c2ecf20Sopenharmony_ci	struct i2c_client *client = tc3589x->i2c;
4608c2ecf20Sopenharmony_ci	int ret = 0;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	/* enable the system into operation */
4638c2ecf20Sopenharmony_ci	if (!device_may_wakeup(&client->dev))
4648c2ecf20Sopenharmony_ci		ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE,
4658c2ecf20Sopenharmony_ci				TC3589x_CLKMODE_MODCTL_OPERATION);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	return ret;
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci#endif
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, tc3589x_resume);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cistatic const struct i2c_device_id tc3589x_id[] = {
4748c2ecf20Sopenharmony_ci	{ "tc35890", TC3589X_TC35890 },
4758c2ecf20Sopenharmony_ci	{ "tc35892", TC3589X_TC35892 },
4768c2ecf20Sopenharmony_ci	{ "tc35893", TC3589X_TC35893 },
4778c2ecf20Sopenharmony_ci	{ "tc35894", TC3589X_TC35894 },
4788c2ecf20Sopenharmony_ci	{ "tc35895", TC3589X_TC35895 },
4798c2ecf20Sopenharmony_ci	{ "tc35896", TC3589X_TC35896 },
4808c2ecf20Sopenharmony_ci	{ "tc3589x", TC3589X_UNKNOWN },
4818c2ecf20Sopenharmony_ci	{ }
4828c2ecf20Sopenharmony_ci};
4838c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tc3589x_id);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cistatic struct i2c_driver tc3589x_driver = {
4868c2ecf20Sopenharmony_ci	.driver = {
4878c2ecf20Sopenharmony_ci		.name	= "tc3589x",
4888c2ecf20Sopenharmony_ci		.pm	= &tc3589x_dev_pm_ops,
4898c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(tc3589x_match),
4908c2ecf20Sopenharmony_ci	},
4918c2ecf20Sopenharmony_ci	.probe		= tc3589x_probe,
4928c2ecf20Sopenharmony_ci	.remove		= tc3589x_remove,
4938c2ecf20Sopenharmony_ci	.id_table	= tc3589x_id,
4948c2ecf20Sopenharmony_ci};
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cistatic int __init tc3589x_init(void)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	return i2c_add_driver(&tc3589x_driver);
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_cisubsys_initcall(tc3589x_init);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic void __exit tc3589x_exit(void)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	i2c_del_driver(&tc3589x_driver);
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_cimodule_exit(tc3589x_exit);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
5098c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TC3589x MFD core driver");
5108c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
511