diff options
author | Andrew Jeffery <andrew@aj.id.au> | 2018-04-11 09:34:10 +0300 |
---|---|---|
committer | Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com> | 2021-10-20 01:10:37 +0300 |
commit | 8eaa1391bca297010b9d0dbf0fc4e4d2642c235e (patch) | |
tree | c3bb966c7be927b53b5a8dd3dbe43af61d290010 | |
parent | 38fd0f320ae5f6ab9bffd1aa7b595250e67f59ec (diff) | |
download | linux-8eaa1391bca297010b9d0dbf0fc4e4d2642c235e.tar.xz |
soc: aspeed: Miscellaneous control interfaces
The ASPEED BMC SoCs have many knobs and switches that are sometimes
design-specific and often defy any approach to unify them under an
existing subsystem.
Add a driver to translate a devicetree table into sysfs entries to
expose bits and fields for manipulation from userspace. This encompasses
concepts from scratch registers to boolean conditions to enable or
disable host interface features.
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Signed-off-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
-rw-r--r-- | arch/arm/boot/dts/aspeed-g4.dtsi | 4 | ||||
-rw-r--r-- | arch/arm/boot/dts/aspeed-g5.dtsi | 4 | ||||
-rw-r--r-- | arch/arm/boot/dts/aspeed-g6.dtsi | 4 | ||||
-rw-r--r-- | drivers/soc/aspeed/Kconfig | 8 | ||||
-rw-r--r-- | drivers/soc/aspeed/Makefile | 1 | ||||
-rw-r--r-- | drivers/soc/aspeed/aspeed-bmc-misc.c | 190 |
6 files changed, 211 insertions, 0 deletions
diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi index 65546d37b8d6..8df25b046421 100644 --- a/arch/arm/boot/dts/aspeed-g4.dtsi +++ b/arch/arm/boot/dts/aspeed-g4.dtsi @@ -385,6 +385,10 @@ status = "disabled"; }; + sio_regs: regs { + compatible = "aspeed,bmc-misc"; + }; + lpc_sio: lpc-sio@180 { compatible = "aspeed,ast2400-lpc-sio"; reg = <0x180 0x20>; diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi index e4ff54287645..5a09707d2b7f 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi @@ -529,6 +529,10 @@ status = "disabled"; }; + sio_regs: regs { + compatible = "aspeed,bmc-misc"; + }; + lpc_sio: lpc-sio@180 { compatible = "aspeed,ast2500-lpc-sio"; reg = <0x180 0x20>; diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi index 59a50f9d4697..e76dfb73430e 100644 --- a/arch/arm/boot/dts/aspeed-g6.dtsi +++ b/arch/arm/boot/dts/aspeed-g6.dtsi @@ -659,6 +659,10 @@ status = "disabled"; }; + sio_regs: regs { + compatible = "aspeed,bmc-misc"; + }; + lpc_sio: lpc-sio@180 { compatible = "aspeed,ast2500-lpc-sio"; reg = <0x180 0x20>; diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig index c70ba12984bb..d36ee43451a5 100644 --- a/drivers/soc/aspeed/Kconfig +++ b/drivers/soc/aspeed/Kconfig @@ -4,6 +4,14 @@ if ARCH_ASPEED || COMPILE_TEST menu "ASPEED SoC drivers" +config ASPEED_BMC_MISC + bool "Miscellaneous ASPEED BMC interfaces" + depends on ARCH_ASPEED || COMPILE_TEST + default ARCH_ASPEED + help + Say yes to expose VGA and LPC scratch registers, and other + miscellaneous control interfaces specific to the ASPEED BMC SoCs + config ASPEED_ESPI_SLAVE depends on ARCH_ASPEED || COMPILE_TEST depends on REGMAP_MMIO diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile index 46ba5bc9a182..f3afc32b58b9 100644 --- a/drivers/soc/aspeed/Makefile +++ b/drivers/soc/aspeed/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_ASPEED_BMC_MISC) += aspeed-bmc-misc.o obj-$(CONFIG_ASPEED_ESPI_SLAVE) += aspeed-espi-slave.o obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o diff --git a/drivers/soc/aspeed/aspeed-bmc-misc.c b/drivers/soc/aspeed/aspeed-bmc-misc.c new file mode 100644 index 000000000000..314007bad74f --- /dev/null +++ b/drivers/soc/aspeed/aspeed-bmc-misc.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2018 IBM Corp. + +#include <linux/kobject.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> + +#define DEVICE_NAME "aspeed-bmc-misc" + +struct aspeed_bmc_ctrl { + const char *name; + u32 offset; + u32 mask; + u32 shift; + struct regmap *map; + struct kobj_attribute attr; +}; + +struct aspeed_bmc_misc { + struct device *dev; + struct regmap *map; + struct aspeed_bmc_ctrl *ctrls; + int nr_ctrls; +}; + +static int aspeed_bmc_misc_parse_dt_child(struct device_node *child, + struct aspeed_bmc_ctrl *ctrl) +{ + int rc; + + /* Example child: + * + * ilpc2ahb { + * offset = <0x80>; + * bit-mask = <0x1>; + * bit-shift = <6>; + * label = "foo"; + * } + */ + if (of_property_read_string(child, "label", &ctrl->name)) + ctrl->name = child->name; + + rc = of_property_read_u32(child, "offset", &ctrl->offset); + if (rc < 0) + return rc; + + rc = of_property_read_u32(child, "bit-mask", &ctrl->mask); + if (rc < 0) + return rc; + + rc = of_property_read_u32(child, "bit-shift", &ctrl->shift); + if (rc < 0) + return rc; + + ctrl->mask <<= ctrl->shift; + + return 0; +} + +static int aspeed_bmc_misc_parse_dt(struct aspeed_bmc_misc *bmc, + struct device_node *parent) +{ + struct aspeed_bmc_ctrl *ctrl; + struct device_node *child; + int rc; + + bmc->nr_ctrls = of_get_child_count(parent); + bmc->ctrls = devm_kcalloc(bmc->dev, bmc->nr_ctrls, sizeof(*bmc->ctrls), + GFP_KERNEL); + if (!bmc->ctrls) + return -ENOMEM; + + ctrl = bmc->ctrls; + for_each_child_of_node(parent, child) { + rc = aspeed_bmc_misc_parse_dt_child(child, ctrl++); + if (rc < 0) { + of_node_put(child); + return rc; + } + } + + return 0; +} + +static ssize_t aspeed_bmc_misc_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct aspeed_bmc_ctrl *ctrl; + unsigned int val; + int rc; + + ctrl = container_of(attr, struct aspeed_bmc_ctrl, attr); + rc = regmap_read(ctrl->map, ctrl->offset, &val); + if (rc) + return rc; + + val &= ctrl->mask; + val >>= ctrl->shift; + + return sprintf(buf, "%u\n", val); +} + +static ssize_t aspeed_bmc_misc_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct aspeed_bmc_ctrl *ctrl; + long val; + int rc; + + rc = kstrtol(buf, 0, &val); + if (rc) + return rc; + + ctrl = container_of(attr, struct aspeed_bmc_ctrl, attr); + val <<= ctrl->shift; + rc = regmap_update_bits(ctrl->map, ctrl->offset, ctrl->mask, val); + + return rc < 0 ? rc : count; +} + +static int aspeed_bmc_misc_add_sysfs_attr(struct aspeed_bmc_misc *bmc, + struct aspeed_bmc_ctrl *ctrl) +{ + ctrl->map = bmc->map; + + sysfs_attr_init(&ctrl->attr.attr); + ctrl->attr.attr.name = ctrl->name; + ctrl->attr.attr.mode = 0664; + ctrl->attr.show = aspeed_bmc_misc_show; + ctrl->attr.store = aspeed_bmc_misc_store; + + return sysfs_create_file(&bmc->dev->kobj, &ctrl->attr.attr); +} + +static int aspeed_bmc_misc_populate_sysfs(struct aspeed_bmc_misc *bmc) +{ + int rc; + int i; + + for (i = 0; i < bmc->nr_ctrls; i++) { + rc = aspeed_bmc_misc_add_sysfs_attr(bmc, &bmc->ctrls[i]); + if (rc < 0) + return rc; + } + + return 0; +} + +static int aspeed_bmc_misc_probe(struct platform_device *pdev) +{ + struct aspeed_bmc_misc *bmc; + int rc; + + bmc = devm_kzalloc(&pdev->dev, sizeof(*bmc), GFP_KERNEL); + if (!bmc) + return -ENOMEM; + + bmc->dev = &pdev->dev; + bmc->map = syscon_node_to_regmap(pdev->dev.parent->of_node); + if (IS_ERR(bmc->map)) + return PTR_ERR(bmc->map); + + rc = aspeed_bmc_misc_parse_dt(bmc, pdev->dev.of_node); + if (rc < 0) + return rc; + + return aspeed_bmc_misc_populate_sysfs(bmc); +} + +static const struct of_device_id aspeed_bmc_misc_match[] = { + { .compatible = "aspeed,bmc-misc" }, + { }, +}; + +static struct platform_driver aspeed_bmc_misc = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = aspeed_bmc_misc_match, + }, + .probe = aspeed_bmc_misc_probe, +}; + +module_platform_driver(aspeed_bmc_misc); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); |