From 674e059d34193930cd48df8fe1f2f5bf49c83469 Mon Sep 17 00:00:00 2001 From: roly Date: Thu, 9 Jan 2025 08:54:00 +0800 Subject: [PATCH] soc aspeed abr Add sysfs attrs for flash toggle --- drivers/spi/spi-aspeed-smc.c | 60 +++++++++++++ include/linux/soc/aspeed/aspeed-abr.h | 118 ++++++++++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 include/linux/soc/aspeed/aspeed-abr.h diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index 70c4e6e3e2e8..8a819f8f65ba 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -13,6 +13,7 @@ #include #include #include +#include #define DEVICE_NAME "spi-aspeed-smc" @@ -110,6 +111,53 @@ struct aspeed_spi { u8 *op_buf; }; +static ssize_t access_primary_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aspeed_spi *aspi = dev_get_drvdata(dev); + + return _access_primary_show(aspi->regs, attr, buf); +} + +static ssize_t access_primary_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct aspeed_spi *aspi = dev_get_drvdata(dev); + + return _access_primary_store(aspi->regs, attr, buf, size); +} + +static ssize_t access_backup_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aspeed_spi *aspi = dev_get_drvdata(dev); + + return _access_backup_show(aspi->regs, attr, buf); +} + +static ssize_t access_backup_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct aspeed_spi *aspi = dev_get_drvdata(dev); + + return _access_backup_store(aspi->regs, attr, buf, size); +} + +static DEVICE_ATTR_RW(access_primary); +static DEVICE_ATTR_RW(access_backup); + +static struct attribute *bswitch_primary_attrs[] = { + &dev_attr_access_primary.attr, NULL +}; + +static struct attribute *bswitch_backup_attrs[] = { + &dev_attr_access_backup.attr, NULL +}; + +ATTRIBUTE_GROUPS(bswitch_primary); +ATTRIBUTE_GROUPS(bswitch_backup); static u32 aspeed_spi_get_io_mode(const struct spi_mem_op *op) { switch (op->data.buswidth) { @@ -908,6 +956,18 @@ static int aspeed_spi_probe(struct platform_device *pdev) ctlr->num_chipselect = data->max_cs; ctlr->dev.of_node = dev->of_node; + if (of_device_is_compatible(dev->of_node, "aspeed,ast2600-fmc")) { + /* if boot from alt source, show access_primary, otherwise show access_backup */ + if (readl(aspi->regs + OFFSET_ABR_CTRL_STATUS) & + ABR_BOOT_SRC_INDICATE) { + if (devm_device_add_groups(dev, bswitch_primary_groups)) + dev_warn(dev, "Could not add access_primary\n"); + } else { + if (devm_device_add_groups(dev, bswitch_backup_groups)) + dev_warn(dev, "Could not add access_backup\n"); + } + } + ret = devm_spi_register_controller(dev, ctlr); if (ret) { dev_err(&pdev->dev, "spi_register_controller failed\n"); diff --git a/include/linux/soc/aspeed/aspeed-abr.h b/include/linux/soc/aspeed/aspeed-abr.h new file mode 100644 index 000000000000..144f43dff1ac --- /dev/null +++ b/include/linux/soc/aspeed/aspeed-abr.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __ASPEED_ABR_H__ +#define __ASPEED_ABR_H__ + +#include +#include +#include + +#define OFFSET_ABR_CTRL_STATUS 0x64 +#define OFFSET_ABR_TIMER_RELOAD 0x68 +#define OFFSET_ABR_TIMER_RESTART 0x6c + +#define ABR_WDT_ENABLE BIT(0) +#define ABR_BOOT_SRC_INDICATE BIT(4) +#define ABR_RESTART_MAGIC 0x4755 +#define ABR_CLEAR_BOOT_SRC_MAGIC (0xEA << 16) +#define ABR_RELOAD_MAX_VALUE 0x1fff + +static inline ssize_t _access_primary_show(void __iomem *regs, + struct device_attribute *attr, + char *buf) +{ + u32 status = readl(regs + OFFSET_ABR_CTRL_STATUS); + + return sysfs_emit(buf, "%u\n", !(status & ABR_BOOT_SRC_INDICATE)); +} + +static inline ssize_t _access_primary_store(void __iomem *regs, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + /* disable watchdog */ + if (val == 0) { + writel(0, regs + OFFSET_ABR_CTRL_STATUS); + return size; + } + + /* val is the microsecond, convert to reload count(0.1s) */ + val /= (100 * 1000); + + /* + * bit[12:0] : Reload value of expire time + * The time unit is 0.1 second. Default set at 22 seconds + * 0: Immediately timeout + */ + val = val < ABR_RELOAD_MAX_VALUE ? val : ABR_RELOAD_MAX_VALUE; + + writel(0, regs + OFFSET_ABR_CTRL_STATUS); + writel(val, regs + OFFSET_ABR_TIMER_RELOAD); + + /* Write 0x4755 value to load the reload value into watchdog counter */ + writel(ABR_RESTART_MAGIC, regs + OFFSET_ABR_TIMER_RESTART); + + /* Enable watchdog */ + writel(ABR_WDT_ENABLE, regs + OFFSET_ABR_CTRL_STATUS); + return size; +} + +static inline ssize_t _access_backup_show(void __iomem *regs, + struct device_attribute *attr, + char *buf) +{ + u32 status = readl(regs + OFFSET_ABR_CTRL_STATUS); + u32 timer_reload = readl(regs + OFFSET_ABR_TIMER_RELOAD); + + if (!(status & ABR_WDT_ENABLE)) + return sysfs_emit(buf, "%u\n", 0); + + /* + * [31:16] Counter value status + * timeout unit is 0.1s, convert to microseconds + */ + return sysfs_emit(buf, "%u\n", (timer_reload >> 16) * 100 * 1000); +} + +static inline ssize_t _access_backup_store(void __iomem *regs, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + + if (kstrtoul(buf, 10, &val)) + return -EINVAL; + + /* disable watchdog */ + if (val == 0) { + writel(0, regs + OFFSET_ABR_CTRL_STATUS); + return size; + } + + /* val is the microsecond, convert to reload count(0.1s) */ + val /= (100 * 1000); + + /* + * bit[12:0] : Reload value of expire time + * The time unit is 0.1 second. Default set at 22 seconds + * 0: Immediately timeout + */ + val = val < ABR_RELOAD_MAX_VALUE ? val : ABR_RELOAD_MAX_VALUE; + + writel(0, regs + OFFSET_ABR_CTRL_STATUS); + writel(val, regs + OFFSET_ABR_TIMER_RELOAD); + + /* Write 0x4755 value to load the reload value into watchdog counter */ + writel(ABR_RESTART_MAGIC, regs + OFFSET_ABR_TIMER_RESTART); + + /* Enable watchdog */ + writel(ABR_WDT_ENABLE, regs + OFFSET_ABR_CTRL_STATUS); + return size; +} + +#endif -- 2.25.1