Simple MFD base patches and cleanups:
- 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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVUFhiAAoJEEEQszewGV1z0p0P+wf7GkyEqDRY5345gs7dsqDr ELX48ikqRod8I+C596sbBuJbCM7kQWGPiN3vKToFgqSusOHwKOMyOhvSCzgWSxfS cTLi86JCNsKjJpIk/AlthBWnvupQMyGdlV2tm2RLC2xrO6t3DKndQ39+yOqd3tMn OKRpETiejiYSbyrHRX03LNAltRzWJDI7wsmJJs7Nqowb0vw/talixkzKG4PXOb6U 1/cVZgfvpf0z7z1TncfLLsXwjSDyRfchCDCSke6wqH7yXTPSzkCUVttEufANqo+o PL5VDClaplppj1eo3T2MdeBSD5JONF18TECKVM1LE46glSjAfNnKko+fDSMQXNVx d09XH5GoDdpKKZlwXP78H2QqLREAjyx/+K7Os/9D1i4kv+oGLigFD6zB3hHV9+lG TNDWDnW8qUTGYfRj3K3FgWgF8sU8esxNUTMg6buOOXhD3lcpeXcIbLd9vALzCVZy EARcIifTyD/JFIMs+lJAGD3ZHpzUDRIx9hI165CKxxpdBKEqRW4s/nsDopFszQgp AIhcbORKNvR/RGdxz0GozN+51r7XWexmoCfLJpntBXp/zbZxKXlrhKNL+1ktCE01 vs++Xci9Qy6ngjTyLikLeiLmDR6V43xAV+8tTR2ifvshfBGpY8y1e7CwNH4yML0c XLV50CAumHx3HIcbXHM9 =1q0y -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
a746568e51
7 changed files with 207 additions and 91 deletions
41
Documentation/devicetree/bindings/mfd/mfd.txt
Normal file
41
Documentation/devicetree/bindings/mfd/mfd.txt
Normal file
|
@ -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";
|
||||||
|
};
|
||||||
|
};
|
|
@ -114,7 +114,7 @@
|
||||||
ranges;
|
ranges;
|
||||||
|
|
||||||
syscon: syscon@10000000 {
|
syscon: syscon@10000000 {
|
||||||
compatible = "arm,realview-pb1176-syscon", "syscon";
|
compatible = "arm,realview-pb1176-syscon", "syscon", "simple-mfd";
|
||||||
reg = <0x10000000 0x1000>;
|
reg = <0x10000000 0x1000>;
|
||||||
|
|
||||||
led@08.0 {
|
led@08.0 {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
/ {
|
/ {
|
||||||
core-module@10000000 {
|
core-module@10000000 {
|
||||||
compatible = "arm,core-module-integrator", "syscon";
|
compatible = "arm,core-module-integrator", "syscon", "simple-mfd";
|
||||||
reg = <0x10000000 0x200>;
|
reg = <0x10000000 0x200>;
|
||||||
|
|
||||||
/* Use core module LED to indicate CPU load */
|
/* Use core module LED to indicate CPU load */
|
||||||
|
@ -95,7 +95,7 @@
|
||||||
|
|
||||||
syscon {
|
syscon {
|
||||||
/* Debug registers mapped as syscon */
|
/* Debug registers mapped as syscon */
|
||||||
compatible = "syscon";
|
compatible = "syscon", "simple-mfd";
|
||||||
reg = <0x1a000000 0x10>;
|
reg = <0x1a000000 0x10>;
|
||||||
|
|
||||||
led@04.0 {
|
led@04.0 {
|
||||||
|
|
|
@ -66,6 +66,74 @@
|
||||||
#size-cells = <1>;
|
#size-cells = <1>;
|
||||||
ranges = <0 3 0 0x200000>;
|
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 {
|
mmci@050000 {
|
||||||
compatible = "arm,pl180", "arm,primecell";
|
compatible = "arm,pl180", "arm,primecell";
|
||||||
reg = <0x050000 0x1000>;
|
reg = <0x050000 0x1000>;
|
||||||
|
|
|
@ -138,6 +138,12 @@ CONFIG_MMC_ARMMMCI=y
|
||||||
CONFIG_MMC_SDHCI=y
|
CONFIG_MMC_SDHCI=y
|
||||||
CONFIG_MMC_SDHCI_PLTFM=y
|
CONFIG_MMC_SDHCI_PLTFM=y
|
||||||
CONFIG_MMC_SPI=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_CLASS=y
|
||||||
CONFIG_RTC_DRV_EFI=y
|
CONFIG_RTC_DRV_EFI=y
|
||||||
CONFIG_RTC_DRV_XGENE=y
|
CONFIG_RTC_DRV_XGENE=y
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
* MA 02111-1307 USA
|
* MA 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/platform_device.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");
|
dev_err(sled->cdev.dev, "error updating LED status\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init syscon_leds_spawn(struct device_node *np,
|
static int syscon_led_probe(struct platform_device *pdev)
|
||||||
struct device *dev,
|
|
||||||
struct regmap *map)
|
|
||||||
{
|
{
|
||||||
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;
|
int ret;
|
||||||
|
|
||||||
for_each_available_child_of_node(np, child) {
|
parent = dev->parent;
|
||||||
struct syscon_led *sled;
|
if (!parent) {
|
||||||
const char *state;
|
dev_err(dev, "no parent for syscon LED\n");
|
||||||
|
return -ENODEV;
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sled->cdev.brightness_set = syscon_led_set;
|
|
||||||
|
|
||||||
ret = led_classdev_register(dev, &sled->cdev);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
dev_info(dev, "registered LED %s\n", sled->cdev.name);
|
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init syscon_leds_init(void)
|
static int syscon_led_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device_node *np;
|
struct syscon_led *sled = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
for_each_of_allnodes(np) {
|
|
||||||
struct platform_device *pdev;
|
|
||||||
struct regmap *map;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!of_device_is_compatible(np, "syscon"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
map = syscon_node_to_regmap(np);
|
|
||||||
if (IS_ERR(map)) {
|
|
||||||
pr_err("error getting regmap for syscon LEDs\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
led_classdev_unregister(&sled->cdev);
|
||||||
|
/* Turn it off */
|
||||||
|
regmap_update_bits(sled->map, sled->offset, sled->mask, 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
device_initcall(syscon_leds_init);
|
|
||||||
|
static const struct of_device_id of_syscon_leds_match[] = {
|
||||||
|
{ .compatible = "register-bit-led", },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE_DEVICE_TABLE(of, of_syscon_leds_match);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
const struct of_device_id of_default_bus_match_table[] = {
|
const struct of_device_id of_default_bus_match_table[] = {
|
||||||
{ .compatible = "simple-bus", },
|
{ .compatible = "simple-bus", },
|
||||||
|
{ .compatible = "simple-mfd", },
|
||||||
#ifdef CONFIG_ARM_AMBA
|
#ifdef CONFIG_ARM_AMBA
|
||||||
{ .compatible = "arm,amba-bus", },
|
{ .compatible = "arm,amba-bus", },
|
||||||
#endif /* CONFIG_ARM_AMBA */
|
#endif /* CONFIG_ARM_AMBA */
|
||||||
|
|
Loading…
Add table
Reference in a new issue