162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/delay.h>
462306a36Sopenharmony_ci#include <linux/err.h>
562306a36Sopenharmony_ci#include <linux/interrupt.h>
662306a36Sopenharmony_ci#include <linux/io.h>
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/of.h>
1062306a36Sopenharmony_ci#include <linux/platform_device.h>
1162306a36Sopenharmony_ci#include <linux/seq_file.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/spmi.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/*
1662306a36Sopenharmony_ci * SPMI register addr
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci#define SPMI_CHANNEL_OFFSET				0x0300
1962306a36Sopenharmony_ci#define SPMI_SLAVE_OFFSET				0x20
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define SPMI_APB_SPMI_CMD_BASE_ADDR			0x0100
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define SPMI_APB_SPMI_WDATA0_BASE_ADDR			0x0104
2462306a36Sopenharmony_ci#define SPMI_APB_SPMI_WDATA1_BASE_ADDR			0x0108
2562306a36Sopenharmony_ci#define SPMI_APB_SPMI_WDATA2_BASE_ADDR			0x010c
2662306a36Sopenharmony_ci#define SPMI_APB_SPMI_WDATA3_BASE_ADDR			0x0110
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define SPMI_APB_SPMI_STATUS_BASE_ADDR			0x0200
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define SPMI_APB_SPMI_RDATA0_BASE_ADDR			0x0204
3162306a36Sopenharmony_ci#define SPMI_APB_SPMI_RDATA1_BASE_ADDR			0x0208
3262306a36Sopenharmony_ci#define SPMI_APB_SPMI_RDATA2_BASE_ADDR			0x020c
3362306a36Sopenharmony_ci#define SPMI_APB_SPMI_RDATA3_BASE_ADDR			0x0210
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define SPMI_PER_DATAREG_BYTE				4
3662306a36Sopenharmony_ci/*
3762306a36Sopenharmony_ci * SPMI cmd register
3862306a36Sopenharmony_ci */
3962306a36Sopenharmony_ci#define SPMI_APB_SPMI_CMD_EN				BIT(31)
4062306a36Sopenharmony_ci#define SPMI_APB_SPMI_CMD_TYPE_OFFSET			24
4162306a36Sopenharmony_ci#define SPMI_APB_SPMI_CMD_LENGTH_OFFSET			20
4262306a36Sopenharmony_ci#define SPMI_APB_SPMI_CMD_SLAVEID_OFFSET		16
4362306a36Sopenharmony_ci#define SPMI_APB_SPMI_CMD_ADDR_OFFSET			0
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* Command Opcodes */
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cienum spmi_controller_cmd_op_code {
4862306a36Sopenharmony_ci	SPMI_CMD_REG_ZERO_WRITE = 0,
4962306a36Sopenharmony_ci	SPMI_CMD_REG_WRITE = 1,
5062306a36Sopenharmony_ci	SPMI_CMD_REG_READ = 2,
5162306a36Sopenharmony_ci	SPMI_CMD_EXT_REG_WRITE = 3,
5262306a36Sopenharmony_ci	SPMI_CMD_EXT_REG_READ = 4,
5362306a36Sopenharmony_ci	SPMI_CMD_EXT_REG_WRITE_L = 5,
5462306a36Sopenharmony_ci	SPMI_CMD_EXT_REG_READ_L = 6,
5562306a36Sopenharmony_ci	SPMI_CMD_REG_RESET = 7,
5662306a36Sopenharmony_ci	SPMI_CMD_REG_SLEEP = 8,
5762306a36Sopenharmony_ci	SPMI_CMD_REG_SHUTDOWN = 9,
5862306a36Sopenharmony_ci	SPMI_CMD_REG_WAKEUP = 10,
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/*
6262306a36Sopenharmony_ci * SPMI status register
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_ci#define SPMI_APB_TRANS_DONE			BIT(0)
6562306a36Sopenharmony_ci#define SPMI_APB_TRANS_FAIL			BIT(2)
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/* Command register fields */
6862306a36Sopenharmony_ci#define SPMI_CONTROLLER_CMD_MAX_BYTE_COUNT	16
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* Maximum number of support PMIC peripherals */
7162306a36Sopenharmony_ci#define SPMI_CONTROLLER_TIMEOUT_US		1000
7262306a36Sopenharmony_ci#define SPMI_CONTROLLER_MAX_TRANS_BYTES		16
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistruct spmi_controller_dev {
7562306a36Sopenharmony_ci	struct spmi_controller	*controller;
7662306a36Sopenharmony_ci	struct device		*dev;
7762306a36Sopenharmony_ci	void __iomem		*base;
7862306a36Sopenharmony_ci	spinlock_t		lock;
7962306a36Sopenharmony_ci	u32			channel;
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic int spmi_controller_wait_for_done(struct device *dev,
8362306a36Sopenharmony_ci					 struct spmi_controller_dev *ctrl_dev,
8462306a36Sopenharmony_ci					 void __iomem *base, u8 sid, u16 addr)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	u32 timeout = SPMI_CONTROLLER_TIMEOUT_US;
8762306a36Sopenharmony_ci	u32 status, offset;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	offset  = SPMI_APB_SPMI_STATUS_BASE_ADDR;
9062306a36Sopenharmony_ci	offset += SPMI_CHANNEL_OFFSET * ctrl_dev->channel + SPMI_SLAVE_OFFSET * sid;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	do {
9362306a36Sopenharmony_ci		status = readl(base + offset);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		if (status & SPMI_APB_TRANS_DONE) {
9662306a36Sopenharmony_ci			if (status & SPMI_APB_TRANS_FAIL) {
9762306a36Sopenharmony_ci				dev_err(dev, "%s: transaction failed (0x%x)\n",
9862306a36Sopenharmony_ci					__func__, status);
9962306a36Sopenharmony_ci				return -EIO;
10062306a36Sopenharmony_ci			}
10162306a36Sopenharmony_ci			dev_dbg(dev, "%s: status 0x%x\n", __func__, status);
10262306a36Sopenharmony_ci			return 0;
10362306a36Sopenharmony_ci		}
10462306a36Sopenharmony_ci		udelay(1);
10562306a36Sopenharmony_ci	} while (timeout--);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	dev_err(dev, "%s: timeout, status 0x%x\n", __func__, status);
10862306a36Sopenharmony_ci	return -ETIMEDOUT;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic int spmi_read_cmd(struct spmi_controller *ctrl,
11262306a36Sopenharmony_ci			 u8 opc, u8 slave_id, u16 slave_addr, u8 *__buf, size_t bc)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev);
11562306a36Sopenharmony_ci	u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel;
11662306a36Sopenharmony_ci	unsigned long flags;
11762306a36Sopenharmony_ci	u8 *buf = __buf;
11862306a36Sopenharmony_ci	u32 cmd, data;
11962306a36Sopenharmony_ci	int rc;
12062306a36Sopenharmony_ci	u8 op_code, i;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) {
12362306a36Sopenharmony_ci		dev_err(&ctrl->dev,
12462306a36Sopenharmony_ci			"spmi_controller supports 1..%d bytes per trans, but:%zu requested\n",
12562306a36Sopenharmony_ci			SPMI_CONTROLLER_MAX_TRANS_BYTES, bc);
12662306a36Sopenharmony_ci		return  -EINVAL;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	switch (opc) {
13062306a36Sopenharmony_ci	case SPMI_CMD_READ:
13162306a36Sopenharmony_ci		op_code = SPMI_CMD_REG_READ;
13262306a36Sopenharmony_ci		break;
13362306a36Sopenharmony_ci	case SPMI_CMD_EXT_READ:
13462306a36Sopenharmony_ci		op_code = SPMI_CMD_EXT_REG_READ;
13562306a36Sopenharmony_ci		break;
13662306a36Sopenharmony_ci	case SPMI_CMD_EXT_READL:
13762306a36Sopenharmony_ci		op_code = SPMI_CMD_EXT_REG_READ_L;
13862306a36Sopenharmony_ci		break;
13962306a36Sopenharmony_ci	default:
14062306a36Sopenharmony_ci		dev_err(&ctrl->dev, "invalid read cmd 0x%x\n", opc);
14162306a36Sopenharmony_ci		return -EINVAL;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	cmd = SPMI_APB_SPMI_CMD_EN |
14562306a36Sopenharmony_ci	     (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) |
14662306a36Sopenharmony_ci	     ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) |
14762306a36Sopenharmony_ci	     ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) |  /* slvid */
14862306a36Sopenharmony_ci	     ((slave_addr & 0xffff)  << SPMI_APB_SPMI_CMD_ADDR_OFFSET); /* slave_addr */
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	spin_lock_irqsave(&spmi_controller->lock, flags);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller,
15562306a36Sopenharmony_ci					   spmi_controller->base, slave_id, slave_addr);
15662306a36Sopenharmony_ci	if (rc)
15762306a36Sopenharmony_ci		goto done;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) {
16062306a36Sopenharmony_ci		data = readl(spmi_controller->base + chnl_ofst +
16162306a36Sopenharmony_ci			     SPMI_SLAVE_OFFSET * slave_id +
16262306a36Sopenharmony_ci			     SPMI_APB_SPMI_RDATA0_BASE_ADDR +
16362306a36Sopenharmony_ci			     i * SPMI_PER_DATAREG_BYTE);
16462306a36Sopenharmony_ci		data = be32_to_cpu((__be32 __force)data);
16562306a36Sopenharmony_ci		if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) {
16662306a36Sopenharmony_ci			memcpy(buf, &data, sizeof(data));
16762306a36Sopenharmony_ci			buf += sizeof(data);
16862306a36Sopenharmony_ci		} else {
16962306a36Sopenharmony_ci			memcpy(buf, &data, bc % SPMI_PER_DATAREG_BYTE);
17062306a36Sopenharmony_ci			buf += (bc % SPMI_PER_DATAREG_BYTE);
17162306a36Sopenharmony_ci		}
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cidone:
17562306a36Sopenharmony_ci	spin_unlock_irqrestore(&spmi_controller->lock, flags);
17662306a36Sopenharmony_ci	if (rc)
17762306a36Sopenharmony_ci		dev_err(&ctrl->dev,
17862306a36Sopenharmony_ci			"spmi read wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n",
17962306a36Sopenharmony_ci			opc, slave_id, slave_addr, bc + 1);
18062306a36Sopenharmony_ci	else
18162306a36Sopenharmony_ci		dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, read value: %*ph\n",
18262306a36Sopenharmony_ci			__func__, slave_id, slave_addr, (int)bc, __buf);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	return rc;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic int spmi_write_cmd(struct spmi_controller *ctrl,
18862306a36Sopenharmony_ci			  u8 opc, u8 slave_id, u16 slave_addr, const u8 *__buf, size_t bc)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev);
19162306a36Sopenharmony_ci	u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel;
19262306a36Sopenharmony_ci	const u8 *buf = __buf;
19362306a36Sopenharmony_ci	unsigned long flags;
19462306a36Sopenharmony_ci	u32 cmd, data;
19562306a36Sopenharmony_ci	int rc;
19662306a36Sopenharmony_ci	u8 op_code, i;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) {
19962306a36Sopenharmony_ci		dev_err(&ctrl->dev,
20062306a36Sopenharmony_ci			"spmi_controller supports 1..%d bytes per trans, but:%zu requested\n",
20162306a36Sopenharmony_ci			SPMI_CONTROLLER_MAX_TRANS_BYTES, bc);
20262306a36Sopenharmony_ci		return  -EINVAL;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	switch (opc) {
20662306a36Sopenharmony_ci	case SPMI_CMD_WRITE:
20762306a36Sopenharmony_ci		op_code = SPMI_CMD_REG_WRITE;
20862306a36Sopenharmony_ci		break;
20962306a36Sopenharmony_ci	case SPMI_CMD_EXT_WRITE:
21062306a36Sopenharmony_ci		op_code = SPMI_CMD_EXT_REG_WRITE;
21162306a36Sopenharmony_ci		break;
21262306a36Sopenharmony_ci	case SPMI_CMD_EXT_WRITEL:
21362306a36Sopenharmony_ci		op_code = SPMI_CMD_EXT_REG_WRITE_L;
21462306a36Sopenharmony_ci		break;
21562306a36Sopenharmony_ci	default:
21662306a36Sopenharmony_ci		dev_err(&ctrl->dev, "invalid write cmd 0x%x\n", opc);
21762306a36Sopenharmony_ci		return -EINVAL;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	cmd = SPMI_APB_SPMI_CMD_EN |
22162306a36Sopenharmony_ci	      (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) |
22262306a36Sopenharmony_ci	      ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) |
22362306a36Sopenharmony_ci	      ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) |
22462306a36Sopenharmony_ci	      ((slave_addr & 0xffff)  << SPMI_APB_SPMI_CMD_ADDR_OFFSET);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Write data to FIFOs */
22762306a36Sopenharmony_ci	spin_lock_irqsave(&spmi_controller->lock, flags);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) {
23062306a36Sopenharmony_ci		data = 0;
23162306a36Sopenharmony_ci		if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) {
23262306a36Sopenharmony_ci			memcpy(&data, buf, sizeof(data));
23362306a36Sopenharmony_ci			buf += sizeof(data);
23462306a36Sopenharmony_ci		} else {
23562306a36Sopenharmony_ci			memcpy(&data, buf, bc % SPMI_PER_DATAREG_BYTE);
23662306a36Sopenharmony_ci			buf += (bc % SPMI_PER_DATAREG_BYTE);
23762306a36Sopenharmony_ci		}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		writel((u32 __force)cpu_to_be32(data),
24062306a36Sopenharmony_ci		       spmi_controller->base + chnl_ofst +
24162306a36Sopenharmony_ci		       SPMI_APB_SPMI_WDATA0_BASE_ADDR +
24262306a36Sopenharmony_ci		       SPMI_PER_DATAREG_BYTE * i);
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* Start the transaction */
24662306a36Sopenharmony_ci	writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller,
24962306a36Sopenharmony_ci					   spmi_controller->base, slave_id,
25062306a36Sopenharmony_ci					   slave_addr);
25162306a36Sopenharmony_ci	spin_unlock_irqrestore(&spmi_controller->lock, flags);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (rc)
25462306a36Sopenharmony_ci		dev_err(&ctrl->dev, "spmi write wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n",
25562306a36Sopenharmony_ci			opc, slave_id, slave_addr, bc);
25662306a36Sopenharmony_ci	else
25762306a36Sopenharmony_ci		dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, wrote value: %*ph\n",
25862306a36Sopenharmony_ci			__func__, slave_id, slave_addr, (int)bc, __buf);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	return rc;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int spmi_controller_probe(struct platform_device *pdev)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct spmi_controller_dev *spmi_controller;
26662306a36Sopenharmony_ci	struct spmi_controller *ctrl;
26762306a36Sopenharmony_ci	struct resource *iores;
26862306a36Sopenharmony_ci	int ret;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*spmi_controller));
27162306a36Sopenharmony_ci	if (!ctrl) {
27262306a36Sopenharmony_ci		dev_err(&pdev->dev, "can not allocate spmi_controller data\n");
27362306a36Sopenharmony_ci		return -ENOMEM;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci	spmi_controller = spmi_controller_get_drvdata(ctrl);
27662306a36Sopenharmony_ci	spmi_controller->controller = ctrl;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
27962306a36Sopenharmony_ci	if (!iores) {
28062306a36Sopenharmony_ci		dev_err(&pdev->dev, "can not get resource!\n");
28162306a36Sopenharmony_ci		ret = -EINVAL;
28262306a36Sopenharmony_ci		goto err_put_controller;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	spmi_controller->base = devm_ioremap(&pdev->dev, iores->start,
28662306a36Sopenharmony_ci					     resource_size(iores));
28762306a36Sopenharmony_ci	if (!spmi_controller->base) {
28862306a36Sopenharmony_ci		dev_err(&pdev->dev, "can not remap base addr!\n");
28962306a36Sopenharmony_ci		ret = -EADDRNOTAVAIL;
29062306a36Sopenharmony_ci		goto err_put_controller;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	ret = of_property_read_u32(pdev->dev.of_node, "hisilicon,spmi-channel",
29462306a36Sopenharmony_ci				   &spmi_controller->channel);
29562306a36Sopenharmony_ci	if (ret) {
29662306a36Sopenharmony_ci		dev_err(&pdev->dev, "can not get channel\n");
29762306a36Sopenharmony_ci		ret = -ENODEV;
29862306a36Sopenharmony_ci		goto err_put_controller;
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	platform_set_drvdata(pdev, spmi_controller);
30262306a36Sopenharmony_ci	dev_set_drvdata(&ctrl->dev, spmi_controller);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	spin_lock_init(&spmi_controller->lock);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	ctrl->nr = spmi_controller->channel;
30762306a36Sopenharmony_ci	ctrl->dev.parent = pdev->dev.parent;
30862306a36Sopenharmony_ci	ctrl->dev.of_node = of_node_get(pdev->dev.of_node);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/* Callbacks */
31162306a36Sopenharmony_ci	ctrl->read_cmd = spmi_read_cmd;
31262306a36Sopenharmony_ci	ctrl->write_cmd = spmi_write_cmd;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	ret = spmi_controller_add(ctrl);
31562306a36Sopenharmony_ci	if (ret) {
31662306a36Sopenharmony_ci		dev_err(&pdev->dev, "spmi_controller_add failed with error %d!\n", ret);
31762306a36Sopenharmony_ci		goto err_put_controller;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	return 0;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cierr_put_controller:
32362306a36Sopenharmony_ci	spmi_controller_put(ctrl);
32462306a36Sopenharmony_ci	return ret;
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic void spmi_del_controller(struct platform_device *pdev)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct spmi_controller *ctrl = platform_get_drvdata(pdev);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	spmi_controller_remove(ctrl);
33262306a36Sopenharmony_ci	spmi_controller_put(ctrl);
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic const struct of_device_id spmi_controller_match_table[] = {
33662306a36Sopenharmony_ci	{
33762306a36Sopenharmony_ci		.compatible = "hisilicon,kirin970-spmi-controller",
33862306a36Sopenharmony_ci	},
33962306a36Sopenharmony_ci	{}
34062306a36Sopenharmony_ci};
34162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, spmi_controller_match_table);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic struct platform_driver spmi_controller_driver = {
34462306a36Sopenharmony_ci	.probe		= spmi_controller_probe,
34562306a36Sopenharmony_ci	.remove_new	= spmi_del_controller,
34662306a36Sopenharmony_ci	.driver		= {
34762306a36Sopenharmony_ci		.name	= "hisi_spmi_controller",
34862306a36Sopenharmony_ci		.of_match_table = spmi_controller_match_table,
34962306a36Sopenharmony_ci	},
35062306a36Sopenharmony_ci};
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic int __init spmi_controller_init(void)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	return platform_driver_register(&spmi_controller_driver);
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_cipostcore_initcall(spmi_controller_init);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic void __exit spmi_controller_exit(void)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	platform_driver_unregister(&spmi_controller_driver);
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_cimodule_exit(spmi_controller_exit);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
36562306a36Sopenharmony_ciMODULE_VERSION("1.0");
36662306a36Sopenharmony_ciMODULE_ALIAS("platform:spmi_controller");
367