diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0020-misc-aspeed-add-lpc-mbox-driver.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0020-misc-aspeed-add-lpc-mbox-driver.patch | 491 |
1 files changed, 491 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0020-misc-aspeed-add-lpc-mbox-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0020-misc-aspeed-add-lpc-mbox-driver.patch new file mode 100644 index 000000000..c5add2dd5 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0020-misc-aspeed-add-lpc-mbox-driver.patch @@ -0,0 +1,491 @@ +From 42326dfecd45c52918272cf6b65e6eb5b8499c46 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Wed, 10 Jul 2019 16:19:33 -0700 +Subject: [PATCH] misc: aspeed: add lpc mbox driver + +This commit adds back the lpc mbox driver which was removed from +the openbmc linux dev-5.2 tree. + +This driver should be rewritten later. + +Signed-off-by: Cyril Bur <cyrilbur@gmail.com>" +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Signed-off-by: Arun P. Mohanan <arun.p.m@linux.intel.com> +--- + arch/arm/boot/dts/aspeed-g4.dtsi | 9 + + arch/arm/boot/dts/aspeed-g5.dtsi | 9 + + arch/arm/boot/dts/aspeed-g6.dtsi | 1 + + drivers/soc/aspeed/Kconfig | 7 + + drivers/soc/aspeed/Makefile | 1 + + drivers/soc/aspeed/aspeed-lpc-mbox.c | 377 +++++++++++++++++++++++++++ + 6 files changed, 404 insertions(+) + create mode 100644 drivers/soc/aspeed/aspeed-lpc-mbox.c + +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index e9fd66ab3099..f3edda4ae477 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -393,6 +393,15 @@ + sio_regs: regs { + compatible = "aspeed,bmc-misc"; + }; ++ ++ mbox: mbox@180 { ++ compatible = "aspeed,ast2400-mbox"; ++ reg = <0x180 0x5c>; ++ interrupts = <46>; ++ #mbox-cells = <1>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; + }; + }; + +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index b4ae3827ed1d..b69110b9eab9 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -522,6 +522,15 @@ + sio_regs: regs { + compatible = "aspeed,bmc-misc"; + }; ++ ++ mbox: mbox@180 { ++ compatible = "aspeed,ast2500-mbox"; ++ reg = <0x180 0x5c>; ++ interrupts = <46>; ++ #mbox-cells = <1>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; ++ status = "disabled"; ++ }; + }; + }; + +diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi +index 825e64ce317a..183869eaf79a 100644 +--- a/arch/arm/boot/dts/aspeed-g6.dtsi ++++ b/arch/arm/boot/dts/aspeed-g6.dtsi +@@ -594,6 +594,7 @@ + reg = <0x180 0x5c>; + interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>; + #mbox-cells = <1>; ++ clocks = <&syscon ASPEED_CLK_GATE_LCLK>; + }; + }; + }; +diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig +index a4b3cac87ce2..464a55c7eb39 100644 +--- a/drivers/soc/aspeed/Kconfig ++++ b/drivers/soc/aspeed/Kconfig +@@ -21,6 +21,13 @@ config ASPEED_LPC_CTRL + ioctl()s, the driver also provides a read/write interface to a BMC ram + region where the host LPC read/write region can be buffered. + ++config ASPEED_LPC_MBOX ++ tristate "Aspeed LPC Mailbox Controller" ++ depends on SOC_ASPEED && REGMAP && MFD_SYSCON ++ ---help--- ++ Expose the ASPEED LPC MBOX registers found on Aspeed SOCs (AST2400 ++ and AST2500) to userspace. ++ + config ASPEED_LPC_SNOOP + tristate "Aspeed ast2500 HOST LPC snoop support" + depends on SOC_ASPEED && REGMAP && MFD_SYSCON +diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile +index 217d876fec25..26924c111af3 100644 +--- a/drivers/soc/aspeed/Makefile ++++ b/drivers/soc/aspeed/Makefile +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + obj-$(CONFIG_ASPEED_BMC_MISC) += aspeed-bmc-misc.o + obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o ++obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o + obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o + obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o + obj-$(CONFIG_ASPEED_XDMA) += aspeed-xdma.o +diff --git a/drivers/soc/aspeed/aspeed-lpc-mbox.c b/drivers/soc/aspeed/aspeed-lpc-mbox.c +new file mode 100644 +index 000000000000..583feecc4f18 +--- /dev/null ++++ b/drivers/soc/aspeed/aspeed-lpc-mbox.c +@@ -0,0 +1,377 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++// Copyright 2017 IBM Corporation ++// TODO: Rewrite this driver ++ ++#include <linux/clk.h> ++#include <linux/interrupt.h> ++#include <linux/mfd/syscon.h> ++#include <linux/miscdevice.h> ++#include <linux/module.h> ++#include <linux/of_irq.h> ++#include <linux/platform_device.h> ++#include <linux/poll.h> ++#include <linux/regmap.h> ++#include <linux/slab.h> ++ ++#define DEVICE_NAME "aspeed-mbox" ++ ++#define MBX_USE_INTERRUPT 0 ++ ++#define ASPEED_MBOX_NUM_REGS 16 ++ ++#define ASPEED_MBOX_DATA_0 0x00 ++#define ASPEED_MBOX_STATUS_0 0x40 ++#define ASPEED_MBOX_STATUS_1 0x44 ++#define ASPEED_MBOX_BMC_CTRL 0x48 ++#define ASPEED_MBOX_CTRL_RECV BIT(7) ++#define ASPEED_MBOX_CTRL_MASK BIT(1) ++#define ASPEED_MBOX_CTRL_SEND BIT(0) ++#define ASPEED_MBOX_HOST_CTRL 0x4c ++#define ASPEED_MBOX_INTERRUPT_0 0x50 ++#define ASPEED_MBOX_INTERRUPT_1 0x54 ++ ++struct aspeed_mbox { ++ struct miscdevice miscdev; ++ struct regmap *regmap; ++ struct clk *clk; ++ unsigned int base; ++ int irq; ++ wait_queue_head_t queue; ++ struct mutex mutex; ++}; ++ ++static atomic_t aspeed_mbox_open_count = ATOMIC_INIT(0); ++ ++static u8 aspeed_mbox_inb(struct aspeed_mbox *mbox, int reg) ++{ ++ /* ++ * The mbox registers are actually only one byte but are addressed ++ * four bytes apart. The other three bytes are marked 'reserved', ++ * they *should* be zero but lets not rely on it. ++ * I am going to rely on the fact we can casually read/write to them... ++ */ ++ unsigned int val = 0xff; /* If regmap throws an error return 0xff */ ++ int rc = regmap_read(mbox->regmap, mbox->base + reg, &val); ++ ++ if (rc) ++ dev_err(mbox->miscdev.parent, "regmap_read() failed with " ++ "%d (reg: 0x%08x)\n", rc, reg); ++ ++ return val & 0xff; ++} ++ ++static void aspeed_mbox_outb(struct aspeed_mbox *mbox, u8 data, int reg) ++{ ++ int rc = regmap_write(mbox->regmap, mbox->base + reg, data); ++ ++ if (rc) ++ dev_err(mbox->miscdev.parent, "regmap_write() failed with " ++ "%d (data: %u reg: 0x%08x)\n", rc, data, reg); ++} ++ ++static struct aspeed_mbox *file_mbox(struct file *file) ++{ ++ return container_of(file->private_data, struct aspeed_mbox, miscdev); ++} ++ ++static int aspeed_mbox_open(struct inode *inode, struct file *file) ++{ ++#if MBX_USE_INTERRUPT ++ struct aspeed_mbox *mbox = file_mbox(file); ++#endif ++ ++ if (atomic_inc_return(&aspeed_mbox_open_count) == 1) { ++#if MBX_USE_INTERRUPT ++ /* ++ * Clear the interrupt status bit if it was left on and unmask ++ * interrupts. ++ * ASPEED_MBOX_CTRL_RECV bit is W1C, this also unmasks in 1 step ++ */ ++ aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_RECV, ASPEED_MBOX_BMC_CTRL); ++#endif ++ return 0; ++ } ++ ++ atomic_dec(&aspeed_mbox_open_count); ++ return -EBUSY; ++} ++ ++static ssize_t aspeed_mbox_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct aspeed_mbox *mbox = file_mbox(file); ++ char __user *p = buf; ++ ssize_t ret; ++ int i; ++ ++ if (!access_ok(buf, count)) ++ return -EFAULT; ++ ++ if (count + *ppos > ASPEED_MBOX_NUM_REGS) ++ return -EINVAL; ++ ++#if MBX_USE_INTERRUPT ++ if (file->f_flags & O_NONBLOCK) { ++ if (!(aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) & ++ ASPEED_MBOX_CTRL_RECV)) ++ return -EAGAIN; ++ } else if (wait_event_interruptible(mbox->queue, ++ aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) & ++ ASPEED_MBOX_CTRL_RECV)) { ++ return -ERESTARTSYS; ++ } ++#endif ++ ++ mutex_lock(&mbox->mutex); ++ ++ for (i = *ppos; count > 0 && i < ASPEED_MBOX_NUM_REGS; i++) { ++ uint8_t reg = aspeed_mbox_inb(mbox, ASPEED_MBOX_DATA_0 + (i * 4)); ++ ++ ret = __put_user(reg, p); ++ if (ret) ++ goto out_unlock; ++ ++ p++; ++ count--; ++ } ++ ++#if MBX_USE_INTERRUPT ++ /* ASPEED_MBOX_CTRL_RECV bit is write to clear, this also unmasks in 1 step */ ++ aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_RECV, ASPEED_MBOX_BMC_CTRL); ++#endif ++ ret = p - buf; ++ ++out_unlock: ++ mutex_unlock(&mbox->mutex); ++ return ret; ++} ++ ++static ssize_t aspeed_mbox_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct aspeed_mbox *mbox = file_mbox(file); ++ const char __user *p = buf; ++ ssize_t ret; ++ char c; ++ int i; ++ ++ if (!access_ok(buf, count)) ++ return -EFAULT; ++ ++ if (count + *ppos > ASPEED_MBOX_NUM_REGS) ++ return -EINVAL; ++ ++ mutex_lock(&mbox->mutex); ++ ++ for (i = *ppos; count > 0 && i < ASPEED_MBOX_NUM_REGS; i++) { ++ ret = __get_user(c, p); ++ if (ret) ++ goto out_unlock; ++ ++ aspeed_mbox_outb(mbox, c, ASPEED_MBOX_DATA_0 + (i * 4)); ++ p++; ++ count--; ++ } ++ ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STATUS_0); ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STATUS_1); ++ aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_RECV | ASPEED_MBOX_CTRL_MASK | ASPEED_MBOX_CTRL_SEND, ASPEED_MBOX_BMC_CTRL); ++ ret = p - buf; ++ ++out_unlock: ++ mutex_unlock(&mbox->mutex); ++ return ret; ++} ++ ++static unsigned int aspeed_mbox_poll(struct file *file, poll_table *wait) ++{ ++ struct aspeed_mbox *mbox = file_mbox(file); ++ unsigned int mask = 0; ++ ++ poll_wait(file, &mbox->queue, wait); ++ ++#if MBX_USE_INTERRUPT ++ if (aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) & ASPEED_MBOX_CTRL_RECV) ++#endif ++ mask |= POLLIN; ++ ++ return mask; ++} ++ ++static int aspeed_mbox_release(struct inode *inode, struct file *file) ++{ ++ atomic_dec(&aspeed_mbox_open_count); ++ return 0; ++} ++ ++static const struct file_operations aspeed_mbox_fops = { ++ .owner = THIS_MODULE, ++ .llseek = no_seek_end_llseek, ++ .read = aspeed_mbox_read, ++ .write = aspeed_mbox_write, ++ .open = aspeed_mbox_open, ++ .release = aspeed_mbox_release, ++ .poll = aspeed_mbox_poll, ++}; ++ ++static irqreturn_t aspeed_mbox_irq(int irq, void *arg) ++{ ++ struct aspeed_mbox *mbox = arg; ++#if MBX_USE_INTERRUPT ++ int i; ++ ++// if (!(aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL) & ASPEED_MBOX_CTRL_RECV)) ++// return IRQ_NONE; ++ ++ printk(KERN_ERR "BMC_CTRL: 0x%02x\n", ++ aspeed_mbox_inb(mbox, ASPEED_MBOX_BMC_CTRL)); ++ printk(KERN_ERR "STATUS_0: 0x%02x\n", ++ aspeed_mbox_inb(mbox, ASPEED_MBOX_STATUS_0)); ++ printk(KERN_ERR "STATUS_1: 0x%02x\n", ++ aspeed_mbox_inb(mbox, ASPEED_MBOX_STATUS_1)); ++ for (i = 0; i < ASPEED_MBOX_NUM_REGS; i++) { ++ printk(KERN_ERR "DATA_%d: 0x%02x\n", i, ++ aspeed_mbox_inb(mbox, ASPEED_MBOX_DATA_0 + (i * 4))); ++ } ++#endif ++ ++ /* Clear interrupt status */ ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STATUS_0); ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STATUS_1); ++ aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_RECV, ASPEED_MBOX_BMC_CTRL); ++ ++ wake_up(&mbox->queue); ++ return IRQ_HANDLED; ++} ++ ++static int aspeed_mbox_config_irq(struct aspeed_mbox *mbox, ++ struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ int rc; ++ ++ mbox->irq = platform_get_irq(pdev, 0); ++ if (!mbox->irq) ++ return -ENODEV; ++ ++ rc = devm_request_irq(dev, mbox->irq, aspeed_mbox_irq, ++ IRQF_SHARED, DEVICE_NAME, mbox); ++ if (rc < 0) { ++ dev_err(dev, "Unable to request IRQ %d\n", mbox->irq); ++ return rc; ++ } ++ ++ /* Disable all register based interrupts. */ ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_INTERRUPT_0); /* regs 0 - 7 */ ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_INTERRUPT_1); /* regs 8 - 15 */ ++ ++ /* These registers are write one to clear. Clear them. */ ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STATUS_0); ++ aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STATUS_1); ++ ++ aspeed_mbox_outb(mbox, ASPEED_MBOX_CTRL_RECV, ASPEED_MBOX_BMC_CTRL); ++ return 0; ++} ++ ++static int aspeed_mbox_probe(struct platform_device *pdev) ++{ ++ struct aspeed_mbox *mbox; ++ struct device *dev; ++ int rc; ++ ++ dev = &pdev->dev; ++ ++ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); ++ if (!mbox) ++ return -ENOMEM; ++ ++ dev_set_drvdata(&pdev->dev, mbox); ++ ++ rc = of_property_read_u32(dev->of_node, "reg", &mbox->base); ++ if (rc) { ++ dev_err(dev, "Couldn't read reg device-tree property\n"); ++ return rc; ++ } ++ ++ mbox->regmap = syscon_node_to_regmap( ++ pdev->dev.parent->of_node); ++ if (IS_ERR(mbox->regmap)) { ++ dev_err(dev, "Couldn't get regmap\n"); ++ return -ENODEV; ++ } ++ ++ mutex_init(&mbox->mutex); ++ init_waitqueue_head(&mbox->queue); ++ ++ mbox->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(mbox->clk)) { ++ rc = PTR_ERR(mbox->clk); ++ if (rc != -EPROBE_DEFER) ++ dev_err(dev, "couldn't get clock\n"); ++ return rc; ++ } ++ rc = clk_prepare_enable(mbox->clk); ++ if (rc) { ++ dev_err(dev, "couldn't enable clock\n"); ++ return rc; ++ } ++ ++ mbox->miscdev.minor = MISC_DYNAMIC_MINOR; ++ mbox->miscdev.name = DEVICE_NAME; ++ mbox->miscdev.fops = &aspeed_mbox_fops; ++ mbox->miscdev.parent = dev; ++ rc = misc_register(&mbox->miscdev); ++ if (rc) { ++ dev_err(dev, "Unable to register device\n"); ++ goto err; ++ } ++ ++ rc = aspeed_mbox_config_irq(mbox, pdev); ++ if (rc) { ++ dev_err(dev, "Failed to configure IRQ\n"); ++ misc_deregister(&mbox->miscdev); ++ goto err; ++ } ++ ++ dev_info(&pdev->dev, "LPC mbox registered, irq %d\n", mbox->irq); ++ ++ return 0; ++ ++err: ++ clk_disable_unprepare(mbox->clk); ++ ++ return rc; ++} ++ ++static int aspeed_mbox_remove(struct platform_device *pdev) ++{ ++ struct aspeed_mbox *mbox = dev_get_drvdata(&pdev->dev); ++ ++ misc_deregister(&mbox->miscdev); ++ clk_disable_unprepare(mbox->clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_mbox_match[] = { ++ { .compatible = "aspeed,ast2400-mbox" }, ++ { .compatible = "aspeed,ast2500-mbox" }, ++ { .compatible = "aspeed,ast2600-mbox" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, aspeed_mbox_match); ++ ++static struct platform_driver aspeed_mbox_driver = { ++ .driver = { ++ .name = DEVICE_NAME, ++ .of_match_table = aspeed_mbox_match, ++ }, ++ .probe = aspeed_mbox_probe, ++ .remove = aspeed_mbox_remove, ++}; ++ ++module_platform_driver(aspeed_mbox_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Cyril Bur <cyrilbur@gmail.com>"); ++MODULE_DESCRIPTION("Aspeed mailbox device driver"); +-- +2.17.1 + |