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