Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH v4 0/7] mux controller abstraction and iio/i2c muxes
From: Peter Rosin @ 2016-11-26 18:38 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: Wolfram Sang, Rob Herring, Mark Rutland, Jonathan Cameron,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Jonathan Corbet, Arnd Bergmann, Greg Kroah-Hartman,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1480000687-5630-1-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>

On 2016-11-24 16:18, Peter Rosin wrote:
> v3 -> v4 changes
> - added support for having the mux-controller in a child node of a
>   mux-consumer if it is a sole consumer, to hopefully even further satisfy
>   the complaint from Rob (and later Lars-Peter) about dt complexity.
> - the above came at the cost of some rather horrible refcounting code,
>   please review and suggest how it should be done...
> 
> v2 -> v3 changes
> - have the mux-controller in the parent node of any mux-controller consumer,
>   to hopefully satisfy complaint from Rob about dt complexity.

I did some further tests and both of these attempts to support fancier
devicetree bindings have severe problems. I will remove them for v5 and
go back to having a phandle reference to the mux-controller from the
consumer (unless I get some revelation of course and just get it). I'm
simply not yet understanding the driver model well enough to pull this
off at the moment...

Cheers,
Peter

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH 5/5] mfd: twl: use mfd_add_devices for TWL6032 regulator
From: Nicolae Rosia @ 2016-11-26 18:13 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Rob Herring, Mark Rutland, Tony Lindgren
  Cc: Liam Girdwood, Paul Gortmaker, Graeme Gregory, Baruch Siach,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Nicolae Rosia
In-Reply-To: <20161126181326.14951-1-Nicolae_Rosia-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>

TWL6032 regulator driver uses the drvdata twl_priv pointer.
In order to avoid accessing an invalid drvdata
when the driver gets unbinded, make sure we remove the
child devices before deleting the drvdata.

Signed-off-by: Nicolae Rosia <Nicolae_Rosia-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>
---
 drivers/mfd/twl-core.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 409b836..1e94364 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -43,6 +43,7 @@
 #include <linux/of_platform.h>
 #include <linux/irq.h>
 #include <linux/irqdomain.h>
+#include <linux/mfd/core.h>
 
 #include <linux/regulator/machine.h>
 
@@ -155,8 +156,16 @@ int twl4030_init_irq(struct device *dev, int irq_num);
 int twl4030_exit_irq(void);
 int twl4030_init_chip_irq(const char *chip);
 
+
 static struct twlcore *twl_priv;
 
+static struct mfd_cell twl6032_devs[] = {
+	{
+		.name = "twl6032-regulator",
+		.of_compatible = "ti,twl6032-regulator",
+	},
+};
+
 static struct twl_mapping twl4030_map[] = {
 	/*
 	 * NOTE:  don't change this table without updating the
@@ -665,6 +674,8 @@ static int twl_remove(struct i2c_client *client)
 	unsigned i, num_slaves;
 	int status;
 
+	mfd_remove_devices(&client->dev);
+
 	if (twl_class_is_4030())
 		status = twl4030_exit_irq();
 	else
@@ -834,6 +845,17 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
 				 TWL4030_DCDC_GLOBAL_CFG);
 	}
 
+	if (id->driver_data & TWL6032_SUBCLASS) {
+		status = mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
+					 twl6032_devs, ARRAY_SIZE(twl6032_devs),
+					 NULL, 0, NULL);
+		if (status != 0) {
+			dev_err(&client->dev, "failed to add mfd devices: %d\n",
+				status);
+			goto fail;
+		}
+	}
+
 fail:
 	if (status < 0)
 		twl_remove(client);
-- 
2.9.3

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 4/5] regulator: Add support for TI TWL6032
From: Nicolae Rosia @ 2016-11-26 18:13 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Rob Herring, Mark Rutland, Tony Lindgren
  Cc: Liam Girdwood, Paul Gortmaker, Graeme Gregory, Baruch Siach,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Nicolae Rosia
In-Reply-To: <20161126181326.14951-1-Nicolae_Rosia-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>

The TWL6032 PMIC is similar to TWL6030, has different
output names, and regulator control logic.
It is used on Barnes & Noble Nook HD and HD+.

Signed-off-by: Nicolae Rosia <Nicolae_Rosia-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>
---
 .../bindings/regulator/twl6032-regulator.txt       | 109 ++++
 drivers/regulator/Kconfig                          |   7 +
 drivers/regulator/Makefile                         |   1 +
 drivers/regulator/twl6032-regulator.c              | 582 +++++++++++++++++++++
 4 files changed, 699 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/regulator/twl6032-regulator.txt
 create mode 100644 drivers/regulator/twl6032-regulator.c

diff --git a/Documentation/devicetree/bindings/regulator/twl6032-regulator.txt b/Documentation/devicetree/bindings/regulator/twl6032-regulator.txt
new file mode 100644
index 0000000..323f5a9
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/twl6032-regulator.txt
@@ -0,0 +1,109 @@
+TWL6032 PMIC Voltage Regulator Bindings
+
+The parent node must be MFD TWL Core, ti,twl6032.
+
+Required properties:
+- compatible: "ti,twl6032"
+
+Optional properties:
+- regulators node containing regulator childs.
+
+The child regulators  must be named after their hardware
+counterparts: LDO[1-6], LDOLN, LDOUSB and VANA.
+
+Each regulator is defined using the standard binding
+for regulators as described in ./regulator.txt
+
+Example:
+twl {
+	compatible = "ti,twl6032";
+
+	[...]
+
+	pmic {
+		compatible = "ti,twl6032-regulator";
+
+		regulators {
+			ldo1: LDO1 {
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <2500000>;
+
+				regulator-state-mem {
+					regulator-off-in-suspend;
+				};
+			};
+
+			ldo2: LDO2 {
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <3000000>;
+
+				regulator-state-mem {
+					regulator-off-in-suspend;
+				};
+			};
+
+			ldo3: LDO3 {
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-boot-on;
+
+				regulator-state-mem {
+					regulator-off-in-suspend;
+				};
+			};
+
+			ldo4: LDO4 {
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+
+				regulator-state-mem {
+					regulator-off-in-suspend;
+				};
+			};
+
+			ldo5: LDO5 {
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <3000000>;
+
+				regulator-state-mem {
+					regulator-off-in-suspend;
+				};
+			};
+
+			ldo6: LDO6 {
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+
+				regulator-state-mem {
+					regulator-off-in-suspend;
+				};
+			};
+
+			ldo7: LDO7 {
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			ldoln: LDOLN {
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <3000000>;
+			};
+
+			ldousb: LDOUSB {
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <3000000>;
+			};
+
+			vana: VANA {
+				regulator-min-microvolt = <2100000>;
+				regulator-max-microvolt = <2100000>;
+				regulator-always-on;
+			};
+		};
+	};
+
+	[...]
+};
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 936f7cc..3168aba 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -843,6 +843,13 @@ config REGULATOR_TWL4030
 	  This driver supports the voltage regulators provided by
 	  this family of companion chips.
 
+config REGULATOR_TWL6032
+	tristate "TI TWL6032 PMIC"
+	depends on TWL4030_CORE
+	depends on OF || COMPILE_TEST
+	help
+	  This driver supports the Texas Instruments TWL6032 voltage regulator.
+
 config REGULATOR_VEXPRESS
 	tristate "Versatile Express regulators"
 	depends on VEXPRESS_CONFIG
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 2142a5d..185a979 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -105,6 +105,7 @@ obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o
 obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
 obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o
 obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o
+obj-$(CONFIG_REGULATOR_TWL6032) += twl6032-regulator.o
 obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
diff --git a/drivers/regulator/twl6032-regulator.c b/drivers/regulator/twl6032-regulator.c
new file mode 100644
index 0000000..70a0fdf
--- /dev/null
+++ b/drivers/regulator/twl6032-regulator.c
@@ -0,0 +1,582 @@
+/*
+ * TWL6032 regulator driver
+ * Copyright (C) 2016 Nicolae Rosia <nicolae.rosia-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/irq.h>
+#include <linux/mfd/twl-core.h>
+
+/* TWL6032 register offsets */
+#define TWL6032_VREG_TRANS 1
+#define TWL6032_VREG_STATE 2
+#define TWL6032_VREG_VOLTAGE 3
+
+#define TWL6032_LDO_MIN_MV 1000
+#define TWL6032_LDO_MAX_MV 3300
+
+/* TWL6030 LDO register values for CFG_TRANS */
+#define TWL6032_CFG_TRANS_STATE_MASK 0x03
+#define TWL6032_CFG_TRANS_STATE_OFF 0x00
+#define TWL6032_CFG_TRANS_STATE_AUTO 0x01
+#define TWL6032_CFG_TRANS_SLEEP_SHIFT 2
+
+#define TWL6032_CFG_STATE_MASK 0x03
+#define TWL6032_CFG_STATE_OFF 0x00
+#define TWL6032_CFG_STATE_ON 0x01
+#define TWL6032_CFG_STATE_OFF2 0x02
+#define TWL6032_CFG_STATE_SLEEP 0x03
+
+static const char *rdev_get_name(struct regulator_dev *rdev)
+{
+	if (rdev->constraints && rdev->constraints->name)
+		return rdev->constraints->name;
+	else if (rdev->desc->name)
+		return rdev->desc->name;
+	else
+		return "";
+}
+
+struct twl6032_regulator_info {
+	u8 base;
+	unsigned int min_mV;
+	struct regulator_desc desc;
+};
+
+struct twl6032_regulator {
+	struct twl6032_regulator_info *info;
+};
+
+static int twl6032_set_trans_state(struct regulator_dev *rdev, u8 shift, u8 val)
+{
+	struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev);
+	struct twl6032_regulator_info *info = twl6032_reg->info;
+	unsigned int state;
+	u8 mask;
+	int ret;
+
+	/* Read CFG_TRANS register of TWL6030 */
+	ret = regmap_read(rdev->regmap, info->base + TWL6032_VREG_TRANS,
+			  &state);
+	if (ret < 0) {
+		dev_err(&rdev->dev, "%s %s: regmap_read: %d\n",
+			rdev_get_name(rdev), __func__, ret);
+		return ret;
+	}
+
+	mask = TWL6032_CFG_TRANS_STATE_MASK << shift;
+	val = (val << shift) & mask;
+
+	/* If value is already set, no need to write to reg */
+	if (val == (state & mask))
+		return 0;
+
+	state &= ~mask;
+	state |= val;
+
+	return regmap_write(rdev->regmap, info->base + TWL6032_VREG_TRANS,
+			    state);
+}
+
+static int
+twl6032_ldo_list_voltage(struct regulator_dev *rdev, unsigned int sel)
+{
+	int ret;
+
+	switch (sel) {
+	case 0:
+		ret = 0;
+		break;
+	case 1 ... 24:
+		/* Linear mapping from 00000001 to 00011000:
+		 * Absolute voltage value = 1.0 V + 0.1 V × (sel – 00000001)
+		 */
+		ret = (TWL6032_LDO_MIN_MV + 100 * (sel - 1)) * 1000;
+		break;
+	case 25 ... 30:
+		ret = -EINVAL;
+		break;
+	case 31:
+		ret = 2750000;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	dev_dbg(&rdev->dev, "%s %s: sel: %d, mV: %d\n", rdev_get_name(rdev),
+		__func__, sel, ret);
+
+	return ret;
+}
+
+static int
+twl6032_ldo_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel)
+{
+	struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev);
+	struct twl6032_regulator_info *info = twl6032_reg->info;
+	int ret;
+
+	dev_dbg(&rdev->dev, "%s %s: sel: 0x%02X\n", rdev_get_name(rdev),
+		__func__, sel);
+
+	ret = regmap_write(rdev->regmap, info->base + TWL6032_VREG_VOLTAGE,
+			   sel);
+	if (ret < 0) {
+		dev_err(&rdev->dev, "%s %s: regmap_write: %d\n",
+			rdev_get_name(rdev), __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int twl6032_ldo_get_voltage_sel(struct regulator_dev *rdev)
+{
+	struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev);
+	struct twl6032_regulator_info *info = twl6032_reg->info;
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(rdev->regmap, info->base + TWL6032_VREG_VOLTAGE,
+			  &val);
+	if (ret < 0) {
+		dev_err(&rdev->dev, "%s %s: regmap_read: %d\n",
+			rdev_get_name(rdev), __func__, ret);
+		return ret;
+	}
+
+	dev_dbg(&rdev->dev, "%s %s: vsel: 0x%02X\n", rdev_get_name(rdev),
+		__func__, val);
+
+	return val;
+}
+
+static int twl6032_ldo_enable(struct regulator_dev *rdev)
+{
+	struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev);
+	struct twl6032_regulator_info *info = twl6032_reg->info;
+	int ret;
+
+	dev_dbg(&rdev->dev, "%s %s\n", rdev_get_name(rdev), __func__);
+
+	ret = regmap_write(rdev->regmap, info->base + TWL6032_VREG_STATE,
+			   TWL6032_CFG_STATE_ON);
+	if (ret < 0) {
+		dev_err(&rdev->dev, "%s %s: regmap_write: %d\n",
+			rdev_get_name(rdev), __func__, ret);
+		return ret;
+	}
+
+	ret = twl6032_set_trans_state(rdev, TWL6032_CFG_TRANS_SLEEP_SHIFT,
+				      TWL6032_CFG_TRANS_STATE_AUTO);
+	if (ret < 0) {
+		dev_err(&rdev->dev, "%s %s: twl6032_set_trans_state: %d\n",
+			rdev_get_name(rdev), __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int twl6032_ldo_disable(struct regulator_dev *rdev)
+{
+	struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev);
+	struct twl6032_regulator_info *info = twl6032_reg->info;
+	int ret;
+
+	dev_dbg(&rdev->dev, "%s %s\n", rdev_get_name(rdev), __func__);
+
+	ret = regmap_write(rdev->regmap, info->base + TWL6032_VREG_STATE,
+			   TWL6032_CFG_STATE_OFF);
+	if (ret < 0) {
+		dev_err(&rdev->dev, "%s %s: regmap_write: %d\n",
+			rdev_get_name(rdev), __func__, ret);
+		return ret;
+	}
+
+	ret = twl6032_set_trans_state(rdev, TWL6032_CFG_TRANS_SLEEP_SHIFT,
+				      TWL6032_CFG_TRANS_STATE_OFF);
+	if (ret < 0) {
+		dev_err(&rdev->dev, "%s %s: twl6032_set_trans_state: %d\n",
+			rdev_get_name(rdev), __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int twl6032_ldo_is_enabled(struct regulator_dev *rdev)
+{
+	struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev);
+	struct twl6032_regulator_info *info = twl6032_reg->info;
+	int ret;
+	unsigned int val;
+
+	ret = regmap_read(rdev->regmap, info->base + TWL6032_VREG_STATE, &val);
+	if (ret < 0) {
+		dev_err(&rdev->dev, "%s regmap_read: %d\n", __func__, ret);
+		return ret;
+	}
+
+	dev_dbg(&rdev->dev, "%s %s: val: 0x%02X, val-masked: 0x%02X, ret: %d\n",
+		rdev_get_name(rdev), __func__,
+		val, val & TWL6032_CFG_STATE_MASK,
+		(val & TWL6032_CFG_STATE_MASK) == TWL6032_CFG_STATE_ON);
+
+	val &= TWL6032_CFG_STATE_MASK;
+
+	return val == TWL6032_CFG_STATE_ON;
+}
+
+static int twl6032_ldo_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+	struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev);
+	struct twl6032_regulator_info *info = twl6032_reg->info;
+	unsigned int val = 0;
+	int ret;
+
+	dev_dbg(&rdev->dev, "%s %s: mode: 0x%02X\n", rdev_get_name(rdev),
+		__func__, mode);
+
+	switch (mode) {
+	case REGULATOR_MODE_NORMAL:
+		val |= TWL6032_CFG_STATE_ON;
+		break;
+	case REGULATOR_MODE_STANDBY:
+		val |= TWL6032_CFG_STATE_SLEEP;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	ret = regmap_write(rdev->regmap, info->base + TWL6032_VREG_STATE, val);
+	if (ret < 0) {
+		dev_err(&rdev->dev, "%s %s: regmap_write: %d\n",
+			rdev_get_name(rdev), __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int twl6032_ldo_get_status(struct regulator_dev *rdev)
+{
+	struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev);
+	struct twl6032_regulator_info *info = twl6032_reg->info;
+	int ret;
+	unsigned int val;
+
+	ret = regmap_read(rdev->regmap, info->base + TWL6032_VREG_STATE, &val);
+	if (ret < 0) {
+		dev_err(&rdev->dev, "%s %s: regmap_read: %d\n",
+			rdev_get_name(rdev), __func__, ret);
+		return ret;
+	}
+
+	dev_dbg(&rdev->dev, "%s %s: val: 0x%02X, val-with-mask: 0x%02X\n",
+		rdev_get_name(rdev), __func__,
+		val, val & TWL6032_CFG_STATE_MASK);
+
+	val &= TWL6032_CFG_STATE_MASK;
+
+	switch (val) {
+	case TWL6032_CFG_STATE_ON:
+		return REGULATOR_STATUS_NORMAL;
+
+	case TWL6032_CFG_STATE_SLEEP:
+		return REGULATOR_STATUS_STANDBY;
+
+	case TWL6032_CFG_STATE_OFF:
+	case TWL6032_CFG_STATE_OFF2:
+	default:
+		break;
+	}
+
+	return REGULATOR_STATUS_OFF;
+}
+
+static int twl6032_ldo_suspend_enable(struct regulator_dev *rdev)
+{
+	return twl6032_set_trans_state(rdev, TWL6032_CFG_TRANS_SLEEP_SHIFT,
+				       TWL6032_CFG_TRANS_STATE_AUTO);
+}
+
+static int twl6032_ldo_suspend_disable(struct regulator_dev *rdev)
+{
+	return twl6032_set_trans_state(rdev, TWL6032_CFG_TRANS_SLEEP_SHIFT,
+				       TWL6032_CFG_TRANS_STATE_OFF);
+}
+
+static int
+twl6032_fixed_list_voltage(struct regulator_dev *rdev, unsigned int sel)
+{
+	struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev);
+	struct twl6032_regulator_info *info = twl6032_reg->info;
+
+	return info->min_mV * 1000; /* mV to V */
+}
+
+static int twl6032_fixed_get_voltage(struct regulator_dev *rdev)
+{
+	struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev);
+	struct twl6032_regulator_info *info = twl6032_reg->info;
+
+	return info->min_mV * 1000; /* mV to V */
+}
+
+static const struct regulator_ops twl6032_ldo_ops = {
+	.list_voltage = twl6032_ldo_list_voltage,
+	.set_voltage_sel = twl6032_ldo_set_voltage_sel,
+	.get_voltage_sel = twl6032_ldo_get_voltage_sel,
+	.enable = twl6032_ldo_enable,
+	.disable = twl6032_ldo_disable,
+	.is_enabled = twl6032_ldo_is_enabled,
+	.set_mode = twl6032_ldo_set_mode,
+	.get_status = twl6032_ldo_get_status,
+	.set_suspend_enable = twl6032_ldo_suspend_enable,
+	.set_suspend_disable =  twl6032_ldo_suspend_disable,
+};
+
+static const struct regulator_ops twl6032_fixed_ops = {
+	.list_voltage = twl6032_fixed_list_voltage,
+	.get_voltage = twl6032_fixed_get_voltage,
+	.enable = twl6032_ldo_enable,
+	.disable = twl6032_ldo_disable,
+	.is_enabled = twl6032_ldo_is_enabled,
+	.set_mode = twl6032_ldo_set_mode,
+	.get_status = twl6032_ldo_get_status,
+	.set_suspend_enable = twl6032_ldo_suspend_enable,
+	.set_suspend_disable =  twl6032_ldo_suspend_disable,
+};
+
+#define TWL6032_LDO_REG_VOLTAGES \
+	((TWL6032_LDO_MAX_MV - TWL6032_LDO_MIN_MV) / 100 + 1)
+#define TWL6032_LDO_REG(_id, _reg) \
+{ \
+	.base = _reg, \
+	.desc = { \
+		.name = "twl6032-reg-" # _id, \
+		.n_voltages = TWL6032_LDO_REG_VOLTAGES, \
+		.ops = &twl6032_ldo_ops, \
+		.type = REGULATOR_VOLTAGE, \
+		.owner = THIS_MODULE, \
+	}, \
+}
+
+#define TWL6032_FIXED_REG(_id, _reg, _min_mV) \
+{ \
+	.base = _reg, \
+	.min_mV = _min_mV, \
+	.desc = { \
+		.name = "twl6032-reg-" # _id, \
+		.n_voltages = 1, \
+		.ops = &twl6032_fixed_ops, \
+		.type = REGULATOR_VOLTAGE, \
+		.owner = THIS_MODULE, \
+	}, \
+}
+
+#define TWL6032_RESOURCE_REG(_id, _reg) \
+{ \
+	.base = _reg, \
+	.desc = { \
+		.name = "twl6032-reg-" # _id, \
+		.ops = &twl6032_ldo_ops, \
+		.type = REGULATOR_VOLTAGE, \
+		.owner = THIS_MODULE, \
+	}, \
+}
+
+static struct twl6032_regulator_info twl6032_ldo_reg_info[] = {
+	TWL6032_LDO_REG(LDO1, 0x9C),
+	TWL6032_LDO_REG(LDO2, 0x84),
+	TWL6032_LDO_REG(LDO3, 0x8C),
+	TWL6032_LDO_REG(LDO4, 0x88),
+	TWL6032_LDO_REG(LDO5, 0x98),
+	TWL6032_LDO_REG(LDO6, 0x90),
+	TWL6032_LDO_REG(LDO7, 0xA4),
+	TWL6032_LDO_REG(LDOLN, 0x94),
+	TWL6032_LDO_REG(LDOUSB, 0xA0),
+};
+
+static struct twl6032_regulator_info twl6032_fixed_reg_info[] = {
+	TWL6032_FIXED_REG(VANA, 0x80, 2100),
+};
+
+static struct of_regulator_match
+twl6032_ldo_reg_matches[] = {
+	{ .name = "LDO1", },
+	{ .name = "LDO2", },
+	{ .name = "LDO3", },
+	{ .name = "LDO4", },
+	{ .name = "LDO5", },
+	{ .name = "LDO6", },
+	{ .name = "LDO7", },
+	{ .name = "LDOLN" },
+	{ .name = "LDOUSB" }
+};
+
+static struct of_regulator_match
+twl6032_fixed_reg_matches[] = {
+	{ .name = "VANA", },
+};
+
+#define TWL6032_LDO_REG_NUM ARRAY_SIZE(twl6032_ldo_reg_matches)
+#define TWL6032_FIXED_REG_NUM ARRAY_SIZE(twl6032_fixed_reg_matches)
+
+struct twl6032_regulator_priv {
+	struct twl6032_regulator ldo_regulators[TWL6032_LDO_REG_NUM];
+	struct twl6032_regulator fixed_regulators[TWL6032_FIXED_REG_NUM];
+};
+
+static int twl6032_regulator_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *regulators;
+	struct of_regulator_match *match;
+	struct twlcore *twl = dev_get_drvdata(pdev->dev.parent);
+	struct twl6032_regulator_priv *priv;
+	struct regulator_config config = {
+		.dev = &pdev->dev,
+	};
+	struct regulator_dev *rdev;
+	int ret, i;
+
+	if (!dev->of_node) {
+		dev_err(&pdev->dev, "no DT info\n");
+		return -EINVAL;
+	}
+
+	regulators = of_get_child_by_name(dev->of_node, "regulators");
+	if (!regulators) {
+		dev_err(dev, "regulator node not found\n");
+		return -EINVAL;
+	}
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, priv);
+
+	/* LDO regulators parsing */
+	ret = of_regulator_match(dev, regulators, twl6032_ldo_reg_matches,
+				 TWL6032_LDO_REG_NUM);
+	of_node_put(regulators);
+	if (ret < 0) {
+		dev_err(dev, "error parsing LDO reg init data: %d\n", ret);
+		return ret;
+	}
+
+	for (i = 0; i < TWL6032_LDO_REG_NUM; i++) {
+		struct twl6032_regulator *twl6032_reg;
+
+		match = &twl6032_ldo_reg_matches[i];
+		if (!match->of_node)
+			continue;
+
+		twl6032_reg = &priv->ldo_regulators[i];
+		twl6032_reg->info = &twl6032_ldo_reg_info[i];
+
+		config.init_data = match->init_data;
+		config.driver_data = &priv->ldo_regulators[i];
+		config.regmap = twl->twl_modules[0].regmap;
+		config.of_node = match->of_node;
+
+		rdev = devm_regulator_register(dev, &twl6032_reg->info->desc,
+					       &config);
+		if (IS_ERR(rdev)) {
+			ret = PTR_ERR(rdev);
+			dev_err(dev, "failed to register regulator %s: %d\n",
+				twl6032_reg->info->desc.name, ret);
+			return ret;
+		}
+	}
+
+	/* Fixed regulators parsing */
+	ret = of_regulator_match(dev, regulators, twl6032_fixed_reg_matches,
+				 TWL6032_FIXED_REG_NUM);
+	of_node_put(regulators);
+	if (ret < 0) {
+		dev_err(dev, "error parsing fixed reg init data: %d\n", ret);
+		return ret;
+	}
+
+	for (i = 0; i < TWL6032_FIXED_REG_NUM; i++) {
+		struct twl6032_regulator *twl6032_reg;
+
+		match = &twl6032_fixed_reg_matches[i];
+		if (!match->of_node)
+			continue;
+
+		twl6032_reg = &priv->fixed_regulators[i];
+		twl6032_reg->info = &twl6032_fixed_reg_info[i];
+
+		config.init_data = match->init_data;
+		config.driver_data = &priv->fixed_regulators[i];
+		config.regmap = twl->twl_modules[0].regmap;
+		config.of_node = match->of_node;
+
+		rdev = devm_regulator_register(dev, &twl6032_reg->info->desc,
+					       &config);
+		if (IS_ERR(rdev)) {
+			ret = PTR_ERR(rdev);
+			dev_err(dev, "failed to register regulator %s: %d\n",
+				twl6032_reg->info->desc.name, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int twl6032_regulator_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id twl6032_dt_match[] = {
+	{ .compatible = "ti,twl6032-regulator" },
+	{ /* last entry */ }
+};
+
+MODULE_DEVICE_TABLE(platform, twl6032_regulator_driver_ids);
+
+static struct platform_driver twl6032_regulator_driver = {
+	.driver = {
+		.name = "twl6032-regulator",
+		.of_match_table = twl6032_dt_match,
+	},
+	.probe = twl6032_regulator_probe,
+	.remove = twl6032_regulator_remove,
+};
+
+static int __init twl6032_regulator_init(void)
+{
+	return platform_driver_register(&twl6032_regulator_driver);
+}
+subsys_initcall(twl6032_regulator_init);
+
+static void __exit twl6032_regulator_exit(void)
+{
+	platform_driver_unregister(&twl6032_regulator_driver);
+}
+module_exit(twl6032_regulator_exit);
+
+MODULE_AUTHOR("Nicolae Rosia <nicolae.rosia-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>");
+MODULE_DESCRIPTION("TI TWL6032 Regulator Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.9.3

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 3/5] mfd: twl: move structure definitions to a public header
From: Nicolae Rosia @ 2016-11-26 18:13 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Rob Herring, Mark Rutland, Tony Lindgren
  Cc: Liam Girdwood, Paul Gortmaker, Graeme Gregory, Baruch Siach,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Nicolae Rosia
In-Reply-To: <20161126181326.14951-1-Nicolae_Rosia-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>

We want to get rid of exported symbols and have
the child devices use structure members directly.
Move the structure definitions to header and set
drvdata so child devices can access it.

Signed-off-by: Nicolae Rosia <Nicolae_Rosia-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>
---
 drivers/mfd/twl-core.c       | 27 ++++-----------------------
 include/linux/mfd/twl-core.h | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 39 insertions(+), 23 deletions(-)
 create mode 100644 include/linux/mfd/twl-core.h

diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index e16084e..409b836 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -48,6 +48,7 @@
 
 #include <linux/i2c.h>
 #include <linux/i2c/twl.h>
+#include <linux/mfd/twl-core.h>
 
 /* Register descriptions for audio */
 #include <linux/mfd/twl4030-audio.h>
@@ -154,28 +155,7 @@ int twl4030_init_irq(struct device *dev, int irq_num);
 int twl4030_exit_irq(void);
 int twl4030_init_chip_irq(const char *chip);
 
-/* Structure for each TWL4030/TWL6030 Slave */
-struct twl_client {
-	struct i2c_client *client;
-	struct regmap *regmap;
-};
-
-/* mapping the module id to slave id and base address */
-struct twl_mapping {
-	unsigned char sid;	/* Slave ID */
-	unsigned char base;	/* base address */
-};
-
-struct twl_private {
-	bool ready; /* The core driver is ready to be used */
-	u32 twl_idcode; /* TWL IDCODE Register value */
-	unsigned int twl_id;
-
-	struct twl_mapping *twl_map;
-	struct twl_client *twl_modules;
-};
-
-static struct twl_private *twl_priv;
+static struct twlcore *twl_priv;
 
 static struct twl_mapping twl4030_map[] = {
 	/*
@@ -745,7 +725,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
 		goto free;
 	}
 
-	twl_priv = devm_kzalloc(&client->dev, sizeof(struct twl_private),
+	twl_priv = devm_kzalloc(&client->dev, sizeof(struct twlcore),
 				GFP_KERNEL);
 	if (!twl_priv) {
 		status = -ENOMEM;
@@ -803,6 +783,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
 	}
 
 	twl_priv->ready = true;
+	dev_set_drvdata(&client->dev, twl_priv);
 
 	/* setup clock framework */
 	clocks_init(&pdev->dev);
diff --git a/include/linux/mfd/twl-core.h b/include/linux/mfd/twl-core.h
new file mode 100644
index 0000000..d1c01b3
--- /dev/null
+++ b/include/linux/mfd/twl-core.h
@@ -0,0 +1,35 @@
+/*
+ * MFD core driver for the Texas Instruments TWL PMIC family
+ *
+ * Copyright (C) 2016 Nicolae Rosia <nicolae.rosia-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __TWL_CORE_H__
+#define __TWL_CORE_H__
+
+/* Structure for each TWL4030/TWL6030 Slave */
+struct twl_client {
+	struct i2c_client *client;
+	struct regmap *regmap;
+};
+
+/* mapping the module id to slave id and base address */
+struct twl_mapping {
+	unsigned char sid; /* Slave ID */
+	unsigned char base; /* base address */
+};
+
+struct twlcore {
+	bool ready; /* The core driver is ready to be used */
+	u32 twl_idcode; /* TWL IDCODE Register value */
+	unsigned int twl_id;
+
+	struct twl_mapping *twl_map;
+	struct twl_client *twl_modules;
+};
+
+#endif
-- 
2.9.3

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 2/5] mfd: twl: remove useless header
From: Nicolae Rosia @ 2016-11-26 18:13 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Rob Herring, Mark Rutland, Tony Lindgren
  Cc: Liam Girdwood, Paul Gortmaker, Graeme Gregory, Baruch Siach,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Nicolae Rosia
In-Reply-To: <20161126181326.14951-1-Nicolae_Rosia-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>

This header has one user, twl-core.c .
Remove it before it gets more users.

Signed-off-by: Nicolae Rosia <Nicolae_Rosia-nmGgyN9QBj3QT0dZR+AlfA@public.gmane.org>
---
 drivers/mfd/twl-core.c    |  8 ++++++--
 drivers/mfd/twl-core.h    | 10 ----------
 drivers/mfd/twl4030-irq.c |  2 --
 drivers/mfd/twl6030-irq.c |  2 --
 4 files changed, 6 insertions(+), 16 deletions(-)
 delete mode 100644 drivers/mfd/twl-core.h

diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 48b0668..e16084e 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -52,8 +52,6 @@
 /* Register descriptions for audio */
 #include <linux/mfd/twl4030-audio.h>
 
-#include "twl-core.h"
-
 /*
  * The TWL4030 "Triton 2" is one of a family of a multi-function "Power
  * Management and System Companion Device" chips originally designed for
@@ -150,6 +148,12 @@
 
 /*----------------------------------------------------------------------*/
 
+int twl6030_init_irq(struct device *dev, int irq_num);
+int twl6030_exit_irq(void);
+int twl4030_init_irq(struct device *dev, int irq_num);
+int twl4030_exit_irq(void);
+int twl4030_init_chip_irq(const char *chip);
+
 /* Structure for each TWL4030/TWL6030 Slave */
 struct twl_client {
 	struct i2c_client *client;
diff --git a/drivers/mfd/twl-core.h b/drivers/mfd/twl-core.h
deleted file mode 100644
index 6ff99dc..0000000
--- a/drivers/mfd/twl-core.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef __TWL_CORE_H__
-#define __TWL_CORE_H__
-
-extern int twl6030_init_irq(struct device *dev, int irq_num);
-extern int twl6030_exit_irq(void);
-extern int twl4030_init_irq(struct device *dev, int irq_num);
-extern int twl4030_exit_irq(void);
-extern int twl4030_init_chip_irq(const char *chip);
-
-#endif /*  __TWL_CORE_H__ */
diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c
index b46c0cf..000c231 100644
--- a/drivers/mfd/twl4030-irq.c
+++ b/drivers/mfd/twl4030-irq.c
@@ -35,8 +35,6 @@
 #include <linux/irqdomain.h>
 #include <linux/i2c/twl.h>
 
-#include "twl-core.h"
-
 /*
  * TWL4030 IRQ handling has two stages in hardware, and thus in software.
  * The Primary Interrupt Handler (PIH) stage exposes status bits saying
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c
index 5357450..63eca76 100644
--- a/drivers/mfd/twl6030-irq.c
+++ b/drivers/mfd/twl6030-irq.c
@@ -42,8 +42,6 @@
 #include <linux/irqdomain.h>
 #include <linux/of_device.h>
 
-#include "twl-core.h"
-
 /*
  * TWL6030 (unlike its predecessors, which had two level interrupt handling)
  * three interrupt registers INT_STS_A, INT_STS_B and INT_STS_C.
-- 
2.9.3

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 1/5] mfd: twl-core: make driver DT only
From: Nicolae Rosia @ 2016-11-26 18:13 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Rob Herring, Mark Rutland, Tony Lindgren
  Cc: Liam Girdwood, Paul Gortmaker, Graeme Gregory, Baruch Siach,
	linux-omap, linux-arm-kernel, linux-kernel, devicetree,
	Nicolae Rosia
In-Reply-To: <20161126181326.14951-1-Nicolae_Rosia@mentor.com>

All users are DT-only and it makes no sense to keep
unused code

Signed-off-by: Nicolae Rosia <Nicolae_Rosia@mentor.com>
---
 drivers/mfd/Kconfig    |   1 +
 drivers/mfd/twl-core.c | 399 +------------------------------------------------
 2 files changed, 8 insertions(+), 392 deletions(-)

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index c6df644..c180f8b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1333,6 +1333,7 @@ config MFD_TPS80031
 config TWL4030_CORE
 	bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 Support"
 	depends on I2C=y
+	depends on OF
 	select IRQ_DOMAIN
 	select REGMAP_I2C
 	help
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index c64615d..48b0668 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -13,6 +13,9 @@
  * Code cleanup and modifications to IRQ handler.
  * by syed khasim <x0khasim@ti.com>
  *
+ * Code cleanup and modifications:
+ * Copyright (C) 2016 Nicolae Rosia <nicolae.rosia@gmail.com>
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -604,376 +607,6 @@ int twl_get_hfclk_rate(void)
 }
 EXPORT_SYMBOL_GPL(twl_get_hfclk_rate);
 
-static struct device *
-add_numbered_child(unsigned mod_no, const char *name, int num,
-		void *pdata, unsigned pdata_len,
-		bool can_wakeup, int irq0, int irq1)
-{
-	struct platform_device	*pdev;
-	struct twl_client	*twl;
-	int			status, sid;
-
-	if (unlikely(mod_no >= twl_get_last_module())) {
-		pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no);
-		return ERR_PTR(-EPERM);
-	}
-	sid = twl_priv->twl_map[mod_no].sid;
-	twl = &twl_priv->twl_modules[sid];
-
-	pdev = platform_device_alloc(name, num);
-	if (!pdev)
-		return ERR_PTR(-ENOMEM);
-
-	pdev->dev.parent = &twl->client->dev;
-
-	if (pdata) {
-		status = platform_device_add_data(pdev, pdata, pdata_len);
-		if (status < 0) {
-			dev_dbg(&pdev->dev, "can't add platform_data\n");
-			goto put_device;
-		}
-	}
-
-	if (irq0) {
-		struct resource r[2] = {
-			{ .start = irq0, .flags = IORESOURCE_IRQ, },
-			{ .start = irq1, .flags = IORESOURCE_IRQ, },
-		};
-
-		status = platform_device_add_resources(pdev, r, irq1 ? 2 : 1);
-		if (status < 0) {
-			dev_dbg(&pdev->dev, "can't add irqs\n");
-			goto put_device;
-		}
-	}
-
-	status = platform_device_add(pdev);
-	if (status)
-		goto put_device;
-
-	device_init_wakeup(&pdev->dev, can_wakeup);
-
-	return &pdev->dev;
-
-put_device:
-	platform_device_put(pdev);
-	dev_err(&twl->client->dev, "failed to add device %s\n", name);
-	return ERR_PTR(status);
-}
-
-static inline struct device *add_child(unsigned mod_no, const char *name,
-		void *pdata, unsigned pdata_len,
-		bool can_wakeup, int irq0, int irq1)
-{
-	return add_numbered_child(mod_no, name, -1, pdata, pdata_len,
-		can_wakeup, irq0, irq1);
-}
-
-static struct device *
-add_regulator_linked(int num, struct regulator_init_data *pdata,
-		struct regulator_consumer_supply *consumers,
-		unsigned num_consumers, unsigned long features)
-{
-	struct twl_regulator_driver_data drv_data;
-
-	/* regulator framework demands init_data ... */
-	if (!pdata)
-		return NULL;
-
-	if (consumers) {
-		pdata->consumer_supplies = consumers;
-		pdata->num_consumer_supplies = num_consumers;
-	}
-
-	if (pdata->driver_data) {
-		/* If we have existing drv_data, just add the flags */
-		struct twl_regulator_driver_data *tmp;
-		tmp = pdata->driver_data;
-		tmp->features |= features;
-	} else {
-		/* add new driver data struct, used only during init */
-		drv_data.features = features;
-		drv_data.set_voltage = NULL;
-		drv_data.get_voltage = NULL;
-		drv_data.data = NULL;
-		pdata->driver_data = &drv_data;
-	}
-
-	/* NOTE:  we currently ignore regulator IRQs, e.g. for short circuits */
-	return add_numbered_child(TWL_MODULE_PM_MASTER, "twl_reg", num,
-		pdata, sizeof(*pdata), false, 0, 0);
-}
-
-static struct device *
-add_regulator(int num, struct regulator_init_data *pdata,
-		unsigned long features)
-{
-	return add_regulator_linked(num, pdata, NULL, 0, features);
-}
-
-/*
- * NOTE:  We know the first 8 IRQs after pdata->base_irq are
- * for the PIH, and the next are for the PWR_INT SIH, since
- * that's how twl_init_irq() sets things up.
- */
-
-static int
-add_children(struct twl4030_platform_data *pdata, unsigned irq_base,
-		unsigned long features)
-{
-	struct device	*child;
-
-	if (IS_ENABLED(CONFIG_GPIO_TWL4030) && pdata->gpio) {
-		child = add_child(TWL4030_MODULE_GPIO, "twl4030_gpio",
-				pdata->gpio, sizeof(*pdata->gpio),
-				false, irq_base + GPIO_INTR_OFFSET, 0);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-	}
-
-	if (IS_ENABLED(CONFIG_KEYBOARD_TWL4030) && pdata->keypad) {
-		child = add_child(TWL4030_MODULE_KEYPAD, "twl4030_keypad",
-				pdata->keypad, sizeof(*pdata->keypad),
-				true, irq_base + KEYPAD_INTR_OFFSET, 0);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-	}
-
-	if (IS_ENABLED(CONFIG_TWL4030_MADC) && pdata->madc &&
-	    twl_class_is_4030()) {
-		child = add_child(TWL4030_MODULE_MADC, "twl4030_madc",
-				pdata->madc, sizeof(*pdata->madc),
-				true, irq_base + MADC_INTR_OFFSET, 0);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-	}
-
-	if (IS_ENABLED(CONFIG_RTC_DRV_TWL4030)) {
-		/*
-		 * REVISIT platform_data here currently might expose the
-		 * "msecure" line ... but for now we just expect board
-		 * setup to tell the chip "it's always ok to SET_TIME".
-		 * Eventually, Linux might become more aware of such
-		 * HW security concerns, and "least privilege".
-		 */
-		child = add_child(TWL_MODULE_RTC, "twl_rtc", NULL, 0,
-				true, irq_base + RTC_INTR_OFFSET, 0);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-	}
-
-	if (IS_ENABLED(CONFIG_PWM_TWL)) {
-		child = add_child(TWL_MODULE_PWM, "twl-pwm", NULL, 0,
-				  false, 0, 0);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-	}
-
-	if (IS_ENABLED(CONFIG_PWM_TWL_LED)) {
-		child = add_child(TWL_MODULE_LED, "twl-pwmled", NULL, 0,
-				  false, 0, 0);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-	}
-
-	if (IS_ENABLED(CONFIG_TWL4030_USB) && pdata->usb &&
-	    twl_class_is_4030()) {
-
-		static struct regulator_consumer_supply usb1v5 = {
-			.supply =	"usb1v5",
-		};
-		static struct regulator_consumer_supply usb1v8 = {
-			.supply =	"usb1v8",
-		};
-		static struct regulator_consumer_supply usb3v1 = {
-			.supply =	"usb3v1",
-		};
-
-	/* First add the regulators so that they can be used by transceiver */
-		if (IS_ENABLED(CONFIG_REGULATOR_TWL4030)) {
-			/* this is a template that gets copied */
-			struct regulator_init_data usb_fixed = {
-				.constraints.valid_modes_mask =
-					REGULATOR_MODE_NORMAL
-					| REGULATOR_MODE_STANDBY,
-				.constraints.valid_ops_mask =
-					REGULATOR_CHANGE_MODE
-					| REGULATOR_CHANGE_STATUS,
-			};
-
-			child = add_regulator_linked(TWL4030_REG_VUSB1V5,
-						      &usb_fixed, &usb1v5, 1,
-						      features);
-			if (IS_ERR(child))
-				return PTR_ERR(child);
-
-			child = add_regulator_linked(TWL4030_REG_VUSB1V8,
-						      &usb_fixed, &usb1v8, 1,
-						      features);
-			if (IS_ERR(child))
-				return PTR_ERR(child);
-
-			child = add_regulator_linked(TWL4030_REG_VUSB3V1,
-						      &usb_fixed, &usb3v1, 1,
-						      features);
-			if (IS_ERR(child))
-				return PTR_ERR(child);
-
-		}
-
-		child = add_child(TWL_MODULE_USB, "twl4030_usb",
-				pdata->usb, sizeof(*pdata->usb), true,
-				/* irq0 = USB_PRES, irq1 = USB */
-				irq_base + USB_PRES_INTR_OFFSET,
-				irq_base + USB_INTR_OFFSET);
-
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-
-		/* we need to connect regulators to this transceiver */
-		if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && child) {
-			usb1v5.dev_name = dev_name(child);
-			usb1v8.dev_name = dev_name(child);
-			usb3v1.dev_name = dev_name(child);
-		}
-	}
-
-	if (IS_ENABLED(CONFIG_TWL4030_WATCHDOG) && twl_class_is_4030()) {
-		child = add_child(TWL_MODULE_PM_RECEIVER, "twl4030_wdt", NULL,
-				  0, false, 0, 0);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-	}
-
-	if (IS_ENABLED(CONFIG_INPUT_TWL4030_PWRBUTTON) && twl_class_is_4030()) {
-		child = add_child(TWL_MODULE_PM_MASTER, "twl4030_pwrbutton",
-				  NULL, 0, true, irq_base + 8 + 0, 0);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-	}
-
-	if (IS_ENABLED(CONFIG_MFD_TWL4030_AUDIO) && pdata->audio &&
-	    twl_class_is_4030()) {
-		child = add_child(TWL4030_MODULE_AUDIO_VOICE, "twl4030-audio",
-				pdata->audio, sizeof(*pdata->audio),
-				false, 0, 0);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-	}
-
-	/* twl4030 regulators */
-	if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && twl_class_is_4030()) {
-		child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1,
-					features);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-
-		child = add_regulator(TWL4030_REG_VIO, pdata->vio,
-					features);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-
-		child = add_regulator(TWL4030_REG_VDD1, pdata->vdd1,
-					features);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-
-		child = add_regulator(TWL4030_REG_VDD2, pdata->vdd2,
-					features);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-
-		child = add_regulator(TWL4030_REG_VMMC1, pdata->vmmc1,
-					features);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-
-		child = add_regulator(TWL4030_REG_VDAC, pdata->vdac,
-					features);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-
-		child = add_regulator((features & TWL4030_VAUX2)
-					? TWL4030_REG_VAUX2_4030
-					: TWL4030_REG_VAUX2,
-				pdata->vaux2, features);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-
-		child = add_regulator(TWL4030_REG_VINTANA1, pdata->vintana1,
-					features);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-
-		child = add_regulator(TWL4030_REG_VINTANA2, pdata->vintana2,
-					features);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-
-		child = add_regulator(TWL4030_REG_VINTDIG, pdata->vintdig,
-					features);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-	}
-
-	/* maybe add LDOs that are omitted on cost-reduced parts */
-	if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && !(features & TPS_SUBSET)
-	  && twl_class_is_4030()) {
-		child = add_regulator(TWL4030_REG_VPLL2, pdata->vpll2,
-					features);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-
-		child = add_regulator(TWL4030_REG_VMMC2, pdata->vmmc2,
-					features);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-
-		child = add_regulator(TWL4030_REG_VSIM, pdata->vsim,
-					features);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-
-		child = add_regulator(TWL4030_REG_VAUX1, pdata->vaux1,
-					features);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-
-		child = add_regulator(TWL4030_REG_VAUX3, pdata->vaux3,
-					features);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-
-		child = add_regulator(TWL4030_REG_VAUX4, pdata->vaux4,
-					features);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-	}
-
-	if (IS_ENABLED(CONFIG_CHARGER_TWL4030) && pdata->bci &&
-			!(features & (TPS_SUBSET | TWL5031))) {
-		child = add_child(TWL_MODULE_MAIN_CHARGE, "twl4030_bci",
-				pdata->bci, sizeof(*pdata->bci), false,
-				/* irq0 = CHG_PRES, irq1 = BCI */
-				irq_base + BCI_PRES_INTR_OFFSET,
-				irq_base + BCI_INTR_OFFSET);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-	}
-
-	if (IS_ENABLED(CONFIG_TWL4030_POWER) && pdata->power) {
-		child = add_child(TWL_MODULE_PM_MASTER, "twl4030_power",
-				  pdata->power, sizeof(*pdata->power), false,
-				  0, 0);
-		if (IS_ERR(child))
-			return PTR_ERR(child);
-	}
-
-	return 0;
-}
-
-/*----------------------------------------------------------------------*/
-
 /*
  * These three functions initialize the on-chip clock framework,
  * letting it generate the right frequencies for USB, MADC, and
@@ -1000,8 +633,7 @@ static inline int __init unprotect_pm_master(void)
 	return e;
 }
 
-static void clocks_init(struct device *dev,
-			struct twl4030_clock_init_data *clock)
+static void clocks_init(struct device *dev)
 {
 	int e = 0;
 	struct clk *osc;
@@ -1031,8 +663,6 @@ static void clocks_init(struct device *dev,
 	}
 
 	ctrl |= HIGH_PERF_SQ;
-	if (clock && clock->ck32k_lowpwr_enable)
-		ctrl |= CK32K_LOWPWR_EN;
 
 	e |= unprotect_pm_master();
 	/* effect->MADC+USB ck en */
@@ -1071,16 +701,10 @@ static int twl_remove(struct i2c_client *client)
 	return 0;
 }
 
-static struct of_dev_auxdata twl_auxdata_lookup[] = {
-	OF_DEV_AUXDATA("ti,twl4030-gpio", 0, "twl4030-gpio", NULL),
-	{ /* sentinel */ },
-};
-
 /* NOTE: This driver only handles a single twl4030/tps659x0 chip */
 static int
 twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
 {
-	struct twl4030_platform_data	*pdata = dev_get_platdata(&client->dev);
 	struct device_node		*node = client->dev.of_node;
 	struct platform_device		*pdev;
 	const struct regmap_config	*twl_regmap_config;
@@ -1088,8 +712,8 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
 	int				status;
 	unsigned			i, num_slaves;
 
-	if (!node && !pdata) {
-		dev_err(&client->dev, "no platform data\n");
+	if (!node) {
+		dev_err(&client->dev, "no DT info\n");
 		return -EINVAL;
 	}
 
@@ -1177,7 +801,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
 	twl_priv->ready = true;
 
 	/* setup clock framework */
-	clocks_init(&pdev->dev, pdata ? pdata->clock : NULL);
+	clocks_init(&pdev->dev);
 
 	/* read TWL IDCODE Register */
 	if (twl_class_is_4030()) {
@@ -1225,15 +849,6 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)
 				 TWL4030_DCDC_GLOBAL_CFG);
 	}
 
-	if (node) {
-		if (pdata)
-			twl_auxdata_lookup[0].platform_data = pdata->gpio;
-		status = of_platform_populate(node, NULL, twl_auxdata_lookup,
-					      &client->dev);
-	} else {
-		status = add_children(pdata, irq_base, id->driver_data);
-	}
-
 fail:
 	if (status < 0)
 		twl_remove(client);
-- 
2.9.3

^ permalink raw reply related

* [PATCH 0/5] mfd: twl: improvements and new regulator driver
From: Nicolae Rosia @ 2016-11-26 18:13 UTC (permalink / raw)
  To: Lee Jones, Mark Brown, Rob Herring, Mark Rutland, Tony Lindgren
  Cc: Liam Girdwood, Paul Gortmaker, Graeme Gregory, Baruch Siach,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Nicolae Rosia

Hello,

The current TWL MFD driver has a number of problems which are
very well described by Russell King [0].
This series attemps to fix this by making the driver's private
structure available to child nodes.
A regulator driver for TWL6032 which makes use of the private
drvdata is introduced.
A driver for TWL6032 PMIC already exists in mainline,
twl-regulator, but it has the following drawbacks:
* has no mainline users
* it does not follow the recommended regulators binding since
it uses a compatible string for every regulator;
* it is broken
** the features flag is not set, hence the TWL6032
support is broken since it depends on TWL6032_SUBCLASS flag;
** even with that fixed, bit manipulations are wrong

If this receives positive feedback, I could convert all TWL drivers
to use drvdata, then get rid of the exported symbols.

[0] https://www.spinics.net/lists/linux-omap/msg133387.html

Nicolae Rosia (5):
  mfd: twl-core: make driver DT only
  mfd: twl: remove useless header
  mfd: twl: move structure definitions to a public header
  regulator: Add support for TI TWL6032
  mfd: twl: use mfd_add_devices for TWL6032 regulator

 .../bindings/regulator/twl6032-regulator.txt       | 109 ++++
 drivers/mfd/Kconfig                                |   1 +
 drivers/mfd/twl-core.c                             | 444 ++--------------
 drivers/mfd/twl-core.h                             |  10 -
 drivers/mfd/twl4030-irq.c                          |   2 -
 drivers/mfd/twl6030-irq.c                          |   2 -
 drivers/regulator/Kconfig                          |   7 +
 drivers/regulator/Makefile                         |   1 +
 drivers/regulator/twl6032-regulator.c              | 582 +++++++++++++++++++++
 include/linux/mfd/twl-core.h                       |  35 ++
 10 files changed, 768 insertions(+), 425 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/regulator/twl6032-regulator.txt
 delete mode 100644 drivers/mfd/twl-core.h
 create mode 100644 drivers/regulator/twl6032-regulator.c
 create mode 100644 include/linux/mfd/twl-core.h

-- 
2.9.3

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH] arm64: dts: uniphier: add SD-ctrl node for LD11 SoC
From: Masahiro Yamada @ 2016-11-26 18:10 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Masahiro Yamada, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Will Deacon,
	Mark Rutland, Catalin Marinas

The LD11 SoC is equipped with SD-ctrl (0x59810000) as well as
MIO-ctrl (0x5b3e0000).  The SD-ctrl block on this SoC has just
one register for controlling RST_n pin of the eMMC device.

Signed-off-by: Masahiro Yamada <yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>
---

 arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi b/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi
index 0e5c58f..7a62fb9 100644
--- a/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi
+++ b/arch/arm64/boot/dts/socionext/uniphier-ld11.dtsi
@@ -273,6 +273,17 @@
 			reg = <0x59801000 0x400>;
 		};
 
+		sdctrl@59810000 {
+			compatible = "socionext,uniphier-ld11-sdctrl",
+				     "simple-mfd", "syscon";
+			reg = <0x59810000 0x400>;
+
+			sd_rst: reset {
+				compatible = "socionext,uniphier-ld11-sd-reset";
+				#reset-cells = <1>;
+			};
+		};
+
 		perictrl@59820000 {
 			compatible = "socionext,uniphier-perictrl",
 				     "simple-mfd", "syscon";
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 39/39] mtd: nand: denali_dt: add compatible strings for UniPhier SoC variants
From: Masahiro Yamada @ 2016-11-26 18:06 UTC (permalink / raw)
  To: linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Masahiro Yamada, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Boris Brezillon, Marek Vasut,
	Brian Norris, Richard Weinberger, David Woodhouse,
	Cyrille Pitchen, Rob Herring, Mark Rutland
In-Reply-To: <1480183585-592-1-git-send-email-yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>

Add two compatible strings for UniPhier SoCs.  The revision register
on both shows revision 5.0, but they are different hardware.

Features:
 - DMA engine with 64 bit physical address support
 - 1024 byte ECC step size
 - 8 / 16 / 24 bit ECC strength
 - The n_banks format depends on SoC

Signed-off-by: Masahiro Yamada <yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>
---

 .../devicetree/bindings/mtd/denali-nand.txt        | 10 +++++--
 drivers/mtd/nand/denali_dt.c                       | 33 ++++++++++++++++++++--
 2 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
index 51fe195..cea46e2 100644
--- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
@@ -1,13 +1,19 @@
 * Denali NAND controller
 
 Required properties:
-  - compatible : should be "denali,denali-nand-dt"
+  - compatible : should be one of the following:
+      "denali,denali-nand-dt"
+      "denali,denali-nand-uniphier-v5a"
+      "denali,denali-nand-uniphier-v5b"
   - reg : should contain registers location and length for data and reg.
   - reg-names: Should contain the reg names "nand_data" and "denali_reg"
   - interrupts : The interrupt number.
 
 Optional properties:
-  - nand-ecc-step-size: must be 512 or 1024.  If not specified, default to 512.
+  - nand-ecc-step-size: must be 512 or 1024.  If not specified, default to:
+      512  for "denali,denali-nand-dt"
+      1024 for "denali,denali-nand-uniphier-v5a"
+      1024 for "denali,denali-nand-uniphier-v5b"
     see nand.txt for details.
   - nand-ecc-strength: see nand.txt for details
   - nand-ecc-maximize: see nand.txt for details
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index aa1e032..b411889 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -34,10 +34,37 @@ struct denali_dt_data {
 	unsigned int caps;
 };
 
+static const int denali_uniphier_ecc_strength[] = {
+	24, 16, 8, 0,
+};
+
+static const struct denali_dt_data denali_uniphier_v5a_data = {
+	.ecc_strength_avail = denali_uniphier_ecc_strength,
+	.caps = DENALI_CAPS_DMA_64BIT |
+		DENALI_CAPS_ECC_SIZE_1024,
+};
+
+static const struct denali_dt_data denali_uniphier_v5b_data = {
+	.ecc_strength_avail = denali_uniphier_ecc_strength,
+	.caps = DENALI_CAPS_DMA_64BIT |
+		DENALI_CAPS_NEW_N_BANKS_FORMAT |
+		DENALI_CAPS_ECC_SIZE_1024,
+};
+
 static const struct of_device_id denali_nand_dt_ids[] = {
-		{ .compatible = "denali,denali-nand-dt" },
-		{ /* sentinel */ }
-	};
+	{
+		.compatible = "denali,denali-nand-dt",
+	},
+	{
+		.compatible = "denali,denali-nand-uniphier-v5a",
+		.data = &denali_uniphier_v5a_data,
+	},
+	{
+		.compatible = "denali,denali-nand-uniphier-v5b",
+		.data = &denali_uniphier_v5b_data,
+	},
+	{ /* sentinel */ }
+};
 
 MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
 
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 37/39] mtd: nand: denali: support "nand-ecc-strength" DT property
From: Masahiro Yamada @ 2016-11-26 18:06 UTC (permalink / raw)
  To: linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Masahiro Yamada, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Boris Brezillon, Marek Vasut,
	Brian Norris, Richard Weinberger, David Woodhouse,
	Cyrille Pitchen, Rob Herring, Mark Rutland
In-Reply-To: <1480183585-592-1-git-send-email-yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>

Historically, this driver tried to choose as big ECC strength as
possible, but it would be reasonable to allow DT to set a particular
ECC strength with "nand-ecc-strength" property.

Going forward, DT platforms should specify "nand-ecc-strength" or
"nand-ecc-maximize" to show the ECC strength strategy explicitly.

If nothing is specified in DT, "nand-ecc-maximize" is implied since
this was the original behavior.  It applies to PCI platforms too.

Signed-off-by: Masahiro Yamada <yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>
---

 .../devicetree/bindings/mtd/denali-nand.txt        |  5 ++++
 drivers/mtd/nand/denali.c                          | 27 +++++++++++++++++++++-
 drivers/mtd/nand/denali_pci.c                      |  2 ++
 3 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
index e9d5818..51fe195 100644
--- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
@@ -9,6 +9,11 @@ Required properties:
 Optional properties:
   - nand-ecc-step-size: must be 512 or 1024.  If not specified, default to 512.
     see nand.txt for details.
+  - nand-ecc-strength: see nand.txt for details
+  - nand-ecc-maximize: see nand.txt for details
+
+Note:
+Either nand-ecc-strength or nand-ecc-maximize should be specified.
 
 The device tree may optionally contain sub-nodes describing partitions of the
 address space. See partition.txt for more detail.
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 54c9e0c..df174ca 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -1393,6 +1393,21 @@ static int denali_set_max_ecc_strength(struct denali_nand_info *denali)
 	return -EINVAL;
 }
 
+static int denali_check_ecc_strength(struct denali_nand_info *denali)
+{
+	const int *ecc_strength = denali->ecc_strength_avail;
+
+	for (; *ecc_strength; ecc_strength++) {
+		if (*ecc_strength == denali->nand.ecc.strength)
+			return 0;
+	}
+
+	dev_err(denali->dev,
+		"Specified ECC strength is not supported for this controller.\n");
+
+	return -EINVAL;
+}
+
 static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
 				struct mtd_oob_region *oobregion)
 {
@@ -1628,7 +1643,17 @@ int denali_init(struct denali_nand_info *denali)
 	if (!denali->ecc_strength_avail)
 		denali->ecc_strength_avail = denali_default_ecc_strength;
 
-	ret = denali_set_max_ecc_strength(denali);
+	if (!chip->ecc.strength && !(chip->ecc.options & NAND_ECC_MAXIMIZE)) {
+		dev_info(denali->dev,
+			"No ECC strength is specified.  Trying max ECC strength strategy\n");
+		chip->ecc.options |= NAND_ECC_MAXIMIZE;
+	}
+
+	if (chip->ecc.options & NAND_ECC_MAXIMIZE)
+		ret = denali_set_max_ecc_strength(denali);
+	else
+		ret = denali_check_ecc_strength(denali);
+
 	if (ret)
 		goto failed_req_irq;
 
diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c
index ac84323..0064f3e 100644
--- a/drivers/mtd/nand/denali_pci.c
+++ b/drivers/mtd/nand/denali_pci.c
@@ -85,6 +85,8 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 		goto failed_remap_reg;
 	}
 
+	denali->nand.ecc.options |= NAND_ECC_MAXIMIZE;
+
 	ret = denali_init(denali);
 	if (ret)
 		goto failed_remap_mem;
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 33/39] mtd: nand: denali: support 1024 byte ECC step size
From: Masahiro Yamada @ 2016-11-26 18:06 UTC (permalink / raw)
  To: linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Masahiro Yamada, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Boris Brezillon, Marek Vasut,
	Brian Norris, Richard Weinberger, David Woodhouse,
	Cyrille Pitchen, Rob Herring, Mark Rutland
In-Reply-To: <1480183585-592-1-git-send-email-yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>

This driver was originally written for the Intel MRST platform with
several platform specific parameters hard-coded.  Another thing we
need to fix is the hard-coded ECC step size.  Currently, it is
defined as follows:

  #define ECC_SECTOR_SIZE 512

(somehow, it is defined in both denali.c and denali.h)

This must be avoided because the Denali IP supports 1024 byte ECC
size as well.  Add a new flag DENALI_CAPS_ECC_SIZE_1024. If it is
specified, ecc.size is set to 1024, otherwise set to 512.

We can use "nand-ecc-step-size" DT property to override the ecc.size
if we want, but this capability flag can provide the reasonable
default because it is associated with the DT compatible strings.

Signed-off-by: Masahiro Yamada <yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>
---

 .../devicetree/bindings/mtd/denali-nand.txt        |  4 ++++
 drivers/mtd/nand/denali.c                          | 26 +++++++++++-----------
 drivers/mtd/nand/denali.h                          |  3 +--
 3 files changed, 18 insertions(+), 15 deletions(-)

diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
index 603110b..e9d5818 100644
--- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
@@ -6,6 +6,10 @@ Required properties:
   - reg-names: Should contain the reg names "nand_data" and "denali_reg"
   - interrupts : The interrupt number.
 
+Optional properties:
+  - nand-ecc-step-size: must be 512 or 1024.  If not specified, default to 512.
+    see nand.txt for details.
+
 The device tree may optionally contain sub-nodes describing partitions of the
 address space. See partition.txt for more detail.
 
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 63f7500..5d80f16 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -894,8 +894,6 @@ static bool denali_hw_ecc_fixup(struct denali_nand_info *denali,
 	return false;
 }
 
-#define ECC_SECTOR_SIZE 512
-
 #define ECC_SECTOR(x)	(((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12)
 #define ECC_BYTE(x)	(((x) & ECC_ERROR_ADDRESS__OFFSET))
 #define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK)
@@ -908,6 +906,7 @@ static bool denali_sw_ecc_fixup(struct denali_nand_info *denali, u8 *buf,
 {
 	bool check_erased_page = false;
 	unsigned int bitflips = 0;
+	unsigned int ecc_size = denali->nand.ecc.size;
 	u32 err_address, err_correction_info, err_byte, err_sector, err_device,
 	    err_correction_value;
 
@@ -930,18 +929,18 @@ static bool denali_sw_ecc_fixup(struct denali_nand_info *denali, u8 *buf,
 
 		if (ECC_ERROR_CORRECTABLE(err_correction_info)) {
 			/*
-			 * If err_byte is larger than ECC_SECTOR_SIZE, means error
+			 * If err_byte is larger than ecc_size, means error
 			 * happened in OOB, so we ignore it. It's no need for
 			 * us to correct it err_device is represented the NAND
 			 * error bits are happened in if there are more than
 			 * one NAND connected.
 			 */
-			if (err_byte < ECC_SECTOR_SIZE) {
+			if (err_byte < ecc_size) {
 				struct mtd_info *mtd =
 					nand_to_mtd(&denali->nand);
 				int offset;
 
-				offset = (err_sector * ECC_SECTOR_SIZE + err_byte) *
+				offset = (err_sector * ecc_size + err_byte) *
 					denali->devnum + err_device;
 				/* correct the ECC error */
 				buf[offset] ^= err_correction_value;
@@ -1590,22 +1589,25 @@ int denali_init(struct denali_nand_info *denali)
 	/* no subpage writes on denali */
 	chip->options |= NAND_NO_SUBPAGE_WRITE;
 
+	/* If "nand-ecc-step-size" DT property is specified, respect it */
+	if (!chip->ecc.size)
+		chip->ecc.size = denali->caps & DENALI_CAPS_ECC_SIZE_1024 ?
+								1024 : 512;
+
 	/*
 	 * Denali Controller only support 15bit and 8bit ECC in MRST,
 	 * so just let controller do 15bit ECC for MLC and 8bit ECC for
 	 * SLC if possible.
 	 * */
 	if (!nand_is_slc(chip) &&
-			(mtd->oobsize > (denali->bbtskipbytes +
-			ECC_15BITS * (mtd->writesize /
-			ECC_SECTOR_SIZE)))) {
+			mtd->oobsize > denali->bbtskipbytes +
+			ECC_15BITS * (mtd->writesize / chip->ecc.size)) {
 		/* if MLC OOB size is large enough, use 15bit ECC*/
 		chip->ecc.strength = 15;
 		chip->ecc.bytes = ECC_15BITS;
 		iowrite32(15, denali->flash_reg + ECC_CORRECTION);
-	} else if (mtd->oobsize < (denali->bbtskipbytes +
-			ECC_8BITS * (mtd->writesize /
-			ECC_SECTOR_SIZE))) {
+	} else if (mtd->oobsize <
+		   denali->bbtskipbytes + ECC_8BITS * (mtd->writesize / chip->ecc.size)) {
 		pr_err("Your NAND chip OOB is not large enough to contain 8bit ECC correction codes");
 		goto failed_req_irq;
 	} else {
@@ -1616,8 +1618,6 @@ int denali_init(struct denali_nand_info *denali)
 
 	mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
 
-	/* override the default read operations */
-	chip->ecc.size = ECC_SECTOR_SIZE;
 	chip->ecc.read_page = denali_read_page;
 	chip->ecc.read_page_raw = denali_read_page_raw;
 	chip->ecc.write_page = denali_write_page;
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index d621b74..5209625 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -396,8 +396,6 @@
 #define MODE_10    0x08000000
 #define MODE_11    0x0C000000
 
-#define ECC_SECTOR_SIZE     512
-
 struct nand_buf {
 	int head;
 	int tail;
@@ -434,6 +432,7 @@ struct denali_nand_info {
 #define DENALI_CAPS_HW_ECC_FIXUP		BIT(0)
 #define DENALI_CAPS_DMA_64BIT			BIT(1)
 #define DENALI_CAPS_NEW_N_BANKS_FORMAT		BIT(2)
+#define DENALI_CAPS_ECC_SIZE_1024		BIT(3)
 };
 
 extern int denali_init(struct denali_nand_info *denali);
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 22/39] mtd: nand: denali_dt: remove dma-mask DT property
From: Masahiro Yamada @ 2016-11-26 18:06 UTC (permalink / raw)
  To: linux-mtd
  Cc: Masahiro Yamada, devicetree, linux-kernel, Boris Brezillon,
	Marek Vasut, Brian Norris, Richard Weinberger, David Woodhouse,
	Cyrille Pitchen, Rob Herring, Mark Rutland
In-Reply-To: <1480183585-592-1-git-send-email-yamada.masahiro@socionext.com>

The driver sets appropriate DMA mask.  Delete the "dma-mask" DT
property.  Refer to the Link tag for negative opinions for this
binding.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Link: https://lkml.org/lkml/2016/2/8/57
---

 Documentation/devicetree/bindings/mtd/denali-nand.txt | 2 --
 drivers/mtd/nand/denali_dt.c                          | 9 ---------
 2 files changed, 11 deletions(-)

diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
index b04d03a..603110b 100644
--- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
@@ -5,7 +5,6 @@ Required properties:
   - reg : should contain registers location and length for data and reg.
   - reg-names: Should contain the reg names "nand_data" and "denali_reg"
   - interrupts : The interrupt number.
-  - dm-mask : DMA bit mask
 
 The device tree may optionally contain sub-nodes describing partitions of the
 address space. See partition.txt for more detail.
@@ -19,5 +18,4 @@ nand: nand@ff900000 {
 	reg = <0xff900000 0x100000>, <0xffb80000 0x10000>;
 	reg-names = "nand_data", "denali_reg";
 	interrupts = <0 144 4>;
-	dma-mask = <0xffffffff>;
 };
diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c
index 9dcd203..6a486a7 100644
--- a/drivers/mtd/nand/denali_dt.c
+++ b/drivers/mtd/nand/denali_dt.c
@@ -40,8 +40,6 @@ static const struct of_device_id denali_nand_dt_ids[] = {
 
 MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
 
-static u64 denali_dma_mask;
-
 static int denali_dt_probe(struct platform_device *ofdev)
 {
 	struct resource *denali_reg, *nand_data;
@@ -79,13 +77,6 @@ static int denali_dt_probe(struct platform_device *ofdev)
 	if (IS_ERR(denali->flash_mem))
 		return PTR_ERR(denali->flash_mem);
 
-	if (!of_property_read_u32(ofdev->dev.of_node,
-		"dma-mask", (u32 *)&denali_dma_mask)) {
-		denali->dev->dma_mask = &denali_dma_mask;
-	} else {
-		denali->dev->dma_mask = NULL;
-	}
-
 	dt->clk = devm_clk_get(&ofdev->dev, NULL);
 	if (IS_ERR(dt->clk)) {
 		dev_err(&ofdev->dev, "no clk available\n");
-- 
2.7.4

^ permalink raw reply related

* [PATCH 00/39] mtd: nand: denali: 2nd round of Denali NAND IP patch bomb
From: Masahiro Yamada @ 2016-11-26 18:05 UTC (permalink / raw)
  To: linux-mtd
  Cc: Masahiro Yamada, devicetree, linux-kernel, Boris Brezillon,
	Marek Vasut, Brian Norris, Richard Weinberger, David Woodhouse,
	Cyrille Pitchen, Rob Herring, Mark Rutland

As I said in the 1st round series, I am tackling on this driver
to use it for my SoCs.

The previous series was just cosmetic things, but this series
includes *real* changes.

After some more cleanups, I will start to add changes that
are really necessary.
One of the biggest problems I want to solve is a bunch of
hard-coded parameters that prevent me from using this driver for
my SoCs.

I will introduce capability flags that are associated with DT
compatible and make platform-dependent parameters overridable.

I still have lots of reworks to get done (so probably 3rd round
series will come), but I hope it is getting better and
I am showing a big picture now.



Masahiro Yamada (39):
  mtd: nand: allow to set only one of ECC size and ECC strength from DT
  mtd: nand: denali: remove unused CONFIG option and macros
  mtd: nand: denali: remove redundant define of BANK(x)
  mtd: nand: denali: remove more unused struct members
  mtd: nand: denali: fix comment of denali_nand_info::flash_mem
  mtd: nand: denali: fix write_oob_data() function
  mtd: nand: denali: transfer OOB only when oob_required is set
  mtd: nand: denali: introduce capability flag
  mtd: nand: denali: fix erased page check code
  mtd: nand: denali: remove redundant if conditional of erased_check
  mtd: nand: denali: increment ecc_stats.failed by one per error
  mtd: nand: denali: return 0 for uncorrectable ECC error
  mtd: nand: denali: increment ecc_stats->corrected
  mtd: nand: denali: replace uint{8/16/32}_t with u{8/16/32}
  mtd: nand: denali: improve readability of handle_ecc()
  mtd: nand: denali: rename handle_ecc() to denali_sw_ecc_fixup()
  mtd: nand: denali: support HW_ECC_FIXUP capability
  mtd: nand: denali: move denali_read_page_raw() above
    denali_read_page()
  mtd: nand: denali: perform erased check against raw transferred page
  mtd: nand: denali_dt: enable HW_ECC_FIXUP capability for DT platform
  mtd: nand: denali: support 64bit capable DMA engine
  mtd: nand: denali_dt: remove dma-mask DT property
  mtd: nand: denali_dt: use pdev instead of ofdev for platform_device
  mtd: nand: denali: add NEW_N_BANKS_FORMAT capability
  mtd: nand: denali: use nand_chip to hold frequently accessed data
  mtd: nand: denali: call nand_set_flash_node() to set DT node
  mtd: nand: denali: do not set mtd->name
  mtd: nand: denali: move multi NAND fixup code to a helper function
  mtd: nand: denali: refactor multi NAND fixup code in more generic way
  mtd: nand: denali: set DEVICES_CONNECTED 1 if not set
  mtd: nand: denali: remove meaningless writes to read-only registers
  mtd: nand: denali: remove unnecessary writes to ECC_CORRECTION
  mtd: nand: denali: support 1024 byte ECC step size
  mtd: nand: denali: fix the condition for 15 bit ECC strength
  mtd: nand: denali: calculate ecc.strength and ecc.bytes generically
  mtd: nand: denali: allow to use SoC-specific ECC strength
  mtd: nand: denali: support "nand-ecc-strength" DT property
  mtd: nand: denali: remove Toshiba, Hynix specific fixup code
  mtd: nand: denali_dt: add compatible strings for UniPhier SoC variants

 .../devicetree/bindings/mtd/denali-nand.txt        |  19 +-
 drivers/mtd/nand/Kconfig                           |  11 -
 drivers/mtd/nand/denali.c                          | 740 ++++++++++++---------
 drivers/mtd/nand/denali.h                          |  84 +--
 drivers/mtd/nand/denali_dt.c                       |  95 ++-
 drivers/mtd/nand/denali_pci.c                      |   2 +
 drivers/mtd/nand/nand_base.c                       |   6 -
 7 files changed, 515 insertions(+), 442 deletions(-)

-- 
2.7.4

^ permalink raw reply

* [PATCH 5/5] ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
	narmstrong-rdvid1DuHRBWk0Htik3J/w, Martin Blumenstingl
In-Reply-To: <20161126145635.24488-1-martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>

All four devices are only using the dwc3 controller. The actual ports
are provided by dwc3's internal USB hub.
The implementation on P230, P231 and Q201 seems identical: the USB VBUS
supply seems to be hard-wired, while on Q200 the USB VBUS is provided
by GPIOAO_5.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
---
 arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi | 12 ++++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts | 17 +++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
index 7a078be..360c91d 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
@@ -188,3 +188,15 @@
 &ethmac {
 	status = "okay";
 };
+
+&usb2_phys {
+	status = "okay";
+};
+
+&usb3_phy0 {
+	status = "okay";
+};
+
+&usb0 {
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts
index 5dbc660..6c28e87 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts
@@ -49,6 +49,19 @@
 / {
 	compatible = "amlogic,q200", "amlogic,s912", "amlogic,meson-gxm";
 	model = "Amlogic Meson GXM (S912) Q200 Development Board";
+
+	usb_pwr: regulator-usb-pwrs {
+		compatible = "regulator-fixed";
+
+		regulator-name = "USB_PWR";
+
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+
+		/* signal name in schematic: USB_PWR_EN */
+		gpio = <&gpio GPIODV_24 GPIO_ACTIVE_HIGH>;
+		enable-active-high;
+	};
 };
 
 /* Q200 has exclusive choice between internal or external PHY */
@@ -75,3 +88,7 @@
 		max-speed = <1000>;
 	};
 };
+
+&usb3_phy0 {
+	phy-supply = <&usb_pwr>;
+};
-- 
2.10.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 4/5] ARM64: dts: meson-gxm: add GXM specific USB configuration
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
	narmstrong-rdvid1DuHRBWk0Htik3J/w, Martin Blumenstingl
In-Reply-To: <20161126145635.24488-1-martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>

The USB configuration on GXM is slightly different than on GXL. On GXM
the dwc2 controller is limited (via GHWCFG2_OP_MODE_MASK) to "device
mode".
The dwc3 controller's internal hub has 3 ports (instead of 2 on GXL)
enabled. It's hardware configuration limits it (via DWC3_GHWPARAMS0)
to host mode only.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
---
 arch/arm64/boot/dts/amlogic/meson-gxm.dtsi | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
index 2b1d276e..0a6b224 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
@@ -121,3 +121,13 @@
 	clock-indices = <0 1>;
 	clock-output-names = "vbig", "vlittle";
 };
+
+&usb3_phy0 {
+	/* dwc3 on GXM enables 3 USB ports on the internal hub */
+	phys = <&usb2_phys 0>, <&usb2_phys 1>, <&usb2_phys 2>;
+};
+
+&usb1 {
+	/* the dwc2 hardware configuration on GXM only allows device mode */
+	dr_mode = "device";
+};
-- 
2.10.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 3/5] arm64: dts: meson-gxl: add USB support
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
	narmstrong-rdvid1DuHRBWk0Htik3J/w, Martin Blumenstingl
In-Reply-To: <20161126145635.24488-1-martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>

This adds USB support the the Meson GXL SoC. On most devices only the
dwc3 controller is used. It has an internal USB hub which provides two
ports. The PHYs for these ports are defined through the USB3 PHY, which
is ensures that all PHYs are using the same mode (because all of them
are used by the same controller).

Signed-off-by: Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
---
 arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 49 ++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
index 3af54dc..99ac900 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
@@ -44,9 +44,58 @@
 #include "meson-gx.dtsi"
 #include <dt-bindings/clock/gxbb-clkc.h>
 #include <dt-bindings/gpio/meson-gxl-gpio.h>
+#include <dt-bindings/reset/amlogic,meson-gxbb-reset.h>
 
 / {
 	compatible = "amlogic,meson-gxl";
+
+	soc {
+
+		usb0: usb@c9000000 {
+			compatible = "snps,dwc3";
+			reg = <0x0 0xc9000000 0x0 0x100000>;
+			interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+			phys = <&usb3_phy0>;
+			phy-names = "usb3-phy";
+			dr_mode = "host";
+			maximum-speed = "high-speed";
+			snps,dis_u2_susphy_quirk;
+		};
+
+		usb1: usb@c9100000 {
+			compatible = "snps,dwc2";
+			reg = <0x0 0xc9100000 0x0 0x40000>;
+			interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&clkc CLKID_USB>;
+			clock-names = "otg";
+			dr_mode = "host";
+			status = "disabled";
+		};
+	};
+};
+
+&apb {
+	usb2_phys: phy@78000 {
+		compatible = "amlogic,meson-gxl-usb2-phy";
+		#phy-cells = <1>;
+		reg = <0x0 0x78000 0x0 0x80>;
+		clocks = <&clkc CLKID_USB1>, <&clkc CLKID_USB1_DDR_BRIDGE>;
+		clock-names = "usb", "usb_ddr";
+		resets = <&reset RESET_USB_OTG>;
+		status = "disabled";
+	};
+
+	usb3_phy0: phy@78080 {
+		compatible = "amlogic,meson-gxl-usb3-phy";
+		#phy-cells = <0>;
+		reg = <0x0 0x78080 0x0 0x20>;
+		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+
+		/* dwc3 on GXL enables 2 USB ports on the internal hub */
+		phys = <&usb2_phys 0>, <&usb2_phys 1>;
+
+		status = "disabled";
+	};
 };
 
 &ethmac {
-- 
2.10.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 2/5] phy: meson: add USB2 and USB3 PHY support for Meson GXL
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
	narmstrong-rdvid1DuHRBWk0Htik3J/w, Martin Blumenstingl
In-Reply-To: <20161126145635.24488-1-martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>

This adds two new USB PHY drivers found on Meson GXL and GXM SoCs.

The registers for the USB2 PHY block handle a maximum of 4 ports (newer
SoCs may allow more ports, the driver handles this as long as the
register length is adjusted in the .dts). The PHY block theoretically
allows powering down each PHY port separately (by putting it into
"reset" state). Unfortunately this does not work (my board has 2 USB
ports, connected to port 1 and 2 of the dwc3's internal hub. When
leaving the third USB PHY disabled then the hub sees that a device is
plugged in, but it does not work: "usb usb1-port2: connect-debounce
failed").
The USB3 PHY will take care of enabling/disabling all available ports,
because the USB3 PHY also manages the mode of the USB2 PHYs.

The USB3 PHY actually has three purposes:
- it provides the USB3 PHY
- it handles the OTG device/host mode detection interrupt
- it notifies the corresponding USB2 PHYs of the OTG mode changes
On GXL and GXM SoCs one references all available USB2 PHY ports in the
USB3 PHY because all are connected to the same USB controller (thus the
mode will always match). This behavior is configurable via devicetree,
by passing (or not passing) a list of other ("child") PHYs which should
be configured by the USB3 PHY.

Unfortunately there are no datasheets available for any of these PHYs.
Both drivers were written by reading the reference drivers provided by
Amlogic and analyzing the registers on the kernel that was shipped with
my board.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
---
 drivers/phy/Kconfig              |  13 ++
 drivers/phy/Makefile             |   2 +
 drivers/phy/phy-meson-gxl-usb2.c | 374 ++++++++++++++++++++++++++++++++++++++
 drivers/phy/phy-meson-gxl-usb3.c | 377 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 766 insertions(+)
 create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
 create mode 100644 drivers/phy/phy-meson-gxl-usb3.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 728e03f..ea74843 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -502,4 +502,17 @@ config PHY_MESON8B_USB2
 	  and GXBB SoCs.
 	  If unsure, say N.
 
+config PHY_MESON_GXL_USB
+	tristate "Meson GXL USB2 and USB3 PHY drivers"
+	default ARCH_MESON
+	depends on OF && (ARCH_MESON || COMPILE_TEST)
+	depends on USB_SUPPORT
+	select USB_COMMON
+	select GENERIC_PHY
+	select REGMAP_MMIO
+	help
+	  Enable this to support the Meson USB2 and USB3 PHYs found in
+	  Meson GXL SoCs.
+	  If unsure, say N.
+
 endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 0c7fdae..960a96e 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -61,3 +61,5 @@ obj-$(CONFIG_PHY_CYGNUS_PCIE)		+= phy-bcm-cygnus-pcie.o
 obj-$(CONFIG_ARCH_TEGRA) += tegra/
 obj-$(CONFIG_PHY_NS2_PCIE)		+= phy-bcm-ns2-pcie.o
 obj-$(CONFIG_PHY_MESON8B_USB2)		+= phy-meson8b-usb2.o
+obj-$(CONFIG_PHY_MESON_GXL_USB)		+= phy-meson-gxl-usb2.o
+obj-$(CONFIG_PHY_MESON_GXL_USB)		+= phy-meson-gxl-usb3.o
diff --git a/drivers/phy/phy-meson-gxl-usb2.c b/drivers/phy/phy-meson-gxl-usb2.c
new file mode 100644
index 0000000..c081ce3
--- /dev/null
+++ b/drivers/phy/phy-meson-gxl-usb2.c
@@ -0,0 +1,374 @@
+/*
+ * Meson GXL USB2 PHY driver
+ *
+ * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/usb/of.h>
+
+/* bits [31:27] are read-only */
+#define U2P_R0							0x0
+	#define U2P_R0_BYPASS_SEL				BIT(0)
+	#define U2P_R0_BYPASS_DM_EN				BIT(1)
+	#define U2P_R0_BYPASS_DP_EN				BIT(2)
+	#define U2P_R0_TXBITSTUFF_ENH				BIT(3)
+	#define U2P_R0_TXBITSTUFF_EN				BIT(4)
+	#define U2P_R0_DM_PULLDOWN				BIT(5)
+	#define U2P_R0_DP_PULLDOWN				BIT(6)
+	#define U2P_R0_DP_VBUS_VLD_EXT_SEL			BIT(7)
+	#define U2P_R0_DP_VBUS_VLD_EXT				BIT(8)
+	#define U2P_R0_ADP_PRB_EN				BIT(9)
+	#define U2P_R0_ADP_DISCHARGE				BIT(10)
+	#define U2P_R0_ADP_CHARGE				BIT(11)
+	#define U2P_R0_DRV_VBUS					BIT(12)
+	#define U2P_R0_ID_PULLUP				BIT(13)
+	#define U2P_R0_LOOPBACK_EN_B				BIT(14)
+	#define U2P_R0_OTG_DISABLE				BIT(15)
+	#define U2P_R0_COMMON_ONN				BIT(16)
+	#define U2P_R0_FSEL_SHIFT				17
+	#define U2P_R0_FSEL_MASK				GENMASK(19, 17)
+	#define U2P_R0_REF_CLK_SEL_SHIFT			20
+	#define U2P_R0_REF_CLK_SEL_MASK				GENMASK(21, 20)
+	#define U2P_R0_POWER_ON_RESET				BIT(22)
+	#define U2P_R0_V_ATE_TEST_EN_B_SHIFT			23
+	#define U2P_R0_V_ATE_TEST_EN_B_MASK			GENMASK(24, 23)
+	#define U2P_R0_ID_SET_ID_DQ				BIT(25)
+	#define U2P_R0_ATE_RESET				BIT(26)
+	#define U2P_R0_FSV_MINUS				BIT(27)
+	#define U2P_R0_FSV_PLUS					BIT(28)
+	#define U2P_R0_BYPASS_DM_DATA				BIT(29)
+	#define U2P_R0_BYPASS_DP_DATA				BIT(30)
+
+#define U2P_R1							0x4
+	#define U2P_R1_BURN_IN_TEST				BIT(0)
+	#define U2P_R1_ACA_ENABLE				BIT(1)
+	#define U2P_R1_DCD_ENABLE				BIT(2)
+	#define U2P_R1_VDAT_SRC_EN_B				BIT(3)
+	#define U2P_R1_VDAT_DET_EN_B				BIT(4)
+	#define U2P_R1_CHARGES_SEL				BIT(5)
+	#define U2P_R1_TX_PREEMP_PULSE_TUNE			BIT(6)
+	#define U2P_R1_TX_PREEMP_AMP_TUNE_SHIFT			7
+	#define U2P_R1_TX_PREEMP_AMP_TUNE_MASK			GENMASK(8, 7)
+	#define U2P_R1_TX_RES_TUNE_SHIFT			9
+	#define U2P_R1_TX_RES_TUNE_MASK				GENMASK(10, 9)
+	#define U2P_R1_TX_RISE_TUNE_SHIFT			11
+	#define U2P_R1_TX_RISE_TUNE_MASK			GENMASK(12, 11)
+	#define U2P_R1_TX_VREF_TUNE_SHIFT			13
+	#define U2P_R1_TX_VREF_TUNE_MASK			GENMASK(16, 13)
+	#define U2P_R1_TX_FSLS_TUNE_SHIFT			17
+	#define U2P_R1_TX_FSLS_TUNE_MASK			GENMASK(20, 17)
+	#define U2P_R1_TX_HSXV_TUNE_SHIFT			21
+	#define U2P_R1_TX_HSXV_TUNE_MASK			GENMASK(22, 21)
+	#define U2P_R1_OTG_TUNE_SHIFT				23
+	#define U2P_R1_OTG_TUNE_MASK				GENMASK(25, 23)
+	#define U2P_R1_SQRX_TUNE_SHIFT				26
+	#define U2P_R1_SQRX_TUNE_MASK				GENMASK(28, 26)
+	#define U2P_R1_COMP_DIS_TUNE_SHIFT			29
+	#define U2P_R1_COMP_DIS_TUNE_MASK			GENMASK(31, 29)
+
+/* bits [31:14] are read-only */
+#define U2P_R2							0x8
+	#define U2P_R2_DATA_IN_SHIFT				0
+	#define U2P_R2_DATA_IN_MASK				GENMASK(3, 0)
+	#define U2P_R2_DATA_IN_EN_SHIFT				4
+	#define U2P_R2_DATA_IN_EN_MASK				GENMASK(7, 4)
+	#define U2P_R2_ADDR_SHIFT				8
+	#define U2P_R2_ADDR_MASK				GENMASK(11, 8)
+	#define U2P_R2_DATA_OUT_SEL				BIT(12)
+	#define U2P_R2_CLK					BIT(13)
+	#define U2P_R2_DATA_OUT_SHIFT				14
+	#define U2P_R2_DATA_OUT_MASK				GENMASK(17, 14)
+	#define U2P_R2_ACA_PIN_RANGE_C				BIT(18)
+	#define U2P_R2_ACA_PIN_RANGE_B				BIT(19)
+	#define U2P_R2_ACA_PIN_RANGE_A				BIT(20)
+	#define U2P_R2_ACA_PIN_GND				BIT(21)
+	#define U2P_R2_ACA_PIN_FLOAT				BIT(22)
+	#define U2P_R2_CHARGE_DETECT				BIT(23)
+	#define U2P_R2_DEVICE_SESSION_VALID			BIT(24)
+	#define U2P_R2_ADP_PROBE				BIT(25)
+	#define U2P_R2_ADP_SENSE				BIT(26)
+	#define U2P_R2_SESSION_END				BIT(27)
+	#define U2P_R2_VBUS_VALID				BIT(28)
+	#define U2P_R2_B_VALID					BIT(29)
+	#define U2P_R2_A_VALID					BIT(30)
+	#define U2P_R2_ID_DIG					BIT(31)
+
+#define U2P_R3							0xc
+
+#define PHY_PORT_RESOURCE_SIZE					0x20
+
+#define RESET_COMPLETE_TIME				500
+
+struct phy_meson_gxl_usb2_priv {
+	struct regmap		*regmap;
+	enum phy_mode		mode;
+};
+
+struct phy_meson_gxl_usb2_drv {
+	void __iomem		*base;
+	int			num_ports;
+	struct phy		**ports;
+	struct clk		*clk_usb;
+	struct clk		*clk_usb_ddr;
+};
+
+static const struct regmap_config phy_meson_gxl_usb2_regmap_conf = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = U2P_R3,
+};
+
+static int phy_meson_gxl_usb2_set_mode(struct phy *phy, enum phy_mode mode)
+{
+	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+
+	switch (mode) {
+	case PHY_MODE_USB_HOST:
+	case PHY_MODE_USB_OTG:
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,
+				   U2P_R0_DM_PULLDOWN);
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
+				   U2P_R0_DP_PULLDOWN);
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP, 0);
+		break;
+
+	case PHY_MODE_USB_DEVICE:
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,
+				   0);
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
+				   0);
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP,
+				   U2P_R0_ID_PULLUP);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/* reset the PHY and wait until settings are stabilized */
+	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,
+			   U2P_R0_POWER_ON_RESET);
+	udelay(RESET_COMPLETE_TIME);
+	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
+	udelay(RESET_COMPLETE_TIME);
+
+	priv->mode = mode;
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb2_power_off(struct phy *phy)
+{
+	struct phy_meson_gxl_usb2_drv *drv_priv =
+		dev_get_drvdata(phy->dev.parent);
+	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+
+	/* power off the PHY by putting it into reset mode */
+	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,
+			   U2P_R0_POWER_ON_RESET);
+
+	clk_disable_unprepare(drv_priv->clk_usb_ddr);
+	clk_disable_unprepare(drv_priv->clk_usb);
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb2_power_on(struct phy *phy)
+{
+	struct phy_meson_gxl_usb2_drv *drv_priv =
+		dev_get_drvdata(phy->dev.parent);
+	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+	int ret;
+
+	ret = clk_prepare_enable(drv_priv->clk_usb);
+	if (ret) {
+		dev_err(&phy->dev, "Failed to enable USB clock\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(drv_priv->clk_usb_ddr);
+	if (ret) {
+		clk_disable_unprepare(drv_priv->clk_usb);
+
+		dev_err(&phy->dev, "Failed to enable USB DDR clock\n");
+		return ret;
+	}
+
+	/* power on the PHY by taking it out of reset mode */
+	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
+
+	ret = phy_meson_gxl_usb2_set_mode(phy, priv->mode);
+	if (ret) {
+		phy_meson_gxl_usb2_power_off(phy);
+
+		dev_err(&phy->dev, "Failed to initialize PHY with mode %d\n",
+			priv->mode);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct phy_ops phy_meson_gxl_usb2_ops = {
+	.power_on	= phy_meson_gxl_usb2_power_on,
+	.power_off	= phy_meson_gxl_usb2_power_off,
+	.set_mode	= phy_meson_gxl_usb2_set_mode,
+	.owner		= THIS_MODULE,
+};
+
+static struct phy *phy_meson_gxl_usb2_of_xlate(struct device *dev,
+					       struct of_phandle_args *args)
+{
+	struct phy_meson_gxl_usb2_drv *priv = dev_get_drvdata(dev);
+	int port;
+
+	if (args->args_count != 1) {
+		dev_err(dev, "Invalid number of cells in 'phy' property\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	port = args->args[0];
+	if (WARN_ON(port >= priv->num_ports))
+		return ERR_PTR(-ENODEV);
+
+	return priv->ports[port];
+}
+
+static int phy_meson_gxl_usb2_probe_port(struct device *dev, int port)
+{
+	struct phy_meson_gxl_usb2_drv *drv_priv = dev_get_drvdata(dev);
+	struct phy_meson_gxl_usb2_priv *phy_priv;
+	struct phy *phy;
+	void __iomem *port_base;
+
+	phy_priv = devm_kzalloc(dev, sizeof(*phy_priv), GFP_KERNEL);
+	if (!phy_priv)
+		return -ENOMEM;
+
+	switch (of_usb_get_dr_mode_by_phy(dev->of_node, port)) {
+	case USB_DR_MODE_PERIPHERAL:
+		phy_priv->mode = PHY_MODE_USB_DEVICE;
+		break;
+	case USB_DR_MODE_OTG:
+		phy_priv->mode = PHY_MODE_USB_OTG;
+		break;
+	case USB_DR_MODE_HOST:
+	default:
+		phy_priv->mode = PHY_MODE_USB_HOST;
+		break;
+	}
+
+	phy = devm_phy_create(dev, NULL, &phy_meson_gxl_usb2_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "failed to create PHY port %d\n", port);
+		return PTR_ERR(phy);
+	}
+
+	port_base = drv_priv->base + (port * PHY_PORT_RESOURCE_SIZE);
+	phy_priv->regmap = devm_regmap_init_mmio(&phy->dev, port_base,
+						 &phy_meson_gxl_usb2_regmap_conf);
+	if (IS_ERR(phy_priv->regmap))
+		return PTR_ERR(phy_priv->regmap);
+
+	phy_set_drvdata(phy, phy_priv);
+
+	drv_priv->ports[port] = phy;
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct phy_meson_gxl_usb2_drv *priv;
+	struct phy_provider *phy_provider;
+	struct resource *res;
+	int i, ret;
+
+	ret = device_reset(dev);
+	if (ret) {
+		dev_err(dev, "failed to reset device\n");
+		return ret;
+	}
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, priv);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	priv->num_ports = resource_size(res) / PHY_PORT_RESOURCE_SIZE;
+	if (priv->num_ports < 1) {
+		dev_err(dev, "specified memory range is too small\n");
+		return -EINVAL;
+	}
+
+	priv->ports = devm_kcalloc(dev, priv->num_ports, sizeof(*priv->ports),
+				   GFP_KERNEL);
+	if (!priv->ports)
+		return -ENOMEM;
+
+	priv->clk_usb = devm_clk_get(dev, "usb");
+	if (IS_ERR(priv->clk_usb)) {
+		dev_err(dev, "failed to get USB clock\n");
+		return PTR_ERR(priv->clk_usb);
+	}
+
+	priv->clk_usb_ddr = devm_clk_get(dev, "usb_ddr");
+	if (IS_ERR(priv->clk_usb_ddr)) {
+		dev_err(dev, "failed to get USB DDR clock\n");
+		return PTR_ERR(priv->clk_usb_ddr);
+	}
+
+	for (i = 0; i < priv->num_ports; i++) {
+		ret = phy_meson_gxl_usb2_probe_port(dev, i);
+		if (ret)
+			return ret;
+	}
+
+	phy_provider = devm_of_phy_provider_register(dev,
+						     phy_meson_gxl_usb2_of_xlate);
+
+	return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id phy_meson_gxl_usb2_of_match[] = {
+	{ .compatible = "amlogic,meson-gxl-usb2-phy", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb2_of_match);
+
+static struct platform_driver phy_meson_gxl_usb2_driver = {
+	.probe	= phy_meson_gxl_usb2_probe,
+	.driver	= {
+		.name		= "phy-meson-gxl-usb2",
+		.of_match_table	= phy_meson_gxl_usb2_of_match,
+	},
+};
+module_platform_driver(phy_meson_gxl_usb2_driver);
+
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>");
+MODULE_DESCRIPTION("Meson GXL USB2 PHY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/phy-meson-gxl-usb3.c b/drivers/phy/phy-meson-gxl-usb3.c
new file mode 100644
index 0000000..90a4028
--- /dev/null
+++ b/drivers/phy/phy-meson-gxl-usb3.c
@@ -0,0 +1,377 @@
+/*
+ * Meson GXL USB3 PHY driver
+ *
+ * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/usb/of.h>
+#include <linux/workqueue.h>
+
+#define USB_R0							0x00
+	#define USB_R0_P30_FSEL_SHIFT				0
+	#define USB_R0_P30_FSEL_MASK				GENMASK(5, 0)
+	#define USB_R0_P30_PHY_RESET				BIT(6)
+	#define USB_R0_P30_TEST_POWERDOWN_HSP			BIT(7)
+	#define USB_R0_P30_TEST_POWERDOWN_SSP			BIT(8)
+	#define USB_R0_P30_ACJT_LEVEL_SHIFT			9
+	#define USB_R0_P30_ACJT_LEVEL_MASK			GENMASK(13, 9)
+	#define USB_R0_P30_TX_BOOST_LEVEL_SHIFT			14
+	#define USB_R0_P30_TX_BOOST_LEVEL_MASK			GENMASK(16, 14)
+	#define USB_R0_P30_LANE0_TX2RX_LOOPBACK			BIT(17)
+	#define USB_R0_P30_LANE0_EXT_PCLK_REQ			BIT(18)
+	#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_SHIFT		19
+	#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK		GENMASK(28, 19)
+	#define USB_R0_U2D_SS_SCALEDOWN_MODE_SHIFT		29
+	#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK		GENMASK(30, 29)
+	#define USB_R0_U2D_ACT					BIT(31)
+
+#define USB_R1							0x04
+	#define USB_R1_U3H_BIGENDIAN_GS				BIT(0)
+	#define USB_R1_U3H_PME_ENABLE				BIT(1)
+	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_SHIFT		2
+	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK		GENMASK(6, 2)
+	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_SHIFT		7
+	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK		GENMASK(11, 7)
+	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_SHIFT		12
+	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK		GENMASK(15, 12)
+	#define USB_R1_U3H_HOST_U3_PORT_DISABLE			BIT(16)
+	#define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT	BIT(17)
+	#define USB_R1_U3H_HOST_MSI_ENABLE			BIT(18)
+	#define USB_R1_U3H_FLADJ_30MHZ_REG_SHIFT		19
+	#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK			GENMASK(24, 19)
+	#define USB_R1_P30_PCS_TX_SWING_FULL_SHIFT		25
+	#define USB_R1_P30_PCS_TX_SWING_FULL_MASK		GENMASK(31, 25)
+
+#define USB_R2							0x08
+	#define USB_R2_P30_CR_DATA_IN_SHIFT			0
+	#define USB_R2_P30_CR_DATA_IN_MASK			GENMASK(15, 0)
+	#define USB_R2_P30_CR_READ				BIT(16)
+	#define USB_R2_P30_CR_WRITE				BIT(17)
+	#define USB_R2_P30_CR_CAP_ADDR				BIT(18)
+	#define USB_R2_P30_CR_CAP_DATA				BIT(19)
+	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_SHIFT		20
+	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK		GENMASK(25, 20)
+	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_SHIFT		26
+	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK		GENMASK(31, 26)
+
+#define USB_R3							0x0c
+	#define USB_R3_P30_SSC_ENABLE				BIT(0)
+	#define USB_R3_P30_SSC_RANGE_SHIFT			1
+	#define USB_R3_P30_SSC_RANGE_MASK			GENMASK(3, 1)
+	#define USB_R3_P30_SSC_REF_CLK_SEL_SHIFT		4
+	#define USB_R3_P30_SSC_REF_CLK_SEL_MASK			GENMASK(12, 4)
+	#define USB_R3_P30_REF_SSP_EN				BIT(13)
+	#define USB_R3_P30_LOS_BIAS_SHIFT			16
+	#define USB_R3_P30_LOS_BIAS_MASK			GENMASK(18, 16)
+	#define USB_R3_P30_LOS_LEVEL_SHIFT			19
+	#define USB_R3_P30_LOS_LEVEL_MASK			GENMASK(23, 19)
+	#define USB_R3_P30_MPLL_MULTIPLIER_SHIFT		24
+	#define USB_R3_P30_MPLL_MULTIPLIER_MASK			GENMASK(30, 24)
+
+#define USB_R4							0x10
+	#define USB_R4_P21_PORT_RESET_0				BIT(0)
+	#define USB_R4_P21_SLEEP_M0				BIT(1)
+	#define USB_R4_MEM_PD_SHIFT				2
+	#define USB_R4_MEM_PD_MASK				GENMASK(3, 2)
+	#define USB_R4_P21_ONLY					BIT(4)
+
+#define USB_R5							0x14
+	#define USB_R5_ID_DIG_SYNC				BIT(0)
+	#define USB_R5_ID_DIG_REG				BIT(1)
+	#define USB_R5_ID_DIG_CFG_SHIFT				2
+	#define USB_R5_ID_DIG_CFG_MASK				GENMASK(3, 2)
+	#define USB_R5_ID_DIG_EN_0				BIT(4)
+	#define USB_R5_ID_DIG_EN_1				BIT(5)
+	#define USB_R5_ID_DIG_CURR				BIT(6)
+	#define USB_R5_ID_DIG_IRQ				BIT(7)
+	#define USB_R5_ID_DIG_TH_SHIFT				8
+	#define USB_R5_ID_DIG_TH_MASK				GENMASK(15, 8)
+	#define USB_R5_ID_DIG_CNT_SHIFT				16
+	#define USB_R5_ID_DIG_CNT_MASK				GENMASK(23, 16)
+
+/* read-only register */
+#define USB_R6							0x18
+	#define USB_R6_P30_CR_DATA_OUT_SHIFT			0
+	#define USB_R6_P30_CR_DATA_OUT_MASK			GENMASK(15, 0)
+	#define USB_R6_P30_CR_ACK				BIT(16)
+
+#define RESET_COMPLETE_TIME				500
+
+struct phy_meson_gxl_usb3_priv {
+	struct regmap		*regmap;
+	struct delayed_work	otg_work;
+	struct phy		*this_phy;
+	int			num_usb2_phys;
+	struct phy		**usb2_phys;
+};
+
+static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = USB_R6,
+};
+
+static int phy_meson_gxl_usb3_update_mode(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	u32 val;
+	enum phy_mode mode;
+	int i, ret;
+
+	ret = regmap_read(priv->regmap, USB_R5, &val);
+	if (ret)
+		return ret;
+
+	if (val & USB_R5_ID_DIG_CURR) {
+		mode = PHY_MODE_USB_DEVICE;
+
+		regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT,
+				   USB_R0_U2D_ACT);
+		regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
+				   USB_R4_P21_SLEEP_M0);
+	} else {
+		mode = PHY_MODE_USB_HOST;
+
+		regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT, 0);
+		regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
+				   0);
+	}
+
+	/* inform the USB2 PHY that we have changed the mode */
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_set_mode(priv->usb2_phys[i], mode);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to update usb2-phy #%d mode to %d\n",
+				i, mode);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static void phy_meson_gxl_usb3_work(struct work_struct *data)
+{
+	struct phy_meson_gxl_usb3_priv *priv =
+		container_of(data, struct phy_meson_gxl_usb3_priv,
+			     otg_work.work);
+
+	phy_meson_gxl_usb3_update_mode(priv->this_phy);
+
+	/* unmask IRQs which may have arrived in the meantime */
+	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
+}
+
+static int phy_meson_gxl_usb3_init(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int i, ret;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_init(priv->usb2_phys[i]);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to initialize related usb2-phy #%d\n",
+				i);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb3_exit(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int i, ret;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_exit(priv->usb2_phys[i]);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to exit related usb2-phy #%d\n", i);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb3_power_on(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int i, ret;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_power_on(priv->usb2_phys[i]);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to power on related usb2-phy #%d\n",
+				i);
+			return ret;
+		}
+	}
+
+	regmap_update_bits(priv->regmap, USB_R1,
+			   USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
+			   0x20 << USB_R1_U3H_FLADJ_30MHZ_REG_SHIFT);
+
+	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0,
+			   USB_R5_ID_DIG_EN_0);
+	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1,
+			   USB_R5_ID_DIG_EN_1);
+	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_TH_MASK,
+			   0xff << USB_R5_ID_DIG_TH_SHIFT);
+
+	return phy_meson_gxl_usb3_update_mode(phy);
+}
+
+static int phy_meson_gxl_usb3_power_off(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int i, ret;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_power_off(priv->usb2_phys[i]);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to power off related usb2-phy #%d\n",
+				i);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static irqreturn_t phy_meson_gxl_usb3_irq(int irq, void *data)
+{
+	u32 val;
+	struct phy_meson_gxl_usb3_priv *priv = data;
+
+	regmap_read(priv->regmap, USB_R5, &val);
+	if (!(val & USB_R5_ID_DIG_IRQ)) {
+		dev_err(&priv->this_phy->dev, "spurious interrupt\n");
+		return IRQ_NONE;
+	}
+
+	schedule_delayed_work(&priv->otg_work, msecs_to_jiffies(10));
+
+	/* acknowledge the IRQ */
+	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
+
+	return IRQ_HANDLED;
+}
+
+static const struct phy_ops phy_meson_gxl_usb3_ops = {
+	.init		= phy_meson_gxl_usb3_init,
+	.exit		= phy_meson_gxl_usb3_exit,
+	.power_on	= phy_meson_gxl_usb3_power_on,
+	.power_off	= phy_meson_gxl_usb3_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct phy_meson_gxl_usb3_priv *priv;
+	struct resource *res;
+	struct phy *phy;
+	struct phy_provider *phy_provider;
+	void __iomem *base;
+	int i, irq;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	priv->regmap = devm_regmap_init_mmio(dev, base,
+					     &phy_meson_gxl_usb3_regmap_conf);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq >= 0) {
+		INIT_DELAYED_WORK(&priv->otg_work, phy_meson_gxl_usb3_work);
+
+		irq = devm_request_irq(dev, irq, phy_meson_gxl_usb3_irq,
+				       IRQF_SHARED, dev_name(dev),
+				       priv);
+		if (irq < 0) {
+			dev_err(dev, "could not register IRQ handler (%d)\n",
+				irq);
+			return -EINVAL;
+		}
+	}
+
+	priv->num_usb2_phys = of_count_phandle_with_args(np, "phys",
+							 "#phy-cells");
+
+	priv->usb2_phys = devm_kcalloc(dev, priv->num_usb2_phys,
+				       sizeof(*priv->usb2_phys), GFP_KERNEL);
+	if (!priv->usb2_phys)
+		return -ENOMEM;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		priv->usb2_phys[i] = devm_of_phy_get_by_index(dev, np, i);
+		if (IS_ERR(priv->usb2_phys[i])) {
+			dev_err(dev, "failed to get related usb2-phy #%d", i);
+			return PTR_ERR(priv->usb2_phys[i]);
+		}
+	}
+
+	phy = devm_phy_create(dev, np, &phy_meson_gxl_usb3_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "failed to create PHY\n");
+		return PTR_ERR(phy);
+	}
+
+	phy_set_drvdata(phy, priv);
+
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+	return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id phy_meson_gxl_usb3_of_match[] = {
+	{ .compatible = "amlogic,meson-gxl-usb3-phy", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb3_of_match);
+
+static struct platform_driver phy_meson_gxl_usb3_driver = {
+	.probe	= phy_meson_gxl_usb3_probe,
+	.driver	= {
+		.name		= "phy-meson-gxl-usb3",
+		.of_match_table	= phy_meson_gxl_usb3_of_match,
+	},
+};
+module_platform_driver(phy_meson_gxl_usb3_driver);
+
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>");
+MODULE_DESCRIPTION("Meson GXL USB3 PHY driver");
+MODULE_LICENSE("GPL");
-- 
2.10.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 1/5] Documentation: dt-bindings: Add documentation for Meson GXL USB2/3 PHYs
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
	narmstrong-rdvid1DuHRBWk0Htik3J/w, Martin Blumenstingl
In-Reply-To: <20161126145635.24488-1-martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>

This adds the DT binding documentation for the USB2 and USB3 PHYs found
in the Meson GXL and GXM SoCs.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
---
 .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt | 25 ++++++++++++++++++++
 .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt | 27 ++++++++++++++++++++++
 2 files changed, 52 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
 create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt

diff --git a/Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt b/Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
new file mode 100644
index 0000000..e7828ca
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
@@ -0,0 +1,25 @@
+* Amlogic Meson GXL and GXM USB2 PHY binding
+
+This describes the USB2 PHY block which provides multiple USB2 PHY ports.
+
+Required properties:
+- compatible:	Should be "amlogic,meson-gxl-usb2-phy"
+- reg:		The base address and length of the registers
+- #phys-cells:	should be 1 (see phy-bindings.txt in this directory)
+- clocks:	phandle and clock identifier for the phy clocks
+- clock-names:	"usb" and "usb_ddr"
+- resets:	reference to the reset controller
+
+Optional properties:
+- phy-supply:	see phy-bindings.txt in this directory
+
+
+Example:
+	usb2_phys: phy@78000 {
+		compatible = "amlogic,meson-gxl-usb2-phy";
+		#phy-cells = <1>;
+		reg = <0x0 0x78000 0x0 0x80>;
+		clocks = <&clkc CLKID_USB1>, <&clkc CLKID_USB1_DDR_BRIDGE>;
+		clock-names = "usb", "usb_ddr";
+		resets = <&reset RESET_USB_OTG>;
+	};
diff --git a/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt b/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
new file mode 100644
index 0000000..be779e8
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
@@ -0,0 +1,27 @@
+* Amlogic Meson GXL and GXM USB3 PHY binding
+
+Required properties:
+- compatible:	Should be "amlogic,meson-gxl-usb3-phy"
+- reg:		The base address and length of the registers
+- #phys-cells:	should be 0 (see phy-bindings.txt in this directory)
+- interrupts:	describes the OTG device/host mode detection interrupt
+- phys:		a list of related PHYs (typically the USB2 PHYs, see
+		meson-gxl-usb2-phy.txt in this directory). The mode of
+		the listed PHYs will be managed by the USB3 PHY (which
+		is required for OTG device/host detection to work).
+		The number of PHYs listed typically matches the number
+		of ports which are enabled in the USB controller which
+		uses this PHY.
+
+Optional properties:
+- phy-supply:	see phy-bindings.txt in this directory
+
+
+Example:
+	usb3_phy0: phy@78080 {
+		compatible = "amlogic,meson-gxl-usb3-phy";
+		#phy-cells = <0>;
+		reg = <0x0 0x78080 0x0 0x20>;
+		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+		phys = <&usb2_phys 0>, <&usb2_phys 1>;
+	};
-- 
2.10.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH 0/5] Meson GXL and GXM USB support
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
	narmstrong-rdvid1DuHRBWk0Htik3J/w, Martin Blumenstingl

USB support on GXL and GXM differs a lot from Meson8b and GXBB:
The most obvious change is that GXL and GXM now have one dwc3
controller and one dwc2 controller (instead of two dwc2 controllers).
With that there are also new USB PHYs.

Due to lack of hardware I was only able to test this on a board with
GXM, but as far as I understand the hardware my preparations should be
correct (so it should also work on GXL).

dwc2 will probably stay unused on most GXM devices since it's limited
to device mode via some dwc2 hardware configuration register.

dwc3 is probably used on all devices, even if there is more than just
one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
ports enabled, while on GXM there are three ports enabled (see below
for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
configuration, meaning that the SoC is limited to high-speed mode.
On my GXM device the dwc3 hardware configuration forces it into "host
only" mode.

The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
(on GXM there are only three enabled, but the registers should support
up to four).
The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
configuration enforces "host only" mode I was not able to test this. It
simply takes care of an interrupt and then notifies all related PHYs
about the new mode.
The USB2 PHY block is a bit different: I created one PHY driver which
spans all "PHY ports" because the handling is a bit tricky. It turns
out that for each available USB port in dwc3's hub the corresponding
PHY must be enabled (even if there is no physical port - in my case
port 3 is not connected to anything, but disabling the PHY breaks
ports 1 and 2 as well).
I decided not not pass the USB2 PHYs directly to dwc3 due to three
reasons: 1. the USB3 PHY (which holds a reference to all relevant
USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
are used with the same controller and thus it makes sense to keep the
mode consistent across all ports) 2. the dwc3 driver does not support
passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
passed to it) 3. it is similar to how the vendor reference driver
manages the PHYs. Please note that this coupling is not a fixed, this
is all configurable via devicetree (so if the third USB2 PHY has to
be passed two the dwc2 controller then this is still possible by
just moving on PHY reference in the .dts).

The coupling of the USB2 and USB3 PHYs is the reason why I sent the
two drivers in one patch, even though they are handling different IP
blocks (different registers, etc.).

Unfortunately there are no datasheets available for any of these PHYs.
Both drivers were written by reading the reference drivers provided by
Amlogic and analyzing the registers on the kernel that was shipped with
my board.

As a last note: the dwc3 driver currently only explicitly enables the
first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
changes to dwc3 are desired any how these should look like, but for now
it's working fine even without changes there.

lsusb output on GXM for the dwc3 hub:
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
...
 Hub Port Status:
   Port 1: 0000.0100 power
   Port 2: 0000.0100 power
   Port 3: 0000.0100 power

NOTE: The devicetree changes depend on my previous series:
"[PATCH 0/2] minor GXL and GXM improvements" - see [0]

NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
(special thanks to Arnd Bergmann and Sriram Dash for fixing that):
"[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]

I have a tree with all dependencies applied available at [2] if
someone wants a quick way to test this (I don't take any responsibility
if anything explodes though).

[0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
[1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
[2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126

Martin Blumenstingl (5):
  Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
    PHYs
  phy: meson: add USB2 and USB3 PHY support for Meson GXL
  arm64: dts: meson-gxl: add USB support
  ARM64: dts: meson-gxm: add GXM specific USB configuration
  ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards

 .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt |  25 ++
 .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt |  27 ++
 .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi |  12 +
 arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |  49 +++
 .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts |  17 +
 arch/arm64/boot/dts/amlogic/meson-gxm.dtsi         |  10 +
 drivers/phy/Kconfig                                |  13 +
 drivers/phy/Makefile                               |   2 +
 drivers/phy/phy-meson-gxl-usb2.c                   | 374 ++++++++++++++++++++
 drivers/phy/phy-meson-gxl-usb3.c                   | 377 +++++++++++++++++++++
 10 files changed, 906 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
 create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
 create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
 create mode 100644 drivers/phy/phy-meson-gxl-usb3.c

-- 
2.10.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v3 00/12] Initial Tegra186 support
From: Pavel Machek @ 2016-11-26 13:39 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Sivaram Nair, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Peter De Schrijver, Timo Alho, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20160819173233.13260-1-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Hi!

> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> 
> Hi everyone,
> 
> This is a set of patches to add initial support for Tegra186. It is
> based on Joseph's patches but I rewrote some of the drivers to be a
> little easier to comprehend and maintain (hopefully). I've also
> included clock and reset drivers as a proof of concept.

Is there any phone/tablet on the market with this chipset?

								Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply

* I hope this email meets you well in good health condition
From: Bentley @ 2016-11-26 12:52 UTC (permalink / raw)


How you doing today? I hope you are doing well. My name is Jones, from the US. I'm in Syria right now fighting ISIS. I want to get to know you better, if I may be so bold. I consider myself an easy-going man, and I am currently looking for a relationship in which I feel loved. Please tell me more about yourself, if you don't mind.

Hope to hear from you soon.

Regards,
Jones.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Xmas Offer
From: Mrs Julie Leach @ 2016-11-26 10:26 UTC (permalink / raw)
  To: Recipients

You are a recipient to Mrs Julie Leach Donation of $3 million USD. Contact ( julieleach93-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org ) for claims.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v4 6/6] arm64: arch_timer: acpi: add hisi timer errata data
From: Ding Tianhong @ 2016-11-26  8:00 UTC (permalink / raw)
  To: catalin.marinas, will.deacon, marc.zyngier, mark.rutland, oss,
	devicetree, shawnguo, stuart.yoder, linux-arm-kernel, linuxarm,
	hanjun.guo
  Cc: Ding Tianhong
In-Reply-To: <1480147248-12828-1-git-send-email-dingtianhong@huawei.com>

From: Hanjun Guo <hanjun.guo@linaro.org>

Add hisi timer specific erratum fixes.

v3: add hisilicon erratum 161601 for ACPI mode.

v4: update some data structures.

Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
---
 drivers/clocksource/arm_arch_timer.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index d4f4a0d..649f7fe 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -1078,10 +1078,28 @@ struct gtdt_arch_timer_fixup {
 	void *context;
 };
 
+#ifdef CONFIG_HISILICON_ERRATUM_161601
+static void __init erratum_workaround_enable(void *context)
+{
+	u64 erratum = (u64) context;
+
+	if (erratum & HISILICON_161601) {
+		timer_unstable_counter_workaround = &arch_timer_hisi_161601;
+		static_branch_enable(&arch_timer_read_ool_enabled);
+		pr_info("Enabling workaround for HISILICON ERRATUM 161601\n");
+	}
+}
+#endif
+
 /* note: this needs to be updated according to the doc of OEM ID
  * and TABLE ID for different board.
  */
 struct gtdt_arch_timer_fixup arch_timer_quirks[] __initdata = {
+#ifdef CONFIG_HISILICON_ERRATUM_161601
+	{"HISI", "hip05", 0, &erratum_workaround_enable, (void *) HISILICON_161601},
+	{"HISI", "hip06", 0, &erratum_workaround_enable, (void *) HISILICON_161601},
+	{"HISI", "hip07", 0, &erratum_workaround_enable, (void *) HISILICON_161601},
+#endif
 };
 
 void __init arch_timer_acpi_quirks_handler(char *oem_id,
-- 
1.9.0

^ permalink raw reply related

* [PATCH v4 5/6] arm64: arch_timer: apci: Introduce a generic aquirk framework for erratum
From: Ding Tianhong @ 2016-11-26  8:00 UTC (permalink / raw)
  To: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
	marc.zyngier-5wv7dgnIgG8, mark.rutland-5wv7dgnIgG8,
	oss-fOR+EgIDQEHk1uMJSBkQmQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	shawnguo-DgEjT+Ai2ygdnm+yROfE0A, stuart.yoder-3arQi8VN3Tc,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linuxarm-hv44wF8Li93QT0dZR+AlfA,
	hanjun.guo-QSEj5FYQhm4dnm+yROfE0A
  Cc: Ding Tianhong
In-Reply-To: <1480147248-12828-1-git-send-email-dingtianhong-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>

From: Hanjun Guo <hanjun.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

Introduce a general quirk framework for each timer erratum in ACPI,
which use the oem information in GTDT table for platform specific erratums.
The struct gtdt_arch_timer_fixup is introduced to record the oem
information to match the quirk and handle the erratum.

v3: Introduce a generic aquick framework for erratum in ACPI mode.

v4: rename the quirk handler parameter to make it more generic, and
    avoid break loop when handling the quirk becasue it need to
    support multi quirks handler.

Signed-off-by: Hanjun Guo <hanjun.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Signed-off-by: Ding Tianhong <dingtianhong-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
---
 drivers/clocksource/arm_arch_timer.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 3d59af1..d4f4a0d 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -1068,6 +1068,39 @@ static int __init arch_timer_mem_init(struct device_node *np)
 		       arch_timer_mem_init);
 
 #ifdef CONFIG_ACPI
+struct gtdt_arch_timer_fixup {
+	char oem_id[ACPI_OEM_ID_SIZE];
+	char oem_table_id[ACPI_OEM_TABLE_ID_SIZE];
+	u32 oem_revision;
+
+	/* quirk handler for arch timer erratum */
+	void (*handler)(void *context);
+	void *context;
+};
+
+/* note: this needs to be updated according to the doc of OEM ID
+ * and TABLE ID for different board.
+ */
+struct gtdt_arch_timer_fixup arch_timer_quirks[] __initdata = {
+};
+
+void __init arch_timer_acpi_quirks_handler(char *oem_id,
+					   char *oem_table_id,
+					   u32 oem_revision)
+{
+	struct gtdt_arch_timer_fixup *quirks = arch_timer_quirks;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(arch_timer_quirks); i++, quirks++) {
+		if (!memcmp(quirks->oem_id, oem_id, ACPI_OEM_ID_SIZE) &&
+		    !memcmp(quirks->oem_table_id, oem_table_id, ACPI_OEM_TABLE_ID_SIZE) &&
+		    quirks->oem_revision == oem_revision) {
+			if (quirks->handler && quirks->context)
+				quirks->handler(quirks->context);
+		}
+	}
+}
+
 static int __init map_generic_timer_interrupt(u32 interrupt, u32 flags)
 {
 	int trigger, polarity;
@@ -1094,6 +1127,9 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table)
 		return -EINVAL;
 	}
 
+	arch_timer_acpi_quirks_handler(table->oem_id, table->oem_table_id,
+				       table->oem_revision);
+
 	gtdt = container_of(table, struct acpi_table_gtdt, header);
 
 	arch_timers_present |= ARCH_CP15_TIMER;
-- 
1.9.0


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH v4 4/6] arm64: arch timer: Add timer erratum property for Hip05-d02 and Hip06-d03
From: Ding Tianhong @ 2016-11-26  8:00 UTC (permalink / raw)
  To: catalin.marinas, will.deacon, marc.zyngier, mark.rutland, oss,
	devicetree, shawnguo, stuart.yoder, linux-arm-kernel, linuxarm,
	hanjun.guo
  Cc: Ding Tianhong
In-Reply-To: <1480147248-12828-1-git-send-email-dingtianhong@huawei.com>

Enable workaround for hisilicon erratum 161601 on Hip05-d02 and Hip06-d03 board.

Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
---
 arch/arm64/boot/dts/hisilicon/hip05.dtsi | 1 +
 arch/arm64/boot/dts/hisilicon/hip06.dtsi | 1 +
 2 files changed, 2 insertions(+)

diff --git a/arch/arm64/boot/dts/hisilicon/hip05.dtsi b/arch/arm64/boot/dts/hisilicon/hip05.dtsi
index 4b472a3..a8e9969 100644
--- a/arch/arm64/boot/dts/hisilicon/hip05.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip05.dtsi
@@ -281,6 +281,7 @@
 			     <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
 			     <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
 			     <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+		hisilicon,erratum-161601;
 	};
 
 	pmu {
diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
index b548763..c31f9f9 100644
--- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
@@ -260,6 +260,7 @@
 			     <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
 			     <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
 			     <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+		hisilicon,erratum-161601;
 	};
 
 	pmu {
-- 
1.9.0

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox