From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3tkgyt6PbzzDwZ3 for ; Thu, 22 Dec 2016 17:07:02 +1100 (AEDT) Received: from pps.filterd (m0098409.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.17/8.16.0.17) with SMTP id uBM64X7N008077 for ; Thu, 22 Dec 2016 01:07:01 -0500 Received: from e23smtp01.au.ibm.com (e23smtp01.au.ibm.com [202.81.31.143]) by mx0a-001b2d01.pphosted.com with ESMTP id 27g1v0nj5d-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Thu, 22 Dec 2016 01:07:01 -0500 Received: from localhost by e23smtp01.au.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 22 Dec 2016 16:06:58 +1000 Received: from d23dlp02.au.ibm.com (202.81.31.213) by e23smtp01.au.ibm.com (202.81.31.207) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Thu, 22 Dec 2016 16:06:56 +1000 Received: from d23relay06.au.ibm.com (d23relay06.au.ibm.com [9.185.63.219]) by d23dlp02.au.ibm.com (Postfix) with ESMTP id BD27C2BB0055 for ; Thu, 22 Dec 2016 17:06:55 +1100 (EST) Received: from d23av06.au.ibm.com (d23av06.au.ibm.com [9.190.235.151]) by d23relay06.au.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id uBM66t7448824518 for ; Thu, 22 Dec 2016 17:06:55 +1100 Received: from d23av06.au.ibm.com (localhost [127.0.0.1]) by d23av06.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id uBM66tmK009098 for ; Thu, 22 Dec 2016 17:06:55 +1100 Received: from ozlabs.au.ibm.com (ozlabs.au.ibm.com [9.192.253.14]) by d23av06.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVin) with ESMTP id uBM66tf7009092; Thu, 22 Dec 2016 17:06:55 +1100 Received: from camb691.ibm.com (unknown [9.81.194.196]) (using TLSv1.2 with cipher DHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ozlabs.au.ibm.com (Postfix) with ESMTPSA id 69593A0114; Thu, 22 Dec 2016 17:06:54 +1100 (AEDT) From: Cyril Bur To: openbmc@lists.ozlabs.org Cc: millerjo@linux.vnet.ibm.com Subject: [PATCH v2 4/5] drivers/mailbox: Add aspeed ast2400/ast2500 mbox driver Date: Thu, 22 Dec 2016 17:06:09 +1100 X-Mailer: git-send-email 2.11.0 In-Reply-To: <20161222060610.29695-1-cyrilbur@gmail.com> References: <20161222060610.29695-1-cyrilbur@gmail.com> X-TM-AS-MML: disable X-Content-Scanned: Fidelis XPS MAILER x-cbid: 16122206-1617-0000-0000-000001918E55 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 16122206-1618-0000-0000-0000476934AA Message-Id: <20161222060610.29695-5-cyrilbur@gmail.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2016-12-22_05:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=1 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1612050000 definitions=main-1612220102 X-BeenThere: openbmc@lists.ozlabs.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Development list for OpenBMC List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 22 Dec 2016 06:07:03 -0000 This provides access to the mbox registers on the ast2400 and ast2500 boards. An ioctl() is used to allow users to write to a specific register with a specific value. This is useful as the other end have configured the mbox registers to provide an interrupt when a specific regster gets written to. Signed-off-by: Cyril Bur --- drivers/mailbox/Kconfig | 9 + drivers/mailbox/Makefile | 3 + drivers/mailbox/aspeed-mbox.c | 354 +++++++++++++++++++++++++++++++++++++++ include/uapi/linux/aspeed-mbox.h | 23 +++ 4 files changed, 389 insertions(+) create mode 100644 drivers/mailbox/aspeed-mbox.c create mode 100644 include/uapi/linux/aspeed-mbox.h diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 5305923752d2..815cfe852dea 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -123,4 +123,13 @@ config XGENE_SLIMPRO_MBOX It is used to send short messages between ARM64-bit cores and the SLIMpro Management Engine, primarily for PM. Say Y here if you want to use the APM X-Gene SLIMpro IPCM support. + +config ASPEED_MBOX + depends on ARCH_ASPEED || COMPILE_TEST + bool "Aspeed ast2400/2500 Mailbox Controller" + default "y" + ---help--- + Provides a driver for the MBOX regsiters found on Aspeed SOCs + (AST2400 and AST2500). This driver provides a device for aspeed + mbox registers endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 0be3e742bb7d..99d17f3b9552 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -25,3 +25,6 @@ obj-$(CONFIG_TI_MESSAGE_MANAGER) += ti-msgmgr.o obj-$(CONFIG_XGENE_SLIMPRO_MBOX) += mailbox-xgene-slimpro.o obj-$(CONFIG_HI6220_MBOX) += hi6220-mailbox.o + +obj-$(CONFIG_ASPEED_MBOX) += aspeed-mbox.o + diff --git a/drivers/mailbox/aspeed-mbox.c b/drivers/mailbox/aspeed-mbox.c new file mode 100644 index 000000000000..a5da7fc0fd23 --- /dev/null +++ b/drivers/mailbox/aspeed-mbox.c @@ -0,0 +1,354 @@ +/* + * Copyright 2016 IBM Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_NAME "aspeed-mbox" + +#define MBOX_NUM_REGS 16 +#define MBOX_NUM_DATA_REGS 14 + +#define MBOX_DATA_0 0x00 +#define MBOX_STATUS_0 0x40 +#define MBOX_STATUS_1 0x44 +#define MBOX_BMC_CTRL 0x48 +#define MBOX_CTRL_RECV BIT(7) +#define MBOX_CTRL_MASK BIT(1) +#define MBOX_CTRL_SEND BIT(0) +#define MBOX_HOST_CTRL 0x4c +#define MBOX_INTERRUPT_0 0x50 +#define MBOX_INTERRUPT_1 0x54 + +struct mbox { + struct miscdevice miscdev; + struct regmap *regmap; + unsigned int base; + int irq; + wait_queue_head_t queue; + struct timer_list poll_timer; +}; + +static atomic_t mbox_open_count = ATOMIC_INIT(0); + +static u8 mbox_inb(struct mbox *mbox, int reg) +{ + /* + * The mbox registers are actually only 1 byte but are addressed 4 + * bytes appart. 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 */ + regmap_read(mbox->regmap, mbox->base + reg, &val); + return val & 0xff; +} + +static void mbox_outb(struct mbox *mbox, u8 data, int reg) +{ + regmap_write(mbox->regmap, mbox->base + reg, data); +} + +static struct mbox *file_mbox(struct file *file) +{ + return container_of(file->private_data, struct mbox, miscdev); +} + +static int mbox_open(struct inode *inode, struct file *file) +{ + struct mbox *mbox = file_mbox(file); + + if (atomic_inc_return(&mbox_open_count) == 1) { + /* + * Clear the interrupt status bit if it was left on and unmask + * interrupts. + * MBOX_CTRL_RECV bit is W1C, this also unmasks in 1 step + */ + mbox_outb(mbox, MBOX_CTRL_RECV, MBOX_BMC_CTRL); + return 0; + } + + atomic_dec(&mbox_open_count); + return -EBUSY; +} + +static ssize_t mbox_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct mbox *mbox = file_mbox(file); + char __user *p = buf; + int i; + + if (!access_ok(VERIFY_WRITE, buf, count)) + return -EFAULT; + + WARN_ON(*ppos); + + if (wait_event_interruptible(mbox->queue, + mbox_inb(mbox, MBOX_BMC_CTRL) & MBOX_CTRL_RECV)) + return -ERESTARTSYS; + + for (i = 0; i < MBOX_NUM_DATA_REGS; i++) + if (__put_user(mbox_inb(mbox, MBOX_DATA_0 + (i * 4)), p++)) + return -EFAULT; + + /* MBOX_CTRL_RECV bit is W1C, this also unmasks in 1 step */ + mbox_outb(mbox, MBOX_CTRL_RECV, MBOX_BMC_CTRL); + return p - buf; +} + +static ssize_t mbox_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct mbox *mbox = file_mbox(file); + const char __user *p = buf; + char c; + int i; + + if (!access_ok(VERIFY_READ, buf, count)) + return -EFAULT; + + WARN_ON(*ppos); + + for (i = 0; i < MBOX_NUM_DATA_REGS; i++) { + if (__get_user(c, p)) + return -EFAULT; + + mbox_outb(mbox, c, MBOX_DATA_0 + (i * 4)); + p++; + } + + mbox_outb(mbox, MBOX_CTRL_SEND, MBOX_BMC_CTRL); + + return p - buf; +} + +static long mbox_ioctl(struct file *file, unsigned int cmd, + unsigned long param) +{ + struct aspeed_mbox_atn atn; + struct mbox *mbox = file_mbox(file); + void __user *p = (void __user *)param; + + switch (cmd) { + case ASPEED_MBOX_IOCTL_ATN: + if (copy_from_user(&atn, p, sizeof(atn))) + return -EFAULT; + if (atn.reg > 15) + return -EINVAL; + + mbox_outb(mbox, atn.val, atn.reg); + return 0; + } + + return -EINVAL; +} + +static unsigned int mbox_poll(struct file *file, poll_table *wait) +{ + struct mbox *mbox = file_mbox(file); + unsigned int mask = 0; + + poll_wait(file, &mbox->queue, wait); + + if (mbox_inb(mbox, MBOX_BMC_CTRL) & MBOX_CTRL_RECV) + mask |= POLLIN; + + return mask; +} + +static int mbox_release(struct inode *inode, struct file *file) +{ + atomic_dec(&mbox_open_count); + return 0; +} + +static const struct file_operations mbox_fops = { + .owner = THIS_MODULE, + .open = mbox_open, + .read = mbox_read, + .write = mbox_write, + .release = mbox_release, + .poll = mbox_poll, + .unlocked_ioctl = mbox_ioctl, +}; + +static void poll_timer(unsigned long data) +{ + struct mbox *mbox = (void *)data; + + mbox->poll_timer.expires += msecs_to_jiffies(500); + wake_up(&mbox->queue); + add_timer(&mbox->poll_timer); +} + +static irqreturn_t mbox_irq(int irq, void *arg) +{ + struct mbox *mbox = arg; + + if (!(mbox_inb(mbox, MBOX_BMC_CTRL) & MBOX_CTRL_RECV)) + return IRQ_NONE; + + /* + * Leave the status bit set so that we know the data is for us, + * clear it once it has been read. + */ + + /* Mask it off, we'll clear it when we the data gets read */ + mbox_outb(mbox, MBOX_CTRL_MASK, MBOX_BMC_CTRL); + + wake_up(&mbox->queue); + return IRQ_HANDLED; +} + +static int mbox_config_irq(struct mbox *mbox, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int rc; + + mbox->irq = irq_of_parse_and_map(dev->of_node, 0); + if (!mbox->irq) + return -ENODEV; + + rc = devm_request_irq(dev, mbox->irq, mbox_irq, IRQF_SHARED, + DEVICE_NAME, mbox); + if (rc < 0) { + dev_warn(dev, "Unable to request IRQ %d\n", mbox->irq); + mbox->irq = 0; + return rc; + } + + /* + * Disable all register based interrupts, we'll have to change + * this, protocol seemingly will require regs 0 and 15 + */ + mbox_outb(mbox, 0x00, MBOX_INTERRUPT_0); /* regs 0 - 7 */ + mbox_outb(mbox, 0x00, MBOX_INTERRUPT_1); /* regs 8 - 15 */ + + /* W1C */ + mbox_outb(mbox, 0xff, MBOX_STATUS_0); + mbox_outb(mbox, 0xff, MBOX_STATUS_1); + + mbox_outb(mbox, MBOX_CTRL_RECV, MBOX_BMC_CTRL); + return 0; +} + +static int mbox_probe(struct platform_device *pdev) +{ + struct mbox *mbox; + struct device *dev; + int rc; + + if (!pdev || !pdev->dev.of_node) + return -ENODEV; + + dev = &pdev->dev; + dev_info(dev, "Found mbox host device\n"); + + 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"); + goto out; + } + + mbox->regmap = syscon_node_to_regmap( + pdev->dev.parent->of_node); + if (IS_ERR(mbox->regmap)) { + dev_err(dev, "Couldn't get regmap\n"); + rc = -ENODEV; + goto out; + } + + init_waitqueue_head(&mbox->queue); + + mbox->miscdev.minor = MISC_DYNAMIC_MINOR; + mbox->miscdev.name = DEVICE_NAME; + mbox->miscdev.fops = &mbox_fops; + mbox->miscdev.parent = dev; + rc = misc_register(&mbox->miscdev); + if (rc) { + dev_err(dev, "Unable to register device\n"); + goto out; + } + + mbox_config_irq(mbox, pdev); + + if (mbox->irq) { + dev_info(dev, "Using IRQ %d\n", mbox->irq); + } else { + dev_info(dev, "No IRQ; using timer\n"); + setup_timer(&mbox->poll_timer, poll_timer, + (unsigned long)mbox); + mbox->poll_timer.expires = jiffies + msecs_to_jiffies(10); + add_timer(&mbox->poll_timer); + } + +out: + return rc; + +} + +static int mbox_remove(struct platform_device *pdev) +{ + struct mbox *mbox = dev_get_drvdata(&pdev->dev); + + misc_deregister(&mbox->miscdev); + if (!mbox->irq) + del_timer_sync(&mbox->poll_timer); + mbox = NULL; + + return 0; +} + +static const struct of_device_id mbox_match[] = { + { .compatible = "aspeed,ast2400-mbox" }, + { }, +}; + +static struct platform_driver mbox_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = mbox_match, + }, + .probe = mbox_probe, + .remove = mbox_remove, +}; + +module_platform_driver(mbox_driver); + +MODULE_DEVICE_TABLE(of, mbox_match); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Cyril Bur "); +MODULE_DESCRIPTION("Linux device interface to the Aspeed MBOX registers"); diff --git a/include/uapi/linux/aspeed-mbox.h b/include/uapi/linux/aspeed-mbox.h new file mode 100644 index 000000000000..f4e6a9242fd0 --- /dev/null +++ b/include/uapi/linux/aspeed-mbox.h @@ -0,0 +1,23 @@ +/* + * Copyright 2016 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _UAPI_LINUX_MBOX_HOST_H +#define _UAPI_LINUX_MBOX_HOST_H + +#include + +struct aspeed_mbox_atn { + uint8_t reg; + uint8_t val; +}; + +#define __ASPEED_MBOX_IOCTL_MAGIC 0xb1 +#define ASPEED_MBOX_IOCTL_ATN _IOW(__ASPEED_MBOX_IOCTL_MAGIC, 0x00, struct aspeed_mbox_atn) + +#endif /* _UAPI_LINUX_MBOX_HOST_H */ -- 2.11.0