From: f.fainelli@gmail.com (Florian Fainelli)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 1/2] mfd: micon: Add Buffalo Kurobox Pro, Terastation II Pro/Live driver
Date: Wed, 28 Dec 2016 16:01:36 -0800 [thread overview]
Message-ID: <20161229000137.5553-2-f.fainelli@gmail.com> (raw)
In-Reply-To: <20161229000137.5553-1-f.fainelli@gmail.com>
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.
Signed-off-by: Florian Fainelli <f.fainelli@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 */
--
2.9.3
WARNING: multiple messages have this Message-ID (diff)
From: Florian Fainelli <f.fainelli@gmail.com>
To: linux-arm-kernel@lists.infradead.org
Cc: lee.jones@linaro.org, linux@armlinux.org.uk,
gregory.clement@free-electrons.com, andrew@lunn.ch,
jason@lakedaemon.net, linux-kernel@vger.kernel.org,
Florian Fainelli <f.fainelli@gmail.com>
Subject: [PATCH 1/2] mfd: micon: Add Buffalo Kurobox Pro, Terastation II Pro/Live driver
Date: Wed, 28 Dec 2016 16:01:36 -0800 [thread overview]
Message-ID: <20161229000137.5553-2-f.fainelli@gmail.com> (raw)
In-Reply-To: <20161229000137.5553-1-f.fainelli@gmail.com>
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.
Signed-off-by: Florian Fainelli <f.fainelli@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 */
--
2.9.3
next prev parent reply other threads:[~2016-12-29 0:01 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-12-29 0:01 [PATCH 0/2] ARM: orion5x: Move micon code to a MFD driver Florian Fainelli
2016-12-29 0:01 ` Florian Fainelli
2016-12-29 0:01 ` Florian Fainelli [this message]
2016-12-29 0:01 ` [PATCH 1/2] mfd: micon: Add Buffalo Kurobox Pro, Terastation II Pro/Live driver Florian Fainelli
2017-01-04 11:22 ` Lee Jones
2017-01-04 11:22 ` Lee Jones
2017-01-05 2:26 ` Florian Fainelli
2017-01-05 2:26 ` Florian Fainelli
2017-01-05 7:45 ` Lee Jones
2017-01-05 7:45 ` Lee Jones
2016-12-29 0:01 ` [PATCH 2/2] ARM: orion5x: Utilize micon MFD driver Florian Fainelli
2016-12-29 0:01 ` Florian Fainelli
2017-01-02 21:24 ` [PATCH 0/2] ARM: orion5x: Move micon code to a " Andrew Lunn
2017-01-02 21:24 ` Andrew Lunn
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20161229000137.5553-2-f.fainelli@gmail.com \
--to=f.fainelli@gmail.com \
--cc=linux-arm-kernel@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.