All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 0/8] phy: Add new Exynos USB 2.0 PHY driver
@ 2014-01-29 17:29 Kamil Debski
  2014-01-29 17:29 ` [PATCH v6 1/8] phy: core: Add an exported of_phy_get function Kamil Debski
                   ` (7 more replies)
  0 siblings, 8 replies; 30+ messages in thread
From: Kamil Debski @ 2014-01-29 17:29 UTC (permalink / raw)
  To: linux-kernel, linux-samsung-soc, linux-usb, devicetree
  Cc: kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, yulgon.kim, p.paneri, av.tikhomirov,
	jg1.han, galak, matt.porter, k.debski, tjakobi, stern

Hi,

This is the sixth version of the patchset. It adds a new Exynos USB 2.0 PHY
driver. The driver uses the Generic PHY Framework.

Again, I would like to thank everyone who commented and read through the fifth
version of the patchset. Your input is very much appreciated. The last version
had two alternative approaches included. This allowed a comparison between these
approaches to the Exynos USB 2.0 PHY driver. The original idea was chosen as it
gave the optimal compromise between code readability and size of the driver.

These patchset depend on a patch by Leela Krishna Amudala.
[PATCH V11 1/3] ARM: dts: Add pmu sysreg node to exynos5250 and exynos5420
dtsi files [1].

Best wishes,
Kamil Debski

[1] - http://www.spinics.net/lists/linux-samsung-soc/msg24528.html

----------------
Changes from v5:
1) phy: core: Add an exported of_phy_get function
- corrected behaviour of the modification when GENERIC_PHY is not enabled
  by adding a stub of the of_phy_get function
2) phy: core: Add devm_of_phy_get to phy-core
- corrected behaviour of the modification when GENERIC_PHY is not enabled
  by adding a stub of the devm_of_phy_get function
3) dts: Add usb2phy to Exynos 4
- no change
4) dts: Add usb2phy to Exynos 5250
- in the previous version, this patch included some phy-exynos5250-usb2.c code
  by mistake, the code has been remove and added to the proper patch
5) phy: Add new Exynos USB PHY driver
- changed strings from Exynos 4212 to Exynos 4x12, as the Exynos 4212 driver is
  actually a driver for the whole Exynos 4x12 family
- added documentation to the Exynos USB 2.0 PHY driver adaptaion layer
- corrected strings HSCI -> HSIC
- fixed a problem introduced by previous change - on Exynos 4x12 the HSIC did
  not work on its own
- mode switch support was added to Exynos 4x12 (same io pins are used by host
  and device)
- support for phy_set_bus_width introduced by Matt Porter was added
6) phy: Add support for S5PV210 to the Exynos USB PHY
- setting of clk register was fixed
7) phy: Add Exynos 5250 support to the Exynos USB 2.0 PHY
- supoprt was added for HSIC and device
8) usb: ehci-exynos: Change to use phy provided by the generic phy framework
- DT documentation was moved from usb-ehci.txt to exynos-usb.txt

----------------
Changes from v4:
1) phy: core: Add an exported of_phy_get function
- the new exported function of_phy_get was changed to take the phy's name as a
  parameter instead of the index
2) phy: core: Add devm_of_phy_get to phy-core
- fixes made in the comments to devm_of_phy_get
3) phy: Add new Exynos USB PHY driver
- move the documentation from a new to an existing file - samsung-phy.txt
- fix typos and uppercase hex addresses
- add more explanations to Kconfig (checkpatch still complains, but I find it
  hard to think what else could I add)
- add selects MFD_SYSCON as the driver needs it (Thank you, Tobias!)
- cleanup included headers in both *.c and .h files
- use BIT(x) macro instead of (1 << x)
- replaced HOST and DEV with PHY0 and PHY1 in phy-exynos4212-usb2.c, the
  registers are described as PHYx in the documentation hence the decision to
  leave the PHYx naming
- fixed typo in exynos4210_rate_to_clk reg -> *reg
- change hax_mode_switch and enabled type to bool
4) usb: ehci-s5p: Change to use phy provided by the generic phy framework
- Put the issue of phy->otg in order - since the new phy driver does not provide
  this field. With the new driver the switch between host and device is done in
  power_on of the respective host and device phys.
5) usb: s3c-hsotg: Use the new Exynos USB phy driver with the generic phy
   framework
- fixed the example in the documentation
6) phy: Add support for S5PV210 to the Exynos USB PHY driver
- include files cleanup
- use BIT(x) macro instead of (1 << x)
7) phy: Add Exynos 5250 support to the Exynos USB 2.0 PHY driver
- include files cleanup
- use BIT(x) macro instead of (1 << x)
8) dts: Add usb2phy to Exynos 4
- no changes
9) dts: Add usb2phy to Exynos 5250
- no changes

----------------
Changes from v3:
- using PMU and system registers indirectly via syscon
- change labelling
- change Kconfig name
- fixed typos/stray whitespace
- move of_phy_provider_register() to the end of probe
- add a regular error return code to the rate_to_clk functions
- cleanup code and remove unused code
- change struct names to avoid collisions
- add mechanism to support multiple phys by the ehci driver

----------------
Changes from v2:
- rebase all patches to the usb-next branch
- fixes in the documentation file
  - remove wrong entries in the phy node (ranges, and #address- & #size-cells)
  - add clocks and clock-names as required properites
  - rephrase a few sentences
- fixes in the ehci-exynos.c file
  - move phy_name variable next to phy in exynos_ehci_hcd
  - remove otg from exynos_ehci_hcd as it was no longer used
  - move devm_phy_get after the Exynos5440 skip_phy check
- fixes in the s3c-hsotg.c file
  - cosmetic fixes (remove empty line that was wrongfully added)
- fixes in the main driver
  - remove cpu_type in favour for a boolean flag matched with the compatible
    value
  - rename files, structures, variables and Kconfig entires - change from simple
    "uphy" to "usb2_phy"
  - fix multiline comments style
  - simplify #ifdefs in of_device_id
  - fix Kconfig description
  - change dev_info to dev_dbg where reasonable
  - cosmetic changes (remove wrongful blank lines)
  - remove unnecessary reference counting

----------------
Changes from v1:
- the changes include minor fixes of the hardware initialization of the PHY
  module
- some other minor fixes were introduced

----------------------
Original cover letter:

Hi,

This patch adds a new drive for USB PHYs for Samsung SoCs. The driver is
using the Generic PHY Framework created by Kishon Vijay Abrahan I. It
can be found here https://lkml.org/lkml/2013/8/21/29. This patch adds
support to Exynos4 family of SoCs. Support for Exynos3 and Exynos5 is
planned to be added in the near future.

I welcome your comments.

----------------------

[1] https://lkml.org/lkml/2013/8/21/29


Kamil Debski (7):
  phy: core: Add an exported of_phy_get function
  phy: core: Add devm_of_phy_get to phy-core
  dts: Add usb2phy to Exynos 4
  dts: Add usb2phy to Exynos 5250
  phy: Add new Exynos USB PHY driver
  phy: Add Exynos 5250 support to the Exynos USB 2.0 PHY driver
  usb: ehci-exynos: Change to use phy provided by the generic phy
    framework

Mateusz Krawczuk (1):
  phy: Add support for S5PV210 to the Exynos USB PHY driver

 .../devicetree/bindings/arm/samsung/pmu.txt        |    2 +
 .../devicetree/bindings/phy/samsung-phy.txt        |   57 +++
 .../devicetree/bindings/usb/exynos-usb.txt         |   13 +
 Documentation/phy/samsung-usb2.txt                 |  135 +++++++
 arch/arm/boot/dts/exynos4.dtsi                     |   31 ++
 arch/arm/boot/dts/exynos4210.dtsi                  |   17 +
 arch/arm/boot/dts/exynos4x12.dtsi                  |   17 +
 arch/arm/boot/dts/exynos5250.dtsi                  |   24 ++
 drivers/phy/Kconfig                                |   50 +++
 drivers/phy/Makefile                               |    5 +
 drivers/phy/phy-core.c                             |   76 +++-
 drivers/phy/phy-exynos4210-usb2.c                  |  257 +++++++++++++
 drivers/phy/phy-exynos4x12-usb2.c                  |  323 ++++++++++++++++
 drivers/phy/phy-exynos5250-usb2.c                  |  406 ++++++++++++++++++++
 drivers/phy/phy-s5pv210-usb2.c                     |  199 ++++++++++
 drivers/phy/phy-samsung-usb2.c                     |  239 ++++++++++++
 drivers/phy/phy-samsung-usb2.h                     |   69 ++++
 drivers/usb/host/ehci-exynos.c                     |   97 +++--
 include/linux/phy/phy.h                            |   14 +
 19 files changed, 1988 insertions(+), 43 deletions(-)
 create mode 100644 Documentation/phy/samsung-usb2.txt
 create mode 100644 drivers/phy/phy-exynos4210-usb2.c
 create mode 100644 drivers/phy/phy-exynos4x12-usb2.c
 create mode 100644 drivers/phy/phy-exynos5250-usb2.c
 create mode 100644 drivers/phy/phy-s5pv210-usb2.c
 create mode 100644 drivers/phy/phy-samsung-usb2.c
 create mode 100644 drivers/phy/phy-samsung-usb2.h

-- 
1.7.9.5


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

* [PATCH v6 1/8] phy: core: Add an exported of_phy_get function
  2014-01-29 17:29 [PATCH v6 0/8] phy: Add new Exynos USB 2.0 PHY driver Kamil Debski
@ 2014-01-29 17:29 ` Kamil Debski
  2014-01-29 17:29 ` [PATCH v6 2/8] phy: core: Add devm_of_phy_get to phy-core Kamil Debski
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 30+ messages in thread
From: Kamil Debski @ 2014-01-29 17:29 UTC (permalink / raw)
  To: linux-kernel, linux-samsung-soc, linux-usb, devicetree
  Cc: kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, yulgon.kim, p.paneri, av.tikhomirov,
	jg1.han, galak, matt.porter, k.debski, tjakobi, stern

Previously the of_phy_get function took a struct device * and
was declared static. It was impossible to call it from
another driver and thus it was impossible to get phy defined
for a given node. The old function was renamed to _of_phy_get
and was left for internal use. of_phy_get function was added
and it was exported. The function enables to get a phy for
a given device tree node.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 drivers/phy/phy-core.c  |   45 ++++++++++++++++++++++++++++++++++++---------
 include/linux/phy/phy.h |    6 ++++++
 2 files changed, 42 insertions(+), 9 deletions(-)

diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 645c867..a1d4387 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -258,8 +258,8 @@ int phy_power_off(struct phy *phy)
 EXPORT_SYMBOL_GPL(phy_power_off);
 
 /**
- * of_phy_get() - lookup and obtain a reference to a phy by phandle
- * @dev: device that requests this phy
+ * _of_phy_get() - lookup and obtain a reference to a phy by phandle
+ * @np: device_node for which to get the phy
  * @index: the index of the phy
  *
  * Returns the phy associated with the given phandle value,
@@ -268,20 +268,17 @@ EXPORT_SYMBOL_GPL(phy_power_off);
  * not yet loaded. This function uses of_xlate call back function provided
  * while registering the phy_provider to find the phy instance.
  */
-static struct phy *of_phy_get(struct device *dev, int index)
+static struct phy *_of_phy_get(struct device_node *np, int index)
 {
 	int ret;
 	struct phy_provider *phy_provider;
 	struct phy *phy = NULL;
 	struct of_phandle_args args;
 
-	ret = of_parse_phandle_with_args(dev->of_node, "phys", "#phy-cells",
+	ret = of_parse_phandle_with_args(np, "phys", "#phy-cells",
 		index, &args);
-	if (ret) {
-		dev_dbg(dev, "failed to get phy in %s node\n",
-			dev->of_node->full_name);
+	if (ret)
 		return ERR_PTR(-ENODEV);
-	}
 
 	mutex_lock(&phy_provider_mutex);
 	phy_provider = of_phy_provider_lookup(args.np);
@@ -301,6 +298,36 @@ err0:
 }
 
 /**
+ * of_phy_get() - lookup and obtain a reference to a phy using a device_node.
+ * @np: device_node for which to get the phy
+ * @con_id: name of the phy from device's point of view
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * -ENODEV if there is no such phy. The caller is responsible for
+ * calling phy_put() to release that count.
+ */
+struct phy *of_phy_get(struct device_node *np, const char *con_id)
+{
+	struct phy *phy = NULL;
+	int index = 0;
+
+	if (con_id)
+		index = of_property_match_string(np, "phy-names", con_id);
+
+	phy = _of_phy_get(np, index);
+	if (IS_ERR(phy))
+		return phy;
+
+	if (!try_module_get(phy->ops->owner))
+		return ERR_PTR(-EPROBE_DEFER);
+
+	get_device(&phy->dev);
+
+	return phy;
+}
+EXPORT_SYMBOL_GPL(of_phy_get);
+
+/**
  * phy_put() - release the PHY
  * @phy: the phy returned by phy_get()
  *
@@ -388,7 +415,7 @@ struct phy *phy_get(struct device *dev, const char *string)
 	if (dev->of_node) {
 		index = of_property_match_string(dev->of_node, "phy-names",
 			string);
-		phy = of_phy_get(dev, index);
+		phy = _of_phy_get(dev->of_node, index);
 		if (IS_ERR(phy)) {
 			dev_err(dev, "unable to find phy\n");
 			return phy;
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index e273e5a..5cffc69 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -149,6 +149,7 @@ struct phy *phy_get(struct device *dev, const char *string);
 struct phy *devm_phy_get(struct device *dev, const char *string);
 void phy_put(struct phy *phy);
 void devm_phy_put(struct device *dev, struct phy *phy);
+struct phy *of_phy_get(struct device_node *np, const char *con_id);
 struct phy *of_phy_simple_xlate(struct device *dev,
 	struct of_phandle_args *args);
 struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
@@ -245,6 +246,11 @@ static inline void devm_phy_put(struct device *dev, struct phy *phy)
 {
 }
 
+struct phy *of_phy_get(struct device_node *np, const char *con_id)
+{
+	return ERR_PTR(-ENOSYS);
+}
+
 static inline struct phy *of_phy_simple_xlate(struct device *dev,
 	struct of_phandle_args *args)
 {
-- 
1.7.9.5


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

* [PATCH v6 2/8] phy: core: Add devm_of_phy_get to phy-core
  2014-01-29 17:29 [PATCH v6 0/8] phy: Add new Exynos USB 2.0 PHY driver Kamil Debski
  2014-01-29 17:29 ` [PATCH v6 1/8] phy: core: Add an exported of_phy_get function Kamil Debski
@ 2014-01-29 17:29 ` Kamil Debski
  2014-01-29 17:29 ` [PATCH v6 3/8] dts: Add usb2phy to Exynos 4 Kamil Debski
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 30+ messages in thread
From: Kamil Debski @ 2014-01-29 17:29 UTC (permalink / raw)
  To: linux-kernel, linux-samsung-soc, linux-usb, devicetree
  Cc: kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, yulgon.kim, p.paneri, av.tikhomirov,
	jg1.han, galak, matt.porter, k.debski, tjakobi, stern

Adding devm_of_phy_get will allow to get phys by supplying a
pointer to the struct device_node instead of struct device.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 drivers/phy/phy-core.c  |   31 +++++++++++++++++++++++++++++++
 include/linux/phy/phy.h |    8 ++++++++
 2 files changed, 39 insertions(+)

diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index a1d4387..6e32fb0 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -468,6 +468,37 @@ struct phy *devm_phy_get(struct device *dev, const char *string)
 EXPORT_SYMBOL_GPL(devm_phy_get);
 
 /**
+ * devm_of_phy_get() - lookup and obtain a reference to a phy.
+ * @dev: device that requests this phy
+ * @np: node containing the phy
+ * @con_id: name of the phy from device's point of view
+ *
+ * Gets the phy using of_phy_get(), and associates a device with it using
+ * devres. On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
+ */
+struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
+			    const char *con_id)
+{
+	struct phy **ptr, *phy;
+
+	ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	phy = of_phy_get(np, con_id);
+	if (!IS_ERR(phy)) {
+		*ptr = phy;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return phy;
+}
+EXPORT_SYMBOL_GPL(devm_of_phy_get);
+
+/**
  * phy_create() - create a new phy
  * @dev: device that is creating the new phy
  * @ops: function pointers for performing phy operations
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index 5cffc69..b17d6d2 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -147,6 +147,8 @@ static inline void phy_set_bus_width(struct phy *phy, int bus_width)
 }
 struct phy *phy_get(struct device *dev, const char *string);
 struct phy *devm_phy_get(struct device *dev, const char *string);
+struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
+			    const char *con_id);
 void phy_put(struct phy *phy);
 void devm_phy_put(struct device *dev, struct phy *phy);
 struct phy *of_phy_get(struct device_node *np, const char *con_id);
@@ -238,6 +240,12 @@ static inline struct phy *devm_phy_get(struct device *dev, const char *string)
 	return ERR_PTR(-ENOSYS);
 }
 
+struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
+			    const char *con_id)
+{
+	return ERR_PTR(-ENOSYS);
+}
+
 static inline void phy_put(struct phy *phy)
 {
 }
-- 
1.7.9.5


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

* [PATCH v6 3/8] dts: Add usb2phy to Exynos 4
  2014-01-29 17:29 [PATCH v6 0/8] phy: Add new Exynos USB 2.0 PHY driver Kamil Debski
  2014-01-29 17:29 ` [PATCH v6 1/8] phy: core: Add an exported of_phy_get function Kamil Debski
  2014-01-29 17:29 ` [PATCH v6 2/8] phy: core: Add devm_of_phy_get to phy-core Kamil Debski
@ 2014-01-29 17:29 ` Kamil Debski
  2014-01-29 20:50     ` Olof Johansson
  2014-01-29 17:29 ` [PATCH v6 4/8] dts: Add usb2phy to Exynos 5250 Kamil Debski
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 30+ messages in thread
From: Kamil Debski @ 2014-01-29 17:29 UTC (permalink / raw)
  To: linux-kernel, linux-samsung-soc, linux-usb, devicetree
  Cc: kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, yulgon.kim, p.paneri, av.tikhomirov,
	jg1.han, galak, matt.porter, k.debski, tjakobi, stern

Add support to PHY of USB2 of the Exynos 4 SoC.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 .../devicetree/bindings/arm/samsung/pmu.txt        |    2 ++
 arch/arm/boot/dts/exynos4.dtsi                     |   31 ++++++++++++++++++++
 arch/arm/boot/dts/exynos4210.dtsi                  |   17 +++++++++++
 arch/arm/boot/dts/exynos4x12.dtsi                  |   17 +++++++++++
 4 files changed, 67 insertions(+)

diff --git a/Documentation/devicetree/bindings/arm/samsung/pmu.txt b/Documentation/devicetree/bindings/arm/samsung/pmu.txt
index 307e727..a76f91d 100644
--- a/Documentation/devicetree/bindings/arm/samsung/pmu.txt
+++ b/Documentation/devicetree/bindings/arm/samsung/pmu.txt
@@ -3,6 +3,8 @@ SAMSUNG Exynos SoC series PMU Registers
 Properties:
  - name : should be 'syscon';
  - compatible : should contain two values. First value must be one from following list:
+		   - "samsung,exynos4210-pmu" - for Exynos4210 SoC,
+		   - "samsung,exynos4x12-pmu" - for Exynos4212 SoC,
 		   - "samsung,exynos5250-pmu" - for Exynos5250 SoC,
 		   - "samsung,exynos5420-pmu" - for Exynos5420 SoC.
 		second value must be always "syscon".
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index a73eeb5..031d07a 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -253,6 +253,17 @@
 		status = "disabled";
 	};
 
+	usbotg@12480000 {
+		compatible = "samsung,s3c6400-hsotg";
+		reg = <0x12480000 0x20000>;
+		interrupts = <0 71 0>;
+		clocks = <&clock 305>;
+		clock-names = "otg";
+		phys = <&usb2phy 0>;
+		phy-names = "usb2-phy";
+		status = "disabled";
+	};
+
 	ehci@12580000 {
 		compatible = "samsung,exynos4210-ehci";
 		reg = <0x12580000 0x100>;
@@ -260,6 +271,26 @@
 		clocks = <&clock 304>;
 		clock-names = "usbhost";
 		status = "disabled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		port@0 {
+			phys = <&usb2phy 1>;
+			phy-names = "host";
+			reg = <0>;
+			status = "disabled";
+		};
+		port@1 {
+			phys = <&usb2phy 2>;
+			phy-names = "hsic0";
+			reg = <1>;
+			status = "disabled";
+		};
+		port@2 {
+			phys = <&usb2phy 3>;
+			phy-names = "hsic1";
+			reg = <2>;
+			status = "disabled";
+		};
 	};
 
 	ohci@12590000 {
diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi
index 057d682..f9d06bb 100644
--- a/arch/arm/boot/dts/exynos4210.dtsi
+++ b/arch/arm/boot/dts/exynos4210.dtsi
@@ -155,4 +155,21 @@
 			samsung,lcd-wb;
 		};
 	};
+
+	pmu_reg: syscon@10020000 {
+		compatible = "samsung,exynos4210-pmu", "syscon";
+		reg = <0x10020000 0x4000>;
+	};
+
+	usb2phy: phy@125B0000 {
+		compatible = "samsung,exynos4210-usb2-phy";
+		reg = <0x125B0000 0x100>;
+		clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
+								<&clock 2>;
+		clock-names = "phy", "device", "host", "hsic0", "hsic1";
+		status = "disabled";
+		#phy-cells = <1>;
+		samsung,sysreg-phandle = <&sys_reg>;
+		samsung,pmureg-phandle = <&pmu_reg>;
+	};
 };
diff --git a/arch/arm/boot/dts/exynos4x12.dtsi b/arch/arm/boot/dts/exynos4x12.dtsi
index ad531fe..63b0fe5 100644
--- a/arch/arm/boot/dts/exynos4x12.dtsi
+++ b/arch/arm/boot/dts/exynos4x12.dtsi
@@ -176,4 +176,21 @@
 			};
 		};
 	};
+
+	pmu_reg: syscon@10020000 {
+		compatible = "samsung,exynos4x12-pmu", "syscon";
+		reg = <0x10020000 0x4000>;
+	};
+
+	usb2phy: phy@125B0000 {
+		compatible = "samsung,exynos4x12-usb2-phy";
+		reg = <0x125B0000 0x100>;
+		clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
+								<&clock 2>;
+		clock-names = "phy", "device", "host", "hsic0", "hsic1";
+		status = "disabled";
+		#phy-cells = <1>;
+		samsung,sysreg-phandle = <&sys_reg>;
+		samsung,pmureg-phandle = <&pmu_reg>;
+	};
 };
-- 
1.7.9.5


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

* [PATCH v6 4/8] dts: Add usb2phy to Exynos 5250
  2014-01-29 17:29 [PATCH v6 0/8] phy: Add new Exynos USB 2.0 PHY driver Kamil Debski
                   ` (2 preceding siblings ...)
  2014-01-29 17:29 ` [PATCH v6 3/8] dts: Add usb2phy to Exynos 4 Kamil Debski
@ 2014-01-29 17:29 ` Kamil Debski
  2014-01-29 17:29 ` [PATCH v6 5/8] phy: Add new Exynos USB PHY driver Kamil Debski
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 30+ messages in thread
From: Kamil Debski @ 2014-01-29 17:29 UTC (permalink / raw)
  To: linux-kernel, linux-samsung-soc, linux-usb, devicetree
  Cc: kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, yulgon.kim, p.paneri, av.tikhomirov,
	jg1.han, galak, matt.porter, k.debski, tjakobi, stern

Add support to PHY of USB2 of the Exynos 5250 SoC.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 arch/arm/boot/dts/exynos5250.dtsi |   24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
index c4480a1..efeaf87 100644
--- a/arch/arm/boot/dts/exynos5250.dtsi
+++ b/arch/arm/boot/dts/exynos5250.dtsi
@@ -163,6 +163,11 @@
 		interrupts = <0 47 0>;
 	};
 
+	sys_syscon: syscon@10040000 {
+		compatible = "samsung,exynos5250-sys", "syscon";
+		reg = <0x10050000 0x5000>;
+	};
+
 	pmu_syscon: syscon@10040000 {
 		compatible = "samsung,exynos5250-pmu", "syscon";
 		reg = <0x10040000 0x5000>;
@@ -505,6 +510,14 @@
 
 		clocks = <&clock 285>;
 		clock-names = "usbhost";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		port@0 {
+			reg = <0>;
+			phys = <&usb2_phy_new 1>;
+			phy-names = "host";
+			status = "ok";
+		};
 	};
 
 	usb@12120000 {
@@ -531,6 +544,17 @@
 		};
 	};
 
+	usb2_phy_new: phy@12130000 {
+		compatible = "samsung,exynos5250-usb2-phy";
+		reg = <0x12130000 0x100>;
+		clocks = <&clock 285>, <&clock 1>, <&clock 1>, <&clock 1>,
+								<&clock 1>;
+		clock-names = "phy", "device", "host", "hsic0", "hsic1";
+		#phy-cells = <1>;
+		samsung,sysreg-phandle = <&sys_syscon>;
+		samsung,pmureg-phandle = <&pmu_syscon>;
+	};
+
 	amba {
 		#address-cells = <1>;
 		#size-cells = <1>;
-- 
1.7.9.5


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

* [PATCH v6 5/8] phy: Add new Exynos USB PHY driver
  2014-01-29 17:29 [PATCH v6 0/8] phy: Add new Exynos USB 2.0 PHY driver Kamil Debski
                   ` (3 preceding siblings ...)
  2014-01-29 17:29 ` [PATCH v6 4/8] dts: Add usb2phy to Exynos 5250 Kamil Debski
@ 2014-01-29 17:29 ` Kamil Debski
  2014-03-03 14:27     ` Kishon Vijay Abraham I
  2014-01-29 17:29   ` Kamil Debski
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 30+ messages in thread
From: Kamil Debski @ 2014-01-29 17:29 UTC (permalink / raw)
  To: linux-kernel, linux-samsung-soc, linux-usb, devicetree
  Cc: kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, yulgon.kim, p.paneri, av.tikhomirov,
	jg1.han, galak, matt.porter, k.debski, tjakobi, stern

Add a new driver for the Exynos USB PHY. The new driver uses the generic
PHY framework. The driver includes support for the Exynos 4x10 and 4x12
SoC families.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 .../devicetree/bindings/phy/samsung-phy.txt        |   55 ++++
 Documentation/phy/samsung-usb2.txt                 |  135 ++++++++
 drivers/phy/Kconfig                                |   29 ++
 drivers/phy/Makefile                               |    3 +
 drivers/phy/phy-exynos4210-usb2.c                  |  257 ++++++++++++++++
 drivers/phy/phy-exynos4x12-usb2.c                  |  323 ++++++++++++++++++++
 drivers/phy/phy-samsung-usb2.c                     |  227 ++++++++++++++
 drivers/phy/phy-samsung-usb2.h                     |   67 ++++
 8 files changed, 1096 insertions(+)
 create mode 100644 Documentation/phy/samsung-usb2.txt
 create mode 100644 drivers/phy/phy-exynos4210-usb2.c
 create mode 100644 drivers/phy/phy-exynos4x12-usb2.c
 create mode 100644 drivers/phy/phy-samsung-usb2.c
 create mode 100644 drivers/phy/phy-samsung-usb2.h

diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt
index c0fccaa..6668c41 100644
--- a/Documentation/devicetree/bindings/phy/samsung-phy.txt
+++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt
@@ -20,3 +20,58 @@ Required properties:
 - compatible : should be "samsung,exynos5250-dp-video-phy";
 - reg : offset and length of the Display Port PHY register set;
 - #phy-cells : from the generic PHY bindings, must be 0;
+
+Samsung S5P/EXYNOS SoC series USB PHY
+-------------------------------------------------
+
+Required properties:
+- compatible : should be one of the listed compatibles:
+	- "samsung,exynos4210-usb2-phy"
+	- "samsung,exynos4x12-usb2-phy"
+- reg : a list of registers used by phy driver
+	- first and obligatory is the location of phy modules registers
+- samsung,sysreg-phandle - handle to syscon used to control the system registers
+- samsung,pmureg-phandle - handle to syscon used to control PMU registers
+- #phy-cells : from the generic phy bindings, must be 1;
+- clocks and clock-names:
+	- the "phy" clocks is required by the phy module
+	- next for each of the phys a clock has to be assigned, this clock
+	  will be used to determine clocking frequency for the phys
+	  (the labels are specified in the paragraph below)
+
+The first phandle argument in the PHY specifier identifies the PHY, its
+meaning is compatible dependent. For the currently supported SoCs (Exynos 4210
+and Exynos 4212) it is as follows:
+  0 - USB device ("device"),
+  1 - USB host ("host"),
+  2 - HSIC0 ("hsic0"),
+  3 - HSIC1 ("hsic1"),
+
+Exynos 4210 and Exynos 4212 use mode switching and require that mode switch
+register is supplied.
+
+Example:
+
+For Exynos 4412 (compatible with Exynos 4212):
+
+usbphy: phy@125b0000 {
+	compatible = "samsung,exynos4212-usb2-phy";
+	reg = <0x125b0000 0x100 0x10020704 0x0c 0x1001021c 0x4>;
+	clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
+							<&clock 2>;
+	clock-names = "phy", "device", "host", "hsic0", "hsic1";
+	status = "okay";
+	#phy-cells = <1>;
+	samsung,sysreg-phandle = <&sys_reg>;
+	samsung,pmureg-phandle = <&pmu_reg>;
+};
+
+Then the PHY can be used in other nodes such as:
+
+phy-consumer@12340000 {
+	phys = <&usbphy 2>;
+	phy-names = "phy";
+};
+
+Refer to DT bindings documentation of particular PHY consumer devices for more
+information about required PHYs and the way of specification.
diff --git a/Documentation/phy/samsung-usb2.txt b/Documentation/phy/samsung-usb2.txt
new file mode 100644
index 0000000..9f5826e
--- /dev/null
+++ b/Documentation/phy/samsung-usb2.txt
@@ -0,0 +1,135 @@
+.------------------------------------------------------------------------------+
+|			Samsung USB 2.0 PHY adaptation layer		       |
++-----------------------------------------------------------------------------+'
+
+| 1. Description
++----------------
+
+The architecture of the USB 2.0 PHY module in Samsung SoCs is similar
+among many SoCs. In spite of the similarities it proved difficult to
+create a one driver that would fit all these PHY controllers. Often
+the differences were minor and were found in particular bits of the
+registers of the PHY. In some rare cases the order of register writes or
+the PHY powering up process had to be altered. This adaptation layer is
+a compromise between having separate drivers and having a single driver
+with added support for many special cases.
+
+| 2. Files description
++----------------------
+
+- phy-samsung-usb2.c
+   This is the main file of the adaptation layer. This file contains
+   the probe function and provides two callbacks to the Generic PHY
+   Framework. This two callbacks are used to power on and power off the
+   phy. They carry out the common work that has to be done on all version
+   of the PHY module. Depending on which SoC was chosen they execute SoC
+   specific callbacks. The specific SoC version is selected by choosing
+   the appropriate compatible string. In addition, this file contains
+   struct of_device_id definitions for particular SoCs.
+
+- phy-samsung-usb2.h
+   This is the include file. It declares the structures used by this
+   driver. In addition it should contain extern declarations for
+   structures that describe particular SoCs.
+
+| 3. Supporting SoCs
++--------------------
+
+To support a new SoC a new file should be added to the drivers/phy
+directory. Each SoC's configuration is stored in an instance of the
+struct samsung_usb2_phy_config.
+
+struct samsung_usb2_phy_config {
+	const struct samsung_usb2_common_phy *phys;
+	unsigned int num_phys;
+	bool has_mode_switch;
+};
+
+The num_phys is the number of phys handled by the driver. *phys is an
+array that contains the configuration for each phy. The has_mode_switch
+property is a boolean flag that determines whether the SoC has USB host
+and device on a single pair of pins. If so, a special register has to
+be modified to change the internal routing of these pins between a USB
+device or host module.
+
+For example the configuration for Exynos 4210 is following:
+
+const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
+	.num_phys		= EXYNOS4210_NUM_PHYS,
+	.phys			= exynos4210_phys,
+}
+
+The exynos4210_phys configuration array is as follows:
+
+static const struct samsung_usb2_common_phy exynos4210_phys[] = {
+	{
+		.label		= "device",
+		.id		= EXYNOS4210_DEVICE,
+		.rate_to_clk	= exynos4210_rate_to_clk,
+		.power_on	= exynos4210_power_on,
+		.power_off	= exynos4210_power_off,
+	},
+	{
+		.label		= "host",
+		.id		= EXYNOS4210_HOST,
+		.rate_to_clk	= exynos4210_rate_to_clk,
+		.power_on	= exynos4210_power_on,
+		.power_off	= exynos4210_power_off,
+	},
+	{
+		.label		= "hsic0",
+		.id		= EXYNOS4210_HSIC0,
+		.rate_to_clk	= exynos4210_rate_to_clk,
+		.power_on	= exynos4210_power_on,
+		.power_off	= exynos4210_power_off,
+	},
+	{
+		.label		= "hsic1",
+		.id		= EXYNOS4210_HSIC1,
+		.rate_to_clk	= exynos4210_rate_to_clk,
+		.power_on	= exynos4210_power_on,
+		.power_off	= exynos4210_power_off,
+	},
+	{},
+};
+
+- int (*rate_to_clk)(unsigned long, u32 *)
+	The rate_to_clk callback is to convert the rate of the clock
+	used to clock the module to the value that should be written to
+	the registers of the PHY module.
+- int (*power_on)(struct samsung_usb2_phy_instance *);
+- int (*power_off)(struct samsung_usb2_phy_instance *);
+	These two callbacks are used to power on and power off the phy
+	by modifying appropriate registers.
+
+Final change to the driver is adding appropriate compatible value to the
+phy-samsung-usb2.c file. In case of Exynos 4210 the following lines were
+added to the struct of_device_id samsung_usb2_phy_of_match[] array:
+
+#ifdef CONFIG_PHY_EXYNOS4210_USB2
+	{
+		.compatible = "samsung,exynos4210-usb2-phy",
+		.data = &exynos4210_usb2_phy_config,
+	},
+#endif
+
+To add further flexibility to the driver the Kconfig file enables to
+include support for selected SoCs in the compiled driver. The Kconfig
+entry for Exynos 4210 is following:
+
+config PHY_EXYNOS4210_USB2
+	bool "Support for Exynos 4210"
+	depends on PHY_SAMSUNG_USB2
+	depends on CPU_EXYNOS4210
+	help
+	  Enable USB PHY support for Exynos 4210. This option requires that
+	  Samsung USB 2.0 PHY driver is enabled and means that support for this
+	  particular SoC is compiled in the driver. In case of Exynos 4210 four
+	  phys are available - device, host, HSCI0 and HSCI1.
+
+The newly created file that supports the new SoC has to be also added to the
+Makefile. In case of Exynos 4210 the added line is following:
+
+obj-$(CONFIG_PHY_EXYNOS4210_USB2)       += phy-exynos4210-usb2.o
+
+After completing these steps the support for the new SoC should be ready.
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index b901c47..3691d24 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -63,4 +63,33 @@ config BCM_KONA_USB2_PHY
 	help
 	  Enable this to support the Broadcom Kona USB 2.0 PHY.
 
+config PHY_SAMSUNG_USB2
+	tristate "Samsung USB 2.0 PHY driver"
+	select GENERIC_PHY
+	select MFD_SYSCON
+	help
+	  Enable this to support the Samsung USB 2.0 PHY driver for Samsung
+	  SoCs. This driver provides the interface for USB 2.0 PHY. Support for
+	  particular SoCs has to be enabled in addition to this driver. Number
+	  and type of supported phys depends on the SoC.
+
+config PHY_EXYNOS4210_USB2
+	bool "Support for Exynos 4210"
+	depends on PHY_SAMSUNG_USB2
+	depends on CPU_EXYNOS4210
+	help
+	  Enable USB PHY support for Exynos 4210. This option requires that
+	  Samsung USB 2.0 PHY driver is enabled and means that support for this
+	  particular SoC is compiled in the driver. In case of Exynos 4210 four
+	  phys are available - device, host, HSIC0 and HSIC1.
+
+config PHY_EXYNOS4X12_USB2
+	bool "Support for Exynos 4x12"
+	depends on PHY_SAMSUNG_USB2
+	depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
+	help
+	  Enable USB PHY support for Exynos 4x12. This option requires that
+	  Samsung USB 2.0 PHY driver is enabled and means that support for this
+	  particular SoC is compiled in the driver. In case of Exynos 4x12 four
+	  phys are available - device, host, HSIC0 and HSIC1.
 endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index b57c253..69d0b3f2 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -9,3 +9,6 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)	+= phy-exynos-mipi-video.o
 obj-$(CONFIG_PHY_MVEBU_SATA)		+= phy-mvebu-sata.o
 obj-$(CONFIG_OMAP_USB2)			+= phy-omap-usb2.o
 obj-$(CONFIG_TWL4030_USB)		+= phy-twl4030-usb.o
+obj-$(CONFIG_PHY_SAMSUNG_USB2)		+= phy-samsung-usb2.o
+obj-$(CONFIG_PHY_EXYNOS4210_USB2)	+= phy-exynos4210-usb2.o
+obj-$(CONFIG_PHY_EXYNOS4X12_USB2)	+= phy-exynos4x12-usb2.o
diff --git a/drivers/phy/phy-exynos4210-usb2.c b/drivers/phy/phy-exynos4210-usb2.c
new file mode 100644
index 0000000..8f86abf
--- /dev/null
+++ b/drivers/phy/phy-exynos4210-usb2.c
@@ -0,0 +1,257 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Kamil Debski <k.debski@samsung.com>
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include "phy-samsung-usb2.h"
+
+/* Exynos USB PHY registers */
+
+/* PHY power control */
+#define EXYNOS_4210_UPHYPWR			0x0
+
+#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND	BIT(0)
+#define EXYNOS_4210_UPHYPWR_PHY0_PWR		BIT(3)
+#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR	BIT(4)
+#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP		BIT(5)
+#define EXYNOS_4210_UPHYPWR_PHY0	( \
+	EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \
+	EXYNOS_4210_UPHYPWR_PHY0_PWR | \
+	EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \
+	EXYNOS_4210_UPHYPWR_PHY0_SLEEP)
+
+#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND	BIT(6)
+#define EXYNOS_4210_UPHYPWR_PHY1_PWR		BIT(7)
+#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP		BIT(8)
+#define EXYNOS_4210_UPHYPWR_PHY1 ( \
+	EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \
+	EXYNOS_4210_UPHYPWR_PHY1_PWR | \
+	EXYNOS_4210_UPHYPWR_PHY1_SLEEP)
+
+#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND	BIT(9)
+#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP		BIT(10)
+#define EXYNOS_4210_UPHYPWR_HSIC0 ( \
+	EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \
+	EXYNOS_4210_UPHYPWR_HSIC0_SLEEP)
+
+#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND	BIT(11)
+#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP		BIT(12)
+#define EXYNOS_4210_UPHYPWR_HSIC1 ( \
+	EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \
+	EXYNOS_4210_UPHYPWR_HSIC1_SLEEP)
+
+/* PHY clock control */
+#define EXYNOS_4210_UPHYCLK			0x4
+
+#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK	(0x3 << 0)
+#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ	(0x0 << 0)
+#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ	(0x3 << 0)
+#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
+
+#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP	BIT(2)
+#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON	BIT(4)
+#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON	BIT(7)
+
+/* PHY reset control */
+#define EXYNOS_4210_UPHYRST			0x8
+
+#define EXYNOS_4210_URSTCON_PHY0		BIT(0)
+#define EXYNOS_4210_URSTCON_OTG_HLINK		BIT(1)
+#define EXYNOS_4210_URSTCON_OTG_PHYLINK		BIT(2)
+#define EXYNOS_4210_URSTCON_PHY1_ALL		BIT(3)
+#define EXYNOS_4210_URSTCON_PHY1_P0		BIT(4)
+#define EXYNOS_4210_URSTCON_PHY1_P1P2		BIT(5)
+#define EXYNOS_4210_URSTCON_HOST_LINK_ALL	BIT(6)
+#define EXYNOS_4210_URSTCON_HOST_LINK_P0	BIT(7)
+#define EXYNOS_4210_URSTCON_HOST_LINK_P1	BIT(8)
+#define EXYNOS_4210_URSTCON_HOST_LINK_P2	BIT(9)
+
+/* Isolation, configured in the power management unit */
+#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET	0x704
+#define EXYNOS_4210_USB_ISOL_DEVICE		BIT(0)
+#define EXYNOS_4210_USB_ISOL_HOST_OFFSET	0x708
+#define EXYNOS_4210_USB_ISOL_HOST		BIT(0)
+
+/* USBYPHY1 Floating prevention */
+#define EXYNOS_4210_UPHY1CON			0x34
+#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION	0x1
+
+/* Mode switching SUB Device <-> Host */
+#define EXYNOS_4210_MODE_SWITCH_OFFSET		0x21c
+#define EXYNOS_4210_MODE_SWITCH_MASK		1
+#define EXYNOS_4210_MODE_SWITCH_DEVICE		0
+#define EXYNOS_4210_MODE_SWITCH_HOST		1
+
+enum exynos4210_phy_id {
+	EXYNOS4210_DEVICE,
+	EXYNOS4210_HOST,
+	EXYNOS4210_HSIC0,
+	EXYNOS4210_HSIC1,
+	EXYNOS4210_NUM_PHYS,
+};
+
+/*
+ * exynos4210_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register.
+ */
+static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg)
+{
+	switch (rate) {
+	case 12 * MHZ:
+		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
+		break;
+	case 24 * MHZ:
+		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
+		break;
+	case 48 * MHZ:
+		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+	u32 offset;
+	u32 mask;
+
+	switch (inst->cfg->id) {
+	case EXYNOS4210_DEVICE:
+		offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET;
+		mask = EXYNOS_4210_USB_ISOL_DEVICE;
+		break;
+	case EXYNOS4210_HOST:
+		offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET;
+		mask = EXYNOS_4210_USB_ISOL_HOST;
+		break;
+	default:
+		return;
+	};
+
+	regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
+}
+
+static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+	u32 rstbits = 0;
+	u32 phypwr = 0;
+	u32 rst;
+	u32 pwr;
+
+	switch (inst->cfg->id) {
+	case EXYNOS4210_DEVICE:
+		phypwr =	EXYNOS_4210_UPHYPWR_PHY0;
+		rstbits =	EXYNOS_4210_URSTCON_PHY0;
+		break;
+	case EXYNOS4210_HOST:
+		phypwr =	EXYNOS_4210_UPHYPWR_PHY1;
+		rstbits =	EXYNOS_4210_URSTCON_PHY1_ALL |
+				EXYNOS_4210_URSTCON_PHY1_P0 |
+				EXYNOS_4210_URSTCON_PHY1_P1P2 |
+				EXYNOS_4210_URSTCON_HOST_LINK_ALL |
+				EXYNOS_4210_URSTCON_HOST_LINK_P0;
+		writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON);
+		break;
+	case EXYNOS4210_HSIC0:
+		phypwr =	EXYNOS_4210_UPHYPWR_HSIC0;
+		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
+				EXYNOS_4210_URSTCON_HOST_LINK_P1;
+		break;
+	case EXYNOS4210_HSIC1:
+		phypwr =	EXYNOS_4210_UPHYPWR_HSIC1;
+		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
+				EXYNOS_4210_URSTCON_HOST_LINK_P2;
+		break;
+	};
+
+	if (on) {
+		writel(inst->clk_reg_val, drv->reg_phy + EXYNOS_4210_UPHYCLK);
+
+		pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
+		pwr &= ~phypwr;
+		writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
+
+		rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST);
+		rst |= rstbits;
+		writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
+		udelay(10);
+		rst &= ~rstbits;
+		writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
+	} else {
+		pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
+		pwr |= phypwr;
+		writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
+	}
+}
+
+static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst)
+{
+	/* Order of initialisation is important - first power then isolation */
+	exynos4210_phy_pwr(inst, 1);
+	exynos4210_isol(inst, 0);
+
+	return 0;
+}
+
+static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst)
+{
+	exynos4210_isol(inst, 1);
+	exynos4210_phy_pwr(inst, 0);
+
+	return 0;
+}
+
+
+static const struct samsung_usb2_common_phy exynos4210_phys[] = {
+	{
+		.label		= "device",
+		.id		= EXYNOS4210_DEVICE,
+		.rate_to_clk	= exynos4210_rate_to_clk,
+		.power_on	= exynos4210_power_on,
+		.power_off	= exynos4210_power_off,
+	},
+	{
+		.label		= "host",
+		.id		= EXYNOS4210_HOST,
+		.rate_to_clk	= exynos4210_rate_to_clk,
+		.power_on	= exynos4210_power_on,
+		.power_off	= exynos4210_power_off,
+	},
+	{
+		.label		= "hsic0",
+		.id		= EXYNOS4210_HSIC0,
+		.rate_to_clk	= exynos4210_rate_to_clk,
+		.power_on	= exynos4210_power_on,
+		.power_off	= exynos4210_power_off,
+	},
+	{
+		.label		= "hsic1",
+		.id		= EXYNOS4210_HSIC1,
+		.rate_to_clk	= exynos4210_rate_to_clk,
+		.power_on	= exynos4210_power_on,
+		.power_off	= exynos4210_power_off,
+	},
+	{},
+};
+
+const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
+	.num_phys		= EXYNOS4210_NUM_PHYS,
+	.phys			= exynos4210_phys,
+	.has_mode_switch	= 0,
+};
+
diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c
new file mode 100644
index 0000000..ce39502
--- /dev/null
+++ b/drivers/phy/phy-exynos4x12-usb2.c
@@ -0,0 +1,323 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4x12 support
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Kamil Debski <k.debski@samsung.com>
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include "phy-samsung-usb2.h"
+
+/* Exynos USB PHY registers */
+
+/* PHY power control */
+#define EXYNOS_4x12_UPHYPWR			0x0
+
+#define EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND	BIT(0)
+#define EXYNOS_4x12_UPHYPWR_PHY0_PWR		BIT(3)
+#define EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR	BIT(4)
+#define EXYNOS_4x12_UPHYPWR_PHY0_SLEEP		BIT(5)
+#define EXYNOS_4x12_UPHYPWR_PHY0 ( \
+	EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND | \
+	EXYNOS_4x12_UPHYPWR_PHY0_PWR | \
+	EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR | \
+	EXYNOS_4x12_UPHYPWR_PHY0_SLEEP)
+
+#define EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND	BIT(6)
+#define EXYNOS_4x12_UPHYPWR_PHY1_PWR		BIT(7)
+#define EXYNOS_4x12_UPHYPWR_PHY1_SLEEP		BIT(8)
+#define EXYNOS_4x12_UPHYPWR_PHY1 ( \
+	EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND | \
+	EXYNOS_4x12_UPHYPWR_PHY1_PWR | \
+	EXYNOS_4x12_UPHYPWR_PHY1_SLEEP)
+
+#define EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND	BIT(9)
+#define EXYNOS_4x12_UPHYPWR_HSIC0_PWR		BIT(10)
+#define EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP		BIT(11)
+#define EXYNOS_4x12_UPHYPWR_HSIC0 ( \
+	EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND | \
+	EXYNOS_4x12_UPHYPWR_HSIC0_PWR | \
+	EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP)
+
+#define EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND	BIT(12)
+#define EXYNOS_4x12_UPHYPWR_HSIC1_PWR		BIT(13)
+#define EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP		BIT(14)
+#define EXYNOS_4x12_UPHYPWR_HSIC1 ( \
+	EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND | \
+	EXYNOS_4x12_UPHYPWR_HSIC1_PWR | \
+	EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP)
+
+/* PHY clock control */
+#define EXYNOS_4x12_UPHYCLK			0x4
+
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK	(0x7 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6	(0x0 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ	(0x1 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2	(0x3 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ	(0x4 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ	(0x5 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ	(0x7 << 0)
+
+#define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP	BIT(3)
+#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON	BIT(4)
+#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON	BIT(7)
+
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK	(0x7f << 10)
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ	(0x24 << 10)
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ	(0x1c << 10)
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ	(0x1a << 10)
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2	(0x15 << 10)
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ	(0x14 << 10)
+
+/* PHY reset control */
+#define EXYNOS_4x12_UPHYRST			0x8
+
+#define EXYNOS_4x12_URSTCON_PHY0		BIT(0)
+#define EXYNOS_4x12_URSTCON_OTG_HLINK		BIT(1)
+#define EXYNOS_4x12_URSTCON_OTG_PHYLINK		BIT(2)
+#define EXYNOS_4x12_URSTCON_HOST_PHY		BIT(3)
+#define EXYNOS_4x12_URSTCON_PHY1		BIT(4)
+#define EXYNOS_4x12_URSTCON_HSIC0		BIT(5)
+#define EXYNOS_4x12_URSTCON_HSIC1		BIT(6)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL	BIT(7)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P0	BIT(8)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P1	BIT(9)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P2	BIT(10)
+
+/* Isolation, configured in the power management unit */
+#define EXYNOS_4x12_USB_ISOL_OFFSET		0x704
+#define EXYNOS_4x12_USB_ISOL_OTG		BIT(0)
+#define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET	0x708
+#define EXYNOS_4x12_USB_ISOL_HSIC0		BIT(0)
+#define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET	0x70c
+#define EXYNOS_4x12_USB_ISOL_HSIC1		BIT(0)
+
+/* Mode switching SUB Device <-> Host */
+#define EXYNOS_4x12_MODE_SWITCH_OFFSET		0x21c
+#define EXYNOS_4x12_MODE_SWITCH_MASK		1
+#define EXYNOS_4x12_MODE_SWITCH_DEVICE		0
+#define EXYNOS_4x12_MODE_SWITCH_HOST		1
+
+enum exynos4x12_phy_id {
+	EXYNOS4x12_DEVICE,
+	EXYNOS4x12_HOST,
+	EXYNOS4x12_HSIC0,
+	EXYNOS4x12_HSIC1,
+	EXYNOS4x12_NUM_PHYS,
+};
+
+/*
+ * exynos4x12_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register.
+ */
+static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg)
+{
+	/* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */
+
+	switch (rate) {
+	case 9600 * KHZ:
+		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6;
+		break;
+	case 10 * MHZ:
+		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ;
+		break;
+	case 12 * MHZ:
+		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ;
+		break;
+	case 19200 * KHZ:
+		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2;
+		break;
+	case 20 * MHZ:
+		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ;
+		break;
+	case 24 * MHZ:
+		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ;
+		break;
+	case 50 * MHZ:
+		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst, bool on)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+	u32 offset;
+	u32 mask;
+
+	switch (inst->cfg->id) {
+	case EXYNOS4x12_DEVICE:
+	case EXYNOS4x12_HOST:
+		offset = EXYNOS_4x12_USB_ISOL_OFFSET;
+		mask = EXYNOS_4x12_USB_ISOL_OTG;
+		break;
+	case EXYNOS4x12_HSIC0:
+		offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET;
+		mask = EXYNOS_4x12_USB_ISOL_HSIC0;
+		break;
+	case EXYNOS4x12_HSIC1:
+		offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET;
+		mask = EXYNOS_4x12_USB_ISOL_HSIC1;
+		break;
+	default:
+		return;
+	};
+
+	regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
+}
+
+static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+	writel(inst->clk_reg_val, drv->reg_phy + EXYNOS_4x12_UPHYCLK);
+}
+
+static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+	u32 rstbits = 0;
+	u32 phypwr = 0;
+	u32 rst;
+	u32 pwr;
+	u32 mode = 0;
+	u32 switch_mode = 0;
+
+	switch (inst->cfg->id) {
+	case EXYNOS4x12_DEVICE:
+		phypwr =	EXYNOS_4x12_UPHYPWR_PHY0;
+		rstbits =	EXYNOS_4x12_URSTCON_PHY0;
+		mode =		EXYNOS_4x12_MODE_SWITCH_DEVICE;
+		switch_mode =	1;
+		break;
+	case EXYNOS4x12_HOST:
+		phypwr =	EXYNOS_4x12_UPHYPWR_PHY1;
+		rstbits =	EXYNOS_4x12_URSTCON_HOST_PHY;
+		mode =		EXYNOS_4x12_MODE_SWITCH_HOST;
+		switch_mode =	1;
+		break;
+	case EXYNOS4x12_HSIC0:
+		phypwr =	EXYNOS_4x12_UPHYPWR_HSIC0;
+		rstbits =	EXYNOS_4x12_URSTCON_HSIC1 |
+				EXYNOS_4x12_URSTCON_HOST_LINK_P0 |
+				EXYNOS_4x12_URSTCON_HOST_PHY;
+		break;
+	case EXYNOS4x12_HSIC1:
+		phypwr =	EXYNOS_4x12_UPHYPWR_HSIC1;
+		rstbits =	EXYNOS_4x12_URSTCON_HSIC1 |
+				EXYNOS_4x12_URSTCON_HOST_LINK_P1;
+		break;
+	};
+
+	if (on) {
+		if (switch_mode)
+			regmap_update_bits(drv->reg_sys,
+			        EXYNOS_4x12_MODE_SWITCH_OFFSET,
+			        EXYNOS_4x12_MODE_SWITCH_MASK,
+				mode);
+
+		pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
+		pwr &= ~phypwr;
+		writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
+
+		rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST);
+		rst |= rstbits;
+		writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
+		udelay(10);
+		rst &= ~rstbits;
+		writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
+	} else {
+		pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
+		pwr |= phypwr;
+		writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
+	}
+}
+
+static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+
+	inst->enabled = 1;
+	exynos4x12_setup_clk(inst);
+	exynos4x12_phy_pwr(inst, 1);
+	exynos4x12_isol(inst, 0);
+
+	/* Power on the device, as it is necessary for HSIC to work */
+	if (inst->cfg->id == EXYNOS4x12_HSIC0) {
+		struct samsung_usb2_phy_instance *device =
+					&drv->instances[EXYNOS4x12_DEVICE];
+		exynos4x12_phy_pwr(device, 1);
+		exynos4x12_isol(device, 0);
+	}
+
+	return 0;
+}
+
+static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+	struct samsung_usb2_phy_instance *device =
+					&drv->instances[EXYNOS4x12_DEVICE];
+
+	inst->enabled = 0;
+	exynos4x12_isol(inst, 1);
+	exynos4x12_phy_pwr(inst, 0);
+
+	if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) {
+		exynos4x12_isol(device, 1);
+		exynos4x12_phy_pwr(device, 0);
+	}
+
+	return 0;
+}
+
+
+static const struct samsung_usb2_common_phy exynos4x12_phys[] = {
+	{
+		.label		= "device",
+		.id		= EXYNOS4x12_DEVICE,
+		.rate_to_clk	= exynos4x12_rate_to_clk,
+		.power_on	= exynos4x12_power_on,
+		.power_off	= exynos4x12_power_off,
+	},
+	{
+		.label		= "host",
+		.id		= EXYNOS4x12_HOST,
+		.rate_to_clk	= exynos4x12_rate_to_clk,
+		.power_on	= exynos4x12_power_on,
+		.power_off	= exynos4x12_power_off,
+	},
+	{
+		.label		= "hsic0",
+		.id		= EXYNOS4x12_HSIC0,
+		.rate_to_clk	= exynos4x12_rate_to_clk,
+		.power_on	= exynos4x12_power_on,
+		.power_off	= exynos4x12_power_off,
+	},
+	{
+		.label		= "hsic1",
+		.id		= EXYNOS4x12_HSIC1,
+		.rate_to_clk	= exynos4x12_rate_to_clk,
+		.power_on	= exynos4x12_power_on,
+		.power_off	= exynos4x12_power_off,
+	},
+	{},
+};
+
+const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = {
+	.num_phys		= EXYNOS4x12_NUM_PHYS,
+	.phys			= exynos4x12_phys,
+	.has_mode_switch	= 1,
+};
+
diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c
new file mode 100644
index 0000000..c1b0fd5
--- /dev/null
+++ b/drivers/phy/phy-samsung-usb2.c
@@ -0,0 +1,227 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Kamil Debski <k.debski@samsung.com>
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include "phy-samsung-usb2.h"
+
+static int samsung_usb2_phy_power_on(struct phy *phy)
+{
+	struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+	int ret;
+
+	dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n",
+							inst->cfg->label);
+	ret = clk_prepare_enable(drv->clk);
+	if (ret)
+		goto err_main_clk;
+	ret = clk_prepare_enable(inst->clk);
+	if (ret)
+		goto err_instance_clk;
+	inst->rate = clk_get_rate(inst->clk);
+	if (inst->cfg->rate_to_clk) {
+		ret = inst->cfg->rate_to_clk(inst->rate, &inst->clk_reg_val);
+		if (ret)
+			goto err_get_rate;
+	}
+	if (inst->cfg->power_on) {
+		spin_lock(&drv->lock);
+		ret = inst->cfg->power_on(inst);
+		spin_unlock(&drv->lock);
+	}
+
+	return 0;
+
+err_get_rate:
+	clk_disable_unprepare(inst->clk);
+err_instance_clk:
+	clk_disable_unprepare(drv->clk);
+err_main_clk:
+	return ret;
+}
+
+static int samsung_usb2_phy_power_off(struct phy *phy)
+{
+	struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+	int ret = 0;
+
+	dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
+							inst->cfg->label);
+	if (inst->cfg->power_off) {
+		spin_lock(&drv->lock);
+		ret = inst->cfg->power_off(inst);
+		spin_unlock(&drv->lock);
+	}
+	clk_disable_unprepare(inst->clk);
+	clk_disable_unprepare(drv->clk);
+	return ret;
+}
+
+static struct phy_ops samsung_usb2_phy_ops = {
+	.power_on	= samsung_usb2_phy_power_on,
+	.power_off	= samsung_usb2_phy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static struct phy *samsung_usb2_phy_xlate(struct device *dev,
+					struct of_phandle_args *args)
+{
+	struct samsung_usb2_phy_driver *drv;
+
+	drv = dev_get_drvdata(dev);
+	if (!drv)
+		return ERR_PTR(-EINVAL);
+
+	if (WARN_ON(args->args[0] >= drv->cfg->num_phys))
+		return ERR_PTR(-ENODEV);
+
+	return drv->instances[args->args[0]].phy;
+}
+
+static const struct of_device_id samsung_usb2_phy_of_match[] = {
+#ifdef CONFIG_PHY_EXYNOS4210_USB2
+	{
+		.compatible = "samsung,exynos4210-usb2-phy",
+		.data = &exynos4210_usb2_phy_config,
+	},
+#endif
+#ifdef CONFIG_PHY_EXYNOS4X12_USB2
+	{
+		.compatible = "samsung,exynos4x12-usb2-phy",
+		.data = &exynos4x12_usb2_phy_config,
+	},
+#endif
+	{ },
+};
+
+static int samsung_usb2_phy_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	const struct samsung_usb2_phy_config *cfg;
+	struct clk *clk;
+	struct device *dev = &pdev->dev;
+	struct phy_provider *phy_provider;
+	struct resource *mem;
+	struct samsung_usb2_phy_driver *drv;
+	int i;
+
+	if (!pdev->dev.of_node) {
+		dev_err(dev, "This driver is required to be instantiated from device tree\n");
+		return -EINVAL;
+	}
+
+	match = of_match_node(samsung_usb2_phy_of_match, pdev->dev.of_node);
+	if (!match) {
+		dev_err(dev, "of_match_node() failed\n");
+		return -EINVAL;
+	}
+	cfg = match->data;
+
+	drv = devm_kzalloc(dev, sizeof(struct samsung_usb2_phy_driver) +
+		cfg->num_phys * sizeof(struct samsung_usb2_phy_instance),
+								GFP_KERNEL);
+	if (!drv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, drv);
+	spin_lock_init(&drv->lock);
+
+	drv->cfg = cfg;
+	drv->dev = dev;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	drv->reg_phy = devm_ioremap_resource(dev, mem);
+	if (IS_ERR(drv->reg_phy)) {
+		dev_err(dev, "Failed to map register memory (phy)\n");
+		return PTR_ERR(drv->reg_phy);
+	}
+
+	drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+		"samsung,pmureg-phandle");
+	if (IS_ERR(drv->reg_pmu)) {
+		dev_err(dev, "Failed to map PMU registers (via syscon)\n");
+		return PTR_ERR(drv->reg_pmu);
+	}
+
+	if (drv->cfg->has_mode_switch) {
+		drv->reg_sys = syscon_regmap_lookup_by_phandle(
+				pdev->dev.of_node, "samsung,sysreg-phandle");
+		if (IS_ERR(drv->reg_sys)) {
+			dev_err(dev, "Failed to map system registers (via syscon)\n");
+			return PTR_ERR(drv->reg_sys);
+		}
+	}
+
+	drv->clk = devm_clk_get(dev, "phy");
+	if (IS_ERR(drv->clk)) {
+		dev_err(dev, "Failed to get clock of phy controller\n");
+		return PTR_ERR(drv->clk);
+	}
+
+	for (i = 0; i < drv->cfg->num_phys; i++) {
+		char *label = drv->cfg->phys[i].label;
+		struct samsung_usb2_phy_instance *p = &drv->instances[i];
+
+		dev_dbg(dev, "Creating phy \"%s\"\n", label);
+		p->phy = devm_phy_create(dev, &samsung_usb2_phy_ops, NULL);
+		if (IS_ERR(p->phy)) {
+			dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
+						label);
+			return PTR_ERR(p->phy);
+		}
+
+		p->cfg = &drv->cfg->phys[i];
+		p->drv = drv;
+		phy_set_bus_width(p->phy, 8);
+		phy_set_drvdata(p->phy, p);
+
+		clk = devm_clk_get(dev, p->cfg->label);
+		if (IS_ERR(clk)) {
+			dev_err(dev, "Failed to get clock of \"%s\" phy\n",
+								p->cfg->label);
+			return PTR_ERR(clk);
+		}
+		p->clk = clk;
+	}
+
+	phy_provider = devm_of_phy_provider_register(dev,
+							samsung_usb2_phy_xlate);
+	if (IS_ERR(phy_provider)) {
+		dev_err(drv->dev, "Failed to register phy provider\n");
+		return PTR_ERR(phy_provider);
+	}
+
+	return 0;
+}
+
+static struct platform_driver samsung_usb2_phy_driver = {
+	.probe	= samsung_usb2_phy_probe,
+	.driver = {
+		.of_match_table	= samsung_usb2_phy_of_match,
+		.name		= "samsung-usb2-phy",
+		.owner		= THIS_MODULE,
+	}
+};
+
+module_platform_driver(samsung_usb2_phy_driver);
+MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver");
+MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:samsung-usb2-phy");
+
diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h
new file mode 100644
index 0000000..868dd33
--- /dev/null
+++ b/drivers/phy/phy-samsung-usb2.h
@@ -0,0 +1,67 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Kamil Debski <k.debski@samsung.com>
+ *
+ * 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 _PHY_EXYNOS_USB2_H
+#define _PHY_EXYNOS_USB2_H
+
+#include <linux/clk.h>
+#include <linux/phy/phy.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+
+#define KHZ 1000
+#define MHZ (KHZ * KHZ)
+
+struct samsung_usb2_phy_driver;
+struct samsung_usb2_phy_instance;
+struct samsung_usb2_phy_config;
+
+struct samsung_usb2_phy_instance {
+	const struct samsung_usb2_common_phy *cfg;
+	struct clk *clk;
+	struct phy *phy;
+	struct samsung_usb2_phy_driver *drv;
+	unsigned long rate;
+	u32 clk_reg_val;
+	bool enabled;
+};
+
+struct samsung_usb2_phy_driver {
+	const struct samsung_usb2_phy_config *cfg;
+	struct clk *clk;
+	struct device *dev;
+	void __iomem *reg_phy;
+	struct regmap *reg_pmu;
+	struct regmap *reg_sys;
+	spinlock_t lock;
+	struct samsung_usb2_phy_instance instances[0];
+};
+
+struct samsung_usb2_common_phy {
+	int (*rate_to_clk)(unsigned long, u32 *);
+	int (*power_on)(struct samsung_usb2_phy_instance *);
+	int (*power_off)(struct samsung_usb2_phy_instance *);
+	unsigned int id;
+	char *label;
+};
+
+
+struct samsung_usb2_phy_config {
+	const struct samsung_usb2_common_phy *phys;
+	unsigned int num_phys;
+	bool has_mode_switch;
+};
+
+extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config;
+extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config;
+#endif
+
-- 
1.7.9.5


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

* [PATCH v6 6/8] phy: Add support for S5PV210 to the Exynos USB PHY driver
@ 2014-01-29 17:29   ` Kamil Debski
  0 siblings, 0 replies; 30+ messages in thread
From: Kamil Debski @ 2014-01-29 17:29 UTC (permalink / raw)
  To: linux-kernel, linux-samsung-soc, linux-usb, devicetree
  Cc: kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, yulgon.kim, p.paneri, av.tikhomirov,
	jg1.han, galak, matt.porter, k.debski, tjakobi, stern

From: Mateusz Krawczuk <mat.krawczuk@gmail.com>

Add support for the Samsung's S5PV210 SoC to the Exynos USB PHY driver.

Signed-off-by: Mateusz Krawczuk <m.krawczuk@partner.samsung.com>
[k.debski@samsung.com: cleanup and commit description]
[k.debski@samsung.com: make changes accordingly to the mailing list
comments]
[k.debski@samsung.com: fix bug with setting of clk register]
Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 .../devicetree/bindings/phy/samsung-phy.txt        |    1 +
 drivers/phy/Kconfig                                |   10 +
 drivers/phy/Makefile                               |    1 +
 drivers/phy/phy-s5pv210-usb2.c                     |  199 ++++++++++++++++++++
 drivers/phy/phy-samsung-usb2.c                     |    6 +
 drivers/phy/phy-samsung-usb2.h                     |    1 +
 6 files changed, 218 insertions(+)
 create mode 100644 drivers/phy/phy-s5pv210-usb2.c

diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt
index 6668c41..710d41f 100644
--- a/Documentation/devicetree/bindings/phy/samsung-phy.txt
+++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt
@@ -26,6 +26,7 @@ Samsung S5P/EXYNOS SoC series USB PHY
 
 Required properties:
 - compatible : should be one of the listed compatibles:
+	- "samsung,s5pv210-usb2-phy"
 	- "samsung,exynos4210-usb2-phy"
 	- "samsung,exynos4x12-usb2-phy"
 - reg : a list of registers used by phy driver
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 3691d24..a5554b5 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -73,6 +73,16 @@ config PHY_SAMSUNG_USB2
 	  particular SoCs has to be enabled in addition to this driver. Number
 	  and type of supported phys depends on the SoC.
 
+config PHY_S5PV210_USB2
+	bool "Support for S5PV210"
+	depends on PHY_SAMSUNG_USB2
+	depends on ARCH_S5PV210
+	help
+	  Enable USB PHY support for S5PV210. This option requires that Samsung
+	  USB 2.0 PHY driver is enabled and means that support for this
+	  particular SoC is compiled in the driver. In case of S5PV210 two phys
+	  are available - device and host.
+
 config PHY_EXYNOS4210_USB2
 	bool "Support for Exynos 4210"
 	depends on PHY_SAMSUNG_USB2
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 69d0b3f2..d738f65 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -10,5 +10,6 @@ obj-$(CONFIG_PHY_MVEBU_SATA)		+= phy-mvebu-sata.o
 obj-$(CONFIG_OMAP_USB2)			+= phy-omap-usb2.o
 obj-$(CONFIG_TWL4030_USB)		+= phy-twl4030-usb.o
 obj-$(CONFIG_PHY_SAMSUNG_USB2)		+= phy-samsung-usb2.o
+obj-$(CONFIG_PHY_S5PV210_USB2)		+= phy-s5pv210-usb2.o
 obj-$(CONFIG_PHY_EXYNOS4210_USB2)	+= phy-exynos4210-usb2.o
 obj-$(CONFIG_PHY_EXYNOS4X12_USB2)	+= phy-exynos4x12-usb2.o
diff --git a/drivers/phy/phy-s5pv210-usb2.c b/drivers/phy/phy-s5pv210-usb2.c
new file mode 100644
index 0000000..08e5094
--- /dev/null
+++ b/drivers/phy/phy-s5pv210-usb2.c
@@ -0,0 +1,199 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver - S5PV210 support
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Authors: Kamil Debski <k.debski@samsung.com>
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/phy/phy.h>
+#include "phy-samsung-usb2.h"
+
+/* Exynos USB PHY registers */
+
+/* PHY power control */
+#define S5PV210_UPHYPWR			0x0
+
+#define S5PV210_UPHYPWR_PHY0_SUSPEND	BIT(0)
+#define S5PV210_UPHYPWR_PHY0_PWR	BIT(3)
+#define S5PV210_UPHYPWR_PHY0_OTG_PWR	BIT(4)
+#define S5PV210_UPHYPWR_PHY0	( \
+	S5PV210_UPHYPWR_PHY0_SUSPEND | \
+	S5PV210_UPHYPWR_PHY0_PWR | \
+	S5PV210_UPHYPWR_PHY0_OTG_PWR)
+
+#define S5PV210_UPHYPWR_PHY1_SUSPEND	BIT(6)
+#define S5PV210_UPHYPWR_PHY1_PWR	BIT(7)
+#define S5PV210_UPHYPWR_PHY1 ( \
+	S5PV210_UPHYPWR_PHY1_SUSPEND | \
+	S5PV210_UPHYPWR_PHY1_PWR)
+
+/* PHY clock control */
+#define S5PV210_UPHYCLK			0x4
+
+#define S5PV210_UPHYCLK_PHYFSEL_MASK	(0x3 << 0)
+#define S5PV210_UPHYCLK_PHYFSEL_48MHZ	(0x0 << 0)
+#define S5PV210_UPHYCLK_PHYFSEL_24MHZ	(0x3 << 0)
+#define S5PV210_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
+
+#define S5PV210_UPHYCLK_PHY0_ID_PULLUP	BIT(2)
+#define S5PV210_UPHYCLK_PHY0_COMMON_ON	BIT(4)
+#define S5PV210_UPHYCLK_PHY1_COMMON_ON	BIT(7)
+
+/* PHY reset control */
+#define S5PV210_UPHYRST			0x8
+
+#define S5PV210_URSTCON_PHY0		BIT(0)
+#define S5PV210_URSTCON_OTG_HLINK	BIT(1)
+#define S5PV210_URSTCON_OTG_PHYLINK	BIT(2)
+#define S5PV210_URSTCON_PHY1_ALL	BIT(3)
+#define S5PV210_URSTCON_HOST_LINK_ALL	BIT(4)
+
+/* Isolation, configured in the power management unit */
+#define S5PV210_USB_ISOL_DEVICE_OFFSET	0x704
+#define S5PV210_USB_ISOL_DEVICE		BIT(0)
+#define S5PV210_USB_ISOL_HOST_OFFSET	0x708
+#define S5PV210_USB_ISOL_HOST		BIT(1)
+
+
+enum s5pv210_phy_id {
+	S5PV210_DEVICE,
+	S5PV210_HOST,
+	S5PV210_NUM_PHYS,
+};
+
+/*
+ * s5pv210_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register.
+ */
+static int s5pv210_rate_to_clk(unsigned long rate, u32 *reg)
+{
+	pr_info("%lu\n", rate);
+	switch (rate) {
+	case 12 * MHZ:
+		*reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ;
+		break;
+	case 24 * MHZ:
+		*reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ;
+		break;
+	case 48 * MHZ:
+		*reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+}
+
+static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+	u32 mask;
+	u32 tmp;
+
+	if (!drv->reg_isol)
+		return;
+
+	switch (inst->cfg->id) {
+	case S5PV210_DEVICE:
+		mask = S5PV210_USB_ISOL_DEVICE;
+		break;
+	case S5PV210_HOST:
+		mask = S5PV210_USB_ISOL_HOST;
+		break;
+	default:
+		return;
+	};
+
+	regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
+}
+
+static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+	u32 rstbits = 0;
+	u32 phypwr = 0;
+	u32 rst;
+	u32 pwr;
+
+	switch (inst->cfg->id) {
+	case S5PV210_DEVICE:
+		phypwr =	S5PV210_UPHYPWR_PHY0;
+		rstbits =	S5PV210_URSTCON_PHY0;
+		break;
+	case S5PV210_HOST:
+		phypwr =	S5PV210_UPHYPWR_PHY1;
+		rstbits =	S5PV210_URSTCON_PHY1_ALL |
+				S5PV210_URSTCON_HOST_LINK_ALL;
+		break;
+	};
+
+	if (on) {
+		writel(inst->clk_reg_val, drv->reg_phy + S5PV210_UPHYCLK);
+
+		pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
+		pwr &= ~phypwr;
+		writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
+
+		rst = readl(drv->reg_phy + S5PV210_UPHYRST);
+		rst |= rstbits;
+		writel(rst, drv->reg_phy + S5PV210_UPHYRST);
+		udelay(10);
+		rst &= ~rstbits;
+		writel(rst, drv->reg_phy + S5PV210_UPHYRST);
+	} else {
+		pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
+		pwr |= phypwr;
+		writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
+	}
+}
+
+static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+
+	s5pv210_isol(inst, 0);
+	s5pv210_phy_pwr(inst, 1);
+
+	return 0;
+}
+
+static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+
+	s5pv210_phy_pwr(inst, 0);
+	s5pv210_isol(inst, 1);
+
+	return 0;
+}
+
+
+static const struct samsung_usb2_common_phy s5pv210_phys[] = {
+	{
+		.label		= "device",
+		.id		= S5PV210_DEVICE,
+		.rate_to_clk	= s5pv210_rate_to_clk,
+		.power_on	= s5pv210_power_on,
+		.power_off	= s5pv210_power_off,
+	},
+	{
+		.label		= "host",
+		.id		= S5PV210_HOST,
+		.rate_to_clk	= s5pv210_rate_to_clk,
+		.power_on	= s5pv210_power_on,
+		.power_off	= s5pv210_power_off,
+	},
+	{},
+};
+
+const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = {
+	.cpu		= TYPE_S5PV210,
+	.num_phys	= S5PV210_NUM_PHYS,
+	.phys		= s5pv210_phys,
+};
+
diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c
index c1b0fd5..3e48f17 100644
--- a/drivers/phy/phy-samsung-usb2.c
+++ b/drivers/phy/phy-samsung-usb2.c
@@ -95,6 +95,12 @@ static struct phy *samsung_usb2_phy_xlate(struct device *dev,
 }
 
 static const struct of_device_id samsung_usb2_phy_of_match[] = {
+#ifdef CONFIG_PHY_S5PV210_USB2
+	{
+		.compatible = "samsung,s5pv210-usb2-phy",
+		.data = &s5pv210_usb2_phy_config,
+	},
+#endif
 #ifdef CONFIG_PHY_EXYNOS4210_USB2
 	{
 		.compatible = "samsung,exynos4210-usb2-phy",
diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h
index 868dd33..ae41d0f 100644
--- a/drivers/phy/phy-samsung-usb2.h
+++ b/drivers/phy/phy-samsung-usb2.h
@@ -61,6 +61,7 @@ struct samsung_usb2_phy_config {
 	bool has_mode_switch;
 };
 
+extern const struct samsung_usb2_phy_config s5pv210_usb2_phy_config;
 extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config;
 extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config;
 #endif
-- 
1.7.9.5


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

* [PATCH v6 6/8] phy: Add support for S5PV210 to the Exynos USB PHY driver
@ 2014-01-29 17:29   ` Kamil Debski
  0 siblings, 0 replies; 30+ messages in thread
From: Kamil Debski @ 2014-01-29 17:29 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ, kishon-l0cyMroinI0,
	t.figa-Sze3O3UU22JBDgjK7y7TUQ, s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ,
	m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ,
	gautam.vivek-Sze3O3UU22JBDgjK7y7TUQ,
	mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w,
	yulgon.kim-Sze3O3UU22JBDgjK7y7TUQ,
	p.paneri-Sze3O3UU22JBDgjK7y7TUQ,
	av.tikhomirov-Sze3O3UU22JBDgjK7y7TUQ,
	jg1.han-Sze3O3UU22JBDgjK7y7TUQ, galak-sgV2jX0FEOL9JmXXK+q4OQ,
	matt.porter-QSEj5FYQhm4dnm+yROfE0A,
	k.debski-Sze3O3UU22JBDgjK7y7TUQ,
	tjakobi-o02PS0xoJP9W0yFyLvAVXMxlOr/tl8fh,
	stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz

From: Mateusz Krawczuk <mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Add support for the Samsung's S5PV210 SoC to the Exynos USB PHY driver.

Signed-off-by: Mateusz Krawczuk <m.krawczuk-GrGkmOP51GdLN7c7dRTbYkEOCMrvLtNR@public.gmane.org>
[k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org: cleanup and commit description]
[k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org: make changes accordingly to the mailing list
comments]
[k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org: fix bug with setting of clk register]
Signed-off-by: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
 .../devicetree/bindings/phy/samsung-phy.txt        |    1 +
 drivers/phy/Kconfig                                |   10 +
 drivers/phy/Makefile                               |    1 +
 drivers/phy/phy-s5pv210-usb2.c                     |  199 ++++++++++++++++++++
 drivers/phy/phy-samsung-usb2.c                     |    6 +
 drivers/phy/phy-samsung-usb2.h                     |    1 +
 6 files changed, 218 insertions(+)
 create mode 100644 drivers/phy/phy-s5pv210-usb2.c

diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt
index 6668c41..710d41f 100644
--- a/Documentation/devicetree/bindings/phy/samsung-phy.txt
+++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt
@@ -26,6 +26,7 @@ Samsung S5P/EXYNOS SoC series USB PHY
 
 Required properties:
 - compatible : should be one of the listed compatibles:
+	- "samsung,s5pv210-usb2-phy"
 	- "samsung,exynos4210-usb2-phy"
 	- "samsung,exynos4x12-usb2-phy"
 - reg : a list of registers used by phy driver
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 3691d24..a5554b5 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -73,6 +73,16 @@ config PHY_SAMSUNG_USB2
 	  particular SoCs has to be enabled in addition to this driver. Number
 	  and type of supported phys depends on the SoC.
 
+config PHY_S5PV210_USB2
+	bool "Support for S5PV210"
+	depends on PHY_SAMSUNG_USB2
+	depends on ARCH_S5PV210
+	help
+	  Enable USB PHY support for S5PV210. This option requires that Samsung
+	  USB 2.0 PHY driver is enabled and means that support for this
+	  particular SoC is compiled in the driver. In case of S5PV210 two phys
+	  are available - device and host.
+
 config PHY_EXYNOS4210_USB2
 	bool "Support for Exynos 4210"
 	depends on PHY_SAMSUNG_USB2
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 69d0b3f2..d738f65 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -10,5 +10,6 @@ obj-$(CONFIG_PHY_MVEBU_SATA)		+= phy-mvebu-sata.o
 obj-$(CONFIG_OMAP_USB2)			+= phy-omap-usb2.o
 obj-$(CONFIG_TWL4030_USB)		+= phy-twl4030-usb.o
 obj-$(CONFIG_PHY_SAMSUNG_USB2)		+= phy-samsung-usb2.o
+obj-$(CONFIG_PHY_S5PV210_USB2)		+= phy-s5pv210-usb2.o
 obj-$(CONFIG_PHY_EXYNOS4210_USB2)	+= phy-exynos4210-usb2.o
 obj-$(CONFIG_PHY_EXYNOS4X12_USB2)	+= phy-exynos4x12-usb2.o
diff --git a/drivers/phy/phy-s5pv210-usb2.c b/drivers/phy/phy-s5pv210-usb2.c
new file mode 100644
index 0000000..08e5094
--- /dev/null
+++ b/drivers/phy/phy-s5pv210-usb2.c
@@ -0,0 +1,199 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver - S5PV210 support
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Authors: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@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.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/phy/phy.h>
+#include "phy-samsung-usb2.h"
+
+/* Exynos USB PHY registers */
+
+/* PHY power control */
+#define S5PV210_UPHYPWR			0x0
+
+#define S5PV210_UPHYPWR_PHY0_SUSPEND	BIT(0)
+#define S5PV210_UPHYPWR_PHY0_PWR	BIT(3)
+#define S5PV210_UPHYPWR_PHY0_OTG_PWR	BIT(4)
+#define S5PV210_UPHYPWR_PHY0	( \
+	S5PV210_UPHYPWR_PHY0_SUSPEND | \
+	S5PV210_UPHYPWR_PHY0_PWR | \
+	S5PV210_UPHYPWR_PHY0_OTG_PWR)
+
+#define S5PV210_UPHYPWR_PHY1_SUSPEND	BIT(6)
+#define S5PV210_UPHYPWR_PHY1_PWR	BIT(7)
+#define S5PV210_UPHYPWR_PHY1 ( \
+	S5PV210_UPHYPWR_PHY1_SUSPEND | \
+	S5PV210_UPHYPWR_PHY1_PWR)
+
+/* PHY clock control */
+#define S5PV210_UPHYCLK			0x4
+
+#define S5PV210_UPHYCLK_PHYFSEL_MASK	(0x3 << 0)
+#define S5PV210_UPHYCLK_PHYFSEL_48MHZ	(0x0 << 0)
+#define S5PV210_UPHYCLK_PHYFSEL_24MHZ	(0x3 << 0)
+#define S5PV210_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
+
+#define S5PV210_UPHYCLK_PHY0_ID_PULLUP	BIT(2)
+#define S5PV210_UPHYCLK_PHY0_COMMON_ON	BIT(4)
+#define S5PV210_UPHYCLK_PHY1_COMMON_ON	BIT(7)
+
+/* PHY reset control */
+#define S5PV210_UPHYRST			0x8
+
+#define S5PV210_URSTCON_PHY0		BIT(0)
+#define S5PV210_URSTCON_OTG_HLINK	BIT(1)
+#define S5PV210_URSTCON_OTG_PHYLINK	BIT(2)
+#define S5PV210_URSTCON_PHY1_ALL	BIT(3)
+#define S5PV210_URSTCON_HOST_LINK_ALL	BIT(4)
+
+/* Isolation, configured in the power management unit */
+#define S5PV210_USB_ISOL_DEVICE_OFFSET	0x704
+#define S5PV210_USB_ISOL_DEVICE		BIT(0)
+#define S5PV210_USB_ISOL_HOST_OFFSET	0x708
+#define S5PV210_USB_ISOL_HOST		BIT(1)
+
+
+enum s5pv210_phy_id {
+	S5PV210_DEVICE,
+	S5PV210_HOST,
+	S5PV210_NUM_PHYS,
+};
+
+/*
+ * s5pv210_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register.
+ */
+static int s5pv210_rate_to_clk(unsigned long rate, u32 *reg)
+{
+	pr_info("%lu\n", rate);
+	switch (rate) {
+	case 12 * MHZ:
+		*reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ;
+		break;
+	case 24 * MHZ:
+		*reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ;
+		break;
+	case 48 * MHZ:
+		*reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+}
+
+static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+	u32 mask;
+	u32 tmp;
+
+	if (!drv->reg_isol)
+		return;
+
+	switch (inst->cfg->id) {
+	case S5PV210_DEVICE:
+		mask = S5PV210_USB_ISOL_DEVICE;
+		break;
+	case S5PV210_HOST:
+		mask = S5PV210_USB_ISOL_HOST;
+		break;
+	default:
+		return;
+	};
+
+	regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
+}
+
+static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+	u32 rstbits = 0;
+	u32 phypwr = 0;
+	u32 rst;
+	u32 pwr;
+
+	switch (inst->cfg->id) {
+	case S5PV210_DEVICE:
+		phypwr =	S5PV210_UPHYPWR_PHY0;
+		rstbits =	S5PV210_URSTCON_PHY0;
+		break;
+	case S5PV210_HOST:
+		phypwr =	S5PV210_UPHYPWR_PHY1;
+		rstbits =	S5PV210_URSTCON_PHY1_ALL |
+				S5PV210_URSTCON_HOST_LINK_ALL;
+		break;
+	};
+
+	if (on) {
+		writel(inst->clk_reg_val, drv->reg_phy + S5PV210_UPHYCLK);
+
+		pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
+		pwr &= ~phypwr;
+		writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
+
+		rst = readl(drv->reg_phy + S5PV210_UPHYRST);
+		rst |= rstbits;
+		writel(rst, drv->reg_phy + S5PV210_UPHYRST);
+		udelay(10);
+		rst &= ~rstbits;
+		writel(rst, drv->reg_phy + S5PV210_UPHYRST);
+	} else {
+		pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
+		pwr |= phypwr;
+		writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
+	}
+}
+
+static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+
+	s5pv210_isol(inst, 0);
+	s5pv210_phy_pwr(inst, 1);
+
+	return 0;
+}
+
+static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+
+	s5pv210_phy_pwr(inst, 0);
+	s5pv210_isol(inst, 1);
+
+	return 0;
+}
+
+
+static const struct samsung_usb2_common_phy s5pv210_phys[] = {
+	{
+		.label		= "device",
+		.id		= S5PV210_DEVICE,
+		.rate_to_clk	= s5pv210_rate_to_clk,
+		.power_on	= s5pv210_power_on,
+		.power_off	= s5pv210_power_off,
+	},
+	{
+		.label		= "host",
+		.id		= S5PV210_HOST,
+		.rate_to_clk	= s5pv210_rate_to_clk,
+		.power_on	= s5pv210_power_on,
+		.power_off	= s5pv210_power_off,
+	},
+	{},
+};
+
+const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = {
+	.cpu		= TYPE_S5PV210,
+	.num_phys	= S5PV210_NUM_PHYS,
+	.phys		= s5pv210_phys,
+};
+
diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c
index c1b0fd5..3e48f17 100644
--- a/drivers/phy/phy-samsung-usb2.c
+++ b/drivers/phy/phy-samsung-usb2.c
@@ -95,6 +95,12 @@ static struct phy *samsung_usb2_phy_xlate(struct device *dev,
 }
 
 static const struct of_device_id samsung_usb2_phy_of_match[] = {
+#ifdef CONFIG_PHY_S5PV210_USB2
+	{
+		.compatible = "samsung,s5pv210-usb2-phy",
+		.data = &s5pv210_usb2_phy_config,
+	},
+#endif
 #ifdef CONFIG_PHY_EXYNOS4210_USB2
 	{
 		.compatible = "samsung,exynos4210-usb2-phy",
diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h
index 868dd33..ae41d0f 100644
--- a/drivers/phy/phy-samsung-usb2.h
+++ b/drivers/phy/phy-samsung-usb2.h
@@ -61,6 +61,7 @@ struct samsung_usb2_phy_config {
 	bool has_mode_switch;
 };
 
+extern const struct samsung_usb2_phy_config s5pv210_usb2_phy_config;
 extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config;
 extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config;
 #endif
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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	[flat|nested] 30+ messages in thread

* [PATCH v6 7/8] phy: Add Exynos 5250 support to the Exynos USB 2.0 PHY driver
  2014-01-29 17:29 [PATCH v6 0/8] phy: Add new Exynos USB 2.0 PHY driver Kamil Debski
                   ` (5 preceding siblings ...)
  2014-01-29 17:29   ` Kamil Debski
@ 2014-01-29 17:29 ` Kamil Debski
  2014-01-29 17:29 ` [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework Kamil Debski
  7 siblings, 0 replies; 30+ messages in thread
From: Kamil Debski @ 2014-01-29 17:29 UTC (permalink / raw)
  To: linux-kernel, linux-samsung-soc, linux-usb, devicetree
  Cc: kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, yulgon.kim, p.paneri, av.tikhomirov,
	jg1.han, galak, matt.porter, k.debski, tjakobi, stern

Add support for Exynos 5250. This driver is to replace the old
USB 2.0 PHY driver.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 .../devicetree/bindings/phy/samsung-phy.txt        |    1 +
 drivers/phy/Kconfig                                |   11 +
 drivers/phy/Makefile                               |    1 +
 drivers/phy/phy-exynos5250-usb2.c                  |  406 ++++++++++++++++++++
 drivers/phy/phy-samsung-usb2.c                     |    6 +
 drivers/phy/phy-samsung-usb2.h                     |    1 +
 6 files changed, 426 insertions(+)
 create mode 100644 drivers/phy/phy-exynos5250-usb2.c

diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt
index 710d41f..d1b534f 100644
--- a/Documentation/devicetree/bindings/phy/samsung-phy.txt
+++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt
@@ -29,6 +29,7 @@ Required properties:
 	- "samsung,s5pv210-usb2-phy"
 	- "samsung,exynos4210-usb2-phy"
 	- "samsung,exynos4x12-usb2-phy"
+	- "samsung,exynos5250-usb2-phy"
 - reg : a list of registers used by phy driver
 	- first and obligatory is the location of phy modules registers
 - samsung,sysreg-phandle - handle to syscon used to control the system registers
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index a5554b5..645ed66 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -102,4 +102,15 @@ config PHY_EXYNOS4X12_USB2
 	  Samsung USB 2.0 PHY driver is enabled and means that support for this
 	  particular SoC is compiled in the driver. In case of Exynos 4x12 four
 	  phys are available - device, host, HSIC0 and HSIC1.
+
+config PHY_EXYNOS5250_USB2
+	bool "Support for Exynos 5250"
+	depends on PHY_SAMSUNG_USB2
+	depends on SOC_EXYNOS5250
+	help
+	  Enable USB PHY support for Exynos 5250. This option requires that
+	  Samsung USB 2.0 PHY driver is enabled and means that support for this
+	  particular SoC is compiled in the driver. In case of Exynos 5250 four
+	  phys are available - device, host, HSIC0 and HSIC.
+
 endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index d738f65..fe7aaee 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_PHY_SAMSUNG_USB2)		+= phy-samsung-usb2.o
 obj-$(CONFIG_PHY_S5PV210_USB2)		+= phy-s5pv210-usb2.o
 obj-$(CONFIG_PHY_EXYNOS4210_USB2)	+= phy-exynos4210-usb2.o
 obj-$(CONFIG_PHY_EXYNOS4X12_USB2)	+= phy-exynos4x12-usb2.o
+obj-$(CONFIG_PHY_EXYNOS5250_USB2)	+= phy-exynos5250-usb2.o
diff --git a/drivers/phy/phy-exynos5250-usb2.c b/drivers/phy/phy-exynos5250-usb2.c
new file mode 100644
index 0000000..2603d5e
--- /dev/null
+++ b/drivers/phy/phy-exynos5250-usb2.c
@@ -0,0 +1,406 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 5250 support
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Kamil Debski <k.debski@samsung.com>
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include "phy-samsung-usb2.h"
+
+/* Exynos USB PHY registers */
+#define EXYNOS_5250_REFCLKSEL_CRYSTAL	0x0
+#define EXYNOS_5250_REFCLKSEL_XO	0x1
+#define EXYNOS_5250_REFCLKSEL_CLKCORE	0x2
+
+#define EXYNOS_5250_FSEL_9MHZ6		0x0
+#define EXYNOS_5250_FSEL_10MHZ		0x1
+#define EXYNOS_5250_FSEL_12MHZ		0x2
+#define EXYNOS_5250_FSEL_19MHZ2		0x3
+#define EXYNOS_5250_FSEL_20MHZ		0x4
+#define EXYNOS_5250_FSEL_24MHZ		0x5
+#define EXYNOS_5250_FSEL_50MHZ		0x7
+
+/* Normal host */
+#define EXYNOS_5250_HOSTPHYCTRL0			0x0
+
+#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL		BIT(31)
+#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT	19
+#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_MASK	\
+		(0x3 << EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT)
+#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT		16
+#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK \
+		(0x7 << EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT)
+#define EXYNOS_5250_HOSTPHYCTRL0_TESTBURNIN		BIT(11)
+#define EXYNOS_5250_HOSTPHYCTRL0_RETENABLE		BIT(10)
+#define EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N		BIT(9)
+#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_MASK		(0x3 << 7)
+#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_DUAL		(0x0 << 7)
+#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ID0		(0x1 << 7)
+#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ANALOGTEST	(0x2 << 7)
+#define EXYNOS_5250_HOSTPHYCTRL0_SIDDQ			BIT(6)
+#define EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP		BIT(5)
+#define EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND		BIT(4)
+#define EXYNOS_5250_HOSTPHYCTRL0_WORDINTERFACE		BIT(3)
+#define EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST		BIT(2)
+#define EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST		BIT(1)
+#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST		BIT(0)
+
+/* HSIC0 & HSIC1 */
+#define EXYNOS_5250_HSICPHYCTRL1			0x10
+#define EXYNOS_5250_HSICPHYCTRL2			0x20
+
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_MASK		(0x3 << 23)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT	(0x2 << 23)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_MASK		(0x7f << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12		(0x24 << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_15		(0x1c << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_16		(0x1a << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_19_2		(0x15 << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_20		(0x14 << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_SIDDQ			BIT(6)
+#define EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP		BIT(5)
+#define EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND		BIT(4)
+#define EXYNOS_5250_HSICPHYCTRLX_WORDINTERFACE		BIT(3)
+#define EXYNOS_5250_HSICPHYCTRLX_UTMISWRST		BIT(2)
+#define EXYNOS_5250_HSICPHYCTRLX_PHYSWRST		BIT(0)
+
+/* EHCI control */
+#define EXYNOS_5250_HOSTEHCICTRL			0x30
+#define EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN		BIT(29)
+#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR4		BIT(28)
+#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR8		BIT(27)
+#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR16		BIT(26)
+#define EXYNOS_5250_HOSTEHCICTRL_AUTOPPDONOVRCUREN	BIT(25)
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT	19
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK	\
+		(0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT)
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT	13
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_MASK	\
+		(0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT)
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL2_SHIFT	7
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK	\
+		(0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT)
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT	1
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_MASK \
+		(0x1 << EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT)
+#define EXYNOS_5250_HOSTEHCICTRL_SIMULATIONMODE		BIT(0)
+
+/* OHCI control */
+#define EXYNOS_5250_HOSTOHCICTRL                        0x34
+#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT	1
+#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_MASK \
+		(0x3ff << EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT)
+#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVALEN		BIT(0)
+
+/* USBOTG */
+#define EXYNOS_5250_USBOTGSYS				0x38
+#define EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET		BIT(14)
+#define EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG		BIT(13)
+#define EXYNOS_5250_USBOTGSYS_PHY_SW_RST		BIT(12)
+#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT		9
+#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK \
+		(0x3 << EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT)
+#define EXYNOS_5250_USBOTGSYS_ID_PULLUP			BIT(8)
+#define EXYNOS_5250_USBOTGSYS_COMMON_ON			BIT(7)
+#define EXYNOS_5250_USBOTGSYS_FSEL_SHIFT		4
+#define EXYNOS_5250_USBOTGSYS_FSEL_MASK \
+		(0x3 << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT)
+#define EXYNOS_5250_USBOTGSYS_FORCE_SLEEP		BIT(3)
+#define EXYNOS_5250_USBOTGSYS_OTGDISABLE		BIT(2)
+#define EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG		BIT(1)
+#define EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND		BIT(0)
+
+/* Isolation, configured in the power management unit */
+#define EXYNOS_5250_USB_ISOL_OTG_OFFSET		0x704
+#define EXYNOS_5250_USB_ISOL_OTG		BIT(0)
+#define EXYNOS_5250_USB_ISOL_HOST_OFFSET	0x708
+#define EXYNOS_5250_USB_ISOL_HOST		BIT(0)
+
+/* Mode swtich register */
+#define EXYNOS_5250_MODE_SWITCH_OFFSET		0x230
+#define EXYNOS_5250_MODE_SWITCH_MASK		1
+#define EXYNOS_5250_MODE_SWITCH_DEVICE		0
+#define EXYNOS_5250_MODE_SWITCH_HOST		1
+
+enum exynos4x12_phy_id {
+	EXYNOS5250_DEVICE,
+	EXYNOS5250_HOST,
+	EXYNOS5250_HSIC0,
+	EXYNOS5250_HSIC1,
+	EXYNOS5250_NUM_PHYS,
+};
+
+/*
+ * exynos5250_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register.
+ */
+static int exynos5250_rate_to_clk(unsigned long rate, u32 *reg)
+{
+	/* EXYNOS_5250_FSEL_MASK */
+
+	switch (rate) {
+	case 9600 * KHZ:
+		*reg = EXYNOS_5250_FSEL_9MHZ6;
+		break;
+	case 10 * MHZ:
+		*reg = EXYNOS_5250_FSEL_10MHZ;
+		break;
+	case 12 * MHZ:
+		*reg = EXYNOS_5250_FSEL_12MHZ;
+		break;
+	case 19200 * KHZ:
+		*reg = EXYNOS_5250_FSEL_19MHZ2;
+		break;
+	case 20 * MHZ:
+		*reg = EXYNOS_5250_FSEL_20MHZ;
+		break;
+	case 24 * MHZ:
+		*reg = EXYNOS_5250_FSEL_24MHZ;
+		break;
+	case 50 * MHZ:
+		*reg = EXYNOS_5250_FSEL_50MHZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void exynos5250_isol(struct samsung_usb2_phy_instance *inst, bool on)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+	u32 offset;
+	u32 mask;
+
+	switch (inst->cfg->id) {
+	case EXYNOS5250_DEVICE:
+		offset = EXYNOS_5250_USB_ISOL_OTG_OFFSET;
+		mask = EXYNOS_5250_USB_ISOL_OTG;
+		break;
+	case EXYNOS5250_HOST:
+		offset = EXYNOS_5250_USB_ISOL_HOST_OFFSET;
+		mask = EXYNOS_5250_USB_ISOL_HOST;
+		break;
+	default:
+		return;
+	};
+
+	regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
+}
+
+static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+	u32 ctrl0;
+	u32 otg;
+	u32 ehci;
+	u32 ohci;
+	u32 hsic;
+
+	switch (inst->cfg->id) {
+	case EXYNOS5250_DEVICE:
+		regmap_update_bits(drv->reg_sys,
+			EXYNOS_5250_MODE_SWITCH_OFFSET,
+			EXYNOS_5250_MODE_SWITCH_MASK,
+			EXYNOS_5250_MODE_SWITCH_DEVICE);
+
+		/* OTG configuration */
+		otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+		/* The clock */
+		otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
+		otg |= inst->clk_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
+		/* Reset */
+		otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
+			EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
+			EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
+		otg |=	EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
+			EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET |
+			EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
+			EXYNOS_5250_USBOTGSYS_OTGDISABLE;
+		/* Ref clock */
+		otg &=	~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK;
+		otg |=  EXYNOS_5250_REFCLKSEL_CLKCORE <<
+					EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT;
+		writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+		udelay(100);
+		otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
+			EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
+			EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET |
+			EXYNOS_5250_USBOTGSYS_OTGDISABLE);
+		writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+
+
+		break;
+	case EXYNOS5250_HOST:
+	case EXYNOS5250_HSIC0:
+	case EXYNOS5250_HSIC1:
+		/* Host registers configuration */
+		ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
+		/* The clock */
+		ctrl0 &= ~EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK;
+		ctrl0 |= inst->clk_reg_val <<
+					EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT;
+
+		/* Reset */
+		ctrl0 &=	~(EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST |
+				EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL |
+				EXYNOS_5250_HOSTPHYCTRL0_SIDDQ |
+				EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND |
+				EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP);
+		ctrl0 |=	EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST |
+				EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST |
+				EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N;
+		writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
+		udelay(10);
+		ctrl0 &=	~(EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST |
+				EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST);
+		writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
+
+		/* OTG configuration */
+		otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+		/* The clock */
+		otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
+		otg |= inst->clk_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
+		/* Reset */
+		otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
+			EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
+			EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
+		otg |=	EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
+			EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET |
+			EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
+			EXYNOS_5250_USBOTGSYS_OTGDISABLE;
+		/* Ref clock */
+		otg &=	~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK;
+		otg |=  EXYNOS_5250_REFCLKSEL_CLKCORE <<
+					EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT;
+		writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+		udelay(10);
+		otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
+			EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
+			EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET);
+
+		/* HSIC phy configuration */
+		hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 |
+				EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT |
+				EXYNOS_5250_HSICPHYCTRLX_PHYSWRST);
+		writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1);
+		writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2);
+		udelay(10);
+		hsic &= ~EXYNOS_5250_HSICPHYCTRLX_PHYSWRST;
+		writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1);
+		writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2);
+		udelay(80);
+
+		/* Enable EHCI DMA burst */
+		ehci = readl(drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL);
+		ehci |=	EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN |
+			EXYNOS_5250_HOSTEHCICTRL_ENAINCR4 |
+			EXYNOS_5250_HOSTEHCICTRL_ENAINCR8 |
+			EXYNOS_5250_HOSTEHCICTRL_ENAINCR16;
+		writel(ehci, drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL);
+
+		/* OHCI settings */
+		ohci = readl(drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
+		/* Following code is based on the old driver */
+		ohci |=	0x1 << 3;
+		writel(ohci, drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
+
+		break;
+	}
+	inst->enabled = 1;
+	exynos5250_isol(inst, 0);
+
+	return 0;
+}
+
+static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst)
+{
+	struct samsung_usb2_phy_driver *drv = inst->drv;
+	u32 ctrl0;
+	u32 otg;
+	u32 hsic;
+
+	inst->enabled = 0;
+	exynos5250_isol(inst, 1);
+
+	switch (inst->cfg->id) {
+	case EXYNOS5250_DEVICE:
+		otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+		otg |= (EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
+			EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG |
+			EXYNOS_5250_USBOTGSYS_FORCE_SLEEP);
+		writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+		break;
+	case EXYNOS5250_HOST:
+		ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
+		ctrl0 |= (EXYNOS_5250_HOSTPHYCTRL0_SIDDQ |
+				EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND |
+				EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP |
+				EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST |
+				EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL);
+		writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
+		break;
+	case EXYNOS5250_HSIC0:
+	case EXYNOS5250_HSIC1:
+		hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 |
+				EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT |
+				EXYNOS_5250_HSICPHYCTRLX_SIDDQ |
+				EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP |
+				EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND
+				);
+		writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1);
+		writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2);
+		break;
+	}
+
+	return 0;
+}
+
+
+static const struct samsung_usb2_common_phy exynos5250_phys[] = {
+	{
+		.label		= "device",
+		.id		= EXYNOS5250_DEVICE,
+		.rate_to_clk	= exynos5250_rate_to_clk,
+		.power_on	= exynos5250_power_on,
+		.power_off	= exynos5250_power_off,
+	},
+	{
+		.label		= "host",
+		.id		= EXYNOS5250_HOST,
+		.rate_to_clk	= exynos5250_rate_to_clk,
+		.power_on	= exynos5250_power_on,
+		.power_off	= exynos5250_power_off,
+	},
+	{
+		.label		= "hsic0",
+		.id		= EXYNOS5250_HSIC0,
+		.rate_to_clk	= exynos5250_rate_to_clk,
+		.power_on	= exynos5250_power_on,
+		.power_off	= exynos5250_power_off,
+	},
+	{
+		.label		= "hsic1",
+		.id		= EXYNOS5250_HSIC1,
+		.rate_to_clk	= exynos5250_rate_to_clk,
+		.power_on	= exynos5250_power_on,
+		.power_off	= exynos5250_power_off,
+	},
+	{},
+};
+
+const struct samsung_usb2_phy_config exynos5250_usb2_phy_config = {
+	.num_phys		= EXYNOS5250_NUM_PHYS,
+	.phys			= exynos5250_phys,
+	.has_mode_switch	= 1,
+};
+
diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c
index 3e48f17..acfdb58 100644
--- a/drivers/phy/phy-samsung-usb2.c
+++ b/drivers/phy/phy-samsung-usb2.c
@@ -113,6 +113,12 @@ static const struct of_device_id samsung_usb2_phy_of_match[] = {
 		.data = &exynos4x12_usb2_phy_config,
 	},
 #endif
+#ifdef CONFIG_PHY_EXYNOS5250_USB2
+	{
+		.compatible = "samsung,exynos5250-usb2-phy",
+		.data = &exynos5250_usb2_phy_config,
+	},
+#endif
 	{ },
 };
 
diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h
index ae41d0f..bb4fb77 100644
--- a/drivers/phy/phy-samsung-usb2.h
+++ b/drivers/phy/phy-samsung-usb2.h
@@ -64,5 +64,6 @@ struct samsung_usb2_phy_config {
 extern const struct samsung_usb2_phy_config s5pv210_usb2_phy_config;
 extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config;
 extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config;
+extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config;
 #endif
 
-- 
1.7.9.5


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

* [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework
  2014-01-29 17:29 [PATCH v6 0/8] phy: Add new Exynos USB 2.0 PHY driver Kamil Debski
                   ` (6 preceding siblings ...)
  2014-01-29 17:29 ` [PATCH v6 7/8] phy: Add Exynos 5250 support to the Exynos USB 2.0 " Kamil Debski
@ 2014-01-29 17:29 ` Kamil Debski
  2014-01-29 20:42     ` Alan Stern
  2014-01-29 20:55     ` Olof Johansson
  7 siblings, 2 replies; 30+ messages in thread
From: Kamil Debski @ 2014-01-29 17:29 UTC (permalink / raw)
  To: linux-kernel, linux-samsung-soc, linux-usb, devicetree
  Cc: kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, yulgon.kim, p.paneri, av.tikhomirov,
	jg1.han, galak, matt.porter, k.debski, tjakobi, stern

Change the phy provider used from the old one using the USB phy
framework to a new one using the Generic phy framework.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 .../devicetree/bindings/usb/exynos-usb.txt         |   13 +++
 drivers/usb/host/ehci-exynos.c                     |   97 +++++++++++++-------
 2 files changed, 76 insertions(+), 34 deletions(-)

diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt b/Documentation/devicetree/bindings/usb/exynos-usb.txt
index d967ba1..25e199a 100644
--- a/Documentation/devicetree/bindings/usb/exynos-usb.txt
+++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt
@@ -12,6 +12,10 @@ Required properties:
  - interrupts: interrupt number to the cpu.
  - clocks: from common clock binding: handle to usb clock.
  - clock-names: from common clock binding: Shall be "usbhost".
+  - port: if in the SoC there are EHCI phys, they should be listed here.
+One phy per port. Each port should have its reg entry with a consecutive
+number. Also it should contain phys and phy-names entries specifying the
+phy used by the port.
 
 Optional properties:
  - samsung,vbus-gpio:  if present, specifies the GPIO that
@@ -27,6 +31,15 @@ Example:
 
 		clocks = <&clock 285>;
 		clock-names = "usbhost";
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+		port@0 {
+		    reg = <0>;
+		    phys = <&usb2phy 1>;
+		    phy-names = "host";
+		    status = "disabled";
+		};
 	};
 
 OHCI
diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c
index d1d8c47..7c35501 100644
--- a/drivers/usb/host/ehci-exynos.c
+++ b/drivers/usb/host/ehci-exynos.c
@@ -19,12 +19,12 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
+#include <linux/phy/phy.h>
 #include <linux/platform_device.h>
 #include <linux/usb/phy.h>
 #include <linux/usb/samsung_usb_phy.h>
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
-#include <linux/usb/otg.h>
 
 #include "ehci.h"
 
@@ -42,10 +42,10 @@
 static const char hcd_name[] = "ehci-exynos";
 static struct hc_driver __read_mostly exynos_ehci_hc_driver;
 
+#define PHY_NUMBER 3
 struct exynos_ehci_hcd {
 	struct clk *clk;
-	struct usb_phy *phy;
-	struct usb_otg *otg;
+	struct phy *phy[PHY_NUMBER];
 };
 
 #define to_exynos_ehci(hcd) (struct exynos_ehci_hcd *)(hcd_to_ehci(hcd)->priv)
@@ -69,13 +69,43 @@ static void exynos_setup_vbus_gpio(struct platform_device *pdev)
 		dev_err(dev, "can't request ehci vbus gpio %d", gpio);
 }
 
+static int exynos_phys_on(struct phy *p[])
+{
+	int i;
+	int ret = 0;
+
+	for (i = 0; ret == 0 && i < PHY_NUMBER; i++)
+		if (p[i])
+			ret = phy_power_on(p[i]);
+	if (ret)
+		for (i--; i > 0; i--)
+			if (p[i])
+				phy_power_off(p[i]);
+
+	return ret;
+}
+
+static int exynos_phys_off(struct phy *p[])
+{
+	int i;
+	int ret = 0;
+
+	for (i = 0; ret == 0 && i < PHY_NUMBER; i++)
+		if (p[i])
+			ret = phy_power_off(p[i]);
+
+	return ret;
+}
+
 static int exynos_ehci_probe(struct platform_device *pdev)
 {
 	struct exynos_ehci_hcd *exynos_ehci;
 	struct usb_hcd *hcd;
 	struct ehci_hcd *ehci;
 	struct resource *res;
-	struct usb_phy *phy;
+	struct phy *phy;
+	struct device_node *child;
+	int phy_number;
 	int irq;
 	int err;
 
@@ -102,14 +132,26 @@ static int exynos_ehci_probe(struct platform_device *pdev)
 					"samsung,exynos5440-ehci"))
 		goto skip_phy;
 
-	phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
-	if (IS_ERR(phy)) {
-		usb_put_hcd(hcd);
-		dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
-		return -EPROBE_DEFER;
-	} else {
-		exynos_ehci->phy = phy;
-		exynos_ehci->otg = phy->otg;
+	for_each_available_child_of_node(pdev->dev.of_node, child) {
+		err = of_property_read_u32(child, "reg", &phy_number);
+		if (err) {
+			dev_err(&pdev->dev, "Failed to parse device tree\n");
+			of_node_put(child);
+			return err;
+		}
+		if (phy_number >= PHY_NUMBER) {
+			dev_err(&pdev->dev, "Failed to parse device tree - number out of range\n");
+			of_node_put(child);
+			return -EINVAL;
+		}
+		phy = devm_of_phy_get(&pdev->dev, child, 0);
+		of_node_put(child);
+		if (IS_ERR(phy)) {
+			dev_err(&pdev->dev, "Failed to get phy number %d",
+								phy_number);
+			return PTR_ERR(phy);
+		}
+		exynos_ehci->phy[phy_number] = phy;
 	}
 
 skip_phy:
@@ -149,11 +191,11 @@ skip_phy:
 		goto fail_io;
 	}
 
-	if (exynos_ehci->otg)
-		exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
-
-	if (exynos_ehci->phy)
-		usb_phy_init(exynos_ehci->phy);
+	err = exynos_phys_on(exynos_ehci->phy);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to enabled phys\n");
+		goto fail_io;
+	}
 
 	ehci = hcd_to_ehci(hcd);
 	ehci->caps = hcd->regs;
@@ -173,8 +215,7 @@ skip_phy:
 	return 0;
 
 fail_add_hcd:
-	if (exynos_ehci->phy)
-		usb_phy_shutdown(exynos_ehci->phy);
+	exynos_phys_off(exynos_ehci->phy);
 fail_io:
 	clk_disable_unprepare(exynos_ehci->clk);
 fail_clk:
@@ -189,11 +230,7 @@ static int exynos_ehci_remove(struct platform_device *pdev)
 
 	usb_remove_hcd(hcd);
 
-	if (exynos_ehci->otg)
-		exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
-
-	if (exynos_ehci->phy)
-		usb_phy_shutdown(exynos_ehci->phy);
+	exynos_phys_off(exynos_ehci->phy);
 
 	clk_disable_unprepare(exynos_ehci->clk);
 
@@ -213,11 +250,7 @@ static int exynos_ehci_suspend(struct device *dev)
 
 	rc = ehci_suspend(hcd, do_wakeup);
 
-	if (exynos_ehci->otg)
-		exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
-
-	if (exynos_ehci->phy)
-		usb_phy_shutdown(exynos_ehci->phy);
+	exynos_phys_off(exynos_ehci->phy);
 
 	clk_disable_unprepare(exynos_ehci->clk);
 
@@ -231,11 +264,7 @@ static int exynos_ehci_resume(struct device *dev)
 
 	clk_prepare_enable(exynos_ehci->clk);
 
-	if (exynos_ehci->otg)
-		exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
-
-	if (exynos_ehci->phy)
-		usb_phy_init(exynos_ehci->phy);
+	exynos_phys_on(exynos_ehci->phy);
 
 	/* DMA burst Enable */
 	writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
-- 
1.7.9.5


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

* Re: [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework
  2014-01-29 17:29 ` [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework Kamil Debski
@ 2014-01-29 20:42     ` Alan Stern
  2014-01-29 20:55     ` Olof Johansson
  1 sibling, 0 replies; 30+ messages in thread
From: Alan Stern @ 2014-01-29 20:42 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, yulgon.kim, p.paneri, av.tikhomirov,
	jg1.han, galak, matt.porter, tjakobi

On Wed, 29 Jan 2014, Kamil Debski wrote:

> Change the phy provider used from the old one using the USB phy
> framework to a new one using the Generic phy framework.
> 
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> ---
>  .../devicetree/bindings/usb/exynos-usb.txt         |   13 +++
>  drivers/usb/host/ehci-exynos.c                     |   97 +++++++++++++-------
>  2 files changed, 76 insertions(+), 34 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt b/Documentation/devicetree/bindings/usb/exynos-usb.txt
> index d967ba1..25e199a 100644
> --- a/Documentation/devicetree/bindings/usb/exynos-usb.txt
> +++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt
> @@ -12,6 +12,10 @@ Required properties:
>   - interrupts: interrupt number to the cpu.
>   - clocks: from common clock binding: handle to usb clock.
>   - clock-names: from common clock binding: Shall be "usbhost".
> +  - port: if in the SoC there are EHCI phys, they should be listed here.
> +One phy per port. Each port should have its reg entry with a consecutive
> +number. Also it should contain phys and phy-names entries specifying the
> +phy used by the port.

What is the reg entry number used for?  As far as I can see, it isn't 
used for anything.  In which case, why have it at all?

> @@ -42,10 +42,10 @@
>  static const char hcd_name[] = "ehci-exynos";
>  static struct hc_driver __read_mostly exynos_ehci_hc_driver;
>  
> +#define PHY_NUMBER 3
>  struct exynos_ehci_hcd {
>  	struct clk *clk;
> -	struct usb_phy *phy;
> -	struct usb_otg *otg;

You have removed all the OTG stuff from the driver.  This wasn't
mentioned in the patch description, and it has no connection with the
PHY work.

> +	struct phy *phy[PHY_NUMBER];
>  };
>  
>  #define to_exynos_ehci(hcd) (struct exynos_ehci_hcd *)(hcd_to_ehci(hcd)->priv)
> @@ -69,13 +69,43 @@ static void exynos_setup_vbus_gpio(struct platform_device *pdev)
>  		dev_err(dev, "can't request ehci vbus gpio %d", gpio);
>  }
>  
> +static int exynos_phys_on(struct phy *p[])
> +{
> +	int i;
> +	int ret = 0;
> +
> +	for (i = 0; ret == 0 && i < PHY_NUMBER; i++)
> +		if (p[i])
> +			ret = phy_power_on(p[i]);
> +	if (ret)
> +		for (i--; i > 0; i--)
> +			if (p[i])
> +				phy_power_off(p[i]);

This loop runs while i > 0.  Therefore you will never turn off the 
power to p[0].

> @@ -102,14 +132,26 @@ static int exynos_ehci_probe(struct platform_device *pdev)
>  					"samsung,exynos5440-ehci"))
>  		goto skip_phy;
>  
> -	phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
> -	if (IS_ERR(phy)) {
> -		usb_put_hcd(hcd);

You omitted this line from the new error returns below.

> -		dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
> -		return -EPROBE_DEFER;
> -	} else {
> -		exynos_ehci->phy = phy;
> -		exynos_ehci->otg = phy->otg;
> +	for_each_available_child_of_node(pdev->dev.of_node, child) {
> +		err = of_property_read_u32(child, "reg", &phy_number);
> +		if (err) {
> +			dev_err(&pdev->dev, "Failed to parse device tree\n");
> +			of_node_put(child);
> +			return err;

Here, for example.  Wouldn't it be better to goto fail_clk?

Alan Stern


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

* Re: [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework
@ 2014-01-29 20:42     ` Alan Stern
  0 siblings, 0 replies; 30+ messages in thread
From: Alan Stern @ 2014-01-29 20:42 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, yulgon.kim, p.paneri, av.tikhomirov,
	jg1.han, galak, matt.porter, tjakobi

On Wed, 29 Jan 2014, Kamil Debski wrote:

> Change the phy provider used from the old one using the USB phy
> framework to a new one using the Generic phy framework.
> 
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> ---
>  .../devicetree/bindings/usb/exynos-usb.txt         |   13 +++
>  drivers/usb/host/ehci-exynos.c                     |   97 +++++++++++++-------
>  2 files changed, 76 insertions(+), 34 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt b/Documentation/devicetree/bindings/usb/exynos-usb.txt
> index d967ba1..25e199a 100644
> --- a/Documentation/devicetree/bindings/usb/exynos-usb.txt
> +++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt
> @@ -12,6 +12,10 @@ Required properties:
>   - interrupts: interrupt number to the cpu.
>   - clocks: from common clock binding: handle to usb clock.
>   - clock-names: from common clock binding: Shall be "usbhost".
> +  - port: if in the SoC there are EHCI phys, they should be listed here.
> +One phy per port. Each port should have its reg entry with a consecutive
> +number. Also it should contain phys and phy-names entries specifying the
> +phy used by the port.

What is the reg entry number used for?  As far as I can see, it isn't 
used for anything.  In which case, why have it at all?

> @@ -42,10 +42,10 @@
>  static const char hcd_name[] = "ehci-exynos";
>  static struct hc_driver __read_mostly exynos_ehci_hc_driver;
>  
> +#define PHY_NUMBER 3
>  struct exynos_ehci_hcd {
>  	struct clk *clk;
> -	struct usb_phy *phy;
> -	struct usb_otg *otg;

You have removed all the OTG stuff from the driver.  This wasn't
mentioned in the patch description, and it has no connection with the
PHY work.

> +	struct phy *phy[PHY_NUMBER];
>  };
>  
>  #define to_exynos_ehci(hcd) (struct exynos_ehci_hcd *)(hcd_to_ehci(hcd)->priv)
> @@ -69,13 +69,43 @@ static void exynos_setup_vbus_gpio(struct platform_device *pdev)
>  		dev_err(dev, "can't request ehci vbus gpio %d", gpio);
>  }
>  
> +static int exynos_phys_on(struct phy *p[])
> +{
> +	int i;
> +	int ret = 0;
> +
> +	for (i = 0; ret == 0 && i < PHY_NUMBER; i++)
> +		if (p[i])
> +			ret = phy_power_on(p[i]);
> +	if (ret)
> +		for (i--; i > 0; i--)
> +			if (p[i])
> +				phy_power_off(p[i]);

This loop runs while i > 0.  Therefore you will never turn off the 
power to p[0].

> @@ -102,14 +132,26 @@ static int exynos_ehci_probe(struct platform_device *pdev)
>  					"samsung,exynos5440-ehci"))
>  		goto skip_phy;
>  
> -	phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
> -	if (IS_ERR(phy)) {
> -		usb_put_hcd(hcd);

You omitted this line from the new error returns below.

> -		dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
> -		return -EPROBE_DEFER;
> -	} else {
> -		exynos_ehci->phy = phy;
> -		exynos_ehci->otg = phy->otg;
> +	for_each_available_child_of_node(pdev->dev.of_node, child) {
> +		err = of_property_read_u32(child, "reg", &phy_number);
> +		if (err) {
> +			dev_err(&pdev->dev, "Failed to parse device tree\n");
> +			of_node_put(child);
> +			return err;

Here, for example.  Wouldn't it be better to goto fail_clk?

Alan Stern

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

* Re: [PATCH v6 3/8] dts: Add usb2phy to Exynos 4
  2014-01-29 17:29 ` [PATCH v6 3/8] dts: Add usb2phy to Exynos 4 Kamil Debski
@ 2014-01-29 20:50     ` Olof Johansson
  0 siblings, 0 replies; 30+ messages in thread
From: Olof Johansson @ 2014-01-29 20:50 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	Kyungmin Park, Kishon Vijay Abraham I, Tomasz Figa,
	Sylwester Nawrocki, Marek Szyprowski, Vivek Gautam, mat.krawczuk,
	yulgon.kim, p.paneri, av.tikhomirov, Jingoo Han, Kumar Gala,
	Matt Porter, tjakobi, Alan Stern

On Wed, Jan 29, 2014 at 9:29 AM, Kamil Debski <k.debski@samsung.com> wrote:
> Add support to PHY of USB2 of the Exynos 4 SoC.
>
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> ---
>  .../devicetree/bindings/arm/samsung/pmu.txt        |    2 ++
>  arch/arm/boot/dts/exynos4.dtsi                     |   31 ++++++++++++++++++++
>  arch/arm/boot/dts/exynos4210.dtsi                  |   17 +++++++++++
>  arch/arm/boot/dts/exynos4x12.dtsi                  |   17 +++++++++++
>  4 files changed, 67 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/arm/samsung/pmu.txt b/Documentation/devicetree/bindings/arm/samsung/pmu.txt
> index 307e727..a76f91d 100644
> --- a/Documentation/devicetree/bindings/arm/samsung/pmu.txt
> +++ b/Documentation/devicetree/bindings/arm/samsung/pmu.txt
> @@ -3,6 +3,8 @@ SAMSUNG Exynos SoC series PMU Registers
>  Properties:
>   - name : should be 'syscon';
>   - compatible : should contain two values. First value must be one from following list:
> +                  - "samsung,exynos4210-pmu" - for Exynos4210 SoC,
> +                  - "samsung,exynos4x12-pmu" - for Exynos4212 SoC,
>                    - "samsung,exynos5250-pmu" - for Exynos5250 SoC,
>                    - "samsung,exynos5420-pmu" - for Exynos5420 SoC.
>                 second value must be always "syscon".

This and other PMU related bindings/dts changes should probably go in
separate patch(es) instead of being snuck in with USB changes.


-Olof

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

* Re: [PATCH v6 3/8] dts: Add usb2phy to Exynos 4
@ 2014-01-29 20:50     ` Olof Johansson
  0 siblings, 0 replies; 30+ messages in thread
From: Olof Johansson @ 2014-01-29 20:50 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	Kyungmin Park, Kishon Vijay Abraham I, Tomasz Figa,
	Sylwester Nawrocki, Marek Szyprowski, Vivek Gautam, mat.krawczuk,
	yulgon.kim, p.paneri, av.tikhomirov, Jingoo Han, Kumar Gala,
	Matt Porter, tjakobi, Alan Stern

On Wed, Jan 29, 2014 at 9:29 AM, Kamil Debski <k.debski@samsung.com> wrote:
> Add support to PHY of USB2 of the Exynos 4 SoC.
>
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> ---
>  .../devicetree/bindings/arm/samsung/pmu.txt        |    2 ++
>  arch/arm/boot/dts/exynos4.dtsi                     |   31 ++++++++++++++++++++
>  arch/arm/boot/dts/exynos4210.dtsi                  |   17 +++++++++++
>  arch/arm/boot/dts/exynos4x12.dtsi                  |   17 +++++++++++
>  4 files changed, 67 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/arm/samsung/pmu.txt b/Documentation/devicetree/bindings/arm/samsung/pmu.txt
> index 307e727..a76f91d 100644
> --- a/Documentation/devicetree/bindings/arm/samsung/pmu.txt
> +++ b/Documentation/devicetree/bindings/arm/samsung/pmu.txt
> @@ -3,6 +3,8 @@ SAMSUNG Exynos SoC series PMU Registers
>  Properties:
>   - name : should be 'syscon';
>   - compatible : should contain two values. First value must be one from following list:
> +                  - "samsung,exynos4210-pmu" - for Exynos4210 SoC,
> +                  - "samsung,exynos4x12-pmu" - for Exynos4212 SoC,
>                    - "samsung,exynos5250-pmu" - for Exynos5250 SoC,
>                    - "samsung,exynos5420-pmu" - for Exynos5420 SoC.
>                 second value must be always "syscon".

This and other PMU related bindings/dts changes should probably go in
separate patch(es) instead of being snuck in with USB changes.


-Olof

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

* Re: [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework
@ 2014-01-29 20:55     ` Olof Johansson
  0 siblings, 0 replies; 30+ messages in thread
From: Olof Johansson @ 2014-01-29 20:55 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	Kyungmin Park, Kishon Vijay Abraham I, Tomasz Figa,
	Sylwester Nawrocki, Marek Szyprowski, Vivek Gautam, mat.krawczuk,
	yulgon.kim, p.paneri, av.tikhomirov, Jingoo Han, Kumar Gala,
	Matt Porter, tjakobi, Alan Stern

Hi,

On Wed, Jan 29, 2014 at 9:29 AM, Kamil Debski <k.debski@samsung.com> wrote:
> Change the phy provider used from the old one using the USB phy
> framework to a new one using the Generic phy framework.
>
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> ---
>  .../devicetree/bindings/usb/exynos-usb.txt         |   13 +++
>  drivers/usb/host/ehci-exynos.c                     |   97 +++++++++++++-------
>  2 files changed, 76 insertions(+), 34 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt b/Documentation/devicetree/bindings/usb/exynos-usb.txt
> index d967ba1..25e199a 100644
> --- a/Documentation/devicetree/bindings/usb/exynos-usb.txt
> +++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt
> @@ -12,6 +12,10 @@ Required properties:
>   - interrupts: interrupt number to the cpu.
>   - clocks: from common clock binding: handle to usb clock.
>   - clock-names: from common clock binding: Shall be "usbhost".
> +  - port: if in the SoC there are EHCI phys, they should be listed here.
> +One phy per port. Each port should have its reg entry with a consecutive
> +number. Also it should contain phys and phy-names entries specifying the
> +phy used by the port.
>
>  Optional properties:
>   - samsung,vbus-gpio:  if present, specifies the GPIO that
> @@ -27,6 +31,15 @@ Example:
>
>                 clocks = <&clock 285>;
>                 clock-names = "usbhost";
> +
> +               #address-cells = <1>;
> +               #size-cells = <0>;
> +               port@0 {
> +                   reg = <0>;
> +                   phys = <&usb2phy 1>;
> +                   phy-names = "host";
> +                   status = "disabled";
> +               };
>         };
>
>  OHCI

[...]

> @@ -102,14 +132,26 @@ static int exynos_ehci_probe(struct platform_device *pdev)
>                                         "samsung,exynos5440-ehci"))
>                 goto skip_phy;
>
> -       phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
> -       if (IS_ERR(phy)) {
> -               usb_put_hcd(hcd);
> -               dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
> -               return -EPROBE_DEFER;
> -       } else {
> -               exynos_ehci->phy = phy;
> -               exynos_ehci->otg = phy->otg;
> +       for_each_available_child_of_node(pdev->dev.of_node, child) {
> +               err = of_property_read_u32(child, "reg", &phy_number);
> +               if (err) {
> +                       dev_err(&pdev->dev, "Failed to parse device tree\n");
> +                       of_node_put(child);
> +                       return err;
> +               }
> +               if (phy_number >= PHY_NUMBER) {
> +                       dev_err(&pdev->dev, "Failed to parse device tree - number out of range\n");
> +                       of_node_put(child);
> +                       return -EINVAL;
> +               }
> +               phy = devm_of_phy_get(&pdev->dev, child, 0);
> +               of_node_put(child);
> +               if (IS_ERR(phy)) {
> +                       dev_err(&pdev->dev, "Failed to get phy number %d",
> +                                                               phy_number);
> +                       return PTR_ERR(phy);
> +               }
> +               exynos_ehci->phy[phy_number] = phy;

this looks like it is now breaking older device trees, where ports
might not be described. Since device tree interfaces need to be
backwards compatible, you still need to handle the old case of not
having ports described.

There are two ways of doing this:

1. Fall back to the old behavior if there are no ports
2. Use a new compatible value for the new model with port subnodes,
and if the old compatible value is used, then fall back to the old
behavior.

I'm guessing (1) might be easiest since you can check for the presence
of #address-cells to tell if this is just an old style node, or if
it's a new-style node without any ports below it.


-Olof

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

* Re: [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework
@ 2014-01-29 20:55     ` Olof Johansson
  0 siblings, 0 replies; 30+ messages in thread
From: Olof Johansson @ 2014-01-29 20:55 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Kyungmin Park,
	Kishon Vijay Abraham I, Tomasz Figa, Sylwester Nawrocki,
	Marek Szyprowski, Vivek Gautam,
	mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w,
	yulgon.kim-Sze3O3UU22JBDgjK7y7TUQ,
	p.paneri-Sze3O3UU22JBDgjK7y7TUQ,
	av.tikhomirov-Sze3O3UU22JBDgjK7y7TUQ, Jingoo Han, Kumar Gala,
	Matt Porter, tjakobi-o02PS0xoJP9W0yFyLvAVXMxlOr/tl8fh,
	Alan Stern

Hi,

On Wed, Jan 29, 2014 at 9:29 AM, Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> wrote:
> Change the phy provider used from the old one using the USB phy
> framework to a new one using the Generic phy framework.
>
> Signed-off-by: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> ---
>  .../devicetree/bindings/usb/exynos-usb.txt         |   13 +++
>  drivers/usb/host/ehci-exynos.c                     |   97 +++++++++++++-------
>  2 files changed, 76 insertions(+), 34 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt b/Documentation/devicetree/bindings/usb/exynos-usb.txt
> index d967ba1..25e199a 100644
> --- a/Documentation/devicetree/bindings/usb/exynos-usb.txt
> +++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt
> @@ -12,6 +12,10 @@ Required properties:
>   - interrupts: interrupt number to the cpu.
>   - clocks: from common clock binding: handle to usb clock.
>   - clock-names: from common clock binding: Shall be "usbhost".
> +  - port: if in the SoC there are EHCI phys, they should be listed here.
> +One phy per port. Each port should have its reg entry with a consecutive
> +number. Also it should contain phys and phy-names entries specifying the
> +phy used by the port.
>
>  Optional properties:
>   - samsung,vbus-gpio:  if present, specifies the GPIO that
> @@ -27,6 +31,15 @@ Example:
>
>                 clocks = <&clock 285>;
>                 clock-names = "usbhost";
> +
> +               #address-cells = <1>;
> +               #size-cells = <0>;
> +               port@0 {
> +                   reg = <0>;
> +                   phys = <&usb2phy 1>;
> +                   phy-names = "host";
> +                   status = "disabled";
> +               };
>         };
>
>  OHCI

[...]

> @@ -102,14 +132,26 @@ static int exynos_ehci_probe(struct platform_device *pdev)
>                                         "samsung,exynos5440-ehci"))
>                 goto skip_phy;
>
> -       phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
> -       if (IS_ERR(phy)) {
> -               usb_put_hcd(hcd);
> -               dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
> -               return -EPROBE_DEFER;
> -       } else {
> -               exynos_ehci->phy = phy;
> -               exynos_ehci->otg = phy->otg;
> +       for_each_available_child_of_node(pdev->dev.of_node, child) {
> +               err = of_property_read_u32(child, "reg", &phy_number);
> +               if (err) {
> +                       dev_err(&pdev->dev, "Failed to parse device tree\n");
> +                       of_node_put(child);
> +                       return err;
> +               }
> +               if (phy_number >= PHY_NUMBER) {
> +                       dev_err(&pdev->dev, "Failed to parse device tree - number out of range\n");
> +                       of_node_put(child);
> +                       return -EINVAL;
> +               }
> +               phy = devm_of_phy_get(&pdev->dev, child, 0);
> +               of_node_put(child);
> +               if (IS_ERR(phy)) {
> +                       dev_err(&pdev->dev, "Failed to get phy number %d",
> +                                                               phy_number);
> +                       return PTR_ERR(phy);
> +               }
> +               exynos_ehci->phy[phy_number] = phy;

this looks like it is now breaking older device trees, where ports
might not be described. Since device tree interfaces need to be
backwards compatible, you still need to handle the old case of not
having ports described.

There are two ways of doing this:

1. Fall back to the old behavior if there are no ports
2. Use a new compatible value for the new model with port subnodes,
and if the old compatible value is used, then fall back to the old
behavior.

I'm guessing (1) might be easiest since you can check for the presence
of #address-cells to tell if this is just an old style node, or if
it's a new-style node without any ports below it.


-Olof
--
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	[flat|nested] 30+ messages in thread

* Re: [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework
  2014-01-29 20:42     ` Alan Stern
  (?)
@ 2014-02-04 15:06     ` Tomasz Figa
  -1 siblings, 0 replies; 30+ messages in thread
From: Tomasz Figa @ 2014-02-04 15:06 UTC (permalink / raw)
  To: Alan Stern, Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, yulgon.kim, p.paneri, av.tikhomirov,
	jg1.han, galak, matt.porter, tjakobi

Hi Alan,

On 29.01.2014 21:42, Alan Stern wrote:
> On Wed, 29 Jan 2014, Kamil Debski wrote:
>
>> Change the phy provider used from the old one using the USB phy
>> framework to a new one using the Generic phy framework.
>>
>> Signed-off-by: Kamil Debski <k.debski@samsung.com>
>> ---
>>   .../devicetree/bindings/usb/exynos-usb.txt         |   13 +++
>>   drivers/usb/host/ehci-exynos.c                     |   97 +++++++++++++-------
>>   2 files changed, 76 insertions(+), 34 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt b/Documentation/devicetree/bindings/usb/exynos-usb.txt
>> index d967ba1..25e199a 100644
>> --- a/Documentation/devicetree/bindings/usb/exynos-usb.txt
>> +++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt
>> @@ -12,6 +12,10 @@ Required properties:
>>    - interrupts: interrupt number to the cpu.
>>    - clocks: from common clock binding: handle to usb clock.
>>    - clock-names: from common clock binding: Shall be "usbhost".
>> +  - port: if in the SoC there are EHCI phys, they should be listed here.
>> +One phy per port. Each port should have its reg entry with a consecutive
>> +number. Also it should contain phys and phy-names entries specifying the
>> +phy used by the port.
>
> What is the reg entry number used for?  As far as I can see, it isn't
> used for anything.  In which case, why have it at all?

The reg property is here to identify which EHCI port the node is 
describing. This should be mentioned in the documentation, though, as 
well as the whole description of port nodes should be written in a more 
structured manner, just as other properties.

Best regards,
Tomasz

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

* RE: [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework
  2014-01-29 20:42     ` Alan Stern
  (?)
  (?)
@ 2014-02-05 15:57     ` Kamil Debski
  2014-02-05 19:55         ` Alan Stern
  -1 siblings, 1 reply; 30+ messages in thread
From: Kamil Debski @ 2014-02-05 15:57 UTC (permalink / raw)
  To: 'Alan Stern'
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	kyungmin.park, kishon, Tomasz Figa, Sylwester Nawrocki,
	Marek Szyprowski, gautam.vivek, mat.krawczuk, yulgon.kim,
	p.paneri, av.tikhomirov, jg1.han, galak, matt.porter, tjakobi

Hi Alan,

Thank you for your review.

> From: Alan Stern [mailto:stern@rowland.harvard.edu]
> Sent: Wednesday, January 29, 2014 9:43 PM
> 
> On Wed, 29 Jan 2014, Kamil Debski wrote:
> 
> > Change the phy provider used from the old one using the USB phy
> > framework to a new one using the Generic phy framework.
> >
> > Signed-off-by: Kamil Debski <k.debski@samsung.com>
> > ---
> >  .../devicetree/bindings/usb/exynos-usb.txt         |   13 +++
> >  drivers/usb/host/ehci-exynos.c                     |   97
> +++++++++++++-------
> >  2 files changed, 76 insertions(+), 34 deletions(-)
> >
> > diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt
> > b/Documentation/devicetree/bindings/usb/exynos-usb.txt
> > index d967ba1..25e199a 100644
> > --- a/Documentation/devicetree/bindings/usb/exynos-usb.txt
> > +++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt
> > @@ -12,6 +12,10 @@ Required properties:
> >   - interrupts: interrupt number to the cpu.
> >   - clocks: from common clock binding: handle to usb clock.
> >   - clock-names: from common clock binding: Shall be "usbhost".
> > +  - port: if in the SoC there are EHCI phys, they should be listed
> here.
> > +One phy per port. Each port should have its reg entry with a
> > +consecutive number. Also it should contain phys and phy-names
> entries
> > +specifying the phy used by the port.
> 
> What is the reg entry number used for?  As far as I can see, it isn't
> used for anything.  In which case, why have it at all?

Tomasz Figa already commented this. I agree with him, that this should
be better described in the documentation.

> 
> > @@ -42,10 +42,10 @@
> >  static const char hcd_name[] = "ehci-exynos";  static struct
> > hc_driver __read_mostly exynos_ehci_hc_driver;
> >
> > +#define PHY_NUMBER 3
> >  struct exynos_ehci_hcd {
> >  	struct clk *clk;
> > -	struct usb_phy *phy;
> > -	struct usb_otg *otg;
> 
> You have removed all the OTG stuff from the driver.  This wasn't
> mentioned in the patch description, and it has no connection with the
> PHY work.

Maybe I'll explain more about what are we trying to achieve. The goal
is to replace the old phy driver with the new one. In the old driver it was
difficult to add support to new SoC. It also had issues with having device
and
host working together.

You're right that until the old phy driver is removed support for the it
should
remain. To be able to remove the old driver both new USB2 and new USB3 phy
drivers
have to be ready. The USB3 driver is written by Vivek Gautam and as I see
it, he
is also close to completion.

Regarding the otg part. The old phy driver is the only provider of the otg
structure. It sets the host field of the structure. It is then used by
samsung_usb2phy_init (drivers/usb/phy/phy-samsung-usb2.c) to check which
driver is requesting the phy (is it host or device). In the new driver this
is determined by the entry in device tree. So no need to check the otg
struct
and strstr (!) to check if dev_name is ehci, ohci or other, like the old
driver.

> > +	struct phy *phy[PHY_NUMBER];
> >  };
> >
> >  #define to_exynos_ehci(hcd) (struct exynos_ehci_hcd
> > *)(hcd_to_ehci(hcd)->priv) @@ -69,13 +69,43 @@ static void
> exynos_setup_vbus_gpio(struct platform_device *pdev)
> >  		dev_err(dev, "can't request ehci vbus gpio %d", gpio);  }
> >
> > +static int exynos_phys_on(struct phy *p[]) {
> > +	int i;
> > +	int ret = 0;
> > +
> > +	for (i = 0; ret == 0 && i < PHY_NUMBER; i++)
> > +		if (p[i])
> > +			ret = phy_power_on(p[i]);
> > +	if (ret)
> > +		for (i--; i > 0; i--)
> > +			if (p[i])
> > +				phy_power_off(p[i]);
> 
> This loop runs while i > 0.  Therefore you will never turn off the
> power to p[0].

Ups, my bad. Thank you for spotting this.

> 
> > @@ -102,14 +132,26 @@ static int exynos_ehci_probe(struct
> platform_device *pdev)
> >  					"samsung,exynos5440-ehci"))
> >  		goto skip_phy;
> >
> > -	phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
> > -	if (IS_ERR(phy)) {
> > -		usb_put_hcd(hcd);
> 
> You omitted this line from the new error returns below.

I see.

> 
> > -		dev_warn(&pdev->dev, "no platform data or transceiver
> defined\n");
> > -		return -EPROBE_DEFER;
> > -	} else {
> > -		exynos_ehci->phy = phy;
> > -		exynos_ehci->otg = phy->otg;
> > +	for_each_available_child_of_node(pdev->dev.of_node, child) {
> > +		err = of_property_read_u32(child, "reg", &phy_number);
> > +		if (err) {
> > +			dev_err(&pdev->dev, "Failed to parse device
tree\n");
> > +			of_node_put(child);
> > +			return err;
> 
> Here, for example.  Wouldn't it be better to goto fail_clk?

Right, I will fix this. Thank you.

Best wishes,
-- 
Kamil Debski
Samsung R&D Institute Poland


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

* RE: [PATCH v6 3/8] dts: Add usb2phy to Exynos 4
  2014-01-29 20:50     ` Olof Johansson
  (?)
@ 2014-02-05 15:57     ` Kamil Debski
  -1 siblings, 0 replies; 30+ messages in thread
From: Kamil Debski @ 2014-02-05 15:57 UTC (permalink / raw)
  To: 'Olof Johansson'
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	'Kyungmin Park', 'Kishon Vijay Abraham I',
	Tomasz Figa, Sylwester Nawrocki, Marek Szyprowski,
	'Vivek Gautam',
	mat.krawczuk, yulgon.kim, p.paneri, av.tikhomirov,
	'Jingoo Han', 'Kumar Gala', 'Matt Porter',
	tjakobi, 'Alan Stern'

Hi Olof,

> From: Olof Johansson [mailto:olof@lixom.net]
> Sent: Wednesday, January 29, 2014 9:51 PM
> 
> On Wed, Jan 29, 2014 at 9:29 AM, Kamil Debski <k.debski@samsung.com>
> wrote:
> > Add support to PHY of USB2 of the Exynos 4 SoC.
> >
> > Signed-off-by: Kamil Debski <k.debski@samsung.com>
> > ---
> >  .../devicetree/bindings/arm/samsung/pmu.txt        |    2 ++
> >  arch/arm/boot/dts/exynos4.dtsi                     |   31
> ++++++++++++++++++++
> >  arch/arm/boot/dts/exynos4210.dtsi                  |   17
> +++++++++++
> >  arch/arm/boot/dts/exynos4x12.dtsi                  |   17
> +++++++++++
> >  4 files changed, 67 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/arm/samsung/pmu.txt
> > b/Documentation/devicetree/bindings/arm/samsung/pmu.txt
> > index 307e727..a76f91d 100644
> > --- a/Documentation/devicetree/bindings/arm/samsung/pmu.txt
> > +++ b/Documentation/devicetree/bindings/arm/samsung/pmu.txt
> > @@ -3,6 +3,8 @@ SAMSUNG Exynos SoC series PMU Registers
> >  Properties:
> >   - name : should be 'syscon';
> >   - compatible : should contain two values. First value must be one
> from following list:
> > +                  - "samsung,exynos4210-pmu" - for Exynos4210 SoC,
> > +                  - "samsung,exynos4x12-pmu" - for Exynos4212 SoC,
> >                    - "samsung,exynos5250-pmu" - for Exynos5250 SoC,
> >                    - "samsung,exynos5420-pmu" - for Exynos5420 SoC.
> >                 second value must be always "syscon".
> 
> This and other PMU related bindings/dts changes should probably go in
> separate patch(es) instead of being snuck in with USB changes.

O, I will move this to a separate patch.

Best wishes,
-- 
Kamil Debski
Samsung R&D Institute Poland


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

* RE: [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework
  2014-01-29 20:55     ` Olof Johansson
  (?)
@ 2014-02-05 15:57     ` Kamil Debski
  2014-02-05 17:30         ` Olof Johansson
  -1 siblings, 1 reply; 30+ messages in thread
From: Kamil Debski @ 2014-02-05 15:57 UTC (permalink / raw)
  To: 'Olof Johansson'
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	'Kyungmin Park', 'Kishon Vijay Abraham I',
	Tomasz Figa, Sylwester Nawrocki, Marek Szyprowski,
	'Vivek Gautam',
	mat.krawczuk, yulgon.kim, p.paneri, av.tikhomirov,
	'Jingoo Han', 'Kumar Gala', 'Matt Porter',
	tjakobi, 'Alan Stern'

Hi Olof,

Thank you for your review.

> From: Olof Johansson [mailto:olof@lixom.net]
> Sent: Wednesday, January 29, 2014 9:55 PM
> 
> Hi,
> 
> On Wed, Jan 29, 2014 at 9:29 AM, Kamil Debski <k.debski@samsung.com>
> wrote:
> > Change the phy provider used from the old one using the USB phy
> > framework to a new one using the Generic phy framework.
> >
> > Signed-off-by: Kamil Debski <k.debski@samsung.com>
> > ---
> >  .../devicetree/bindings/usb/exynos-usb.txt         |   13 +++
> >  drivers/usb/host/ehci-exynos.c                     |   97
> +++++++++++++-------
> >  2 files changed, 76 insertions(+), 34 deletions(-)
> >
> > diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt
> > b/Documentation/devicetree/bindings/usb/exynos-usb.txt
> > index d967ba1..25e199a 100644
> > --- a/Documentation/devicetree/bindings/usb/exynos-usb.txt
> > +++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt
> > @@ -12,6 +12,10 @@ Required properties:
> >   - interrupts: interrupt number to the cpu.
> >   - clocks: from common clock binding: handle to usb clock.
> >   - clock-names: from common clock binding: Shall be "usbhost".
> > +  - port: if in the SoC there are EHCI phys, they should be listed
> here.
> > +One phy per port. Each port should have its reg entry with a
> > +consecutive number. Also it should contain phys and phy-names
> entries
> > +specifying the phy used by the port.
> >
> >  Optional properties:
> >   - samsung,vbus-gpio:  if present, specifies the GPIO that @@ -27,6
> > +31,15 @@ Example:
> >
> >                 clocks = <&clock 285>;
> >                 clock-names = "usbhost";
> > +
> > +               #address-cells = <1>;
> > +               #size-cells = <0>;
> > +               port@0 {
> > +                   reg = <0>;
> > +                   phys = <&usb2phy 1>;
> > +                   phy-names = "host";
> > +                   status = "disabled";
> > +               };
> >         };
> >
> >  OHCI
> 
> [...]
> 
> > @@ -102,14 +132,26 @@ static int exynos_ehci_probe(struct
> platform_device *pdev)
> >                                         "samsung,exynos5440-ehci"))
> >                 goto skip_phy;
> >
> > -       phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
> > -       if (IS_ERR(phy)) {
> > -               usb_put_hcd(hcd);
> > -               dev_warn(&pdev->dev, "no platform data or transceiver
> defined\n");
> > -               return -EPROBE_DEFER;
> > -       } else {
> > -               exynos_ehci->phy = phy;
> > -               exynos_ehci->otg = phy->otg;
> > +       for_each_available_child_of_node(pdev->dev.of_node, child) {
> > +               err = of_property_read_u32(child, "reg",
> &phy_number);
> > +               if (err) {
> > +                       dev_err(&pdev->dev, "Failed to parse device
> tree\n");
> > +                       of_node_put(child);
> > +                       return err;
> > +               }
> > +               if (phy_number >= PHY_NUMBER) {
> > +                       dev_err(&pdev->dev, "Failed to parse device
> tree - number out of range\n");
> > +                       of_node_put(child);
> > +                       return -EINVAL;
> > +               }
> > +               phy = devm_of_phy_get(&pdev->dev, child, 0);
> > +               of_node_put(child);
> > +               if (IS_ERR(phy)) {
> > +                       dev_err(&pdev->dev, "Failed to get phy number
> %d",
> > +
> phy_number);
> > +                       return PTR_ERR(phy);
> > +               }
> > +               exynos_ehci->phy[phy_number] = phy;
> 
> this looks like it is now breaking older device trees, where ports
> might not be described. Since device tree interfaces need to be
> backwards compatible, you still need to handle the old case of not
> having ports described.
> 
> There are two ways of doing this:
> 
> 1. Fall back to the old behavior if there are no ports 2. Use a new
> compatible value for the new model with port subnodes, and if the old
> compatible value is used, then fall back to the old behavior.
> 
> I'm guessing (1) might be easiest since you can check for the presence
> of #address-cells to tell if this is just an old style node, or if it's
> a new-style node without any ports below it.

The ultimate goal is to remove the old phy driver. Unfortunately
this has to be synced with the new USB3 phy driver by Vivek Gautam. I think
he
is also close to completion. What about this case? In the end the old driver
will be removed and no longer be supported. Having backward compatibility in
mind, it is possible to have the old and the new phy driver together in one
kernel release. But do we want to have two drivers doing the same thing at
the same time?

Best wishes,
-- 
Kamil Debski
Samsung R&D Institute Poland



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

* Re: [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework
@ 2014-02-05 17:30         ` Olof Johansson
  0 siblings, 0 replies; 30+ messages in thread
From: Olof Johansson @ 2014-02-05 17:30 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	Kyungmin Park, Kishon Vijay Abraham I, Tomasz Figa,
	Sylwester Nawrocki, Marek Szyprowski, Vivek Gautam,
	Mateusz Krawczuk, yulgon.kim, Praveen Paneri, av.tikhomirov,
	Jingoo Han, Kumar Gala, Matt Porter, tjakobi, Alan Stern

On Wed, Feb 5, 2014 at 7:57 AM, Kamil Debski <k.debski@samsung.com> wrote:
> Hi Olof,
>
> Thank you for your review.
>
>> From: Olof Johansson [mailto:olof@lixom.net]
>> Sent: Wednesday, January 29, 2014 9:55 PM
>>
>> Hi,
>>
>> On Wed, Jan 29, 2014 at 9:29 AM, Kamil Debski <k.debski@samsung.com>
>> wrote:
>> > Change the phy provider used from the old one using the USB phy
>> > framework to a new one using the Generic phy framework.
>> >
>> > Signed-off-by: Kamil Debski <k.debski@samsung.com>
>> > ---
>> >  .../devicetree/bindings/usb/exynos-usb.txt         |   13 +++
>> >  drivers/usb/host/ehci-exynos.c                     |   97
>> +++++++++++++-------
>> >  2 files changed, 76 insertions(+), 34 deletions(-)
>> >
>> > diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt
>> > b/Documentation/devicetree/bindings/usb/exynos-usb.txt
>> > index d967ba1..25e199a 100644
>> > --- a/Documentation/devicetree/bindings/usb/exynos-usb.txt
>> > +++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt
>> > @@ -12,6 +12,10 @@ Required properties:
>> >   - interrupts: interrupt number to the cpu.
>> >   - clocks: from common clock binding: handle to usb clock.
>> >   - clock-names: from common clock binding: Shall be "usbhost".
>> > +  - port: if in the SoC there are EHCI phys, they should be listed
>> here.
>> > +One phy per port. Each port should have its reg entry with a
>> > +consecutive number. Also it should contain phys and phy-names
>> entries
>> > +specifying the phy used by the port.
>> >
>> >  Optional properties:
>> >   - samsung,vbus-gpio:  if present, specifies the GPIO that @@ -27,6
>> > +31,15 @@ Example:
>> >
>> >                 clocks = <&clock 285>;
>> >                 clock-names = "usbhost";
>> > +
>> > +               #address-cells = <1>;
>> > +               #size-cells = <0>;
>> > +               port@0 {
>> > +                   reg = <0>;
>> > +                   phys = <&usb2phy 1>;
>> > +                   phy-names = "host";
>> > +                   status = "disabled";
>> > +               };
>> >         };
>> >
>> >  OHCI
>>
>> [...]
>>
>> > @@ -102,14 +132,26 @@ static int exynos_ehci_probe(struct
>> platform_device *pdev)
>> >                                         "samsung,exynos5440-ehci"))
>> >                 goto skip_phy;
>> >
>> > -       phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
>> > -       if (IS_ERR(phy)) {
>> > -               usb_put_hcd(hcd);
>> > -               dev_warn(&pdev->dev, "no platform data or transceiver
>> defined\n");
>> > -               return -EPROBE_DEFER;
>> > -       } else {
>> > -               exynos_ehci->phy = phy;
>> > -               exynos_ehci->otg = phy->otg;
>> > +       for_each_available_child_of_node(pdev->dev.of_node, child) {
>> > +               err = of_property_read_u32(child, "reg",
>> &phy_number);
>> > +               if (err) {
>> > +                       dev_err(&pdev->dev, "Failed to parse device
>> tree\n");
>> > +                       of_node_put(child);
>> > +                       return err;
>> > +               }
>> > +               if (phy_number >= PHY_NUMBER) {
>> > +                       dev_err(&pdev->dev, "Failed to parse device
>> tree - number out of range\n");
>> > +                       of_node_put(child);
>> > +                       return -EINVAL;
>> > +               }
>> > +               phy = devm_of_phy_get(&pdev->dev, child, 0);
>> > +               of_node_put(child);
>> > +               if (IS_ERR(phy)) {
>> > +                       dev_err(&pdev->dev, "Failed to get phy number
>> %d",
>> > +
>> phy_number);
>> > +                       return PTR_ERR(phy);
>> > +               }
>> > +               exynos_ehci->phy[phy_number] = phy;
>>
>> this looks like it is now breaking older device trees, where ports
>> might not be described. Since device tree interfaces need to be
>> backwards compatible, you still need to handle the old case of not
>> having ports described.
>>
>> There are two ways of doing this:
>>
>> 1. Fall back to the old behavior if there are no ports 2. Use a new
>> compatible value for the new model with port subnodes, and if the old
>> compatible value is used, then fall back to the old behavior.
>>
>> I'm guessing (1) might be easiest since you can check for the presence
>> of #address-cells to tell if this is just an old style node, or if it's
>> a new-style node without any ports below it.
>
> The ultimate goal is to remove the old phy driver. Unfortunately
> this has to be synced with the new USB3 phy driver by Vivek Gautam. I think
> he
> is also close to completion. What about this case? In the end the old driver
> will be removed and no longer be supported. Having backward compatibility in
> mind, it is possible to have the old and the new phy driver together in one
> kernel release. But do we want to have two drivers doing the same thing at
> the same time?

It is mostly irrelevant if there is a new driver or not -- the old
device tree has to keep working. In this case it would mean that the
new driver needs to work with older device trees as well, or people
will see functionality regressing.

The device tree is a description of the hardware, not an extension of
the driver.


-Olof

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

* Re: [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework
@ 2014-02-05 17:30         ` Olof Johansson
  0 siblings, 0 replies; 30+ messages in thread
From: Olof Johansson @ 2014-02-05 17:30 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Kyungmin Park,
	Kishon Vijay Abraham I, Tomasz Figa, Sylwester Nawrocki,
	Marek Szyprowski, Vivek Gautam, Mateusz Krawczuk,
	yulgon.kim-Sze3O3UU22JBDgjK7y7TUQ, Praveen Paneri,
	av.tikhomirov-Sze3O3UU22JBDgjK7y7TUQ, Jingoo Han, Kumar Gala,
	Matt Porter, tjakobi-o02PS0xoJP9W0yFyLvAVXMxlOr/tl8fh,
	Alan Stern

On Wed, Feb 5, 2014 at 7:57 AM, Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> wrote:
> Hi Olof,
>
> Thank you for your review.
>
>> From: Olof Johansson [mailto:olof-nZhT3qVonbNeoWH0uzbU5w@public.gmane.org]
>> Sent: Wednesday, January 29, 2014 9:55 PM
>>
>> Hi,
>>
>> On Wed, Jan 29, 2014 at 9:29 AM, Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>> wrote:
>> > Change the phy provider used from the old one using the USB phy
>> > framework to a new one using the Generic phy framework.
>> >
>> > Signed-off-by: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>> > ---
>> >  .../devicetree/bindings/usb/exynos-usb.txt         |   13 +++
>> >  drivers/usb/host/ehci-exynos.c                     |   97
>> +++++++++++++-------
>> >  2 files changed, 76 insertions(+), 34 deletions(-)
>> >
>> > diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt
>> > b/Documentation/devicetree/bindings/usb/exynos-usb.txt
>> > index d967ba1..25e199a 100644
>> > --- a/Documentation/devicetree/bindings/usb/exynos-usb.txt
>> > +++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt
>> > @@ -12,6 +12,10 @@ Required properties:
>> >   - interrupts: interrupt number to the cpu.
>> >   - clocks: from common clock binding: handle to usb clock.
>> >   - clock-names: from common clock binding: Shall be "usbhost".
>> > +  - port: if in the SoC there are EHCI phys, they should be listed
>> here.
>> > +One phy per port. Each port should have its reg entry with a
>> > +consecutive number. Also it should contain phys and phy-names
>> entries
>> > +specifying the phy used by the port.
>> >
>> >  Optional properties:
>> >   - samsung,vbus-gpio:  if present, specifies the GPIO that @@ -27,6
>> > +31,15 @@ Example:
>> >
>> >                 clocks = <&clock 285>;
>> >                 clock-names = "usbhost";
>> > +
>> > +               #address-cells = <1>;
>> > +               #size-cells = <0>;
>> > +               port@0 {
>> > +                   reg = <0>;
>> > +                   phys = <&usb2phy 1>;
>> > +                   phy-names = "host";
>> > +                   status = "disabled";
>> > +               };
>> >         };
>> >
>> >  OHCI
>>
>> [...]
>>
>> > @@ -102,14 +132,26 @@ static int exynos_ehci_probe(struct
>> platform_device *pdev)
>> >                                         "samsung,exynos5440-ehci"))
>> >                 goto skip_phy;
>> >
>> > -       phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
>> > -       if (IS_ERR(phy)) {
>> > -               usb_put_hcd(hcd);
>> > -               dev_warn(&pdev->dev, "no platform data or transceiver
>> defined\n");
>> > -               return -EPROBE_DEFER;
>> > -       } else {
>> > -               exynos_ehci->phy = phy;
>> > -               exynos_ehci->otg = phy->otg;
>> > +       for_each_available_child_of_node(pdev->dev.of_node, child) {
>> > +               err = of_property_read_u32(child, "reg",
>> &phy_number);
>> > +               if (err) {
>> > +                       dev_err(&pdev->dev, "Failed to parse device
>> tree\n");
>> > +                       of_node_put(child);
>> > +                       return err;
>> > +               }
>> > +               if (phy_number >= PHY_NUMBER) {
>> > +                       dev_err(&pdev->dev, "Failed to parse device
>> tree - number out of range\n");
>> > +                       of_node_put(child);
>> > +                       return -EINVAL;
>> > +               }
>> > +               phy = devm_of_phy_get(&pdev->dev, child, 0);
>> > +               of_node_put(child);
>> > +               if (IS_ERR(phy)) {
>> > +                       dev_err(&pdev->dev, "Failed to get phy number
>> %d",
>> > +
>> phy_number);
>> > +                       return PTR_ERR(phy);
>> > +               }
>> > +               exynos_ehci->phy[phy_number] = phy;
>>
>> this looks like it is now breaking older device trees, where ports
>> might not be described. Since device tree interfaces need to be
>> backwards compatible, you still need to handle the old case of not
>> having ports described.
>>
>> There are two ways of doing this:
>>
>> 1. Fall back to the old behavior if there are no ports 2. Use a new
>> compatible value for the new model with port subnodes, and if the old
>> compatible value is used, then fall back to the old behavior.
>>
>> I'm guessing (1) might be easiest since you can check for the presence
>> of #address-cells to tell if this is just an old style node, or if it's
>> a new-style node without any ports below it.
>
> The ultimate goal is to remove the old phy driver. Unfortunately
> this has to be synced with the new USB3 phy driver by Vivek Gautam. I think
> he
> is also close to completion. What about this case? In the end the old driver
> will be removed and no longer be supported. Having backward compatibility in
> mind, it is possible to have the old and the new phy driver together in one
> kernel release. But do we want to have two drivers doing the same thing at
> the same time?

It is mostly irrelevant if there is a new driver or not -- the old
device tree has to keep working. In this case it would mean that the
new driver needs to work with older device trees as well, or people
will see functionality regressing.

The device tree is a description of the hardware, not an extension of
the driver.


-Olof
--
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	[flat|nested] 30+ messages in thread

* RE: [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework
@ 2014-02-05 19:55         ` Alan Stern
  0 siblings, 0 replies; 30+ messages in thread
From: Alan Stern @ 2014-02-05 19:55 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	kyungmin.park, kishon, Tomasz Figa, Sylwester Nawrocki,
	Marek Szyprowski, gautam.vivek, mat.krawczuk, yulgon.kim,
	p.paneri, av.tikhomirov, jg1.han, galak, matt.porter, tjakobi

On Wed, 5 Feb 2014, Kamil Debski wrote:

> Hi Alan,
> 
> Thank you for your review.

You're welcome.

> > 
> > > Change the phy provider used from the old one using the USB phy
> > You have removed all the OTG stuff from the driver.  This wasn't
> > mentioned in the patch description, and it has no connection with the
> > PHY work.
> 
> Maybe I'll explain more about what are we trying to achieve. The goal
> is to replace the old phy driver with the new one. In the old driver it was
> difficult to add support to new SoC. It also had issues with having device
> and
> host working together.
> 
> You're right that until the old phy driver is removed support for the it
> should
> remain. To be able to remove the old driver both new USB2 and new USB3 phy
> drivers
> have to be ready. The USB3 driver is written by Vivek Gautam and as I see
> it, he
> is also close to completion.
> 
> Regarding the otg part. The old phy driver is the only provider of the otg
> structure. It sets the host field of the structure. It is then used by
> samsung_usb2phy_init (drivers/usb/phy/phy-samsung-usb2.c) to check which
> driver is requesting the phy (is it host or device). In the new driver this
> is determined by the entry in device tree. So no need to check the otg
> struct
> and strstr (!) to check if dev_name is ehci, ohci or other, like the old
> driver.

Okay, that's fine.  But please explain this in the patch description
next time.  Otherwise the connection between the phy driver and the otg
structure is not at all clear; they look like two unrelated things.

Alan Stern


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

* RE: [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework
@ 2014-02-05 19:55         ` Alan Stern
  0 siblings, 0 replies; 30+ messages in thread
From: Alan Stern @ 2014-02-05 19:55 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ, kishon-l0cyMroinI0,
	Tomasz Figa, Sylwester Nawrocki, Marek Szyprowski,
	gautam.vivek-Sze3O3UU22JBDgjK7y7TUQ,
	mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w,
	yulgon.kim-Sze3O3UU22JBDgjK7y7TUQ,
	p.paneri-Sze3O3UU22JBDgjK7y7TUQ,
	av.tikhomirov-Sze3O3UU22JBDgjK7y7TUQ,
	jg1.han-Sze3O3UU22JBDgjK7y7TUQ, galak-sgV2jX0FEOL9JmXXK+q4OQ,
	matt.porter-QSEj5FYQhm4dnm+yROfE0A,
	tjakobi-o02PS0xoJP9W0yFyLvAVXMxlOr/tl8fh

On Wed, 5 Feb 2014, Kamil Debski wrote:

> Hi Alan,
> 
> Thank you for your review.

You're welcome.

> > 
> > > Change the phy provider used from the old one using the USB phy
> > You have removed all the OTG stuff from the driver.  This wasn't
> > mentioned in the patch description, and it has no connection with the
> > PHY work.
> 
> Maybe I'll explain more about what are we trying to achieve. The goal
> is to replace the old phy driver with the new one. In the old driver it was
> difficult to add support to new SoC. It also had issues with having device
> and
> host working together.
> 
> You're right that until the old phy driver is removed support for the it
> should
> remain. To be able to remove the old driver both new USB2 and new USB3 phy
> drivers
> have to be ready. The USB3 driver is written by Vivek Gautam and as I see
> it, he
> is also close to completion.
> 
> Regarding the otg part. The old phy driver is the only provider of the otg
> structure. It sets the host field of the structure. It is then used by
> samsung_usb2phy_init (drivers/usb/phy/phy-samsung-usb2.c) to check which
> driver is requesting the phy (is it host or device). In the new driver this
> is determined by the entry in device tree. So no need to check the otg
> struct
> and strstr (!) to check if dev_name is ehci, ohci or other, like the old
> driver.

Okay, that's fine.  But please explain this in the patch description
next time.  Otherwise the connection between the phy driver and the otg
structure is not at all clear; they look like two unrelated things.

Alan Stern

--
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	[flat|nested] 30+ messages in thread

* Re: [PATCH v6 5/8] phy: Add new Exynos USB PHY driver
@ 2014-03-03 14:27     ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 30+ messages in thread
From: Kishon Vijay Abraham I @ 2014-03-03 14:27 UTC (permalink / raw)
  To: Kamil Debski, linux-kernel, linux-samsung-soc, linux-usb, devicetree
  Cc: kyungmin.park, t.figa, s.nawrocki, m.szyprowski, gautam.vivek,
	mat.krawczuk, yulgon.kim, p.paneri, av.tikhomirov, jg1.han,
	galak, matt.porter, tjakobi, stern

Hi,

On Wednesday 29 January 2014 10:59 PM, Kamil Debski wrote:
> Add a new driver for the Exynos USB PHY. The new driver uses the generic
> PHY framework. The driver includes support for the Exynos 4x10 and 4x12
> SoC families.

Can the PHY part of this series be merged independently of the 
controller part?

Thanks
Kishon
>
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> ---
>   .../devicetree/bindings/phy/samsung-phy.txt        |   55 ++++
>   Documentation/phy/samsung-usb2.txt                 |  135 ++++++++
>   drivers/phy/Kconfig                                |   29 ++
>   drivers/phy/Makefile                               |    3 +
>   drivers/phy/phy-exynos4210-usb2.c                  |  257 ++++++++++++++++
>   drivers/phy/phy-exynos4x12-usb2.c                  |  323 ++++++++++++++++++++
>   drivers/phy/phy-samsung-usb2.c                     |  227 ++++++++++++++
>   drivers/phy/phy-samsung-usb2.h                     |   67 ++++
>   8 files changed, 1096 insertions(+)
>   create mode 100644 Documentation/phy/samsung-usb2.txt
>   create mode 100644 drivers/phy/phy-exynos4210-usb2.c
>   create mode 100644 drivers/phy/phy-exynos4x12-usb2.c
>   create mode 100644 drivers/phy/phy-samsung-usb2.c
>   create mode 100644 drivers/phy/phy-samsung-usb2.h
>
> diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt
> index c0fccaa..6668c41 100644
> --- a/Documentation/devicetree/bindings/phy/samsung-phy.txt
> +++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt
> @@ -20,3 +20,58 @@ Required properties:
>   - compatible : should be "samsung,exynos5250-dp-video-phy";
>   - reg : offset and length of the Display Port PHY register set;
>   - #phy-cells : from the generic PHY bindings, must be 0;
> +
> +Samsung S5P/EXYNOS SoC series USB PHY
> +-------------------------------------------------
> +
> +Required properties:
> +- compatible : should be one of the listed compatibles:
> +	- "samsung,exynos4210-usb2-phy"
> +	- "samsung,exynos4x12-usb2-phy"
> +- reg : a list of registers used by phy driver
> +	- first and obligatory is the location of phy modules registers
> +- samsung,sysreg-phandle - handle to syscon used to control the system registers
> +- samsung,pmureg-phandle - handle to syscon used to control PMU registers
> +- #phy-cells : from the generic phy bindings, must be 1;
> +- clocks and clock-names:
> +	- the "phy" clocks is required by the phy module
> +	- next for each of the phys a clock has to be assigned, this clock
> +	  will be used to determine clocking frequency for the phys
> +	  (the labels are specified in the paragraph below)
> +
> +The first phandle argument in the PHY specifier identifies the PHY, its
> +meaning is compatible dependent. For the currently supported SoCs (Exynos 4210
> +and Exynos 4212) it is as follows:
> +  0 - USB device ("device"),
> +  1 - USB host ("host"),
> +  2 - HSIC0 ("hsic0"),
> +  3 - HSIC1 ("hsic1"),
> +
> +Exynos 4210 and Exynos 4212 use mode switching and require that mode switch
> +register is supplied.
> +
> +Example:
> +
> +For Exynos 4412 (compatible with Exynos 4212):
> +
> +usbphy: phy@125b0000 {
> +	compatible = "samsung,exynos4212-usb2-phy";
> +	reg = <0x125b0000 0x100 0x10020704 0x0c 0x1001021c 0x4>;
> +	clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
> +							<&clock 2>;
> +	clock-names = "phy", "device", "host", "hsic0", "hsic1";
> +	status = "okay";
> +	#phy-cells = <1>;
> +	samsung,sysreg-phandle = <&sys_reg>;
> +	samsung,pmureg-phandle = <&pmu_reg>;
> +};
> +
> +Then the PHY can be used in other nodes such as:
> +
> +phy-consumer@12340000 {
> +	phys = <&usbphy 2>;
> +	phy-names = "phy";
> +};
> +
> +Refer to DT bindings documentation of particular PHY consumer devices for more
> +information about required PHYs and the way of specification.
> diff --git a/Documentation/phy/samsung-usb2.txt b/Documentation/phy/samsung-usb2.txt
> new file mode 100644
> index 0000000..9f5826e
> --- /dev/null
> +++ b/Documentation/phy/samsung-usb2.txt
> @@ -0,0 +1,135 @@
> +.------------------------------------------------------------------------------+
> +|			Samsung USB 2.0 PHY adaptation layer		       |
> ++-----------------------------------------------------------------------------+'
> +
> +| 1. Description
> ++----------------
> +
> +The architecture of the USB 2.0 PHY module in Samsung SoCs is similar
> +among many SoCs. In spite of the similarities it proved difficult to
> +create a one driver that would fit all these PHY controllers. Often
> +the differences were minor and were found in particular bits of the
> +registers of the PHY. In some rare cases the order of register writes or
> +the PHY powering up process had to be altered. This adaptation layer is
> +a compromise between having separate drivers and having a single driver
> +with added support for many special cases.
> +
> +| 2. Files description
> ++----------------------
> +
> +- phy-samsung-usb2.c
> +   This is the main file of the adaptation layer. This file contains
> +   the probe function and provides two callbacks to the Generic PHY
> +   Framework. This two callbacks are used to power on and power off the
> +   phy. They carry out the common work that has to be done on all version
> +   of the PHY module. Depending on which SoC was chosen they execute SoC
> +   specific callbacks. The specific SoC version is selected by choosing
> +   the appropriate compatible string. In addition, this file contains
> +   struct of_device_id definitions for particular SoCs.
> +
> +- phy-samsung-usb2.h
> +   This is the include file. It declares the structures used by this
> +   driver. In addition it should contain extern declarations for
> +   structures that describe particular SoCs.
> +
> +| 3. Supporting SoCs
> ++--------------------
> +
> +To support a new SoC a new file should be added to the drivers/phy
> +directory. Each SoC's configuration is stored in an instance of the
> +struct samsung_usb2_phy_config.
> +
> +struct samsung_usb2_phy_config {
> +	const struct samsung_usb2_common_phy *phys;
> +	unsigned int num_phys;
> +	bool has_mode_switch;
> +};
> +
> +The num_phys is the number of phys handled by the driver. *phys is an
> +array that contains the configuration for each phy. The has_mode_switch
> +property is a boolean flag that determines whether the SoC has USB host
> +and device on a single pair of pins. If so, a special register has to
> +be modified to change the internal routing of these pins between a USB
> +device or host module.
> +
> +For example the configuration for Exynos 4210 is following:
> +
> +const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
> +	.num_phys		= EXYNOS4210_NUM_PHYS,
> +	.phys			= exynos4210_phys,
> +}
> +
> +The exynos4210_phys configuration array is as follows:
> +
> +static const struct samsung_usb2_common_phy exynos4210_phys[] = {
> +	{
> +		.label		= "device",
> +		.id		= EXYNOS4210_DEVICE,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "host",
> +		.id		= EXYNOS4210_HOST,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "hsic0",
> +		.id		= EXYNOS4210_HSIC0,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "hsic1",
> +		.id		= EXYNOS4210_HSIC1,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{},
> +};
> +
> +- int (*rate_to_clk)(unsigned long, u32 *)
> +	The rate_to_clk callback is to convert the rate of the clock
> +	used to clock the module to the value that should be written to
> +	the registers of the PHY module.
> +- int (*power_on)(struct samsung_usb2_phy_instance *);
> +- int (*power_off)(struct samsung_usb2_phy_instance *);
> +	These two callbacks are used to power on and power off the phy
> +	by modifying appropriate registers.
> +
> +Final change to the driver is adding appropriate compatible value to the
> +phy-samsung-usb2.c file. In case of Exynos 4210 the following lines were
> +added to the struct of_device_id samsung_usb2_phy_of_match[] array:
> +
> +#ifdef CONFIG_PHY_EXYNOS4210_USB2
> +	{
> +		.compatible = "samsung,exynos4210-usb2-phy",
> +		.data = &exynos4210_usb2_phy_config,
> +	},
> +#endif
> +
> +To add further flexibility to the driver the Kconfig file enables to
> +include support for selected SoCs in the compiled driver. The Kconfig
> +entry for Exynos 4210 is following:
> +
> +config PHY_EXYNOS4210_USB2
> +	bool "Support for Exynos 4210"
> +	depends on PHY_SAMSUNG_USB2
> +	depends on CPU_EXYNOS4210
> +	help
> +	  Enable USB PHY support for Exynos 4210. This option requires that
> +	  Samsung USB 2.0 PHY driver is enabled and means that support for this
> +	  particular SoC is compiled in the driver. In case of Exynos 4210 four
> +	  phys are available - device, host, HSCI0 and HSCI1.
> +
> +The newly created file that supports the new SoC has to be also added to the
> +Makefile. In case of Exynos 4210 the added line is following:
> +
> +obj-$(CONFIG_PHY_EXYNOS4210_USB2)       += phy-exynos4210-usb2.o
> +
> +After completing these steps the support for the new SoC should be ready.
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index b901c47..3691d24 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -63,4 +63,33 @@ config BCM_KONA_USB2_PHY
>   	help
>   	  Enable this to support the Broadcom Kona USB 2.0 PHY.
>
> +config PHY_SAMSUNG_USB2
> +	tristate "Samsung USB 2.0 PHY driver"
> +	select GENERIC_PHY
> +	select MFD_SYSCON
> +	help
> +	  Enable this to support the Samsung USB 2.0 PHY driver for Samsung
> +	  SoCs. This driver provides the interface for USB 2.0 PHY. Support for
> +	  particular SoCs has to be enabled in addition to this driver. Number
> +	  and type of supported phys depends on the SoC.
> +
> +config PHY_EXYNOS4210_USB2
> +	bool "Support for Exynos 4210"
> +	depends on PHY_SAMSUNG_USB2
> +	depends on CPU_EXYNOS4210
> +	help
> +	  Enable USB PHY support for Exynos 4210. This option requires that
> +	  Samsung USB 2.0 PHY driver is enabled and means that support for this
> +	  particular SoC is compiled in the driver. In case of Exynos 4210 four
> +	  phys are available - device, host, HSIC0 and HSIC1.
> +
> +config PHY_EXYNOS4X12_USB2
> +	bool "Support for Exynos 4x12"
> +	depends on PHY_SAMSUNG_USB2
> +	depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
> +	help
> +	  Enable USB PHY support for Exynos 4x12. This option requires that
> +	  Samsung USB 2.0 PHY driver is enabled and means that support for this
> +	  particular SoC is compiled in the driver. In case of Exynos 4x12 four
> +	  phys are available - device, host, HSIC0 and HSIC1.
>   endmenu
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index b57c253..69d0b3f2 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -9,3 +9,6 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)	+= phy-exynos-mipi-video.o
>   obj-$(CONFIG_PHY_MVEBU_SATA)		+= phy-mvebu-sata.o
>   obj-$(CONFIG_OMAP_USB2)			+= phy-omap-usb2.o
>   obj-$(CONFIG_TWL4030_USB)		+= phy-twl4030-usb.o
> +obj-$(CONFIG_PHY_SAMSUNG_USB2)		+= phy-samsung-usb2.o
> +obj-$(CONFIG_PHY_EXYNOS4210_USB2)	+= phy-exynos4210-usb2.o
> +obj-$(CONFIG_PHY_EXYNOS4X12_USB2)	+= phy-exynos4x12-usb2.o
> diff --git a/drivers/phy/phy-exynos4210-usb2.c b/drivers/phy/phy-exynos4210-usb2.c
> new file mode 100644
> index 0000000..8f86abf
> --- /dev/null
> +++ b/drivers/phy/phy-exynos4210-usb2.c
> @@ -0,0 +1,257 @@
> +/*
> + * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support
> + *
> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> + * Author: Kamil Debski <k.debski@samsung.com>
> + *
> + * 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.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/phy/phy.h>
> +#include <linux/regmap.h>
> +#include "phy-samsung-usb2.h"
> +
> +/* Exynos USB PHY registers */
> +
> +/* PHY power control */
> +#define EXYNOS_4210_UPHYPWR			0x0
> +
> +#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND	BIT(0)
> +#define EXYNOS_4210_UPHYPWR_PHY0_PWR		BIT(3)
> +#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR	BIT(4)
> +#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP		BIT(5)
> +#define EXYNOS_4210_UPHYPWR_PHY0	( \
> +	EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \
> +	EXYNOS_4210_UPHYPWR_PHY0_PWR | \
> +	EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \
> +	EXYNOS_4210_UPHYPWR_PHY0_SLEEP)
> +
> +#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND	BIT(6)
> +#define EXYNOS_4210_UPHYPWR_PHY1_PWR		BIT(7)
> +#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP		BIT(8)
> +#define EXYNOS_4210_UPHYPWR_PHY1 ( \
> +	EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \
> +	EXYNOS_4210_UPHYPWR_PHY1_PWR | \
> +	EXYNOS_4210_UPHYPWR_PHY1_SLEEP)
> +
> +#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND	BIT(9)
> +#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP		BIT(10)
> +#define EXYNOS_4210_UPHYPWR_HSIC0 ( \
> +	EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \
> +	EXYNOS_4210_UPHYPWR_HSIC0_SLEEP)
> +
> +#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND	BIT(11)
> +#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP		BIT(12)
> +#define EXYNOS_4210_UPHYPWR_HSIC1 ( \
> +	EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \
> +	EXYNOS_4210_UPHYPWR_HSIC1_SLEEP)
> +
> +/* PHY clock control */
> +#define EXYNOS_4210_UPHYCLK			0x4
> +
> +#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK	(0x3 << 0)
> +#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ	(0x0 << 0)
> +#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ	(0x3 << 0)
> +#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
> +
> +#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP	BIT(2)
> +#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON	BIT(4)
> +#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON	BIT(7)
> +
> +/* PHY reset control */
> +#define EXYNOS_4210_UPHYRST			0x8
> +
> +#define EXYNOS_4210_URSTCON_PHY0		BIT(0)
> +#define EXYNOS_4210_URSTCON_OTG_HLINK		BIT(1)
> +#define EXYNOS_4210_URSTCON_OTG_PHYLINK		BIT(2)
> +#define EXYNOS_4210_URSTCON_PHY1_ALL		BIT(3)
> +#define EXYNOS_4210_URSTCON_PHY1_P0		BIT(4)
> +#define EXYNOS_4210_URSTCON_PHY1_P1P2		BIT(5)
> +#define EXYNOS_4210_URSTCON_HOST_LINK_ALL	BIT(6)
> +#define EXYNOS_4210_URSTCON_HOST_LINK_P0	BIT(7)
> +#define EXYNOS_4210_URSTCON_HOST_LINK_P1	BIT(8)
> +#define EXYNOS_4210_URSTCON_HOST_LINK_P2	BIT(9)
> +
> +/* Isolation, configured in the power management unit */
> +#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET	0x704
> +#define EXYNOS_4210_USB_ISOL_DEVICE		BIT(0)
> +#define EXYNOS_4210_USB_ISOL_HOST_OFFSET	0x708
> +#define EXYNOS_4210_USB_ISOL_HOST		BIT(0)
> +
> +/* USBYPHY1 Floating prevention */
> +#define EXYNOS_4210_UPHY1CON			0x34
> +#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION	0x1
> +
> +/* Mode switching SUB Device <-> Host */
> +#define EXYNOS_4210_MODE_SWITCH_OFFSET		0x21c
> +#define EXYNOS_4210_MODE_SWITCH_MASK		1
> +#define EXYNOS_4210_MODE_SWITCH_DEVICE		0
> +#define EXYNOS_4210_MODE_SWITCH_HOST		1
> +
> +enum exynos4210_phy_id {
> +	EXYNOS4210_DEVICE,
> +	EXYNOS4210_HOST,
> +	EXYNOS4210_HSIC0,
> +	EXYNOS4210_HSIC1,
> +	EXYNOS4210_NUM_PHYS,
> +};
> +
> +/*
> + * exynos4210_rate_to_clk() converts the supplied clock rate to the value that
> + * can be written to the phy register.
> + */
> +static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg)
> +{
> +	switch (rate) {
> +	case 12 * MHZ:
> +		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
> +		break;
> +	case 24 * MHZ:
> +		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
> +		break;
> +	case 48 * MHZ:
> +		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on)
> +{
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +	u32 offset;
> +	u32 mask;
> +
> +	switch (inst->cfg->id) {
> +	case EXYNOS4210_DEVICE:
> +		offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET;
> +		mask = EXYNOS_4210_USB_ISOL_DEVICE;
> +		break;
> +	case EXYNOS4210_HOST:
> +		offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET;
> +		mask = EXYNOS_4210_USB_ISOL_HOST;
> +		break;
> +	default:
> +		return;
> +	};
> +
> +	regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
> +}
> +
> +static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
> +{
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +	u32 rstbits = 0;
> +	u32 phypwr = 0;
> +	u32 rst;
> +	u32 pwr;
> +
> +	switch (inst->cfg->id) {
> +	case EXYNOS4210_DEVICE:
> +		phypwr =	EXYNOS_4210_UPHYPWR_PHY0;
> +		rstbits =	EXYNOS_4210_URSTCON_PHY0;
> +		break;
> +	case EXYNOS4210_HOST:
> +		phypwr =	EXYNOS_4210_UPHYPWR_PHY1;
> +		rstbits =	EXYNOS_4210_URSTCON_PHY1_ALL |
> +				EXYNOS_4210_URSTCON_PHY1_P0 |
> +				EXYNOS_4210_URSTCON_PHY1_P1P2 |
> +				EXYNOS_4210_URSTCON_HOST_LINK_ALL |
> +				EXYNOS_4210_URSTCON_HOST_LINK_P0;
> +		writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON);
> +		break;
> +	case EXYNOS4210_HSIC0:
> +		phypwr =	EXYNOS_4210_UPHYPWR_HSIC0;
> +		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
> +				EXYNOS_4210_URSTCON_HOST_LINK_P1;
> +		break;
> +	case EXYNOS4210_HSIC1:
> +		phypwr =	EXYNOS_4210_UPHYPWR_HSIC1;
> +		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
> +				EXYNOS_4210_URSTCON_HOST_LINK_P2;
> +		break;
> +	};
> +
> +	if (on) {
> +		writel(inst->clk_reg_val, drv->reg_phy + EXYNOS_4210_UPHYCLK);
> +
> +		pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
> +		pwr &= ~phypwr;
> +		writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
> +
> +		rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST);
> +		rst |= rstbits;
> +		writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
> +		udelay(10);
> +		rst &= ~rstbits;
> +		writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
> +	} else {
> +		pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
> +		pwr |= phypwr;
> +		writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
> +	}
> +}
> +
> +static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst)
> +{
> +	/* Order of initialisation is important - first power then isolation */
> +	exynos4210_phy_pwr(inst, 1);
> +	exynos4210_isol(inst, 0);
> +
> +	return 0;
> +}
> +
> +static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst)
> +{
> +	exynos4210_isol(inst, 1);
> +	exynos4210_phy_pwr(inst, 0);
> +
> +	return 0;
> +}
> +
> +
> +static const struct samsung_usb2_common_phy exynos4210_phys[] = {
> +	{
> +		.label		= "device",
> +		.id		= EXYNOS4210_DEVICE,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "host",
> +		.id		= EXYNOS4210_HOST,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "hsic0",
> +		.id		= EXYNOS4210_HSIC0,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "hsic1",
> +		.id		= EXYNOS4210_HSIC1,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{},
> +};
> +
> +const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
> +	.num_phys		= EXYNOS4210_NUM_PHYS,
> +	.phys			= exynos4210_phys,
> +	.has_mode_switch	= 0,
> +};
> +
> diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c
> new file mode 100644
> index 0000000..ce39502
> --- /dev/null
> +++ b/drivers/phy/phy-exynos4x12-usb2.c
> @@ -0,0 +1,323 @@
> +/*
> + * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4x12 support
> + *
> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> + * Author: Kamil Debski <k.debski@samsung.com>
> + *
> + * 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.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/phy/phy.h>
> +#include <linux/regmap.h>
> +#include "phy-samsung-usb2.h"
> +
> +/* Exynos USB PHY registers */
> +
> +/* PHY power control */
> +#define EXYNOS_4x12_UPHYPWR			0x0
> +
> +#define EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND	BIT(0)
> +#define EXYNOS_4x12_UPHYPWR_PHY0_PWR		BIT(3)
> +#define EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR	BIT(4)
> +#define EXYNOS_4x12_UPHYPWR_PHY0_SLEEP		BIT(5)
> +#define EXYNOS_4x12_UPHYPWR_PHY0 ( \
> +	EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND | \
> +	EXYNOS_4x12_UPHYPWR_PHY0_PWR | \
> +	EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR | \
> +	EXYNOS_4x12_UPHYPWR_PHY0_SLEEP)
> +
> +#define EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND	BIT(6)
> +#define EXYNOS_4x12_UPHYPWR_PHY1_PWR		BIT(7)
> +#define EXYNOS_4x12_UPHYPWR_PHY1_SLEEP		BIT(8)
> +#define EXYNOS_4x12_UPHYPWR_PHY1 ( \
> +	EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND | \
> +	EXYNOS_4x12_UPHYPWR_PHY1_PWR | \
> +	EXYNOS_4x12_UPHYPWR_PHY1_SLEEP)
> +
> +#define EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND	BIT(9)
> +#define EXYNOS_4x12_UPHYPWR_HSIC0_PWR		BIT(10)
> +#define EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP		BIT(11)
> +#define EXYNOS_4x12_UPHYPWR_HSIC0 ( \
> +	EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND | \
> +	EXYNOS_4x12_UPHYPWR_HSIC0_PWR | \
> +	EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP)
> +
> +#define EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND	BIT(12)
> +#define EXYNOS_4x12_UPHYPWR_HSIC1_PWR		BIT(13)
> +#define EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP		BIT(14)
> +#define EXYNOS_4x12_UPHYPWR_HSIC1 ( \
> +	EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND | \
> +	EXYNOS_4x12_UPHYPWR_HSIC1_PWR | \
> +	EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP)
> +
> +/* PHY clock control */
> +#define EXYNOS_4x12_UPHYCLK			0x4
> +
> +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK	(0x7 << 0)
> +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6	(0x0 << 0)
> +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ	(0x1 << 0)
> +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
> +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2	(0x3 << 0)
> +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ	(0x4 << 0)
> +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ	(0x5 << 0)
> +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ	(0x7 << 0)
> +
> +#define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP	BIT(3)
> +#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON	BIT(4)
> +#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON	BIT(7)
> +
> +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK	(0x7f << 10)
> +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ	(0x24 << 10)
> +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ	(0x1c << 10)
> +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ	(0x1a << 10)
> +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2	(0x15 << 10)
> +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ	(0x14 << 10)
> +
> +/* PHY reset control */
> +#define EXYNOS_4x12_UPHYRST			0x8
> +
> +#define EXYNOS_4x12_URSTCON_PHY0		BIT(0)
> +#define EXYNOS_4x12_URSTCON_OTG_HLINK		BIT(1)
> +#define EXYNOS_4x12_URSTCON_OTG_PHYLINK		BIT(2)
> +#define EXYNOS_4x12_URSTCON_HOST_PHY		BIT(3)
> +#define EXYNOS_4x12_URSTCON_PHY1		BIT(4)
> +#define EXYNOS_4x12_URSTCON_HSIC0		BIT(5)
> +#define EXYNOS_4x12_URSTCON_HSIC1		BIT(6)
> +#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL	BIT(7)
> +#define EXYNOS_4x12_URSTCON_HOST_LINK_P0	BIT(8)
> +#define EXYNOS_4x12_URSTCON_HOST_LINK_P1	BIT(9)
> +#define EXYNOS_4x12_URSTCON_HOST_LINK_P2	BIT(10)
> +
> +/* Isolation, configured in the power management unit */
> +#define EXYNOS_4x12_USB_ISOL_OFFSET		0x704
> +#define EXYNOS_4x12_USB_ISOL_OTG		BIT(0)
> +#define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET	0x708
> +#define EXYNOS_4x12_USB_ISOL_HSIC0		BIT(0)
> +#define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET	0x70c
> +#define EXYNOS_4x12_USB_ISOL_HSIC1		BIT(0)
> +
> +/* Mode switching SUB Device <-> Host */
> +#define EXYNOS_4x12_MODE_SWITCH_OFFSET		0x21c
> +#define EXYNOS_4x12_MODE_SWITCH_MASK		1
> +#define EXYNOS_4x12_MODE_SWITCH_DEVICE		0
> +#define EXYNOS_4x12_MODE_SWITCH_HOST		1
> +
> +enum exynos4x12_phy_id {
> +	EXYNOS4x12_DEVICE,
> +	EXYNOS4x12_HOST,
> +	EXYNOS4x12_HSIC0,
> +	EXYNOS4x12_HSIC1,
> +	EXYNOS4x12_NUM_PHYS,
> +};
> +
> +/*
> + * exynos4x12_rate_to_clk() converts the supplied clock rate to the value that
> + * can be written to the phy register.
> + */
> +static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg)
> +{
> +	/* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */
> +
> +	switch (rate) {
> +	case 9600 * KHZ:
> +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6;
> +		break;
> +	case 10 * MHZ:
> +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ;
> +		break;
> +	case 12 * MHZ:
> +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ;
> +		break;
> +	case 19200 * KHZ:
> +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2;
> +		break;
> +	case 20 * MHZ:
> +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ;
> +		break;
> +	case 24 * MHZ:
> +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ;
> +		break;
> +	case 50 * MHZ:
> +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst, bool on)
> +{
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +	u32 offset;
> +	u32 mask;
> +
> +	switch (inst->cfg->id) {
> +	case EXYNOS4x12_DEVICE:
> +	case EXYNOS4x12_HOST:
> +		offset = EXYNOS_4x12_USB_ISOL_OFFSET;
> +		mask = EXYNOS_4x12_USB_ISOL_OTG;
> +		break;
> +	case EXYNOS4x12_HSIC0:
> +		offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET;
> +		mask = EXYNOS_4x12_USB_ISOL_HSIC0;
> +		break;
> +	case EXYNOS4x12_HSIC1:
> +		offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET;
> +		mask = EXYNOS_4x12_USB_ISOL_HSIC1;
> +		break;
> +	default:
> +		return;
> +	};
> +
> +	regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
> +}
> +
> +static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst)
> +{
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +	writel(inst->clk_reg_val, drv->reg_phy + EXYNOS_4x12_UPHYCLK);
> +}
> +
> +static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
> +{
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +	u32 rstbits = 0;
> +	u32 phypwr = 0;
> +	u32 rst;
> +	u32 pwr;
> +	u32 mode = 0;
> +	u32 switch_mode = 0;
> +
> +	switch (inst->cfg->id) {
> +	case EXYNOS4x12_DEVICE:
> +		phypwr =	EXYNOS_4x12_UPHYPWR_PHY0;
> +		rstbits =	EXYNOS_4x12_URSTCON_PHY0;
> +		mode =		EXYNOS_4x12_MODE_SWITCH_DEVICE;
> +		switch_mode =	1;
> +		break;
> +	case EXYNOS4x12_HOST:
> +		phypwr =	EXYNOS_4x12_UPHYPWR_PHY1;
> +		rstbits =	EXYNOS_4x12_URSTCON_HOST_PHY;
> +		mode =		EXYNOS_4x12_MODE_SWITCH_HOST;
> +		switch_mode =	1;
> +		break;
> +	case EXYNOS4x12_HSIC0:
> +		phypwr =	EXYNOS_4x12_UPHYPWR_HSIC0;
> +		rstbits =	EXYNOS_4x12_URSTCON_HSIC1 |
> +				EXYNOS_4x12_URSTCON_HOST_LINK_P0 |
> +				EXYNOS_4x12_URSTCON_HOST_PHY;
> +		break;
> +	case EXYNOS4x12_HSIC1:
> +		phypwr =	EXYNOS_4x12_UPHYPWR_HSIC1;
> +		rstbits =	EXYNOS_4x12_URSTCON_HSIC1 |
> +				EXYNOS_4x12_URSTCON_HOST_LINK_P1;
> +		break;
> +	};
> +
> +	if (on) {
> +		if (switch_mode)
> +			regmap_update_bits(drv->reg_sys,
> +			        EXYNOS_4x12_MODE_SWITCH_OFFSET,
> +			        EXYNOS_4x12_MODE_SWITCH_MASK,
> +				mode);
> +
> +		pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
> +		pwr &= ~phypwr;
> +		writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
> +
> +		rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST);
> +		rst |= rstbits;
> +		writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
> +		udelay(10);
> +		rst &= ~rstbits;
> +		writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
> +	} else {
> +		pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
> +		pwr |= phypwr;
> +		writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
> +	}
> +}
> +
> +static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst)
> +{
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +
> +	inst->enabled = 1;
> +	exynos4x12_setup_clk(inst);
> +	exynos4x12_phy_pwr(inst, 1);
> +	exynos4x12_isol(inst, 0);
> +
> +	/* Power on the device, as it is necessary for HSIC to work */
> +	if (inst->cfg->id == EXYNOS4x12_HSIC0) {
> +		struct samsung_usb2_phy_instance *device =
> +					&drv->instances[EXYNOS4x12_DEVICE];
> +		exynos4x12_phy_pwr(device, 1);
> +		exynos4x12_isol(device, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst)
> +{
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +	struct samsung_usb2_phy_instance *device =
> +					&drv->instances[EXYNOS4x12_DEVICE];
> +
> +	inst->enabled = 0;
> +	exynos4x12_isol(inst, 1);
> +	exynos4x12_phy_pwr(inst, 0);
> +
> +	if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) {
> +		exynos4x12_isol(device, 1);
> +		exynos4x12_phy_pwr(device, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +
> +static const struct samsung_usb2_common_phy exynos4x12_phys[] = {
> +	{
> +		.label		= "device",
> +		.id		= EXYNOS4x12_DEVICE,
> +		.rate_to_clk	= exynos4x12_rate_to_clk,
> +		.power_on	= exynos4x12_power_on,
> +		.power_off	= exynos4x12_power_off,
> +	},
> +	{
> +		.label		= "host",
> +		.id		= EXYNOS4x12_HOST,
> +		.rate_to_clk	= exynos4x12_rate_to_clk,
> +		.power_on	= exynos4x12_power_on,
> +		.power_off	= exynos4x12_power_off,
> +	},
> +	{
> +		.label		= "hsic0",
> +		.id		= EXYNOS4x12_HSIC0,
> +		.rate_to_clk	= exynos4x12_rate_to_clk,
> +		.power_on	= exynos4x12_power_on,
> +		.power_off	= exynos4x12_power_off,
> +	},
> +	{
> +		.label		= "hsic1",
> +		.id		= EXYNOS4x12_HSIC1,
> +		.rate_to_clk	= exynos4x12_rate_to_clk,
> +		.power_on	= exynos4x12_power_on,
> +		.power_off	= exynos4x12_power_off,
> +	},
> +	{},
> +};
> +
> +const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = {
> +	.num_phys		= EXYNOS4x12_NUM_PHYS,
> +	.phys			= exynos4x12_phys,
> +	.has_mode_switch	= 1,
> +};
> +
> diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c
> new file mode 100644
> index 0000000..c1b0fd5
> --- /dev/null
> +++ b/drivers/phy/phy-samsung-usb2.c
> @@ -0,0 +1,227 @@
> +/*
> + * Samsung SoC USB 1.1/2.0 PHY driver
> + *
> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> + * Author: Kamil Debski <k.debski@samsung.com>
> + *
> + * 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.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include "phy-samsung-usb2.h"
> +
> +static int samsung_usb2_phy_power_on(struct phy *phy)
> +{
> +	struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +	int ret;
> +
> +	dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n",
> +							inst->cfg->label);
> +	ret = clk_prepare_enable(drv->clk);
> +	if (ret)
> +		goto err_main_clk;
> +	ret = clk_prepare_enable(inst->clk);
> +	if (ret)
> +		goto err_instance_clk;
> +	inst->rate = clk_get_rate(inst->clk);
> +	if (inst->cfg->rate_to_clk) {
> +		ret = inst->cfg->rate_to_clk(inst->rate, &inst->clk_reg_val);
> +		if (ret)
> +			goto err_get_rate;
> +	}
> +	if (inst->cfg->power_on) {
> +		spin_lock(&drv->lock);
> +		ret = inst->cfg->power_on(inst);
> +		spin_unlock(&drv->lock);
> +	}
> +
> +	return 0;
> +
> +err_get_rate:
> +	clk_disable_unprepare(inst->clk);
> +err_instance_clk:
> +	clk_disable_unprepare(drv->clk);
> +err_main_clk:
> +	return ret;
> +}
> +
> +static int samsung_usb2_phy_power_off(struct phy *phy)
> +{
> +	struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +	int ret = 0;
> +
> +	dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
> +							inst->cfg->label);
> +	if (inst->cfg->power_off) {
> +		spin_lock(&drv->lock);
> +		ret = inst->cfg->power_off(inst);
> +		spin_unlock(&drv->lock);
> +	}
> +	clk_disable_unprepare(inst->clk);
> +	clk_disable_unprepare(drv->clk);
> +	return ret;
> +}
> +
> +static struct phy_ops samsung_usb2_phy_ops = {
> +	.power_on	= samsung_usb2_phy_power_on,
> +	.power_off	= samsung_usb2_phy_power_off,
> +	.owner		= THIS_MODULE,
> +};
> +
> +static struct phy *samsung_usb2_phy_xlate(struct device *dev,
> +					struct of_phandle_args *args)
> +{
> +	struct samsung_usb2_phy_driver *drv;
> +
> +	drv = dev_get_drvdata(dev);
> +	if (!drv)
> +		return ERR_PTR(-EINVAL);
> +
> +	if (WARN_ON(args->args[0] >= drv->cfg->num_phys))
> +		return ERR_PTR(-ENODEV);
> +
> +	return drv->instances[args->args[0]].phy;
> +}
> +
> +static const struct of_device_id samsung_usb2_phy_of_match[] = {
> +#ifdef CONFIG_PHY_EXYNOS4210_USB2
> +	{
> +		.compatible = "samsung,exynos4210-usb2-phy",
> +		.data = &exynos4210_usb2_phy_config,
> +	},
> +#endif
> +#ifdef CONFIG_PHY_EXYNOS4X12_USB2
> +	{
> +		.compatible = "samsung,exynos4x12-usb2-phy",
> +		.data = &exynos4x12_usb2_phy_config,
> +	},
> +#endif
> +	{ },
> +};
> +
> +static int samsung_usb2_phy_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	const struct samsung_usb2_phy_config *cfg;
> +	struct clk *clk;
> +	struct device *dev = &pdev->dev;
> +	struct phy_provider *phy_provider;
> +	struct resource *mem;
> +	struct samsung_usb2_phy_driver *drv;
> +	int i;
> +
> +	if (!pdev->dev.of_node) {
> +		dev_err(dev, "This driver is required to be instantiated from device tree\n");
> +		return -EINVAL;
> +	}
> +
> +	match = of_match_node(samsung_usb2_phy_of_match, pdev->dev.of_node);
> +	if (!match) {
> +		dev_err(dev, "of_match_node() failed\n");
> +		return -EINVAL;
> +	}
> +	cfg = match->data;
> +
> +	drv = devm_kzalloc(dev, sizeof(struct samsung_usb2_phy_driver) +
> +		cfg->num_phys * sizeof(struct samsung_usb2_phy_instance),
> +								GFP_KERNEL);
> +	if (!drv)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(dev, drv);
> +	spin_lock_init(&drv->lock);
> +
> +	drv->cfg = cfg;
> +	drv->dev = dev;
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	drv->reg_phy = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(drv->reg_phy)) {
> +		dev_err(dev, "Failed to map register memory (phy)\n");
> +		return PTR_ERR(drv->reg_phy);
> +	}
> +
> +	drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
> +		"samsung,pmureg-phandle");
> +	if (IS_ERR(drv->reg_pmu)) {
> +		dev_err(dev, "Failed to map PMU registers (via syscon)\n");
> +		return PTR_ERR(drv->reg_pmu);
> +	}
> +
> +	if (drv->cfg->has_mode_switch) {
> +		drv->reg_sys = syscon_regmap_lookup_by_phandle(
> +				pdev->dev.of_node, "samsung,sysreg-phandle");
> +		if (IS_ERR(drv->reg_sys)) {
> +			dev_err(dev, "Failed to map system registers (via syscon)\n");
> +			return PTR_ERR(drv->reg_sys);
> +		}
> +	}
> +
> +	drv->clk = devm_clk_get(dev, "phy");
> +	if (IS_ERR(drv->clk)) {
> +		dev_err(dev, "Failed to get clock of phy controller\n");
> +		return PTR_ERR(drv->clk);
> +	}
> +
> +	for (i = 0; i < drv->cfg->num_phys; i++) {
> +		char *label = drv->cfg->phys[i].label;
> +		struct samsung_usb2_phy_instance *p = &drv->instances[i];
> +
> +		dev_dbg(dev, "Creating phy \"%s\"\n", label);
> +		p->phy = devm_phy_create(dev, &samsung_usb2_phy_ops, NULL);
> +		if (IS_ERR(p->phy)) {
> +			dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
> +						label);
> +			return PTR_ERR(p->phy);
> +		}
> +
> +		p->cfg = &drv->cfg->phys[i];
> +		p->drv = drv;
> +		phy_set_bus_width(p->phy, 8);
> +		phy_set_drvdata(p->phy, p);
> +
> +		clk = devm_clk_get(dev, p->cfg->label);
> +		if (IS_ERR(clk)) {
> +			dev_err(dev, "Failed to get clock of \"%s\" phy\n",
> +								p->cfg->label);
> +			return PTR_ERR(clk);
> +		}
> +		p->clk = clk;
> +	}
> +
> +	phy_provider = devm_of_phy_provider_register(dev,
> +							samsung_usb2_phy_xlate);
> +	if (IS_ERR(phy_provider)) {
> +		dev_err(drv->dev, "Failed to register phy provider\n");
> +		return PTR_ERR(phy_provider);
> +	}
> +
> +	return 0;
> +}
> +
> +static struct platform_driver samsung_usb2_phy_driver = {
> +	.probe	= samsung_usb2_phy_probe,
> +	.driver = {
> +		.of_match_table	= samsung_usb2_phy_of_match,
> +		.name		= "samsung-usb2-phy",
> +		.owner		= THIS_MODULE,
> +	}
> +};
> +
> +module_platform_driver(samsung_usb2_phy_driver);
> +MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver");
> +MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:samsung-usb2-phy");
> +
> diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h
> new file mode 100644
> index 0000000..868dd33
> --- /dev/null
> +++ b/drivers/phy/phy-samsung-usb2.h
> @@ -0,0 +1,67 @@
> +/*
> + * Samsung SoC USB 1.1/2.0 PHY driver
> + *
> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> + * Author: Kamil Debski <k.debski@samsung.com>
> + *
> + * 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 _PHY_EXYNOS_USB2_H
> +#define _PHY_EXYNOS_USB2_H
> +
> +#include <linux/clk.h>
> +#include <linux/phy/phy.h>
> +#include <linux/device.h>
> +#include <linux/regmap.h>
> +#include <linux/spinlock.h>
> +
> +#define KHZ 1000
> +#define MHZ (KHZ * KHZ)
> +
> +struct samsung_usb2_phy_driver;
> +struct samsung_usb2_phy_instance;
> +struct samsung_usb2_phy_config;
> +
> +struct samsung_usb2_phy_instance {
> +	const struct samsung_usb2_common_phy *cfg;
> +	struct clk *clk;
> +	struct phy *phy;
> +	struct samsung_usb2_phy_driver *drv;
> +	unsigned long rate;
> +	u32 clk_reg_val;
> +	bool enabled;
> +};
> +
> +struct samsung_usb2_phy_driver {
> +	const struct samsung_usb2_phy_config *cfg;
> +	struct clk *clk;
> +	struct device *dev;
> +	void __iomem *reg_phy;
> +	struct regmap *reg_pmu;
> +	struct regmap *reg_sys;
> +	spinlock_t lock;
> +	struct samsung_usb2_phy_instance instances[0];
> +};
> +
> +struct samsung_usb2_common_phy {
> +	int (*rate_to_clk)(unsigned long, u32 *);
> +	int (*power_on)(struct samsung_usb2_phy_instance *);
> +	int (*power_off)(struct samsung_usb2_phy_instance *);
> +	unsigned int id;
> +	char *label;
> +};
> +
> +
> +struct samsung_usb2_phy_config {
> +	const struct samsung_usb2_common_phy *phys;
> +	unsigned int num_phys;
> +	bool has_mode_switch;
> +};
> +
> +extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config;
> +extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config;
> +#endif
> +
>

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

* Re: [PATCH v6 5/8] phy: Add new Exynos USB PHY driver
@ 2014-03-03 14:27     ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 30+ messages in thread
From: Kishon Vijay Abraham I @ 2014-03-03 14:27 UTC (permalink / raw)
  To: Kamil Debski, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ,
	t.figa-Sze3O3UU22JBDgjK7y7TUQ, s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ,
	m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ,
	gautam.vivek-Sze3O3UU22JBDgjK7y7TUQ,
	mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w,
	yulgon.kim-Sze3O3UU22JBDgjK7y7TUQ,
	p.paneri-Sze3O3UU22JBDgjK7y7TUQ,
	av.tikhomirov-Sze3O3UU22JBDgjK7y7TUQ,
	jg1.han-Sze3O3UU22JBDgjK7y7TUQ, galak-sgV2jX0FEOL9JmXXK+q4OQ,
	matt.porter-QSEj5FYQhm4dnm+yROfE0A,
	tjakobi-o02PS0xoJP9W0yFyLvAVXMxlOr/tl8fh,
	stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz

Hi,

On Wednesday 29 January 2014 10:59 PM, Kamil Debski wrote:
> Add a new driver for the Exynos USB PHY. The new driver uses the generic
> PHY framework. The driver includes support for the Exynos 4x10 and 4x12
> SoC families.

Can the PHY part of this series be merged independently of the 
controller part?

Thanks
Kishon
>
> Signed-off-by: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> ---
>   .../devicetree/bindings/phy/samsung-phy.txt        |   55 ++++
>   Documentation/phy/samsung-usb2.txt                 |  135 ++++++++
>   drivers/phy/Kconfig                                |   29 ++
>   drivers/phy/Makefile                               |    3 +
>   drivers/phy/phy-exynos4210-usb2.c                  |  257 ++++++++++++++++
>   drivers/phy/phy-exynos4x12-usb2.c                  |  323 ++++++++++++++++++++
>   drivers/phy/phy-samsung-usb2.c                     |  227 ++++++++++++++
>   drivers/phy/phy-samsung-usb2.h                     |   67 ++++
>   8 files changed, 1096 insertions(+)
>   create mode 100644 Documentation/phy/samsung-usb2.txt
>   create mode 100644 drivers/phy/phy-exynos4210-usb2.c
>   create mode 100644 drivers/phy/phy-exynos4x12-usb2.c
>   create mode 100644 drivers/phy/phy-samsung-usb2.c
>   create mode 100644 drivers/phy/phy-samsung-usb2.h
>
> diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt
> index c0fccaa..6668c41 100644
> --- a/Documentation/devicetree/bindings/phy/samsung-phy.txt
> +++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt
> @@ -20,3 +20,58 @@ Required properties:
>   - compatible : should be "samsung,exynos5250-dp-video-phy";
>   - reg : offset and length of the Display Port PHY register set;
>   - #phy-cells : from the generic PHY bindings, must be 0;
> +
> +Samsung S5P/EXYNOS SoC series USB PHY
> +-------------------------------------------------
> +
> +Required properties:
> +- compatible : should be one of the listed compatibles:
> +	- "samsung,exynos4210-usb2-phy"
> +	- "samsung,exynos4x12-usb2-phy"
> +- reg : a list of registers used by phy driver
> +	- first and obligatory is the location of phy modules registers
> +- samsung,sysreg-phandle - handle to syscon used to control the system registers
> +- samsung,pmureg-phandle - handle to syscon used to control PMU registers
> +- #phy-cells : from the generic phy bindings, must be 1;
> +- clocks and clock-names:
> +	- the "phy" clocks is required by the phy module
> +	- next for each of the phys a clock has to be assigned, this clock
> +	  will be used to determine clocking frequency for the phys
> +	  (the labels are specified in the paragraph below)
> +
> +The first phandle argument in the PHY specifier identifies the PHY, its
> +meaning is compatible dependent. For the currently supported SoCs (Exynos 4210
> +and Exynos 4212) it is as follows:
> +  0 - USB device ("device"),
> +  1 - USB host ("host"),
> +  2 - HSIC0 ("hsic0"),
> +  3 - HSIC1 ("hsic1"),
> +
> +Exynos 4210 and Exynos 4212 use mode switching and require that mode switch
> +register is supplied.
> +
> +Example:
> +
> +For Exynos 4412 (compatible with Exynos 4212):
> +
> +usbphy: phy@125b0000 {
> +	compatible = "samsung,exynos4212-usb2-phy";
> +	reg = <0x125b0000 0x100 0x10020704 0x0c 0x1001021c 0x4>;
> +	clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
> +							<&clock 2>;
> +	clock-names = "phy", "device", "host", "hsic0", "hsic1";
> +	status = "okay";
> +	#phy-cells = <1>;
> +	samsung,sysreg-phandle = <&sys_reg>;
> +	samsung,pmureg-phandle = <&pmu_reg>;
> +};
> +
> +Then the PHY can be used in other nodes such as:
> +
> +phy-consumer@12340000 {
> +	phys = <&usbphy 2>;
> +	phy-names = "phy";
> +};
> +
> +Refer to DT bindings documentation of particular PHY consumer devices for more
> +information about required PHYs and the way of specification.
> diff --git a/Documentation/phy/samsung-usb2.txt b/Documentation/phy/samsung-usb2.txt
> new file mode 100644
> index 0000000..9f5826e
> --- /dev/null
> +++ b/Documentation/phy/samsung-usb2.txt
> @@ -0,0 +1,135 @@
> +.------------------------------------------------------------------------------+
> +|			Samsung USB 2.0 PHY adaptation layer		       |
> ++-----------------------------------------------------------------------------+'
> +
> +| 1. Description
> ++----------------
> +
> +The architecture of the USB 2.0 PHY module in Samsung SoCs is similar
> +among many SoCs. In spite of the similarities it proved difficult to
> +create a one driver that would fit all these PHY controllers. Often
> +the differences were minor and were found in particular bits of the
> +registers of the PHY. In some rare cases the order of register writes or
> +the PHY powering up process had to be altered. This adaptation layer is
> +a compromise between having separate drivers and having a single driver
> +with added support for many special cases.
> +
> +| 2. Files description
> ++----------------------
> +
> +- phy-samsung-usb2.c
> +   This is the main file of the adaptation layer. This file contains
> +   the probe function and provides two callbacks to the Generic PHY
> +   Framework. This two callbacks are used to power on and power off the
> +   phy. They carry out the common work that has to be done on all version
> +   of the PHY module. Depending on which SoC was chosen they execute SoC
> +   specific callbacks. The specific SoC version is selected by choosing
> +   the appropriate compatible string. In addition, this file contains
> +   struct of_device_id definitions for particular SoCs.
> +
> +- phy-samsung-usb2.h
> +   This is the include file. It declares the structures used by this
> +   driver. In addition it should contain extern declarations for
> +   structures that describe particular SoCs.
> +
> +| 3. Supporting SoCs
> ++--------------------
> +
> +To support a new SoC a new file should be added to the drivers/phy
> +directory. Each SoC's configuration is stored in an instance of the
> +struct samsung_usb2_phy_config.
> +
> +struct samsung_usb2_phy_config {
> +	const struct samsung_usb2_common_phy *phys;
> +	unsigned int num_phys;
> +	bool has_mode_switch;
> +};
> +
> +The num_phys is the number of phys handled by the driver. *phys is an
> +array that contains the configuration for each phy. The has_mode_switch
> +property is a boolean flag that determines whether the SoC has USB host
> +and device on a single pair of pins. If so, a special register has to
> +be modified to change the internal routing of these pins between a USB
> +device or host module.
> +
> +For example the configuration for Exynos 4210 is following:
> +
> +const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
> +	.num_phys		= EXYNOS4210_NUM_PHYS,
> +	.phys			= exynos4210_phys,
> +}
> +
> +The exynos4210_phys configuration array is as follows:
> +
> +static const struct samsung_usb2_common_phy exynos4210_phys[] = {
> +	{
> +		.label		= "device",
> +		.id		= EXYNOS4210_DEVICE,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "host",
> +		.id		= EXYNOS4210_HOST,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "hsic0",
> +		.id		= EXYNOS4210_HSIC0,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "hsic1",
> +		.id		= EXYNOS4210_HSIC1,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{},
> +};
> +
> +- int (*rate_to_clk)(unsigned long, u32 *)
> +	The rate_to_clk callback is to convert the rate of the clock
> +	used to clock the module to the value that should be written to
> +	the registers of the PHY module.
> +- int (*power_on)(struct samsung_usb2_phy_instance *);
> +- int (*power_off)(struct samsung_usb2_phy_instance *);
> +	These two callbacks are used to power on and power off the phy
> +	by modifying appropriate registers.
> +
> +Final change to the driver is adding appropriate compatible value to the
> +phy-samsung-usb2.c file. In case of Exynos 4210 the following lines were
> +added to the struct of_device_id samsung_usb2_phy_of_match[] array:
> +
> +#ifdef CONFIG_PHY_EXYNOS4210_USB2
> +	{
> +		.compatible = "samsung,exynos4210-usb2-phy",
> +		.data = &exynos4210_usb2_phy_config,
> +	},
> +#endif
> +
> +To add further flexibility to the driver the Kconfig file enables to
> +include support for selected SoCs in the compiled driver. The Kconfig
> +entry for Exynos 4210 is following:
> +
> +config PHY_EXYNOS4210_USB2
> +	bool "Support for Exynos 4210"
> +	depends on PHY_SAMSUNG_USB2
> +	depends on CPU_EXYNOS4210
> +	help
> +	  Enable USB PHY support for Exynos 4210. This option requires that
> +	  Samsung USB 2.0 PHY driver is enabled and means that support for this
> +	  particular SoC is compiled in the driver. In case of Exynos 4210 four
> +	  phys are available - device, host, HSCI0 and HSCI1.
> +
> +The newly created file that supports the new SoC has to be also added to the
> +Makefile. In case of Exynos 4210 the added line is following:
> +
> +obj-$(CONFIG_PHY_EXYNOS4210_USB2)       += phy-exynos4210-usb2.o
> +
> +After completing these steps the support for the new SoC should be ready.
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index b901c47..3691d24 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -63,4 +63,33 @@ config BCM_KONA_USB2_PHY
>   	help
>   	  Enable this to support the Broadcom Kona USB 2.0 PHY.
>
> +config PHY_SAMSUNG_USB2
> +	tristate "Samsung USB 2.0 PHY driver"
> +	select GENERIC_PHY
> +	select MFD_SYSCON
> +	help
> +	  Enable this to support the Samsung USB 2.0 PHY driver for Samsung
> +	  SoCs. This driver provides the interface for USB 2.0 PHY. Support for
> +	  particular SoCs has to be enabled in addition to this driver. Number
> +	  and type of supported phys depends on the SoC.
> +
> +config PHY_EXYNOS4210_USB2
> +	bool "Support for Exynos 4210"
> +	depends on PHY_SAMSUNG_USB2
> +	depends on CPU_EXYNOS4210
> +	help
> +	  Enable USB PHY support for Exynos 4210. This option requires that
> +	  Samsung USB 2.0 PHY driver is enabled and means that support for this
> +	  particular SoC is compiled in the driver. In case of Exynos 4210 four
> +	  phys are available - device, host, HSIC0 and HSIC1.
> +
> +config PHY_EXYNOS4X12_USB2
> +	bool "Support for Exynos 4x12"
> +	depends on PHY_SAMSUNG_USB2
> +	depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
> +	help
> +	  Enable USB PHY support for Exynos 4x12. This option requires that
> +	  Samsung USB 2.0 PHY driver is enabled and means that support for this
> +	  particular SoC is compiled in the driver. In case of Exynos 4x12 four
> +	  phys are available - device, host, HSIC0 and HSIC1.
>   endmenu
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index b57c253..69d0b3f2 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -9,3 +9,6 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)	+= phy-exynos-mipi-video.o
>   obj-$(CONFIG_PHY_MVEBU_SATA)		+= phy-mvebu-sata.o
>   obj-$(CONFIG_OMAP_USB2)			+= phy-omap-usb2.o
>   obj-$(CONFIG_TWL4030_USB)		+= phy-twl4030-usb.o
> +obj-$(CONFIG_PHY_SAMSUNG_USB2)		+= phy-samsung-usb2.o
> +obj-$(CONFIG_PHY_EXYNOS4210_USB2)	+= phy-exynos4210-usb2.o
> +obj-$(CONFIG_PHY_EXYNOS4X12_USB2)	+= phy-exynos4x12-usb2.o
> diff --git a/drivers/phy/phy-exynos4210-usb2.c b/drivers/phy/phy-exynos4210-usb2.c
> new file mode 100644
> index 0000000..8f86abf
> --- /dev/null
> +++ b/drivers/phy/phy-exynos4210-usb2.c
> @@ -0,0 +1,257 @@
> +/*
> + * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support
> + *
> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> + * Author: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@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.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/phy/phy.h>
> +#include <linux/regmap.h>
> +#include "phy-samsung-usb2.h"
> +
> +/* Exynos USB PHY registers */
> +
> +/* PHY power control */
> +#define EXYNOS_4210_UPHYPWR			0x0
> +
> +#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND	BIT(0)
> +#define EXYNOS_4210_UPHYPWR_PHY0_PWR		BIT(3)
> +#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR	BIT(4)
> +#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP		BIT(5)
> +#define EXYNOS_4210_UPHYPWR_PHY0	( \
> +	EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \
> +	EXYNOS_4210_UPHYPWR_PHY0_PWR | \
> +	EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \
> +	EXYNOS_4210_UPHYPWR_PHY0_SLEEP)
> +
> +#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND	BIT(6)
> +#define EXYNOS_4210_UPHYPWR_PHY1_PWR		BIT(7)
> +#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP		BIT(8)
> +#define EXYNOS_4210_UPHYPWR_PHY1 ( \
> +	EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \
> +	EXYNOS_4210_UPHYPWR_PHY1_PWR | \
> +	EXYNOS_4210_UPHYPWR_PHY1_SLEEP)
> +
> +#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND	BIT(9)
> +#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP		BIT(10)
> +#define EXYNOS_4210_UPHYPWR_HSIC0 ( \
> +	EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \
> +	EXYNOS_4210_UPHYPWR_HSIC0_SLEEP)
> +
> +#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND	BIT(11)
> +#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP		BIT(12)
> +#define EXYNOS_4210_UPHYPWR_HSIC1 ( \
> +	EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \
> +	EXYNOS_4210_UPHYPWR_HSIC1_SLEEP)
> +
> +/* PHY clock control */
> +#define EXYNOS_4210_UPHYCLK			0x4
> +
> +#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK	(0x3 << 0)
> +#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ	(0x0 << 0)
> +#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ	(0x3 << 0)
> +#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
> +
> +#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP	BIT(2)
> +#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON	BIT(4)
> +#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON	BIT(7)
> +
> +/* PHY reset control */
> +#define EXYNOS_4210_UPHYRST			0x8
> +
> +#define EXYNOS_4210_URSTCON_PHY0		BIT(0)
> +#define EXYNOS_4210_URSTCON_OTG_HLINK		BIT(1)
> +#define EXYNOS_4210_URSTCON_OTG_PHYLINK		BIT(2)
> +#define EXYNOS_4210_URSTCON_PHY1_ALL		BIT(3)
> +#define EXYNOS_4210_URSTCON_PHY1_P0		BIT(4)
> +#define EXYNOS_4210_URSTCON_PHY1_P1P2		BIT(5)
> +#define EXYNOS_4210_URSTCON_HOST_LINK_ALL	BIT(6)
> +#define EXYNOS_4210_URSTCON_HOST_LINK_P0	BIT(7)
> +#define EXYNOS_4210_URSTCON_HOST_LINK_P1	BIT(8)
> +#define EXYNOS_4210_URSTCON_HOST_LINK_P2	BIT(9)
> +
> +/* Isolation, configured in the power management unit */
> +#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET	0x704
> +#define EXYNOS_4210_USB_ISOL_DEVICE		BIT(0)
> +#define EXYNOS_4210_USB_ISOL_HOST_OFFSET	0x708
> +#define EXYNOS_4210_USB_ISOL_HOST		BIT(0)
> +
> +/* USBYPHY1 Floating prevention */
> +#define EXYNOS_4210_UPHY1CON			0x34
> +#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION	0x1
> +
> +/* Mode switching SUB Device <-> Host */
> +#define EXYNOS_4210_MODE_SWITCH_OFFSET		0x21c
> +#define EXYNOS_4210_MODE_SWITCH_MASK		1
> +#define EXYNOS_4210_MODE_SWITCH_DEVICE		0
> +#define EXYNOS_4210_MODE_SWITCH_HOST		1
> +
> +enum exynos4210_phy_id {
> +	EXYNOS4210_DEVICE,
> +	EXYNOS4210_HOST,
> +	EXYNOS4210_HSIC0,
> +	EXYNOS4210_HSIC1,
> +	EXYNOS4210_NUM_PHYS,
> +};
> +
> +/*
> + * exynos4210_rate_to_clk() converts the supplied clock rate to the value that
> + * can be written to the phy register.
> + */
> +static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg)
> +{
> +	switch (rate) {
> +	case 12 * MHZ:
> +		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
> +		break;
> +	case 24 * MHZ:
> +		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
> +		break;
> +	case 48 * MHZ:
> +		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on)
> +{
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +	u32 offset;
> +	u32 mask;
> +
> +	switch (inst->cfg->id) {
> +	case EXYNOS4210_DEVICE:
> +		offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET;
> +		mask = EXYNOS_4210_USB_ISOL_DEVICE;
> +		break;
> +	case EXYNOS4210_HOST:
> +		offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET;
> +		mask = EXYNOS_4210_USB_ISOL_HOST;
> +		break;
> +	default:
> +		return;
> +	};
> +
> +	regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
> +}
> +
> +static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
> +{
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +	u32 rstbits = 0;
> +	u32 phypwr = 0;
> +	u32 rst;
> +	u32 pwr;
> +
> +	switch (inst->cfg->id) {
> +	case EXYNOS4210_DEVICE:
> +		phypwr =	EXYNOS_4210_UPHYPWR_PHY0;
> +		rstbits =	EXYNOS_4210_URSTCON_PHY0;
> +		break;
> +	case EXYNOS4210_HOST:
> +		phypwr =	EXYNOS_4210_UPHYPWR_PHY1;
> +		rstbits =	EXYNOS_4210_URSTCON_PHY1_ALL |
> +				EXYNOS_4210_URSTCON_PHY1_P0 |
> +				EXYNOS_4210_URSTCON_PHY1_P1P2 |
> +				EXYNOS_4210_URSTCON_HOST_LINK_ALL |
> +				EXYNOS_4210_URSTCON_HOST_LINK_P0;
> +		writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON);
> +		break;
> +	case EXYNOS4210_HSIC0:
> +		phypwr =	EXYNOS_4210_UPHYPWR_HSIC0;
> +		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
> +				EXYNOS_4210_URSTCON_HOST_LINK_P1;
> +		break;
> +	case EXYNOS4210_HSIC1:
> +		phypwr =	EXYNOS_4210_UPHYPWR_HSIC1;
> +		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
> +				EXYNOS_4210_URSTCON_HOST_LINK_P2;
> +		break;
> +	};
> +
> +	if (on) {
> +		writel(inst->clk_reg_val, drv->reg_phy + EXYNOS_4210_UPHYCLK);
> +
> +		pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
> +		pwr &= ~phypwr;
> +		writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
> +
> +		rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST);
> +		rst |= rstbits;
> +		writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
> +		udelay(10);
> +		rst &= ~rstbits;
> +		writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
> +	} else {
> +		pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
> +		pwr |= phypwr;
> +		writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
> +	}
> +}
> +
> +static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst)
> +{
> +	/* Order of initialisation is important - first power then isolation */
> +	exynos4210_phy_pwr(inst, 1);
> +	exynos4210_isol(inst, 0);
> +
> +	return 0;
> +}
> +
> +static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst)
> +{
> +	exynos4210_isol(inst, 1);
> +	exynos4210_phy_pwr(inst, 0);
> +
> +	return 0;
> +}
> +
> +
> +static const struct samsung_usb2_common_phy exynos4210_phys[] = {
> +	{
> +		.label		= "device",
> +		.id		= EXYNOS4210_DEVICE,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "host",
> +		.id		= EXYNOS4210_HOST,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "hsic0",
> +		.id		= EXYNOS4210_HSIC0,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "hsic1",
> +		.id		= EXYNOS4210_HSIC1,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{},
> +};
> +
> +const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
> +	.num_phys		= EXYNOS4210_NUM_PHYS,
> +	.phys			= exynos4210_phys,
> +	.has_mode_switch	= 0,
> +};
> +
> diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c
> new file mode 100644
> index 0000000..ce39502
> --- /dev/null
> +++ b/drivers/phy/phy-exynos4x12-usb2.c
> @@ -0,0 +1,323 @@
> +/*
> + * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4x12 support
> + *
> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> + * Author: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@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.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/phy/phy.h>
> +#include <linux/regmap.h>
> +#include "phy-samsung-usb2.h"
> +
> +/* Exynos USB PHY registers */
> +
> +/* PHY power control */
> +#define EXYNOS_4x12_UPHYPWR			0x0
> +
> +#define EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND	BIT(0)
> +#define EXYNOS_4x12_UPHYPWR_PHY0_PWR		BIT(3)
> +#define EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR	BIT(4)
> +#define EXYNOS_4x12_UPHYPWR_PHY0_SLEEP		BIT(5)
> +#define EXYNOS_4x12_UPHYPWR_PHY0 ( \
> +	EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND | \
> +	EXYNOS_4x12_UPHYPWR_PHY0_PWR | \
> +	EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR | \
> +	EXYNOS_4x12_UPHYPWR_PHY0_SLEEP)
> +
> +#define EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND	BIT(6)
> +#define EXYNOS_4x12_UPHYPWR_PHY1_PWR		BIT(7)
> +#define EXYNOS_4x12_UPHYPWR_PHY1_SLEEP		BIT(8)
> +#define EXYNOS_4x12_UPHYPWR_PHY1 ( \
> +	EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND | \
> +	EXYNOS_4x12_UPHYPWR_PHY1_PWR | \
> +	EXYNOS_4x12_UPHYPWR_PHY1_SLEEP)
> +
> +#define EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND	BIT(9)
> +#define EXYNOS_4x12_UPHYPWR_HSIC0_PWR		BIT(10)
> +#define EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP		BIT(11)
> +#define EXYNOS_4x12_UPHYPWR_HSIC0 ( \
> +	EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND | \
> +	EXYNOS_4x12_UPHYPWR_HSIC0_PWR | \
> +	EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP)
> +
> +#define EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND	BIT(12)
> +#define EXYNOS_4x12_UPHYPWR_HSIC1_PWR		BIT(13)
> +#define EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP		BIT(14)
> +#define EXYNOS_4x12_UPHYPWR_HSIC1 ( \
> +	EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND | \
> +	EXYNOS_4x12_UPHYPWR_HSIC1_PWR | \
> +	EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP)
> +
> +/* PHY clock control */
> +#define EXYNOS_4x12_UPHYCLK			0x4
> +
> +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK	(0x7 << 0)
> +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6	(0x0 << 0)
> +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ	(0x1 << 0)
> +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
> +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2	(0x3 << 0)
> +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ	(0x4 << 0)
> +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ	(0x5 << 0)
> +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ	(0x7 << 0)
> +
> +#define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP	BIT(3)
> +#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON	BIT(4)
> +#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON	BIT(7)
> +
> +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK	(0x7f << 10)
> +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ	(0x24 << 10)
> +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ	(0x1c << 10)
> +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ	(0x1a << 10)
> +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2	(0x15 << 10)
> +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ	(0x14 << 10)
> +
> +/* PHY reset control */
> +#define EXYNOS_4x12_UPHYRST			0x8
> +
> +#define EXYNOS_4x12_URSTCON_PHY0		BIT(0)
> +#define EXYNOS_4x12_URSTCON_OTG_HLINK		BIT(1)
> +#define EXYNOS_4x12_URSTCON_OTG_PHYLINK		BIT(2)
> +#define EXYNOS_4x12_URSTCON_HOST_PHY		BIT(3)
> +#define EXYNOS_4x12_URSTCON_PHY1		BIT(4)
> +#define EXYNOS_4x12_URSTCON_HSIC0		BIT(5)
> +#define EXYNOS_4x12_URSTCON_HSIC1		BIT(6)
> +#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL	BIT(7)
> +#define EXYNOS_4x12_URSTCON_HOST_LINK_P0	BIT(8)
> +#define EXYNOS_4x12_URSTCON_HOST_LINK_P1	BIT(9)
> +#define EXYNOS_4x12_URSTCON_HOST_LINK_P2	BIT(10)
> +
> +/* Isolation, configured in the power management unit */
> +#define EXYNOS_4x12_USB_ISOL_OFFSET		0x704
> +#define EXYNOS_4x12_USB_ISOL_OTG		BIT(0)
> +#define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET	0x708
> +#define EXYNOS_4x12_USB_ISOL_HSIC0		BIT(0)
> +#define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET	0x70c
> +#define EXYNOS_4x12_USB_ISOL_HSIC1		BIT(0)
> +
> +/* Mode switching SUB Device <-> Host */
> +#define EXYNOS_4x12_MODE_SWITCH_OFFSET		0x21c
> +#define EXYNOS_4x12_MODE_SWITCH_MASK		1
> +#define EXYNOS_4x12_MODE_SWITCH_DEVICE		0
> +#define EXYNOS_4x12_MODE_SWITCH_HOST		1
> +
> +enum exynos4x12_phy_id {
> +	EXYNOS4x12_DEVICE,
> +	EXYNOS4x12_HOST,
> +	EXYNOS4x12_HSIC0,
> +	EXYNOS4x12_HSIC1,
> +	EXYNOS4x12_NUM_PHYS,
> +};
> +
> +/*
> + * exynos4x12_rate_to_clk() converts the supplied clock rate to the value that
> + * can be written to the phy register.
> + */
> +static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg)
> +{
> +	/* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */
> +
> +	switch (rate) {
> +	case 9600 * KHZ:
> +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6;
> +		break;
> +	case 10 * MHZ:
> +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ;
> +		break;
> +	case 12 * MHZ:
> +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ;
> +		break;
> +	case 19200 * KHZ:
> +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2;
> +		break;
> +	case 20 * MHZ:
> +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ;
> +		break;
> +	case 24 * MHZ:
> +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ;
> +		break;
> +	case 50 * MHZ:
> +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst, bool on)
> +{
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +	u32 offset;
> +	u32 mask;
> +
> +	switch (inst->cfg->id) {
> +	case EXYNOS4x12_DEVICE:
> +	case EXYNOS4x12_HOST:
> +		offset = EXYNOS_4x12_USB_ISOL_OFFSET;
> +		mask = EXYNOS_4x12_USB_ISOL_OTG;
> +		break;
> +	case EXYNOS4x12_HSIC0:
> +		offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET;
> +		mask = EXYNOS_4x12_USB_ISOL_HSIC0;
> +		break;
> +	case EXYNOS4x12_HSIC1:
> +		offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET;
> +		mask = EXYNOS_4x12_USB_ISOL_HSIC1;
> +		break;
> +	default:
> +		return;
> +	};
> +
> +	regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
> +}
> +
> +static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst)
> +{
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +	writel(inst->clk_reg_val, drv->reg_phy + EXYNOS_4x12_UPHYCLK);
> +}
> +
> +static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
> +{
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +	u32 rstbits = 0;
> +	u32 phypwr = 0;
> +	u32 rst;
> +	u32 pwr;
> +	u32 mode = 0;
> +	u32 switch_mode = 0;
> +
> +	switch (inst->cfg->id) {
> +	case EXYNOS4x12_DEVICE:
> +		phypwr =	EXYNOS_4x12_UPHYPWR_PHY0;
> +		rstbits =	EXYNOS_4x12_URSTCON_PHY0;
> +		mode =		EXYNOS_4x12_MODE_SWITCH_DEVICE;
> +		switch_mode =	1;
> +		break;
> +	case EXYNOS4x12_HOST:
> +		phypwr =	EXYNOS_4x12_UPHYPWR_PHY1;
> +		rstbits =	EXYNOS_4x12_URSTCON_HOST_PHY;
> +		mode =		EXYNOS_4x12_MODE_SWITCH_HOST;
> +		switch_mode =	1;
> +		break;
> +	case EXYNOS4x12_HSIC0:
> +		phypwr =	EXYNOS_4x12_UPHYPWR_HSIC0;
> +		rstbits =	EXYNOS_4x12_URSTCON_HSIC1 |
> +				EXYNOS_4x12_URSTCON_HOST_LINK_P0 |
> +				EXYNOS_4x12_URSTCON_HOST_PHY;
> +		break;
> +	case EXYNOS4x12_HSIC1:
> +		phypwr =	EXYNOS_4x12_UPHYPWR_HSIC1;
> +		rstbits =	EXYNOS_4x12_URSTCON_HSIC1 |
> +				EXYNOS_4x12_URSTCON_HOST_LINK_P1;
> +		break;
> +	};
> +
> +	if (on) {
> +		if (switch_mode)
> +			regmap_update_bits(drv->reg_sys,
> +			        EXYNOS_4x12_MODE_SWITCH_OFFSET,
> +			        EXYNOS_4x12_MODE_SWITCH_MASK,
> +				mode);
> +
> +		pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
> +		pwr &= ~phypwr;
> +		writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
> +
> +		rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST);
> +		rst |= rstbits;
> +		writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
> +		udelay(10);
> +		rst &= ~rstbits;
> +		writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
> +	} else {
> +		pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
> +		pwr |= phypwr;
> +		writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
> +	}
> +}
> +
> +static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst)
> +{
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +
> +	inst->enabled = 1;
> +	exynos4x12_setup_clk(inst);
> +	exynos4x12_phy_pwr(inst, 1);
> +	exynos4x12_isol(inst, 0);
> +
> +	/* Power on the device, as it is necessary for HSIC to work */
> +	if (inst->cfg->id == EXYNOS4x12_HSIC0) {
> +		struct samsung_usb2_phy_instance *device =
> +					&drv->instances[EXYNOS4x12_DEVICE];
> +		exynos4x12_phy_pwr(device, 1);
> +		exynos4x12_isol(device, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst)
> +{
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +	struct samsung_usb2_phy_instance *device =
> +					&drv->instances[EXYNOS4x12_DEVICE];
> +
> +	inst->enabled = 0;
> +	exynos4x12_isol(inst, 1);
> +	exynos4x12_phy_pwr(inst, 0);
> +
> +	if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) {
> +		exynos4x12_isol(device, 1);
> +		exynos4x12_phy_pwr(device, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +
> +static const struct samsung_usb2_common_phy exynos4x12_phys[] = {
> +	{
> +		.label		= "device",
> +		.id		= EXYNOS4x12_DEVICE,
> +		.rate_to_clk	= exynos4x12_rate_to_clk,
> +		.power_on	= exynos4x12_power_on,
> +		.power_off	= exynos4x12_power_off,
> +	},
> +	{
> +		.label		= "host",
> +		.id		= EXYNOS4x12_HOST,
> +		.rate_to_clk	= exynos4x12_rate_to_clk,
> +		.power_on	= exynos4x12_power_on,
> +		.power_off	= exynos4x12_power_off,
> +	},
> +	{
> +		.label		= "hsic0",
> +		.id		= EXYNOS4x12_HSIC0,
> +		.rate_to_clk	= exynos4x12_rate_to_clk,
> +		.power_on	= exynos4x12_power_on,
> +		.power_off	= exynos4x12_power_off,
> +	},
> +	{
> +		.label		= "hsic1",
> +		.id		= EXYNOS4x12_HSIC1,
> +		.rate_to_clk	= exynos4x12_rate_to_clk,
> +		.power_on	= exynos4x12_power_on,
> +		.power_off	= exynos4x12_power_off,
> +	},
> +	{},
> +};
> +
> +const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = {
> +	.num_phys		= EXYNOS4x12_NUM_PHYS,
> +	.phys			= exynos4x12_phys,
> +	.has_mode_switch	= 1,
> +};
> +
> diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c
> new file mode 100644
> index 0000000..c1b0fd5
> --- /dev/null
> +++ b/drivers/phy/phy-samsung-usb2.c
> @@ -0,0 +1,227 @@
> +/*
> + * Samsung SoC USB 1.1/2.0 PHY driver
> + *
> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> + * Author: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@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.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include "phy-samsung-usb2.h"
> +
> +static int samsung_usb2_phy_power_on(struct phy *phy)
> +{
> +	struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +	int ret;
> +
> +	dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n",
> +							inst->cfg->label);
> +	ret = clk_prepare_enable(drv->clk);
> +	if (ret)
> +		goto err_main_clk;
> +	ret = clk_prepare_enable(inst->clk);
> +	if (ret)
> +		goto err_instance_clk;
> +	inst->rate = clk_get_rate(inst->clk);
> +	if (inst->cfg->rate_to_clk) {
> +		ret = inst->cfg->rate_to_clk(inst->rate, &inst->clk_reg_val);
> +		if (ret)
> +			goto err_get_rate;
> +	}
> +	if (inst->cfg->power_on) {
> +		spin_lock(&drv->lock);
> +		ret = inst->cfg->power_on(inst);
> +		spin_unlock(&drv->lock);
> +	}
> +
> +	return 0;
> +
> +err_get_rate:
> +	clk_disable_unprepare(inst->clk);
> +err_instance_clk:
> +	clk_disable_unprepare(drv->clk);
> +err_main_clk:
> +	return ret;
> +}
> +
> +static int samsung_usb2_phy_power_off(struct phy *phy)
> +{
> +	struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
> +	struct samsung_usb2_phy_driver *drv = inst->drv;
> +	int ret = 0;
> +
> +	dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
> +							inst->cfg->label);
> +	if (inst->cfg->power_off) {
> +		spin_lock(&drv->lock);
> +		ret = inst->cfg->power_off(inst);
> +		spin_unlock(&drv->lock);
> +	}
> +	clk_disable_unprepare(inst->clk);
> +	clk_disable_unprepare(drv->clk);
> +	return ret;
> +}
> +
> +static struct phy_ops samsung_usb2_phy_ops = {
> +	.power_on	= samsung_usb2_phy_power_on,
> +	.power_off	= samsung_usb2_phy_power_off,
> +	.owner		= THIS_MODULE,
> +};
> +
> +static struct phy *samsung_usb2_phy_xlate(struct device *dev,
> +					struct of_phandle_args *args)
> +{
> +	struct samsung_usb2_phy_driver *drv;
> +
> +	drv = dev_get_drvdata(dev);
> +	if (!drv)
> +		return ERR_PTR(-EINVAL);
> +
> +	if (WARN_ON(args->args[0] >= drv->cfg->num_phys))
> +		return ERR_PTR(-ENODEV);
> +
> +	return drv->instances[args->args[0]].phy;
> +}
> +
> +static const struct of_device_id samsung_usb2_phy_of_match[] = {
> +#ifdef CONFIG_PHY_EXYNOS4210_USB2
> +	{
> +		.compatible = "samsung,exynos4210-usb2-phy",
> +		.data = &exynos4210_usb2_phy_config,
> +	},
> +#endif
> +#ifdef CONFIG_PHY_EXYNOS4X12_USB2
> +	{
> +		.compatible = "samsung,exynos4x12-usb2-phy",
> +		.data = &exynos4x12_usb2_phy_config,
> +	},
> +#endif
> +	{ },
> +};
> +
> +static int samsung_usb2_phy_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	const struct samsung_usb2_phy_config *cfg;
> +	struct clk *clk;
> +	struct device *dev = &pdev->dev;
> +	struct phy_provider *phy_provider;
> +	struct resource *mem;
> +	struct samsung_usb2_phy_driver *drv;
> +	int i;
> +
> +	if (!pdev->dev.of_node) {
> +		dev_err(dev, "This driver is required to be instantiated from device tree\n");
> +		return -EINVAL;
> +	}
> +
> +	match = of_match_node(samsung_usb2_phy_of_match, pdev->dev.of_node);
> +	if (!match) {
> +		dev_err(dev, "of_match_node() failed\n");
> +		return -EINVAL;
> +	}
> +	cfg = match->data;
> +
> +	drv = devm_kzalloc(dev, sizeof(struct samsung_usb2_phy_driver) +
> +		cfg->num_phys * sizeof(struct samsung_usb2_phy_instance),
> +								GFP_KERNEL);
> +	if (!drv)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(dev, drv);
> +	spin_lock_init(&drv->lock);
> +
> +	drv->cfg = cfg;
> +	drv->dev = dev;
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	drv->reg_phy = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(drv->reg_phy)) {
> +		dev_err(dev, "Failed to map register memory (phy)\n");
> +		return PTR_ERR(drv->reg_phy);
> +	}
> +
> +	drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
> +		"samsung,pmureg-phandle");
> +	if (IS_ERR(drv->reg_pmu)) {
> +		dev_err(dev, "Failed to map PMU registers (via syscon)\n");
> +		return PTR_ERR(drv->reg_pmu);
> +	}
> +
> +	if (drv->cfg->has_mode_switch) {
> +		drv->reg_sys = syscon_regmap_lookup_by_phandle(
> +				pdev->dev.of_node, "samsung,sysreg-phandle");
> +		if (IS_ERR(drv->reg_sys)) {
> +			dev_err(dev, "Failed to map system registers (via syscon)\n");
> +			return PTR_ERR(drv->reg_sys);
> +		}
> +	}
> +
> +	drv->clk = devm_clk_get(dev, "phy");
> +	if (IS_ERR(drv->clk)) {
> +		dev_err(dev, "Failed to get clock of phy controller\n");
> +		return PTR_ERR(drv->clk);
> +	}
> +
> +	for (i = 0; i < drv->cfg->num_phys; i++) {
> +		char *label = drv->cfg->phys[i].label;
> +		struct samsung_usb2_phy_instance *p = &drv->instances[i];
> +
> +		dev_dbg(dev, "Creating phy \"%s\"\n", label);
> +		p->phy = devm_phy_create(dev, &samsung_usb2_phy_ops, NULL);
> +		if (IS_ERR(p->phy)) {
> +			dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
> +						label);
> +			return PTR_ERR(p->phy);
> +		}
> +
> +		p->cfg = &drv->cfg->phys[i];
> +		p->drv = drv;
> +		phy_set_bus_width(p->phy, 8);
> +		phy_set_drvdata(p->phy, p);
> +
> +		clk = devm_clk_get(dev, p->cfg->label);
> +		if (IS_ERR(clk)) {
> +			dev_err(dev, "Failed to get clock of \"%s\" phy\n",
> +								p->cfg->label);
> +			return PTR_ERR(clk);
> +		}
> +		p->clk = clk;
> +	}
> +
> +	phy_provider = devm_of_phy_provider_register(dev,
> +							samsung_usb2_phy_xlate);
> +	if (IS_ERR(phy_provider)) {
> +		dev_err(drv->dev, "Failed to register phy provider\n");
> +		return PTR_ERR(phy_provider);
> +	}
> +
> +	return 0;
> +}
> +
> +static struct platform_driver samsung_usb2_phy_driver = {
> +	.probe	= samsung_usb2_phy_probe,
> +	.driver = {
> +		.of_match_table	= samsung_usb2_phy_of_match,
> +		.name		= "samsung-usb2-phy",
> +		.owner		= THIS_MODULE,
> +	}
> +};
> +
> +module_platform_driver(samsung_usb2_phy_driver);
> +MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver");
> +MODULE_AUTHOR("Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:samsung-usb2-phy");
> +
> diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h
> new file mode 100644
> index 0000000..868dd33
> --- /dev/null
> +++ b/drivers/phy/phy-samsung-usb2.h
> @@ -0,0 +1,67 @@
> +/*
> + * Samsung SoC USB 1.1/2.0 PHY driver
> + *
> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> + * Author: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@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 _PHY_EXYNOS_USB2_H
> +#define _PHY_EXYNOS_USB2_H
> +
> +#include <linux/clk.h>
> +#include <linux/phy/phy.h>
> +#include <linux/device.h>
> +#include <linux/regmap.h>
> +#include <linux/spinlock.h>
> +
> +#define KHZ 1000
> +#define MHZ (KHZ * KHZ)
> +
> +struct samsung_usb2_phy_driver;
> +struct samsung_usb2_phy_instance;
> +struct samsung_usb2_phy_config;
> +
> +struct samsung_usb2_phy_instance {
> +	const struct samsung_usb2_common_phy *cfg;
> +	struct clk *clk;
> +	struct phy *phy;
> +	struct samsung_usb2_phy_driver *drv;
> +	unsigned long rate;
> +	u32 clk_reg_val;
> +	bool enabled;
> +};
> +
> +struct samsung_usb2_phy_driver {
> +	const struct samsung_usb2_phy_config *cfg;
> +	struct clk *clk;
> +	struct device *dev;
> +	void __iomem *reg_phy;
> +	struct regmap *reg_pmu;
> +	struct regmap *reg_sys;
> +	spinlock_t lock;
> +	struct samsung_usb2_phy_instance instances[0];
> +};
> +
> +struct samsung_usb2_common_phy {
> +	int (*rate_to_clk)(unsigned long, u32 *);
> +	int (*power_on)(struct samsung_usb2_phy_instance *);
> +	int (*power_off)(struct samsung_usb2_phy_instance *);
> +	unsigned int id;
> +	char *label;
> +};
> +
> +
> +struct samsung_usb2_phy_config {
> +	const struct samsung_usb2_common_phy *phys;
> +	unsigned int num_phys;
> +	bool has_mode_switch;
> +};
> +
> +extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config;
> +extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config;
> +#endif
> +
>
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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	[flat|nested] 30+ messages in thread

* Re: [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework
  2014-02-05 17:30         ` Olof Johansson
@ 2014-03-03 14:37           ` Tomasz Figa
  -1 siblings, 0 replies; 30+ messages in thread
From: Tomasz Figa @ 2014-03-03 14:37 UTC (permalink / raw)
  To: Olof Johansson, Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	Kyungmin Park, Kishon Vijay Abraham I, Tomasz Figa,
	Sylwester Nawrocki, Marek Szyprowski, Vivek Gautam,
	Mateusz Krawczuk, yulgon.kim, Praveen Paneri, av.tikhomirov,
	Jingoo Han, Kumar Gala, Matt Porter, tjakobi, Alan Stern



On 05.02.2014 18:30, Olof Johansson wrote:
> On Wed, Feb 5, 2014 at 7:57 AM, Kamil Debski <k.debski@samsung.com> wrote:
>> Hi Olof,
>>
>> Thank you for your review.
>>
>>> From: Olof Johansson [mailto:olof@lixom.net]
>>> Sent: Wednesday, January 29, 2014 9:55 PM
>>>
>>> Hi,
>>>
>>> On Wed, Jan 29, 2014 at 9:29 AM, Kamil Debski <k.debski@samsung.com>
>>> wrote:
>>>> Change the phy provider used from the old one using the USB phy
>>>> framework to a new one using the Generic phy framework.
>>>>
>>>> Signed-off-by: Kamil Debski <k.debski@samsung.com>
>>>> ---
>>>>   .../devicetree/bindings/usb/exynos-usb.txt         |   13 +++
>>>>   drivers/usb/host/ehci-exynos.c                     |   97
>>> +++++++++++++-------
>>>>   2 files changed, 76 insertions(+), 34 deletions(-)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt
>>>> b/Documentation/devicetree/bindings/usb/exynos-usb.txt
>>>> index d967ba1..25e199a 100644
>>>> --- a/Documentation/devicetree/bindings/usb/exynos-usb.txt
>>>> +++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt
>>>> @@ -12,6 +12,10 @@ Required properties:
>>>>    - interrupts: interrupt number to the cpu.
>>>>    - clocks: from common clock binding: handle to usb clock.
>>>>    - clock-names: from common clock binding: Shall be "usbhost".
>>>> +  - port: if in the SoC there are EHCI phys, they should be listed
>>> here.
>>>> +One phy per port. Each port should have its reg entry with a
>>>> +consecutive number. Also it should contain phys and phy-names
>>> entries
>>>> +specifying the phy used by the port.
>>>>
>>>>   Optional properties:
>>>>    - samsung,vbus-gpio:  if present, specifies the GPIO that @@ -27,6
>>>> +31,15 @@ Example:
>>>>
>>>>                  clocks = <&clock 285>;
>>>>                  clock-names = "usbhost";
>>>> +
>>>> +               #address-cells = <1>;
>>>> +               #size-cells = <0>;
>>>> +               port@0 {
>>>> +                   reg = <0>;
>>>> +                   phys = <&usb2phy 1>;
>>>> +                   phy-names = "host";
>>>> +                   status = "disabled";
>>>> +               };
>>>>          };
>>>>
>>>>   OHCI
>>>
>>> [...]
>>>
>>>> @@ -102,14 +132,26 @@ static int exynos_ehci_probe(struct
>>> platform_device *pdev)
>>>>                                          "samsung,exynos5440-ehci"))
>>>>                  goto skip_phy;
>>>>
>>>> -       phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
>>>> -       if (IS_ERR(phy)) {
>>>> -               usb_put_hcd(hcd);
>>>> -               dev_warn(&pdev->dev, "no platform data or transceiver
>>> defined\n");
>>>> -               return -EPROBE_DEFER;
>>>> -       } else {
>>>> -               exynos_ehci->phy = phy;
>>>> -               exynos_ehci->otg = phy->otg;
>>>> +       for_each_available_child_of_node(pdev->dev.of_node, child) {
>>>> +               err = of_property_read_u32(child, "reg",
>>> &phy_number);
>>>> +               if (err) {
>>>> +                       dev_err(&pdev->dev, "Failed to parse device
>>> tree\n");
>>>> +                       of_node_put(child);
>>>> +                       return err;
>>>> +               }
>>>> +               if (phy_number >= PHY_NUMBER) {
>>>> +                       dev_err(&pdev->dev, "Failed to parse device
>>> tree - number out of range\n");
>>>> +                       of_node_put(child);
>>>> +                       return -EINVAL;
>>>> +               }
>>>> +               phy = devm_of_phy_get(&pdev->dev, child, 0);
>>>> +               of_node_put(child);
>>>> +               if (IS_ERR(phy)) {
>>>> +                       dev_err(&pdev->dev, "Failed to get phy number
>>> %d",
>>>> +
>>> phy_number);
>>>> +                       return PTR_ERR(phy);
>>>> +               }
>>>> +               exynos_ehci->phy[phy_number] = phy;
>>>
>>> this looks like it is now breaking older device trees, where ports
>>> might not be described. Since device tree interfaces need to be
>>> backwards compatible, you still need to handle the old case of not
>>> having ports described.
>>>
>>> There are two ways of doing this:
>>>
>>> 1. Fall back to the old behavior if there are no ports 2. Use a new
>>> compatible value for the new model with port subnodes, and if the old
>>> compatible value is used, then fall back to the old behavior.
>>>
>>> I'm guessing (1) might be easiest since you can check for the presence
>>> of #address-cells to tell if this is just an old style node, or if it's
>>> a new-style node without any ports below it.
>>
>> The ultimate goal is to remove the old phy driver. Unfortunately
>> this has to be synced with the new USB3 phy driver by Vivek Gautam. I think
>> he
>> is also close to completion. What about this case? In the end the old driver
>> will be removed and no longer be supported. Having backward compatibility in
>> mind, it is possible to have the old and the new phy driver together in one
>> kernel release. But do we want to have two drivers doing the same thing at
>> the same time?
>
> It is mostly irrelevant if there is a new driver or not -- the old
> device tree has to keep working. In this case it would mean that the
> new driver needs to work with older device trees as well, or people
> will see functionality regressing.
>
> The device tree is a description of the hardware, not an extension of
> the driver.

The problem with this case is that when the original driver was added 
there was no way to bind PHY providers and consumers together.

Basically there was no generic PHY subsystem. Instead the hacky USB PHY 
subsystem was used with hardcoded PHY IDs (types) in both PHY driver and 
user drivers. In DT you only had nodes for the PHY controller and 
consumer IPs (e.g. EHCI, HSOTG), but no relation between them.

This was broken, merged without proper review and I don't think we 
should be supporting this, along with a lot of completely messed up 
bindings we were supposed to fix after a lot of discussion that happened 
last year, but I haven't seen much movement in this direction, instead 
ending with such FUBDs being stable and a need to maintain support for 
them...

Best regards,
Tomasz

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

* Re: [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework
@ 2014-03-03 14:37           ` Tomasz Figa
  0 siblings, 0 replies; 30+ messages in thread
From: Tomasz Figa @ 2014-03-03 14:37 UTC (permalink / raw)
  To: Olof Johansson, Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	Kyungmin Park, Kishon Vijay Abraham I, Tomasz Figa,
	Sylwester Nawrocki, Marek Szyprowski, Vivek Gautam,
	Mateusz Krawczuk, yulgon.kim, Praveen Paneri, av.tikhomirov,
	Jingoo Han, Kumar Gala, Matt Porter, tjakobi, Alan Stern



On 05.02.2014 18:30, Olof Johansson wrote:
> On Wed, Feb 5, 2014 at 7:57 AM, Kamil Debski <k.debski@samsung.com> wrote:
>> Hi Olof,
>>
>> Thank you for your review.
>>
>>> From: Olof Johansson [mailto:olof@lixom.net]
>>> Sent: Wednesday, January 29, 2014 9:55 PM
>>>
>>> Hi,
>>>
>>> On Wed, Jan 29, 2014 at 9:29 AM, Kamil Debski <k.debski@samsung.com>
>>> wrote:
>>>> Change the phy provider used from the old one using the USB phy
>>>> framework to a new one using the Generic phy framework.
>>>>
>>>> Signed-off-by: Kamil Debski <k.debski@samsung.com>
>>>> ---
>>>>   .../devicetree/bindings/usb/exynos-usb.txt         |   13 +++
>>>>   drivers/usb/host/ehci-exynos.c                     |   97
>>> +++++++++++++-------
>>>>   2 files changed, 76 insertions(+), 34 deletions(-)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt
>>>> b/Documentation/devicetree/bindings/usb/exynos-usb.txt
>>>> index d967ba1..25e199a 100644
>>>> --- a/Documentation/devicetree/bindings/usb/exynos-usb.txt
>>>> +++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt
>>>> @@ -12,6 +12,10 @@ Required properties:
>>>>    - interrupts: interrupt number to the cpu.
>>>>    - clocks: from common clock binding: handle to usb clock.
>>>>    - clock-names: from common clock binding: Shall be "usbhost".
>>>> +  - port: if in the SoC there are EHCI phys, they should be listed
>>> here.
>>>> +One phy per port. Each port should have its reg entry with a
>>>> +consecutive number. Also it should contain phys and phy-names
>>> entries
>>>> +specifying the phy used by the port.
>>>>
>>>>   Optional properties:
>>>>    - samsung,vbus-gpio:  if present, specifies the GPIO that @@ -27,6
>>>> +31,15 @@ Example:
>>>>
>>>>                  clocks = <&clock 285>;
>>>>                  clock-names = "usbhost";
>>>> +
>>>> +               #address-cells = <1>;
>>>> +               #size-cells = <0>;
>>>> +               port@0 {
>>>> +                   reg = <0>;
>>>> +                   phys = <&usb2phy 1>;
>>>> +                   phy-names = "host";
>>>> +                   status = "disabled";
>>>> +               };
>>>>          };
>>>>
>>>>   OHCI
>>>
>>> [...]
>>>
>>>> @@ -102,14 +132,26 @@ static int exynos_ehci_probe(struct
>>> platform_device *pdev)
>>>>                                          "samsung,exynos5440-ehci"))
>>>>                  goto skip_phy;
>>>>
>>>> -       phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
>>>> -       if (IS_ERR(phy)) {
>>>> -               usb_put_hcd(hcd);
>>>> -               dev_warn(&pdev->dev, "no platform data or transceiver
>>> defined\n");
>>>> -               return -EPROBE_DEFER;
>>>> -       } else {
>>>> -               exynos_ehci->phy = phy;
>>>> -               exynos_ehci->otg = phy->otg;
>>>> +       for_each_available_child_of_node(pdev->dev.of_node, child) {
>>>> +               err = of_property_read_u32(child, "reg",
>>> &phy_number);
>>>> +               if (err) {
>>>> +                       dev_err(&pdev->dev, "Failed to parse device
>>> tree\n");
>>>> +                       of_node_put(child);
>>>> +                       return err;
>>>> +               }
>>>> +               if (phy_number >= PHY_NUMBER) {
>>>> +                       dev_err(&pdev->dev, "Failed to parse device
>>> tree - number out of range\n");
>>>> +                       of_node_put(child);
>>>> +                       return -EINVAL;
>>>> +               }
>>>> +               phy = devm_of_phy_get(&pdev->dev, child, 0);
>>>> +               of_node_put(child);
>>>> +               if (IS_ERR(phy)) {
>>>> +                       dev_err(&pdev->dev, "Failed to get phy number
>>> %d",
>>>> +
>>> phy_number);
>>>> +                       return PTR_ERR(phy);
>>>> +               }
>>>> +               exynos_ehci->phy[phy_number] = phy;
>>>
>>> this looks like it is now breaking older device trees, where ports
>>> might not be described. Since device tree interfaces need to be
>>> backwards compatible, you still need to handle the old case of not
>>> having ports described.
>>>
>>> There are two ways of doing this:
>>>
>>> 1. Fall back to the old behavior if there are no ports 2. Use a new
>>> compatible value for the new model with port subnodes, and if the old
>>> compatible value is used, then fall back to the old behavior.
>>>
>>> I'm guessing (1) might be easiest since you can check for the presence
>>> of #address-cells to tell if this is just an old style node, or if it's
>>> a new-style node without any ports below it.
>>
>> The ultimate goal is to remove the old phy driver. Unfortunately
>> this has to be synced with the new USB3 phy driver by Vivek Gautam. I think
>> he
>> is also close to completion. What about this case? In the end the old driver
>> will be removed and no longer be supported. Having backward compatibility in
>> mind, it is possible to have the old and the new phy driver together in one
>> kernel release. But do we want to have two drivers doing the same thing at
>> the same time?
>
> It is mostly irrelevant if there is a new driver or not -- the old
> device tree has to keep working. In this case it would mean that the
> new driver needs to work with older device trees as well, or people
> will see functionality regressing.
>
> The device tree is a description of the hardware, not an extension of
> the driver.

The problem with this case is that when the original driver was added 
there was no way to bind PHY providers and consumers together.

Basically there was no generic PHY subsystem. Instead the hacky USB PHY 
subsystem was used with hardcoded PHY IDs (types) in both PHY driver and 
user drivers. In DT you only had nodes for the PHY controller and 
consumer IPs (e.g. EHCI, HSOTG), but no relation between them.

This was broken, merged without proper review and I don't think we 
should be supporting this, along with a lot of completely messed up 
bindings we were supposed to fix after a lot of discussion that happened 
last year, but I haven't seen much movement in this direction, instead 
ending with such FUBDs being stable and a need to maintain support for 
them...

Best regards,
Tomasz

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

* RE: [PATCH v6 5/8] phy: Add new Exynos USB PHY driver
@ 2014-03-03 15:07       ` Kamil Debski
  0 siblings, 0 replies; 30+ messages in thread
From: Kamil Debski @ 2014-03-03 15:07 UTC (permalink / raw)
  To: 'Kishon Vijay Abraham I',
	linux-kernel, linux-samsung-soc, linux-usb, devicetree
  Cc: kyungmin.park, Tomasz Figa, Sylwester Nawrocki, Marek Szyprowski,
	gautam.vivek, mat.krawczuk, yulgon.kim, p.paneri, av.tikhomirov,
	jg1.han, galak, matt.porter, tjakobi, stern

Hi Kishon,

> From: Kishon Vijay Abraham I [mailto:kishon@ti.com]
> Sent: Monday, March 03, 2014 3:28 PM
> 
> Hi,
> 
> On Wednesday 29 January 2014 10:59 PM, Kamil Debski wrote:
> > Add a new driver for the Exynos USB PHY. The new driver uses the
> > generic PHY framework. The driver includes support for the Exynos
> 4x10
> > and 4x12 SoC families.
> 
> Can the PHY part of this series be merged independently of the
> controller part?

I see no problems with merging the PHY part independently. However,
I would like to post an updated version of these patches. I am working
on them now and should send them tomorrow.

Best wishes,
-- 
Kamil Debski
Samsung R&D Institute Poland

> >
> > Signed-off-by: Kamil Debski <k.debski@samsung.com>
> > ---
> >   .../devicetree/bindings/phy/samsung-phy.txt        |   55 ++++
> >   Documentation/phy/samsung-usb2.txt                 |  135 ++++++++
> >   drivers/phy/Kconfig                                |   29 ++
> >   drivers/phy/Makefile                               |    3 +
> >   drivers/phy/phy-exynos4210-usb2.c                  |  257
> ++++++++++++++++
> >   drivers/phy/phy-exynos4x12-usb2.c                  |  323
> ++++++++++++++++++++
> >   drivers/phy/phy-samsung-usb2.c                     |  227
> ++++++++++++++
> >   drivers/phy/phy-samsung-usb2.h                     |   67 ++++
> >   8 files changed, 1096 insertions(+)
> >   create mode 100644 Documentation/phy/samsung-usb2.txt
> >   create mode 100644 drivers/phy/phy-exynos4210-usb2.c
> >   create mode 100644 drivers/phy/phy-exynos4x12-usb2.c
> >   create mode 100644 drivers/phy/phy-samsung-usb2.c
> >   create mode 100644 drivers/phy/phy-samsung-usb2.h
> >
> > diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt
> > b/Documentation/devicetree/bindings/phy/samsung-phy.txt
> > index c0fccaa..6668c41 100644
> > --- a/Documentation/devicetree/bindings/phy/samsung-phy.txt
> > +++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt
> > @@ -20,3 +20,58 @@ Required properties:
> >   - compatible : should be "samsung,exynos5250-dp-video-phy";
> >   - reg : offset and length of the Display Port PHY register set;
> >   - #phy-cells : from the generic PHY bindings, must be 0;
> > +
> > +Samsung S5P/EXYNOS SoC series USB PHY
> > +-------------------------------------------------
> > +
> > +Required properties:
> > +- compatible : should be one of the listed compatibles:
> > +	- "samsung,exynos4210-usb2-phy"
> > +	- "samsung,exynos4x12-usb2-phy"
> > +- reg : a list of registers used by phy driver
> > +	- first and obligatory is the location of phy modules registers
> > +- samsung,sysreg-phandle - handle to syscon used to control the
> > +system registers
> > +- samsung,pmureg-phandle - handle to syscon used to control PMU
> > +registers
> > +- #phy-cells : from the generic phy bindings, must be 1;
> > +- clocks and clock-names:
> > +	- the "phy" clocks is required by the phy module
> > +	- next for each of the phys a clock has to be assigned, this
> clock
> > +	  will be used to determine clocking frequency for the phys
> > +	  (the labels are specified in the paragraph below)
> > +
> > +The first phandle argument in the PHY specifier identifies the PHY,
> > +its meaning is compatible dependent. For the currently supported
> SoCs
> > +(Exynos 4210 and Exynos 4212) it is as follows:
> > +  0 - USB device ("device"),
> > +  1 - USB host ("host"),
> > +  2 - HSIC0 ("hsic0"),
> > +  3 - HSIC1 ("hsic1"),
> > +
> > +Exynos 4210 and Exynos 4212 use mode switching and require that mode
> > +switch register is supplied.
> > +
> > +Example:
> > +
> > +For Exynos 4412 (compatible with Exynos 4212):
> > +
> > +usbphy: phy@125b0000 {
> > +	compatible = "samsung,exynos4212-usb2-phy";
> > +	reg = <0x125b0000 0x100 0x10020704 0x0c 0x1001021c 0x4>;
> > +	clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
> > +							<&clock 2>;
> > +	clock-names = "phy", "device", "host", "hsic0", "hsic1";
> > +	status = "okay";
> > +	#phy-cells = <1>;
> > +	samsung,sysreg-phandle = <&sys_reg>;
> > +	samsung,pmureg-phandle = <&pmu_reg>; };
> > +
> > +Then the PHY can be used in other nodes such as:
> > +
> > +phy-consumer@12340000 {
> > +	phys = <&usbphy 2>;
> > +	phy-names = "phy";
> > +};
> > +
> > +Refer to DT bindings documentation of particular PHY consumer
> devices
> > +for more information about required PHYs and the way of
> specification.
> > diff --git a/Documentation/phy/samsung-usb2.txt
> > b/Documentation/phy/samsung-usb2.txt
> > new file mode 100644
> > index 0000000..9f5826e
> > --- /dev/null
> > +++ b/Documentation/phy/samsung-usb2.txt
> > @@ -0,0 +1,135 @@
> > +.-------------------------------------------------------------------
> -
> > +----------+
> > +|			Samsung USB 2.0 PHY adaptation layer
> |
> > ++-------------------------------------------------------------------
> ----------+'
> > +
> > +| 1. Description
> > ++----------------
> > +
> > +The architecture of the USB 2.0 PHY module in Samsung SoCs is
> similar
> > +among many SoCs. In spite of the similarities it proved difficult to
> > +create a one driver that would fit all these PHY controllers. Often
> > +the differences were minor and were found in particular bits of the
> > +registers of the PHY. In some rare cases the order of register
> writes
> > +or the PHY powering up process had to be altered. This adaptation
> > +layer is a compromise between having separate drivers and having a
> > +single driver with added support for many special cases.
> > +
> > +| 2. Files description
> > ++----------------------
> > +
> > +- phy-samsung-usb2.c
> > +   This is the main file of the adaptation layer. This file contains
> > +   the probe function and provides two callbacks to the Generic PHY
> > +   Framework. This two callbacks are used to power on and power off
> the
> > +   phy. They carry out the common work that has to be done on all
> version
> > +   of the PHY module. Depending on which SoC was chosen they execute
> SoC
> > +   specific callbacks. The specific SoC version is selected by
> choosing
> > +   the appropriate compatible string. In addition, this file
> contains
> > +   struct of_device_id definitions for particular SoCs.
> > +
> > +- phy-samsung-usb2.h
> > +   This is the include file. It declares the structures used by this
> > +   driver. In addition it should contain extern declarations for
> > +   structures that describe particular SoCs.
> > +
> > +| 3. Supporting SoCs
> > ++--------------------
> > +
> > +To support a new SoC a new file should be added to the drivers/phy
> > +directory. Each SoC's configuration is stored in an instance of the
> > +struct samsung_usb2_phy_config.
> > +
> > +struct samsung_usb2_phy_config {
> > +	const struct samsung_usb2_common_phy *phys;
> > +	unsigned int num_phys;
> > +	bool has_mode_switch;
> > +};
> > +
> > +The num_phys is the number of phys handled by the driver. *phys is
> an
> > +array that contains the configuration for each phy. The
> > +has_mode_switch property is a boolean flag that determines whether
> > +the SoC has USB host and device on a single pair of pins. If so, a
> > +special register has to be modified to change the internal routing
> of
> > +these pins between a USB device or host module.
> > +
> > +For example the configuration for Exynos 4210 is following:
> > +
> > +const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
> > +	.num_phys		= EXYNOS4210_NUM_PHYS,
> > +	.phys			= exynos4210_phys,
> > +}
> > +
> > +The exynos4210_phys configuration array is as follows:
> > +
> > +static const struct samsung_usb2_common_phy exynos4210_phys[] = {
> > +	{
> > +		.label		= "device",
> > +		.id		= EXYNOS4210_DEVICE,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "host",
> > +		.id		= EXYNOS4210_HOST,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic0",
> > +		.id		= EXYNOS4210_HSIC0,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic1",
> > +		.id		= EXYNOS4210_HSIC1,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{},
> > +};
> > +
> > +- int (*rate_to_clk)(unsigned long, u32 *)
> > +	The rate_to_clk callback is to convert the rate of the clock
> > +	used to clock the module to the value that should be written to
> > +	the registers of the PHY module.
> > +- int (*power_on)(struct samsung_usb2_phy_instance *);
> > +- int (*power_off)(struct samsung_usb2_phy_instance *);
> > +	These two callbacks are used to power on and power off the phy
> > +	by modifying appropriate registers.
> > +
> > +Final change to the driver is adding appropriate compatible value to
> > +the phy-samsung-usb2.c file. In case of Exynos 4210 the following
> > +lines were added to the struct of_device_id
> samsung_usb2_phy_of_match[] array:
> > +
> > +#ifdef CONFIG_PHY_EXYNOS4210_USB2
> > +	{
> > +		.compatible = "samsung,exynos4210-usb2-phy",
> > +		.data = &exynos4210_usb2_phy_config,
> > +	},
> > +#endif
> > +
> > +To add further flexibility to the driver the Kconfig file enables to
> > +include support for selected SoCs in the compiled driver. The
> Kconfig
> > +entry for Exynos 4210 is following:
> > +
> > +config PHY_EXYNOS4210_USB2
> > +	bool "Support for Exynos 4210"
> > +	depends on PHY_SAMSUNG_USB2
> > +	depends on CPU_EXYNOS4210
> > +	help
> > +	  Enable USB PHY support for Exynos 4210. This option requires
> that
> > +	  Samsung USB 2.0 PHY driver is enabled and means that support
> for this
> > +	  particular SoC is compiled in the driver. In case of Exynos
> 4210 four
> > +	  phys are available - device, host, HSCI0 and HSCI1.
> > +
> > +The newly created file that supports the new SoC has to be also
> added
> > +to the Makefile. In case of Exynos 4210 the added line is following:
> > +
> > +obj-$(CONFIG_PHY_EXYNOS4210_USB2)       += phy-exynos4210-usb2.o
> > +
> > +After completing these steps the support for the new SoC should be
> ready.
> > diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index
> > b901c47..3691d24 100644
> > --- a/drivers/phy/Kconfig
> > +++ b/drivers/phy/Kconfig
> > @@ -63,4 +63,33 @@ config BCM_KONA_USB2_PHY
> >   	help
> >   	  Enable this to support the Broadcom Kona USB 2.0 PHY.
> >
> > +config PHY_SAMSUNG_USB2
> > +	tristate "Samsung USB 2.0 PHY driver"
> > +	select GENERIC_PHY
> > +	select MFD_SYSCON
> > +	help
> > +	  Enable this to support the Samsung USB 2.0 PHY driver for
> Samsung
> > +	  SoCs. This driver provides the interface for USB 2.0 PHY.
> Support for
> > +	  particular SoCs has to be enabled in addition to this driver.
> Number
> > +	  and type of supported phys depends on the SoC.
> > +
> > +config PHY_EXYNOS4210_USB2
> > +	bool "Support for Exynos 4210"
> > +	depends on PHY_SAMSUNG_USB2
> > +	depends on CPU_EXYNOS4210
> > +	help
> > +	  Enable USB PHY support for Exynos 4210. This option requires
> that
> > +	  Samsung USB 2.0 PHY driver is enabled and means that support
> for this
> > +	  particular SoC is compiled in the driver. In case of Exynos
> 4210 four
> > +	  phys are available - device, host, HSIC0 and HSIC1.
> > +
> > +config PHY_EXYNOS4X12_USB2
> > +	bool "Support for Exynos 4x12"
> > +	depends on PHY_SAMSUNG_USB2
> > +	depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
> > +	help
> > +	  Enable USB PHY support for Exynos 4x12. This option requires
> that
> > +	  Samsung USB 2.0 PHY driver is enabled and means that support
> for this
> > +	  particular SoC is compiled in the driver. In case of Exynos
> 4x12 four
> > +	  phys are available - device, host, HSIC0 and HSIC1.
> >   endmenu
> > diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index
> > b57c253..69d0b3f2 100644
> > --- a/drivers/phy/Makefile
> > +++ b/drivers/phy/Makefile
> > @@ -9,3 +9,6 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)	+= phy-
> exynos-mipi-video.o
> >   obj-$(CONFIG_PHY_MVEBU_SATA)		+= phy-mvebu-sata.o
> >   obj-$(CONFIG_OMAP_USB2)			+= phy-omap-usb2.o
> >   obj-$(CONFIG_TWL4030_USB)		+= phy-twl4030-usb.o
> > +obj-$(CONFIG_PHY_SAMSUNG_USB2)		+= phy-samsung-usb2.o
> > +obj-$(CONFIG_PHY_EXYNOS4210_USB2)	+= phy-exynos4210-usb2.o
> > +obj-$(CONFIG_PHY_EXYNOS4X12_USB2)	+= phy-exynos4x12-usb2.o
> > diff --git a/drivers/phy/phy-exynos4210-usb2.c
> > b/drivers/phy/phy-exynos4210-usb2.c
> > new file mode 100644
> > index 0000000..8f86abf
> > --- /dev/null
> > +++ b/drivers/phy/phy-exynos4210-usb2.c
> > @@ -0,0 +1,257 @@
> > +/*
> > + * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support
> > + *
> > + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> > + * Author: Kamil Debski <k.debski@samsung.com>
> > + *
> > + * 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.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/regmap.h>
> > +#include "phy-samsung-usb2.h"
> > +
> > +/* Exynos USB PHY registers */
> > +
> > +/* PHY power control */
> > +#define EXYNOS_4210_UPHYPWR			0x0
> > +
> > +#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND	BIT(0)
> > +#define EXYNOS_4210_UPHYPWR_PHY0_PWR		BIT(3)
> > +#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR	BIT(4)
> > +#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP		BIT(5)
> > +#define EXYNOS_4210_UPHYPWR_PHY0	( \
> > +	EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \
> > +	EXYNOS_4210_UPHYPWR_PHY0_PWR | \
> > +	EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \
> > +	EXYNOS_4210_UPHYPWR_PHY0_SLEEP)
> > +
> > +#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND	BIT(6)
> > +#define EXYNOS_4210_UPHYPWR_PHY1_PWR		BIT(7)
> > +#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP		BIT(8)
> > +#define EXYNOS_4210_UPHYPWR_PHY1 ( \
> > +	EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \
> > +	EXYNOS_4210_UPHYPWR_PHY1_PWR | \
> > +	EXYNOS_4210_UPHYPWR_PHY1_SLEEP)
> > +
> > +#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND	BIT(9)
> > +#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP		BIT(10)
> > +#define EXYNOS_4210_UPHYPWR_HSIC0 ( \
> > +	EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \
> > +	EXYNOS_4210_UPHYPWR_HSIC0_SLEEP)
> > +
> > +#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND	BIT(11)
> > +#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP		BIT(12)
> > +#define EXYNOS_4210_UPHYPWR_HSIC1 ( \
> > +	EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \
> > +	EXYNOS_4210_UPHYPWR_HSIC1_SLEEP)
> > +
> > +/* PHY clock control */
> > +#define EXYNOS_4210_UPHYCLK			0x4
> > +
> > +#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK	(0x3 << 0)
> > +#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ	(0x0 << 0)
> > +#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ	(0x3 << 0)
> > +#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
> > +
> > +#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP	BIT(2)
> > +#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON	BIT(4)
> > +#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON	BIT(7)
> > +
> > +/* PHY reset control */
> > +#define EXYNOS_4210_UPHYRST			0x8
> > +
> > +#define EXYNOS_4210_URSTCON_PHY0		BIT(0)
> > +#define EXYNOS_4210_URSTCON_OTG_HLINK		BIT(1)
> > +#define EXYNOS_4210_URSTCON_OTG_PHYLINK		BIT(2)
> > +#define EXYNOS_4210_URSTCON_PHY1_ALL		BIT(3)
> > +#define EXYNOS_4210_URSTCON_PHY1_P0		BIT(4)
> > +#define EXYNOS_4210_URSTCON_PHY1_P1P2		BIT(5)
> > +#define EXYNOS_4210_URSTCON_HOST_LINK_ALL	BIT(6)
> > +#define EXYNOS_4210_URSTCON_HOST_LINK_P0	BIT(7)
> > +#define EXYNOS_4210_URSTCON_HOST_LINK_P1	BIT(8)
> > +#define EXYNOS_4210_URSTCON_HOST_LINK_P2	BIT(9)
> > +
> > +/* Isolation, configured in the power management unit */
> > +#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET	0x704
> > +#define EXYNOS_4210_USB_ISOL_DEVICE		BIT(0)
> > +#define EXYNOS_4210_USB_ISOL_HOST_OFFSET	0x708
> > +#define EXYNOS_4210_USB_ISOL_HOST		BIT(0)
> > +
> > +/* USBYPHY1 Floating prevention */
> > +#define EXYNOS_4210_UPHY1CON			0x34
> > +#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION	0x1
> > +
> > +/* Mode switching SUB Device <-> Host */
> > +#define EXYNOS_4210_MODE_SWITCH_OFFSET		0x21c
> > +#define EXYNOS_4210_MODE_SWITCH_MASK		1
> > +#define EXYNOS_4210_MODE_SWITCH_DEVICE		0
> > +#define EXYNOS_4210_MODE_SWITCH_HOST		1
> > +
> > +enum exynos4210_phy_id {
> > +	EXYNOS4210_DEVICE,
> > +	EXYNOS4210_HOST,
> > +	EXYNOS4210_HSIC0,
> > +	EXYNOS4210_HSIC1,
> > +	EXYNOS4210_NUM_PHYS,
> > +};
> > +
> > +/*
> > + * exynos4210_rate_to_clk() converts the supplied clock rate to the
> > +value that
> > + * can be written to the phy register.
> > + */
> > +static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg) {
> > +	switch (rate) {
> > +	case 12 * MHZ:
> > +		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
> > +		break;
> > +	case 24 * MHZ:
> > +		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
> > +		break;
> > +	case 48 * MHZ:
> > +		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void exynos4210_isol(struct samsung_usb2_phy_instance *inst,
> > +bool on) {
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +	u32 offset;
> > +	u32 mask;
> > +
> > +	switch (inst->cfg->id) {
> > +	case EXYNOS4210_DEVICE:
> > +		offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET;
> > +		mask = EXYNOS_4210_USB_ISOL_DEVICE;
> > +		break;
> > +	case EXYNOS4210_HOST:
> > +		offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET;
> > +		mask = EXYNOS_4210_USB_ISOL_HOST;
> > +		break;
> > +	default:
> > +		return;
> > +	};
> > +
> > +	regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); }
> > +
> > +static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance
> > +*inst, bool on) {
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +	u32 rstbits = 0;
> > +	u32 phypwr = 0;
> > +	u32 rst;
> > +	u32 pwr;
> > +
> > +	switch (inst->cfg->id) {
> > +	case EXYNOS4210_DEVICE:
> > +		phypwr =	EXYNOS_4210_UPHYPWR_PHY0;
> > +		rstbits =	EXYNOS_4210_URSTCON_PHY0;
> > +		break;
> > +	case EXYNOS4210_HOST:
> > +		phypwr =	EXYNOS_4210_UPHYPWR_PHY1;
> > +		rstbits =	EXYNOS_4210_URSTCON_PHY1_ALL |
> > +				EXYNOS_4210_URSTCON_PHY1_P0 |
> > +				EXYNOS_4210_URSTCON_PHY1_P1P2 |
> > +				EXYNOS_4210_URSTCON_HOST_LINK_ALL |
> > +				EXYNOS_4210_URSTCON_HOST_LINK_P0;
> > +		writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON);
> > +		break;
> > +	case EXYNOS4210_HSIC0:
> > +		phypwr =	EXYNOS_4210_UPHYPWR_HSIC0;
> > +		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
> > +				EXYNOS_4210_URSTCON_HOST_LINK_P1;
> > +		break;
> > +	case EXYNOS4210_HSIC1:
> > +		phypwr =	EXYNOS_4210_UPHYPWR_HSIC1;
> > +		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
> > +				EXYNOS_4210_URSTCON_HOST_LINK_P2;
> > +		break;
> > +	};
> > +
> > +	if (on) {
> > +		writel(inst->clk_reg_val, drv->reg_phy +
> EXYNOS_4210_UPHYCLK);
> > +
> > +		pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
> > +		pwr &= ~phypwr;
> > +		writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
> > +
> > +		rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST);
> > +		rst |= rstbits;
> > +		writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
> > +		udelay(10);
> > +		rst &= ~rstbits;
> > +		writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
> > +	} else {
> > +		pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
> > +		pwr |= phypwr;
> > +		writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
> > +	}
> > +}
> > +
> > +static int exynos4210_power_on(struct samsung_usb2_phy_instance
> > +*inst) {
> > +	/* Order of initialisation is important - first power then
> isolation */
> > +	exynos4210_phy_pwr(inst, 1);
> > +	exynos4210_isol(inst, 0);
> > +
> > +	return 0;
> > +}
> > +
> > +static int exynos4210_power_off(struct samsung_usb2_phy_instance
> > +*inst) {
> > +	exynos4210_isol(inst, 1);
> > +	exynos4210_phy_pwr(inst, 0);
> > +
> > +	return 0;
> > +}
> > +
> > +
> > +static const struct samsung_usb2_common_phy exynos4210_phys[] = {
> > +	{
> > +		.label		= "device",
> > +		.id		= EXYNOS4210_DEVICE,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "host",
> > +		.id		= EXYNOS4210_HOST,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic0",
> > +		.id		= EXYNOS4210_HSIC0,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic1",
> > +		.id		= EXYNOS4210_HSIC1,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{},
> > +};
> > +
> > +const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
> > +	.num_phys		= EXYNOS4210_NUM_PHYS,
> > +	.phys			= exynos4210_phys,
> > +	.has_mode_switch	= 0,
> > +};
> > +
> > diff --git a/drivers/phy/phy-exynos4x12-usb2.c
> > b/drivers/phy/phy-exynos4x12-usb2.c
> > new file mode 100644
> > index 0000000..ce39502
> > --- /dev/null
> > +++ b/drivers/phy/phy-exynos4x12-usb2.c
> > @@ -0,0 +1,323 @@
> > +/*
> > + * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4x12 support
> > + *
> > + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> > + * Author: Kamil Debski <k.debski@samsung.com>
> > + *
> > + * 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.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/regmap.h>
> > +#include "phy-samsung-usb2.h"
> > +
> > +/* Exynos USB PHY registers */
> > +
> > +/* PHY power control */
> > +#define EXYNOS_4x12_UPHYPWR			0x0
> > +
> > +#define EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND	BIT(0)
> > +#define EXYNOS_4x12_UPHYPWR_PHY0_PWR		BIT(3)
> > +#define EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR	BIT(4)
> > +#define EXYNOS_4x12_UPHYPWR_PHY0_SLEEP		BIT(5)
> > +#define EXYNOS_4x12_UPHYPWR_PHY0 ( \
> > +	EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND | \
> > +	EXYNOS_4x12_UPHYPWR_PHY0_PWR | \
> > +	EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR | \
> > +	EXYNOS_4x12_UPHYPWR_PHY0_SLEEP)
> > +
> > +#define EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND	BIT(6)
> > +#define EXYNOS_4x12_UPHYPWR_PHY1_PWR		BIT(7)
> > +#define EXYNOS_4x12_UPHYPWR_PHY1_SLEEP		BIT(8)
> > +#define EXYNOS_4x12_UPHYPWR_PHY1 ( \
> > +	EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND | \
> > +	EXYNOS_4x12_UPHYPWR_PHY1_PWR | \
> > +	EXYNOS_4x12_UPHYPWR_PHY1_SLEEP)
> > +
> > +#define EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND	BIT(9)
> > +#define EXYNOS_4x12_UPHYPWR_HSIC0_PWR		BIT(10)
> > +#define EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP		BIT(11)
> > +#define EXYNOS_4x12_UPHYPWR_HSIC0 ( \
> > +	EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND | \
> > +	EXYNOS_4x12_UPHYPWR_HSIC0_PWR | \
> > +	EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP)
> > +
> > +#define EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND	BIT(12)
> > +#define EXYNOS_4x12_UPHYPWR_HSIC1_PWR		BIT(13)
> > +#define EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP		BIT(14)
> > +#define EXYNOS_4x12_UPHYPWR_HSIC1 ( \
> > +	EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND | \
> > +	EXYNOS_4x12_UPHYPWR_HSIC1_PWR | \
> > +	EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP)
> > +
> > +/* PHY clock control */
> > +#define EXYNOS_4x12_UPHYCLK			0x4
> > +
> > +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK	(0x7 << 0)
> > +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6	(0x0 << 0)
> > +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ	(0x1 << 0)
> > +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
> > +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2	(0x3 << 0)
> > +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ	(0x4 << 0)
> > +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ	(0x5 << 0)
> > +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ	(0x7 << 0)
> > +
> > +#define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP	BIT(3)
> > +#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON	BIT(4)
> > +#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON	BIT(7)
> > +
> > +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK	(0x7f << 10)
> > +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ	(0x24 << 10)
> > +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ	(0x1c << 10)
> > +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ	(0x1a << 10)
> > +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2	(0x15 << 10)
> > +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ	(0x14 << 10)
> > +
> > +/* PHY reset control */
> > +#define EXYNOS_4x12_UPHYRST			0x8
> > +
> > +#define EXYNOS_4x12_URSTCON_PHY0		BIT(0)
> > +#define EXYNOS_4x12_URSTCON_OTG_HLINK		BIT(1)
> > +#define EXYNOS_4x12_URSTCON_OTG_PHYLINK		BIT(2)
> > +#define EXYNOS_4x12_URSTCON_HOST_PHY		BIT(3)
> > +#define EXYNOS_4x12_URSTCON_PHY1		BIT(4)
> > +#define EXYNOS_4x12_URSTCON_HSIC0		BIT(5)
> > +#define EXYNOS_4x12_URSTCON_HSIC1		BIT(6)
> > +#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL	BIT(7)
> > +#define EXYNOS_4x12_URSTCON_HOST_LINK_P0	BIT(8)
> > +#define EXYNOS_4x12_URSTCON_HOST_LINK_P1	BIT(9)
> > +#define EXYNOS_4x12_URSTCON_HOST_LINK_P2	BIT(10)
> > +
> > +/* Isolation, configured in the power management unit */
> > +#define EXYNOS_4x12_USB_ISOL_OFFSET		0x704
> > +#define EXYNOS_4x12_USB_ISOL_OTG		BIT(0)
> > +#define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET	0x708
> > +#define EXYNOS_4x12_USB_ISOL_HSIC0		BIT(0)
> > +#define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET	0x70c
> > +#define EXYNOS_4x12_USB_ISOL_HSIC1		BIT(0)
> > +
> > +/* Mode switching SUB Device <-> Host */
> > +#define EXYNOS_4x12_MODE_SWITCH_OFFSET		0x21c
> > +#define EXYNOS_4x12_MODE_SWITCH_MASK		1
> > +#define EXYNOS_4x12_MODE_SWITCH_DEVICE		0
> > +#define EXYNOS_4x12_MODE_SWITCH_HOST		1
> > +
> > +enum exynos4x12_phy_id {
> > +	EXYNOS4x12_DEVICE,
> > +	EXYNOS4x12_HOST,
> > +	EXYNOS4x12_HSIC0,
> > +	EXYNOS4x12_HSIC1,
> > +	EXYNOS4x12_NUM_PHYS,
> > +};
> > +
> > +/*
> > + * exynos4x12_rate_to_clk() converts the supplied clock rate to the
> > +value that
> > + * can be written to the phy register.
> > + */
> > +static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg) {
> > +	/* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */
> > +
> > +	switch (rate) {
> > +	case 9600 * KHZ:
> > +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6;
> > +		break;
> > +	case 10 * MHZ:
> > +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ;
> > +		break;
> > +	case 12 * MHZ:
> > +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ;
> > +		break;
> > +	case 19200 * KHZ:
> > +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2;
> > +		break;
> > +	case 20 * MHZ:
> > +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ;
> > +		break;
> > +	case 24 * MHZ:
> > +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ;
> > +		break;
> > +	case 50 * MHZ:
> > +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ;
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst,
> > +bool on) {
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +	u32 offset;
> > +	u32 mask;
> > +
> > +	switch (inst->cfg->id) {
> > +	case EXYNOS4x12_DEVICE:
> > +	case EXYNOS4x12_HOST:
> > +		offset = EXYNOS_4x12_USB_ISOL_OFFSET;
> > +		mask = EXYNOS_4x12_USB_ISOL_OTG;
> > +		break;
> > +	case EXYNOS4x12_HSIC0:
> > +		offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET;
> > +		mask = EXYNOS_4x12_USB_ISOL_HSIC0;
> > +		break;
> > +	case EXYNOS4x12_HSIC1:
> > +		offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET;
> > +		mask = EXYNOS_4x12_USB_ISOL_HSIC1;
> > +		break;
> > +	default:
> > +		return;
> > +	};
> > +
> > +	regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); }
> > +
> > +static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance
> > +*inst) {
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +	writel(inst->clk_reg_val, drv->reg_phy + EXYNOS_4x12_UPHYCLK); }
> > +
> > +static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance
> > +*inst, bool on) {
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +	u32 rstbits = 0;
> > +	u32 phypwr = 0;
> > +	u32 rst;
> > +	u32 pwr;
> > +	u32 mode = 0;
> > +	u32 switch_mode = 0;
> > +
> > +	switch (inst->cfg->id) {
> > +	case EXYNOS4x12_DEVICE:
> > +		phypwr =	EXYNOS_4x12_UPHYPWR_PHY0;
> > +		rstbits =	EXYNOS_4x12_URSTCON_PHY0;
> > +		mode =		EXYNOS_4x12_MODE_SWITCH_DEVICE;
> > +		switch_mode =	1;
> > +		break;
> > +	case EXYNOS4x12_HOST:
> > +		phypwr =	EXYNOS_4x12_UPHYPWR_PHY1;
> > +		rstbits =	EXYNOS_4x12_URSTCON_HOST_PHY;
> > +		mode =		EXYNOS_4x12_MODE_SWITCH_HOST;
> > +		switch_mode =	1;
> > +		break;
> > +	case EXYNOS4x12_HSIC0:
> > +		phypwr =	EXYNOS_4x12_UPHYPWR_HSIC0;
> > +		rstbits =	EXYNOS_4x12_URSTCON_HSIC1 |
> > +				EXYNOS_4x12_URSTCON_HOST_LINK_P0 |
> > +				EXYNOS_4x12_URSTCON_HOST_PHY;
> > +		break;
> > +	case EXYNOS4x12_HSIC1:
> > +		phypwr =	EXYNOS_4x12_UPHYPWR_HSIC1;
> > +		rstbits =	EXYNOS_4x12_URSTCON_HSIC1 |
> > +				EXYNOS_4x12_URSTCON_HOST_LINK_P1;
> > +		break;
> > +	};
> > +
> > +	if (on) {
> > +		if (switch_mode)
> > +			regmap_update_bits(drv->reg_sys,
> > +			        EXYNOS_4x12_MODE_SWITCH_OFFSET,
> > +			        EXYNOS_4x12_MODE_SWITCH_MASK,
> > +				mode);
> > +
> > +		pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
> > +		pwr &= ~phypwr;
> > +		writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
> > +
> > +		rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST);
> > +		rst |= rstbits;
> > +		writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
> > +		udelay(10);
> > +		rst &= ~rstbits;
> > +		writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
> > +	} else {
> > +		pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
> > +		pwr |= phypwr;
> > +		writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
> > +	}
> > +}
> > +
> > +static int exynos4x12_power_on(struct samsung_usb2_phy_instance
> > +*inst) {
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +
> > +	inst->enabled = 1;
> > +	exynos4x12_setup_clk(inst);
> > +	exynos4x12_phy_pwr(inst, 1);
> > +	exynos4x12_isol(inst, 0);
> > +
> > +	/* Power on the device, as it is necessary for HSIC to work */
> > +	if (inst->cfg->id == EXYNOS4x12_HSIC0) {
> > +		struct samsung_usb2_phy_instance *device =
> > +					&drv->instances[EXYNOS4x12_DEVICE];
> > +		exynos4x12_phy_pwr(device, 1);
> > +		exynos4x12_isol(device, 0);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int exynos4x12_power_off(struct samsung_usb2_phy_instance
> > +*inst) {
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +	struct samsung_usb2_phy_instance *device =
> > +					&drv->instances[EXYNOS4x12_DEVICE];
> > +
> > +	inst->enabled = 0;
> > +	exynos4x12_isol(inst, 1);
> > +	exynos4x12_phy_pwr(inst, 0);
> > +
> > +	if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) {
> > +		exynos4x12_isol(device, 1);
> > +		exynos4x12_phy_pwr(device, 0);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +
> > +static const struct samsung_usb2_common_phy exynos4x12_phys[] = {
> > +	{
> > +		.label		= "device",
> > +		.id		= EXYNOS4x12_DEVICE,
> > +		.rate_to_clk	= exynos4x12_rate_to_clk,
> > +		.power_on	= exynos4x12_power_on,
> > +		.power_off	= exynos4x12_power_off,
> > +	},
> > +	{
> > +		.label		= "host",
> > +		.id		= EXYNOS4x12_HOST,
> > +		.rate_to_clk	= exynos4x12_rate_to_clk,
> > +		.power_on	= exynos4x12_power_on,
> > +		.power_off	= exynos4x12_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic0",
> > +		.id		= EXYNOS4x12_HSIC0,
> > +		.rate_to_clk	= exynos4x12_rate_to_clk,
> > +		.power_on	= exynos4x12_power_on,
> > +		.power_off	= exynos4x12_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic1",
> > +		.id		= EXYNOS4x12_HSIC1,
> > +		.rate_to_clk	= exynos4x12_rate_to_clk,
> > +		.power_on	= exynos4x12_power_on,
> > +		.power_off	= exynos4x12_power_off,
> > +	},
> > +	{},
> > +};
> > +
> > +const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = {
> > +	.num_phys		= EXYNOS4x12_NUM_PHYS,
> > +	.phys			= exynos4x12_phys,
> > +	.has_mode_switch	= 1,
> > +};
> > +
> > diff --git a/drivers/phy/phy-samsung-usb2.c
> > b/drivers/phy/phy-samsung-usb2.c new file mode 100644 index
> > 0000000..c1b0fd5
> > --- /dev/null
> > +++ b/drivers/phy/phy-samsung-usb2.c
> > @@ -0,0 +1,227 @@
> > +/*
> > + * Samsung SoC USB 1.1/2.0 PHY driver
> > + *
> > + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> > + * Author: Kamil Debski <k.debski@samsung.com>
> > + *
> > + * 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.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/spinlock.h>
> > +#include "phy-samsung-usb2.h"
> > +
> > +static int samsung_usb2_phy_power_on(struct phy *phy) {
> > +	struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +	int ret;
> > +
> > +	dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n",
> > +							inst->cfg->label);
> > +	ret = clk_prepare_enable(drv->clk);
> > +	if (ret)
> > +		goto err_main_clk;
> > +	ret = clk_prepare_enable(inst->clk);
> > +	if (ret)
> > +		goto err_instance_clk;
> > +	inst->rate = clk_get_rate(inst->clk);
> > +	if (inst->cfg->rate_to_clk) {
> > +		ret = inst->cfg->rate_to_clk(inst->rate, &inst-
> >clk_reg_val);
> > +		if (ret)
> > +			goto err_get_rate;
> > +	}
> > +	if (inst->cfg->power_on) {
> > +		spin_lock(&drv->lock);
> > +		ret = inst->cfg->power_on(inst);
> > +		spin_unlock(&drv->lock);
> > +	}
> > +
> > +	return 0;
> > +
> > +err_get_rate:
> > +	clk_disable_unprepare(inst->clk);
> > +err_instance_clk:
> > +	clk_disable_unprepare(drv->clk);
> > +err_main_clk:
> > +	return ret;
> > +}
> > +
> > +static int samsung_usb2_phy_power_off(struct phy *phy) {
> > +	struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +	int ret = 0;
> > +
> > +	dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
> > +							inst->cfg->label);
> > +	if (inst->cfg->power_off) {
> > +		spin_lock(&drv->lock);
> > +		ret = inst->cfg->power_off(inst);
> > +		spin_unlock(&drv->lock);
> > +	}
> > +	clk_disable_unprepare(inst->clk);
> > +	clk_disable_unprepare(drv->clk);
> > +	return ret;
> > +}
> > +
> > +static struct phy_ops samsung_usb2_phy_ops = {
> > +	.power_on	= samsung_usb2_phy_power_on,
> > +	.power_off	= samsung_usb2_phy_power_off,
> > +	.owner		= THIS_MODULE,
> > +};
> > +
> > +static struct phy *samsung_usb2_phy_xlate(struct device *dev,
> > +					struct of_phandle_args *args)
> > +{
> > +	struct samsung_usb2_phy_driver *drv;
> > +
> > +	drv = dev_get_drvdata(dev);
> > +	if (!drv)
> > +		return ERR_PTR(-EINVAL);
> > +
> > +	if (WARN_ON(args->args[0] >= drv->cfg->num_phys))
> > +		return ERR_PTR(-ENODEV);
> > +
> > +	return drv->instances[args->args[0]].phy;
> > +}
> > +
> > +static const struct of_device_id samsung_usb2_phy_of_match[] = {
> > +#ifdef CONFIG_PHY_EXYNOS4210_USB2
> > +	{
> > +		.compatible = "samsung,exynos4210-usb2-phy",
> > +		.data = &exynos4210_usb2_phy_config,
> > +	},
> > +#endif
> > +#ifdef CONFIG_PHY_EXYNOS4X12_USB2
> > +	{
> > +		.compatible = "samsung,exynos4x12-usb2-phy",
> > +		.data = &exynos4x12_usb2_phy_config,
> > +	},
> > +#endif
> > +	{ },
> > +};
> > +
> > +static int samsung_usb2_phy_probe(struct platform_device *pdev) {
> > +	const struct of_device_id *match;
> > +	const struct samsung_usb2_phy_config *cfg;
> > +	struct clk *clk;
> > +	struct device *dev = &pdev->dev;
> > +	struct phy_provider *phy_provider;
> > +	struct resource *mem;
> > +	struct samsung_usb2_phy_driver *drv;
> > +	int i;
> > +
> > +	if (!pdev->dev.of_node) {
> > +		dev_err(dev, "This driver is required to be instantiated
> from device tree\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	match = of_match_node(samsung_usb2_phy_of_match, pdev-
> >dev.of_node);
> > +	if (!match) {
> > +		dev_err(dev, "of_match_node() failed\n");
> > +		return -EINVAL;
> > +	}
> > +	cfg = match->data;
> > +
> > +	drv = devm_kzalloc(dev, sizeof(struct samsung_usb2_phy_driver) +
> > +		cfg->num_phys * sizeof(struct samsung_usb2_phy_instance),
> > +								GFP_KERNEL);
> > +	if (!drv)
> > +		return -ENOMEM;
> > +
> > +	dev_set_drvdata(dev, drv);
> > +	spin_lock_init(&drv->lock);
> > +
> > +	drv->cfg = cfg;
> > +	drv->dev = dev;
> > +
> > +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	drv->reg_phy = devm_ioremap_resource(dev, mem);
> > +	if (IS_ERR(drv->reg_phy)) {
> > +		dev_err(dev, "Failed to map register memory (phy)\n");
> > +		return PTR_ERR(drv->reg_phy);
> > +	}
> > +
> > +	drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
> > +		"samsung,pmureg-phandle");
> > +	if (IS_ERR(drv->reg_pmu)) {
> > +		dev_err(dev, "Failed to map PMU registers (via syscon)\n");
> > +		return PTR_ERR(drv->reg_pmu);
> > +	}
> > +
> > +	if (drv->cfg->has_mode_switch) {
> > +		drv->reg_sys = syscon_regmap_lookup_by_phandle(
> > +				pdev->dev.of_node,
"samsung,sysreg-phandle");
> > +		if (IS_ERR(drv->reg_sys)) {
> > +			dev_err(dev, "Failed to map system registers (via
> syscon)\n");
> > +			return PTR_ERR(drv->reg_sys);
> > +		}
> > +	}
> > +
> > +	drv->clk = devm_clk_get(dev, "phy");
> > +	if (IS_ERR(drv->clk)) {
> > +		dev_err(dev, "Failed to get clock of phy controller\n");
> > +		return PTR_ERR(drv->clk);
> > +	}
> > +
> > +	for (i = 0; i < drv->cfg->num_phys; i++) {
> > +		char *label = drv->cfg->phys[i].label;
> > +		struct samsung_usb2_phy_instance *p = &drv->instances[i];
> > +
> > +		dev_dbg(dev, "Creating phy \"%s\"\n", label);
> > +		p->phy = devm_phy_create(dev, &samsung_usb2_phy_ops, NULL);
> > +		if (IS_ERR(p->phy)) {
> > +			dev_err(drv->dev, "Failed to create usb2_phy
> \"%s\"\n",
> > +						label);
> > +			return PTR_ERR(p->phy);
> > +		}
> > +
> > +		p->cfg = &drv->cfg->phys[i];
> > +		p->drv = drv;
> > +		phy_set_bus_width(p->phy, 8);
> > +		phy_set_drvdata(p->phy, p);
> > +
> > +		clk = devm_clk_get(dev, p->cfg->label);
> > +		if (IS_ERR(clk)) {
> > +			dev_err(dev, "Failed to get clock of \"%s\" phy\n",
> > +
p->cfg->label);
> > +			return PTR_ERR(clk);
> > +		}
> > +		p->clk = clk;
> > +	}
> > +
> > +	phy_provider = devm_of_phy_provider_register(dev,
> > +
samsung_usb2_phy_xlate);
> > +	if (IS_ERR(phy_provider)) {
> > +		dev_err(drv->dev, "Failed to register phy provider\n");
> > +		return PTR_ERR(phy_provider);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static struct platform_driver samsung_usb2_phy_driver = {
> > +	.probe	= samsung_usb2_phy_probe,
> > +	.driver = {
> > +		.of_match_table	= samsung_usb2_phy_of_match,
> > +		.name		= "samsung-usb2-phy",
> > +		.owner		= THIS_MODULE,
> > +	}
> > +};
> > +
> > +module_platform_driver(samsung_usb2_phy_driver);
> > +MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver");
> > +MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
> > +MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:samsung-usb2-phy");
> > +
> > diff --git a/drivers/phy/phy-samsung-usb2.h
> > b/drivers/phy/phy-samsung-usb2.h new file mode 100644 index
> > 0000000..868dd33
> > --- /dev/null
> > +++ b/drivers/phy/phy-samsung-usb2.h
> > @@ -0,0 +1,67 @@
> > +/*
> > + * Samsung SoC USB 1.1/2.0 PHY driver
> > + *
> > + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> > + * Author: Kamil Debski <k.debski@samsung.com>
> > + *
> > + * 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 _PHY_EXYNOS_USB2_H
> > +#define _PHY_EXYNOS_USB2_H
> > +
> > +#include <linux/clk.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/device.h>
> > +#include <linux/regmap.h>
> > +#include <linux/spinlock.h>
> > +
> > +#define KHZ 1000
> > +#define MHZ (KHZ * KHZ)
> > +
> > +struct samsung_usb2_phy_driver;
> > +struct samsung_usb2_phy_instance;
> > +struct samsung_usb2_phy_config;
> > +
> > +struct samsung_usb2_phy_instance {
> > +	const struct samsung_usb2_common_phy *cfg;
> > +	struct clk *clk;
> > +	struct phy *phy;
> > +	struct samsung_usb2_phy_driver *drv;
> > +	unsigned long rate;
> > +	u32 clk_reg_val;
> > +	bool enabled;
> > +};
> > +
> > +struct samsung_usb2_phy_driver {
> > +	const struct samsung_usb2_phy_config *cfg;
> > +	struct clk *clk;
> > +	struct device *dev;
> > +	void __iomem *reg_phy;
> > +	struct regmap *reg_pmu;
> > +	struct regmap *reg_sys;
> > +	spinlock_t lock;
> > +	struct samsung_usb2_phy_instance instances[0]; };
> > +
> > +struct samsung_usb2_common_phy {
> > +	int (*rate_to_clk)(unsigned long, u32 *);
> > +	int (*power_on)(struct samsung_usb2_phy_instance *);
> > +	int (*power_off)(struct samsung_usb2_phy_instance *);
> > +	unsigned int id;
> > +	char *label;
> > +};
> > +
> > +
> > +struct samsung_usb2_phy_config {
> > +	const struct samsung_usb2_common_phy *phys;
> > +	unsigned int num_phys;
> > +	bool has_mode_switch;
> > +};
> > +
> > +extern const struct samsung_usb2_phy_config
> > +exynos4210_usb2_phy_config; extern const struct
> > +samsung_usb2_phy_config exynos4x12_usb2_phy_config; #endif
> > +
> >


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

* RE: [PATCH v6 5/8] phy: Add new Exynos USB PHY driver
@ 2014-03-03 15:07       ` Kamil Debski
  0 siblings, 0 replies; 30+ messages in thread
From: Kamil Debski @ 2014-03-03 15:07 UTC (permalink / raw)
  To: 'Kishon Vijay Abraham I',
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ, Tomasz Figa,
	Sylwester Nawrocki, Marek Szyprowski,
	gautam.vivek-Sze3O3UU22JBDgjK7y7TUQ,
	mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w,
	yulgon.kim-Sze3O3UU22JBDgjK7y7TUQ,
	p.paneri-Sze3O3UU22JBDgjK7y7TUQ,
	av.tikhomirov-Sze3O3UU22JBDgjK7y7TUQ,
	jg1.han-Sze3O3UU22JBDgjK7y7TUQ, galak-sgV2jX0FEOL9JmXXK+q4OQ,
	matt.porter-QSEj5FYQhm4dnm+yROfE0A,
	tjakobi-o02PS0xoJP9W0yFyLvAVXMxlOr/tl8fh,
	stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz

Hi Kishon,

> From: Kishon Vijay Abraham I [mailto:kishon-l0cyMroinI0@public.gmane.org]
> Sent: Monday, March 03, 2014 3:28 PM
> 
> Hi,
> 
> On Wednesday 29 January 2014 10:59 PM, Kamil Debski wrote:
> > Add a new driver for the Exynos USB PHY. The new driver uses the
> > generic PHY framework. The driver includes support for the Exynos
> 4x10
> > and 4x12 SoC families.
> 
> Can the PHY part of this series be merged independently of the
> controller part?

I see no problems with merging the PHY part independently. However,
I would like to post an updated version of these patches. I am working
on them now and should send them tomorrow.

Best wishes,
-- 
Kamil Debski
Samsung R&D Institute Poland

> >
> > Signed-off-by: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> > ---
> >   .../devicetree/bindings/phy/samsung-phy.txt        |   55 ++++
> >   Documentation/phy/samsung-usb2.txt                 |  135 ++++++++
> >   drivers/phy/Kconfig                                |   29 ++
> >   drivers/phy/Makefile                               |    3 +
> >   drivers/phy/phy-exynos4210-usb2.c                  |  257
> ++++++++++++++++
> >   drivers/phy/phy-exynos4x12-usb2.c                  |  323
> ++++++++++++++++++++
> >   drivers/phy/phy-samsung-usb2.c                     |  227
> ++++++++++++++
> >   drivers/phy/phy-samsung-usb2.h                     |   67 ++++
> >   8 files changed, 1096 insertions(+)
> >   create mode 100644 Documentation/phy/samsung-usb2.txt
> >   create mode 100644 drivers/phy/phy-exynos4210-usb2.c
> >   create mode 100644 drivers/phy/phy-exynos4x12-usb2.c
> >   create mode 100644 drivers/phy/phy-samsung-usb2.c
> >   create mode 100644 drivers/phy/phy-samsung-usb2.h
> >
> > diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt
> > b/Documentation/devicetree/bindings/phy/samsung-phy.txt
> > index c0fccaa..6668c41 100644
> > --- a/Documentation/devicetree/bindings/phy/samsung-phy.txt
> > +++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt
> > @@ -20,3 +20,58 @@ Required properties:
> >   - compatible : should be "samsung,exynos5250-dp-video-phy";
> >   - reg : offset and length of the Display Port PHY register set;
> >   - #phy-cells : from the generic PHY bindings, must be 0;
> > +
> > +Samsung S5P/EXYNOS SoC series USB PHY
> > +-------------------------------------------------
> > +
> > +Required properties:
> > +- compatible : should be one of the listed compatibles:
> > +	- "samsung,exynos4210-usb2-phy"
> > +	- "samsung,exynos4x12-usb2-phy"
> > +- reg : a list of registers used by phy driver
> > +	- first and obligatory is the location of phy modules registers
> > +- samsung,sysreg-phandle - handle to syscon used to control the
> > +system registers
> > +- samsung,pmureg-phandle - handle to syscon used to control PMU
> > +registers
> > +- #phy-cells : from the generic phy bindings, must be 1;
> > +- clocks and clock-names:
> > +	- the "phy" clocks is required by the phy module
> > +	- next for each of the phys a clock has to be assigned, this
> clock
> > +	  will be used to determine clocking frequency for the phys
> > +	  (the labels are specified in the paragraph below)
> > +
> > +The first phandle argument in the PHY specifier identifies the PHY,
> > +its meaning is compatible dependent. For the currently supported
> SoCs
> > +(Exynos 4210 and Exynos 4212) it is as follows:
> > +  0 - USB device ("device"),
> > +  1 - USB host ("host"),
> > +  2 - HSIC0 ("hsic0"),
> > +  3 - HSIC1 ("hsic1"),
> > +
> > +Exynos 4210 and Exynos 4212 use mode switching and require that mode
> > +switch register is supplied.
> > +
> > +Example:
> > +
> > +For Exynos 4412 (compatible with Exynos 4212):
> > +
> > +usbphy: phy@125b0000 {
> > +	compatible = "samsung,exynos4212-usb2-phy";
> > +	reg = <0x125b0000 0x100 0x10020704 0x0c 0x1001021c 0x4>;
> > +	clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
> > +							<&clock 2>;
> > +	clock-names = "phy", "device", "host", "hsic0", "hsic1";
> > +	status = "okay";
> > +	#phy-cells = <1>;
> > +	samsung,sysreg-phandle = <&sys_reg>;
> > +	samsung,pmureg-phandle = <&pmu_reg>; };
> > +
> > +Then the PHY can be used in other nodes such as:
> > +
> > +phy-consumer@12340000 {
> > +	phys = <&usbphy 2>;
> > +	phy-names = "phy";
> > +};
> > +
> > +Refer to DT bindings documentation of particular PHY consumer
> devices
> > +for more information about required PHYs and the way of
> specification.
> > diff --git a/Documentation/phy/samsung-usb2.txt
> > b/Documentation/phy/samsung-usb2.txt
> > new file mode 100644
> > index 0000000..9f5826e
> > --- /dev/null
> > +++ b/Documentation/phy/samsung-usb2.txt
> > @@ -0,0 +1,135 @@
> > +.-------------------------------------------------------------------
> -
> > +----------+
> > +|			Samsung USB 2.0 PHY adaptation layer
> |
> > ++-------------------------------------------------------------------
> ----------+'
> > +
> > +| 1. Description
> > ++----------------
> > +
> > +The architecture of the USB 2.0 PHY module in Samsung SoCs is
> similar
> > +among many SoCs. In spite of the similarities it proved difficult to
> > +create a one driver that would fit all these PHY controllers. Often
> > +the differences were minor and were found in particular bits of the
> > +registers of the PHY. In some rare cases the order of register
> writes
> > +or the PHY powering up process had to be altered. This adaptation
> > +layer is a compromise between having separate drivers and having a
> > +single driver with added support for many special cases.
> > +
> > +| 2. Files description
> > ++----------------------
> > +
> > +- phy-samsung-usb2.c
> > +   This is the main file of the adaptation layer. This file contains
> > +   the probe function and provides two callbacks to the Generic PHY
> > +   Framework. This two callbacks are used to power on and power off
> the
> > +   phy. They carry out the common work that has to be done on all
> version
> > +   of the PHY module. Depending on which SoC was chosen they execute
> SoC
> > +   specific callbacks. The specific SoC version is selected by
> choosing
> > +   the appropriate compatible string. In addition, this file
> contains
> > +   struct of_device_id definitions for particular SoCs.
> > +
> > +- phy-samsung-usb2.h
> > +   This is the include file. It declares the structures used by this
> > +   driver. In addition it should contain extern declarations for
> > +   structures that describe particular SoCs.
> > +
> > +| 3. Supporting SoCs
> > ++--------------------
> > +
> > +To support a new SoC a new file should be added to the drivers/phy
> > +directory. Each SoC's configuration is stored in an instance of the
> > +struct samsung_usb2_phy_config.
> > +
> > +struct samsung_usb2_phy_config {
> > +	const struct samsung_usb2_common_phy *phys;
> > +	unsigned int num_phys;
> > +	bool has_mode_switch;
> > +};
> > +
> > +The num_phys is the number of phys handled by the driver. *phys is
> an
> > +array that contains the configuration for each phy. The
> > +has_mode_switch property is a boolean flag that determines whether
> > +the SoC has USB host and device on a single pair of pins. If so, a
> > +special register has to be modified to change the internal routing
> of
> > +these pins between a USB device or host module.
> > +
> > +For example the configuration for Exynos 4210 is following:
> > +
> > +const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
> > +	.num_phys		= EXYNOS4210_NUM_PHYS,
> > +	.phys			= exynos4210_phys,
> > +}
> > +
> > +The exynos4210_phys configuration array is as follows:
> > +
> > +static const struct samsung_usb2_common_phy exynos4210_phys[] = {
> > +	{
> > +		.label		= "device",
> > +		.id		= EXYNOS4210_DEVICE,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "host",
> > +		.id		= EXYNOS4210_HOST,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic0",
> > +		.id		= EXYNOS4210_HSIC0,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic1",
> > +		.id		= EXYNOS4210_HSIC1,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{},
> > +};
> > +
> > +- int (*rate_to_clk)(unsigned long, u32 *)
> > +	The rate_to_clk callback is to convert the rate of the clock
> > +	used to clock the module to the value that should be written to
> > +	the registers of the PHY module.
> > +- int (*power_on)(struct samsung_usb2_phy_instance *);
> > +- int (*power_off)(struct samsung_usb2_phy_instance *);
> > +	These two callbacks are used to power on and power off the phy
> > +	by modifying appropriate registers.
> > +
> > +Final change to the driver is adding appropriate compatible value to
> > +the phy-samsung-usb2.c file. In case of Exynos 4210 the following
> > +lines were added to the struct of_device_id
> samsung_usb2_phy_of_match[] array:
> > +
> > +#ifdef CONFIG_PHY_EXYNOS4210_USB2
> > +	{
> > +		.compatible = "samsung,exynos4210-usb2-phy",
> > +		.data = &exynos4210_usb2_phy_config,
> > +	},
> > +#endif
> > +
> > +To add further flexibility to the driver the Kconfig file enables to
> > +include support for selected SoCs in the compiled driver. The
> Kconfig
> > +entry for Exynos 4210 is following:
> > +
> > +config PHY_EXYNOS4210_USB2
> > +	bool "Support for Exynos 4210"
> > +	depends on PHY_SAMSUNG_USB2
> > +	depends on CPU_EXYNOS4210
> > +	help
> > +	  Enable USB PHY support for Exynos 4210. This option requires
> that
> > +	  Samsung USB 2.0 PHY driver is enabled and means that support
> for this
> > +	  particular SoC is compiled in the driver. In case of Exynos
> 4210 four
> > +	  phys are available - device, host, HSCI0 and HSCI1.
> > +
> > +The newly created file that supports the new SoC has to be also
> added
> > +to the Makefile. In case of Exynos 4210 the added line is following:
> > +
> > +obj-$(CONFIG_PHY_EXYNOS4210_USB2)       += phy-exynos4210-usb2.o
> > +
> > +After completing these steps the support for the new SoC should be
> ready.
> > diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index
> > b901c47..3691d24 100644
> > --- a/drivers/phy/Kconfig
> > +++ b/drivers/phy/Kconfig
> > @@ -63,4 +63,33 @@ config BCM_KONA_USB2_PHY
> >   	help
> >   	  Enable this to support the Broadcom Kona USB 2.0 PHY.
> >
> > +config PHY_SAMSUNG_USB2
> > +	tristate "Samsung USB 2.0 PHY driver"
> > +	select GENERIC_PHY
> > +	select MFD_SYSCON
> > +	help
> > +	  Enable this to support the Samsung USB 2.0 PHY driver for
> Samsung
> > +	  SoCs. This driver provides the interface for USB 2.0 PHY.
> Support for
> > +	  particular SoCs has to be enabled in addition to this driver.
> Number
> > +	  and type of supported phys depends on the SoC.
> > +
> > +config PHY_EXYNOS4210_USB2
> > +	bool "Support for Exynos 4210"
> > +	depends on PHY_SAMSUNG_USB2
> > +	depends on CPU_EXYNOS4210
> > +	help
> > +	  Enable USB PHY support for Exynos 4210. This option requires
> that
> > +	  Samsung USB 2.0 PHY driver is enabled and means that support
> for this
> > +	  particular SoC is compiled in the driver. In case of Exynos
> 4210 four
> > +	  phys are available - device, host, HSIC0 and HSIC1.
> > +
> > +config PHY_EXYNOS4X12_USB2
> > +	bool "Support for Exynos 4x12"
> > +	depends on PHY_SAMSUNG_USB2
> > +	depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
> > +	help
> > +	  Enable USB PHY support for Exynos 4x12. This option requires
> that
> > +	  Samsung USB 2.0 PHY driver is enabled and means that support
> for this
> > +	  particular SoC is compiled in the driver. In case of Exynos
> 4x12 four
> > +	  phys are available - device, host, HSIC0 and HSIC1.
> >   endmenu
> > diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index
> > b57c253..69d0b3f2 100644
> > --- a/drivers/phy/Makefile
> > +++ b/drivers/phy/Makefile
> > @@ -9,3 +9,6 @@ obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)	+= phy-
> exynos-mipi-video.o
> >   obj-$(CONFIG_PHY_MVEBU_SATA)		+= phy-mvebu-sata.o
> >   obj-$(CONFIG_OMAP_USB2)			+= phy-omap-usb2.o
> >   obj-$(CONFIG_TWL4030_USB)		+= phy-twl4030-usb.o
> > +obj-$(CONFIG_PHY_SAMSUNG_USB2)		+= phy-samsung-usb2.o
> > +obj-$(CONFIG_PHY_EXYNOS4210_USB2)	+= phy-exynos4210-usb2.o
> > +obj-$(CONFIG_PHY_EXYNOS4X12_USB2)	+= phy-exynos4x12-usb2.o
> > diff --git a/drivers/phy/phy-exynos4210-usb2.c
> > b/drivers/phy/phy-exynos4210-usb2.c
> > new file mode 100644
> > index 0000000..8f86abf
> > --- /dev/null
> > +++ b/drivers/phy/phy-exynos4210-usb2.c
> > @@ -0,0 +1,257 @@
> > +/*
> > + * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support
> > + *
> > + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> > + * Author: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@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.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/regmap.h>
> > +#include "phy-samsung-usb2.h"
> > +
> > +/* Exynos USB PHY registers */
> > +
> > +/* PHY power control */
> > +#define EXYNOS_4210_UPHYPWR			0x0
> > +
> > +#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND	BIT(0)
> > +#define EXYNOS_4210_UPHYPWR_PHY0_PWR		BIT(3)
> > +#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR	BIT(4)
> > +#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP		BIT(5)
> > +#define EXYNOS_4210_UPHYPWR_PHY0	( \
> > +	EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \
> > +	EXYNOS_4210_UPHYPWR_PHY0_PWR | \
> > +	EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \
> > +	EXYNOS_4210_UPHYPWR_PHY0_SLEEP)
> > +
> > +#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND	BIT(6)
> > +#define EXYNOS_4210_UPHYPWR_PHY1_PWR		BIT(7)
> > +#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP		BIT(8)
> > +#define EXYNOS_4210_UPHYPWR_PHY1 ( \
> > +	EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \
> > +	EXYNOS_4210_UPHYPWR_PHY1_PWR | \
> > +	EXYNOS_4210_UPHYPWR_PHY1_SLEEP)
> > +
> > +#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND	BIT(9)
> > +#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP		BIT(10)
> > +#define EXYNOS_4210_UPHYPWR_HSIC0 ( \
> > +	EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \
> > +	EXYNOS_4210_UPHYPWR_HSIC0_SLEEP)
> > +
> > +#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND	BIT(11)
> > +#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP		BIT(12)
> > +#define EXYNOS_4210_UPHYPWR_HSIC1 ( \
> > +	EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \
> > +	EXYNOS_4210_UPHYPWR_HSIC1_SLEEP)
> > +
> > +/* PHY clock control */
> > +#define EXYNOS_4210_UPHYCLK			0x4
> > +
> > +#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK	(0x3 << 0)
> > +#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ	(0x0 << 0)
> > +#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ	(0x3 << 0)
> > +#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
> > +
> > +#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP	BIT(2)
> > +#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON	BIT(4)
> > +#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON	BIT(7)
> > +
> > +/* PHY reset control */
> > +#define EXYNOS_4210_UPHYRST			0x8
> > +
> > +#define EXYNOS_4210_URSTCON_PHY0		BIT(0)
> > +#define EXYNOS_4210_URSTCON_OTG_HLINK		BIT(1)
> > +#define EXYNOS_4210_URSTCON_OTG_PHYLINK		BIT(2)
> > +#define EXYNOS_4210_URSTCON_PHY1_ALL		BIT(3)
> > +#define EXYNOS_4210_URSTCON_PHY1_P0		BIT(4)
> > +#define EXYNOS_4210_URSTCON_PHY1_P1P2		BIT(5)
> > +#define EXYNOS_4210_URSTCON_HOST_LINK_ALL	BIT(6)
> > +#define EXYNOS_4210_URSTCON_HOST_LINK_P0	BIT(7)
> > +#define EXYNOS_4210_URSTCON_HOST_LINK_P1	BIT(8)
> > +#define EXYNOS_4210_URSTCON_HOST_LINK_P2	BIT(9)
> > +
> > +/* Isolation, configured in the power management unit */
> > +#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET	0x704
> > +#define EXYNOS_4210_USB_ISOL_DEVICE		BIT(0)
> > +#define EXYNOS_4210_USB_ISOL_HOST_OFFSET	0x708
> > +#define EXYNOS_4210_USB_ISOL_HOST		BIT(0)
> > +
> > +/* USBYPHY1 Floating prevention */
> > +#define EXYNOS_4210_UPHY1CON			0x34
> > +#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION	0x1
> > +
> > +/* Mode switching SUB Device <-> Host */
> > +#define EXYNOS_4210_MODE_SWITCH_OFFSET		0x21c
> > +#define EXYNOS_4210_MODE_SWITCH_MASK		1
> > +#define EXYNOS_4210_MODE_SWITCH_DEVICE		0
> > +#define EXYNOS_4210_MODE_SWITCH_HOST		1
> > +
> > +enum exynos4210_phy_id {
> > +	EXYNOS4210_DEVICE,
> > +	EXYNOS4210_HOST,
> > +	EXYNOS4210_HSIC0,
> > +	EXYNOS4210_HSIC1,
> > +	EXYNOS4210_NUM_PHYS,
> > +};
> > +
> > +/*
> > + * exynos4210_rate_to_clk() converts the supplied clock rate to the
> > +value that
> > + * can be written to the phy register.
> > + */
> > +static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg) {
> > +	switch (rate) {
> > +	case 12 * MHZ:
> > +		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
> > +		break;
> > +	case 24 * MHZ:
> > +		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
> > +		break;
> > +	case 48 * MHZ:
> > +		*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void exynos4210_isol(struct samsung_usb2_phy_instance *inst,
> > +bool on) {
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +	u32 offset;
> > +	u32 mask;
> > +
> > +	switch (inst->cfg->id) {
> > +	case EXYNOS4210_DEVICE:
> > +		offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET;
> > +		mask = EXYNOS_4210_USB_ISOL_DEVICE;
> > +		break;
> > +	case EXYNOS4210_HOST:
> > +		offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET;
> > +		mask = EXYNOS_4210_USB_ISOL_HOST;
> > +		break;
> > +	default:
> > +		return;
> > +	};
> > +
> > +	regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); }
> > +
> > +static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance
> > +*inst, bool on) {
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +	u32 rstbits = 0;
> > +	u32 phypwr = 0;
> > +	u32 rst;
> > +	u32 pwr;
> > +
> > +	switch (inst->cfg->id) {
> > +	case EXYNOS4210_DEVICE:
> > +		phypwr =	EXYNOS_4210_UPHYPWR_PHY0;
> > +		rstbits =	EXYNOS_4210_URSTCON_PHY0;
> > +		break;
> > +	case EXYNOS4210_HOST:
> > +		phypwr =	EXYNOS_4210_UPHYPWR_PHY1;
> > +		rstbits =	EXYNOS_4210_URSTCON_PHY1_ALL |
> > +				EXYNOS_4210_URSTCON_PHY1_P0 |
> > +				EXYNOS_4210_URSTCON_PHY1_P1P2 |
> > +				EXYNOS_4210_URSTCON_HOST_LINK_ALL |
> > +				EXYNOS_4210_URSTCON_HOST_LINK_P0;
> > +		writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON);
> > +		break;
> > +	case EXYNOS4210_HSIC0:
> > +		phypwr =	EXYNOS_4210_UPHYPWR_HSIC0;
> > +		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
> > +				EXYNOS_4210_URSTCON_HOST_LINK_P1;
> > +		break;
> > +	case EXYNOS4210_HSIC1:
> > +		phypwr =	EXYNOS_4210_UPHYPWR_HSIC1;
> > +		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
> > +				EXYNOS_4210_URSTCON_HOST_LINK_P2;
> > +		break;
> > +	};
> > +
> > +	if (on) {
> > +		writel(inst->clk_reg_val, drv->reg_phy +
> EXYNOS_4210_UPHYCLK);
> > +
> > +		pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
> > +		pwr &= ~phypwr;
> > +		writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
> > +
> > +		rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST);
> > +		rst |= rstbits;
> > +		writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
> > +		udelay(10);
> > +		rst &= ~rstbits;
> > +		writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
> > +	} else {
> > +		pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
> > +		pwr |= phypwr;
> > +		writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
> > +	}
> > +}
> > +
> > +static int exynos4210_power_on(struct samsung_usb2_phy_instance
> > +*inst) {
> > +	/* Order of initialisation is important - first power then
> isolation */
> > +	exynos4210_phy_pwr(inst, 1);
> > +	exynos4210_isol(inst, 0);
> > +
> > +	return 0;
> > +}
> > +
> > +static int exynos4210_power_off(struct samsung_usb2_phy_instance
> > +*inst) {
> > +	exynos4210_isol(inst, 1);
> > +	exynos4210_phy_pwr(inst, 0);
> > +
> > +	return 0;
> > +}
> > +
> > +
> > +static const struct samsung_usb2_common_phy exynos4210_phys[] = {
> > +	{
> > +		.label		= "device",
> > +		.id		= EXYNOS4210_DEVICE,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "host",
> > +		.id		= EXYNOS4210_HOST,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic0",
> > +		.id		= EXYNOS4210_HSIC0,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic1",
> > +		.id		= EXYNOS4210_HSIC1,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{},
> > +};
> > +
> > +const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
> > +	.num_phys		= EXYNOS4210_NUM_PHYS,
> > +	.phys			= exynos4210_phys,
> > +	.has_mode_switch	= 0,
> > +};
> > +
> > diff --git a/drivers/phy/phy-exynos4x12-usb2.c
> > b/drivers/phy/phy-exynos4x12-usb2.c
> > new file mode 100644
> > index 0000000..ce39502
> > --- /dev/null
> > +++ b/drivers/phy/phy-exynos4x12-usb2.c
> > @@ -0,0 +1,323 @@
> > +/*
> > + * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4x12 support
> > + *
> > + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> > + * Author: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@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.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/regmap.h>
> > +#include "phy-samsung-usb2.h"
> > +
> > +/* Exynos USB PHY registers */
> > +
> > +/* PHY power control */
> > +#define EXYNOS_4x12_UPHYPWR			0x0
> > +
> > +#define EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND	BIT(0)
> > +#define EXYNOS_4x12_UPHYPWR_PHY0_PWR		BIT(3)
> > +#define EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR	BIT(4)
> > +#define EXYNOS_4x12_UPHYPWR_PHY0_SLEEP		BIT(5)
> > +#define EXYNOS_4x12_UPHYPWR_PHY0 ( \
> > +	EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND | \
> > +	EXYNOS_4x12_UPHYPWR_PHY0_PWR | \
> > +	EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR | \
> > +	EXYNOS_4x12_UPHYPWR_PHY0_SLEEP)
> > +
> > +#define EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND	BIT(6)
> > +#define EXYNOS_4x12_UPHYPWR_PHY1_PWR		BIT(7)
> > +#define EXYNOS_4x12_UPHYPWR_PHY1_SLEEP		BIT(8)
> > +#define EXYNOS_4x12_UPHYPWR_PHY1 ( \
> > +	EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND | \
> > +	EXYNOS_4x12_UPHYPWR_PHY1_PWR | \
> > +	EXYNOS_4x12_UPHYPWR_PHY1_SLEEP)
> > +
> > +#define EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND	BIT(9)
> > +#define EXYNOS_4x12_UPHYPWR_HSIC0_PWR		BIT(10)
> > +#define EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP		BIT(11)
> > +#define EXYNOS_4x12_UPHYPWR_HSIC0 ( \
> > +	EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND | \
> > +	EXYNOS_4x12_UPHYPWR_HSIC0_PWR | \
> > +	EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP)
> > +
> > +#define EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND	BIT(12)
> > +#define EXYNOS_4x12_UPHYPWR_HSIC1_PWR		BIT(13)
> > +#define EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP		BIT(14)
> > +#define EXYNOS_4x12_UPHYPWR_HSIC1 ( \
> > +	EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND | \
> > +	EXYNOS_4x12_UPHYPWR_HSIC1_PWR | \
> > +	EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP)
> > +
> > +/* PHY clock control */
> > +#define EXYNOS_4x12_UPHYCLK			0x4
> > +
> > +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK	(0x7 << 0)
> > +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6	(0x0 << 0)
> > +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ	(0x1 << 0)
> > +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
> > +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2	(0x3 << 0)
> > +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ	(0x4 << 0)
> > +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ	(0x5 << 0)
> > +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ	(0x7 << 0)
> > +
> > +#define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP	BIT(3)
> > +#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON	BIT(4)
> > +#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON	BIT(7)
> > +
> > +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK	(0x7f << 10)
> > +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ	(0x24 << 10)
> > +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ	(0x1c << 10)
> > +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ	(0x1a << 10)
> > +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2	(0x15 << 10)
> > +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ	(0x14 << 10)
> > +
> > +/* PHY reset control */
> > +#define EXYNOS_4x12_UPHYRST			0x8
> > +
> > +#define EXYNOS_4x12_URSTCON_PHY0		BIT(0)
> > +#define EXYNOS_4x12_URSTCON_OTG_HLINK		BIT(1)
> > +#define EXYNOS_4x12_URSTCON_OTG_PHYLINK		BIT(2)
> > +#define EXYNOS_4x12_URSTCON_HOST_PHY		BIT(3)
> > +#define EXYNOS_4x12_URSTCON_PHY1		BIT(4)
> > +#define EXYNOS_4x12_URSTCON_HSIC0		BIT(5)
> > +#define EXYNOS_4x12_URSTCON_HSIC1		BIT(6)
> > +#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL	BIT(7)
> > +#define EXYNOS_4x12_URSTCON_HOST_LINK_P0	BIT(8)
> > +#define EXYNOS_4x12_URSTCON_HOST_LINK_P1	BIT(9)
> > +#define EXYNOS_4x12_URSTCON_HOST_LINK_P2	BIT(10)
> > +
> > +/* Isolation, configured in the power management unit */
> > +#define EXYNOS_4x12_USB_ISOL_OFFSET		0x704
> > +#define EXYNOS_4x12_USB_ISOL_OTG		BIT(0)
> > +#define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET	0x708
> > +#define EXYNOS_4x12_USB_ISOL_HSIC0		BIT(0)
> > +#define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET	0x70c
> > +#define EXYNOS_4x12_USB_ISOL_HSIC1		BIT(0)
> > +
> > +/* Mode switching SUB Device <-> Host */
> > +#define EXYNOS_4x12_MODE_SWITCH_OFFSET		0x21c
> > +#define EXYNOS_4x12_MODE_SWITCH_MASK		1
> > +#define EXYNOS_4x12_MODE_SWITCH_DEVICE		0
> > +#define EXYNOS_4x12_MODE_SWITCH_HOST		1
> > +
> > +enum exynos4x12_phy_id {
> > +	EXYNOS4x12_DEVICE,
> > +	EXYNOS4x12_HOST,
> > +	EXYNOS4x12_HSIC0,
> > +	EXYNOS4x12_HSIC1,
> > +	EXYNOS4x12_NUM_PHYS,
> > +};
> > +
> > +/*
> > + * exynos4x12_rate_to_clk() converts the supplied clock rate to the
> > +value that
> > + * can be written to the phy register.
> > + */
> > +static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg) {
> > +	/* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */
> > +
> > +	switch (rate) {
> > +	case 9600 * KHZ:
> > +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6;
> > +		break;
> > +	case 10 * MHZ:
> > +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ;
> > +		break;
> > +	case 12 * MHZ:
> > +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ;
> > +		break;
> > +	case 19200 * KHZ:
> > +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2;
> > +		break;
> > +	case 20 * MHZ:
> > +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ;
> > +		break;
> > +	case 24 * MHZ:
> > +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ;
> > +		break;
> > +	case 50 * MHZ:
> > +		*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ;
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst,
> > +bool on) {
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +	u32 offset;
> > +	u32 mask;
> > +
> > +	switch (inst->cfg->id) {
> > +	case EXYNOS4x12_DEVICE:
> > +	case EXYNOS4x12_HOST:
> > +		offset = EXYNOS_4x12_USB_ISOL_OFFSET;
> > +		mask = EXYNOS_4x12_USB_ISOL_OTG;
> > +		break;
> > +	case EXYNOS4x12_HSIC0:
> > +		offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET;
> > +		mask = EXYNOS_4x12_USB_ISOL_HSIC0;
> > +		break;
> > +	case EXYNOS4x12_HSIC1:
> > +		offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET;
> > +		mask = EXYNOS_4x12_USB_ISOL_HSIC1;
> > +		break;
> > +	default:
> > +		return;
> > +	};
> > +
> > +	regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); }
> > +
> > +static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance
> > +*inst) {
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +	writel(inst->clk_reg_val, drv->reg_phy + EXYNOS_4x12_UPHYCLK); }
> > +
> > +static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance
> > +*inst, bool on) {
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +	u32 rstbits = 0;
> > +	u32 phypwr = 0;
> > +	u32 rst;
> > +	u32 pwr;
> > +	u32 mode = 0;
> > +	u32 switch_mode = 0;
> > +
> > +	switch (inst->cfg->id) {
> > +	case EXYNOS4x12_DEVICE:
> > +		phypwr =	EXYNOS_4x12_UPHYPWR_PHY0;
> > +		rstbits =	EXYNOS_4x12_URSTCON_PHY0;
> > +		mode =		EXYNOS_4x12_MODE_SWITCH_DEVICE;
> > +		switch_mode =	1;
> > +		break;
> > +	case EXYNOS4x12_HOST:
> > +		phypwr =	EXYNOS_4x12_UPHYPWR_PHY1;
> > +		rstbits =	EXYNOS_4x12_URSTCON_HOST_PHY;
> > +		mode =		EXYNOS_4x12_MODE_SWITCH_HOST;
> > +		switch_mode =	1;
> > +		break;
> > +	case EXYNOS4x12_HSIC0:
> > +		phypwr =	EXYNOS_4x12_UPHYPWR_HSIC0;
> > +		rstbits =	EXYNOS_4x12_URSTCON_HSIC1 |
> > +				EXYNOS_4x12_URSTCON_HOST_LINK_P0 |
> > +				EXYNOS_4x12_URSTCON_HOST_PHY;
> > +		break;
> > +	case EXYNOS4x12_HSIC1:
> > +		phypwr =	EXYNOS_4x12_UPHYPWR_HSIC1;
> > +		rstbits =	EXYNOS_4x12_URSTCON_HSIC1 |
> > +				EXYNOS_4x12_URSTCON_HOST_LINK_P1;
> > +		break;
> > +	};
> > +
> > +	if (on) {
> > +		if (switch_mode)
> > +			regmap_update_bits(drv->reg_sys,
> > +			        EXYNOS_4x12_MODE_SWITCH_OFFSET,
> > +			        EXYNOS_4x12_MODE_SWITCH_MASK,
> > +				mode);
> > +
> > +		pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
> > +		pwr &= ~phypwr;
> > +		writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
> > +
> > +		rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST);
> > +		rst |= rstbits;
> > +		writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
> > +		udelay(10);
> > +		rst &= ~rstbits;
> > +		writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
> > +	} else {
> > +		pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
> > +		pwr |= phypwr;
> > +		writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
> > +	}
> > +}
> > +
> > +static int exynos4x12_power_on(struct samsung_usb2_phy_instance
> > +*inst) {
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +
> > +	inst->enabled = 1;
> > +	exynos4x12_setup_clk(inst);
> > +	exynos4x12_phy_pwr(inst, 1);
> > +	exynos4x12_isol(inst, 0);
> > +
> > +	/* Power on the device, as it is necessary for HSIC to work */
> > +	if (inst->cfg->id == EXYNOS4x12_HSIC0) {
> > +		struct samsung_usb2_phy_instance *device =
> > +					&drv->instances[EXYNOS4x12_DEVICE];
> > +		exynos4x12_phy_pwr(device, 1);
> > +		exynos4x12_isol(device, 0);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int exynos4x12_power_off(struct samsung_usb2_phy_instance
> > +*inst) {
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +	struct samsung_usb2_phy_instance *device =
> > +					&drv->instances[EXYNOS4x12_DEVICE];
> > +
> > +	inst->enabled = 0;
> > +	exynos4x12_isol(inst, 1);
> > +	exynos4x12_phy_pwr(inst, 0);
> > +
> > +	if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) {
> > +		exynos4x12_isol(device, 1);
> > +		exynos4x12_phy_pwr(device, 0);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +
> > +static const struct samsung_usb2_common_phy exynos4x12_phys[] = {
> > +	{
> > +		.label		= "device",
> > +		.id		= EXYNOS4x12_DEVICE,
> > +		.rate_to_clk	= exynos4x12_rate_to_clk,
> > +		.power_on	= exynos4x12_power_on,
> > +		.power_off	= exynos4x12_power_off,
> > +	},
> > +	{
> > +		.label		= "host",
> > +		.id		= EXYNOS4x12_HOST,
> > +		.rate_to_clk	= exynos4x12_rate_to_clk,
> > +		.power_on	= exynos4x12_power_on,
> > +		.power_off	= exynos4x12_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic0",
> > +		.id		= EXYNOS4x12_HSIC0,
> > +		.rate_to_clk	= exynos4x12_rate_to_clk,
> > +		.power_on	= exynos4x12_power_on,
> > +		.power_off	= exynos4x12_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic1",
> > +		.id		= EXYNOS4x12_HSIC1,
> > +		.rate_to_clk	= exynos4x12_rate_to_clk,
> > +		.power_on	= exynos4x12_power_on,
> > +		.power_off	= exynos4x12_power_off,
> > +	},
> > +	{},
> > +};
> > +
> > +const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = {
> > +	.num_phys		= EXYNOS4x12_NUM_PHYS,
> > +	.phys			= exynos4x12_phys,
> > +	.has_mode_switch	= 1,
> > +};
> > +
> > diff --git a/drivers/phy/phy-samsung-usb2.c
> > b/drivers/phy/phy-samsung-usb2.c new file mode 100644 index
> > 0000000..c1b0fd5
> > --- /dev/null
> > +++ b/drivers/phy/phy-samsung-usb2.c
> > @@ -0,0 +1,227 @@
> > +/*
> > + * Samsung SoC USB 1.1/2.0 PHY driver
> > + *
> > + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> > + * Author: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@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.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/spinlock.h>
> > +#include "phy-samsung-usb2.h"
> > +
> > +static int samsung_usb2_phy_power_on(struct phy *phy) {
> > +	struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +	int ret;
> > +
> > +	dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n",
> > +							inst->cfg->label);
> > +	ret = clk_prepare_enable(drv->clk);
> > +	if (ret)
> > +		goto err_main_clk;
> > +	ret = clk_prepare_enable(inst->clk);
> > +	if (ret)
> > +		goto err_instance_clk;
> > +	inst->rate = clk_get_rate(inst->clk);
> > +	if (inst->cfg->rate_to_clk) {
> > +		ret = inst->cfg->rate_to_clk(inst->rate, &inst-
> >clk_reg_val);
> > +		if (ret)
> > +			goto err_get_rate;
> > +	}
> > +	if (inst->cfg->power_on) {
> > +		spin_lock(&drv->lock);
> > +		ret = inst->cfg->power_on(inst);
> > +		spin_unlock(&drv->lock);
> > +	}
> > +
> > +	return 0;
> > +
> > +err_get_rate:
> > +	clk_disable_unprepare(inst->clk);
> > +err_instance_clk:
> > +	clk_disable_unprepare(drv->clk);
> > +err_main_clk:
> > +	return ret;
> > +}
> > +
> > +static int samsung_usb2_phy_power_off(struct phy *phy) {
> > +	struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
> > +	struct samsung_usb2_phy_driver *drv = inst->drv;
> > +	int ret = 0;
> > +
> > +	dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
> > +							inst->cfg->label);
> > +	if (inst->cfg->power_off) {
> > +		spin_lock(&drv->lock);
> > +		ret = inst->cfg->power_off(inst);
> > +		spin_unlock(&drv->lock);
> > +	}
> > +	clk_disable_unprepare(inst->clk);
> > +	clk_disable_unprepare(drv->clk);
> > +	return ret;
> > +}
> > +
> > +static struct phy_ops samsung_usb2_phy_ops = {
> > +	.power_on	= samsung_usb2_phy_power_on,
> > +	.power_off	= samsung_usb2_phy_power_off,
> > +	.owner		= THIS_MODULE,
> > +};
> > +
> > +static struct phy *samsung_usb2_phy_xlate(struct device *dev,
> > +					struct of_phandle_args *args)
> > +{
> > +	struct samsung_usb2_phy_driver *drv;
> > +
> > +	drv = dev_get_drvdata(dev);
> > +	if (!drv)
> > +		return ERR_PTR(-EINVAL);
> > +
> > +	if (WARN_ON(args->args[0] >= drv->cfg->num_phys))
> > +		return ERR_PTR(-ENODEV);
> > +
> > +	return drv->instances[args->args[0]].phy;
> > +}
> > +
> > +static const struct of_device_id samsung_usb2_phy_of_match[] = {
> > +#ifdef CONFIG_PHY_EXYNOS4210_USB2
> > +	{
> > +		.compatible = "samsung,exynos4210-usb2-phy",
> > +		.data = &exynos4210_usb2_phy_config,
> > +	},
> > +#endif
> > +#ifdef CONFIG_PHY_EXYNOS4X12_USB2
> > +	{
> > +		.compatible = "samsung,exynos4x12-usb2-phy",
> > +		.data = &exynos4x12_usb2_phy_config,
> > +	},
> > +#endif
> > +	{ },
> > +};
> > +
> > +static int samsung_usb2_phy_probe(struct platform_device *pdev) {
> > +	const struct of_device_id *match;
> > +	const struct samsung_usb2_phy_config *cfg;
> > +	struct clk *clk;
> > +	struct device *dev = &pdev->dev;
> > +	struct phy_provider *phy_provider;
> > +	struct resource *mem;
> > +	struct samsung_usb2_phy_driver *drv;
> > +	int i;
> > +
> > +	if (!pdev->dev.of_node) {
> > +		dev_err(dev, "This driver is required to be instantiated
> from device tree\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	match = of_match_node(samsung_usb2_phy_of_match, pdev-
> >dev.of_node);
> > +	if (!match) {
> > +		dev_err(dev, "of_match_node() failed\n");
> > +		return -EINVAL;
> > +	}
> > +	cfg = match->data;
> > +
> > +	drv = devm_kzalloc(dev, sizeof(struct samsung_usb2_phy_driver) +
> > +		cfg->num_phys * sizeof(struct samsung_usb2_phy_instance),
> > +								GFP_KERNEL);
> > +	if (!drv)
> > +		return -ENOMEM;
> > +
> > +	dev_set_drvdata(dev, drv);
> > +	spin_lock_init(&drv->lock);
> > +
> > +	drv->cfg = cfg;
> > +	drv->dev = dev;
> > +
> > +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	drv->reg_phy = devm_ioremap_resource(dev, mem);
> > +	if (IS_ERR(drv->reg_phy)) {
> > +		dev_err(dev, "Failed to map register memory (phy)\n");
> > +		return PTR_ERR(drv->reg_phy);
> > +	}
> > +
> > +	drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
> > +		"samsung,pmureg-phandle");
> > +	if (IS_ERR(drv->reg_pmu)) {
> > +		dev_err(dev, "Failed to map PMU registers (via syscon)\n");
> > +		return PTR_ERR(drv->reg_pmu);
> > +	}
> > +
> > +	if (drv->cfg->has_mode_switch) {
> > +		drv->reg_sys = syscon_regmap_lookup_by_phandle(
> > +				pdev->dev.of_node,
"samsung,sysreg-phandle");
> > +		if (IS_ERR(drv->reg_sys)) {
> > +			dev_err(dev, "Failed to map system registers (via
> syscon)\n");
> > +			return PTR_ERR(drv->reg_sys);
> > +		}
> > +	}
> > +
> > +	drv->clk = devm_clk_get(dev, "phy");
> > +	if (IS_ERR(drv->clk)) {
> > +		dev_err(dev, "Failed to get clock of phy controller\n");
> > +		return PTR_ERR(drv->clk);
> > +	}
> > +
> > +	for (i = 0; i < drv->cfg->num_phys; i++) {
> > +		char *label = drv->cfg->phys[i].label;
> > +		struct samsung_usb2_phy_instance *p = &drv->instances[i];
> > +
> > +		dev_dbg(dev, "Creating phy \"%s\"\n", label);
> > +		p->phy = devm_phy_create(dev, &samsung_usb2_phy_ops, NULL);
> > +		if (IS_ERR(p->phy)) {
> > +			dev_err(drv->dev, "Failed to create usb2_phy
> \"%s\"\n",
> > +						label);
> > +			return PTR_ERR(p->phy);
> > +		}
> > +
> > +		p->cfg = &drv->cfg->phys[i];
> > +		p->drv = drv;
> > +		phy_set_bus_width(p->phy, 8);
> > +		phy_set_drvdata(p->phy, p);
> > +
> > +		clk = devm_clk_get(dev, p->cfg->label);
> > +		if (IS_ERR(clk)) {
> > +			dev_err(dev, "Failed to get clock of \"%s\" phy\n",
> > +
p->cfg->label);
> > +			return PTR_ERR(clk);
> > +		}
> > +		p->clk = clk;
> > +	}
> > +
> > +	phy_provider = devm_of_phy_provider_register(dev,
> > +
samsung_usb2_phy_xlate);
> > +	if (IS_ERR(phy_provider)) {
> > +		dev_err(drv->dev, "Failed to register phy provider\n");
> > +		return PTR_ERR(phy_provider);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static struct platform_driver samsung_usb2_phy_driver = {
> > +	.probe	= samsung_usb2_phy_probe,
> > +	.driver = {
> > +		.of_match_table	= samsung_usb2_phy_of_match,
> > +		.name		= "samsung-usb2-phy",
> > +		.owner		= THIS_MODULE,
> > +	}
> > +};
> > +
> > +module_platform_driver(samsung_usb2_phy_driver);
> > +MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver");
> > +MODULE_AUTHOR("Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>");
> > +MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:samsung-usb2-phy");
> > +
> > diff --git a/drivers/phy/phy-samsung-usb2.h
> > b/drivers/phy/phy-samsung-usb2.h new file mode 100644 index
> > 0000000..868dd33
> > --- /dev/null
> > +++ b/drivers/phy/phy-samsung-usb2.h
> > @@ -0,0 +1,67 @@
> > +/*
> > + * Samsung SoC USB 1.1/2.0 PHY driver
> > + *
> > + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
> > + * Author: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@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 _PHY_EXYNOS_USB2_H
> > +#define _PHY_EXYNOS_USB2_H
> > +
> > +#include <linux/clk.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/device.h>
> > +#include <linux/regmap.h>
> > +#include <linux/spinlock.h>
> > +
> > +#define KHZ 1000
> > +#define MHZ (KHZ * KHZ)
> > +
> > +struct samsung_usb2_phy_driver;
> > +struct samsung_usb2_phy_instance;
> > +struct samsung_usb2_phy_config;
> > +
> > +struct samsung_usb2_phy_instance {
> > +	const struct samsung_usb2_common_phy *cfg;
> > +	struct clk *clk;
> > +	struct phy *phy;
> > +	struct samsung_usb2_phy_driver *drv;
> > +	unsigned long rate;
> > +	u32 clk_reg_val;
> > +	bool enabled;
> > +};
> > +
> > +struct samsung_usb2_phy_driver {
> > +	const struct samsung_usb2_phy_config *cfg;
> > +	struct clk *clk;
> > +	struct device *dev;
> > +	void __iomem *reg_phy;
> > +	struct regmap *reg_pmu;
> > +	struct regmap *reg_sys;
> > +	spinlock_t lock;
> > +	struct samsung_usb2_phy_instance instances[0]; };
> > +
> > +struct samsung_usb2_common_phy {
> > +	int (*rate_to_clk)(unsigned long, u32 *);
> > +	int (*power_on)(struct samsung_usb2_phy_instance *);
> > +	int (*power_off)(struct samsung_usb2_phy_instance *);
> > +	unsigned int id;
> > +	char *label;
> > +};
> > +
> > +
> > +struct samsung_usb2_phy_config {
> > +	const struct samsung_usb2_common_phy *phys;
> > +	unsigned int num_phys;
> > +	bool has_mode_switch;
> > +};
> > +
> > +extern const struct samsung_usb2_phy_config
> > +exynos4210_usb2_phy_config; extern const struct
> > +samsung_usb2_phy_config exynos4x12_usb2_phy_config; #endif
> > +
> >

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2014-03-03 15:07 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-01-29 17:29 [PATCH v6 0/8] phy: Add new Exynos USB 2.0 PHY driver Kamil Debski
2014-01-29 17:29 ` [PATCH v6 1/8] phy: core: Add an exported of_phy_get function Kamil Debski
2014-01-29 17:29 ` [PATCH v6 2/8] phy: core: Add devm_of_phy_get to phy-core Kamil Debski
2014-01-29 17:29 ` [PATCH v6 3/8] dts: Add usb2phy to Exynos 4 Kamil Debski
2014-01-29 20:50   ` Olof Johansson
2014-01-29 20:50     ` Olof Johansson
2014-02-05 15:57     ` Kamil Debski
2014-01-29 17:29 ` [PATCH v6 4/8] dts: Add usb2phy to Exynos 5250 Kamil Debski
2014-01-29 17:29 ` [PATCH v6 5/8] phy: Add new Exynos USB PHY driver Kamil Debski
2014-03-03 14:27   ` Kishon Vijay Abraham I
2014-03-03 14:27     ` Kishon Vijay Abraham I
2014-03-03 15:07     ` Kamil Debski
2014-03-03 15:07       ` Kamil Debski
2014-01-29 17:29 ` [PATCH v6 6/8] phy: Add support for S5PV210 to the " Kamil Debski
2014-01-29 17:29   ` Kamil Debski
2014-01-29 17:29 ` [PATCH v6 7/8] phy: Add Exynos 5250 support to the Exynos USB 2.0 " Kamil Debski
2014-01-29 17:29 ` [PATCH v6 8/8] usb: ehci-exynos: Change to use phy provided by the generic phy framework Kamil Debski
2014-01-29 20:42   ` Alan Stern
2014-01-29 20:42     ` Alan Stern
2014-02-04 15:06     ` Tomasz Figa
2014-02-05 15:57     ` Kamil Debski
2014-02-05 19:55       ` Alan Stern
2014-02-05 19:55         ` Alan Stern
2014-01-29 20:55   ` Olof Johansson
2014-01-29 20:55     ` Olof Johansson
2014-02-05 15:57     ` Kamil Debski
2014-02-05 17:30       ` Olof Johansson
2014-02-05 17:30         ` Olof Johansson
2014-03-03 14:37         ` Tomasz Figa
2014-03-03 14:37           ` Tomasz Figa

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