lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1451396032-23708-3-git-send-email-zourongrong@gmail.com>
Date:	Tue, 29 Dec 2015 21:33:51 +0800
From:	Rongrong Zou <zourongrong@...il.com>
To:	arnd@...db.de, catalin.marinas@....com, will.deacon@....com
Cc:	benh@...nel.crashing.org, lijianhua@...wei.com,
	lixiancai@...wei.com, linuxarm@...wei.com,
	linux-kernel@...r.kernel.org, minyard@....org,
	gregkh@...uxfoundation.org
Subject: [PATCH v1 2/3] ARM64 LPC: 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.

The low pin count specification is at 
http://www.intel.com/design/chipsets/industry/lpc.htm

Signed-off-by: Rongrong Zou <zourongrong@...il.com>
---
 arch/arm64/kernel/Makefile |   1 +
 arch/arm64/kernel/lpc.c    | 294 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 295 insertions(+)
 create mode 100644 arch/arm64/kernel/lpc.c

diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 474691f..4324575 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -41,6 +41,7 @@ arm64-obj-$(CONFIG_EFI)			+= efi.o efi-entry.stub.o
 arm64-obj-$(CONFIG_PCI)			+= pci.o
 arm64-obj-$(CONFIG_ARMV8_DEPRECATED)	+= armv8_deprecated.o
 arm64-obj-$(CONFIG_ACPI)		+= acpi.o
+arm64-obj-$(CONFIG_ARM64_INDIRECT_PIO)	+= lpc.o
 
 obj-y					+= $(arm64-obj-y) vdso/
 obj-m					+= $(arm64-obj-m)
diff --git a/arch/arm64/kernel/lpc.c b/arch/arm64/kernel/lpc.c
new file mode 100644
index 0000000..174c293
--- /dev/null
+++ b/arch/arm64/kernel/lpc.c
@@ -0,0 +1,294 @@
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/version.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_FWH_ID_MSIZE     (0x18)
+#define LPC_REG_ADDR             (0x20)
+#define LPC_REG_WDATA            (0x24)
+#define LPC_REG_RDATA            (0x28)
+#define LPC_REG_LONG_CNT         (0x30)
+#define LPC_REG_TX_FIFO_ST       (0x50)
+#define LPC_REG_RX_FIFO_ST       (0x54)
+#define LPC_REG_TIME_OUT         (0x58)
+#define LPC_REG_STRQ_CTRL0       (0x80)
+#define LPC_REG_STRQ_CTRL1       (0x84)
+#define LPC_REG_STRQ_INT         (0x90)
+#define LPC_REG_STRQ_INT_MASK    (0x94)
+#define LPC_REG_STRQ_STAT        (0xa0)
+
+#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)
+
+#define LPC_FRAME_LEN (0x10)
+
+#define LPC_CURR_STATUS_IDLE     0
+#define LPC_CURR_STATUS_START    1
+#define LPC_CURR_STATUS_TYPE_DIR 2
+#define LPC_CURR_STATUS_ADDR     3
+#define LPC_CURR_STATUS_MSIZE    4
+#define LPC_CURR_STATUS_WDATA    5
+#define LPC_CURR_STATUS_TARHOST  6
+#define LPC_CURR_STATUS_SYNC     7
+#define LPC_CURR_STATUS_RDATA    8
+#define LPC_CURR_STATUS_TARSLAVE 9
+#define LPC_CURR_STATUS_ABORT    10
+
+struct lpc_dev {
+	spinlock_t lock;
+	void __iomem  *regs;
+	struct device *dev;
+};
+
+static struct lpc_dev *lpc_dev;
+
+int lpc_master_write(unsigned int slv_access_mode, unsigned int cycle_type,
+		      unsigned int addr, unsigned char *buf, unsigned int len)
+{
+	unsigned int i;
+	unsigned int lpc_cmd_value;
+	unsigned int lpc_op_state_value;
+	unsigned int retry = 0;
+
+	/* para check */
+	if (!buf || !len)
+		return -EINVAL;
+
+	if (slv_access_mode != LPC_CMD_SAMEADDR_SING &&
+		slv_access_mode != LPC_CMD_SAMEADDR_INC) {
+		return -EINVAL;
+	}
+
+	if ((cycle_type != LPC_CMD_TYPE_IO) &&
+		(cycle_type != LPC_CMD_TYPE_MEM) &&
+		(cycle_type != LPC_CMD_TYPE_FWH)) {
+		return -EINVAL;
+	}
+
+	writel(LPC_IRQ_CLEAR, lpc_dev->regs + LPC_REG_IRQ_ST);
+	retry = 0;
+	while (!(readl(lpc_dev->regs + LPC_REG_OP_STATUS)
+		& LPC_STATUS_DILE)) {
+		udelay(1);
+		retry++;
+		if (retry >= 2)
+			return -ETIME;
+	}
+
+	/* set lpc master write cycle type and slv access mode */
+	lpc_cmd_value = LPC_CMD_WRITE | cycle_type | slv_access_mode;
+	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);
+
+	retry = 0;
+	while (!(readl(lpc_dev->regs + LPC_REG_IRQ_ST) &
+		 LPC_IRQ_OCCURRED)) {
+		udelay(1);
+		retry++;
+		if (retry >= 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;
+}
+
+void  lpc_io_write_byte(u8 value, unsigned long addr)
+{
+	unsigned long flags;
+
+	if (!lpc_dev)
+		return;
+	spin_lock_irqsave(&lpc_dev->lock, flags);
+	(void)lpc_master_write(LPC_CMD_SAMEADDR_SING, LPC_CMD_TYPE_IO,
+				 addr, &value, 1);
+	spin_unlock_irqrestore(&lpc_dev->lock, flags);
+}
+
+int lpc_master_read(unsigned int slv_access_mode, unsigned int cycle_type,
+		     unsigned int addr, unsigned char *buf, unsigned int len)
+{
+	unsigned int i;
+	unsigned int lpc_cmd_value;
+	unsigned int lpc_op_state_value;
+	unsigned int retry = 0;
+
+	/* para check */
+	if (!buf || !len)
+		return -EINVAL;
+
+	if (slv_access_mode != LPC_CMD_SAMEADDR_SING &&
+	    slv_access_mode != LPC_CMD_SAMEADDR_INC) {
+		return -EINVAL;
+	}
+
+	if (cycle_type != LPC_CMD_TYPE_IO &&
+	    cycle_type != LPC_CMD_TYPE_MEM &&
+	    cycle_type != LPC_CMD_TYPE_FWH) {
+		return -EINVAL;
+	}
+
+	writel(LPC_IRQ_CLEAR, lpc_dev->regs + LPC_REG_IRQ_ST);
+
+	retry = 0;
+	while (!(readl(lpc_dev->regs + LPC_REG_OP_STATUS) &
+	       LPC_STATUS_DILE)) {
+		udelay(1);
+		retry++;
+		if (retry >= 2)
+			return -ETIME;
+	}
+
+	/* set lpc master read cycle type and slv access mode */
+	lpc_cmd_value = LPC_CMD_READ | cycle_type | slv_access_mode;
+	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);
+
+	while (!(readl(lpc_dev->regs + LPC_REG_IRQ_ST) &
+	       LPC_IRQ_OCCURRED)) {
+		udelay(1);
+		retry++;
+		if (retry >= 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;
+}
+
+u8 lpc_io_read_byte(unsigned long addr)
+{
+	unsigned char value;
+	unsigned long flags;
+	int ret;
+
+	if (!lpc_dev)
+		return 0xff;
+
+	spin_lock_irqsave(&lpc_dev->lock, flags);
+	ret = lpc_master_read(LPC_CMD_SAMEADDR_SING,
+		LPC_CMD_TYPE_IO, addr, &value, 1);
+	spin_unlock_irqrestore(&lpc_dev->lock, flags);
+	return ret ? 0xff : value;
+}
+
+static const struct arm64_isa_io lpc_io = {
+	.inb  = lpc_io_read_byte,
+	.outb = lpc_io_write_byte,
+};
+
+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);
+
+	dev_info(&pdev->dev, "Low pin count driver initialized successfully\n");
+
+	lpc_dev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, lpc_dev);
+	arm64_isa_io = lpc_io;
+
+	return 0;
+}
+
+static int lpc_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id lpc_pltfm_match[] = {
+	{
+		.compatible = "low-pin-count",
+	},
+	{},
+};
+
+static struct platform_driver lpc_driver = {
+	.driver = {
+		.name           = "LPC",
+		.owner          = THIS_MODULE,
+		.of_match_table = lpc_pltfm_match,
+	},
+	.probe                = lpc_probe,
+	.remove               = lpc_remove,
+};
+
+static int __init lpc_init_driver(void)
+{
+	return platform_driver_register(&lpc_driver);
+}
+
+static void __exit lpc_init_exit(void)
+{
+	platform_driver_unregister(&lpc_driver);
+}
+
+arch_initcall(lpc_init_driver);
+module_exit(lpc_init_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Huawei Tech. Co., Ltd.");
+MODULE_DESCRIPTION("LPC driver for linux");
+MODULE_VERSION("v1.0");
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ