diff options
author | Arnd Bergmann <arnd@arndb.de> | 2015-05-12 16:35:29 +0200 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2015-05-12 16:35:29 +0200 |
commit | a746568e5173214f0fa1b532c6c2f12482bc637f (patch) | |
tree | 80706d6cbe0342b05cb38e2a8eb43fc00a5ccfca | |
parent | 030bbdbf4c833bc69f502eae58498bc5572db736 (diff) | |
parent | 48f1a9a4e268dec7c7f3e79339cfb85699c48eb0 (diff) |
Merge tag 'simple-mfd' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-integrator into next/drivers
Merge "Simple MFD base patches and cleanups" from Linus Walleij:
- Document MFD DT bindings
- Instantiate subdevices for simple MFDs
- Switch Integrator and RealView to use the simple MFD
- Augment LED driver to probe from platform device
- Add LEDs to Juno Vexpress64
* tag 'simple-mfd' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-integrator:
arm64: add LEDs and some trigger support to defconfig
arm64: juno: Add APB registers and LEDs using syscon
leds: syscon: instantiate from platform device
ARM: dts: update syscons to use simple-mfd
MFD/OF: document MFD devices and handle simple-mfd
-rw-r--r-- | Documentation/devicetree/bindings/mfd/mfd.txt | 41 | ||||
-rw-r--r-- | arch/arm/boot/dts/arm-realview-pb1176.dts | 2 | ||||
-rw-r--r-- | arch/arm/boot/dts/integrator.dtsi | 4 | ||||
-rw-r--r-- | arch/arm64/boot/dts/arm/juno-motherboard.dtsi | 68 | ||||
-rw-r--r-- | arch/arm64/configs/defconfig | 6 | ||||
-rw-r--r-- | drivers/leds/leds-syscon.c | 170 | ||||
-rw-r--r-- | drivers/of/platform.c | 1 |
7 files changed, 204 insertions, 88 deletions
diff --git a/Documentation/devicetree/bindings/mfd/mfd.txt b/Documentation/devicetree/bindings/mfd/mfd.txt new file mode 100644 index 000000000000..af9d6931a1a2 --- /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/arch/arm/boot/dts/arm-realview-pb1176.dts b/arch/arm/boot/dts/arm-realview-pb1176.dts index ff26c7ed8c41..1bc64cda819e 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 28e38f8c6b0f..3807d4f46ef7 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 { diff --git a/arch/arm64/boot/dts/arm/juno-motherboard.dtsi b/arch/arm64/boot/dts/arm/juno-motherboard.dtsi index c138b95a8356..5ce111dcf16d 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>; diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 2ed7449d9273..866640be32b5 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 diff --git a/drivers/leds/leds-syscon.c b/drivers/leds/leds-syscon.c index 6896e2d9ba58..d1660b039812 100644 --- a/drivers/leds/leds-syscon.c +++ b/drivers/leds/leds-syscon.c @@ -20,6 +20,7 @@ * MA 02111-1307 USA */ #include <linux/io.h> +#include <linux/module.h> #include <linux/of_device.h> #include <linux/of_address.h> #include <linux/platform_device.h> @@ -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); diff --git a/drivers/of/platform.c b/drivers/of/platform.c index a01f57c9e34e..ddf8e42c9367 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 */ |