From 22869a9eca4ea5b534538d160b68c7aef44e378a Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 3 Mar 2015 09:52:20 +0100 Subject: MFD/OF: document MFD devices and handle simple-mfd This defines a new compatible option for MFD devices "simple-mfd" that will make the OF core spawn child devices for all subnodes of that MFD device. It is optional but handy for things like syscon and possibly other simpler MFD devices. Since there was no file to put the documentation in, I took this opportunity to make a small writeup on MFD devices and add the compatible definition there. Suggested-by: Lee Jones Acked-by: Lee Jones Acked-by: Antoine Tenart Acked-by: Alexandre Belloni Cc: Arnd Bergmann Cc: Devicetree Cc: Rob Herring Cc: Benjamin Herrenschmidt Cc: Grant Likely Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/mfd/mfd.txt | 41 +++++++++++++++++++++++++++ drivers/of/platform.c | 1 + 2 files changed, 42 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/mfd.txt diff --git a/Documentation/devicetree/bindings/mfd/mfd.txt b/Documentation/devicetree/bindings/mfd/mfd.txt new file mode 100644 index 0000000..af9d693 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/mfd.txt @@ -0,0 +1,41 @@ +Multi-Function Devices (MFD) + +These devices comprise a nexus for heterogeneous hardware blocks containing +more than one non-unique yet varying hardware functionality. + +A typical MFD can be: + +- A mixed signal ASIC on an external bus, sometimes a PMIC (Power Management + Integrated Circuit) that is manufactured in a lower technology node (rough + silicon) that handles analog drivers for things like audio amplifiers, LED + drivers, level shifters, PHY (physical interfaces to things like USB or + ethernet), regulators etc. + +- A range of memory registers containing "miscellaneous system registers" also + known as a system controller "syscon" or any other memory range containing a + mix of unrelated hardware devices. + +Optional properties: + +- compatible : "simple-mfd" - this signifies that the operating system should + consider all subnodes of the MFD device as separate devices akin to how + "simple-bus" inidicates when to see subnodes as children for a simple + memory-mapped bus. For more complex devices, when the nexus driver has to + probe registers to figure out what child devices exist etc, this should not + be used. In the latter case the child devices will be determined by the + operating system. + +Example: + +foo@1000 { + compatible = "syscon", "simple-mfd"; + reg = <0x01000 0x1000>; + + led@08.0 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x01>; + label = "myled"; + default-state = "on"; + }; +}; diff --git a/drivers/of/platform.c b/drivers/of/platform.c index a01f57c..ddf8e42 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -25,6 +25,7 @@ const struct of_device_id of_default_bus_match_table[] = { { .compatible = "simple-bus", }, + { .compatible = "simple-mfd", }, #ifdef CONFIG_ARM_AMBA { .compatible = "arm,amba-bus", }, #endif /* CONFIG_ARM_AMBA */ -- cgit v1.1 From 480aa74c386121644336d9b59d0b61cde1c3c29c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 3 Mar 2015 10:06:16 +0100 Subject: ARM: dts: update syscons to use simple-mfd The Integrators and the RealView use simple MFD devices with register bit LEDs as subnodes, update these to use the "simple-mfd" compatible property so that subdevices get spawned from the MFD nexi. Cc: Arnd Bergmann Cc: Lee Jones Cc: Pawel Moll Cc: Mark Rutland Signed-off-by: Linus Walleij --- arch/arm/boot/dts/arm-realview-pb1176.dts | 2 +- arch/arm/boot/dts/integrator.dtsi | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/arm-realview-pb1176.dts b/arch/arm/boot/dts/arm-realview-pb1176.dts index ff26c7e..1bc64cd 100644 --- a/arch/arm/boot/dts/arm-realview-pb1176.dts +++ b/arch/arm/boot/dts/arm-realview-pb1176.dts @@ -114,7 +114,7 @@ ranges; syscon: syscon@10000000 { - compatible = "arm,realview-pb1176-syscon", "syscon"; + compatible = "arm,realview-pb1176-syscon", "syscon", "simple-mfd"; reg = <0x10000000 0x1000>; led@08.0 { diff --git a/arch/arm/boot/dts/integrator.dtsi b/arch/arm/boot/dts/integrator.dtsi index 28e38f8..3807d4f 100644 --- a/arch/arm/boot/dts/integrator.dtsi +++ b/arch/arm/boot/dts/integrator.dtsi @@ -6,7 +6,7 @@ / { core-module@10000000 { - compatible = "arm,core-module-integrator", "syscon"; + compatible = "arm,core-module-integrator", "syscon", "simple-mfd"; reg = <0x10000000 0x200>; /* Use core module LED to indicate CPU load */ @@ -95,7 +95,7 @@ syscon { /* Debug registers mapped as syscon */ - compatible = "syscon"; + compatible = "syscon", "simple-mfd"; reg = <0x1a000000 0x10>; led@04.0 { -- cgit v1.1 From a917d4b44afc968d35e9b2717fea52946f512df1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 3 Mar 2015 10:08:19 +0100 Subject: leds: syscon: instantiate from platform device Currently syscon LEDs will traverse the device tree looking for syscon devices and if found, traverse any subnodes of these to identify matching children and from there instantiate LED class devices. This is not a good use of the Linux device model. Instead we have converted the device trees to add the "simple-mfd" property to the MFD nexi spawning syscon LEDs so that these will appear as platform devices in the system and we can use the proper device probing mechanism. Cc: Arnd Bergmann Cc: Lee Jones Cc: Pawel Moll Cc: Mark Rutland Reviewed-by: Bjorn Andersson Signed-off-by: Linus Walleij --- drivers/leds/leds-syscon.c | 170 ++++++++++++++++++++++----------------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/drivers/leds/leds-syscon.c b/drivers/leds/leds-syscon.c index 6896e2d..d1660b0 100644 --- a/drivers/leds/leds-syscon.c +++ b/drivers/leds/leds-syscon.c @@ -20,6 +20,7 @@ * MA 02111-1307 USA */ #include +#include #include #include #include @@ -66,102 +67,101 @@ static void syscon_led_set(struct led_classdev *led_cdev, dev_err(sled->cdev.dev, "error updating LED status\n"); } -static int __init syscon_leds_spawn(struct device_node *np, - struct device *dev, - struct regmap *map) +static int syscon_led_probe(struct platform_device *pdev) { - struct device_node *child; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device *parent; + struct regmap *map; + struct syscon_led *sled; + const char *state; int ret; - for_each_available_child_of_node(np, child) { - struct syscon_led *sled; - const char *state; - - /* Only check for register-bit-leds */ - if (of_property_match_string(child, "compatible", - "register-bit-led") < 0) - continue; - - sled = devm_kzalloc(dev, sizeof(*sled), GFP_KERNEL); - if (!sled) - return -ENOMEM; - - sled->map = map; - - if (of_property_read_u32(child, "offset", &sled->offset)) - return -EINVAL; - if (of_property_read_u32(child, "mask", &sled->mask)) - return -EINVAL; - sled->cdev.name = - of_get_property(child, "label", NULL) ? : child->name; - sled->cdev.default_trigger = - of_get_property(child, "linux,default-trigger", NULL); - - state = of_get_property(child, "default-state", NULL); - if (state) { - if (!strcmp(state, "keep")) { - u32 val; - - ret = regmap_read(map, sled->offset, &val); - if (ret < 0) - return ret; - sled->state = !!(val & sled->mask); - } else if (!strcmp(state, "on")) { - sled->state = true; - ret = regmap_update_bits(map, sled->offset, - sled->mask, - sled->mask); - if (ret < 0) - return ret; - } else { - sled->state = false; - ret = regmap_update_bits(map, sled->offset, - sled->mask, 0); - if (ret < 0) - return ret; - } + parent = dev->parent; + if (!parent) { + dev_err(dev, "no parent for syscon LED\n"); + return -ENODEV; + } + map = syscon_node_to_regmap(parent->of_node); + if (!map) { + dev_err(dev, "no regmap for syscon LED parent\n"); + return -ENODEV; + } + + sled = devm_kzalloc(dev, sizeof(*sled), GFP_KERNEL); + if (!sled) + return -ENOMEM; + + sled->map = map; + + if (of_property_read_u32(np, "offset", &sled->offset)) + return -EINVAL; + if (of_property_read_u32(np, "mask", &sled->mask)) + return -EINVAL; + sled->cdev.name = + of_get_property(np, "label", NULL) ? : np->name; + sled->cdev.default_trigger = + of_get_property(np, "linux,default-trigger", NULL); + + state = of_get_property(np, "default-state", NULL); + if (state) { + if (!strcmp(state, "keep")) { + u32 val; + + ret = regmap_read(map, sled->offset, &val); + if (ret < 0) + return ret; + sled->state = !!(val & sled->mask); + } else if (!strcmp(state, "on")) { + sled->state = true; + ret = regmap_update_bits(map, sled->offset, + sled->mask, + sled->mask); + if (ret < 0) + return ret; + } else { + sled->state = false; + ret = regmap_update_bits(map, sled->offset, + sled->mask, 0); + if (ret < 0) + return ret; } - sled->cdev.brightness_set = syscon_led_set; + } + sled->cdev.brightness_set = syscon_led_set; - ret = led_classdev_register(dev, &sled->cdev); - if (ret < 0) - return ret; + ret = led_classdev_register(dev, &sled->cdev); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, sled); + dev_info(dev, "registered LED %s\n", sled->cdev.name); - dev_info(dev, "registered LED %s\n", sled->cdev.name); - } return 0; } -static int __init syscon_leds_init(void) +static int syscon_led_remove(struct platform_device *pdev) { - struct device_node *np; - - for_each_of_allnodes(np) { - struct platform_device *pdev; - struct regmap *map; - int ret; + struct syscon_led *sled = platform_get_drvdata(pdev); - if (!of_device_is_compatible(np, "syscon")) - continue; + led_classdev_unregister(&sled->cdev); + /* Turn it off */ + regmap_update_bits(sled->map, sled->offset, sled->mask, 0); + return 0; +} - map = syscon_node_to_regmap(np); - if (IS_ERR(map)) { - pr_err("error getting regmap for syscon LEDs\n"); - continue; - } +static const struct of_device_id of_syscon_leds_match[] = { + { .compatible = "register-bit-led", }, + {}, +}; - /* - * If the map is there, the device should be there, we allocate - * memory on the syscon device's behalf here. - */ - pdev = of_find_device_by_node(np); - if (!pdev) - return -ENODEV; - ret = syscon_leds_spawn(np, &pdev->dev, map); - if (ret) - dev_err(&pdev->dev, "could not spawn syscon LEDs\n"); - } +MODULE_DEVICE_TABLE(of, of_syscon_leds_match); - return 0; -} -device_initcall(syscon_leds_init); +static struct platform_driver syscon_led_driver = { + .probe = syscon_led_probe, + .remove = syscon_led_remove, + .driver = { + .name = "leds-syscon", + .of_match_table = of_syscon_leds_match, + }, +}; +module_platform_driver(syscon_led_driver); -- cgit v1.1 From bfb476290f2f0b9e6df4e9717738875754c7d242 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 25 Feb 2015 13:06:50 +0100 Subject: arm64: juno: Add APB registers and LEDs using syscon This defines the Juno "APB system registers" as a syscon device, and all the LEDs controlled by the APB system registers right below it using the syscon LEDs driver on top of syscon. Define LED0 for heartbeat, LED1 for MMC0 activity and the following four LEDs indicating CPU activity using the Linux-specific DT bindings for triggers. This is the pattern and same drivers as used on the legacy platform device trees for the ARM Integrators and the RealView PB1176. Cc: Arnd Bergmann Cc: Mark Rutland Cc: Robin Murphy Cc: Pawel Moll Tested-by: Liviu Dudau Signed-off-by: Linus Walleij --- arch/arm64/boot/dts/arm/juno-motherboard.dtsi | 68 +++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/arch/arm64/boot/dts/arm/juno-motherboard.dtsi b/arch/arm64/boot/dts/arm/juno-motherboard.dtsi index c138b95..5ce111d 100644 --- a/arch/arm64/boot/dts/arm/juno-motherboard.dtsi +++ b/arch/arm64/boot/dts/arm/juno-motherboard.dtsi @@ -66,6 +66,74 @@ #size-cells = <1>; ranges = <0 3 0 0x200000>; + apbregs@010000 { + compatible = "syscon", "simple-mfd"; + reg = <0x010000 0x1000>; + + led@08.0 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x01>; + label = "vexpress:0"; + linux,default-trigger = "heartbeat"; + default-state = "on"; + }; + led@08.1 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x02>; + label = "vexpress:1"; + linux,default-trigger = "mmc0"; + default-state = "off"; + }; + led@08.2 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x04>; + label = "vexpress:2"; + linux,default-trigger = "cpu0"; + default-state = "off"; + }; + led@08.3 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x08>; + label = "vexpress:3"; + linux,default-trigger = "cpu1"; + default-state = "off"; + }; + led@08.4 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x10>; + label = "vexpress:4"; + linux,default-trigger = "cpu2"; + default-state = "off"; + }; + led@08.5 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x20>; + label = "vexpress:5"; + linux,default-trigger = "cpu3"; + default-state = "off"; + }; + led@08.6 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x40>; + label = "vexpress:6"; + default-state = "off"; + }; + led@08.7 { + compatible = "register-bit-led"; + offset = <0x08>; + mask = <0x80>; + label = "vexpress:7"; + default-state = "off"; + }; + }; + mmci@050000 { compatible = "arm,pl180", "arm,primecell"; reg = <0x050000 0x1000>; -- cgit v1.1 From 48f1a9a4e268dec7c7f3e79339cfb85699c48eb0 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 25 Feb 2015 13:11:55 +0100 Subject: arm64: add LEDs and some trigger support to defconfig Since many boards for ARM, experimental or server alike, will feature LEDs, let's include LED class and trigger support for heartbeat and CPU in the defconfig. Many systems (such as the ARM Juno) will use a system controller "syscon" to access the LED registers, so include support for this as well. Cc: Arnd Bergmann Cc: Liviu Dudau Cc: Mark Rutland Cc: Robin Murphy Signed-off-by: Linus Walleij --- arch/arm64/configs/defconfig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 2ed7449..866640b 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -138,6 +138,12 @@ CONFIG_MMC_ARMMMCI=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SPI=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_SYSCON=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_CPU=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_EFI=y CONFIG_RTC_DRV_XGENE=y -- cgit v1.1