On Wed, 28 Dec 2016, Florian Fainelli wrote: > This driver is currently only used to reboot the devices, but the > microcontroller hanging off UART1 on the Buffalo Kurobox Pro and > Terastation II Pro/Live is capable of a lot more than that, which we > will support in subsequent patches. For now, just add the reboot > functionality to make the kernel reboot correctly.
This is not an MFD driver. You have written a UART driver. Please relocate it to drivers/char/tty/serial. > Signed-off-by: Florian Fainelli <f.faine...@gmail.com> > --- > drivers/mfd/Kconfig | 8 ++ > drivers/mfd/Makefile | 2 + > drivers/mfd/micon.c | 204 > ++++++++++++++++++++++++++++++++++++ > include/linux/platform_data/micon.h | 10 ++ > 4 files changed, 224 insertions(+) > create mode 100644 drivers/mfd/micon.c > create mode 100644 include/linux/platform_data/micon.h > > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig > index 4ce3b6f11830..e7df3d29351c 100644 > --- a/drivers/mfd/Kconfig > +++ b/drivers/mfd/Kconfig > @@ -352,6 +352,14 @@ config MFD_MX25_TSADC > i.MX25 processors. They consist of a conversion queue for general > purpose ADC and a queue for Touchscreens. > > +config MFD_MICON > + bool "Buffalo Kurobox Pro/Terastation II Pro/Live MCU interface" > + depends on PLAT_ORION > + help > + Enables support for the UART1 attached micro controller on Buffalo > + Kurobox Pro and Terastation II Pro/Live NAS devices. Used for > + machine restart. > + > config MFD_HI6421_PMIC > tristate "HiSilicon Hi6421 PMU/Codec IC" > depends on OF > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile > index dda4d4f73ad7..61a226878129 100644 > --- a/drivers/mfd/Makefile > +++ b/drivers/mfd/Makefile > @@ -94,6 +94,8 @@ obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o > obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o > obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o > > +obj-$(CONFIG_MFD_MICON) += micon.o > + > obj-$(CONFIG_MFD_CORE) += mfd-core.o > > obj-$(CONFIG_EZX_PCAP) += ezx-pcap.o > diff --git a/drivers/mfd/micon.c b/drivers/mfd/micon.c > new file mode 100644 > index 000000000000..fe56abdc0160 > --- /dev/null > +++ b/drivers/mfd/micon.c > @@ -0,0 +1,204 @@ > +/* > + * Buffalo Kurobox/Terastation Pro II specific power off method via > + * UART1-attached microcontroller > + * 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 <linux/kernel.h> > +#include <linux/io.h> > +#include <linux/delay.h> > +#include <linux/platform_device.h> > +#include <linux/platform_data/micon.h> > +#include <linux/serial_reg.h> > +#include <linux/notifier.h> > +#include <linux/reboot.h> > + > +struct micon_priv { > + struct device *dev; > + void __iomem *base; > + int tclk; > + struct notifier_block nb; > +}; > + > +#define UART1_REG(x) ((UART_##x) << 2) > + > +static int miconread(struct micon_priv *priv, unsigned char *buf, int count) > +{ > + int i; > + int timeout; > + > + for (i = 0; i < count; i++) { > + timeout = 10; > + > + while (!(readl(priv->base + UART1_REG(LSR)) & UART_LSR_DR)) { > + if (--timeout == 0) > + break; > + udelay(1000); > + } > + > + if (timeout == 0) > + break; > + buf[i] = readl(priv->base + UART1_REG(RX)); > + } > + > + /* return read bytes */ > + return i; > +} > + > +static int miconwrite(struct micon_priv *priv, const unsigned char *buf, > + int count) > +{ > + int i = 0; > + > + while (count--) { > + while (!(readl(priv->base + UART1_REG(LSR)) & UART_LSR_THRE)) > + barrier(); > + writel(buf[i++], priv->base + UART1_REG(TX)); > + } > + > + return 0; > +} > + > +static int miconsend(struct micon_priv *priv, const unsigned char *data, > + int count) > +{ > + int i; > + unsigned char checksum = 0; > + unsigned char recv_buf[40]; > + unsigned char send_buf[40]; > + unsigned char correct_ack[3]; > + int retry = 2; > + > + /* Generate checksum */ > + for (i = 0; i < count; i++) > + checksum -= data[i]; > + > + do { > + /* Send data */ > + miconwrite(priv, data, count); > + > + /* send checksum */ > + miconwrite(priv, &checksum, 1); > + > + if (miconread(priv, recv_buf, sizeof(recv_buf)) <= 3) { > + dev_err(priv->dev, ">%s: receive failed.\n", __func__); > + > + /* send preamble to clear the receive buffer */ > + memset(&send_buf, 0xff, sizeof(send_buf)); > + miconwrite(priv, send_buf, sizeof(send_buf)); > + > + /* make dummy reads */ > + mdelay(100); > + miconread(priv, recv_buf, sizeof(recv_buf)); > + } else { > + /* Generate expected ack */ > + correct_ack[0] = 0x01; > + correct_ack[1] = data[1]; > + correct_ack[2] = 0x00; > + > + /* checksum Check */ > + if ((recv_buf[0] + recv_buf[1] + recv_buf[2] + > + recv_buf[3]) & 0xFF) { > + dev_err(priv->dev, ">%s: Checksum Error : " > + "Received data[%02x, %02x, %02x, %02x]" > + "\n", __func__, recv_buf[0], > + recv_buf[1], recv_buf[2], recv_buf[3]); > + } else { > + /* Check Received Data */ > + if (correct_ack[0] == recv_buf[0] && > + correct_ack[1] == recv_buf[1] && > + correct_ack[2] == recv_buf[2]) { > + /* Interval for next command */ > + mdelay(10); > + > + /* Receive ACK */ > + return 0; > + } > + } > + /* Received NAK or illegal Data */ > + dev_err(priv->dev, ">%s: Error : NAK or Illegal Data " > + "Received\n", __func__); > + } > + } while (retry--); > + > + /* Interval for next command */ > + mdelay(10); > + > + return -1; > +} > + > +static int restart_handler(struct notifier_block *this, > + unsigned long code, void *cmd) > +{ > + struct micon_priv *priv = container_of(this, struct micon_priv, nb); > + > + const unsigned char watchdogkill[] = {0x01, 0x35, 0x00}; > + const unsigned char shutdownwait[] = {0x00, 0x0c}; > + const unsigned char poweroff[] = {0x00, 0x06}; > + /* 38400 baud divisor */ > + unsigned int divisor = ((priv->tclk + (8 * 38400)) / (16 * 38400)); > + > + if (code != SYS_DOWN && code != SYS_HALT) > + return NOTIFY_DONE; > + > + dev_info(priv->dev, "%s: triggering power-off...\n", __func__); > + > + /* hijack uart1 and reset into sane state (38400,8n1,even parity) */ > + writel(0x83, priv->base + UART1_REG(LCR)); > + writel(divisor & 0xff, priv->base + UART1_REG(DLL)); > + writel((divisor >> 8) & 0xff, priv->base + UART1_REG(DLM)); > + writel(0x1b, priv->base + UART1_REG(LCR)); > + writel(0x00, priv->base + UART1_REG(IER)); > + writel(0x07, priv->base + UART1_REG(FCR)); > + writel(0x00, priv->base + UART1_REG(MCR)); > + > + /* Send the commands to shutdown the Terastation Pro II */ > + miconsend(priv, watchdogkill, sizeof(watchdogkill)); > + miconsend(priv, shutdownwait, sizeof(shutdownwait)); > + miconsend(priv, poweroff, sizeof(poweroff)); > + > + return NOTIFY_DONE; > +} > + > +static int micon_probe(struct platform_device *pdev) > +{ > + struct micon_platform_data *pdata = pdev->dev.platform_data; > + struct micon_priv *priv; > + struct resource *r; > + int ret; > + > + if (!pdata) > + return -ENODEV; > + > + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->dev = &pdev->dev; > + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + > + /* Don't request exclusive access since 8250 may also utilize this > + * resource > + */ > + priv->base = devm_ioremap(&pdev->dev, r->start, resource_size(r)); > + if (IS_ERR(priv->base)) > + return -ENOMEM; > + > + priv->tclk = pdata->tclk; > + > + priv->nb.notifier_call = restart_handler; > + > + return register_reboot_notifier(&priv->nb); > +} > + > +static struct platform_driver micon_driver = { > + .probe = micon_probe, > + .driver = { > + .name = MICON_NAME, > + }, > +}; > +builtin_platform_driver(micon_driver); > diff --git a/include/linux/platform_data/micon.h > b/include/linux/platform_data/micon.h > new file mode 100644 > index 000000000000..67dbaad5dfaa > --- /dev/null > +++ b/include/linux/platform_data/micon.h > @@ -0,0 +1,10 @@ > +#ifndef __MICON_PDATA_H > +#define __MICON_PDATA_H > + > +#define MICON_NAME "micon" > + > +struct micon_platform_data { > + int tclk; > +}; > + > +#endif /* __MICON_PDATA_H */ -- Lee Jones Linaro STMicroelectronics Landing Team Lead Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog