xref: /linux/drivers/spmi/hisi-spmi-controller.c (revision e562cf3aea3e1ea46566907f7627e5512840a2b4)
12ea3f6a0SMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0
270f59c90SMayulong 
370f59c90SMayulong #include <linux/delay.h>
470f59c90SMayulong #include <linux/err.h>
57f3ac6c5SMauro Carvalho Chehab #include <linux/interrupt.h>
670f59c90SMayulong #include <linux/io.h>
770f59c90SMayulong #include <linux/kernel.h>
870f59c90SMayulong #include <linux/module.h>
97f3ac6c5SMauro Carvalho Chehab #include <linux/of.h>
107f3ac6c5SMauro Carvalho Chehab #include <linux/platform_device.h>
1170f59c90SMayulong #include <linux/seq_file.h>
127f3ac6c5SMauro Carvalho Chehab #include <linux/slab.h>
1370f59c90SMayulong #include <linux/spmi.h>
1470f59c90SMayulong 
1570f59c90SMayulong /*
1670f59c90SMayulong  * SPMI register addr
1770f59c90SMayulong  */
1870f59c90SMayulong #define SPMI_CHANNEL_OFFSET				0x0300
1970f59c90SMayulong #define SPMI_SLAVE_OFFSET				0x20
2070f59c90SMayulong 
2170f59c90SMayulong #define SPMI_APB_SPMI_CMD_BASE_ADDR			0x0100
222ea3f6a0SMauro Carvalho Chehab 
2370f59c90SMayulong #define SPMI_APB_SPMI_WDATA0_BASE_ADDR			0x0104
2470f59c90SMayulong #define SPMI_APB_SPMI_WDATA1_BASE_ADDR			0x0108
2570f59c90SMayulong #define SPMI_APB_SPMI_WDATA2_BASE_ADDR			0x010c
2670f59c90SMayulong #define SPMI_APB_SPMI_WDATA3_BASE_ADDR			0x0110
2770f59c90SMayulong 
2870f59c90SMayulong #define SPMI_APB_SPMI_STATUS_BASE_ADDR			0x0200
2970f59c90SMayulong 
3070f59c90SMayulong #define SPMI_APB_SPMI_RDATA0_BASE_ADDR			0x0204
3170f59c90SMayulong #define SPMI_APB_SPMI_RDATA1_BASE_ADDR			0x0208
3270f59c90SMayulong #define SPMI_APB_SPMI_RDATA2_BASE_ADDR			0x020c
3370f59c90SMayulong #define SPMI_APB_SPMI_RDATA3_BASE_ADDR			0x0210
3470f59c90SMayulong 
3570f59c90SMayulong #define SPMI_PER_DATAREG_BYTE				4
3670f59c90SMayulong /*
3770f59c90SMayulong  * SPMI cmd register
3870f59c90SMayulong  */
392ea3f6a0SMauro Carvalho Chehab #define SPMI_APB_SPMI_CMD_EN				BIT(31)
4070f59c90SMayulong #define SPMI_APB_SPMI_CMD_TYPE_OFFSET			24
4170f59c90SMayulong #define SPMI_APB_SPMI_CMD_LENGTH_OFFSET			20
42974e3bdcSMauro Carvalho Chehab #define SPMI_APB_SPMI_CMD_SLAVEID_OFFSET		16
43974e3bdcSMauro Carvalho Chehab #define SPMI_APB_SPMI_CMD_ADDR_OFFSET			0
4470f59c90SMayulong 
4570f59c90SMayulong /* Command Opcodes */
462ea3f6a0SMauro Carvalho Chehab 
4770f59c90SMayulong enum spmi_controller_cmd_op_code {
4870f59c90SMayulong 	SPMI_CMD_REG_ZERO_WRITE = 0,
4970f59c90SMayulong 	SPMI_CMD_REG_WRITE = 1,
5070f59c90SMayulong 	SPMI_CMD_REG_READ = 2,
5170f59c90SMayulong 	SPMI_CMD_EXT_REG_WRITE = 3,
5270f59c90SMayulong 	SPMI_CMD_EXT_REG_READ = 4,
5370f59c90SMayulong 	SPMI_CMD_EXT_REG_WRITE_L = 5,
5470f59c90SMayulong 	SPMI_CMD_EXT_REG_READ_L = 6,
5570f59c90SMayulong 	SPMI_CMD_REG_RESET = 7,
5670f59c90SMayulong 	SPMI_CMD_REG_SLEEP = 8,
5770f59c90SMayulong 	SPMI_CMD_REG_SHUTDOWN = 9,
5870f59c90SMayulong 	SPMI_CMD_REG_WAKEUP = 10,
5970f59c90SMayulong };
6070f59c90SMayulong 
6170f59c90SMayulong /*
6270f59c90SMayulong  * SPMI status register
6370f59c90SMayulong  */
642ea3f6a0SMauro Carvalho Chehab #define SPMI_APB_TRANS_DONE			BIT(0)
652ea3f6a0SMauro Carvalho Chehab #define SPMI_APB_TRANS_FAIL			BIT(2)
6670f59c90SMayulong 
6770f59c90SMayulong /* Command register fields */
6870f59c90SMayulong #define SPMI_CONTROLLER_CMD_MAX_BYTE_COUNT	16
6970f59c90SMayulong 
7070f59c90SMayulong /* Maximum number of support PMIC peripherals */
7170f59c90SMayulong #define SPMI_CONTROLLER_TIMEOUT_US		1000
72974e3bdcSMauro Carvalho Chehab #define SPMI_CONTROLLER_MAX_TRANS_BYTES		16
7370f59c90SMayulong 
7470f59c90SMayulong struct spmi_controller_dev {
7570f59c90SMayulong 	struct spmi_controller	*controller;
7670f59c90SMayulong 	struct device		*dev;
7770f59c90SMayulong 	void __iomem		*base;
7870f59c90SMayulong 	spinlock_t		lock;
7970f59c90SMayulong 	u32			channel;
8070f59c90SMayulong };
8170f59c90SMayulong 
824d914a8cSMauro Carvalho Chehab static int spmi_controller_wait_for_done(struct device *dev,
834d914a8cSMauro Carvalho Chehab 					 struct spmi_controller_dev *ctrl_dev,
8470f59c90SMayulong 					 void __iomem *base, u8 sid, u16 addr)
8570f59c90SMayulong {
8670f59c90SMayulong 	u32 timeout = SPMI_CONTROLLER_TIMEOUT_US;
877f3ac6c5SMauro Carvalho Chehab 	u32 status, offset;
88974e3bdcSMauro Carvalho Chehab 
89974e3bdcSMauro Carvalho Chehab 	offset  = SPMI_APB_SPMI_STATUS_BASE_ADDR;
90974e3bdcSMauro Carvalho Chehab 	offset += SPMI_CHANNEL_OFFSET * ctrl_dev->channel + SPMI_SLAVE_OFFSET * sid;
9170f59c90SMayulong 
927f3ac6c5SMauro Carvalho Chehab 	do {
932ea3f6a0SMauro Carvalho Chehab 		status = readl(base + offset);
9470f59c90SMayulong 
9570f59c90SMayulong 		if (status & SPMI_APB_TRANS_DONE) {
9670f59c90SMayulong 			if (status & SPMI_APB_TRANS_FAIL) {
974d914a8cSMauro Carvalho Chehab 				dev_err(dev, "%s: transaction failed (0x%x)\n",
9870f59c90SMayulong 					__func__, status);
9970f59c90SMayulong 				return -EIO;
10070f59c90SMayulong 			}
1014d914a8cSMauro Carvalho Chehab 			dev_dbg(dev, "%s: status 0x%x\n", __func__, status);
10270f59c90SMayulong 			return 0;
10370f59c90SMayulong 		}
1042ea3f6a0SMauro Carvalho Chehab 		udelay(1);
1057f3ac6c5SMauro Carvalho Chehab 	} while (timeout--);
10670f59c90SMayulong 
1074d914a8cSMauro Carvalho Chehab 	dev_err(dev, "%s: timeout, status 0x%x\n", __func__, status);
1082ea3f6a0SMauro Carvalho Chehab 	return -ETIMEDOUT;
1092ea3f6a0SMauro Carvalho Chehab }
11070f59c90SMayulong 
11170f59c90SMayulong static int spmi_read_cmd(struct spmi_controller *ctrl,
1127f3ac6c5SMauro Carvalho Chehab 			 u8 opc, u8 slave_id, u16 slave_addr, u8 *__buf, size_t bc)
11370f59c90SMayulong {
11470f59c90SMayulong 	struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev);
1157f3ac6c5SMauro Carvalho Chehab 	u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel;
11670f59c90SMayulong 	unsigned long flags;
1176af36450SMauro Carvalho Chehab 	u8 *buf = __buf;
11870f59c90SMayulong 	u32 cmd, data;
11970f59c90SMayulong 	int rc;
12070f59c90SMayulong 	u8 op_code, i;
12170f59c90SMayulong 
12270f59c90SMayulong 	if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) {
1234d914a8cSMauro Carvalho Chehab 		dev_err(&ctrl->dev,
1244c6491a3SYueHaibing 			"spmi_controller supports 1..%d bytes per trans, but:%zu requested\n",
1254d914a8cSMauro Carvalho Chehab 			SPMI_CONTROLLER_MAX_TRANS_BYTES, bc);
12670f59c90SMayulong 		return  -EINVAL;
12770f59c90SMayulong 	}
12870f59c90SMayulong 
1297f3ac6c5SMauro Carvalho Chehab 	switch (opc) {
1307f3ac6c5SMauro Carvalho Chehab 	case SPMI_CMD_READ:
13170f59c90SMayulong 		op_code = SPMI_CMD_REG_READ;
1327f3ac6c5SMauro Carvalho Chehab 		break;
1337f3ac6c5SMauro Carvalho Chehab 	case SPMI_CMD_EXT_READ:
13470f59c90SMayulong 		op_code = SPMI_CMD_EXT_REG_READ;
1357f3ac6c5SMauro Carvalho Chehab 		break;
1367f3ac6c5SMauro Carvalho Chehab 	case SPMI_CMD_EXT_READL:
13770f59c90SMayulong 		op_code = SPMI_CMD_EXT_REG_READ_L;
1387f3ac6c5SMauro Carvalho Chehab 		break;
1397f3ac6c5SMauro Carvalho Chehab 	default:
1407f3ac6c5SMauro Carvalho Chehab 		dev_err(&ctrl->dev, "invalid read cmd 0x%x\n", opc);
14170f59c90SMayulong 		return -EINVAL;
14270f59c90SMayulong 	}
14370f59c90SMayulong 
1442ea3f6a0SMauro Carvalho Chehab 	cmd = SPMI_APB_SPMI_CMD_EN |
1452ea3f6a0SMauro Carvalho Chehab 	     (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) |
1462ea3f6a0SMauro Carvalho Chehab 	     ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) |
1477f3ac6c5SMauro Carvalho Chehab 	     ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) |  /* slvid */
1487f3ac6c5SMauro Carvalho Chehab 	     ((slave_addr & 0xffff)  << SPMI_APB_SPMI_CMD_ADDR_OFFSET); /* slave_addr */
14970f59c90SMayulong 
1502ea3f6a0SMauro Carvalho Chehab 	spin_lock_irqsave(&spmi_controller->lock, flags);
15170f59c90SMayulong 
1522ea3f6a0SMauro Carvalho Chehab 	writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR);
15370f59c90SMayulong 
1544d914a8cSMauro Carvalho Chehab 	rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller,
1557f3ac6c5SMauro Carvalho Chehab 					   spmi_controller->base, slave_id, slave_addr);
15670f59c90SMayulong 	if (rc)
15770f59c90SMayulong 		goto done;
15870f59c90SMayulong 
1597f3ac6c5SMauro Carvalho Chehab 	for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) {
1607f3ac6c5SMauro Carvalho Chehab 		data = readl(spmi_controller->base + chnl_ofst +
1617f3ac6c5SMauro Carvalho Chehab 			     SPMI_SLAVE_OFFSET * slave_id +
1627f3ac6c5SMauro Carvalho Chehab 			     SPMI_APB_SPMI_RDATA0_BASE_ADDR +
1637f3ac6c5SMauro Carvalho Chehab 			     i * SPMI_PER_DATAREG_BYTE);
1641b9419d1SJuan Antonio Aldea-Armenteros 		data = be32_to_cpu((__be32 __force)data);
1652ea3f6a0SMauro Carvalho Chehab 		if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) {
16670f59c90SMayulong 			memcpy(buf, &data, sizeof(data));
16770f59c90SMayulong 			buf += sizeof(data);
16870f59c90SMayulong 		} else {
1692ea3f6a0SMauro Carvalho Chehab 			memcpy(buf, &data, bc % SPMI_PER_DATAREG_BYTE);
17070f59c90SMayulong 			buf += (bc % SPMI_PER_DATAREG_BYTE);
17170f59c90SMayulong 		}
1727f3ac6c5SMauro Carvalho Chehab 	}
17370f59c90SMayulong 
17470f59c90SMayulong done:
17570f59c90SMayulong 	spin_unlock_irqrestore(&spmi_controller->lock, flags);
17670f59c90SMayulong 	if (rc)
1774d914a8cSMauro Carvalho Chehab 		dev_err(&ctrl->dev,
1784c6491a3SYueHaibing 			"spmi read wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n",
1797f3ac6c5SMauro Carvalho Chehab 			opc, slave_id, slave_addr, bc + 1);
1806af36450SMauro Carvalho Chehab 	else
1817f3ac6c5SMauro Carvalho Chehab 		dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, read value: %*ph\n",
1827f3ac6c5SMauro Carvalho Chehab 			__func__, slave_id, slave_addr, (int)bc, __buf);
1836af36450SMauro Carvalho Chehab 
18470f59c90SMayulong 	return rc;
1852ea3f6a0SMauro Carvalho Chehab }
18670f59c90SMayulong 
18770f59c90SMayulong static int spmi_write_cmd(struct spmi_controller *ctrl,
1887f3ac6c5SMauro Carvalho Chehab 			  u8 opc, u8 slave_id, u16 slave_addr, const u8 *__buf, size_t bc)
18970f59c90SMayulong {
19070f59c90SMayulong 	struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev);
1917f3ac6c5SMauro Carvalho Chehab 	u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel;
1926af36450SMauro Carvalho Chehab 	const u8 *buf = __buf;
19370f59c90SMayulong 	unsigned long flags;
1948788a30cSMauro Carvalho Chehab 	u32 cmd, data;
19570f59c90SMayulong 	int rc;
19670f59c90SMayulong 	u8 op_code, i;
19770f59c90SMayulong 
19870f59c90SMayulong 	if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) {
1994d914a8cSMauro Carvalho Chehab 		dev_err(&ctrl->dev,
2004c6491a3SYueHaibing 			"spmi_controller supports 1..%d bytes per trans, but:%zu requested\n",
2014d914a8cSMauro Carvalho Chehab 			SPMI_CONTROLLER_MAX_TRANS_BYTES, bc);
20270f59c90SMayulong 		return  -EINVAL;
20370f59c90SMayulong 	}
20470f59c90SMayulong 
2057f3ac6c5SMauro Carvalho Chehab 	switch (opc) {
2067f3ac6c5SMauro Carvalho Chehab 	case SPMI_CMD_WRITE:
20770f59c90SMayulong 		op_code = SPMI_CMD_REG_WRITE;
2087f3ac6c5SMauro Carvalho Chehab 		break;
2097f3ac6c5SMauro Carvalho Chehab 	case SPMI_CMD_EXT_WRITE:
21070f59c90SMayulong 		op_code = SPMI_CMD_EXT_REG_WRITE;
2117f3ac6c5SMauro Carvalho Chehab 		break;
2127f3ac6c5SMauro Carvalho Chehab 	case SPMI_CMD_EXT_WRITEL:
21370f59c90SMayulong 		op_code = SPMI_CMD_EXT_REG_WRITE_L;
2147f3ac6c5SMauro Carvalho Chehab 		break;
2157f3ac6c5SMauro Carvalho Chehab 	default:
2167f3ac6c5SMauro Carvalho Chehab 		dev_err(&ctrl->dev, "invalid write cmd 0x%x\n", opc);
21770f59c90SMayulong 		return -EINVAL;
21870f59c90SMayulong 	}
21970f59c90SMayulong 
2202ea3f6a0SMauro Carvalho Chehab 	cmd = SPMI_APB_SPMI_CMD_EN |
2212ea3f6a0SMauro Carvalho Chehab 	      (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) |
2222ea3f6a0SMauro Carvalho Chehab 	      ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) |
2237f3ac6c5SMauro Carvalho Chehab 	      ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) |
2247f3ac6c5SMauro Carvalho Chehab 	      ((slave_addr & 0xffff)  << SPMI_APB_SPMI_CMD_ADDR_OFFSET);
22570f59c90SMayulong 
22670f59c90SMayulong 	/* Write data to FIFOs */
2272ea3f6a0SMauro Carvalho Chehab 	spin_lock_irqsave(&spmi_controller->lock, flags);
22870f59c90SMayulong 
2297f3ac6c5SMauro Carvalho Chehab 	for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) {
2308788a30cSMauro Carvalho Chehab 		data = 0;
2312ea3f6a0SMauro Carvalho Chehab 		if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) {
23270f59c90SMayulong 			memcpy(&data, buf, sizeof(data));
23370f59c90SMayulong 			buf += sizeof(data);
23470f59c90SMayulong 		} else {
2352ea3f6a0SMauro Carvalho Chehab 			memcpy(&data, buf, bc % SPMI_PER_DATAREG_BYTE);
23670f59c90SMayulong 			buf += (bc % SPMI_PER_DATAREG_BYTE);
23770f59c90SMayulong 		}
23870f59c90SMayulong 
2391b9419d1SJuan Antonio Aldea-Armenteros 		writel((u32 __force)cpu_to_be32(data),
2407f3ac6c5SMauro Carvalho Chehab 		       spmi_controller->base + chnl_ofst +
2417f3ac6c5SMauro Carvalho Chehab 		       SPMI_APB_SPMI_WDATA0_BASE_ADDR +
2427f3ac6c5SMauro Carvalho Chehab 		       SPMI_PER_DATAREG_BYTE * i);
2437f3ac6c5SMauro Carvalho Chehab 	}
24470f59c90SMayulong 
24570f59c90SMayulong 	/* Start the transaction */
2462ea3f6a0SMauro Carvalho Chehab 	writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR);
24770f59c90SMayulong 
2484d914a8cSMauro Carvalho Chehab 	rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller,
2497f3ac6c5SMauro Carvalho Chehab 					   spmi_controller->base, slave_id,
2507f3ac6c5SMauro Carvalho Chehab 					   slave_addr);
25170f59c90SMayulong 	spin_unlock_irqrestore(&spmi_controller->lock, flags);
25270f59c90SMayulong 
25370f59c90SMayulong 	if (rc)
2544c6491a3SYueHaibing 		dev_err(&ctrl->dev, "spmi write wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n",
2557f3ac6c5SMauro Carvalho Chehab 			opc, slave_id, slave_addr, bc);
2566af36450SMauro Carvalho Chehab 	else
2577f3ac6c5SMauro Carvalho Chehab 		dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, wrote value: %*ph\n",
2587f3ac6c5SMauro Carvalho Chehab 			__func__, slave_id, slave_addr, (int)bc, __buf);
25970f59c90SMayulong 
26070f59c90SMayulong 	return rc;
2612ea3f6a0SMauro Carvalho Chehab }
2622ea3f6a0SMauro Carvalho Chehab 
26370f59c90SMayulong static int spmi_controller_probe(struct platform_device *pdev)
26470f59c90SMayulong {
26570f59c90SMayulong 	struct spmi_controller_dev *spmi_controller;
26670f59c90SMayulong 	struct spmi_controller *ctrl;
26770f59c90SMayulong 	struct resource *iores;
2687f3ac6c5SMauro Carvalho Chehab 	int ret;
2696af36450SMauro Carvalho Chehab 
27070f59c90SMayulong 	ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*spmi_controller));
27170f59c90SMayulong 	if (!ctrl) {
27270f59c90SMayulong 		dev_err(&pdev->dev, "can not allocate spmi_controller data\n");
2732ea3f6a0SMauro Carvalho Chehab 		return -ENOMEM;
27470f59c90SMayulong 	}
27570f59c90SMayulong 	spmi_controller = spmi_controller_get_drvdata(ctrl);
27670f59c90SMayulong 	spmi_controller->controller = ctrl;
27770f59c90SMayulong 
27870f59c90SMayulong 	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
27970f59c90SMayulong 	if (!iores) {
28070f59c90SMayulong 		dev_err(&pdev->dev, "can not get resource!\n");
28112b38ea0SChristophe JAILLET 		ret = -EINVAL;
28212b38ea0SChristophe JAILLET 		goto err_put_controller;
28370f59c90SMayulong 	}
28470f59c90SMayulong 
285dbbc8fdfSDan Carpenter 	spmi_controller->base = devm_ioremap(&pdev->dev, iores->start,
286dbbc8fdfSDan Carpenter 					     resource_size(iores));
28770f59c90SMayulong 	if (!spmi_controller->base) {
28870f59c90SMayulong 		dev_err(&pdev->dev, "can not remap base addr!\n");
28912b38ea0SChristophe JAILLET 		ret = -EADDRNOTAVAIL;
29012b38ea0SChristophe JAILLET 		goto err_put_controller;
29170f59c90SMayulong 	}
29270f59c90SMayulong 
293*fcc84fe1SMauro Carvalho Chehab 	ret = of_property_read_u32(pdev->dev.of_node, "hisilicon,spmi-channel",
2942ea3f6a0SMauro Carvalho Chehab 				   &spmi_controller->channel);
29570f59c90SMayulong 	if (ret) {
2966196331eSMauro Carvalho Chehab 		dev_err(&pdev->dev, "can not get channel\n");
29712b38ea0SChristophe JAILLET 		ret = -ENODEV;
29812b38ea0SChristophe JAILLET 		goto err_put_controller;
29970f59c90SMayulong 	}
30070f59c90SMayulong 
30170f59c90SMayulong 	platform_set_drvdata(pdev, spmi_controller);
30270f59c90SMayulong 	dev_set_drvdata(&ctrl->dev, spmi_controller);
30370f59c90SMayulong 
30470f59c90SMayulong 	spin_lock_init(&spmi_controller->lock);
30570f59c90SMayulong 
30670f59c90SMayulong 	ctrl->nr = spmi_controller->channel;
30770f59c90SMayulong 	ctrl->dev.parent = pdev->dev.parent;
30870f59c90SMayulong 	ctrl->dev.of_node = of_node_get(pdev->dev.of_node);
30970f59c90SMayulong 
31070f59c90SMayulong 	/* Callbacks */
31170f59c90SMayulong 	ctrl->read_cmd = spmi_read_cmd;
31270f59c90SMayulong 	ctrl->write_cmd = spmi_write_cmd;
31370f59c90SMayulong 
31470f59c90SMayulong 	ret = spmi_controller_add(ctrl);
31512b38ea0SChristophe JAILLET 	if (ret) {
31612b38ea0SChristophe JAILLET 		dev_err(&pdev->dev, "spmi_controller_add failed with error %d!\n", ret);
31712b38ea0SChristophe JAILLET 		goto err_put_controller;
31812b38ea0SChristophe JAILLET 	}
31985eb5344SMauro Carvalho Chehab 
32012b38ea0SChristophe JAILLET 	return 0;
32112b38ea0SChristophe JAILLET 
32212b38ea0SChristophe JAILLET err_put_controller:
32312b38ea0SChristophe JAILLET 	spmi_controller_put(ctrl);
3242ea3f6a0SMauro Carvalho Chehab 	return ret;
32570f59c90SMayulong }
32670f59c90SMayulong 
32770f59c90SMayulong static int spmi_del_controller(struct platform_device *pdev)
32870f59c90SMayulong {
32970f59c90SMayulong 	struct spmi_controller *ctrl = platform_get_drvdata(pdev);
33070f59c90SMayulong 
33170f59c90SMayulong 	spmi_controller_remove(ctrl);
33212b38ea0SChristophe JAILLET 	spmi_controller_put(ctrl);
33370f59c90SMayulong 	return 0;
33470f59c90SMayulong }
33570f59c90SMayulong 
3362ea3f6a0SMauro Carvalho Chehab static const struct of_device_id spmi_controller_match_table[] = {
337de1a93b6SMauro Carvalho Chehab 	{
338de1a93b6SMauro Carvalho Chehab 		.compatible = "hisilicon,kirin970-spmi-controller",
3392ea3f6a0SMauro Carvalho Chehab 	},
3402ea3f6a0SMauro Carvalho Chehab 	{}
34170f59c90SMayulong };
34285eb5344SMauro Carvalho Chehab MODULE_DEVICE_TABLE(of, spmi_controller_match_table);
34370f59c90SMayulong 
34470f59c90SMayulong static struct platform_driver spmi_controller_driver = {
34570f59c90SMayulong 	.probe		= spmi_controller_probe,
34670f59c90SMayulong 	.remove		= spmi_del_controller,
34770f59c90SMayulong 	.driver		= {
3487f3ac6c5SMauro Carvalho Chehab 		.name	= "hisi_spmi_controller",
34970f59c90SMayulong 		.of_match_table = spmi_controller_match_table,
3502ea3f6a0SMauro Carvalho Chehab 	},
3512ea3f6a0SMauro Carvalho Chehab };
3522ea3f6a0SMauro Carvalho Chehab 
35370f59c90SMayulong static int __init spmi_controller_init(void)
35470f59c90SMayulong {
3552ea3f6a0SMauro Carvalho Chehab 	return platform_driver_register(&spmi_controller_driver);
35670f59c90SMayulong }
35770f59c90SMayulong postcore_initcall(spmi_controller_init);
35870f59c90SMayulong 
35970f59c90SMayulong static void __exit spmi_controller_exit(void)
36070f59c90SMayulong {
36170f59c90SMayulong 	platform_driver_unregister(&spmi_controller_driver);
36270f59c90SMayulong }
36370f59c90SMayulong module_exit(spmi_controller_exit);
36470f59c90SMayulong 
3652ea3f6a0SMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
3662ea3f6a0SMauro Carvalho Chehab MODULE_VERSION("1.0");
367e4cebcaeSColin Ian King MODULE_ALIAS("platform:spmi_controller");
368