162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved. 462306a36Sopenharmony_ci * Author: Zhichang Yuan <yuanzhichang@hisilicon.com> 562306a36Sopenharmony_ci * Author: Zou Rongrong <zourongrong@huawei.com> 662306a36Sopenharmony_ci * Author: John Garry <john.garry@huawei.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/acpi.h> 1062306a36Sopenharmony_ci#include <linux/console.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/logic_pio.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_platform.h> 1762306a36Sopenharmony_ci#include <linux/pci.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci#include <linux/serial_8250.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define DRV_NAME "hisi-lpc" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * Setting this bit means each IO operation will target a different port 2662306a36Sopenharmony_ci * address; 0 means repeated IO operations will use the same port, 2762306a36Sopenharmony_ci * such as BT. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci#define FG_INCRADDR_LPC 0x02 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct lpc_cycle_para { 3262306a36Sopenharmony_ci unsigned int opflags; 3362306a36Sopenharmony_ci unsigned int csize; /* data length of each operation */ 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct hisi_lpc_dev { 3762306a36Sopenharmony_ci spinlock_t cycle_lock; 3862306a36Sopenharmony_ci void __iomem *membase; 3962306a36Sopenharmony_ci struct logic_pio_hwaddr *io_host; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* The max IO cycle counts supported is four per operation at maximum */ 4362306a36Sopenharmony_ci#define LPC_MAX_DWIDTH 4 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define LPC_REG_STARTUP_SIGNAL 0x00 4662306a36Sopenharmony_ci#define LPC_REG_STARTUP_SIGNAL_START BIT(0) 4762306a36Sopenharmony_ci#define LPC_REG_OP_STATUS 0x04 4862306a36Sopenharmony_ci#define LPC_REG_OP_STATUS_IDLE BIT(0) 4962306a36Sopenharmony_ci#define LPC_REG_OP_STATUS_FINISHED BIT(1) 5062306a36Sopenharmony_ci#define LPC_REG_OP_LEN 0x10 /* LPC cycles count per start */ 5162306a36Sopenharmony_ci#define LPC_REG_CMD 0x14 5262306a36Sopenharmony_ci#define LPC_REG_CMD_OP BIT(0) /* 0: read, 1: write */ 5362306a36Sopenharmony_ci#define LPC_REG_CMD_SAMEADDR BIT(3) 5462306a36Sopenharmony_ci#define LPC_REG_ADDR 0x20 /* target address */ 5562306a36Sopenharmony_ci#define LPC_REG_WDATA 0x24 /* write FIFO */ 5662306a36Sopenharmony_ci#define LPC_REG_RDATA 0x28 /* read FIFO */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* The minimal nanosecond interval for each query on LPC cycle status */ 5962306a36Sopenharmony_ci#define LPC_NSEC_PERWAIT 100 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* 6262306a36Sopenharmony_ci * The maximum waiting time is about 128us. It is specific for stream I/O, 6362306a36Sopenharmony_ci * such as ins. 6462306a36Sopenharmony_ci * 6562306a36Sopenharmony_ci * The fastest IO cycle time is about 390ns, but the worst case will wait 6662306a36Sopenharmony_ci * for extra 256 lpc clocks, so (256 + 13) * 30ns = 8 us. The maximum burst 6762306a36Sopenharmony_ci * cycles is 16. So, the maximum waiting time is about 128us under worst 6862306a36Sopenharmony_ci * case. 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * Choose 1300 as the maximum. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci#define LPC_MAX_WAITCNT 1300 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* About 10us. This is specific for single IO operations, such as inb */ 7562306a36Sopenharmony_ci#define LPC_PEROP_WAITCNT 100 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int wait_lpc_idle(void __iomem *mbase, unsigned int waitcnt) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci u32 status; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci do { 8262306a36Sopenharmony_ci status = readl(mbase + LPC_REG_OP_STATUS); 8362306a36Sopenharmony_ci if (status & LPC_REG_OP_STATUS_IDLE) 8462306a36Sopenharmony_ci return (status & LPC_REG_OP_STATUS_FINISHED) ? 0 : -EIO; 8562306a36Sopenharmony_ci ndelay(LPC_NSEC_PERWAIT); 8662306a36Sopenharmony_ci } while (--waitcnt); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return -ETIMEDOUT; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * hisi_lpc_target_in - trigger a series of LPC cycles for read operation 9362306a36Sopenharmony_ci * @lpcdev: pointer to hisi lpc device 9462306a36Sopenharmony_ci * @para: some parameters used to control the lpc I/O operations 9562306a36Sopenharmony_ci * @addr: the lpc I/O target port address 9662306a36Sopenharmony_ci * @buf: where the read back data is stored 9762306a36Sopenharmony_ci * @opcnt: how many I/O operations required, i.e. data width 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci * Returns 0 on success, non-zero on fail. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_cistatic int hisi_lpc_target_in(struct hisi_lpc_dev *lpcdev, 10262306a36Sopenharmony_ci struct lpc_cycle_para *para, unsigned long addr, 10362306a36Sopenharmony_ci unsigned char *buf, unsigned long opcnt) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci unsigned int cmd_word; 10662306a36Sopenharmony_ci unsigned int waitcnt; 10762306a36Sopenharmony_ci unsigned long flags; 10862306a36Sopenharmony_ci int ret; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (!buf || !opcnt || !para || !para->csize || !lpcdev) 11162306a36Sopenharmony_ci return -EINVAL; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci cmd_word = 0; /* IO mode, Read */ 11462306a36Sopenharmony_ci waitcnt = LPC_PEROP_WAITCNT; 11562306a36Sopenharmony_ci if (!(para->opflags & FG_INCRADDR_LPC)) { 11662306a36Sopenharmony_ci cmd_word |= LPC_REG_CMD_SAMEADDR; 11762306a36Sopenharmony_ci waitcnt = LPC_MAX_WAITCNT; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* whole operation must be atomic */ 12162306a36Sopenharmony_ci spin_lock_irqsave(&lpcdev->cycle_lock, flags); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci writel_relaxed(opcnt, lpcdev->membase + LPC_REG_OP_LEN); 12462306a36Sopenharmony_ci writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD); 12562306a36Sopenharmony_ci writel_relaxed(addr, lpcdev->membase + LPC_REG_ADDR); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci writel(LPC_REG_STARTUP_SIGNAL_START, 12862306a36Sopenharmony_ci lpcdev->membase + LPC_REG_STARTUP_SIGNAL); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* whether the operation is finished */ 13162306a36Sopenharmony_ci ret = wait_lpc_idle(lpcdev->membase, waitcnt); 13262306a36Sopenharmony_ci if (ret) { 13362306a36Sopenharmony_ci spin_unlock_irqrestore(&lpcdev->cycle_lock, flags); 13462306a36Sopenharmony_ci return ret; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci readsb(lpcdev->membase + LPC_REG_RDATA, buf, opcnt); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci spin_unlock_irqrestore(&lpcdev->cycle_lock, flags); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* 14562306a36Sopenharmony_ci * hisi_lpc_target_out - trigger a series of LPC cycles for write operation 14662306a36Sopenharmony_ci * @lpcdev: pointer to hisi lpc device 14762306a36Sopenharmony_ci * @para: some parameters used to control the lpc I/O operations 14862306a36Sopenharmony_ci * @addr: the lpc I/O target port address 14962306a36Sopenharmony_ci * @buf: where the data to be written is stored 15062306a36Sopenharmony_ci * @opcnt: how many I/O operations required, i.e. data width 15162306a36Sopenharmony_ci * 15262306a36Sopenharmony_ci * Returns 0 on success, non-zero on fail. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_cistatic int hisi_lpc_target_out(struct hisi_lpc_dev *lpcdev, 15562306a36Sopenharmony_ci struct lpc_cycle_para *para, unsigned long addr, 15662306a36Sopenharmony_ci const unsigned char *buf, unsigned long opcnt) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci unsigned int waitcnt; 15962306a36Sopenharmony_ci unsigned long flags; 16062306a36Sopenharmony_ci u32 cmd_word; 16162306a36Sopenharmony_ci int ret; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (!buf || !opcnt || !para || !lpcdev) 16462306a36Sopenharmony_ci return -EINVAL; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* default is increasing address */ 16762306a36Sopenharmony_ci cmd_word = LPC_REG_CMD_OP; /* IO mode, write */ 16862306a36Sopenharmony_ci waitcnt = LPC_PEROP_WAITCNT; 16962306a36Sopenharmony_ci if (!(para->opflags & FG_INCRADDR_LPC)) { 17062306a36Sopenharmony_ci cmd_word |= LPC_REG_CMD_SAMEADDR; 17162306a36Sopenharmony_ci waitcnt = LPC_MAX_WAITCNT; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci spin_lock_irqsave(&lpcdev->cycle_lock, flags); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci writel_relaxed(opcnt, lpcdev->membase + LPC_REG_OP_LEN); 17762306a36Sopenharmony_ci writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD); 17862306a36Sopenharmony_ci writel_relaxed(addr, lpcdev->membase + LPC_REG_ADDR); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci writesb(lpcdev->membase + LPC_REG_WDATA, buf, opcnt); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci writel(LPC_REG_STARTUP_SIGNAL_START, 18362306a36Sopenharmony_ci lpcdev->membase + LPC_REG_STARTUP_SIGNAL); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* whether the operation is finished */ 18662306a36Sopenharmony_ci ret = wait_lpc_idle(lpcdev->membase, waitcnt); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci spin_unlock_irqrestore(&lpcdev->cycle_lock, flags); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return ret; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic unsigned long hisi_lpc_pio_to_addr(struct hisi_lpc_dev *lpcdev, 19462306a36Sopenharmony_ci unsigned long pio) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci return pio - lpcdev->io_host->io_start + lpcdev->io_host->hw_start; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/* 20062306a36Sopenharmony_ci * hisi_lpc_comm_in - input the data in a single operation 20162306a36Sopenharmony_ci * @hostdata: pointer to the device information relevant to LPC controller 20262306a36Sopenharmony_ci * @pio: the target I/O port address 20362306a36Sopenharmony_ci * @dwidth: the data length required to read from the target I/O port 20462306a36Sopenharmony_ci * 20562306a36Sopenharmony_ci * When success, data is returned. Otherwise, ~0 is returned. 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_cistatic u32 hisi_lpc_comm_in(void *hostdata, unsigned long pio, size_t dwidth) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct hisi_lpc_dev *lpcdev = hostdata; 21062306a36Sopenharmony_ci struct lpc_cycle_para iopara; 21162306a36Sopenharmony_ci unsigned long addr; 21262306a36Sopenharmony_ci __le32 rd_data = 0; 21362306a36Sopenharmony_ci int ret; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH) 21662306a36Sopenharmony_ci return ~0; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci addr = hisi_lpc_pio_to_addr(lpcdev, pio); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci iopara.opflags = FG_INCRADDR_LPC; 22162306a36Sopenharmony_ci iopara.csize = dwidth; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ret = hisi_lpc_target_in(lpcdev, &iopara, addr, 22462306a36Sopenharmony_ci (unsigned char *)&rd_data, dwidth); 22562306a36Sopenharmony_ci if (ret) 22662306a36Sopenharmony_ci return ~0; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return le32_to_cpu(rd_data); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/* 23262306a36Sopenharmony_ci * hisi_lpc_comm_out - output the data in a single operation 23362306a36Sopenharmony_ci * @hostdata: pointer to the device information relevant to LPC controller 23462306a36Sopenharmony_ci * @pio: the target I/O port address 23562306a36Sopenharmony_ci * @val: a value to be output from caller, maximum is four bytes 23662306a36Sopenharmony_ci * @dwidth: the data width required writing to the target I/O port 23762306a36Sopenharmony_ci * 23862306a36Sopenharmony_ci * This function corresponds to out(b,w,l) only. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_cistatic void hisi_lpc_comm_out(void *hostdata, unsigned long pio, 24162306a36Sopenharmony_ci u32 val, size_t dwidth) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct hisi_lpc_dev *lpcdev = hostdata; 24462306a36Sopenharmony_ci struct lpc_cycle_para iopara; 24562306a36Sopenharmony_ci const unsigned char *buf; 24662306a36Sopenharmony_ci unsigned long addr; 24762306a36Sopenharmony_ci __le32 _val = cpu_to_le32(val); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH) 25062306a36Sopenharmony_ci return; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci buf = (const unsigned char *)&_val; 25362306a36Sopenharmony_ci addr = hisi_lpc_pio_to_addr(lpcdev, pio); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci iopara.opflags = FG_INCRADDR_LPC; 25662306a36Sopenharmony_ci iopara.csize = dwidth; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci hisi_lpc_target_out(lpcdev, &iopara, addr, buf, dwidth); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci/* 26262306a36Sopenharmony_ci * hisi_lpc_comm_ins - input the data in the buffer in multiple operations 26362306a36Sopenharmony_ci * @hostdata: pointer to the device information relevant to LPC controller 26462306a36Sopenharmony_ci * @pio: the target I/O port address 26562306a36Sopenharmony_ci * @buffer: a buffer where read/input data bytes are stored 26662306a36Sopenharmony_ci * @dwidth: the data width required writing to the target I/O port 26762306a36Sopenharmony_ci * @count: how many data units whose length is dwidth will be read 26862306a36Sopenharmony_ci * 26962306a36Sopenharmony_ci * When success, the data read back is stored in buffer pointed by buffer. 27062306a36Sopenharmony_ci * Returns 0 on success, -errno otherwise. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_cistatic u32 hisi_lpc_comm_ins(void *hostdata, unsigned long pio, void *buffer, 27362306a36Sopenharmony_ci size_t dwidth, unsigned int count) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct hisi_lpc_dev *lpcdev = hostdata; 27662306a36Sopenharmony_ci unsigned char *buf = buffer; 27762306a36Sopenharmony_ci struct lpc_cycle_para iopara; 27862306a36Sopenharmony_ci unsigned long addr; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (!lpcdev || !buf || !count || !dwidth || dwidth > LPC_MAX_DWIDTH) 28162306a36Sopenharmony_ci return -EINVAL; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci iopara.opflags = 0; 28462306a36Sopenharmony_ci if (dwidth > 1) 28562306a36Sopenharmony_ci iopara.opflags |= FG_INCRADDR_LPC; 28662306a36Sopenharmony_ci iopara.csize = dwidth; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci addr = hisi_lpc_pio_to_addr(lpcdev, pio); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci do { 29162306a36Sopenharmony_ci int ret; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci ret = hisi_lpc_target_in(lpcdev, &iopara, addr, buf, dwidth); 29462306a36Sopenharmony_ci if (ret) 29562306a36Sopenharmony_ci return ret; 29662306a36Sopenharmony_ci buf += dwidth; 29762306a36Sopenharmony_ci } while (--count); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return 0; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci/* 30362306a36Sopenharmony_ci * hisi_lpc_comm_outs - output the data in the buffer in multiple operations 30462306a36Sopenharmony_ci * @hostdata: pointer to the device information relevant to LPC controller 30562306a36Sopenharmony_ci * @pio: the target I/O port address 30662306a36Sopenharmony_ci * @buffer: a buffer where write/output data bytes are stored 30762306a36Sopenharmony_ci * @dwidth: the data width required writing to the target I/O port 30862306a36Sopenharmony_ci * @count: how many data units whose length is dwidth will be written 30962306a36Sopenharmony_ci */ 31062306a36Sopenharmony_cistatic void hisi_lpc_comm_outs(void *hostdata, unsigned long pio, 31162306a36Sopenharmony_ci const void *buffer, size_t dwidth, 31262306a36Sopenharmony_ci unsigned int count) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct hisi_lpc_dev *lpcdev = hostdata; 31562306a36Sopenharmony_ci struct lpc_cycle_para iopara; 31662306a36Sopenharmony_ci const unsigned char *buf = buffer; 31762306a36Sopenharmony_ci unsigned long addr; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (!lpcdev || !buf || !count || !dwidth || dwidth > LPC_MAX_DWIDTH) 32062306a36Sopenharmony_ci return; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci iopara.opflags = 0; 32362306a36Sopenharmony_ci if (dwidth > 1) 32462306a36Sopenharmony_ci iopara.opflags |= FG_INCRADDR_LPC; 32562306a36Sopenharmony_ci iopara.csize = dwidth; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci addr = hisi_lpc_pio_to_addr(lpcdev, pio); 32862306a36Sopenharmony_ci do { 32962306a36Sopenharmony_ci if (hisi_lpc_target_out(lpcdev, &iopara, addr, buf, dwidth)) 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci buf += dwidth; 33262306a36Sopenharmony_ci } while (--count); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic const struct logic_pio_host_ops hisi_lpc_ops = { 33662306a36Sopenharmony_ci .in = hisi_lpc_comm_in, 33762306a36Sopenharmony_ci .out = hisi_lpc_comm_out, 33862306a36Sopenharmony_ci .ins = hisi_lpc_comm_ins, 33962306a36Sopenharmony_ci .outs = hisi_lpc_comm_outs, 34062306a36Sopenharmony_ci}; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci#ifdef CONFIG_ACPI 34362306a36Sopenharmony_cistatic int hisi_lpc_acpi_xlat_io_res(struct acpi_device *adev, 34462306a36Sopenharmony_ci struct acpi_device *host, 34562306a36Sopenharmony_ci struct resource *res) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci unsigned long sys_port; 34862306a36Sopenharmony_ci resource_size_t len = resource_size(res); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci sys_port = logic_pio_trans_hwaddr(acpi_fwnode_handle(host), res->start, len); 35162306a36Sopenharmony_ci if (sys_port == ~0UL) 35262306a36Sopenharmony_ci return -EFAULT; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci res->start = sys_port; 35562306a36Sopenharmony_ci res->end = sys_port + len; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci/* 36162306a36Sopenharmony_ci * Released firmware describes the IO port max address as 0x3fff, which is 36262306a36Sopenharmony_ci * the max host bus address. Fixup to a proper range. This will probably 36362306a36Sopenharmony_ci * never be fixed in firmware. 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_cistatic void hisi_lpc_acpi_fixup_child_resource(struct device *hostdev, 36662306a36Sopenharmony_ci struct resource *r) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci if (r->end != 0x3fff) 36962306a36Sopenharmony_ci return; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (r->start == 0xe4) 37262306a36Sopenharmony_ci r->end = 0xe4 + 0x04 - 1; 37362306a36Sopenharmony_ci else if (r->start == 0x2f8) 37462306a36Sopenharmony_ci r->end = 0x2f8 + 0x08 - 1; 37562306a36Sopenharmony_ci else 37662306a36Sopenharmony_ci dev_warn(hostdev, "unrecognised resource %pR to fixup, ignoring\n", 37762306a36Sopenharmony_ci r); 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/* 38162306a36Sopenharmony_ci * hisi_lpc_acpi_set_io_res - set the resources for a child 38262306a36Sopenharmony_ci * @adev: ACPI companion of the device node to be updated the I/O resource 38362306a36Sopenharmony_ci * @hostdev: the device node associated with host controller 38462306a36Sopenharmony_ci * @res: double pointer to be set to the address of translated resources 38562306a36Sopenharmony_ci * @num_res: pointer to variable to hold the number of translated resources 38662306a36Sopenharmony_ci * 38762306a36Sopenharmony_ci * Returns 0 when successful, and a negative value for failure. 38862306a36Sopenharmony_ci * 38962306a36Sopenharmony_ci * For a given host controller, each child device will have an associated 39062306a36Sopenharmony_ci * host-relative address resource. This function will return the translated 39162306a36Sopenharmony_ci * logical PIO addresses for each child devices resources. 39262306a36Sopenharmony_ci */ 39362306a36Sopenharmony_cistatic int hisi_lpc_acpi_set_io_res(struct acpi_device *adev, 39462306a36Sopenharmony_ci struct device *hostdev, 39562306a36Sopenharmony_ci const struct resource **res, int *num_res) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct acpi_device *host = to_acpi_device(adev->dev.parent); 39862306a36Sopenharmony_ci struct resource_entry *rentry; 39962306a36Sopenharmony_ci LIST_HEAD(resource_list); 40062306a36Sopenharmony_ci struct resource *resources; 40162306a36Sopenharmony_ci int count; 40262306a36Sopenharmony_ci int i; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (!adev->status.present) { 40562306a36Sopenharmony_ci dev_dbg(&adev->dev, "device is not present\n"); 40662306a36Sopenharmony_ci return -EIO; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (acpi_device_enumerated(adev)) { 41062306a36Sopenharmony_ci dev_dbg(&adev->dev, "has been enumerated\n"); 41162306a36Sopenharmony_ci return -EIO; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* 41562306a36Sopenharmony_ci * The following code segment to retrieve the resources is common to 41662306a36Sopenharmony_ci * acpi_create_platform_device(), so consider a common helper function 41762306a36Sopenharmony_ci * in future. 41862306a36Sopenharmony_ci */ 41962306a36Sopenharmony_ci count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); 42062306a36Sopenharmony_ci if (count <= 0) { 42162306a36Sopenharmony_ci dev_dbg(&adev->dev, "failed to get resources\n"); 42262306a36Sopenharmony_ci return count ? count : -EIO; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci resources = devm_kcalloc(hostdev, count, sizeof(*resources), 42662306a36Sopenharmony_ci GFP_KERNEL); 42762306a36Sopenharmony_ci if (!resources) { 42862306a36Sopenharmony_ci dev_warn(hostdev, "could not allocate memory for %d resources\n", 42962306a36Sopenharmony_ci count); 43062306a36Sopenharmony_ci acpi_dev_free_resource_list(&resource_list); 43162306a36Sopenharmony_ci return -ENOMEM; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci count = 0; 43462306a36Sopenharmony_ci list_for_each_entry(rentry, &resource_list, node) { 43562306a36Sopenharmony_ci resources[count] = *rentry->res; 43662306a36Sopenharmony_ci hisi_lpc_acpi_fixup_child_resource(hostdev, &resources[count]); 43762306a36Sopenharmony_ci count++; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci acpi_dev_free_resource_list(&resource_list); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* translate the I/O resources */ 44362306a36Sopenharmony_ci for (i = 0; i < count; i++) { 44462306a36Sopenharmony_ci int ret; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (!(resources[i].flags & IORESOURCE_IO)) 44762306a36Sopenharmony_ci continue; 44862306a36Sopenharmony_ci ret = hisi_lpc_acpi_xlat_io_res(adev, host, &resources[i]); 44962306a36Sopenharmony_ci if (ret) { 45062306a36Sopenharmony_ci dev_err(&adev->dev, "translate IO range %pR failed (%d)\n", 45162306a36Sopenharmony_ci &resources[i], ret); 45262306a36Sopenharmony_ci return ret; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci *res = resources; 45662306a36Sopenharmony_ci *num_res = count; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic int hisi_lpc_acpi_remove_subdev(struct device *dev, void *unused) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci platform_device_unregister(to_platform_device(dev)); 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic int hisi_lpc_acpi_clear_enumerated(struct acpi_device *adev, void *not_used) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci acpi_device_clear_enumerated(adev); 47062306a36Sopenharmony_ci return 0; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistruct hisi_lpc_acpi_cell { 47462306a36Sopenharmony_ci const char *hid; 47562306a36Sopenharmony_ci const struct platform_device_info *pdevinfo; 47662306a36Sopenharmony_ci}; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic void hisi_lpc_acpi_remove(struct device *hostdev) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci device_for_each_child(hostdev, NULL, hisi_lpc_acpi_remove_subdev); 48162306a36Sopenharmony_ci acpi_dev_for_each_child(ACPI_COMPANION(hostdev), 48262306a36Sopenharmony_ci hisi_lpc_acpi_clear_enumerated, NULL); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int hisi_lpc_acpi_add_child(struct acpi_device *child, void *data) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci const char *hid = acpi_device_hid(child); 48862306a36Sopenharmony_ci struct device *hostdev = data; 48962306a36Sopenharmony_ci const struct hisi_lpc_acpi_cell *cell; 49062306a36Sopenharmony_ci struct platform_device *pdev; 49162306a36Sopenharmony_ci const struct resource *res; 49262306a36Sopenharmony_ci bool found = false; 49362306a36Sopenharmony_ci int num_res; 49462306a36Sopenharmony_ci int ret; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ret = hisi_lpc_acpi_set_io_res(child, hostdev, &res, &num_res); 49762306a36Sopenharmony_ci if (ret) { 49862306a36Sopenharmony_ci dev_warn(hostdev, "set resource fail (%d)\n", ret); 49962306a36Sopenharmony_ci return ret; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci cell = (struct hisi_lpc_acpi_cell []){ 50362306a36Sopenharmony_ci /* ipmi */ 50462306a36Sopenharmony_ci { 50562306a36Sopenharmony_ci .hid = "IPI0001", 50662306a36Sopenharmony_ci .pdevinfo = (struct platform_device_info []) { 50762306a36Sopenharmony_ci { 50862306a36Sopenharmony_ci .parent = hostdev, 50962306a36Sopenharmony_ci .fwnode = acpi_fwnode_handle(child), 51062306a36Sopenharmony_ci .name = "hisi-lpc-ipmi", 51162306a36Sopenharmony_ci .id = PLATFORM_DEVID_AUTO, 51262306a36Sopenharmony_ci .res = res, 51362306a36Sopenharmony_ci .num_res = num_res, 51462306a36Sopenharmony_ci }, 51562306a36Sopenharmony_ci }, 51662306a36Sopenharmony_ci }, 51762306a36Sopenharmony_ci /* 8250-compatible uart */ 51862306a36Sopenharmony_ci { 51962306a36Sopenharmony_ci .hid = "HISI1031", 52062306a36Sopenharmony_ci .pdevinfo = (struct platform_device_info []) { 52162306a36Sopenharmony_ci { 52262306a36Sopenharmony_ci .parent = hostdev, 52362306a36Sopenharmony_ci .fwnode = acpi_fwnode_handle(child), 52462306a36Sopenharmony_ci .name = "serial8250", 52562306a36Sopenharmony_ci .id = PLATFORM_DEVID_AUTO, 52662306a36Sopenharmony_ci .res = res, 52762306a36Sopenharmony_ci .num_res = num_res, 52862306a36Sopenharmony_ci .data = (struct plat_serial8250_port []) { 52962306a36Sopenharmony_ci { 53062306a36Sopenharmony_ci .iobase = res->start, 53162306a36Sopenharmony_ci .uartclk = 1843200, 53262306a36Sopenharmony_ci .iotype = UPIO_PORT, 53362306a36Sopenharmony_ci .flags = UPF_BOOT_AUTOCONF, 53462306a36Sopenharmony_ci }, 53562306a36Sopenharmony_ci {} 53662306a36Sopenharmony_ci }, 53762306a36Sopenharmony_ci .size_data = 2 * sizeof(struct plat_serial8250_port), 53862306a36Sopenharmony_ci }, 53962306a36Sopenharmony_ci }, 54062306a36Sopenharmony_ci }, 54162306a36Sopenharmony_ci {} 54262306a36Sopenharmony_ci }; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci for (; cell && cell->hid; cell++) { 54562306a36Sopenharmony_ci if (!strcmp(cell->hid, hid)) { 54662306a36Sopenharmony_ci found = true; 54762306a36Sopenharmony_ci break; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (!found) { 55262306a36Sopenharmony_ci dev_warn(hostdev, 55362306a36Sopenharmony_ci "could not find cell for child device (%s), discarding\n", 55462306a36Sopenharmony_ci hid); 55562306a36Sopenharmony_ci return 0; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci pdev = platform_device_register_full(cell->pdevinfo); 55962306a36Sopenharmony_ci if (IS_ERR(pdev)) 56062306a36Sopenharmony_ci return PTR_ERR(pdev); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci acpi_device_set_enumerated(child); 56362306a36Sopenharmony_ci return 0; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci/* 56762306a36Sopenharmony_ci * hisi_lpc_acpi_probe - probe children for ACPI FW 56862306a36Sopenharmony_ci * @hostdev: LPC host device pointer 56962306a36Sopenharmony_ci * 57062306a36Sopenharmony_ci * Returns 0 when successful, and a negative value for failure. 57162306a36Sopenharmony_ci * 57262306a36Sopenharmony_ci * Create a platform device per child, fixing up the resources 57362306a36Sopenharmony_ci * from bus addresses to Logical PIO addresses. 57462306a36Sopenharmony_ci * 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_cistatic int hisi_lpc_acpi_probe(struct device *hostdev) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci int ret; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* Only consider the children of the host */ 58162306a36Sopenharmony_ci ret = acpi_dev_for_each_child(ACPI_COMPANION(hostdev), 58262306a36Sopenharmony_ci hisi_lpc_acpi_add_child, hostdev); 58362306a36Sopenharmony_ci if (ret) 58462306a36Sopenharmony_ci hisi_lpc_acpi_remove(hostdev); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci return ret; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci#else 58962306a36Sopenharmony_cistatic int hisi_lpc_acpi_probe(struct device *dev) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci return -ENODEV; 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic void hisi_lpc_acpi_remove(struct device *hostdev) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci#endif // CONFIG_ACPI 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci/* 60062306a36Sopenharmony_ci * hisi_lpc_probe - the probe callback function for hisi lpc host, 60162306a36Sopenharmony_ci * will finish all the initialization. 60262306a36Sopenharmony_ci * @pdev: the platform device corresponding to hisi lpc host 60362306a36Sopenharmony_ci * 60462306a36Sopenharmony_ci * Returns 0 on success, non-zero on fail. 60562306a36Sopenharmony_ci */ 60662306a36Sopenharmony_cistatic int hisi_lpc_probe(struct platform_device *pdev) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 60962306a36Sopenharmony_ci struct logic_pio_hwaddr *range; 61062306a36Sopenharmony_ci struct hisi_lpc_dev *lpcdev; 61162306a36Sopenharmony_ci resource_size_t io_end; 61262306a36Sopenharmony_ci int ret; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci lpcdev = devm_kzalloc(dev, sizeof(*lpcdev), GFP_KERNEL); 61562306a36Sopenharmony_ci if (!lpcdev) 61662306a36Sopenharmony_ci return -ENOMEM; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci spin_lock_init(&lpcdev->cycle_lock); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci lpcdev->membase = devm_platform_ioremap_resource(pdev, 0); 62162306a36Sopenharmony_ci if (IS_ERR(lpcdev->membase)) 62262306a36Sopenharmony_ci return PTR_ERR(lpcdev->membase); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL); 62562306a36Sopenharmony_ci if (!range) 62662306a36Sopenharmony_ci return -ENOMEM; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci range->fwnode = dev_fwnode(dev); 62962306a36Sopenharmony_ci range->flags = LOGIC_PIO_INDIRECT; 63062306a36Sopenharmony_ci range->size = PIO_INDIRECT_SIZE; 63162306a36Sopenharmony_ci range->hostdata = lpcdev; 63262306a36Sopenharmony_ci range->ops = &hisi_lpc_ops; 63362306a36Sopenharmony_ci lpcdev->io_host = range; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci ret = logic_pio_register_range(range); 63662306a36Sopenharmony_ci if (ret) { 63762306a36Sopenharmony_ci dev_err(dev, "register IO range failed (%d)!\n", ret); 63862306a36Sopenharmony_ci return ret; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* register the LPC host PIO resources */ 64262306a36Sopenharmony_ci if (is_acpi_device_node(range->fwnode)) 64362306a36Sopenharmony_ci ret = hisi_lpc_acpi_probe(dev); 64462306a36Sopenharmony_ci else 64562306a36Sopenharmony_ci ret = of_platform_populate(dev->of_node, NULL, NULL, dev); 64662306a36Sopenharmony_ci if (ret) { 64762306a36Sopenharmony_ci logic_pio_unregister_range(range); 64862306a36Sopenharmony_ci return ret; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci dev_set_drvdata(dev, lpcdev); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci io_end = lpcdev->io_host->io_start + lpcdev->io_host->size; 65462306a36Sopenharmony_ci dev_info(dev, "registered range [%pa - %pa]\n", 65562306a36Sopenharmony_ci &lpcdev->io_host->io_start, &io_end); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return ret; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic int hisi_lpc_remove(struct platform_device *pdev) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 66362306a36Sopenharmony_ci struct hisi_lpc_dev *lpcdev = dev_get_drvdata(dev); 66462306a36Sopenharmony_ci struct logic_pio_hwaddr *range = lpcdev->io_host; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (is_acpi_device_node(range->fwnode)) 66762306a36Sopenharmony_ci hisi_lpc_acpi_remove(dev); 66862306a36Sopenharmony_ci else 66962306a36Sopenharmony_ci of_platform_depopulate(dev); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci logic_pio_unregister_range(range); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci return 0; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic const struct of_device_id hisi_lpc_of_match[] = { 67762306a36Sopenharmony_ci { .compatible = "hisilicon,hip06-lpc", }, 67862306a36Sopenharmony_ci { .compatible = "hisilicon,hip07-lpc", }, 67962306a36Sopenharmony_ci {} 68062306a36Sopenharmony_ci}; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic const struct acpi_device_id hisi_lpc_acpi_match[] = { 68362306a36Sopenharmony_ci {"HISI0191"}, 68462306a36Sopenharmony_ci {} 68562306a36Sopenharmony_ci}; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic struct platform_driver hisi_lpc_driver = { 68862306a36Sopenharmony_ci .driver = { 68962306a36Sopenharmony_ci .name = DRV_NAME, 69062306a36Sopenharmony_ci .of_match_table = hisi_lpc_of_match, 69162306a36Sopenharmony_ci .acpi_match_table = hisi_lpc_acpi_match, 69262306a36Sopenharmony_ci }, 69362306a36Sopenharmony_ci .probe = hisi_lpc_probe, 69462306a36Sopenharmony_ci .remove = hisi_lpc_remove, 69562306a36Sopenharmony_ci}; 69662306a36Sopenharmony_cibuiltin_platform_driver(hisi_lpc_driver); 697