xref: /openbmc/linux/drivers/bus/hisi_lpc.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1adf38bb0SZhichang Yuan // SPDX-License-Identifier: GPL-2.0+
2adf38bb0SZhichang Yuan /*
3adf38bb0SZhichang Yuan  * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
4adf38bb0SZhichang Yuan  * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
5adf38bb0SZhichang Yuan  * Author: Zou Rongrong <zourongrong@huawei.com>
6adf38bb0SZhichang Yuan  * Author: John Garry <john.garry@huawei.com>
7adf38bb0SZhichang Yuan  */
8adf38bb0SZhichang Yuan 
9adf38bb0SZhichang Yuan #include <linux/acpi.h>
10adf38bb0SZhichang Yuan #include <linux/console.h>
11adf38bb0SZhichang Yuan #include <linux/delay.h>
12adf38bb0SZhichang Yuan #include <linux/io.h>
13adf38bb0SZhichang Yuan #include <linux/logic_pio.h>
14adf38bb0SZhichang Yuan #include <linux/module.h>
15adf38bb0SZhichang Yuan #include <linux/of.h>
16adf38bb0SZhichang Yuan #include <linux/of_platform.h>
17adf38bb0SZhichang Yuan #include <linux/pci.h>
18*53c5ae63SRob Herring #include <linux/platform_device.h>
19adf3457bSJohn Garry #include <linux/serial_8250.h>
20adf38bb0SZhichang Yuan #include <linux/slab.h>
21adf38bb0SZhichang Yuan 
22adf38bb0SZhichang Yuan #define DRV_NAME "hisi-lpc"
23adf38bb0SZhichang Yuan 
24adf38bb0SZhichang Yuan /*
25adf38bb0SZhichang Yuan  * Setting this bit means each IO operation will target a different port
26adf38bb0SZhichang Yuan  * address; 0 means repeated IO operations will use the same port,
27adf38bb0SZhichang Yuan  * such as BT.
28adf38bb0SZhichang Yuan  */
29adf38bb0SZhichang Yuan #define FG_INCRADDR_LPC		0x02
30adf38bb0SZhichang Yuan 
31adf38bb0SZhichang Yuan struct lpc_cycle_para {
32adf38bb0SZhichang Yuan 	unsigned int opflags;
33adf38bb0SZhichang Yuan 	unsigned int csize; /* data length of each operation */
34adf38bb0SZhichang Yuan };
35adf38bb0SZhichang Yuan 
36adf38bb0SZhichang Yuan struct hisi_lpc_dev {
37adf38bb0SZhichang Yuan 	spinlock_t cycle_lock;
38adf38bb0SZhichang Yuan 	void __iomem  *membase;
39adf38bb0SZhichang Yuan 	struct logic_pio_hwaddr *io_host;
40adf38bb0SZhichang Yuan };
41adf38bb0SZhichang Yuan 
42adf38bb0SZhichang Yuan /* The max IO cycle counts supported is four per operation at maximum */
43adf38bb0SZhichang Yuan #define LPC_MAX_DWIDTH	4
44adf38bb0SZhichang Yuan 
45adf38bb0SZhichang Yuan #define LPC_REG_STARTUP_SIGNAL		0x00
46adf38bb0SZhichang Yuan #define LPC_REG_STARTUP_SIGNAL_START	BIT(0)
47adf38bb0SZhichang Yuan #define LPC_REG_OP_STATUS		0x04
48adf38bb0SZhichang Yuan #define LPC_REG_OP_STATUS_IDLE		BIT(0)
49adf38bb0SZhichang Yuan #define LPC_REG_OP_STATUS_FINISHED	BIT(1)
50adf38bb0SZhichang Yuan #define LPC_REG_OP_LEN			0x10 /* LPC cycles count per start */
51adf38bb0SZhichang Yuan #define LPC_REG_CMD			0x14
52adf38bb0SZhichang Yuan #define LPC_REG_CMD_OP			BIT(0) /* 0: read, 1: write */
53adf38bb0SZhichang Yuan #define LPC_REG_CMD_SAMEADDR		BIT(3)
54adf38bb0SZhichang Yuan #define LPC_REG_ADDR			0x20 /* target address */
55adf38bb0SZhichang Yuan #define LPC_REG_WDATA			0x24 /* write FIFO */
56adf38bb0SZhichang Yuan #define LPC_REG_RDATA			0x28 /* read FIFO */
57adf38bb0SZhichang Yuan 
58adf38bb0SZhichang Yuan /* The minimal nanosecond interval for each query on LPC cycle status */
59adf38bb0SZhichang Yuan #define LPC_NSEC_PERWAIT	100
60adf38bb0SZhichang Yuan 
61adf38bb0SZhichang Yuan /*
62adf38bb0SZhichang Yuan  * The maximum waiting time is about 128us.  It is specific for stream I/O,
63adf38bb0SZhichang Yuan  * such as ins.
64adf38bb0SZhichang Yuan  *
65adf38bb0SZhichang Yuan  * The fastest IO cycle time is about 390ns, but the worst case will wait
66adf38bb0SZhichang Yuan  * for extra 256 lpc clocks, so (256 + 13) * 30ns = 8 us. The maximum burst
67adf38bb0SZhichang Yuan  * cycles is 16. So, the maximum waiting time is about 128us under worst
68adf38bb0SZhichang Yuan  * case.
69adf38bb0SZhichang Yuan  *
70adf38bb0SZhichang Yuan  * Choose 1300 as the maximum.
71adf38bb0SZhichang Yuan  */
72adf38bb0SZhichang Yuan #define LPC_MAX_WAITCNT		1300
73adf38bb0SZhichang Yuan 
74adf38bb0SZhichang Yuan /* About 10us. This is specific for single IO operations, such as inb */
75adf38bb0SZhichang Yuan #define LPC_PEROP_WAITCNT	100
76adf38bb0SZhichang Yuan 
wait_lpc_idle(void __iomem * mbase,unsigned int waitcnt)77663accf1SJohn Garry static int wait_lpc_idle(void __iomem *mbase, unsigned int waitcnt)
78adf38bb0SZhichang Yuan {
79adf38bb0SZhichang Yuan 	u32 status;
80adf38bb0SZhichang Yuan 
81adf38bb0SZhichang Yuan 	do {
82adf38bb0SZhichang Yuan 		status = readl(mbase + LPC_REG_OP_STATUS);
83adf38bb0SZhichang Yuan 		if (status & LPC_REG_OP_STATUS_IDLE)
84adf38bb0SZhichang Yuan 			return (status & LPC_REG_OP_STATUS_FINISHED) ? 0 : -EIO;
85adf38bb0SZhichang Yuan 		ndelay(LPC_NSEC_PERWAIT);
86adf38bb0SZhichang Yuan 	} while (--waitcnt);
87adf38bb0SZhichang Yuan 
885e3e70b8SAndy Shevchenko 	return -ETIMEDOUT;
89adf38bb0SZhichang Yuan }
90adf38bb0SZhichang Yuan 
91adf38bb0SZhichang Yuan /*
92adf38bb0SZhichang Yuan  * hisi_lpc_target_in - trigger a series of LPC cycles for read operation
93adf38bb0SZhichang Yuan  * @lpcdev: pointer to hisi lpc device
94adf38bb0SZhichang Yuan  * @para: some parameters used to control the lpc I/O operations
95adf38bb0SZhichang Yuan  * @addr: the lpc I/O target port address
96adf38bb0SZhichang Yuan  * @buf: where the read back data is stored
97adf38bb0SZhichang Yuan  * @opcnt: how many I/O operations required, i.e. data width
98adf38bb0SZhichang Yuan  *
99adf38bb0SZhichang Yuan  * Returns 0 on success, non-zero on fail.
100adf38bb0SZhichang Yuan  */
hisi_lpc_target_in(struct hisi_lpc_dev * lpcdev,struct lpc_cycle_para * para,unsigned long addr,unsigned char * buf,unsigned long opcnt)101adf38bb0SZhichang Yuan static int hisi_lpc_target_in(struct hisi_lpc_dev *lpcdev,
102adf38bb0SZhichang Yuan 			      struct lpc_cycle_para *para, unsigned long addr,
103adf38bb0SZhichang Yuan 			      unsigned char *buf, unsigned long opcnt)
104adf38bb0SZhichang Yuan {
105adf38bb0SZhichang Yuan 	unsigned int cmd_word;
106adf38bb0SZhichang Yuan 	unsigned int waitcnt;
107adf38bb0SZhichang Yuan 	unsigned long flags;
108adf38bb0SZhichang Yuan 	int ret;
109adf38bb0SZhichang Yuan 
110adf38bb0SZhichang Yuan 	if (!buf || !opcnt || !para || !para->csize || !lpcdev)
111adf38bb0SZhichang Yuan 		return -EINVAL;
112adf38bb0SZhichang Yuan 
113adf38bb0SZhichang Yuan 	cmd_word = 0; /* IO mode, Read */
114adf38bb0SZhichang Yuan 	waitcnt = LPC_PEROP_WAITCNT;
115adf38bb0SZhichang Yuan 	if (!(para->opflags & FG_INCRADDR_LPC)) {
116adf38bb0SZhichang Yuan 		cmd_word |= LPC_REG_CMD_SAMEADDR;
117adf38bb0SZhichang Yuan 		waitcnt = LPC_MAX_WAITCNT;
118adf38bb0SZhichang Yuan 	}
119adf38bb0SZhichang Yuan 
120adf38bb0SZhichang Yuan 	/* whole operation must be atomic */
121adf38bb0SZhichang Yuan 	spin_lock_irqsave(&lpcdev->cycle_lock, flags);
122adf38bb0SZhichang Yuan 
123adf38bb0SZhichang Yuan 	writel_relaxed(opcnt, lpcdev->membase + LPC_REG_OP_LEN);
124adf38bb0SZhichang Yuan 	writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD);
125adf38bb0SZhichang Yuan 	writel_relaxed(addr, lpcdev->membase + LPC_REG_ADDR);
126adf38bb0SZhichang Yuan 
127adf38bb0SZhichang Yuan 	writel(LPC_REG_STARTUP_SIGNAL_START,
128adf38bb0SZhichang Yuan 	       lpcdev->membase + LPC_REG_STARTUP_SIGNAL);
129adf38bb0SZhichang Yuan 
130adf38bb0SZhichang Yuan 	/* whether the operation is finished */
131adf38bb0SZhichang Yuan 	ret = wait_lpc_idle(lpcdev->membase, waitcnt);
132adf38bb0SZhichang Yuan 	if (ret) {
133adf38bb0SZhichang Yuan 		spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
134adf38bb0SZhichang Yuan 		return ret;
135adf38bb0SZhichang Yuan 	}
136adf38bb0SZhichang Yuan 
137adf38bb0SZhichang Yuan 	readsb(lpcdev->membase + LPC_REG_RDATA, buf, opcnt);
138adf38bb0SZhichang Yuan 
139adf38bb0SZhichang Yuan 	spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
140adf38bb0SZhichang Yuan 
141adf38bb0SZhichang Yuan 	return 0;
142adf38bb0SZhichang Yuan }
143adf38bb0SZhichang Yuan 
144adf38bb0SZhichang Yuan /*
145adf38bb0SZhichang Yuan  * hisi_lpc_target_out - trigger a series of LPC cycles for write operation
146adf38bb0SZhichang Yuan  * @lpcdev: pointer to hisi lpc device
147adf38bb0SZhichang Yuan  * @para: some parameters used to control the lpc I/O operations
148adf38bb0SZhichang Yuan  * @addr: the lpc I/O target port address
149adf38bb0SZhichang Yuan  * @buf: where the data to be written is stored
150adf38bb0SZhichang Yuan  * @opcnt: how many I/O operations required, i.e. data width
151adf38bb0SZhichang Yuan  *
152adf38bb0SZhichang Yuan  * Returns 0 on success, non-zero on fail.
153adf38bb0SZhichang Yuan  */
hisi_lpc_target_out(struct hisi_lpc_dev * lpcdev,struct lpc_cycle_para * para,unsigned long addr,const unsigned char * buf,unsigned long opcnt)154adf38bb0SZhichang Yuan static int hisi_lpc_target_out(struct hisi_lpc_dev *lpcdev,
155adf38bb0SZhichang Yuan 			       struct lpc_cycle_para *para, unsigned long addr,
156adf38bb0SZhichang Yuan 			       const unsigned char *buf, unsigned long opcnt)
157adf38bb0SZhichang Yuan {
158adf38bb0SZhichang Yuan 	unsigned int waitcnt;
159adf38bb0SZhichang Yuan 	unsigned long flags;
160adf38bb0SZhichang Yuan 	u32 cmd_word;
161adf38bb0SZhichang Yuan 	int ret;
162adf38bb0SZhichang Yuan 
163adf38bb0SZhichang Yuan 	if (!buf || !opcnt || !para || !lpcdev)
164adf38bb0SZhichang Yuan 		return -EINVAL;
165adf38bb0SZhichang Yuan 
166adf38bb0SZhichang Yuan 	/* default is increasing address */
167adf38bb0SZhichang Yuan 	cmd_word = LPC_REG_CMD_OP; /* IO mode, write */
168adf38bb0SZhichang Yuan 	waitcnt = LPC_PEROP_WAITCNT;
169adf38bb0SZhichang Yuan 	if (!(para->opflags & FG_INCRADDR_LPC)) {
170adf38bb0SZhichang Yuan 		cmd_word |= LPC_REG_CMD_SAMEADDR;
171adf38bb0SZhichang Yuan 		waitcnt = LPC_MAX_WAITCNT;
172adf38bb0SZhichang Yuan 	}
173adf38bb0SZhichang Yuan 
174adf38bb0SZhichang Yuan 	spin_lock_irqsave(&lpcdev->cycle_lock, flags);
175adf38bb0SZhichang Yuan 
176adf38bb0SZhichang Yuan 	writel_relaxed(opcnt, lpcdev->membase + LPC_REG_OP_LEN);
177adf38bb0SZhichang Yuan 	writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD);
178adf38bb0SZhichang Yuan 	writel_relaxed(addr, lpcdev->membase + LPC_REG_ADDR);
179adf38bb0SZhichang Yuan 
180adf38bb0SZhichang Yuan 	writesb(lpcdev->membase + LPC_REG_WDATA, buf, opcnt);
181adf38bb0SZhichang Yuan 
182adf38bb0SZhichang Yuan 	writel(LPC_REG_STARTUP_SIGNAL_START,
183adf38bb0SZhichang Yuan 	       lpcdev->membase + LPC_REG_STARTUP_SIGNAL);
184adf38bb0SZhichang Yuan 
185adf38bb0SZhichang Yuan 	/* whether the operation is finished */
186adf38bb0SZhichang Yuan 	ret = wait_lpc_idle(lpcdev->membase, waitcnt);
187adf38bb0SZhichang Yuan 
188adf38bb0SZhichang Yuan 	spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
189adf38bb0SZhichang Yuan 
190adf38bb0SZhichang Yuan 	return ret;
191adf38bb0SZhichang Yuan }
192adf38bb0SZhichang Yuan 
hisi_lpc_pio_to_addr(struct hisi_lpc_dev * lpcdev,unsigned long pio)193adf38bb0SZhichang Yuan static unsigned long hisi_lpc_pio_to_addr(struct hisi_lpc_dev *lpcdev,
194adf38bb0SZhichang Yuan 					  unsigned long pio)
195adf38bb0SZhichang Yuan {
196adf38bb0SZhichang Yuan 	return pio - lpcdev->io_host->io_start + lpcdev->io_host->hw_start;
197adf38bb0SZhichang Yuan }
198adf38bb0SZhichang Yuan 
199adf38bb0SZhichang Yuan /*
200adf38bb0SZhichang Yuan  * hisi_lpc_comm_in - input the data in a single operation
201adf38bb0SZhichang Yuan  * @hostdata: pointer to the device information relevant to LPC controller
202adf38bb0SZhichang Yuan  * @pio: the target I/O port address
203adf38bb0SZhichang Yuan  * @dwidth: the data length required to read from the target I/O port
204adf38bb0SZhichang Yuan  *
205adf38bb0SZhichang Yuan  * When success, data is returned. Otherwise, ~0 is returned.
206adf38bb0SZhichang Yuan  */
hisi_lpc_comm_in(void * hostdata,unsigned long pio,size_t dwidth)207adf38bb0SZhichang Yuan static u32 hisi_lpc_comm_in(void *hostdata, unsigned long pio, size_t dwidth)
208adf38bb0SZhichang Yuan {
209adf38bb0SZhichang Yuan 	struct hisi_lpc_dev *lpcdev = hostdata;
210adf38bb0SZhichang Yuan 	struct lpc_cycle_para iopara;
211adf38bb0SZhichang Yuan 	unsigned long addr;
212663accf1SJohn Garry 	__le32 rd_data = 0;
213adf38bb0SZhichang Yuan 	int ret;
214adf38bb0SZhichang Yuan 
215adf38bb0SZhichang Yuan 	if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH)
216adf38bb0SZhichang Yuan 		return ~0;
217adf38bb0SZhichang Yuan 
218adf38bb0SZhichang Yuan 	addr = hisi_lpc_pio_to_addr(lpcdev, pio);
219adf38bb0SZhichang Yuan 
220adf38bb0SZhichang Yuan 	iopara.opflags = FG_INCRADDR_LPC;
221adf38bb0SZhichang Yuan 	iopara.csize = dwidth;
222adf38bb0SZhichang Yuan 
223adf38bb0SZhichang Yuan 	ret = hisi_lpc_target_in(lpcdev, &iopara, addr,
224adf38bb0SZhichang Yuan 				 (unsigned char *)&rd_data, dwidth);
225adf38bb0SZhichang Yuan 	if (ret)
226adf38bb0SZhichang Yuan 		return ~0;
227adf38bb0SZhichang Yuan 
228adf38bb0SZhichang Yuan 	return le32_to_cpu(rd_data);
229adf38bb0SZhichang Yuan }
230adf38bb0SZhichang Yuan 
231adf38bb0SZhichang Yuan /*
232adf38bb0SZhichang Yuan  * hisi_lpc_comm_out - output the data in a single operation
233adf38bb0SZhichang Yuan  * @hostdata: pointer to the device information relevant to LPC controller
234adf38bb0SZhichang Yuan  * @pio: the target I/O port address
235adf38bb0SZhichang Yuan  * @val: a value to be output from caller, maximum is four bytes
236adf38bb0SZhichang Yuan  * @dwidth: the data width required writing to the target I/O port
237adf38bb0SZhichang Yuan  *
238adf38bb0SZhichang Yuan  * This function corresponds to out(b,w,l) only.
239adf38bb0SZhichang Yuan  */
hisi_lpc_comm_out(void * hostdata,unsigned long pio,u32 val,size_t dwidth)240adf38bb0SZhichang Yuan static void hisi_lpc_comm_out(void *hostdata, unsigned long pio,
241adf38bb0SZhichang Yuan 			      u32 val, size_t dwidth)
242adf38bb0SZhichang Yuan {
243adf38bb0SZhichang Yuan 	struct hisi_lpc_dev *lpcdev = hostdata;
244adf38bb0SZhichang Yuan 	struct lpc_cycle_para iopara;
245adf38bb0SZhichang Yuan 	const unsigned char *buf;
246adf38bb0SZhichang Yuan 	unsigned long addr;
247663accf1SJohn Garry 	__le32 _val = cpu_to_le32(val);
248adf38bb0SZhichang Yuan 
249adf38bb0SZhichang Yuan 	if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH)
250adf38bb0SZhichang Yuan 		return;
251adf38bb0SZhichang Yuan 
252663accf1SJohn Garry 	buf = (const unsigned char *)&_val;
253adf38bb0SZhichang Yuan 	addr = hisi_lpc_pio_to_addr(lpcdev, pio);
254adf38bb0SZhichang Yuan 
255adf38bb0SZhichang Yuan 	iopara.opflags = FG_INCRADDR_LPC;
256adf38bb0SZhichang Yuan 	iopara.csize = dwidth;
257adf38bb0SZhichang Yuan 
258adf38bb0SZhichang Yuan 	hisi_lpc_target_out(lpcdev, &iopara, addr, buf, dwidth);
259adf38bb0SZhichang Yuan }
260adf38bb0SZhichang Yuan 
261adf38bb0SZhichang Yuan /*
262adf38bb0SZhichang Yuan  * hisi_lpc_comm_ins - input the data in the buffer in multiple operations
263adf38bb0SZhichang Yuan  * @hostdata: pointer to the device information relevant to LPC controller
264adf38bb0SZhichang Yuan  * @pio: the target I/O port address
265adf38bb0SZhichang Yuan  * @buffer: a buffer where read/input data bytes are stored
266adf38bb0SZhichang Yuan  * @dwidth: the data width required writing to the target I/O port
267adf38bb0SZhichang Yuan  * @count: how many data units whose length is dwidth will be read
268adf38bb0SZhichang Yuan  *
269adf38bb0SZhichang Yuan  * When success, the data read back is stored in buffer pointed by buffer.
270adf38bb0SZhichang Yuan  * Returns 0 on success, -errno otherwise.
271adf38bb0SZhichang Yuan  */
hisi_lpc_comm_ins(void * hostdata,unsigned long pio,void * buffer,size_t dwidth,unsigned int count)272adf38bb0SZhichang Yuan static u32 hisi_lpc_comm_ins(void *hostdata, unsigned long pio, void *buffer,
273adf38bb0SZhichang Yuan 			     size_t dwidth, unsigned int count)
274adf38bb0SZhichang Yuan {
275adf38bb0SZhichang Yuan 	struct hisi_lpc_dev *lpcdev = hostdata;
276adf38bb0SZhichang Yuan 	unsigned char *buf = buffer;
277adf38bb0SZhichang Yuan 	struct lpc_cycle_para iopara;
278adf38bb0SZhichang Yuan 	unsigned long addr;
279adf38bb0SZhichang Yuan 
280adf38bb0SZhichang Yuan 	if (!lpcdev || !buf || !count || !dwidth || dwidth > LPC_MAX_DWIDTH)
281adf38bb0SZhichang Yuan 		return -EINVAL;
282adf38bb0SZhichang Yuan 
283adf38bb0SZhichang Yuan 	iopara.opflags = 0;
284adf38bb0SZhichang Yuan 	if (dwidth > 1)
285adf38bb0SZhichang Yuan 		iopara.opflags |= FG_INCRADDR_LPC;
286adf38bb0SZhichang Yuan 	iopara.csize = dwidth;
287adf38bb0SZhichang Yuan 
288adf38bb0SZhichang Yuan 	addr = hisi_lpc_pio_to_addr(lpcdev, pio);
289adf38bb0SZhichang Yuan 
290adf38bb0SZhichang Yuan 	do {
291adf38bb0SZhichang Yuan 		int ret;
292adf38bb0SZhichang Yuan 
293adf38bb0SZhichang Yuan 		ret = hisi_lpc_target_in(lpcdev, &iopara, addr, buf, dwidth);
294adf38bb0SZhichang Yuan 		if (ret)
295adf38bb0SZhichang Yuan 			return ret;
296adf38bb0SZhichang Yuan 		buf += dwidth;
297adf38bb0SZhichang Yuan 	} while (--count);
298adf38bb0SZhichang Yuan 
299adf38bb0SZhichang Yuan 	return 0;
300adf38bb0SZhichang Yuan }
301adf38bb0SZhichang Yuan 
302adf38bb0SZhichang Yuan /*
303adf38bb0SZhichang Yuan  * hisi_lpc_comm_outs - output the data in the buffer in multiple operations
304adf38bb0SZhichang Yuan  * @hostdata: pointer to the device information relevant to LPC controller
305adf38bb0SZhichang Yuan  * @pio: the target I/O port address
306adf38bb0SZhichang Yuan  * @buffer: a buffer where write/output data bytes are stored
307adf38bb0SZhichang Yuan  * @dwidth: the data width required writing to the target I/O port
308adf38bb0SZhichang Yuan  * @count: how many data units whose length is dwidth will be written
309adf38bb0SZhichang Yuan  */
hisi_lpc_comm_outs(void * hostdata,unsigned long pio,const void * buffer,size_t dwidth,unsigned int count)310adf38bb0SZhichang Yuan static void hisi_lpc_comm_outs(void *hostdata, unsigned long pio,
311adf38bb0SZhichang Yuan 			       const void *buffer, size_t dwidth,
312adf38bb0SZhichang Yuan 			       unsigned int count)
313adf38bb0SZhichang Yuan {
314adf38bb0SZhichang Yuan 	struct hisi_lpc_dev *lpcdev = hostdata;
315adf38bb0SZhichang Yuan 	struct lpc_cycle_para iopara;
316adf38bb0SZhichang Yuan 	const unsigned char *buf = buffer;
317adf38bb0SZhichang Yuan 	unsigned long addr;
318adf38bb0SZhichang Yuan 
319adf38bb0SZhichang Yuan 	if (!lpcdev || !buf || !count || !dwidth || dwidth > LPC_MAX_DWIDTH)
320adf38bb0SZhichang Yuan 		return;
321adf38bb0SZhichang Yuan 
322adf38bb0SZhichang Yuan 	iopara.opflags = 0;
323adf38bb0SZhichang Yuan 	if (dwidth > 1)
324adf38bb0SZhichang Yuan 		iopara.opflags |= FG_INCRADDR_LPC;
325adf38bb0SZhichang Yuan 	iopara.csize = dwidth;
326adf38bb0SZhichang Yuan 
327adf38bb0SZhichang Yuan 	addr = hisi_lpc_pio_to_addr(lpcdev, pio);
328adf38bb0SZhichang Yuan 	do {
329adf38bb0SZhichang Yuan 		if (hisi_lpc_target_out(lpcdev, &iopara, addr, buf, dwidth))
330adf38bb0SZhichang Yuan 			break;
331adf38bb0SZhichang Yuan 		buf += dwidth;
332adf38bb0SZhichang Yuan 	} while (--count);
333adf38bb0SZhichang Yuan }
334adf38bb0SZhichang Yuan 
335adf38bb0SZhichang Yuan static const struct logic_pio_host_ops hisi_lpc_ops = {
336adf38bb0SZhichang Yuan 	.in = hisi_lpc_comm_in,
337adf38bb0SZhichang Yuan 	.out = hisi_lpc_comm_out,
338adf38bb0SZhichang Yuan 	.ins = hisi_lpc_comm_ins,
339adf38bb0SZhichang Yuan 	.outs = hisi_lpc_comm_outs,
340adf38bb0SZhichang Yuan };
341adf38bb0SZhichang Yuan 
342e0aa1563SJohn Garry #ifdef CONFIG_ACPI
hisi_lpc_acpi_xlat_io_res(struct acpi_device * adev,struct acpi_device * host,struct resource * res)343e0aa1563SJohn Garry static int hisi_lpc_acpi_xlat_io_res(struct acpi_device *adev,
344e0aa1563SJohn Garry 				     struct acpi_device *host,
345e0aa1563SJohn Garry 				     struct resource *res)
346e0aa1563SJohn Garry {
347e0aa1563SJohn Garry 	unsigned long sys_port;
348e0aa1563SJohn Garry 	resource_size_t len = resource_size(res);
349e0aa1563SJohn Garry 
350947f11d1SAndy Shevchenko 	sys_port = logic_pio_trans_hwaddr(acpi_fwnode_handle(host), res->start, len);
351e0aa1563SJohn Garry 	if (sys_port == ~0UL)
352e0aa1563SJohn Garry 		return -EFAULT;
353e0aa1563SJohn Garry 
354e0aa1563SJohn Garry 	res->start = sys_port;
355e0aa1563SJohn Garry 	res->end = sys_port + len;
356e0aa1563SJohn Garry 
357e0aa1563SJohn Garry 	return 0;
358e0aa1563SJohn Garry }
359e0aa1563SJohn Garry 
360e0aa1563SJohn Garry /*
361a6dd255bSJohn Garry  * Released firmware describes the IO port max address as 0x3fff, which is
362a6dd255bSJohn Garry  * the max host bus address. Fixup to a proper range. This will probably
363a6dd255bSJohn Garry  * never be fixed in firmware.
364a6dd255bSJohn Garry  */
hisi_lpc_acpi_fixup_child_resource(struct device * hostdev,struct resource * r)365a6dd255bSJohn Garry static void hisi_lpc_acpi_fixup_child_resource(struct device *hostdev,
366a6dd255bSJohn Garry 					       struct resource *r)
367a6dd255bSJohn Garry {
368a6dd255bSJohn Garry 	if (r->end != 0x3fff)
369a6dd255bSJohn Garry 		return;
370a6dd255bSJohn Garry 
371a6dd255bSJohn Garry 	if (r->start == 0xe4)
372a6dd255bSJohn Garry 		r->end = 0xe4 + 0x04 - 1;
373a6dd255bSJohn Garry 	else if (r->start == 0x2f8)
374a6dd255bSJohn Garry 		r->end = 0x2f8 + 0x08 - 1;
375a6dd255bSJohn Garry 	else
376a6dd255bSJohn Garry 		dev_warn(hostdev, "unrecognised resource %pR to fixup, ignoring\n",
377a6dd255bSJohn Garry 			 r);
378a6dd255bSJohn Garry }
379a6dd255bSJohn Garry 
380a6dd255bSJohn Garry /*
381332f632eSJohn Garry  * hisi_lpc_acpi_set_io_res - set the resources for a child
382d6745530SRafael J. Wysocki  * @adev: ACPI companion of the device node to be updated the I/O resource
383e0aa1563SJohn Garry  * @hostdev: the device node associated with host controller
384e0aa1563SJohn Garry  * @res: double pointer to be set to the address of translated resources
385e0aa1563SJohn Garry  * @num_res: pointer to variable to hold the number of translated resources
386e0aa1563SJohn Garry  *
387e0aa1563SJohn Garry  * Returns 0 when successful, and a negative value for failure.
388e0aa1563SJohn Garry  *
389e0aa1563SJohn Garry  * For a given host controller, each child device will have an associated
390e0aa1563SJohn Garry  * host-relative address resource.  This function will return the translated
391e0aa1563SJohn Garry  * logical PIO addresses for each child devices resources.
392e0aa1563SJohn Garry  */
hisi_lpc_acpi_set_io_res(struct acpi_device * adev,struct device * hostdev,const struct resource ** res,int * num_res)393d6745530SRafael J. Wysocki static int hisi_lpc_acpi_set_io_res(struct acpi_device *adev,
394e0aa1563SJohn Garry 				    struct device *hostdev,
395e0aa1563SJohn Garry 				    const struct resource **res, int *num_res)
396e0aa1563SJohn Garry {
397d6745530SRafael J. Wysocki 	struct acpi_device *host = to_acpi_device(adev->dev.parent);
398e0aa1563SJohn Garry 	struct resource_entry *rentry;
399e0aa1563SJohn Garry 	LIST_HEAD(resource_list);
400e0aa1563SJohn Garry 	struct resource *resources;
401e0aa1563SJohn Garry 	int count;
402e0aa1563SJohn Garry 	int i;
403e0aa1563SJohn Garry 
404e0aa1563SJohn Garry 	if (!adev->status.present) {
405d6745530SRafael J. Wysocki 		dev_dbg(&adev->dev, "device is not present\n");
406e0aa1563SJohn Garry 		return -EIO;
407e0aa1563SJohn Garry 	}
408e0aa1563SJohn Garry 
409e0aa1563SJohn Garry 	if (acpi_device_enumerated(adev)) {
410d6745530SRafael J. Wysocki 		dev_dbg(&adev->dev, "has been enumerated\n");
411e0aa1563SJohn Garry 		return -EIO;
412e0aa1563SJohn Garry 	}
413e0aa1563SJohn Garry 
414e0aa1563SJohn Garry 	/*
415e0aa1563SJohn Garry 	 * The following code segment to retrieve the resources is common to
416e0aa1563SJohn Garry 	 * acpi_create_platform_device(), so consider a common helper function
417e0aa1563SJohn Garry 	 * in future.
418e0aa1563SJohn Garry 	 */
419e0aa1563SJohn Garry 	count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
420e0aa1563SJohn Garry 	if (count <= 0) {
421d6745530SRafael J. Wysocki 		dev_dbg(&adev->dev, "failed to get resources\n");
422e0aa1563SJohn Garry 		return count ? count : -EIO;
423e0aa1563SJohn Garry 	}
424e0aa1563SJohn Garry 
425e0aa1563SJohn Garry 	resources = devm_kcalloc(hostdev, count, sizeof(*resources),
426e0aa1563SJohn Garry 				 GFP_KERNEL);
427e0aa1563SJohn Garry 	if (!resources) {
428e0aa1563SJohn Garry 		dev_warn(hostdev, "could not allocate memory for %d resources\n",
429e0aa1563SJohn Garry 			 count);
430e0aa1563SJohn Garry 		acpi_dev_free_resource_list(&resource_list);
431e0aa1563SJohn Garry 		return -ENOMEM;
432e0aa1563SJohn Garry 	}
433e0aa1563SJohn Garry 	count = 0;
434a6dd255bSJohn Garry 	list_for_each_entry(rentry, &resource_list, node) {
435a6dd255bSJohn Garry 		resources[count] = *rentry->res;
436a6dd255bSJohn Garry 		hisi_lpc_acpi_fixup_child_resource(hostdev, &resources[count]);
437a6dd255bSJohn Garry 		count++;
438a6dd255bSJohn Garry 	}
439e0aa1563SJohn Garry 
440e0aa1563SJohn Garry 	acpi_dev_free_resource_list(&resource_list);
441e0aa1563SJohn Garry 
442e0aa1563SJohn Garry 	/* translate the I/O resources */
443e0aa1563SJohn Garry 	for (i = 0; i < count; i++) {
444e0aa1563SJohn Garry 		int ret;
445e0aa1563SJohn Garry 
446e0aa1563SJohn Garry 		if (!(resources[i].flags & IORESOURCE_IO))
447e0aa1563SJohn Garry 			continue;
448e0aa1563SJohn Garry 		ret = hisi_lpc_acpi_xlat_io_res(adev, host, &resources[i]);
449e0aa1563SJohn Garry 		if (ret) {
450d6745530SRafael J. Wysocki 			dev_err(&adev->dev, "translate IO range %pR failed (%d)\n",
451e0aa1563SJohn Garry 				&resources[i], ret);
452e0aa1563SJohn Garry 			return ret;
453e0aa1563SJohn Garry 		}
454e0aa1563SJohn Garry 	}
455e0aa1563SJohn Garry 	*res = resources;
456e0aa1563SJohn Garry 	*num_res = count;
457e0aa1563SJohn Garry 
458e0aa1563SJohn Garry 	return 0;
459e0aa1563SJohn Garry }
460e0aa1563SJohn Garry 
hisi_lpc_acpi_remove_subdev(struct device * dev,void * unused)46199c0228dSJohn Garry static int hisi_lpc_acpi_remove_subdev(struct device *dev, void *unused)
46299c0228dSJohn Garry {
46399c0228dSJohn Garry 	platform_device_unregister(to_platform_device(dev));
46499c0228dSJohn Garry 	return 0;
46599c0228dSJohn Garry }
46699c0228dSJohn Garry 
hisi_lpc_acpi_clear_enumerated(struct acpi_device * adev,void * not_used)467d6745530SRafael J. Wysocki static int hisi_lpc_acpi_clear_enumerated(struct acpi_device *adev, void *not_used)
468d6745530SRafael J. Wysocki {
469d6745530SRafael J. Wysocki 	acpi_device_clear_enumerated(adev);
470d6745530SRafael J. Wysocki 	return 0;
471d6745530SRafael J. Wysocki }
472d6745530SRafael J. Wysocki 
47399c0228dSJohn Garry struct hisi_lpc_acpi_cell {
47499c0228dSJohn Garry 	const char *hid;
4754678a2d3SJohn Garry 	const struct platform_device_info *pdevinfo;
47699c0228dSJohn Garry };
47799c0228dSJohn Garry 
hisi_lpc_acpi_remove(struct device * hostdev)47810e62b47SJohn Garry static void hisi_lpc_acpi_remove(struct device *hostdev)
47910e62b47SJohn Garry {
48010e62b47SJohn Garry 	device_for_each_child(hostdev, NULL, hisi_lpc_acpi_remove_subdev);
481d6745530SRafael J. Wysocki 	acpi_dev_for_each_child(ACPI_COMPANION(hostdev),
482d6745530SRafael J. Wysocki 				hisi_lpc_acpi_clear_enumerated, NULL);
48310e62b47SJohn Garry }
48410e62b47SJohn Garry 
hisi_lpc_acpi_add_child(struct acpi_device * child,void * data)485d6745530SRafael J. Wysocki static int hisi_lpc_acpi_add_child(struct acpi_device *child, void *data)
486e0aa1563SJohn Garry {
48799c0228dSJohn Garry 	const char *hid = acpi_device_hid(child);
488d6745530SRafael J. Wysocki 	struct device *hostdev = data;
48999c0228dSJohn Garry 	const struct hisi_lpc_acpi_cell *cell;
490d6745530SRafael J. Wysocki 	struct platform_device *pdev;
491332f632eSJohn Garry 	const struct resource *res;
49299c0228dSJohn Garry 	bool found = false;
493332f632eSJohn Garry 	int num_res;
494d6745530SRafael J. Wysocki 	int ret;
495e0aa1563SJohn Garry 
496d6745530SRafael J. Wysocki 	ret = hisi_lpc_acpi_set_io_res(child, hostdev, &res, &num_res);
497e0aa1563SJohn Garry 	if (ret) {
498332f632eSJohn Garry 		dev_warn(hostdev, "set resource fail (%d)\n", ret);
499d6745530SRafael J. Wysocki 		return ret;
50099c0228dSJohn Garry 	}
50199c0228dSJohn Garry 
50299c0228dSJohn Garry 	cell = (struct hisi_lpc_acpi_cell []){
50399c0228dSJohn Garry 		/* ipmi */
50499c0228dSJohn Garry 		{
50599c0228dSJohn Garry 			.hid = "IPI0001",
5064678a2d3SJohn Garry 			.pdevinfo = (struct platform_device_info []) {
5074678a2d3SJohn Garry 				{
5084678a2d3SJohn Garry 					.parent = hostdev,
5094678a2d3SJohn Garry 					.fwnode = acpi_fwnode_handle(child),
51099c0228dSJohn Garry 					.name = "hisi-lpc-ipmi",
5114678a2d3SJohn Garry 					.id = PLATFORM_DEVID_AUTO,
5124678a2d3SJohn Garry 					.res = res,
5134678a2d3SJohn Garry 					.num_res = num_res,
5144678a2d3SJohn Garry 				},
5154678a2d3SJohn Garry 			},
51699c0228dSJohn Garry 		},
517adf3457bSJohn Garry 		/* 8250-compatible uart */
518adf3457bSJohn Garry 		{
519adf3457bSJohn Garry 			.hid = "HISI1031",
5204678a2d3SJohn Garry 			.pdevinfo = (struct platform_device_info []) {
5214678a2d3SJohn Garry 				{
5224678a2d3SJohn Garry 					.parent = hostdev,
5234678a2d3SJohn Garry 					.fwnode = acpi_fwnode_handle(child),
524adf3457bSJohn Garry 					.name = "serial8250",
5254678a2d3SJohn Garry 					.id = PLATFORM_DEVID_AUTO,
5264678a2d3SJohn Garry 					.res = res,
5274678a2d3SJohn Garry 					.num_res = num_res,
5284678a2d3SJohn Garry 					.data = (struct plat_serial8250_port []) {
529adf3457bSJohn Garry 						{
530adf3457bSJohn Garry 							.iobase = res->start,
531adf3457bSJohn Garry 							.uartclk = 1843200,
532adf3457bSJohn Garry 							.iotype = UPIO_PORT,
533adf3457bSJohn Garry 							.flags = UPF_BOOT_AUTOCONF,
534adf3457bSJohn Garry 						},
535adf3457bSJohn Garry 						{}
536adf3457bSJohn Garry 					},
5374678a2d3SJohn Garry 					.size_data =  2 * sizeof(struct plat_serial8250_port),
5384678a2d3SJohn Garry 				},
5394678a2d3SJohn Garry 			},
540adf3457bSJohn Garry 		},
54199c0228dSJohn Garry 		{}
54299c0228dSJohn Garry 	};
54399c0228dSJohn Garry 
5444678a2d3SJohn Garry 	for (; cell && cell->hid; cell++) {
54599c0228dSJohn Garry 		if (!strcmp(cell->hid, hid)) {
54699c0228dSJohn Garry 			found = true;
54799c0228dSJohn Garry 			break;
548e0aa1563SJohn Garry 		}
549e0aa1563SJohn Garry 	}
550e0aa1563SJohn Garry 
55199c0228dSJohn Garry 	if (!found) {
55299c0228dSJohn Garry 		dev_warn(hostdev,
553705c0ee8SJohn Garry 			 "could not find cell for child device (%s), discarding\n",
55499c0228dSJohn Garry 			 hid);
555d6745530SRafael J. Wysocki 		return 0;
55699c0228dSJohn Garry 	}
55799c0228dSJohn Garry 
5584678a2d3SJohn Garry 	pdev = platform_device_register_full(cell->pdevinfo);
5594678a2d3SJohn Garry 	if (IS_ERR(pdev))
5604678a2d3SJohn Garry 		return PTR_ERR(pdev);
56199c0228dSJohn Garry 
56299c0228dSJohn Garry 	acpi_device_set_enumerated(child);
563e0aa1563SJohn Garry 	return 0;
564d6745530SRafael J. Wysocki }
565d6745530SRafael J. Wysocki 
566d6745530SRafael J. Wysocki /*
567d6745530SRafael J. Wysocki  * hisi_lpc_acpi_probe - probe children for ACPI FW
568d6745530SRafael J. Wysocki  * @hostdev: LPC host device pointer
569d6745530SRafael J. Wysocki  *
570d6745530SRafael J. Wysocki  * Returns 0 when successful, and a negative value for failure.
571d6745530SRafael J. Wysocki  *
572d6745530SRafael J. Wysocki  * Create a platform device per child, fixing up the resources
573d6745530SRafael J. Wysocki  * from bus addresses to Logical PIO addresses.
574d6745530SRafael J. Wysocki  *
575d6745530SRafael J. Wysocki  */
hisi_lpc_acpi_probe(struct device * hostdev)576d6745530SRafael J. Wysocki static int hisi_lpc_acpi_probe(struct device *hostdev)
577d6745530SRafael J. Wysocki {
578d6745530SRafael J. Wysocki 	int ret;
579d6745530SRafael J. Wysocki 
580d6745530SRafael J. Wysocki 	/* Only consider the children of the host */
581d6745530SRafael J. Wysocki 	ret = acpi_dev_for_each_child(ACPI_COMPANION(hostdev),
582d6745530SRafael J. Wysocki 				      hisi_lpc_acpi_add_child, hostdev);
583d6745530SRafael J. Wysocki 	if (ret)
58410e62b47SJohn Garry 		hisi_lpc_acpi_remove(hostdev);
585d6745530SRafael J. Wysocki 
58699c0228dSJohn Garry 	return ret;
587e0aa1563SJohn Garry }
588e0aa1563SJohn Garry #else
hisi_lpc_acpi_probe(struct device * dev)589e0aa1563SJohn Garry static int hisi_lpc_acpi_probe(struct device *dev)
590e0aa1563SJohn Garry {
591e0aa1563SJohn Garry 	return -ENODEV;
592e0aa1563SJohn Garry }
59310e62b47SJohn Garry 
hisi_lpc_acpi_remove(struct device * hostdev)59410e62b47SJohn Garry static void hisi_lpc_acpi_remove(struct device *hostdev)
59510e62b47SJohn Garry {
59610e62b47SJohn Garry }
597e0aa1563SJohn Garry #endif // CONFIG_ACPI
598e0aa1563SJohn Garry 
599adf38bb0SZhichang Yuan /*
600adf38bb0SZhichang Yuan  * hisi_lpc_probe - the probe callback function for hisi lpc host,
601adf38bb0SZhichang Yuan  *		   will finish all the initialization.
602adf38bb0SZhichang Yuan  * @pdev: the platform device corresponding to hisi lpc host
603adf38bb0SZhichang Yuan  *
604adf38bb0SZhichang Yuan  * Returns 0 on success, non-zero on fail.
605adf38bb0SZhichang Yuan  */
hisi_lpc_probe(struct platform_device * pdev)606adf38bb0SZhichang Yuan static int hisi_lpc_probe(struct platform_device *pdev)
607adf38bb0SZhichang Yuan {
608adf38bb0SZhichang Yuan 	struct device *dev = &pdev->dev;
609adf38bb0SZhichang Yuan 	struct logic_pio_hwaddr *range;
610adf38bb0SZhichang Yuan 	struct hisi_lpc_dev *lpcdev;
611adf38bb0SZhichang Yuan 	resource_size_t io_end;
612adf38bb0SZhichang Yuan 	int ret;
613adf38bb0SZhichang Yuan 
614adf38bb0SZhichang Yuan 	lpcdev = devm_kzalloc(dev, sizeof(*lpcdev), GFP_KERNEL);
615adf38bb0SZhichang Yuan 	if (!lpcdev)
616adf38bb0SZhichang Yuan 		return -ENOMEM;
617adf38bb0SZhichang Yuan 
618adf38bb0SZhichang Yuan 	spin_lock_init(&lpcdev->cycle_lock);
619adf38bb0SZhichang Yuan 
62023bea44cSAndy Shevchenko 	lpcdev->membase = devm_platform_ioremap_resource(pdev, 0);
621adf38bb0SZhichang Yuan 	if (IS_ERR(lpcdev->membase))
622adf38bb0SZhichang Yuan 		return PTR_ERR(lpcdev->membase);
623adf38bb0SZhichang Yuan 
624adf38bb0SZhichang Yuan 	range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL);
625adf38bb0SZhichang Yuan 	if (!range)
626adf38bb0SZhichang Yuan 		return -ENOMEM;
627adf38bb0SZhichang Yuan 
628947f11d1SAndy Shevchenko 	range->fwnode = dev_fwnode(dev);
629adf38bb0SZhichang Yuan 	range->flags = LOGIC_PIO_INDIRECT;
630adf38bb0SZhichang Yuan 	range->size = PIO_INDIRECT_SIZE;
6311b15a563SJohn Garry 	range->hostdata = lpcdev;
6321b15a563SJohn Garry 	range->ops = &hisi_lpc_ops;
6331b15a563SJohn Garry 	lpcdev->io_host = range;
634adf38bb0SZhichang Yuan 
635adf38bb0SZhichang Yuan 	ret = logic_pio_register_range(range);
636adf38bb0SZhichang Yuan 	if (ret) {
637adf38bb0SZhichang Yuan 		dev_err(dev, "register IO range failed (%d)!\n", ret);
638adf38bb0SZhichang Yuan 		return ret;
639adf38bb0SZhichang Yuan 	}
640adf38bb0SZhichang Yuan 
641adf38bb0SZhichang Yuan 	/* register the LPC host PIO resources */
642947f11d1SAndy Shevchenko 	if (is_acpi_device_node(range->fwnode))
643e0aa1563SJohn Garry 		ret = hisi_lpc_acpi_probe(dev);
644e0aa1563SJohn Garry 	else
645adf38bb0SZhichang Yuan 		ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
6461b15a563SJohn Garry 	if (ret) {
6471b15a563SJohn Garry 		logic_pio_unregister_range(range);
648adf38bb0SZhichang Yuan 		return ret;
6491b15a563SJohn Garry 	}
650adf38bb0SZhichang Yuan 
65110e62b47SJohn Garry 	dev_set_drvdata(dev, lpcdev);
65210e62b47SJohn Garry 
653adf38bb0SZhichang Yuan 	io_end = lpcdev->io_host->io_start + lpcdev->io_host->size;
654adf38bb0SZhichang Yuan 	dev_info(dev, "registered range [%pa - %pa]\n",
655adf38bb0SZhichang Yuan 		 &lpcdev->io_host->io_start, &io_end);
656adf38bb0SZhichang Yuan 
657adf38bb0SZhichang Yuan 	return ret;
658adf38bb0SZhichang Yuan }
659adf38bb0SZhichang Yuan 
hisi_lpc_remove(struct platform_device * pdev)66010e62b47SJohn Garry static int hisi_lpc_remove(struct platform_device *pdev)
66110e62b47SJohn Garry {
66210e62b47SJohn Garry 	struct device *dev = &pdev->dev;
66310e62b47SJohn Garry 	struct hisi_lpc_dev *lpcdev = dev_get_drvdata(dev);
66410e62b47SJohn Garry 	struct logic_pio_hwaddr *range = lpcdev->io_host;
66510e62b47SJohn Garry 
666947f11d1SAndy Shevchenko 	if (is_acpi_device_node(range->fwnode))
66710e62b47SJohn Garry 		hisi_lpc_acpi_remove(dev);
66810e62b47SJohn Garry 	else
66910e62b47SJohn Garry 		of_platform_depopulate(dev);
67010e62b47SJohn Garry 
67110e62b47SJohn Garry 	logic_pio_unregister_range(range);
67210e62b47SJohn Garry 
67310e62b47SJohn Garry 	return 0;
67410e62b47SJohn Garry }
67510e62b47SJohn Garry 
676adf38bb0SZhichang Yuan static const struct of_device_id hisi_lpc_of_match[] = {
677adf38bb0SZhichang Yuan 	{ .compatible = "hisilicon,hip06-lpc", },
678adf38bb0SZhichang Yuan 	{ .compatible = "hisilicon,hip07-lpc", },
679adf38bb0SZhichang Yuan 	{}
680adf38bb0SZhichang Yuan };
681adf38bb0SZhichang Yuan 
682e8cd6506SAndy Shevchenko static const struct acpi_device_id hisi_lpc_acpi_match[] = {
683e8cd6506SAndy Shevchenko 	{"HISI0191"},
684e8cd6506SAndy Shevchenko 	{}
685e8cd6506SAndy Shevchenko };
686e8cd6506SAndy Shevchenko 
687adf38bb0SZhichang Yuan static struct platform_driver hisi_lpc_driver = {
688adf38bb0SZhichang Yuan 	.driver = {
689adf38bb0SZhichang Yuan 		.name           = DRV_NAME,
690adf38bb0SZhichang Yuan 		.of_match_table = hisi_lpc_of_match,
691e8cd6506SAndy Shevchenko 		.acpi_match_table = hisi_lpc_acpi_match,
692adf38bb0SZhichang Yuan 	},
693adf38bb0SZhichang Yuan 	.probe = hisi_lpc_probe,
69410e62b47SJohn Garry 	.remove = hisi_lpc_remove,
695adf38bb0SZhichang Yuan };
696adf38bb0SZhichang Yuan builtin_platform_driver(hisi_lpc_driver);
697