linux-hwmon.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v1 0/2] misc: Add NPCM LPC BPC driver support
@ 2019-04-15 12:12 Tomer Maimon
  2019-04-15 12:12 ` [PATCH v1 1/2] dt-binding: misc: Add common LPC snoop documentation Tomer Maimon
  2019-04-15 12:12 ` [PATCH v1 2/2] misc: npcm: add NPCM LPC BPC driver Tomer Maimon
  0 siblings, 2 replies; 5+ messages in thread
From: Tomer Maimon @ 2019-04-15 12:12 UTC (permalink / raw)
  To: gregkh, arnd, robh+dt, mark.rutland, yuenn, venture,
	brendanhiggins, avifishman70, joel
  Cc: linux-hwmon, devicetree, openbmc, linux-kernel, Tomer Maimon

This patch set adds BIOS Post code (BPC) support for the
Nuvoton NPCM Baseboard Management Controller (BMC).

Nuvoton BMC NPCM BIOS Post Code (BPC) monitoring two
configurable I/O addresses written by the host on the
Low Pin Count (LPC) bus, the capture data stored in 128-word FIFO.

NPCM BPC can support capture double words.

I have created common lpc-snoop documentation for both 
Nuvoton and Aspeed drivers as Andrew suggested.
Andrew Jeffery: https://patchwork.kernel.org/patch/10506269/ 

I prefer to add the lpc-dnoop documentation to misc folder instead 
of creating new /devicetree/binding folder that named BMC
as been done in the last lpc-snoop patchset because I am not sure
is something that should be done for only 
one module for now. 
https://patchwork.kernel.org/patch/10506269/

The NPCM7xx BPC driver tested on NPCM750 evaluation board.

Tomer Maimon (2):
  dt-binding: misc: Add common LPC snoop documentation
  misc: npcm: add NPCM LPC BPC driver

 .../devicetree/bindings/misc/lpc-snoop.txt         |  27 ++
 drivers/misc/Kconfig                               |   8 +
 drivers/misc/Makefile                              |   1 +
 drivers/misc/npcm-lpc-bpc-snoop.c                  | 393 +++++++++++++++++++++
 4 files changed, 429 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/misc/lpc-snoop.txt
 create mode 100644 drivers/misc/npcm-lpc-bpc-snoop.c

-- 
2.14.1


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v1 1/2] dt-binding: misc: Add common LPC snoop documentation
  2019-04-15 12:12 [PATCH v1 0/2] misc: Add NPCM LPC BPC driver support Tomer Maimon
@ 2019-04-15 12:12 ` Tomer Maimon
  2019-04-16  1:04   ` Andrew Jeffery
  2019-04-15 12:12 ` [PATCH v1 2/2] misc: npcm: add NPCM LPC BPC driver Tomer Maimon
  1 sibling, 1 reply; 5+ messages in thread
From: Tomer Maimon @ 2019-04-15 12:12 UTC (permalink / raw)
  To: gregkh, arnd, robh+dt, mark.rutland, yuenn, venture,
	brendanhiggins, avifishman70, joel
  Cc: linux-hwmon, devicetree, openbmc, linux-kernel, Tomer Maimon

Added device tree binding documentation for Nuvoton BMC
NPCM BIOS Post Code (BPC) and Apeed AST2500 LPC snoop.
The LPC snoop monitoring two configurable I/O addresses
written by the host on Low Pin Count (LPC) bus.

Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
---
 .../devicetree/bindings/misc/lpc-snoop.txt         | 27 ++++++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/misc/lpc-snoop.txt

diff --git a/Documentation/devicetree/bindings/misc/lpc-snoop.txt b/Documentation/devicetree/bindings/misc/lpc-snoop.txt
new file mode 100644
index 000000000000..7deac244ef9d
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/lpc-snoop.txt
@@ -0,0 +1,27 @@
+LPC snoop interface
+
+The LPC snoop (BIOS Post Code) interface can monitor
+two configurable I/O addresses written by the host on
+the Low Pin Count (LPC) bus.
+
+Nuvoton NPCM7xx LPC snoop supports capture double words,
+when using capture double word only I/O address 1 is monitored.
+
+Required properties for lpc-snoop node
+- compatible   : "nuvoton,npcm750-lpc-bpc-snoop" for Poleg NPCM7XX
+                 "aspeed,ast2500-lpc-snoop" for Aspeed AST2500.
+- reg          : specifies physical base address and size of the registers.
+- interrupts   : contain the LPC snoop interrupt with flags for falling edge.
+- snoop-ports  : contain monitor I/O addresses, at least one monitor I/O
+                 address required
+
+Optional property for NPCM7xx lpc-snoop node
+- nuvoton,lpc-en-dwcapture : enable capture double words support.
+
+Example:
+	lpc-snoop: lpc_snoop@f0007040 {
+		compatible = "nuvoton,npcm750-lpc-bpc-snoop";
+		reg = <0xf0007040 0x14>;
+		snoop-ports = <0x80>;
+		interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+	};
\ No newline at end of file
-- 
2.14.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v1 2/2] misc: npcm: add NPCM LPC BPC driver
  2019-04-15 12:12 [PATCH v1 0/2] misc: Add NPCM LPC BPC driver support Tomer Maimon
  2019-04-15 12:12 ` [PATCH v1 1/2] dt-binding: misc: Add common LPC snoop documentation Tomer Maimon
@ 2019-04-15 12:12 ` Tomer Maimon
  2019-04-15 13:21   ` Guenter Roeck
  1 sibling, 1 reply; 5+ messages in thread
From: Tomer Maimon @ 2019-04-15 12:12 UTC (permalink / raw)
  To: gregkh, arnd, robh+dt, mark.rutland, yuenn, venture,
	brendanhiggins, avifishman70, joel
  Cc: linux-hwmon, devicetree, openbmc, linux-kernel, Tomer Maimon

Add Nuvoton BMC NPCM BIOS post code (BPC) driver.

The NPCM BPC monitoring two I/O address written by
the host on the Low Pin Count (LPC) bus, the capure
data stored in 128-word FIFO.

Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
---
 drivers/misc/Kconfig              |   8 +
 drivers/misc/Makefile             |   1 +
 drivers/misc/npcm-lpc-bpc-snoop.c | 393 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 402 insertions(+)
 create mode 100644 drivers/misc/npcm-lpc-bpc-snoop.c

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 42ab8ec92a04..320a1d0083d2 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -532,6 +532,14 @@ config PVPANIC
 	  a paravirtualized device provided by QEMU; it lets a virtual machine
 	  (guest) communicate panic events to the host.
 
+config NPCM_LPC_BPC_SNOOP
+	tristate "NPCM LPC BIOS Post Code snoop support"
+	depends on (ARCH_NPCM || COMPILE_TEST)
+	help
+	  Provides a NPCM BMC driver to control the LPC BIOS Post Code
+	  interface which allows the BMC to monitoring and save
+	  the data written by the host to an arbitrary LPC I/O port.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index d5b7d3404dc7..5dd0bff75b60 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -59,5 +59,6 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP)	+= aspeed-lpc-snoop.o
 obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
 obj-$(CONFIG_OCXL)		+= ocxl/
 obj-y				+= cardreader/
+obj-$(CONFIG_NPCM_LPC_BPC_SNOOP) += npcm-lpc-bpc-snoop.o
 obj-$(CONFIG_PVPANIC)   	+= pvpanic.o
 obj-$(CONFIG_HABANA_AI)		+= habanalabs/
diff --git a/drivers/misc/npcm-lpc-bpc-snoop.c b/drivers/misc/npcm-lpc-bpc-snoop.c
new file mode 100644
index 000000000000..851994184076
--- /dev/null
+++ b/drivers/misc/npcm-lpc-bpc-snoop.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2014-2018 Nuvoton Technology corporation.
+
+#include <linux/fs.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+
+#define DEVICE_NAME	"npcm-lpc-bpc"
+
+#define NUM_BPC_CHANNELS		2
+#define DW_PAD_SIZE			3
+
+/* BIOS POST Code FIFO Registers */
+#define NPCM_BPCFA2L_REG	0x2 //BIOS POST Code FIFO Address 2 LSB
+#define NPCM_BPCFA2M_REG	0x4 //BIOS POST Code FIFO Address 2 MSB
+#define NPCM_BPCFEN_REG	0x6 //BIOS POST Code FIFO Enable
+#define NPCM_BPCFSTAT_REG	0x8 //BIOS POST Code FIFO Status
+#define NPCM_BPCFDATA_REG	0xA //BIOS POST Code FIFO Data
+#define NPCM_BPCFMSTAT_REG	0xC //BIOS POST Code FIFO Miscellaneous Status
+#define NPCM_BPCFA1L_REG	0x10 //BIOS POST Code FIFO Address 1 LSB
+#define NPCM_BPCFA1M_REG	0x12 //BIOS POST Code FIFO Address 1 MSB
+
+/*BIOS regiser data*/
+#define FIFO_IOADDR1_ENABLE	0x80
+#define FIFO_IOADDR2_ENABLE	0x40
+
+/* BPC interface package and structure definition */
+#define BPC_KFIFO_SIZE		0x400
+
+/*BPC regiser data*/
+#define FIFO_DATA_VALID		0x80
+#define FIFO_OVERFLOW		0x20
+#define FIFO_READY_INT_ENABLE	0x8
+#define FIFO_DWCAPTURE		0x4
+#define FIFO_ADDR_DECODE	0x1
+
+/*Host Reset*/
+#define HOST_RESET_INT_ENABLE	0x10
+#define HOST_RESET_CHANGED	0x40
+
+struct npcm_bpc_channel {
+	struct npcm_bpc	*data;
+	struct kfifo		fifo;
+	wait_queue_head_t	wq;
+	bool			host_reset;
+	struct miscdevice	miscdev;
+};
+
+struct npcm_bpc {
+	void __iomem			*base;
+	int				irq;
+	bool				en_dwcap;
+	struct npcm_bpc_channel	ch[NUM_BPC_CHANNELS];
+};
+
+static struct npcm_bpc_channel *npcm_file_to_ch(struct file *file)
+{
+	return container_of(file->private_data, struct npcm_bpc_channel,
+			    miscdev);
+}
+
+static ssize_t npcm_bpc_read(struct file *file, char __user *buffer,
+			     size_t count, loff_t *ppos)
+{
+	struct npcm_bpc_channel *chan = npcm_file_to_ch(file);
+	struct npcm_bpc *lpc_bpc = chan->data;
+	unsigned int copied;
+	int ret = 0;
+	int cond_size = 1;
+
+	if (lpc_bpc->en_dwcap)
+		cond_size = 3;
+
+	if (kfifo_len(&chan->fifo) < cond_size) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		ret = wait_event_interruptible
+			(chan->wq, kfifo_len(&chan->fifo) > cond_size);
+		if (ret == -ERESTARTSYS)
+			return -EINTR;
+	}
+
+	ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
+
+	return ret ? ret : copied;
+}
+
+static __poll_t npcm_bpc_poll(struct file *file, struct poll_table_struct *pt)
+{
+	struct npcm_bpc_channel *chan = npcm_file_to_ch(file);
+	__poll_t mask = 0;
+
+	poll_wait(file, &chan->wq, pt);
+	if (!kfifo_is_empty(&chan->fifo))
+		mask |= POLLIN;
+
+	if (chan->host_reset) {
+		mask |= POLLHUP;
+		chan->host_reset = false;
+	}
+
+	return mask;
+}
+
+static const struct file_operations npcm_bpc_fops = {
+	.owner		= THIS_MODULE,
+	.read		= npcm_bpc_read,
+	.poll		= npcm_bpc_poll,
+	.llseek		= noop_llseek,
+};
+
+static irqreturn_t npcm_bpc_irq(int irq, void *arg)
+{
+	struct npcm_bpc *lpc_bpc = arg;
+	u8 fifo_st;
+	u8 host_st;
+	u8 addr_index = 0;
+	u8 Data;
+	u8 padzero[3] = {0};
+	u8 last_addr_bit = 0;
+	bool isr_flag = false;
+
+	fifo_st = ioread8(lpc_bpc->base + NPCM_BPCFSTAT_REG);
+	while (FIFO_DATA_VALID & fifo_st) {
+		 /* If dwcapture enabled only channel 0 (FIFO 0) used */
+		if (!lpc_bpc->en_dwcap)
+			addr_index = fifo_st & FIFO_ADDR_DECODE;
+		else
+			last_addr_bit = fifo_st & FIFO_ADDR_DECODE;
+
+		/*Read data from FIFO to clear interrupt*/
+		Data = ioread8(lpc_bpc->base + NPCM_BPCFDATA_REG);
+		if (kfifo_is_full(&lpc_bpc->ch[addr_index].fifo))
+			kfifo_skip(&lpc_bpc->ch[addr_index].fifo);
+		kfifo_put(&lpc_bpc->ch[addr_index].fifo, Data);
+		if (fifo_st & FIFO_OVERFLOW)
+			pr_info("BIOS Post Codes FIFO Overflow!!!\n");
+
+		fifo_st = ioread8(lpc_bpc->base + NPCM_BPCFSTAT_REG);
+		if (lpc_bpc->en_dwcap && last_addr_bit) {
+			if ((fifo_st & FIFO_ADDR_DECODE) ||
+			    ((FIFO_DATA_VALID & fifo_st) == 0)) {
+				while (kfifo_avail(&lpc_bpc->ch[addr_index].fifo) < DW_PAD_SIZE)
+					kfifo_skip(&lpc_bpc->ch[addr_index].fifo);
+				kfifo_in(&lpc_bpc->ch[addr_index].fifo,
+					 padzero, DW_PAD_SIZE);
+			}
+		}
+		isr_flag = true;
+	}
+
+	host_st = ioread8(lpc_bpc->base + NPCM_BPCFMSTAT_REG);
+	if (host_st & HOST_RESET_CHANGED) {
+		iowrite8(HOST_RESET_CHANGED,
+			 lpc_bpc->base + NPCM_BPCFMSTAT_REG);
+		lpc_bpc->ch[addr_index].host_reset = true;
+		isr_flag = true;
+	}
+
+	if (isr_flag) {
+		wake_up_interruptible(&lpc_bpc->ch[addr_index].wq);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static int npcm_bpc_config_irq(struct npcm_bpc *lpc_bpc,
+			       struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	int rc;
+
+	lpc_bpc->irq = platform_get_irq(pdev, 0);
+	if (lpc_bpc->irq < 0) {
+		dev_err(dev, "get IRQ failed\n");
+		return lpc_bpc->irq;
+	}
+
+	rc = devm_request_irq(dev, lpc_bpc->irq,
+			      npcm_bpc_irq, IRQF_SHARED,
+			      DEVICE_NAME, lpc_bpc);
+	if (rc < 0) {
+		dev_warn(dev, "Unable to request IRQ %d\n", lpc_bpc->irq);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int npcm_enable_bpc(struct npcm_bpc *lpc_bpc, struct device *dev,
+			   int channel, u16 lpc_port)
+{
+	int rc;
+	u8 addr_en, reg_en;
+
+	init_waitqueue_head(&lpc_bpc->ch[channel].wq);
+
+	rc = kfifo_alloc(&lpc_bpc->ch[channel].fifo,
+			 BPC_KFIFO_SIZE, GFP_KERNEL);
+	if (rc)
+		return rc;
+
+	lpc_bpc->ch[channel].miscdev.minor = MISC_DYNAMIC_MINOR;
+	lpc_bpc->ch[channel].miscdev.name =
+		devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel);
+	lpc_bpc->ch[channel].miscdev.fops = &npcm_bpc_fops;
+	lpc_bpc->ch[channel].miscdev.parent = dev;
+	rc = misc_register(&lpc_bpc->ch[channel].miscdev);
+	if (rc)
+		return rc;
+
+	lpc_bpc->ch[channel].data = lpc_bpc;
+	lpc_bpc->ch[channel].host_reset = false;
+
+	/* Enable LPC snoop channel at requested port */
+	switch (channel) {
+	case 0:
+		addr_en = FIFO_IOADDR1_ENABLE;
+		iowrite8((u8)lpc_port & 0xFF,
+			 lpc_bpc->base + NPCM_BPCFA1L_REG);
+		iowrite8((u8)(lpc_port >> 8),
+			 lpc_bpc->base + NPCM_BPCFA1M_REG);
+		break;
+	case 1:
+		addr_en = FIFO_IOADDR2_ENABLE;
+		iowrite8((u8)lpc_port & 0xFF,
+			 lpc_bpc->base + NPCM_BPCFA2L_REG);
+		iowrite8((u8)(lpc_port >> 8),
+			 lpc_bpc->base + NPCM_BPCFA2M_REG);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (lpc_bpc->en_dwcap)
+		addr_en = FIFO_DWCAPTURE;
+
+	/*
+	 * Enable FIFO Ready Interrupt, FIFO Capture of I/O addr,
+	 * and Host Reset
+	 */
+	reg_en = ioread8(lpc_bpc->base + NPCM_BPCFEN_REG);
+	iowrite8(reg_en | addr_en | FIFO_READY_INT_ENABLE |
+		 HOST_RESET_INT_ENABLE, lpc_bpc->base + NPCM_BPCFEN_REG);
+
+	return 0;
+}
+
+static void npcm_disable_bpc(struct npcm_bpc *lpc_bpc, int channel)
+{
+	u8 reg_en;
+
+	switch (channel) {
+	case 0:
+		reg_en = ioread8(lpc_bpc->base + NPCM_BPCFEN_REG);
+		if (lpc_bpc->en_dwcap)
+			iowrite8(reg_en & ~FIFO_DWCAPTURE,
+				 lpc_bpc->base + NPCM_BPCFEN_REG);
+		else
+			iowrite8(reg_en & ~FIFO_IOADDR1_ENABLE,
+				 lpc_bpc->base + NPCM_BPCFEN_REG);
+		break;
+	case 1:
+		reg_en = ioread8(lpc_bpc->base + NPCM_BPCFEN_REG);
+		iowrite8(reg_en & ~FIFO_IOADDR2_ENABLE,
+			 lpc_bpc->base + NPCM_BPCFEN_REG);
+		break;
+	default:
+		return;
+	}
+
+	if (!(reg_en & (FIFO_IOADDR1_ENABLE | FIFO_IOADDR2_ENABLE)))
+		iowrite8(reg_en &
+			 ~(FIFO_READY_INT_ENABLE | HOST_RESET_INT_ENABLE),
+			 lpc_bpc->base + NPCM_BPCFEN_REG);
+
+	kfifo_free(&lpc_bpc->ch[channel].fifo);
+	misc_deregister(&lpc_bpc->ch[channel].miscdev);
+}
+
+static int npcm_bpc_probe(struct platform_device *pdev)
+{
+	struct npcm_bpc *lpc_bpc;
+	struct resource *res;
+	struct device *dev;
+	u32 port;
+	int rc;
+
+	dev = &pdev->dev;
+
+	lpc_bpc = devm_kzalloc(dev, sizeof(*lpc_bpc), GFP_KERNEL);
+	if (!lpc_bpc)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "BIOS post code reg resource not found\n");
+		return -ENODEV;
+	}
+
+	dev_dbg(dev, "BIOS post code base resource is %pR\n", res);
+	lpc_bpc->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(lpc_bpc->base))
+		return PTR_ERR(lpc_bpc->base);
+
+	dev_set_drvdata(&pdev->dev, lpc_bpc);
+
+	rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0,
+					&port);
+	if (rc) {
+		dev_err(dev, "no snoop ports configured\n");
+		return -ENODEV;
+	}
+
+	lpc_bpc->en_dwcap =
+		of_property_read_bool(dev->of_node, "nuvoton,lpc-en-dwcapture");
+
+	rc = npcm_bpc_config_irq(lpc_bpc, pdev);
+	if (rc)
+		return rc;
+
+	rc = npcm_enable_bpc(lpc_bpc, dev, 0, port);
+	if (rc) {
+		dev_err(dev, "Enable BIOS post code I/O port 0 failed\n");
+		return rc;
+	}
+
+	/*
+	 * Configuration of second BPC channel port is optional
+	 * Double-Word Capture ignoring address 2
+	 */
+	if (!lpc_bpc->en_dwcap) {
+		if (of_property_read_u32_index(dev->of_node, "snoop-ports",
+					       1, &port) == 0) {
+			rc = npcm_enable_bpc(lpc_bpc, dev, 1, port);
+			if (rc) {
+				dev_err(dev, "Enable BIOS post code I/O port 1 failed, disable I/O port 0\n");
+				npcm_disable_bpc(lpc_bpc, 0);
+				return rc;
+			}
+		}
+	}
+
+	pr_info("NPCM BIOS post code probe\n");
+
+	return rc;
+}
+
+static int npcm_bpc_remove(struct platform_device *pdev)
+{
+	struct npcm_bpc *lpc_bpc = dev_get_drvdata(&pdev->dev);
+	u8 reg_en;
+
+	reg_en = ioread8(lpc_bpc->base + NPCM_BPCFEN_REG);
+
+	if (reg_en & FIFO_IOADDR1_ENABLE)
+		npcm_disable_bpc(lpc_bpc, 0);
+	if (reg_en & FIFO_IOADDR2_ENABLE)
+		npcm_disable_bpc(lpc_bpc, 1);
+
+	return 0;
+}
+
+static const struct of_device_id npcm_bpc_match[] = {
+	{ .compatible = "nuvoton,npcm750-lpc-bpc-snoop" },
+	{ },
+};
+
+static struct platform_driver npcm_bpc_driver = {
+	.driver = {
+		.name		= DEVICE_NAME,
+		.of_match_table = npcm_bpc_match,
+	},
+	.probe = npcm_bpc_probe,
+	.remove = npcm_bpc_remove,
+};
+
+module_platform_driver(npcm_bpc_driver);
+
+MODULE_DEVICE_TABLE(of, npcm_bpc_match);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_DESCRIPTION("Linux driver to control NPCM LPC BIOS post code snooping");
-- 
2.14.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH v1 2/2] misc: npcm: add NPCM LPC BPC driver
  2019-04-15 12:12 ` [PATCH v1 2/2] misc: npcm: add NPCM LPC BPC driver Tomer Maimon
@ 2019-04-15 13:21   ` Guenter Roeck
  0 siblings, 0 replies; 5+ messages in thread
From: Guenter Roeck @ 2019-04-15 13:21 UTC (permalink / raw)
  To: Tomer Maimon, gregkh, arnd, robh+dt, mark.rutland, yuenn,
	venture, brendanhiggins, avifishman70, joel
  Cc: linux-hwmon, devicetree, openbmc, linux-kernel

On 4/15/19 5:12 AM, Tomer Maimon wrote:
> Add Nuvoton BMC NPCM BIOS post code (BPC) driver.
> 
> The NPCM BPC monitoring two I/O address written by
> the host on the Low Pin Count (LPC) bus, the capure
> data stored in 128-word FIFO.
> 
> Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
> ---
>   drivers/misc/Kconfig              |   8 +
>   drivers/misc/Makefile             |   1 +
>   drivers/misc/npcm-lpc-bpc-snoop.c | 393 ++++++++++++++++++++++++++++++++++++++
>   3 files changed, 402 insertions(+)
>   create mode 100644 drivers/misc/npcm-lpc-bpc-snoop.c
> 
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> index 42ab8ec92a04..320a1d0083d2 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -532,6 +532,14 @@ config PVPANIC
>   	  a paravirtualized device provided by QEMU; it lets a virtual machine
>   	  (guest) communicate panic events to the host.
>   
> +config NPCM_LPC_BPC_SNOOP
> +	tristate "NPCM LPC BIOS Post Code snoop support"
> +	depends on (ARCH_NPCM || COMPILE_TEST)
> +	help
> +	  Provides a NPCM BMC driver to control the LPC BIOS Post Code
> +	  interface which allows the BMC to monitoring and save
> +	  the data written by the host to an arbitrary LPC I/O port.
> +
>   source "drivers/misc/c2port/Kconfig"
>   source "drivers/misc/eeprom/Kconfig"
>   source "drivers/misc/cb710/Kconfig"
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> index d5b7d3404dc7..5dd0bff75b60 100644
> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -59,5 +59,6 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP)	+= aspeed-lpc-snoop.o
>   obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
>   obj-$(CONFIG_OCXL)		+= ocxl/
>   obj-y				+= cardreader/
> +obj-$(CONFIG_NPCM_LPC_BPC_SNOOP) += npcm-lpc-bpc-snoop.o
>   obj-$(CONFIG_PVPANIC)   	+= pvpanic.o
>   obj-$(CONFIG_HABANA_AI)		+= habanalabs/
> diff --git a/drivers/misc/npcm-lpc-bpc-snoop.c b/drivers/misc/npcm-lpc-bpc-snoop.c
> new file mode 100644
> index 000000000000..851994184076
> --- /dev/null
> +++ b/drivers/misc/npcm-lpc-bpc-snoop.c
> @@ -0,0 +1,393 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2014-2018 Nuvoton Technology corporation.
> +
> +#include <linux/fs.h>
> +#include <linux/bitops.h>
> +#include <linux/interrupt.h>
> +#include <linux/kfifo.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/miscdevice.h>
> +#include <linux/poll.h>
> +
> +#define DEVICE_NAME	"npcm-lpc-bpc"
> +
> +#define NUM_BPC_CHANNELS		2
> +#define DW_PAD_SIZE			3
> +
> +/* BIOS POST Code FIFO Registers */
> +#define NPCM_BPCFA2L_REG	0x2 //BIOS POST Code FIFO Address 2 LSB
> +#define NPCM_BPCFA2M_REG	0x4 //BIOS POST Code FIFO Address 2 MSB
> +#define NPCM_BPCFEN_REG	0x6 //BIOS POST Code FIFO Enable
> +#define NPCM_BPCFSTAT_REG	0x8 //BIOS POST Code FIFO Status
> +#define NPCM_BPCFDATA_REG	0xA //BIOS POST Code FIFO Data
> +#define NPCM_BPCFMSTAT_REG	0xC //BIOS POST Code FIFO Miscellaneous Status
> +#define NPCM_BPCFA1L_REG	0x10 //BIOS POST Code FIFO Address 1 LSB
> +#define NPCM_BPCFA1M_REG	0x12 //BIOS POST Code FIFO Address 1 MSB
> +
> +/*BIOS regiser data*/
> +#define FIFO_IOADDR1_ENABLE	0x80
> +#define FIFO_IOADDR2_ENABLE	0x40
> +
> +/* BPC interface package and structure definition */
> +#define BPC_KFIFO_SIZE		0x400
> +
> +/*BPC regiser data*/
> +#define FIFO_DATA_VALID		0x80
> +#define FIFO_OVERFLOW		0x20
> +#define FIFO_READY_INT_ENABLE	0x8
> +#define FIFO_DWCAPTURE		0x4
> +#define FIFO_ADDR_DECODE	0x1
> +
> +/*Host Reset*/
> +#define HOST_RESET_INT_ENABLE	0x10
> +#define HOST_RESET_CHANGED	0x40
> +
> +struct npcm_bpc_channel {
> +	struct npcm_bpc	*data;
> +	struct kfifo		fifo;
> +	wait_queue_head_t	wq;
> +	bool			host_reset;
> +	struct miscdevice	miscdev;
> +};
> +
> +struct npcm_bpc {
> +	void __iomem			*base;
> +	int				irq;
> +	bool				en_dwcap;
> +	struct npcm_bpc_channel	ch[NUM_BPC_CHANNELS];
> +};
> +
> +static struct npcm_bpc_channel *npcm_file_to_ch(struct file *file)
> +{
> +	return container_of(file->private_data, struct npcm_bpc_channel,
> +			    miscdev);
> +}
> +
> +static ssize_t npcm_bpc_read(struct file *file, char __user *buffer,
> +			     size_t count, loff_t *ppos)
> +{
> +	struct npcm_bpc_channel *chan = npcm_file_to_ch(file);
> +	struct npcm_bpc *lpc_bpc = chan->data;
> +	unsigned int copied;
> +	int ret = 0;
> +	int cond_size = 1;
> +
> +	if (lpc_bpc->en_dwcap)
> +		cond_size = 3;
> +
> +	if (kfifo_len(&chan->fifo) < cond_size) {
> +		if (file->f_flags & O_NONBLOCK)
> +			return -EAGAIN;
> +
> +		ret = wait_event_interruptible
> +			(chan->wq, kfifo_len(&chan->fifo) > cond_size);
> +		if (ret == -ERESTARTSYS)
> +			return -EINTR;
> +	}
> +
> +	ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
> +
> +	return ret ? ret : copied;
> +}
> +
> +static __poll_t npcm_bpc_poll(struct file *file, struct poll_table_struct *pt)
> +{
> +	struct npcm_bpc_channel *chan = npcm_file_to_ch(file);
> +	__poll_t mask = 0;
> +
> +	poll_wait(file, &chan->wq, pt);
> +	if (!kfifo_is_empty(&chan->fifo))
> +		mask |= POLLIN;
> +
> +	if (chan->host_reset) {
> +		mask |= POLLHUP;
> +		chan->host_reset = false;
> +	}
> +
> +	return mask;
> +}
> +
> +static const struct file_operations npcm_bpc_fops = {
> +	.owner		= THIS_MODULE,
> +	.read		= npcm_bpc_read,
> +	.poll		= npcm_bpc_poll,
> +	.llseek		= noop_llseek,
> +};
> +
> +static irqreturn_t npcm_bpc_irq(int irq, void *arg)
> +{
> +	struct npcm_bpc *lpc_bpc = arg;
> +	u8 fifo_st;
> +	u8 host_st;
> +	u8 addr_index = 0;
> +	u8 Data;
> +	u8 padzero[3] = {0};
> +	u8 last_addr_bit = 0;
> +	bool isr_flag = false;
> +
> +	fifo_st = ioread8(lpc_bpc->base + NPCM_BPCFSTAT_REG);
> +	while (FIFO_DATA_VALID & fifo_st) {
> +		 /* If dwcapture enabled only channel 0 (FIFO 0) used */
> +		if (!lpc_bpc->en_dwcap)
> +			addr_index = fifo_st & FIFO_ADDR_DECODE;
> +		else
> +			last_addr_bit = fifo_st & FIFO_ADDR_DECODE;
> +
> +		/*Read data from FIFO to clear interrupt*/
> +		Data = ioread8(lpc_bpc->base + NPCM_BPCFDATA_REG);
> +		if (kfifo_is_full(&lpc_bpc->ch[addr_index].fifo))
> +			kfifo_skip(&lpc_bpc->ch[addr_index].fifo);
> +		kfifo_put(&lpc_bpc->ch[addr_index].fifo, Data);
> +		if (fifo_st & FIFO_OVERFLOW)
> +			pr_info("BIOS Post Codes FIFO Overflow!!!\n");
> +
> +		fifo_st = ioread8(lpc_bpc->base + NPCM_BPCFSTAT_REG);
> +		if (lpc_bpc->en_dwcap && last_addr_bit) {
> +			if ((fifo_st & FIFO_ADDR_DECODE) ||
> +			    ((FIFO_DATA_VALID & fifo_st) == 0)) {
> +				while (kfifo_avail(&lpc_bpc->ch[addr_index].fifo) < DW_PAD_SIZE)
> +					kfifo_skip(&lpc_bpc->ch[addr_index].fifo);
> +				kfifo_in(&lpc_bpc->ch[addr_index].fifo,
> +					 padzero, DW_PAD_SIZE);
> +			}
> +		}
> +		isr_flag = true;
> +	}
> +
> +	host_st = ioread8(lpc_bpc->base + NPCM_BPCFMSTAT_REG);
> +	if (host_st & HOST_RESET_CHANGED) {
> +		iowrite8(HOST_RESET_CHANGED,
> +			 lpc_bpc->base + NPCM_BPCFMSTAT_REG);
> +		lpc_bpc->ch[addr_index].host_reset = true;
> +		isr_flag = true;
> +	}
> +
> +	if (isr_flag) {
> +		wake_up_interruptible(&lpc_bpc->ch[addr_index].wq);
> +		return IRQ_HANDLED;
> +	}
> +
> +	return IRQ_NONE;
> +}
> +
> +static int npcm_bpc_config_irq(struct npcm_bpc *lpc_bpc,
> +			       struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	int rc;
> +
> +	lpc_bpc->irq = platform_get_irq(pdev, 0);
> +	if (lpc_bpc->irq < 0) {
> +		dev_err(dev, "get IRQ failed\n");
> +		return lpc_bpc->irq;
> +	}
> +
> +	rc = devm_request_irq(dev, lpc_bpc->irq,
> +			      npcm_bpc_irq, IRQF_SHARED,
> +			      DEVICE_NAME, lpc_bpc);
> +	if (rc < 0) {
> +		dev_warn(dev, "Unable to request IRQ %d\n", lpc_bpc->irq);
> +		return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +static int npcm_enable_bpc(struct npcm_bpc *lpc_bpc, struct device *dev,
> +			   int channel, u16 lpc_port)
> +{
> +	int rc;
> +	u8 addr_en, reg_en;
> +
> +	init_waitqueue_head(&lpc_bpc->ch[channel].wq);
> +
> +	rc = kfifo_alloc(&lpc_bpc->ch[channel].fifo,
> +			 BPC_KFIFO_SIZE, GFP_KERNEL);
> +	if (rc)
> +		return rc;
> +
> +	lpc_bpc->ch[channel].miscdev.minor = MISC_DYNAMIC_MINOR;
> +	lpc_bpc->ch[channel].miscdev.name =
> +		devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel);
> +	lpc_bpc->ch[channel].miscdev.fops = &npcm_bpc_fops;
> +	lpc_bpc->ch[channel].miscdev.parent = dev;
> +	rc = misc_register(&lpc_bpc->ch[channel].miscdev);
> +	if (rc)
> +		return rc;
> +
> +	lpc_bpc->ch[channel].data = lpc_bpc;
> +	lpc_bpc->ch[channel].host_reset = false;
> +
> +	/* Enable LPC snoop channel at requested port */
> +	switch (channel) {
> +	case 0:
> +		addr_en = FIFO_IOADDR1_ENABLE;
> +		iowrite8((u8)lpc_port & 0xFF,
> +			 lpc_bpc->base + NPCM_BPCFA1L_REG);
> +		iowrite8((u8)(lpc_port >> 8),
> +			 lpc_bpc->base + NPCM_BPCFA1M_REG);
> +		break;
> +	case 1:
> +		addr_en = FIFO_IOADDR2_ENABLE;
> +		iowrite8((u8)lpc_port & 0xFF,
> +			 lpc_bpc->base + NPCM_BPCFA2L_REG);
> +		iowrite8((u8)(lpc_port >> 8),
> +			 lpc_bpc->base + NPCM_BPCFA2M_REG);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	if (lpc_bpc->en_dwcap)
> +		addr_en = FIFO_DWCAPTURE;
> +
> +	/*
> +	 * Enable FIFO Ready Interrupt, FIFO Capture of I/O addr,
> +	 * and Host Reset
> +	 */
> +	reg_en = ioread8(lpc_bpc->base + NPCM_BPCFEN_REG);
> +	iowrite8(reg_en | addr_en | FIFO_READY_INT_ENABLE |
> +		 HOST_RESET_INT_ENABLE, lpc_bpc->base + NPCM_BPCFEN_REG);
> +
> +	return 0;
> +}
> +
> +static void npcm_disable_bpc(struct npcm_bpc *lpc_bpc, int channel)
> +{
> +	u8 reg_en;
> +
> +	switch (channel) {
> +	case 0:
> +		reg_en = ioread8(lpc_bpc->base + NPCM_BPCFEN_REG);
> +		if (lpc_bpc->en_dwcap)
> +			iowrite8(reg_en & ~FIFO_DWCAPTURE,
> +				 lpc_bpc->base + NPCM_BPCFEN_REG);
> +		else
> +			iowrite8(reg_en & ~FIFO_IOADDR1_ENABLE,
> +				 lpc_bpc->base + NPCM_BPCFEN_REG);
> +		break;
> +	case 1:
> +		reg_en = ioread8(lpc_bpc->base + NPCM_BPCFEN_REG);
> +		iowrite8(reg_en & ~FIFO_IOADDR2_ENABLE,
> +			 lpc_bpc->base + NPCM_BPCFEN_REG);
> +		break;
> +	default:
> +		return;
> +	}
> +
> +	if (!(reg_en & (FIFO_IOADDR1_ENABLE | FIFO_IOADDR2_ENABLE)))
> +		iowrite8(reg_en &
> +			 ~(FIFO_READY_INT_ENABLE | HOST_RESET_INT_ENABLE),
> +			 lpc_bpc->base + NPCM_BPCFEN_REG);
> +
> +	kfifo_free(&lpc_bpc->ch[channel].fifo);
> +	misc_deregister(&lpc_bpc->ch[channel].miscdev);
> +}
> +
> +static int npcm_bpc_probe(struct platform_device *pdev)
> +{
> +	struct npcm_bpc *lpc_bpc;
> +	struct resource *res;
> +	struct device *dev;
> +	u32 port;
> +	int rc;
> +
> +	dev = &pdev->dev;
> +
> +	lpc_bpc = devm_kzalloc(dev, sizeof(*lpc_bpc), GFP_KERNEL);
> +	if (!lpc_bpc)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(dev, "BIOS post code reg resource not found\n");
> +		return -ENODEV;
> +	}
> +
> +	dev_dbg(dev, "BIOS post code base resource is %pR\n", res);
> +	lpc_bpc->base = devm_ioremap_resource(dev, res);

Any reason for not using devm_platform_ioremap_resource() ?

Guenter

> +	if (IS_ERR(lpc_bpc->base))
> +		return PTR_ERR(lpc_bpc->base);
> +
> +	dev_set_drvdata(&pdev->dev, lpc_bpc);
> +
> +	rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0,
> +					&port);
> +	if (rc) {
> +		dev_err(dev, "no snoop ports configured\n");
> +		return -ENODEV;
> +	}
> +
> +	lpc_bpc->en_dwcap =
> +		of_property_read_bool(dev->of_node, "nuvoton,lpc-en-dwcapture");
> +
> +	rc = npcm_bpc_config_irq(lpc_bpc, pdev);
> +	if (rc)
> +		return rc;
> +
> +	rc = npcm_enable_bpc(lpc_bpc, dev, 0, port);
> +	if (rc) {
> +		dev_err(dev, "Enable BIOS post code I/O port 0 failed\n");
> +		return rc;
> +	}
> +
> +	/*
> +	 * Configuration of second BPC channel port is optional
> +	 * Double-Word Capture ignoring address 2
> +	 */
> +	if (!lpc_bpc->en_dwcap) {
> +		if (of_property_read_u32_index(dev->of_node, "snoop-ports",
> +					       1, &port) == 0) {
> +			rc = npcm_enable_bpc(lpc_bpc, dev, 1, port);
> +			if (rc) {
> +				dev_err(dev, "Enable BIOS post code I/O port 1 failed, disable I/O port 0\n");
> +				npcm_disable_bpc(lpc_bpc, 0);
> +				return rc;
> +			}
> +		}
> +	}
> +
> +	pr_info("NPCM BIOS post code probe\n");
> +
> +	return rc;
> +}
> +
> +static int npcm_bpc_remove(struct platform_device *pdev)
> +{
> +	struct npcm_bpc *lpc_bpc = dev_get_drvdata(&pdev->dev);
> +	u8 reg_en;
> +
> +	reg_en = ioread8(lpc_bpc->base + NPCM_BPCFEN_REG);
> +
> +	if (reg_en & FIFO_IOADDR1_ENABLE)
> +		npcm_disable_bpc(lpc_bpc, 0);
> +	if (reg_en & FIFO_IOADDR2_ENABLE)
> +		npcm_disable_bpc(lpc_bpc, 1);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id npcm_bpc_match[] = {
> +	{ .compatible = "nuvoton,npcm750-lpc-bpc-snoop" },
> +	{ },
> +};
> +
> +static struct platform_driver npcm_bpc_driver = {
> +	.driver = {
> +		.name		= DEVICE_NAME,
> +		.of_match_table = npcm_bpc_match,
> +	},
> +	.probe = npcm_bpc_probe,
> +	.remove = npcm_bpc_remove,
> +};
> +
> +module_platform_driver(npcm_bpc_driver);
> +
> +MODULE_DEVICE_TABLE(of, npcm_bpc_match);
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
> +MODULE_DESCRIPTION("Linux driver to control NPCM LPC BIOS post code snooping");
> 


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v1 1/2] dt-binding: misc: Add common LPC snoop documentation
  2019-04-15 12:12 ` [PATCH v1 1/2] dt-binding: misc: Add common LPC snoop documentation Tomer Maimon
@ 2019-04-16  1:04   ` Andrew Jeffery
  0 siblings, 0 replies; 5+ messages in thread
From: Andrew Jeffery @ 2019-04-16  1:04 UTC (permalink / raw)
  To: Tomer Maimon, Greg Kroah-Hartman, arnd, Rob Herring,
	mark.rutland, Nancy Yuen, Patrick Venture, Brendan Higgins,
	Avi Fishman, Joel Stanley
  Cc: linux-hwmon, devicetree, openbmc, linux-kernel



On Mon, 15 Apr 2019, at 21:51, Tomer Maimon wrote:
> Added device tree binding documentation for Nuvoton BMC
> NPCM BIOS Post Code (BPC) and Apeed AST2500 LPC snoop.
> The LPC snoop monitoring two configurable I/O addresses
> written by the host on Low Pin Count (LPC) bus.
> 
> Signed-off-by: Tomer Maimon <tmaimon77@gmail.com>
> ---
>  .../devicetree/bindings/misc/lpc-snoop.txt         | 27 ++++++++++++++++++++++
>  1 file changed, 27 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/misc/lpc-snoop.txt
> 
> diff --git a/Documentation/devicetree/bindings/misc/lpc-snoop.txt 
> b/Documentation/devicetree/bindings/misc/lpc-snoop.txt
> new file mode 100644
> index 000000000000..7deac244ef9d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/misc/lpc-snoop.txt
> @@ -0,0 +1,27 @@
> +LPC snoop interface
> +
> +The LPC snoop (BIOS Post Code) interface can monitor
> +two configurable I/O addresses written by the host on
> +the Low Pin Count (LPC) bus.

Wrapping looks kind of odd and there's a missing newline at the
end of the file, but for the content:

Reviewed-by: Andrew Jeffery <andrew@aj.id.au>

> +
> +Nuvoton NPCM7xx LPC snoop supports capture double words,
> +when using capture double word only I/O address 1 is monitored.
> +
> +Required properties for lpc-snoop node
> +- compatible   : "nuvoton,npcm750-lpc-bpc-snoop" for Poleg NPCM7XX
> +                 "aspeed,ast2500-lpc-snoop" for Aspeed AST2500.
> +- reg          : specifies physical base address and size of the 
> registers.
> +- interrupts   : contain the LPC snoop interrupt with flags for 
> falling edge.
> +- snoop-ports  : contain monitor I/O addresses, at least one monitor 
> I/O
> +                 address required
> +
> +Optional property for NPCM7xx lpc-snoop node
> +- nuvoton,lpc-en-dwcapture : enable capture double words support.
> +
> +Example:
> +	lpc-snoop: lpc_snoop@f0007040 {
> +		compatible = "nuvoton,npcm750-lpc-bpc-snoop";
> +		reg = <0xf0007040 0x14>;
> +		snoop-ports = <0x80>;
> +		interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
> +	};
> \ No newline at end of file
> -- 
> 2.14.1
> 
>

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2019-04-16  1:04 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-04-15 12:12 [PATCH v1 0/2] misc: Add NPCM LPC BPC driver support Tomer Maimon
2019-04-15 12:12 ` [PATCH v1 1/2] dt-binding: misc: Add common LPC snoop documentation Tomer Maimon
2019-04-16  1:04   ` Andrew Jeffery
2019-04-15 12:12 ` [PATCH v1 2/2] misc: npcm: add NPCM LPC BPC driver Tomer Maimon
2019-04-15 13:21   ` Guenter Roeck

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).