mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 0/6] phy: usb: add support for STM32 usbphyc
@ 2020-02-20 10:01 Ahmad Fatoum
  2020-02-20 10:01 ` [PATCH 1/6] regulator: import Linux regulator_bulk API Ahmad Fatoum
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2020-02-20 10:01 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum, mgr, rcz

This is only USB PHY support. Changes to the DWC2 (OTG) and/or OHCI
(Host) are still needed to make USB on the MP1 work.

I haven't yet groked the relation between generic phy and usb phy
frameworks, but the code is a straight port from Linux and it seems to
work[1], so let's upstream it now to make collaboration easier.

[1]: clock the EHCI and power on the PHY and 20% of the time, the bus
     can be enumerated completely. Still needs further debugging,
     but it's unlikely that the PHY driver is at fault.

Cheers,
Ahmad Fatoum (6):
  regulator: import Linux regulator_bulk API
  phy: remove unused init_data parameter
  phy: populate existing ->pwr member with phy-supply
  phy: introduce phy_get_by_index
  regulator: port over Linux stm32 PWR regulator driver
  phy: port over Linux stm32 usbphyc driver

 drivers/phy/Kconfig                        |  13 +
 drivers/phy/Makefile                       |   1 +
 drivers/phy/freescale/phy-fsl-imx8mq-usb.c |   2 +-
 drivers/phy/phy-core.c                     |  29 +-
 drivers/phy/phy-stm32-usbphyc.c            | 434 +++++++++++++++++++++
 drivers/phy/usb-nop-xceiv.c                |   2 +-
 drivers/pinctrl/pinctrl-tegra-xusb.c       |   4 +-
 drivers/regulator/Kconfig                  |   7 +
 drivers/regulator/Makefile                 |   1 +
 drivers/regulator/core.c                   | 139 +++++++
 drivers/regulator/stm32-pwr.c              | 215 ++++++++++
 drivers/usb/imx/imx-usb-phy.c              |   2 +-
 include/linux/phy/phy.h                    |  14 +-
 include/regulator.h                        |  49 +++
 14 files changed, 897 insertions(+), 15 deletions(-)
 create mode 100644 drivers/phy/phy-stm32-usbphyc.c
 create mode 100644 drivers/regulator/stm32-pwr.c

-- 
2.25.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 1/6] regulator: import Linux regulator_bulk API
  2020-02-20 10:01 [PATCH 0/6] phy: usb: add support for STM32 usbphyc Ahmad Fatoum
@ 2020-02-20 10:01 ` Ahmad Fatoum
  2020-02-20 10:01 ` [PATCH 2/6] phy: remove unused init_data parameter Ahmad Fatoum
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2020-02-20 10:01 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum, mgr, rcz

Linux v5.6-rc1 contains 168 references to regultor_bulk_get, which
allows getting multiple regulators to set at once. Instead of open
coding them when porting code, port over the helpers to barebox.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/regulator/core.c | 139 +++++++++++++++++++++++++++++++++++++++
 include/regulator.h      |  49 ++++++++++++++
 2 files changed, 188 insertions(+)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index f0de7a52e391..f459d072a987 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -397,6 +397,145 @@ int regulator_set_voltage(struct regulator *r, int min_uV, int max_uV)
 	return regulator_set_voltage_internal(r->ri, min_uV, max_uV);
 }
 
+/**
+ * regulator_bulk_get - get multiple regulator consumers
+ *
+ * @dev:           Device to supply
+ * @num_consumers: Number of consumers to register
+ * @consumers:     Configuration of consumers; clients are stored here.
+ *
+ * @return 0 on success, an errno on failure.
+ *
+ * This helper function allows drivers to get several regulator
+ * consumers in one operation.  If any of the regulators cannot be
+ * acquired then any regulators that were allocated will be freed
+ * before returning to the caller.
+ */
+int regulator_bulk_get(struct device_d *dev, int num_consumers,
+		       struct regulator_bulk_data *consumers)
+{
+	int i;
+	int ret;
+
+	for (i = 0; i < num_consumers; i++)
+		consumers[i].consumer = NULL;
+
+	for (i = 0; i < num_consumers; i++) {
+		consumers[i].consumer = regulator_get(dev,
+						      consumers[i].supply);
+		if (IS_ERR(consumers[i].consumer)) {
+			ret = PTR_ERR(consumers[i].consumer);
+			consumers[i].consumer = NULL;
+			goto err;
+		}
+	}
+
+	return 0;
+
+err:
+	if (ret != -EPROBE_DEFER)
+		dev_err(dev, "Failed to get supply '%s': %d\n",
+			consumers[i].supply, ret);
+	else
+		dev_dbg(dev, "Failed to get supply '%s', deferring\n",
+			consumers[i].supply);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_bulk_get);
+
+/**
+ * regulator_bulk_enable - enable multiple regulator consumers
+ *
+ * @num_consumers: Number of consumers
+ * @consumers:     Consumer data; clients are stored here.
+ * @return         0 on success, an errno on failure
+ *
+ * This convenience API allows consumers to enable multiple regulator
+ * clients in a single API call.  If any consumers cannot be enabled
+ * then any others that were enabled will be disabled again prior to
+ * return.
+ */
+int regulator_bulk_enable(int num_consumers,
+			  struct regulator_bulk_data *consumers)
+{
+	int ret;
+	int i;
+
+	for (i = 0; i < num_consumers; i++) {
+		ret = regulator_enable(consumers[i].consumer);
+		if (ret)
+			goto err;
+	}
+
+	return 0;
+
+err:
+	while (--i >= 0)
+		regulator_disable(consumers[i].consumer);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_bulk_enable);
+
+/**
+ * regulator_bulk_disable - disable multiple regulator consumers
+ *
+ * @num_consumers: Number of consumers
+ * @consumers:     Consumer data; clients are stored here.
+ * @return         0 on success, an errno on failure
+ *
+ * This convenience API allows consumers to disable multiple regulator
+ * clients in a single API call.  If any consumers cannot be disabled
+ * then any others that were disabled will be enabled again prior to
+ * return.
+ */
+int regulator_bulk_disable(int num_consumers,
+			   struct regulator_bulk_data *consumers)
+{
+	int i;
+	int ret, r;
+
+	for (i = num_consumers - 1; i >= 0; --i) {
+		ret = regulator_disable(consumers[i].consumer);
+		if (ret != 0)
+			goto err;
+	}
+
+	return 0;
+
+err:
+	pr_err("Failed to disable %s: %d\n", consumers[i].supply, ret);
+	for (++i; i < num_consumers; ++i) {
+		r = regulator_enable(consumers[i].consumer);
+		if (r != 0)
+			pr_err("Failed to re-enable %s: %d\n",
+			       consumers[i].supply, r);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_bulk_disable);
+
+/**
+ * regulator_bulk_free - free multiple regulator consumers
+ *
+ * @num_consumers: Number of consumers
+ * @consumers:     Consumer data; clients are stored here.
+ *
+ * This convenience API allows consumers to free multiple regulator
+ * clients in a single API call.
+ */
+void regulator_bulk_free(int num_consumers,
+			 struct regulator_bulk_data *consumers)
+{
+	int i;
+
+	for (i = 0; i < num_consumers; i++)
+		consumers[i].consumer = NULL;
+}
+EXPORT_SYMBOL_GPL(regulator_bulk_free);
+
 static void regulator_print_one(struct regulator_internal *ri)
 {
 	struct regulator *r;
diff --git a/include/regulator.h b/include/regulator.h
index d01535df5207..dfa808d662bd 100644
--- a/include/regulator.h
+++ b/include/regulator.h
@@ -5,6 +5,23 @@
 /* struct regulator is an opaque object for consumers */
 struct regulator;
 
+/**
+ * struct regulator_bulk_data - Data used for bulk regulator operations.
+ *
+ * @supply:   The name of the supply.  Initialised by the user before
+ *            using the bulk regulator APIs.
+ * @consumer: The regulator consumer for the supply.  This will be managed
+ *            by the bulk API.
+ *
+ * The regulator APIs provide a series of regulator_bulk_() API calls as
+ * a convenience to consumers which require multiple supplies.  This
+ * structure is used to manage data for these calls.
+ */
+struct regulator_bulk_data {
+	const char *supply;
+	struct regulator *consumer;
+};
+
 /**
  * struct regulator_desc - Static regulator descriptor
  *
@@ -136,6 +153,14 @@ int regulator_list_voltage_linear_range(struct regulator_dev *rdev,
 int regulator_get_voltage_sel_regmap(struct regulator_dev *rdev);
 int regulator_map_voltage_iterate(struct regulator_dev *rdev,
 				  int min_uV, int max_uV);
+int regulator_bulk_get(struct device_d *dev, int num_consumers,
+		       struct regulator_bulk_data *consumers);
+int regulator_bulk_enable(int num_consumers,
+			  struct regulator_bulk_data *consumers);
+int regulator_bulk_disable(int num_consumers,
+			   struct regulator_bulk_data *consumers);
+void regulator_bulk_free(int num_consumers,
+			 struct regulator_bulk_data *consumers);
 
 /*
  * Helper functions intended to be used by regulator drivers prior registering
@@ -166,6 +191,30 @@ static inline int regulator_set_voltage(struct regulator *regulator,
 	return 0;
 }
 
+static inline int regulator_bulk_get(struct device_d *dev, int num_consumers,
+				     struct regulator_bulk_data *consumers)
+{
+	return 0;
+}
+
+static inline int regulator_bulk_enable(int num_consumers,
+					struct regulator_bulk_data *consumers)
+{
+	return 0;
+}
+
+static inline int regulator_bulk_disable(int num_consumers,
+					 struct regulator_bulk_data *consumers)
+{
+	return 0;
+}
+
+static inline void regulator_bulk_free(int num_consumers,
+				       struct regulator_bulk_data *consumers)
+{
+	return 0;
+}
+
 #endif
 
 #endif /* __REGULATOR_H */
-- 
2.25.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 2/6] phy: remove unused init_data parameter
  2020-02-20 10:01 [PATCH 0/6] phy: usb: add support for STM32 usbphyc Ahmad Fatoum
  2020-02-20 10:01 ` [PATCH 1/6] regulator: import Linux regulator_bulk API Ahmad Fatoum
@ 2020-02-20 10:01 ` Ahmad Fatoum
  2020-02-20 10:01 ` [PATCH 3/6] phy: populate existing ->pwr member with phy-supply Ahmad Fatoum
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2020-02-20 10:01 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum, mgr, rcz

Linux has since migrated to a new lookup API that lacks the init_data
parameter. As it's unused in barebox, follow suit.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/phy/freescale/phy-fsl-imx8mq-usb.c | 2 +-
 drivers/phy/phy-core.c                     | 5 +----
 drivers/phy/usb-nop-xceiv.c                | 2 +-
 drivers/pinctrl/pinctrl-tegra-xusb.c       | 4 ++--
 drivers/usb/imx/imx-usb-phy.c              | 2 +-
 include/linux/phy/phy.h                    | 8 ++------
 6 files changed, 8 insertions(+), 15 deletions(-)

diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
index 1aef2b300448..6d60eacd7ff9 100644
--- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
+++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
@@ -106,7 +106,7 @@ static int imx8mq_usb_phy_probe(struct device_d *dev)
 	if (IS_ERR(imx_phy->base))
 		return PTR_ERR(imx_phy->base);
 
-	imx_phy->phy = phy_create(dev, NULL, &imx8mq_usb_phy_ops, NULL);
+	imx_phy->phy = phy_create(dev, NULL, &imx8mq_usb_phy_ops);
 	if (IS_ERR(imx_phy->phy))
 		return PTR_ERR(imx_phy->phy);
 
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 066a887a2226..d1f3c7fde4a2 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -25,13 +25,11 @@ static int phy_ida;
  * @dev: device that is creating the new phy
  * @node: device node of the phy
  * @ops: function pointers for performing phy operations
- * @init_data: contains the list of PHY consumers or NULL
  *
  * Called to create a phy using phy framework.
  */
 struct phy *phy_create(struct device_d *dev, struct device_node *node,
-		       const struct phy_ops *ops,
-		       struct phy_init_data *init_data)
+		       const struct phy_ops *ops)
 {
 	int ret;
 	int id;
@@ -52,7 +50,6 @@ struct phy *phy_create(struct device_d *dev, struct device_node *node,
 	phy->dev.device_node = node ?: dev->device_node;
 	phy->id = id;
 	phy->ops = ops;
-	phy->init_data = init_data;
 
 	ret = register_device(&phy->dev);
 	if (ret)
diff --git a/drivers/phy/usb-nop-xceiv.c b/drivers/phy/usb-nop-xceiv.c
index b124e6c0c48a..a9031fa7f8a8 100644
--- a/drivers/phy/usb-nop-xceiv.c
+++ b/drivers/phy/usb-nop-xceiv.c
@@ -107,7 +107,7 @@ static int nop_usbphy_probe(struct device_d *dev)
 	/* FIXME: Add vbus-detect-gpio support */
 
 	nopphy->usb_phy.dev = dev;
-	nopphy->phy = phy_create(dev, NULL, &nop_phy_ops, NULL);
+	nopphy->phy = phy_create(dev, NULL, &nop_phy_ops);
 	if (IS_ERR(nopphy->phy)) {
 		ret = PTR_ERR(nopphy->phy);
 		goto release_gpio;
diff --git a/drivers/pinctrl/pinctrl-tegra-xusb.c b/drivers/pinctrl/pinctrl-tegra-xusb.c
index e477280e62c3..c4d3bbe8d429 100644
--- a/drivers/pinctrl/pinctrl-tegra-xusb.c
+++ b/drivers/pinctrl/pinctrl-tegra-xusb.c
@@ -415,7 +415,7 @@ static int pinctrl_tegra_xusb_probe(struct device_d *dev)
 		goto reset;
 	}
 
-	phy = phy_create(dev, NULL, &pcie_phy_ops, NULL);
+	phy = phy_create(dev, NULL, &pcie_phy_ops);
 	if (IS_ERR(phy)) {
 		err = PTR_ERR(phy);
 		goto unregister;
@@ -424,7 +424,7 @@ static int pinctrl_tegra_xusb_probe(struct device_d *dev)
 	padctl->phys[TEGRA_XUSB_PADCTL_PCIE] = phy;
 	phy_set_drvdata(phy, padctl);
 
-	phy = phy_create(dev, NULL, &sata_phy_ops, NULL);
+	phy = phy_create(dev, NULL, &sata_phy_ops);
 	if (IS_ERR(phy)) {
 		err = PTR_ERR(phy);
 		goto unregister;
diff --git a/drivers/usb/imx/imx-usb-phy.c b/drivers/usb/imx/imx-usb-phy.c
index d4562285c068..069dddcacbc7 100644
--- a/drivers/usb/imx/imx-usb-phy.c
+++ b/drivers/usb/imx/imx-usb-phy.c
@@ -174,7 +174,7 @@ static int imx_usbphy_probe(struct device_d *dev)
 	imxphy->usb_phy.dev = dev;
 	imxphy->usb_phy.notify_connect = imx_usbphy_notify_connect;
 	imxphy->usb_phy.notify_disconnect = imx_usbphy_notify_disconnect;
-	imxphy->phy = phy_create(dev, NULL, &imx_phy_ops, NULL);
+	imxphy->phy = phy_create(dev, NULL, &imx_phy_ops);
 	if (IS_ERR(imxphy->phy)) {
 		ret = PTR_ERR(imxphy->phy);
 		goto err_clk;
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index 5d96e02df4fd..df480d4634ac 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -49,7 +49,6 @@ struct phy_attrs {
  * @dev: phy device
  * @id: id of the phy device
  * @ops: function pointers for performing phy operations
- * @init_data: list of PHY consumers (non-dt only)
  * @mutex: mutex to protect phy_ops
  * @init_count: used to protect when the PHY is used by multiple consumers
  * @power_count: used to protect when the PHY is used by multiple consumers
@@ -59,7 +58,6 @@ struct phy {
 	struct device_d		dev;
 	int			id;
 	const struct phy_ops	*ops;
-	struct phy_init_data	*init_data;
 	int			init_count;
 	int			power_count;
 	struct phy_attrs	attrs;
@@ -144,8 +142,7 @@ struct phy *of_phy_get(struct device_node *np, const char *con_id);
 struct phy *of_phy_simple_xlate(struct device_d *dev,
 	struct of_phandle_args *args);
 struct phy *phy_create(struct device_d *dev, struct device_node *node,
-		       const struct phy_ops *ops,
-		       struct phy_init_data *init_data);
+		       const struct phy_ops *ops);
 void phy_destroy(struct phy *phy);
 struct phy_provider *__of_phy_provider_register(struct device_d *dev,
 	struct phy * (*of_xlate)(struct device_d *dev,
@@ -225,8 +222,7 @@ static inline struct phy *of_phy_simple_xlate(struct device_d *dev,
 
 static inline struct phy *phy_create(struct device_d *dev,
 				     struct device_node *node,
-				     const struct phy_ops *ops,
-				     struct phy_init_data *init_data)
+				     const struct phy_ops *ops)
 {
 	return ERR_PTR(-ENOSYS);
 }
-- 
2.25.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 3/6] phy: populate existing ->pwr member with phy-supply
  2020-02-20 10:01 [PATCH 0/6] phy: usb: add support for STM32 usbphyc Ahmad Fatoum
  2020-02-20 10:01 ` [PATCH 1/6] regulator: import Linux regulator_bulk API Ahmad Fatoum
  2020-02-20 10:01 ` [PATCH 2/6] phy: remove unused init_data parameter Ahmad Fatoum
@ 2020-02-20 10:01 ` Ahmad Fatoum
  2020-02-20 10:01 ` [PATCH 4/6] phy: introduce phy_get_by_index Ahmad Fatoum
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2020-02-20 10:01 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum, mgr, rcz

barebox already enables/disables the ->pwr regulator at the correct
places, but doesn't assign a value anywhere. Initialize it with the
phy-supply regulator like Linux does.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/phy/phy-core.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index d1f3c7fde4a2..ad1e8147881b 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -51,6 +51,16 @@ struct phy *phy_create(struct device_d *dev, struct device_node *node,
 	phy->id = id;
 	phy->ops = ops;
 
+	/* phy-supply */
+	phy->pwr = regulator_get(&phy->dev, "phy");
+	if (IS_ERR(phy->pwr)) {
+		ret = PTR_ERR(phy->pwr);
+		if (ret == -EPROBE_DEFER)
+			goto free_ida;
+
+		phy->pwr = NULL;
+	}
+
 	ret = register_device(&phy->dev);
 	if (ret)
 		goto free_ida;
-- 
2.25.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 4/6] phy: introduce phy_get_by_index
  2020-02-20 10:01 [PATCH 0/6] phy: usb: add support for STM32 usbphyc Ahmad Fatoum
                   ` (2 preceding siblings ...)
  2020-02-20 10:01 ` [PATCH 3/6] phy: populate existing ->pwr member with phy-supply Ahmad Fatoum
@ 2020-02-20 10:01 ` Ahmad Fatoum
  2020-02-20 10:01 ` [PATCH 5/6] regulator: port over Linux stm32 PWR regulator driver Ahmad Fatoum
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2020-02-20 10:01 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum, mgr, rcz

The upstream (v5.6-rc1) device tree node of the stm32mp157c-dk2's OHCI
has a phys property, but not phy-names. We have no API to reference
such a phy easily (passing NULL isn't allowed). Add one.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/phy/phy-core.c  | 14 ++++++++++++++
 include/linux/phy/phy.h |  6 ++++++
 2 files changed, 20 insertions(+)

diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index ad1e8147881b..ff6e35d16060 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -371,3 +371,17 @@ struct phy *phy_optional_get(struct device_d *dev, const char *string)
 	return phy;
 }
 
+/**
+ * phy_get_by_index() - lookup and obtain a reference to a phy by index.
+ * @dev: device with node that references this phy
+ * @index: index of the phy
+ *
+ * Gets the phy using _of_phy_get()
+ */
+struct phy *phy_get_by_index(struct device_d *dev, int index)
+{
+	if (!dev->device_node)
+		return ERR_PTR(-ENODEV);
+
+	return _of_phy_get(dev->device_node, index);
+}
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index df480d4634ac..8a28b8e068b1 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -149,6 +149,7 @@ struct phy_provider *__of_phy_provider_register(struct device_d *dev,
 	struct of_phandle_args *args));
 void of_phy_provider_unregister(struct phy_provider *phy_provider);
 struct usb_phy *phy_to_usbphy(struct phy *phy);
+struct phy *phy_get_by_index(struct device_d *dev, int index);
 #else
 static inline int phy_init(struct phy *phy)
 {
@@ -247,6 +248,11 @@ static inline struct usb_phy *phy_to_usbphy(struct phy *phy)
 	return NULL;
 }
 
+static struct phy *phy_get_by_index(struct device_d *dev, int index)
+{
+	return ERR_PTR(-ENODEV);
+}
+
 #endif
 
 #endif /* __DRIVERS_PHY_H */
-- 
2.25.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 5/6] regulator: port over Linux stm32 PWR regulator driver
  2020-02-20 10:01 [PATCH 0/6] phy: usb: add support for STM32 usbphyc Ahmad Fatoum
                   ` (3 preceding siblings ...)
  2020-02-20 10:01 ` [PATCH 4/6] phy: introduce phy_get_by_index Ahmad Fatoum
@ 2020-02-20 10:01 ` Ahmad Fatoum
  2020-02-20 10:01 ` [PATCH 6/6] phy: port over Linux stm32 usbphyc driver Ahmad Fatoum
  2020-02-25  8:11 ` [PATCH 0/6] phy: usb: add support for STM32 usbphyc Sascha Hauer
  6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2020-02-20 10:01 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum, mgr, rcz

This driver supports internal regulators (1V1, 1V8, 3V3) in the
STMicroelectronics STM32 chips. Control of these regulators will
be required when adding USB support later on.

Imported here is the Linux v5.6-rc1 state of the driver.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/regulator/Kconfig     |   7 ++
 drivers/regulator/Makefile    |   1 +
 drivers/regulator/stm32-pwr.c | 215 ++++++++++++++++++++++++++++++++++
 3 files changed, 223 insertions(+)
 create mode 100644 drivers/regulator/stm32-pwr.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index f47a115da240..1ce057180a01 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -21,6 +21,13 @@ config REGULATOR_PFUZE
 	depends on I2C
 	depends on ARCH_IMX6 || ARCH_IMX8MQ
 
+config REGULATOR_STM32_PWR
+	bool "STMicroelectronics STM32 PWR"
+	depends on ARCH_STM32MP
+	help
+	  This driver supports internal regulators (1V1, 1V8, 3V3) in the
+	  STMicroelectronics STM32 chips.
+
 config REGULATOR_STPMIC1
 	tristate "STMicroelectronics STPMIC1 PMIC Regulators"
 	depends on MFD_STPMIC1
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index e27e155cf624..4d0bba6c52dd 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_REGULATOR_BCM283X) += bcm2835.o
 obj-$(CONFIG_REGULATOR_PFUZE) += pfuze.o
 obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o
 obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
+obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o
diff --git a/drivers/regulator/stm32-pwr.c b/drivers/regulator/stm32-pwr.c
new file mode 100644
index 000000000000..296f95bc4c32
--- /dev/null
+++ b/drivers/regulator/stm32-pwr.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) STMicroelectronics 2019
+// Authors: Gabriel Fernandez <gabriel.fernandez@st.com>
+//          Pascal Paillet <p.paillet@st.com>.
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <linux/iopoll.h>
+#include <of.h>
+#include <regulator.h>
+
+/*
+ * Registers description
+ */
+#define REG_PWR_CR3 0x0C
+
+#define USB_3_3_EN BIT(24)
+#define USB_3_3_RDY BIT(26)
+#define REG_1_8_EN BIT(28)
+#define REG_1_8_RDY BIT(29)
+#define REG_1_1_EN BIT(30)
+#define REG_1_1_RDY BIT(31)
+
+/* list of supported regulators */
+enum {
+	PWR_REG11,
+	PWR_REG18,
+	PWR_USB33,
+	STM32PWR_REG_NUM_REGS
+};
+
+struct stm32_pwr_desc {
+	struct regulator_desc desc;
+	const char *name;
+	const char *supply_name;
+};
+
+static u32 ready_mask_table[STM32PWR_REG_NUM_REGS] = {
+	[PWR_REG11] = REG_1_1_RDY,
+	[PWR_REG18] = REG_1_8_RDY,
+	[PWR_USB33] = USB_3_3_RDY,
+};
+
+struct stm32_pwr_reg {
+	void __iomem *base;
+	struct device_d *dev;
+	u32 ready_mask;
+	struct regulator_dev rdev;
+	struct regulator *supply;
+};
+
+static inline struct stm32_pwr_reg *to_pwr_reg(struct regulator_dev *rdev)
+{
+	return container_of(rdev, struct stm32_pwr_reg, rdev);
+}
+
+static inline struct stm32_pwr_desc *to_desc(struct regulator_dev *rdev)
+{
+	return container_of(rdev->desc, struct stm32_pwr_desc, desc);
+}
+
+static int stm32_pwr_reg_is_ready(struct regulator_dev *rdev)
+{
+	struct stm32_pwr_reg *priv = to_pwr_reg(rdev);
+	u32 val;
+
+	val = readl(priv->base + REG_PWR_CR3);
+
+	return (val & priv->ready_mask);
+}
+
+static int stm32_pwr_reg_is_enabled(struct regulator_dev *rdev)
+{
+	struct stm32_pwr_reg *priv = to_pwr_reg(rdev);
+	u32 val;
+
+	val = readl(priv->base + REG_PWR_CR3);
+
+	return (val & rdev->desc->enable_mask);
+}
+
+static int stm32_pwr_reg_enable(struct regulator_dev *rdev)
+{
+	struct stm32_pwr_reg *priv = to_pwr_reg(rdev);
+	struct stm32_pwr_desc *desc = to_desc(rdev);
+	int ret;
+	u32 val;
+
+	regulator_enable(priv->supply);
+
+	val = readl(priv->base + REG_PWR_CR3);
+	val |= rdev->desc->enable_mask;
+	writel(val, priv->base + REG_PWR_CR3);
+
+	/* use an arbitrary timeout of 20ms */
+	ret = readx_poll_timeout(stm32_pwr_reg_is_ready, rdev, val, val,
+				 20 * USEC_PER_MSEC);
+	if (ret)
+		dev_err(priv->dev, "%s: regulator enable timed out!\n",
+			desc->name);
+
+	return ret;
+}
+
+static int stm32_pwr_reg_disable(struct regulator_dev *rdev)
+{
+	struct stm32_pwr_reg *priv = to_pwr_reg(rdev);
+	struct stm32_pwr_desc *desc = to_desc(rdev);
+	int ret;
+	u32 val;
+
+	val = readl(priv->base + REG_PWR_CR3);
+	val &= ~rdev->desc->enable_mask;
+	writel(val, priv->base + REG_PWR_CR3);
+
+	/* use an arbitrary timeout of 20ms */
+	ret = readx_poll_timeout(stm32_pwr_reg_is_ready, rdev, val, !val,
+				 20 * USEC_PER_MSEC);
+	if (ret)
+		dev_err(priv->dev, "%s: regulator disable timed out!\n",
+			desc->name);
+
+	regulator_disable(priv->supply);
+
+	return ret;
+}
+
+static const struct regulator_ops stm32_pwr_reg_ops = {
+	.enable		= stm32_pwr_reg_enable,
+	.disable	= stm32_pwr_reg_disable,
+	.is_enabled	= stm32_pwr_reg_is_enabled,
+};
+
+#define PWR_REG(_id, _name, _volt, _en, _supply) \
+	[_id] = { { \
+		.n_voltages = 1, \
+		.ops = &stm32_pwr_reg_ops, \
+		.min_uV = _volt, \
+		.enable_mask = _en, \
+	}, .name = _name, .supply_name = _supply, }
+
+static const struct stm32_pwr_desc stm32_pwr_desc[] = {
+	PWR_REG(PWR_REG11, "reg11", 1100000, REG_1_1_EN, "vdd"),
+	PWR_REG(PWR_REG18, "reg18", 1800000, REG_1_8_EN, "vdd"),
+	PWR_REG(PWR_USB33, "usb33", 3300000, USB_3_3_EN, "vdd_3v3_usbfs"),
+};
+
+static int stm32_pwr_regulator_probe(struct device_d *dev)
+{
+	struct stm32_pwr_reg *priv;
+	struct device_node *child;
+	struct resource *iores;
+	int i, ret;
+
+	iores = dev_request_mem_resource(dev, 0);
+	if (IS_ERR(iores))
+		return PTR_ERR(iores);
+
+	for_each_child_of_node(dev->device_node, child) {
+		const struct stm32_pwr_desc *desc = NULL;
+
+		for (i = 0; i < STM32PWR_REG_NUM_REGS; i++) {
+			const char *name;
+
+			name = of_get_property(child, "regulator-name", NULL);
+			if (name && strcmp(stm32_pwr_desc[i].name, name)) {
+				desc = &stm32_pwr_desc[i];
+				break;
+			}
+		}
+
+		if (!desc) {
+			dev_warn(dev, "Skipping unknown child node %s\n",
+				 child->name);
+			continue;
+		}
+
+		priv = xzalloc(sizeof(*priv));
+		priv->base = IOMEM(iores->start);
+		priv->ready_mask = ready_mask_table[i];
+		priv->dev = dev;
+
+		priv->rdev.desc = &desc->desc;
+
+		priv->supply = regulator_get(dev, desc->supply_name);
+		if (IS_ERR(priv->supply))
+			return PTR_ERR(priv->supply);
+
+		ret = of_regulator_register(&priv->rdev, child);
+		if (ret) {
+			dev_err(dev, "%s: Failed to register regulator: %d\n",
+				desc->name, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static const struct of_device_id stm32_pwr_of_match[] = {
+	{ .compatible = "st,stm32mp1,pwr-reg", },
+	{ /* sentinel */ },
+};
+
+static struct driver_d stm32_pwr_driver = {
+	.probe = stm32_pwr_regulator_probe,
+	.name  = "stm32-pwr-regulator",
+	.of_compatible = stm32_pwr_of_match,
+};
+device_platform_driver(stm32_pwr_driver);
+
+MODULE_DESCRIPTION("STM32MP1 PWR voltage regulator driver");
+MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.25.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 6/6] phy: port over Linux stm32 usbphyc driver
  2020-02-20 10:01 [PATCH 0/6] phy: usb: add support for STM32 usbphyc Ahmad Fatoum
                   ` (4 preceding siblings ...)
  2020-02-20 10:01 ` [PATCH 5/6] regulator: port over Linux stm32 PWR regulator driver Ahmad Fatoum
@ 2020-02-20 10:01 ` Ahmad Fatoum
  2020-02-25  8:11 ` [PATCH 0/6] phy: usb: add support for STM32 usbphyc Sascha Hauer
  6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2020-02-20 10:01 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum, mgr, rcz

This ports over the Linux v5.6-rc1 state of the driver.
It'll be needed for both EHCI and DWC2 usb connectivity on the stm32mp1.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/phy/Kconfig             |  13 +
 drivers/phy/Makefile            |   1 +
 drivers/phy/phy-stm32-usbphyc.c | 434 ++++++++++++++++++++++++++++++++
 3 files changed, 448 insertions(+)
 create mode 100644 drivers/phy/phy-stm32-usbphyc.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index b5cefb2ff3d1..b5c8e98b9e48 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -24,4 +24,17 @@ config USB_NOP_XCEIV
 
 source "drivers/phy/freescale/Kconfig"
 
+config PHY_STM32_USBPHYC
+	tristate "STM32 USB HS PHY Controller"
+	depends on ARCH_STM32MP
+	help
+	  Enable this to support the High-Speed USB transceivers that are part
+	  of some STMicroelectronics STM32 SoCs.
+
+	  This driver controls the entire USB PHY block: the USB PHY controller
+	  (USBPHYC) and the two 8-bit wide UTMI+ interfaces. First interface is
+	  used by an HS USB Host controller, and the second one is shared
+	  between an HS USB OTG controller and an HS USB Host controller,
+	  selected by a USB switch.
+
 endif
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 179c55e60abe..684aaed75aa2 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -5,3 +5,4 @@
 obj-$(CONFIG_GENERIC_PHY)		+= phy-core.o
 obj-$(CONFIG_USB_NOP_XCEIV)		+= usb-nop-xceiv.o
 obj-y					+= freescale/
+obj-$(CONFIG_PHY_STM32_USBPHYC) 	+= phy-stm32-usbphyc.o
diff --git a/drivers/phy/phy-stm32-usbphyc.c b/drivers/phy/phy-stm32-usbphyc.c
new file mode 100644
index 000000000000..093842fe1460
--- /dev/null
+++ b/drivers/phy/phy-stm32-usbphyc.c
@@ -0,0 +1,434 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * STMicroelectronics STM32 USB PHY Controller driver
+ *
+ * Copyright (C) 2018 STMicroelectronics
+ * Author(s): Amelie Delaunay <amelie.delaunay@st.com>.
+ */
+#include <common.h>
+#include <init.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <io.h>
+#include <linux/phy/phy.h>
+#include <linux/reset.h>
+#include <asm-generic/div64.h>
+#include <usb/phy.h>
+
+#define STM32_USBPHYC_PLL	0x0
+#define STM32_USBPHYC_MISC	0x8
+#define STM32_USBPHYC_VERSION	0x3F4
+
+/* STM32_USBPHYC_PLL bit fields */
+#define PLLNDIV			GENMASK(6, 0)
+#define PLLFRACIN		GENMASK(25, 10)
+#define PLLEN			BIT(26)
+#define PLLSTRB			BIT(27)
+#define PLLSTRBYP		BIT(28)
+#define PLLFRACCTL		BIT(29)
+#define PLLDITHEN0		BIT(30)
+#define PLLDITHEN1		BIT(31)
+
+/* STM32_USBPHYC_MISC bit fields */
+#define SWITHOST		BIT(0)
+
+/* STM32_USBPHYC_VERSION bit fields */
+#define MINREV			GENMASK(3, 0)
+#define MAJREV			GENMASK(7, 4)
+
+static const char * const supplies_names[] = {
+	"vdda1v1",	/* 1V1 */
+	"vdda1v8",	/* 1V8 */
+};
+
+#define NUM_SUPPLIES		ARRAY_SIZE(supplies_names)
+
+#define PLL_LOCK_TIME_US	100
+#define PLL_PWR_DOWN_TIME_US	5
+#define PLL_FVCO_MHZ		2880
+#define PLL_INFF_MIN_RATE_HZ	19200000
+#define PLL_INFF_MAX_RATE_HZ	38400000
+#define HZ_PER_MHZ		1000000L
+
+struct pll_params {
+	u8 ndiv;
+	u16 frac;
+};
+
+struct stm32_usbphyc_phy {
+	struct phy *phy;
+	struct stm32_usbphyc *usbphyc;
+	struct regulator_bulk_data supplies[NUM_SUPPLIES];
+	u32 index;
+	bool active;
+};
+
+struct stm32_usbphyc {
+	struct device_d *dev;
+	void __iomem *base;
+	struct clk *clk;
+	struct stm32_usbphyc_phy **phys;
+	int nphys;
+	int switch_setup;
+};
+
+static inline void stm32_usbphyc_set_bits(void __iomem *reg, u32 bits)
+{
+	writel(readl(reg) | bits, reg);
+}
+
+static inline void stm32_usbphyc_clr_bits(void __iomem *reg, u32 bits)
+{
+	writel(readl(reg) & ~bits, reg);
+}
+
+static void stm32_usbphyc_get_pll_params(u32 clk_rate,
+					 struct pll_params *pll_params)
+{
+	unsigned long long fvco, ndiv, frac;
+
+	/*    _
+	 *   | FVCO = INFF*2*(NDIV + FRACT/2^16) when DITHER_DISABLE[1] = 1
+	 *   | FVCO = 2880MHz
+	 *  <
+	 *   | NDIV = integer part of input bits to set the LDF
+	 *   |_FRACT = fractional part of input bits to set the LDF
+	 *  =>	PLLNDIV = integer part of (FVCO / (INFF*2))
+	 *  =>	PLLFRACIN = fractional part of(FVCO / INFF*2) * 2^16
+	 * <=>  PLLFRACIN = ((FVCO / (INFF*2)) - PLLNDIV) * 2^16
+	 */
+	fvco = (unsigned long long)PLL_FVCO_MHZ * HZ_PER_MHZ;
+
+	ndiv = fvco;
+	do_div(ndiv, (clk_rate * 2));
+	pll_params->ndiv = (u8)ndiv;
+
+	frac = fvco * (1 << 16);
+	do_div(frac, (clk_rate * 2));
+	frac = frac - (ndiv * (1 << 16));
+	pll_params->frac = (u16)frac;
+}
+
+static int stm32_usbphyc_pll_init(struct stm32_usbphyc *usbphyc)
+{
+	struct pll_params pll_params;
+	u32 clk_rate = clk_get_rate(usbphyc->clk);
+	u32 ndiv, frac;
+	u32 usbphyc_pll;
+
+	if ((clk_rate < PLL_INFF_MIN_RATE_HZ) ||
+	    (clk_rate > PLL_INFF_MAX_RATE_HZ)) {
+		dev_err(usbphyc->dev, "input clk freq (%dHz) out of range\n",
+			clk_rate);
+		return -EINVAL;
+	}
+
+	stm32_usbphyc_get_pll_params(clk_rate, &pll_params);
+	ndiv = FIELD_PREP(PLLNDIV, pll_params.ndiv);
+	frac = FIELD_PREP(PLLFRACIN, pll_params.frac);
+
+	usbphyc_pll = PLLDITHEN1 | PLLDITHEN0 | PLLSTRBYP | ndiv;
+
+	if (pll_params.frac)
+		usbphyc_pll |= PLLFRACCTL | frac;
+
+	writel(usbphyc_pll, usbphyc->base + STM32_USBPHYC_PLL);
+
+	dev_dbg(usbphyc->dev, "input clk freq=%dHz, ndiv=%lu, frac=%lu\n",
+		clk_rate, FIELD_GET(PLLNDIV, usbphyc_pll),
+		FIELD_GET(PLLFRACIN, usbphyc_pll));
+
+	return 0;
+}
+
+static bool stm32_usbphyc_has_one_phy_active(struct stm32_usbphyc *usbphyc)
+{
+	int i;
+
+	for (i = 0; i < usbphyc->nphys; i++)
+		if (usbphyc->phys[i]->active)
+			return true;
+
+	return false;
+}
+
+static int stm32_usbphyc_pll_enable(struct stm32_usbphyc *usbphyc)
+{
+	void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
+	bool pllen = (readl(pll_reg) & PLLEN);
+	int ret;
+
+	/* Check if one phy port has already configured the pll */
+	if (pllen && stm32_usbphyc_has_one_phy_active(usbphyc))
+		return 0;
+
+	if (pllen) {
+		stm32_usbphyc_clr_bits(pll_reg, PLLEN);
+		/* Wait for minimum width of powerdown pulse (ENABLE = Low) */
+		udelay(PLL_PWR_DOWN_TIME_US);
+	}
+
+	ret = stm32_usbphyc_pll_init(usbphyc);
+	if (ret)
+		return ret;
+
+	stm32_usbphyc_set_bits(pll_reg, PLLEN);
+
+	/* Wait for maximum lock time */
+	udelay(PLL_LOCK_TIME_US);
+
+	if (!(readl(pll_reg) & PLLEN)) {
+		dev_err(usbphyc->dev, "PLLEN not set\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc)
+{
+	void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
+
+	/* Check if other phy port active */
+	if (stm32_usbphyc_has_one_phy_active(usbphyc))
+		return 0;
+
+	stm32_usbphyc_clr_bits(pll_reg, PLLEN);
+	/* Wait for minimum width of powerdown pulse (ENABLE = Low) */
+	udelay(PLL_PWR_DOWN_TIME_US);
+
+	if (readl(pll_reg) & PLLEN) {
+		dev_err(usbphyc->dev, "PLL not reset\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int stm32_usbphyc_phy_init(struct phy *phy)
+{
+	struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+	struct stm32_usbphyc *usbphyc = usbphyc_phy->usbphyc;
+	int ret;
+
+	ret = stm32_usbphyc_pll_enable(usbphyc);
+	if (ret)
+		return ret;
+
+	usbphyc_phy->active = true;
+
+	return 0;
+}
+
+static int stm32_usbphyc_phy_exit(struct phy *phy)
+{
+	struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+	struct stm32_usbphyc *usbphyc = usbphyc_phy->usbphyc;
+
+	usbphyc_phy->active = false;
+
+	return stm32_usbphyc_pll_disable(usbphyc);
+}
+
+static int stm32_usbphyc_phy_power_on(struct phy *phy)
+{
+	struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+
+	return regulator_bulk_enable(NUM_SUPPLIES, usbphyc_phy->supplies);
+}
+
+static int stm32_usbphyc_phy_power_off(struct phy *phy)
+{
+	struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
+
+	return regulator_bulk_disable(NUM_SUPPLIES, usbphyc_phy->supplies);
+}
+
+static const struct phy_ops stm32_usbphyc_phy_ops = {
+	.init = stm32_usbphyc_phy_init,
+	.exit = stm32_usbphyc_phy_exit,
+	.power_on = stm32_usbphyc_phy_power_on,
+	.power_off = stm32_usbphyc_phy_power_off,
+};
+
+static void stm32_usbphyc_switch_setup(struct stm32_usbphyc *usbphyc,
+				       u32 utmi_switch)
+{
+	if (!utmi_switch)
+		stm32_usbphyc_clr_bits(usbphyc->base + STM32_USBPHYC_MISC,
+				       SWITHOST);
+	else
+		stm32_usbphyc_set_bits(usbphyc->base + STM32_USBPHYC_MISC,
+				       SWITHOST);
+	usbphyc->switch_setup = utmi_switch;
+}
+
+static struct phy *stm32_usbphyc_of_xlate(struct device_d *dev,
+					  struct of_phandle_args *args)
+{
+	struct stm32_usbphyc *usbphyc = dev->priv;
+	struct stm32_usbphyc_phy *usbphyc_phy = NULL;
+	struct device_node *phynode = args->np;
+	int port = 0;
+
+	for (port = 0; port < usbphyc->nphys; port++) {
+		if (phynode == usbphyc->phys[port]->phy->dev.device_node) {
+			usbphyc_phy = usbphyc->phys[port];
+			break;
+		}
+	}
+
+	if (!usbphyc_phy) {
+		dev_err(dev, "failed to find phy\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (((usbphyc_phy->index == 0) && (args->args_count != 0)) ||
+	    ((usbphyc_phy->index == 1) && (args->args_count != 1))) {
+		dev_err(dev, "invalid number of cells for phy port%d\n",
+			usbphyc_phy->index);
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* Configure the UTMI switch for PHY port#2 */
+	if (usbphyc_phy->index == 1) {
+		if (usbphyc->switch_setup < 0) {
+			stm32_usbphyc_switch_setup(usbphyc, args->args[0]);
+		} else {
+			if (args->args[0] != usbphyc->switch_setup) {
+				dev_err(dev, "phy port1 already used\n");
+				return ERR_PTR(-EBUSY);
+			}
+		}
+	}
+
+	return usbphyc_phy->phy;
+}
+
+static int stm32_usbphyc_probe(struct device_d *dev)
+{
+	struct stm32_usbphyc *usbphyc;
+	struct device_node *child, *np = dev->device_node;
+	struct resource *iores;
+	struct phy_provider *phy_provider;
+	u32 version;
+	int ret, port = 0;
+
+	usbphyc = xzalloc(sizeof(*usbphyc));
+
+	usbphyc->dev = dev;
+	dev->priv = usbphyc;
+
+	iores = dev_request_mem_resource(dev, 0);
+	if (IS_ERR(iores))
+		return PTR_ERR(iores);
+	usbphyc->base = IOMEM(iores->start);
+
+	usbphyc->clk = clk_get(dev, 0);
+	if (IS_ERR(usbphyc->clk)) {
+		ret = PTR_ERR(usbphyc->clk);
+		dev_err(dev, "clk get failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_enable(usbphyc->clk);
+	if (ret) {
+		dev_err(dev, "clk enable failed: %d\n", ret);
+		return ret;
+	}
+
+	device_reset_us(dev, 2);
+
+	usbphyc->switch_setup = -EINVAL;
+	usbphyc->nphys = of_get_child_count(np);
+	usbphyc->phys = xzalloc(usbphyc->nphys * sizeof(*usbphyc->phys));
+
+	for_each_child_of_node(np, child) {
+		struct stm32_usbphyc_phy *usbphyc_phy;
+		struct phy *phy;
+		u32 index;
+		int i;
+
+		phy = phy_create(dev, child, &stm32_usbphyc_phy_ops);
+		if (IS_ERR(phy)) {
+			ret = PTR_ERR(phy);
+			if (ret != -EPROBE_DEFER)
+				dev_err(dev, "failed to create phy%d: %d\n",
+					port, ret);
+			goto clk_disable;
+		}
+
+		usbphyc_phy = xzalloc(sizeof(*usbphyc_phy));
+
+		for (i = 0; i < NUM_SUPPLIES; i++)
+			usbphyc_phy->supplies[i].supply = supplies_names[i];
+
+		ret = regulator_bulk_get(&phy->dev, NUM_SUPPLIES,
+					 usbphyc_phy->supplies);
+		if (ret) {
+			if (ret != -EPROBE_DEFER)
+				dev_err(&phy->dev,
+					"failed to get regulators: %d\n", ret);
+			goto clk_disable;
+		}
+
+		ret = of_property_read_u32(child, "reg", &index);
+		if (ret || index > usbphyc->nphys) {
+			dev_err(&phy->dev, "invalid reg property: %d\n", ret);
+			goto clk_disable;
+		}
+
+		usbphyc->phys[port] = usbphyc_phy;
+		phy_set_bus_width(phy, 8);
+		phy_set_drvdata(phy, usbphyc_phy);
+
+		usbphyc->phys[port]->phy = phy;
+		usbphyc->phys[port]->usbphyc = usbphyc;
+		usbphyc->phys[port]->index = index;
+		usbphyc->phys[port]->active = false;
+
+		port++;
+	}
+
+	phy_provider = of_phy_provider_register(dev, stm32_usbphyc_of_xlate);
+	if (IS_ERR(phy_provider)) {
+		ret = PTR_ERR(phy_provider);
+		dev_err(dev, "failed to register phy provider: %d\n", ret);
+		goto clk_disable;
+	}
+
+	version = readl(usbphyc->base + STM32_USBPHYC_VERSION);
+	dev_info(dev, "registered rev: %lu.%lu\n",
+		 FIELD_GET(MAJREV, version), FIELD_GET(MINREV, version));
+
+	return 0;
+
+clk_disable:
+	clk_disable(usbphyc->clk);
+
+	return ret;
+}
+
+static void stm32_usbphyc_remove(struct device_d *dev)
+{
+	struct stm32_usbphyc *usbphyc = dev->priv;
+
+	clk_disable(usbphyc->clk);
+}
+
+static const struct of_device_id stm32_usbphyc_of_match[] = {
+	{ .compatible = "st,stm32mp1-usbphyc", },
+	{ /* sentinel */ },
+};
+
+static struct driver_d stm32_usbphyc_driver = {
+	.name = "stm32-usbphyc",
+	.probe = stm32_usbphyc_probe,
+	.remove = stm32_usbphyc_remove,
+	.of_compatible = stm32_usbphyc_of_match,
+};
+device_platform_driver(stm32_usbphyc_driver);
+
+MODULE_DESCRIPTION("STMicroelectronics STM32 USBPHYC driver");
+MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.25.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH 0/6] phy: usb: add support for STM32 usbphyc
  2020-02-20 10:01 [PATCH 0/6] phy: usb: add support for STM32 usbphyc Ahmad Fatoum
                   ` (5 preceding siblings ...)
  2020-02-20 10:01 ` [PATCH 6/6] phy: port over Linux stm32 usbphyc driver Ahmad Fatoum
@ 2020-02-25  8:11 ` Sascha Hauer
  6 siblings, 0 replies; 8+ messages in thread
From: Sascha Hauer @ 2020-02-25  8:11 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: barebox, mgr, rcz

On Thu, Feb 20, 2020 at 11:01:05AM +0100, Ahmad Fatoum wrote:
> This is only USB PHY support. Changes to the DWC2 (OTG) and/or OHCI
> (Host) are still needed to make USB on the MP1 work.
> 
> I haven't yet groked the relation between generic phy and usb phy
> frameworks, but the code is a straight port from Linux and it seems to
> work[1], so let's upstream it now to make collaboration easier.
> 
> [1]: clock the EHCI and power on the PHY and 20% of the time, the bus
>      can be enumerated completely. Still needs further debugging,
>      but it's unlikely that the PHY driver is at fault.
> 
> Cheers,
> Ahmad Fatoum (6):
>   regulator: import Linux regulator_bulk API
>   phy: remove unused init_data parameter
>   phy: populate existing ->pwr member with phy-supply
>   phy: introduce phy_get_by_index
>   regulator: port over Linux stm32 PWR regulator driver
>   phy: port over Linux stm32 usbphyc driver

Applied, thanks

Sascha

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

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

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-20 10:01 [PATCH 0/6] phy: usb: add support for STM32 usbphyc Ahmad Fatoum
2020-02-20 10:01 ` [PATCH 1/6] regulator: import Linux regulator_bulk API Ahmad Fatoum
2020-02-20 10:01 ` [PATCH 2/6] phy: remove unused init_data parameter Ahmad Fatoum
2020-02-20 10:01 ` [PATCH 3/6] phy: populate existing ->pwr member with phy-supply Ahmad Fatoum
2020-02-20 10:01 ` [PATCH 4/6] phy: introduce phy_get_by_index Ahmad Fatoum
2020-02-20 10:01 ` [PATCH 5/6] regulator: port over Linux stm32 PWR regulator driver Ahmad Fatoum
2020-02-20 10:01 ` [PATCH 6/6] phy: port over Linux stm32 usbphyc driver Ahmad Fatoum
2020-02-25  8:11 ` [PATCH 0/6] phy: usb: add support for STM32 usbphyc Sascha Hauer

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