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
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;
|
||||
|
||||
syscon: syscon@10000000 {
|
||||
compatible = "arm,realview-pb1176-syscon", "syscon";
|
||||
compatible = "arm,realview-pb1176-syscon", "syscon", "simple-mfd";
|
||||
reg = <0x10000000 0x1000>;
|
||||
|
||||
led@08.0 {
|
||||
|
@ -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 {
|
||||
|
@ -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>;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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");
|
||||
}
|
||||
struct syscon_led *sled = platform_get_drvdata(pdev);
|
||||
|
||||
led_classdev_unregister(&sled->cdev);
|
||||
/* Turn it off */
|
||||
regmap_update_bits(sled->map, sled->offset, sled->mask, 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[] = {
|
||||
{ .compatible = "simple-bus", },
|
||||
{ .compatible = "simple-mfd", },
|
||||
#ifdef CONFIG_ARM_AMBA
|
||||
{ .compatible = "arm,amba-bus", },
|
||||
#endif /* CONFIG_ARM_AMBA */
|
||||
|
Loading…
Reference in New Issue
Block a user