From 5d411ed0d66d3d00232519ed7d4ab6fac45e7c6e Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo 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 " Signed-off-by: Jae Hyun Yoo --- arch/arm/boot/dts/aspeed-g4.dtsi | 9 + arch/arm/boot/dts/aspeed-g5.dtsi | 9 + drivers/soc/aspeed/Kconfig | 7 + drivers/soc/aspeed/Makefile | 1 + drivers/soc/aspeed/aspeed-lpc-mbox.c | 376 +++++++++++++++++++++++++++++++++++ 5 files changed, 402 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 20b2eb8052b7..bd6d1461e4bd 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi @@ -502,6 +502,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/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig index 78dd74c49ddb..a4be8e566bc7 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 e631b23d519b..f3ff29b874ed 100644 --- a/drivers/soc/aspeed/Makefile +++ b/drivers/soc/aspeed/Makefile @@ -1,5 +1,6 @@ # 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 diff --git a/drivers/soc/aspeed/aspeed-lpc-mbox.c b/drivers/soc/aspeed/aspeed-lpc-mbox.c new file mode 100644 index 000000000000..795107206022 --- /dev/null +++ b/drivers/soc/aspeed/aspeed-lpc-mbox.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright 2017 IBM Corporation +// TODO: Rewrite this driver + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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" }, + { }, +}; +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 "); +MODULE_DESCRIPTION("Aspeed mailbox device driver"); -- 2.7.4