18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * P2WI (Push-Pull Two Wire Interface) bus driver.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public License
78c2ecf20Sopenharmony_ci * version 2.  This program is licensed "as is" without any warranty of any
88c2ecf20Sopenharmony_ci * kind, whether express or implied.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * The P2WI controller looks like an SMBus controller which only supports byte
118c2ecf20Sopenharmony_ci * data transfers. But, it differs from standard SMBus protocol on several
128c2ecf20Sopenharmony_ci * aspects:
138c2ecf20Sopenharmony_ci * - it supports only one slave device, and thus drop the address field
148c2ecf20Sopenharmony_ci * - it adds a parity bit every 8bits of data
158c2ecf20Sopenharmony_ci * - only one read access is required to read a byte (instead of a write
168c2ecf20Sopenharmony_ci *   followed by a read access in standard SMBus protocol)
178c2ecf20Sopenharmony_ci * - there's no Ack bit after each byte transfer
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * This means this bus cannot be used to interface with standard SMBus
208c2ecf20Sopenharmony_ci * devices (the only known device to support this interface is the AXP221
218c2ecf20Sopenharmony_ci * PMIC).
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci#include <linux/clk.h>
258c2ecf20Sopenharmony_ci#include <linux/i2c.h>
268c2ecf20Sopenharmony_ci#include <linux/io.h>
278c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
288c2ecf20Sopenharmony_ci#include <linux/module.h>
298c2ecf20Sopenharmony_ci#include <linux/of.h>
308c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
318c2ecf20Sopenharmony_ci#include <linux/reset.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* P2WI registers */
358c2ecf20Sopenharmony_ci#define P2WI_CTRL		0x0
368c2ecf20Sopenharmony_ci#define P2WI_CCR		0x4
378c2ecf20Sopenharmony_ci#define P2WI_INTE		0x8
388c2ecf20Sopenharmony_ci#define P2WI_INTS		0xc
398c2ecf20Sopenharmony_ci#define P2WI_DADDR0		0x10
408c2ecf20Sopenharmony_ci#define P2WI_DADDR1		0x14
418c2ecf20Sopenharmony_ci#define P2WI_DLEN		0x18
428c2ecf20Sopenharmony_ci#define P2WI_DATA0		0x1c
438c2ecf20Sopenharmony_ci#define P2WI_DATA1		0x20
448c2ecf20Sopenharmony_ci#define P2WI_LCR		0x24
458c2ecf20Sopenharmony_ci#define P2WI_PMCR		0x28
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* CTRL fields */
488c2ecf20Sopenharmony_ci#define P2WI_CTRL_START_TRANS		BIT(7)
498c2ecf20Sopenharmony_ci#define P2WI_CTRL_ABORT_TRANS		BIT(6)
508c2ecf20Sopenharmony_ci#define P2WI_CTRL_GLOBAL_INT_ENB	BIT(1)
518c2ecf20Sopenharmony_ci#define P2WI_CTRL_SOFT_RST		BIT(0)
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* CLK CTRL fields */
548c2ecf20Sopenharmony_ci#define P2WI_CCR_SDA_OUT_DELAY(v)	(((v) & 0x7) << 8)
558c2ecf20Sopenharmony_ci#define P2WI_CCR_MAX_CLK_DIV		0xff
568c2ecf20Sopenharmony_ci#define P2WI_CCR_CLK_DIV(v)		((v) & P2WI_CCR_MAX_CLK_DIV)
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* STATUS fields */
598c2ecf20Sopenharmony_ci#define P2WI_INTS_TRANS_ERR_ID(v)	(((v) >> 8) & 0xff)
608c2ecf20Sopenharmony_ci#define P2WI_INTS_LOAD_BSY		BIT(2)
618c2ecf20Sopenharmony_ci#define P2WI_INTS_TRANS_ERR		BIT(1)
628c2ecf20Sopenharmony_ci#define P2WI_INTS_TRANS_OVER		BIT(0)
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* DATA LENGTH fields*/
658c2ecf20Sopenharmony_ci#define P2WI_DLEN_READ			BIT(4)
668c2ecf20Sopenharmony_ci#define P2WI_DLEN_DATA_LENGTH(v)	((v - 1) & 0x7)
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* LINE CTRL fields*/
698c2ecf20Sopenharmony_ci#define P2WI_LCR_SCL_STATE		BIT(5)
708c2ecf20Sopenharmony_ci#define P2WI_LCR_SDA_STATE		BIT(4)
718c2ecf20Sopenharmony_ci#define P2WI_LCR_SCL_CTL		BIT(3)
728c2ecf20Sopenharmony_ci#define P2WI_LCR_SCL_CTL_EN		BIT(2)
738c2ecf20Sopenharmony_ci#define P2WI_LCR_SDA_CTL		BIT(1)
748c2ecf20Sopenharmony_ci#define P2WI_LCR_SDA_CTL_EN		BIT(0)
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* PMU MODE CTRL fields */
778c2ecf20Sopenharmony_ci#define P2WI_PMCR_PMU_INIT_SEND		BIT(31)
788c2ecf20Sopenharmony_ci#define P2WI_PMCR_PMU_INIT_DATA(v)	(((v) & 0xff) << 16)
798c2ecf20Sopenharmony_ci#define P2WI_PMCR_PMU_MODE_REG(v)	(((v) & 0xff) << 8)
808c2ecf20Sopenharmony_ci#define P2WI_PMCR_PMU_DEV_ADDR(v)	((v) & 0xff)
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define P2WI_MAX_FREQ			6000000
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistruct p2wi {
858c2ecf20Sopenharmony_ci	struct i2c_adapter adapter;
868c2ecf20Sopenharmony_ci	struct completion complete;
878c2ecf20Sopenharmony_ci	unsigned int status;
888c2ecf20Sopenharmony_ci	void __iomem *regs;
898c2ecf20Sopenharmony_ci	struct clk *clk;
908c2ecf20Sopenharmony_ci	struct reset_control *rstc;
918c2ecf20Sopenharmony_ci	int slave_addr;
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic irqreturn_t p2wi_interrupt(int irq, void *dev_id)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	struct p2wi *p2wi = dev_id;
978c2ecf20Sopenharmony_ci	unsigned long status;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	status = readl(p2wi->regs + P2WI_INTS);
1008c2ecf20Sopenharmony_ci	p2wi->status = status;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* Clear interrupts */
1038c2ecf20Sopenharmony_ci	status &= (P2WI_INTS_LOAD_BSY | P2WI_INTS_TRANS_ERR |
1048c2ecf20Sopenharmony_ci		   P2WI_INTS_TRANS_OVER);
1058c2ecf20Sopenharmony_ci	writel(status, p2wi->regs + P2WI_INTS);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	complete(&p2wi->complete);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic u32 p2wi_functionality(struct i2c_adapter *adap)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	return I2C_FUNC_SMBUS_BYTE_DATA;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic int p2wi_smbus_xfer(struct i2c_adapter *adap, u16 addr,
1188c2ecf20Sopenharmony_ci			   unsigned short flags, char read_write,
1198c2ecf20Sopenharmony_ci			   u8 command, int size, union i2c_smbus_data *data)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct p2wi *p2wi = i2c_get_adapdata(adap);
1228c2ecf20Sopenharmony_ci	unsigned long dlen = P2WI_DLEN_DATA_LENGTH(1);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (p2wi->slave_addr >= 0 && addr != p2wi->slave_addr) {
1258c2ecf20Sopenharmony_ci		dev_err(&adap->dev, "invalid P2WI address\n");
1268c2ecf20Sopenharmony_ci		return -EINVAL;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (!data)
1308c2ecf20Sopenharmony_ci		return -EINVAL;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	writel(command, p2wi->regs + P2WI_DADDR0);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (read_write == I2C_SMBUS_READ)
1358c2ecf20Sopenharmony_ci		dlen |= P2WI_DLEN_READ;
1368c2ecf20Sopenharmony_ci	else
1378c2ecf20Sopenharmony_ci		writel(data->byte, p2wi->regs + P2WI_DATA0);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	writel(dlen, p2wi->regs + P2WI_DLEN);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (readl(p2wi->regs + P2WI_CTRL) & P2WI_CTRL_START_TRANS) {
1428c2ecf20Sopenharmony_ci		dev_err(&adap->dev, "P2WI bus busy\n");
1438c2ecf20Sopenharmony_ci		return -EBUSY;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	reinit_completion(&p2wi->complete);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	writel(P2WI_INTS_LOAD_BSY | P2WI_INTS_TRANS_ERR | P2WI_INTS_TRANS_OVER,
1498c2ecf20Sopenharmony_ci	       p2wi->regs + P2WI_INTE);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	writel(P2WI_CTRL_START_TRANS | P2WI_CTRL_GLOBAL_INT_ENB,
1528c2ecf20Sopenharmony_ci	       p2wi->regs + P2WI_CTRL);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	wait_for_completion(&p2wi->complete);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (p2wi->status & P2WI_INTS_LOAD_BSY) {
1578c2ecf20Sopenharmony_ci		dev_err(&adap->dev, "P2WI bus busy\n");
1588c2ecf20Sopenharmony_ci		return -EBUSY;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (p2wi->status & P2WI_INTS_TRANS_ERR) {
1628c2ecf20Sopenharmony_ci		dev_err(&adap->dev, "P2WI bus xfer error\n");
1638c2ecf20Sopenharmony_ci		return -ENXIO;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (read_write == I2C_SMBUS_READ)
1678c2ecf20Sopenharmony_ci		data->byte = readl(p2wi->regs + P2WI_DATA0);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return 0;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic const struct i2c_algorithm p2wi_algo = {
1738c2ecf20Sopenharmony_ci	.smbus_xfer = p2wi_smbus_xfer,
1748c2ecf20Sopenharmony_ci	.functionality = p2wi_functionality,
1758c2ecf20Sopenharmony_ci};
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic const struct of_device_id p2wi_of_match_table[] = {
1788c2ecf20Sopenharmony_ci	{ .compatible = "allwinner,sun6i-a31-p2wi" },
1798c2ecf20Sopenharmony_ci	{}
1808c2ecf20Sopenharmony_ci};
1818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, p2wi_of_match_table);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic int p2wi_probe(struct platform_device *pdev)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
1868c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
1878c2ecf20Sopenharmony_ci	struct device_node *childnp;
1888c2ecf20Sopenharmony_ci	unsigned long parent_clk_freq;
1898c2ecf20Sopenharmony_ci	u32 clk_freq = I2C_MAX_STANDARD_MODE_FREQ;
1908c2ecf20Sopenharmony_ci	struct p2wi *p2wi;
1918c2ecf20Sopenharmony_ci	u32 slave_addr;
1928c2ecf20Sopenharmony_ci	int clk_div;
1938c2ecf20Sopenharmony_ci	int irq;
1948c2ecf20Sopenharmony_ci	int ret;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	of_property_read_u32(np, "clock-frequency", &clk_freq);
1978c2ecf20Sopenharmony_ci	if (clk_freq > P2WI_MAX_FREQ) {
1988c2ecf20Sopenharmony_ci		dev_err(dev,
1998c2ecf20Sopenharmony_ci			"required clock-frequency (%u Hz) is too high (max = 6MHz)",
2008c2ecf20Sopenharmony_ci			clk_freq);
2018c2ecf20Sopenharmony_ci		return -EINVAL;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (clk_freq == 0) {
2058c2ecf20Sopenharmony_ci		dev_err(dev, "clock-frequency is set to 0 in DT\n");
2068c2ecf20Sopenharmony_ci		return -EINVAL;
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (of_get_child_count(np) > 1) {
2108c2ecf20Sopenharmony_ci		dev_err(dev, "P2WI only supports one slave device\n");
2118c2ecf20Sopenharmony_ci		return -EINVAL;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	p2wi = devm_kzalloc(dev, sizeof(struct p2wi), GFP_KERNEL);
2158c2ecf20Sopenharmony_ci	if (!p2wi)
2168c2ecf20Sopenharmony_ci		return -ENOMEM;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	p2wi->slave_addr = -1;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/*
2218c2ecf20Sopenharmony_ci	 * Authorize a p2wi node without any children to be able to use an
2228c2ecf20Sopenharmony_ci	 * i2c-dev from userpace.
2238c2ecf20Sopenharmony_ci	 * In this case the slave_addr is set to -1 and won't be checked when
2248c2ecf20Sopenharmony_ci	 * launching a P2WI transfer.
2258c2ecf20Sopenharmony_ci	 */
2268c2ecf20Sopenharmony_ci	childnp = of_get_next_available_child(np, NULL);
2278c2ecf20Sopenharmony_ci	if (childnp) {
2288c2ecf20Sopenharmony_ci		ret = of_property_read_u32(childnp, "reg", &slave_addr);
2298c2ecf20Sopenharmony_ci		if (ret) {
2308c2ecf20Sopenharmony_ci			dev_err(dev, "invalid slave address on node %pOF\n",
2318c2ecf20Sopenharmony_ci				childnp);
2328c2ecf20Sopenharmony_ci			return -EINVAL;
2338c2ecf20Sopenharmony_ci		}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		p2wi->slave_addr = slave_addr;
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	p2wi->regs = devm_platform_ioremap_resource(pdev, 0);
2398c2ecf20Sopenharmony_ci	if (IS_ERR(p2wi->regs))
2408c2ecf20Sopenharmony_ci		return PTR_ERR(p2wi->regs);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	strlcpy(p2wi->adapter.name, pdev->name, sizeof(p2wi->adapter.name));
2438c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
2448c2ecf20Sopenharmony_ci	if (irq < 0)
2458c2ecf20Sopenharmony_ci		return irq;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	p2wi->clk = devm_clk_get(dev, NULL);
2488c2ecf20Sopenharmony_ci	if (IS_ERR(p2wi->clk)) {
2498c2ecf20Sopenharmony_ci		ret = PTR_ERR(p2wi->clk);
2508c2ecf20Sopenharmony_ci		dev_err(dev, "failed to retrieve clk: %d\n", ret);
2518c2ecf20Sopenharmony_ci		return ret;
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(p2wi->clk);
2558c2ecf20Sopenharmony_ci	if (ret) {
2568c2ecf20Sopenharmony_ci		dev_err(dev, "failed to enable clk: %d\n", ret);
2578c2ecf20Sopenharmony_ci		return ret;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	parent_clk_freq = clk_get_rate(p2wi->clk);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	p2wi->rstc = devm_reset_control_get_exclusive(dev, NULL);
2638c2ecf20Sopenharmony_ci	if (IS_ERR(p2wi->rstc)) {
2648c2ecf20Sopenharmony_ci		ret = PTR_ERR(p2wi->rstc);
2658c2ecf20Sopenharmony_ci		dev_err(dev, "failed to retrieve reset controller: %d\n", ret);
2668c2ecf20Sopenharmony_ci		goto err_clk_disable;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	ret = reset_control_deassert(p2wi->rstc);
2708c2ecf20Sopenharmony_ci	if (ret) {
2718c2ecf20Sopenharmony_ci		dev_err(dev, "failed to deassert reset line: %d\n", ret);
2728c2ecf20Sopenharmony_ci		goto err_clk_disable;
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	init_completion(&p2wi->complete);
2768c2ecf20Sopenharmony_ci	p2wi->adapter.dev.parent = dev;
2778c2ecf20Sopenharmony_ci	p2wi->adapter.algo = &p2wi_algo;
2788c2ecf20Sopenharmony_ci	p2wi->adapter.owner = THIS_MODULE;
2798c2ecf20Sopenharmony_ci	p2wi->adapter.dev.of_node = pdev->dev.of_node;
2808c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, p2wi);
2818c2ecf20Sopenharmony_ci	i2c_set_adapdata(&p2wi->adapter, p2wi);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	ret = devm_request_irq(dev, irq, p2wi_interrupt, 0, pdev->name, p2wi);
2848c2ecf20Sopenharmony_ci	if (ret) {
2858c2ecf20Sopenharmony_ci		dev_err(dev, "can't register interrupt handler irq%d: %d\n",
2868c2ecf20Sopenharmony_ci			irq, ret);
2878c2ecf20Sopenharmony_ci		goto err_reset_assert;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	writel(P2WI_CTRL_SOFT_RST, p2wi->regs + P2WI_CTRL);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	clk_div = parent_clk_freq / clk_freq;
2938c2ecf20Sopenharmony_ci	if (!clk_div) {
2948c2ecf20Sopenharmony_ci		dev_warn(dev,
2958c2ecf20Sopenharmony_ci			 "clock-frequency is too high, setting it to %lu Hz\n",
2968c2ecf20Sopenharmony_ci			 parent_clk_freq);
2978c2ecf20Sopenharmony_ci		clk_div = 1;
2988c2ecf20Sopenharmony_ci	} else if (clk_div > P2WI_CCR_MAX_CLK_DIV) {
2998c2ecf20Sopenharmony_ci		dev_warn(dev,
3008c2ecf20Sopenharmony_ci			 "clock-frequency is too low, setting it to %lu Hz\n",
3018c2ecf20Sopenharmony_ci			 parent_clk_freq / P2WI_CCR_MAX_CLK_DIV);
3028c2ecf20Sopenharmony_ci		clk_div = P2WI_CCR_MAX_CLK_DIV;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	writel(P2WI_CCR_SDA_OUT_DELAY(1) | P2WI_CCR_CLK_DIV(clk_div),
3068c2ecf20Sopenharmony_ci	       p2wi->regs + P2WI_CCR);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	ret = i2c_add_adapter(&p2wi->adapter);
3098c2ecf20Sopenharmony_ci	if (!ret)
3108c2ecf20Sopenharmony_ci		return 0;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cierr_reset_assert:
3138c2ecf20Sopenharmony_ci	reset_control_assert(p2wi->rstc);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cierr_clk_disable:
3168c2ecf20Sopenharmony_ci	clk_disable_unprepare(p2wi->clk);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return ret;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic int p2wi_remove(struct platform_device *dev)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct p2wi *p2wi = platform_get_drvdata(dev);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	reset_control_assert(p2wi->rstc);
3268c2ecf20Sopenharmony_ci	clk_disable_unprepare(p2wi->clk);
3278c2ecf20Sopenharmony_ci	i2c_del_adapter(&p2wi->adapter);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	return 0;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic struct platform_driver p2wi_driver = {
3338c2ecf20Sopenharmony_ci	.probe	= p2wi_probe,
3348c2ecf20Sopenharmony_ci	.remove	= p2wi_remove,
3358c2ecf20Sopenharmony_ci	.driver	= {
3368c2ecf20Sopenharmony_ci		.name = "i2c-sunxi-p2wi",
3378c2ecf20Sopenharmony_ci		.of_match_table = p2wi_of_match_table,
3388c2ecf20Sopenharmony_ci	},
3398c2ecf20Sopenharmony_ci};
3408c2ecf20Sopenharmony_cimodule_platform_driver(p2wi_driver);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ciMODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>");
3438c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Allwinner P2WI driver");
3448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
345