aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRongrongZou <zourongrong@huawei.com>2016-10-18 12:52:57 +0800
committerGraeme Gregory <graeme.gregory@linaro.org>2017-06-06 10:00:42 +0100
commit640069fb07ee30941beb0ad0495111332ab84f3d (patch)
tree6ed17893cf2b9d2398f0feebf55d9e50d9c1da55
parent3b9a01f7cd2eb37e6fc74e172b53394ba8460fcc (diff)
downloadleg-kernel-640069fb07ee30941beb0ad0495111332ab84f3d.tar.gz
ARM64 LPC: Hisilicon Hip06 LPC driver implementation
We only implement io cycles here, we hook the lpc_io_write_byte and lpc_io_read_byte to inb/outb. So the drivers(ipmi/uart) which access the legacy ISA I/O port need no modification. Signed-off-by: RongrongZou <zourongrong@huawei.com> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org> Signed-off-by: Kefeng Wang <wangkefeng.wang@huawei.com> Signed-off-by: Xinliang Liu <xinliang.liu@linaro.org> Conflicts: drivers/bus/Makefile
-rw-r--r--drivers/bus/Kconfig10
-rw-r--r--drivers/bus/Makefile2
-rw-r--r--drivers/bus/hisi_lpc.c199
3 files changed, 211 insertions, 0 deletions
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 0a52da439abf..f252268be3a7 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -92,6 +92,16 @@ config MVEBU_MBUS
Driver needed for the MBus configuration on Marvell EBU SoCs
(Kirkwood, Dove, Orion5x, MV78XX0 and Armada 370/XP).
+config HISILICON_LPC
+ bool "Workaround for nonstandard ISA I/O space on Hisilicon Hip0X SoC"
+ depends on ARCH_HISI || COMPILE_TEST
+ select ARM64_INDIRECT_PIO
+ help
+ This driver support for some legacy driver that use the ISA I/O ports,
+ in arm64, there is no real ISA I/O ports, so we hook the drivers to in/out
+ func in arch/arm64/include/asm/io.h, when upper layer drivers call in/out
+ func to access I/O ports, the lpc driver is called indeed.
+
config OMAP_INTERCONNECT
tristate "OMAP INTERCONNECT DRIVER"
depends on ARCH_OMAP2PLUS
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index cc6364bec054..a8d69ecb8e41 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -23,4 +23,6 @@ obj-$(CONFIG_TEGRA_GMI) += tegra-gmi.o
obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o
+obj-$(CONFIG_HISILICON_LPC) += hisi_lpc.o
+
obj-$(CONFIG_DA8XX_MSTPRI) += da8xx-mstpri.o
diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
new file mode 100644
index 000000000000..501a36148d55
--- /dev/null
+++ b/drivers/bus/hisi_lpc.c
@@ -0,0 +1,199 @@
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/version.h>
+#include <linux/of_platform.h>
+#include <linux/iopoll.h>
+
+
+#define LPC_REG_START (0x00)
+#define LPC_REG_OP_STATUS (0x04)
+#define LPC_REG_IRQ_ST (0x08)
+#define LPC_REG_OP_LEN (0x10)
+#define LPC_REG_CMD (0x14)
+#define LPC_REG_ADDR (0x20)
+#define LPC_REG_WDATA (0x24)
+#define LPC_REG_RDATA (0x28)
+
+#define LPC_CMD_SAMEADDR_SING (0x00000008)
+#define LPC_CMD_SAMEADDR_INC (0x00000000)
+#define LPC_CMD_TYPE_IO (0x00000000)
+#define LPC_CMD_TYPE_MEM (0x00000002)
+#define LPC_CMD_TYPE_FWH (0x00000004)
+#define LPC_CMD_WRITE (0x00000001)
+#define LPC_CMD_READ (0x00000000)
+
+#define LPC_IRQ_CLEAR (0x02)
+#define LPC_IRQ_OCCURRED (0x02)
+#define LPC_STATUS_DILE (0x01)
+#define LPC_OP_FINISHED (0x02)
+#define START_WORK (0x01)
+
+
+struct lpc_dev {
+ spinlock_t lock;
+ void __iomem *regs;
+ struct device *dev;
+};
+
+static struct lpc_dev *lpc_dev;
+
+static int lpc_master_write(unsigned int addr, unsigned char *buf,
+ unsigned int len)
+{
+ unsigned int i;
+ unsigned int lpc_cmd_value;
+ unsigned int lpc_op_state_value;
+ u32 tmp;
+
+ /* para check */
+ if (!buf || !len)
+ return -EINVAL;
+
+ writel(LPC_IRQ_CLEAR, lpc_dev->regs + LPC_REG_IRQ_ST);
+ if (readl_poll_timeout_atomic(lpc_dev->regs + LPC_REG_OP_STATUS, tmp,
+ tmp & LPC_STATUS_DILE, 1, 2))
+ return -ETIME;
+
+ /* set lpc master write,cycle type and slv access mode */
+ lpc_cmd_value = LPC_CMD_WRITE | LPC_CMD_SAMEADDR_SING | LPC_CMD_TYPE_IO;
+ writel(lpc_cmd_value, lpc_dev->regs + LPC_REG_CMD);
+
+ /* set lpc op len */
+ writel(len, lpc_dev->regs + LPC_REG_OP_LEN);
+
+ /* Set write data */
+ for (i = 0; i < len; i++)
+ writel(buf[i], lpc_dev->regs + LPC_REG_WDATA);
+
+ /* set lpc addr config */
+ writel(addr, lpc_dev->regs + LPC_REG_ADDR);
+
+ /* set lpc start work */
+ writel(START_WORK, lpc_dev->regs + LPC_REG_START);
+ if (readl_poll_timeout_atomic(lpc_dev->regs + LPC_REG_IRQ_ST, tmp,
+ tmp & LPC_IRQ_OCCURRED, 1, 2))
+ return -ETIME;
+ writel(LPC_IRQ_CLEAR, lpc_dev->regs + LPC_REG_IRQ_ST);
+
+ lpc_op_state_value = readl(lpc_dev->regs + LPC_REG_OP_STATUS);
+
+ if (lpc_op_state_value & LPC_OP_FINISHED)
+ return 0;
+
+ return -EIO;
+}
+
+static int lpc_master_read(unsigned int addr, unsigned char *buf,
+ unsigned int len)
+{
+ unsigned int i;
+ unsigned int lpc_cmd_value;
+ unsigned int lpc_op_state_value;
+ u32 tmp;
+
+ /* para check */
+ if (!buf || !len)
+ return -EINVAL;
+ writel(LPC_IRQ_CLEAR, lpc_dev->regs + LPC_REG_IRQ_ST);
+ if (readl_poll_timeout_atomic(lpc_dev->regs + LPC_REG_OP_STATUS, tmp,
+ tmp & LPC_STATUS_DILE, 1, 2))
+ return -ETIME;
+
+ /* set lpc master read, cycle type and slv access mode */
+ lpc_cmd_value = LPC_CMD_READ | LPC_CMD_SAMEADDR_SING | LPC_CMD_TYPE_IO;
+ writel(lpc_cmd_value, lpc_dev->regs + LPC_REG_CMD);
+
+ /* set lpc op len */
+ writel(len, lpc_dev->regs + LPC_REG_OP_LEN);
+
+ /* set lpc addr config */
+ writel(addr, lpc_dev->regs + LPC_REG_ADDR);
+
+ /* set lpc start work */
+ writel(START_WORK, lpc_dev->regs + LPC_REG_START);
+ if (readl_poll_timeout_atomic(lpc_dev->regs + LPC_REG_IRQ_ST, tmp,
+ tmp & LPC_IRQ_OCCURRED, 1, 2))
+ return -ETIME;
+
+ writel(LPC_IRQ_CLEAR, lpc_dev->regs + LPC_REG_IRQ_ST);
+
+ lpc_op_state_value = readl(lpc_dev->regs + LPC_REG_OP_STATUS);
+ /* Get read data */
+ if (lpc_op_state_value & LPC_OP_FINISHED) {
+ for (i = 0; i < len; i++)
+ buf[i] = readl(lpc_dev->regs + LPC_REG_RDATA);
+
+ return 0;
+ }
+
+ return -EIO;
+}
+
+static int lpc_indirect_pio(unsigned long port, bool dir, u32 size, void *data)
+{
+ unsigned long flags;
+ int ret;
+
+
+ spin_lock_irqsave(&lpc_dev->lock, flags);
+ if (dir) {/* non-zero means read*/
+ ret = lpc_master_read(port, data, size);
+ } else {/*read*/
+ ret = lpc_master_write(port, data, size);
+ }
+
+ spin_unlock_irqrestore(&lpc_dev->lock, flags);
+
+ return ret;
+}
+
+static int lpc_probe(struct platform_device *pdev)
+{
+ struct resource *regs = NULL;
+
+ lpc_dev = devm_kzalloc(&pdev->dev,
+ sizeof(struct lpc_dev), GFP_KERNEL);
+ if (!lpc_dev)
+ return -ENOMEM;
+
+ spin_lock_init(&lpc_dev->lock);
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ lpc_dev->regs = devm_ioremap_resource(&pdev->dev, regs);
+ if (IS_ERR(lpc_dev->regs))
+ return PTR_ERR(lpc_dev->regs);
+
+ lpc_dev->dev = &pdev->dev;
+ platform_set_drvdata(pdev, lpc_dev);
+
+ return arm64_set_isa_pio(lpc_indirect_pio);
+}
+
+static const struct of_device_id lpc_pltfm_match[] = {
+ {
+ .compatible = "hisilicon,low-pin-count",
+ },
+ {},
+};
+
+static struct platform_driver lpc_driver = {
+ .driver = {
+ .name = "LPC",
+ .of_match_table = lpc_pltfm_match,
+ },
+ .probe = lpc_probe,
+};
+static int __init lpc_init_driver(void)
+{
+ return platform_driver_register(&lpc_driver);
+}
+arch_initcall(lpc_init_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Rongrong Zou.");
+MODULE_DESCRIPTION("LPC driver for hisilicon");
+MODULE_VERSION("v1.0");