[RFC 1/2] spi: aspeed: ASPEED 24XX/25XX SPI1 disable/passthrough/master mode control
Kun Yi
kunyi at google.com
Sat Oct 22 11:45:57 AEDT 2016
Simple driver that exposes userspace sysfs to control SPI1 mode. It also
optionally override initial SPI mode if given "start_mode" parameter in
the device tree.
Signed-off-by: Kun Yi <kunyi at google.com>
---
drivers/misc/Kconfig | 6 ++
drivers/misc/Makefile | 1 +
drivers/misc/aspeed-spi-mode.c | 191 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 198 insertions(+)
create mode 100644 drivers/misc/aspeed-spi-mode.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 4617ddc..ea9b713 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -809,6 +809,12 @@ config ASPEED_BT_IPMI_HOST
help
Support for the Aspeed BT ipmi host.
+config SPI_MODECTRL_ASPEED
+ tristate "Support for SPI1 host flash mode change on Aspeed 24xx/25xx"
+ ---help---
+ Support for the Aspeed 24xx/25xx sysfs control toggle between
+ disabled/master/pass-through modes.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 724861b..0cce3d6 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -58,3 +58,4 @@ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
obj-$(CONFIG_CXL_BASE) += cxl/
obj-$(CONFIG_PANEL) += panel.o
obj-$(CONFIG_ASPEED_BT_IPMI_HOST) += bt-host.o
+obj-$(CONFIG_SPI_MODECTRL_ASPEED) += aspeed-spi-mode.o
diff --git a/drivers/misc/aspeed-spi-mode.c b/drivers/misc/aspeed-spi-mode.c
new file mode 100644
index 0000000..20f7754
--- /dev/null
+++ b/drivers/misc/aspeed-spi-mode.c
@@ -0,0 +1,191 @@
+/*
+ * ASPEED host flash SPI1 mode controller driver.
+ *
+ * Copyright (c) 2016 Google Inc.
+ *
+ * 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/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_platform.h>
+
+struct aspeed_spi_mode_controller {
+ struct mutex mutex; /* controller access mutex */
+ void __iomem *hwstrap_regs; /* controller registers */
+ void __iomem *hwstrap_reset; /* scu reset registers */
+};
+
+/* SPI mode definitions. MODE_DEBUG is reserved on AST2500. */
+#define SPI_MODE_DISABLED 0
+#define SPI_MODE_MASTER 1
+#define SPI_MODE_DEBUG 2
+#define SPI_MODE_PASSTHROUGH 3
+/* SPI mode control bits in HW strap register */
+#define SPI_MODE_OFFSET (12)
+#define SPI_MODE_MASK (BIT(12) | BIT(13))
+/* Extract SPI mode from register value */
+#define SPI_MODE(reg) ((reg & SPI_MODE_MASK) >> SPI_MODE_OFFSET)
+
+static int aspeed_get_spi_mode(struct aspeed_spi_mode_controller *controller)
+{
+ u32 reg;
+
+ mutex_lock(&controller->mutex);
+ reg = readl(controller->hwstrap_regs);
+ mutex_unlock(&controller->mutex);
+
+ return SPI_MODE(reg);
+}
+
+/*
+ * Set SPI1 interface to given mode.
+ * Note SCU70 reg bits cannot be set to 0. Need to write to SCU7C in order to
+ * clear bits in SCU70.
+ */
+static int aspeed_set_spi_mode(struct aspeed_spi_mode_controller *controller,
+ unsigned int mode)
+{
+
+ if (mode != SPI_MODE_DISABLED &&
+ mode != SPI_MODE_MASTER &&
+ mode != SPI_MODE_PASSTHROUGH) {
+ return -EPERM;
+ }
+
+ void __iomem *regs;
+ void __iomem *regs_reset;
+ u32 val;
+
+ regs = controller->hwstrap_regs;
+ regs_reset = controller->hwstrap_reset;
+ mutex_lock(&controller->mutex);
+ val = readl(regs);
+
+ if (SPI_MODE(val) == mode)
+ goto finish;
+
+ /* Reset bit 13:12 and set to new value */
+ writel(SPI_MODE_MASK, regs_reset);
+ val &= ~SPI_MODE_MASK;
+ val |= (mode << SPI_MODE_OFFSET);
+ writel(val, regs);
+
+ pr_debug("HW strap register set to 0x%x\n", readl(regs));
+
+finish:
+ mutex_unlock(&controller->mutex);
+ return 0;
+}
+
+static ssize_t aspeed_spi_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct aspeed_spi_mode_controller *controller;
+
+ controller = dev_get_drvdata(dev);
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ aspeed_get_spi_mode(controller));
+}
+
+static ssize_t aspeed_spi_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aspeed_spi_mode_controller *controller = dev_get_drvdata(dev);
+ unsigned long mode;
+ int err;
+
+ err = kstrtoul(buf, 10, &mode);
+ if (err)
+ return err;
+
+ err = aspeed_set_spi_mode(controller, mode);
+
+ if (err) {
+ dev_err(dev, "Support disabled(0) master(1) pass-through(3)");
+ return err;
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(spi_mode, S_IRUSR | S_IWUSR, aspeed_spi_mode_show,
+ aspeed_spi_mode_store);
+
+
+static const struct of_device_id aspeed_spi_mode_match[] = {
+ { .compatible = "aspeed,aspeed_spi_mode" },
+ {},
+}
+MODULE_DEVICE_TABLE(of, aspeed_spi_mode_match);
+
+static int aspeed_spi_mode_probe(struct platform_device *pdev)
+{
+ struct aspeed_spi_mode_controller *controller;
+ struct resource *r;
+ void __iomem *regs, *regs_reset;
+ u32 mode;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ regs_reset = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(regs_reset))
+ return PTR_ERR(regs_reset);
+
+ controller = devm_kzalloc(&pdev->dev, sizeof(*controller), GFP_KERNEL);
+ if (!controller)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, controller);
+ controller->hwstrap_regs = regs;
+ controller->hwstrap_reset = regs_reset;
+ mutex_init(&controller->mutex);
+
+ if (!of_property_read_u32(pdev->dev.of_node, "start_mode", &mode)) {
+ int err;
+
+ err = aspeed_set_spi_mode(controller, mode);
+ if (!err)
+ dev_info(&pdev->dev, "Set SPI mode to %d\n", mode);
+ }
+
+ if (device_create_file(&pdev->dev, &dev_attr_spi_mode) != 0)
+ dev_err(&pdev->dev, "Sysfs attribute creation failed");
+
+ dev_info(&pdev->dev, "ASPEED spi mode controller initialized");
+
+ return 0;
+}
+
+static int aspeed_spi_mode_remove(struct platform_device *pdev)
+{
+ device_remove_file(&pdev->dev, &dev_attr_spi_mode);
+ return 0;
+}
+
+static struct platform_driver aspeed_spi_mode_driver = {
+ .probe = aspeed_spi_mode_probe,
+ .remove = aspeed_spi_mode_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = aspeed_spi_mode_match,
+ }
+};
+
+module_platform_driver(aspeed_spi_mode_driver);
+
+MODULE_AUTHOR("Kun Yi <kunyi at google.com>");
+MODULE_DESCRIPTION("ASPEED SPI mode controller");
+MODULE_LICENSE("GPL v2");
--
2.8.0.rc3.226.g39d4020
More information about the openbmc
mailing list