All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH/RFC] mfd: as3711: add OF support
@ 2013-02-15 10:07 ` Guennadi Liakhovetski
  0 siblings, 0 replies; 16+ messages in thread
From: Guennadi Liakhovetski @ 2013-02-15 10:07 UTC (permalink / raw)
  To: linux-kernel
  Cc: Magnus Damm, Simon Horman, devicetree-discuss, Samuel Ortiz,
	Mark Brown, Liam Girdwood, Richard Purdie, Andrew Morton,
	linux-fbdev

Add device-tree bindings to the AS3711 regulator and backlight drivers.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---

As usual - comments to the new bindings are very welcome!

 Documentation/devicetree/bindings/mfd/as3711.txt |   73 +++++++++++++
 drivers/mfd/as3711.c                             |   30 +++++-
 drivers/regulator/as3711-regulator.c             |   69 ++++++++++++-
 drivers/video/backlight/as3711_bl.c              |  118 +++++++++++++++++++++-
 4 files changed, 282 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mfd/as3711.txt

diff --git a/Documentation/devicetree/bindings/mfd/as3711.txt b/Documentation/devicetree/bindings/mfd/as3711.txt
new file mode 100644
index 0000000..d98cf18
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/as3711.txt
@@ -0,0 +1,73 @@
+AS3711 is an I2C PMIC from Austria MicroSystems with multiple DCDC and LDO power
+supplies, a battery charger and an RTC. So far only bindings for the two stepup
+DCDC converters are defined. Other DCDC and LDO supplies are configured, using
+standard regulator properties, they must belong to a sub-node, called
+"regulators" and be called "sd1" to "sd4" and "ldo1" to "ldo8." Stepup converter
+configuration should be placed in a subnode, called "backlight."
+
+Compulsory properties:
+- compatible		: must be "ams,as3711"
+- reg			: specifies the I2C address
+
+To use the SU1 converter as a backlight source the following two properties must
+be provided:
+- su1-dev		: framebuffer phandle
+- su1-max-uA		: maximum current
+
+To use the SU2 converter as a backlight source the following two properties must
+be provided:
+- su2-dev		: framebuffer phandle
+- su1-max-uA		: maximum current
+
+Additionally one of these properties must be provided to select the type of
+feedback used:
+- su2-feedback-voltage	: voltage feedback is used
+- su2-feedback-curr1	: CURR1 input used for current feedback
+- su2-feedback-curr2	: CURR2 input used for current feedback
+- su2-feedback-curr3	: CURR3 input used for current feedback
+- su2-feedback-curr-auto: automatic current feedback selection
+
+and one of these to select the over-voltage protection pin
+- su2-fbprot-lx-sd4	: LX_SD4 is used for over-voltage protection
+- su2-fbprot-gpio2	: GPIO2 is used for over-voltage protection
+- su2-fbprot-gpio3	: GPIO3 is used for over-voltage protection
+- su2-fbprot-gpio4	: GPIO4 is used for over-voltage protection
+
+If "su2-feedback-curr-auto" is selected, one or more of the following properties
+have to be specified:
+- su2-auto-curr1	: use CURR1 input for current feedback
+- su2-auto-curr2	: use CURR2 input for current feedback
+- su2-auto-curr3	: use CURR3 input for current feedback
+
+Example:
+
+as3711@40 {
+	compatible = "ams,as3711";
+	reg = <0x40>;
+
+	regulators {
+		sd4 {
+			regulator-name = "1.215V";
+			regulator-min-microvolt = <1215000>;
+			regulator-max-microvolt = <1235000>;
+		};
+		ldo2 {
+			regulator-name = "2.8V CPU";
+			regulator-min-microvolt = <2800000>;
+			regulator-max-microvolt = <2800000>;
+			regulator-always-on;
+			regulator-boot-on;
+		};
+	};
+
+	backlight {
+		compatible = "ams,as3711-bl";
+		su2-dev = <&lcdc>;
+		su2-max-uA = <36000>;
+		su2-feedback-curr-auto;
+		su2-fbprot-gpio4;
+		su2-auto-curr1;
+		su2-auto-curr2;
+		su2-auto-curr3;
+	};
+};
diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c
index e994c96..5e0e8b3 100644
--- a/drivers/mfd/as3711.c
+++ b/drivers/mfd/as3711.c
@@ -112,16 +112,37 @@ static const struct regmap_config as3711_regmap_config = {
 	.cache_type = REGCACHE_RBTREE,
 };
 
+#ifdef CONFIG_OF
+static struct of_device_id as3711_of_match[] = {
+	{.compatible = "ams,as3711",},
+	{}
+};
+MODULE_DEVICE_TABLE(of, as3711_of_match);
+#endif
+
 static int as3711_i2c_probe(struct i2c_client *client,
 			    const struct i2c_device_id *id)
 {
 	struct as3711 *as3711;
-	struct as3711_platform_data *pdata = client->dev.platform_data;
+	struct as3711_platform_data *pdata;
 	unsigned int id1, id2;
 	int ret;
 
-	if (!pdata)
-		dev_dbg(&client->dev, "Platform data not found\n");
+	if (!client->dev.of_node) {
+		pdata = client->dev.platform_data;
+		if (!pdata)
+			dev_dbg(&client->dev, "Platform data not found\n");
+	} else {
+		if (!of_device_is_available(client->dev.of_node))
+			return -ENODEV;
+
+		pdata = devm_kzalloc(&client->dev,
+				     sizeof(*pdata), GFP_KERNEL);
+		if (!pdata) {
+			dev_err(&client->dev, "Failed to allocate pdata\n");
+			return -ENOMEM;
+		}
+	}
 
 	as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
 	if (!as3711) {
@@ -193,7 +214,8 @@ static struct i2c_driver as3711_i2c_driver = {
 	.driver = {
 		   .name = "as3711",
 		   .owner = THIS_MODULE,
-		   },
+		   .of_match_table = of_match_ptr(as3711_of_match),
+	},
 	.probe = as3711_i2c_probe,
 	.remove = as3711_i2c_remove,
 	.id_table = as3711_i2c_id,
diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c
index f0ba8c4..cf145fc 100644
--- a/drivers/regulator/as3711-regulator.c
+++ b/drivers/regulator/as3711-regulator.c
@@ -13,9 +13,11 @@
 #include <linux/init.h>
 #include <linux/mfd/as3711.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
 #include <linux/slab.h>
 
 struct as3711_regulator_info {
@@ -276,6 +278,57 @@ static struct as3711_regulator_info as3711_reg_info[] = {
 
 #define AS3711_REGULATOR_NUM ARRAY_SIZE(as3711_reg_info)
 
+static const char *as3711_regulator_of_names[AS3711_REGULATOR_NUM] = {
+	[AS3711_REGULATOR_SD_1] = "sd1",
+	[AS3711_REGULATOR_SD_2] = "sd2",
+	[AS3711_REGULATOR_SD_3] = "sd3",
+	[AS3711_REGULATOR_SD_4] = "sd4",
+	[AS3711_REGULATOR_LDO_1] = "ldo1",
+	[AS3711_REGULATOR_LDO_2] = "ldo2",
+	[AS3711_REGULATOR_LDO_3] = "ldo3",
+	[AS3711_REGULATOR_LDO_4] = "ldo4",
+	[AS3711_REGULATOR_LDO_5] = "ldo5",
+	[AS3711_REGULATOR_LDO_6] = "ldo6",
+	[AS3711_REGULATOR_LDO_7] = "ldo7",
+	[AS3711_REGULATOR_LDO_8] = "ldo8",
+};
+
+static int as3711_regulator_parse_dt(struct device *dev)
+{
+	struct as3711_regulator_pdata *pdata = dev_get_platdata(dev);
+	struct device_node *regulators =
+		of_find_node_by_name(dev->parent->of_node, "regulators");
+	struct of_regulator_match *matches, *match;
+	const int count = AS3711_REGULATOR_NUM;
+	int ret, i;
+
+	if (!regulators) {
+		dev_err(dev, "regulator node not found\n");
+		return -ENODEV;
+	}
+
+	matches = devm_kzalloc(dev, sizeof(*matches) * count, GFP_KERNEL);
+	if (!matches)
+		return -ENOMEM;
+
+	for (i = 0, match = matches; i < count; i++, match++) {
+		match->name = as3711_regulator_of_names[i];
+		match->driver_data = as3711_reg_info + i;
+	}
+
+	ret = of_regulator_match(dev->parent, regulators, matches, count);
+	if (ret < 0) {
+		dev_err(dev, "Error parsing regulator init data: %d\n", ret);
+		return ret;
+	}
+
+	for (i = 0, match = matches; i < count; i++, match++)
+		if (match->of_node)
+			pdata->init_data[i] = match->init_data;
+
+	return 0;
+}
+
 static int as3711_regulator_probe(struct platform_device *pdev)
 {
 	struct as3711_regulator_pdata *pdata = dev_get_platdata(&pdev->dev);
@@ -289,8 +342,18 @@ static int as3711_regulator_probe(struct platform_device *pdev)
 	int ret;
 	int id;
 
-	if (!pdata)
-		dev_dbg(&pdev->dev, "No platform data...\n");
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data...\n");
+		return -ENODEV;
+	}
+
+	if (pdev->dev.parent->of_node) {
+		ret = as3711_regulator_parse_dt(&pdev->dev);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
+			return ret;
+		}
+	}
 
 	regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM *
 			sizeof(struct as3711_regulator), GFP_KERNEL);
@@ -300,7 +363,7 @@ static int as3711_regulator_probe(struct platform_device *pdev)
 	}
 
 	for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) {
-		reg_data = pdata ? pdata->init_data[id] : NULL;
+		reg_data = pdata->init_data[id];
 
 		/* No need to register if there is no regulator data */
 		if (!reg_data)
diff --git a/drivers/video/backlight/as3711_bl.c b/drivers/video/backlight/as3711_bl.c
index c6bc65d..90c9208 100644
--- a/drivers/video/backlight/as3711_bl.c
+++ b/drivers/video/backlight/as3711_bl.c
@@ -257,6 +257,109 @@ static int as3711_bl_register(struct platform_device *pdev,
 	return 0;
 }
 
+static int as3711_backlight_parse_dt(struct device *dev)
+{
+	struct as3711_bl_pdata *pdata = dev_get_platdata(dev);
+	struct device_node *bl =
+		of_find_node_by_name(dev->parent->of_node, "backlight"), *fb;
+	int ret;
+
+	if (!bl) {
+		dev_dbg(dev, "backlight node not found\n");
+		return -ENODEV;
+	}
+
+	fb = of_parse_phandle(bl, "su1-dev", 0);
+	if (fb) {
+		pdata->su1_fb = fb->full_name;
+
+		ret = of_property_read_u32(bl, "su1-max-uA", &pdata->su1_max_uA);
+		if (pdata->su1_max_uA <= 0)
+			ret = -EINVAL;
+		if (ret < 0)
+			return ret;
+	}
+
+	fb = of_parse_phandle(bl, "su2-dev", 0);
+	if (fb) {
+		int count = 0;
+
+		pdata->su2_fb = fb->full_name;
+
+		ret = of_property_read_u32(bl, "su2-max-uA", &pdata->su2_max_uA);
+		if (pdata->su2_max_uA <= 0)
+			ret = -EINVAL;
+		if (ret < 0)
+			return ret;
+
+		if (of_find_property(bl, "su2-feedback-voltage", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_VOLTAGE;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr1", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR1;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr2", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR2;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr3", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR3;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr-auto", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR_AUTO;
+			count++;
+		}
+		if (count != 1)
+			return -EINVAL;
+
+		count = 0;
+		if (of_find_property(bl, "su2-fbprot-lx-sd4", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_LX_SD4;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio2", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO2;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio3", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO3;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio4", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO4;
+			count++;
+		}
+		if (count != 1)
+			return -EINVAL;
+
+		count = 0;
+		if (of_find_property(bl, "su2-auto-curr1", NULL)) {
+			pdata->su2_auto_curr1 = true;
+			count++;
+		}
+		if (of_find_property(bl, "su2-auto-curr2", NULL)) {
+			pdata->su2_auto_curr2 = true;
+			count++;
+		}
+		if (of_find_property(bl, "su2-auto-curr3", NULL)) {
+			pdata->su2_auto_curr3 = true;
+			count++;
+		}
+
+		/*
+		 * At least one su2-auto-curr* must be specified iff
+		 * AS3711_SU2_CURR_AUTO is used
+		 */
+		if (!count ^ pdata->su2_feedback != AS3711_SU2_CURR_AUTO)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int as3711_backlight_probe(struct platform_device *pdev)
 {
 	struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev);
@@ -266,11 +369,24 @@ static int as3711_backlight_probe(struct platform_device *pdev)
 	unsigned int max_brightness;
 	int ret;
 
-	if (!pdata || (!pdata->su1_fb && !pdata->su2_fb)) {
+	if (!pdata) {
 		dev_err(&pdev->dev, "No platform data, exiting...\n");
 		return -ENODEV;
 	}
 
+	if (pdev->dev.parent->of_node) {
+		ret = as3711_backlight_parse_dt(&pdev->dev);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
+			return ret;
+		}
+	}
+
+	if (!pdata->su1_fb && !pdata->su2_fb) {
+		dev_err(&pdev->dev, "No framebuffer specified\n");
+		return -EINVAL;
+	}
+
 	/*
 	 * Due to possible hardware damage I chose to block all modes,
 	 * unsupported on my hardware. Anyone, wishing to use any of those modes
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH/RFC] mfd: as3711: add OF support
@ 2013-02-15 10:07 ` Guennadi Liakhovetski
  0 siblings, 0 replies; 16+ messages in thread
From: Guennadi Liakhovetski @ 2013-02-15 10:07 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-fbdev-u79uwXL29TY76Z2rM5mHXA, Samuel Ortiz,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Mark Brown,
	Magnus Damm, Simon Horman, Richard Purdie, Andrew Morton,
	Liam Girdwood

Add device-tree bindings to the AS3711 regulator and backlight drivers.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski-Mmb7MZpHnFY@public.gmane.org>
---

As usual - comments to the new bindings are very welcome!

 Documentation/devicetree/bindings/mfd/as3711.txt |   73 +++++++++++++
 drivers/mfd/as3711.c                             |   30 +++++-
 drivers/regulator/as3711-regulator.c             |   69 ++++++++++++-
 drivers/video/backlight/as3711_bl.c              |  118 +++++++++++++++++++++-
 4 files changed, 282 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mfd/as3711.txt

diff --git a/Documentation/devicetree/bindings/mfd/as3711.txt b/Documentation/devicetree/bindings/mfd/as3711.txt
new file mode 100644
index 0000000..d98cf18
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/as3711.txt
@@ -0,0 +1,73 @@
+AS3711 is an I2C PMIC from Austria MicroSystems with multiple DCDC and LDO power
+supplies, a battery charger and an RTC. So far only bindings for the two stepup
+DCDC converters are defined. Other DCDC and LDO supplies are configured, using
+standard regulator properties, they must belong to a sub-node, called
+"regulators" and be called "sd1" to "sd4" and "ldo1" to "ldo8." Stepup converter
+configuration should be placed in a subnode, called "backlight."
+
+Compulsory properties:
+- compatible		: must be "ams,as3711"
+- reg			: specifies the I2C address
+
+To use the SU1 converter as a backlight source the following two properties must
+be provided:
+- su1-dev		: framebuffer phandle
+- su1-max-uA		: maximum current
+
+To use the SU2 converter as a backlight source the following two properties must
+be provided:
+- su2-dev		: framebuffer phandle
+- su1-max-uA		: maximum current
+
+Additionally one of these properties must be provided to select the type of
+feedback used:
+- su2-feedback-voltage	: voltage feedback is used
+- su2-feedback-curr1	: CURR1 input used for current feedback
+- su2-feedback-curr2	: CURR2 input used for current feedback
+- su2-feedback-curr3	: CURR3 input used for current feedback
+- su2-feedback-curr-auto: automatic current feedback selection
+
+and one of these to select the over-voltage protection pin
+- su2-fbprot-lx-sd4	: LX_SD4 is used for over-voltage protection
+- su2-fbprot-gpio2	: GPIO2 is used for over-voltage protection
+- su2-fbprot-gpio3	: GPIO3 is used for over-voltage protection
+- su2-fbprot-gpio4	: GPIO4 is used for over-voltage protection
+
+If "su2-feedback-curr-auto" is selected, one or more of the following properties
+have to be specified:
+- su2-auto-curr1	: use CURR1 input for current feedback
+- su2-auto-curr2	: use CURR2 input for current feedback
+- su2-auto-curr3	: use CURR3 input for current feedback
+
+Example:
+
+as3711@40 {
+	compatible = "ams,as3711";
+	reg = <0x40>;
+
+	regulators {
+		sd4 {
+			regulator-name = "1.215V";
+			regulator-min-microvolt = <1215000>;
+			regulator-max-microvolt = <1235000>;
+		};
+		ldo2 {
+			regulator-name = "2.8V CPU";
+			regulator-min-microvolt = <2800000>;
+			regulator-max-microvolt = <2800000>;
+			regulator-always-on;
+			regulator-boot-on;
+		};
+	};
+
+	backlight {
+		compatible = "ams,as3711-bl";
+		su2-dev = <&lcdc>;
+		su2-max-uA = <36000>;
+		su2-feedback-curr-auto;
+		su2-fbprot-gpio4;
+		su2-auto-curr1;
+		su2-auto-curr2;
+		su2-auto-curr3;
+	};
+};
diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c
index e994c96..5e0e8b3 100644
--- a/drivers/mfd/as3711.c
+++ b/drivers/mfd/as3711.c
@@ -112,16 +112,37 @@ static const struct regmap_config as3711_regmap_config = {
 	.cache_type = REGCACHE_RBTREE,
 };
 
+#ifdef CONFIG_OF
+static struct of_device_id as3711_of_match[] = {
+	{.compatible = "ams,as3711",},
+	{}
+};
+MODULE_DEVICE_TABLE(of, as3711_of_match);
+#endif
+
 static int as3711_i2c_probe(struct i2c_client *client,
 			    const struct i2c_device_id *id)
 {
 	struct as3711 *as3711;
-	struct as3711_platform_data *pdata = client->dev.platform_data;
+	struct as3711_platform_data *pdata;
 	unsigned int id1, id2;
 	int ret;
 
-	if (!pdata)
-		dev_dbg(&client->dev, "Platform data not found\n");
+	if (!client->dev.of_node) {
+		pdata = client->dev.platform_data;
+		if (!pdata)
+			dev_dbg(&client->dev, "Platform data not found\n");
+	} else {
+		if (!of_device_is_available(client->dev.of_node))
+			return -ENODEV;
+
+		pdata = devm_kzalloc(&client->dev,
+				     sizeof(*pdata), GFP_KERNEL);
+		if (!pdata) {
+			dev_err(&client->dev, "Failed to allocate pdata\n");
+			return -ENOMEM;
+		}
+	}
 
 	as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
 	if (!as3711) {
@@ -193,7 +214,8 @@ static struct i2c_driver as3711_i2c_driver = {
 	.driver = {
 		   .name = "as3711",
 		   .owner = THIS_MODULE,
-		   },
+		   .of_match_table = of_match_ptr(as3711_of_match),
+	},
 	.probe = as3711_i2c_probe,
 	.remove = as3711_i2c_remove,
 	.id_table = as3711_i2c_id,
diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c
index f0ba8c4..cf145fc 100644
--- a/drivers/regulator/as3711-regulator.c
+++ b/drivers/regulator/as3711-regulator.c
@@ -13,9 +13,11 @@
 #include <linux/init.h>
 #include <linux/mfd/as3711.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
 #include <linux/slab.h>
 
 struct as3711_regulator_info {
@@ -276,6 +278,57 @@ static struct as3711_regulator_info as3711_reg_info[] = {
 
 #define AS3711_REGULATOR_NUM ARRAY_SIZE(as3711_reg_info)
 
+static const char *as3711_regulator_of_names[AS3711_REGULATOR_NUM] = {
+	[AS3711_REGULATOR_SD_1] = "sd1",
+	[AS3711_REGULATOR_SD_2] = "sd2",
+	[AS3711_REGULATOR_SD_3] = "sd3",
+	[AS3711_REGULATOR_SD_4] = "sd4",
+	[AS3711_REGULATOR_LDO_1] = "ldo1",
+	[AS3711_REGULATOR_LDO_2] = "ldo2",
+	[AS3711_REGULATOR_LDO_3] = "ldo3",
+	[AS3711_REGULATOR_LDO_4] = "ldo4",
+	[AS3711_REGULATOR_LDO_5] = "ldo5",
+	[AS3711_REGULATOR_LDO_6] = "ldo6",
+	[AS3711_REGULATOR_LDO_7] = "ldo7",
+	[AS3711_REGULATOR_LDO_8] = "ldo8",
+};
+
+static int as3711_regulator_parse_dt(struct device *dev)
+{
+	struct as3711_regulator_pdata *pdata = dev_get_platdata(dev);
+	struct device_node *regulators =
+		of_find_node_by_name(dev->parent->of_node, "regulators");
+	struct of_regulator_match *matches, *match;
+	const int count = AS3711_REGULATOR_NUM;
+	int ret, i;
+
+	if (!regulators) {
+		dev_err(dev, "regulator node not found\n");
+		return -ENODEV;
+	}
+
+	matches = devm_kzalloc(dev, sizeof(*matches) * count, GFP_KERNEL);
+	if (!matches)
+		return -ENOMEM;
+
+	for (i = 0, match = matches; i < count; i++, match++) {
+		match->name = as3711_regulator_of_names[i];
+		match->driver_data = as3711_reg_info + i;
+	}
+
+	ret = of_regulator_match(dev->parent, regulators, matches, count);
+	if (ret < 0) {
+		dev_err(dev, "Error parsing regulator init data: %d\n", ret);
+		return ret;
+	}
+
+	for (i = 0, match = matches; i < count; i++, match++)
+		if (match->of_node)
+			pdata->init_data[i] = match->init_data;
+
+	return 0;
+}
+
 static int as3711_regulator_probe(struct platform_device *pdev)
 {
 	struct as3711_regulator_pdata *pdata = dev_get_platdata(&pdev->dev);
@@ -289,8 +342,18 @@ static int as3711_regulator_probe(struct platform_device *pdev)
 	int ret;
 	int id;
 
-	if (!pdata)
-		dev_dbg(&pdev->dev, "No platform data...\n");
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data...\n");
+		return -ENODEV;
+	}
+
+	if (pdev->dev.parent->of_node) {
+		ret = as3711_regulator_parse_dt(&pdev->dev);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
+			return ret;
+		}
+	}
 
 	regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM *
 			sizeof(struct as3711_regulator), GFP_KERNEL);
@@ -300,7 +363,7 @@ static int as3711_regulator_probe(struct platform_device *pdev)
 	}
 
 	for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) {
-		reg_data = pdata ? pdata->init_data[id] : NULL;
+		reg_data = pdata->init_data[id];
 
 		/* No need to register if there is no regulator data */
 		if (!reg_data)
diff --git a/drivers/video/backlight/as3711_bl.c b/drivers/video/backlight/as3711_bl.c
index c6bc65d..90c9208 100644
--- a/drivers/video/backlight/as3711_bl.c
+++ b/drivers/video/backlight/as3711_bl.c
@@ -257,6 +257,109 @@ static int as3711_bl_register(struct platform_device *pdev,
 	return 0;
 }
 
+static int as3711_backlight_parse_dt(struct device *dev)
+{
+	struct as3711_bl_pdata *pdata = dev_get_platdata(dev);
+	struct device_node *bl =
+		of_find_node_by_name(dev->parent->of_node, "backlight"), *fb;
+	int ret;
+
+	if (!bl) {
+		dev_dbg(dev, "backlight node not found\n");
+		return -ENODEV;
+	}
+
+	fb = of_parse_phandle(bl, "su1-dev", 0);
+	if (fb) {
+		pdata->su1_fb = fb->full_name;
+
+		ret = of_property_read_u32(bl, "su1-max-uA", &pdata->su1_max_uA);
+		if (pdata->su1_max_uA <= 0)
+			ret = -EINVAL;
+		if (ret < 0)
+			return ret;
+	}
+
+	fb = of_parse_phandle(bl, "su2-dev", 0);
+	if (fb) {
+		int count = 0;
+
+		pdata->su2_fb = fb->full_name;
+
+		ret = of_property_read_u32(bl, "su2-max-uA", &pdata->su2_max_uA);
+		if (pdata->su2_max_uA <= 0)
+			ret = -EINVAL;
+		if (ret < 0)
+			return ret;
+
+		if (of_find_property(bl, "su2-feedback-voltage", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_VOLTAGE;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr1", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR1;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr2", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR2;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr3", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR3;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr-auto", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR_AUTO;
+			count++;
+		}
+		if (count != 1)
+			return -EINVAL;
+
+		count = 0;
+		if (of_find_property(bl, "su2-fbprot-lx-sd4", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_LX_SD4;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio2", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO2;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio3", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO3;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio4", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO4;
+			count++;
+		}
+		if (count != 1)
+			return -EINVAL;
+
+		count = 0;
+		if (of_find_property(bl, "su2-auto-curr1", NULL)) {
+			pdata->su2_auto_curr1 = true;
+			count++;
+		}
+		if (of_find_property(bl, "su2-auto-curr2", NULL)) {
+			pdata->su2_auto_curr2 = true;
+			count++;
+		}
+		if (of_find_property(bl, "su2-auto-curr3", NULL)) {
+			pdata->su2_auto_curr3 = true;
+			count++;
+		}
+
+		/*
+		 * At least one su2-auto-curr* must be specified iff
+		 * AS3711_SU2_CURR_AUTO is used
+		 */
+		if (!count ^ pdata->su2_feedback != AS3711_SU2_CURR_AUTO)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int as3711_backlight_probe(struct platform_device *pdev)
 {
 	struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev);
@@ -266,11 +369,24 @@ static int as3711_backlight_probe(struct platform_device *pdev)
 	unsigned int max_brightness;
 	int ret;
 
-	if (!pdata || (!pdata->su1_fb && !pdata->su2_fb)) {
+	if (!pdata) {
 		dev_err(&pdev->dev, "No platform data, exiting...\n");
 		return -ENODEV;
 	}
 
+	if (pdev->dev.parent->of_node) {
+		ret = as3711_backlight_parse_dt(&pdev->dev);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
+			return ret;
+		}
+	}
+
+	if (!pdata->su1_fb && !pdata->su2_fb) {
+		dev_err(&pdev->dev, "No framebuffer specified\n");
+		return -EINVAL;
+	}
+
 	/*
 	 * Due to possible hardware damage I chose to block all modes,
 	 * unsupported on my hardware. Anyone, wishing to use any of those modes
-- 
1.7.2.5

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH/RFC] mfd: as3711: add OF support
@ 2013-02-15 10:07 ` Guennadi Liakhovetski
  0 siblings, 0 replies; 16+ messages in thread
From: Guennadi Liakhovetski @ 2013-02-15 10:07 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-fbdev-u79uwXL29TY76Z2rM5mHXA, Samuel Ortiz,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Mark Brown,
	Magnus Damm, Simon Horman, Richard Purdie, Andrew Morton,
	Liam Girdwood

Add device-tree bindings to the AS3711 regulator and backlight drivers.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---

As usual - comments to the new bindings are very welcome!

 Documentation/devicetree/bindings/mfd/as3711.txt |   73 +++++++++++++
 drivers/mfd/as3711.c                             |   30 +++++-
 drivers/regulator/as3711-regulator.c             |   69 ++++++++++++-
 drivers/video/backlight/as3711_bl.c              |  118 +++++++++++++++++++++-
 4 files changed, 282 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mfd/as3711.txt

diff --git a/Documentation/devicetree/bindings/mfd/as3711.txt b/Documentation/devicetree/bindings/mfd/as3711.txt
new file mode 100644
index 0000000..d98cf18
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/as3711.txt
@@ -0,0 +1,73 @@
+AS3711 is an I2C PMIC from Austria MicroSystems with multiple DCDC and LDO power
+supplies, a battery charger and an RTC. So far only bindings for the two stepup
+DCDC converters are defined. Other DCDC and LDO supplies are configured, using
+standard regulator properties, they must belong to a sub-node, called
+"regulators" and be called "sd1" to "sd4" and "ldo1" to "ldo8." Stepup converter
+configuration should be placed in a subnode, called "backlight."
+
+Compulsory properties:
+- compatible		: must be "ams,as3711"
+- reg			: specifies the I2C address
+
+To use the SU1 converter as a backlight source the following two properties must
+be provided:
+- su1-dev		: framebuffer phandle
+- su1-max-uA		: maximum current
+
+To use the SU2 converter as a backlight source the following two properties must
+be provided:
+- su2-dev		: framebuffer phandle
+- su1-max-uA		: maximum current
+
+Additionally one of these properties must be provided to select the type of
+feedback used:
+- su2-feedback-voltage	: voltage feedback is used
+- su2-feedback-curr1	: CURR1 input used for current feedback
+- su2-feedback-curr2	: CURR2 input used for current feedback
+- su2-feedback-curr3	: CURR3 input used for current feedback
+- su2-feedback-curr-auto: automatic current feedback selection
+
+and one of these to select the over-voltage protection pin
+- su2-fbprot-lx-sd4	: LX_SD4 is used for over-voltage protection
+- su2-fbprot-gpio2	: GPIO2 is used for over-voltage protection
+- su2-fbprot-gpio3	: GPIO3 is used for over-voltage protection
+- su2-fbprot-gpio4	: GPIO4 is used for over-voltage protection
+
+If "su2-feedback-curr-auto" is selected, one or more of the following properties
+have to be specified:
+- su2-auto-curr1	: use CURR1 input for current feedback
+- su2-auto-curr2	: use CURR2 input for current feedback
+- su2-auto-curr3	: use CURR3 input for current feedback
+
+Example:
+
+as3711@40 {
+	compatible = "ams,as3711";
+	reg = <0x40>;
+
+	regulators {
+		sd4 {
+			regulator-name = "1.215V";
+			regulator-min-microvolt = <1215000>;
+			regulator-max-microvolt = <1235000>;
+		};
+		ldo2 {
+			regulator-name = "2.8V CPU";
+			regulator-min-microvolt = <2800000>;
+			regulator-max-microvolt = <2800000>;
+			regulator-always-on;
+			regulator-boot-on;
+		};
+	};
+
+	backlight {
+		compatible = "ams,as3711-bl";
+		su2-dev = <&lcdc>;
+		su2-max-uA = <36000>;
+		su2-feedback-curr-auto;
+		su2-fbprot-gpio4;
+		su2-auto-curr1;
+		su2-auto-curr2;
+		su2-auto-curr3;
+	};
+};
diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c
index e994c96..5e0e8b3 100644
--- a/drivers/mfd/as3711.c
+++ b/drivers/mfd/as3711.c
@@ -112,16 +112,37 @@ static const struct regmap_config as3711_regmap_config = {
 	.cache_type = REGCACHE_RBTREE,
 };
 
+#ifdef CONFIG_OF
+static struct of_device_id as3711_of_match[] = {
+	{.compatible = "ams,as3711",},
+	{}
+};
+MODULE_DEVICE_TABLE(of, as3711_of_match);
+#endif
+
 static int as3711_i2c_probe(struct i2c_client *client,
 			    const struct i2c_device_id *id)
 {
 	struct as3711 *as3711;
-	struct as3711_platform_data *pdata = client->dev.platform_data;
+	struct as3711_platform_data *pdata;
 	unsigned int id1, id2;
 	int ret;
 
-	if (!pdata)
-		dev_dbg(&client->dev, "Platform data not found\n");
+	if (!client->dev.of_node) {
+		pdata = client->dev.platform_data;
+		if (!pdata)
+			dev_dbg(&client->dev, "Platform data not found\n");
+	} else {
+		if (!of_device_is_available(client->dev.of_node))
+			return -ENODEV;
+
+		pdata = devm_kzalloc(&client->dev,
+				     sizeof(*pdata), GFP_KERNEL);
+		if (!pdata) {
+			dev_err(&client->dev, "Failed to allocate pdata\n");
+			return -ENOMEM;
+		}
+	}
 
 	as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
 	if (!as3711) {
@@ -193,7 +214,8 @@ static struct i2c_driver as3711_i2c_driver = {
 	.driver = {
 		   .name = "as3711",
 		   .owner = THIS_MODULE,
-		   },
+		   .of_match_table = of_match_ptr(as3711_of_match),
+	},
 	.probe = as3711_i2c_probe,
 	.remove = as3711_i2c_remove,
 	.id_table = as3711_i2c_id,
diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c
index f0ba8c4..cf145fc 100644
--- a/drivers/regulator/as3711-regulator.c
+++ b/drivers/regulator/as3711-regulator.c
@@ -13,9 +13,11 @@
 #include <linux/init.h>
 #include <linux/mfd/as3711.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
 #include <linux/slab.h>
 
 struct as3711_regulator_info {
@@ -276,6 +278,57 @@ static struct as3711_regulator_info as3711_reg_info[] = {
 
 #define AS3711_REGULATOR_NUM ARRAY_SIZE(as3711_reg_info)
 
+static const char *as3711_regulator_of_names[AS3711_REGULATOR_NUM] = {
+	[AS3711_REGULATOR_SD_1] = "sd1",
+	[AS3711_REGULATOR_SD_2] = "sd2",
+	[AS3711_REGULATOR_SD_3] = "sd3",
+	[AS3711_REGULATOR_SD_4] = "sd4",
+	[AS3711_REGULATOR_LDO_1] = "ldo1",
+	[AS3711_REGULATOR_LDO_2] = "ldo2",
+	[AS3711_REGULATOR_LDO_3] = "ldo3",
+	[AS3711_REGULATOR_LDO_4] = "ldo4",
+	[AS3711_REGULATOR_LDO_5] = "ldo5",
+	[AS3711_REGULATOR_LDO_6] = "ldo6",
+	[AS3711_REGULATOR_LDO_7] = "ldo7",
+	[AS3711_REGULATOR_LDO_8] = "ldo8",
+};
+
+static int as3711_regulator_parse_dt(struct device *dev)
+{
+	struct as3711_regulator_pdata *pdata = dev_get_platdata(dev);
+	struct device_node *regulators +		of_find_node_by_name(dev->parent->of_node, "regulators");
+	struct of_regulator_match *matches, *match;
+	const int count = AS3711_REGULATOR_NUM;
+	int ret, i;
+
+	if (!regulators) {
+		dev_err(dev, "regulator node not found\n");
+		return -ENODEV;
+	}
+
+	matches = devm_kzalloc(dev, sizeof(*matches) * count, GFP_KERNEL);
+	if (!matches)
+		return -ENOMEM;
+
+	for (i = 0, match = matches; i < count; i++, match++) {
+		match->name = as3711_regulator_of_names[i];
+		match->driver_data = as3711_reg_info + i;
+	}
+
+	ret = of_regulator_match(dev->parent, regulators, matches, count);
+	if (ret < 0) {
+		dev_err(dev, "Error parsing regulator init data: %d\n", ret);
+		return ret;
+	}
+
+	for (i = 0, match = matches; i < count; i++, match++)
+		if (match->of_node)
+			pdata->init_data[i] = match->init_data;
+
+	return 0;
+}
+
 static int as3711_regulator_probe(struct platform_device *pdev)
 {
 	struct as3711_regulator_pdata *pdata = dev_get_platdata(&pdev->dev);
@@ -289,8 +342,18 @@ static int as3711_regulator_probe(struct platform_device *pdev)
 	int ret;
 	int id;
 
-	if (!pdata)
-		dev_dbg(&pdev->dev, "No platform data...\n");
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data...\n");
+		return -ENODEV;
+	}
+
+	if (pdev->dev.parent->of_node) {
+		ret = as3711_regulator_parse_dt(&pdev->dev);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
+			return ret;
+		}
+	}
 
 	regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM *
 			sizeof(struct as3711_regulator), GFP_KERNEL);
@@ -300,7 +363,7 @@ static int as3711_regulator_probe(struct platform_device *pdev)
 	}
 
 	for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) {
-		reg_data = pdata ? pdata->init_data[id] : NULL;
+		reg_data = pdata->init_data[id];
 
 		/* No need to register if there is no regulator data */
 		if (!reg_data)
diff --git a/drivers/video/backlight/as3711_bl.c b/drivers/video/backlight/as3711_bl.c
index c6bc65d..90c9208 100644
--- a/drivers/video/backlight/as3711_bl.c
+++ b/drivers/video/backlight/as3711_bl.c
@@ -257,6 +257,109 @@ static int as3711_bl_register(struct platform_device *pdev,
 	return 0;
 }
 
+static int as3711_backlight_parse_dt(struct device *dev)
+{
+	struct as3711_bl_pdata *pdata = dev_get_platdata(dev);
+	struct device_node *bl +		of_find_node_by_name(dev->parent->of_node, "backlight"), *fb;
+	int ret;
+
+	if (!bl) {
+		dev_dbg(dev, "backlight node not found\n");
+		return -ENODEV;
+	}
+
+	fb = of_parse_phandle(bl, "su1-dev", 0);
+	if (fb) {
+		pdata->su1_fb = fb->full_name;
+
+		ret = of_property_read_u32(bl, "su1-max-uA", &pdata->su1_max_uA);
+		if (pdata->su1_max_uA <= 0)
+			ret = -EINVAL;
+		if (ret < 0)
+			return ret;
+	}
+
+	fb = of_parse_phandle(bl, "su2-dev", 0);
+	if (fb) {
+		int count = 0;
+
+		pdata->su2_fb = fb->full_name;
+
+		ret = of_property_read_u32(bl, "su2-max-uA", &pdata->su2_max_uA);
+		if (pdata->su2_max_uA <= 0)
+			ret = -EINVAL;
+		if (ret < 0)
+			return ret;
+
+		if (of_find_property(bl, "su2-feedback-voltage", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_VOLTAGE;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr1", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR1;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr2", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR2;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr3", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR3;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr-auto", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR_AUTO;
+			count++;
+		}
+		if (count != 1)
+			return -EINVAL;
+
+		count = 0;
+		if (of_find_property(bl, "su2-fbprot-lx-sd4", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_LX_SD4;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio2", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO2;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio3", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO3;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio4", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO4;
+			count++;
+		}
+		if (count != 1)
+			return -EINVAL;
+
+		count = 0;
+		if (of_find_property(bl, "su2-auto-curr1", NULL)) {
+			pdata->su2_auto_curr1 = true;
+			count++;
+		}
+		if (of_find_property(bl, "su2-auto-curr2", NULL)) {
+			pdata->su2_auto_curr2 = true;
+			count++;
+		}
+		if (of_find_property(bl, "su2-auto-curr3", NULL)) {
+			pdata->su2_auto_curr3 = true;
+			count++;
+		}
+
+		/*
+		 * At least one su2-auto-curr* must be specified iff
+		 * AS3711_SU2_CURR_AUTO is used
+		 */
+		if (!count ^ pdata->su2_feedback != AS3711_SU2_CURR_AUTO)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int as3711_backlight_probe(struct platform_device *pdev)
 {
 	struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev);
@@ -266,11 +369,24 @@ static int as3711_backlight_probe(struct platform_device *pdev)
 	unsigned int max_brightness;
 	int ret;
 
-	if (!pdata || (!pdata->su1_fb && !pdata->su2_fb)) {
+	if (!pdata) {
 		dev_err(&pdev->dev, "No platform data, exiting...\n");
 		return -ENODEV;
 	}
 
+	if (pdev->dev.parent->of_node) {
+		ret = as3711_backlight_parse_dt(&pdev->dev);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
+			return ret;
+		}
+	}
+
+	if (!pdata->su1_fb && !pdata->su2_fb) {
+		dev_err(&pdev->dev, "No framebuffer specified\n");
+		return -EINVAL;
+	}
+
 	/*
 	 * Due to possible hardware damage I chose to block all modes,
 	 * unsupported on my hardware. Anyone, wishing to use any of those modes
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC] ARM: shmobile: add framebuffer and backlight support to kzm9g-reference
  2013-02-15 10:07 ` Guennadi Liakhovetski
@ 2013-02-15 10:36   ` Guennadi Liakhovetski
  -1 siblings, 0 replies; 16+ messages in thread
From: Guennadi Liakhovetski @ 2013-02-15 10:36 UTC (permalink / raw)
  To: linux-kernel
  Cc: Magnus Damm, Simon Horman, devicetree-discuss, Samuel Ortiz,
	Mark Brown, Liam Girdwood, Richard Purdie, Andrew Morton,
	linux-fbdev

This adds support for the framebuffer and an AS3711 PMIC, used for supplying
power to the CPU, some peripherals and the backlight.

not-Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---

As is clear from the not-Sob line and the RFC in the subject, this is not 
for mainline. This is just an illustration how the recently submitted 
"mfd: as3711: add OF support" patch can be used with the kzm9g board. I 
had to add the framebuffer support to kzm9g, because without it the 
backlight would just be turned on with no image on the LCD, which doesn't 
look pretty. In the future, if needed, we could first only apply the 
regulator part of the .dts, leaving the backlight disabled.

 arch/arm/boot/dts/sh73a0-kzm9g-reference.dts   |   90 ++++++++++++++++++++++++
 arch/arm/mach-shmobile/board-kzm9g-reference.c |   73 +++++++++++++++++++-
 2 files changed, 162 insertions(+), 1 deletions(-)

diff --git a/arch/arm/boot/dts/sh73a0-kzm9g-reference.dts b/arch/arm/boot/dts/sh73a0-kzm9g-reference.dts
index b93b16a..a5810fc 100644
--- a/arch/arm/boot/dts/sh73a0-kzm9g-reference.dts
+++ b/arch/arm/boot/dts/sh73a0-kzm9g-reference.dts
@@ -99,6 +99,12 @@
 		toshiba,mmc-wrprotect-disable;
 		toshiba,mmc-has-idle-wait;
 	};
+
+	/* A dummy LCDC DT node for now */
+	lcdc: lcdc@0xfe940000 {
+		compatible = "renesas,sh-mobile-lcdc";
+		reg = <0xfe940000 0x4000>;
+	};
 };
 
 &i2c3 {
@@ -106,6 +112,90 @@
 	pinctrl-0 = <&i2c3_pins>;
 };
 
+&i2c0 {
+	as3711@40 {
+		compatible = "ams,as3711";
+		reg = <0x40>;
+
+		regulators {
+			sd1 {
+				regulator-name = "1.315V CPU";
+				regulator-min-microvolt = <1315000>;
+				regulator-max-microvolt = <1335000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			sd2 {
+				regulator-name = "1.8V";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			sd4 {
+				regulator-name = "1.215V";
+				regulator-min-microvolt = <1215000>;
+				regulator-max-microvolt = <1235000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			ldo2 {
+				regulator-name = "2.8V CPU";
+				regulator-min-microvolt = <2800000>;
+				regulator-max-microvolt = <2800000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			ldo3 {
+				regulator-name = "3.0V CPU";
+				regulator-min-microvolt = <3000000>;
+				regulator-max-microvolt = <3000000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			ldo4 {
+				regulator-name = "2.8V";
+				regulator-min-microvolt = <2800000>;
+				regulator-max-microvolt = <2800000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			ldo5 {
+				regulator-name = "2.8V #2";
+				regulator-min-microvolt = <2800000>;
+				regulator-max-microvolt = <2800000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			ldo7 {
+				regulator-name = "1.15V CPU";
+				regulator-min-microvolt = <1150000>;
+				regulator-max-microvolt = <1150000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			ldo8 {
+				regulator-name = "1.15V CPU #2";
+				regulator-min-microvolt = <1150000>;
+				regulator-max-microvolt = <1150000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+		};
+
+		backlight {
+			compatible = "ams,as3711-bl";
+			su2-dev = <&lcdc>;
+			su2-max-uA = <36000>;
+			su2-feedback-curr-auto;
+			su2-fbprot-gpio4;
+			su2-auto-curr1;
+			su2-auto-curr2;
+			su2-auto-curr3;
+		};
+	};
+};
+
 &gpio {
 	sdhi0_pins: pfc_sdhi0_pins {
 		renesas,pins = "sdhi0_data4", "sdhi0_ctrl", "sdhi0_wp", "sdhi0_cd";
diff --git a/arch/arm/mach-shmobile/board-kzm9g-reference.c b/arch/arm/mach-shmobile/board-kzm9g-reference.c
index 1490246..9d520ca 100644
--- a/arch/arm/mach-shmobile/board-kzm9g-reference.c
+++ b/arch/arm/mach-shmobile/board-kzm9g-reference.c
@@ -31,6 +31,7 @@
 #include <linux/regulator/fixed.h>
 #include <linux/regulator/machine.h>
 #include <linux/smsc911x.h>
+#include <linux/videodev2.h>
 
 #include <mach/irqs.h>
 #include <mach/sh73a0.h>
@@ -39,6 +40,62 @@
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 
+#include <video/sh_mobile_lcdc.h>
+
+/* LCDC */
+static struct fb_videomode kzm_lcdc_mode = {
+	.name		= "WVGA Panel",
+	.xres		= 800,
+	.yres		= 480,
+	.left_margin	= 220,
+	.right_margin	= 110,
+	.hsync_len	= 70,
+	.upper_margin	= 20,
+	.lower_margin	= 5,
+	.vsync_len	= 5,
+	.sync		= 0,
+};
+
+static struct sh_mobile_lcdc_info lcdc_info = {
+	.clock_source = LCDC_CLK_BUS,
+	.ch[0] = {
+		.chan		= LCDC_CHAN_MAINLCD,
+		.fourcc		= V4L2_PIX_FMT_RGB565,
+		.interface_type	= RGB24,
+		.lcd_modes	= &kzm_lcdc_mode,
+		.num_modes	= 1,
+		.clock_divider	= 5,
+		.flags		= 0,
+		.panel_cfg = {
+			.width	= 152,
+			.height	= 91,
+		},
+	}
+};
+
+static struct resource lcdc_resources[] = {
+	[0] = {
+		.name	= "LCDC",
+		.start	= 0xfe940000,
+		.end	= 0xfe943fff,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= intcs_evt2irq(0x580),
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device lcdc_device = {
+	.name		= "sh_mobile_lcdc_fb",
+	.num_resources	= ARRAY_SIZE(lcdc_resources),
+	.resource	= lcdc_resources,
+	.dev	= {
+		.platform_data	= &lcdc_info,
+		.coherent_dma_mask = ~0,
+	},
+};
+
 /* Dummy supplies, where voltage doesn't matter */
 static struct regulator_consumer_supply dummy_supplies[] = {
 	REGULATOR_SUPPLY("vddvario", "smsc911x"),
@@ -79,6 +136,15 @@ static const struct pinctrl_map kzm_pinctrl_map[] = {
 				  "scifa4_data", "scifa4"),
 	PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.4", "e6050000.pfc",
 				  "scifa4_ctrl", "scifa4"),
+	PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_lcdc_fb.0", "e6050000.pfc",
+				  "lcd_data24", "lcd"),
+	PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_lcdc_fb.0", "e6050000.pfc",
+				  "lcd_sync", "lcd"),
+};
+
+static struct platform_device *kzm_devices[] __initdata = {
+	&smsc_device,
+	&lcdc_device,
 };
 
 static void __init kzm_init(void)
@@ -88,7 +154,10 @@ static void __init kzm_init(void)
 	pinctrl_register_mappings(kzm_pinctrl_map, ARRAY_SIZE(kzm_pinctrl_map));
 
 	regulator_register_fixed(2, dummy_supplies, ARRAY_SIZE(dummy_supplies));
-	platform_device_register(&smsc_device);
+
+	/* LCDC */
+	gpio_request_one(222, GPIOF_OUT_INIT_HIGH, NULL); /* LCDCDON */
+	gpio_request_one(226, GPIOF_OUT_INIT_HIGH, NULL); /* SC */
 
 	/*
 	 * Enable SD: this is a pseudo-GPIO, it actually only sets bit 28 in
@@ -101,6 +170,8 @@ static void __init kzm_init(void)
 	/* Early BRESP enable, Shared attribute override enable, 64K*8way */
 	l2x0_init(IOMEM(0xf0100000), 0x40460000, 0x82000fff);
 #endif
+
+	platform_add_devices(kzm_devices, ARRAY_SIZE(kzm_devices));
 }
 
 static void kzm9g_restart(char mode, const char *cmd)
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [RFC] ARM: shmobile: add framebuffer and backlight support to kzm9g-reference
@ 2013-02-15 10:36   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 16+ messages in thread
From: Guennadi Liakhovetski @ 2013-02-15 10:36 UTC (permalink / raw)
  To: linux-kernel
  Cc: Magnus Damm, Simon Horman, devicetree-discuss, Samuel Ortiz,
	Mark Brown, Liam Girdwood, Richard Purdie, Andrew Morton,
	linux-fbdev

This adds support for the framebuffer and an AS3711 PMIC, used for supplying
power to the CPU, some peripherals and the backlight.

not-Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---

As is clear from the not-Sob line and the RFC in the subject, this is not 
for mainline. This is just an illustration how the recently submitted 
"mfd: as3711: add OF support" patch can be used with the kzm9g board. I 
had to add the framebuffer support to kzm9g, because without it the 
backlight would just be turned on with no image on the LCD, which doesn't 
look pretty. In the future, if needed, we could first only apply the 
regulator part of the .dts, leaving the backlight disabled.

 arch/arm/boot/dts/sh73a0-kzm9g-reference.dts   |   90 ++++++++++++++++++++++++
 arch/arm/mach-shmobile/board-kzm9g-reference.c |   73 +++++++++++++++++++-
 2 files changed, 162 insertions(+), 1 deletions(-)

diff --git a/arch/arm/boot/dts/sh73a0-kzm9g-reference.dts b/arch/arm/boot/dts/sh73a0-kzm9g-reference.dts
index b93b16a..a5810fc 100644
--- a/arch/arm/boot/dts/sh73a0-kzm9g-reference.dts
+++ b/arch/arm/boot/dts/sh73a0-kzm9g-reference.dts
@@ -99,6 +99,12 @@
 		toshiba,mmc-wrprotect-disable;
 		toshiba,mmc-has-idle-wait;
 	};
+
+	/* A dummy LCDC DT node for now */
+	lcdc: lcdc@0xfe940000 {
+		compatible = "renesas,sh-mobile-lcdc";
+		reg = <0xfe940000 0x4000>;
+	};
 };
 
 &i2c3 {
@@ -106,6 +112,90 @@
 	pinctrl-0 = <&i2c3_pins>;
 };
 
+&i2c0 {
+	as3711@40 {
+		compatible = "ams,as3711";
+		reg = <0x40>;
+
+		regulators {
+			sd1 {
+				regulator-name = "1.315V CPU";
+				regulator-min-microvolt = <1315000>;
+				regulator-max-microvolt = <1335000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			sd2 {
+				regulator-name = "1.8V";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			sd4 {
+				regulator-name = "1.215V";
+				regulator-min-microvolt = <1215000>;
+				regulator-max-microvolt = <1235000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			ldo2 {
+				regulator-name = "2.8V CPU";
+				regulator-min-microvolt = <2800000>;
+				regulator-max-microvolt = <2800000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			ldo3 {
+				regulator-name = "3.0V CPU";
+				regulator-min-microvolt = <3000000>;
+				regulator-max-microvolt = <3000000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			ldo4 {
+				regulator-name = "2.8V";
+				regulator-min-microvolt = <2800000>;
+				regulator-max-microvolt = <2800000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			ldo5 {
+				regulator-name = "2.8V #2";
+				regulator-min-microvolt = <2800000>;
+				regulator-max-microvolt = <2800000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			ldo7 {
+				regulator-name = "1.15V CPU";
+				regulator-min-microvolt = <1150000>;
+				regulator-max-microvolt = <1150000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+			ldo8 {
+				regulator-name = "1.15V CPU #2";
+				regulator-min-microvolt = <1150000>;
+				regulator-max-microvolt = <1150000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+		};
+
+		backlight {
+			compatible = "ams,as3711-bl";
+			su2-dev = <&lcdc>;
+			su2-max-uA = <36000>;
+			su2-feedback-curr-auto;
+			su2-fbprot-gpio4;
+			su2-auto-curr1;
+			su2-auto-curr2;
+			su2-auto-curr3;
+		};
+	};
+};
+
 &gpio {
 	sdhi0_pins: pfc_sdhi0_pins {
 		renesas,pins = "sdhi0_data4", "sdhi0_ctrl", "sdhi0_wp", "sdhi0_cd";
diff --git a/arch/arm/mach-shmobile/board-kzm9g-reference.c b/arch/arm/mach-shmobile/board-kzm9g-reference.c
index 1490246..9d520ca 100644
--- a/arch/arm/mach-shmobile/board-kzm9g-reference.c
+++ b/arch/arm/mach-shmobile/board-kzm9g-reference.c
@@ -31,6 +31,7 @@
 #include <linux/regulator/fixed.h>
 #include <linux/regulator/machine.h>
 #include <linux/smsc911x.h>
+#include <linux/videodev2.h>
 
 #include <mach/irqs.h>
 #include <mach/sh73a0.h>
@@ -39,6 +40,62 @@
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 
+#include <video/sh_mobile_lcdc.h>
+
+/* LCDC */
+static struct fb_videomode kzm_lcdc_mode = {
+	.name		= "WVGA Panel",
+	.xres		= 800,
+	.yres		= 480,
+	.left_margin	= 220,
+	.right_margin	= 110,
+	.hsync_len	= 70,
+	.upper_margin	= 20,
+	.lower_margin	= 5,
+	.vsync_len	= 5,
+	.sync		= 0,
+};
+
+static struct sh_mobile_lcdc_info lcdc_info = {
+	.clock_source = LCDC_CLK_BUS,
+	.ch[0] = {
+		.chan		= LCDC_CHAN_MAINLCD,
+		.fourcc		= V4L2_PIX_FMT_RGB565,
+		.interface_type	= RGB24,
+		.lcd_modes	= &kzm_lcdc_mode,
+		.num_modes	= 1,
+		.clock_divider	= 5,
+		.flags		= 0,
+		.panel_cfg = {
+			.width	= 152,
+			.height	= 91,
+		},
+	}
+};
+
+static struct resource lcdc_resources[] = {
+	[0] = {
+		.name	= "LCDC",
+		.start	= 0xfe940000,
+		.end	= 0xfe943fff,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= intcs_evt2irq(0x580),
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device lcdc_device = {
+	.name		= "sh_mobile_lcdc_fb",
+	.num_resources	= ARRAY_SIZE(lcdc_resources),
+	.resource	= lcdc_resources,
+	.dev	= {
+		.platform_data	= &lcdc_info,
+		.coherent_dma_mask = ~0,
+	},
+};
+
 /* Dummy supplies, where voltage doesn't matter */
 static struct regulator_consumer_supply dummy_supplies[] = {
 	REGULATOR_SUPPLY("vddvario", "smsc911x"),
@@ -79,6 +136,15 @@ static const struct pinctrl_map kzm_pinctrl_map[] = {
 				  "scifa4_data", "scifa4"),
 	PIN_MAP_MUX_GROUP_DEFAULT("sh-sci.4", "e6050000.pfc",
 				  "scifa4_ctrl", "scifa4"),
+	PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_lcdc_fb.0", "e6050000.pfc",
+				  "lcd_data24", "lcd"),
+	PIN_MAP_MUX_GROUP_DEFAULT("sh_mobile_lcdc_fb.0", "e6050000.pfc",
+				  "lcd_sync", "lcd"),
+};
+
+static struct platform_device *kzm_devices[] __initdata = {
+	&smsc_device,
+	&lcdc_device,
 };
 
 static void __init kzm_init(void)
@@ -88,7 +154,10 @@ static void __init kzm_init(void)
 	pinctrl_register_mappings(kzm_pinctrl_map, ARRAY_SIZE(kzm_pinctrl_map));
 
 	regulator_register_fixed(2, dummy_supplies, ARRAY_SIZE(dummy_supplies));
-	platform_device_register(&smsc_device);
+
+	/* LCDC */
+	gpio_request_one(222, GPIOF_OUT_INIT_HIGH, NULL); /* LCDCDON */
+	gpio_request_one(226, GPIOF_OUT_INIT_HIGH, NULL); /* SC */
 
 	/*
 	 * Enable SD: this is a pseudo-GPIO, it actually only sets bit 28 in
@@ -101,6 +170,8 @@ static void __init kzm_init(void)
 	/* Early BRESP enable, Shared attribute override enable, 64K*8way */
 	l2x0_init(IOMEM(0xf0100000), 0x40460000, 0x82000fff);
 #endif
+
+	platform_add_devices(kzm_devices, ARRAY_SIZE(kzm_devices));
 }
 
 static void kzm9g_restart(char mode, const char *cmd)
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH/RFC] mfd: as3711: add OF support
  2013-02-15 10:07 ` Guennadi Liakhovetski
@ 2013-02-16  2:26   ` Simon Horman
  -1 siblings, 0 replies; 16+ messages in thread
From: Simon Horman @ 2013-02-16  2:26 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-kernel, Magnus Damm, devicetree-discuss, Samuel Ortiz,
	Mark Brown, Liam Girdwood, Richard Purdie, Andrew Morton,
	linux-fbdev

On Fri, Feb 15, 2013 at 11:07:16AM +0100, Guennadi Liakhovetski wrote:
> Add device-tree bindings to the AS3711 regulator and backlight drivers.

Hi,

at this stage I do not expect this code to go through the renesas tree.
However, in order to provide a basis for work on renesas SoCs I have added
this patch to the topic/backlight topic branch in the reneas tree on
kernel.org and merged it into topic/all+next.

In other words, I am not picking this patch up to merge it or add it to
linux-next, rather I am storing it for reference.

> 
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> ---
> 
> As usual - comments to the new bindings are very welcome!
> 
>  Documentation/devicetree/bindings/mfd/as3711.txt |   73 +++++++++++++
>  drivers/mfd/as3711.c                             |   30 +++++-
>  drivers/regulator/as3711-regulator.c             |   69 ++++++++++++-
>  drivers/video/backlight/as3711_bl.c              |  118 +++++++++++++++++++++-
>  4 files changed, 282 insertions(+), 8 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/mfd/as3711.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/as3711.txt b/Documentation/devicetree/bindings/mfd/as3711.txt
> new file mode 100644
> index 0000000..d98cf18
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/as3711.txt
> @@ -0,0 +1,73 @@
> +AS3711 is an I2C PMIC from Austria MicroSystems with multiple DCDC and LDO power
> +supplies, a battery charger and an RTC. So far only bindings for the two stepup
> +DCDC converters are defined. Other DCDC and LDO supplies are configured, using
> +standard regulator properties, they must belong to a sub-node, called
> +"regulators" and be called "sd1" to "sd4" and "ldo1" to "ldo8." Stepup converter
> +configuration should be placed in a subnode, called "backlight."
> +
> +Compulsory properties:
> +- compatible		: must be "ams,as3711"
> +- reg			: specifies the I2C address
> +
> +To use the SU1 converter as a backlight source the following two properties must
> +be provided:
> +- su1-dev		: framebuffer phandle
> +- su1-max-uA		: maximum current
> +
> +To use the SU2 converter as a backlight source the following two properties must
> +be provided:
> +- su2-dev		: framebuffer phandle
> +- su1-max-uA		: maximum current
> +
> +Additionally one of these properties must be provided to select the type of
> +feedback used:
> +- su2-feedback-voltage	: voltage feedback is used
> +- su2-feedback-curr1	: CURR1 input used for current feedback
> +- su2-feedback-curr2	: CURR2 input used for current feedback
> +- su2-feedback-curr3	: CURR3 input used for current feedback
> +- su2-feedback-curr-auto: automatic current feedback selection
> +
> +and one of these to select the over-voltage protection pin
> +- su2-fbprot-lx-sd4	: LX_SD4 is used for over-voltage protection
> +- su2-fbprot-gpio2	: GPIO2 is used for over-voltage protection
> +- su2-fbprot-gpio3	: GPIO3 is used for over-voltage protection
> +- su2-fbprot-gpio4	: GPIO4 is used for over-voltage protection
> +
> +If "su2-feedback-curr-auto" is selected, one or more of the following properties
> +have to be specified:
> +- su2-auto-curr1	: use CURR1 input for current feedback
> +- su2-auto-curr2	: use CURR2 input for current feedback
> +- su2-auto-curr3	: use CURR3 input for current feedback
> +
> +Example:
> +
> +as3711@40 {
> +	compatible = "ams,as3711";
> +	reg = <0x40>;
> +
> +	regulators {
> +		sd4 {
> +			regulator-name = "1.215V";
> +			regulator-min-microvolt = <1215000>;
> +			regulator-max-microvolt = <1235000>;
> +		};
> +		ldo2 {
> +			regulator-name = "2.8V CPU";
> +			regulator-min-microvolt = <2800000>;
> +			regulator-max-microvolt = <2800000>;
> +			regulator-always-on;
> +			regulator-boot-on;
> +		};
> +	};
> +
> +	backlight {
> +		compatible = "ams,as3711-bl";
> +		su2-dev = <&lcdc>;
> +		su2-max-uA = <36000>;
> +		su2-feedback-curr-auto;
> +		su2-fbprot-gpio4;
> +		su2-auto-curr1;
> +		su2-auto-curr2;
> +		su2-auto-curr3;
> +	};
> +};
> diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c
> index e994c96..5e0e8b3 100644
> --- a/drivers/mfd/as3711.c
> +++ b/drivers/mfd/as3711.c
> @@ -112,16 +112,37 @@ static const struct regmap_config as3711_regmap_config = {
>  	.cache_type = REGCACHE_RBTREE,
>  };
>  
> +#ifdef CONFIG_OF
> +static struct of_device_id as3711_of_match[] = {
> +	{.compatible = "ams,as3711",},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, as3711_of_match);
> +#endif
> +
>  static int as3711_i2c_probe(struct i2c_client *client,
>  			    const struct i2c_device_id *id)
>  {
>  	struct as3711 *as3711;
> -	struct as3711_platform_data *pdata = client->dev.platform_data;
> +	struct as3711_platform_data *pdata;
>  	unsigned int id1, id2;
>  	int ret;
>  
> -	if (!pdata)
> -		dev_dbg(&client->dev, "Platform data not found\n");
> +	if (!client->dev.of_node) {
> +		pdata = client->dev.platform_data;
> +		if (!pdata)
> +			dev_dbg(&client->dev, "Platform data not found\n");
> +	} else {
> +		if (!of_device_is_available(client->dev.of_node))
> +			return -ENODEV;
> +
> +		pdata = devm_kzalloc(&client->dev,
> +				     sizeof(*pdata), GFP_KERNEL);
> +		if (!pdata) {
> +			dev_err(&client->dev, "Failed to allocate pdata\n");
> +			return -ENOMEM;
> +		}
> +	}
>  
>  	as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
>  	if (!as3711) {
> @@ -193,7 +214,8 @@ static struct i2c_driver as3711_i2c_driver = {
>  	.driver = {
>  		   .name = "as3711",
>  		   .owner = THIS_MODULE,
> -		   },
> +		   .of_match_table = of_match_ptr(as3711_of_match),
> +	},
>  	.probe = as3711_i2c_probe,
>  	.remove = as3711_i2c_remove,
>  	.id_table = as3711_i2c_id,
> diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c
> index f0ba8c4..cf145fc 100644
> --- a/drivers/regulator/as3711-regulator.c
> +++ b/drivers/regulator/as3711-regulator.c
> @@ -13,9 +13,11 @@
>  #include <linux/init.h>
>  #include <linux/mfd/as3711.h>
>  #include <linux/module.h>
> +#include <linux/of.h>
>  #include <linux/platform_device.h>
>  #include <linux/regmap.h>
>  #include <linux/regulator/driver.h>
> +#include <linux/regulator/of_regulator.h>
>  #include <linux/slab.h>
>  
>  struct as3711_regulator_info {
> @@ -276,6 +278,57 @@ static struct as3711_regulator_info as3711_reg_info[] = {
>  
>  #define AS3711_REGULATOR_NUM ARRAY_SIZE(as3711_reg_info)
>  
> +static const char *as3711_regulator_of_names[AS3711_REGULATOR_NUM] = {
> +	[AS3711_REGULATOR_SD_1] = "sd1",
> +	[AS3711_REGULATOR_SD_2] = "sd2",
> +	[AS3711_REGULATOR_SD_3] = "sd3",
> +	[AS3711_REGULATOR_SD_4] = "sd4",
> +	[AS3711_REGULATOR_LDO_1] = "ldo1",
> +	[AS3711_REGULATOR_LDO_2] = "ldo2",
> +	[AS3711_REGULATOR_LDO_3] = "ldo3",
> +	[AS3711_REGULATOR_LDO_4] = "ldo4",
> +	[AS3711_REGULATOR_LDO_5] = "ldo5",
> +	[AS3711_REGULATOR_LDO_6] = "ldo6",
> +	[AS3711_REGULATOR_LDO_7] = "ldo7",
> +	[AS3711_REGULATOR_LDO_8] = "ldo8",
> +};
> +
> +static int as3711_regulator_parse_dt(struct device *dev)
> +{
> +	struct as3711_regulator_pdata *pdata = dev_get_platdata(dev);
> +	struct device_node *regulators =
> +		of_find_node_by_name(dev->parent->of_node, "regulators");
> +	struct of_regulator_match *matches, *match;
> +	const int count = AS3711_REGULATOR_NUM;
> +	int ret, i;
> +
> +	if (!regulators) {
> +		dev_err(dev, "regulator node not found\n");
> +		return -ENODEV;
> +	}
> +
> +	matches = devm_kzalloc(dev, sizeof(*matches) * count, GFP_KERNEL);
> +	if (!matches)
> +		return -ENOMEM;
> +
> +	for (i = 0, match = matches; i < count; i++, match++) {
> +		match->name = as3711_regulator_of_names[i];
> +		match->driver_data = as3711_reg_info + i;
> +	}
> +
> +	ret = of_regulator_match(dev->parent, regulators, matches, count);
> +	if (ret < 0) {
> +		dev_err(dev, "Error parsing regulator init data: %d\n", ret);
> +		return ret;
> +	}
> +
> +	for (i = 0, match = matches; i < count; i++, match++)
> +		if (match->of_node)
> +			pdata->init_data[i] = match->init_data;
> +
> +	return 0;
> +}
> +
>  static int as3711_regulator_probe(struct platform_device *pdev)
>  {
>  	struct as3711_regulator_pdata *pdata = dev_get_platdata(&pdev->dev);
> @@ -289,8 +342,18 @@ static int as3711_regulator_probe(struct platform_device *pdev)
>  	int ret;
>  	int id;
>  
> -	if (!pdata)
> -		dev_dbg(&pdev->dev, "No platform data...\n");
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "No platform data...\n");
> +		return -ENODEV;
> +	}
> +
> +	if (pdev->dev.parent->of_node) {
> +		ret = as3711_regulator_parse_dt(&pdev->dev);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
> +			return ret;
> +		}
> +	}
>  
>  	regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM *
>  			sizeof(struct as3711_regulator), GFP_KERNEL);
> @@ -300,7 +363,7 @@ static int as3711_regulator_probe(struct platform_device *pdev)
>  	}
>  
>  	for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) {
> -		reg_data = pdata ? pdata->init_data[id] : NULL;
> +		reg_data = pdata->init_data[id];
>  
>  		/* No need to register if there is no regulator data */
>  		if (!reg_data)
> diff --git a/drivers/video/backlight/as3711_bl.c b/drivers/video/backlight/as3711_bl.c
> index c6bc65d..90c9208 100644
> --- a/drivers/video/backlight/as3711_bl.c
> +++ b/drivers/video/backlight/as3711_bl.c
> @@ -257,6 +257,109 @@ static int as3711_bl_register(struct platform_device *pdev,
>  	return 0;
>  }
>  
> +static int as3711_backlight_parse_dt(struct device *dev)
> +{
> +	struct as3711_bl_pdata *pdata = dev_get_platdata(dev);
> +	struct device_node *bl =
> +		of_find_node_by_name(dev->parent->of_node, "backlight"), *fb;
> +	int ret;
> +
> +	if (!bl) {
> +		dev_dbg(dev, "backlight node not found\n");
> +		return -ENODEV;
> +	}
> +
> +	fb = of_parse_phandle(bl, "su1-dev", 0);
> +	if (fb) {
> +		pdata->su1_fb = fb->full_name;
> +
> +		ret = of_property_read_u32(bl, "su1-max-uA", &pdata->su1_max_uA);
> +		if (pdata->su1_max_uA <= 0)
> +			ret = -EINVAL;
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	fb = of_parse_phandle(bl, "su2-dev", 0);
> +	if (fb) {
> +		int count = 0;
> +
> +		pdata->su2_fb = fb->full_name;
> +
> +		ret = of_property_read_u32(bl, "su2-max-uA", &pdata->su2_max_uA);
> +		if (pdata->su2_max_uA <= 0)
> +			ret = -EINVAL;
> +		if (ret < 0)
> +			return ret;
> +
> +		if (of_find_property(bl, "su2-feedback-voltage", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_VOLTAGE;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr1", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR1;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr2", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR2;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr3", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR3;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr-auto", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR_AUTO;
> +			count++;
> +		}
> +		if (count != 1)
> +			return -EINVAL;
> +
> +		count = 0;
> +		if (of_find_property(bl, "su2-fbprot-lx-sd4", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_LX_SD4;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-fbprot-gpio2", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_GPIO2;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-fbprot-gpio3", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_GPIO3;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-fbprot-gpio4", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_GPIO4;
> +			count++;
> +		}
> +		if (count != 1)
> +			return -EINVAL;
> +
> +		count = 0;
> +		if (of_find_property(bl, "su2-auto-curr1", NULL)) {
> +			pdata->su2_auto_curr1 = true;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-auto-curr2", NULL)) {
> +			pdata->su2_auto_curr2 = true;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-auto-curr3", NULL)) {
> +			pdata->su2_auto_curr3 = true;
> +			count++;
> +		}
> +
> +		/*
> +		 * At least one su2-auto-curr* must be specified iff
> +		 * AS3711_SU2_CURR_AUTO is used
> +		 */
> +		if (!count ^ pdata->su2_feedback != AS3711_SU2_CURR_AUTO)
> +			return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
>  static int as3711_backlight_probe(struct platform_device *pdev)
>  {
>  	struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev);
> @@ -266,11 +369,24 @@ static int as3711_backlight_probe(struct platform_device *pdev)
>  	unsigned int max_brightness;
>  	int ret;
>  
> -	if (!pdata || (!pdata->su1_fb && !pdata->su2_fb)) {
> +	if (!pdata) {
>  		dev_err(&pdev->dev, "No platform data, exiting...\n");
>  		return -ENODEV;
>  	}
>  
> +	if (pdev->dev.parent->of_node) {
> +		ret = as3711_backlight_parse_dt(&pdev->dev);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	if (!pdata->su1_fb && !pdata->su2_fb) {
> +		dev_err(&pdev->dev, "No framebuffer specified\n");
> +		return -EINVAL;
> +	}
> +
>  	/*
>  	 * Due to possible hardware damage I chose to block all modes,
>  	 * unsupported on my hardware. Anyone, wishing to use any of those modes
> -- 
> 1.7.2.5
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH/RFC] mfd: as3711: add OF support
@ 2013-02-16  2:26   ` Simon Horman
  0 siblings, 0 replies; 16+ messages in thread
From: Simon Horman @ 2013-02-16  2:26 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-kernel, Magnus Damm, devicetree-discuss, Samuel Ortiz,
	Mark Brown, Liam Girdwood, Richard Purdie, Andrew Morton,
	linux-fbdev

On Fri, Feb 15, 2013 at 11:07:16AM +0100, Guennadi Liakhovetski wrote:
> Add device-tree bindings to the AS3711 regulator and backlight drivers.

Hi,

at this stage I do not expect this code to go through the renesas tree.
However, in order to provide a basis for work on renesas SoCs I have added
this patch to the topic/backlight topic branch in the reneas tree on
kernel.org and merged it into topic/all+next.

In other words, I am not picking this patch up to merge it or add it to
linux-next, rather I am storing it for reference.

> 
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> ---
> 
> As usual - comments to the new bindings are very welcome!
> 
>  Documentation/devicetree/bindings/mfd/as3711.txt |   73 +++++++++++++
>  drivers/mfd/as3711.c                             |   30 +++++-
>  drivers/regulator/as3711-regulator.c             |   69 ++++++++++++-
>  drivers/video/backlight/as3711_bl.c              |  118 +++++++++++++++++++++-
>  4 files changed, 282 insertions(+), 8 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/mfd/as3711.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/as3711.txt b/Documentation/devicetree/bindings/mfd/as3711.txt
> new file mode 100644
> index 0000000..d98cf18
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/as3711.txt
> @@ -0,0 +1,73 @@
> +AS3711 is an I2C PMIC from Austria MicroSystems with multiple DCDC and LDO power
> +supplies, a battery charger and an RTC. So far only bindings for the two stepup
> +DCDC converters are defined. Other DCDC and LDO supplies are configured, using
> +standard regulator properties, they must belong to a sub-node, called
> +"regulators" and be called "sd1" to "sd4" and "ldo1" to "ldo8." Stepup converter
> +configuration should be placed in a subnode, called "backlight."
> +
> +Compulsory properties:
> +- compatible		: must be "ams,as3711"
> +- reg			: specifies the I2C address
> +
> +To use the SU1 converter as a backlight source the following two properties must
> +be provided:
> +- su1-dev		: framebuffer phandle
> +- su1-max-uA		: maximum current
> +
> +To use the SU2 converter as a backlight source the following two properties must
> +be provided:
> +- su2-dev		: framebuffer phandle
> +- su1-max-uA		: maximum current
> +
> +Additionally one of these properties must be provided to select the type of
> +feedback used:
> +- su2-feedback-voltage	: voltage feedback is used
> +- su2-feedback-curr1	: CURR1 input used for current feedback
> +- su2-feedback-curr2	: CURR2 input used for current feedback
> +- su2-feedback-curr3	: CURR3 input used for current feedback
> +- su2-feedback-curr-auto: automatic current feedback selection
> +
> +and one of these to select the over-voltage protection pin
> +- su2-fbprot-lx-sd4	: LX_SD4 is used for over-voltage protection
> +- su2-fbprot-gpio2	: GPIO2 is used for over-voltage protection
> +- su2-fbprot-gpio3	: GPIO3 is used for over-voltage protection
> +- su2-fbprot-gpio4	: GPIO4 is used for over-voltage protection
> +
> +If "su2-feedback-curr-auto" is selected, one or more of the following properties
> +have to be specified:
> +- su2-auto-curr1	: use CURR1 input for current feedback
> +- su2-auto-curr2	: use CURR2 input for current feedback
> +- su2-auto-curr3	: use CURR3 input for current feedback
> +
> +Example:
> +
> +as3711@40 {
> +	compatible = "ams,as3711";
> +	reg = <0x40>;
> +
> +	regulators {
> +		sd4 {
> +			regulator-name = "1.215V";
> +			regulator-min-microvolt = <1215000>;
> +			regulator-max-microvolt = <1235000>;
> +		};
> +		ldo2 {
> +			regulator-name = "2.8V CPU";
> +			regulator-min-microvolt = <2800000>;
> +			regulator-max-microvolt = <2800000>;
> +			regulator-always-on;
> +			regulator-boot-on;
> +		};
> +	};
> +
> +	backlight {
> +		compatible = "ams,as3711-bl";
> +		su2-dev = <&lcdc>;
> +		su2-max-uA = <36000>;
> +		su2-feedback-curr-auto;
> +		su2-fbprot-gpio4;
> +		su2-auto-curr1;
> +		su2-auto-curr2;
> +		su2-auto-curr3;
> +	};
> +};
> diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c
> index e994c96..5e0e8b3 100644
> --- a/drivers/mfd/as3711.c
> +++ b/drivers/mfd/as3711.c
> @@ -112,16 +112,37 @@ static const struct regmap_config as3711_regmap_config = {
>  	.cache_type = REGCACHE_RBTREE,
>  };
>  
> +#ifdef CONFIG_OF
> +static struct of_device_id as3711_of_match[] = {
> +	{.compatible = "ams,as3711",},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, as3711_of_match);
> +#endif
> +
>  static int as3711_i2c_probe(struct i2c_client *client,
>  			    const struct i2c_device_id *id)
>  {
>  	struct as3711 *as3711;
> -	struct as3711_platform_data *pdata = client->dev.platform_data;
> +	struct as3711_platform_data *pdata;
>  	unsigned int id1, id2;
>  	int ret;
>  
> -	if (!pdata)
> -		dev_dbg(&client->dev, "Platform data not found\n");
> +	if (!client->dev.of_node) {
> +		pdata = client->dev.platform_data;
> +		if (!pdata)
> +			dev_dbg(&client->dev, "Platform data not found\n");
> +	} else {
> +		if (!of_device_is_available(client->dev.of_node))
> +			return -ENODEV;
> +
> +		pdata = devm_kzalloc(&client->dev,
> +				     sizeof(*pdata), GFP_KERNEL);
> +		if (!pdata) {
> +			dev_err(&client->dev, "Failed to allocate pdata\n");
> +			return -ENOMEM;
> +		}
> +	}
>  
>  	as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
>  	if (!as3711) {
> @@ -193,7 +214,8 @@ static struct i2c_driver as3711_i2c_driver = {
>  	.driver = {
>  		   .name = "as3711",
>  		   .owner = THIS_MODULE,
> -		   },
> +		   .of_match_table = of_match_ptr(as3711_of_match),
> +	},
>  	.probe = as3711_i2c_probe,
>  	.remove = as3711_i2c_remove,
>  	.id_table = as3711_i2c_id,
> diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c
> index f0ba8c4..cf145fc 100644
> --- a/drivers/regulator/as3711-regulator.c
> +++ b/drivers/regulator/as3711-regulator.c
> @@ -13,9 +13,11 @@
>  #include <linux/init.h>
>  #include <linux/mfd/as3711.h>
>  #include <linux/module.h>
> +#include <linux/of.h>
>  #include <linux/platform_device.h>
>  #include <linux/regmap.h>
>  #include <linux/regulator/driver.h>
> +#include <linux/regulator/of_regulator.h>
>  #include <linux/slab.h>
>  
>  struct as3711_regulator_info {
> @@ -276,6 +278,57 @@ static struct as3711_regulator_info as3711_reg_info[] = {
>  
>  #define AS3711_REGULATOR_NUM ARRAY_SIZE(as3711_reg_info)
>  
> +static const char *as3711_regulator_of_names[AS3711_REGULATOR_NUM] = {
> +	[AS3711_REGULATOR_SD_1] = "sd1",
> +	[AS3711_REGULATOR_SD_2] = "sd2",
> +	[AS3711_REGULATOR_SD_3] = "sd3",
> +	[AS3711_REGULATOR_SD_4] = "sd4",
> +	[AS3711_REGULATOR_LDO_1] = "ldo1",
> +	[AS3711_REGULATOR_LDO_2] = "ldo2",
> +	[AS3711_REGULATOR_LDO_3] = "ldo3",
> +	[AS3711_REGULATOR_LDO_4] = "ldo4",
> +	[AS3711_REGULATOR_LDO_5] = "ldo5",
> +	[AS3711_REGULATOR_LDO_6] = "ldo6",
> +	[AS3711_REGULATOR_LDO_7] = "ldo7",
> +	[AS3711_REGULATOR_LDO_8] = "ldo8",
> +};
> +
> +static int as3711_regulator_parse_dt(struct device *dev)
> +{
> +	struct as3711_regulator_pdata *pdata = dev_get_platdata(dev);
> +	struct device_node *regulators > +		of_find_node_by_name(dev->parent->of_node, "regulators");
> +	struct of_regulator_match *matches, *match;
> +	const int count = AS3711_REGULATOR_NUM;
> +	int ret, i;
> +
> +	if (!regulators) {
> +		dev_err(dev, "regulator node not found\n");
> +		return -ENODEV;
> +	}
> +
> +	matches = devm_kzalloc(dev, sizeof(*matches) * count, GFP_KERNEL);
> +	if (!matches)
> +		return -ENOMEM;
> +
> +	for (i = 0, match = matches; i < count; i++, match++) {
> +		match->name = as3711_regulator_of_names[i];
> +		match->driver_data = as3711_reg_info + i;
> +	}
> +
> +	ret = of_regulator_match(dev->parent, regulators, matches, count);
> +	if (ret < 0) {
> +		dev_err(dev, "Error parsing regulator init data: %d\n", ret);
> +		return ret;
> +	}
> +
> +	for (i = 0, match = matches; i < count; i++, match++)
> +		if (match->of_node)
> +			pdata->init_data[i] = match->init_data;
> +
> +	return 0;
> +}
> +
>  static int as3711_regulator_probe(struct platform_device *pdev)
>  {
>  	struct as3711_regulator_pdata *pdata = dev_get_platdata(&pdev->dev);
> @@ -289,8 +342,18 @@ static int as3711_regulator_probe(struct platform_device *pdev)
>  	int ret;
>  	int id;
>  
> -	if (!pdata)
> -		dev_dbg(&pdev->dev, "No platform data...\n");
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "No platform data...\n");
> +		return -ENODEV;
> +	}
> +
> +	if (pdev->dev.parent->of_node) {
> +		ret = as3711_regulator_parse_dt(&pdev->dev);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
> +			return ret;
> +		}
> +	}
>  
>  	regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM *
>  			sizeof(struct as3711_regulator), GFP_KERNEL);
> @@ -300,7 +363,7 @@ static int as3711_regulator_probe(struct platform_device *pdev)
>  	}
>  
>  	for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) {
> -		reg_data = pdata ? pdata->init_data[id] : NULL;
> +		reg_data = pdata->init_data[id];
>  
>  		/* No need to register if there is no regulator data */
>  		if (!reg_data)
> diff --git a/drivers/video/backlight/as3711_bl.c b/drivers/video/backlight/as3711_bl.c
> index c6bc65d..90c9208 100644
> --- a/drivers/video/backlight/as3711_bl.c
> +++ b/drivers/video/backlight/as3711_bl.c
> @@ -257,6 +257,109 @@ static int as3711_bl_register(struct platform_device *pdev,
>  	return 0;
>  }
>  
> +static int as3711_backlight_parse_dt(struct device *dev)
> +{
> +	struct as3711_bl_pdata *pdata = dev_get_platdata(dev);
> +	struct device_node *bl > +		of_find_node_by_name(dev->parent->of_node, "backlight"), *fb;
> +	int ret;
> +
> +	if (!bl) {
> +		dev_dbg(dev, "backlight node not found\n");
> +		return -ENODEV;
> +	}
> +
> +	fb = of_parse_phandle(bl, "su1-dev", 0);
> +	if (fb) {
> +		pdata->su1_fb = fb->full_name;
> +
> +		ret = of_property_read_u32(bl, "su1-max-uA", &pdata->su1_max_uA);
> +		if (pdata->su1_max_uA <= 0)
> +			ret = -EINVAL;
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	fb = of_parse_phandle(bl, "su2-dev", 0);
> +	if (fb) {
> +		int count = 0;
> +
> +		pdata->su2_fb = fb->full_name;
> +
> +		ret = of_property_read_u32(bl, "su2-max-uA", &pdata->su2_max_uA);
> +		if (pdata->su2_max_uA <= 0)
> +			ret = -EINVAL;
> +		if (ret < 0)
> +			return ret;
> +
> +		if (of_find_property(bl, "su2-feedback-voltage", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_VOLTAGE;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr1", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR1;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr2", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR2;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr3", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR3;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr-auto", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR_AUTO;
> +			count++;
> +		}
> +		if (count != 1)
> +			return -EINVAL;
> +
> +		count = 0;
> +		if (of_find_property(bl, "su2-fbprot-lx-sd4", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_LX_SD4;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-fbprot-gpio2", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_GPIO2;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-fbprot-gpio3", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_GPIO3;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-fbprot-gpio4", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_GPIO4;
> +			count++;
> +		}
> +		if (count != 1)
> +			return -EINVAL;
> +
> +		count = 0;
> +		if (of_find_property(bl, "su2-auto-curr1", NULL)) {
> +			pdata->su2_auto_curr1 = true;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-auto-curr2", NULL)) {
> +			pdata->su2_auto_curr2 = true;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-auto-curr3", NULL)) {
> +			pdata->su2_auto_curr3 = true;
> +			count++;
> +		}
> +
> +		/*
> +		 * At least one su2-auto-curr* must be specified iff
> +		 * AS3711_SU2_CURR_AUTO is used
> +		 */
> +		if (!count ^ pdata->su2_feedback != AS3711_SU2_CURR_AUTO)
> +			return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
>  static int as3711_backlight_probe(struct platform_device *pdev)
>  {
>  	struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev);
> @@ -266,11 +369,24 @@ static int as3711_backlight_probe(struct platform_device *pdev)
>  	unsigned int max_brightness;
>  	int ret;
>  
> -	if (!pdata || (!pdata->su1_fb && !pdata->su2_fb)) {
> +	if (!pdata) {
>  		dev_err(&pdev->dev, "No platform data, exiting...\n");
>  		return -ENODEV;
>  	}
>  
> +	if (pdev->dev.parent->of_node) {
> +		ret = as3711_backlight_parse_dt(&pdev->dev);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	if (!pdata->su1_fb && !pdata->su2_fb) {
> +		dev_err(&pdev->dev, "No framebuffer specified\n");
> +		return -EINVAL;
> +	}
> +
>  	/*
>  	 * Due to possible hardware damage I chose to block all modes,
>  	 * unsupported on my hardware. Anyone, wishing to use any of those modes
> -- 
> 1.7.2.5
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH/RFC] mfd: as3711: add OF support
@ 2013-02-16  5:54   ` Simon Horman
  0 siblings, 0 replies; 16+ messages in thread
From: Simon Horman @ 2013-02-16  5:54 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-kernel, Magnus Damm, devicetree-discuss, Samuel Ortiz,
	Mark Brown, Liam Girdwood, Richard Purdie, Andrew Morton,
	linux-fbdev

On Fri, Feb 15, 2013 at 11:07:16AM +0100, Guennadi Liakhovetski wrote:
> Add device-tree bindings to the AS3711 regulator and backlight drivers.
> 
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> ---
> 
> As usual - comments to the new bindings are very welcome!
> 
>  Documentation/devicetree/bindings/mfd/as3711.txt |   73 +++++++++++++
>  drivers/mfd/as3711.c                             |   30 +++++-
>  drivers/regulator/as3711-regulator.c             |   69 ++++++++++++-
>  drivers/video/backlight/as3711_bl.c              |  118 +++++++++++++++++++++-
>  4 files changed, 282 insertions(+), 8 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/mfd/as3711.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/as3711.txt b/Documentation/devicetree/bindings/mfd/as3711.txt
> new file mode 100644
> index 0000000..d98cf18
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/as3711.txt
> @@ -0,0 +1,73 @@
> +AS3711 is an I2C PMIC from Austria MicroSystems with multiple DCDC and LDO power
> +supplies, a battery charger and an RTC. So far only bindings for the two stepup
> +DCDC converters are defined. Other DCDC and LDO supplies are configured, using
> +standard regulator properties, they must belong to a sub-node, called
> +"regulators" and be called "sd1" to "sd4" and "ldo1" to "ldo8." Stepup converter
> +configuration should be placed in a subnode, called "backlight."
> +
> +Compulsory properties:
> +- compatible		: must be "ams,as3711"
> +- reg			: specifies the I2C address
> +
> +To use the SU1 converter as a backlight source the following two properties must
> +be provided:
> +- su1-dev		: framebuffer phandle
> +- su1-max-uA		: maximum current
> +
> +To use the SU2 converter as a backlight source the following two properties must
> +be provided:
> +- su2-dev		: framebuffer phandle
> +- su1-max-uA		: maximum current
> +
> +Additionally one of these properties must be provided to select the type of
> +feedback used:
> +- su2-feedback-voltage	: voltage feedback is used
> +- su2-feedback-curr1	: CURR1 input used for current feedback
> +- su2-feedback-curr2	: CURR2 input used for current feedback
> +- su2-feedback-curr3	: CURR3 input used for current feedback
> +- su2-feedback-curr-auto: automatic current feedback selection
> +
> +and one of these to select the over-voltage protection pin
> +- su2-fbprot-lx-sd4	: LX_SD4 is used for over-voltage protection
> +- su2-fbprot-gpio2	: GPIO2 is used for over-voltage protection
> +- su2-fbprot-gpio3	: GPIO3 is used for over-voltage protection
> +- su2-fbprot-gpio4	: GPIO4 is used for over-voltage protection
> +
> +If "su2-feedback-curr-auto" is selected, one or more of the following properties
> +have to be specified:
> +- su2-auto-curr1	: use CURR1 input for current feedback
> +- su2-auto-curr2	: use CURR2 input for current feedback
> +- su2-auto-curr3	: use CURR3 input for current feedback
> +
> +Example:
> +
> +as3711@40 {
> +	compatible = "ams,as3711";
> +	reg = <0x40>;
> +
> +	regulators {
> +		sd4 {
> +			regulator-name = "1.215V";
> +			regulator-min-microvolt = <1215000>;
> +			regulator-max-microvolt = <1235000>;
> +		};
> +		ldo2 {
> +			regulator-name = "2.8V CPU";
> +			regulator-min-microvolt = <2800000>;
> +			regulator-max-microvolt = <2800000>;
> +			regulator-always-on;
> +			regulator-boot-on;
> +		};
> +	};
> +
> +	backlight {
> +		compatible = "ams,as3711-bl";
> +		su2-dev = <&lcdc>;
> +		su2-max-uA = <36000>;
> +		su2-feedback-curr-auto;
> +		su2-fbprot-gpio4;
> +		su2-auto-curr1;
> +		su2-auto-curr2;
> +		su2-auto-curr3;
> +	};
> +};
> diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c
> index e994c96..5e0e8b3 100644
> --- a/drivers/mfd/as3711.c
> +++ b/drivers/mfd/as3711.c
> @@ -112,16 +112,37 @@ static const struct regmap_config as3711_regmap_config = {
>  	.cache_type = REGCACHE_RBTREE,
>  };
>  
> +#ifdef CONFIG_OF
> +static struct of_device_id as3711_of_match[] = {
> +	{.compatible = "ams,as3711",},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, as3711_of_match);
> +#endif
> +
>  static int as3711_i2c_probe(struct i2c_client *client,
>  			    const struct i2c_device_id *id)
>  {
>  	struct as3711 *as3711;
> -	struct as3711_platform_data *pdata = client->dev.platform_data;
> +	struct as3711_platform_data *pdata;
>  	unsigned int id1, id2;
>  	int ret;
>  
> -	if (!pdata)
> -		dev_dbg(&client->dev, "Platform data not found\n");
> +	if (!client->dev.of_node) {
> +		pdata = client->dev.platform_data;
> +		if (!pdata)
> +			dev_dbg(&client->dev, "Platform data not found\n");
> +	} else {
> +		if (!of_device_is_available(client->dev.of_node))
> +			return -ENODEV;

The wonder that is kbuild tells me that the above call to
of_device_is_available() may cause a build-time error.
Perhaps it or this entire else clause should be guarded by CONFIG_OF?

> +
> +		pdata = devm_kzalloc(&client->dev,
> +				     sizeof(*pdata), GFP_KERNEL);
> +		if (!pdata) {
> +			dev_err(&client->dev, "Failed to allocate pdata\n");
> +			return -ENOMEM;
> +		}
> +	}
>  
>  	as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
>  	if (!as3711) {
> @@ -193,7 +214,8 @@ static struct i2c_driver as3711_i2c_driver = {
>  	.driver = {
>  		   .name = "as3711",
>  		   .owner = THIS_MODULE,
> -		   },
> +		   .of_match_table = of_match_ptr(as3711_of_match),
> +	},
>  	.probe = as3711_i2c_probe,
>  	.remove = as3711_i2c_remove,
>  	.id_table = as3711_i2c_id,
> diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c
> index f0ba8c4..cf145fc 100644
> --- a/drivers/regulator/as3711-regulator.c
> +++ b/drivers/regulator/as3711-regulator.c
> @@ -13,9 +13,11 @@
>  #include <linux/init.h>
>  #include <linux/mfd/as3711.h>
>  #include <linux/module.h>
> +#include <linux/of.h>
>  #include <linux/platform_device.h>
>  #include <linux/regmap.h>
>  #include <linux/regulator/driver.h>
> +#include <linux/regulator/of_regulator.h>
>  #include <linux/slab.h>
>  
>  struct as3711_regulator_info {
> @@ -276,6 +278,57 @@ static struct as3711_regulator_info as3711_reg_info[] = {
>  
>  #define AS3711_REGULATOR_NUM ARRAY_SIZE(as3711_reg_info)
>  
> +static const char *as3711_regulator_of_names[AS3711_REGULATOR_NUM] = {
> +	[AS3711_REGULATOR_SD_1] = "sd1",
> +	[AS3711_REGULATOR_SD_2] = "sd2",
> +	[AS3711_REGULATOR_SD_3] = "sd3",
> +	[AS3711_REGULATOR_SD_4] = "sd4",
> +	[AS3711_REGULATOR_LDO_1] = "ldo1",
> +	[AS3711_REGULATOR_LDO_2] = "ldo2",
> +	[AS3711_REGULATOR_LDO_3] = "ldo3",
> +	[AS3711_REGULATOR_LDO_4] = "ldo4",
> +	[AS3711_REGULATOR_LDO_5] = "ldo5",
> +	[AS3711_REGULATOR_LDO_6] = "ldo6",
> +	[AS3711_REGULATOR_LDO_7] = "ldo7",
> +	[AS3711_REGULATOR_LDO_8] = "ldo8",
> +};
> +
> +static int as3711_regulator_parse_dt(struct device *dev)
> +{
> +	struct as3711_regulator_pdata *pdata = dev_get_platdata(dev);
> +	struct device_node *regulators =
> +		of_find_node_by_name(dev->parent->of_node, "regulators");
> +	struct of_regulator_match *matches, *match;
> +	const int count = AS3711_REGULATOR_NUM;
> +	int ret, i;
> +
> +	if (!regulators) {
> +		dev_err(dev, "regulator node not found\n");
> +		return -ENODEV;
> +	}
> +
> +	matches = devm_kzalloc(dev, sizeof(*matches) * count, GFP_KERNEL);
> +	if (!matches)
> +		return -ENOMEM;
> +
> +	for (i = 0, match = matches; i < count; i++, match++) {
> +		match->name = as3711_regulator_of_names[i];
> +		match->driver_data = as3711_reg_info + i;
> +	}
> +
> +	ret = of_regulator_match(dev->parent, regulators, matches, count);
> +	if (ret < 0) {
> +		dev_err(dev, "Error parsing regulator init data: %d\n", ret);
> +		return ret;
> +	}
> +
> +	for (i = 0, match = matches; i < count; i++, match++)
> +		if (match->of_node)
> +			pdata->init_data[i] = match->init_data;
> +
> +	return 0;
> +}
> +
>  static int as3711_regulator_probe(struct platform_device *pdev)
>  {
>  	struct as3711_regulator_pdata *pdata = dev_get_platdata(&pdev->dev);
> @@ -289,8 +342,18 @@ static int as3711_regulator_probe(struct platform_device *pdev)
>  	int ret;
>  	int id;
>  
> -	if (!pdata)
> -		dev_dbg(&pdev->dev, "No platform data...\n");
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "No platform data...\n");
> +		return -ENODEV;
> +	}
> +
> +	if (pdev->dev.parent->of_node) {
> +		ret = as3711_regulator_parse_dt(&pdev->dev);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
> +			return ret;
> +		}
> +	}
>  
>  	regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM *
>  			sizeof(struct as3711_regulator), GFP_KERNEL);
> @@ -300,7 +363,7 @@ static int as3711_regulator_probe(struct platform_device *pdev)
>  	}
>  
>  	for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) {
> -		reg_data = pdata ? pdata->init_data[id] : NULL;
> +		reg_data = pdata->init_data[id];
>  
>  		/* No need to register if there is no regulator data */
>  		if (!reg_data)
> diff --git a/drivers/video/backlight/as3711_bl.c b/drivers/video/backlight/as3711_bl.c
> index c6bc65d..90c9208 100644
> --- a/drivers/video/backlight/as3711_bl.c
> +++ b/drivers/video/backlight/as3711_bl.c
> @@ -257,6 +257,109 @@ static int as3711_bl_register(struct platform_device *pdev,
>  	return 0;
>  }
>  
> +static int as3711_backlight_parse_dt(struct device *dev)
> +{
> +	struct as3711_bl_pdata *pdata = dev_get_platdata(dev);
> +	struct device_node *bl =
> +		of_find_node_by_name(dev->parent->of_node, "backlight"), *fb;
> +	int ret;
> +
> +	if (!bl) {
> +		dev_dbg(dev, "backlight node not found\n");
> +		return -ENODEV;
> +	}
> +
> +	fb = of_parse_phandle(bl, "su1-dev", 0);
> +	if (fb) {
> +		pdata->su1_fb = fb->full_name;
> +
> +		ret = of_property_read_u32(bl, "su1-max-uA", &pdata->su1_max_uA);
> +		if (pdata->su1_max_uA <= 0)
> +			ret = -EINVAL;
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	fb = of_parse_phandle(bl, "su2-dev", 0);
> +	if (fb) {
> +		int count = 0;
> +
> +		pdata->su2_fb = fb->full_name;
> +
> +		ret = of_property_read_u32(bl, "su2-max-uA", &pdata->su2_max_uA);
> +		if (pdata->su2_max_uA <= 0)
> +			ret = -EINVAL;
> +		if (ret < 0)
> +			return ret;
> +
> +		if (of_find_property(bl, "su2-feedback-voltage", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_VOLTAGE;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr1", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR1;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr2", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR2;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr3", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR3;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr-auto", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR_AUTO;
> +			count++;
> +		}
> +		if (count != 1)
> +			return -EINVAL;
> +
> +		count = 0;
> +		if (of_find_property(bl, "su2-fbprot-lx-sd4", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_LX_SD4;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-fbprot-gpio2", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_GPIO2;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-fbprot-gpio3", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_GPIO3;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-fbprot-gpio4", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_GPIO4;
> +			count++;
> +		}
> +		if (count != 1)
> +			return -EINVAL;
> +
> +		count = 0;
> +		if (of_find_property(bl, "su2-auto-curr1", NULL)) {
> +			pdata->su2_auto_curr1 = true;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-auto-curr2", NULL)) {
> +			pdata->su2_auto_curr2 = true;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-auto-curr3", NULL)) {
> +			pdata->su2_auto_curr3 = true;
> +			count++;
> +		}
> +
> +		/*
> +		 * At least one su2-auto-curr* must be specified iff
> +		 * AS3711_SU2_CURR_AUTO is used
> +		 */
> +		if (!count ^ pdata->su2_feedback != AS3711_SU2_CURR_AUTO)
> +			return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
>  static int as3711_backlight_probe(struct platform_device *pdev)
>  {
>  	struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev);
> @@ -266,11 +369,24 @@ static int as3711_backlight_probe(struct platform_device *pdev)
>  	unsigned int max_brightness;
>  	int ret;
>  
> -	if (!pdata || (!pdata->su1_fb && !pdata->su2_fb)) {
> +	if (!pdata) {
>  		dev_err(&pdev->dev, "No platform data, exiting...\n");
>  		return -ENODEV;
>  	}
>  
> +	if (pdev->dev.parent->of_node) {
> +		ret = as3711_backlight_parse_dt(&pdev->dev);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	if (!pdata->su1_fb && !pdata->su2_fb) {
> +		dev_err(&pdev->dev, "No framebuffer specified\n");
> +		return -EINVAL;
> +	}
> +
>  	/*
>  	 * Due to possible hardware damage I chose to block all modes,
>  	 * unsupported on my hardware. Anyone, wishing to use any of those modes
> -- 
> 1.7.2.5
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH/RFC] mfd: as3711: add OF support
@ 2013-02-16  5:54   ` Simon Horman
  0 siblings, 0 replies; 16+ messages in thread
From: Simon Horman @ 2013-02-16  5:54 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-fbdev-u79uwXL29TY76Z2rM5mHXA, Samuel Ortiz,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Mark Brown,
	Magnus Damm, linux-kernel-u79uwXL29TY76Z2rM5mHXA, Richard Purdie,
	Andrew Morton, Liam Girdwood

On Fri, Feb 15, 2013 at 11:07:16AM +0100, Guennadi Liakhovetski wrote:
> Add device-tree bindings to the AS3711 regulator and backlight drivers.
> 
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski-Mmb7MZpHnFY@public.gmane.org>
> ---
> 
> As usual - comments to the new bindings are very welcome!
> 
>  Documentation/devicetree/bindings/mfd/as3711.txt |   73 +++++++++++++
>  drivers/mfd/as3711.c                             |   30 +++++-
>  drivers/regulator/as3711-regulator.c             |   69 ++++++++++++-
>  drivers/video/backlight/as3711_bl.c              |  118 +++++++++++++++++++++-
>  4 files changed, 282 insertions(+), 8 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/mfd/as3711.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/as3711.txt b/Documentation/devicetree/bindings/mfd/as3711.txt
> new file mode 100644
> index 0000000..d98cf18
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/as3711.txt
> @@ -0,0 +1,73 @@
> +AS3711 is an I2C PMIC from Austria MicroSystems with multiple DCDC and LDO power
> +supplies, a battery charger and an RTC. So far only bindings for the two stepup
> +DCDC converters are defined. Other DCDC and LDO supplies are configured, using
> +standard regulator properties, they must belong to a sub-node, called
> +"regulators" and be called "sd1" to "sd4" and "ldo1" to "ldo8." Stepup converter
> +configuration should be placed in a subnode, called "backlight."
> +
> +Compulsory properties:
> +- compatible		: must be "ams,as3711"
> +- reg			: specifies the I2C address
> +
> +To use the SU1 converter as a backlight source the following two properties must
> +be provided:
> +- su1-dev		: framebuffer phandle
> +- su1-max-uA		: maximum current
> +
> +To use the SU2 converter as a backlight source the following two properties must
> +be provided:
> +- su2-dev		: framebuffer phandle
> +- su1-max-uA		: maximum current
> +
> +Additionally one of these properties must be provided to select the type of
> +feedback used:
> +- su2-feedback-voltage	: voltage feedback is used
> +- su2-feedback-curr1	: CURR1 input used for current feedback
> +- su2-feedback-curr2	: CURR2 input used for current feedback
> +- su2-feedback-curr3	: CURR3 input used for current feedback
> +- su2-feedback-curr-auto: automatic current feedback selection
> +
> +and one of these to select the over-voltage protection pin
> +- su2-fbprot-lx-sd4	: LX_SD4 is used for over-voltage protection
> +- su2-fbprot-gpio2	: GPIO2 is used for over-voltage protection
> +- su2-fbprot-gpio3	: GPIO3 is used for over-voltage protection
> +- su2-fbprot-gpio4	: GPIO4 is used for over-voltage protection
> +
> +If "su2-feedback-curr-auto" is selected, one or more of the following properties
> +have to be specified:
> +- su2-auto-curr1	: use CURR1 input for current feedback
> +- su2-auto-curr2	: use CURR2 input for current feedback
> +- su2-auto-curr3	: use CURR3 input for current feedback
> +
> +Example:
> +
> +as3711@40 {
> +	compatible = "ams,as3711";
> +	reg = <0x40>;
> +
> +	regulators {
> +		sd4 {
> +			regulator-name = "1.215V";
> +			regulator-min-microvolt = <1215000>;
> +			regulator-max-microvolt = <1235000>;
> +		};
> +		ldo2 {
> +			regulator-name = "2.8V CPU";
> +			regulator-min-microvolt = <2800000>;
> +			regulator-max-microvolt = <2800000>;
> +			regulator-always-on;
> +			regulator-boot-on;
> +		};
> +	};
> +
> +	backlight {
> +		compatible = "ams,as3711-bl";
> +		su2-dev = <&lcdc>;
> +		su2-max-uA = <36000>;
> +		su2-feedback-curr-auto;
> +		su2-fbprot-gpio4;
> +		su2-auto-curr1;
> +		su2-auto-curr2;
> +		su2-auto-curr3;
> +	};
> +};
> diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c
> index e994c96..5e0e8b3 100644
> --- a/drivers/mfd/as3711.c
> +++ b/drivers/mfd/as3711.c
> @@ -112,16 +112,37 @@ static const struct regmap_config as3711_regmap_config = {
>  	.cache_type = REGCACHE_RBTREE,
>  };
>  
> +#ifdef CONFIG_OF
> +static struct of_device_id as3711_of_match[] = {
> +	{.compatible = "ams,as3711",},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, as3711_of_match);
> +#endif
> +
>  static int as3711_i2c_probe(struct i2c_client *client,
>  			    const struct i2c_device_id *id)
>  {
>  	struct as3711 *as3711;
> -	struct as3711_platform_data *pdata = client->dev.platform_data;
> +	struct as3711_platform_data *pdata;
>  	unsigned int id1, id2;
>  	int ret;
>  
> -	if (!pdata)
> -		dev_dbg(&client->dev, "Platform data not found\n");
> +	if (!client->dev.of_node) {
> +		pdata = client->dev.platform_data;
> +		if (!pdata)
> +			dev_dbg(&client->dev, "Platform data not found\n");
> +	} else {
> +		if (!of_device_is_available(client->dev.of_node))
> +			return -ENODEV;

The wonder that is kbuild tells me that the above call to
of_device_is_available() may cause a build-time error.
Perhaps it or this entire else clause should be guarded by CONFIG_OF?

> +
> +		pdata = devm_kzalloc(&client->dev,
> +				     sizeof(*pdata), GFP_KERNEL);
> +		if (!pdata) {
> +			dev_err(&client->dev, "Failed to allocate pdata\n");
> +			return -ENOMEM;
> +		}
> +	}
>  
>  	as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
>  	if (!as3711) {
> @@ -193,7 +214,8 @@ static struct i2c_driver as3711_i2c_driver = {
>  	.driver = {
>  		   .name = "as3711",
>  		   .owner = THIS_MODULE,
> -		   },
> +		   .of_match_table = of_match_ptr(as3711_of_match),
> +	},
>  	.probe = as3711_i2c_probe,
>  	.remove = as3711_i2c_remove,
>  	.id_table = as3711_i2c_id,
> diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c
> index f0ba8c4..cf145fc 100644
> --- a/drivers/regulator/as3711-regulator.c
> +++ b/drivers/regulator/as3711-regulator.c
> @@ -13,9 +13,11 @@
>  #include <linux/init.h>
>  #include <linux/mfd/as3711.h>
>  #include <linux/module.h>
> +#include <linux/of.h>
>  #include <linux/platform_device.h>
>  #include <linux/regmap.h>
>  #include <linux/regulator/driver.h>
> +#include <linux/regulator/of_regulator.h>
>  #include <linux/slab.h>
>  
>  struct as3711_regulator_info {
> @@ -276,6 +278,57 @@ static struct as3711_regulator_info as3711_reg_info[] = {
>  
>  #define AS3711_REGULATOR_NUM ARRAY_SIZE(as3711_reg_info)
>  
> +static const char *as3711_regulator_of_names[AS3711_REGULATOR_NUM] = {
> +	[AS3711_REGULATOR_SD_1] = "sd1",
> +	[AS3711_REGULATOR_SD_2] = "sd2",
> +	[AS3711_REGULATOR_SD_3] = "sd3",
> +	[AS3711_REGULATOR_SD_4] = "sd4",
> +	[AS3711_REGULATOR_LDO_1] = "ldo1",
> +	[AS3711_REGULATOR_LDO_2] = "ldo2",
> +	[AS3711_REGULATOR_LDO_3] = "ldo3",
> +	[AS3711_REGULATOR_LDO_4] = "ldo4",
> +	[AS3711_REGULATOR_LDO_5] = "ldo5",
> +	[AS3711_REGULATOR_LDO_6] = "ldo6",
> +	[AS3711_REGULATOR_LDO_7] = "ldo7",
> +	[AS3711_REGULATOR_LDO_8] = "ldo8",
> +};
> +
> +static int as3711_regulator_parse_dt(struct device *dev)
> +{
> +	struct as3711_regulator_pdata *pdata = dev_get_platdata(dev);
> +	struct device_node *regulators =
> +		of_find_node_by_name(dev->parent->of_node, "regulators");
> +	struct of_regulator_match *matches, *match;
> +	const int count = AS3711_REGULATOR_NUM;
> +	int ret, i;
> +
> +	if (!regulators) {
> +		dev_err(dev, "regulator node not found\n");
> +		return -ENODEV;
> +	}
> +
> +	matches = devm_kzalloc(dev, sizeof(*matches) * count, GFP_KERNEL);
> +	if (!matches)
> +		return -ENOMEM;
> +
> +	for (i = 0, match = matches; i < count; i++, match++) {
> +		match->name = as3711_regulator_of_names[i];
> +		match->driver_data = as3711_reg_info + i;
> +	}
> +
> +	ret = of_regulator_match(dev->parent, regulators, matches, count);
> +	if (ret < 0) {
> +		dev_err(dev, "Error parsing regulator init data: %d\n", ret);
> +		return ret;
> +	}
> +
> +	for (i = 0, match = matches; i < count; i++, match++)
> +		if (match->of_node)
> +			pdata->init_data[i] = match->init_data;
> +
> +	return 0;
> +}
> +
>  static int as3711_regulator_probe(struct platform_device *pdev)
>  {
>  	struct as3711_regulator_pdata *pdata = dev_get_platdata(&pdev->dev);
> @@ -289,8 +342,18 @@ static int as3711_regulator_probe(struct platform_device *pdev)
>  	int ret;
>  	int id;
>  
> -	if (!pdata)
> -		dev_dbg(&pdev->dev, "No platform data...\n");
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "No platform data...\n");
> +		return -ENODEV;
> +	}
> +
> +	if (pdev->dev.parent->of_node) {
> +		ret = as3711_regulator_parse_dt(&pdev->dev);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
> +			return ret;
> +		}
> +	}
>  
>  	regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM *
>  			sizeof(struct as3711_regulator), GFP_KERNEL);
> @@ -300,7 +363,7 @@ static int as3711_regulator_probe(struct platform_device *pdev)
>  	}
>  
>  	for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) {
> -		reg_data = pdata ? pdata->init_data[id] : NULL;
> +		reg_data = pdata->init_data[id];
>  
>  		/* No need to register if there is no regulator data */
>  		if (!reg_data)
> diff --git a/drivers/video/backlight/as3711_bl.c b/drivers/video/backlight/as3711_bl.c
> index c6bc65d..90c9208 100644
> --- a/drivers/video/backlight/as3711_bl.c
> +++ b/drivers/video/backlight/as3711_bl.c
> @@ -257,6 +257,109 @@ static int as3711_bl_register(struct platform_device *pdev,
>  	return 0;
>  }
>  
> +static int as3711_backlight_parse_dt(struct device *dev)
> +{
> +	struct as3711_bl_pdata *pdata = dev_get_platdata(dev);
> +	struct device_node *bl =
> +		of_find_node_by_name(dev->parent->of_node, "backlight"), *fb;
> +	int ret;
> +
> +	if (!bl) {
> +		dev_dbg(dev, "backlight node not found\n");
> +		return -ENODEV;
> +	}
> +
> +	fb = of_parse_phandle(bl, "su1-dev", 0);
> +	if (fb) {
> +		pdata->su1_fb = fb->full_name;
> +
> +		ret = of_property_read_u32(bl, "su1-max-uA", &pdata->su1_max_uA);
> +		if (pdata->su1_max_uA <= 0)
> +			ret = -EINVAL;
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	fb = of_parse_phandle(bl, "su2-dev", 0);
> +	if (fb) {
> +		int count = 0;
> +
> +		pdata->su2_fb = fb->full_name;
> +
> +		ret = of_property_read_u32(bl, "su2-max-uA", &pdata->su2_max_uA);
> +		if (pdata->su2_max_uA <= 0)
> +			ret = -EINVAL;
> +		if (ret < 0)
> +			return ret;
> +
> +		if (of_find_property(bl, "su2-feedback-voltage", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_VOLTAGE;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr1", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR1;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr2", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR2;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr3", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR3;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr-auto", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR_AUTO;
> +			count++;
> +		}
> +		if (count != 1)
> +			return -EINVAL;
> +
> +		count = 0;
> +		if (of_find_property(bl, "su2-fbprot-lx-sd4", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_LX_SD4;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-fbprot-gpio2", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_GPIO2;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-fbprot-gpio3", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_GPIO3;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-fbprot-gpio4", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_GPIO4;
> +			count++;
> +		}
> +		if (count != 1)
> +			return -EINVAL;
> +
> +		count = 0;
> +		if (of_find_property(bl, "su2-auto-curr1", NULL)) {
> +			pdata->su2_auto_curr1 = true;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-auto-curr2", NULL)) {
> +			pdata->su2_auto_curr2 = true;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-auto-curr3", NULL)) {
> +			pdata->su2_auto_curr3 = true;
> +			count++;
> +		}
> +
> +		/*
> +		 * At least one su2-auto-curr* must be specified iff
> +		 * AS3711_SU2_CURR_AUTO is used
> +		 */
> +		if (!count ^ pdata->su2_feedback != AS3711_SU2_CURR_AUTO)
> +			return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
>  static int as3711_backlight_probe(struct platform_device *pdev)
>  {
>  	struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev);
> @@ -266,11 +369,24 @@ static int as3711_backlight_probe(struct platform_device *pdev)
>  	unsigned int max_brightness;
>  	int ret;
>  
> -	if (!pdata || (!pdata->su1_fb && !pdata->su2_fb)) {
> +	if (!pdata) {
>  		dev_err(&pdev->dev, "No platform data, exiting...\n");
>  		return -ENODEV;
>  	}
>  
> +	if (pdev->dev.parent->of_node) {
> +		ret = as3711_backlight_parse_dt(&pdev->dev);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	if (!pdata->su1_fb && !pdata->su2_fb) {
> +		dev_err(&pdev->dev, "No framebuffer specified\n");
> +		return -EINVAL;
> +	}
> +
>  	/*
>  	 * Due to possible hardware damage I chose to block all modes,
>  	 * unsupported on my hardware. Anyone, wishing to use any of those modes
> -- 
> 1.7.2.5
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH/RFC] mfd: as3711: add OF support
@ 2013-02-16  5:54   ` Simon Horman
  0 siblings, 0 replies; 16+ messages in thread
From: Simon Horman @ 2013-02-16  5:54 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-fbdev-u79uwXL29TY76Z2rM5mHXA, Samuel Ortiz,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Mark Brown,
	Magnus Damm, linux-kernel-u79uwXL29TY76Z2rM5mHXA, Richard Purdie,
	Andrew Morton, Liam Girdwood

On Fri, Feb 15, 2013 at 11:07:16AM +0100, Guennadi Liakhovetski wrote:
> Add device-tree bindings to the AS3711 regulator and backlight drivers.
> 
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> ---
> 
> As usual - comments to the new bindings are very welcome!
> 
>  Documentation/devicetree/bindings/mfd/as3711.txt |   73 +++++++++++++
>  drivers/mfd/as3711.c                             |   30 +++++-
>  drivers/regulator/as3711-regulator.c             |   69 ++++++++++++-
>  drivers/video/backlight/as3711_bl.c              |  118 +++++++++++++++++++++-
>  4 files changed, 282 insertions(+), 8 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/mfd/as3711.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/as3711.txt b/Documentation/devicetree/bindings/mfd/as3711.txt
> new file mode 100644
> index 0000000..d98cf18
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/as3711.txt
> @@ -0,0 +1,73 @@
> +AS3711 is an I2C PMIC from Austria MicroSystems with multiple DCDC and LDO power
> +supplies, a battery charger and an RTC. So far only bindings for the two stepup
> +DCDC converters are defined. Other DCDC and LDO supplies are configured, using
> +standard regulator properties, they must belong to a sub-node, called
> +"regulators" and be called "sd1" to "sd4" and "ldo1" to "ldo8." Stepup converter
> +configuration should be placed in a subnode, called "backlight."
> +
> +Compulsory properties:
> +- compatible		: must be "ams,as3711"
> +- reg			: specifies the I2C address
> +
> +To use the SU1 converter as a backlight source the following two properties must
> +be provided:
> +- su1-dev		: framebuffer phandle
> +- su1-max-uA		: maximum current
> +
> +To use the SU2 converter as a backlight source the following two properties must
> +be provided:
> +- su2-dev		: framebuffer phandle
> +- su1-max-uA		: maximum current
> +
> +Additionally one of these properties must be provided to select the type of
> +feedback used:
> +- su2-feedback-voltage	: voltage feedback is used
> +- su2-feedback-curr1	: CURR1 input used for current feedback
> +- su2-feedback-curr2	: CURR2 input used for current feedback
> +- su2-feedback-curr3	: CURR3 input used for current feedback
> +- su2-feedback-curr-auto: automatic current feedback selection
> +
> +and one of these to select the over-voltage protection pin
> +- su2-fbprot-lx-sd4	: LX_SD4 is used for over-voltage protection
> +- su2-fbprot-gpio2	: GPIO2 is used for over-voltage protection
> +- su2-fbprot-gpio3	: GPIO3 is used for over-voltage protection
> +- su2-fbprot-gpio4	: GPIO4 is used for over-voltage protection
> +
> +If "su2-feedback-curr-auto" is selected, one or more of the following properties
> +have to be specified:
> +- su2-auto-curr1	: use CURR1 input for current feedback
> +- su2-auto-curr2	: use CURR2 input for current feedback
> +- su2-auto-curr3	: use CURR3 input for current feedback
> +
> +Example:
> +
> +as3711@40 {
> +	compatible = "ams,as3711";
> +	reg = <0x40>;
> +
> +	regulators {
> +		sd4 {
> +			regulator-name = "1.215V";
> +			regulator-min-microvolt = <1215000>;
> +			regulator-max-microvolt = <1235000>;
> +		};
> +		ldo2 {
> +			regulator-name = "2.8V CPU";
> +			regulator-min-microvolt = <2800000>;
> +			regulator-max-microvolt = <2800000>;
> +			regulator-always-on;
> +			regulator-boot-on;
> +		};
> +	};
> +
> +	backlight {
> +		compatible = "ams,as3711-bl";
> +		su2-dev = <&lcdc>;
> +		su2-max-uA = <36000>;
> +		su2-feedback-curr-auto;
> +		su2-fbprot-gpio4;
> +		su2-auto-curr1;
> +		su2-auto-curr2;
> +		su2-auto-curr3;
> +	};
> +};
> diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c
> index e994c96..5e0e8b3 100644
> --- a/drivers/mfd/as3711.c
> +++ b/drivers/mfd/as3711.c
> @@ -112,16 +112,37 @@ static const struct regmap_config as3711_regmap_config = {
>  	.cache_type = REGCACHE_RBTREE,
>  };
>  
> +#ifdef CONFIG_OF
> +static struct of_device_id as3711_of_match[] = {
> +	{.compatible = "ams,as3711",},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, as3711_of_match);
> +#endif
> +
>  static int as3711_i2c_probe(struct i2c_client *client,
>  			    const struct i2c_device_id *id)
>  {
>  	struct as3711 *as3711;
> -	struct as3711_platform_data *pdata = client->dev.platform_data;
> +	struct as3711_platform_data *pdata;
>  	unsigned int id1, id2;
>  	int ret;
>  
> -	if (!pdata)
> -		dev_dbg(&client->dev, "Platform data not found\n");
> +	if (!client->dev.of_node) {
> +		pdata = client->dev.platform_data;
> +		if (!pdata)
> +			dev_dbg(&client->dev, "Platform data not found\n");
> +	} else {
> +		if (!of_device_is_available(client->dev.of_node))
> +			return -ENODEV;

The wonder that is kbuild tells me that the above call to
of_device_is_available() may cause a build-time error.
Perhaps it or this entire else clause should be guarded by CONFIG_OF?

> +
> +		pdata = devm_kzalloc(&client->dev,
> +				     sizeof(*pdata), GFP_KERNEL);
> +		if (!pdata) {
> +			dev_err(&client->dev, "Failed to allocate pdata\n");
> +			return -ENOMEM;
> +		}
> +	}
>  
>  	as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
>  	if (!as3711) {
> @@ -193,7 +214,8 @@ static struct i2c_driver as3711_i2c_driver = {
>  	.driver = {
>  		   .name = "as3711",
>  		   .owner = THIS_MODULE,
> -		   },
> +		   .of_match_table = of_match_ptr(as3711_of_match),
> +	},
>  	.probe = as3711_i2c_probe,
>  	.remove = as3711_i2c_remove,
>  	.id_table = as3711_i2c_id,
> diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c
> index f0ba8c4..cf145fc 100644
> --- a/drivers/regulator/as3711-regulator.c
> +++ b/drivers/regulator/as3711-regulator.c
> @@ -13,9 +13,11 @@
>  #include <linux/init.h>
>  #include <linux/mfd/as3711.h>
>  #include <linux/module.h>
> +#include <linux/of.h>
>  #include <linux/platform_device.h>
>  #include <linux/regmap.h>
>  #include <linux/regulator/driver.h>
> +#include <linux/regulator/of_regulator.h>
>  #include <linux/slab.h>
>  
>  struct as3711_regulator_info {
> @@ -276,6 +278,57 @@ static struct as3711_regulator_info as3711_reg_info[] = {
>  
>  #define AS3711_REGULATOR_NUM ARRAY_SIZE(as3711_reg_info)
>  
> +static const char *as3711_regulator_of_names[AS3711_REGULATOR_NUM] = {
> +	[AS3711_REGULATOR_SD_1] = "sd1",
> +	[AS3711_REGULATOR_SD_2] = "sd2",
> +	[AS3711_REGULATOR_SD_3] = "sd3",
> +	[AS3711_REGULATOR_SD_4] = "sd4",
> +	[AS3711_REGULATOR_LDO_1] = "ldo1",
> +	[AS3711_REGULATOR_LDO_2] = "ldo2",
> +	[AS3711_REGULATOR_LDO_3] = "ldo3",
> +	[AS3711_REGULATOR_LDO_4] = "ldo4",
> +	[AS3711_REGULATOR_LDO_5] = "ldo5",
> +	[AS3711_REGULATOR_LDO_6] = "ldo6",
> +	[AS3711_REGULATOR_LDO_7] = "ldo7",
> +	[AS3711_REGULATOR_LDO_8] = "ldo8",
> +};
> +
> +static int as3711_regulator_parse_dt(struct device *dev)
> +{
> +	struct as3711_regulator_pdata *pdata = dev_get_platdata(dev);
> +	struct device_node *regulators > +		of_find_node_by_name(dev->parent->of_node, "regulators");
> +	struct of_regulator_match *matches, *match;
> +	const int count = AS3711_REGULATOR_NUM;
> +	int ret, i;
> +
> +	if (!regulators) {
> +		dev_err(dev, "regulator node not found\n");
> +		return -ENODEV;
> +	}
> +
> +	matches = devm_kzalloc(dev, sizeof(*matches) * count, GFP_KERNEL);
> +	if (!matches)
> +		return -ENOMEM;
> +
> +	for (i = 0, match = matches; i < count; i++, match++) {
> +		match->name = as3711_regulator_of_names[i];
> +		match->driver_data = as3711_reg_info + i;
> +	}
> +
> +	ret = of_regulator_match(dev->parent, regulators, matches, count);
> +	if (ret < 0) {
> +		dev_err(dev, "Error parsing regulator init data: %d\n", ret);
> +		return ret;
> +	}
> +
> +	for (i = 0, match = matches; i < count; i++, match++)
> +		if (match->of_node)
> +			pdata->init_data[i] = match->init_data;
> +
> +	return 0;
> +}
> +
>  static int as3711_regulator_probe(struct platform_device *pdev)
>  {
>  	struct as3711_regulator_pdata *pdata = dev_get_platdata(&pdev->dev);
> @@ -289,8 +342,18 @@ static int as3711_regulator_probe(struct platform_device *pdev)
>  	int ret;
>  	int id;
>  
> -	if (!pdata)
> -		dev_dbg(&pdev->dev, "No platform data...\n");
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "No platform data...\n");
> +		return -ENODEV;
> +	}
> +
> +	if (pdev->dev.parent->of_node) {
> +		ret = as3711_regulator_parse_dt(&pdev->dev);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
> +			return ret;
> +		}
> +	}
>  
>  	regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM *
>  			sizeof(struct as3711_regulator), GFP_KERNEL);
> @@ -300,7 +363,7 @@ static int as3711_regulator_probe(struct platform_device *pdev)
>  	}
>  
>  	for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) {
> -		reg_data = pdata ? pdata->init_data[id] : NULL;
> +		reg_data = pdata->init_data[id];
>  
>  		/* No need to register if there is no regulator data */
>  		if (!reg_data)
> diff --git a/drivers/video/backlight/as3711_bl.c b/drivers/video/backlight/as3711_bl.c
> index c6bc65d..90c9208 100644
> --- a/drivers/video/backlight/as3711_bl.c
> +++ b/drivers/video/backlight/as3711_bl.c
> @@ -257,6 +257,109 @@ static int as3711_bl_register(struct platform_device *pdev,
>  	return 0;
>  }
>  
> +static int as3711_backlight_parse_dt(struct device *dev)
> +{
> +	struct as3711_bl_pdata *pdata = dev_get_platdata(dev);
> +	struct device_node *bl > +		of_find_node_by_name(dev->parent->of_node, "backlight"), *fb;
> +	int ret;
> +
> +	if (!bl) {
> +		dev_dbg(dev, "backlight node not found\n");
> +		return -ENODEV;
> +	}
> +
> +	fb = of_parse_phandle(bl, "su1-dev", 0);
> +	if (fb) {
> +		pdata->su1_fb = fb->full_name;
> +
> +		ret = of_property_read_u32(bl, "su1-max-uA", &pdata->su1_max_uA);
> +		if (pdata->su1_max_uA <= 0)
> +			ret = -EINVAL;
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	fb = of_parse_phandle(bl, "su2-dev", 0);
> +	if (fb) {
> +		int count = 0;
> +
> +		pdata->su2_fb = fb->full_name;
> +
> +		ret = of_property_read_u32(bl, "su2-max-uA", &pdata->su2_max_uA);
> +		if (pdata->su2_max_uA <= 0)
> +			ret = -EINVAL;
> +		if (ret < 0)
> +			return ret;
> +
> +		if (of_find_property(bl, "su2-feedback-voltage", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_VOLTAGE;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr1", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR1;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr2", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR2;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr3", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR3;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-feedback-curr-auto", NULL)) {
> +			pdata->su2_feedback = AS3711_SU2_CURR_AUTO;
> +			count++;
> +		}
> +		if (count != 1)
> +			return -EINVAL;
> +
> +		count = 0;
> +		if (of_find_property(bl, "su2-fbprot-lx-sd4", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_LX_SD4;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-fbprot-gpio2", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_GPIO2;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-fbprot-gpio3", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_GPIO3;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-fbprot-gpio4", NULL)) {
> +			pdata->su2_fbprot = AS3711_SU2_GPIO4;
> +			count++;
> +		}
> +		if (count != 1)
> +			return -EINVAL;
> +
> +		count = 0;
> +		if (of_find_property(bl, "su2-auto-curr1", NULL)) {
> +			pdata->su2_auto_curr1 = true;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-auto-curr2", NULL)) {
> +			pdata->su2_auto_curr2 = true;
> +			count++;
> +		}
> +		if (of_find_property(bl, "su2-auto-curr3", NULL)) {
> +			pdata->su2_auto_curr3 = true;
> +			count++;
> +		}
> +
> +		/*
> +		 * At least one su2-auto-curr* must be specified iff
> +		 * AS3711_SU2_CURR_AUTO is used
> +		 */
> +		if (!count ^ pdata->su2_feedback != AS3711_SU2_CURR_AUTO)
> +			return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
>  static int as3711_backlight_probe(struct platform_device *pdev)
>  {
>  	struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev);
> @@ -266,11 +369,24 @@ static int as3711_backlight_probe(struct platform_device *pdev)
>  	unsigned int max_brightness;
>  	int ret;
>  
> -	if (!pdata || (!pdata->su1_fb && !pdata->su2_fb)) {
> +	if (!pdata) {
>  		dev_err(&pdev->dev, "No platform data, exiting...\n");
>  		return -ENODEV;
>  	}
>  
> +	if (pdev->dev.parent->of_node) {
> +		ret = as3711_backlight_parse_dt(&pdev->dev);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	if (!pdata->su1_fb && !pdata->su2_fb) {
> +		dev_err(&pdev->dev, "No framebuffer specified\n");
> +		return -EINVAL;
> +	}
> +
>  	/*
>  	 * Due to possible hardware damage I chose to block all modes,
>  	 * unsupported on my hardware. Anyone, wishing to use any of those modes
> -- 
> 1.7.2.5
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH/RFC] mfd: as3711: add OF support
@ 2013-02-16  6:13     ` Simon Horman
  0 siblings, 0 replies; 16+ messages in thread
From: Simon Horman @ 2013-02-16  6:13 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-kernel, Magnus Damm, devicetree-discuss, Samuel Ortiz,
	Mark Brown, Liam Girdwood, Richard Purdie, Andrew Morton,
	linux-fbdev

On Sat, Feb 16, 2013 at 11:26:30AM +0900, Simon Horman wrote:
> On Fri, Feb 15, 2013 at 11:07:16AM +0100, Guennadi Liakhovetski wrote:
> > Add device-tree bindings to the AS3711 regulator and backlight drivers.
> 
> Hi,
> 
> at this stage I do not expect this code to go through the renesas tree.
> However, in order to provide a basis for work on renesas SoCs I have added
> this patch to the topic/backlight topic branch in the reneas tree on
> kernel.org and merged it into topic/all+next.
> 
> In other words, I am not picking this patch up to merge it or add it to
> linux-next, rather I am storing it for reference.

I have dropped this patch due to the of_device_is_available build error
reported by kbuild and reported in another sub-thread.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH/RFC] mfd: as3711: add OF support
@ 2013-02-16  6:13     ` Simon Horman
  0 siblings, 0 replies; 16+ messages in thread
From: Simon Horman @ 2013-02-16  6:13 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-fbdev-u79uwXL29TY76Z2rM5mHXA, Samuel Ortiz,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Mark Brown,
	Magnus Damm, linux-kernel-u79uwXL29TY76Z2rM5mHXA, Richard Purdie,
	Andrew Morton, Liam Girdwood

On Sat, Feb 16, 2013 at 11:26:30AM +0900, Simon Horman wrote:
> On Fri, Feb 15, 2013 at 11:07:16AM +0100, Guennadi Liakhovetski wrote:
> > Add device-tree bindings to the AS3711 regulator and backlight drivers.
> 
> Hi,
> 
> at this stage I do not expect this code to go through the renesas tree.
> However, in order to provide a basis for work on renesas SoCs I have added
> this patch to the topic/backlight topic branch in the reneas tree on
> kernel.org and merged it into topic/all+next.
> 
> In other words, I am not picking this patch up to merge it or add it to
> linux-next, rather I am storing it for reference.

I have dropped this patch due to the of_device_is_available build error
reported by kbuild and reported in another sub-thread.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH/RFC] mfd: as3711: add OF support
@ 2013-02-16  6:13     ` Simon Horman
  0 siblings, 0 replies; 16+ messages in thread
From: Simon Horman @ 2013-02-16  6:13 UTC (permalink / raw)
  To: Guennadi Liakhovetski
  Cc: linux-fbdev-u79uwXL29TY76Z2rM5mHXA, Samuel Ortiz,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Mark Brown,
	Magnus Damm, linux-kernel-u79uwXL29TY76Z2rM5mHXA, Richard Purdie,
	Andrew Morton, Liam Girdwood

On Sat, Feb 16, 2013 at 11:26:30AM +0900, Simon Horman wrote:
> On Fri, Feb 15, 2013 at 11:07:16AM +0100, Guennadi Liakhovetski wrote:
> > Add device-tree bindings to the AS3711 regulator and backlight drivers.
> 
> Hi,
> 
> at this stage I do not expect this code to go through the renesas tree.
> However, in order to provide a basis for work on renesas SoCs I have added
> this patch to the topic/backlight topic branch in the reneas tree on
> kernel.org and merged it into topic/all+next.
> 
> In other words, I am not picking this patch up to merge it or add it to
> linux-next, rather I am storing it for reference.

I have dropped this patch due to the of_device_is_available build error
reported by kbuild and reported in another sub-thread.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v2] mfd: as3711: add OF support
@ 2013-02-25 11:26   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 16+ messages in thread
From: Guennadi Liakhovetski @ 2013-02-25 11:26 UTC (permalink / raw)
  To: linux-kernel
  Cc: Magnus Damm, Simon Horman, devicetree-discuss, Samuel Ortiz,
	Mark Brown, Liam Girdwood, Richard Purdie, Andrew Morton,
	linux-fbdev

Add device-tree bindings to the AS3711 regulator and backlight drivers.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---

v2:
1. remove a redundant of_device_is_available() check, this also eliminates 
   a compile breakage
2. add .of_node regulator configuration field initialisation
3. add parenthesis to silence compiler warnings

 Documentation/devicetree/bindings/mfd/as3711.txt |   73 +++++++++++++
 drivers/mfd/as3711.c                             |   27 ++++-
 drivers/regulator/as3711-regulator.c             |   74 +++++++++++++-
 drivers/video/backlight/as3711_bl.c              |  118 +++++++++++++++++++++-
 4 files changed, 284 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mfd/as3711.txt

diff --git a/Documentation/devicetree/bindings/mfd/as3711.txt b/Documentation/devicetree/bindings/mfd/as3711.txt
new file mode 100644
index 0000000..d98cf18
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/as3711.txt
@@ -0,0 +1,73 @@
+AS3711 is an I2C PMIC from Austria MicroSystems with multiple DCDC and LDO power
+supplies, a battery charger and an RTC. So far only bindings for the two stepup
+DCDC converters are defined. Other DCDC and LDO supplies are configured, using
+standard regulator properties, they must belong to a sub-node, called
+"regulators" and be called "sd1" to "sd4" and "ldo1" to "ldo8." Stepup converter
+configuration should be placed in a subnode, called "backlight."
+
+Compulsory properties:
+- compatible		: must be "ams,as3711"
+- reg			: specifies the I2C address
+
+To use the SU1 converter as a backlight source the following two properties must
+be provided:
+- su1-dev		: framebuffer phandle
+- su1-max-uA		: maximum current
+
+To use the SU2 converter as a backlight source the following two properties must
+be provided:
+- su2-dev		: framebuffer phandle
+- su1-max-uA		: maximum current
+
+Additionally one of these properties must be provided to select the type of
+feedback used:
+- su2-feedback-voltage	: voltage feedback is used
+- su2-feedback-curr1	: CURR1 input used for current feedback
+- su2-feedback-curr2	: CURR2 input used for current feedback
+- su2-feedback-curr3	: CURR3 input used for current feedback
+- su2-feedback-curr-auto: automatic current feedback selection
+
+and one of these to select the over-voltage protection pin
+- su2-fbprot-lx-sd4	: LX_SD4 is used for over-voltage protection
+- su2-fbprot-gpio2	: GPIO2 is used for over-voltage protection
+- su2-fbprot-gpio3	: GPIO3 is used for over-voltage protection
+- su2-fbprot-gpio4	: GPIO4 is used for over-voltage protection
+
+If "su2-feedback-curr-auto" is selected, one or more of the following properties
+have to be specified:
+- su2-auto-curr1	: use CURR1 input for current feedback
+- su2-auto-curr2	: use CURR2 input for current feedback
+- su2-auto-curr3	: use CURR3 input for current feedback
+
+Example:
+
+as3711@40 {
+	compatible = "ams,as3711";
+	reg = <0x40>;
+
+	regulators {
+		sd4 {
+			regulator-name = "1.215V";
+			regulator-min-microvolt = <1215000>;
+			regulator-max-microvolt = <1235000>;
+		};
+		ldo2 {
+			regulator-name = "2.8V CPU";
+			regulator-min-microvolt = <2800000>;
+			regulator-max-microvolt = <2800000>;
+			regulator-always-on;
+			regulator-boot-on;
+		};
+	};
+
+	backlight {
+		compatible = "ams,as3711-bl";
+		su2-dev = <&lcdc>;
+		su2-max-uA = <36000>;
+		su2-feedback-curr-auto;
+		su2-fbprot-gpio4;
+		su2-auto-curr1;
+		su2-auto-curr2;
+		su2-auto-curr3;
+	};
+};
diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c
index e994c96..01e4141 100644
--- a/drivers/mfd/as3711.c
+++ b/drivers/mfd/as3711.c
@@ -112,16 +112,34 @@ static const struct regmap_config as3711_regmap_config = {
 	.cache_type = REGCACHE_RBTREE,
 };
 
+#ifdef CONFIG_OF
+static struct of_device_id as3711_of_match[] = {
+	{.compatible = "ams,as3711",},
+	{}
+};
+MODULE_DEVICE_TABLE(of, as3711_of_match);
+#endif
+
 static int as3711_i2c_probe(struct i2c_client *client,
 			    const struct i2c_device_id *id)
 {
 	struct as3711 *as3711;
-	struct as3711_platform_data *pdata = client->dev.platform_data;
+	struct as3711_platform_data *pdata;
 	unsigned int id1, id2;
 	int ret;
 
-	if (!pdata)
-		dev_dbg(&client->dev, "Platform data not found\n");
+	if (!client->dev.of_node) {
+		pdata = client->dev.platform_data;
+		if (!pdata)
+			dev_dbg(&client->dev, "Platform data not found\n");
+	} else {
+		pdata = devm_kzalloc(&client->dev,
+				     sizeof(*pdata), GFP_KERNEL);
+		if (!pdata) {
+			dev_err(&client->dev, "Failed to allocate pdata\n");
+			return -ENOMEM;
+		}
+	}
 
 	as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
 	if (!as3711) {
@@ -193,7 +211,8 @@ static struct i2c_driver as3711_i2c_driver = {
 	.driver = {
 		   .name = "as3711",
 		   .owner = THIS_MODULE,
-		   },
+		   .of_match_table = of_match_ptr(as3711_of_match),
+	},
 	.probe = as3711_i2c_probe,
 	.remove = as3711_i2c_remove,
 	.id_table = as3711_i2c_id,
diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c
index f0ba8c4..0539b3e 100644
--- a/drivers/regulator/as3711-regulator.c
+++ b/drivers/regulator/as3711-regulator.c
@@ -13,9 +13,11 @@
 #include <linux/init.h>
 #include <linux/mfd/as3711.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
 #include <linux/slab.h>
 
 struct as3711_regulator_info {
@@ -276,6 +278,60 @@ static struct as3711_regulator_info as3711_reg_info[] = {
 
 #define AS3711_REGULATOR_NUM ARRAY_SIZE(as3711_reg_info)
 
+static const char *as3711_regulator_of_names[AS3711_REGULATOR_NUM] = {
+	[AS3711_REGULATOR_SD_1] = "sd1",
+	[AS3711_REGULATOR_SD_2] = "sd2",
+	[AS3711_REGULATOR_SD_3] = "sd3",
+	[AS3711_REGULATOR_SD_4] = "sd4",
+	[AS3711_REGULATOR_LDO_1] = "ldo1",
+	[AS3711_REGULATOR_LDO_2] = "ldo2",
+	[AS3711_REGULATOR_LDO_3] = "ldo3",
+	[AS3711_REGULATOR_LDO_4] = "ldo4",
+	[AS3711_REGULATOR_LDO_5] = "ldo5",
+	[AS3711_REGULATOR_LDO_6] = "ldo6",
+	[AS3711_REGULATOR_LDO_7] = "ldo7",
+	[AS3711_REGULATOR_LDO_8] = "ldo8",
+};
+
+static int as3711_regulator_parse_dt(struct device *dev,
+				struct device_node **of_node, const int count)
+{
+	struct as3711_regulator_pdata *pdata = dev_get_platdata(dev);
+	struct device_node *regulators =
+		of_find_node_by_name(dev->parent->of_node, "regulators");
+	struct of_regulator_match *matches, *match;
+	int ret, i;
+
+	if (!regulators) {
+		dev_err(dev, "regulator node not found\n");
+		return -ENODEV;
+	}
+
+	matches = devm_kzalloc(dev, sizeof(*matches) * count, GFP_KERNEL);
+	if (!matches)
+		return -ENOMEM;
+
+	for (i = 0, match = matches; i < count; i++, match++) {
+		match->name = as3711_regulator_of_names[i];
+		match->driver_data = as3711_reg_info + i;
+	}
+
+	ret = of_regulator_match(dev->parent, regulators, matches, count);
+	of_node_put(regulators);
+	if (ret < 0) {
+		dev_err(dev, "Error parsing regulator init data: %d\n", ret);
+		return ret;
+	}
+
+	for (i = 0, match = matches; i < count; i++, match++)
+		if (match->of_node) {
+			pdata->init_data[i] = match->init_data;
+			of_node[i] = match->of_node;
+		}
+
+	return 0;
+}
+
 static int as3711_regulator_probe(struct platform_device *pdev)
 {
 	struct as3711_regulator_pdata *pdata = dev_get_platdata(&pdev->dev);
@@ -284,13 +340,24 @@ static int as3711_regulator_probe(struct platform_device *pdev)
 	struct regulator_config config = {.dev = &pdev->dev,};
 	struct as3711_regulator *reg = NULL;
 	struct as3711_regulator *regs;
+	struct device_node *of_node[AS3711_REGULATOR_NUM] = {};
 	struct regulator_dev *rdev;
 	struct as3711_regulator_info *ri;
 	int ret;
 	int id;
 
-	if (!pdata)
-		dev_dbg(&pdev->dev, "No platform data...\n");
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data...\n");
+		return -ENODEV;
+	}
+
+	if (pdev->dev.parent->of_node) {
+		ret = as3711_regulator_parse_dt(&pdev->dev, of_node, AS3711_REGULATOR_NUM);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
+			return ret;
+		}
+	}
 
 	regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM *
 			sizeof(struct as3711_regulator), GFP_KERNEL);
@@ -300,7 +367,7 @@ static int as3711_regulator_probe(struct platform_device *pdev)
 	}
 
 	for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) {
-		reg_data = pdata ? pdata->init_data[id] : NULL;
+		reg_data = pdata->init_data[id];
 
 		/* No need to register if there is no regulator data */
 		if (!reg_data)
@@ -312,6 +379,7 @@ static int as3711_regulator_probe(struct platform_device *pdev)
 		config.init_data = reg_data;
 		config.driver_data = reg;
 		config.regmap = as3711->regmap;
+		config.of_node = of_node[id];
 
 		rdev = regulator_register(&ri->desc, &config);
 		if (IS_ERR(rdev)) {
diff --git a/drivers/video/backlight/as3711_bl.c b/drivers/video/backlight/as3711_bl.c
index c6bc65d..c78e4cb 100644
--- a/drivers/video/backlight/as3711_bl.c
+++ b/drivers/video/backlight/as3711_bl.c
@@ -257,6 +257,109 @@ static int as3711_bl_register(struct platform_device *pdev,
 	return 0;
 }
 
+static int as3711_backlight_parse_dt(struct device *dev)
+{
+	struct as3711_bl_pdata *pdata = dev_get_platdata(dev);
+	struct device_node *bl =
+		of_find_node_by_name(dev->parent->of_node, "backlight"), *fb;
+	int ret;
+
+	if (!bl) {
+		dev_dbg(dev, "backlight node not found\n");
+		return -ENODEV;
+	}
+
+	fb = of_parse_phandle(bl, "su1-dev", 0);
+	if (fb) {
+		pdata->su1_fb = fb->full_name;
+
+		ret = of_property_read_u32(bl, "su1-max-uA", &pdata->su1_max_uA);
+		if (pdata->su1_max_uA <= 0)
+			ret = -EINVAL;
+		if (ret < 0)
+			return ret;
+	}
+
+	fb = of_parse_phandle(bl, "su2-dev", 0);
+	if (fb) {
+		int count = 0;
+
+		pdata->su2_fb = fb->full_name;
+
+		ret = of_property_read_u32(bl, "su2-max-uA", &pdata->su2_max_uA);
+		if (pdata->su2_max_uA <= 0)
+			ret = -EINVAL;
+		if (ret < 0)
+			return ret;
+
+		if (of_find_property(bl, "su2-feedback-voltage", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_VOLTAGE;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr1", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR1;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr2", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR2;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr3", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR3;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr-auto", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR_AUTO;
+			count++;
+		}
+		if (count != 1)
+			return -EINVAL;
+
+		count = 0;
+		if (of_find_property(bl, "su2-fbprot-lx-sd4", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_LX_SD4;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio2", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO2;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio3", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO3;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio4", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO4;
+			count++;
+		}
+		if (count != 1)
+			return -EINVAL;
+
+		count = 0;
+		if (of_find_property(bl, "su2-auto-curr1", NULL)) {
+			pdata->su2_auto_curr1 = true;
+			count++;
+		}
+		if (of_find_property(bl, "su2-auto-curr2", NULL)) {
+			pdata->su2_auto_curr2 = true;
+			count++;
+		}
+		if (of_find_property(bl, "su2-auto-curr3", NULL)) {
+			pdata->su2_auto_curr3 = true;
+			count++;
+		}
+
+		/*
+		 * At least one su2-auto-curr* must be specified iff
+		 * AS3711_SU2_CURR_AUTO is used
+		 */
+		if (!count ^ (pdata->su2_feedback != AS3711_SU2_CURR_AUTO))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int as3711_backlight_probe(struct platform_device *pdev)
 {
 	struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev);
@@ -266,11 +369,24 @@ static int as3711_backlight_probe(struct platform_device *pdev)
 	unsigned int max_brightness;
 	int ret;
 
-	if (!pdata || (!pdata->su1_fb && !pdata->su2_fb)) {
+	if (!pdata) {
 		dev_err(&pdev->dev, "No platform data, exiting...\n");
 		return -ENODEV;
 	}
 
+	if (pdev->dev.parent->of_node) {
+		ret = as3711_backlight_parse_dt(&pdev->dev);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
+			return ret;
+		}
+	}
+
+	if (!pdata->su1_fb && !pdata->su2_fb) {
+		dev_err(&pdev->dev, "No framebuffer specified\n");
+		return -EINVAL;
+	}
+
 	/*
 	 * Due to possible hardware damage I chose to block all modes,
 	 * unsupported on my hardware. Anyone, wishing to use any of those modes
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v2] mfd: as3711: add OF support
@ 2013-02-25 11:26   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 16+ messages in thread
From: Guennadi Liakhovetski @ 2013-02-25 11:26 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-fbdev-u79uwXL29TY76Z2rM5mHXA, Samuel Ortiz,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Mark Brown,
	Magnus Damm, Simon Horman, Richard Purdie, Andrew Morton,
	Liam Girdwood

Add device-tree bindings to the AS3711 regulator and backlight drivers.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski-Mmb7MZpHnFY@public.gmane.org>
---

v2:
1. remove a redundant of_device_is_available() check, this also eliminates 
   a compile breakage
2. add .of_node regulator configuration field initialisation
3. add parenthesis to silence compiler warnings

 Documentation/devicetree/bindings/mfd/as3711.txt |   73 +++++++++++++
 drivers/mfd/as3711.c                             |   27 ++++-
 drivers/regulator/as3711-regulator.c             |   74 +++++++++++++-
 drivers/video/backlight/as3711_bl.c              |  118 +++++++++++++++++++++-
 4 files changed, 284 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mfd/as3711.txt

diff --git a/Documentation/devicetree/bindings/mfd/as3711.txt b/Documentation/devicetree/bindings/mfd/as3711.txt
new file mode 100644
index 0000000..d98cf18
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/as3711.txt
@@ -0,0 +1,73 @@
+AS3711 is an I2C PMIC from Austria MicroSystems with multiple DCDC and LDO power
+supplies, a battery charger and an RTC. So far only bindings for the two stepup
+DCDC converters are defined. Other DCDC and LDO supplies are configured, using
+standard regulator properties, they must belong to a sub-node, called
+"regulators" and be called "sd1" to "sd4" and "ldo1" to "ldo8." Stepup converter
+configuration should be placed in a subnode, called "backlight."
+
+Compulsory properties:
+- compatible		: must be "ams,as3711"
+- reg			: specifies the I2C address
+
+To use the SU1 converter as a backlight source the following two properties must
+be provided:
+- su1-dev		: framebuffer phandle
+- su1-max-uA		: maximum current
+
+To use the SU2 converter as a backlight source the following two properties must
+be provided:
+- su2-dev		: framebuffer phandle
+- su1-max-uA		: maximum current
+
+Additionally one of these properties must be provided to select the type of
+feedback used:
+- su2-feedback-voltage	: voltage feedback is used
+- su2-feedback-curr1	: CURR1 input used for current feedback
+- su2-feedback-curr2	: CURR2 input used for current feedback
+- su2-feedback-curr3	: CURR3 input used for current feedback
+- su2-feedback-curr-auto: automatic current feedback selection
+
+and one of these to select the over-voltage protection pin
+- su2-fbprot-lx-sd4	: LX_SD4 is used for over-voltage protection
+- su2-fbprot-gpio2	: GPIO2 is used for over-voltage protection
+- su2-fbprot-gpio3	: GPIO3 is used for over-voltage protection
+- su2-fbprot-gpio4	: GPIO4 is used for over-voltage protection
+
+If "su2-feedback-curr-auto" is selected, one or more of the following properties
+have to be specified:
+- su2-auto-curr1	: use CURR1 input for current feedback
+- su2-auto-curr2	: use CURR2 input for current feedback
+- su2-auto-curr3	: use CURR3 input for current feedback
+
+Example:
+
+as3711@40 {
+	compatible = "ams,as3711";
+	reg = <0x40>;
+
+	regulators {
+		sd4 {
+			regulator-name = "1.215V";
+			regulator-min-microvolt = <1215000>;
+			regulator-max-microvolt = <1235000>;
+		};
+		ldo2 {
+			regulator-name = "2.8V CPU";
+			regulator-min-microvolt = <2800000>;
+			regulator-max-microvolt = <2800000>;
+			regulator-always-on;
+			regulator-boot-on;
+		};
+	};
+
+	backlight {
+		compatible = "ams,as3711-bl";
+		su2-dev = <&lcdc>;
+		su2-max-uA = <36000>;
+		su2-feedback-curr-auto;
+		su2-fbprot-gpio4;
+		su2-auto-curr1;
+		su2-auto-curr2;
+		su2-auto-curr3;
+	};
+};
diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c
index e994c96..01e4141 100644
--- a/drivers/mfd/as3711.c
+++ b/drivers/mfd/as3711.c
@@ -112,16 +112,34 @@ static const struct regmap_config as3711_regmap_config = {
 	.cache_type = REGCACHE_RBTREE,
 };
 
+#ifdef CONFIG_OF
+static struct of_device_id as3711_of_match[] = {
+	{.compatible = "ams,as3711",},
+	{}
+};
+MODULE_DEVICE_TABLE(of, as3711_of_match);
+#endif
+
 static int as3711_i2c_probe(struct i2c_client *client,
 			    const struct i2c_device_id *id)
 {
 	struct as3711 *as3711;
-	struct as3711_platform_data *pdata = client->dev.platform_data;
+	struct as3711_platform_data *pdata;
 	unsigned int id1, id2;
 	int ret;
 
-	if (!pdata)
-		dev_dbg(&client->dev, "Platform data not found\n");
+	if (!client->dev.of_node) {
+		pdata = client->dev.platform_data;
+		if (!pdata)
+			dev_dbg(&client->dev, "Platform data not found\n");
+	} else {
+		pdata = devm_kzalloc(&client->dev,
+				     sizeof(*pdata), GFP_KERNEL);
+		if (!pdata) {
+			dev_err(&client->dev, "Failed to allocate pdata\n");
+			return -ENOMEM;
+		}
+	}
 
 	as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
 	if (!as3711) {
@@ -193,7 +211,8 @@ static struct i2c_driver as3711_i2c_driver = {
 	.driver = {
 		   .name = "as3711",
 		   .owner = THIS_MODULE,
-		   },
+		   .of_match_table = of_match_ptr(as3711_of_match),
+	},
 	.probe = as3711_i2c_probe,
 	.remove = as3711_i2c_remove,
 	.id_table = as3711_i2c_id,
diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c
index f0ba8c4..0539b3e 100644
--- a/drivers/regulator/as3711-regulator.c
+++ b/drivers/regulator/as3711-regulator.c
@@ -13,9 +13,11 @@
 #include <linux/init.h>
 #include <linux/mfd/as3711.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
 #include <linux/slab.h>
 
 struct as3711_regulator_info {
@@ -276,6 +278,60 @@ static struct as3711_regulator_info as3711_reg_info[] = {
 
 #define AS3711_REGULATOR_NUM ARRAY_SIZE(as3711_reg_info)
 
+static const char *as3711_regulator_of_names[AS3711_REGULATOR_NUM] = {
+	[AS3711_REGULATOR_SD_1] = "sd1",
+	[AS3711_REGULATOR_SD_2] = "sd2",
+	[AS3711_REGULATOR_SD_3] = "sd3",
+	[AS3711_REGULATOR_SD_4] = "sd4",
+	[AS3711_REGULATOR_LDO_1] = "ldo1",
+	[AS3711_REGULATOR_LDO_2] = "ldo2",
+	[AS3711_REGULATOR_LDO_3] = "ldo3",
+	[AS3711_REGULATOR_LDO_4] = "ldo4",
+	[AS3711_REGULATOR_LDO_5] = "ldo5",
+	[AS3711_REGULATOR_LDO_6] = "ldo6",
+	[AS3711_REGULATOR_LDO_7] = "ldo7",
+	[AS3711_REGULATOR_LDO_8] = "ldo8",
+};
+
+static int as3711_regulator_parse_dt(struct device *dev,
+				struct device_node **of_node, const int count)
+{
+	struct as3711_regulator_pdata *pdata = dev_get_platdata(dev);
+	struct device_node *regulators =
+		of_find_node_by_name(dev->parent->of_node, "regulators");
+	struct of_regulator_match *matches, *match;
+	int ret, i;
+
+	if (!regulators) {
+		dev_err(dev, "regulator node not found\n");
+		return -ENODEV;
+	}
+
+	matches = devm_kzalloc(dev, sizeof(*matches) * count, GFP_KERNEL);
+	if (!matches)
+		return -ENOMEM;
+
+	for (i = 0, match = matches; i < count; i++, match++) {
+		match->name = as3711_regulator_of_names[i];
+		match->driver_data = as3711_reg_info + i;
+	}
+
+	ret = of_regulator_match(dev->parent, regulators, matches, count);
+	of_node_put(regulators);
+	if (ret < 0) {
+		dev_err(dev, "Error parsing regulator init data: %d\n", ret);
+		return ret;
+	}
+
+	for (i = 0, match = matches; i < count; i++, match++)
+		if (match->of_node) {
+			pdata->init_data[i] = match->init_data;
+			of_node[i] = match->of_node;
+		}
+
+	return 0;
+}
+
 static int as3711_regulator_probe(struct platform_device *pdev)
 {
 	struct as3711_regulator_pdata *pdata = dev_get_platdata(&pdev->dev);
@@ -284,13 +340,24 @@ static int as3711_regulator_probe(struct platform_device *pdev)
 	struct regulator_config config = {.dev = &pdev->dev,};
 	struct as3711_regulator *reg = NULL;
 	struct as3711_regulator *regs;
+	struct device_node *of_node[AS3711_REGULATOR_NUM] = {};
 	struct regulator_dev *rdev;
 	struct as3711_regulator_info *ri;
 	int ret;
 	int id;
 
-	if (!pdata)
-		dev_dbg(&pdev->dev, "No platform data...\n");
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data...\n");
+		return -ENODEV;
+	}
+
+	if (pdev->dev.parent->of_node) {
+		ret = as3711_regulator_parse_dt(&pdev->dev, of_node, AS3711_REGULATOR_NUM);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
+			return ret;
+		}
+	}
 
 	regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM *
 			sizeof(struct as3711_regulator), GFP_KERNEL);
@@ -300,7 +367,7 @@ static int as3711_regulator_probe(struct platform_device *pdev)
 	}
 
 	for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) {
-		reg_data = pdata ? pdata->init_data[id] : NULL;
+		reg_data = pdata->init_data[id];
 
 		/* No need to register if there is no regulator data */
 		if (!reg_data)
@@ -312,6 +379,7 @@ static int as3711_regulator_probe(struct platform_device *pdev)
 		config.init_data = reg_data;
 		config.driver_data = reg;
 		config.regmap = as3711->regmap;
+		config.of_node = of_node[id];
 
 		rdev = regulator_register(&ri->desc, &config);
 		if (IS_ERR(rdev)) {
diff --git a/drivers/video/backlight/as3711_bl.c b/drivers/video/backlight/as3711_bl.c
index c6bc65d..c78e4cb 100644
--- a/drivers/video/backlight/as3711_bl.c
+++ b/drivers/video/backlight/as3711_bl.c
@@ -257,6 +257,109 @@ static int as3711_bl_register(struct platform_device *pdev,
 	return 0;
 }
 
+static int as3711_backlight_parse_dt(struct device *dev)
+{
+	struct as3711_bl_pdata *pdata = dev_get_platdata(dev);
+	struct device_node *bl =
+		of_find_node_by_name(dev->parent->of_node, "backlight"), *fb;
+	int ret;
+
+	if (!bl) {
+		dev_dbg(dev, "backlight node not found\n");
+		return -ENODEV;
+	}
+
+	fb = of_parse_phandle(bl, "su1-dev", 0);
+	if (fb) {
+		pdata->su1_fb = fb->full_name;
+
+		ret = of_property_read_u32(bl, "su1-max-uA", &pdata->su1_max_uA);
+		if (pdata->su1_max_uA <= 0)
+			ret = -EINVAL;
+		if (ret < 0)
+			return ret;
+	}
+
+	fb = of_parse_phandle(bl, "su2-dev", 0);
+	if (fb) {
+		int count = 0;
+
+		pdata->su2_fb = fb->full_name;
+
+		ret = of_property_read_u32(bl, "su2-max-uA", &pdata->su2_max_uA);
+		if (pdata->su2_max_uA <= 0)
+			ret = -EINVAL;
+		if (ret < 0)
+			return ret;
+
+		if (of_find_property(bl, "su2-feedback-voltage", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_VOLTAGE;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr1", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR1;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr2", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR2;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr3", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR3;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr-auto", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR_AUTO;
+			count++;
+		}
+		if (count != 1)
+			return -EINVAL;
+
+		count = 0;
+		if (of_find_property(bl, "su2-fbprot-lx-sd4", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_LX_SD4;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio2", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO2;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio3", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO3;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio4", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO4;
+			count++;
+		}
+		if (count != 1)
+			return -EINVAL;
+
+		count = 0;
+		if (of_find_property(bl, "su2-auto-curr1", NULL)) {
+			pdata->su2_auto_curr1 = true;
+			count++;
+		}
+		if (of_find_property(bl, "su2-auto-curr2", NULL)) {
+			pdata->su2_auto_curr2 = true;
+			count++;
+		}
+		if (of_find_property(bl, "su2-auto-curr3", NULL)) {
+			pdata->su2_auto_curr3 = true;
+			count++;
+		}
+
+		/*
+		 * At least one su2-auto-curr* must be specified iff
+		 * AS3711_SU2_CURR_AUTO is used
+		 */
+		if (!count ^ (pdata->su2_feedback != AS3711_SU2_CURR_AUTO))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int as3711_backlight_probe(struct platform_device *pdev)
 {
 	struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev);
@@ -266,11 +369,24 @@ static int as3711_backlight_probe(struct platform_device *pdev)
 	unsigned int max_brightness;
 	int ret;
 
-	if (!pdata || (!pdata->su1_fb && !pdata->su2_fb)) {
+	if (!pdata) {
 		dev_err(&pdev->dev, "No platform data, exiting...\n");
 		return -ENODEV;
 	}
 
+	if (pdev->dev.parent->of_node) {
+		ret = as3711_backlight_parse_dt(&pdev->dev);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
+			return ret;
+		}
+	}
+
+	if (!pdata->su1_fb && !pdata->su2_fb) {
+		dev_err(&pdev->dev, "No framebuffer specified\n");
+		return -EINVAL;
+	}
+
 	/*
 	 * Due to possible hardware damage I chose to block all modes,
 	 * unsupported on my hardware. Anyone, wishing to use any of those modes
-- 
1.7.2.5

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v2] mfd: as3711: add OF support
@ 2013-02-25 11:26   ` Guennadi Liakhovetski
  0 siblings, 0 replies; 16+ messages in thread
From: Guennadi Liakhovetski @ 2013-02-25 11:26 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-fbdev-u79uwXL29TY76Z2rM5mHXA, Samuel Ortiz,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Mark Brown,
	Magnus Damm, Simon Horman, Richard Purdie, Andrew Morton,
	Liam Girdwood

Add device-tree bindings to the AS3711 regulator and backlight drivers.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---

v2:
1. remove a redundant of_device_is_available() check, this also eliminates 
   a compile breakage
2. add .of_node regulator configuration field initialisation
3. add parenthesis to silence compiler warnings

 Documentation/devicetree/bindings/mfd/as3711.txt |   73 +++++++++++++
 drivers/mfd/as3711.c                             |   27 ++++-
 drivers/regulator/as3711-regulator.c             |   74 +++++++++++++-
 drivers/video/backlight/as3711_bl.c              |  118 +++++++++++++++++++++-
 4 files changed, 284 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mfd/as3711.txt

diff --git a/Documentation/devicetree/bindings/mfd/as3711.txt b/Documentation/devicetree/bindings/mfd/as3711.txt
new file mode 100644
index 0000000..d98cf18
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/as3711.txt
@@ -0,0 +1,73 @@
+AS3711 is an I2C PMIC from Austria MicroSystems with multiple DCDC and LDO power
+supplies, a battery charger and an RTC. So far only bindings for the two stepup
+DCDC converters are defined. Other DCDC and LDO supplies are configured, using
+standard regulator properties, they must belong to a sub-node, called
+"regulators" and be called "sd1" to "sd4" and "ldo1" to "ldo8." Stepup converter
+configuration should be placed in a subnode, called "backlight."
+
+Compulsory properties:
+- compatible		: must be "ams,as3711"
+- reg			: specifies the I2C address
+
+To use the SU1 converter as a backlight source the following two properties must
+be provided:
+- su1-dev		: framebuffer phandle
+- su1-max-uA		: maximum current
+
+To use the SU2 converter as a backlight source the following two properties must
+be provided:
+- su2-dev		: framebuffer phandle
+- su1-max-uA		: maximum current
+
+Additionally one of these properties must be provided to select the type of
+feedback used:
+- su2-feedback-voltage	: voltage feedback is used
+- su2-feedback-curr1	: CURR1 input used for current feedback
+- su2-feedback-curr2	: CURR2 input used for current feedback
+- su2-feedback-curr3	: CURR3 input used for current feedback
+- su2-feedback-curr-auto: automatic current feedback selection
+
+and one of these to select the over-voltage protection pin
+- su2-fbprot-lx-sd4	: LX_SD4 is used for over-voltage protection
+- su2-fbprot-gpio2	: GPIO2 is used for over-voltage protection
+- su2-fbprot-gpio3	: GPIO3 is used for over-voltage protection
+- su2-fbprot-gpio4	: GPIO4 is used for over-voltage protection
+
+If "su2-feedback-curr-auto" is selected, one or more of the following properties
+have to be specified:
+- su2-auto-curr1	: use CURR1 input for current feedback
+- su2-auto-curr2	: use CURR2 input for current feedback
+- su2-auto-curr3	: use CURR3 input for current feedback
+
+Example:
+
+as3711@40 {
+	compatible = "ams,as3711";
+	reg = <0x40>;
+
+	regulators {
+		sd4 {
+			regulator-name = "1.215V";
+			regulator-min-microvolt = <1215000>;
+			regulator-max-microvolt = <1235000>;
+		};
+		ldo2 {
+			regulator-name = "2.8V CPU";
+			regulator-min-microvolt = <2800000>;
+			regulator-max-microvolt = <2800000>;
+			regulator-always-on;
+			regulator-boot-on;
+		};
+	};
+
+	backlight {
+		compatible = "ams,as3711-bl";
+		su2-dev = <&lcdc>;
+		su2-max-uA = <36000>;
+		su2-feedback-curr-auto;
+		su2-fbprot-gpio4;
+		su2-auto-curr1;
+		su2-auto-curr2;
+		su2-auto-curr3;
+	};
+};
diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c
index e994c96..01e4141 100644
--- a/drivers/mfd/as3711.c
+++ b/drivers/mfd/as3711.c
@@ -112,16 +112,34 @@ static const struct regmap_config as3711_regmap_config = {
 	.cache_type = REGCACHE_RBTREE,
 };
 
+#ifdef CONFIG_OF
+static struct of_device_id as3711_of_match[] = {
+	{.compatible = "ams,as3711",},
+	{}
+};
+MODULE_DEVICE_TABLE(of, as3711_of_match);
+#endif
+
 static int as3711_i2c_probe(struct i2c_client *client,
 			    const struct i2c_device_id *id)
 {
 	struct as3711 *as3711;
-	struct as3711_platform_data *pdata = client->dev.platform_data;
+	struct as3711_platform_data *pdata;
 	unsigned int id1, id2;
 	int ret;
 
-	if (!pdata)
-		dev_dbg(&client->dev, "Platform data not found\n");
+	if (!client->dev.of_node) {
+		pdata = client->dev.platform_data;
+		if (!pdata)
+			dev_dbg(&client->dev, "Platform data not found\n");
+	} else {
+		pdata = devm_kzalloc(&client->dev,
+				     sizeof(*pdata), GFP_KERNEL);
+		if (!pdata) {
+			dev_err(&client->dev, "Failed to allocate pdata\n");
+			return -ENOMEM;
+		}
+	}
 
 	as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL);
 	if (!as3711) {
@@ -193,7 +211,8 @@ static struct i2c_driver as3711_i2c_driver = {
 	.driver = {
 		   .name = "as3711",
 		   .owner = THIS_MODULE,
-		   },
+		   .of_match_table = of_match_ptr(as3711_of_match),
+	},
 	.probe = as3711_i2c_probe,
 	.remove = as3711_i2c_remove,
 	.id_table = as3711_i2c_id,
diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c
index f0ba8c4..0539b3e 100644
--- a/drivers/regulator/as3711-regulator.c
+++ b/drivers/regulator/as3711-regulator.c
@@ -13,9 +13,11 @@
 #include <linux/init.h>
 #include <linux/mfd/as3711.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
 #include <linux/slab.h>
 
 struct as3711_regulator_info {
@@ -276,6 +278,60 @@ static struct as3711_regulator_info as3711_reg_info[] = {
 
 #define AS3711_REGULATOR_NUM ARRAY_SIZE(as3711_reg_info)
 
+static const char *as3711_regulator_of_names[AS3711_REGULATOR_NUM] = {
+	[AS3711_REGULATOR_SD_1] = "sd1",
+	[AS3711_REGULATOR_SD_2] = "sd2",
+	[AS3711_REGULATOR_SD_3] = "sd3",
+	[AS3711_REGULATOR_SD_4] = "sd4",
+	[AS3711_REGULATOR_LDO_1] = "ldo1",
+	[AS3711_REGULATOR_LDO_2] = "ldo2",
+	[AS3711_REGULATOR_LDO_3] = "ldo3",
+	[AS3711_REGULATOR_LDO_4] = "ldo4",
+	[AS3711_REGULATOR_LDO_5] = "ldo5",
+	[AS3711_REGULATOR_LDO_6] = "ldo6",
+	[AS3711_REGULATOR_LDO_7] = "ldo7",
+	[AS3711_REGULATOR_LDO_8] = "ldo8",
+};
+
+static int as3711_regulator_parse_dt(struct device *dev,
+				struct device_node **of_node, const int count)
+{
+	struct as3711_regulator_pdata *pdata = dev_get_platdata(dev);
+	struct device_node *regulators +		of_find_node_by_name(dev->parent->of_node, "regulators");
+	struct of_regulator_match *matches, *match;
+	int ret, i;
+
+	if (!regulators) {
+		dev_err(dev, "regulator node not found\n");
+		return -ENODEV;
+	}
+
+	matches = devm_kzalloc(dev, sizeof(*matches) * count, GFP_KERNEL);
+	if (!matches)
+		return -ENOMEM;
+
+	for (i = 0, match = matches; i < count; i++, match++) {
+		match->name = as3711_regulator_of_names[i];
+		match->driver_data = as3711_reg_info + i;
+	}
+
+	ret = of_regulator_match(dev->parent, regulators, matches, count);
+	of_node_put(regulators);
+	if (ret < 0) {
+		dev_err(dev, "Error parsing regulator init data: %d\n", ret);
+		return ret;
+	}
+
+	for (i = 0, match = matches; i < count; i++, match++)
+		if (match->of_node) {
+			pdata->init_data[i] = match->init_data;
+			of_node[i] = match->of_node;
+		}
+
+	return 0;
+}
+
 static int as3711_regulator_probe(struct platform_device *pdev)
 {
 	struct as3711_regulator_pdata *pdata = dev_get_platdata(&pdev->dev);
@@ -284,13 +340,24 @@ static int as3711_regulator_probe(struct platform_device *pdev)
 	struct regulator_config config = {.dev = &pdev->dev,};
 	struct as3711_regulator *reg = NULL;
 	struct as3711_regulator *regs;
+	struct device_node *of_node[AS3711_REGULATOR_NUM] = {};
 	struct regulator_dev *rdev;
 	struct as3711_regulator_info *ri;
 	int ret;
 	int id;
 
-	if (!pdata)
-		dev_dbg(&pdev->dev, "No platform data...\n");
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data...\n");
+		return -ENODEV;
+	}
+
+	if (pdev->dev.parent->of_node) {
+		ret = as3711_regulator_parse_dt(&pdev->dev, of_node, AS3711_REGULATOR_NUM);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
+			return ret;
+		}
+	}
 
 	regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM *
 			sizeof(struct as3711_regulator), GFP_KERNEL);
@@ -300,7 +367,7 @@ static int as3711_regulator_probe(struct platform_device *pdev)
 	}
 
 	for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) {
-		reg_data = pdata ? pdata->init_data[id] : NULL;
+		reg_data = pdata->init_data[id];
 
 		/* No need to register if there is no regulator data */
 		if (!reg_data)
@@ -312,6 +379,7 @@ static int as3711_regulator_probe(struct platform_device *pdev)
 		config.init_data = reg_data;
 		config.driver_data = reg;
 		config.regmap = as3711->regmap;
+		config.of_node = of_node[id];
 
 		rdev = regulator_register(&ri->desc, &config);
 		if (IS_ERR(rdev)) {
diff --git a/drivers/video/backlight/as3711_bl.c b/drivers/video/backlight/as3711_bl.c
index c6bc65d..c78e4cb 100644
--- a/drivers/video/backlight/as3711_bl.c
+++ b/drivers/video/backlight/as3711_bl.c
@@ -257,6 +257,109 @@ static int as3711_bl_register(struct platform_device *pdev,
 	return 0;
 }
 
+static int as3711_backlight_parse_dt(struct device *dev)
+{
+	struct as3711_bl_pdata *pdata = dev_get_platdata(dev);
+	struct device_node *bl +		of_find_node_by_name(dev->parent->of_node, "backlight"), *fb;
+	int ret;
+
+	if (!bl) {
+		dev_dbg(dev, "backlight node not found\n");
+		return -ENODEV;
+	}
+
+	fb = of_parse_phandle(bl, "su1-dev", 0);
+	if (fb) {
+		pdata->su1_fb = fb->full_name;
+
+		ret = of_property_read_u32(bl, "su1-max-uA", &pdata->su1_max_uA);
+		if (pdata->su1_max_uA <= 0)
+			ret = -EINVAL;
+		if (ret < 0)
+			return ret;
+	}
+
+	fb = of_parse_phandle(bl, "su2-dev", 0);
+	if (fb) {
+		int count = 0;
+
+		pdata->su2_fb = fb->full_name;
+
+		ret = of_property_read_u32(bl, "su2-max-uA", &pdata->su2_max_uA);
+		if (pdata->su2_max_uA <= 0)
+			ret = -EINVAL;
+		if (ret < 0)
+			return ret;
+
+		if (of_find_property(bl, "su2-feedback-voltage", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_VOLTAGE;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr1", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR1;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr2", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR2;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr3", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR3;
+			count++;
+		}
+		if (of_find_property(bl, "su2-feedback-curr-auto", NULL)) {
+			pdata->su2_feedback = AS3711_SU2_CURR_AUTO;
+			count++;
+		}
+		if (count != 1)
+			return -EINVAL;
+
+		count = 0;
+		if (of_find_property(bl, "su2-fbprot-lx-sd4", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_LX_SD4;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio2", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO2;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio3", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO3;
+			count++;
+		}
+		if (of_find_property(bl, "su2-fbprot-gpio4", NULL)) {
+			pdata->su2_fbprot = AS3711_SU2_GPIO4;
+			count++;
+		}
+		if (count != 1)
+			return -EINVAL;
+
+		count = 0;
+		if (of_find_property(bl, "su2-auto-curr1", NULL)) {
+			pdata->su2_auto_curr1 = true;
+			count++;
+		}
+		if (of_find_property(bl, "su2-auto-curr2", NULL)) {
+			pdata->su2_auto_curr2 = true;
+			count++;
+		}
+		if (of_find_property(bl, "su2-auto-curr3", NULL)) {
+			pdata->su2_auto_curr3 = true;
+			count++;
+		}
+
+		/*
+		 * At least one su2-auto-curr* must be specified iff
+		 * AS3711_SU2_CURR_AUTO is used
+		 */
+		if (!count ^ (pdata->su2_feedback != AS3711_SU2_CURR_AUTO))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int as3711_backlight_probe(struct platform_device *pdev)
 {
 	struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev);
@@ -266,11 +369,24 @@ static int as3711_backlight_probe(struct platform_device *pdev)
 	unsigned int max_brightness;
 	int ret;
 
-	if (!pdata || (!pdata->su1_fb && !pdata->su2_fb)) {
+	if (!pdata) {
 		dev_err(&pdev->dev, "No platform data, exiting...\n");
 		return -ENODEV;
 	}
 
+	if (pdev->dev.parent->of_node) {
+		ret = as3711_backlight_parse_dt(&pdev->dev);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "DT parsing failed: %d\n", ret);
+			return ret;
+		}
+	}
+
+	if (!pdata->su1_fb && !pdata->su2_fb) {
+		dev_err(&pdev->dev, "No framebuffer specified\n");
+		return -EINVAL;
+	}
+
 	/*
 	 * Due to possible hardware damage I chose to block all modes,
 	 * unsupported on my hardware. Anyone, wishing to use any of those modes
-- 
1.7.2.5


^ permalink raw reply related	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2013-02-25 11:27 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-02-15 10:07 [PATCH/RFC] mfd: as3711: add OF support Guennadi Liakhovetski
2013-02-15 10:07 ` Guennadi Liakhovetski
2013-02-15 10:07 ` Guennadi Liakhovetski
2013-02-15 10:36 ` [RFC] ARM: shmobile: add framebuffer and backlight support to kzm9g-reference Guennadi Liakhovetski
2013-02-15 10:36   ` Guennadi Liakhovetski
2013-02-16  2:26 ` [PATCH/RFC] mfd: as3711: add OF support Simon Horman
2013-02-16  2:26   ` Simon Horman
2013-02-16  6:13   ` Simon Horman
2013-02-16  6:13     ` Simon Horman
2013-02-16  6:13     ` Simon Horman
2013-02-16  5:54 ` Simon Horman
2013-02-16  5:54   ` Simon Horman
2013-02-16  5:54   ` Simon Horman
2013-02-25 11:26 ` [PATCH v2] " Guennadi Liakhovetski
2013-02-25 11:26   ` Guennadi Liakhovetski
2013-02-25 11:26   ` Guennadi Liakhovetski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.