All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] phy: Add new Exynos USB PHY driver
@ 2013-10-25 14:15 ` Kamil Debski
  0 siblings, 0 replies; 46+ messages in thread
From: Kamil Debski @ 2013-10-25 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-samsung-soc, linux-usb, devicetree, linux-arm
  Cc: kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, Kamil Debski

Hi,

This is the second version of the patch adding support for USB PHY module
of the Exynos series of SoCs by Samsung. The driver is utilising the newly
added Generic PHY Framework by Kishon Vijay Abraham I [1].

In addition to the PHY driver this patchset contains:
- work in progress support for Exynos 5250
  (based on the drivers/usb/phy/phy-samsung-usb2.c driver)
- support for S5PV210 added by Mateusz Krawczuk during his summer internship at
  Samsung
- change to the ehci-s5p driver which modifies the driver to use the General
  PHY Framework
- change to the s3c-hsotg driver which modifies the driver to use the General
  PHY Framework

Best wishes,
Kamil Debski
----------------
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 (4):
  phy: Add new Exynos USB PHY driver
  phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver
  usb: ehci-s5p: Change to use phy provided by the generic phy
    framework
  usb: s3c-hsotg: Use the new Exynos USB phy driver with the generic
    phy framework

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

 .../devicetree/bindings/phy/samsung-usbphy.txt     |   51 +++
 drivers/phy/Kconfig                                |   35 ++
 drivers/phy/Makefile                               |    4 +
 drivers/phy/phy-exynos-usb.c                       |  265 +++++++++++++
 drivers/phy/phy-exynos-usb.h                       |   96 +++++
 drivers/phy/phy-exynos4210-usb.c                   |  295 ++++++++++++++
 drivers/phy/phy-exynos4212-usb.c                   |  343 ++++++++++++++++
 drivers/phy/phy-exynos5250-usb.c                   |  411 ++++++++++++++++++++
 drivers/phy/phy-s5pv210-usb.c                      |  236 +++++++++++
 drivers/usb/gadget/s3c-hsotg.c                     |   13 +-
 drivers/usb/host/ehci-s5p.c                        |   21 +-
 11 files changed, 1755 insertions(+), 15 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/phy/samsung-usbphy.txt
 create mode 100644 drivers/phy/phy-exynos-usb.c
 create mode 100644 drivers/phy/phy-exynos-usb.h
 create mode 100644 drivers/phy/phy-exynos4210-usb.c
 create mode 100644 drivers/phy/phy-exynos4212-usb.c
 create mode 100644 drivers/phy/phy-exynos5250-usb.c
 create mode 100644 drivers/phy/phy-s5pv210-usb.c

-- 
1.7.9.5


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

* [PATCH 0/5] phy: Add new Exynos USB PHY driver
@ 2013-10-25 14:15 ` Kamil Debski
  0 siblings, 0 replies; 46+ messages in thread
From: Kamil Debski @ 2013-10-25 14:15 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-u79uwXL29TY76Z2rM5mHXA
  Cc: kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ, kishon-l0cyMroinI0,
	t.figa-Sze3O3UU22JBDgjK7y7TUQ, s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ,
	m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ,
	gautam.vivek-Sze3O3UU22JBDgjK7y7TUQ,
	mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w, Kamil Debski

Hi,

This is the second version of the patch adding support for USB PHY module
of the Exynos series of SoCs by Samsung. The driver is utilising the newly
added Generic PHY Framework by Kishon Vijay Abraham I [1].

In addition to the PHY driver this patchset contains:
- work in progress support for Exynos 5250
  (based on the drivers/usb/phy/phy-samsung-usb2.c driver)
- support for S5PV210 added by Mateusz Krawczuk during his summer internship at
  Samsung
- change to the ehci-s5p driver which modifies the driver to use the General
  PHY Framework
- change to the s3c-hsotg driver which modifies the driver to use the General
  PHY Framework

Best wishes,
Kamil Debski
----------------
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 (4):
  phy: Add new Exynos USB PHY driver
  phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver
  usb: ehci-s5p: Change to use phy provided by the generic phy
    framework
  usb: s3c-hsotg: Use the new Exynos USB phy driver with the generic
    phy framework

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

 .../devicetree/bindings/phy/samsung-usbphy.txt     |   51 +++
 drivers/phy/Kconfig                                |   35 ++
 drivers/phy/Makefile                               |    4 +
 drivers/phy/phy-exynos-usb.c                       |  265 +++++++++++++
 drivers/phy/phy-exynos-usb.h                       |   96 +++++
 drivers/phy/phy-exynos4210-usb.c                   |  295 ++++++++++++++
 drivers/phy/phy-exynos4212-usb.c                   |  343 ++++++++++++++++
 drivers/phy/phy-exynos5250-usb.c                   |  411 ++++++++++++++++++++
 drivers/phy/phy-s5pv210-usb.c                      |  236 +++++++++++
 drivers/usb/gadget/s3c-hsotg.c                     |   13 +-
 drivers/usb/host/ehci-s5p.c                        |   21 +-
 11 files changed, 1755 insertions(+), 15 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/phy/samsung-usbphy.txt
 create mode 100644 drivers/phy/phy-exynos-usb.c
 create mode 100644 drivers/phy/phy-exynos-usb.h
 create mode 100644 drivers/phy/phy-exynos4210-usb.c
 create mode 100644 drivers/phy/phy-exynos4212-usb.c
 create mode 100644 drivers/phy/phy-exynos5250-usb.c
 create mode 100644 drivers/phy/phy-s5pv210-usb.c

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

* [PATCH v2 1/5] phy: Add new Exynos USB PHY driver
@ 2013-10-25 14:15   ` Kamil Debski
  0 siblings, 0 replies; 46+ messages in thread
From: Kamil Debski @ 2013-10-25 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-samsung-soc, linux-usb, devicetree, linux-arm
  Cc: kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, Kamil Debski

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>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 .../devicetree/bindings/phy/samsung-usbphy.txt     |   51 +++
 drivers/phy/Kconfig                                |   21 ++
 drivers/phy/Makefile                               |    3 +
 drivers/phy/phy-exynos-usb.c                       |  245 ++++++++++++++
 drivers/phy/phy-exynos-usb.h                       |   94 ++++++
 drivers/phy/phy-exynos4210-usb.c                   |  295 +++++++++++++++++
 drivers/phy/phy-exynos4212-usb.c                   |  343 ++++++++++++++++++++
 7 files changed, 1052 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/samsung-usbphy.txt
 create mode 100644 drivers/phy/phy-exynos-usb.c
 create mode 100644 drivers/phy/phy-exynos-usb.h
 create mode 100644 drivers/phy/phy-exynos4210-usb.c
 create mode 100644 drivers/phy/phy-exynos4212-usb.c

diff --git a/Documentation/devicetree/bindings/phy/samsung-usbphy.txt b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
new file mode 100644
index 0000000..f112b37
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
@@ -0,0 +1,51 @@
+Samsung S5P/EXYNOS SoC series USB PHY
+-------------------------------------------------
+
+Required properties:
+- compatible : should be one of the listed compatibles:
+	- "samsung,exynos4210-usbphy"
+	- "samsung,exynos4212-usbphy"
+- reg : a list of registers used by phy driver
+	- first and obligatory is the location of phy modules registers
+	- second and also required is the location of isolation registers
+	  (isolation registers control the physical connection between the in
+	  SoC modules and outside of the SoC, this also can be called enable
+	  control in the documentation of the SoC)
+	- third is the location of the mode switch register, this only applies
+	  to SoCs that have such a feature; mode switching enables to have
+	  both host and device used the same SoC pins and is commonly used
+	  when OTG is supported
+- #phy-cells : from the generic phy bindings, must be 1;
+
+The second cell in the PHY specifier identifies the PHY its meaning is SoC
+dependent. For the currently supported SoCs (Exynos 4210 and Exynos 4212) it
+is as follows:
+  0 - USB device,
+  1 - USB host,
+  2 - HSIC0,
+  3 - HSIC1,
+
+Example:
+
+For Exynos 4412 (compatible with Exynos 4212):
+
+exynos_usbphy: exynos-usbphy@125B0000 {
+	compatible = "samsung,exynos4212-usbphy";
+	reg = <0x125B0000 0x100 0x10020704 0x0c 0x1001021c 0x4>;
+	ranges;
+	#address-cells = <1>;
+	#size-cells = <1>;
+	clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
+							<&clock 2>;
+	clock-names = "phy", "device", "host", "hsic0", "hsic1";
+	status = "okay";
+	#phy-cells = <1>;
+};
+
+Then the PHY can be used in other nodes such as:
+
+ehci@12580000 {
+	status = "okay";
+	phys = <&exynos_usbphy 2>;
+	phy-names = "hsic0";
+};
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 349bef2..2f7ac0a 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -15,4 +15,25 @@ config GENERIC_PHY
 	  phy users can obtain reference to the PHY. All the users of this
 	  framework should select this config.
 
+config PHY_EXYNOS_USB
+	tristate "Samsung USB PHY driver (using the Generic PHY Framework)"
+	help
+	  Enable this to support Samsung USB phy helper driver for Samsung SoCs.
+	  This driver provides common interface to interact, for Samsung
+	  USB 2.0 PHY driver.
+
+config PHY_EXYNOS4210_USB
+	bool "Support for Exynos 4210"
+	depends on PHY_EXYNOS_USB
+	depends on CPU_EXYNOS4210
+	help
+	  Enable USB PHY support for Exynos 4210
+
+config PHY_EXYNOS4212_USB
+	bool "Support for Exynos 4212"
+	depends on PHY_EXYNOS_USB
+	depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
+	help
+	  Enable USB PHY support for Exynos 4212
+
 endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 9e9560f..ca3dc82 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -3,3 +3,6 @@
 #
 
 obj-$(CONFIG_GENERIC_PHY)	+= phy-core.o
+obj-$(CONFIG_PHY_EXYNOS_USB)		+= phy-exynos-usb.o
+obj-$(CONFIG_PHY_EXYNOS4210_USB)	+= phy-exynos4210-usb.o
+obj-$(CONFIG_PHY_EXYNOS4212_USB)	+= phy-exynos4212-usb.o
diff --git a/drivers/phy/phy-exynos-usb.c b/drivers/phy/phy-exynos-usb.c
new file mode 100644
index 0000000..d4a26df
--- /dev/null
+++ b/drivers/phy/phy-exynos-usb.c
@@ -0,0 +1,245 @@
+/*
+ * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.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-exynos-usb.h"
+
+static int exynos_uphy_power_on(struct phy *phy)
+{
+	struct uphy_instance *inst = phy_get_drvdata(phy);
+	struct uphy_driver *drv = inst->drv;
+	int ret;
+
+	dev_info(drv->dev, "Request to power_on \"%s\" usb phy\n",
+							inst->cfg->label);
+	ret = clk_prepare_enable(drv->clk);
+	if (ret)
+		return ret;
+	if (inst->cfg->power_on) {
+		spin_lock(&drv->lock);
+		ret = inst->cfg->power_on(inst);
+		spin_unlock(&drv->lock);
+	}
+	clk_disable_unprepare(drv->clk);
+	return ret;
+}
+
+static int exynos_uphy_power_off(struct phy *phy)
+{
+	struct uphy_instance *inst = phy_get_drvdata(phy);
+	struct uphy_driver *drv = inst->drv;
+	int ret;
+
+	dev_info(drv->dev, "Request to power_off \"%s\" usb phy\n",
+							inst->cfg->label);
+	ret = clk_prepare_enable(drv->clk);
+	if (ret)
+		return ret;
+	if (inst->cfg->power_off) {
+		spin_lock(&drv->lock);
+		ret = inst->cfg->power_off(inst);
+		spin_unlock(&drv->lock);
+	}
+	clk_disable_unprepare(drv->clk);
+	return ret;
+}
+
+static struct phy_ops exynos_uphy_ops = {
+	.power_on	= exynos_uphy_power_on,
+	.power_off	= exynos_uphy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static struct phy *exynos_uphy_xlate(struct device *dev,
+					struct of_phandle_args *args)
+{
+	struct uphy_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->uphy_instances[args->args[0]].phy;
+}
+
+static const struct of_device_id exynos_uphy_of_match[];
+
+static int exynos_uphy_probe(struct platform_device *pdev)
+{
+	struct uphy_driver *drv;
+	struct device *dev = &pdev->dev;
+	struct resource *mem;
+	struct phy_provider *phy_provider;
+
+	const struct of_device_id *match;
+	const struct uphy_config *cfg;
+	struct clk *clk;
+
+	int i;
+
+	match = of_match_node(exynos_uphy_of_match, pdev->dev.of_node);
+	if (!match) {
+		dev_err(dev, "of_match_node() failed\n");
+		return -EINVAL;
+	}
+	cfg = match->data;
+	if (!cfg) {
+		dev_err(dev, "Failed to get configuration\n");
+		return -EINVAL;
+	}
+
+	drv = devm_kzalloc(dev, sizeof(struct uphy_driver) +
+		cfg->num_phys * sizeof(struct uphy_instance), GFP_KERNEL);
+
+	if (!drv) {
+		dev_err(dev, "Failed to allocate memory\n");
+		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);
+	}
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	drv->reg_isol = devm_ioremap_resource(dev, mem);
+	if (IS_ERR(drv->reg_isol)) {
+		dev_err(dev, "Failed to map register memory (isolation)\n");
+		return PTR_ERR(drv->reg_isol);
+	}
+
+	switch (drv->cfg->cpu) {
+	case TYPE_EXYNOS4210:
+	case TYPE_EXYNOS4212:
+		mem = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+		drv->reg_mode = devm_ioremap_resource(dev, mem);
+		if (IS_ERR(drv->reg_mode)) {
+			dev_err(dev, "Failed to map register memory (mode switch)\n");
+			return PTR_ERR(drv->reg_mode);
+		}
+		break;
+	default:
+		break;
+	}
+
+	phy_provider = devm_of_phy_provider_register(dev,
+							exynos_uphy_xlate);
+	if (IS_ERR(phy_provider)) {
+		dev_err(drv->dev, "Failed to register phy provider\n");
+		return PTR_ERR(phy_provider);
+	}
+
+	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 uphy_instance *p = &drv->uphy_instances[i];
+
+		dev_info(dev, "Creating phy \"%s\"\n", label);
+		p->phy = devm_phy_create(dev, &exynos_uphy_ops, NULL);
+		if (IS_ERR(p->phy)) {
+			dev_err(drv->dev, "Failed to create uphy \"%s\"\n",
+						label);
+			return PTR_ERR(p->phy);
+		}
+
+		p->cfg = &drv->cfg->phys[i];
+		p->drv = drv;
+		phy_set_drvdata(p->phy, p);
+
+		clk = 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->rate = clk_get_rate(clk);
+
+		if (p->cfg->rate_to_clk) {
+			p->clk = p->cfg->rate_to_clk(p->rate);
+			if (p->clk == CLKSEL_ERROR) {
+				dev_err(dev, "Clock rate (%ld) not supported\n",
+								p->rate);
+				clk_put(clk);
+				return -EINVAL;
+			}
+		}
+		clk_put(clk);
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PHY_EXYNOS4210_USB
+extern const struct uphy_config exynos4210_uphy_config;
+#endif
+
+#ifdef CONFIG_PHY_EXYNOS4212_USB
+extern const struct uphy_config exynos4212_uphy_config;
+#endif
+
+static const struct of_device_id exynos_uphy_of_match[] = {
+#ifdef CONFIG_PHY_EXYNOS4210_USB
+	{
+		.compatible = "samsung,exynos4210-usbphy",
+		.data = &exynos4210_uphy_config,
+	},
+#endif
+#ifdef CONFIG_PHY_EXYNOS4212_USB
+	{
+		.compatible = "samsung,exynos4212-usbphy",
+		.data = &exynos4212_uphy_config,
+	},
+#endif
+	{ },
+};
+
+static struct platform_driver exynos_uphy_driver = {
+	.probe	= exynos_uphy_probe,
+	.driver = {
+		.of_match_table	= exynos_uphy_of_match,
+		.name		= "exynos-usbphy-new",
+		.owner		= THIS_MODULE,
+	}
+};
+
+module_platform_driver(exynos_uphy_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:exynos-uphy-new");
+
diff --git a/drivers/phy/phy-exynos-usb.h b/drivers/phy/phy-exynos-usb.h
new file mode 100644
index 0000000..f45cb3c
--- /dev/null
+++ b/drivers/phy/phy-exynos-usb.h
@@ -0,0 +1,94 @@
+/*
+ * Samsung S5P/EXYNOS SoC series USB 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_SAMSUNG_NEW_H
+#define _PHY_SAMSUNG_NEW_H
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.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>
+
+#define CLKSEL_ERROR                       -1
+
+#ifndef KHZ
+#define KHZ 1000
+#endif
+
+#ifndef MHZ
+#define MHZ (KHZ * KHZ)
+#endif
+
+enum phy_type {
+	PHY_DEVICE,
+	PHY_HOST,
+};
+
+enum samsung_cpu_type {
+	TYPE_S3C64XX,
+	TYPE_EXYNOS4210,
+	TYPE_EXYNOS4212,
+};
+
+enum uphy_state {
+	STATE_OFF,
+	STATE_ON,
+};
+
+struct uphy_driver;
+struct uphy_instance;
+struct uphy_config;
+
+struct uphy_instance {
+	struct uphy_driver *drv;
+	struct phy *phy;
+	const struct common_phy *cfg;
+	enum uphy_state state;
+	int ref_cnt;
+	u32 clk;
+	unsigned long rate;
+};
+
+struct uphy_driver {
+	struct device *dev;
+	spinlock_t lock;
+	void __iomem *reg_phy;
+	void __iomem *reg_isol;
+	void __iomem *reg_mode;
+	const struct uphy_config *cfg;
+	struct clk *clk;
+	struct uphy_instance uphy_instances[0];
+};
+
+struct common_phy {
+	char *label;
+	enum phy_type type;
+	unsigned int id;
+	u32 (*rate_to_clk)(unsigned long);
+	int (*power_on)(struct uphy_instance*);
+	int (*power_off)(struct uphy_instance*);
+};
+
+
+struct uphy_config {
+	enum samsung_cpu_type cpu;
+	int num_phys;
+	const struct common_phy *phys;
+};
+
+#endif
+
diff --git a/drivers/phy/phy-exynos4210-usb.c b/drivers/phy/phy-exynos4210-usb.c
new file mode 100644
index 0000000..6cf74f7
--- /dev/null
+++ b/drivers/phy/phy-exynos4210-usb.c
@@ -0,0 +1,295 @@
+/*
+ * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.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-exynos-usb.h"
+
+/* Exynos USB PHY registers */
+
+/* PHY power control */
+#define EXYNOS_4210_UPHYPWR			0x0
+
+#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND	(1 << 0)
+#define EXYNOS_4210_UPHYPWR_PHY0_PWR		(1 << 3)
+#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR	(1 << 4)
+#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP		(1 << 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	(1 << 6)
+#define EXYNOS_4210_UPHYPWR_PHY1_PWR		(1 << 7)
+#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP		(1 << 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_HSCI0_SUSPEND	(1 << 9)
+#define EXYNOS_4210_UPHYPWR_HSCI0_SLEEP		(1 << 10)
+#define EXYNOS_4210_UPHYPWR_HSCI0 ( \
+	EXYNOS_4210_UPHYPWR_HSCI0_SUSPEND | \
+	EXYNOS_4210_UPHYPWR_HSCI0_SLEEP)
+
+#define EXYNOS_4210_UPHYPWR_HSCI1_SUSPEND	(1 << 11)
+#define EXYNOS_4210_UPHYPWR_HSCI1_SLEEP		(1 << 12)
+#define EXYNOS_4210_UPHYPWR_HSCI1 ( \
+	EXYNOS_4210_UPHYPWR_HSCI1_SUSPEND | \
+	EXYNOS_4210_UPHYPWR_HSCI1_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	(0x1 << 2)
+#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON	(0x1 << 4)
+#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON	(0x1 << 7)
+
+/* PHY reset control */
+#define EXYNOS_4210_UPHYRST			0x8
+
+#define EXYNOS_4210_URSTCON_PHY0		(1 << 0)
+#define EXYNOS_4210_URSTCON_OTG_HLINK		(1 << 1)
+#define EXYNOS_4210_URSTCON_OTG_PHYLINK		(1 << 2)
+#define EXYNOS_4210_URSTCON_PHY1_ALL		(1 << 3)
+#define EXYNOS_4210_URSTCON_PHY1_P0		(1 << 4)
+#define EXYNOS_4210_URSTCON_PHY1_P1P2		(1 << 5)
+#define EXYNOS_4210_URSTCON_HOST_LINK_ALL	(1 << 6)
+#define EXYNOS_4210_URSTCON_HOST_LINK_P0	(1 << 7)
+#define EXYNOS_4210_URSTCON_HOST_LINK_P1	(1 << 8)
+#define EXYNOS_4210_URSTCON_HOST_LINK_P2	(1 << 9)
+
+/* Isolation, configured in the power management unit */
+#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET	0x0
+#define EXYNOS_4210_USB_ISOL_DEVICE		(1 << 0)
+#define EXYNOS_4210_USB_ISOL_HOST_OFFSET	0x4
+#define EXYNOS_4210_USB_ISOL_HOST		(1 << 0)
+
+/* USBYPHY1 Floating prevention */
+#define EXYNOS_4210_UPHY1CON			0x34
+#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION	0x1
+
+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 u32 exynos4210_rate_to_clk(unsigned long rate)
+{
+	unsigned int clksel;
+
+	switch (rate) {
+	case 12 * MHZ:
+		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
+		break;
+	case 24 * MHZ:
+		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
+		break;
+	case 48 * MHZ:
+		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
+		break;
+	default:
+		clksel = CLKSEL_ERROR;
+	}
+
+	return clksel;
+}
+
+static void exynos4210_isol(struct uphy_instance *inst, bool on)
+{
+	struct uphy_driver *drv = inst->drv;
+	u32 offset;
+	u32 mask;
+	u32 tmp;
+
+	if (!drv->reg_isol)
+		return;
+
+	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;
+	};
+
+	tmp = readl(drv->reg_isol + offset);
+	if (on)
+		tmp &= ~mask;
+	else
+		tmp |= mask;
+	writel(tmp, drv->reg_isol + offset);
+}
+
+static void exynos4210_phy_pwr(struct uphy_instance *inst, bool on)
+{
+	struct uphy_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_HSCI0;
+		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
+				EXYNOS_4210_URSTCON_HOST_LINK_P1;
+		break;
+	case EXYNOS4210_HSIC1:
+		phypwr =	EXYNOS_4210_UPHYPWR_HSCI1;
+		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
+				EXYNOS_4210_URSTCON_HOST_LINK_P2;
+		break;
+	};
+
+	if (on) {
+		writel(inst->clk, 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 uphy_instance *inst)
+{
+	struct uphy_driver *drv = inst->drv;
+
+	if (inst->state == STATE_ON) {
+		dev_err(drv->dev, "usb phy \"%s\" already on",
+							inst->cfg->label);
+		return -ENODEV;
+	}
+	inst->state = STATE_ON;
+	inst->ref_cnt++;
+	if (inst->ref_cnt > 1)
+		return 0;
+
+	/* 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 uphy_instance *inst)
+{
+	struct uphy_driver *drv = inst->drv;
+
+	if (inst->state == STATE_OFF) {
+		dev_err(drv->dev, "usb phy \"%s\" already off",
+							inst->cfg->label);
+		return -EINVAL;
+	}
+
+	inst->state = STATE_OFF;
+	inst->ref_cnt--;
+	if (inst->ref_cnt > 0)
+		return 0;
+
+	exynos4210_isol(inst, 1);
+	exynos4210_phy_pwr(inst, 0);
+
+	return 0;
+}
+
+
+static const struct common_phy exynos4210_phys[] = {
+	{
+		.label		= "device",
+		.type		= PHY_DEVICE,
+		.id		= EXYNOS4210_DEVICE,
+		.rate_to_clk	= exynos4210_rate_to_clk,
+		.power_on	= exynos4210_power_on,
+		.power_off	= exynos4210_power_off,
+	},
+	{
+		.label		= "host",
+		.type		= PHY_HOST,
+		.id		= EXYNOS4210_HOST,
+		.rate_to_clk	= exynos4210_rate_to_clk,
+		.power_on	= exynos4210_power_on,
+		.power_off	= exynos4210_power_off,
+	},
+	{
+		.label		= "hsic0",
+		.type		= PHY_HOST,
+		.id		= EXYNOS4210_HSIC0,
+		.rate_to_clk	= exynos4210_rate_to_clk,
+		.power_on	= exynos4210_power_on,
+		.power_off	= exynos4210_power_off,
+	},
+	{
+		.label		= "hsic1",
+		.type		= PHY_HOST,
+		.id		= EXYNOS4210_HSIC1,
+		.rate_to_clk	= exynos4210_rate_to_clk,
+		.power_on	= exynos4210_power_on,
+		.power_off	= exynos4210_power_off,
+	},
+	{},
+};
+
+const struct uphy_config exynos4210_uphy_config = {
+	.cpu		= TYPE_EXYNOS4210,
+	.num_phys	= EXYNOS4210_NUM_PHYS,
+	.phys		= exynos4210_phys,
+};
+
diff --git a/drivers/phy/phy-exynos4212-usb.c b/drivers/phy/phy-exynos4212-usb.c
new file mode 100644
index 0000000..c07ae8e
--- /dev/null
+++ b/drivers/phy/phy-exynos4212-usb.c
@@ -0,0 +1,343 @@
+/*
+ * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.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-exynos-usb.h"
+
+/* Exynos USB PHY registers */
+
+/* PHY power control */
+#define EXYNOS_4212_UPHYPWR			0x0
+
+#define EXYNOS_4212_UPHYPWR_DEV_SUSPEND		(1 << 0)
+#define EXYNOS_4212_UPHYPWR_DEV_PWR		(1 << 3)
+#define EXYNOS_4212_UPHYPWR_DEV_OTG_PWR		(1 << 4)
+#define EXYNOS_4212_UPHYPWR_DEV_SLEEP		(1 << 5)
+#define EXYNOS_4212_UPHYPWR_DEV	( \
+	EXYNOS_4212_UPHYPWR_DEV_SUSPEND | \
+	EXYNOS_4212_UPHYPWR_DEV_PWR | \
+	EXYNOS_4212_UPHYPWR_DEV_OTG_PWR | \
+	EXYNOS_4212_UPHYPWR_DEV_SLEEP)
+
+#define EXYNOS_4212_UPHYPWR_HOST_SUSPEND	(1 << 6)
+#define EXYNOS_4212_UPHYPWR_HOST_PWR		(1 << 7)
+#define EXYNOS_4212_UPHYPWR_HOST_SLEEP		(1 << 8)
+#define EXYNOS_4212_UPHYPWR_HOST ( \
+	EXYNOS_4212_UPHYPWR_HOST_SUSPEND | \
+	EXYNOS_4212_UPHYPWR_HOST_PWR | \
+	EXYNOS_4212_UPHYPWR_HOST_SLEEP)
+
+#define EXYNOS_4212_UPHYPWR_HSCI0_SUSPEND	(1 << 9)
+#define EXYNOS_4212_UPHYPWR_HSCI0_PWR		(1 << 10)
+#define EXYNOS_4212_UPHYPWR_HSCI0_SLEEP		(1 << 11)
+#define EXYNOS_4212_UPHYPWR_HSCI0 ( \
+	EXYNOS_4212_UPHYPWR_HSCI0_SUSPEND | \
+	EXYNOS_4212_UPHYPWR_HSCI0_PWR | \
+	EXYNOS_4212_UPHYPWR_HSCI0_SLEEP)
+
+#define EXYNOS_4212_UPHYPWR_HSCI1_SUSPEND	(1 << 12)
+#define EXYNOS_4212_UPHYPWR_HSCI1_PWR		(1 << 13)
+#define EXYNOS_4212_UPHYPWR_HSCI1_SLEEP		(1 << 14)
+#define EXYNOS_4212_UPHYPWR_HSCI1 ( \
+	EXYNOS_4212_UPHYPWR_HSCI1_SUSPEND | \
+	EXYNOS_4212_UPHYPWR_HSCI1_PWR | \
+	EXYNOS_4212_UPHYPWR_HSCI1_SLEEP)
+
+/* PHY clock control */
+#define EXYNOS_4212_UPHYCLK			0x4
+
+#define EXYNOS_4212_UPHYCLK_PHYFSEL_MASK	(0x7 << 0)
+#define EXYNOS_4212_UPHYCLK_PHYFSEL_9MHZ6	(0x0 << 0)
+#define EXYNOS_4212_UPHYCLK_PHYFSEL_10MHZ	(0x1 << 0)
+#define EXYNOS_4212_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
+#define EXYNOS_4212_UPHYCLK_PHYFSEL_19MHZ2	(0x3 << 0)
+#define EXYNOS_4212_UPHYCLK_PHYFSEL_20MHZ	(0x4 << 0)
+#define EXYNOS_4212_UPHYCLK_PHYFSEL_24MHZ	(0x5 << 0)
+#define EXYNOS_4212_UPHYCLK_PHYFSEL_50MHZ	(0x7 << 0)
+
+#define EXYNOS_4212_UPHYCLK_PHY0_ID_PULLUP	(0x1 << 3)
+#define EXYNOS_4212_UPHYCLK_PHY0_COMMON_ON	(0x1 << 4)
+#define EXYNOS_4212_UPHYCLK_PHY1_COMMON_ON	(0x1 << 7)
+
+#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_MASK	(0x7f << 10)
+#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_12MHZ	(0x24 << 10)
+#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_15MHZ	(0x1c << 10)
+#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_16MHZ	(0x1a << 10)
+#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_19MHZ2	(0x15 << 10)
+#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_20MHZ	(0x14 << 10)
+
+/* PHY reset control */
+#define EXYNOS_4212_UPHYRST			0x8
+
+#define EXYNOS_4212_URSTCON_DEVICE		(1 << 0)
+#define EXYNOS_4212_URSTCON_OTG_HLINK		(1 << 1)
+#define EXYNOS_4212_URSTCON_OTG_PHYLINK		(1 << 2)
+#define EXYNOS_4212_URSTCON_HOST_PHY		(1 << 3)
+#define EXYNOS_4212_URSTCON_PHY1		(1 << 4)
+#define EXYNOS_4212_URSTCON_HSIC0		(1 << 5)
+#define EXYNOS_4212_URSTCON_HSIC1		(1 << 6)
+#define EXYNOS_4212_URSTCON_HOST_LINK_ALL	(1 << 7)
+#define EXYNOS_4212_URSTCON_HOST_LINK_P0	(1 << 8)
+#define EXYNOS_4212_URSTCON_HOST_LINK_P1	(1 << 9)
+#define EXYNOS_4212_URSTCON_HOST_LINK_P2	(1 << 10)
+
+/* Isolation, configured in the power management unit */
+#define EXYNOS_4212_USB_ISOL_OFFSET		0x0
+#define EXYNOS_4212_USB_ISOL_OTG		(1 << 0)
+#define EXYNOS_4212_USB_ISOL_HSIC0_OFFSET	0x4
+#define EXYNOS_4212_USB_ISOL_HSIC0		(1 << 0)
+#define EXYNOS_4212_USB_ISOL_HSIC1_OFFSET	0x8
+#define EXYNOS_4212_USB_ISOL_HSIC1		(1 << 0)
+
+enum exynos4x12_phy_id {
+	EXYNOS4212_DEVICE,
+	EXYNOS4212_HOST,
+	EXYNOS4212_HSIC0,
+	EXYNOS4212_HSIC1,
+	EXYNOS4212_NUM_PHYS,
+};
+
+/* exynos4212_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register. */
+static u32 exynos4212_rate_to_clk(unsigned long rate)
+{
+	unsigned int clksel;
+
+	/* EXYNOS_4212_UPHYCLK_PHYFSEL_MASK */
+
+	switch (rate) {
+	case 9600 * KHZ:
+		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_9MHZ6;
+		break;
+	case 10 * MHZ:
+		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_10MHZ;
+		break;
+	case 12 * MHZ:
+		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_12MHZ;
+		break;
+	case 19200 * KHZ:
+		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_19MHZ2;
+		break;
+	case 20 * MHZ:
+		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_20MHZ;
+		break;
+	case 24 * MHZ:
+		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_24MHZ;
+		break;
+	case 50 * MHZ:
+		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_50MHZ;
+		break;
+	default:
+		clksel = CLKSEL_ERROR;
+	}
+
+	return clksel;
+}
+
+static void exynos4212_isol(struct uphy_instance *inst, bool on)
+{
+	struct uphy_driver *drv = inst->drv;
+	u32 offset;
+	u32 mask;
+	u32 tmp;
+
+	if (!drv->reg_isol)
+		return;
+
+	switch (inst->cfg->id) {
+	case EXYNOS4212_DEVICE:
+		offset = EXYNOS_4212_USB_ISOL_OFFSET;
+		mask = EXYNOS_4212_USB_ISOL_OTG;
+		break;
+	case EXYNOS4212_HOST:
+		offset = EXYNOS_4212_USB_ISOL_OFFSET;
+		mask = EXYNOS_4212_USB_ISOL_OTG;
+		break;
+	case EXYNOS4212_HSIC0:
+		offset = EXYNOS_4212_USB_ISOL_HSIC0_OFFSET;
+		mask = EXYNOS_4212_USB_ISOL_HSIC0;
+		break;
+	case EXYNOS4212_HSIC1:
+		offset = EXYNOS_4212_USB_ISOL_HSIC1_OFFSET;
+		mask = EXYNOS_4212_USB_ISOL_HSIC1;
+		break;
+	default:
+		return;
+	};
+
+	tmp = readl(drv->reg_isol + offset);
+	if (on)
+		tmp &= ~mask;
+	else
+		tmp |= mask;
+	writel(tmp, drv->reg_isol + offset);
+}
+
+static void exynos4212_phy_pwr(struct uphy_instance *inst, bool on)
+{
+	struct uphy_driver *drv = inst->drv;
+	u32 rstbits = 0;
+	u32 phypwr = 0;
+	u32 rst;
+	u32 pwr;
+
+	switch (inst->cfg->id) {
+	case EXYNOS4212_DEVICE:
+		phypwr =	EXYNOS_4212_UPHYPWR_DEV;
+		rstbits =	EXYNOS_4212_URSTCON_DEVICE;
+		break;
+	case EXYNOS4212_HOST:
+		phypwr =	EXYNOS_4212_UPHYPWR_HOST;
+		rstbits =	EXYNOS_4212_URSTCON_HOST_PHY;
+		break;
+	case EXYNOS4212_HSIC0:
+		phypwr =	EXYNOS_4212_UPHYPWR_HSCI0;
+		rstbits =	EXYNOS_4212_URSTCON_HSIC1 |
+				EXYNOS_4212_URSTCON_HOST_LINK_P0 |
+				EXYNOS_4212_URSTCON_HOST_PHY;
+		break;
+	case EXYNOS4212_HSIC1:
+		phypwr =	EXYNOS_4212_UPHYPWR_HSCI1;
+		rstbits =	EXYNOS_4212_URSTCON_HSIC1 |
+				EXYNOS_4212_URSTCON_HOST_LINK_P1;
+		break;
+	};
+
+	if (on) {
+		writel(inst->clk, drv->reg_phy + EXYNOS_4212_UPHYCLK);
+
+		pwr = readl(drv->reg_phy + EXYNOS_4212_UPHYPWR);
+		pwr &= ~phypwr;
+		writel(pwr, drv->reg_phy + EXYNOS_4212_UPHYPWR);
+
+		rst = readl(drv->reg_phy + EXYNOS_4212_UPHYRST);
+		rst |= rstbits;
+		writel(rst, drv->reg_phy + EXYNOS_4212_UPHYRST);
+		udelay(10);
+		rst &= ~rstbits;
+		writel(rst, drv->reg_phy + EXYNOS_4212_UPHYRST);
+	} else {
+		pwr = readl(drv->reg_phy + EXYNOS_4212_UPHYPWR);
+		pwr |= phypwr;
+		writel(pwr, drv->reg_phy + EXYNOS_4212_UPHYPWR);
+	}
+}
+
+static int exynos4212_power_on(struct uphy_instance *inst)
+{
+	struct uphy_driver *drv = inst->drv;
+
+	if (inst->state == STATE_ON) {
+		dev_err(drv->dev, "usb phy \"%s\" already on",
+							inst->cfg->label);
+		return -ENODEV;
+	}
+
+	inst->state = STATE_ON;
+	inst->ref_cnt++;
+	if (inst->ref_cnt > 1)
+		return 0;
+
+	exynos4212_phy_pwr(inst, 1);
+	exynos4212_isol(inst, 0);
+
+	/* Power on the device, as it is necessary for HSIC to work */
+	if (inst->cfg->id == EXYNOS4212_HSIC0) {
+		struct uphy_instance *device =
+					&drv->uphy_instances[EXYNOS4212_DEVICE];
+		exynos4212_phy_pwr(device, 1);
+		exynos4212_isol(device, 0);
+	}
+
+	return 0;
+}
+
+static int exynos4212_power_off(struct uphy_instance *inst)
+{
+	struct uphy_driver *drv = inst->drv;
+
+	if (inst->state == STATE_OFF) {
+		dev_err(drv->dev, "usb phy \"%s\" already off",
+							inst->cfg->label);
+		return -EINVAL;
+	}
+
+	inst->state = STATE_OFF;
+	inst->ref_cnt--;
+
+	if (inst->ref_cnt > 0)
+		return 0;
+
+	exynos4212_isol(inst, 1);
+	exynos4212_phy_pwr(inst, 0);
+
+	if (inst->cfg->id == EXYNOS4212_HSIC0) {
+		struct uphy_instance *device =
+					&drv->uphy_instances[EXYNOS4212_DEVICE];
+		exynos4212_isol(device, 1);
+		exynos4212_phy_pwr(device, 0);
+	}
+
+	return 0;
+}
+
+
+static const struct common_phy exynos4212_phys[] = {
+	{
+		.label		= "device",
+		.type		= PHY_DEVICE,
+		.id		= EXYNOS4212_DEVICE,
+		.rate_to_clk	= exynos4212_rate_to_clk,
+		.power_on	= exynos4212_power_on,
+		.power_off	= exynos4212_power_off,
+	},
+	{
+		.label		= "host",
+		.type		= PHY_HOST,
+		.id		= EXYNOS4212_HOST,
+		.rate_to_clk	= exynos4212_rate_to_clk,
+		.power_on	= exynos4212_power_on,
+		.power_off	= exynos4212_power_off,
+	},
+	{
+		.label		= "hsic0",
+		.type		= PHY_HOST,
+		.id		= EXYNOS4212_HSIC0,
+		.rate_to_clk	= exynos4212_rate_to_clk,
+		.power_on	= exynos4212_power_on,
+		.power_off	= exynos4212_power_off,
+	},
+	{
+		.label		= "hsic1",
+		.type		= PHY_HOST,
+		.id		= EXYNOS4212_HSIC1,
+		.rate_to_clk	= exynos4212_rate_to_clk,
+		.power_on	= exynos4212_power_on,
+		.power_off	= exynos4212_power_off,
+	},
+	{},
+};
+
+const struct uphy_config exynos4212_uphy_config = {
+	.cpu		= TYPE_EXYNOS4212,
+	.num_phys	= EXYNOS4212_NUM_PHYS,
+	.phys		= exynos4212_phys,
+};
+
-- 
1.7.9.5


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

* [PATCH v2 1/5] phy: Add new Exynos USB PHY driver
@ 2013-10-25 14:15   ` Kamil Debski
  0 siblings, 0 replies; 46+ messages in thread
From: Kamil Debski @ 2013-10-25 14:15 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-u79uwXL29TY76Z2rM5mHXA
  Cc: kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ, kishon-l0cyMroinI0,
	t.figa-Sze3O3UU22JBDgjK7y7TUQ, s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ,
	m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ,
	gautam.vivek-Sze3O3UU22JBDgjK7y7TUQ,
	mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w, Kamil Debski

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-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Signed-off-by: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
 .../devicetree/bindings/phy/samsung-usbphy.txt     |   51 +++
 drivers/phy/Kconfig                                |   21 ++
 drivers/phy/Makefile                               |    3 +
 drivers/phy/phy-exynos-usb.c                       |  245 ++++++++++++++
 drivers/phy/phy-exynos-usb.h                       |   94 ++++++
 drivers/phy/phy-exynos4210-usb.c                   |  295 +++++++++++++++++
 drivers/phy/phy-exynos4212-usb.c                   |  343 ++++++++++++++++++++
 7 files changed, 1052 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/samsung-usbphy.txt
 create mode 100644 drivers/phy/phy-exynos-usb.c
 create mode 100644 drivers/phy/phy-exynos-usb.h
 create mode 100644 drivers/phy/phy-exynos4210-usb.c
 create mode 100644 drivers/phy/phy-exynos4212-usb.c

diff --git a/Documentation/devicetree/bindings/phy/samsung-usbphy.txt b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
new file mode 100644
index 0000000..f112b37
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
@@ -0,0 +1,51 @@
+Samsung S5P/EXYNOS SoC series USB PHY
+-------------------------------------------------
+
+Required properties:
+- compatible : should be one of the listed compatibles:
+	- "samsung,exynos4210-usbphy"
+	- "samsung,exynos4212-usbphy"
+- reg : a list of registers used by phy driver
+	- first and obligatory is the location of phy modules registers
+	- second and also required is the location of isolation registers
+	  (isolation registers control the physical connection between the in
+	  SoC modules and outside of the SoC, this also can be called enable
+	  control in the documentation of the SoC)
+	- third is the location of the mode switch register, this only applies
+	  to SoCs that have such a feature; mode switching enables to have
+	  both host and device used the same SoC pins and is commonly used
+	  when OTG is supported
+- #phy-cells : from the generic phy bindings, must be 1;
+
+The second cell in the PHY specifier identifies the PHY its meaning is SoC
+dependent. For the currently supported SoCs (Exynos 4210 and Exynos 4212) it
+is as follows:
+  0 - USB device,
+  1 - USB host,
+  2 - HSIC0,
+  3 - HSIC1,
+
+Example:
+
+For Exynos 4412 (compatible with Exynos 4212):
+
+exynos_usbphy: exynos-usbphy@125B0000 {
+	compatible = "samsung,exynos4212-usbphy";
+	reg = <0x125B0000 0x100 0x10020704 0x0c 0x1001021c 0x4>;
+	ranges;
+	#address-cells = <1>;
+	#size-cells = <1>;
+	clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
+							<&clock 2>;
+	clock-names = "phy", "device", "host", "hsic0", "hsic1";
+	status = "okay";
+	#phy-cells = <1>;
+};
+
+Then the PHY can be used in other nodes such as:
+
+ehci@12580000 {
+	status = "okay";
+	phys = <&exynos_usbphy 2>;
+	phy-names = "hsic0";
+};
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 349bef2..2f7ac0a 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -15,4 +15,25 @@ config GENERIC_PHY
 	  phy users can obtain reference to the PHY. All the users of this
 	  framework should select this config.
 
+config PHY_EXYNOS_USB
+	tristate "Samsung USB PHY driver (using the Generic PHY Framework)"
+	help
+	  Enable this to support Samsung USB phy helper driver for Samsung SoCs.
+	  This driver provides common interface to interact, for Samsung
+	  USB 2.0 PHY driver.
+
+config PHY_EXYNOS4210_USB
+	bool "Support for Exynos 4210"
+	depends on PHY_EXYNOS_USB
+	depends on CPU_EXYNOS4210
+	help
+	  Enable USB PHY support for Exynos 4210
+
+config PHY_EXYNOS4212_USB
+	bool "Support for Exynos 4212"
+	depends on PHY_EXYNOS_USB
+	depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
+	help
+	  Enable USB PHY support for Exynos 4212
+
 endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 9e9560f..ca3dc82 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -3,3 +3,6 @@
 #
 
 obj-$(CONFIG_GENERIC_PHY)	+= phy-core.o
+obj-$(CONFIG_PHY_EXYNOS_USB)		+= phy-exynos-usb.o
+obj-$(CONFIG_PHY_EXYNOS4210_USB)	+= phy-exynos4210-usb.o
+obj-$(CONFIG_PHY_EXYNOS4212_USB)	+= phy-exynos4212-usb.o
diff --git a/drivers/phy/phy-exynos-usb.c b/drivers/phy/phy-exynos-usb.c
new file mode 100644
index 0000000..d4a26df
--- /dev/null
+++ b/drivers/phy/phy-exynos-usb.c
@@ -0,0 +1,245 @@
+/*
+ * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.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-exynos-usb.h"
+
+static int exynos_uphy_power_on(struct phy *phy)
+{
+	struct uphy_instance *inst = phy_get_drvdata(phy);
+	struct uphy_driver *drv = inst->drv;
+	int ret;
+
+	dev_info(drv->dev, "Request to power_on \"%s\" usb phy\n",
+							inst->cfg->label);
+	ret = clk_prepare_enable(drv->clk);
+	if (ret)
+		return ret;
+	if (inst->cfg->power_on) {
+		spin_lock(&drv->lock);
+		ret = inst->cfg->power_on(inst);
+		spin_unlock(&drv->lock);
+	}
+	clk_disable_unprepare(drv->clk);
+	return ret;
+}
+
+static int exynos_uphy_power_off(struct phy *phy)
+{
+	struct uphy_instance *inst = phy_get_drvdata(phy);
+	struct uphy_driver *drv = inst->drv;
+	int ret;
+
+	dev_info(drv->dev, "Request to power_off \"%s\" usb phy\n",
+							inst->cfg->label);
+	ret = clk_prepare_enable(drv->clk);
+	if (ret)
+		return ret;
+	if (inst->cfg->power_off) {
+		spin_lock(&drv->lock);
+		ret = inst->cfg->power_off(inst);
+		spin_unlock(&drv->lock);
+	}
+	clk_disable_unprepare(drv->clk);
+	return ret;
+}
+
+static struct phy_ops exynos_uphy_ops = {
+	.power_on	= exynos_uphy_power_on,
+	.power_off	= exynos_uphy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static struct phy *exynos_uphy_xlate(struct device *dev,
+					struct of_phandle_args *args)
+{
+	struct uphy_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->uphy_instances[args->args[0]].phy;
+}
+
+static const struct of_device_id exynos_uphy_of_match[];
+
+static int exynos_uphy_probe(struct platform_device *pdev)
+{
+	struct uphy_driver *drv;
+	struct device *dev = &pdev->dev;
+	struct resource *mem;
+	struct phy_provider *phy_provider;
+
+	const struct of_device_id *match;
+	const struct uphy_config *cfg;
+	struct clk *clk;
+
+	int i;
+
+	match = of_match_node(exynos_uphy_of_match, pdev->dev.of_node);
+	if (!match) {
+		dev_err(dev, "of_match_node() failed\n");
+		return -EINVAL;
+	}
+	cfg = match->data;
+	if (!cfg) {
+		dev_err(dev, "Failed to get configuration\n");
+		return -EINVAL;
+	}
+
+	drv = devm_kzalloc(dev, sizeof(struct uphy_driver) +
+		cfg->num_phys * sizeof(struct uphy_instance), GFP_KERNEL);
+
+	if (!drv) {
+		dev_err(dev, "Failed to allocate memory\n");
+		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);
+	}
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	drv->reg_isol = devm_ioremap_resource(dev, mem);
+	if (IS_ERR(drv->reg_isol)) {
+		dev_err(dev, "Failed to map register memory (isolation)\n");
+		return PTR_ERR(drv->reg_isol);
+	}
+
+	switch (drv->cfg->cpu) {
+	case TYPE_EXYNOS4210:
+	case TYPE_EXYNOS4212:
+		mem = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+		drv->reg_mode = devm_ioremap_resource(dev, mem);
+		if (IS_ERR(drv->reg_mode)) {
+			dev_err(dev, "Failed to map register memory (mode switch)\n");
+			return PTR_ERR(drv->reg_mode);
+		}
+		break;
+	default:
+		break;
+	}
+
+	phy_provider = devm_of_phy_provider_register(dev,
+							exynos_uphy_xlate);
+	if (IS_ERR(phy_provider)) {
+		dev_err(drv->dev, "Failed to register phy provider\n");
+		return PTR_ERR(phy_provider);
+	}
+
+	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 uphy_instance *p = &drv->uphy_instances[i];
+
+		dev_info(dev, "Creating phy \"%s\"\n", label);
+		p->phy = devm_phy_create(dev, &exynos_uphy_ops, NULL);
+		if (IS_ERR(p->phy)) {
+			dev_err(drv->dev, "Failed to create uphy \"%s\"\n",
+						label);
+			return PTR_ERR(p->phy);
+		}
+
+		p->cfg = &drv->cfg->phys[i];
+		p->drv = drv;
+		phy_set_drvdata(p->phy, p);
+
+		clk = 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->rate = clk_get_rate(clk);
+
+		if (p->cfg->rate_to_clk) {
+			p->clk = p->cfg->rate_to_clk(p->rate);
+			if (p->clk == CLKSEL_ERROR) {
+				dev_err(dev, "Clock rate (%ld) not supported\n",
+								p->rate);
+				clk_put(clk);
+				return -EINVAL;
+			}
+		}
+		clk_put(clk);
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PHY_EXYNOS4210_USB
+extern const struct uphy_config exynos4210_uphy_config;
+#endif
+
+#ifdef CONFIG_PHY_EXYNOS4212_USB
+extern const struct uphy_config exynos4212_uphy_config;
+#endif
+
+static const struct of_device_id exynos_uphy_of_match[] = {
+#ifdef CONFIG_PHY_EXYNOS4210_USB
+	{
+		.compatible = "samsung,exynos4210-usbphy",
+		.data = &exynos4210_uphy_config,
+	},
+#endif
+#ifdef CONFIG_PHY_EXYNOS4212_USB
+	{
+		.compatible = "samsung,exynos4212-usbphy",
+		.data = &exynos4212_uphy_config,
+	},
+#endif
+	{ },
+};
+
+static struct platform_driver exynos_uphy_driver = {
+	.probe	= exynos_uphy_probe,
+	.driver = {
+		.of_match_table	= exynos_uphy_of_match,
+		.name		= "exynos-usbphy-new",
+		.owner		= THIS_MODULE,
+	}
+};
+
+module_platform_driver(exynos_uphy_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:exynos-uphy-new");
+
diff --git a/drivers/phy/phy-exynos-usb.h b/drivers/phy/phy-exynos-usb.h
new file mode 100644
index 0000000..f45cb3c
--- /dev/null
+++ b/drivers/phy/phy-exynos-usb.h
@@ -0,0 +1,94 @@
+/*
+ * Samsung S5P/EXYNOS SoC series USB 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_SAMSUNG_NEW_H
+#define _PHY_SAMSUNG_NEW_H
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.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>
+
+#define CLKSEL_ERROR                       -1
+
+#ifndef KHZ
+#define KHZ 1000
+#endif
+
+#ifndef MHZ
+#define MHZ (KHZ * KHZ)
+#endif
+
+enum phy_type {
+	PHY_DEVICE,
+	PHY_HOST,
+};
+
+enum samsung_cpu_type {
+	TYPE_S3C64XX,
+	TYPE_EXYNOS4210,
+	TYPE_EXYNOS4212,
+};
+
+enum uphy_state {
+	STATE_OFF,
+	STATE_ON,
+};
+
+struct uphy_driver;
+struct uphy_instance;
+struct uphy_config;
+
+struct uphy_instance {
+	struct uphy_driver *drv;
+	struct phy *phy;
+	const struct common_phy *cfg;
+	enum uphy_state state;
+	int ref_cnt;
+	u32 clk;
+	unsigned long rate;
+};
+
+struct uphy_driver {
+	struct device *dev;
+	spinlock_t lock;
+	void __iomem *reg_phy;
+	void __iomem *reg_isol;
+	void __iomem *reg_mode;
+	const struct uphy_config *cfg;
+	struct clk *clk;
+	struct uphy_instance uphy_instances[0];
+};
+
+struct common_phy {
+	char *label;
+	enum phy_type type;
+	unsigned int id;
+	u32 (*rate_to_clk)(unsigned long);
+	int (*power_on)(struct uphy_instance*);
+	int (*power_off)(struct uphy_instance*);
+};
+
+
+struct uphy_config {
+	enum samsung_cpu_type cpu;
+	int num_phys;
+	const struct common_phy *phys;
+};
+
+#endif
+
diff --git a/drivers/phy/phy-exynos4210-usb.c b/drivers/phy/phy-exynos4210-usb.c
new file mode 100644
index 0000000..6cf74f7
--- /dev/null
+++ b/drivers/phy/phy-exynos4210-usb.c
@@ -0,0 +1,295 @@
+/*
+ * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.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-exynos-usb.h"
+
+/* Exynos USB PHY registers */
+
+/* PHY power control */
+#define EXYNOS_4210_UPHYPWR			0x0
+
+#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND	(1 << 0)
+#define EXYNOS_4210_UPHYPWR_PHY0_PWR		(1 << 3)
+#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR	(1 << 4)
+#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP		(1 << 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	(1 << 6)
+#define EXYNOS_4210_UPHYPWR_PHY1_PWR		(1 << 7)
+#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP		(1 << 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_HSCI0_SUSPEND	(1 << 9)
+#define EXYNOS_4210_UPHYPWR_HSCI0_SLEEP		(1 << 10)
+#define EXYNOS_4210_UPHYPWR_HSCI0 ( \
+	EXYNOS_4210_UPHYPWR_HSCI0_SUSPEND | \
+	EXYNOS_4210_UPHYPWR_HSCI0_SLEEP)
+
+#define EXYNOS_4210_UPHYPWR_HSCI1_SUSPEND	(1 << 11)
+#define EXYNOS_4210_UPHYPWR_HSCI1_SLEEP		(1 << 12)
+#define EXYNOS_4210_UPHYPWR_HSCI1 ( \
+	EXYNOS_4210_UPHYPWR_HSCI1_SUSPEND | \
+	EXYNOS_4210_UPHYPWR_HSCI1_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	(0x1 << 2)
+#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON	(0x1 << 4)
+#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON	(0x1 << 7)
+
+/* PHY reset control */
+#define EXYNOS_4210_UPHYRST			0x8
+
+#define EXYNOS_4210_URSTCON_PHY0		(1 << 0)
+#define EXYNOS_4210_URSTCON_OTG_HLINK		(1 << 1)
+#define EXYNOS_4210_URSTCON_OTG_PHYLINK		(1 << 2)
+#define EXYNOS_4210_URSTCON_PHY1_ALL		(1 << 3)
+#define EXYNOS_4210_URSTCON_PHY1_P0		(1 << 4)
+#define EXYNOS_4210_URSTCON_PHY1_P1P2		(1 << 5)
+#define EXYNOS_4210_URSTCON_HOST_LINK_ALL	(1 << 6)
+#define EXYNOS_4210_URSTCON_HOST_LINK_P0	(1 << 7)
+#define EXYNOS_4210_URSTCON_HOST_LINK_P1	(1 << 8)
+#define EXYNOS_4210_URSTCON_HOST_LINK_P2	(1 << 9)
+
+/* Isolation, configured in the power management unit */
+#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET	0x0
+#define EXYNOS_4210_USB_ISOL_DEVICE		(1 << 0)
+#define EXYNOS_4210_USB_ISOL_HOST_OFFSET	0x4
+#define EXYNOS_4210_USB_ISOL_HOST		(1 << 0)
+
+/* USBYPHY1 Floating prevention */
+#define EXYNOS_4210_UPHY1CON			0x34
+#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION	0x1
+
+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 u32 exynos4210_rate_to_clk(unsigned long rate)
+{
+	unsigned int clksel;
+
+	switch (rate) {
+	case 12 * MHZ:
+		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
+		break;
+	case 24 * MHZ:
+		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
+		break;
+	case 48 * MHZ:
+		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
+		break;
+	default:
+		clksel = CLKSEL_ERROR;
+	}
+
+	return clksel;
+}
+
+static void exynos4210_isol(struct uphy_instance *inst, bool on)
+{
+	struct uphy_driver *drv = inst->drv;
+	u32 offset;
+	u32 mask;
+	u32 tmp;
+
+	if (!drv->reg_isol)
+		return;
+
+	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;
+	};
+
+	tmp = readl(drv->reg_isol + offset);
+	if (on)
+		tmp &= ~mask;
+	else
+		tmp |= mask;
+	writel(tmp, drv->reg_isol + offset);
+}
+
+static void exynos4210_phy_pwr(struct uphy_instance *inst, bool on)
+{
+	struct uphy_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_HSCI0;
+		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
+				EXYNOS_4210_URSTCON_HOST_LINK_P1;
+		break;
+	case EXYNOS4210_HSIC1:
+		phypwr =	EXYNOS_4210_UPHYPWR_HSCI1;
+		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
+				EXYNOS_4210_URSTCON_HOST_LINK_P2;
+		break;
+	};
+
+	if (on) {
+		writel(inst->clk, 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 uphy_instance *inst)
+{
+	struct uphy_driver *drv = inst->drv;
+
+	if (inst->state == STATE_ON) {
+		dev_err(drv->dev, "usb phy \"%s\" already on",
+							inst->cfg->label);
+		return -ENODEV;
+	}
+	inst->state = STATE_ON;
+	inst->ref_cnt++;
+	if (inst->ref_cnt > 1)
+		return 0;
+
+	/* 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 uphy_instance *inst)
+{
+	struct uphy_driver *drv = inst->drv;
+
+	if (inst->state == STATE_OFF) {
+		dev_err(drv->dev, "usb phy \"%s\" already off",
+							inst->cfg->label);
+		return -EINVAL;
+	}
+
+	inst->state = STATE_OFF;
+	inst->ref_cnt--;
+	if (inst->ref_cnt > 0)
+		return 0;
+
+	exynos4210_isol(inst, 1);
+	exynos4210_phy_pwr(inst, 0);
+
+	return 0;
+}
+
+
+static const struct common_phy exynos4210_phys[] = {
+	{
+		.label		= "device",
+		.type		= PHY_DEVICE,
+		.id		= EXYNOS4210_DEVICE,
+		.rate_to_clk	= exynos4210_rate_to_clk,
+		.power_on	= exynos4210_power_on,
+		.power_off	= exynos4210_power_off,
+	},
+	{
+		.label		= "host",
+		.type		= PHY_HOST,
+		.id		= EXYNOS4210_HOST,
+		.rate_to_clk	= exynos4210_rate_to_clk,
+		.power_on	= exynos4210_power_on,
+		.power_off	= exynos4210_power_off,
+	},
+	{
+		.label		= "hsic0",
+		.type		= PHY_HOST,
+		.id		= EXYNOS4210_HSIC0,
+		.rate_to_clk	= exynos4210_rate_to_clk,
+		.power_on	= exynos4210_power_on,
+		.power_off	= exynos4210_power_off,
+	},
+	{
+		.label		= "hsic1",
+		.type		= PHY_HOST,
+		.id		= EXYNOS4210_HSIC1,
+		.rate_to_clk	= exynos4210_rate_to_clk,
+		.power_on	= exynos4210_power_on,
+		.power_off	= exynos4210_power_off,
+	},
+	{},
+};
+
+const struct uphy_config exynos4210_uphy_config = {
+	.cpu		= TYPE_EXYNOS4210,
+	.num_phys	= EXYNOS4210_NUM_PHYS,
+	.phys		= exynos4210_phys,
+};
+
diff --git a/drivers/phy/phy-exynos4212-usb.c b/drivers/phy/phy-exynos4212-usb.c
new file mode 100644
index 0000000..c07ae8e
--- /dev/null
+++ b/drivers/phy/phy-exynos4212-usb.c
@@ -0,0 +1,343 @@
+/*
+ * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.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-exynos-usb.h"
+
+/* Exynos USB PHY registers */
+
+/* PHY power control */
+#define EXYNOS_4212_UPHYPWR			0x0
+
+#define EXYNOS_4212_UPHYPWR_DEV_SUSPEND		(1 << 0)
+#define EXYNOS_4212_UPHYPWR_DEV_PWR		(1 << 3)
+#define EXYNOS_4212_UPHYPWR_DEV_OTG_PWR		(1 << 4)
+#define EXYNOS_4212_UPHYPWR_DEV_SLEEP		(1 << 5)
+#define EXYNOS_4212_UPHYPWR_DEV	( \
+	EXYNOS_4212_UPHYPWR_DEV_SUSPEND | \
+	EXYNOS_4212_UPHYPWR_DEV_PWR | \
+	EXYNOS_4212_UPHYPWR_DEV_OTG_PWR | \
+	EXYNOS_4212_UPHYPWR_DEV_SLEEP)
+
+#define EXYNOS_4212_UPHYPWR_HOST_SUSPEND	(1 << 6)
+#define EXYNOS_4212_UPHYPWR_HOST_PWR		(1 << 7)
+#define EXYNOS_4212_UPHYPWR_HOST_SLEEP		(1 << 8)
+#define EXYNOS_4212_UPHYPWR_HOST ( \
+	EXYNOS_4212_UPHYPWR_HOST_SUSPEND | \
+	EXYNOS_4212_UPHYPWR_HOST_PWR | \
+	EXYNOS_4212_UPHYPWR_HOST_SLEEP)
+
+#define EXYNOS_4212_UPHYPWR_HSCI0_SUSPEND	(1 << 9)
+#define EXYNOS_4212_UPHYPWR_HSCI0_PWR		(1 << 10)
+#define EXYNOS_4212_UPHYPWR_HSCI0_SLEEP		(1 << 11)
+#define EXYNOS_4212_UPHYPWR_HSCI0 ( \
+	EXYNOS_4212_UPHYPWR_HSCI0_SUSPEND | \
+	EXYNOS_4212_UPHYPWR_HSCI0_PWR | \
+	EXYNOS_4212_UPHYPWR_HSCI0_SLEEP)
+
+#define EXYNOS_4212_UPHYPWR_HSCI1_SUSPEND	(1 << 12)
+#define EXYNOS_4212_UPHYPWR_HSCI1_PWR		(1 << 13)
+#define EXYNOS_4212_UPHYPWR_HSCI1_SLEEP		(1 << 14)
+#define EXYNOS_4212_UPHYPWR_HSCI1 ( \
+	EXYNOS_4212_UPHYPWR_HSCI1_SUSPEND | \
+	EXYNOS_4212_UPHYPWR_HSCI1_PWR | \
+	EXYNOS_4212_UPHYPWR_HSCI1_SLEEP)
+
+/* PHY clock control */
+#define EXYNOS_4212_UPHYCLK			0x4
+
+#define EXYNOS_4212_UPHYCLK_PHYFSEL_MASK	(0x7 << 0)
+#define EXYNOS_4212_UPHYCLK_PHYFSEL_9MHZ6	(0x0 << 0)
+#define EXYNOS_4212_UPHYCLK_PHYFSEL_10MHZ	(0x1 << 0)
+#define EXYNOS_4212_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
+#define EXYNOS_4212_UPHYCLK_PHYFSEL_19MHZ2	(0x3 << 0)
+#define EXYNOS_4212_UPHYCLK_PHYFSEL_20MHZ	(0x4 << 0)
+#define EXYNOS_4212_UPHYCLK_PHYFSEL_24MHZ	(0x5 << 0)
+#define EXYNOS_4212_UPHYCLK_PHYFSEL_50MHZ	(0x7 << 0)
+
+#define EXYNOS_4212_UPHYCLK_PHY0_ID_PULLUP	(0x1 << 3)
+#define EXYNOS_4212_UPHYCLK_PHY0_COMMON_ON	(0x1 << 4)
+#define EXYNOS_4212_UPHYCLK_PHY1_COMMON_ON	(0x1 << 7)
+
+#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_MASK	(0x7f << 10)
+#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_12MHZ	(0x24 << 10)
+#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_15MHZ	(0x1c << 10)
+#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_16MHZ	(0x1a << 10)
+#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_19MHZ2	(0x15 << 10)
+#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_20MHZ	(0x14 << 10)
+
+/* PHY reset control */
+#define EXYNOS_4212_UPHYRST			0x8
+
+#define EXYNOS_4212_URSTCON_DEVICE		(1 << 0)
+#define EXYNOS_4212_URSTCON_OTG_HLINK		(1 << 1)
+#define EXYNOS_4212_URSTCON_OTG_PHYLINK		(1 << 2)
+#define EXYNOS_4212_URSTCON_HOST_PHY		(1 << 3)
+#define EXYNOS_4212_URSTCON_PHY1		(1 << 4)
+#define EXYNOS_4212_URSTCON_HSIC0		(1 << 5)
+#define EXYNOS_4212_URSTCON_HSIC1		(1 << 6)
+#define EXYNOS_4212_URSTCON_HOST_LINK_ALL	(1 << 7)
+#define EXYNOS_4212_URSTCON_HOST_LINK_P0	(1 << 8)
+#define EXYNOS_4212_URSTCON_HOST_LINK_P1	(1 << 9)
+#define EXYNOS_4212_URSTCON_HOST_LINK_P2	(1 << 10)
+
+/* Isolation, configured in the power management unit */
+#define EXYNOS_4212_USB_ISOL_OFFSET		0x0
+#define EXYNOS_4212_USB_ISOL_OTG		(1 << 0)
+#define EXYNOS_4212_USB_ISOL_HSIC0_OFFSET	0x4
+#define EXYNOS_4212_USB_ISOL_HSIC0		(1 << 0)
+#define EXYNOS_4212_USB_ISOL_HSIC1_OFFSET	0x8
+#define EXYNOS_4212_USB_ISOL_HSIC1		(1 << 0)
+
+enum exynos4x12_phy_id {
+	EXYNOS4212_DEVICE,
+	EXYNOS4212_HOST,
+	EXYNOS4212_HSIC0,
+	EXYNOS4212_HSIC1,
+	EXYNOS4212_NUM_PHYS,
+};
+
+/* exynos4212_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register. */
+static u32 exynos4212_rate_to_clk(unsigned long rate)
+{
+	unsigned int clksel;
+
+	/* EXYNOS_4212_UPHYCLK_PHYFSEL_MASK */
+
+	switch (rate) {
+	case 9600 * KHZ:
+		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_9MHZ6;
+		break;
+	case 10 * MHZ:
+		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_10MHZ;
+		break;
+	case 12 * MHZ:
+		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_12MHZ;
+		break;
+	case 19200 * KHZ:
+		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_19MHZ2;
+		break;
+	case 20 * MHZ:
+		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_20MHZ;
+		break;
+	case 24 * MHZ:
+		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_24MHZ;
+		break;
+	case 50 * MHZ:
+		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_50MHZ;
+		break;
+	default:
+		clksel = CLKSEL_ERROR;
+	}
+
+	return clksel;
+}
+
+static void exynos4212_isol(struct uphy_instance *inst, bool on)
+{
+	struct uphy_driver *drv = inst->drv;
+	u32 offset;
+	u32 mask;
+	u32 tmp;
+
+	if (!drv->reg_isol)
+		return;
+
+	switch (inst->cfg->id) {
+	case EXYNOS4212_DEVICE:
+		offset = EXYNOS_4212_USB_ISOL_OFFSET;
+		mask = EXYNOS_4212_USB_ISOL_OTG;
+		break;
+	case EXYNOS4212_HOST:
+		offset = EXYNOS_4212_USB_ISOL_OFFSET;
+		mask = EXYNOS_4212_USB_ISOL_OTG;
+		break;
+	case EXYNOS4212_HSIC0:
+		offset = EXYNOS_4212_USB_ISOL_HSIC0_OFFSET;
+		mask = EXYNOS_4212_USB_ISOL_HSIC0;
+		break;
+	case EXYNOS4212_HSIC1:
+		offset = EXYNOS_4212_USB_ISOL_HSIC1_OFFSET;
+		mask = EXYNOS_4212_USB_ISOL_HSIC1;
+		break;
+	default:
+		return;
+	};
+
+	tmp = readl(drv->reg_isol + offset);
+	if (on)
+		tmp &= ~mask;
+	else
+		tmp |= mask;
+	writel(tmp, drv->reg_isol + offset);
+}
+
+static void exynos4212_phy_pwr(struct uphy_instance *inst, bool on)
+{
+	struct uphy_driver *drv = inst->drv;
+	u32 rstbits = 0;
+	u32 phypwr = 0;
+	u32 rst;
+	u32 pwr;
+
+	switch (inst->cfg->id) {
+	case EXYNOS4212_DEVICE:
+		phypwr =	EXYNOS_4212_UPHYPWR_DEV;
+		rstbits =	EXYNOS_4212_URSTCON_DEVICE;
+		break;
+	case EXYNOS4212_HOST:
+		phypwr =	EXYNOS_4212_UPHYPWR_HOST;
+		rstbits =	EXYNOS_4212_URSTCON_HOST_PHY;
+		break;
+	case EXYNOS4212_HSIC0:
+		phypwr =	EXYNOS_4212_UPHYPWR_HSCI0;
+		rstbits =	EXYNOS_4212_URSTCON_HSIC1 |
+				EXYNOS_4212_URSTCON_HOST_LINK_P0 |
+				EXYNOS_4212_URSTCON_HOST_PHY;
+		break;
+	case EXYNOS4212_HSIC1:
+		phypwr =	EXYNOS_4212_UPHYPWR_HSCI1;
+		rstbits =	EXYNOS_4212_URSTCON_HSIC1 |
+				EXYNOS_4212_URSTCON_HOST_LINK_P1;
+		break;
+	};
+
+	if (on) {
+		writel(inst->clk, drv->reg_phy + EXYNOS_4212_UPHYCLK);
+
+		pwr = readl(drv->reg_phy + EXYNOS_4212_UPHYPWR);
+		pwr &= ~phypwr;
+		writel(pwr, drv->reg_phy + EXYNOS_4212_UPHYPWR);
+
+		rst = readl(drv->reg_phy + EXYNOS_4212_UPHYRST);
+		rst |= rstbits;
+		writel(rst, drv->reg_phy + EXYNOS_4212_UPHYRST);
+		udelay(10);
+		rst &= ~rstbits;
+		writel(rst, drv->reg_phy + EXYNOS_4212_UPHYRST);
+	} else {
+		pwr = readl(drv->reg_phy + EXYNOS_4212_UPHYPWR);
+		pwr |= phypwr;
+		writel(pwr, drv->reg_phy + EXYNOS_4212_UPHYPWR);
+	}
+}
+
+static int exynos4212_power_on(struct uphy_instance *inst)
+{
+	struct uphy_driver *drv = inst->drv;
+
+	if (inst->state == STATE_ON) {
+		dev_err(drv->dev, "usb phy \"%s\" already on",
+							inst->cfg->label);
+		return -ENODEV;
+	}
+
+	inst->state = STATE_ON;
+	inst->ref_cnt++;
+	if (inst->ref_cnt > 1)
+		return 0;
+
+	exynos4212_phy_pwr(inst, 1);
+	exynos4212_isol(inst, 0);
+
+	/* Power on the device, as it is necessary for HSIC to work */
+	if (inst->cfg->id == EXYNOS4212_HSIC0) {
+		struct uphy_instance *device =
+					&drv->uphy_instances[EXYNOS4212_DEVICE];
+		exynos4212_phy_pwr(device, 1);
+		exynos4212_isol(device, 0);
+	}
+
+	return 0;
+}
+
+static int exynos4212_power_off(struct uphy_instance *inst)
+{
+	struct uphy_driver *drv = inst->drv;
+
+	if (inst->state == STATE_OFF) {
+		dev_err(drv->dev, "usb phy \"%s\" already off",
+							inst->cfg->label);
+		return -EINVAL;
+	}
+
+	inst->state = STATE_OFF;
+	inst->ref_cnt--;
+
+	if (inst->ref_cnt > 0)
+		return 0;
+
+	exynos4212_isol(inst, 1);
+	exynos4212_phy_pwr(inst, 0);
+
+	if (inst->cfg->id == EXYNOS4212_HSIC0) {
+		struct uphy_instance *device =
+					&drv->uphy_instances[EXYNOS4212_DEVICE];
+		exynos4212_isol(device, 1);
+		exynos4212_phy_pwr(device, 0);
+	}
+
+	return 0;
+}
+
+
+static const struct common_phy exynos4212_phys[] = {
+	{
+		.label		= "device",
+		.type		= PHY_DEVICE,
+		.id		= EXYNOS4212_DEVICE,
+		.rate_to_clk	= exynos4212_rate_to_clk,
+		.power_on	= exynos4212_power_on,
+		.power_off	= exynos4212_power_off,
+	},
+	{
+		.label		= "host",
+		.type		= PHY_HOST,
+		.id		= EXYNOS4212_HOST,
+		.rate_to_clk	= exynos4212_rate_to_clk,
+		.power_on	= exynos4212_power_on,
+		.power_off	= exynos4212_power_off,
+	},
+	{
+		.label		= "hsic0",
+		.type		= PHY_HOST,
+		.id		= EXYNOS4212_HSIC0,
+		.rate_to_clk	= exynos4212_rate_to_clk,
+		.power_on	= exynos4212_power_on,
+		.power_off	= exynos4212_power_off,
+	},
+	{
+		.label		= "hsic1",
+		.type		= PHY_HOST,
+		.id		= EXYNOS4212_HSIC1,
+		.rate_to_clk	= exynos4212_rate_to_clk,
+		.power_on	= exynos4212_power_on,
+		.power_off	= exynos4212_power_off,
+	},
+	{},
+};
+
+const struct uphy_config exynos4212_uphy_config = {
+	.cpu		= TYPE_EXYNOS4212,
+	.num_phys	= EXYNOS4212_NUM_PHYS,
+	.phys		= exynos4212_phys,
+};
+
-- 
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] 46+ messages in thread

* [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver
  2013-10-25 14:15 ` Kamil Debski
  (?)
  (?)
@ 2013-10-25 14:15 ` Kamil Debski
  2013-10-25 15:43     ` Kishon Vijay Abraham I
  -1 siblings, 1 reply; 46+ messages in thread
From: Kamil Debski @ 2013-10-25 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-samsung-soc, linux-usb, devicetree, linux-arm
  Cc: kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, Kamil Debski

Add support for Exynos 5250. This is work-in-progress commit. Not
for merging.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/phy/Kconfig              |    7 +
 drivers/phy/Makefile             |    1 +
 drivers/phy/phy-exynos-usb.c     |   10 +
 drivers/phy/phy-exynos-usb.h     |    1 +
 drivers/phy/phy-exynos5250-usb.c |  411 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 430 insertions(+)
 create mode 100644 drivers/phy/phy-exynos5250-usb.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 2f7ac0a..0f598d0 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -36,4 +36,11 @@ config PHY_EXYNOS4212_USB
 	help
 	  Enable USB PHY support for Exynos 4212
 
+config PHY_EXYNOS5250_USB
+	bool "Support for Exynos 5250"
+	depends on PHY_EXYNOS_USB
+	depends on SOC_EXYNOS5250
+	help
+	  Enable USB PHY support for Exynos 5250
+
 endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index ca3dc82..0dff0dd 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_GENERIC_PHY)	+= phy-core.o
 obj-$(CONFIG_PHY_EXYNOS_USB)		+= phy-exynos-usb.o
 obj-$(CONFIG_PHY_EXYNOS4210_USB)	+= phy-exynos4210-usb.o
 obj-$(CONFIG_PHY_EXYNOS4212_USB)	+= phy-exynos4212-usb.o
+obj-$(CONFIG_PHY_EXYNOS5250_USB)	+= phy-exynos5250-usb.o
diff --git a/drivers/phy/phy-exynos-usb.c b/drivers/phy/phy-exynos-usb.c
index d4a26df..172b774 100644
--- a/drivers/phy/phy-exynos-usb.c
+++ b/drivers/phy/phy-exynos-usb.c
@@ -212,6 +212,10 @@ extern const struct uphy_config exynos4210_uphy_config;
 extern const struct uphy_config exynos4212_uphy_config;
 #endif
 
+#ifdef CONFIG_PHY_EXYNOS5250_USB
+extern const struct uphy_config exynos5250_uphy_config;
+#endif
+
 static const struct of_device_id exynos_uphy_of_match[] = {
 #ifdef CONFIG_PHY_EXYNOS4210_USB
 	{
@@ -225,6 +229,12 @@ static const struct of_device_id exynos_uphy_of_match[] = {
 		.data = &exynos4212_uphy_config,
 	},
 #endif
+#ifdef CONFIG_PHY_EXYNOS5250_USB
+	{
+		.compatible = "samsung,exynos5250-usbphy",
+		.data = &exynos5250_uphy_config,
+	},
+#endif
 	{ },
 };
 
diff --git a/drivers/phy/phy-exynos-usb.h b/drivers/phy/phy-exynos-usb.h
index f45cb3c..a9febfa 100644
--- a/drivers/phy/phy-exynos-usb.h
+++ b/drivers/phy/phy-exynos-usb.h
@@ -42,6 +42,7 @@ enum samsung_cpu_type {
 	TYPE_S3C64XX,
 	TYPE_EXYNOS4210,
 	TYPE_EXYNOS4212,
+	TYPE_EXYNOS5250,
 };
 
 enum uphy_state {
diff --git a/drivers/phy/phy-exynos5250-usb.c b/drivers/phy/phy-exynos5250-usb.c
new file mode 100644
index 0000000..156093b
--- /dev/null
+++ b/drivers/phy/phy-exynos5250-usb.c
@@ -0,0 +1,411 @@
+/*
+ * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.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-exynos-usb.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		(0x1 << 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		(0x1 << 11)
+#define EXYNOS_5250_HOSTPHYCTRL0_RETENABLE		(0x1 << 10)
+#define EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N		(0x1 << 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			(0x1 << 6)
+#define EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP		(0x1 << 5)
+#define EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND		(0x1 << 4)
+#define EXYNOS_5250_HOSTPHYCTRL0_WORDINTERFACE		(0x1 << 3)
+#define EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST		(0x1 << 2)
+#define EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST		(0x1 << 1)
+#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST		(0x1 << 0)
+
+/* HSIC0 & HSCI1 */
+#define EXYNOS_5250_HOSTPHYCTRL1			0x10
+#define EXYNOS_5250_HOSTPHYCTRL2			0x20
+
+#define EXYNOS_5250_HOSTPHYCTRLX_REFCLKSEL_MASK		(0x3 << 23)
+#define EXYNOS_5250_HOSTPHYCTRLX_REFCLKDIV_MASK		(0x7f << 16)
+#define EXYNOS_5250_HOSTPHYCTRLX_SIDDQ			(0x1 << 6)
+#define EXYNOS_5250_HOSTPHYCTRLX_FORCESLEEP		(0x1 << 5)
+#define EXYNOS_5250_HOSTPHYCTRLX_FORCESUSPEND		(0x1 << 4)
+#define EXYNOS_5250_HOSTPHYCTRLX_WORDINTERFACE		(0x1 << 3)
+#define EXYNOS_5250_HOSTPHYCTRLX_UTMISWRST		(0x1 << 2)
+#define EXYNOS_5250_HOSTPHYCTRLX_PHYSWRST		(0x1 << 0)
+
+/* EHCI control */
+#define EXYNOS_5250_HOSTEHCICTRL			0x30
+#define EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN		(0x1 << 29)
+#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR4		(0x1 << 28)
+#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR8		(0x1 << 27)
+#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR16		(0x1 << 26)
+#define EXYNOS_5250_HOSTEHCICTRL_AUTOPPDONOVRCUREN	(0x1 << 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		(0x1 << 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		(0x1 << 0)
+
+/* USBOTG */
+#define EXYNOS_5250_USBOTGSYS				0x38
+#define EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET		(0x1 << 14)
+#define EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG		(0x1 << 13)
+#define EXYNOS_5250_USBOTGSYS_PHY_SW_RST		(0x1 << 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			(0x1 << 8)
+#define EXYNOS_5250_USBOTGSYS_COMMON_ON			(0x1 << 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		(0x1 << 3)
+#define EXYNOS_5250_USBOTGSYS_OTGDISABLE		(0x1 << 2)
+#define EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG		(0x1 << 1)
+#define EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND		(0x1 << 0)
+
+/* Isolation, configured in the power management unit */
+#define EXYNOS_5250_USB_ISOL_OTG_OFFSET		0x0
+#define EXYNOS_5250_USB_ISOL_OTG		(1 << 0)
+#define EXYNOS_5250_USB_ISOL_HOST_OFFSET	0x4
+#define EXYNOS_5250_USB_ISOL_HOST		(1 << 0)
+
+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 u32 exynos5250_rate_to_clk(unsigned long rate)
+{
+	unsigned int clksel;
+
+	/* EXYNOS_5250_FSEL_MASK */
+
+	switch (rate) {
+	case 9600 * KHZ:
+		clksel = EXYNOS_5250_FSEL_9MHZ6;
+		break;
+	case 10 * MHZ:
+		clksel = EXYNOS_5250_FSEL_10MHZ;
+		break;
+	case 12 * MHZ:
+		clksel = EXYNOS_5250_FSEL_12MHZ;
+		break;
+	case 19200 * KHZ:
+		clksel = EXYNOS_5250_FSEL_19MHZ2;
+		break;
+	case 20 * MHZ:
+		clksel = EXYNOS_5250_FSEL_20MHZ;
+		break;
+	case 24 * MHZ:
+		clksel = EXYNOS_5250_FSEL_24MHZ;
+		break;
+	case 50 * MHZ:
+		clksel = EXYNOS_5250_FSEL_50MHZ;
+		break;
+	default:
+		clksel = CLKSEL_ERROR;
+	}
+
+	return clksel;
+}
+
+static void exynos5250_isol(struct uphy_instance *inst, bool on)
+{
+	struct uphy_driver *drv = inst->drv;
+	u32 offset;
+	u32 mask;
+	u32 tmp;
+
+	if (!drv->reg_isol)
+		return;
+
+	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;
+	};
+
+	tmp = readl(drv->reg_isol + offset);
+	if (on)
+		tmp &= ~mask;
+	else
+		tmp |= mask;
+	writel(tmp, drv->reg_isol + offset);
+}
+
+static void exynos5250_phy_pwr(struct uphy_instance *inst, bool on)
+{
+	struct uphy_driver *drv = inst->drv;
+	u32 ctrl0;
+	u32 otg;
+	u32 ehci;
+	u32 ohci;
+
+	switch (inst->cfg->id) {
+	case EXYNOS5250_DEVICE:
+		writel(0, drv->reg_mode);
+
+		/* OTG configuration */
+		otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+		/* The clock */
+		otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
+		otg |= inst->clk << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
+		/* Reset */
+		otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
+			EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
+			EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
+		/* TODO: Clear 4 bits as the old driver does. */
+		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);
+
+
+		break;
+	case EXYNOS5250_HOST:
+		/* Host registers configuration */
+		ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
+		/* The clock */
+		ctrl0 &= ~EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK;
+		ctrl0 |= inst->clk << 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 << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
+		/* Reset */
+		otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
+			EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
+			EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
+		/* TODO: Clear 4 bits as the old driver does. */
+		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);
+
+		/* 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);
+		/* Let's do some undocumented magic (based on the old driver) */
+		ohci |=	0x1 << 3;
+		writel(ohci, drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
+
+		break;
+	}
+}
+
+static int exynos5250_power_on(struct uphy_instance *inst)
+{
+	struct uphy_driver *drv = inst->drv;
+
+	if (inst->state == STATE_ON) {
+		dev_err(drv->dev, "usb phy \"%s\" already on",
+							inst->cfg->label);
+		return -ENODEV;
+	}
+
+	inst->state = STATE_ON;
+	inst->ref_cnt++;
+	if (inst->ref_cnt > 1)
+		return 0;
+
+	exynos5250_phy_pwr(inst, 1);
+	exynos5250_isol(inst, 0);
+
+	/* Power on the device, as it is necessary for HSIC to work */
+	if (inst->cfg->id == EXYNOS5250_HSIC0) {
+		struct uphy_instance *device =
+					&drv->uphy_instances[EXYNOS5250_DEVICE];
+		device->ref_cnt++;
+		if (device->ref_cnt > 1)
+			return 0;
+		exynos5250_phy_pwr(device, 1);
+		exynos5250_isol(device, 0);
+	}
+
+	return 0;
+}
+
+static int exynos5250_power_off(struct uphy_instance *inst)
+{
+	struct uphy_driver *drv = inst->drv;
+
+	if (inst->state == STATE_OFF) {
+		dev_err(drv->dev, "usb phy \"%s\" already off",
+							inst->cfg->label);
+		return -EINVAL;
+	}
+
+	inst->state = STATE_OFF;
+	inst->ref_cnt--;
+
+	if (inst->ref_cnt > 0)
+		return 0;
+
+	exynos5250_isol(inst, 1);
+	exynos5250_phy_pwr(inst, 0);
+
+	if (inst->cfg->id == EXYNOS5250_HSIC0) {
+		struct uphy_instance *device =
+					&drv->uphy_instances[EXYNOS5250_DEVICE];
+		device->ref_cnt--;
+		if (device->ref_cnt > 0)
+			return 0;
+		exynos5250_isol(device, 1);
+		exynos5250_phy_pwr(device, 0);
+	}
+
+	return 0;
+}
+
+
+static const struct common_phy exynos5250_phys[] = {
+	{
+		.label		= "device",
+		.type		= PHY_DEVICE,
+		.id		= EXYNOS5250_DEVICE,
+		.rate_to_clk	= exynos5250_rate_to_clk,
+		.power_on	= exynos5250_power_on,
+		.power_off	= exynos5250_power_off,
+	},
+	{
+		.label		= "host",
+		.type		= PHY_HOST,
+		.id		= EXYNOS5250_HOST,
+		.rate_to_clk	= exynos5250_rate_to_clk,
+		.power_on	= exynos5250_power_on,
+		.power_off	= exynos5250_power_off,
+	},
+	{
+		.label		= "hsic0",
+		.type		= PHY_HOST,
+		.id		= EXYNOS5250_HSIC0,
+		.rate_to_clk	= exynos5250_rate_to_clk,
+		.power_on	= exynos5250_power_on,
+		.power_off	= exynos5250_power_off,
+	},
+	{
+		.label		= "hsic1",
+		.type		= PHY_HOST,
+		.id		= EXYNOS5250_HSIC1,
+		.rate_to_clk	= exynos5250_rate_to_clk,
+		.power_on	= exynos5250_power_on,
+		.power_off	= exynos5250_power_off,
+	},
+	{},
+};
+
+const struct uphy_config exynos5250_uphy_config = {
+	.cpu		= TYPE_EXYNOS5250,
+	.num_phys	= EXYNOS5250_NUM_PHYS,
+	.phys		= exynos5250_phys,
+};
+
-- 
1.7.9.5


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

* [PATCH 3/5] phy: Add support for S5PV210 to the Exynos USB PHY driver
  2013-10-25 14:15 ` Kamil Debski
                   ` (2 preceding siblings ...)
  (?)
@ 2013-10-25 14:15 ` Kamil Debski
  2013-10-25 15:50     ` Kishon Vijay Abraham I
  -1 siblings, 1 reply; 46+ messages in thread
From: Kamil Debski @ 2013-10-25 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-samsung-soc, linux-usb, devicetree, linux-arm
  Cc: kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, Mateusz Krawczuk, Kamil Debski

From: Mateusz Krawczuk <m.krawczuk@partner.samsung.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: whitespace cleanup and commit description]
Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 drivers/phy/Kconfig           |    7 ++
 drivers/phy/phy-exynos-usb.c  |   10 ++
 drivers/phy/phy-exynos-usb.h  |    1 +
 drivers/phy/phy-s5pv210-usb.c |  236 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 254 insertions(+)
 create mode 100644 drivers/phy/phy-s5pv210-usb.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 0f598d0..d3517f9 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -22,6 +22,13 @@ config PHY_EXYNOS_USB
 	  This driver provides common interface to interact, for Samsung
 	  USB 2.0 PHY driver.
 
+config PHY_S5PV210_USB
+	bool "Support for S5PV210"
+	depends on PHY_EXYNOS_USB
+	depends on ARCH_S5PV210
+	help
+	  Enable USB PHY support for S5PV210
+
 config PHY_EXYNOS4210_USB
 	bool "Support for Exynos 4210"
 	depends on PHY_EXYNOS_USB
diff --git a/drivers/phy/phy-exynos-usb.c b/drivers/phy/phy-exynos-usb.c
index 172b774..01fb12a 100644
--- a/drivers/phy/phy-exynos-usb.c
+++ b/drivers/phy/phy-exynos-usb.c
@@ -216,7 +216,17 @@ extern const struct uphy_config exynos4212_uphy_config;
 extern const struct uphy_config exynos5250_uphy_config;
 #endif
 
+#ifdef CONFIG_PHY_S5PV210_USB
+extern const struct uphy_config s5pv210_uphy_config;
+#endif
+
 static const struct of_device_id exynos_uphy_of_match[] = {
+#ifdef CONFIG_PHY_S5PV210_USB
+	{
+		.compatible = "samsung,s5pv210-usbphy",
+		.data = &s5pv210_uphy_config,
+	},
+#endif
 #ifdef CONFIG_PHY_EXYNOS4210_USB
 	{
 		.compatible = "samsung,exynos4210-usbphy",
diff --git a/drivers/phy/phy-exynos-usb.h b/drivers/phy/phy-exynos-usb.h
index a9febfa..0f385ca 100644
--- a/drivers/phy/phy-exynos-usb.h
+++ b/drivers/phy/phy-exynos-usb.h
@@ -40,6 +40,7 @@ enum phy_type {
 
 enum samsung_cpu_type {
 	TYPE_S3C64XX,
+	TYPE_S5PV210,
 	TYPE_EXYNOS4210,
 	TYPE_EXYNOS4212,
 	TYPE_EXYNOS5250,
diff --git a/drivers/phy/phy-s5pv210-usb.c b/drivers/phy/phy-s5pv210-usb.c
new file mode 100644
index 0000000..575275d
--- /dev/null
+++ b/drivers/phy/phy-s5pv210-usb.c
@@ -0,0 +1,236 @@
+/*
+ * Samsung S5P/EXYNOS SoC series USB PHY driver
+ *
+ * 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/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.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-exynos-usb.h"
+
+/* Exynos USB PHY registers */
+
+/* PHY power control */
+#define S5PV210_UPHYPWR			0x0
+
+#define S5PV210_UPHYPWR_PHY0_SUSPEND	(1 << 0)
+#define S5PV210_UPHYPWR_PHY0_PWR		(1 << 3)
+#define S5PV210_UPHYPWR_PHY0_OTG_PWR	(1 << 4)
+#define S5PV210_UPHYPWR_PHY0	( \
+	S5PV210_UPHYPWR_PHY0_SUSPEND | \
+	S5PV210_UPHYPWR_PHY0_PWR | \
+	S5PV210_UPHYPWR_PHY0_OTG_PWR)
+
+#define S5PV210_UPHYPWR_PHY1_SUSPEND	(1 << 6)
+#define S5PV210_UPHYPWR_PHY1_PWR		(1 << 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	(0x1 << 2)
+#define S5PV210_UPHYCLK_PHY0_COMMON_ON	(0x1 << 4)
+#define S5PV210_UPHYCLK_PHY1_COMMON_ON	(0x1 << 7)
+
+/* PHY reset control */
+#define S5PV210_UPHYRST			0x8
+
+#define S5PV210_URSTCON_PHY0		(1 << 0)
+#define S5PV210_URSTCON_OTG_HLINK		(1 << 1)
+#define S5PV210_URSTCON_OTG_PHYLINK		(1 << 2)
+#define S5PV210_URSTCON_PHY1_ALL		(1 << 3)
+#define S5PV210_URSTCON_HOST_LINK_ALL	(1 << 4)
+
+/* Isolation, configured in the power management unit */
+#define S5PV210_USB_ISOL_DEVICE_OFFSET	0x0
+#define S5PV210_USB_ISOL_DEVICE		(1 << 0)
+#define S5PV210_USB_ISOL_HOST_OFFSET	0x4
+#define S5PV210_USB_ISOL_HOST		(1 << 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 u32 s5pv210_rate_to_clk(unsigned long rate)
+{
+	unsigned int clksel;
+
+	pr_info("%lu \n",rate);
+	switch (rate) {
+	case 12 * MHZ:
+		clksel = S5PV210_UPHYCLK_PHYFSEL_12MHZ;
+		break;
+	case 24 * MHZ:
+		clksel = S5PV210_UPHYCLK_PHYFSEL_24MHZ;
+		break;
+	case 48 * MHZ:
+		clksel = S5PV210_UPHYCLK_PHYFSEL_48MHZ;
+		break;
+	default:
+		clksel = CLKSEL_ERROR;
+	}
+
+	return clksel;
+}
+
+static void s5pv210_isol(struct uphy_instance *inst, bool on)
+{
+	struct uphy_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;
+	};
+
+	tmp = readl(drv->reg_isol);
+	if (on)
+		tmp &= ~mask;
+	else
+		tmp |= mask;
+	writel(tmp, drv->reg_isol);
+}
+
+static void s5pv210_phy_pwr(struct uphy_instance *inst, bool on)
+{
+	struct uphy_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, 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 uphy_instance *inst)
+{
+	struct uphy_driver *drv = inst->drv;
+
+	if (inst->state == STATE_ON) {
+		dev_err(drv->dev, "usb phy \"%s\" already on",
+							inst->cfg->label);
+		return -ENODEV;
+	}
+	inst->state = STATE_ON;
+	inst->ref_cnt++;
+	if (inst->ref_cnt > 1)
+		return 0;
+
+	s5pv210_isol(inst, 0);
+	s5pv210_phy_pwr(inst, 1);
+
+	return 0;
+}
+
+static int s5pv210_power_off(struct uphy_instance *inst)
+{
+	struct uphy_driver *drv = inst->drv;
+
+	if (inst->state == STATE_OFF) {
+		dev_err(drv->dev, "usb phy \"%s\" already off",
+							inst->cfg->label);
+		return -EINVAL;
+	}
+
+	inst->state = STATE_OFF;
+	inst->ref_cnt++;
+	if (inst->ref_cnt > 0)
+		return 0;
+
+	s5pv210_phy_pwr(inst, 0);
+	s5pv210_isol(inst, 1);
+
+	return 0;
+}
+
+
+static const struct common_phy s5pv210_phys[] = {
+	{
+		.label		= "device",
+		.type		= PHY_DEVICE,
+		.id		= S5PV210_DEVICE,
+		.rate_to_clk	= s5pv210_rate_to_clk,
+		.power_on	= s5pv210_power_on,
+		.power_off	= s5pv210_power_off,
+	},
+	{
+		.label		= "host",
+		.type		= PHY_HOST,
+		.id		= S5PV210_HOST,
+		.rate_to_clk	= s5pv210_rate_to_clk,
+		.power_on	= s5pv210_power_on,
+		.power_off	= s5pv210_power_off,
+	},
+	{},
+};
+
+const struct uphy_config s5pv210_uphy_config = {
+	.cpu		= TYPE_S5PV210,
+	.num_phys	= S5PV210_NUM_PHYS,
+	.phys		= s5pv210_phys,
+};
+
-- 
1.7.9.5


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

* [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework
  2013-10-25 14:15 ` Kamil Debski
                   ` (3 preceding siblings ...)
  (?)
@ 2013-10-25 14:15 ` Kamil Debski
  2013-10-25 15:52     ` Kishon Vijay Abraham I
                     ` (2 more replies)
  -1 siblings, 3 replies; 46+ messages in thread
From: Kamil Debski @ 2013-10-25 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-samsung-soc, linux-usb, devicetree, linux-arm
  Cc: kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, Kamil Debski

Change the phy provider used from the old usb phy specific to a new one
using the generic phy framework.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/usb/host/ehci-s5p.c |   21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
index 7cc26e6..76606ff 100644
--- a/drivers/usb/host/ehci-s5p.c
+++ b/drivers/usb/host/ehci-s5p.c
@@ -19,6 +19,7 @@
 #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/platform_data/usb-ehci-s5p.h>
 #include <linux/usb/phy.h>
@@ -45,7 +46,7 @@ static struct hc_driver __read_mostly s5p_ehci_hc_driver;
 
 struct s5p_ehci_hcd {
 	struct clk *clk;
-	struct usb_phy *phy;
+	struct phy *phy;
 	struct usb_otg *otg;
 	struct s5p_ehci_platdata *pdata;
 };
@@ -77,10 +78,11 @@ static int s5p_ehci_probe(struct platform_device *pdev)
 {
 	struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
 	struct s5p_ehci_hcd *s5p_ehci;
+	struct phy *phy;
 	struct usb_hcd *hcd;
 	struct ehci_hcd *ehci;
 	struct resource *res;
-	struct usb_phy *phy;
+	const char *phy_name;
 	int irq;
 	int err;
 
@@ -103,14 +105,14 @@ static int s5p_ehci_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	}
 	s5p_ehci = to_s5p_ehci(hcd);
-
+	phy_name = of_get_property(pdev->dev.of_node, "phy-names", NULL);
+	phy =  devm_phy_get(&pdev->dev, phy_name);
 	if (of_device_is_compatible(pdev->dev.of_node,
 					"samsung,exynos5440-ehci")) {
 		s5p_ehci->pdata = &empty_platdata;
 		goto skip_phy;
 	}
 
-	phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
 	if (IS_ERR(phy)) {
 		/* Fallback to pdata */
 		if (!pdata) {
@@ -122,7 +124,6 @@ static int s5p_ehci_probe(struct platform_device *pdev)
 		}
 	} else {
 		s5p_ehci->phy = phy;
-		s5p_ehci->otg = phy->otg;
 	}
 
 skip_phy:
@@ -166,7 +167,7 @@ skip_phy:
 		s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
 
 	if (s5p_ehci->phy)
-		usb_phy_init(s5p_ehci->phy);
+		phy_power_on(s5p_ehci->phy);
 	else if (s5p_ehci->pdata->phy_init)
 		s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
 
@@ -188,7 +189,7 @@ skip_phy:
 
 fail_add_hcd:
 	if (s5p_ehci->phy)
-		usb_phy_shutdown(s5p_ehci->phy);
+		phy_power_off(s5p_ehci->phy);
 	else if (s5p_ehci->pdata->phy_exit)
 		s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
 fail_io:
@@ -209,7 +210,7 @@ static int s5p_ehci_remove(struct platform_device *pdev)
 		s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
 
 	if (s5p_ehci->phy)
-		usb_phy_shutdown(s5p_ehci->phy);
+		phy_power_off(s5p_ehci->phy);
 	else if (s5p_ehci->pdata->phy_exit)
 		s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
 
@@ -244,7 +245,7 @@ static int s5p_ehci_suspend(struct device *dev)
 		s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
 
 	if (s5p_ehci->phy)
-		usb_phy_shutdown(s5p_ehci->phy);
+		phy_power_off(s5p_ehci->phy);
 	else if (s5p_ehci->pdata->phy_exit)
 		s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
 
@@ -265,7 +266,7 @@ static int s5p_ehci_resume(struct device *dev)
 		s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
 
 	if (s5p_ehci->phy)
-		usb_phy_init(s5p_ehci->phy);
+		phy_power_on(s5p_ehci->phy);
 	else if (s5p_ehci->pdata->phy_init)
 		s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
 
-- 
1.7.9.5


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

* [PATCH 5/5] usb: s3c-hsotg: Use the new Exynos USB phy driver with the generic phy framework
  2013-10-25 14:15 ` Kamil Debski
                   ` (4 preceding siblings ...)
  (?)
@ 2013-10-25 14:15 ` Kamil Debski
  2013-10-25 15:53     ` Kishon Vijay Abraham I
  -1 siblings, 1 reply; 46+ messages in thread
From: Kamil Debski @ 2013-10-25 14:15 UTC (permalink / raw)
  To: linux-kernel, linux-samsung-soc, linux-usb, devicetree, linux-arm
  Cc: kyungmin.park, kishon, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, Kamil Debski

Change the used phy driver to the new Exynos USB phy driver that uses the
generic phy framework.

Signed-off-by: Kamil Debski <k.debski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/usb/gadget/s3c-hsotg.c |   13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c
index 7705c41..2ce84a5 100644
--- a/drivers/usb/gadget/s3c-hsotg.c
+++ b/drivers/usb/gadget/s3c-hsotg.c
@@ -29,6 +29,7 @@
 #include <linux/slab.h>
 #include <linux/clk.h>
 #include <linux/regulator/consumer.h>
+#include <linux/phy/phy.h>
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
@@ -131,6 +132,7 @@ struct s3c_hsotg_ep {
 /**
  * struct s3c_hsotg - driver state.
  * @dev: The parent device supplied to the probe function
+
  * @driver: USB gadget driver
  * @phy: The otg phy transceiver structure for phy control.
  * @plat: The platform specific configuration data. This can be removed once
@@ -154,7 +156,7 @@ struct s3c_hsotg_ep {
 struct s3c_hsotg {
 	struct device		 *dev;
 	struct usb_gadget_driver *driver;
-	struct usb_phy		*phy;
+	struct phy		 *phy;
 	struct s3c_hsotg_plat	 *plat;
 
 	spinlock_t              lock;
@@ -2858,9 +2860,10 @@ static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg)
 	dev_dbg(hsotg->dev, "pdev 0x%p\n", pdev);
 
 	if (hsotg->phy)
-		usb_phy_init(hsotg->phy);
+		phy_power_on(hsotg->phy);
 	else if (hsotg->plat->phy_init)
 		hsotg->plat->phy_init(pdev, hsotg->plat->phy_type);
+
 }
 
 /**
@@ -2875,7 +2878,7 @@ static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg)
 	struct platform_device *pdev = to_platform_device(hsotg->dev);
 
 	if (hsotg->phy)
-		usb_phy_shutdown(hsotg->phy);
+		phy_power_off(hsotg->phy);
 	else if (hsotg->plat->phy_exit)
 		hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type);
 }
@@ -3485,7 +3488,7 @@ static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg)
 static int s3c_hsotg_probe(struct platform_device *pdev)
 {
 	struct s3c_hsotg_plat *plat = pdev->dev.platform_data;
-	struct usb_phy *phy;
+	struct phy *phy;
 	struct device *dev = &pdev->dev;
 	struct s3c_hsotg_ep *eps;
 	struct s3c_hsotg *hsotg;
@@ -3500,7 +3503,7 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	}
 
-	phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+	phy = devm_phy_get(&pdev->dev, "device");
 	if (IS_ERR(phy)) {
 		/* Fallback for pdata */
 		plat = pdev->dev.platform_data;
-- 
1.7.9.5


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

* Re: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver
@ 2013-10-25 15:39     ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 46+ messages in thread
From: Kishon Vijay Abraham I @ 2013-10-25 15:39 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, kyungmin.park, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk

Hi,

On Friday 25 October 2013 07:45 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.
> 
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  .../devicetree/bindings/phy/samsung-usbphy.txt     |   51 +++
>  drivers/phy/Kconfig                                |   21 ++
>  drivers/phy/Makefile                               |    3 +
>  drivers/phy/phy-exynos-usb.c                       |  245 ++++++++++++++
>  drivers/phy/phy-exynos-usb.h                       |   94 ++++++
>  drivers/phy/phy-exynos4210-usb.c                   |  295 +++++++++++++++++
>  drivers/phy/phy-exynos4212-usb.c                   |  343 ++++++++++++++++++++
>  7 files changed, 1052 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/samsung-usbphy.txt
>  create mode 100644 drivers/phy/phy-exynos-usb.c
>  create mode 100644 drivers/phy/phy-exynos-usb.h
>  create mode 100644 drivers/phy/phy-exynos4210-usb.c
>  create mode 100644 drivers/phy/phy-exynos4212-usb.c
> 
> diff --git a/Documentation/devicetree/bindings/phy/samsung-usbphy.txt b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> new file mode 100644
> index 0000000..f112b37
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> @@ -0,0 +1,51 @@
> +Samsung S5P/EXYNOS SoC series USB PHY
> +-------------------------------------------------
> +
> +Required properties:
> +- compatible : should be one of the listed compatibles:
> +	- "samsung,exynos4210-usbphy"
> +	- "samsung,exynos4212-usbphy"
> +- reg : a list of registers used by phy driver
> +	- first and obligatory is the location of phy modules registers
> +	- second and also required is the location of isolation registers
> +	  (isolation registers control the physical connection between the in
> +	  SoC modules and outside of the SoC, this also can be called enable
> +	  control in the documentation of the SoC)
> +	- third is the location of the mode switch register, this only applies
> +	  to SoCs that have such a feature; mode switching enables to have
> +	  both host and device used the same SoC pins and is commonly used
> +	  when OTG is supported
> +- #phy-cells : from the generic phy bindings, must be 1;
> +
> +The second cell in the PHY specifier identifies the PHY its meaning is SoC
> +dependent. For the currently supported SoCs (Exynos 4210 and Exynos 4212) it
> +is as follows:
> +  0 - USB device,
> +  1 - USB host,
> +  2 - HSIC0,
> +  3 - HSIC1,

HSIC is supposedly to be transceiver less no? You have to program something in
the digital side?
You have a single IP that have all these functionalities?
> +
> +Example:
> +
> +For Exynos 4412 (compatible with Exynos 4212):
> +
> +exynos_usbphy: exynos-usbphy@125B0000 {
> +	compatible = "samsung,exynos4212-usbphy";
> +	reg = <0x125B0000 0x100 0x10020704 0x0c 0x1001021c 0x4>;
> +	ranges;
> +	#address-cells = <1>;
> +	#size-cells = <1>;

The above 3 properties aren't documented? Are they needed here?
> +	clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
> +							<&clock 2>;
> +	clock-names = "phy", "device", "host", "hsic0", "hsic1";
> +	status = "okay";
> +	#phy-cells = <1>;
> +};
> +
> +Then the PHY can be used in other nodes such as:
> +
> +ehci@12580000 {
> +	status = "okay";
> +	phys = <&exynos_usbphy 2>;
> +	phy-names = "hsic0";
> +};
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 349bef2..2f7ac0a 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -15,4 +15,25 @@ config GENERIC_PHY
>  	  phy users can obtain reference to the PHY. All the users of this
>  	  framework should select this config.
>  
> +config PHY_EXYNOS_USB
> +	tristate "Samsung USB PHY driver (using the Generic PHY Framework)"
Mentioning *Generic PHY Framework* is not necessary.
*select GENERIC_PHY* here
> +	help
> +	  Enable this to support Samsung USB phy helper driver for Samsung SoCs.
> +	  This driver provides common interface to interact, for Samsung
> +	  USB 2.0 PHY driver.

If it's going to be used only for usb2, name it PHY_EXYNOS_USB2.
> +
> +config PHY_EXYNOS4210_USB
> +	bool "Support for Exynos 4210"
> +	depends on PHY_EXYNOS_USB
> +	depends on CPU_EXYNOS4210
> +	help
> +	  Enable USB PHY support for Exynos 4210
> +
> +config PHY_EXYNOS4212_USB
> +	bool "Support for Exynos 4212"
> +	depends on PHY_EXYNOS_USB
> +	depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
> +	help
> +	  Enable USB PHY support for Exynos 4212

How difference is USB PHY in Exynos 4212 from Exynos 4210? If there isn't much,
I would suggest to use a single driver.
> +
>  endmenu
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index 9e9560f..ca3dc82 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -3,3 +3,6 @@
>  #
>  
>  obj-$(CONFIG_GENERIC_PHY)	+= phy-core.o
> +obj-$(CONFIG_PHY_EXYNOS_USB)		+= phy-exynos-usb.o
> +obj-$(CONFIG_PHY_EXYNOS4210_USB)	+= phy-exynos4210-usb.o
> +obj-$(CONFIG_PHY_EXYNOS4212_USB)	+= phy-exynos4212-usb.o
> diff --git a/drivers/phy/phy-exynos-usb.c b/drivers/phy/phy-exynos-usb.c
> new file mode 100644
> index 0000000..d4a26df
> --- /dev/null
> +++ b/drivers/phy/phy-exynos-usb.c

phy-exynos-usb2.c?
> @@ -0,0 +1,245 @@
> +/*
> + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
> +#include <linux/io.h>
> +#include <linux/kernel.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-exynos-usb.h"
> +
> +static int exynos_uphy_power_on(struct phy *phy)

exynos_usb2_phy here and everywhere below.
> +{
> +	struct uphy_instance *inst = phy_get_drvdata(phy);
> +	struct uphy_driver *drv = inst->drv;
> +	int ret;
> +
> +	dev_info(drv->dev, "Request to power_on \"%s\" usb phy\n",
> +							inst->cfg->label);

make it dev_dbg if it's necessary.
> +	ret = clk_prepare_enable(drv->clk);
> +	if (ret)
> +		return ret;
> +	if (inst->cfg->power_on) {
> +		spin_lock(&drv->lock);
> +		ret = inst->cfg->power_on(inst);
> +		spin_unlock(&drv->lock);
> +	}
> +	clk_disable_unprepare(drv->clk);

hmm.. don't you need the clock to be on during the duration of the PHY operation?
> +	return ret;
> +}
> +
> +static int exynos_uphy_power_off(struct phy *phy)
> +{
> +	struct uphy_instance *inst = phy_get_drvdata(phy);
> +	struct uphy_driver *drv = inst->drv;
> +	int ret;
> +
> +	dev_info(drv->dev, "Request to power_off \"%s\" usb phy\n",
> +							inst->cfg->label);

dev_dbg?
> +	ret = clk_prepare_enable(drv->clk);
> +	if (ret)
> +		return ret;
> +	if (inst->cfg->power_off) {
> +		spin_lock(&drv->lock);
> +		ret = inst->cfg->power_off(inst);
> +		spin_unlock(&drv->lock);
> +	}
> +	clk_disable_unprepare(drv->clk);
> +	return ret;
> +}
> +
> +static struct phy_ops exynos_uphy_ops = {
> +	.power_on	= exynos_uphy_power_on,
> +	.power_off	= exynos_uphy_power_off,
> +	.owner		= THIS_MODULE,
> +};
> +
> +static struct phy *exynos_uphy_xlate(struct device *dev,
> +					struct of_phandle_args *args)
> +{
> +	struct uphy_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->uphy_instances[args->args[0]].phy;
> +}
> +
> +static const struct of_device_id exynos_uphy_of_match[];
> +
> +static int exynos_uphy_probe(struct platform_device *pdev)
> +{
> +	struct uphy_driver *drv;
> +	struct device *dev = &pdev->dev;
> +	struct resource *mem;
> +	struct phy_provider *phy_provider;
> +
> +	const struct of_device_id *match;
> +	const struct uphy_config *cfg;
> +	struct clk *clk;
> +
> +	int i;
> +
> +	match = of_match_node(exynos_uphy_of_match, pdev->dev.of_node);
> +	if (!match) {
> +		dev_err(dev, "of_match_node() failed\n");
> +		return -EINVAL;
> +	}
> +	cfg = match->data;
> +	if (!cfg) {
> +		dev_err(dev, "Failed to get configuration\n");
> +		return -EINVAL;
> +	}
> +
> +	drv = devm_kzalloc(dev, sizeof(struct uphy_driver) +
> +		cfg->num_phys * sizeof(struct uphy_instance), GFP_KERNEL);
> +
> +	if (!drv) {
> +		dev_err(dev, "Failed to allocate memory\n");
> +		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);
> +
empty blank line.
> +	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);
> +	}
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	drv->reg_isol = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(drv->reg_isol)) {
> +		dev_err(dev, "Failed to map register memory (isolation)\n");
> +		return PTR_ERR(drv->reg_isol);
> +	}
> +
> +	switch (drv->cfg->cpu) {
> +	case TYPE_EXYNOS4210:
> +	case TYPE_EXYNOS4212:

Lets not add such cpu checks inside driver.
> +		mem = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> +		drv->reg_mode = devm_ioremap_resource(dev, mem);
> +		if (IS_ERR(drv->reg_mode)) {
> +			dev_err(dev, "Failed to map register memory (mode switch)\n");
> +			return PTR_ERR(drv->reg_mode);
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	phy_provider = devm_of_phy_provider_register(dev,
> +							exynos_uphy_xlate);
> +	if (IS_ERR(phy_provider)) {
> +		dev_err(drv->dev, "Failed to register phy provider\n");
> +		return PTR_ERR(phy_provider);
> +	}
> +
> +	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 uphy_instance *p = &drv->uphy_instances[i];
> +
> +		dev_info(dev, "Creating phy \"%s\"\n", label);
> +		p->phy = devm_phy_create(dev, &exynos_uphy_ops, NULL);
> +		if (IS_ERR(p->phy)) {
> +			dev_err(drv->dev, "Failed to create uphy \"%s\"\n",
> +						label);
> +			return PTR_ERR(p->phy);
> +		}
> +
> +		p->cfg = &drv->cfg->phys[i];
> +		p->drv = drv;
> +		phy_set_drvdata(p->phy, p);
> +
> +		clk = 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->rate = clk_get_rate(clk);
> +
> +		if (p->cfg->rate_to_clk) {
> +			p->clk = p->cfg->rate_to_clk(p->rate);
> +			if (p->clk == CLKSEL_ERROR) {
> +				dev_err(dev, "Clock rate (%ld) not supported\n",
> +								p->rate);
> +				clk_put(clk);
> +				return -EINVAL;
> +			}
> +		}
> +		clk_put(clk);
> +	}
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PHY_EXYNOS4210_USB
Do we really need this?

> +extern const struct uphy_config exynos4210_uphy_config;
> +#endif
> +
> +#ifdef CONFIG_PHY_EXYNOS4212_USB

Same here.
> +extern const struct uphy_config exynos4212_uphy_config;
> +#endif
> +
> +static const struct of_device_id exynos_uphy_of_match[] = {
> +#ifdef CONFIG_PHY_EXYNOS4210_USB

#if not needed here.
> +	{
> +		.compatible = "samsung,exynos4210-usbphy",
> +		.data = &exynos4210_uphy_config,
> +	},
> +#endif
> +#ifdef CONFIG_PHY_EXYNOS4212_USB

here too.
> +	{
> +		.compatible = "samsung,exynos4212-usbphy",
> +		.data = &exynos4212_uphy_config,
> +	},
> +#endif
> +	{ },
> +};
> +
> +static struct platform_driver exynos_uphy_driver = {
> +	.probe	= exynos_uphy_probe,
> +	.driver = {
> +		.of_match_table	= exynos_uphy_of_match,
> +		.name		= "exynos-usbphy-new",
"exynos-usb2-phy".
> +		.owner		= THIS_MODULE,
> +	}
> +};
> +
> +module_platform_driver(exynos_uphy_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:exynos-uphy-new");
> +
> diff --git a/drivers/phy/phy-exynos-usb.h b/drivers/phy/phy-exynos-usb.h
> new file mode 100644
> index 0000000..f45cb3c
> --- /dev/null
> +++ b/drivers/phy/phy-exynos-usb.h
> @@ -0,0 +1,94 @@
> +/*
> + * Samsung S5P/EXYNOS SoC series USB 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_SAMSUNG_NEW_H
> +#define _PHY_SAMSUNG_NEW_H
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/kernel.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>
> +
> +#define CLKSEL_ERROR                       -1
> +
> +#ifndef KHZ
> +#define KHZ 1000
> +#endif
> +
> +#ifndef MHZ
> +#define MHZ (KHZ * KHZ)
> +#endif
> +
> +enum phy_type {
> +	PHY_DEVICE,
> +	PHY_HOST,
> +};
> +
> +enum samsung_cpu_type {
> +	TYPE_S3C64XX,
> +	TYPE_EXYNOS4210,
> +	TYPE_EXYNOS4212,

No *cpu_type* inside driver files.
> +};
> +
> +enum uphy_state {
> +	STATE_OFF,
> +	STATE_ON,
> +};
> +
> +struct uphy_driver;
> +struct uphy_instance;
> +struct uphy_config;
> +
> +struct uphy_instance {
> +	struct uphy_driver *drv;
> +	struct phy *phy;
> +	const struct common_phy *cfg;
> +	enum uphy_state state;
> +	int ref_cnt;
> +	u32 clk;
> +	unsigned long rate;
> +};
> +
> +struct uphy_driver {
> +	struct device *dev;
> +	spinlock_t lock;
> +	void __iomem *reg_phy;
> +	void __iomem *reg_isol;
> +	void __iomem *reg_mode;
> +	const struct uphy_config *cfg;
> +	struct clk *clk;
> +	struct uphy_instance uphy_instances[0];

you might have more than one phy instance right?
> +};
> +
> +struct common_phy {
> +	char *label;
> +	enum phy_type type;
> +	unsigned int id;
> +	u32 (*rate_to_clk)(unsigned long);
> +	int (*power_on)(struct uphy_instance*);
> +	int (*power_off)(struct uphy_instance*);
> +};
> +
> +
> +struct uphy_config {
> +	enum samsung_cpu_type cpu;
> +	int num_phys;
> +	const struct common_phy *phys;
> +};
> +
> +#endif
> +
> diff --git a/drivers/phy/phy-exynos4210-usb.c b/drivers/phy/phy-exynos4210-usb.c
> new file mode 100644
> index 0000000..6cf74f7
> --- /dev/null
> +++ b/drivers/phy/phy-exynos4210-usb.c
> @@ -0,0 +1,295 @@
> +/*
> + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
> +#include <linux/io.h>
> +#include <linux/kernel.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-exynos-usb.h"
> +
> +/* Exynos USB PHY registers */
> +
> +/* PHY power control */
> +#define EXYNOS_4210_UPHYPWR			0x0
> +
> +#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND	(1 << 0)
> +#define EXYNOS_4210_UPHYPWR_PHY0_PWR		(1 << 3)
> +#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR	(1 << 4)
> +#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP		(1 << 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	(1 << 6)
> +#define EXYNOS_4210_UPHYPWR_PHY1_PWR		(1 << 7)
> +#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP		(1 << 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_HSCI0_SUSPEND	(1 << 9)
> +#define EXYNOS_4210_UPHYPWR_HSCI0_SLEEP		(1 << 10)
> +#define EXYNOS_4210_UPHYPWR_HSCI0 ( \
> +	EXYNOS_4210_UPHYPWR_HSCI0_SUSPEND | \
> +	EXYNOS_4210_UPHYPWR_HSCI0_SLEEP)
> +
> +#define EXYNOS_4210_UPHYPWR_HSCI1_SUSPEND	(1 << 11)
> +#define EXYNOS_4210_UPHYPWR_HSCI1_SLEEP		(1 << 12)
> +#define EXYNOS_4210_UPHYPWR_HSCI1 ( \
> +	EXYNOS_4210_UPHYPWR_HSCI1_SUSPEND | \
> +	EXYNOS_4210_UPHYPWR_HSCI1_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	(0x1 << 2)
> +#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON	(0x1 << 4)
> +#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON	(0x1 << 7)
> +
> +/* PHY reset control */
> +#define EXYNOS_4210_UPHYRST			0x8
> +
> +#define EXYNOS_4210_URSTCON_PHY0		(1 << 0)
> +#define EXYNOS_4210_URSTCON_OTG_HLINK		(1 << 1)
> +#define EXYNOS_4210_URSTCON_OTG_PHYLINK		(1 << 2)
> +#define EXYNOS_4210_URSTCON_PHY1_ALL		(1 << 3)
> +#define EXYNOS_4210_URSTCON_PHY1_P0		(1 << 4)
> +#define EXYNOS_4210_URSTCON_PHY1_P1P2		(1 << 5)
> +#define EXYNOS_4210_URSTCON_HOST_LINK_ALL	(1 << 6)
> +#define EXYNOS_4210_URSTCON_HOST_LINK_P0	(1 << 7)
> +#define EXYNOS_4210_URSTCON_HOST_LINK_P1	(1 << 8)
> +#define EXYNOS_4210_URSTCON_HOST_LINK_P2	(1 << 9)But 
> +
> +/* Isolation, configured in the power management unit */
> +#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET	0x0
> +#define EXYNOS_4210_USB_ISOL_DEVICE		(1 << 0)
> +#define EXYNOS_4210_USB_ISOL_HOST_OFFSET	0x4
> +#define EXYNOS_4210_USB_ISOL_HOST		(1 << 0)
> +
> +/* USBYPHY1 Floating prevention */
> +#define EXYNOS_4210_UPHY1CON			0x34
> +#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION	0x1
> +
> +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. */

use proper commenting styles Documentation/CodingStyle
> +static u32 exynos4210_rate_to_clk(unsigned long rate)
> +{
> +	unsigned int clksel;
> +
> +	switch (rate) {
> +	case 12 * MHZ:
> +		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
> +		break;
> +	case 24 * MHZ:
> +		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
> +		break;
> +	case 48 * MHZ:
> +		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
> +		break;
> +	default:
> +		clksel = CLKSEL_ERROR;
> +	}
> +
> +	return clksel;
> +}
> +
> +static void exynos4210_isol(struct uphy_instance *inst, bool on)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +	u32 offset;
> +	u32 mask;
> +	u32 tmp;
> +
> +	if (!drv->reg_isol)
> +		return;
> +
> +	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;
> +	};
> +
> +	tmp = readl(drv->reg_isol + offset);
> +	if (on)
> +		tmp &= ~mask;
> +	else
> +		tmp |= mask;
> +	writel(tmp, drv->reg_isol + offset);
> +}
> +
> +static void exynos4210_phy_pwr(struct uphy_instance *inst, bool on)
> +{
> +	struct uphy_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_HSCI0;
> +		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
> +				EXYNOS_4210_URSTCON_HOST_LINK_P1;
> +		break;
> +	case EXYNOS4210_HSIC1:
> +		phypwr =	EXYNOS_4210_UPHYPWR_HSCI1;
> +		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
> +				EXYNOS_4210_URSTCON_HOST_LINK_P2;
> +		break;
> +	};
> +
> +	if (on) {
> +		writel(inst->clk, 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);

usleep_range?
> +		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 uphy_instance *inst)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +
> +	if (inst->state == STATE_ON) {
> +		dev_err(drv->dev, "usb phy \"%s\" already on",
> +							inst->cfg->label);
> +		return -ENODEV;
> +	}
> +	inst->state = STATE_ON;
> +	inst->ref_cnt++;
> +	if (inst->ref_cnt > 1)
> +		return 0;

reference counting shouldn't be needed. It's been handled by the PHY framework.
> +
> +	/* 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 uphy_instance *inst)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +
> +	if (inst->state == STATE_OFF) {
> +		dev_err(drv->dev, "usb phy \"%s\" already off",
> +							inst->cfg->label);
> +		return -EINVAL;
> +	}
> +
> +	inst->state = STATE_OFF;
> +	inst->ref_cnt--;
> +	if (inst->ref_cnt > 0)
> +		return 0;

same here.
> +
> +	exynos4210_isol(inst, 1);
> +	exynos4210_phy_pwr(inst, 0);
> +
> +	return 0;
> +}
> +
> +
> +static const struct common_phy exynos4210_phys[] = {
> +	{
> +		.label		= "device",
> +		.type		= PHY_DEVICE,
> +		.id		= EXYNOS4210_DEVICE,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "host",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS4210_HOST,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "hsic0",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS4210_HSIC0,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "hsic1",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS4210_HSIC1,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{},
> +};
> +
> +const struct uphy_config exynos4210_uphy_config = {
> +	.cpu		= TYPE_EXYNOS4210,
> +	.num_phys	= EXYNOS4210_NUM_PHYS,
> +	.phys		= exynos4210_phys,
> +};
> +
> diff --git a/drivers/phy/phy-exynos4212-usb.c b/drivers/phy/phy-exynos4212-usb.c
> new file mode 100644
> index 0000000..c07ae8e
> --- /dev/null
> +++ b/drivers/phy/phy-exynos4212-usb.c
> @@ -0,0 +1,343 @@
> +/*
> + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
> +#include <linux/io.h>
> +#include <linux/kernel.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-exynos-usb.h"
> +
> +/* Exynos USB PHY registers */
> +
> +/* PHY power control */
> +#define EXYNOS_4212_UPHYPWR			0x0
> +
> +#define EXYNOS_4212_UPHYPWR_DEV_SUSPEND		(1 << 0)
> +#define EXYNOS_4212_UPHYPWR_DEV_PWR		(1 << 3)
> +#define EXYNOS_4212_UPHYPWR_DEV_OTG_PWR		(1 << 4)
> +#define EXYNOS_4212_UPHYPWR_DEV_SLEEP		(1 << 5)
> +#define EXYNOS_4212_UPHYPWR_DEV	( \
> +	EXYNOS_4212_UPHYPWR_DEV_SUSPEND | \
> +	EXYNOS_4212_UPHYPWR_DEV_PWR | \
> +	EXYNOS_4212_UPHYPWR_DEV_OTG_PWR | \
> +	EXYNOS_4212_UPHYPWR_DEV_SLEEP)
> +
> +#define EXYNOS_4212_UPHYPWR_HOST_SUSPEND	(1 << 6)
> +#define EXYNOS_4212_UPHYPWR_HOST_PWR		(1 << 7)
> +#define EXYNOS_4212_UPHYPWR_HOST_SLEEP		(1 << 8)
> +#define EXYNOS_4212_UPHYPWR_HOST ( \
> +	EXYNOS_4212_UPHYPWR_HOST_SUSPEND | \
> +	EXYNOS_4212_UPHYPWR_HOST_PWR | \
> +	EXYNOS_4212_UPHYPWR_HOST_SLEEP)
> +
> +#define EXYNOS_4212_UPHYPWR_HSCI0_SUSPEND	(1 << 9)
> +#define EXYNOS_4212_UPHYPWR_HSCI0_PWR		(1 << 10)
> +#define EXYNOS_4212_UPHYPWR_HSCI0_SLEEP		(1 << 11)
> +#define EXYNOS_4212_UPHYPWR_HSCI0 ( \
> +	EXYNOS_4212_UPHYPWR_HSCI0_SUSPEND | \
> +	EXYNOS_4212_UPHYPWR_HSCI0_PWR | \
> +	EXYNOS_4212_UPHYPWR_HSCI0_SLEEP)
> +
> +#define EXYNOS_4212_UPHYPWR_HSCI1_SUSPEND	(1 << 12)
> +#define EXYNOS_4212_UPHYPWR_HSCI1_PWR		(1 << 13)
> +#define EXYNOS_4212_UPHYPWR_HSCI1_SLEEP		(1 << 14)
> +#define EXYNOS_4212_UPHYPWR_HSCI1 ( \
> +	EXYNOS_4212_UPHYPWR_HSCI1_SUSPEND | \
> +	EXYNOS_4212_UPHYPWR_HSCI1_PWR | \
> +	EXYNOS_4212_UPHYPWR_HSCI1_SLEEP)
> +
> +/* PHY clock control */
> +#define EXYNOS_4212_UPHYCLK			0x4
> +
> +#define EXYNOS_4212_UPHYCLK_PHYFSEL_MASK	(0x7 << 0)
> +#define EXYNOS_4212_UPHYCLK_PHYFSEL_9MHZ6	(0x0 << 0)
> +#define EXYNOS_4212_UPHYCLK_PHYFSEL_10MHZ	(0x1 << 0)
> +#define EXYNOS_4212_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
> +#define EXYNOS_4212_UPHYCLK_PHYFSEL_19MHZ2	(0x3 << 0)
> +#define EXYNOS_4212_UPHYCLK_PHYFSEL_20MHZ	(0x4 << 0)
> +#define EXYNOS_4212_UPHYCLK_PHYFSEL_24MHZ	(0x5 << 0)
> +#define EXYNOS_4212_UPHYCLK_PHYFSEL_50MHZ	(0x7 << 0)
> +
> +#define EXYNOS_4212_UPHYCLK_PHY0_ID_PULLUP	(0x1 << 3)
> +#define EXYNOS_4212_UPHYCLK_PHY0_COMMON_ON	(0x1 << 4)
> +#define EXYNOS_4212_UPHYCLK_PHY1_COMMON_ON	(0x1 << 7)
> +
> +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_MASK	(0x7f << 10)
> +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_12MHZ	(0x24 << 10)
> +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_15MHZ	(0x1c << 10)
> +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_16MHZ	(0x1a << 10)
> +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_19MHZ2	(0x15 << 10)
> +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_20MHZ	(0x14 << 10)
> +
> +/* PHY reset control */
> +#define EXYNOS_4212_UPHYRST			0x8
> +
> +#define EXYNOS_4212_URSTCON_DEVICE		(1 << 0)
> +#define EXYNOS_4212_URSTCON_OTG_HLINK		(1 << 1)
> +#define EXYNOS_4212_URSTCON_OTG_PHYLINK		(1 << 2)
> +#define EXYNOS_4212_URSTCON_HOST_PHY		(1 << 3)
> +#define EXYNOS_4212_URSTCON_PHY1		(1 << 4)
> +#define EXYNOS_4212_URSTCON_HSIC0		(1 << 5)
> +#define EXYNOS_4212_URSTCON_HSIC1		(1 << 6)
> +#define EXYNOS_4212_URSTCON_HOST_LINK_ALL	(1 << 7)
> +#define EXYNOS_4212_URSTCON_HOST_LINK_P0	(1 << 8)
> +#define EXYNOS_4212_URSTCON_HOST_LINK_P1	(1 << 9)
> +#define EXYNOS_4212_URSTCON_HOST_LINK_P2	(1 << 10)
> +
> +/* Isolation, configured in the power management unit */
> +#define EXYNOS_4212_USB_ISOL_OFFSET		0x0
> +#define EXYNOS_4212_USB_ISOL_OTG		(1 << 0)
> +#define EXYNOS_4212_USB_ISOL_HSIC0_OFFSET	0x4
> +#define EXYNOS_4212_USB_ISOL_HSIC0		(1 << 0)
> +#define EXYNOS_4212_USB_ISOL_HSIC1_OFFSET	0x8
> +#define EXYNOS_4212_USB_ISOL_HSIC1		(1 << 0)
> +
> +enum exynos4x12_phy_id {
> +	EXYNOS4212_DEVICE,
> +	EXYNOS4212_HOST,
> +	EXYNOS4212_HSIC0,
> +	EXYNOS4212_HSIC1,
> +	EXYNOS4212_NUM_PHYS,
> +};
> +
> +/* exynos4212_rate_to_clk() converts the supplied clock rate to the value that
> + * can be written to the phy register. */
> +static u32 exynos4212_rate_to_clk(unsigned long rate)
> +{
> +	unsigned int clksel;
> +
> +	/* EXYNOS_4212_UPHYCLK_PHYFSEL_MASK */
> +
> +	switch (rate) {
> +	case 9600 * KHZ:
> +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_9MHZ6;
> +		break;
> +	case 10 * MHZ:
> +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_10MHZ;
> +		break;
> +	case 12 * MHZ:
> +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_12MHZ;
> +		break;
> +	case 19200 * KHZ:
> +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_19MHZ2;
> +		break;
> +	case 20 * MHZ:
> +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_20MHZ;
> +		break;
> +	case 24 * MHZ:
> +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_24MHZ;
> +		break;
> +	case 50 * MHZ:
> +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_50MHZ;
> +		break;
> +	default:
> +		clksel = CLKSEL_ERROR;
> +	}
> +
> +	return clksel;
> +}
> +
> +static void exynos4212_isol(struct uphy_instance *inst, bool on)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +	u32 offset;
> +	u32 mask;
> +	u32 tmp;
> +
> +	if (!drv->reg_isol)
> +		return;
> +
> +	switch (inst->cfg->id) {
> +	case EXYNOS4212_DEVICE:
> +		offset = EXYNOS_4212_USB_ISOL_OFFSET;
> +		mask = EXYNOS_4212_USB_ISOL_OTG;
> +		break;
> +	case EXYNOS4212_HOST:
> +		offset = EXYNOS_4212_USB_ISOL_OFFSET;
> +		mask = EXYNOS_4212_USB_ISOL_OTG;
> +		break;
> +	case EXYNOS4212_HSIC0:
> +		offset = EXYNOS_4212_USB_ISOL_HSIC0_OFFSET;
> +		mask = EXYNOS_4212_USB_ISOL_HSIC0;
> +		break;
> +	case EXYNOS4212_HSIC1:
> +		offset = EXYNOS_4212_USB_ISOL_HSIC1_OFFSET;
> +		mask = EXYNOS_4212_USB_ISOL_HSIC1;
> +		break;
> +	default:
> +		return;
> +	};
> +
> +	tmp = readl(drv->reg_isol + offset);
> +	if (on)
> +		tmp &= ~mask;
> +	else
> +		tmp |= mask;
> +	writel(tmp, drv->reg_isol + offset);
> +}
> +
> +static void exynos4212_phy_pwr(struct uphy_instance *inst, bool on)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +	u32 rstbits = 0;
> +	u32 phypwr = 0;
> +	u32 rst;
> +	u32 pwr;
> +
> +	switch (inst->cfg->id) {
> +	case EXYNOS4212_DEVICE:
> +		phypwr =	EXYNOS_4212_UPHYPWR_DEV;
> +		rstbits =	EXYNOS_4212_URSTCON_DEVICE;
> +		break;
> +	case EXYNOS4212_HOST:
> +		phypwr =	EXYNOS_4212_UPHYPWR_HOST;
> +		rstbits =	EXYNOS_4212_URSTCON_HOST_PHY;
> +		break;
> +	case EXYNOS4212_HSIC0:
> +		phypwr =	EXYNOS_4212_UPHYPWR_HSCI0;
> +		rstbits =	EXYNOS_4212_URSTCON_HSIC1 |
> +				EXYNOS_4212_URSTCON_HOST_LINK_P0 |
> +				EXYNOS_4212_URSTCON_HOST_PHY;
> +		break;
> +	case EXYNOS4212_HSIC1:
> +		phypwr =	EXYNOS_4212_UPHYPWR_HSCI1;
> +		rstbits =	EXYNOS_4212_URSTCON_HSIC1 |
> +				EXYNOS_4212_URSTCON_HOST_LINK_P1;
> +		break;
> +	};
> +
> +	if (on) {
> +		writel(inst->clk, drv->reg_phy + EXYNOS_4212_UPHYCLK);
> +
> +		pwr = readl(drv->reg_phy + EXYNOS_4212_UPHYPWR);
> +		pwr &= ~phypwr;
> +		writel(pwr, drv->reg_phy + EXYNOS_4212_UPHYPWR);
> +
> +		rst = readl(drv->reg_phy + EXYNOS_4212_UPHYRST);
> +		rst |= rstbits;
> +		writel(rst, drv->reg_phy + EXYNOS_4212_UPHYRST);
> +		udelay(10);
> +		rst &= ~rstbits;
> +		writel(rst, drv->reg_phy + EXYNOS_4212_UPHYRST);
> +	} else {
> +		pwr = readl(drv->reg_phy + EXYNOS_4212_UPHYPWR);
> +		pwr |= phypwr;
> +		writel(pwr, drv->reg_phy + EXYNOS_4212_UPHYPWR);
> +	}
> +}
> +
> +static int exynos4212_power_on(struct uphy_instance *inst)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +
> +	if (inst->state == STATE_ON) {
> +		dev_err(drv->dev, "usb phy \"%s\" already on",
> +							inst->cfg->label);
> +		return -ENODEV;
> +	}
> +
> +	inst->state = STATE_ON;
> +	inst->ref_cnt++;
> +	if (inst->ref_cnt > 1)
> +		return 0;
> +
> +	exynos4212_phy_pwr(inst, 1);
> +	exynos4212_isol(inst, 0);
> +
> +	/* Power on the device, as it is necessary for HSIC to work */
> +	if (inst->cfg->id == EXYNOS4212_HSIC0) {
> +		struct uphy_instance *device =
> +					&drv->uphy_instances[EXYNOS4212_DEVICE];
> +		exynos4212_phy_pwr(device, 1);
> +		exynos4212_isol(device, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +static int exynos4212_power_off(struct uphy_instance *inst)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +
> +	if (inst->state == STATE_OFF) {
> +		dev_err(drv->dev, "usb phy \"%s\" already off",
> +							inst->cfg->label);
> +		return -EINVAL;
> +	}
> +
> +	inst->state = STATE_OFF;
> +	inst->ref_cnt--;
> +
> +	if (inst->ref_cnt > 0)
> +		return 0;
> +
> +	exynos4212_isol(inst, 1);
> +	exynos4212_phy_pwr(inst, 0);
> +
> +	if (inst->cfg->id == EXYNOS4212_HSIC0) {
> +		struct uphy_instance *device =
> +					&drv->uphy_instances[EXYNOS4212_DEVICE];
> +		exynos4212_isol(device, 1);
> +		exynos4212_phy_pwr(device, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +
> +static const struct common_phy exynos4212_phys[] = {
> +	{
> +		.label		= "device",
> +		.type		= PHY_DEVICE,
> +		.id		= EXYNOS4212_DEVICE,
> +		.rate_to_clk	= exynos4212_rate_to_clk,
> +		.power_on	= exynos4212_power_on,
> +		.power_off	= exynos4212_power_off,
> +	},
> +	{
> +		.label		= "host",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS4212_HOST,
> +		.rate_to_clk	= exynos4212_rate_to_clk,
> +		.power_on	= exynos4212_power_on,
> +		.power_off	= exynos4212_power_off,
> +	},
> +	{
> +		.label		= "hsic0",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS4212_HSIC0,
> +		.rate_to_clk	= exynos4212_rate_to_clk,
> +		.power_on	= exynos4212_power_on,
> +		.power_off	= exynos4212_power_off,
> +	},
> +	{
> +		.label		= "hsic1",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS4212_HSIC1,
> +		.rate_to_clk	= exynos4212_rate_to_clk,
> +		.power_on	= exynos4212_power_on,
> +		.power_off	= exynos4212_power_off,
> +	},
> +	{},
> +};
> +
> +const struct uphy_config exynos4212_uphy_config = {
> +	.cpu		= TYPE_EXYNOS4212,
> +	.num_phys	= EXYNOS4212_NUM_PHYS,
> +	.phys		= exynos4212_phys,
> +};

I see quite an amount of similarities between the two PHY drivers. It would be
better if we can have a single driver to handle it.

Thanks
Kishon

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

* Re: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver
@ 2013-10-25 15:39     ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 46+ messages in thread
From: Kishon Vijay Abraham I @ 2013-10-25 15:39 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-u79uwXL29TY76Z2rM5mHXA,
	kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ,
	t.figa-Sze3O3UU22JBDgjK7y7TUQ, s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ,
	m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ,
	gautam.vivek-Sze3O3UU22JBDgjK7y7TUQ,
	mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w

Hi,

On Friday 25 October 2013 07:45 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.
> 
> Signed-off-by: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> Signed-off-by: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> ---
>  .../devicetree/bindings/phy/samsung-usbphy.txt     |   51 +++
>  drivers/phy/Kconfig                                |   21 ++
>  drivers/phy/Makefile                               |    3 +
>  drivers/phy/phy-exynos-usb.c                       |  245 ++++++++++++++
>  drivers/phy/phy-exynos-usb.h                       |   94 ++++++
>  drivers/phy/phy-exynos4210-usb.c                   |  295 +++++++++++++++++
>  drivers/phy/phy-exynos4212-usb.c                   |  343 ++++++++++++++++++++
>  7 files changed, 1052 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/samsung-usbphy.txt
>  create mode 100644 drivers/phy/phy-exynos-usb.c
>  create mode 100644 drivers/phy/phy-exynos-usb.h
>  create mode 100644 drivers/phy/phy-exynos4210-usb.c
>  create mode 100644 drivers/phy/phy-exynos4212-usb.c
> 
> diff --git a/Documentation/devicetree/bindings/phy/samsung-usbphy.txt b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> new file mode 100644
> index 0000000..f112b37
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> @@ -0,0 +1,51 @@
> +Samsung S5P/EXYNOS SoC series USB PHY
> +-------------------------------------------------
> +
> +Required properties:
> +- compatible : should be one of the listed compatibles:
> +	- "samsung,exynos4210-usbphy"
> +	- "samsung,exynos4212-usbphy"
> +- reg : a list of registers used by phy driver
> +	- first and obligatory is the location of phy modules registers
> +	- second and also required is the location of isolation registers
> +	  (isolation registers control the physical connection between the in
> +	  SoC modules and outside of the SoC, this also can be called enable
> +	  control in the documentation of the SoC)
> +	- third is the location of the mode switch register, this only applies
> +	  to SoCs that have such a feature; mode switching enables to have
> +	  both host and device used the same SoC pins and is commonly used
> +	  when OTG is supported
> +- #phy-cells : from the generic phy bindings, must be 1;
> +
> +The second cell in the PHY specifier identifies the PHY its meaning is SoC
> +dependent. For the currently supported SoCs (Exynos 4210 and Exynos 4212) it
> +is as follows:
> +  0 - USB device,
> +  1 - USB host,
> +  2 - HSIC0,
> +  3 - HSIC1,

HSIC is supposedly to be transceiver less no? You have to program something in
the digital side?
You have a single IP that have all these functionalities?
> +
> +Example:
> +
> +For Exynos 4412 (compatible with Exynos 4212):
> +
> +exynos_usbphy: exynos-usbphy@125B0000 {
> +	compatible = "samsung,exynos4212-usbphy";
> +	reg = <0x125B0000 0x100 0x10020704 0x0c 0x1001021c 0x4>;
> +	ranges;
> +	#address-cells = <1>;
> +	#size-cells = <1>;

The above 3 properties aren't documented? Are they needed here?
> +	clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
> +							<&clock 2>;
> +	clock-names = "phy", "device", "host", "hsic0", "hsic1";
> +	status = "okay";
> +	#phy-cells = <1>;
> +};
> +
> +Then the PHY can be used in other nodes such as:
> +
> +ehci@12580000 {
> +	status = "okay";
> +	phys = <&exynos_usbphy 2>;
> +	phy-names = "hsic0";
> +};
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 349bef2..2f7ac0a 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -15,4 +15,25 @@ config GENERIC_PHY
>  	  phy users can obtain reference to the PHY. All the users of this
>  	  framework should select this config.
>  
> +config PHY_EXYNOS_USB
> +	tristate "Samsung USB PHY driver (using the Generic PHY Framework)"
Mentioning *Generic PHY Framework* is not necessary.
*select GENERIC_PHY* here
> +	help
> +	  Enable this to support Samsung USB phy helper driver for Samsung SoCs.
> +	  This driver provides common interface to interact, for Samsung
> +	  USB 2.0 PHY driver.

If it's going to be used only for usb2, name it PHY_EXYNOS_USB2.
> +
> +config PHY_EXYNOS4210_USB
> +	bool "Support for Exynos 4210"
> +	depends on PHY_EXYNOS_USB
> +	depends on CPU_EXYNOS4210
> +	help
> +	  Enable USB PHY support for Exynos 4210
> +
> +config PHY_EXYNOS4212_USB
> +	bool "Support for Exynos 4212"
> +	depends on PHY_EXYNOS_USB
> +	depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
> +	help
> +	  Enable USB PHY support for Exynos 4212

How difference is USB PHY in Exynos 4212 from Exynos 4210? If there isn't much,
I would suggest to use a single driver.
> +
>  endmenu
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index 9e9560f..ca3dc82 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -3,3 +3,6 @@
>  #
>  
>  obj-$(CONFIG_GENERIC_PHY)	+= phy-core.o
> +obj-$(CONFIG_PHY_EXYNOS_USB)		+= phy-exynos-usb.o
> +obj-$(CONFIG_PHY_EXYNOS4210_USB)	+= phy-exynos4210-usb.o
> +obj-$(CONFIG_PHY_EXYNOS4212_USB)	+= phy-exynos4212-usb.o
> diff --git a/drivers/phy/phy-exynos-usb.c b/drivers/phy/phy-exynos-usb.c
> new file mode 100644
> index 0000000..d4a26df
> --- /dev/null
> +++ b/drivers/phy/phy-exynos-usb.c

phy-exynos-usb2.c?
> @@ -0,0 +1,245 @@
> +/*
> + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
> +#include <linux/io.h>
> +#include <linux/kernel.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-exynos-usb.h"
> +
> +static int exynos_uphy_power_on(struct phy *phy)

exynos_usb2_phy here and everywhere below.
> +{
> +	struct uphy_instance *inst = phy_get_drvdata(phy);
> +	struct uphy_driver *drv = inst->drv;
> +	int ret;
> +
> +	dev_info(drv->dev, "Request to power_on \"%s\" usb phy\n",
> +							inst->cfg->label);

make it dev_dbg if it's necessary.
> +	ret = clk_prepare_enable(drv->clk);
> +	if (ret)
> +		return ret;
> +	if (inst->cfg->power_on) {
> +		spin_lock(&drv->lock);
> +		ret = inst->cfg->power_on(inst);
> +		spin_unlock(&drv->lock);
> +	}
> +	clk_disable_unprepare(drv->clk);

hmm.. don't you need the clock to be on during the duration of the PHY operation?
> +	return ret;
> +}
> +
> +static int exynos_uphy_power_off(struct phy *phy)
> +{
> +	struct uphy_instance *inst = phy_get_drvdata(phy);
> +	struct uphy_driver *drv = inst->drv;
> +	int ret;
> +
> +	dev_info(drv->dev, "Request to power_off \"%s\" usb phy\n",
> +							inst->cfg->label);

dev_dbg?
> +	ret = clk_prepare_enable(drv->clk);
> +	if (ret)
> +		return ret;
> +	if (inst->cfg->power_off) {
> +		spin_lock(&drv->lock);
> +		ret = inst->cfg->power_off(inst);
> +		spin_unlock(&drv->lock);
> +	}
> +	clk_disable_unprepare(drv->clk);
> +	return ret;
> +}
> +
> +static struct phy_ops exynos_uphy_ops = {
> +	.power_on	= exynos_uphy_power_on,
> +	.power_off	= exynos_uphy_power_off,
> +	.owner		= THIS_MODULE,
> +};
> +
> +static struct phy *exynos_uphy_xlate(struct device *dev,
> +					struct of_phandle_args *args)
> +{
> +	struct uphy_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->uphy_instances[args->args[0]].phy;
> +}
> +
> +static const struct of_device_id exynos_uphy_of_match[];
> +
> +static int exynos_uphy_probe(struct platform_device *pdev)
> +{
> +	struct uphy_driver *drv;
> +	struct device *dev = &pdev->dev;
> +	struct resource *mem;
> +	struct phy_provider *phy_provider;
> +
> +	const struct of_device_id *match;
> +	const struct uphy_config *cfg;
> +	struct clk *clk;
> +
> +	int i;
> +
> +	match = of_match_node(exynos_uphy_of_match, pdev->dev.of_node);
> +	if (!match) {
> +		dev_err(dev, "of_match_node() failed\n");
> +		return -EINVAL;
> +	}
> +	cfg = match->data;
> +	if (!cfg) {
> +		dev_err(dev, "Failed to get configuration\n");
> +		return -EINVAL;
> +	}
> +
> +	drv = devm_kzalloc(dev, sizeof(struct uphy_driver) +
> +		cfg->num_phys * sizeof(struct uphy_instance), GFP_KERNEL);
> +
> +	if (!drv) {
> +		dev_err(dev, "Failed to allocate memory\n");
> +		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);
> +
empty blank line.
> +	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);
> +	}
> +
> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	drv->reg_isol = devm_ioremap_resource(dev, mem);
> +	if (IS_ERR(drv->reg_isol)) {
> +		dev_err(dev, "Failed to map register memory (isolation)\n");
> +		return PTR_ERR(drv->reg_isol);
> +	}
> +
> +	switch (drv->cfg->cpu) {
> +	case TYPE_EXYNOS4210:
> +	case TYPE_EXYNOS4212:

Lets not add such cpu checks inside driver.
> +		mem = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> +		drv->reg_mode = devm_ioremap_resource(dev, mem);
> +		if (IS_ERR(drv->reg_mode)) {
> +			dev_err(dev, "Failed to map register memory (mode switch)\n");
> +			return PTR_ERR(drv->reg_mode);
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	phy_provider = devm_of_phy_provider_register(dev,
> +							exynos_uphy_xlate);
> +	if (IS_ERR(phy_provider)) {
> +		dev_err(drv->dev, "Failed to register phy provider\n");
> +		return PTR_ERR(phy_provider);
> +	}
> +
> +	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 uphy_instance *p = &drv->uphy_instances[i];
> +
> +		dev_info(dev, "Creating phy \"%s\"\n", label);
> +		p->phy = devm_phy_create(dev, &exynos_uphy_ops, NULL);
> +		if (IS_ERR(p->phy)) {
> +			dev_err(drv->dev, "Failed to create uphy \"%s\"\n",
> +						label);
> +			return PTR_ERR(p->phy);
> +		}
> +
> +		p->cfg = &drv->cfg->phys[i];
> +		p->drv = drv;
> +		phy_set_drvdata(p->phy, p);
> +
> +		clk = 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->rate = clk_get_rate(clk);
> +
> +		if (p->cfg->rate_to_clk) {
> +			p->clk = p->cfg->rate_to_clk(p->rate);
> +			if (p->clk == CLKSEL_ERROR) {
> +				dev_err(dev, "Clock rate (%ld) not supported\n",
> +								p->rate);
> +				clk_put(clk);
> +				return -EINVAL;
> +			}
> +		}
> +		clk_put(clk);
> +	}
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PHY_EXYNOS4210_USB
Do we really need this?

> +extern const struct uphy_config exynos4210_uphy_config;
> +#endif
> +
> +#ifdef CONFIG_PHY_EXYNOS4212_USB

Same here.
> +extern const struct uphy_config exynos4212_uphy_config;
> +#endif
> +
> +static const struct of_device_id exynos_uphy_of_match[] = {
> +#ifdef CONFIG_PHY_EXYNOS4210_USB

#if not needed here.
> +	{
> +		.compatible = "samsung,exynos4210-usbphy",
> +		.data = &exynos4210_uphy_config,
> +	},
> +#endif
> +#ifdef CONFIG_PHY_EXYNOS4212_USB

here too.
> +	{
> +		.compatible = "samsung,exynos4212-usbphy",
> +		.data = &exynos4212_uphy_config,
> +	},
> +#endif
> +	{ },
> +};
> +
> +static struct platform_driver exynos_uphy_driver = {
> +	.probe	= exynos_uphy_probe,
> +	.driver = {
> +		.of_match_table	= exynos_uphy_of_match,
> +		.name		= "exynos-usbphy-new",
"exynos-usb2-phy".
> +		.owner		= THIS_MODULE,
> +	}
> +};
> +
> +module_platform_driver(exynos_uphy_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:exynos-uphy-new");
> +
> diff --git a/drivers/phy/phy-exynos-usb.h b/drivers/phy/phy-exynos-usb.h
> new file mode 100644
> index 0000000..f45cb3c
> --- /dev/null
> +++ b/drivers/phy/phy-exynos-usb.h
> @@ -0,0 +1,94 @@
> +/*
> + * Samsung S5P/EXYNOS SoC series USB 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_SAMSUNG_NEW_H
> +#define _PHY_SAMSUNG_NEW_H
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/kernel.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>
> +
> +#define CLKSEL_ERROR                       -1
> +
> +#ifndef KHZ
> +#define KHZ 1000
> +#endif
> +
> +#ifndef MHZ
> +#define MHZ (KHZ * KHZ)
> +#endif
> +
> +enum phy_type {
> +	PHY_DEVICE,
> +	PHY_HOST,
> +};
> +
> +enum samsung_cpu_type {
> +	TYPE_S3C64XX,
> +	TYPE_EXYNOS4210,
> +	TYPE_EXYNOS4212,

No *cpu_type* inside driver files.
> +};
> +
> +enum uphy_state {
> +	STATE_OFF,
> +	STATE_ON,
> +};
> +
> +struct uphy_driver;
> +struct uphy_instance;
> +struct uphy_config;
> +
> +struct uphy_instance {
> +	struct uphy_driver *drv;
> +	struct phy *phy;
> +	const struct common_phy *cfg;
> +	enum uphy_state state;
> +	int ref_cnt;
> +	u32 clk;
> +	unsigned long rate;
> +};
> +
> +struct uphy_driver {
> +	struct device *dev;
> +	spinlock_t lock;
> +	void __iomem *reg_phy;
> +	void __iomem *reg_isol;
> +	void __iomem *reg_mode;
> +	const struct uphy_config *cfg;
> +	struct clk *clk;
> +	struct uphy_instance uphy_instances[0];

you might have more than one phy instance right?
> +};
> +
> +struct common_phy {
> +	char *label;
> +	enum phy_type type;
> +	unsigned int id;
> +	u32 (*rate_to_clk)(unsigned long);
> +	int (*power_on)(struct uphy_instance*);
> +	int (*power_off)(struct uphy_instance*);
> +};
> +
> +
> +struct uphy_config {
> +	enum samsung_cpu_type cpu;
> +	int num_phys;
> +	const struct common_phy *phys;
> +};
> +
> +#endif
> +
> diff --git a/drivers/phy/phy-exynos4210-usb.c b/drivers/phy/phy-exynos4210-usb.c
> new file mode 100644
> index 0000000..6cf74f7
> --- /dev/null
> +++ b/drivers/phy/phy-exynos4210-usb.c
> @@ -0,0 +1,295 @@
> +/*
> + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
> +#include <linux/io.h>
> +#include <linux/kernel.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-exynos-usb.h"
> +
> +/* Exynos USB PHY registers */
> +
> +/* PHY power control */
> +#define EXYNOS_4210_UPHYPWR			0x0
> +
> +#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND	(1 << 0)
> +#define EXYNOS_4210_UPHYPWR_PHY0_PWR		(1 << 3)
> +#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR	(1 << 4)
> +#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP		(1 << 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	(1 << 6)
> +#define EXYNOS_4210_UPHYPWR_PHY1_PWR		(1 << 7)
> +#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP		(1 << 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_HSCI0_SUSPEND	(1 << 9)
> +#define EXYNOS_4210_UPHYPWR_HSCI0_SLEEP		(1 << 10)
> +#define EXYNOS_4210_UPHYPWR_HSCI0 ( \
> +	EXYNOS_4210_UPHYPWR_HSCI0_SUSPEND | \
> +	EXYNOS_4210_UPHYPWR_HSCI0_SLEEP)
> +
> +#define EXYNOS_4210_UPHYPWR_HSCI1_SUSPEND	(1 << 11)
> +#define EXYNOS_4210_UPHYPWR_HSCI1_SLEEP		(1 << 12)
> +#define EXYNOS_4210_UPHYPWR_HSCI1 ( \
> +	EXYNOS_4210_UPHYPWR_HSCI1_SUSPEND | \
> +	EXYNOS_4210_UPHYPWR_HSCI1_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	(0x1 << 2)
> +#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON	(0x1 << 4)
> +#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON	(0x1 << 7)
> +
> +/* PHY reset control */
> +#define EXYNOS_4210_UPHYRST			0x8
> +
> +#define EXYNOS_4210_URSTCON_PHY0		(1 << 0)
> +#define EXYNOS_4210_URSTCON_OTG_HLINK		(1 << 1)
> +#define EXYNOS_4210_URSTCON_OTG_PHYLINK		(1 << 2)
> +#define EXYNOS_4210_URSTCON_PHY1_ALL		(1 << 3)
> +#define EXYNOS_4210_URSTCON_PHY1_P0		(1 << 4)
> +#define EXYNOS_4210_URSTCON_PHY1_P1P2		(1 << 5)
> +#define EXYNOS_4210_URSTCON_HOST_LINK_ALL	(1 << 6)
> +#define EXYNOS_4210_URSTCON_HOST_LINK_P0	(1 << 7)
> +#define EXYNOS_4210_URSTCON_HOST_LINK_P1	(1 << 8)
> +#define EXYNOS_4210_URSTCON_HOST_LINK_P2	(1 << 9)But 
> +
> +/* Isolation, configured in the power management unit */
> +#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET	0x0
> +#define EXYNOS_4210_USB_ISOL_DEVICE		(1 << 0)
> +#define EXYNOS_4210_USB_ISOL_HOST_OFFSET	0x4
> +#define EXYNOS_4210_USB_ISOL_HOST		(1 << 0)
> +
> +/* USBYPHY1 Floating prevention */
> +#define EXYNOS_4210_UPHY1CON			0x34
> +#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION	0x1
> +
> +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. */

use proper commenting styles Documentation/CodingStyle
> +static u32 exynos4210_rate_to_clk(unsigned long rate)
> +{
> +	unsigned int clksel;
> +
> +	switch (rate) {
> +	case 12 * MHZ:
> +		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
> +		break;
> +	case 24 * MHZ:
> +		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
> +		break;
> +	case 48 * MHZ:
> +		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
> +		break;
> +	default:
> +		clksel = CLKSEL_ERROR;
> +	}
> +
> +	return clksel;
> +}
> +
> +static void exynos4210_isol(struct uphy_instance *inst, bool on)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +	u32 offset;
> +	u32 mask;
> +	u32 tmp;
> +
> +	if (!drv->reg_isol)
> +		return;
> +
> +	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;
> +	};
> +
> +	tmp = readl(drv->reg_isol + offset);
> +	if (on)
> +		tmp &= ~mask;
> +	else
> +		tmp |= mask;
> +	writel(tmp, drv->reg_isol + offset);
> +}
> +
> +static void exynos4210_phy_pwr(struct uphy_instance *inst, bool on)
> +{
> +	struct uphy_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_HSCI0;
> +		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
> +				EXYNOS_4210_URSTCON_HOST_LINK_P1;
> +		break;
> +	case EXYNOS4210_HSIC1:
> +		phypwr =	EXYNOS_4210_UPHYPWR_HSCI1;
> +		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
> +				EXYNOS_4210_URSTCON_HOST_LINK_P2;
> +		break;
> +	};
> +
> +	if (on) {
> +		writel(inst->clk, 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);

usleep_range?
> +		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 uphy_instance *inst)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +
> +	if (inst->state == STATE_ON) {
> +		dev_err(drv->dev, "usb phy \"%s\" already on",
> +							inst->cfg->label);
> +		return -ENODEV;
> +	}
> +	inst->state = STATE_ON;
> +	inst->ref_cnt++;
> +	if (inst->ref_cnt > 1)
> +		return 0;

reference counting shouldn't be needed. It's been handled by the PHY framework.
> +
> +	/* 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 uphy_instance *inst)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +
> +	if (inst->state == STATE_OFF) {
> +		dev_err(drv->dev, "usb phy \"%s\" already off",
> +							inst->cfg->label);
> +		return -EINVAL;
> +	}
> +
> +	inst->state = STATE_OFF;
> +	inst->ref_cnt--;
> +	if (inst->ref_cnt > 0)
> +		return 0;

same here.
> +
> +	exynos4210_isol(inst, 1);
> +	exynos4210_phy_pwr(inst, 0);
> +
> +	return 0;
> +}
> +
> +
> +static const struct common_phy exynos4210_phys[] = {
> +	{
> +		.label		= "device",
> +		.type		= PHY_DEVICE,
> +		.id		= EXYNOS4210_DEVICE,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "host",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS4210_HOST,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "hsic0",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS4210_HSIC0,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{
> +		.label		= "hsic1",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS4210_HSIC1,
> +		.rate_to_clk	= exynos4210_rate_to_clk,
> +		.power_on	= exynos4210_power_on,
> +		.power_off	= exynos4210_power_off,
> +	},
> +	{},
> +};
> +
> +const struct uphy_config exynos4210_uphy_config = {
> +	.cpu		= TYPE_EXYNOS4210,
> +	.num_phys	= EXYNOS4210_NUM_PHYS,
> +	.phys		= exynos4210_phys,
> +};
> +
> diff --git a/drivers/phy/phy-exynos4212-usb.c b/drivers/phy/phy-exynos4212-usb.c
> new file mode 100644
> index 0000000..c07ae8e
> --- /dev/null
> +++ b/drivers/phy/phy-exynos4212-usb.c
> @@ -0,0 +1,343 @@
> +/*
> + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
> +#include <linux/io.h>
> +#include <linux/kernel.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-exynos-usb.h"
> +
> +/* Exynos USB PHY registers */
> +
> +/* PHY power control */
> +#define EXYNOS_4212_UPHYPWR			0x0
> +
> +#define EXYNOS_4212_UPHYPWR_DEV_SUSPEND		(1 << 0)
> +#define EXYNOS_4212_UPHYPWR_DEV_PWR		(1 << 3)
> +#define EXYNOS_4212_UPHYPWR_DEV_OTG_PWR		(1 << 4)
> +#define EXYNOS_4212_UPHYPWR_DEV_SLEEP		(1 << 5)
> +#define EXYNOS_4212_UPHYPWR_DEV	( \
> +	EXYNOS_4212_UPHYPWR_DEV_SUSPEND | \
> +	EXYNOS_4212_UPHYPWR_DEV_PWR | \
> +	EXYNOS_4212_UPHYPWR_DEV_OTG_PWR | \
> +	EXYNOS_4212_UPHYPWR_DEV_SLEEP)
> +
> +#define EXYNOS_4212_UPHYPWR_HOST_SUSPEND	(1 << 6)
> +#define EXYNOS_4212_UPHYPWR_HOST_PWR		(1 << 7)
> +#define EXYNOS_4212_UPHYPWR_HOST_SLEEP		(1 << 8)
> +#define EXYNOS_4212_UPHYPWR_HOST ( \
> +	EXYNOS_4212_UPHYPWR_HOST_SUSPEND | \
> +	EXYNOS_4212_UPHYPWR_HOST_PWR | \
> +	EXYNOS_4212_UPHYPWR_HOST_SLEEP)
> +
> +#define EXYNOS_4212_UPHYPWR_HSCI0_SUSPEND	(1 << 9)
> +#define EXYNOS_4212_UPHYPWR_HSCI0_PWR		(1 << 10)
> +#define EXYNOS_4212_UPHYPWR_HSCI0_SLEEP		(1 << 11)
> +#define EXYNOS_4212_UPHYPWR_HSCI0 ( \
> +	EXYNOS_4212_UPHYPWR_HSCI0_SUSPEND | \
> +	EXYNOS_4212_UPHYPWR_HSCI0_PWR | \
> +	EXYNOS_4212_UPHYPWR_HSCI0_SLEEP)
> +
> +#define EXYNOS_4212_UPHYPWR_HSCI1_SUSPEND	(1 << 12)
> +#define EXYNOS_4212_UPHYPWR_HSCI1_PWR		(1 << 13)
> +#define EXYNOS_4212_UPHYPWR_HSCI1_SLEEP		(1 << 14)
> +#define EXYNOS_4212_UPHYPWR_HSCI1 ( \
> +	EXYNOS_4212_UPHYPWR_HSCI1_SUSPEND | \
> +	EXYNOS_4212_UPHYPWR_HSCI1_PWR | \
> +	EXYNOS_4212_UPHYPWR_HSCI1_SLEEP)
> +
> +/* PHY clock control */
> +#define EXYNOS_4212_UPHYCLK			0x4
> +
> +#define EXYNOS_4212_UPHYCLK_PHYFSEL_MASK	(0x7 << 0)
> +#define EXYNOS_4212_UPHYCLK_PHYFSEL_9MHZ6	(0x0 << 0)
> +#define EXYNOS_4212_UPHYCLK_PHYFSEL_10MHZ	(0x1 << 0)
> +#define EXYNOS_4212_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
> +#define EXYNOS_4212_UPHYCLK_PHYFSEL_19MHZ2	(0x3 << 0)
> +#define EXYNOS_4212_UPHYCLK_PHYFSEL_20MHZ	(0x4 << 0)
> +#define EXYNOS_4212_UPHYCLK_PHYFSEL_24MHZ	(0x5 << 0)
> +#define EXYNOS_4212_UPHYCLK_PHYFSEL_50MHZ	(0x7 << 0)
> +
> +#define EXYNOS_4212_UPHYCLK_PHY0_ID_PULLUP	(0x1 << 3)
> +#define EXYNOS_4212_UPHYCLK_PHY0_COMMON_ON	(0x1 << 4)
> +#define EXYNOS_4212_UPHYCLK_PHY1_COMMON_ON	(0x1 << 7)
> +
> +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_MASK	(0x7f << 10)
> +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_12MHZ	(0x24 << 10)
> +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_15MHZ	(0x1c << 10)
> +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_16MHZ	(0x1a << 10)
> +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_19MHZ2	(0x15 << 10)
> +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_20MHZ	(0x14 << 10)
> +
> +/* PHY reset control */
> +#define EXYNOS_4212_UPHYRST			0x8
> +
> +#define EXYNOS_4212_URSTCON_DEVICE		(1 << 0)
> +#define EXYNOS_4212_URSTCON_OTG_HLINK		(1 << 1)
> +#define EXYNOS_4212_URSTCON_OTG_PHYLINK		(1 << 2)
> +#define EXYNOS_4212_URSTCON_HOST_PHY		(1 << 3)
> +#define EXYNOS_4212_URSTCON_PHY1		(1 << 4)
> +#define EXYNOS_4212_URSTCON_HSIC0		(1 << 5)
> +#define EXYNOS_4212_URSTCON_HSIC1		(1 << 6)
> +#define EXYNOS_4212_URSTCON_HOST_LINK_ALL	(1 << 7)
> +#define EXYNOS_4212_URSTCON_HOST_LINK_P0	(1 << 8)
> +#define EXYNOS_4212_URSTCON_HOST_LINK_P1	(1 << 9)
> +#define EXYNOS_4212_URSTCON_HOST_LINK_P2	(1 << 10)
> +
> +/* Isolation, configured in the power management unit */
> +#define EXYNOS_4212_USB_ISOL_OFFSET		0x0
> +#define EXYNOS_4212_USB_ISOL_OTG		(1 << 0)
> +#define EXYNOS_4212_USB_ISOL_HSIC0_OFFSET	0x4
> +#define EXYNOS_4212_USB_ISOL_HSIC0		(1 << 0)
> +#define EXYNOS_4212_USB_ISOL_HSIC1_OFFSET	0x8
> +#define EXYNOS_4212_USB_ISOL_HSIC1		(1 << 0)
> +
> +enum exynos4x12_phy_id {
> +	EXYNOS4212_DEVICE,
> +	EXYNOS4212_HOST,
> +	EXYNOS4212_HSIC0,
> +	EXYNOS4212_HSIC1,
> +	EXYNOS4212_NUM_PHYS,
> +};
> +
> +/* exynos4212_rate_to_clk() converts the supplied clock rate to the value that
> + * can be written to the phy register. */
> +static u32 exynos4212_rate_to_clk(unsigned long rate)
> +{
> +	unsigned int clksel;
> +
> +	/* EXYNOS_4212_UPHYCLK_PHYFSEL_MASK */
> +
> +	switch (rate) {
> +	case 9600 * KHZ:
> +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_9MHZ6;
> +		break;
> +	case 10 * MHZ:
> +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_10MHZ;
> +		break;
> +	case 12 * MHZ:
> +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_12MHZ;
> +		break;
> +	case 19200 * KHZ:
> +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_19MHZ2;
> +		break;
> +	case 20 * MHZ:
> +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_20MHZ;
> +		break;
> +	case 24 * MHZ:
> +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_24MHZ;
> +		break;
> +	case 50 * MHZ:
> +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_50MHZ;
> +		break;
> +	default:
> +		clksel = CLKSEL_ERROR;
> +	}
> +
> +	return clksel;
> +}
> +
> +static void exynos4212_isol(struct uphy_instance *inst, bool on)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +	u32 offset;
> +	u32 mask;
> +	u32 tmp;
> +
> +	if (!drv->reg_isol)
> +		return;
> +
> +	switch (inst->cfg->id) {
> +	case EXYNOS4212_DEVICE:
> +		offset = EXYNOS_4212_USB_ISOL_OFFSET;
> +		mask = EXYNOS_4212_USB_ISOL_OTG;
> +		break;
> +	case EXYNOS4212_HOST:
> +		offset = EXYNOS_4212_USB_ISOL_OFFSET;
> +		mask = EXYNOS_4212_USB_ISOL_OTG;
> +		break;
> +	case EXYNOS4212_HSIC0:
> +		offset = EXYNOS_4212_USB_ISOL_HSIC0_OFFSET;
> +		mask = EXYNOS_4212_USB_ISOL_HSIC0;
> +		break;
> +	case EXYNOS4212_HSIC1:
> +		offset = EXYNOS_4212_USB_ISOL_HSIC1_OFFSET;
> +		mask = EXYNOS_4212_USB_ISOL_HSIC1;
> +		break;
> +	default:
> +		return;
> +	};
> +
> +	tmp = readl(drv->reg_isol + offset);
> +	if (on)
> +		tmp &= ~mask;
> +	else
> +		tmp |= mask;
> +	writel(tmp, drv->reg_isol + offset);
> +}
> +
> +static void exynos4212_phy_pwr(struct uphy_instance *inst, bool on)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +	u32 rstbits = 0;
> +	u32 phypwr = 0;
> +	u32 rst;
> +	u32 pwr;
> +
> +	switch (inst->cfg->id) {
> +	case EXYNOS4212_DEVICE:
> +		phypwr =	EXYNOS_4212_UPHYPWR_DEV;
> +		rstbits =	EXYNOS_4212_URSTCON_DEVICE;
> +		break;
> +	case EXYNOS4212_HOST:
> +		phypwr =	EXYNOS_4212_UPHYPWR_HOST;
> +		rstbits =	EXYNOS_4212_URSTCON_HOST_PHY;
> +		break;
> +	case EXYNOS4212_HSIC0:
> +		phypwr =	EXYNOS_4212_UPHYPWR_HSCI0;
> +		rstbits =	EXYNOS_4212_URSTCON_HSIC1 |
> +				EXYNOS_4212_URSTCON_HOST_LINK_P0 |
> +				EXYNOS_4212_URSTCON_HOST_PHY;
> +		break;
> +	case EXYNOS4212_HSIC1:
> +		phypwr =	EXYNOS_4212_UPHYPWR_HSCI1;
> +		rstbits =	EXYNOS_4212_URSTCON_HSIC1 |
> +				EXYNOS_4212_URSTCON_HOST_LINK_P1;
> +		break;
> +	};
> +
> +	if (on) {
> +		writel(inst->clk, drv->reg_phy + EXYNOS_4212_UPHYCLK);
> +
> +		pwr = readl(drv->reg_phy + EXYNOS_4212_UPHYPWR);
> +		pwr &= ~phypwr;
> +		writel(pwr, drv->reg_phy + EXYNOS_4212_UPHYPWR);
> +
> +		rst = readl(drv->reg_phy + EXYNOS_4212_UPHYRST);
> +		rst |= rstbits;
> +		writel(rst, drv->reg_phy + EXYNOS_4212_UPHYRST);
> +		udelay(10);
> +		rst &= ~rstbits;
> +		writel(rst, drv->reg_phy + EXYNOS_4212_UPHYRST);
> +	} else {
> +		pwr = readl(drv->reg_phy + EXYNOS_4212_UPHYPWR);
> +		pwr |= phypwr;
> +		writel(pwr, drv->reg_phy + EXYNOS_4212_UPHYPWR);
> +	}
> +}
> +
> +static int exynos4212_power_on(struct uphy_instance *inst)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +
> +	if (inst->state == STATE_ON) {
> +		dev_err(drv->dev, "usb phy \"%s\" already on",
> +							inst->cfg->label);
> +		return -ENODEV;
> +	}
> +
> +	inst->state = STATE_ON;
> +	inst->ref_cnt++;
> +	if (inst->ref_cnt > 1)
> +		return 0;
> +
> +	exynos4212_phy_pwr(inst, 1);
> +	exynos4212_isol(inst, 0);
> +
> +	/* Power on the device, as it is necessary for HSIC to work */
> +	if (inst->cfg->id == EXYNOS4212_HSIC0) {
> +		struct uphy_instance *device =
> +					&drv->uphy_instances[EXYNOS4212_DEVICE];
> +		exynos4212_phy_pwr(device, 1);
> +		exynos4212_isol(device, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +static int exynos4212_power_off(struct uphy_instance *inst)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +
> +	if (inst->state == STATE_OFF) {
> +		dev_err(drv->dev, "usb phy \"%s\" already off",
> +							inst->cfg->label);
> +		return -EINVAL;
> +	}
> +
> +	inst->state = STATE_OFF;
> +	inst->ref_cnt--;
> +
> +	if (inst->ref_cnt > 0)
> +		return 0;
> +
> +	exynos4212_isol(inst, 1);
> +	exynos4212_phy_pwr(inst, 0);
> +
> +	if (inst->cfg->id == EXYNOS4212_HSIC0) {
> +		struct uphy_instance *device =
> +					&drv->uphy_instances[EXYNOS4212_DEVICE];
> +		exynos4212_isol(device, 1);
> +		exynos4212_phy_pwr(device, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +
> +static const struct common_phy exynos4212_phys[] = {
> +	{
> +		.label		= "device",
> +		.type		= PHY_DEVICE,
> +		.id		= EXYNOS4212_DEVICE,
> +		.rate_to_clk	= exynos4212_rate_to_clk,
> +		.power_on	= exynos4212_power_on,
> +		.power_off	= exynos4212_power_off,
> +	},
> +	{
> +		.label		= "host",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS4212_HOST,
> +		.rate_to_clk	= exynos4212_rate_to_clk,
> +		.power_on	= exynos4212_power_on,
> +		.power_off	= exynos4212_power_off,
> +	},
> +	{
> +		.label		= "hsic0",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS4212_HSIC0,
> +		.rate_to_clk	= exynos4212_rate_to_clk,
> +		.power_on	= exynos4212_power_on,
> +		.power_off	= exynos4212_power_off,
> +	},
> +	{
> +		.label		= "hsic1",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS4212_HSIC1,
> +		.rate_to_clk	= exynos4212_rate_to_clk,
> +		.power_on	= exynos4212_power_on,
> +		.power_off	= exynos4212_power_off,
> +	},
> +	{},
> +};
> +
> +const struct uphy_config exynos4212_uphy_config = {
> +	.cpu		= TYPE_EXYNOS4212,
> +	.num_phys	= EXYNOS4212_NUM_PHYS,
> +	.phys		= exynos4212_phys,
> +};

I see quite an amount of similarities between the two PHY drivers. It would be
better if we can have a single driver to handle it.

Thanks
Kishon
--
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] 46+ messages in thread

* Re: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver
@ 2013-10-25 15:43     ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 46+ messages in thread
From: Kishon Vijay Abraham I @ 2013-10-25 15:43 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, kyungmin.park, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk

Hi,

On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
> Add support for Exynos 5250. This is work-in-progress commit. Not
> for merging.
> 
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/phy/Kconfig              |    7 +
>  drivers/phy/Makefile             |    1 +
>  drivers/phy/phy-exynos-usb.c     |   10 +
>  drivers/phy/phy-exynos-usb.h     |    1 +
>  drivers/phy/phy-exynos5250-usb.c |  411 ++++++++++++++++++++++++++++++++++++++
>  5 files changed, 430 insertions(+)
>  create mode 100644 drivers/phy/phy-exynos5250-usb.c
> 
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 2f7ac0a..0f598d0 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -36,4 +36,11 @@ config PHY_EXYNOS4212_USB
>  	help
>  	  Enable USB PHY support for Exynos 4212
>  
> +config PHY_EXYNOS5250_USB
> +	bool "Support for Exynos 5250"
> +	depends on PHY_EXYNOS_USB

This should be a separate driver. Not necessary to use PHY_EXYNOS_USB.
> +	depends on SOC_EXYNOS5250
> +	help
> +	  Enable USB PHY support for Exynos 5250
> +
>  endmenu
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index ca3dc82..0dff0dd 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -6,3 +6,4 @@ obj-$(CONFIG_GENERIC_PHY)	+= phy-core.o
>  obj-$(CONFIG_PHY_EXYNOS_USB)		+= phy-exynos-usb.o
>  obj-$(CONFIG_PHY_EXYNOS4210_USB)	+= phy-exynos4210-usb.o
>  obj-$(CONFIG_PHY_EXYNOS4212_USB)	+= phy-exynos4212-usb.o
> +obj-$(CONFIG_PHY_EXYNOS5250_USB)	+= phy-exynos5250-usb.o
> diff --git a/drivers/phy/phy-exynos-usb.c b/drivers/phy/phy-exynos-usb.c
> index d4a26df..172b774 100644
> --- a/drivers/phy/phy-exynos-usb.c
> +++ b/drivers/phy/phy-exynos-usb.c
> @@ -212,6 +212,10 @@ extern const struct uphy_config exynos4210_uphy_config;
>  extern const struct uphy_config exynos4212_uphy_config;
>  #endif
>  
> +#ifdef CONFIG_PHY_EXYNOS5250_USB
> +extern const struct uphy_config exynos5250_uphy_config;
> +#endif
> +
>  static const struct of_device_id exynos_uphy_of_match[] = {
>  #ifdef CONFIG_PHY_EXYNOS4210_USB
>  	{
> @@ -225,6 +229,12 @@ static const struct of_device_id exynos_uphy_of_match[] = {
>  		.data = &exynos4212_uphy_config,
>  	},
>  #endif
> +#ifdef CONFIG_PHY_EXYNOS5250_USB
> +	{
> +		.compatible = "samsung,exynos5250-usbphy",
> +		.data = &exynos5250_uphy_config,
> +	},
> +#endif
>  	{ },
>  };
>  
> diff --git a/drivers/phy/phy-exynos-usb.h b/drivers/phy/phy-exynos-usb.h
> index f45cb3c..a9febfa 100644
> --- a/drivers/phy/phy-exynos-usb.h
> +++ b/drivers/phy/phy-exynos-usb.h
> @@ -42,6 +42,7 @@ enum samsung_cpu_type {
>  	TYPE_S3C64XX,
>  	TYPE_EXYNOS4210,
>  	TYPE_EXYNOS4212,
> +	TYPE_EXYNOS5250,

No cpu types here.
>  };
>  
>  enum uphy_state {
> diff --git a/drivers/phy/phy-exynos5250-usb.c b/drivers/phy/phy-exynos5250-usb.c
> new file mode 100644
> index 0000000..156093b
> --- /dev/null
> +++ b/drivers/phy/phy-exynos5250-usb.c
> @@ -0,0 +1,411 @@
> +/*
> + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
> +#include <linux/io.h>
> +#include <linux/kernel.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-exynos-usb.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		(0x1 << 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		(0x1 << 11)
> +#define EXYNOS_5250_HOSTPHYCTRL0_RETENABLE		(0x1 << 10)
> +#define EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N		(0x1 << 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			(0x1 << 6)
> +#define EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP		(0x1 << 5)
> +#define EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND		(0x1 << 4)
> +#define EXYNOS_5250_HOSTPHYCTRL0_WORDINTERFACE		(0x1 << 3)
> +#define EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST		(0x1 << 2)
> +#define EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST		(0x1 << 1)
> +#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST		(0x1 << 0)
> +
> +/* HSIC0 & HSCI1 */
> +#define EXYNOS_5250_HOSTPHYCTRL1			0x10
> +#define EXYNOS_5250_HOSTPHYCTRL2			0x20
> +
> +#define EXYNOS_5250_HOSTPHYCTRLX_REFCLKSEL_MASK		(0x3 << 23)
> +#define EXYNOS_5250_HOSTPHYCTRLX_REFCLKDIV_MASK		(0x7f << 16)
> +#define EXYNOS_5250_HOSTPHYCTRLX_SIDDQ			(0x1 << 6)
> +#define EXYNOS_5250_HOSTPHYCTRLX_FORCESLEEP		(0x1 << 5)
> +#define EXYNOS_5250_HOSTPHYCTRLX_FORCESUSPEND		(0x1 << 4)
> +#define EXYNOS_5250_HOSTPHYCTRLX_WORDINTERFACE		(0x1 << 3)
> +#define EXYNOS_5250_HOSTPHYCTRLX_UTMISWRST		(0x1 << 2)
> +#define EXYNOS_5250_HOSTPHYCTRLX_PHYSWRST		(0x1 << 0)
> +
> +/* EHCI control */
> +#define EXYNOS_5250_HOSTEHCICTRL			0x30
> +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN		(0x1 << 29)
> +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR4		(0x1 << 28)
> +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR8		(0x1 << 27)
> +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR16		(0x1 << 26)
> +#define EXYNOS_5250_HOSTEHCICTRL_AUTOPPDONOVRCUREN	(0x1 << 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		(0x1 << 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		(0x1 << 0)
> +
> +/* USBOTG */
> +#define EXYNOS_5250_USBOTGSYS				0x38
> +#define EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET		(0x1 << 14)
> +#define EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG		(0x1 << 13)
> +#define EXYNOS_5250_USBOTGSYS_PHY_SW_RST		(0x1 << 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			(0x1 << 8)
> +#define EXYNOS_5250_USBOTGSYS_COMMON_ON			(0x1 << 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		(0x1 << 3)
> +#define EXYNOS_5250_USBOTGSYS_OTGDISABLE		(0x1 << 2)
> +#define EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG		(0x1 << 1)
> +#define EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND		(0x1 << 0)
> +
> +/* Isolation, configured in the power management unit */
> +#define EXYNOS_5250_USB_ISOL_OTG_OFFSET		0x0
> +#define EXYNOS_5250_USB_ISOL_OTG		(1 << 0)
> +#define EXYNOS_5250_USB_ISOL_HOST_OFFSET	0x4
> +#define EXYNOS_5250_USB_ISOL_HOST		(1 << 0)
> +
> +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. */

Follow coding guidelines ;-)
> +static u32 exynos5250_rate_to_clk(unsigned long rate)
> +{
> +	unsigned int clksel;
> +
> +	/* EXYNOS_5250_FSEL_MASK */
> +
> +	switch (rate) {
> +	case 9600 * KHZ:
> +		clksel = EXYNOS_5250_FSEL_9MHZ6;
> +		break;
> +	case 10 * MHZ:
> +		clksel = EXYNOS_5250_FSEL_10MHZ;
> +		break;
> +	case 12 * MHZ:
> +		clksel = EXYNOS_5250_FSEL_12MHZ;
> +		break;
> +	case 19200 * KHZ:
> +		clksel = EXYNOS_5250_FSEL_19MHZ2;
> +		break;
> +	case 20 * MHZ:
> +		clksel = EXYNOS_5250_FSEL_20MHZ;
> +		break;
> +	case 24 * MHZ:
> +		clksel = EXYNOS_5250_FSEL_24MHZ;
> +		break;
> +	case 50 * MHZ:
> +		clksel = EXYNOS_5250_FSEL_50MHZ;
> +		break;
> +	default:
> +		clksel = CLKSEL_ERROR;
> +	}
> +
> +	return clksel;
> +}
> +
> +static void exynos5250_isol(struct uphy_instance *inst, bool on)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +	u32 offset;
> +	u32 mask;
> +	u32 tmp;
> +
> +	if (!drv->reg_isol)
> +		return;
> +
> +	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;
> +	};
> +
> +	tmp = readl(drv->reg_isol + offset);
> +	if (on)
> +		tmp &= ~mask;
> +	else
> +		tmp |= mask;
> +	writel(tmp, drv->reg_isol + offset);
> +}
> +
> +static void exynos5250_phy_pwr(struct uphy_instance *inst, bool on)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +	u32 ctrl0;
> +	u32 otg;
> +	u32 ehci;
> +	u32 ohci;
> +
> +	switch (inst->cfg->id) {
> +	case EXYNOS5250_DEVICE:
> +		writel(0, drv->reg_mode);
> +
> +		/* OTG configuration */
> +		otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
> +		/* The clock */
> +		otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
> +		otg |= inst->clk << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
> +		/* Reset */
> +		otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
> +			EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
> +			EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
> +		/* TODO: Clear 4 bits as the old driver does. */
> +		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);
> +
> +
> +		break;
> +	case EXYNOS5250_HOST:
> +		/* Host registers configuration */
> +		ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
> +		/* The clock */
> +		ctrl0 &= ~EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK;
> +		ctrl0 |= inst->clk << 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 << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
> +		/* Reset */
> +		otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
> +			EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
> +			EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
> +		/* TODO: Clear 4 bits as the old driver does. */
> +		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);
> +
> +		/* 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);
> +		/* Let's do some undocumented magic (based on the old driver) */
> +		ohci |=	0x1 << 3;
> +		writel(ohci, drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
> +
> +		break;
> +	}
> +}
> +
> +static int exynos5250_power_on(struct uphy_instance *inst)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +
> +	if (inst->state == STATE_ON) {
> +		dev_err(drv->dev, "usb phy \"%s\" already on",
> +							inst->cfg->label);
> +		return -ENODEV;
> +	}
> +
> +	inst->state = STATE_ON;
> +	inst->ref_cnt++;
> +	if (inst->ref_cnt > 1)
> +		return 0;
> +
> +	exynos5250_phy_pwr(inst, 1);
> +	exynos5250_isol(inst, 0);
> +
> +	/* Power on the device, as it is necessary for HSIC to work */
> +	if (inst->cfg->id == EXYNOS5250_HSIC0) {
> +		struct uphy_instance *device =
> +					&drv->uphy_instances[EXYNOS5250_DEVICE];
> +		device->ref_cnt++;
> +		if (device->ref_cnt > 1)
> +			return 0;
> +		exynos5250_phy_pwr(device, 1);
> +		exynos5250_isol(device, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +static int exynos5250_power_off(struct uphy_instance *inst)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +
> +	if (inst->state == STATE_OFF) {
> +		dev_err(drv->dev, "usb phy \"%s\" already off",
> +							inst->cfg->label);
> +		return -EINVAL;
> +	}
> +
> +	inst->state = STATE_OFF;
> +	inst->ref_cnt--;
> +
> +	if (inst->ref_cnt > 0)
> +		return 0;
> +
> +	exynos5250_isol(inst, 1);
> +	exynos5250_phy_pwr(inst, 0);
> +
> +	if (inst->cfg->id == EXYNOS5250_HSIC0) {
> +		struct uphy_instance *device =
> +					&drv->uphy_instances[EXYNOS5250_DEVICE];
> +		device->ref_cnt--;
> +		if (device->ref_cnt > 0)
> +			return 0;
> +		exynos5250_isol(device, 1);
> +		exynos5250_phy_pwr(device, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +
> +static const struct common_phy exynos5250_phys[] = {
> +	{
> +		.label		= "device",
> +		.type		= PHY_DEVICE,
> +		.id		= EXYNOS5250_DEVICE,
> +		.rate_to_clk	= exynos5250_rate_to_clk,
> +		.power_on	= exynos5250_power_on,
> +		.power_off	= exynos5250_power_off,
> +	},
> +	{
> +		.label		= "host",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS5250_HOST,
> +		.rate_to_clk	= exynos5250_rate_to_clk,
> +		.power_on	= exynos5250_power_on,
> +		.power_off	= exynos5250_power_off,
> +	},
> +	{
> +		.label		= "hsic0",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS5250_HSIC0,
> +		.rate_to_clk	= exynos5250_rate_to_clk,
> +		.power_on	= exynos5250_power_on,
> +		.power_off	= exynos5250_power_off,
> +	},
> +	{
> +		.label		= "hsic1",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS5250_HSIC1,
> +		.rate_to_clk	= exynos5250_rate_to_clk,
> +		.power_on	= exynos5250_power_on,
> +		.power_off	= exynos5250_power_off,
> +	},
> +	{},
> +};

All this can be removed if you don't use exynos common PHY.

Thanks
Kishon

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

* Re: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver
@ 2013-10-25 15:43     ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 46+ messages in thread
From: Kishon Vijay Abraham I @ 2013-10-25 15:43 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-u79uwXL29TY76Z2rM5mHXA,
	kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ,
	t.figa-Sze3O3UU22JBDgjK7y7TUQ, s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ,
	m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ,
	gautam.vivek-Sze3O3UU22JBDgjK7y7TUQ,
	mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w

Hi,

On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
> Add support for Exynos 5250. This is work-in-progress commit. Not
> for merging.
> 
> Signed-off-by: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> Signed-off-by: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> ---
>  drivers/phy/Kconfig              |    7 +
>  drivers/phy/Makefile             |    1 +
>  drivers/phy/phy-exynos-usb.c     |   10 +
>  drivers/phy/phy-exynos-usb.h     |    1 +
>  drivers/phy/phy-exynos5250-usb.c |  411 ++++++++++++++++++++++++++++++++++++++
>  5 files changed, 430 insertions(+)
>  create mode 100644 drivers/phy/phy-exynos5250-usb.c
> 
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 2f7ac0a..0f598d0 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -36,4 +36,11 @@ config PHY_EXYNOS4212_USB
>  	help
>  	  Enable USB PHY support for Exynos 4212
>  
> +config PHY_EXYNOS5250_USB
> +	bool "Support for Exynos 5250"
> +	depends on PHY_EXYNOS_USB

This should be a separate driver. Not necessary to use PHY_EXYNOS_USB.
> +	depends on SOC_EXYNOS5250
> +	help
> +	  Enable USB PHY support for Exynos 5250
> +
>  endmenu
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index ca3dc82..0dff0dd 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -6,3 +6,4 @@ obj-$(CONFIG_GENERIC_PHY)	+= phy-core.o
>  obj-$(CONFIG_PHY_EXYNOS_USB)		+= phy-exynos-usb.o
>  obj-$(CONFIG_PHY_EXYNOS4210_USB)	+= phy-exynos4210-usb.o
>  obj-$(CONFIG_PHY_EXYNOS4212_USB)	+= phy-exynos4212-usb.o
> +obj-$(CONFIG_PHY_EXYNOS5250_USB)	+= phy-exynos5250-usb.o
> diff --git a/drivers/phy/phy-exynos-usb.c b/drivers/phy/phy-exynos-usb.c
> index d4a26df..172b774 100644
> --- a/drivers/phy/phy-exynos-usb.c
> +++ b/drivers/phy/phy-exynos-usb.c
> @@ -212,6 +212,10 @@ extern const struct uphy_config exynos4210_uphy_config;
>  extern const struct uphy_config exynos4212_uphy_config;
>  #endif
>  
> +#ifdef CONFIG_PHY_EXYNOS5250_USB
> +extern const struct uphy_config exynos5250_uphy_config;
> +#endif
> +
>  static const struct of_device_id exynos_uphy_of_match[] = {
>  #ifdef CONFIG_PHY_EXYNOS4210_USB
>  	{
> @@ -225,6 +229,12 @@ static const struct of_device_id exynos_uphy_of_match[] = {
>  		.data = &exynos4212_uphy_config,
>  	},
>  #endif
> +#ifdef CONFIG_PHY_EXYNOS5250_USB
> +	{
> +		.compatible = "samsung,exynos5250-usbphy",
> +		.data = &exynos5250_uphy_config,
> +	},
> +#endif
>  	{ },
>  };
>  
> diff --git a/drivers/phy/phy-exynos-usb.h b/drivers/phy/phy-exynos-usb.h
> index f45cb3c..a9febfa 100644
> --- a/drivers/phy/phy-exynos-usb.h
> +++ b/drivers/phy/phy-exynos-usb.h
> @@ -42,6 +42,7 @@ enum samsung_cpu_type {
>  	TYPE_S3C64XX,
>  	TYPE_EXYNOS4210,
>  	TYPE_EXYNOS4212,
> +	TYPE_EXYNOS5250,

No cpu types here.
>  };
>  
>  enum uphy_state {
> diff --git a/drivers/phy/phy-exynos5250-usb.c b/drivers/phy/phy-exynos5250-usb.c
> new file mode 100644
> index 0000000..156093b
> --- /dev/null
> +++ b/drivers/phy/phy-exynos5250-usb.c
> @@ -0,0 +1,411 @@
> +/*
> + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
> +#include <linux/io.h>
> +#include <linux/kernel.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-exynos-usb.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		(0x1 << 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		(0x1 << 11)
> +#define EXYNOS_5250_HOSTPHYCTRL0_RETENABLE		(0x1 << 10)
> +#define EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N		(0x1 << 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			(0x1 << 6)
> +#define EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP		(0x1 << 5)
> +#define EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND		(0x1 << 4)
> +#define EXYNOS_5250_HOSTPHYCTRL0_WORDINTERFACE		(0x1 << 3)
> +#define EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST		(0x1 << 2)
> +#define EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST		(0x1 << 1)
> +#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST		(0x1 << 0)
> +
> +/* HSIC0 & HSCI1 */
> +#define EXYNOS_5250_HOSTPHYCTRL1			0x10
> +#define EXYNOS_5250_HOSTPHYCTRL2			0x20
> +
> +#define EXYNOS_5250_HOSTPHYCTRLX_REFCLKSEL_MASK		(0x3 << 23)
> +#define EXYNOS_5250_HOSTPHYCTRLX_REFCLKDIV_MASK		(0x7f << 16)
> +#define EXYNOS_5250_HOSTPHYCTRLX_SIDDQ			(0x1 << 6)
> +#define EXYNOS_5250_HOSTPHYCTRLX_FORCESLEEP		(0x1 << 5)
> +#define EXYNOS_5250_HOSTPHYCTRLX_FORCESUSPEND		(0x1 << 4)
> +#define EXYNOS_5250_HOSTPHYCTRLX_WORDINTERFACE		(0x1 << 3)
> +#define EXYNOS_5250_HOSTPHYCTRLX_UTMISWRST		(0x1 << 2)
> +#define EXYNOS_5250_HOSTPHYCTRLX_PHYSWRST		(0x1 << 0)
> +
> +/* EHCI control */
> +#define EXYNOS_5250_HOSTEHCICTRL			0x30
> +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN		(0x1 << 29)
> +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR4		(0x1 << 28)
> +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR8		(0x1 << 27)
> +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR16		(0x1 << 26)
> +#define EXYNOS_5250_HOSTEHCICTRL_AUTOPPDONOVRCUREN	(0x1 << 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		(0x1 << 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		(0x1 << 0)
> +
> +/* USBOTG */
> +#define EXYNOS_5250_USBOTGSYS				0x38
> +#define EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET		(0x1 << 14)
> +#define EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG		(0x1 << 13)
> +#define EXYNOS_5250_USBOTGSYS_PHY_SW_RST		(0x1 << 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			(0x1 << 8)
> +#define EXYNOS_5250_USBOTGSYS_COMMON_ON			(0x1 << 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		(0x1 << 3)
> +#define EXYNOS_5250_USBOTGSYS_OTGDISABLE		(0x1 << 2)
> +#define EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG		(0x1 << 1)
> +#define EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND		(0x1 << 0)
> +
> +/* Isolation, configured in the power management unit */
> +#define EXYNOS_5250_USB_ISOL_OTG_OFFSET		0x0
> +#define EXYNOS_5250_USB_ISOL_OTG		(1 << 0)
> +#define EXYNOS_5250_USB_ISOL_HOST_OFFSET	0x4
> +#define EXYNOS_5250_USB_ISOL_HOST		(1 << 0)
> +
> +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. */

Follow coding guidelines ;-)
> +static u32 exynos5250_rate_to_clk(unsigned long rate)
> +{
> +	unsigned int clksel;
> +
> +	/* EXYNOS_5250_FSEL_MASK */
> +
> +	switch (rate) {
> +	case 9600 * KHZ:
> +		clksel = EXYNOS_5250_FSEL_9MHZ6;
> +		break;
> +	case 10 * MHZ:
> +		clksel = EXYNOS_5250_FSEL_10MHZ;
> +		break;
> +	case 12 * MHZ:
> +		clksel = EXYNOS_5250_FSEL_12MHZ;
> +		break;
> +	case 19200 * KHZ:
> +		clksel = EXYNOS_5250_FSEL_19MHZ2;
> +		break;
> +	case 20 * MHZ:
> +		clksel = EXYNOS_5250_FSEL_20MHZ;
> +		break;
> +	case 24 * MHZ:
> +		clksel = EXYNOS_5250_FSEL_24MHZ;
> +		break;
> +	case 50 * MHZ:
> +		clksel = EXYNOS_5250_FSEL_50MHZ;
> +		break;
> +	default:
> +		clksel = CLKSEL_ERROR;
> +	}
> +
> +	return clksel;
> +}
> +
> +static void exynos5250_isol(struct uphy_instance *inst, bool on)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +	u32 offset;
> +	u32 mask;
> +	u32 tmp;
> +
> +	if (!drv->reg_isol)
> +		return;
> +
> +	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;
> +	};
> +
> +	tmp = readl(drv->reg_isol + offset);
> +	if (on)
> +		tmp &= ~mask;
> +	else
> +		tmp |= mask;
> +	writel(tmp, drv->reg_isol + offset);
> +}
> +
> +static void exynos5250_phy_pwr(struct uphy_instance *inst, bool on)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +	u32 ctrl0;
> +	u32 otg;
> +	u32 ehci;
> +	u32 ohci;
> +
> +	switch (inst->cfg->id) {
> +	case EXYNOS5250_DEVICE:
> +		writel(0, drv->reg_mode);
> +
> +		/* OTG configuration */
> +		otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
> +		/* The clock */
> +		otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
> +		otg |= inst->clk << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
> +		/* Reset */
> +		otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
> +			EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
> +			EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
> +		/* TODO: Clear 4 bits as the old driver does. */
> +		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);
> +
> +
> +		break;
> +	case EXYNOS5250_HOST:
> +		/* Host registers configuration */
> +		ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
> +		/* The clock */
> +		ctrl0 &= ~EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK;
> +		ctrl0 |= inst->clk << 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 << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
> +		/* Reset */
> +		otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
> +			EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
> +			EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
> +		/* TODO: Clear 4 bits as the old driver does. */
> +		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);
> +
> +		/* 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);
> +		/* Let's do some undocumented magic (based on the old driver) */
> +		ohci |=	0x1 << 3;
> +		writel(ohci, drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
> +
> +		break;
> +	}
> +}
> +
> +static int exynos5250_power_on(struct uphy_instance *inst)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +
> +	if (inst->state == STATE_ON) {
> +		dev_err(drv->dev, "usb phy \"%s\" already on",
> +							inst->cfg->label);
> +		return -ENODEV;
> +	}
> +
> +	inst->state = STATE_ON;
> +	inst->ref_cnt++;
> +	if (inst->ref_cnt > 1)
> +		return 0;
> +
> +	exynos5250_phy_pwr(inst, 1);
> +	exynos5250_isol(inst, 0);
> +
> +	/* Power on the device, as it is necessary for HSIC to work */
> +	if (inst->cfg->id == EXYNOS5250_HSIC0) {
> +		struct uphy_instance *device =
> +					&drv->uphy_instances[EXYNOS5250_DEVICE];
> +		device->ref_cnt++;
> +		if (device->ref_cnt > 1)
> +			return 0;
> +		exynos5250_phy_pwr(device, 1);
> +		exynos5250_isol(device, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +static int exynos5250_power_off(struct uphy_instance *inst)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +
> +	if (inst->state == STATE_OFF) {
> +		dev_err(drv->dev, "usb phy \"%s\" already off",
> +							inst->cfg->label);
> +		return -EINVAL;
> +	}
> +
> +	inst->state = STATE_OFF;
> +	inst->ref_cnt--;
> +
> +	if (inst->ref_cnt > 0)
> +		return 0;
> +
> +	exynos5250_isol(inst, 1);
> +	exynos5250_phy_pwr(inst, 0);
> +
> +	if (inst->cfg->id == EXYNOS5250_HSIC0) {
> +		struct uphy_instance *device =
> +					&drv->uphy_instances[EXYNOS5250_DEVICE];
> +		device->ref_cnt--;
> +		if (device->ref_cnt > 0)
> +			return 0;
> +		exynos5250_isol(device, 1);
> +		exynos5250_phy_pwr(device, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +
> +static const struct common_phy exynos5250_phys[] = {
> +	{
> +		.label		= "device",
> +		.type		= PHY_DEVICE,
> +		.id		= EXYNOS5250_DEVICE,
> +		.rate_to_clk	= exynos5250_rate_to_clk,
> +		.power_on	= exynos5250_power_on,
> +		.power_off	= exynos5250_power_off,
> +	},
> +	{
> +		.label		= "host",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS5250_HOST,
> +		.rate_to_clk	= exynos5250_rate_to_clk,
> +		.power_on	= exynos5250_power_on,
> +		.power_off	= exynos5250_power_off,
> +	},
> +	{
> +		.label		= "hsic0",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS5250_HSIC0,
> +		.rate_to_clk	= exynos5250_rate_to_clk,
> +		.power_on	= exynos5250_power_on,
> +		.power_off	= exynos5250_power_off,
> +	},
> +	{
> +		.label		= "hsic1",
> +		.type		= PHY_HOST,
> +		.id		= EXYNOS5250_HSIC1,
> +		.rate_to_clk	= exynos5250_rate_to_clk,
> +		.power_on	= exynos5250_power_on,
> +		.power_off	= exynos5250_power_off,
> +	},
> +	{},
> +};

All this can be removed if you don't use exynos common PHY.

Thanks
Kishon
--
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] 46+ messages in thread

* Re: [PATCH 3/5] phy: Add support for S5PV210 to the Exynos USB PHY driver
@ 2013-10-25 15:50     ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 46+ messages in thread
From: Kishon Vijay Abraham I @ 2013-10-25 15:50 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, kyungmin.park, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk, Mateusz Krawczuk

Hi,

On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
> From: Mateusz Krawczuk <m.krawczuk@partner.samsung.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: whitespace cleanup and commit description]
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> ---
>  drivers/phy/Kconfig           |    7 ++
>  drivers/phy/phy-exynos-usb.c  |   10 ++
>  drivers/phy/phy-exynos-usb.h  |    1 +
>  drivers/phy/phy-s5pv210-usb.c |  236 +++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 254 insertions(+)
>  create mode 100644 drivers/phy/phy-s5pv210-usb.c
> 
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 0f598d0..d3517f9 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -22,6 +22,13 @@ config PHY_EXYNOS_USB
>  	  This driver provides common interface to interact, for Samsung
>  	  USB 2.0 PHY driver.
>  
> +config PHY_S5PV210_USB
> +	bool "Support for S5PV210"
> +	depends on PHY_EXYNOS_USB
> +	depends on ARCH_S5PV210
> +	help
> +	  Enable USB PHY support for S5PV210
> +
>  config PHY_EXYNOS4210_USB
>  	bool "Support for Exynos 4210"
>  	depends on PHY_EXYNOS_USB
> diff --git a/drivers/phy/phy-exynos-usb.c b/drivers/phy/phy-exynos-usb.c
> index 172b774..01fb12a 100644
> --- a/drivers/phy/phy-exynos-usb.c
> +++ b/drivers/phy/phy-exynos-usb.c
> @@ -216,7 +216,17 @@ extern const struct uphy_config exynos4212_uphy_config;
>  extern const struct uphy_config exynos5250_uphy_config;
>  #endif
>  
> +#ifdef CONFIG_PHY_S5PV210_USB
> +extern const struct uphy_config s5pv210_uphy_config;
> +#endif
> +
>  static const struct of_device_id exynos_uphy_of_match[] = {
> +#ifdef CONFIG_PHY_S5PV210_USB
> +	{
> +		.compatible = "samsung,s5pv210-usbphy",
> +		.data = &s5pv210_uphy_config,
> +	},
> +#endif
>  #ifdef CONFIG_PHY_EXYNOS4210_USB
>  	{
>  		.compatible = "samsung,exynos4210-usbphy",
> diff --git a/drivers/phy/phy-exynos-usb.h b/drivers/phy/phy-exynos-usb.h
> index a9febfa..0f385ca 100644
> --- a/drivers/phy/phy-exynos-usb.h
> +++ b/drivers/phy/phy-exynos-usb.h
> @@ -40,6 +40,7 @@ enum phy_type {
>  
>  enum samsung_cpu_type {
>  	TYPE_S3C64XX,
> +	TYPE_S5PV210,
>  	TYPE_EXYNOS4210,
>  	TYPE_EXYNOS4212,
>  	TYPE_EXYNOS5250,
> diff --git a/drivers/phy/phy-s5pv210-usb.c b/drivers/phy/phy-s5pv210-usb.c
> new file mode 100644
> index 0000000..575275d
> --- /dev/null
> +++ b/drivers/phy/phy-s5pv210-usb.c

how different is this IP different from the previous ones? Does it deserve a
separate driver or the previous drivers can be re-used?
> @@ -0,0 +1,236 @@
> +/*
> + * Samsung S5P/EXYNOS SoC series USB PHY driver
> + *
> + * 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/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/kernel.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-exynos-usb.h"
> +
> +/* Exynos USB PHY registers */
> +
> +/* PHY power control */
> +#define S5PV210_UPHYPWR			0x0
> +
> +#define S5PV210_UPHYPWR_PHY0_SUSPEND	(1 << 0)
> +#define S5PV210_UPHYPWR_PHY0_PWR		(1 << 3)
> +#define S5PV210_UPHYPWR_PHY0_OTG_PWR	(1 << 4)
> +#define S5PV210_UPHYPWR_PHY0	( \
> +	S5PV210_UPHYPWR_PHY0_SUSPEND | \
> +	S5PV210_UPHYPWR_PHY0_PWR | \
> +	S5PV210_UPHYPWR_PHY0_OTG_PWR)
> +
> +#define S5PV210_UPHYPWR_PHY1_SUSPEND	(1 << 6)
> +#define S5PV210_UPHYPWR_PHY1_PWR		(1 << 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	(0x1 << 2)
> +#define S5PV210_UPHYCLK_PHY0_COMMON_ON	(0x1 << 4)
> +#define S5PV210_UPHYCLK_PHY1_COMMON_ON	(0x1 << 7)
> +
> +/* PHY reset control */
> +#define S5PV210_UPHYRST			0x8
> +
> +#define S5PV210_URSTCON_PHY0		(1 << 0)
> +#define S5PV210_URSTCON_OTG_HLINK		(1 << 1)
> +#define S5PV210_URSTCON_OTG_PHYLINK		(1 << 2)
> +#define S5PV210_URSTCON_PHY1_ALL		(1 << 3)
> +#define S5PV210_URSTCON_HOST_LINK_ALL	(1 << 4)
> +
> +/* Isolation, configured in the power management unit */
> +#define S5PV210_USB_ISOL_DEVICE_OFFSET	0x0
> +#define S5PV210_USB_ISOL_DEVICE		(1 << 0)
> +#define S5PV210_USB_ISOL_HOST_OFFSET	0x4
> +#define S5PV210_USB_ISOL_HOST		(1 << 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 u32 s5pv210_rate_to_clk(unsigned long rate)
> +{
> +	unsigned int clksel;
> +
> +	pr_info("%lu \n",rate);
> +	switch (rate) {
> +	case 12 * MHZ:
> +		clksel = S5PV210_UPHYCLK_PHYFSEL_12MHZ;
> +		break;
> +	case 24 * MHZ:
> +		clksel = S5PV210_UPHYCLK_PHYFSEL_24MHZ;
> +		break;
> +	case 48 * MHZ:
> +		clksel = S5PV210_UPHYCLK_PHYFSEL_48MHZ;
> +		break;
> +	default:
> +		clksel = CLKSEL_ERROR;
> +	}
> +
> +	return clksel;
> +}
> +
> +static void s5pv210_isol(struct uphy_instance *inst, bool on)
> +{
> +	struct uphy_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;
> +	};
> +
> +	tmp = readl(drv->reg_isol);
> +	if (on)
> +		tmp &= ~mask;
> +	else
> +		tmp |= mask;
> +	writel(tmp, drv->reg_isol);
> +}
> +
> +static void s5pv210_phy_pwr(struct uphy_instance *inst, bool on)
> +{
> +	struct uphy_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, 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 uphy_instance *inst)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +
> +	if (inst->state == STATE_ON) {
> +		dev_err(drv->dev, "usb phy \"%s\" already on",
> +							inst->cfg->label);
> +		return -ENODEV;
> +	}
> +	inst->state = STATE_ON;
> +	inst->ref_cnt++;
> +	if (inst->ref_cnt > 1)
> +		return 0;
> +
> +	s5pv210_isol(inst, 0);
> +	s5pv210_phy_pwr(inst, 1);
> +
> +	return 0;
> +}
> +
> +static int s5pv210_power_off(struct uphy_instance *inst)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +
> +	if (inst->state == STATE_OFF) {
> +		dev_err(drv->dev, "usb phy \"%s\" already off",
> +							inst->cfg->label);
> +		return -EINVAL;
> +	}
> +
> +	inst->state = STATE_OFF;
> +	inst->ref_cnt++;
> +	if (inst->ref_cnt > 0)
> +		return 0;
> +
> +	s5pv210_phy_pwr(inst, 0);
> +	s5pv210_isol(inst, 1);
> +
> +	return 0;
> +}
> +
> +
> +static const struct common_phy s5pv210_phys[] = {
> +	{
> +		.label		= "device",
> +		.type		= PHY_DEVICE,
> +		.id		= S5PV210_DEVICE,
> +		.rate_to_clk	= s5pv210_rate_to_clk,
> +		.power_on	= s5pv210_power_on,
> +		.power_off	= s5pv210_power_off,
> +	},
> +	{
> +		.label		= "host",
> +		.type		= PHY_HOST,
> +		.id		= S5PV210_HOST,
> +		.rate_to_clk	= s5pv210_rate_to_clk,
> +		.power_on	= s5pv210_power_on,
> +		.power_off	= s5pv210_power_off,
> +	},
> +	{},
> +};
> +
> +const struct uphy_config s5pv210_uphy_config = {
> +	.cpu		= TYPE_S5PV210,
> +	.num_phys	= S5PV210_NUM_PHYS,
> +	.phys		= s5pv210_phys,
> +};

IMO this looks similar to the other drivers. Please re-use the drivers wherever
possible.

Thanks
Kishon

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

* Re: [PATCH 3/5] phy: Add support for S5PV210 to the Exynos USB PHY driver
@ 2013-10-25 15:50     ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 46+ messages in thread
From: Kishon Vijay Abraham I @ 2013-10-25 15:50 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-u79uwXL29TY76Z2rM5mHXA,
	kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ,
	t.figa-Sze3O3UU22JBDgjK7y7TUQ, s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ,
	m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ,
	gautam.vivek-Sze3O3UU22JBDgjK7y7TUQ,
	mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w, Mateusz Krawczuk

Hi,

On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
> From: Mateusz Krawczuk <m.krawczuk-GrGkmOP51GdLN7c7dRTbYkEOCMrvLtNR@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: whitespace cleanup and commit description]
> Signed-off-by: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> ---
>  drivers/phy/Kconfig           |    7 ++
>  drivers/phy/phy-exynos-usb.c  |   10 ++
>  drivers/phy/phy-exynos-usb.h  |    1 +
>  drivers/phy/phy-s5pv210-usb.c |  236 +++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 254 insertions(+)
>  create mode 100644 drivers/phy/phy-s5pv210-usb.c
> 
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 0f598d0..d3517f9 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -22,6 +22,13 @@ config PHY_EXYNOS_USB
>  	  This driver provides common interface to interact, for Samsung
>  	  USB 2.0 PHY driver.
>  
> +config PHY_S5PV210_USB
> +	bool "Support for S5PV210"
> +	depends on PHY_EXYNOS_USB
> +	depends on ARCH_S5PV210
> +	help
> +	  Enable USB PHY support for S5PV210
> +
>  config PHY_EXYNOS4210_USB
>  	bool "Support for Exynos 4210"
>  	depends on PHY_EXYNOS_USB
> diff --git a/drivers/phy/phy-exynos-usb.c b/drivers/phy/phy-exynos-usb.c
> index 172b774..01fb12a 100644
> --- a/drivers/phy/phy-exynos-usb.c
> +++ b/drivers/phy/phy-exynos-usb.c
> @@ -216,7 +216,17 @@ extern const struct uphy_config exynos4212_uphy_config;
>  extern const struct uphy_config exynos5250_uphy_config;
>  #endif
>  
> +#ifdef CONFIG_PHY_S5PV210_USB
> +extern const struct uphy_config s5pv210_uphy_config;
> +#endif
> +
>  static const struct of_device_id exynos_uphy_of_match[] = {
> +#ifdef CONFIG_PHY_S5PV210_USB
> +	{
> +		.compatible = "samsung,s5pv210-usbphy",
> +		.data = &s5pv210_uphy_config,
> +	},
> +#endif
>  #ifdef CONFIG_PHY_EXYNOS4210_USB
>  	{
>  		.compatible = "samsung,exynos4210-usbphy",
> diff --git a/drivers/phy/phy-exynos-usb.h b/drivers/phy/phy-exynos-usb.h
> index a9febfa..0f385ca 100644
> --- a/drivers/phy/phy-exynos-usb.h
> +++ b/drivers/phy/phy-exynos-usb.h
> @@ -40,6 +40,7 @@ enum phy_type {
>  
>  enum samsung_cpu_type {
>  	TYPE_S3C64XX,
> +	TYPE_S5PV210,
>  	TYPE_EXYNOS4210,
>  	TYPE_EXYNOS4212,
>  	TYPE_EXYNOS5250,
> diff --git a/drivers/phy/phy-s5pv210-usb.c b/drivers/phy/phy-s5pv210-usb.c
> new file mode 100644
> index 0000000..575275d
> --- /dev/null
> +++ b/drivers/phy/phy-s5pv210-usb.c

how different is this IP different from the previous ones? Does it deserve a
separate driver or the previous drivers can be re-used?
> @@ -0,0 +1,236 @@
> +/*
> + * Samsung S5P/EXYNOS SoC series USB PHY driver
> + *
> + * 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/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/kernel.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-exynos-usb.h"
> +
> +/* Exynos USB PHY registers */
> +
> +/* PHY power control */
> +#define S5PV210_UPHYPWR			0x0
> +
> +#define S5PV210_UPHYPWR_PHY0_SUSPEND	(1 << 0)
> +#define S5PV210_UPHYPWR_PHY0_PWR		(1 << 3)
> +#define S5PV210_UPHYPWR_PHY0_OTG_PWR	(1 << 4)
> +#define S5PV210_UPHYPWR_PHY0	( \
> +	S5PV210_UPHYPWR_PHY0_SUSPEND | \
> +	S5PV210_UPHYPWR_PHY0_PWR | \
> +	S5PV210_UPHYPWR_PHY0_OTG_PWR)
> +
> +#define S5PV210_UPHYPWR_PHY1_SUSPEND	(1 << 6)
> +#define S5PV210_UPHYPWR_PHY1_PWR		(1 << 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	(0x1 << 2)
> +#define S5PV210_UPHYCLK_PHY0_COMMON_ON	(0x1 << 4)
> +#define S5PV210_UPHYCLK_PHY1_COMMON_ON	(0x1 << 7)
> +
> +/* PHY reset control */
> +#define S5PV210_UPHYRST			0x8
> +
> +#define S5PV210_URSTCON_PHY0		(1 << 0)
> +#define S5PV210_URSTCON_OTG_HLINK		(1 << 1)
> +#define S5PV210_URSTCON_OTG_PHYLINK		(1 << 2)
> +#define S5PV210_URSTCON_PHY1_ALL		(1 << 3)
> +#define S5PV210_URSTCON_HOST_LINK_ALL	(1 << 4)
> +
> +/* Isolation, configured in the power management unit */
> +#define S5PV210_USB_ISOL_DEVICE_OFFSET	0x0
> +#define S5PV210_USB_ISOL_DEVICE		(1 << 0)
> +#define S5PV210_USB_ISOL_HOST_OFFSET	0x4
> +#define S5PV210_USB_ISOL_HOST		(1 << 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 u32 s5pv210_rate_to_clk(unsigned long rate)
> +{
> +	unsigned int clksel;
> +
> +	pr_info("%lu \n",rate);
> +	switch (rate) {
> +	case 12 * MHZ:
> +		clksel = S5PV210_UPHYCLK_PHYFSEL_12MHZ;
> +		break;
> +	case 24 * MHZ:
> +		clksel = S5PV210_UPHYCLK_PHYFSEL_24MHZ;
> +		break;
> +	case 48 * MHZ:
> +		clksel = S5PV210_UPHYCLK_PHYFSEL_48MHZ;
> +		break;
> +	default:
> +		clksel = CLKSEL_ERROR;
> +	}
> +
> +	return clksel;
> +}
> +
> +static void s5pv210_isol(struct uphy_instance *inst, bool on)
> +{
> +	struct uphy_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;
> +	};
> +
> +	tmp = readl(drv->reg_isol);
> +	if (on)
> +		tmp &= ~mask;
> +	else
> +		tmp |= mask;
> +	writel(tmp, drv->reg_isol);
> +}
> +
> +static void s5pv210_phy_pwr(struct uphy_instance *inst, bool on)
> +{
> +	struct uphy_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, 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 uphy_instance *inst)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +
> +	if (inst->state == STATE_ON) {
> +		dev_err(drv->dev, "usb phy \"%s\" already on",
> +							inst->cfg->label);
> +		return -ENODEV;
> +	}
> +	inst->state = STATE_ON;
> +	inst->ref_cnt++;
> +	if (inst->ref_cnt > 1)
> +		return 0;
> +
> +	s5pv210_isol(inst, 0);
> +	s5pv210_phy_pwr(inst, 1);
> +
> +	return 0;
> +}
> +
> +static int s5pv210_power_off(struct uphy_instance *inst)
> +{
> +	struct uphy_driver *drv = inst->drv;
> +
> +	if (inst->state == STATE_OFF) {
> +		dev_err(drv->dev, "usb phy \"%s\" already off",
> +							inst->cfg->label);
> +		return -EINVAL;
> +	}
> +
> +	inst->state = STATE_OFF;
> +	inst->ref_cnt++;
> +	if (inst->ref_cnt > 0)
> +		return 0;
> +
> +	s5pv210_phy_pwr(inst, 0);
> +	s5pv210_isol(inst, 1);
> +
> +	return 0;
> +}
> +
> +
> +static const struct common_phy s5pv210_phys[] = {
> +	{
> +		.label		= "device",
> +		.type		= PHY_DEVICE,
> +		.id		= S5PV210_DEVICE,
> +		.rate_to_clk	= s5pv210_rate_to_clk,
> +		.power_on	= s5pv210_power_on,
> +		.power_off	= s5pv210_power_off,
> +	},
> +	{
> +		.label		= "host",
> +		.type		= PHY_HOST,
> +		.id		= S5PV210_HOST,
> +		.rate_to_clk	= s5pv210_rate_to_clk,
> +		.power_on	= s5pv210_power_on,
> +		.power_off	= s5pv210_power_off,
> +	},
> +	{},
> +};
> +
> +const struct uphy_config s5pv210_uphy_config = {
> +	.cpu		= TYPE_S5PV210,
> +	.num_phys	= S5PV210_NUM_PHYS,
> +	.phys		= s5pv210_phys,
> +};

IMO this looks similar to the other drivers. Please re-use the drivers wherever
possible.

Thanks
Kishon
--
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] 46+ messages in thread

* Re: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework
  2013-10-25 14:15 ` [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework Kamil Debski
@ 2013-10-25 15:52     ` Kishon Vijay Abraham I
  2013-10-26  1:27   ` Jingoo Han
  2013-10-26  9:41     ` Vivek Gautam
  2 siblings, 0 replies; 46+ messages in thread
From: Kishon Vijay Abraham I @ 2013-10-25 15:52 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, kyungmin.park, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk

Hi,

On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
> Change the phy provider used from the old usb phy specific to a new one
> using the generic phy framework.

looks good :-)

Thanks
Kishon

> 
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/usb/host/ehci-s5p.c |   21 +++++++++++----------
>  1 file changed, 11 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
> index 7cc26e6..76606ff 100644
> --- a/drivers/usb/host/ehci-s5p.c
> +++ b/drivers/usb/host/ehci-s5p.c
> @@ -19,6 +19,7 @@
>  #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/platform_data/usb-ehci-s5p.h>
>  #include <linux/usb/phy.h>
> @@ -45,7 +46,7 @@ static struct hc_driver __read_mostly s5p_ehci_hc_driver;
>  
>  struct s5p_ehci_hcd {
>  	struct clk *clk;
> -	struct usb_phy *phy;
> +	struct phy *phy;
>  	struct usb_otg *otg;
>  	struct s5p_ehci_platdata *pdata;
>  };
> @@ -77,10 +78,11 @@ static int s5p_ehci_probe(struct platform_device *pdev)
>  {
>  	struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
>  	struct s5p_ehci_hcd *s5p_ehci;
> +	struct phy *phy;
>  	struct usb_hcd *hcd;
>  	struct ehci_hcd *ehci;
>  	struct resource *res;
> -	struct usb_phy *phy;
> +	const char *phy_name;
>  	int irq;
>  	int err;
>  
> @@ -103,14 +105,14 @@ static int s5p_ehci_probe(struct platform_device *pdev)
>  		return -ENOMEM;
>  	}
>  	s5p_ehci = to_s5p_ehci(hcd);
> -
> +	phy_name = of_get_property(pdev->dev.of_node, "phy-names", NULL);
> +	phy =  devm_phy_get(&pdev->dev, phy_name);
>  	if (of_device_is_compatible(pdev->dev.of_node,
>  					"samsung,exynos5440-ehci")) {
>  		s5p_ehci->pdata = &empty_platdata;
>  		goto skip_phy;
>  	}
>  
> -	phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
>  	if (IS_ERR(phy)) {
>  		/* Fallback to pdata */
>  		if (!pdata) {
> @@ -122,7 +124,6 @@ static int s5p_ehci_probe(struct platform_device *pdev)
>  		}
>  	} else {
>  		s5p_ehci->phy = phy;
> -		s5p_ehci->otg = phy->otg;
>  	}
>  
>  skip_phy:
> @@ -166,7 +167,7 @@ skip_phy:
>  		s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
>  
>  	if (s5p_ehci->phy)
> -		usb_phy_init(s5p_ehci->phy);
> +		phy_power_on(s5p_ehci->phy);
>  	else if (s5p_ehci->pdata->phy_init)
>  		s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
>  
> @@ -188,7 +189,7 @@ skip_phy:
>  
>  fail_add_hcd:
>  	if (s5p_ehci->phy)
> -		usb_phy_shutdown(s5p_ehci->phy);
> +		phy_power_off(s5p_ehci->phy);
>  	else if (s5p_ehci->pdata->phy_exit)
>  		s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>  fail_io:
> @@ -209,7 +210,7 @@ static int s5p_ehci_remove(struct platform_device *pdev)
>  		s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
>  
>  	if (s5p_ehci->phy)
> -		usb_phy_shutdown(s5p_ehci->phy);
> +		phy_power_off(s5p_ehci->phy);
>  	else if (s5p_ehci->pdata->phy_exit)
>  		s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>  
> @@ -244,7 +245,7 @@ static int s5p_ehci_suspend(struct device *dev)
>  		s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
>  
>  	if (s5p_ehci->phy)
> -		usb_phy_shutdown(s5p_ehci->phy);
> +		phy_power_off(s5p_ehci->phy);
>  	else if (s5p_ehci->pdata->phy_exit)
>  		s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>  
> @@ -265,7 +266,7 @@ static int s5p_ehci_resume(struct device *dev)
>  		s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
>  
>  	if (s5p_ehci->phy)
> -		usb_phy_init(s5p_ehci->phy);
> +		phy_power_on(s5p_ehci->phy);
>  	else if (s5p_ehci->pdata->phy_init)
>  		s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
>  
> 


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

* Re: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework
@ 2013-10-25 15:52     ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 46+ messages in thread
From: Kishon Vijay Abraham I @ 2013-10-25 15:52 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, kyungmin.park, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk

Hi,

On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
> Change the phy provider used from the old usb phy specific to a new one
> using the generic phy framework.

looks good :-)

Thanks
Kishon

> 
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/usb/host/ehci-s5p.c |   21 +++++++++++----------
>  1 file changed, 11 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
> index 7cc26e6..76606ff 100644
> --- a/drivers/usb/host/ehci-s5p.c
> +++ b/drivers/usb/host/ehci-s5p.c
> @@ -19,6 +19,7 @@
>  #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/platform_data/usb-ehci-s5p.h>
>  #include <linux/usb/phy.h>
> @@ -45,7 +46,7 @@ static struct hc_driver __read_mostly s5p_ehci_hc_driver;
>  
>  struct s5p_ehci_hcd {
>  	struct clk *clk;
> -	struct usb_phy *phy;
> +	struct phy *phy;
>  	struct usb_otg *otg;
>  	struct s5p_ehci_platdata *pdata;
>  };
> @@ -77,10 +78,11 @@ static int s5p_ehci_probe(struct platform_device *pdev)
>  {
>  	struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
>  	struct s5p_ehci_hcd *s5p_ehci;
> +	struct phy *phy;
>  	struct usb_hcd *hcd;
>  	struct ehci_hcd *ehci;
>  	struct resource *res;
> -	struct usb_phy *phy;
> +	const char *phy_name;
>  	int irq;
>  	int err;
>  
> @@ -103,14 +105,14 @@ static int s5p_ehci_probe(struct platform_device *pdev)
>  		return -ENOMEM;
>  	}
>  	s5p_ehci = to_s5p_ehci(hcd);
> -
> +	phy_name = of_get_property(pdev->dev.of_node, "phy-names", NULL);
> +	phy =  devm_phy_get(&pdev->dev, phy_name);
>  	if (of_device_is_compatible(pdev->dev.of_node,
>  					"samsung,exynos5440-ehci")) {
>  		s5p_ehci->pdata = &empty_platdata;
>  		goto skip_phy;
>  	}
>  
> -	phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
>  	if (IS_ERR(phy)) {
>  		/* Fallback to pdata */
>  		if (!pdata) {
> @@ -122,7 +124,6 @@ static int s5p_ehci_probe(struct platform_device *pdev)
>  		}
>  	} else {
>  		s5p_ehci->phy = phy;
> -		s5p_ehci->otg = phy->otg;
>  	}
>  
>  skip_phy:
> @@ -166,7 +167,7 @@ skip_phy:
>  		s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
>  
>  	if (s5p_ehci->phy)
> -		usb_phy_init(s5p_ehci->phy);
> +		phy_power_on(s5p_ehci->phy);
>  	else if (s5p_ehci->pdata->phy_init)
>  		s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
>  
> @@ -188,7 +189,7 @@ skip_phy:
>  
>  fail_add_hcd:
>  	if (s5p_ehci->phy)
> -		usb_phy_shutdown(s5p_ehci->phy);
> +		phy_power_off(s5p_ehci->phy);
>  	else if (s5p_ehci->pdata->phy_exit)
>  		s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>  fail_io:
> @@ -209,7 +210,7 @@ static int s5p_ehci_remove(struct platform_device *pdev)
>  		s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
>  
>  	if (s5p_ehci->phy)
> -		usb_phy_shutdown(s5p_ehci->phy);
> +		phy_power_off(s5p_ehci->phy);
>  	else if (s5p_ehci->pdata->phy_exit)
>  		s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>  
> @@ -244,7 +245,7 @@ static int s5p_ehci_suspend(struct device *dev)
>  		s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
>  
>  	if (s5p_ehci->phy)
> -		usb_phy_shutdown(s5p_ehci->phy);
> +		phy_power_off(s5p_ehci->phy);
>  	else if (s5p_ehci->pdata->phy_exit)
>  		s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>  
> @@ -265,7 +266,7 @@ static int s5p_ehci_resume(struct device *dev)
>  		s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
>  
>  	if (s5p_ehci->phy)
> -		usb_phy_init(s5p_ehci->phy);
> +		phy_power_on(s5p_ehci->phy);
>  	else if (s5p_ehci->pdata->phy_init)
>  		s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
>  
> 

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

* Re: [PATCH 5/5] usb: s3c-hsotg: Use the new Exynos USB phy driver with the generic phy framework
  2013-10-25 14:15 ` [PATCH 5/5] usb: s3c-hsotg: Use the new Exynos USB phy driver with " Kamil Debski
@ 2013-10-25 15:53     ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 46+ messages in thread
From: Kishon Vijay Abraham I @ 2013-10-25 15:53 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, kyungmin.park, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk

On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
> Change the used phy driver to the new Exynos USB phy driver that uses the
> generic phy framework.
> 
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/usb/gadget/s3c-hsotg.c |   13 ++++++++-----
>  1 file changed, 8 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c
> index 7705c41..2ce84a5 100644
> --- a/drivers/usb/gadget/s3c-hsotg.c
> +++ b/drivers/usb/gadget/s3c-hsotg.c
> @@ -29,6 +29,7 @@
>  #include <linux/slab.h>
>  #include <linux/clk.h>
>  #include <linux/regulator/consumer.h>
> +#include <linux/phy/phy.h>
>  
>  #include <linux/usb/ch9.h>
>  #include <linux/usb/gadget.h>
> @@ -131,6 +132,7 @@ struct s3c_hsotg_ep {
>  /**
>   * struct s3c_hsotg - driver state.
>   * @dev: The parent device supplied to the probe function
> +

empty blank line.
>   * @driver: USB gadget driver
>   * @phy: The otg phy transceiver structure for phy control.
>   * @plat: The platform specific configuration data. This can be removed once
> @@ -154,7 +156,7 @@ struct s3c_hsotg_ep {
>  struct s3c_hsotg {
>  	struct device		 *dev;
>  	struct usb_gadget_driver *driver;
> -	struct usb_phy		*phy;
> +	struct phy		 *phy;
>  	struct s3c_hsotg_plat	 *plat;
>  
>  	spinlock_t              lock;
> @@ -2858,9 +2860,10 @@ static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg)
>  	dev_dbg(hsotg->dev, "pdev 0x%p\n", pdev);
>  
>  	if (hsotg->phy)
> -		usb_phy_init(hsotg->phy);
> +		phy_power_on(hsotg->phy);
>  	else if (hsotg->plat->phy_init)
>  		hsotg->plat->phy_init(pdev, hsotg->plat->phy_type);
> +
>  }
>  
>  /**
> @@ -2875,7 +2878,7 @@ static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg)
>  	struct platform_device *pdev = to_platform_device(hsotg->dev);
>  
>  	if (hsotg->phy)
> -		usb_phy_shutdown(hsotg->phy);
> +		phy_power_off(hsotg->phy);
>  	else if (hsotg->plat->phy_exit)
>  		hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type);
>  }
> @@ -3485,7 +3488,7 @@ static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg)
>  static int s3c_hsotg_probe(struct platform_device *pdev)
>  {
>  	struct s3c_hsotg_plat *plat = pdev->dev.platform_data;
> -	struct usb_phy *phy;
> +	struct phy *phy;
>  	struct device *dev = &pdev->dev;
>  	struct s3c_hsotg_ep *eps;
>  	struct s3c_hsotg *hsotg;
> @@ -3500,7 +3503,7 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
>  		return -ENOMEM;
>  	}
>  
> -	phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
> +	phy = devm_phy_get(&pdev->dev, "device");
>  	if (IS_ERR(phy)) {
>  		/* Fallback for pdata */
>  		plat = pdev->dev.platform_data;

Apart from that one minor comment, this patch looks good.

Thanks
Kishon

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

* Re: [PATCH 5/5] usb: s3c-hsotg: Use the new Exynos USB phy driver with the generic phy framework
@ 2013-10-25 15:53     ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 46+ messages in thread
From: Kishon Vijay Abraham I @ 2013-10-25 15:53 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, kyungmin.park, t.figa, s.nawrocki, m.szyprowski,
	gautam.vivek, mat.krawczuk

On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
> Change the used phy driver to the new Exynos USB phy driver that uses the
> generic phy framework.
> 
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/usb/gadget/s3c-hsotg.c |   13 ++++++++-----
>  1 file changed, 8 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c
> index 7705c41..2ce84a5 100644
> --- a/drivers/usb/gadget/s3c-hsotg.c
> +++ b/drivers/usb/gadget/s3c-hsotg.c
> @@ -29,6 +29,7 @@
>  #include <linux/slab.h>
>  #include <linux/clk.h>
>  #include <linux/regulator/consumer.h>
> +#include <linux/phy/phy.h>
>  
>  #include <linux/usb/ch9.h>
>  #include <linux/usb/gadget.h>
> @@ -131,6 +132,7 @@ struct s3c_hsotg_ep {
>  /**
>   * struct s3c_hsotg - driver state.
>   * @dev: The parent device supplied to the probe function
> +

empty blank line.
>   * @driver: USB gadget driver
>   * @phy: The otg phy transceiver structure for phy control.
>   * @plat: The platform specific configuration data. This can be removed once
> @@ -154,7 +156,7 @@ struct s3c_hsotg_ep {
>  struct s3c_hsotg {
>  	struct device		 *dev;
>  	struct usb_gadget_driver *driver;
> -	struct usb_phy		*phy;
> +	struct phy		 *phy;
>  	struct s3c_hsotg_plat	 *plat;
>  
>  	spinlock_t              lock;
> @@ -2858,9 +2860,10 @@ static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg)
>  	dev_dbg(hsotg->dev, "pdev 0x%p\n", pdev);
>  
>  	if (hsotg->phy)
> -		usb_phy_init(hsotg->phy);
> +		phy_power_on(hsotg->phy);
>  	else if (hsotg->plat->phy_init)
>  		hsotg->plat->phy_init(pdev, hsotg->plat->phy_type);
> +
>  }
>  
>  /**
> @@ -2875,7 +2878,7 @@ static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg)
>  	struct platform_device *pdev = to_platform_device(hsotg->dev);
>  
>  	if (hsotg->phy)
> -		usb_phy_shutdown(hsotg->phy);
> +		phy_power_off(hsotg->phy);
>  	else if (hsotg->plat->phy_exit)
>  		hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type);
>  }
> @@ -3485,7 +3488,7 @@ static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg)
>  static int s3c_hsotg_probe(struct platform_device *pdev)
>  {
>  	struct s3c_hsotg_plat *plat = pdev->dev.platform_data;
> -	struct usb_phy *phy;
> +	struct phy *phy;
>  	struct device *dev = &pdev->dev;
>  	struct s3c_hsotg_ep *eps;
>  	struct s3c_hsotg *hsotg;
> @@ -3500,7 +3503,7 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
>  		return -ENOMEM;
>  	}
>  
> -	phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
> +	phy = devm_phy_get(&pdev->dev, "device");
>  	if (IS_ERR(phy)) {
>  		/* Fallback for pdata */
>  		plat = pdev->dev.platform_data;

Apart from that one minor comment, this patch looks good.

Thanks
Kishon

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

* Re: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver
@ 2013-10-25 21:36     ` Kumar Gala
  0 siblings, 0 replies; 46+ messages in thread
From: Kumar Gala @ 2013-10-25 21:36 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, kyungmin.park, kishon, t.figa, s.nawrocki,
	m.szyprowski, gautam.vivek, mat.krawczuk


On Oct 25, 2013, at 9:15 AM, 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.
> 
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
> .../devicetree/bindings/phy/samsung-usbphy.txt     |   51 +++
> drivers/phy/Kconfig                                |   21 ++
> drivers/phy/Makefile                               |    3 +
> drivers/phy/phy-exynos-usb.c                       |  245 ++++++++++++++
> drivers/phy/phy-exynos-usb.h                       |   94 ++++++
> drivers/phy/phy-exynos4210-usb.c                   |  295 +++++++++++++++++
> drivers/phy/phy-exynos4212-usb.c                   |  343 ++++++++++++++++++++
> 7 files changed, 1052 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> create mode 100644 drivers/phy/phy-exynos-usb.c
> create mode 100644 drivers/phy/phy-exynos-usb.h
> create mode 100644 drivers/phy/phy-exynos4210-usb.c
> create mode 100644 drivers/phy/phy-exynos4212-usb.c
> 
> diff --git a/Documentation/devicetree/bindings/phy/samsung-usbphy.txt b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> new file mode 100644
> index 0000000..f112b37
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> @@ -0,0 +1,51 @@
> +Samsung S5P/EXYNOS SoC series USB PHY
> +-------------------------------------------------
> +
> +Required properties:
> +- compatible : should be one of the listed compatibles:
> +	- "samsung,exynos4210-usbphy"
> +	- "samsung,exynos4212-usbphy"
> +- reg : a list of registers used by phy driver
> +	- first and obligatory is the location of phy modules registers
> +	- second and also required is the location of isolation registers
> +	  (isolation registers control the physical connection between the in
> +	  SoC modules and outside of the SoC, this also can be called enable
> +	  control in the documentation of the SoC)
> +	- third is the location of the mode switch register, this only applies
> +	  to SoCs that have such a feature; mode switching enables to have
> +	  both host and device used the same SoC pins and is commonly used
> +	  when OTG is supported
> +- #phy-cells : from the generic phy bindings, must be 1;

Please add if clock & clock-names are required properties.

> +
> +The second cell in the PHY specifier identifies the PHY its meaning is SoC
> +dependent. For the currently supported SoCs (Exynos 4210 and Exynos 4212) it
> +is as follows:

Can we say instead of 'its meaning is SoC dependent...' something like 'its meaning is compatible dependent"?

> +  0 - USB device,
> +  1 - USB host,
> +  2 - HSIC0,
> +  3 - HSIC1,
> +
> +Example:
> +
> +For Exynos 4412 (compatible with Exynos 4212):
> +
> +exynos_usbphy: exynos-usbphy@125B0000 {
> +	compatible = "samsung,exynos4212-usbphy";
> +	reg = <0x125B0000 0x100 0x10020704 0x0c 0x1001021c 0x4>;
> +	ranges;
> +	#address-cells = <1>;
> +	#size-cells = <1>;

Why do you have ranges, and #address-cells & #size-cells here?

> +	clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
> +							<&clock 2>;
> +	clock-names = "phy", "device", "host", "hsic0", "hsic1";
> +	status = "okay";
> +	#phy-cells = <1>;
> +};
> +
> +Then the PHY can be used in other nodes such as:
> +
> +ehci@12580000 {
> +	status = "okay";
> +	phys = <&exynos_usbphy 2>;
> +	phy-names = "hsic0";
> +};

-- 
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


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

* Re: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver
@ 2013-10-25 21:36     ` Kumar Gala
  0 siblings, 0 replies; 46+ messages in thread
From: Kumar Gala @ 2013-10-25 21:36 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-u79uwXL29TY76Z2rM5mHXA,
	kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ, kishon-l0cyMroinI0,
	t.figa-Sze3O3UU22JBDgjK7y7TUQ, s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ,
	m.szyprowski-Sze3O3UU22JBDgjK7y7TUQ,
	gautam.vivek-Sze3O3UU22JBDgjK7y7TUQ,
	mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w


On Oct 25, 2013, at 9:15 AM, 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.
> 
> Signed-off-by: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> Signed-off-by: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> ---
> .../devicetree/bindings/phy/samsung-usbphy.txt     |   51 +++
> drivers/phy/Kconfig                                |   21 ++
> drivers/phy/Makefile                               |    3 +
> drivers/phy/phy-exynos-usb.c                       |  245 ++++++++++++++
> drivers/phy/phy-exynos-usb.h                       |   94 ++++++
> drivers/phy/phy-exynos4210-usb.c                   |  295 +++++++++++++++++
> drivers/phy/phy-exynos4212-usb.c                   |  343 ++++++++++++++++++++
> 7 files changed, 1052 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> create mode 100644 drivers/phy/phy-exynos-usb.c
> create mode 100644 drivers/phy/phy-exynos-usb.h
> create mode 100644 drivers/phy/phy-exynos4210-usb.c
> create mode 100644 drivers/phy/phy-exynos4212-usb.c
> 
> diff --git a/Documentation/devicetree/bindings/phy/samsung-usbphy.txt b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> new file mode 100644
> index 0000000..f112b37
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> @@ -0,0 +1,51 @@
> +Samsung S5P/EXYNOS SoC series USB PHY
> +-------------------------------------------------
> +
> +Required properties:
> +- compatible : should be one of the listed compatibles:
> +	- "samsung,exynos4210-usbphy"
> +	- "samsung,exynos4212-usbphy"
> +- reg : a list of registers used by phy driver
> +	- first and obligatory is the location of phy modules registers
> +	- second and also required is the location of isolation registers
> +	  (isolation registers control the physical connection between the in
> +	  SoC modules and outside of the SoC, this also can be called enable
> +	  control in the documentation of the SoC)
> +	- third is the location of the mode switch register, this only applies
> +	  to SoCs that have such a feature; mode switching enables to have
> +	  both host and device used the same SoC pins and is commonly used
> +	  when OTG is supported
> +- #phy-cells : from the generic phy bindings, must be 1;

Please add if clock & clock-names are required properties.

> +
> +The second cell in the PHY specifier identifies the PHY its meaning is SoC
> +dependent. For the currently supported SoCs (Exynos 4210 and Exynos 4212) it
> +is as follows:

Can we say instead of 'its meaning is SoC dependent...' something like 'its meaning is compatible dependent"?

> +  0 - USB device,
> +  1 - USB host,
> +  2 - HSIC0,
> +  3 - HSIC1,
> +
> +Example:
> +
> +For Exynos 4412 (compatible with Exynos 4212):
> +
> +exynos_usbphy: exynos-usbphy@125B0000 {
> +	compatible = "samsung,exynos4212-usbphy";
> +	reg = <0x125B0000 0x100 0x10020704 0x0c 0x1001021c 0x4>;
> +	ranges;
> +	#address-cells = <1>;
> +	#size-cells = <1>;

Why do you have ranges, and #address-cells & #size-cells here?

> +	clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
> +							<&clock 2>;
> +	clock-names = "phy", "device", "host", "hsic0", "hsic1";
> +	status = "okay";
> +	#phy-cells = <1>;
> +};
> +
> +Then the PHY can be used in other nodes such as:
> +
> +ehci@12580000 {
> +	status = "okay";
> +	phys = <&exynos_usbphy 2>;
> +	phy-names = "hsic0";
> +};

-- 
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

--
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] 46+ messages in thread

* Re: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework
  2013-10-25 14:15 ` [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework Kamil Debski
  2013-10-25 15:52     ` Kishon Vijay Abraham I
@ 2013-10-26  1:27   ` Jingoo Han
  2013-10-28 13:52       ` Kamil Debski
  2013-10-26  9:41     ` Vivek Gautam
  2 siblings, 1 reply; 46+ messages in thread
From: Jingoo Han @ 2013-10-26  1:27 UTC (permalink / raw)
  To: 'Kamil Debski'
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, 'Kyungmin Park',
	'Kishon Vijay Abraham I', 'Tomasz Figa',
	'Sylwester Nawrocki', 'Marek Szyprowski',
	'Vivek Gautam', 'Mateusz Krawczuk',
	'Jingoo Han'

On Friday, October 25, 2013 11:15 PM, Kamil Debski wrote:
> 
> Change the phy provider used from the old usb phy specific to a new one
> using the generic phy framework.
> 
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/usb/host/ehci-s5p.c |   21 +++++++++++----------
>  1 file changed, 11 insertions(+), 10 deletions(-)

Hi Kamil Debski,
It looks good. :-)

However, could you re-basing against Greg's 'usb-next' branch?
Now, the file name of 'ehci-s5p.c' is renamed to 'ehci-exynos.c'.
Also, 'Generic PHY Framework' was already merged to Greg's
'usb-next' branch.

Thank you.

Best regards,
Jingoo Han


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

* Re: [PATCH 3/5] phy: Add support for S5PV210 to the Exynos USB PHY driver
@ 2013-10-26  1:40       ` Jingoo Han
  0 siblings, 0 replies; 46+ messages in thread
From: Jingoo Han @ 2013-10-26  1:40 UTC (permalink / raw)
  To: 'Kishon Vijay Abraham I', 'Kamil Debski'
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, 'Kyungmin Park', 'Tomasz Figa',
	'Sylwester Nawrocki', 'Marek Szyprowski',
	'Vivek Gautam', 'Mateusz Krawczuk',
	'Praveen Paneri', 'Yulgon Kim',
	'Anton Tikhomirov', 'Jingoo Han'

On Saturday, October 26, 2013 12:50 AM, Kishon Vijay Abraham I wrote:
> On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
> > From: Mateusz Krawczuk <m.krawczuk@partner.samsung.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: whitespace cleanup and commit description]
> > Signed-off-by: Kamil Debski <k.debski@samsung.com>
> > ---
> >  drivers/phy/Kconfig           |    7 ++
> >  drivers/phy/phy-exynos-usb.c  |   10 ++
> >  drivers/phy/phy-exynos-usb.h  |    1 +
> >  drivers/phy/phy-s5pv210-usb.c |  236 +++++++++++++++++++++++++++++++++++++++++
> >  4 files changed, 254 insertions(+)
> >  create mode 100644 drivers/phy/phy-s5pv210-usb.c

[.....]

> > diff --git a/drivers/phy/phy-s5pv210-usb.c b/drivers/phy/phy-s5pv210-usb.c
> > new file mode 100644
> > index 0000000..575275d
> > --- /dev/null
> > +++ b/drivers/phy/phy-s5pv210-usb.c
> 
> how different is this IP different from the previous ones? Does it deserve a
> separate driver or the previous drivers can be re-used?

[.....]

> > +const struct uphy_config s5pv210_uphy_config = {
> > +	.cpu		= TYPE_S5PV210,
> > +	.num_phys	= S5PV210_NUM_PHYS,
> > +	.phys		= s5pv210_phys,
> > +};
> 
> IMO this looks similar to the other drivers. Please re-use the drivers wherever
> possible.
> 

+cc Praveen Paneri(Author of Samsung PHY driver), Yulgon Kim, Anton Tikhomirov

I agree with Kishon's opinion.

Actually, all Exynos USB PHY controllers are not different IPs.
However, the differences between Exynos USB PHY controllers are
not little.

Best regards,
Jingoo Han


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

* Re: [PATCH 3/5] phy: Add support for S5PV210 to the Exynos USB PHY driver
@ 2013-10-26  1:40       ` Jingoo Han
  0 siblings, 0 replies; 46+ messages in thread
From: Jingoo Han @ 2013-10-26  1:40 UTC (permalink / raw)
  To: 'Kishon Vijay Abraham I', 'Kamil Debski'
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-u79uwXL29TY76Z2rM5mHXA, 'Kyungmin Park',
	'Tomasz Figa', 'Sylwester Nawrocki',
	'Marek Szyprowski', 'Vivek Gautam',
	'Mateusz Krawczuk', 'Praveen Paneri',
	'Yulgon Kim', 'Anton Tikhomirov',
	'Jingoo Han'

On Saturday, October 26, 2013 12:50 AM, Kishon Vijay Abraham I wrote:
> On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
> > From: Mateusz Krawczuk <m.krawczuk-GrGkmOP51GdLN7c7dRTbYkEOCMrvLtNR@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: whitespace cleanup and commit description]
> > Signed-off-by: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> > ---
> >  drivers/phy/Kconfig           |    7 ++
> >  drivers/phy/phy-exynos-usb.c  |   10 ++
> >  drivers/phy/phy-exynos-usb.h  |    1 +
> >  drivers/phy/phy-s5pv210-usb.c |  236 +++++++++++++++++++++++++++++++++++++++++
> >  4 files changed, 254 insertions(+)
> >  create mode 100644 drivers/phy/phy-s5pv210-usb.c

[.....]

> > diff --git a/drivers/phy/phy-s5pv210-usb.c b/drivers/phy/phy-s5pv210-usb.c
> > new file mode 100644
> > index 0000000..575275d
> > --- /dev/null
> > +++ b/drivers/phy/phy-s5pv210-usb.c
> 
> how different is this IP different from the previous ones? Does it deserve a
> separate driver or the previous drivers can be re-used?

[.....]

> > +const struct uphy_config s5pv210_uphy_config = {
> > +	.cpu		= TYPE_S5PV210,
> > +	.num_phys	= S5PV210_NUM_PHYS,
> > +	.phys		= s5pv210_phys,
> > +};
> 
> IMO this looks similar to the other drivers. Please re-use the drivers wherever
> possible.
> 

+cc Praveen Paneri(Author of Samsung PHY driver), Yulgon Kim, Anton Tikhomirov

I agree with Kishon's opinion.

Actually, all Exynos USB PHY controllers are not different IPs.
However, the differences between Exynos USB PHY controllers are
not little.

Best regards,
Jingoo Han

--
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] 46+ messages in thread

* Re: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework
@ 2013-10-26  9:41     ` Vivek Gautam
  0 siblings, 0 replies; 46+ messages in thread
From: Vivek Gautam @ 2013-10-26  9:41 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, Linux USB Mailing List,
	devicetree, linux-arm, Kyungmin Park, kishon, Tomasz Figa,
	Sylwester Nawrocki, Marek Szyprowski, Vivek Gautam, mat.krawczuk

Hi Kamil,


On Fri, Oct 25, 2013 at 7:45 PM, Kamil Debski <k.debski@samsung.com> wrote:
> Change the phy provider used from the old usb phy specific to a new one
> using the generic phy framework.
>
> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/usb/host/ehci-s5p.c |   21 +++++++++++----------
>  1 file changed, 11 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
> index 7cc26e6..76606ff 100644
> --- a/drivers/usb/host/ehci-s5p.c
> +++ b/drivers/usb/host/ehci-s5p.c
> @@ -19,6 +19,7 @@
>  #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/platform_data/usb-ehci-s5p.h>
>  #include <linux/usb/phy.h>
> @@ -45,7 +46,7 @@ static struct hc_driver __read_mostly s5p_ehci_hc_driver;
>
>  struct s5p_ehci_hcd {
>         struct clk *clk;
> -       struct usb_phy *phy;
> +       struct phy *phy;
>         struct usb_otg *otg;
>         struct s5p_ehci_platdata *pdata;
>  };
> @@ -77,10 +78,11 @@ static int s5p_ehci_probe(struct platform_device *pdev)
>  {
>         struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
>         struct s5p_ehci_hcd *s5p_ehci;
> +       struct phy *phy;

just a nit here:
Lets keep the pointer to 'phy' and 'phy_name' together ?
and move this above phy_name ?

>         struct usb_hcd *hcd;
>         struct ehci_hcd *ehci;
>         struct resource *res;
> -       struct usb_phy *phy;
> +       const char *phy_name;
>         int irq;
>         int err;
>
> @@ -103,14 +105,14 @@ static int s5p_ehci_probe(struct platform_device *pdev)
>                 return -ENOMEM;
>         }
>         s5p_ehci = to_s5p_ehci(hcd);
> -
> +       phy_name = of_get_property(pdev->dev.of_node, "phy-names", NULL);
> +       phy =  devm_phy_get(&pdev->dev, phy_name);

Below check for exynos5440 was supposed to skip any request phy.
So shouldn't we place above two assignments to the original place
where devm_usb_get_phy()
was called ?
May be i am not getting you intention of changing the place.

>         if (of_device_is_compatible(pdev->dev.of_node,
>                                         "samsung,exynos5440-ehci")) {
>                 s5p_ehci->pdata = &empty_platdata;
>                 goto skip_phy;
>         }
>
> -       phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
>         if (IS_ERR(phy)) {
>                 /* Fallback to pdata */
>                 if (!pdata) {
> @@ -122,7 +124,6 @@ static int s5p_ehci_probe(struct platform_device *pdev)
>                 }
>         } else {
>                 s5p_ehci->phy = phy;
> -               s5p_ehci->otg = phy->otg;
>         }
>
>  skip_phy:
> @@ -166,7 +167,7 @@ skip_phy:
>                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);

Lets remove this line and similar calls to 'set_host()' in the driver,
since we don't have
s5p_ehci->otg anymore after the same is removed above.
Anyways this was helping the old phy-samsung-usb2 driver, and not needed now.

>
>         if (s5p_ehci->phy)
> -               usb_phy_init(s5p_ehci->phy);
> +               phy_power_on(s5p_ehci->phy);
>         else if (s5p_ehci->pdata->phy_init)
>                 s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
>
> @@ -188,7 +189,7 @@ skip_phy:
>
>  fail_add_hcd:
>         if (s5p_ehci->phy)
> -               usb_phy_shutdown(s5p_ehci->phy);
> +               phy_power_off(s5p_ehci->phy);
>         else if (s5p_ehci->pdata->phy_exit)
>                 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>  fail_io:
> @@ -209,7 +210,7 @@ static int s5p_ehci_remove(struct platform_device *pdev)
>                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);

ditto

>
>         if (s5p_ehci->phy)
> -               usb_phy_shutdown(s5p_ehci->phy);
> +               phy_power_off(s5p_ehci->phy);
>         else if (s5p_ehci->pdata->phy_exit)
>                 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>
> @@ -244,7 +245,7 @@ static int s5p_ehci_suspend(struct device *dev)
>                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
ditto

>
>         if (s5p_ehci->phy)
> -               usb_phy_shutdown(s5p_ehci->phy);
> +               phy_power_off(s5p_ehci->phy);
>         else if (s5p_ehci->pdata->phy_exit)
>                 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>
> @@ -265,7 +266,7 @@ static int s5p_ehci_resume(struct device *dev)
>                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
ditto

>
>         if (s5p_ehci->phy)
> -               usb_phy_init(s5p_ehci->phy);
> +               phy_power_on(s5p_ehci->phy);
>         else if (s5p_ehci->pdata->phy_init)
>                 s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
>
[..] Rest looks good. :-)



-- 
Best Regards
Vivek

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

* Re: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework
@ 2013-10-26  9:41     ` Vivek Gautam
  0 siblings, 0 replies; 46+ messages in thread
From: Vivek Gautam @ 2013-10-26  9:41 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA, Linux USB Mailing List,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-u79uwXL29TY76Z2rM5mHXA, Kyungmin Park, kishon,
	Tomasz Figa, Sylwester Nawrocki, Marek Szyprowski, Vivek Gautam,
	mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w

Hi Kamil,


On Fri, Oct 25, 2013 at 7:45 PM, Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> wrote:
> Change the phy provider used from the old usb phy specific to a new one
> using the generic phy framework.
>
> Signed-off-by: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> Signed-off-by: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> ---
>  drivers/usb/host/ehci-s5p.c |   21 +++++++++++----------
>  1 file changed, 11 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
> index 7cc26e6..76606ff 100644
> --- a/drivers/usb/host/ehci-s5p.c
> +++ b/drivers/usb/host/ehci-s5p.c
> @@ -19,6 +19,7 @@
>  #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/platform_data/usb-ehci-s5p.h>
>  #include <linux/usb/phy.h>
> @@ -45,7 +46,7 @@ static struct hc_driver __read_mostly s5p_ehci_hc_driver;
>
>  struct s5p_ehci_hcd {
>         struct clk *clk;
> -       struct usb_phy *phy;
> +       struct phy *phy;
>         struct usb_otg *otg;
>         struct s5p_ehci_platdata *pdata;
>  };
> @@ -77,10 +78,11 @@ static int s5p_ehci_probe(struct platform_device *pdev)
>  {
>         struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
>         struct s5p_ehci_hcd *s5p_ehci;
> +       struct phy *phy;

just a nit here:
Lets keep the pointer to 'phy' and 'phy_name' together ?
and move this above phy_name ?

>         struct usb_hcd *hcd;
>         struct ehci_hcd *ehci;
>         struct resource *res;
> -       struct usb_phy *phy;
> +       const char *phy_name;
>         int irq;
>         int err;
>
> @@ -103,14 +105,14 @@ static int s5p_ehci_probe(struct platform_device *pdev)
>                 return -ENOMEM;
>         }
>         s5p_ehci = to_s5p_ehci(hcd);
> -
> +       phy_name = of_get_property(pdev->dev.of_node, "phy-names", NULL);
> +       phy =  devm_phy_get(&pdev->dev, phy_name);

Below check for exynos5440 was supposed to skip any request phy.
So shouldn't we place above two assignments to the original place
where devm_usb_get_phy()
was called ?
May be i am not getting you intention of changing the place.

>         if (of_device_is_compatible(pdev->dev.of_node,
>                                         "samsung,exynos5440-ehci")) {
>                 s5p_ehci->pdata = &empty_platdata;
>                 goto skip_phy;
>         }
>
> -       phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
>         if (IS_ERR(phy)) {
>                 /* Fallback to pdata */
>                 if (!pdata) {
> @@ -122,7 +124,6 @@ static int s5p_ehci_probe(struct platform_device *pdev)
>                 }
>         } else {
>                 s5p_ehci->phy = phy;
> -               s5p_ehci->otg = phy->otg;
>         }
>
>  skip_phy:
> @@ -166,7 +167,7 @@ skip_phy:
>                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);

Lets remove this line and similar calls to 'set_host()' in the driver,
since we don't have
s5p_ehci->otg anymore after the same is removed above.
Anyways this was helping the old phy-samsung-usb2 driver, and not needed now.

>
>         if (s5p_ehci->phy)
> -               usb_phy_init(s5p_ehci->phy);
> +               phy_power_on(s5p_ehci->phy);
>         else if (s5p_ehci->pdata->phy_init)
>                 s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
>
> @@ -188,7 +189,7 @@ skip_phy:
>
>  fail_add_hcd:
>         if (s5p_ehci->phy)
> -               usb_phy_shutdown(s5p_ehci->phy);
> +               phy_power_off(s5p_ehci->phy);
>         else if (s5p_ehci->pdata->phy_exit)
>                 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>  fail_io:
> @@ -209,7 +210,7 @@ static int s5p_ehci_remove(struct platform_device *pdev)
>                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);

ditto

>
>         if (s5p_ehci->phy)
> -               usb_phy_shutdown(s5p_ehci->phy);
> +               phy_power_off(s5p_ehci->phy);
>         else if (s5p_ehci->pdata->phy_exit)
>                 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>
> @@ -244,7 +245,7 @@ static int s5p_ehci_suspend(struct device *dev)
>                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
ditto

>
>         if (s5p_ehci->phy)
> -               usb_phy_shutdown(s5p_ehci->phy);
> +               phy_power_off(s5p_ehci->phy);
>         else if (s5p_ehci->pdata->phy_exit)
>                 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>
> @@ -265,7 +266,7 @@ static int s5p_ehci_resume(struct device *dev)
>                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
ditto

>
>         if (s5p_ehci->phy)
> -               usb_phy_init(s5p_ehci->phy);
> +               phy_power_on(s5p_ehci->phy);
>         else if (s5p_ehci->pdata->phy_init)
>                 s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
>
[..] Rest looks good. :-)



-- 
Best Regards
Vivek
--
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] 46+ messages in thread

* RE: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver
@ 2013-10-28 13:52       ` Kamil Debski
  0 siblings, 0 replies; 46+ messages in thread
From: Kamil Debski @ 2013-10-28 13:52 UTC (permalink / raw)
  To: 'Kishon Vijay Abraham I'
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, kyungmin.park, Tomasz Figa, Sylwester Nawrocki,
	Marek Szyprowski, gautam.vivek, mat.krawczuk

Hi Kishon,

Thank you for your review! I will answer your comments below.

> From: Kishon Vijay Abraham I [mailto:kishon@ti.com]
> Sent: Friday, October 25, 2013 5:40 PM
> 
> Hi,
> 
> On Friday 25 October 2013 07:45 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.
> >
> > Signed-off-by: Kamil Debski <k.debski@samsung.com>
> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> > ---
> >  .../devicetree/bindings/phy/samsung-usbphy.txt     |   51 +++
> >  drivers/phy/Kconfig                                |   21 ++
> >  drivers/phy/Makefile                               |    3 +
> >  drivers/phy/phy-exynos-usb.c                       |  245
> ++++++++++++++
> >  drivers/phy/phy-exynos-usb.h                       |   94 ++++++
> >  drivers/phy/phy-exynos4210-usb.c                   |  295
> +++++++++++++++++
> >  drivers/phy/phy-exynos4212-usb.c                   |  343
> ++++++++++++++++++++
> >  7 files changed, 1052 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> >  create mode 100644 drivers/phy/phy-exynos-usb.c  create mode 100644
> > drivers/phy/phy-exynos-usb.h  create mode 100644
> > drivers/phy/phy-exynos4210-usb.c  create mode 100644
> > drivers/phy/phy-exynos4212-usb.c
> >
> > diff --git a/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> > b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> > new file mode 100644
> > index 0000000..f112b37
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> > @@ -0,0 +1,51 @@
> > +Samsung S5P/EXYNOS SoC series USB PHY
> > +-------------------------------------------------
> > +
> > +Required properties:
> > +- compatible : should be one of the listed compatibles:
> > +	- "samsung,exynos4210-usbphy"
> > +	- "samsung,exynos4212-usbphy"
> > +- reg : a list of registers used by phy driver
> > +	- first and obligatory is the location of phy modules registers
> > +	- second and also required is the location of isolation registers
> > +	  (isolation registers control the physical connection between
> the in
> > +	  SoC modules and outside of the SoC, this also can be called
> enable
> > +	  control in the documentation of the SoC)
> > +	- third is the location of the mode switch register, this only
> applies
> > +	  to SoCs that have such a feature; mode switching enables to
> have
> > +	  both host and device used the same SoC pins and is commonly
> used
> > +	  when OTG is supported
> > +- #phy-cells : from the generic phy bindings, must be 1;
> > +
> > +The second cell in the PHY specifier identifies the PHY its meaning
> > +is SoC dependent. For the currently supported SoCs (Exynos 4210 and
> > +Exynos 4212) it is as follows:
> > +  0 - USB device,
> > +  1 - USB host,
> > +  2 - HSIC0,
> > +  3 - HSIC1,
> 
> HSIC is supposedly to be transceiver less no? You have to program
> something in the digital side?
> You have a single IP that have all these functionalities?

There is a single USB PHY controller for all the above functionalities
(i.e. host, device, hsic 0 and 1).

> > +
> > +Example:
> > +
> > +For Exynos 4412 (compatible with Exynos 4212):
> > +
> > +exynos_usbphy: exynos-usbphy@125B0000 {
> > +	compatible = "samsung,exynos4212-usbphy";
> > +	reg = <0x125B0000 0x100 0x10020704 0x0c 0x1001021c 0x4>;
> > +	ranges;
> > +	#address-cells = <1>;
> > +	#size-cells = <1>;
> 
> The above 3 properties aren't documented? Are they needed here?

My bad. I was working on two branches and corrected it in only one
of them.

> > +	clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
> > +							<&clock 2>;
> > +	clock-names = "phy", "device", "host", "hsic0", "hsic1";
> > +	status = "okay";
> > +	#phy-cells = <1>;
> > +};
> > +
> > +Then the PHY can be used in other nodes such as:
> > +
> > +ehci@12580000 {
> > +	status = "okay";
> > +	phys = <&exynos_usbphy 2>;
> > +	phy-names = "hsic0";
> > +};
> > diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index
> > 349bef2..2f7ac0a 100644
> > --- a/drivers/phy/Kconfig
> > +++ b/drivers/phy/Kconfig
> > @@ -15,4 +15,25 @@ config GENERIC_PHY
> >  	  phy users can obtain reference to the PHY. All the users of
> this
> >  	  framework should select this config.
> >
> > +config PHY_EXYNOS_USB
> > +	tristate "Samsung USB PHY driver (using the Generic PHY
> Framework)"
> Mentioning *Generic PHY Framework* is not necessary.
> *select GENERIC_PHY* here

This was added to distinguish this driver from the ols USB PHY driver.
I agree that in the final version it should be removed. I understand that
the correct way to do this is by removing the old driver when the new gets
merged. Yes?

> > +	help
> > +	  Enable this to support Samsung USB phy helper driver for
> Samsung SoCs.
> > +	  This driver provides common interface to interact, for Samsung
> > +	  USB 2.0 PHY driver.
> 
> If it's going to be used only for usb2, name it PHY_EXYNOS_USB2.

I agree.

> > +
> > +config PHY_EXYNOS4210_USB
> > +	bool "Support for Exynos 4210"
> > +	depends on PHY_EXYNOS_USB
> > +	depends on CPU_EXYNOS4210
> > +	help
> > +	  Enable USB PHY support for Exynos 4210
> > +
> > +config PHY_EXYNOS4212_USB
> > +	bool "Support for Exynos 4212"
> > +	depends on PHY_EXYNOS_USB
> > +	depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
> > +	help
> > +	  Enable USB PHY support for Exynos 4212
> 
> How difference is USB PHY in Exynos 4212 from Exynos 4210? If th
ere
> isn't much, I would suggest to use a single driver.

My idea for the driver is to have a single driver for the whole Exynos USB2
PHY. The core of the driver is in phy-exynos-usb.c and phy-exynos*-usb.c
provide registers and setup sequences for particular SoC versions.

There are a few differences between Exynos 4210, 4212, 5250 and S5PV210:
- the registers are different on each
- the setup sequence is different 
- 4212 and 5250 have a single pair of regular USB OTG DEVICE/HOST lines
- 4210 has a USB OTG and a separate USB HOST lines
- S5PV210 has both USB OTG and a separate USB HOST but lacks any HSIC
  interfaces

I agree with you that it is best to add as little code as possible.
Hence the idea to have a separate driver core and additional files for
distinctive SoCs. Enabling PHY_EXYNOS4210_USB (or other SoC specific
options)
does not add another driver it only includes support for enabled SoC.

It would be possible to skip this distinction altogether and include support
for all SoCs in the driver without config options.

> > +
> >  endmenu
> > diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index
> > 9e9560f..ca3dc82 100644
> > --- a/drivers/phy/Makefile
> > +++ b/drivers/phy/Makefile
> > @@ -3,3 +3,6 @@
> >  #
> >
> >  obj-$(CONFIG_GENERIC_PHY)	+= phy-core.o
> > +obj-$(CONFIG_PHY_EXYNOS_USB)		+= phy-exynos-usb.o
> > +obj-$(CONFIG_PHY_EXYNOS4210_USB)	+= phy-exynos4210-usb.o
> > +obj-$(CONFIG_PHY_EXYNOS4212_USB)	+= phy-exynos4212-usb.o
> > diff --git a/drivers/phy/phy-exynos-usb.c
> > b/drivers/phy/phy-exynos-usb.c new file mode 100644 index
> > 0000000..d4a26df
> > --- /dev/null
> > +++ b/drivers/phy/phy-exynos-usb.c
> 
> phy-exynos-usb2.c?

Ok.

> > @@ -0,0 +1,245 @@
> > +/*
> > + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.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-exynos-usb.h"
> > +
> > +static int exynos_uphy_power_on(struct phy *phy)
> 
> exynos_usb2_phy here and everywhere below.

Ok.

> > +{
> > +	struct uphy_instance *inst = phy_get_drvdata(phy);
> > +	struct uphy_driver *drv = inst->drv;
> > +	int ret;
> > +
> > +	dev_info(drv->dev, "Request to power_on \"%s\" usb phy\n",
> > +							inst->cfg->label);
> 
> make it dev_dbg if it's necessary.

Ok.

> > +	ret = clk_prepare_enable(drv->clk);
> > +	if (ret)
> > +		return ret;
> > +	if (inst->cfg->power_on) {
> > +		spin_lock(&drv->lock);
> > +		ret = inst->cfg->power_on(inst);
> > +		spin_unlock(&drv->lock);
> > +	}
> > +	clk_disable_unprepare(drv->clk);
> 
> hmm.. don't you need the clock to be on during the duration of the PHY
> operation?

I think that it is enough to have the clock enabled only during changes.

> > +	return ret;
> > +}
> > +
> > +static int exynos_uphy_power_off(struct phy *phy) {
> > +	struct uphy_instance *inst = phy_get_drvdata(phy);
> > +	struct uphy_driver *drv = inst->drv;
> > +	int ret;
> > +
> > +	dev_info(drv->dev, "Request to power_off \"%s\" usb phy\n",
> > +							inst->cfg->label);
> 
> dev_dbg?

Ok.

> > +	ret = clk_prepare_enable(drv->clk);
> > +	if (ret)
> > +		return ret;
> > +	if (inst->cfg->power_off) {
> > +		spin_lock(&drv->lock);
> > +		ret = inst->cfg->power_off(inst);
> > +		spin_unlock(&drv->lock);
> > +	}
> > +	clk_disable_unprepare(drv->clk);
> > +	return ret;
> > +}
> > +
> > +static struct phy_ops exynos_uphy_ops = {
> > +	.power_on	= exynos_uphy_power_on,
> > +	.power_off	= exynos_uphy_power_off,
> > +	.owner		= THIS_MODULE,
> > +};
> > +
> > +static struct phy *exynos_uphy_xlate(struct device *dev,
> > +					struct of_phandle_args *args)
> > +{
> > +	struct uphy_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->uphy_instances[args->args[0]].phy;
> > +}
> > +
> > +static const struct of_device_id exynos_uphy_of_match[];
> > +
> > +static int exynos_uphy_probe(struct platform_device *pdev) {
> > +	struct uphy_driver *drv;
> > +	struct device *dev = &pdev->dev;
> > +	struct resource *mem;
> > +	struct phy_provider *phy_provider;
> > +
> > +	const struct of_device_id *match;
> > +	const struct uphy_config *cfg;
> > +	struct clk *clk;
> > +
> > +	int i;
> > +
> > +	match = of_match_node(exynos_uphy_of_match, pdev->dev.of_node);
> > +	if (!match) {
> > +		dev_err(dev, "of_match_node() failed\n");
> > +		return -EINVAL;
> > +	}
> > +	cfg = match->data;
> > +	if (!cfg) {
> > +		dev_err(dev, "Failed to get configuration\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	drv = devm_kzalloc(dev, sizeof(struct uphy_driver) +
> > +		cfg->num_phys * sizeof(struct uphy_instance), GFP_KERNEL);
> > +
> > +	if (!drv) {
> > +		dev_err(dev, "Failed to allocate memory\n");
> > +		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);
> > +
> empty blank line.

Will fix.

> > +	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);
> > +	}
> > +
> > +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> > +	drv->reg_isol = devm_ioremap_resource(dev, mem);
> > +	if (IS_ERR(drv->reg_isol)) {
> > +		dev_err(dev, "Failed to map register memory (isolation)\n");
> > +		return PTR_ERR(drv->reg_isol);
> > +	}
> > +
> > +	switch (drv->cfg->cpu) {
> > +	case TYPE_EXYNOS4210:
> > +	case TYPE_EXYNOS4212:
> 
> Lets not add such cpu checks inside driver.

Some SoC have a special register, which switches the OTG lines between
device and host modes. I understand that it might not be the prettiest
code. I see this as a good compromise between having a single huge
driver for all Exynos SoCs and having a multiple drivers for each SoC
version. PHY IPs in these chips very are similar but have to be handled
differently. Any other ideas to solve this issue?

> > +		mem = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> > +		drv->reg_mode = devm_ioremap_resource(dev, mem);
> > +		if (IS_ERR(drv->reg_mode)) {
> > +			dev_err(dev, "Failed to map register memory (mode
> switch)\n");
> > +			return PTR_ERR(drv->reg_mode);
> > +		}
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +
> > +	phy_provider = devm_of_phy_provider_register(dev,
> > +							exynos_uphy_xlate);
> > +	if (IS_ERR(phy_provider)) {
> > +		dev_err(drv->dev, "Failed to register phy provider\n");
> > +		return PTR_ERR(phy_provider);
> > +	}
> > +
> > +	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 uphy_instance *p = &drv->uphy_instances[i];
> > +
> > +		dev_info(dev, "Creating phy \"%s\"\n", label);
> > +		p->phy = devm_phy_create(dev, &exynos_uphy_ops, NULL);
> > +		if (IS_ERR(p->phy)) {
> > +			dev_err(drv->dev, "Failed to create uphy \"%s\"\n",
> > +						label);
> > +			return PTR_ERR(p->phy);
> > +		}
> > +
> > +		p->cfg = &drv->cfg->phys[i];
> > +		p->drv = drv;
> > +		phy_set_drvdata(p->phy, p);
> > +
> > +		clk = 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->rate = clk_get_rate(clk);
> > +
> > +		if (p->cfg->rate_to_clk) {
> > +			p->clk = p->cfg->rate_to_clk(p->rate);
> > +			if (p->clk == CLKSEL_ERROR) {
> > +				dev_err(dev, "Clock rate (%ld) not
supported\n",
> > +								p->rate);
> > +				clk_put(clk);
> > +				return -EINVAL;
> > +			}
> > +		}
> > +		clk_put(clk);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +#ifdef CONFIG_PHY_EXYNOS4210_USB
> Do we really need this?

No we don't. The driver can always support all Exynos SoC versions. These
config options were added for flexibility.

> 
> > +extern const struct uphy_config exynos4210_uphy_config; #endif
> > +
> > +#ifdef CONFIG_PHY_EXYNOS4212_USB
> 
> Same here.
> > +extern const struct uphy_config exynos4212_uphy_config; #endif
> > +
> > +static const struct of_device_id exynos_uphy_of_match[] = { #ifdef
> > +CONFIG_PHY_EXYNOS4210_USB
> 
> #if not needed here.

If the #ifdef CONFIG_PHY_EXYNOS4210_USB is removed then yes. Otherwise
it is necessary - exynos4210_uphy_config may be undefined.

> > +	{
> > +		.compatible = "samsung,exynos4210-usbphy",
> > +		.data = &exynos4210_uphy_config,
> > +	},
> > +#endif
> > +#ifdef CONFIG_PHY_EXYNOS4212_USB
> 
> here too.
> > +	{
> > +		.compatible = "samsung,exynos4212-usbphy",
> > +		.data = &exynos4212_uphy_config,
> > +	},
> > +#endif
> > +	{ },
> > +};
> > +
> > +static struct platform_driver exynos_uphy_driver = {
> > +	.probe	= exynos_uphy_probe,
> > +	.driver = {
> > +		.of_match_table	= exynos_uphy_of_match,
> > +		.name		= "exynos-usbphy-new",
> "exynos-usb2-phy".
> > +		.owner		= THIS_MODULE,
> > +	}
> > +};
> > +
> > +module_platform_driver(exynos_uphy_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:exynos-uphy-new");
> > +
> > diff --git a/drivers/phy/phy-exynos-usb.h
> > b/drivers/phy/phy-exynos-usb.h new file mode 100644 index
> > 0000000..f45cb3c
> > --- /dev/null
> > +++ b/drivers/phy/phy-exynos-usb.h
> > @@ -0,0 +1,94 @@
> > +/*
> > + * Samsung S5P/EXYNOS SoC series USB 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_SAMSUNG_NEW_H
> > +#define _PHY_SAMSUNG_NEW_H
> > +
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.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>
> > +
> > +#define CLKSEL_ERROR                       -1
> > +
> > +#ifndef KHZ
> > +#define KHZ 1000
> > +#endif
> > +
> > +#ifndef MHZ
> > +#define MHZ (KHZ * KHZ)
> > +#endif
> > +
> > +enum phy_type {
> > +	PHY_DEVICE,
> > +	PHY_HOST,
> > +};
> > +
> > +enum samsung_cpu_type {
> > +	TYPE_S3C64XX,
> > +	TYPE_EXYNOS4210,
> > +	TYPE_EXYNOS4212,
> 
> No *cpu_type* inside driver files.

I guess that you are in favor a "a separate driver for each phy version".
For me it can be both. But we have to discuss which apporach is better:
1) separate driver for each phy version - no iffs and significant code
   duplication
2) a single driver driver supporting all Exynos variants - it needs ifs,
   code is always bigger
3) a single driver with support for particular SoC enabled in the config
file
   - with ifs, but the driver can be compiled smaller

> > +};
> > +
> > +enum uphy_state {
> > +	STATE_OFF,
> > +	STATE_ON,
> > +};
> > +
> > +struct uphy_driver;
> > +struct uphy_instance;
> > +struct uphy_config;
> > +
> > +struct uphy_instance {
> > +	struct uphy_driver *drv;
> > +	struct phy *phy;
> > +	const struct common_phy *cfg;
> > +	enum uphy_state state;
> > +	int ref_cnt;
> > +	u32 clk;
> > +	unsigned long rate;
> > +};
> > +
> > +struct uphy_driver {
> > +	struct device *dev;
> > +	spinlock_t lock;
> > +	void __iomem *reg_phy;
> > +	void __iomem *reg_isol;
> > +	void __iomem *reg_mode;
> > +	const struct uphy_config *cfg;
> > +	struct clk *clk;
> > +	struct uphy_instance uphy_instances[0];
> 
> you might have more than one phy instance right?

Yes. The USB PHY IP controls USB device, host and hsics.

> > +};
> > +
> > +struct common_phy {
> > +	char *label;
> > +	enum phy_type type;
> > +	unsigned int id;
> > +	u32 (*rate_to_clk)(unsigned long);
> > +	int (*power_on)(struct uphy_instance*);
> > +	int (*power_off)(struct uphy_instance*); };
> > +
> > +
> > +struct uphy_config {
> > +	enum samsung_cpu_type cpu;
> > +	int num_phys;
> > +	const struct common_phy *phys;
> > +};
> > +
> > +#endif
> > +
> > diff --git a/drivers/phy/phy-exynos4210-usb.c
> > b/drivers/phy/phy-exynos4210-usb.c
> > new file mode 100644
> > index 0000000..6cf74f7
> > --- /dev/null
> > +++ b/drivers/phy/phy-exynos4210-usb.c
> > @@ -0,0 +1,295 @@
> > +/*
> > + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.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-exynos-usb.h"
> > +
> > +/* Exynos USB PHY registers */
> > +
> > +/* PHY power control */
> > +#define EXYNOS_4210_UPHYPWR			0x0
> > +
> > +#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND	(1 << 0)
> > +#define EXYNOS_4210_UPHYPWR_PHY0_PWR		(1 << 3)
> > +#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR	(1 << 4)
> > +#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP		(1 << 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	(1 << 6)
> > +#define EXYNOS_4210_UPHYPWR_PHY1_PWR		(1 << 7)
> > +#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP		(1 << 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_HSCI0_SUSPEND	(1 << 9)
> > +#define EXYNOS_4210_UPHYPWR_HSCI0_SLEEP		(1 << 10)
> > +#define EXYNOS_4210_UPHYPWR_HSCI0 ( \
> > +	EXYNOS_4210_UPHYPWR_HSCI0_SUSPEND | \
> > +	EXYNOS_4210_UPHYPWR_HSCI0_SLEEP)
> > +
> > +#define EXYNOS_4210_UPHYPWR_HSCI1_SUSPEND	(1 << 11)
> > +#define EXYNOS_4210_UPHYPWR_HSCI1_SLEEP		(1 << 12)
> > +#define EXYNOS_4210_UPHYPWR_HSCI1 ( \
> > +	EXYNOS_4210_UPHYPWR_HSCI1_SUSPEND | \
> > +	EXYNOS_4210_UPHYPWR_HSCI1_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	(0x1 << 2)
> > +#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON	(0x1 << 4)
> > +#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON	(0x1 << 7)
> > +
> > +/* PHY reset control */
> > +#define EXYNOS_4210_UPHYRST			0x8
> > +
> > +#define EXYNOS_4210_URSTCON_PHY0		(1 << 0)
> > +#define EXYNOS_4210_URSTCON_OTG_HLINK		(1 << 1)
> > +#define EXYNOS_4210_URSTCON_OTG_PHYLINK		(1 << 2)
> > +#define EXYNOS_4210_URSTCON_PHY1_ALL		(1 << 3)
> > +#define EXYNOS_4210_URSTCON_PHY1_P0		(1 << 4)
> > +#define EXYNOS_4210_URSTCON_PHY1_P1P2		(1 << 5)
> > +#define EXYNOS_4210_URSTCON_HOST_LINK_ALL	(1 << 6)
> > +#define EXYNOS_4210_URSTCON_HOST_LINK_P0	(1 << 7)
> > +#define EXYNOS_4210_URSTCON_HOST_LINK_P1	(1 << 8)
> > +#define EXYNOS_4210_URSTCON_HOST_LINK_P2	(1 << 9)But
> > +
> > +/* Isolation, configured in the power management unit */
> > +#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET	0x0
> > +#define EXYNOS_4210_USB_ISOL_DEVICE		(1 << 0)
> > +#define EXYNOS_4210_USB_ISOL_HOST_OFFSET	0x4
> > +#define EXYNOS_4210_USB_ISOL_HOST		(1 << 0)
> > +
> > +/* USBYPHY1 Floating prevention */
> > +#define EXYNOS_4210_UPHY1CON			0x34
> > +#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION	0x1
> > +
> > +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. */
> 
> use proper commenting styles Documentation/CodingStyle

Ok, I will.

> > +static u32 exynos4210_rate_to_clk(unsigned long rate) {
> > +	unsigned int clksel;
> > +
> > +	switch (rate) {
> > +	case 12 * MHZ:
> > +		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
> > +		break;
> > +	case 24 * MHZ:
> > +		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
> > +		break;
> > +	case 48 * MHZ:
> > +		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
> > +		break;
> > +	default:
> > +		clksel = CLKSEL_ERROR;
> > +	}
> > +
> > +	return clksel;
> > +}
> > +
> > +static void exynos4210_isol(struct uphy_instance *inst, bool on) {
> > +	struct uphy_driver *drv = inst->drv;
> > +	u32 offset;
> > +	u32 mask;
> > +	u32 tmp;
> > +
> > +	if (!drv->reg_isol)
> > +		return;
> > +
> > +	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;
> > +	};
> > +
> > +	tmp = readl(drv->reg_isol + offset);
> > +	if (on)
> > +		tmp &= ~mask;
> > +	else
> > +		tmp |= mask;
> > +	writel(tmp, drv->reg_isol + offset); }
> > +
> > +static void exynos4210_phy_pwr(struct uphy_instance *inst, bool on)
> {
> > +	struct uphy_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_HSCI0;
> > +		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
> > +				EXYNOS_4210_URSTCON_HOST_LINK_P1;
> > +		break;
> > +	case EXYNOS4210_HSIC1:
> > +		phypwr =	EXYNOS_4210_UPHYPWR_HSCI1;
> > +		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
> > +				EXYNOS_4210_URSTCON_HOST_LINK_P2;
> > +		break;
> > +	};
> > +
> > +	if (on) {
> > +		writel(inst->clk, 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);
> 
> usleep_range?

Maybe. The above code is based on the drivers/usb/phy/phy-samsung-usb2.c.
My documentation does not specify the acceptable delay range.

> > +		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 uphy_instance *inst) {
> > +	struct uphy_driver *drv = inst->drv;
> > +
> > +	if (inst->state == STATE_ON) {
> > +		dev_err(drv->dev, "usb phy \"%s\" already on",
> > +							inst->cfg->label);
> > +		return -ENODEV;
> > +	}
> > +	inst->state = STATE_ON;
> > +	inst->ref_cnt++;
> > +	if (inst->ref_cnt > 1)
> > +		return 0;
> 
> reference counting shouldn't be needed. It's been handled by the PHY
> framework.

I will try how it works without this. There was some code that was removed
and
used the reference counter. I had some trouble figuring out the correct
setup
sequence, when multiple phys were enabled (e.g. only host, host + device,
only
device).

> > +
> > +	/* 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 uphy_instance *inst) {
> > +	struct uphy_driver *drv = inst->drv;
> > +
> > +	if (inst->state == STATE_OFF) {
> > +		dev_err(drv->dev, "usb phy \"%s\" already off",
> > +							inst->cfg->label);
> > +		return -EINVAL;
> > +	}
> > +
> > +	inst->state = STATE_OFF;
> > +	inst->ref_cnt--;
> > +	if (inst->ref_cnt > 0)
> > +		return 0;
> 
> same here.
> > +
> > +	exynos4210_isol(inst, 1);
> > +	exynos4210_phy_pwr(inst, 0);
> > +
> > +	return 0;
> > +}
> > +
> > +
> > +static const struct common_phy exynos4210_phys[] = {
> > +	{
> > +		.label		= "device",
> > +		.type		= PHY_DEVICE,
> > +		.id		= EXYNOS4210_DEVICE,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "host",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS4210_HOST,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic0",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS4210_HSIC0,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic1",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS4210_HSIC1,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{},
> > +};
> > +
> > +const struct uphy_config exynos4210_uphy_config = {
> > +	.cpu		= TYPE_EXYNOS4210,
> > +	.num_phys	= EXYNOS4210_NUM_PHYS,
> > +	.phys		= exynos4210_phys,
> > +};
> > +
> > diff --git a/drivers/phy/phy-exynos4212-usb.c
> > b/drivers/phy/phy-exynos4212-usb.c
> > new file mode 100644
> > index 0000000..c07ae8e
> > --- /dev/null
> > +++ b/drivers/phy/phy-exynos4212-usb.c
> > @@ -0,0 +1,343 @@
> > +/*
> > + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.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-exynos-usb.h"
> > +
> > +/* Exynos USB PHY registers */
> > +
> > +/* PHY power control */
> > +#define EXYNOS_4212_UPHYPWR			0x0
> > +
> > +#define EXYNOS_4212_UPHYPWR_DEV_SUSPEND		(1 << 0)
> > +#define EXYNOS_4212_UPHYPWR_DEV_PWR		(1 << 3)
> > +#define EXYNOS_4212_UPHYPWR_DEV_OTG_PWR		(1 << 4)
> > +#define EXYNOS_4212_UPHYPWR_DEV_SLEEP		(1 << 5)
> > +#define EXYNOS_4212_UPHYPWR_DEV	( \
> > +	EXYNOS_4212_UPHYPWR_DEV_SUSPEND | \
> > +	EXYNOS_4212_UPHYPWR_DEV_PWR | \
> > +	EXYNOS_4212_UPHYPWR_DEV_OTG_PWR | \
> > +	EXYNOS_4212_UPHYPWR_DEV_SLEEP)
> > +
> > +#define EXYNOS_4212_UPHYPWR_HOST_SUSPEND	(1 << 6)
> > +#define EXYNOS_4212_UPHYPWR_HOST_PWR		(1 << 7)
> > +#define EXYNOS_4212_UPHYPWR_HOST_SLEEP		(1 << 8)
> > +#define EXYNOS_4212_UPHYPWR_HOST ( \
> > +	EXYNOS_4212_UPHYPWR_HOST_SUSPEND | \
> > +	EXYNOS_4212_UPHYPWR_HOST_PWR | \
> > +	EXYNOS_4212_UPHYPWR_HOST_SLEEP)
> > +
> > +#define EXYNOS_4212_UPHYPWR_HSCI0_SUSPEND	(1 << 9)
> > +#define EXYNOS_4212_UPHYPWR_HSCI0_PWR		(1 << 10)
> > +#define EXYNOS_4212_UPHYPWR_HSCI0_SLEEP		(1 << 11)
> > +#define EXYNOS_4212_UPHYPWR_HSCI0 ( \
> > +	EXYNOS_4212_UPHYPWR_HSCI0_SUSPEND | \
> > +	EXYNOS_4212_UPHYPWR_HSCI0_PWR | \
> > +	EXYNOS_4212_UPHYPWR_HSCI0_SLEEP)
> > +
> > +#define EXYNOS_4212_UPHYPWR_HSCI1_SUSPEND	(1 << 12)
> > +#define EXYNOS_4212_UPHYPWR_HSCI1_PWR		(1 << 13)
> > +#define EXYNOS_4212_UPHYPWR_HSCI1_SLEEP		(1 << 14)
> > +#define EXYNOS_4212_UPHYPWR_HSCI1 ( \
> > +	EXYNOS_4212_UPHYPWR_HSCI1_SUSPEND | \
> > +	EXYNOS_4212_UPHYPWR_HSCI1_PWR | \
> > +	EXYNOS_4212_UPHYPWR_HSCI1_SLEEP)
> > +
> > +/* PHY clock control */
> > +#define EXYNOS_4212_UPHYCLK			0x4
> > +
> > +#define EXYNOS_4212_UPHYCLK_PHYFSEL_MASK	(0x7 << 0)
> > +#define EXYNOS_4212_UPHYCLK_PHYFSEL_9MHZ6	(0x0 << 0)
> > +#define EXYNOS_4212_UPHYCLK_PHYFSEL_10MHZ	(0x1 << 0)
> > +#define EXYNOS_4212_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
> > +#define EXYNOS_4212_UPHYCLK_PHYFSEL_19MHZ2	(0x3 << 0)
> > +#define EXYNOS_4212_UPHYCLK_PHYFSEL_20MHZ	(0x4 << 0)
> > +#define EXYNOS_4212_UPHYCLK_PHYFSEL_24MHZ	(0x5 << 0)
> > +#define EXYNOS_4212_UPHYCLK_PHYFSEL_50MHZ	(0x7 << 0)
> > +
> > +#define EXYNOS_4212_UPHYCLK_PHY0_ID_PULLUP	(0x1 << 3)
> > +#define EXYNOS_4212_UPHYCLK_PHY0_COMMON_ON	(0x1 << 4)
> > +#define EXYNOS_4212_UPHYCLK_PHY1_COMMON_ON	(0x1 << 7)
> > +
> > +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_MASK	(0x7f << 10)
> > +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_12MHZ	(0x24 << 10)
> > +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_15MHZ	(0x1c << 10)
> > +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_16MHZ	(0x1a << 10)
> > +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_19MHZ2	(0x15 << 10)
> > +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_20MHZ	(0x14 << 10)
> > +
> > +/* PHY reset control */
> > +#define EXYNOS_4212_UPHYRST			0x8
> > +
> > +#define EXYNOS_4212_URSTCON_DEVICE		(1 << 0)
> > +#define EXYNOS_4212_URSTCON_OTG_HLINK		(1 << 1)
> > +#define EXYNOS_4212_URSTCON_OTG_PHYLINK		(1 << 2)
> > +#define EXYNOS_4212_URSTCON_HOST_PHY		(1 << 3)
> > +#define EXYNOS_4212_URSTCON_PHY1		(1 << 4)
> > +#define EXYNOS_4212_URSTCON_HSIC0		(1 << 5)
> > +#define EXYNOS_4212_URSTCON_HSIC1		(1 << 6)
> > +#define EXYNOS_4212_URSTCON_HOST_LINK_ALL	(1 << 7)
> > +#define EXYNOS_4212_URSTCON_HOST_LINK_P0	(1 << 8)
> > +#define EXYNOS_4212_URSTCON_HOST_LINK_P1	(1 << 9)
> > +#define EXYNOS_4212_URSTCON_HOST_LINK_P2	(1 << 10)
> > +
> > +/* Isolation, configured in the power management unit */
> > +#define EXYNOS_4212_USB_ISOL_OFFSET		0x0
> > +#define EXYNOS_4212_USB_ISOL_OTG		(1 << 0)
> > +#define EXYNOS_4212_USB_ISOL_HSIC0_OFFSET	0x4
> > +#define EXYNOS_4212_USB_ISOL_HSIC0		(1 << 0)
> > +#define EXYNOS_4212_USB_ISOL_HSIC1_OFFSET	0x8
> > +#define EXYNOS_4212_USB_ISOL_HSIC1		(1 << 0)
> > +
> > +enum exynos4x12_phy_id {
> > +	EXYNOS4212_DEVICE,
> > +	EXYNOS4212_HOST,
> > +	EXYNOS4212_HSIC0,
> > +	EXYNOS4212_HSIC1,
> > +	EXYNOS4212_NUM_PHYS,
> > +};
> > +
> > +/* exynos4212_rate_to_clk() converts the supplied clock rate to the
> > +value that
> > + * can be written to the phy register. */ static u32
> > +exynos4212_rate_to_clk(unsigned long rate) {
> > +	unsigned int clksel;
> > +
> > +	/* EXYNOS_4212_UPHYCLK_PHYFSEL_MASK */
> > +
> > +	switch (rate) {
> > +	case 9600 * KHZ:
> > +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_9MHZ6;
> > +		break;
> > +	case 10 * MHZ:
> > +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_10MHZ;
> > +		break;
> > +	case 12 * MHZ:
> > +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_12MHZ;
> > +		break;
> > +	case 19200 * KHZ:
> > +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_19MHZ2;
> > +		break;
> > +	case 20 * MHZ:
> > +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_20MHZ;
> > +		break;
> > +	case 24 * MHZ:
> > +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_24MHZ;
> > +		break;
> > +	case 50 * MHZ:
> > +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_50MHZ;
> > +		break;
> > +	default:
> > +		clksel = CLKSEL_ERROR;
> > +	}
> > +
> > +	return clksel;
> > +}
> > +
> > +static void exynos4212_isol(struct uphy_instance *inst, bool on) {
> > +	struct uphy_driver *drv = inst->drv;
> > +	u32 offset;
> > +	u32 mask;
> > +	u32 tmp;
> > +
> > +	if (!drv->reg_isol)
> > +		return;
> > +
> > +	switch (inst->cfg->id) {
> > +	case EXYNOS4212_DEVICE:
> > +		offset = EXYNOS_4212_USB_ISOL_OFFSET;
> > +		mask = EXYNOS_4212_USB_ISOL_OTG;
> > +		break;
> > +	case EXYNOS4212_HOST:
> > +		offset = EXYNOS_4212_USB_ISOL_OFFSET;
> > +		mask = EXYNOS_4212_USB_ISOL_OTG;
> > +		break;
> > +	case EXYNOS4212_HSIC0:
> > +		offset = EXYNOS_4212_USB_ISOL_HSIC0_OFFSET;
> > +		mask = EXYNOS_4212_USB_ISOL_HSIC0;
> > +		break;
> > +	case EXYNOS4212_HSIC1:
> > +		offset = EXYNOS_4212_USB_ISOL_HSIC1_OFFSET;
> > +		mask = EXYNOS_4212_USB_ISOL_HSIC1;
> > +		break;
> > +	default:
> > +		return;
> > +	};
> > +
> > +	tmp = readl(drv->reg_isol + offset);
> > +	if (on)
> > +		tmp &= ~mask;
> > +	else
> > +		tmp |= mask;
> > +	writel(tmp, drv->reg_isol + offset); }
> > +
> > +static void exynos4212_phy_pwr(struct uphy_instance *inst, bool on)
> {
> > +	struct uphy_driver *drv = inst->drv;
> > +	u32 rstbits = 0;
> > +	u32 phypwr = 0;
> > +	u32 rst;
> > +	u32 pwr;
> > +
> > +	switch (inst->cfg->id) {
> > +	case EXYNOS4212_DEVICE:
> > +		phypwr =	EXYNOS_4212_UPHYPWR_DEV;
> > +		rstbits =	EXYNOS_4212_URSTCON_DEVICE;
> > +		break;
> > +	case EXYNOS4212_HOST:
> > +		phypwr =	EXYNOS_4212_UPHYPWR_HOST;
> > +		rstbits =	EXYNOS_4212_URSTCON_HOST_PHY;
> > +		break;
> > +	case EXYNOS4212_HSIC0:
> > +		phypwr =	EXYNOS_4212_UPHYPWR_HSCI0;
> > +		rstbits =	EXYNOS_4212_URSTCON_HSIC1 |
> > +				EXYNOS_4212_URSTCON_HOST_LINK_P0 |
> > +				EXYNOS_4212_URSTCON_HOST_PHY;
> > +		break;
> > +	case EXYNOS4212_HSIC1:
> > +		phypwr =	EXYNOS_4212_UPHYPWR_HSCI1;
> > +		rstbits =	EXYNOS_4212_URSTCON_HSIC1 |
> > +				EXYNOS_4212_URSTCON_HOST_LINK_P1;
> > +		break;
> > +	};
> > +
> > +	if (on) {
> > +		writel(inst->clk, drv->reg_phy + EXYNOS_4212_UPHYCLK);
> > +
> > +		pwr = readl(drv->reg_phy + EXYNOS_4212_UPHYPWR);
> > +		pwr &= ~phypwr;
> > +		writel(pwr, drv->reg_phy + EXYNOS_4212_UPHYPWR);
> > +
> > +		rst = readl(drv->reg_phy + EXYNOS_4212_UPHYRST);
> > +		rst |= rstbits;
> > +		writel(rst, drv->reg_phy + EXYNOS_4212_UPHYRST);
> > +		udelay(10);
> > +		rst &= ~rstbits;
> > +		writel(rst, drv->reg_phy + EXYNOS_4212_UPHYRST);
> > +	} else {
> > +		pwr = readl(drv->reg_phy + EXYNOS_4212_UPHYPWR);
> > +		pwr |= phypwr;
> > +		writel(pwr, drv->reg_phy + EXYNOS_4212_UPHYPWR);
> > +	}
> > +}
> > +
> > +static int exynos4212_power_on(struct uphy_instance *inst) {
> > +	struct uphy_driver *drv = inst->drv;
> > +
> > +	if (inst->state == STATE_ON) {
> > +		dev_err(drv->dev, "usb phy \"%s\" already on",
> > +							inst->cfg->label);
> > +		return -ENODEV;
> > +	}
> > +
> > +	inst->state = STATE_ON;
> > +	inst->ref_cnt++;
> > +	if (inst->ref_cnt > 1)
> > +		return 0;
> > +
> > +	exynos4212_phy_pwr(inst, 1);
> > +	exynos4212_isol(inst, 0);
> > +
> > +	/* Power on the device, as it is necessary for HSIC to work */
> > +	if (inst->cfg->id == EXYNOS4212_HSIC0) {
> > +		struct uphy_instance *device =
> > +
&drv->uphy_instances[EXYNOS4212_DEVICE];
> > +		exynos4212_phy_pwr(device, 1);
> > +		exynos4212_isol(device, 0);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int exynos4212_power_off(struct uphy_instance *inst) {
> > +	struct uphy_driver *drv = inst->drv;
> > +
> > +	if (inst->state == STATE_OFF) {
> > +		dev_err(drv->dev, "usb phy \"%s\" already off",
> > +							inst->cfg->label);
> > +		return -EINVAL;
> > +	}
> > +
> > +	inst->state = STATE_OFF;
> > +	inst->ref_cnt--;
> > +
> > +	if (inst->ref_cnt > 0)
> > +		return 0;
> > +
> > +	exynos4212_isol(inst, 1);
> > +	exynos4212_phy_pwr(inst, 0);
> > +
> > +	if (inst->cfg->id == EXYNOS4212_HSIC0) {
> > +		struct uphy_instance *device =
> > +
&drv->uphy_instances[EXYNOS4212_DEVICE];
> > +		exynos4212_isol(device, 1);
> > +		exynos4212_phy_pwr(device, 0);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +
> > +static const struct common_phy exynos4212_phys[] = {
> > +	{
> > +		.label		= "device",
> > +		.type		= PHY_DEVICE,
> > +		.id		= EXYNOS4212_DEVICE,
> > +		.rate_to_clk	= exynos4212_rate_to_clk,
> > +		.power_on	= exynos4212_power_on,
> > +		.power_off	= exynos4212_power_off,
> > +	},
> > +	{
> > +		.label		= "host",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS4212_HOST,
> > +		.rate_to_clk	= exynos4212_rate_to_clk,
> > +		.power_on	= exynos4212_power_on,
> > +		.power_off	= exynos4212_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic0",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS4212_HSIC0,
> > +		.rate_to_clk	= exynos4212_rate_to_clk,
> > +		.power_on	= exynos4212_power_on,
> > +		.power_off	= exynos4212_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic1",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS4212_HSIC1,
> > +		.rate_to_clk	= exynos4212_rate_to_clk,
> > +		.power_on	= exynos4212_power_on,
> > +		.power_off	= exynos4212_power_off,
> > +	},
> > +	{},
> > +};
> > +
> > +const struct uphy_config exynos4212_uphy_config = {
> > +	.cpu		= TYPE_EXYNOS4212,
> > +	.num_phys	= EXYNOS4212_NUM_PHYS,
> > +	.phys		= exynos4212_phys,
> > +};
> 
> I see quite an amount of similarities between the two PHY drivers. It
> would be better if we can have a single driver to handle it.

Maybe my description was confusing. This is a single driver with the option
to compile in or leave out support for particular SoCs. It is not a problem
to have all compiled in, maybe it will be clearer that way. The problem is
that
the registers are different for particular SoCs and the initialization
procedures differ a bit.
  
> 
> Thanks
> Kishon

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


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

* RE: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver
@ 2013-10-28 13:52       ` Kamil Debski
  0 siblings, 0 replies; 46+ messages in thread
From: Kamil Debski @ 2013-10-28 13:52 UTC (permalink / raw)
  To: 'Kishon Vijay Abraham I'
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-u79uwXL29TY76Z2rM5mHXA,
	kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ, Tomasz Figa,
	Sylwester Nawrocki, Marek Szyprowski,
	gautam.vivek-Sze3O3UU22JBDgjK7y7TUQ,
	mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w

Hi Kishon,

Thank you for your review! I will answer your comments below.

> From: Kishon Vijay Abraham I [mailto:kishon-l0cyMroinI0@public.gmane.org]
> Sent: Friday, October 25, 2013 5:40 PM
> 
> Hi,
> 
> On Friday 25 October 2013 07:45 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.
> >
> > Signed-off-by: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> > Signed-off-by: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> > ---
> >  .../devicetree/bindings/phy/samsung-usbphy.txt     |   51 +++
> >  drivers/phy/Kconfig                                |   21 ++
> >  drivers/phy/Makefile                               |    3 +
> >  drivers/phy/phy-exynos-usb.c                       |  245
> ++++++++++++++
> >  drivers/phy/phy-exynos-usb.h                       |   94 ++++++
> >  drivers/phy/phy-exynos4210-usb.c                   |  295
> +++++++++++++++++
> >  drivers/phy/phy-exynos4212-usb.c                   |  343
> ++++++++++++++++++++
> >  7 files changed, 1052 insertions(+)
> >  create mode 100644
> > Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> >  create mode 100644 drivers/phy/phy-exynos-usb.c  create mode 100644
> > drivers/phy/phy-exynos-usb.h  create mode 100644
> > drivers/phy/phy-exynos4210-usb.c  create mode 100644
> > drivers/phy/phy-exynos4212-usb.c
> >
> > diff --git a/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> > b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> > new file mode 100644
> > index 0000000..f112b37
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> > @@ -0,0 +1,51 @@
> > +Samsung S5P/EXYNOS SoC series USB PHY
> > +-------------------------------------------------
> > +
> > +Required properties:
> > +- compatible : should be one of the listed compatibles:
> > +	- "samsung,exynos4210-usbphy"
> > +	- "samsung,exynos4212-usbphy"
> > +- reg : a list of registers used by phy driver
> > +	- first and obligatory is the location of phy modules registers
> > +	- second and also required is the location of isolation registers
> > +	  (isolation registers control the physical connection between
> the in
> > +	  SoC modules and outside of the SoC, this also can be called
> enable
> > +	  control in the documentation of the SoC)
> > +	- third is the location of the mode switch register, this only
> applies
> > +	  to SoCs that have such a feature; mode switching enables to
> have
> > +	  both host and device used the same SoC pins and is commonly
> used
> > +	  when OTG is supported
> > +- #phy-cells : from the generic phy bindings, must be 1;
> > +
> > +The second cell in the PHY specifier identifies the PHY its meaning
> > +is SoC dependent. For the currently supported SoCs (Exynos 4210 and
> > +Exynos 4212) it is as follows:
> > +  0 - USB device,
> > +  1 - USB host,
> > +  2 - HSIC0,
> > +  3 - HSIC1,
> 
> HSIC is supposedly to be transceiver less no? You have to program
> something in the digital side?
> You have a single IP that have all these functionalities?

There is a single USB PHY controller for all the above functionalities
(i.e. host, device, hsic 0 and 1).

> > +
> > +Example:
> > +
> > +For Exynos 4412 (compatible with Exynos 4212):
> > +
> > +exynos_usbphy: exynos-usbphy@125B0000 {
> > +	compatible = "samsung,exynos4212-usbphy";
> > +	reg = <0x125B0000 0x100 0x10020704 0x0c 0x1001021c 0x4>;
> > +	ranges;
> > +	#address-cells = <1>;
> > +	#size-cells = <1>;
> 
> The above 3 properties aren't documented? Are they needed here?

My bad. I was working on two branches and corrected it in only one
of them.

> > +	clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
> > +							<&clock 2>;
> > +	clock-names = "phy", "device", "host", "hsic0", "hsic1";
> > +	status = "okay";
> > +	#phy-cells = <1>;
> > +};
> > +
> > +Then the PHY can be used in other nodes such as:
> > +
> > +ehci@12580000 {
> > +	status = "okay";
> > +	phys = <&exynos_usbphy 2>;
> > +	phy-names = "hsic0";
> > +};
> > diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index
> > 349bef2..2f7ac0a 100644
> > --- a/drivers/phy/Kconfig
> > +++ b/drivers/phy/Kconfig
> > @@ -15,4 +15,25 @@ config GENERIC_PHY
> >  	  phy users can obtain reference to the PHY. All the users of
> this
> >  	  framework should select this config.
> >
> > +config PHY_EXYNOS_USB
> > +	tristate "Samsung USB PHY driver (using the Generic PHY
> Framework)"
> Mentioning *Generic PHY Framework* is not necessary.
> *select GENERIC_PHY* here

This was added to distinguish this driver from the ols USB PHY driver.
I agree that in the final version it should be removed. I understand that
the correct way to do this is by removing the old driver when the new gets
merged. Yes?

> > +	help
> > +	  Enable this to support Samsung USB phy helper driver for
> Samsung SoCs.
> > +	  This driver provides common interface to interact, for Samsung
> > +	  USB 2.0 PHY driver.
> 
> If it's going to be used only for usb2, name it PHY_EXYNOS_USB2.

I agree.

> > +
> > +config PHY_EXYNOS4210_USB
> > +	bool "Support for Exynos 4210"
> > +	depends on PHY_EXYNOS_USB
> > +	depends on CPU_EXYNOS4210
> > +	help
> > +	  Enable USB PHY support for Exynos 4210
> > +
> > +config PHY_EXYNOS4212_USB
> > +	bool "Support for Exynos 4212"
> > +	depends on PHY_EXYNOS_USB
> > +	depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
> > +	help
> > +	  Enable USB PHY support for Exynos 4212
> 
> How difference is USB PHY in Exynos 4212 from Exynos 4210? If th
ere
> isn't much, I would suggest to use a single driver.

My idea for the driver is to have a single driver for the whole Exynos USB2
PHY. The core of the driver is in phy-exynos-usb.c and phy-exynos*-usb.c
provide registers and setup sequences for particular SoC versions.

There are a few differences between Exynos 4210, 4212, 5250 and S5PV210:
- the registers are different on each
- the setup sequence is different 
- 4212 and 5250 have a single pair of regular USB OTG DEVICE/HOST lines
- 4210 has a USB OTG and a separate USB HOST lines
- S5PV210 has both USB OTG and a separate USB HOST but lacks any HSIC
  interfaces

I agree with you that it is best to add as little code as possible.
Hence the idea to have a separate driver core and additional files for
distinctive SoCs. Enabling PHY_EXYNOS4210_USB (or other SoC specific
options)
does not add another driver it only includes support for enabled SoC.

It would be possible to skip this distinction altogether and include support
for all SoCs in the driver without config options.

> > +
> >  endmenu
> > diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index
> > 9e9560f..ca3dc82 100644
> > --- a/drivers/phy/Makefile
> > +++ b/drivers/phy/Makefile
> > @@ -3,3 +3,6 @@
> >  #
> >
> >  obj-$(CONFIG_GENERIC_PHY)	+= phy-core.o
> > +obj-$(CONFIG_PHY_EXYNOS_USB)		+= phy-exynos-usb.o
> > +obj-$(CONFIG_PHY_EXYNOS4210_USB)	+= phy-exynos4210-usb.o
> > +obj-$(CONFIG_PHY_EXYNOS4212_USB)	+= phy-exynos4212-usb.o
> > diff --git a/drivers/phy/phy-exynos-usb.c
> > b/drivers/phy/phy-exynos-usb.c new file mode 100644 index
> > 0000000..d4a26df
> > --- /dev/null
> > +++ b/drivers/phy/phy-exynos-usb.c
> 
> phy-exynos-usb2.c?

Ok.

> > @@ -0,0 +1,245 @@
> > +/*
> > + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.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-exynos-usb.h"
> > +
> > +static int exynos_uphy_power_on(struct phy *phy)
> 
> exynos_usb2_phy here and everywhere below.

Ok.

> > +{
> > +	struct uphy_instance *inst = phy_get_drvdata(phy);
> > +	struct uphy_driver *drv = inst->drv;
> > +	int ret;
> > +
> > +	dev_info(drv->dev, "Request to power_on \"%s\" usb phy\n",
> > +							inst->cfg->label);
> 
> make it dev_dbg if it's necessary.

Ok.

> > +	ret = clk_prepare_enable(drv->clk);
> > +	if (ret)
> > +		return ret;
> > +	if (inst->cfg->power_on) {
> > +		spin_lock(&drv->lock);
> > +		ret = inst->cfg->power_on(inst);
> > +		spin_unlock(&drv->lock);
> > +	}
> > +	clk_disable_unprepare(drv->clk);
> 
> hmm.. don't you need the clock to be on during the duration of the PHY
> operation?

I think that it is enough to have the clock enabled only during changes.

> > +	return ret;
> > +}
> > +
> > +static int exynos_uphy_power_off(struct phy *phy) {
> > +	struct uphy_instance *inst = phy_get_drvdata(phy);
> > +	struct uphy_driver *drv = inst->drv;
> > +	int ret;
> > +
> > +	dev_info(drv->dev, "Request to power_off \"%s\" usb phy\n",
> > +							inst->cfg->label);
> 
> dev_dbg?

Ok.

> > +	ret = clk_prepare_enable(drv->clk);
> > +	if (ret)
> > +		return ret;
> > +	if (inst->cfg->power_off) {
> > +		spin_lock(&drv->lock);
> > +		ret = inst->cfg->power_off(inst);
> > +		spin_unlock(&drv->lock);
> > +	}
> > +	clk_disable_unprepare(drv->clk);
> > +	return ret;
> > +}
> > +
> > +static struct phy_ops exynos_uphy_ops = {
> > +	.power_on	= exynos_uphy_power_on,
> > +	.power_off	= exynos_uphy_power_off,
> > +	.owner		= THIS_MODULE,
> > +};
> > +
> > +static struct phy *exynos_uphy_xlate(struct device *dev,
> > +					struct of_phandle_args *args)
> > +{
> > +	struct uphy_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->uphy_instances[args->args[0]].phy;
> > +}
> > +
> > +static const struct of_device_id exynos_uphy_of_match[];
> > +
> > +static int exynos_uphy_probe(struct platform_device *pdev) {
> > +	struct uphy_driver *drv;
> > +	struct device *dev = &pdev->dev;
> > +	struct resource *mem;
> > +	struct phy_provider *phy_provider;
> > +
> > +	const struct of_device_id *match;
> > +	const struct uphy_config *cfg;
> > +	struct clk *clk;
> > +
> > +	int i;
> > +
> > +	match = of_match_node(exynos_uphy_of_match, pdev->dev.of_node);
> > +	if (!match) {
> > +		dev_err(dev, "of_match_node() failed\n");
> > +		return -EINVAL;
> > +	}
> > +	cfg = match->data;
> > +	if (!cfg) {
> > +		dev_err(dev, "Failed to get configuration\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	drv = devm_kzalloc(dev, sizeof(struct uphy_driver) +
> > +		cfg->num_phys * sizeof(struct uphy_instance), GFP_KERNEL);
> > +
> > +	if (!drv) {
> > +		dev_err(dev, "Failed to allocate memory\n");
> > +		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);
> > +
> empty blank line.

Will fix.

> > +	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);
> > +	}
> > +
> > +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> > +	drv->reg_isol = devm_ioremap_resource(dev, mem);
> > +	if (IS_ERR(drv->reg_isol)) {
> > +		dev_err(dev, "Failed to map register memory (isolation)\n");
> > +		return PTR_ERR(drv->reg_isol);
> > +	}
> > +
> > +	switch (drv->cfg->cpu) {
> > +	case TYPE_EXYNOS4210:
> > +	case TYPE_EXYNOS4212:
> 
> Lets not add such cpu checks inside driver.

Some SoC have a special register, which switches the OTG lines between
device and host modes. I understand that it might not be the prettiest
code. I see this as a good compromise between having a single huge
driver for all Exynos SoCs and having a multiple drivers for each SoC
version. PHY IPs in these chips very are similar but have to be handled
differently. Any other ideas to solve this issue?

> > +		mem = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> > +		drv->reg_mode = devm_ioremap_resource(dev, mem);
> > +		if (IS_ERR(drv->reg_mode)) {
> > +			dev_err(dev, "Failed to map register memory (mode
> switch)\n");
> > +			return PTR_ERR(drv->reg_mode);
> > +		}
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +
> > +	phy_provider = devm_of_phy_provider_register(dev,
> > +							exynos_uphy_xlate);
> > +	if (IS_ERR(phy_provider)) {
> > +		dev_err(drv->dev, "Failed to register phy provider\n");
> > +		return PTR_ERR(phy_provider);
> > +	}
> > +
> > +	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 uphy_instance *p = &drv->uphy_instances[i];
> > +
> > +		dev_info(dev, "Creating phy \"%s\"\n", label);
> > +		p->phy = devm_phy_create(dev, &exynos_uphy_ops, NULL);
> > +		if (IS_ERR(p->phy)) {
> > +			dev_err(drv->dev, "Failed to create uphy \"%s\"\n",
> > +						label);
> > +			return PTR_ERR(p->phy);
> > +		}
> > +
> > +		p->cfg = &drv->cfg->phys[i];
> > +		p->drv = drv;
> > +		phy_set_drvdata(p->phy, p);
> > +
> > +		clk = 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->rate = clk_get_rate(clk);
> > +
> > +		if (p->cfg->rate_to_clk) {
> > +			p->clk = p->cfg->rate_to_clk(p->rate);
> > +			if (p->clk == CLKSEL_ERROR) {
> > +				dev_err(dev, "Clock rate (%ld) not
supported\n",
> > +								p->rate);
> > +				clk_put(clk);
> > +				return -EINVAL;
> > +			}
> > +		}
> > +		clk_put(clk);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +#ifdef CONFIG_PHY_EXYNOS4210_USB
> Do we really need this?

No we don't. The driver can always support all Exynos SoC versions. These
config options were added for flexibility.

> 
> > +extern const struct uphy_config exynos4210_uphy_config; #endif
> > +
> > +#ifdef CONFIG_PHY_EXYNOS4212_USB
> 
> Same here.
> > +extern const struct uphy_config exynos4212_uphy_config; #endif
> > +
> > +static const struct of_device_id exynos_uphy_of_match[] = { #ifdef
> > +CONFIG_PHY_EXYNOS4210_USB
> 
> #if not needed here.

If the #ifdef CONFIG_PHY_EXYNOS4210_USB is removed then yes. Otherwise
it is necessary - exynos4210_uphy_config may be undefined.

> > +	{
> > +		.compatible = "samsung,exynos4210-usbphy",
> > +		.data = &exynos4210_uphy_config,
> > +	},
> > +#endif
> > +#ifdef CONFIG_PHY_EXYNOS4212_USB
> 
> here too.
> > +	{
> > +		.compatible = "samsung,exynos4212-usbphy",
> > +		.data = &exynos4212_uphy_config,
> > +	},
> > +#endif
> > +	{ },
> > +};
> > +
> > +static struct platform_driver exynos_uphy_driver = {
> > +	.probe	= exynos_uphy_probe,
> > +	.driver = {
> > +		.of_match_table	= exynos_uphy_of_match,
> > +		.name		= "exynos-usbphy-new",
> "exynos-usb2-phy".
> > +		.owner		= THIS_MODULE,
> > +	}
> > +};
> > +
> > +module_platform_driver(exynos_uphy_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:exynos-uphy-new");
> > +
> > diff --git a/drivers/phy/phy-exynos-usb.h
> > b/drivers/phy/phy-exynos-usb.h new file mode 100644 index
> > 0000000..f45cb3c
> > --- /dev/null
> > +++ b/drivers/phy/phy-exynos-usb.h
> > @@ -0,0 +1,94 @@
> > +/*
> > + * Samsung S5P/EXYNOS SoC series USB 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_SAMSUNG_NEW_H
> > +#define _PHY_SAMSUNG_NEW_H
> > +
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.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>
> > +
> > +#define CLKSEL_ERROR                       -1
> > +
> > +#ifndef KHZ
> > +#define KHZ 1000
> > +#endif
> > +
> > +#ifndef MHZ
> > +#define MHZ (KHZ * KHZ)
> > +#endif
> > +
> > +enum phy_type {
> > +	PHY_DEVICE,
> > +	PHY_HOST,
> > +};
> > +
> > +enum samsung_cpu_type {
> > +	TYPE_S3C64XX,
> > +	TYPE_EXYNOS4210,
> > +	TYPE_EXYNOS4212,
> 
> No *cpu_type* inside driver files.

I guess that you are in favor a "a separate driver for each phy version".
For me it can be both. But we have to discuss which apporach is better:
1) separate driver for each phy version - no iffs and significant code
   duplication
2) a single driver driver supporting all Exynos variants - it needs ifs,
   code is always bigger
3) a single driver with support for particular SoC enabled in the config
file
   - with ifs, but the driver can be compiled smaller

> > +};
> > +
> > +enum uphy_state {
> > +	STATE_OFF,
> > +	STATE_ON,
> > +};
> > +
> > +struct uphy_driver;
> > +struct uphy_instance;
> > +struct uphy_config;
> > +
> > +struct uphy_instance {
> > +	struct uphy_driver *drv;
> > +	struct phy *phy;
> > +	const struct common_phy *cfg;
> > +	enum uphy_state state;
> > +	int ref_cnt;
> > +	u32 clk;
> > +	unsigned long rate;
> > +};
> > +
> > +struct uphy_driver {
> > +	struct device *dev;
> > +	spinlock_t lock;
> > +	void __iomem *reg_phy;
> > +	void __iomem *reg_isol;
> > +	void __iomem *reg_mode;
> > +	const struct uphy_config *cfg;
> > +	struct clk *clk;
> > +	struct uphy_instance uphy_instances[0];
> 
> you might have more than one phy instance right?

Yes. The USB PHY IP controls USB device, host and hsics.

> > +};
> > +
> > +struct common_phy {
> > +	char *label;
> > +	enum phy_type type;
> > +	unsigned int id;
> > +	u32 (*rate_to_clk)(unsigned long);
> > +	int (*power_on)(struct uphy_instance*);
> > +	int (*power_off)(struct uphy_instance*); };
> > +
> > +
> > +struct uphy_config {
> > +	enum samsung_cpu_type cpu;
> > +	int num_phys;
> > +	const struct common_phy *phys;
> > +};
> > +
> > +#endif
> > +
> > diff --git a/drivers/phy/phy-exynos4210-usb.c
> > b/drivers/phy/phy-exynos4210-usb.c
> > new file mode 100644
> > index 0000000..6cf74f7
> > --- /dev/null
> > +++ b/drivers/phy/phy-exynos4210-usb.c
> > @@ -0,0 +1,295 @@
> > +/*
> > + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.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-exynos-usb.h"
> > +
> > +/* Exynos USB PHY registers */
> > +
> > +/* PHY power control */
> > +#define EXYNOS_4210_UPHYPWR			0x0
> > +
> > +#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND	(1 << 0)
> > +#define EXYNOS_4210_UPHYPWR_PHY0_PWR		(1 << 3)
> > +#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR	(1 << 4)
> > +#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP		(1 << 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	(1 << 6)
> > +#define EXYNOS_4210_UPHYPWR_PHY1_PWR		(1 << 7)
> > +#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP		(1 << 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_HSCI0_SUSPEND	(1 << 9)
> > +#define EXYNOS_4210_UPHYPWR_HSCI0_SLEEP		(1 << 10)
> > +#define EXYNOS_4210_UPHYPWR_HSCI0 ( \
> > +	EXYNOS_4210_UPHYPWR_HSCI0_SUSPEND | \
> > +	EXYNOS_4210_UPHYPWR_HSCI0_SLEEP)
> > +
> > +#define EXYNOS_4210_UPHYPWR_HSCI1_SUSPEND	(1 << 11)
> > +#define EXYNOS_4210_UPHYPWR_HSCI1_SLEEP		(1 << 12)
> > +#define EXYNOS_4210_UPHYPWR_HSCI1 ( \
> > +	EXYNOS_4210_UPHYPWR_HSCI1_SUSPEND | \
> > +	EXYNOS_4210_UPHYPWR_HSCI1_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	(0x1 << 2)
> > +#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON	(0x1 << 4)
> > +#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON	(0x1 << 7)
> > +
> > +/* PHY reset control */
> > +#define EXYNOS_4210_UPHYRST			0x8
> > +
> > +#define EXYNOS_4210_URSTCON_PHY0		(1 << 0)
> > +#define EXYNOS_4210_URSTCON_OTG_HLINK		(1 << 1)
> > +#define EXYNOS_4210_URSTCON_OTG_PHYLINK		(1 << 2)
> > +#define EXYNOS_4210_URSTCON_PHY1_ALL		(1 << 3)
> > +#define EXYNOS_4210_URSTCON_PHY1_P0		(1 << 4)
> > +#define EXYNOS_4210_URSTCON_PHY1_P1P2		(1 << 5)
> > +#define EXYNOS_4210_URSTCON_HOST_LINK_ALL	(1 << 6)
> > +#define EXYNOS_4210_URSTCON_HOST_LINK_P0	(1 << 7)
> > +#define EXYNOS_4210_URSTCON_HOST_LINK_P1	(1 << 8)
> > +#define EXYNOS_4210_URSTCON_HOST_LINK_P2	(1 << 9)But
> > +
> > +/* Isolation, configured in the power management unit */
> > +#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET	0x0
> > +#define EXYNOS_4210_USB_ISOL_DEVICE		(1 << 0)
> > +#define EXYNOS_4210_USB_ISOL_HOST_OFFSET	0x4
> > +#define EXYNOS_4210_USB_ISOL_HOST		(1 << 0)
> > +
> > +/* USBYPHY1 Floating prevention */
> > +#define EXYNOS_4210_UPHY1CON			0x34
> > +#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION	0x1
> > +
> > +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. */
> 
> use proper commenting styles Documentation/CodingStyle

Ok, I will.

> > +static u32 exynos4210_rate_to_clk(unsigned long rate) {
> > +	unsigned int clksel;
> > +
> > +	switch (rate) {
> > +	case 12 * MHZ:
> > +		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
> > +		break;
> > +	case 24 * MHZ:
> > +		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
> > +		break;
> > +	case 48 * MHZ:
> > +		clksel = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
> > +		break;
> > +	default:
> > +		clksel = CLKSEL_ERROR;
> > +	}
> > +
> > +	return clksel;
> > +}
> > +
> > +static void exynos4210_isol(struct uphy_instance *inst, bool on) {
> > +	struct uphy_driver *drv = inst->drv;
> > +	u32 offset;
> > +	u32 mask;
> > +	u32 tmp;
> > +
> > +	if (!drv->reg_isol)
> > +		return;
> > +
> > +	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;
> > +	};
> > +
> > +	tmp = readl(drv->reg_isol + offset);
> > +	if (on)
> > +		tmp &= ~mask;
> > +	else
> > +		tmp |= mask;
> > +	writel(tmp, drv->reg_isol + offset); }
> > +
> > +static void exynos4210_phy_pwr(struct uphy_instance *inst, bool on)
> {
> > +	struct uphy_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_HSCI0;
> > +		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
> > +				EXYNOS_4210_URSTCON_HOST_LINK_P1;
> > +		break;
> > +	case EXYNOS4210_HSIC1:
> > +		phypwr =	EXYNOS_4210_UPHYPWR_HSCI1;
> > +		rstbits =	EXYNOS_4210_URSTCON_PHY1_P1P2 |
> > +				EXYNOS_4210_URSTCON_HOST_LINK_P2;
> > +		break;
> > +	};
> > +
> > +	if (on) {
> > +		writel(inst->clk, 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);
> 
> usleep_range?

Maybe. The above code is based on the drivers/usb/phy/phy-samsung-usb2.c.
My documentation does not specify the acceptable delay range.

> > +		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 uphy_instance *inst) {
> > +	struct uphy_driver *drv = inst->drv;
> > +
> > +	if (inst->state == STATE_ON) {
> > +		dev_err(drv->dev, "usb phy \"%s\" already on",
> > +							inst->cfg->label);
> > +		return -ENODEV;
> > +	}
> > +	inst->state = STATE_ON;
> > +	inst->ref_cnt++;
> > +	if (inst->ref_cnt > 1)
> > +		return 0;
> 
> reference counting shouldn't be needed. It's been handled by the PHY
> framework.

I will try how it works without this. There was some code that was removed
and
used the reference counter. I had some trouble figuring out the correct
setup
sequence, when multiple phys were enabled (e.g. only host, host + device,
only
device).

> > +
> > +	/* 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 uphy_instance *inst) {
> > +	struct uphy_driver *drv = inst->drv;
> > +
> > +	if (inst->state == STATE_OFF) {
> > +		dev_err(drv->dev, "usb phy \"%s\" already off",
> > +							inst->cfg->label);
> > +		return -EINVAL;
> > +	}
> > +
> > +	inst->state = STATE_OFF;
> > +	inst->ref_cnt--;
> > +	if (inst->ref_cnt > 0)
> > +		return 0;
> 
> same here.
> > +
> > +	exynos4210_isol(inst, 1);
> > +	exynos4210_phy_pwr(inst, 0);
> > +
> > +	return 0;
> > +}
> > +
> > +
> > +static const struct common_phy exynos4210_phys[] = {
> > +	{
> > +		.label		= "device",
> > +		.type		= PHY_DEVICE,
> > +		.id		= EXYNOS4210_DEVICE,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "host",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS4210_HOST,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic0",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS4210_HSIC0,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic1",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS4210_HSIC1,
> > +		.rate_to_clk	= exynos4210_rate_to_clk,
> > +		.power_on	= exynos4210_power_on,
> > +		.power_off	= exynos4210_power_off,
> > +	},
> > +	{},
> > +};
> > +
> > +const struct uphy_config exynos4210_uphy_config = {
> > +	.cpu		= TYPE_EXYNOS4210,
> > +	.num_phys	= EXYNOS4210_NUM_PHYS,
> > +	.phys		= exynos4210_phys,
> > +};
> > +
> > diff --git a/drivers/phy/phy-exynos4212-usb.c
> > b/drivers/phy/phy-exynos4212-usb.c
> > new file mode 100644
> > index 0000000..c07ae8e
> > --- /dev/null
> > +++ b/drivers/phy/phy-exynos4212-usb.c
> > @@ -0,0 +1,343 @@
> > +/*
> > + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.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-exynos-usb.h"
> > +
> > +/* Exynos USB PHY registers */
> > +
> > +/* PHY power control */
> > +#define EXYNOS_4212_UPHYPWR			0x0
> > +
> > +#define EXYNOS_4212_UPHYPWR_DEV_SUSPEND		(1 << 0)
> > +#define EXYNOS_4212_UPHYPWR_DEV_PWR		(1 << 3)
> > +#define EXYNOS_4212_UPHYPWR_DEV_OTG_PWR		(1 << 4)
> > +#define EXYNOS_4212_UPHYPWR_DEV_SLEEP		(1 << 5)
> > +#define EXYNOS_4212_UPHYPWR_DEV	( \
> > +	EXYNOS_4212_UPHYPWR_DEV_SUSPEND | \
> > +	EXYNOS_4212_UPHYPWR_DEV_PWR | \
> > +	EXYNOS_4212_UPHYPWR_DEV_OTG_PWR | \
> > +	EXYNOS_4212_UPHYPWR_DEV_SLEEP)
> > +
> > +#define EXYNOS_4212_UPHYPWR_HOST_SUSPEND	(1 << 6)
> > +#define EXYNOS_4212_UPHYPWR_HOST_PWR		(1 << 7)
> > +#define EXYNOS_4212_UPHYPWR_HOST_SLEEP		(1 << 8)
> > +#define EXYNOS_4212_UPHYPWR_HOST ( \
> > +	EXYNOS_4212_UPHYPWR_HOST_SUSPEND | \
> > +	EXYNOS_4212_UPHYPWR_HOST_PWR | \
> > +	EXYNOS_4212_UPHYPWR_HOST_SLEEP)
> > +
> > +#define EXYNOS_4212_UPHYPWR_HSCI0_SUSPEND	(1 << 9)
> > +#define EXYNOS_4212_UPHYPWR_HSCI0_PWR		(1 << 10)
> > +#define EXYNOS_4212_UPHYPWR_HSCI0_SLEEP		(1 << 11)
> > +#define EXYNOS_4212_UPHYPWR_HSCI0 ( \
> > +	EXYNOS_4212_UPHYPWR_HSCI0_SUSPEND | \
> > +	EXYNOS_4212_UPHYPWR_HSCI0_PWR | \
> > +	EXYNOS_4212_UPHYPWR_HSCI0_SLEEP)
> > +
> > +#define EXYNOS_4212_UPHYPWR_HSCI1_SUSPEND	(1 << 12)
> > +#define EXYNOS_4212_UPHYPWR_HSCI1_PWR		(1 << 13)
> > +#define EXYNOS_4212_UPHYPWR_HSCI1_SLEEP		(1 << 14)
> > +#define EXYNOS_4212_UPHYPWR_HSCI1 ( \
> > +	EXYNOS_4212_UPHYPWR_HSCI1_SUSPEND | \
> > +	EXYNOS_4212_UPHYPWR_HSCI1_PWR | \
> > +	EXYNOS_4212_UPHYPWR_HSCI1_SLEEP)
> > +
> > +/* PHY clock control */
> > +#define EXYNOS_4212_UPHYCLK			0x4
> > +
> > +#define EXYNOS_4212_UPHYCLK_PHYFSEL_MASK	(0x7 << 0)
> > +#define EXYNOS_4212_UPHYCLK_PHYFSEL_9MHZ6	(0x0 << 0)
> > +#define EXYNOS_4212_UPHYCLK_PHYFSEL_10MHZ	(0x1 << 0)
> > +#define EXYNOS_4212_UPHYCLK_PHYFSEL_12MHZ	(0x2 << 0)
> > +#define EXYNOS_4212_UPHYCLK_PHYFSEL_19MHZ2	(0x3 << 0)
> > +#define EXYNOS_4212_UPHYCLK_PHYFSEL_20MHZ	(0x4 << 0)
> > +#define EXYNOS_4212_UPHYCLK_PHYFSEL_24MHZ	(0x5 << 0)
> > +#define EXYNOS_4212_UPHYCLK_PHYFSEL_50MHZ	(0x7 << 0)
> > +
> > +#define EXYNOS_4212_UPHYCLK_PHY0_ID_PULLUP	(0x1 << 3)
> > +#define EXYNOS_4212_UPHYCLK_PHY0_COMMON_ON	(0x1 << 4)
> > +#define EXYNOS_4212_UPHYCLK_PHY1_COMMON_ON	(0x1 << 7)
> > +
> > +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_MASK	(0x7f << 10)
> > +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_12MHZ	(0x24 << 10)
> > +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_15MHZ	(0x1c << 10)
> > +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_16MHZ	(0x1a << 10)
> > +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_19MHZ2	(0x15 << 10)
> > +#define EXYNOS_4212_UPHYCLK_HSIC_REFCLK_20MHZ	(0x14 << 10)
> > +
> > +/* PHY reset control */
> > +#define EXYNOS_4212_UPHYRST			0x8
> > +
> > +#define EXYNOS_4212_URSTCON_DEVICE		(1 << 0)
> > +#define EXYNOS_4212_URSTCON_OTG_HLINK		(1 << 1)
> > +#define EXYNOS_4212_URSTCON_OTG_PHYLINK		(1 << 2)
> > +#define EXYNOS_4212_URSTCON_HOST_PHY		(1 << 3)
> > +#define EXYNOS_4212_URSTCON_PHY1		(1 << 4)
> > +#define EXYNOS_4212_URSTCON_HSIC0		(1 << 5)
> > +#define EXYNOS_4212_URSTCON_HSIC1		(1 << 6)
> > +#define EXYNOS_4212_URSTCON_HOST_LINK_ALL	(1 << 7)
> > +#define EXYNOS_4212_URSTCON_HOST_LINK_P0	(1 << 8)
> > +#define EXYNOS_4212_URSTCON_HOST_LINK_P1	(1 << 9)
> > +#define EXYNOS_4212_URSTCON_HOST_LINK_P2	(1 << 10)
> > +
> > +/* Isolation, configured in the power management unit */
> > +#define EXYNOS_4212_USB_ISOL_OFFSET		0x0
> > +#define EXYNOS_4212_USB_ISOL_OTG		(1 << 0)
> > +#define EXYNOS_4212_USB_ISOL_HSIC0_OFFSET	0x4
> > +#define EXYNOS_4212_USB_ISOL_HSIC0		(1 << 0)
> > +#define EXYNOS_4212_USB_ISOL_HSIC1_OFFSET	0x8
> > +#define EXYNOS_4212_USB_ISOL_HSIC1		(1 << 0)
> > +
> > +enum exynos4x12_phy_id {
> > +	EXYNOS4212_DEVICE,
> > +	EXYNOS4212_HOST,
> > +	EXYNOS4212_HSIC0,
> > +	EXYNOS4212_HSIC1,
> > +	EXYNOS4212_NUM_PHYS,
> > +};
> > +
> > +/* exynos4212_rate_to_clk() converts the supplied clock rate to the
> > +value that
> > + * can be written to the phy register. */ static u32
> > +exynos4212_rate_to_clk(unsigned long rate) {
> > +	unsigned int clksel;
> > +
> > +	/* EXYNOS_4212_UPHYCLK_PHYFSEL_MASK */
> > +
> > +	switch (rate) {
> > +	case 9600 * KHZ:
> > +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_9MHZ6;
> > +		break;
> > +	case 10 * MHZ:
> > +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_10MHZ;
> > +		break;
> > +	case 12 * MHZ:
> > +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_12MHZ;
> > +		break;
> > +	case 19200 * KHZ:
> > +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_19MHZ2;
> > +		break;
> > +	case 20 * MHZ:
> > +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_20MHZ;
> > +		break;
> > +	case 24 * MHZ:
> > +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_24MHZ;
> > +		break;
> > +	case 50 * MHZ:
> > +		clksel = EXYNOS_4212_UPHYCLK_PHYFSEL_50MHZ;
> > +		break;
> > +	default:
> > +		clksel = CLKSEL_ERROR;
> > +	}
> > +
> > +	return clksel;
> > +}
> > +
> > +static void exynos4212_isol(struct uphy_instance *inst, bool on) {
> > +	struct uphy_driver *drv = inst->drv;
> > +	u32 offset;
> > +	u32 mask;
> > +	u32 tmp;
> > +
> > +	if (!drv->reg_isol)
> > +		return;
> > +
> > +	switch (inst->cfg->id) {
> > +	case EXYNOS4212_DEVICE:
> > +		offset = EXYNOS_4212_USB_ISOL_OFFSET;
> > +		mask = EXYNOS_4212_USB_ISOL_OTG;
> > +		break;
> > +	case EXYNOS4212_HOST:
> > +		offset = EXYNOS_4212_USB_ISOL_OFFSET;
> > +		mask = EXYNOS_4212_USB_ISOL_OTG;
> > +		break;
> > +	case EXYNOS4212_HSIC0:
> > +		offset = EXYNOS_4212_USB_ISOL_HSIC0_OFFSET;
> > +		mask = EXYNOS_4212_USB_ISOL_HSIC0;
> > +		break;
> > +	case EXYNOS4212_HSIC1:
> > +		offset = EXYNOS_4212_USB_ISOL_HSIC1_OFFSET;
> > +		mask = EXYNOS_4212_USB_ISOL_HSIC1;
> > +		break;
> > +	default:
> > +		return;
> > +	};
> > +
> > +	tmp = readl(drv->reg_isol + offset);
> > +	if (on)
> > +		tmp &= ~mask;
> > +	else
> > +		tmp |= mask;
> > +	writel(tmp, drv->reg_isol + offset); }
> > +
> > +static void exynos4212_phy_pwr(struct uphy_instance *inst, bool on)
> {
> > +	struct uphy_driver *drv = inst->drv;
> > +	u32 rstbits = 0;
> > +	u32 phypwr = 0;
> > +	u32 rst;
> > +	u32 pwr;
> > +
> > +	switch (inst->cfg->id) {
> > +	case EXYNOS4212_DEVICE:
> > +		phypwr =	EXYNOS_4212_UPHYPWR_DEV;
> > +		rstbits =	EXYNOS_4212_URSTCON_DEVICE;
> > +		break;
> > +	case EXYNOS4212_HOST:
> > +		phypwr =	EXYNOS_4212_UPHYPWR_HOST;
> > +		rstbits =	EXYNOS_4212_URSTCON_HOST_PHY;
> > +		break;
> > +	case EXYNOS4212_HSIC0:
> > +		phypwr =	EXYNOS_4212_UPHYPWR_HSCI0;
> > +		rstbits =	EXYNOS_4212_URSTCON_HSIC1 |
> > +				EXYNOS_4212_URSTCON_HOST_LINK_P0 |
> > +				EXYNOS_4212_URSTCON_HOST_PHY;
> > +		break;
> > +	case EXYNOS4212_HSIC1:
> > +		phypwr =	EXYNOS_4212_UPHYPWR_HSCI1;
> > +		rstbits =	EXYNOS_4212_URSTCON_HSIC1 |
> > +				EXYNOS_4212_URSTCON_HOST_LINK_P1;
> > +		break;
> > +	};
> > +
> > +	if (on) {
> > +		writel(inst->clk, drv->reg_phy + EXYNOS_4212_UPHYCLK);
> > +
> > +		pwr = readl(drv->reg_phy + EXYNOS_4212_UPHYPWR);
> > +		pwr &= ~phypwr;
> > +		writel(pwr, drv->reg_phy + EXYNOS_4212_UPHYPWR);
> > +
> > +		rst = readl(drv->reg_phy + EXYNOS_4212_UPHYRST);
> > +		rst |= rstbits;
> > +		writel(rst, drv->reg_phy + EXYNOS_4212_UPHYRST);
> > +		udelay(10);
> > +		rst &= ~rstbits;
> > +		writel(rst, drv->reg_phy + EXYNOS_4212_UPHYRST);
> > +	} else {
> > +		pwr = readl(drv->reg_phy + EXYNOS_4212_UPHYPWR);
> > +		pwr |= phypwr;
> > +		writel(pwr, drv->reg_phy + EXYNOS_4212_UPHYPWR);
> > +	}
> > +}
> > +
> > +static int exynos4212_power_on(struct uphy_instance *inst) {
> > +	struct uphy_driver *drv = inst->drv;
> > +
> > +	if (inst->state == STATE_ON) {
> > +		dev_err(drv->dev, "usb phy \"%s\" already on",
> > +							inst->cfg->label);
> > +		return -ENODEV;
> > +	}
> > +
> > +	inst->state = STATE_ON;
> > +	inst->ref_cnt++;
> > +	if (inst->ref_cnt > 1)
> > +		return 0;
> > +
> > +	exynos4212_phy_pwr(inst, 1);
> > +	exynos4212_isol(inst, 0);
> > +
> > +	/* Power on the device, as it is necessary for HSIC to work */
> > +	if (inst->cfg->id == EXYNOS4212_HSIC0) {
> > +		struct uphy_instance *device =
> > +
&drv->uphy_instances[EXYNOS4212_DEVICE];
> > +		exynos4212_phy_pwr(device, 1);
> > +		exynos4212_isol(device, 0);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int exynos4212_power_off(struct uphy_instance *inst) {
> > +	struct uphy_driver *drv = inst->drv;
> > +
> > +	if (inst->state == STATE_OFF) {
> > +		dev_err(drv->dev, "usb phy \"%s\" already off",
> > +							inst->cfg->label);
> > +		return -EINVAL;
> > +	}
> > +
> > +	inst->state = STATE_OFF;
> > +	inst->ref_cnt--;
> > +
> > +	if (inst->ref_cnt > 0)
> > +		return 0;
> > +
> > +	exynos4212_isol(inst, 1);
> > +	exynos4212_phy_pwr(inst, 0);
> > +
> > +	if (inst->cfg->id == EXYNOS4212_HSIC0) {
> > +		struct uphy_instance *device =
> > +
&drv->uphy_instances[EXYNOS4212_DEVICE];
> > +		exynos4212_isol(device, 1);
> > +		exynos4212_phy_pwr(device, 0);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +
> > +static const struct common_phy exynos4212_phys[] = {
> > +	{
> > +		.label		= "device",
> > +		.type		= PHY_DEVICE,
> > +		.id		= EXYNOS4212_DEVICE,
> > +		.rate_to_clk	= exynos4212_rate_to_clk,
> > +		.power_on	= exynos4212_power_on,
> > +		.power_off	= exynos4212_power_off,
> > +	},
> > +	{
> > +		.label		= "host",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS4212_HOST,
> > +		.rate_to_clk	= exynos4212_rate_to_clk,
> > +		.power_on	= exynos4212_power_on,
> > +		.power_off	= exynos4212_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic0",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS4212_HSIC0,
> > +		.rate_to_clk	= exynos4212_rate_to_clk,
> > +		.power_on	= exynos4212_power_on,
> > +		.power_off	= exynos4212_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic1",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS4212_HSIC1,
> > +		.rate_to_clk	= exynos4212_rate_to_clk,
> > +		.power_on	= exynos4212_power_on,
> > +		.power_off	= exynos4212_power_off,
> > +	},
> > +	{},
> > +};
> > +
> > +const struct uphy_config exynos4212_uphy_config = {
> > +	.cpu		= TYPE_EXYNOS4212,
> > +	.num_phys	= EXYNOS4212_NUM_PHYS,
> > +	.phys		= exynos4212_phys,
> > +};
> 
> I see quite an amount of similarities between the two PHY drivers. It
> would be better if we can have a single driver to handle it.

Maybe my description was confusing. This is a single driver with the option
to compile in or leave out support for particular SoCs. It is not a problem
to have all compiled in, maybe it will be clearer that way. The problem is
that
the registers are different for particular SoCs and the initialization
procedures differ a bit.
  
> 
> Thanks
> Kishon

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

--
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] 46+ messages in thread

* RE: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver
@ 2013-10-28 13:52       ` Kamil Debski
  0 siblings, 0 replies; 46+ messages in thread
From: Kamil Debski @ 2013-10-28 13:52 UTC (permalink / raw)
  To: 'Kishon Vijay Abraham I'
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, kyungmin.park, Tomasz Figa, Sylwester Nawrocki,
	Marek Szyprowski, gautam.vivek, mat.krawczuk

Hi Kishon,

> From: Kishon Vijay Abraham I [mailto:kishon@ti.com]
> Sent: Friday, October 25, 2013 5:44 PM
> 
> Hi,
> 
> On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
> > Add support for Exynos 5250. This is work-in-progress commit. Not for
> > merging.
> >
> > Signed-off-by: Kamil Debski <k.debski@samsung.com>
> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> > ---
> >  drivers/phy/Kconfig              |    7 +
> >  drivers/phy/Makefile             |    1 +
> >  drivers/phy/phy-exynos-usb.c     |   10 +
> >  drivers/phy/phy-exynos-usb.h     |    1 +
> >  drivers/phy/phy-exynos5250-usb.c |  411
> > ++++++++++++++++++++++++++++++++++++++
> >  5 files changed, 430 insertions(+)
> >  create mode 100644 drivers/phy/phy-exynos5250-usb.c
> >
> > diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index
> > 2f7ac0a..0f598d0 100644
> > --- a/drivers/phy/Kconfig
> > +++ b/drivers/phy/Kconfig
> > @@ -36,4 +36,11 @@ config PHY_EXYNOS4212_USB
> >  	help
> >  	  Enable USB PHY support for Exynos 4212
> >
> > +config PHY_EXYNOS5250_USB
> > +	bool "Support for Exynos 5250"
> > +	depends on PHY_EXYNOS_USB
> 
> This should be a separate driver. Not necessary to use PHY_EXYNOS_USB.
> > +	depends on SOC_EXYNOS5250
> > +	help
> > +	  Enable USB PHY support for Exynos 5250
> > +
> >  endmenu
> > diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index
> > ca3dc82..0dff0dd 100644
> > --- a/drivers/phy/Makefile
> > +++ b/drivers/phy/Makefile
> > @@ -6,3 +6,4 @@ obj-$(CONFIG_GENERIC_PHY)	+= phy-core.o
> >  obj-$(CONFIG_PHY_EXYNOS_USB)		+= phy-exynos-usb.o
> >  obj-$(CONFIG_PHY_EXYNOS4210_USB)	+= phy-exynos4210-usb.o
> >  obj-$(CONFIG_PHY_EXYNOS4212_USB)	+= phy-exynos4212-usb.o
> > +obj-$(CONFIG_PHY_EXYNOS5250_USB)	+= phy-exynos5250-usb.o
> > diff --git a/drivers/phy/phy-exynos-usb.c
> > b/drivers/phy/phy-exynos-usb.c index d4a26df..172b774 100644
> > --- a/drivers/phy/phy-exynos-usb.c
> > +++ b/drivers/phy/phy-exynos-usb.c
> > @@ -212,6 +212,10 @@ extern const struct uphy_config
> > exynos4210_uphy_config;  extern const struct uphy_config
> > exynos4212_uphy_config;  #endif
> >
> > +#ifdef CONFIG_PHY_EXYNOS5250_USB
> > +extern const struct uphy_config exynos5250_uphy_config; #endif
> > +
> >  static const struct of_device_id exynos_uphy_of_match[] = {  #ifdef
> > CONFIG_PHY_EXYNOS4210_USB
> >  	{
> > @@ -225,6 +229,12 @@ static const struct of_device_id
> exynos_uphy_of_match[] = {
> >  		.data = &exynos4212_uphy_config,
> >  	},
> >  #endif
> > +#ifdef CONFIG_PHY_EXYNOS5250_USB
> > +	{
> > +		.compatible = "samsung,exynos5250-usbphy",
> > +		.data = &exynos5250_uphy_config,
> > +	},
> > +#endif
> >  	{ },
> >  };
> >
> > diff --git a/drivers/phy/phy-exynos-usb.h
> > b/drivers/phy/phy-exynos-usb.h index f45cb3c..a9febfa 100644
> > --- a/drivers/phy/phy-exynos-usb.h
> > +++ b/drivers/phy/phy-exynos-usb.h
> > @@ -42,6 +42,7 @@ enum samsung_cpu_type {
> >  	TYPE_S3C64XX,
> >  	TYPE_EXYNOS4210,
> >  	TYPE_EXYNOS4212,
> > +	TYPE_EXYNOS5250,
> 
> No cpu types here.
> >  };
> >
> >  enum uphy_state {
> > diff --git a/drivers/phy/phy-exynos5250-usb.c
> > b/drivers/phy/phy-exynos5250-usb.c
> > new file mode 100644
> > index 0000000..156093b
> > --- /dev/null
> > +++ b/drivers/phy/phy-exynos5250-usb.c
> > @@ -0,0 +1,411 @@
> > +/*
> > + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.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-exynos-usb.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		(0x1 << 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		(0x1 << 11)
> > +#define EXYNOS_5250_HOSTPHYCTRL0_RETENABLE		(0x1 << 10)
> > +#define EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N		(0x1 << 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			(0x1 << 6)
> > +#define EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP		(0x1 << 5)
> > +#define EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND		(0x1 << 4)
> > +#define EXYNOS_5250_HOSTPHYCTRL0_WORDINTERFACE		(0x1 << 3)
> > +#define EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST		(0x1 << 2)
> > +#define EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST		(0x1 << 1)
> > +#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST		(0x1 << 0)
> > +
> > +/* HSIC0 & HSCI1 */
> > +#define EXYNOS_5250_HOSTPHYCTRL1			0x10
> > +#define EXYNOS_5250_HOSTPHYCTRL2			0x20
> > +
> > +#define EXYNOS_5250_HOSTPHYCTRLX_REFCLKSEL_MASK		(0x3 << 23)
> > +#define EXYNOS_5250_HOSTPHYCTRLX_REFCLKDIV_MASK		(0x7f << 16)
> > +#define EXYNOS_5250_HOSTPHYCTRLX_SIDDQ			(0x1 << 6)
> > +#define EXYNOS_5250_HOSTPHYCTRLX_FORCESLEEP		(0x1 << 5)
> > +#define EXYNOS_5250_HOSTPHYCTRLX_FORCESUSPEND		(0x1 << 4)
> > +#define EXYNOS_5250_HOSTPHYCTRLX_WORDINTERFACE		(0x1 << 3)
> > +#define EXYNOS_5250_HOSTPHYCTRLX_UTMISWRST		(0x1 << 2)
> > +#define EXYNOS_5250_HOSTPHYCTRLX_PHYSWRST		(0x1 << 0)
> > +
> > +/* EHCI control */
> > +#define EXYNOS_5250_HOSTEHCICTRL			0x30
> > +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN		(0x1 << 29)
> > +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR4		(0x1 << 28)
> > +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR8		(0x1 << 27)
> > +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR16		(0x1 << 26)
> > +#define EXYNOS_5250_HOSTEHCICTRL_AUTOPPDONOVRCUREN	(0x1 << 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		(0x1 << 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		(0x1 << 0)
> > +
> > +/* USBOTG */
> > +#define EXYNOS_5250_USBOTGSYS				0x38
> > +#define EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET		(0x1 << 14)
> > +#define EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG		(0x1 << 13)
> > +#define EXYNOS_5250_USBOTGSYS_PHY_SW_RST		(0x1 << 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			(0x1 << 8)
> > +#define EXYNOS_5250_USBOTGSYS_COMMON_ON			(0x1 << 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		(0x1 << 3)
> > +#define EXYNOS_5250_USBOTGSYS_OTGDISABLE		(0x1 << 2)
> > +#define EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG		(0x1 << 1)
> > +#define EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND		(0x1 << 0)
> > +
> > +/* Isolation, configured in the power management unit */
> > +#define EXYNOS_5250_USB_ISOL_OTG_OFFSET		0x0
> > +#define EXYNOS_5250_USB_ISOL_OTG		(1 << 0)
> > +#define EXYNOS_5250_USB_ISOL_HOST_OFFSET	0x4
> > +#define EXYNOS_5250_USB_ISOL_HOST		(1 << 0)
> > +
> > +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. */
> 
> Follow coding guidelines ;-)
> > +static u32 exynos5250_rate_to_clk(unsigned long rate) {
> > +	unsigned int clksel;
> > +
> > +	/* EXYNOS_5250_FSEL_MASK */
> > +
> > +	switch (rate) {
> > +	case 9600 * KHZ:
> > +		clksel = EXYNOS_5250_FSEL_9MHZ6;
> > +		break;
> > +	case 10 * MHZ:
> > +		clksel = EXYNOS_5250_FSEL_10MHZ;
> > +		break;
> > +	case 12 * MHZ:
> > +		clksel = EXYNOS_5250_FSEL_12MHZ;
> > +		break;
> > +	case 19200 * KHZ:
> > +		clksel = EXYNOS_5250_FSEL_19MHZ2;
> > +		break;
> > +	case 20 * MHZ:
> > +		clksel = EXYNOS_5250_FSEL_20MHZ;
> > +		break;
> > +	case 24 * MHZ:
> > +		clksel = EXYNOS_5250_FSEL_24MHZ;
> > +		break;
> > +	case 50 * MHZ:
> > +		clksel = EXYNOS_5250_FSEL_50MHZ;
> > +		break;
> > +	default:
> > +		clksel = CLKSEL_ERROR;
> > +	}
> > +
> > +	return clksel;
> > +}
> > +
> > +static void exynos5250_isol(struct uphy_instance *inst, bool on) {
> > +	struct uphy_driver *drv = inst->drv;
> > +	u32 offset;
> > +	u32 mask;
> > +	u32 tmp;
> > +
> > +	if (!drv->reg_isol)
> > +		return;
> > +
> > +	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;
> > +	};
> > +
> > +	tmp = readl(drv->reg_isol + offset);
> > +	if (on)
> > +		tmp &= ~mask;
> > +	else
> > +		tmp |= mask;
> > +	writel(tmp, drv->reg_isol + offset); }
> > +
> > +static void exynos5250_phy_pwr(struct uphy_instance *inst, bool on)
> {
> > +	struct uphy_driver *drv = inst->drv;
> > +	u32 ctrl0;
> > +	u32 otg;
> > +	u32 ehci;
> > +	u32 ohci;
> > +
> > +	switch (inst->cfg->id) {
> > +	case EXYNOS5250_DEVICE:
> > +		writel(0, drv->reg_mode);
> > +
> > +		/* OTG configuration */
> > +		otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
> > +		/* The clock */
> > +		otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
> > +		otg |= inst->clk << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
> > +		/* Reset */
> > +		otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
> > +			EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
> > +			EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
> > +		/* TODO: Clear 4 bits as the old driver does. */
> > +		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);
> > +
> > +
> > +		break;
> > +	case EXYNOS5250_HOST:
> > +		/* Host registers configuration */
> > +		ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
> > +		/* The clock */
> > +		ctrl0 &= ~EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK;
> > +		ctrl0 |= inst->clk << 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 << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
> > +		/* Reset */
> > +		otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
> > +			EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
> > +			EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
> > +		/* TODO: Clear 4 bits as the old driver does. */
> > +		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);
> > +
> > +		/* 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);
> > +		/* Let's do some undocumented magic (based on the old
> driver) */
> > +		ohci |=	0x1 << 3;
> > +		writel(ohci, drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
> > +
> > +		break;
> > +	}
> > +}
> > +
> > +static int exynos5250_power_on(struct uphy_instance *inst) {
> > +	struct uphy_driver *drv = inst->drv;
> > +
> > +	if (inst->state == STATE_ON) {
> > +		dev_err(drv->dev, "usb phy \"%s\" already on",
> > +							inst->cfg->label);
> > +		return -ENODEV;
> > +	}
> > +
> > +	inst->state = STATE_ON;
> > +	inst->ref_cnt++;
> > +	if (inst->ref_cnt > 1)
> > +		return 0;
> > +
> > +	exynos5250_phy_pwr(inst, 1);
> > +	exynos5250_isol(inst, 0);
> > +
> > +	/* Power on the device, as it is necessary for HSIC to work */
> > +	if (inst->cfg->id == EXYNOS5250_HSIC0) {
> > +		struct uphy_instance *device =
> > +
&drv->uphy_instances[EXYNOS5250_DEVICE];
> > +		device->ref_cnt++;
> > +		if (device->ref_cnt > 1)
> > +			return 0;
> > +		exynos5250_phy_pwr(device, 1);
> > +		exynos5250_isol(device, 0);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int exynos5250_power_off(struct uphy_instance *inst) {
> > +	struct uphy_driver *drv = inst->drv;
> > +
> > +	if (inst->state == STATE_OFF) {
> > +		dev_err(drv->dev, "usb phy \"%s\" already off",
> > +							inst->cfg->label);
> > +		return -EINVAL;
> > +	}
> > +
> > +	inst->state = STATE_OFF;
> > +	inst->ref_cnt--;
> > +
> > +	if (inst->ref_cnt > 0)
> > +		return 0;
> > +
> > +	exynos5250_isol(inst, 1);
> > +	exynos5250_phy_pwr(inst, 0);
> > +
> > +	if (inst->cfg->id == EXYNOS5250_HSIC0) {
> > +		struct uphy_instance *device =
> > +
&drv->uphy_instances[EXYNOS5250_DEVICE];
> > +		device->ref_cnt--;
> > +		if (device->ref_cnt > 0)
> > +			return 0;
> > +		exynos5250_isol(device, 1);
> > +		exynos5250_phy_pwr(device, 0);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +
> > +static const struct common_phy exynos5250_phys[] = {
> > +	{
> > +		.label		= "device",
> > +		.type		= PHY_DEVICE,
> > +		.id		= EXYNOS5250_DEVICE,
> > +		.rate_to_clk	= exynos5250_rate_to_clk,
> > +		.power_on	= exynos5250_power_on,
> > +		.power_off	= exynos5250_power_off,
> > +	},
> > +	{
> > +		.label		= "host",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS5250_HOST,
> > +		.rate_to_clk	= exynos5250_rate_to_clk,
> > +		.power_on	= exynos5250_power_on,
> > +		.power_off	= exynos5250_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic0",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS5250_HSIC0,
> > +		.rate_to_clk	= exynos5250_rate_to_clk,
> > +		.power_on	= exynos5250_power_on,
> > +		.power_off	= exynos5250_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic1",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS5250_HSIC1,
> > +		.rate_to_clk	= exynos5250_rate_to_clk,
> > +		.power_on	= exynos5250_power_on,
> > +		.power_off	= exynos5250_power_off,
> > +	},
> > +	{},
> > +};
> 
> All this can be removed if you don't use exynos common PHY.

Ok. Now, I think, I start to understand you better. Still there is the
question - one single driver or many small drivers with repeated code.

> 
> Thanks
> Kishon

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




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

* RE: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver
@ 2013-10-28 13:52       ` Kamil Debski
  0 siblings, 0 replies; 46+ messages in thread
From: Kamil Debski @ 2013-10-28 13:52 UTC (permalink / raw)
  To: 'Kishon Vijay Abraham I'
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-u79uwXL29TY76Z2rM5mHXA,
	kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ, Tomasz Figa,
	Sylwester Nawrocki, Marek Szyprowski,
	gautam.vivek-Sze3O3UU22JBDgjK7y7TUQ,
	mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w

Hi Kishon,

> From: Kishon Vijay Abraham I [mailto:kishon-l0cyMroinI0@public.gmane.org]
> Sent: Friday, October 25, 2013 5:44 PM
> 
> Hi,
> 
> On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
> > Add support for Exynos 5250. This is work-in-progress commit. Not for
> > merging.
> >
> > Signed-off-by: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> > Signed-off-by: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> > ---
> >  drivers/phy/Kconfig              |    7 +
> >  drivers/phy/Makefile             |    1 +
> >  drivers/phy/phy-exynos-usb.c     |   10 +
> >  drivers/phy/phy-exynos-usb.h     |    1 +
> >  drivers/phy/phy-exynos5250-usb.c |  411
> > ++++++++++++++++++++++++++++++++++++++
> >  5 files changed, 430 insertions(+)
> >  create mode 100644 drivers/phy/phy-exynos5250-usb.c
> >
> > diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index
> > 2f7ac0a..0f598d0 100644
> > --- a/drivers/phy/Kconfig
> > +++ b/drivers/phy/Kconfig
> > @@ -36,4 +36,11 @@ config PHY_EXYNOS4212_USB
> >  	help
> >  	  Enable USB PHY support for Exynos 4212
> >
> > +config PHY_EXYNOS5250_USB
> > +	bool "Support for Exynos 5250"
> > +	depends on PHY_EXYNOS_USB
> 
> This should be a separate driver. Not necessary to use PHY_EXYNOS_USB.
> > +	depends on SOC_EXYNOS5250
> > +	help
> > +	  Enable USB PHY support for Exynos 5250
> > +
> >  endmenu
> > diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index
> > ca3dc82..0dff0dd 100644
> > --- a/drivers/phy/Makefile
> > +++ b/drivers/phy/Makefile
> > @@ -6,3 +6,4 @@ obj-$(CONFIG_GENERIC_PHY)	+= phy-core.o
> >  obj-$(CONFIG_PHY_EXYNOS_USB)		+= phy-exynos-usb.o
> >  obj-$(CONFIG_PHY_EXYNOS4210_USB)	+= phy-exynos4210-usb.o
> >  obj-$(CONFIG_PHY_EXYNOS4212_USB)	+= phy-exynos4212-usb.o
> > +obj-$(CONFIG_PHY_EXYNOS5250_USB)	+= phy-exynos5250-usb.o
> > diff --git a/drivers/phy/phy-exynos-usb.c
> > b/drivers/phy/phy-exynos-usb.c index d4a26df..172b774 100644
> > --- a/drivers/phy/phy-exynos-usb.c
> > +++ b/drivers/phy/phy-exynos-usb.c
> > @@ -212,6 +212,10 @@ extern const struct uphy_config
> > exynos4210_uphy_config;  extern const struct uphy_config
> > exynos4212_uphy_config;  #endif
> >
> > +#ifdef CONFIG_PHY_EXYNOS5250_USB
> > +extern const struct uphy_config exynos5250_uphy_config; #endif
> > +
> >  static const struct of_device_id exynos_uphy_of_match[] = {  #ifdef
> > CONFIG_PHY_EXYNOS4210_USB
> >  	{
> > @@ -225,6 +229,12 @@ static const struct of_device_id
> exynos_uphy_of_match[] = {
> >  		.data = &exynos4212_uphy_config,
> >  	},
> >  #endif
> > +#ifdef CONFIG_PHY_EXYNOS5250_USB
> > +	{
> > +		.compatible = "samsung,exynos5250-usbphy",
> > +		.data = &exynos5250_uphy_config,
> > +	},
> > +#endif
> >  	{ },
> >  };
> >
> > diff --git a/drivers/phy/phy-exynos-usb.h
> > b/drivers/phy/phy-exynos-usb.h index f45cb3c..a9febfa 100644
> > --- a/drivers/phy/phy-exynos-usb.h
> > +++ b/drivers/phy/phy-exynos-usb.h
> > @@ -42,6 +42,7 @@ enum samsung_cpu_type {
> >  	TYPE_S3C64XX,
> >  	TYPE_EXYNOS4210,
> >  	TYPE_EXYNOS4212,
> > +	TYPE_EXYNOS5250,
> 
> No cpu types here.
> >  };
> >
> >  enum uphy_state {
> > diff --git a/drivers/phy/phy-exynos5250-usb.c
> > b/drivers/phy/phy-exynos5250-usb.c
> > new file mode 100644
> > index 0000000..156093b
> > --- /dev/null
> > +++ b/drivers/phy/phy-exynos5250-usb.c
> > @@ -0,0 +1,411 @@
> > +/*
> > + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.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-exynos-usb.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		(0x1 << 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		(0x1 << 11)
> > +#define EXYNOS_5250_HOSTPHYCTRL0_RETENABLE		(0x1 << 10)
> > +#define EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N		(0x1 << 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			(0x1 << 6)
> > +#define EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP		(0x1 << 5)
> > +#define EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND		(0x1 << 4)
> > +#define EXYNOS_5250_HOSTPHYCTRL0_WORDINTERFACE		(0x1 << 3)
> > +#define EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST		(0x1 << 2)
> > +#define EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST		(0x1 << 1)
> > +#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST		(0x1 << 0)
> > +
> > +/* HSIC0 & HSCI1 */
> > +#define EXYNOS_5250_HOSTPHYCTRL1			0x10
> > +#define EXYNOS_5250_HOSTPHYCTRL2			0x20
> > +
> > +#define EXYNOS_5250_HOSTPHYCTRLX_REFCLKSEL_MASK		(0x3 << 23)
> > +#define EXYNOS_5250_HOSTPHYCTRLX_REFCLKDIV_MASK		(0x7f << 16)
> > +#define EXYNOS_5250_HOSTPHYCTRLX_SIDDQ			(0x1 << 6)
> > +#define EXYNOS_5250_HOSTPHYCTRLX_FORCESLEEP		(0x1 << 5)
> > +#define EXYNOS_5250_HOSTPHYCTRLX_FORCESUSPEND		(0x1 << 4)
> > +#define EXYNOS_5250_HOSTPHYCTRLX_WORDINTERFACE		(0x1 << 3)
> > +#define EXYNOS_5250_HOSTPHYCTRLX_UTMISWRST		(0x1 << 2)
> > +#define EXYNOS_5250_HOSTPHYCTRLX_PHYSWRST		(0x1 << 0)
> > +
> > +/* EHCI control */
> > +#define EXYNOS_5250_HOSTEHCICTRL			0x30
> > +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN		(0x1 << 29)
> > +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR4		(0x1 << 28)
> > +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR8		(0x1 << 27)
> > +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR16		(0x1 << 26)
> > +#define EXYNOS_5250_HOSTEHCICTRL_AUTOPPDONOVRCUREN	(0x1 << 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		(0x1 << 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		(0x1 << 0)
> > +
> > +/* USBOTG */
> > +#define EXYNOS_5250_USBOTGSYS				0x38
> > +#define EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET		(0x1 << 14)
> > +#define EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG		(0x1 << 13)
> > +#define EXYNOS_5250_USBOTGSYS_PHY_SW_RST		(0x1 << 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			(0x1 << 8)
> > +#define EXYNOS_5250_USBOTGSYS_COMMON_ON			(0x1 << 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		(0x1 << 3)
> > +#define EXYNOS_5250_USBOTGSYS_OTGDISABLE		(0x1 << 2)
> > +#define EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG		(0x1 << 1)
> > +#define EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND		(0x1 << 0)
> > +
> > +/* Isolation, configured in the power management unit */
> > +#define EXYNOS_5250_USB_ISOL_OTG_OFFSET		0x0
> > +#define EXYNOS_5250_USB_ISOL_OTG		(1 << 0)
> > +#define EXYNOS_5250_USB_ISOL_HOST_OFFSET	0x4
> > +#define EXYNOS_5250_USB_ISOL_HOST		(1 << 0)
> > +
> > +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. */
> 
> Follow coding guidelines ;-)
> > +static u32 exynos5250_rate_to_clk(unsigned long rate) {
> > +	unsigned int clksel;
> > +
> > +	/* EXYNOS_5250_FSEL_MASK */
> > +
> > +	switch (rate) {
> > +	case 9600 * KHZ:
> > +		clksel = EXYNOS_5250_FSEL_9MHZ6;
> > +		break;
> > +	case 10 * MHZ:
> > +		clksel = EXYNOS_5250_FSEL_10MHZ;
> > +		break;
> > +	case 12 * MHZ:
> > +		clksel = EXYNOS_5250_FSEL_12MHZ;
> > +		break;
> > +	case 19200 * KHZ:
> > +		clksel = EXYNOS_5250_FSEL_19MHZ2;
> > +		break;
> > +	case 20 * MHZ:
> > +		clksel = EXYNOS_5250_FSEL_20MHZ;
> > +		break;
> > +	case 24 * MHZ:
> > +		clksel = EXYNOS_5250_FSEL_24MHZ;
> > +		break;
> > +	case 50 * MHZ:
> > +		clksel = EXYNOS_5250_FSEL_50MHZ;
> > +		break;
> > +	default:
> > +		clksel = CLKSEL_ERROR;
> > +	}
> > +
> > +	return clksel;
> > +}
> > +
> > +static void exynos5250_isol(struct uphy_instance *inst, bool on) {
> > +	struct uphy_driver *drv = inst->drv;
> > +	u32 offset;
> > +	u32 mask;
> > +	u32 tmp;
> > +
> > +	if (!drv->reg_isol)
> > +		return;
> > +
> > +	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;
> > +	};
> > +
> > +	tmp = readl(drv->reg_isol + offset);
> > +	if (on)
> > +		tmp &= ~mask;
> > +	else
> > +		tmp |= mask;
> > +	writel(tmp, drv->reg_isol + offset); }
> > +
> > +static void exynos5250_phy_pwr(struct uphy_instance *inst, bool on)
> {
> > +	struct uphy_driver *drv = inst->drv;
> > +	u32 ctrl0;
> > +	u32 otg;
> > +	u32 ehci;
> > +	u32 ohci;
> > +
> > +	switch (inst->cfg->id) {
> > +	case EXYNOS5250_DEVICE:
> > +		writel(0, drv->reg_mode);
> > +
> > +		/* OTG configuration */
> > +		otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
> > +		/* The clock */
> > +		otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
> > +		otg |= inst->clk << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
> > +		/* Reset */
> > +		otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
> > +			EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
> > +			EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
> > +		/* TODO: Clear 4 bits as the old driver does. */
> > +		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);
> > +
> > +
> > +		break;
> > +	case EXYNOS5250_HOST:
> > +		/* Host registers configuration */
> > +		ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
> > +		/* The clock */
> > +		ctrl0 &= ~EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK;
> > +		ctrl0 |= inst->clk << 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 << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
> > +		/* Reset */
> > +		otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
> > +			EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
> > +			EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
> > +		/* TODO: Clear 4 bits as the old driver does. */
> > +		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);
> > +
> > +		/* 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);
> > +		/* Let's do some undocumented magic (based on the old
> driver) */
> > +		ohci |=	0x1 << 3;
> > +		writel(ohci, drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
> > +
> > +		break;
> > +	}
> > +}
> > +
> > +static int exynos5250_power_on(struct uphy_instance *inst) {
> > +	struct uphy_driver *drv = inst->drv;
> > +
> > +	if (inst->state == STATE_ON) {
> > +		dev_err(drv->dev, "usb phy \"%s\" already on",
> > +							inst->cfg->label);
> > +		return -ENODEV;
> > +	}
> > +
> > +	inst->state = STATE_ON;
> > +	inst->ref_cnt++;
> > +	if (inst->ref_cnt > 1)
> > +		return 0;
> > +
> > +	exynos5250_phy_pwr(inst, 1);
> > +	exynos5250_isol(inst, 0);
> > +
> > +	/* Power on the device, as it is necessary for HSIC to work */
> > +	if (inst->cfg->id == EXYNOS5250_HSIC0) {
> > +		struct uphy_instance *device =
> > +
&drv->uphy_instances[EXYNOS5250_DEVICE];
> > +		device->ref_cnt++;
> > +		if (device->ref_cnt > 1)
> > +			return 0;
> > +		exynos5250_phy_pwr(device, 1);
> > +		exynos5250_isol(device, 0);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int exynos5250_power_off(struct uphy_instance *inst) {
> > +	struct uphy_driver *drv = inst->drv;
> > +
> > +	if (inst->state == STATE_OFF) {
> > +		dev_err(drv->dev, "usb phy \"%s\" already off",
> > +							inst->cfg->label);
> > +		return -EINVAL;
> > +	}
> > +
> > +	inst->state = STATE_OFF;
> > +	inst->ref_cnt--;
> > +
> > +	if (inst->ref_cnt > 0)
> > +		return 0;
> > +
> > +	exynos5250_isol(inst, 1);
> > +	exynos5250_phy_pwr(inst, 0);
> > +
> > +	if (inst->cfg->id == EXYNOS5250_HSIC0) {
> > +		struct uphy_instance *device =
> > +
&drv->uphy_instances[EXYNOS5250_DEVICE];
> > +		device->ref_cnt--;
> > +		if (device->ref_cnt > 0)
> > +			return 0;
> > +		exynos5250_isol(device, 1);
> > +		exynos5250_phy_pwr(device, 0);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +
> > +static const struct common_phy exynos5250_phys[] = {
> > +	{
> > +		.label		= "device",
> > +		.type		= PHY_DEVICE,
> > +		.id		= EXYNOS5250_DEVICE,
> > +		.rate_to_clk	= exynos5250_rate_to_clk,
> > +		.power_on	= exynos5250_power_on,
> > +		.power_off	= exynos5250_power_off,
> > +	},
> > +	{
> > +		.label		= "host",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS5250_HOST,
> > +		.rate_to_clk	= exynos5250_rate_to_clk,
> > +		.power_on	= exynos5250_power_on,
> > +		.power_off	= exynos5250_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic0",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS5250_HSIC0,
> > +		.rate_to_clk	= exynos5250_rate_to_clk,
> > +		.power_on	= exynos5250_power_on,
> > +		.power_off	= exynos5250_power_off,
> > +	},
> > +	{
> > +		.label		= "hsic1",
> > +		.type		= PHY_HOST,
> > +		.id		= EXYNOS5250_HSIC1,
> > +		.rate_to_clk	= exynos5250_rate_to_clk,
> > +		.power_on	= exynos5250_power_on,
> > +		.power_off	= exynos5250_power_off,
> > +	},
> > +	{},
> > +};
> 
> All this can be removed if you don't use exynos common PHY.

Ok. Now, I think, I start to understand you better. Still there is the
question - one single driver or many small drivers with repeated code.

> 
> Thanks
> Kishon

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



--
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] 46+ messages in thread

* RE: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver
  2013-10-25 21:36     ` Kumar Gala
  (?)
@ 2013-10-28 13:52     ` Kamil Debski
  -1 siblings, 0 replies; 46+ messages in thread
From: Kamil Debski @ 2013-10-28 13:52 UTC (permalink / raw)
  To: 'Kumar Gala'
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, kyungmin.park, kishon, Tomasz Figa,
	Sylwester Nawrocki, Marek Szyprowski, gautam.vivek, mat.krawczuk

Hi Kumar Gala,

> From: Kumar Gala [mailto:galak@codeaurora.org]
> Sent: Friday, October 25, 2013 11:36 PM
> 
> On Oct 25, 2013, at 9:15 AM, 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.
> >
> > Signed-off-by: Kamil Debski <k.debski@samsung.com>
> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> > ---
> > .../devicetree/bindings/phy/samsung-usbphy.txt     |   51 +++
> > drivers/phy/Kconfig                                |   21 ++
> > drivers/phy/Makefile                               |    3 +
> > drivers/phy/phy-exynos-usb.c                       |  245
> ++++++++++++++
> > drivers/phy/phy-exynos-usb.h                       |   94 ++++++
> > drivers/phy/phy-exynos4210-usb.c                   |  295
> +++++++++++++++++
> > drivers/phy/phy-exynos4212-usb.c                   |  343
> ++++++++++++++++++++
> > 7 files changed, 1052 insertions(+)
> > create mode 100644
> > Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> > create mode 100644 drivers/phy/phy-exynos-usb.c create mode 100644
> > drivers/phy/phy-exynos-usb.h create mode 100644
> > drivers/phy/phy-exynos4210-usb.c create mode 100644
> > drivers/phy/phy-exynos4212-usb.c
> >
> > diff --git a/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> > b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> > new file mode 100644
> > index 0000000..f112b37
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
> > @@ -0,0 +1,51 @@
> > +Samsung S5P/EXYNOS SoC series USB PHY
> > +-------------------------------------------------
> > +
> > +Required properties:
> > +- compatible : should be one of the listed compatibles:
> > +	- "samsung,exynos4210-usbphy"
> > +	- "samsung,exynos4212-usbphy"
> > +- reg : a list of registers used by phy driver
> > +	- first and obligatory is the location of phy modules registers
> > +	- second and also required is the location of isolation registers
> > +	  (isolation registers control the physical connection between
> the in
> > +	  SoC modules and outside of the SoC, this also can be called
> enable
> > +	  control in the documentation of the SoC)
> > +	- third is the location of the mode switch register, this only
> applies
> > +	  to SoCs that have such a feature; mode switching enables to
> have
> > +	  both host and device used the same SoC pins and is commonly
> used
> > +	  when OTG is supported
> > +- #phy-cells : from the generic phy bindings, must be 1;
> 
> Please add if clock & clock-names are required properties.

Ok, thanks for pointing this out.

> 
> > +
> > +The second cell in the PHY specifier identifies the PHY its meaning
> > +is SoC dependent. For the currently supported SoCs (Exynos 4210 and
> > +Exynos 4212) it is as follows:
> 
> Can we say instead of 'its meaning is SoC dependent...' something like
> 'its meaning is compatible dependent"?

Ok, this sounds better in deed.
 
> > +  0 - USB device,
> > +  1 - USB host,
> > +  2 - HSIC0,
> > +  3 - HSIC1,
> > +
> > +Example:
> > +
> > +For Exynos 4412 (compatible with Exynos 4212):
> > +
> > +exynos_usbphy: exynos-usbphy@125B0000 {
> > +	compatible = "samsung,exynos4212-usbphy";
> > +	reg = <0x125B0000 0x100 0x10020704 0x0c 0x1001021c 0x4>;
> > +	ranges;
> > +	#address-cells = <1>;
> > +	#size-cells = <1>;
> 
> Why do you have ranges, and #address-cells & #size-cells here?

As, I mentioned in my reply to Kishon. I worked on two branches
and I forgot to remove this in the one used to generate patches.

> 
> > +	clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
> > +							<&clock 2>;
> > +	clock-names = "phy", "device", "host", "hsic0", "hsic1";
> > +	status = "okay";
> > +	#phy-cells = <1>;
> > +};
> > +
> > +Then the PHY can be used in other nodes such as:
> > +
> > +ehci@12580000 {
> > +	status = "okay";
> > +	phys = <&exynos_usbphy 2>;
> > +	phy-names = "hsic0";
> > +};
> 

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


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

* RE: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework
@ 2013-10-28 13:52       ` Kamil Debski
  0 siblings, 0 replies; 46+ messages in thread
From: Kamil Debski @ 2013-10-28 13:52 UTC (permalink / raw)
  To: 'Jingoo Han'
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, 'Kyungmin Park',
	'Kishon Vijay Abraham I',
	Tomasz Figa, Sylwester Nawrocki, Marek Szyprowski,
	'Vivek Gautam', 'Mateusz Krawczuk'

Hi Jingoo,

> From: Jingoo Han [mailto:jg1.han@samsung.com]
> Sent: Saturday, October 26, 2013 3:27 AM
> 
> On Friday, October 25, 2013 11:15 PM, Kamil Debski wrote:
> >
> > Change the phy provider used from the old usb phy specific to a new
> > one using the generic phy framework.
> >
> > Signed-off-by: Kamil Debski <k.debski@samsung.com>
> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> > ---
> >  drivers/usb/host/ehci-s5p.c |   21 +++++++++++----------
> >  1 file changed, 11 insertions(+), 10 deletions(-)
> 
> Hi Kamil Debski,
> It looks good. :-)

Thank you.
 
> However, could you re-basing against Greg's 'usb-next' branch?
> Now, the file name of 'ehci-s5p.c' is renamed to 'ehci-exynos.c'.
> Also, 'Generic PHY Framework' was already merged to Greg's 'usb-next'
> branch.

Thanks for pointing out this.

> 
> Thank you.
> 
> Best regards,
> Jingoo Han

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


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

* RE: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework
@ 2013-10-28 13:52       ` Kamil Debski
  0 siblings, 0 replies; 46+ messages in thread
From: Kamil Debski @ 2013-10-28 13:52 UTC (permalink / raw)
  To: 'Jingoo Han'
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-u79uwXL29TY76Z2rM5mHXA, 'Kyungmin Park',
	'Kishon Vijay Abraham I',
	Tomasz Figa, Sylwester Nawrocki, Marek Szyprowski,
	'Vivek Gautam', 'Mateusz Krawczuk'

Hi Jingoo,

> From: Jingoo Han [mailto:jg1.han-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org]
> Sent: Saturday, October 26, 2013 3:27 AM
> 
> On Friday, October 25, 2013 11:15 PM, Kamil Debski wrote:
> >
> > Change the phy provider used from the old usb phy specific to a new
> > one using the generic phy framework.
> >
> > Signed-off-by: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> > Signed-off-by: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
> > ---
> >  drivers/usb/host/ehci-s5p.c |   21 +++++++++++----------
> >  1 file changed, 11 insertions(+), 10 deletions(-)
> 
> Hi Kamil Debski,
> It looks good. :-)

Thank you.
 
> However, could you re-basing against Greg's 'usb-next' branch?
> Now, the file name of 'ehci-s5p.c' is renamed to 'ehci-exynos.c'.
> Also, 'Generic PHY Framework' was already merged to Greg's 'usb-next'
> branch.

Thanks for pointing out this.

> 
> Thank you.
> 
> Best regards,
> Jingoo Han

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

--
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] 46+ messages in thread

* RE: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework
  2013-10-26  9:41     ` Vivek Gautam
  (?)
@ 2013-10-28 13:53     ` Kamil Debski
  2013-10-28 14:36         ` Vivek Gautam
  -1 siblings, 1 reply; 46+ messages in thread
From: Kamil Debski @ 2013-10-28 13:53 UTC (permalink / raw)
  To: 'Vivek Gautam'
  Cc: linux-kernel, linux-samsung-soc, 'Linux USB Mailing List',
	devicetree, linux-arm, 'Kyungmin Park', 'kishon',
	Tomasz Figa, Sylwester Nawrocki, Marek Szyprowski,
	'Vivek Gautam',
	mat.krawczuk

Hi Vivek,

> From: Vivek Gautam [mailto:gautamvivek1987@gmail.com]
> Sent: Saturday, October 26, 2013 11:41 AM
> 
> Hi Kamil,
> 
> 
> On Fri, Oct 25, 2013 at 7:45 PM, Kamil Debski <k.debski@samsung.com>
> wrote:
> > Change the phy provider used from the old usb phy specific to a new
> > one using the generic phy framework.
> >
> > Signed-off-by: Kamil Debski <k.debski@samsung.com>
> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> > ---
> >  drivers/usb/host/ehci-s5p.c |   21 +++++++++++----------
> >  1 file changed, 11 insertions(+), 10 deletions(-)
> >
> > diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-
> s5p.c
> > index 7cc26e6..76606ff 100644
> > --- a/drivers/usb/host/ehci-s5p.c
> > +++ b/drivers/usb/host/ehci-s5p.c
> > @@ -19,6 +19,7 @@
> >  #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/platform_data/usb-ehci-s5p.h>
> >  #include <linux/usb/phy.h>
> > @@ -45,7 +46,7 @@ static struct hc_driver __read_mostly
> > s5p_ehci_hc_driver;
> >
> >  struct s5p_ehci_hcd {
> >         struct clk *clk;
> > -       struct usb_phy *phy;
> > +       struct phy *phy;
> >         struct usb_otg *otg;
> >         struct s5p_ehci_platdata *pdata;  }; @@ -77,10 +78,11 @@
> > static int s5p_ehci_probe(struct platform_device *pdev)  {
> >         struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
> >         struct s5p_ehci_hcd *s5p_ehci;
> > +       struct phy *phy;
> 
> just a nit here:
> Lets keep the pointer to 'phy' and 'phy_name' together ?
> and move this above phy_name ?

Thanks for pointing this out.
 
> >         struct usb_hcd *hcd;
> >         struct ehci_hcd *ehci;
> >         struct resource *res;
> > -       struct usb_phy *phy;
> > +       const char *phy_name;
> >         int irq;
> >         int err;
> >
> > @@ -103,14 +105,14 @@ static int s5p_ehci_probe(struct
> platform_device *pdev)
> >                 return -ENOMEM;
> >         }
> >         s5p_ehci = to_s5p_ehci(hcd);
> > -
> > +       phy_name = of_get_property(pdev->dev.of_node, "phy-names",
> NULL);
> > +       phy =  devm_phy_get(&pdev->dev, phy_name);
> 
> Below check for exynos5440 was supposed to skip any request phy.
> So shouldn't we place above two assignments to the original place where
> devm_usb_get_phy() was called ?
> May be i am not getting you intention of changing the place.

Hm... You are right - if we want to leave this check and skip phy request
for
5450 then I should leave the order as it was. And if we want to use the new
phy driver for 5450 then the check to skip phy requesting should be simply
removed.

> 
> >         if (of_device_is_compatible(pdev->dev.of_node,
> >                                         "samsung,exynos5440-ehci")) {
> >                 s5p_ehci->pdata = &empty_platdata;
> >                 goto skip_phy;
> >         }
> >
> > -       phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
> >         if (IS_ERR(phy)) {
> >                 /* Fallback to pdata */
> >                 if (!pdata) {
> > @@ -122,7 +124,6 @@ static int s5p_ehci_probe(struct platform_device
> *pdev)
> >                 }
> >         } else {
> >                 s5p_ehci->phy = phy;
> > -               s5p_ehci->otg = phy->otg;
> >         }
> >
> >  skip_phy:
> > @@ -166,7 +167,7 @@ skip_phy:
> >                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
> 
> Lets remove this line and similar calls to 'set_host()' in the driver,
> since we don't have s5p_ehci->otg anymore after the same is removed
> above.
> Anyways this was helping the old phy-samsung-usb2 driver, and not
> needed now.

Ok, I will.

> 
> >
> >         if (s5p_ehci->phy)
> > -               usb_phy_init(s5p_ehci->phy);
> > +               phy_power_on(s5p_ehci->phy);
> >         else if (s5p_ehci->pdata->phy_init)
> >                 s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
> >
> > @@ -188,7 +189,7 @@ skip_phy:
> >
> >  fail_add_hcd:
> >         if (s5p_ehci->phy)
> > -               usb_phy_shutdown(s5p_ehci->phy);
> > +               phy_power_off(s5p_ehci->phy);
> >         else if (s5p_ehci->pdata->phy_exit)
> >                 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
> >  fail_io:
> > @@ -209,7 +210,7 @@ static int s5p_ehci_remove(struct platform_device
> *pdev)
> >                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
> 
> ditto
> 
> >
> >         if (s5p_ehci->phy)
> > -               usb_phy_shutdown(s5p_ehci->phy);
> > +               phy_power_off(s5p_ehci->phy);
> >         else if (s5p_ehci->pdata->phy_exit)
> >                 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
> >
> > @@ -244,7 +245,7 @@ static int s5p_ehci_suspend(struct device *dev)
> >                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
> ditto
> 
> >
> >         if (s5p_ehci->phy)
> > -               usb_phy_shutdown(s5p_ehci->phy);
> > +               phy_power_off(s5p_ehci->phy);
> >         else if (s5p_ehci->pdata->phy_exit)
> >                 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
> >
> > @@ -265,7 +266,7 @@ static int s5p_ehci_resume(struct device *dev)
> >                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
> ditto
> 
> >
> >         if (s5p_ehci->phy)
> > -               usb_phy_init(s5p_ehci->phy);
> > +               phy_power_on(s5p_ehci->phy);
> >         else if (s5p_ehci->pdata->phy_init)
> >                 s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
> >
> [..] Rest looks good. :-)
> 

Thank you!

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



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

* Re: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework
@ 2013-10-28 14:36         ` Vivek Gautam
  0 siblings, 0 replies; 46+ messages in thread
From: Vivek Gautam @ 2013-10-28 14:36 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, Linux USB Mailing List,
	devicetree, linux-arm, Kyungmin Park, kishon, Tomasz Figa,
	Sylwester Nawrocki, Marek Szyprowski, Vivek Gautam, mat.krawczuk

Hi Kamil,


On Mon, Oct 28, 2013 at 7:23 PM, Kamil Debski <k.debski@samsung.com> wrote:
> Hi Vivek,
>
>> From: Vivek Gautam [mailto:gautamvivek1987@gmail.com]
>> Sent: Saturday, October 26, 2013 11:41 AM
>>
>> Hi Kamil,
>>
>>
>> On Fri, Oct 25, 2013 at 7:45 PM, Kamil Debski <k.debski@samsung.com>
>> wrote:
>> > Change the phy provider used from the old usb phy specific to a new
>> > one using the generic phy framework.
>> >
>> > Signed-off-by: Kamil Debski <k.debski@samsung.com>
>> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>> > ---

Similar change for ohci-exynos too ?

>> >  drivers/usb/host/ehci-s5p.c |   21 +++++++++++----------
>> >  1 file changed, 11 insertions(+), 10 deletions(-)
>> >
>> > diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-
>> s5p.c
>> > index 7cc26e6..76606ff 100644
>> > --- a/drivers/usb/host/ehci-s5p.c
>> > +++ b/drivers/usb/host/ehci-s5p.c
>> > @@ -19,6 +19,7 @@
>> >  #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/platform_data/usb-ehci-s5p.h>
>> >  #include <linux/usb/phy.h>
>> > @@ -45,7 +46,7 @@ static struct hc_driver __read_mostly
>> > s5p_ehci_hc_driver;
>> >
>> >  struct s5p_ehci_hcd {
>> >         struct clk *clk;
>> > -       struct usb_phy *phy;
>> > +       struct phy *phy;
>> >         struct usb_otg *otg;

This can also be removed.

>> >         struct s5p_ehci_platdata *pdata;  }; @@ -77,10 +78,11 @@
>> > static int s5p_ehci_probe(struct platform_device *pdev)  {
>> >         struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
>> >         struct s5p_ehci_hcd *s5p_ehci;
>> > +       struct phy *phy;
>>
>> just a nit here:
>> Lets keep the pointer to 'phy' and 'phy_name' together ?
>> and move this above phy_name ?
>
> Thanks for pointing this out.
>
>> >         struct usb_hcd *hcd;
>> >         struct ehci_hcd *ehci;
>> >         struct resource *res;
>> > -       struct usb_phy *phy;
>> > +       const char *phy_name;
>> >         int irq;
>> >         int err;
>> >
>> > @@ -103,14 +105,14 @@ static int s5p_ehci_probe(struct
>> platform_device *pdev)
>> >                 return -ENOMEM;
>> >         }
>> >         s5p_ehci = to_s5p_ehci(hcd);
>> > -
>> > +       phy_name = of_get_property(pdev->dev.of_node, "phy-names",
>> NULL);

Sorry one more doubt here :-)
Now that we have HOST, DEVICE, HSIC0, HSIC1 as different PHYs,
and i think EHCI will be the candidate to request HSIC phy too (once
we have HSIC phy
initialization code available for each SoC);
shouldn't we be using of_count_phandle_with_args() to get HOST, and HSIC phys
and then request multiple phys here ?

>> > +       phy =  devm_phy_get(&pdev->dev, phy_name);
>>
>> Below check for exynos5440 was supposed to skip any request phy.
>> So shouldn't we place above two assignments to the original place where
>> devm_usb_get_phy() was called ?
>> May be i am not getting you intention of changing the place.
>
> Hm... You are right - if we want to leave this check and skip phy request
> for
> 5450 then I should leave the order as it was. And if we want to use the new
> phy driver for 5450 then the check to skip phy requesting should be simply
> removed.
>
>>
>> >         if (of_device_is_compatible(pdev->dev.of_node,
>> >                                         "samsung,exynos5440-ehci")) {
>> >                 s5p_ehci->pdata = &empty_platdata;
>> >                 goto skip_phy;
>> >         }
>> >
>> > -       phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
>> >         if (IS_ERR(phy)) {
>> >                 /* Fallback to pdata */
>> >                 if (!pdata) {
>> > @@ -122,7 +124,6 @@ static int s5p_ehci_probe(struct platform_device
>> *pdev)
>> >                 }
>> >         } else {
>> >                 s5p_ehci->phy = phy;
>> > -               s5p_ehci->otg = phy->otg;
>> >         }
>> >
>> >  skip_phy:
>> > @@ -166,7 +167,7 @@ skip_phy:
>> >                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
>>
>> Lets remove this line and similar calls to 'set_host()' in the driver,
>> since we don't have s5p_ehci->otg anymore after the same is removed
>> above.
>> Anyways this was helping the old phy-samsung-usb2 driver, and not
>> needed now.
>
> Ok, I will.
>
>>
>> >
>> >         if (s5p_ehci->phy)
>> > -               usb_phy_init(s5p_ehci->phy);
>> > +               phy_power_on(s5p_ehci->phy);
>> >         else if (s5p_ehci->pdata->phy_init)
>> >                 s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
>> >
>> > @@ -188,7 +189,7 @@ skip_phy:
>> >
>> >  fail_add_hcd:
>> >         if (s5p_ehci->phy)
>> > -               usb_phy_shutdown(s5p_ehci->phy);
>> > +               phy_power_off(s5p_ehci->phy);
>> >         else if (s5p_ehci->pdata->phy_exit)
>> >                 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>> >  fail_io:
>> > @@ -209,7 +210,7 @@ static int s5p_ehci_remove(struct platform_device
>> *pdev)
>> >                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
>>
>> ditto
>>
>> >
>> >         if (s5p_ehci->phy)
>> > -               usb_phy_shutdown(s5p_ehci->phy);
>> > +               phy_power_off(s5p_ehci->phy);
>> >         else if (s5p_ehci->pdata->phy_exit)
>> >                 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>> >
>> > @@ -244,7 +245,7 @@ static int s5p_ehci_suspend(struct device *dev)
>> >                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
>> ditto
>>
>> >
>> >         if (s5p_ehci->phy)
>> > -               usb_phy_shutdown(s5p_ehci->phy);
>> > +               phy_power_off(s5p_ehci->phy);
>> >         else if (s5p_ehci->pdata->phy_exit)
>> >                 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>> >
>> > @@ -265,7 +266,7 @@ static int s5p_ehci_resume(struct device *dev)
>> >                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
>> ditto
>>
>> >
>> >         if (s5p_ehci->phy)
>> > -               usb_phy_init(s5p_ehci->phy);
>> > +               phy_power_on(s5p_ehci->phy);
>> >         else if (s5p_ehci->pdata->phy_init)
>> >                 s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
>> >
>> [..] Rest looks good. :-)
>>
>
> Thank you!
>
> Best wishes,
> --
> Kamil Debski
> Samsung R&D Institute Poland
>
>



-- 
Best Regards
Vivek Gautam

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

* Re: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework
@ 2013-10-28 14:36         ` Vivek Gautam
  0 siblings, 0 replies; 46+ messages in thread
From: Vivek Gautam @ 2013-10-28 14:36 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA, Linux USB Mailing List,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-u79uwXL29TY76Z2rM5mHXA, Kyungmin Park, kishon,
	Tomasz Figa, Sylwester Nawrocki, Marek Szyprowski, Vivek Gautam,
	mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w

Hi Kamil,


On Mon, Oct 28, 2013 at 7:23 PM, Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> wrote:
> Hi Vivek,
>
>> From: Vivek Gautam [mailto:gautamvivek1987-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org]
>> Sent: Saturday, October 26, 2013 11:41 AM
>>
>> Hi Kamil,
>>
>>
>> On Fri, Oct 25, 2013 at 7:45 PM, Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>> wrote:
>> > Change the phy provider used from the old usb phy specific to a new
>> > one using the generic phy framework.
>> >
>> > Signed-off-by: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>> > Signed-off-by: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>> > ---

Similar change for ohci-exynos too ?

>> >  drivers/usb/host/ehci-s5p.c |   21 +++++++++++----------
>> >  1 file changed, 11 insertions(+), 10 deletions(-)
>> >
>> > diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-
>> s5p.c
>> > index 7cc26e6..76606ff 100644
>> > --- a/drivers/usb/host/ehci-s5p.c
>> > +++ b/drivers/usb/host/ehci-s5p.c
>> > @@ -19,6 +19,7 @@
>> >  #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/platform_data/usb-ehci-s5p.h>
>> >  #include <linux/usb/phy.h>
>> > @@ -45,7 +46,7 @@ static struct hc_driver __read_mostly
>> > s5p_ehci_hc_driver;
>> >
>> >  struct s5p_ehci_hcd {
>> >         struct clk *clk;
>> > -       struct usb_phy *phy;
>> > +       struct phy *phy;
>> >         struct usb_otg *otg;

This can also be removed.

>> >         struct s5p_ehci_platdata *pdata;  }; @@ -77,10 +78,11 @@
>> > static int s5p_ehci_probe(struct platform_device *pdev)  {
>> >         struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
>> >         struct s5p_ehci_hcd *s5p_ehci;
>> > +       struct phy *phy;
>>
>> just a nit here:
>> Lets keep the pointer to 'phy' and 'phy_name' together ?
>> and move this above phy_name ?
>
> Thanks for pointing this out.
>
>> >         struct usb_hcd *hcd;
>> >         struct ehci_hcd *ehci;
>> >         struct resource *res;
>> > -       struct usb_phy *phy;
>> > +       const char *phy_name;
>> >         int irq;
>> >         int err;
>> >
>> > @@ -103,14 +105,14 @@ static int s5p_ehci_probe(struct
>> platform_device *pdev)
>> >                 return -ENOMEM;
>> >         }
>> >         s5p_ehci = to_s5p_ehci(hcd);
>> > -
>> > +       phy_name = of_get_property(pdev->dev.of_node, "phy-names",
>> NULL);

Sorry one more doubt here :-)
Now that we have HOST, DEVICE, HSIC0, HSIC1 as different PHYs,
and i think EHCI will be the candidate to request HSIC phy too (once
we have HSIC phy
initialization code available for each SoC);
shouldn't we be using of_count_phandle_with_args() to get HOST, and HSIC phys
and then request multiple phys here ?

>> > +       phy =  devm_phy_get(&pdev->dev, phy_name);
>>
>> Below check for exynos5440 was supposed to skip any request phy.
>> So shouldn't we place above two assignments to the original place where
>> devm_usb_get_phy() was called ?
>> May be i am not getting you intention of changing the place.
>
> Hm... You are right - if we want to leave this check and skip phy request
> for
> 5450 then I should leave the order as it was. And if we want to use the new
> phy driver for 5450 then the check to skip phy requesting should be simply
> removed.
>
>>
>> >         if (of_device_is_compatible(pdev->dev.of_node,
>> >                                         "samsung,exynos5440-ehci")) {
>> >                 s5p_ehci->pdata = &empty_platdata;
>> >                 goto skip_phy;
>> >         }
>> >
>> > -       phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
>> >         if (IS_ERR(phy)) {
>> >                 /* Fallback to pdata */
>> >                 if (!pdata) {
>> > @@ -122,7 +124,6 @@ static int s5p_ehci_probe(struct platform_device
>> *pdev)
>> >                 }
>> >         } else {
>> >                 s5p_ehci->phy = phy;
>> > -               s5p_ehci->otg = phy->otg;
>> >         }
>> >
>> >  skip_phy:
>> > @@ -166,7 +167,7 @@ skip_phy:
>> >                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
>>
>> Lets remove this line and similar calls to 'set_host()' in the driver,
>> since we don't have s5p_ehci->otg anymore after the same is removed
>> above.
>> Anyways this was helping the old phy-samsung-usb2 driver, and not
>> needed now.
>
> Ok, I will.
>
>>
>> >
>> >         if (s5p_ehci->phy)
>> > -               usb_phy_init(s5p_ehci->phy);
>> > +               phy_power_on(s5p_ehci->phy);
>> >         else if (s5p_ehci->pdata->phy_init)
>> >                 s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
>> >
>> > @@ -188,7 +189,7 @@ skip_phy:
>> >
>> >  fail_add_hcd:
>> >         if (s5p_ehci->phy)
>> > -               usb_phy_shutdown(s5p_ehci->phy);
>> > +               phy_power_off(s5p_ehci->phy);
>> >         else if (s5p_ehci->pdata->phy_exit)
>> >                 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>> >  fail_io:
>> > @@ -209,7 +210,7 @@ static int s5p_ehci_remove(struct platform_device
>> *pdev)
>> >                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
>>
>> ditto
>>
>> >
>> >         if (s5p_ehci->phy)
>> > -               usb_phy_shutdown(s5p_ehci->phy);
>> > +               phy_power_off(s5p_ehci->phy);
>> >         else if (s5p_ehci->pdata->phy_exit)
>> >                 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>> >
>> > @@ -244,7 +245,7 @@ static int s5p_ehci_suspend(struct device *dev)
>> >                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
>> ditto
>>
>> >
>> >         if (s5p_ehci->phy)
>> > -               usb_phy_shutdown(s5p_ehci->phy);
>> > +               phy_power_off(s5p_ehci->phy);
>> >         else if (s5p_ehci->pdata->phy_exit)
>> >                 s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
>> >
>> > @@ -265,7 +266,7 @@ static int s5p_ehci_resume(struct device *dev)
>> >                 s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
>> ditto
>>
>> >
>> >         if (s5p_ehci->phy)
>> > -               usb_phy_init(s5p_ehci->phy);
>> > +               phy_power_on(s5p_ehci->phy);
>> >         else if (s5p_ehci->pdata->phy_init)
>> >                 s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
>> >
>> [..] Rest looks good. :-)
>>
>
> Thank you!
>
> Best wishes,
> --
> Kamil Debski
> Samsung R&D Institute Poland
>
>



-- 
Best Regards
Vivek Gautam
--
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] 46+ messages in thread

* Re: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver
  2013-10-25 15:43     ` Kishon Vijay Abraham I
@ 2013-10-28 14:41       ` Vivek Gautam
  -1 siblings, 0 replies; 46+ messages in thread
From: Vivek Gautam @ 2013-10-28 14:41 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Kamil Debski, linux-kernel, linux-samsung-soc,
	Linux USB Mailing List, devicetree, linux-arm, Kyungmin Park,
	Tomasz Figa, Sylwester Nawrocki, Marek Szyprowski, Vivek Gautam,
	Mateusz Krawczuk

Hi Kishon,


On Fri, Oct 25, 2013 at 9:13 PM, Kishon Vijay Abraham I <kishon@ti.com> wrote:
> Hi,
>
> On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
>> Add support for Exynos 5250. This is work-in-progress commit. Not
>> for merging.
>>
>> Signed-off-by: Kamil Debski <k.debski@samsung.com>
>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>> ---
>>  drivers/phy/Kconfig              |    7 +
>>  drivers/phy/Makefile             |    1 +
>>  drivers/phy/phy-exynos-usb.c     |   10 +
>>  drivers/phy/phy-exynos-usb.h     |    1 +
>>  drivers/phy/phy-exynos5250-usb.c |  411 ++++++++++++++++++++++++++++++++++++++
>>  5 files changed, 430 insertions(+)
>>  create mode 100644 drivers/phy/phy-exynos5250-usb.c
>>
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index 2f7ac0a..0f598d0 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -36,4 +36,11 @@ config PHY_EXYNOS4212_USB
>>       help
>>         Enable USB PHY support for Exynos 4212
>>
>> +config PHY_EXYNOS5250_USB
>> +     bool "Support for Exynos 5250"
>> +     depends on PHY_EXYNOS_USB
>
> This should be a separate driver. Not necessary to use PHY_EXYNOS_USB.
>> +     depends on SOC_EXYNOS5250
>> +     help
>> +       Enable USB PHY support for Exynos 5250
>> +
>>  endmenu
>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
>> index ca3dc82..0dff0dd 100644
>> --- a/drivers/phy/Makefile
>> +++ b/drivers/phy/Makefile
>> @@ -6,3 +6,4 @@ obj-$(CONFIG_GENERIC_PHY)     += phy-core.o
>>  obj-$(CONFIG_PHY_EXYNOS_USB)         += phy-exynos-usb.o
>>  obj-$(CONFIG_PHY_EXYNOS4210_USB)     += phy-exynos4210-usb.o
>>  obj-$(CONFIG_PHY_EXYNOS4212_USB)     += phy-exynos4212-usb.o
>> +obj-$(CONFIG_PHY_EXYNOS5250_USB)     += phy-exynos5250-usb.o
>> diff --git a/drivers/phy/phy-exynos-usb.c b/drivers/phy/phy-exynos-usb.c
>> index d4a26df..172b774 100644
>> --- a/drivers/phy/phy-exynos-usb.c
>> +++ b/drivers/phy/phy-exynos-usb.c
>> @@ -212,6 +212,10 @@ extern const struct uphy_config exynos4210_uphy_config;
>>  extern const struct uphy_config exynos4212_uphy_config;
>>  #endif
>>
>> +#ifdef CONFIG_PHY_EXYNOS5250_USB
>> +extern const struct uphy_config exynos5250_uphy_config;
>> +#endif
>> +
>>  static const struct of_device_id exynos_uphy_of_match[] = {
>>  #ifdef CONFIG_PHY_EXYNOS4210_USB
>>       {
>> @@ -225,6 +229,12 @@ static const struct of_device_id exynos_uphy_of_match[] = {
>>               .data = &exynos4212_uphy_config,
>>       },
>>  #endif
>> +#ifdef CONFIG_PHY_EXYNOS5250_USB
>> +     {
>> +             .compatible = "samsung,exynos5250-usbphy",
>> +             .data = &exynos5250_uphy_config,
>> +     },
>> +#endif
>>       { },
>>  };
>>
>> diff --git a/drivers/phy/phy-exynos-usb.h b/drivers/phy/phy-exynos-usb.h
>> index f45cb3c..a9febfa 100644
>> --- a/drivers/phy/phy-exynos-usb.h
>> +++ b/drivers/phy/phy-exynos-usb.h
>> @@ -42,6 +42,7 @@ enum samsung_cpu_type {
>>       TYPE_S3C64XX,
>>       TYPE_EXYNOS4210,
>>       TYPE_EXYNOS4212,
>> +     TYPE_EXYNOS5250,
>
> No cpu types here.

One question here.
In case we move to single driver for Exynos4 SoCs (4210, 4212 and 4412
later) as well as S5PV210,
there will be certain things changing from one SoC to another, how
should we target that in case we
don't have CPU types ?
May be i am misinterpreting your suggestion ?

>>  };
>>
>>  enum uphy_state {
>> diff --git a/drivers/phy/phy-exynos5250-usb.c b/drivers/phy/phy-exynos5250-usb.c
>> new file mode 100644
>> index 0000000..156093b
>> --- /dev/null
>> +++ b/drivers/phy/phy-exynos5250-usb.c
>> @@ -0,0 +1,411 @@
>> +/*
>> + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.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-exynos-usb.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         (0x1 << 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          (0x1 << 11)
>> +#define EXYNOS_5250_HOSTPHYCTRL0_RETENABLE           (0x1 << 10)
>> +#define EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N         (0x1 << 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                       (0x1 << 6)
>> +#define EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP          (0x1 << 5)
>> +#define EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND                (0x1 << 4)
>> +#define EXYNOS_5250_HOSTPHYCTRL0_WORDINTERFACE               (0x1 << 3)
>> +#define EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST           (0x1 << 2)
>> +#define EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST           (0x1 << 1)
>> +#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST            (0x1 << 0)
>> +
>> +/* HSIC0 & HSCI1 */
>> +#define EXYNOS_5250_HOSTPHYCTRL1                     0x10
>> +#define EXYNOS_5250_HOSTPHYCTRL2                     0x20
>> +
>> +#define EXYNOS_5250_HOSTPHYCTRLX_REFCLKSEL_MASK              (0x3 << 23)
>> +#define EXYNOS_5250_HOSTPHYCTRLX_REFCLKDIV_MASK              (0x7f << 16)
>> +#define EXYNOS_5250_HOSTPHYCTRLX_SIDDQ                       (0x1 << 6)
>> +#define EXYNOS_5250_HOSTPHYCTRLX_FORCESLEEP          (0x1 << 5)
>> +#define EXYNOS_5250_HOSTPHYCTRLX_FORCESUSPEND                (0x1 << 4)
>> +#define EXYNOS_5250_HOSTPHYCTRLX_WORDINTERFACE               (0x1 << 3)
>> +#define EXYNOS_5250_HOSTPHYCTRLX_UTMISWRST           (0x1 << 2)
>> +#define EXYNOS_5250_HOSTPHYCTRLX_PHYSWRST            (0x1 << 0)
>> +
>> +/* EHCI control */
>> +#define EXYNOS_5250_HOSTEHCICTRL                     0x30
>> +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN               (0x1 << 29)
>> +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR4            (0x1 << 28)
>> +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR8            (0x1 << 27)
>> +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR16           (0x1 << 26)
>> +#define EXYNOS_5250_HOSTEHCICTRL_AUTOPPDONOVRCUREN   (0x1 << 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              (0x1 << 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               (0x1 << 0)
>> +
>> +/* USBOTG */
>> +#define EXYNOS_5250_USBOTGSYS                                0x38
>> +#define EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET               (0x1 << 14)
>> +#define EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG               (0x1 << 13)
>> +#define EXYNOS_5250_USBOTGSYS_PHY_SW_RST             (0x1 << 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                      (0x1 << 8)
>> +#define EXYNOS_5250_USBOTGSYS_COMMON_ON                      (0x1 << 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            (0x1 << 3)
>> +#define EXYNOS_5250_USBOTGSYS_OTGDISABLE             (0x1 << 2)
>> +#define EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG             (0x1 << 1)
>> +#define EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND          (0x1 << 0)
>> +
>> +/* Isolation, configured in the power management unit */
>> +#define EXYNOS_5250_USB_ISOL_OTG_OFFSET              0x0
>> +#define EXYNOS_5250_USB_ISOL_OTG             (1 << 0)
>> +#define EXYNOS_5250_USB_ISOL_HOST_OFFSET     0x4
>> +#define EXYNOS_5250_USB_ISOL_HOST            (1 << 0)
>> +
>> +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. */
>
> Follow coding guidelines ;-)
>> +static u32 exynos5250_rate_to_clk(unsigned long rate)
>> +{
>> +     unsigned int clksel;
>> +
>> +     /* EXYNOS_5250_FSEL_MASK */
>> +
>> +     switch (rate) {
>> +     case 9600 * KHZ:
>> +             clksel = EXYNOS_5250_FSEL_9MHZ6;
>> +             break;
>> +     case 10 * MHZ:
>> +             clksel = EXYNOS_5250_FSEL_10MHZ;
>> +             break;
>> +     case 12 * MHZ:
>> +             clksel = EXYNOS_5250_FSEL_12MHZ;
>> +             break;
>> +     case 19200 * KHZ:
>> +             clksel = EXYNOS_5250_FSEL_19MHZ2;
>> +             break;
>> +     case 20 * MHZ:
>> +             clksel = EXYNOS_5250_FSEL_20MHZ;
>> +             break;
>> +     case 24 * MHZ:
>> +             clksel = EXYNOS_5250_FSEL_24MHZ;
>> +             break;
>> +     case 50 * MHZ:
>> +             clksel = EXYNOS_5250_FSEL_50MHZ;
>> +             break;
>> +     default:
>> +             clksel = CLKSEL_ERROR;
>> +     }
>> +
>> +     return clksel;
>> +}
>> +
>> +static void exynos5250_isol(struct uphy_instance *inst, bool on)
>> +{
>> +     struct uphy_driver *drv = inst->drv;
>> +     u32 offset;
>> +     u32 mask;
>> +     u32 tmp;
>> +
>> +     if (!drv->reg_isol)
>> +             return;
>> +
>> +     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;
>> +     };
>> +
>> +     tmp = readl(drv->reg_isol + offset);
>> +     if (on)
>> +             tmp &= ~mask;
>> +     else
>> +             tmp |= mask;
>> +     writel(tmp, drv->reg_isol + offset);
>> +}
>> +
>> +static void exynos5250_phy_pwr(struct uphy_instance *inst, bool on)
>> +{
>> +     struct uphy_driver *drv = inst->drv;
>> +     u32 ctrl0;
>> +     u32 otg;
>> +     u32 ehci;
>> +     u32 ohci;
>> +
>> +     switch (inst->cfg->id) {
>> +     case EXYNOS5250_DEVICE:
>> +             writel(0, drv->reg_mode);
>> +
>> +             /* OTG configuration */
>> +             otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
>> +             /* The clock */
>> +             otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
>> +             otg |= inst->clk << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
>> +             /* Reset */
>> +             otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
>> +                     EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
>> +                     EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
>> +             /* TODO: Clear 4 bits as the old driver does. */
>> +             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);
>> +
>> +
>> +             break;
>> +     case EXYNOS5250_HOST:
>> +             /* Host registers configuration */
>> +             ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
>> +             /* The clock */
>> +             ctrl0 &= ~EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK;
>> +             ctrl0 |= inst->clk << 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 << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
>> +             /* Reset */
>> +             otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
>> +                     EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
>> +                     EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
>> +             /* TODO: Clear 4 bits as the old driver does. */
>> +             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);
>> +
>> +             /* 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);
>> +             /* Let's do some undocumented magic (based on the old driver) */
>> +             ohci |= 0x1 << 3;
>> +             writel(ohci, drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
>> +
>> +             break;
>> +     }
>> +}
>> +
>> +static int exynos5250_power_on(struct uphy_instance *inst)
>> +{
>> +     struct uphy_driver *drv = inst->drv;
>> +
>> +     if (inst->state == STATE_ON) {
>> +             dev_err(drv->dev, "usb phy \"%s\" already on",
>> +                                                     inst->cfg->label);
>> +             return -ENODEV;
>> +     }
>> +
>> +     inst->state = STATE_ON;
>> +     inst->ref_cnt++;
>> +     if (inst->ref_cnt > 1)
>> +             return 0;
>> +
>> +     exynos5250_phy_pwr(inst, 1);
>> +     exynos5250_isol(inst, 0);
>> +
>> +     /* Power on the device, as it is necessary for HSIC to work */
>> +     if (inst->cfg->id == EXYNOS5250_HSIC0) {
>> +             struct uphy_instance *device =
>> +                                     &drv->uphy_instances[EXYNOS5250_DEVICE];
>> +             device->ref_cnt++;
>> +             if (device->ref_cnt > 1)
>> +                     return 0;
>> +             exynos5250_phy_pwr(device, 1);
>> +             exynos5250_isol(device, 0);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int exynos5250_power_off(struct uphy_instance *inst)
>> +{
>> +     struct uphy_driver *drv = inst->drv;
>> +
>> +     if (inst->state == STATE_OFF) {
>> +             dev_err(drv->dev, "usb phy \"%s\" already off",
>> +                                                     inst->cfg->label);
>> +             return -EINVAL;
>> +     }
>> +
>> +     inst->state = STATE_OFF;
>> +     inst->ref_cnt--;
>> +
>> +     if (inst->ref_cnt > 0)
>> +             return 0;
>> +
>> +     exynos5250_isol(inst, 1);
>> +     exynos5250_phy_pwr(inst, 0);
>> +
>> +     if (inst->cfg->id == EXYNOS5250_HSIC0) {
>> +             struct uphy_instance *device =
>> +                                     &drv->uphy_instances[EXYNOS5250_DEVICE];
>> +             device->ref_cnt--;
>> +             if (device->ref_cnt > 0)
>> +                     return 0;
>> +             exynos5250_isol(device, 1);
>> +             exynos5250_phy_pwr(device, 0);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +
>> +static const struct common_phy exynos5250_phys[] = {
>> +     {
>> +             .label          = "device",
>> +             .type           = PHY_DEVICE,
>> +             .id             = EXYNOS5250_DEVICE,
>> +             .rate_to_clk    = exynos5250_rate_to_clk,
>> +             .power_on       = exynos5250_power_on,
>> +             .power_off      = exynos5250_power_off,
>> +     },
>> +     {
>> +             .label          = "host",
>> +             .type           = PHY_HOST,
>> +             .id             = EXYNOS5250_HOST,
>> +             .rate_to_clk    = exynos5250_rate_to_clk,
>> +             .power_on       = exynos5250_power_on,
>> +             .power_off      = exynos5250_power_off,
>> +     },
>> +     {
>> +             .label          = "hsic0",
>> +             .type           = PHY_HOST,
>> +             .id             = EXYNOS5250_HSIC0,
>> +             .rate_to_clk    = exynos5250_rate_to_clk,
>> +             .power_on       = exynos5250_power_on,
>> +             .power_off      = exynos5250_power_off,
>> +     },
>> +     {
>> +             .label          = "hsic1",
>> +             .type           = PHY_HOST,
>> +             .id             = EXYNOS5250_HSIC1,
>> +             .rate_to_clk    = exynos5250_rate_to_clk,
>> +             .power_on       = exynos5250_power_on,
>> +             .power_off      = exynos5250_power_off,
>> +     },
>> +     {},
>> +};
>
> All this can be removed if you don't use exynos common PHY.
>
> Thanks
> Kishon
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



-- 
Best Regards
Vivek Gautam
Samsung R&D Institute, Bangalore
India

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

* Re: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver
@ 2013-10-28 14:41       ` Vivek Gautam
  0 siblings, 0 replies; 46+ messages in thread
From: Vivek Gautam @ 2013-10-28 14:41 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Kamil Debski, linux-kernel, linux-samsung-soc,
	Linux USB Mailing List, devicetree, linux-arm, Kyungmin Park,
	Tomasz Figa, Sylwester Nawrocki, Marek Szyprowski, Vivek Gautam,
	Mateusz Krawczuk

Hi Kishon,


On Fri, Oct 25, 2013 at 9:13 PM, Kishon Vijay Abraham I <kishon@ti.com> wrote:
> Hi,
>
> On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
>> Add support for Exynos 5250. This is work-in-progress commit. Not
>> for merging.
>>
>> Signed-off-by: Kamil Debski <k.debski@samsung.com>
>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>> ---
>>  drivers/phy/Kconfig              |    7 +
>>  drivers/phy/Makefile             |    1 +
>>  drivers/phy/phy-exynos-usb.c     |   10 +
>>  drivers/phy/phy-exynos-usb.h     |    1 +
>>  drivers/phy/phy-exynos5250-usb.c |  411 ++++++++++++++++++++++++++++++++++++++
>>  5 files changed, 430 insertions(+)
>>  create mode 100644 drivers/phy/phy-exynos5250-usb.c
>>
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index 2f7ac0a..0f598d0 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -36,4 +36,11 @@ config PHY_EXYNOS4212_USB
>>       help
>>         Enable USB PHY support for Exynos 4212
>>
>> +config PHY_EXYNOS5250_USB
>> +     bool "Support for Exynos 5250"
>> +     depends on PHY_EXYNOS_USB
>
> This should be a separate driver. Not necessary to use PHY_EXYNOS_USB.
>> +     depends on SOC_EXYNOS5250
>> +     help
>> +       Enable USB PHY support for Exynos 5250
>> +
>>  endmenu
>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
>> index ca3dc82..0dff0dd 100644
>> --- a/drivers/phy/Makefile
>> +++ b/drivers/phy/Makefile
>> @@ -6,3 +6,4 @@ obj-$(CONFIG_GENERIC_PHY)     += phy-core.o
>>  obj-$(CONFIG_PHY_EXYNOS_USB)         += phy-exynos-usb.o
>>  obj-$(CONFIG_PHY_EXYNOS4210_USB)     += phy-exynos4210-usb.o
>>  obj-$(CONFIG_PHY_EXYNOS4212_USB)     += phy-exynos4212-usb.o
>> +obj-$(CONFIG_PHY_EXYNOS5250_USB)     += phy-exynos5250-usb.o
>> diff --git a/drivers/phy/phy-exynos-usb.c b/drivers/phy/phy-exynos-usb.c
>> index d4a26df..172b774 100644
>> --- a/drivers/phy/phy-exynos-usb.c
>> +++ b/drivers/phy/phy-exynos-usb.c
>> @@ -212,6 +212,10 @@ extern const struct uphy_config exynos4210_uphy_config;
>>  extern const struct uphy_config exynos4212_uphy_config;
>>  #endif
>>
>> +#ifdef CONFIG_PHY_EXYNOS5250_USB
>> +extern const struct uphy_config exynos5250_uphy_config;
>> +#endif
>> +
>>  static const struct of_device_id exynos_uphy_of_match[] = {
>>  #ifdef CONFIG_PHY_EXYNOS4210_USB
>>       {
>> @@ -225,6 +229,12 @@ static const struct of_device_id exynos_uphy_of_match[] = {
>>               .data = &exynos4212_uphy_config,
>>       },
>>  #endif
>> +#ifdef CONFIG_PHY_EXYNOS5250_USB
>> +     {
>> +             .compatible = "samsung,exynos5250-usbphy",
>> +             .data = &exynos5250_uphy_config,
>> +     },
>> +#endif
>>       { },
>>  };
>>
>> diff --git a/drivers/phy/phy-exynos-usb.h b/drivers/phy/phy-exynos-usb.h
>> index f45cb3c..a9febfa 100644
>> --- a/drivers/phy/phy-exynos-usb.h
>> +++ b/drivers/phy/phy-exynos-usb.h
>> @@ -42,6 +42,7 @@ enum samsung_cpu_type {
>>       TYPE_S3C64XX,
>>       TYPE_EXYNOS4210,
>>       TYPE_EXYNOS4212,
>> +     TYPE_EXYNOS5250,
>
> No cpu types here.

One question here.
In case we move to single driver for Exynos4 SoCs (4210, 4212 and 4412
later) as well as S5PV210,
there will be certain things changing from one SoC to another, how
should we target that in case we
don't have CPU types ?
May be i am misinterpreting your suggestion ?

>>  };
>>
>>  enum uphy_state {
>> diff --git a/drivers/phy/phy-exynos5250-usb.c b/drivers/phy/phy-exynos5250-usb.c
>> new file mode 100644
>> index 0000000..156093b
>> --- /dev/null
>> +++ b/drivers/phy/phy-exynos5250-usb.c
>> @@ -0,0 +1,411 @@
>> +/*
>> + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.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-exynos-usb.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         (0x1 << 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          (0x1 << 11)
>> +#define EXYNOS_5250_HOSTPHYCTRL0_RETENABLE           (0x1 << 10)
>> +#define EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N         (0x1 << 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                       (0x1 << 6)
>> +#define EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP          (0x1 << 5)
>> +#define EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND                (0x1 << 4)
>> +#define EXYNOS_5250_HOSTPHYCTRL0_WORDINTERFACE               (0x1 << 3)
>> +#define EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST           (0x1 << 2)
>> +#define EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST           (0x1 << 1)
>> +#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST            (0x1 << 0)
>> +
>> +/* HSIC0 & HSCI1 */
>> +#define EXYNOS_5250_HOSTPHYCTRL1                     0x10
>> +#define EXYNOS_5250_HOSTPHYCTRL2                     0x20
>> +
>> +#define EXYNOS_5250_HOSTPHYCTRLX_REFCLKSEL_MASK              (0x3 << 23)
>> +#define EXYNOS_5250_HOSTPHYCTRLX_REFCLKDIV_MASK              (0x7f << 16)
>> +#define EXYNOS_5250_HOSTPHYCTRLX_SIDDQ                       (0x1 << 6)
>> +#define EXYNOS_5250_HOSTPHYCTRLX_FORCESLEEP          (0x1 << 5)
>> +#define EXYNOS_5250_HOSTPHYCTRLX_FORCESUSPEND                (0x1 << 4)
>> +#define EXYNOS_5250_HOSTPHYCTRLX_WORDINTERFACE               (0x1 << 3)
>> +#define EXYNOS_5250_HOSTPHYCTRLX_UTMISWRST           (0x1 << 2)
>> +#define EXYNOS_5250_HOSTPHYCTRLX_PHYSWRST            (0x1 << 0)
>> +
>> +/* EHCI control */
>> +#define EXYNOS_5250_HOSTEHCICTRL                     0x30
>> +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN               (0x1 << 29)
>> +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR4            (0x1 << 28)
>> +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR8            (0x1 << 27)
>> +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR16           (0x1 << 26)
>> +#define EXYNOS_5250_HOSTEHCICTRL_AUTOPPDONOVRCUREN   (0x1 << 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              (0x1 << 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               (0x1 << 0)
>> +
>> +/* USBOTG */
>> +#define EXYNOS_5250_USBOTGSYS                                0x38
>> +#define EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET               (0x1 << 14)
>> +#define EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG               (0x1 << 13)
>> +#define EXYNOS_5250_USBOTGSYS_PHY_SW_RST             (0x1 << 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                      (0x1 << 8)
>> +#define EXYNOS_5250_USBOTGSYS_COMMON_ON                      (0x1 << 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            (0x1 << 3)
>> +#define EXYNOS_5250_USBOTGSYS_OTGDISABLE             (0x1 << 2)
>> +#define EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG             (0x1 << 1)
>> +#define EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND          (0x1 << 0)
>> +
>> +/* Isolation, configured in the power management unit */
>> +#define EXYNOS_5250_USB_ISOL_OTG_OFFSET              0x0
>> +#define EXYNOS_5250_USB_ISOL_OTG             (1 << 0)
>> +#define EXYNOS_5250_USB_ISOL_HOST_OFFSET     0x4
>> +#define EXYNOS_5250_USB_ISOL_HOST            (1 << 0)
>> +
>> +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. */
>
> Follow coding guidelines ;-)
>> +static u32 exynos5250_rate_to_clk(unsigned long rate)
>> +{
>> +     unsigned int clksel;
>> +
>> +     /* EXYNOS_5250_FSEL_MASK */
>> +
>> +     switch (rate) {
>> +     case 9600 * KHZ:
>> +             clksel = EXYNOS_5250_FSEL_9MHZ6;
>> +             break;
>> +     case 10 * MHZ:
>> +             clksel = EXYNOS_5250_FSEL_10MHZ;
>> +             break;
>> +     case 12 * MHZ:
>> +             clksel = EXYNOS_5250_FSEL_12MHZ;
>> +             break;
>> +     case 19200 * KHZ:
>> +             clksel = EXYNOS_5250_FSEL_19MHZ2;
>> +             break;
>> +     case 20 * MHZ:
>> +             clksel = EXYNOS_5250_FSEL_20MHZ;
>> +             break;
>> +     case 24 * MHZ:
>> +             clksel = EXYNOS_5250_FSEL_24MHZ;
>> +             break;
>> +     case 50 * MHZ:
>> +             clksel = EXYNOS_5250_FSEL_50MHZ;
>> +             break;
>> +     default:
>> +             clksel = CLKSEL_ERROR;
>> +     }
>> +
>> +     return clksel;
>> +}
>> +
>> +static void exynos5250_isol(struct uphy_instance *inst, bool on)
>> +{
>> +     struct uphy_driver *drv = inst->drv;
>> +     u32 offset;
>> +     u32 mask;
>> +     u32 tmp;
>> +
>> +     if (!drv->reg_isol)
>> +             return;
>> +
>> +     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;
>> +     };
>> +
>> +     tmp = readl(drv->reg_isol + offset);
>> +     if (on)
>> +             tmp &= ~mask;
>> +     else
>> +             tmp |= mask;
>> +     writel(tmp, drv->reg_isol + offset);
>> +}
>> +
>> +static void exynos5250_phy_pwr(struct uphy_instance *inst, bool on)
>> +{
>> +     struct uphy_driver *drv = inst->drv;
>> +     u32 ctrl0;
>> +     u32 otg;
>> +     u32 ehci;
>> +     u32 ohci;
>> +
>> +     switch (inst->cfg->id) {
>> +     case EXYNOS5250_DEVICE:
>> +             writel(0, drv->reg_mode);
>> +
>> +             /* OTG configuration */
>> +             otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
>> +             /* The clock */
>> +             otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
>> +             otg |= inst->clk << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
>> +             /* Reset */
>> +             otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
>> +                     EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
>> +                     EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
>> +             /* TODO: Clear 4 bits as the old driver does. */
>> +             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);
>> +
>> +
>> +             break;
>> +     case EXYNOS5250_HOST:
>> +             /* Host registers configuration */
>> +             ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
>> +             /* The clock */
>> +             ctrl0 &= ~EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK;
>> +             ctrl0 |= inst->clk << 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 << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
>> +             /* Reset */
>> +             otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
>> +                     EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
>> +                     EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
>> +             /* TODO: Clear 4 bits as the old driver does. */
>> +             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);
>> +
>> +             /* 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);
>> +             /* Let's do some undocumented magic (based on the old driver) */
>> +             ohci |= 0x1 << 3;
>> +             writel(ohci, drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
>> +
>> +             break;
>> +     }
>> +}
>> +
>> +static int exynos5250_power_on(struct uphy_instance *inst)
>> +{
>> +     struct uphy_driver *drv = inst->drv;
>> +
>> +     if (inst->state == STATE_ON) {
>> +             dev_err(drv->dev, "usb phy \"%s\" already on",
>> +                                                     inst->cfg->label);
>> +             return -ENODEV;
>> +     }
>> +
>> +     inst->state = STATE_ON;
>> +     inst->ref_cnt++;
>> +     if (inst->ref_cnt > 1)
>> +             return 0;
>> +
>> +     exynos5250_phy_pwr(inst, 1);
>> +     exynos5250_isol(inst, 0);
>> +
>> +     /* Power on the device, as it is necessary for HSIC to work */
>> +     if (inst->cfg->id == EXYNOS5250_HSIC0) {
>> +             struct uphy_instance *device =
>> +                                     &drv->uphy_instances[EXYNOS5250_DEVICE];
>> +             device->ref_cnt++;
>> +             if (device->ref_cnt > 1)
>> +                     return 0;
>> +             exynos5250_phy_pwr(device, 1);
>> +             exynos5250_isol(device, 0);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int exynos5250_power_off(struct uphy_instance *inst)
>> +{
>> +     struct uphy_driver *drv = inst->drv;
>> +
>> +     if (inst->state == STATE_OFF) {
>> +             dev_err(drv->dev, "usb phy \"%s\" already off",
>> +                                                     inst->cfg->label);
>> +             return -EINVAL;
>> +     }
>> +
>> +     inst->state = STATE_OFF;
>> +     inst->ref_cnt--;
>> +
>> +     if (inst->ref_cnt > 0)
>> +             return 0;
>> +
>> +     exynos5250_isol(inst, 1);
>> +     exynos5250_phy_pwr(inst, 0);
>> +
>> +     if (inst->cfg->id == EXYNOS5250_HSIC0) {
>> +             struct uphy_instance *device =
>> +                                     &drv->uphy_instances[EXYNOS5250_DEVICE];
>> +             device->ref_cnt--;
>> +             if (device->ref_cnt > 0)
>> +                     return 0;
>> +             exynos5250_isol(device, 1);
>> +             exynos5250_phy_pwr(device, 0);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +
>> +static const struct common_phy exynos5250_phys[] = {
>> +     {
>> +             .label          = "device",
>> +             .type           = PHY_DEVICE,
>> +             .id             = EXYNOS5250_DEVICE,
>> +             .rate_to_clk    = exynos5250_rate_to_clk,
>> +             .power_on       = exynos5250_power_on,
>> +             .power_off      = exynos5250_power_off,
>> +     },
>> +     {
>> +             .label          = "host",
>> +             .type           = PHY_HOST,
>> +             .id             = EXYNOS5250_HOST,
>> +             .rate_to_clk    = exynos5250_rate_to_clk,
>> +             .power_on       = exynos5250_power_on,
>> +             .power_off      = exynos5250_power_off,
>> +     },
>> +     {
>> +             .label          = "hsic0",
>> +             .type           = PHY_HOST,
>> +             .id             = EXYNOS5250_HSIC0,
>> +             .rate_to_clk    = exynos5250_rate_to_clk,
>> +             .power_on       = exynos5250_power_on,
>> +             .power_off      = exynos5250_power_off,
>> +     },
>> +     {
>> +             .label          = "hsic1",
>> +             .type           = PHY_HOST,
>> +             .id             = EXYNOS5250_HSIC1,
>> +             .rate_to_clk    = exynos5250_rate_to_clk,
>> +             .power_on       = exynos5250_power_on,
>> +             .power_off      = exynos5250_power_off,
>> +     },
>> +     {},
>> +};
>
> All this can be removed if you don't use exynos common PHY.
>
> Thanks
> Kishon
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



-- 
Best Regards
Vivek Gautam
Samsung R&D Institute, Bangalore
India

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

* Re: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver
  2013-10-28 13:52       ` Kamil Debski
  (?)
@ 2013-10-28 20:00       ` Tomasz Figa
  2013-10-29 10:16         ` Kamil Debski
  -1 siblings, 1 reply; 46+ messages in thread
From: Tomasz Figa @ 2013-10-28 20:00 UTC (permalink / raw)
  To: Kamil Debski
  Cc: 'Kishon Vijay Abraham I',
	linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, kyungmin.park, Tomasz Figa, Sylwester Nawrocki,
	Marek Szyprowski, gautam.vivek, mat.krawczuk

Hi Kamil,

On Monday 28 of October 2013 14:52:19 Kamil Debski wrote:
> Hi Kishon,
> 
> Thank you for your review! I will answer your comments below.
[snip]
> > > +
> > > +	switch (drv->cfg->cpu) {
> > > +	case TYPE_EXYNOS4210:
> > 
> > > +	case TYPE_EXYNOS4212:
> > Lets not add such cpu checks inside driver.
> 
> Some SoC have a special register, which switches the OTG lines between
> device and host modes. I understand that it might not be the prettiest
> code. I see this as a good compromise between having a single huge
> driver for all Exynos SoCs and having a multiple drivers for each SoC
> version. PHY IPs in these chips very are similar but have to be handled
> differently. Any other ideas to solve this issue?

Maybe adding a flag in drv->cfg called, for example, .has_mode_switch 
could solve this problem without having to check the SoC type explicitly?

[snip]
> > > +#ifdef CONFIG_PHY_EXYNOS4210_USB
> > 
> > Do we really need this?
> 
> No we don't. The driver can always support all Exynos SoC versions.
> These config options were added for flexibility.
> 
> > > +extern const struct uphy_config exynos4210_uphy_config; #endif
> > > +
> > > +#ifdef CONFIG_PHY_EXYNOS4212_USB
> > 
> > Same here.
> > 
> > > +extern const struct uphy_config exynos4212_uphy_config; #endif
> > > +
> > > +static const struct of_device_id exynos_uphy_of_match[] = { #ifdef
> > > +CONFIG_PHY_EXYNOS4210_USB
> > 
> > #if not needed here.
> 
> If the #ifdef CONFIG_PHY_EXYNOS4210_USB is removed then yes. Otherwise
> it is necessary - exynos4210_uphy_config may be undefined.

I believe this and other ifdefs below are needed, otherwise, with support
for one of the SoCs disabled, the driver could still bind to its 
compatible value.

Best regards,
Tomasz


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

* Re: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver
@ 2013-10-29  9:52         ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 46+ messages in thread
From: Kishon Vijay Abraham I @ 2013-10-29  9:52 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, kyungmin.park, Tomasz Figa, Sylwester Nawrocki,
	Marek Szyprowski, gautam.vivek, mat.krawczuk

Hi,

On Monday 28 October 2013 07:22 PM, Kamil Debski wrote:
> Hi Kishon,
> 
> Thank you for your review! I will answer your comments below.
> 
>> From: Kishon Vijay Abraham I [mailto:kishon@ti.com]
>> Sent: Friday, October 25, 2013 5:40 PM
>>
>> Hi,
>>
>> On Friday 25 October 2013 07:45 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.
>>>
>>> Signed-off-by: Kamil Debski <k.debski@samsung.com>
>>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>>> ---
>>>  .../devicetree/bindings/phy/samsung-usbphy.txt     |   51 +++
>>>  drivers/phy/Kconfig                                |   21 ++
>>>  drivers/phy/Makefile                               |    3 +
>>>  drivers/phy/phy-exynos-usb.c                       |  245
>> ++++++++++++++
>>>  drivers/phy/phy-exynos-usb.h                       |   94 ++++++
>>>  drivers/phy/phy-exynos4210-usb.c                   |  295
>> +++++++++++++++++
>>>  drivers/phy/phy-exynos4212-usb.c                   |  343
>> ++++++++++++++++++++
>>>  7 files changed, 1052 insertions(+)
>>>  create mode 100644
>>> Documentation/devicetree/bindings/phy/samsung-usbphy.txt
>>>  create mode 100644 drivers/phy/phy-exynos-usb.c  create mode 100644
>>> drivers/phy/phy-exynos-usb.h  create mode 100644
>>> drivers/phy/phy-exynos4210-usb.c  create mode 100644
>>> drivers/phy/phy-exynos4212-usb.c
>>>
>>> diff --git a/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
>>> b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
>>> new file mode 100644
>>> index 0000000..f112b37
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
>>> @@ -0,0 +1,51 @@
>>> +Samsung S5P/EXYNOS SoC series USB PHY
>>> +-------------------------------------------------
>>> +
>>> +Required properties:
>>> +- compatible : should be one of the listed compatibles:
>>> +	- "samsung,exynos4210-usbphy"
>>> +	- "samsung,exynos4212-usbphy"
>>> +- reg : a list of registers used by phy driver
>>> +	- first and obligatory is the location of phy modules registers
>>> +	- second and also required is the location of isolation registers
>>> +	  (isolation registers control the physical connection between
>> the in
>>> +	  SoC modules and outside of the SoC, this also can be called
>> enable
>>> +	  control in the documentation of the SoC)
>>> +	- third is the location of the mode switch register, this only
>> applies
>>> +	  to SoCs that have such a feature; mode switching enables to
>> have
>>> +	  both host and device used the same SoC pins and is commonly
>> used
>>> +	  when OTG is supported
>>> +- #phy-cells : from the generic phy bindings, must be 1;
>>> +
>>> +The second cell in the PHY specifier identifies the PHY its meaning
>>> +is SoC dependent. For the currently supported SoCs (Exynos 4210 and
>>> +Exynos 4212) it is as follows:
>>> +  0 - USB device,
>>> +  1 - USB host,
>>> +  2 - HSIC0,
>>> +  3 - HSIC1,
>>
>> HSIC is supposedly to be transceiver less no? You have to program
>> something in the digital side?
>> You have a single IP that have all these functionalities?
> 
> There is a single USB PHY controller for all the above functionalities
> (i.e. host, device, hsic 0 and 1).

Ok.
> 
>>> +
>>> +Example:
>>> +
>>> +For Exynos 4412 (compatible with Exynos 4212):
>>> +
>>> +exynos_usbphy: exynos-usbphy@125B0000 {
>>> +	compatible = "samsung,exynos4212-usbphy";
>>> +	reg = <0x125B0000 0x100 0x10020704 0x0c 0x1001021c 0x4>;
>>> +	ranges;
>>> +	#address-cells = <1>;
>>> +	#size-cells = <1>;
>>
>> The above 3 properties aren't documented? Are they needed here?
> 
> My bad. I was working on two branches and corrected it in only one
> of them.
> 
>>> +	clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
>>> +							<&clock 2>;
>>> +	clock-names = "phy", "device", "host", "hsic0", "hsic1";
>>> +	status = "okay";
>>> +	#phy-cells = <1>;
>>> +};
>>> +
>>> +Then the PHY can be used in other nodes such as:
>>> +
>>> +ehci@12580000 {
>>> +	status = "okay";
>>> +	phys = <&exynos_usbphy 2>;
>>> +	phy-names = "hsic0";
>>> +};
>>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index
>>> 349bef2..2f7ac0a 100644
>>> --- a/drivers/phy/Kconfig
>>> +++ b/drivers/phy/Kconfig
>>> @@ -15,4 +15,25 @@ config GENERIC_PHY
>>>  	  phy users can obtain reference to the PHY. All the users of
>> this
>>>  	  framework should select this config.
>>>
>>> +config PHY_EXYNOS_USB
>>> +	tristate "Samsung USB PHY driver (using the Generic PHY
>> Framework)"
>> Mentioning *Generic PHY Framework* is not necessary.
>> *select GENERIC_PHY* here
> 
> This was added to distinguish this driver from the ols USB PHY driver.
> I agree that in the final version it should be removed. I understand that
> the correct way to do this is by removing the old driver when the new gets
> merged. Yes?

right.
> 
>>> +	help
>>> +	  Enable this to support Samsung USB phy helper driver for
>> Samsung SoCs.
>>> +	  This driver provides common interface to interact, for Samsung
>>> +	  USB 2.0 PHY driver.
>>
>> If it's going to be used only for usb2, name it PHY_EXYNOS_USB2.
> 
> I agree.
> 
>>> +
>>> +config PHY_EXYNOS4210_USB
>>> +	bool "Support for Exynos 4210"
>>> +	depends on PHY_EXYNOS_USB
>>> +	depends on CPU_EXYNOS4210
>>> +	help
>>> +	  Enable USB PHY support for Exynos 4210
>>> +
>>> +config PHY_EXYNOS4212_USB
>>> +	bool "Support for Exynos 4212"
>>> +	depends on PHY_EXYNOS_USB
>>> +	depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
>>> +	help
>>> +	  Enable USB PHY support for Exynos 4212
>>
>> How difference is USB PHY in Exynos 4212 from Exynos 4210? If th
> ere
>> isn't much, I would suggest to use a single driver.
> 
> My idea for the driver is to have a single driver for the whole Exynos USB2
> PHY. The core of the driver is in phy-exynos-usb.c and phy-exynos*-usb.c
> provide registers and setup sequences for particular SoC versions.
> 
> There are a few differences between Exynos 4210, 4212, 5250 and S5PV210:
> - the registers are different on each
> - the setup sequence is different 
> - 4212 and 5250 have a single pair of regular USB OTG DEVICE/HOST lines
> - 4210 has a USB OTG and a separate USB HOST lines
> - S5PV210 has both USB OTG and a separate USB HOST but lacks any HSIC
>   interfaces
> 
> I agree with you that it is best to add as little code as possible.
> Hence the idea to have a separate driver core and additional files for
> distinctive SoCs. Enabling PHY_EXYNOS4210_USB (or other SoC specific
> options)
> does not add another driver it only includes support for enabled SoC.
> 
> It would be possible to skip this distinction altogether and include support
> for all SoCs in the driver without config options.
> 
>>> +
>>>  endmenu
>>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index
>>> 9e9560f..ca3dc82 100644
>>> --- a/drivers/phy/Makefile
>>> +++ b/drivers/phy/Makefile
>>> @@ -3,3 +3,6 @@
>>>  #
>>>
>>>  obj-$(CONFIG_GENERIC_PHY)	+= phy-core.o
>>> +obj-$(CONFIG_PHY_EXYNOS_USB)		+= phy-exynos-usb.o
>>> +obj-$(CONFIG_PHY_EXYNOS4210_USB)	+= phy-exynos4210-usb.o
>>> +obj-$(CONFIG_PHY_EXYNOS4212_USB)	+= phy-exynos4212-usb.o
>>> diff --git a/drivers/phy/phy-exynos-usb.c
>>> b/drivers/phy/phy-exynos-usb.c new file mode 100644 index
>>> 0000000..d4a26df
>>> --- /dev/null
>>> +++ b/drivers/phy/phy-exynos-usb.c
>>
>> phy-exynos-usb2.c?
> 
> Ok.
> 
>>> @@ -0,0 +1,245 @@
>>> +/*
>>> + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
>>> +#include <linux/io.h>
>>> +#include <linux/kernel.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-exynos-usb.h"
>>> +
>>> +static int exynos_uphy_power_on(struct phy *phy)
>>
>> exynos_usb2_phy here and everywhere below.
> 
> Ok.
> 
>>> +{
>>> +	struct uphy_instance *inst = phy_get_drvdata(phy);
>>> +	struct uphy_driver *drv = inst->drv;
>>> +	int ret;
>>> +
>>> +	dev_info(drv->dev, "Request to power_on \"%s\" usb phy\n",
>>> +							inst->cfg->label);
>>
>> make it dev_dbg if it's necessary.
> 
> Ok.
> 
>>> +	ret = clk_prepare_enable(drv->clk);
>>> +	if (ret)
>>> +		return ret;
>>> +	if (inst->cfg->power_on) {
>>> +		spin_lock(&drv->lock);
>>> +		ret = inst->cfg->power_on(inst);
>>> +		spin_unlock(&drv->lock);
>>> +	}
>>> +	clk_disable_unprepare(drv->clk);
>>
>> hmm.. don't you need the clock to be on during the duration of the PHY
>> operation?
> 
> I think that it is enough to have the clock enabled only during changes.
> 
>>> +	return ret;
>>> +}
>>> +
>>> +static int exynos_uphy_power_off(struct phy *phy) {
>>> +	struct uphy_instance *inst = phy_get_drvdata(phy);
>>> +	struct uphy_driver *drv = inst->drv;
>>> +	int ret;
>>> +
>>> +	dev_info(drv->dev, "Request to power_off \"%s\" usb phy\n",
>>> +							inst->cfg->label);
>>
>> dev_dbg?
> 
> Ok.
> 
>>> +	ret = clk_prepare_enable(drv->clk);
>>> +	if (ret)
>>> +		return ret;
>>> +	if (inst->cfg->power_off) {
>>> +		spin_lock(&drv->lock);
>>> +		ret = inst->cfg->power_off(inst);
>>> +		spin_unlock(&drv->lock);
>>> +	}
>>> +	clk_disable_unprepare(drv->clk);
>>> +	return ret;
>>> +}
>>> +
>>> +static struct phy_ops exynos_uphy_ops = {
>>> +	.power_on	= exynos_uphy_power_on,
>>> +	.power_off	= exynos_uphy_power_off,
>>> +	.owner		= THIS_MODULE,
>>> +};
>>> +
>>> +static struct phy *exynos_uphy_xlate(struct device *dev,
>>> +					struct of_phandle_args *args)
>>> +{
>>> +	struct uphy_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->uphy_instances[args->args[0]].phy;
>>> +}
>>> +
>>> +static const struct of_device_id exynos_uphy_of_match[];
>>> +
>>> +static int exynos_uphy_probe(struct platform_device *pdev) {
>>> +	struct uphy_driver *drv;
>>> +	struct device *dev = &pdev->dev;
>>> +	struct resource *mem;
>>> +	struct phy_provider *phy_provider;
>>> +
>>> +	const struct of_device_id *match;
>>> +	const struct uphy_config *cfg;
>>> +	struct clk *clk;
>>> +
>>> +	int i;
>>> +
>>> +	match = of_match_node(exynos_uphy_of_match, pdev->dev.of_node);
>>> +	if (!match) {
>>> +		dev_err(dev, "of_match_node() failed\n");
>>> +		return -EINVAL;
>>> +	}
>>> +	cfg = match->data;
>>> +	if (!cfg) {
>>> +		dev_err(dev, "Failed to get configuration\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	drv = devm_kzalloc(dev, sizeof(struct uphy_driver) +
>>> +		cfg->num_phys * sizeof(struct uphy_instance), GFP_KERNEL);
>>> +
>>> +	if (!drv) {
>>> +		dev_err(dev, "Failed to allocate memory\n");
>>> +		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);
>>> +
>> empty blank line.
> 
> Will fix.
> 
>>> +	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);
>>> +	}
>>> +
>>> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>>> +	drv->reg_isol = devm_ioremap_resource(dev, mem);
>>> +	if (IS_ERR(drv->reg_isol)) {
>>> +		dev_err(dev, "Failed to map register memory (isolation)\n");
>>> +		return PTR_ERR(drv->reg_isol);
>>> +	}
>>> +
>>> +	switch (drv->cfg->cpu) {
>>> +	case TYPE_EXYNOS4210:
>>> +	case TYPE_EXYNOS4212:
>>
>> Lets not add such cpu checks inside driver.
> 
> Some SoC have a special register, which switches the OTG lines between
> device and host modes. I understand that it might not be the prettiest
> code. I see this as a good compromise between having a single huge
> driver for all Exynos SoCs and having a multiple drivers for each SoC
> version. PHY IPs in these chips very are similar but have to be handled
> differently. Any other ideas to solve this issue?

revision checks?
> 
>>> +		mem = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>>> +		drv->reg_mode = devm_ioremap_resource(dev, mem);
>>> +		if (IS_ERR(drv->reg_mode)) {
>>> +			dev_err(dev, "Failed to map register memory (mode
>> switch)\n");
>>> +			return PTR_ERR(drv->reg_mode);
>>> +		}
>>> +		break;
>>> +	default:
>>> +		break;
>>> +	}
>>> +
>>> +	phy_provider = devm_of_phy_provider_register(dev,
>>> +							exynos_uphy_xlate);
>>> +	if (IS_ERR(phy_provider)) {
>>> +		dev_err(drv->dev, "Failed to register phy provider\n");
>>> +		return PTR_ERR(phy_provider);
>>> +	}
>>> +
>>> +	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 uphy_instance *p = &drv->uphy_instances[i];
>>> +
>>> +		dev_info(dev, "Creating phy \"%s\"\n", label);
>>> +		p->phy = devm_phy_create(dev, &exynos_uphy_ops, NULL);
>>> +		if (IS_ERR(p->phy)) {
>>> +			dev_err(drv->dev, "Failed to create uphy \"%s\"\n",
>>> +						label);
>>> +			return PTR_ERR(p->phy);
>>> +		}
>>> +
>>> +		p->cfg = &drv->cfg->phys[i];
>>> +		p->drv = drv;
>>> +		phy_set_drvdata(p->phy, p);
>>> +
>>> +		clk = 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->rate = clk_get_rate(clk);
>>> +
>>> +		if (p->cfg->rate_to_clk) {
>>> +			p->clk = p->cfg->rate_to_clk(p->rate);
>>> +			if (p->clk == CLKSEL_ERROR) {
>>> +				dev_err(dev, "Clock rate (%ld) not
> supported\n",
>>> +								p->rate);
>>> +				clk_put(clk);
>>> +				return -EINVAL;
>>> +			}
>>> +		}
>>> +		clk_put(clk);
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +#ifdef CONFIG_PHY_EXYNOS4210_USB
>> Do we really need this?
> 
> No we don't. The driver can always support all Exynos SoC versions. These
> config options were added for flexibility.
> 
>>
>>> +extern const struct uphy_config exynos4210_uphy_config; #endif
>>> +
>>> +#ifdef CONFIG_PHY_EXYNOS4212_USB
>>
>> Same here.
>>> +extern const struct uphy_config exynos4212_uphy_config; #endif
>>> +
>>> +static const struct of_device_id exynos_uphy_of_match[] = { #ifdef
>>> +CONFIG_PHY_EXYNOS4210_USB
>>
>> #if not needed here.
> 
> If the #ifdef CONFIG_PHY_EXYNOS4210_USB is removed then yes. Otherwise
> it is necessary - exynos4210_uphy_config may be undefined.
> 
>>> +	{
>>> +		.compatible = "samsung,exynos4210-usbphy",
>>> +		.data = &exynos4210_uphy_config,
>>> +	},
>>> +#endif
>>> +#ifdef CONFIG_PHY_EXYNOS4212_USB
>>
>> here too.
>>> +	{
>>> +		.compatible = "samsung,exynos4212-usbphy",
>>> +		.data = &exynos4212_uphy_config,
>>> +	},
>>> +#endif
>>> +	{ },
>>> +};
>>> +
>>> +static struct platform_driver exynos_uphy_driver = {
>>> +	.probe	= exynos_uphy_probe,
>>> +	.driver = {
>>> +		.of_match_table	= exynos_uphy_of_match,
>>> +		.name		= "exynos-usbphy-new",
>> "exynos-usb2-phy".
>>> +		.owner		= THIS_MODULE,
>>> +	}
>>> +};
>>> +
>>> +module_platform_driver(exynos_uphy_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:exynos-uphy-new");
>>> +
>>> diff --git a/drivers/phy/phy-exynos-usb.h
>>> b/drivers/phy/phy-exynos-usb.h new file mode 100644 index
>>> 0000000..f45cb3c
>>> --- /dev/null
>>> +++ b/drivers/phy/phy-exynos-usb.h
>>> @@ -0,0 +1,94 @@
>>> +/*
>>> + * Samsung S5P/EXYNOS SoC series USB 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_SAMSUNG_NEW_H
>>> +#define _PHY_SAMSUNG_NEW_H
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/io.h>
>>> +#include <linux/kernel.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>
>>> +
>>> +#define CLKSEL_ERROR                       -1
>>> +
>>> +#ifndef KHZ
>>> +#define KHZ 1000
>>> +#endif
>>> +
>>> +#ifndef MHZ
>>> +#define MHZ (KHZ * KHZ)
>>> +#endif
>>> +
>>> +enum phy_type {
>>> +	PHY_DEVICE,
>>> +	PHY_HOST,
>>> +};
>>> +
>>> +enum samsung_cpu_type {
>>> +	TYPE_S3C64XX,
>>> +	TYPE_EXYNOS4210,
>>> +	TYPE_EXYNOS4212,
>>
>> No *cpu_type* inside driver files.
> 
> I guess that you are in favor a "a separate driver for each phy version".
> For me it can be both. But we have to discuss which apporach is better:
> 1) separate driver for each phy version - no iffs and significant code
>    duplication

Creating separate driver for each PHY version is not recommended.
drivers/usb/dwc3/dwc3-omap.c is used for two different SoCs with different
register offsets for your reference.
> 2) a single driver driver supporting all Exynos variants - it needs ifs,
>    code is always bigger

IMO it can be done without #if's. Use revision checks or compatible values.
> 3) a single driver with support for particular SoC enabled in the config
> file
>    - with ifs, but the driver can be compiled smaller

Sorry, don't prefer ifs in driver files.

Thanks
Kishon

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

* Re: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver
@ 2013-10-29  9:52         ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 46+ messages in thread
From: Kishon Vijay Abraham I @ 2013-10-29  9:52 UTC (permalink / raw)
  To: Kamil Debski
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-u79uwXL29TY76Z2rM5mHXA,
	kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ, Tomasz Figa,
	Sylwester Nawrocki, Marek Szyprowski,
	gautam.vivek-Sze3O3UU22JBDgjK7y7TUQ,
	mat.krawczuk-Re5JQEeQqe8AvxtiuMwx3w

Hi,

On Monday 28 October 2013 07:22 PM, Kamil Debski wrote:
> Hi Kishon,
> 
> Thank you for your review! I will answer your comments below.
> 
>> From: Kishon Vijay Abraham I [mailto:kishon-l0cyMroinI0@public.gmane.org]
>> Sent: Friday, October 25, 2013 5:40 PM
>>
>> Hi,
>>
>> On Friday 25 October 2013 07:45 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.
>>>
>>> Signed-off-by: Kamil Debski <k.debski-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>>> Signed-off-by: Kyungmin Park <kyungmin.park-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>>> ---
>>>  .../devicetree/bindings/phy/samsung-usbphy.txt     |   51 +++
>>>  drivers/phy/Kconfig                                |   21 ++
>>>  drivers/phy/Makefile                               |    3 +
>>>  drivers/phy/phy-exynos-usb.c                       |  245
>> ++++++++++++++
>>>  drivers/phy/phy-exynos-usb.h                       |   94 ++++++
>>>  drivers/phy/phy-exynos4210-usb.c                   |  295
>> +++++++++++++++++
>>>  drivers/phy/phy-exynos4212-usb.c                   |  343
>> ++++++++++++++++++++
>>>  7 files changed, 1052 insertions(+)
>>>  create mode 100644
>>> Documentation/devicetree/bindings/phy/samsung-usbphy.txt
>>>  create mode 100644 drivers/phy/phy-exynos-usb.c  create mode 100644
>>> drivers/phy/phy-exynos-usb.h  create mode 100644
>>> drivers/phy/phy-exynos4210-usb.c  create mode 100644
>>> drivers/phy/phy-exynos4212-usb.c
>>>
>>> diff --git a/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
>>> b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
>>> new file mode 100644
>>> index 0000000..f112b37
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/phy/samsung-usbphy.txt
>>> @@ -0,0 +1,51 @@
>>> +Samsung S5P/EXYNOS SoC series USB PHY
>>> +-------------------------------------------------
>>> +
>>> +Required properties:
>>> +- compatible : should be one of the listed compatibles:
>>> +	- "samsung,exynos4210-usbphy"
>>> +	- "samsung,exynos4212-usbphy"
>>> +- reg : a list of registers used by phy driver
>>> +	- first and obligatory is the location of phy modules registers
>>> +	- second and also required is the location of isolation registers
>>> +	  (isolation registers control the physical connection between
>> the in
>>> +	  SoC modules and outside of the SoC, this also can be called
>> enable
>>> +	  control in the documentation of the SoC)
>>> +	- third is the location of the mode switch register, this only
>> applies
>>> +	  to SoCs that have such a feature; mode switching enables to
>> have
>>> +	  both host and device used the same SoC pins and is commonly
>> used
>>> +	  when OTG is supported
>>> +- #phy-cells : from the generic phy bindings, must be 1;
>>> +
>>> +The second cell in the PHY specifier identifies the PHY its meaning
>>> +is SoC dependent. For the currently supported SoCs (Exynos 4210 and
>>> +Exynos 4212) it is as follows:
>>> +  0 - USB device,
>>> +  1 - USB host,
>>> +  2 - HSIC0,
>>> +  3 - HSIC1,
>>
>> HSIC is supposedly to be transceiver less no? You have to program
>> something in the digital side?
>> You have a single IP that have all these functionalities?
> 
> There is a single USB PHY controller for all the above functionalities
> (i.e. host, device, hsic 0 and 1).

Ok.
> 
>>> +
>>> +Example:
>>> +
>>> +For Exynos 4412 (compatible with Exynos 4212):
>>> +
>>> +exynos_usbphy: exynos-usbphy@125B0000 {
>>> +	compatible = "samsung,exynos4212-usbphy";
>>> +	reg = <0x125B0000 0x100 0x10020704 0x0c 0x1001021c 0x4>;
>>> +	ranges;
>>> +	#address-cells = <1>;
>>> +	#size-cells = <1>;
>>
>> The above 3 properties aren't documented? Are they needed here?
> 
> My bad. I was working on two branches and corrected it in only one
> of them.
> 
>>> +	clocks = <&clock 305>, <&clock 2>, <&clock 2>, <&clock 2>,
>>> +							<&clock 2>;
>>> +	clock-names = "phy", "device", "host", "hsic0", "hsic1";
>>> +	status = "okay";
>>> +	#phy-cells = <1>;
>>> +};
>>> +
>>> +Then the PHY can be used in other nodes such as:
>>> +
>>> +ehci@12580000 {
>>> +	status = "okay";
>>> +	phys = <&exynos_usbphy 2>;
>>> +	phy-names = "hsic0";
>>> +};
>>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index
>>> 349bef2..2f7ac0a 100644
>>> --- a/drivers/phy/Kconfig
>>> +++ b/drivers/phy/Kconfig
>>> @@ -15,4 +15,25 @@ config GENERIC_PHY
>>>  	  phy users can obtain reference to the PHY. All the users of
>> this
>>>  	  framework should select this config.
>>>
>>> +config PHY_EXYNOS_USB
>>> +	tristate "Samsung USB PHY driver (using the Generic PHY
>> Framework)"
>> Mentioning *Generic PHY Framework* is not necessary.
>> *select GENERIC_PHY* here
> 
> This was added to distinguish this driver from the ols USB PHY driver.
> I agree that in the final version it should be removed. I understand that
> the correct way to do this is by removing the old driver when the new gets
> merged. Yes?

right.
> 
>>> +	help
>>> +	  Enable this to support Samsung USB phy helper driver for
>> Samsung SoCs.
>>> +	  This driver provides common interface to interact, for Samsung
>>> +	  USB 2.0 PHY driver.
>>
>> If it's going to be used only for usb2, name it PHY_EXYNOS_USB2.
> 
> I agree.
> 
>>> +
>>> +config PHY_EXYNOS4210_USB
>>> +	bool "Support for Exynos 4210"
>>> +	depends on PHY_EXYNOS_USB
>>> +	depends on CPU_EXYNOS4210
>>> +	help
>>> +	  Enable USB PHY support for Exynos 4210
>>> +
>>> +config PHY_EXYNOS4212_USB
>>> +	bool "Support for Exynos 4212"
>>> +	depends on PHY_EXYNOS_USB
>>> +	depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
>>> +	help
>>> +	  Enable USB PHY support for Exynos 4212
>>
>> How difference is USB PHY in Exynos 4212 from Exynos 4210? If th
> ere
>> isn't much, I would suggest to use a single driver.
> 
> My idea for the driver is to have a single driver for the whole Exynos USB2
> PHY. The core of the driver is in phy-exynos-usb.c and phy-exynos*-usb.c
> provide registers and setup sequences for particular SoC versions.
> 
> There are a few differences between Exynos 4210, 4212, 5250 and S5PV210:
> - the registers are different on each
> - the setup sequence is different 
> - 4212 and 5250 have a single pair of regular USB OTG DEVICE/HOST lines
> - 4210 has a USB OTG and a separate USB HOST lines
> - S5PV210 has both USB OTG and a separate USB HOST but lacks any HSIC
>   interfaces
> 
> I agree with you that it is best to add as little code as possible.
> Hence the idea to have a separate driver core and additional files for
> distinctive SoCs. Enabling PHY_EXYNOS4210_USB (or other SoC specific
> options)
> does not add another driver it only includes support for enabled SoC.
> 
> It would be possible to skip this distinction altogether and include support
> for all SoCs in the driver without config options.
> 
>>> +
>>>  endmenu
>>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index
>>> 9e9560f..ca3dc82 100644
>>> --- a/drivers/phy/Makefile
>>> +++ b/drivers/phy/Makefile
>>> @@ -3,3 +3,6 @@
>>>  #
>>>
>>>  obj-$(CONFIG_GENERIC_PHY)	+= phy-core.o
>>> +obj-$(CONFIG_PHY_EXYNOS_USB)		+= phy-exynos-usb.o
>>> +obj-$(CONFIG_PHY_EXYNOS4210_USB)	+= phy-exynos4210-usb.o
>>> +obj-$(CONFIG_PHY_EXYNOS4212_USB)	+= phy-exynos4212-usb.o
>>> diff --git a/drivers/phy/phy-exynos-usb.c
>>> b/drivers/phy/phy-exynos-usb.c new file mode 100644 index
>>> 0000000..d4a26df
>>> --- /dev/null
>>> +++ b/drivers/phy/phy-exynos-usb.c
>>
>> phy-exynos-usb2.c?
> 
> Ok.
> 
>>> @@ -0,0 +1,245 @@
>>> +/*
>>> + * Samsung S5P/EXYNOS SoC series USB 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/delay.h>
>>> +#include <linux/io.h>
>>> +#include <linux/kernel.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-exynos-usb.h"
>>> +
>>> +static int exynos_uphy_power_on(struct phy *phy)
>>
>> exynos_usb2_phy here and everywhere below.
> 
> Ok.
> 
>>> +{
>>> +	struct uphy_instance *inst = phy_get_drvdata(phy);
>>> +	struct uphy_driver *drv = inst->drv;
>>> +	int ret;
>>> +
>>> +	dev_info(drv->dev, "Request to power_on \"%s\" usb phy\n",
>>> +							inst->cfg->label);
>>
>> make it dev_dbg if it's necessary.
> 
> Ok.
> 
>>> +	ret = clk_prepare_enable(drv->clk);
>>> +	if (ret)
>>> +		return ret;
>>> +	if (inst->cfg->power_on) {
>>> +		spin_lock(&drv->lock);
>>> +		ret = inst->cfg->power_on(inst);
>>> +		spin_unlock(&drv->lock);
>>> +	}
>>> +	clk_disable_unprepare(drv->clk);
>>
>> hmm.. don't you need the clock to be on during the duration of the PHY
>> operation?
> 
> I think that it is enough to have the clock enabled only during changes.
> 
>>> +	return ret;
>>> +}
>>> +
>>> +static int exynos_uphy_power_off(struct phy *phy) {
>>> +	struct uphy_instance *inst = phy_get_drvdata(phy);
>>> +	struct uphy_driver *drv = inst->drv;
>>> +	int ret;
>>> +
>>> +	dev_info(drv->dev, "Request to power_off \"%s\" usb phy\n",
>>> +							inst->cfg->label);
>>
>> dev_dbg?
> 
> Ok.
> 
>>> +	ret = clk_prepare_enable(drv->clk);
>>> +	if (ret)
>>> +		return ret;
>>> +	if (inst->cfg->power_off) {
>>> +		spin_lock(&drv->lock);
>>> +		ret = inst->cfg->power_off(inst);
>>> +		spin_unlock(&drv->lock);
>>> +	}
>>> +	clk_disable_unprepare(drv->clk);
>>> +	return ret;
>>> +}
>>> +
>>> +static struct phy_ops exynos_uphy_ops = {
>>> +	.power_on	= exynos_uphy_power_on,
>>> +	.power_off	= exynos_uphy_power_off,
>>> +	.owner		= THIS_MODULE,
>>> +};
>>> +
>>> +static struct phy *exynos_uphy_xlate(struct device *dev,
>>> +					struct of_phandle_args *args)
>>> +{
>>> +	struct uphy_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->uphy_instances[args->args[0]].phy;
>>> +}
>>> +
>>> +static const struct of_device_id exynos_uphy_of_match[];
>>> +
>>> +static int exynos_uphy_probe(struct platform_device *pdev) {
>>> +	struct uphy_driver *drv;
>>> +	struct device *dev = &pdev->dev;
>>> +	struct resource *mem;
>>> +	struct phy_provider *phy_provider;
>>> +
>>> +	const struct of_device_id *match;
>>> +	const struct uphy_config *cfg;
>>> +	struct clk *clk;
>>> +
>>> +	int i;
>>> +
>>> +	match = of_match_node(exynos_uphy_of_match, pdev->dev.of_node);
>>> +	if (!match) {
>>> +		dev_err(dev, "of_match_node() failed\n");
>>> +		return -EINVAL;
>>> +	}
>>> +	cfg = match->data;
>>> +	if (!cfg) {
>>> +		dev_err(dev, "Failed to get configuration\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	drv = devm_kzalloc(dev, sizeof(struct uphy_driver) +
>>> +		cfg->num_phys * sizeof(struct uphy_instance), GFP_KERNEL);
>>> +
>>> +	if (!drv) {
>>> +		dev_err(dev, "Failed to allocate memory\n");
>>> +		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);
>>> +
>> empty blank line.
> 
> Will fix.
> 
>>> +	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);
>>> +	}
>>> +
>>> +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>>> +	drv->reg_isol = devm_ioremap_resource(dev, mem);
>>> +	if (IS_ERR(drv->reg_isol)) {
>>> +		dev_err(dev, "Failed to map register memory (isolation)\n");
>>> +		return PTR_ERR(drv->reg_isol);
>>> +	}
>>> +
>>> +	switch (drv->cfg->cpu) {
>>> +	case TYPE_EXYNOS4210:
>>> +	case TYPE_EXYNOS4212:
>>
>> Lets not add such cpu checks inside driver.
> 
> Some SoC have a special register, which switches the OTG lines between
> device and host modes. I understand that it might not be the prettiest
> code. I see this as a good compromise between having a single huge
> driver for all Exynos SoCs and having a multiple drivers for each SoC
> version. PHY IPs in these chips very are similar but have to be handled
> differently. Any other ideas to solve this issue?

revision checks?
> 
>>> +		mem = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>>> +		drv->reg_mode = devm_ioremap_resource(dev, mem);
>>> +		if (IS_ERR(drv->reg_mode)) {
>>> +			dev_err(dev, "Failed to map register memory (mode
>> switch)\n");
>>> +			return PTR_ERR(drv->reg_mode);
>>> +		}
>>> +		break;
>>> +	default:
>>> +		break;
>>> +	}
>>> +
>>> +	phy_provider = devm_of_phy_provider_register(dev,
>>> +							exynos_uphy_xlate);
>>> +	if (IS_ERR(phy_provider)) {
>>> +		dev_err(drv->dev, "Failed to register phy provider\n");
>>> +		return PTR_ERR(phy_provider);
>>> +	}
>>> +
>>> +	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 uphy_instance *p = &drv->uphy_instances[i];
>>> +
>>> +		dev_info(dev, "Creating phy \"%s\"\n", label);
>>> +		p->phy = devm_phy_create(dev, &exynos_uphy_ops, NULL);
>>> +		if (IS_ERR(p->phy)) {
>>> +			dev_err(drv->dev, "Failed to create uphy \"%s\"\n",
>>> +						label);
>>> +			return PTR_ERR(p->phy);
>>> +		}
>>> +
>>> +		p->cfg = &drv->cfg->phys[i];
>>> +		p->drv = drv;
>>> +		phy_set_drvdata(p->phy, p);
>>> +
>>> +		clk = 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->rate = clk_get_rate(clk);
>>> +
>>> +		if (p->cfg->rate_to_clk) {
>>> +			p->clk = p->cfg->rate_to_clk(p->rate);
>>> +			if (p->clk == CLKSEL_ERROR) {
>>> +				dev_err(dev, "Clock rate (%ld) not
> supported\n",
>>> +								p->rate);
>>> +				clk_put(clk);
>>> +				return -EINVAL;
>>> +			}
>>> +		}
>>> +		clk_put(clk);
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +#ifdef CONFIG_PHY_EXYNOS4210_USB
>> Do we really need this?
> 
> No we don't. The driver can always support all Exynos SoC versions. These
> config options were added for flexibility.
> 
>>
>>> +extern const struct uphy_config exynos4210_uphy_config; #endif
>>> +
>>> +#ifdef CONFIG_PHY_EXYNOS4212_USB
>>
>> Same here.
>>> +extern const struct uphy_config exynos4212_uphy_config; #endif
>>> +
>>> +static const struct of_device_id exynos_uphy_of_match[] = { #ifdef
>>> +CONFIG_PHY_EXYNOS4210_USB
>>
>> #if not needed here.
> 
> If the #ifdef CONFIG_PHY_EXYNOS4210_USB is removed then yes. Otherwise
> it is necessary - exynos4210_uphy_config may be undefined.
> 
>>> +	{
>>> +		.compatible = "samsung,exynos4210-usbphy",
>>> +		.data = &exynos4210_uphy_config,
>>> +	},
>>> +#endif
>>> +#ifdef CONFIG_PHY_EXYNOS4212_USB
>>
>> here too.
>>> +	{
>>> +		.compatible = "samsung,exynos4212-usbphy",
>>> +		.data = &exynos4212_uphy_config,
>>> +	},
>>> +#endif
>>> +	{ },
>>> +};
>>> +
>>> +static struct platform_driver exynos_uphy_driver = {
>>> +	.probe	= exynos_uphy_probe,
>>> +	.driver = {
>>> +		.of_match_table	= exynos_uphy_of_match,
>>> +		.name		= "exynos-usbphy-new",
>> "exynos-usb2-phy".
>>> +		.owner		= THIS_MODULE,
>>> +	}
>>> +};
>>> +
>>> +module_platform_driver(exynos_uphy_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:exynos-uphy-new");
>>> +
>>> diff --git a/drivers/phy/phy-exynos-usb.h
>>> b/drivers/phy/phy-exynos-usb.h new file mode 100644 index
>>> 0000000..f45cb3c
>>> --- /dev/null
>>> +++ b/drivers/phy/phy-exynos-usb.h
>>> @@ -0,0 +1,94 @@
>>> +/*
>>> + * Samsung S5P/EXYNOS SoC series USB 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_SAMSUNG_NEW_H
>>> +#define _PHY_SAMSUNG_NEW_H
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/io.h>
>>> +#include <linux/kernel.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>
>>> +
>>> +#define CLKSEL_ERROR                       -1
>>> +
>>> +#ifndef KHZ
>>> +#define KHZ 1000
>>> +#endif
>>> +
>>> +#ifndef MHZ
>>> +#define MHZ (KHZ * KHZ)
>>> +#endif
>>> +
>>> +enum phy_type {
>>> +	PHY_DEVICE,
>>> +	PHY_HOST,
>>> +};
>>> +
>>> +enum samsung_cpu_type {
>>> +	TYPE_S3C64XX,
>>> +	TYPE_EXYNOS4210,
>>> +	TYPE_EXYNOS4212,
>>
>> No *cpu_type* inside driver files.
> 
> I guess that you are in favor a "a separate driver for each phy version".
> For me it can be both. But we have to discuss which apporach is better:
> 1) separate driver for each phy version - no iffs and significant code
>    duplication

Creating separate driver for each PHY version is not recommended.
drivers/usb/dwc3/dwc3-omap.c is used for two different SoCs with different
register offsets for your reference.
> 2) a single driver driver supporting all Exynos variants - it needs ifs,
>    code is always bigger

IMO it can be done without #if's. Use revision checks or compatible values.
> 3) a single driver with support for particular SoC enabled in the config
> file
>    - with ifs, but the driver can be compiled smaller

Sorry, don't prefer ifs in driver files.

Thanks
Kishon
--
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] 46+ messages in thread

* Re: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver
  2013-10-28 14:41       ` Vivek Gautam
@ 2013-10-29  9:55         ` Kishon Vijay Abraham I
  -1 siblings, 0 replies; 46+ messages in thread
From: Kishon Vijay Abraham I @ 2013-10-29  9:55 UTC (permalink / raw)
  To: Vivek Gautam
  Cc: Kamil Debski, linux-kernel, linux-samsung-soc,
	Linux USB Mailing List, devicetree, linux-arm, Kyungmin Park,
	Tomasz Figa, Sylwester Nawrocki, Marek Szyprowski, Vivek Gautam,
	Mateusz Krawczuk

Hi,

On Monday 28 October 2013 08:11 PM, Vivek Gautam wrote:
> Hi Kishon,
> 
> 
> On Fri, Oct 25, 2013 at 9:13 PM, Kishon Vijay Abraham I <kishon@ti.com> wrote:
>> Hi,
>>
>> On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
>>> Add support for Exynos 5250. This is work-in-progress commit. Not
>>> for merging.
>>>
>>> Signed-off-by: Kamil Debski <k.debski@samsung.com>
>>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>>> ---
>>>  drivers/phy/Kconfig              |    7 +
>>>  drivers/phy/Makefile             |    1 +
>>>  drivers/phy/phy-exynos-usb.c     |   10 +
>>>  drivers/phy/phy-exynos-usb.h     |    1 +
>>>  drivers/phy/phy-exynos5250-usb.c |  411 ++++++++++++++++++++++++++++++++++++++
>>>  5 files changed, 430 insertions(+)
>>>  create mode 100644 drivers/phy/phy-exynos5250-usb.c
>>>
>>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>>> index 2f7ac0a..0f598d0 100644
>>> --- a/drivers/phy/Kconfig
>>> +++ b/drivers/phy/Kconfig
>>> @@ -36,4 +36,11 @@ config PHY_EXYNOS4212_USB
>>>       help
>>>         Enable USB PHY support for Exynos 4212
>>>
>>> +config PHY_EXYNOS5250_USB
>>> +     bool "Support for Exynos 5250"
>>> +     depends on PHY_EXYNOS_USB
>>
>> This should be a separate driver. Not necessary to use PHY_EXYNOS_USB.
>>> +     depends on SOC_EXYNOS5250
>>> +     help
>>> +       Enable USB PHY support for Exynos 5250
>>> +
>>>  endmenu
>>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
>>> index ca3dc82..0dff0dd 100644
>>> --- a/drivers/phy/Makefile
>>> +++ b/drivers/phy/Makefile
>>> @@ -6,3 +6,4 @@ obj-$(CONFIG_GENERIC_PHY)     += phy-core.o
>>>  obj-$(CONFIG_PHY_EXYNOS_USB)         += phy-exynos-usb.o
>>>  obj-$(CONFIG_PHY_EXYNOS4210_USB)     += phy-exynos4210-usb.o
>>>  obj-$(CONFIG_PHY_EXYNOS4212_USB)     += phy-exynos4212-usb.o
>>> +obj-$(CONFIG_PHY_EXYNOS5250_USB)     += phy-exynos5250-usb.o
>>> diff --git a/drivers/phy/phy-exynos-usb.c b/drivers/phy/phy-exynos-usb.c
>>> index d4a26df..172b774 100644
>>> --- a/drivers/phy/phy-exynos-usb.c
>>> +++ b/drivers/phy/phy-exynos-usb.c
>>> @@ -212,6 +212,10 @@ extern const struct uphy_config exynos4210_uphy_config;
>>>  extern const struct uphy_config exynos4212_uphy_config;
>>>  #endif
>>>
>>> +#ifdef CONFIG_PHY_EXYNOS5250_USB
>>> +extern const struct uphy_config exynos5250_uphy_config;
>>> +#endif
>>> +
>>>  static const struct of_device_id exynos_uphy_of_match[] = {
>>>  #ifdef CONFIG_PHY_EXYNOS4210_USB
>>>       {
>>> @@ -225,6 +229,12 @@ static const struct of_device_id exynos_uphy_of_match[] = {
>>>               .data = &exynos4212_uphy_config,
>>>       },
>>>  #endif
>>> +#ifdef CONFIG_PHY_EXYNOS5250_USB
>>> +     {
>>> +             .compatible = "samsung,exynos5250-usbphy",
>>> +             .data = &exynos5250_uphy_config,
>>> +     },
>>> +#endif
>>>       { },
>>>  };
>>>
>>> diff --git a/drivers/phy/phy-exynos-usb.h b/drivers/phy/phy-exynos-usb.h
>>> index f45cb3c..a9febfa 100644
>>> --- a/drivers/phy/phy-exynos-usb.h
>>> +++ b/drivers/phy/phy-exynos-usb.h
>>> @@ -42,6 +42,7 @@ enum samsung_cpu_type {
>>>       TYPE_S3C64XX,
>>>       TYPE_EXYNOS4210,
>>>       TYPE_EXYNOS4212,
>>> +     TYPE_EXYNOS5250,
>>
>> No cpu types here.
> 
> One question here.
> In case we move to single driver for Exynos4 SoCs (4210, 4212 and 4412
> later) as well as S5PV210,
> there will be certain things changing from one SoC to another, how
> should we target that in case we
> don't have CPU types ?
> May be i am misinterpreting your suggestion ?

We should be using the IP revision register or check for compatible values.

Thanks
Kishon

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

* Re: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver
@ 2013-10-29  9:55         ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 46+ messages in thread
From: Kishon Vijay Abraham I @ 2013-10-29  9:55 UTC (permalink / raw)
  To: Vivek Gautam
  Cc: Kamil Debski, linux-kernel, linux-samsung-soc,
	Linux USB Mailing List, devicetree, linux-arm, Kyungmin Park,
	Tomasz Figa, Sylwester Nawrocki, Marek Szyprowski, Vivek Gautam,
	Mateusz Krawczuk

Hi,

On Monday 28 October 2013 08:11 PM, Vivek Gautam wrote:
> Hi Kishon,
> 
> 
> On Fri, Oct 25, 2013 at 9:13 PM, Kishon Vijay Abraham I <kishon@ti.com> wrote:
>> Hi,
>>
>> On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
>>> Add support for Exynos 5250. This is work-in-progress commit. Not
>>> for merging.
>>>
>>> Signed-off-by: Kamil Debski <k.debski@samsung.com>
>>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>>> ---
>>>  drivers/phy/Kconfig              |    7 +
>>>  drivers/phy/Makefile             |    1 +
>>>  drivers/phy/phy-exynos-usb.c     |   10 +
>>>  drivers/phy/phy-exynos-usb.h     |    1 +
>>>  drivers/phy/phy-exynos5250-usb.c |  411 ++++++++++++++++++++++++++++++++++++++
>>>  5 files changed, 430 insertions(+)
>>>  create mode 100644 drivers/phy/phy-exynos5250-usb.c
>>>
>>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>>> index 2f7ac0a..0f598d0 100644
>>> --- a/drivers/phy/Kconfig
>>> +++ b/drivers/phy/Kconfig
>>> @@ -36,4 +36,11 @@ config PHY_EXYNOS4212_USB
>>>       help
>>>         Enable USB PHY support for Exynos 4212
>>>
>>> +config PHY_EXYNOS5250_USB
>>> +     bool "Support for Exynos 5250"
>>> +     depends on PHY_EXYNOS_USB
>>
>> This should be a separate driver. Not necessary to use PHY_EXYNOS_USB.
>>> +     depends on SOC_EXYNOS5250
>>> +     help
>>> +       Enable USB PHY support for Exynos 5250
>>> +
>>>  endmenu
>>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
>>> index ca3dc82..0dff0dd 100644
>>> --- a/drivers/phy/Makefile
>>> +++ b/drivers/phy/Makefile
>>> @@ -6,3 +6,4 @@ obj-$(CONFIG_GENERIC_PHY)     += phy-core.o
>>>  obj-$(CONFIG_PHY_EXYNOS_USB)         += phy-exynos-usb.o
>>>  obj-$(CONFIG_PHY_EXYNOS4210_USB)     += phy-exynos4210-usb.o
>>>  obj-$(CONFIG_PHY_EXYNOS4212_USB)     += phy-exynos4212-usb.o
>>> +obj-$(CONFIG_PHY_EXYNOS5250_USB)     += phy-exynos5250-usb.o
>>> diff --git a/drivers/phy/phy-exynos-usb.c b/drivers/phy/phy-exynos-usb.c
>>> index d4a26df..172b774 100644
>>> --- a/drivers/phy/phy-exynos-usb.c
>>> +++ b/drivers/phy/phy-exynos-usb.c
>>> @@ -212,6 +212,10 @@ extern const struct uphy_config exynos4210_uphy_config;
>>>  extern const struct uphy_config exynos4212_uphy_config;
>>>  #endif
>>>
>>> +#ifdef CONFIG_PHY_EXYNOS5250_USB
>>> +extern const struct uphy_config exynos5250_uphy_config;
>>> +#endif
>>> +
>>>  static const struct of_device_id exynos_uphy_of_match[] = {
>>>  #ifdef CONFIG_PHY_EXYNOS4210_USB
>>>       {
>>> @@ -225,6 +229,12 @@ static const struct of_device_id exynos_uphy_of_match[] = {
>>>               .data = &exynos4212_uphy_config,
>>>       },
>>>  #endif
>>> +#ifdef CONFIG_PHY_EXYNOS5250_USB
>>> +     {
>>> +             .compatible = "samsung,exynos5250-usbphy",
>>> +             .data = &exynos5250_uphy_config,
>>> +     },
>>> +#endif
>>>       { },
>>>  };
>>>
>>> diff --git a/drivers/phy/phy-exynos-usb.h b/drivers/phy/phy-exynos-usb.h
>>> index f45cb3c..a9febfa 100644
>>> --- a/drivers/phy/phy-exynos-usb.h
>>> +++ b/drivers/phy/phy-exynos-usb.h
>>> @@ -42,6 +42,7 @@ enum samsung_cpu_type {
>>>       TYPE_S3C64XX,
>>>       TYPE_EXYNOS4210,
>>>       TYPE_EXYNOS4212,
>>> +     TYPE_EXYNOS5250,
>>
>> No cpu types here.
> 
> One question here.
> In case we move to single driver for Exynos4 SoCs (4210, 4212 and 4412
> later) as well as S5PV210,
> there will be certain things changing from one SoC to another, how
> should we target that in case we
> don't have CPU types ?
> May be i am misinterpreting your suggestion ?

We should be using the IP revision register or check for compatible values.

Thanks
Kishon

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

* RE: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver
  2013-10-29  9:55         ` Kishon Vijay Abraham I
  (?)
@ 2013-10-29 10:14         ` Kamil Debski
  2013-10-29 10:51             ` Kishon Vijay Abraham I
  -1 siblings, 1 reply; 46+ messages in thread
From: Kamil Debski @ 2013-10-29 10:14 UTC (permalink / raw)
  To: 'Kishon Vijay Abraham I', 'Vivek Gautam'
  Cc: linux-kernel, linux-samsung-soc, 'Linux USB Mailing List',
	devicetree, linux-arm, 'Kyungmin Park',
	Tomasz Figa, Sylwester Nawrocki, Marek Szyprowski,
	'Vivek Gautam', 'Mateusz Krawczuk'

Hi,

> From: Kishon Vijay Abraham I [mailto:kishon@ti.com]
> Sent: Tuesday, October 29, 2013 10:55 AM
> 
> Hi,
> 
> On Monday 28 October 2013 08:11 PM, Vivek Gautam wrote:
> > Hi Kishon,
> >
> >
> > On Fri, Oct 25, 2013 at 9:13 PM, Kishon Vijay Abraham I
> <kishon@ti.com> wrote:
> >> Hi,
> >>
> >> On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
> >>> Add support for Exynos 5250. This is work-in-progress commit. Not
> >>> for merging.
> >>>
> >>> Signed-off-by: Kamil Debski <k.debski@samsung.com>
> >>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> >>> ---
> >>>  drivers/phy/Kconfig              |    7 +
> >>>  drivers/phy/Makefile             |    1 +
> >>>  drivers/phy/phy-exynos-usb.c     |   10 +
> >>>  drivers/phy/phy-exynos-usb.h     |    1 +
> >>>  drivers/phy/phy-exynos5250-usb.c |  411
> >>> ++++++++++++++++++++++++++++++++++++++
> >>>  5 files changed, 430 insertions(+)
> >>>  create mode 100644 drivers/phy/phy-exynos5250-usb.c
> >>>
> >>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index
> >>> 2f7ac0a..0f598d0 100644
> >>> --- a/drivers/phy/Kconfig
> >>> +++ b/drivers/phy/Kconfig
> >>> @@ -36,4 +36,11 @@ config PHY_EXYNOS4212_USB
> >>>       help
> >>>         Enable USB PHY support for Exynos 4212
> >>>
> >>> +config PHY_EXYNOS5250_USB
> >>> +     bool "Support for Exynos 5250"
> >>> +     depends on PHY_EXYNOS_USB
> >>
> >> This should be a separate driver. Not necessary to use
> PHY_EXYNOS_USB.
> >>> +     depends on SOC_EXYNOS5250
> >>> +     help
> >>> +       Enable USB PHY support for Exynos 5250
> >>> +
> >>>  endmenu
> >>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index
> >>> ca3dc82..0dff0dd 100644
> >>> --- a/drivers/phy/Makefile
> >>> +++ b/drivers/phy/Makefile
> >>> @@ -6,3 +6,4 @@ obj-$(CONFIG_GENERIC_PHY)     += phy-core.o
> >>>  obj-$(CONFIG_PHY_EXYNOS_USB)         += phy-exynos-usb.o
> >>>  obj-$(CONFIG_PHY_EXYNOS4210_USB)     += phy-exynos4210-usb.o
> >>>  obj-$(CONFIG_PHY_EXYNOS4212_USB)     += phy-exynos4212-usb.o
> >>> +obj-$(CONFIG_PHY_EXYNOS5250_USB)     += phy-exynos5250-usb.o
> >>> diff --git a/drivers/phy/phy-exynos-usb.c
> >>> b/drivers/phy/phy-exynos-usb.c index d4a26df..172b774 100644
> >>> --- a/drivers/phy/phy-exynos-usb.c
> >>> +++ b/drivers/phy/phy-exynos-usb.c
> >>> @@ -212,6 +212,10 @@ extern const struct uphy_config
> >>> exynos4210_uphy_config;  extern const struct uphy_config
> >>> exynos4212_uphy_config;  #endif
> >>>
> >>> +#ifdef CONFIG_PHY_EXYNOS5250_USB
> >>> +extern const struct uphy_config exynos5250_uphy_config; #endif
> >>> +
> >>>  static const struct of_device_id exynos_uphy_of_match[] =
> {  #ifdef
> >>> CONFIG_PHY_EXYNOS4210_USB
> >>>       {
> >>> @@ -225,6 +229,12 @@ static const struct of_device_id
> exynos_uphy_of_match[] = {
> >>>               .data = &exynos4212_uphy_config,
> >>>       },
> >>>  #endif
> >>> +#ifdef CONFIG_PHY_EXYNOS5250_USB
> >>> +     {
> >>> +             .compatible = "samsung,exynos5250-usbphy",
> >>> +             .data = &exynos5250_uphy_config,
> >>> +     },
> >>> +#endif
> >>>       { },
> >>>  };
> >>>
> >>> diff --git a/drivers/phy/phy-exynos-usb.h
> >>> b/drivers/phy/phy-exynos-usb.h index f45cb3c..a9febfa 100644
> >>> --- a/drivers/phy/phy-exynos-usb.h
> >>> +++ b/drivers/phy/phy-exynos-usb.h
> >>> @@ -42,6 +42,7 @@ enum samsung_cpu_type {
> >>>       TYPE_S3C64XX,
> >>>       TYPE_EXYNOS4210,
> >>>       TYPE_EXYNOS4212,
> >>> +     TYPE_EXYNOS5250,
> >>
> >> No cpu types here.
> >
> > One question here.
> > In case we move to single driver for Exynos4 SoCs (4210, 4212 and
> 4412
> > later) as well as S5PV210,
> > there will be certain things changing from one SoC to another, how
> > should we target that in case we don't have CPU types ?
> > May be i am misinterpreting your suggestion ?
> 
> We should be using the IP revision register or check for compatible
> values.
> 

In case of this driver the compatible is checked. Maybe it is not as
straight forward, but the choice is based on compatible value.
Compatible is matched to an appropriate data entry in the of_device_id
table. The data entry contains a cpu field which contains the information
which PHY version we have. Maybe the "cpu" name is confusing and should be
changed to something like "version" or "revision".

For example:
"samsung,exynos4212-usbphy" compatible is matched to exynos4212_uphy_config
via data field of of_device_id, and the cpu field of exynos4212_uphy_config
is equal to TYPE_EXYNOS4212.
This way in the code all what is needed is to check the value of cpu field.
It already got matched through the compatible.

Still, Tomasz Figa's idea sound good - using a boolean flag
"has_mode_switch".

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


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

* RE: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver
  2013-10-28 20:00       ` Tomasz Figa
@ 2013-10-29 10:16         ` Kamil Debski
  0 siblings, 0 replies; 46+ messages in thread
From: Kamil Debski @ 2013-10-29 10:16 UTC (permalink / raw)
  To: 'Tomasz Figa'
  Cc: 'Kishon Vijay Abraham I',
	linux-kernel, linux-samsung-soc, linux-usb, devicetree,
	linux-arm, kyungmin.park, Tomasz Figa, Sylwester Nawrocki,
	Marek Szyprowski, gautam.vivek, mat.krawczuk

Hi,

> From: Tomasz Figa [mailto:tomasz.figa@gmail.com]
> Sent: Monday, October 28, 2013 9:00 PM
> 
> Hi Kamil,
> 
> On Monday 28 of October 2013 14:52:19 Kamil Debski wrote:
> > Hi Kishon,
> >
> > Thank you for your review! I will answer your comments below.
> [snip]
> > > > +
> > > > +	switch (drv->cfg->cpu) {
> > > > +	case TYPE_EXYNOS4210:
> > >
> > > > +	case TYPE_EXYNOS4212:
> > > Lets not add such cpu checks inside driver.
> >
> > Some SoC have a special register, which switches the OTG lines
> between
> > device and host modes. I understand that it might not be the
> prettiest
> > code. I see this as a good compromise between having a single huge
> > driver for all Exynos SoCs and having a multiple drivers for each SoC
> > version. PHY IPs in these chips very are similar but have to be
> > handled differently. Any other ideas to solve this issue?
> 
> Maybe adding a flag in drv->cfg called, for example, .has_mode_switch
> could solve this problem without having to check the SoC type
> explicitly?

Sounds like a good idea.
 
> [snip]
> > > > +#ifdef CONFIG_PHY_EXYNOS4210_USB
> > >
> > > Do we really need this?
> >
> > No we don't. The driver can always support all Exynos SoC versions.
> > These config options were added for flexibility.
> >
> > > > +extern const struct uphy_config exynos4210_uphy_config; #endif
> > > > +
> > > > +#ifdef CONFIG_PHY_EXYNOS4212_USB
> > >
> > > Same here.
> > >
> > > > +extern const struct uphy_config exynos4212_uphy_config; #endif
> > > > +
> > > > +static const struct of_device_id exynos_uphy_of_match[] = {
> > > > +#ifdef CONFIG_PHY_EXYNOS4210_USB
> > >
> > > #if not needed here.
> >
> > If the #ifdef CONFIG_PHY_EXYNOS4210_USB is removed then yes.
> Otherwise
> > it is necessary - exynos4210_uphy_config may be undefined.
> 
> I believe this and other ifdefs below are needed, otherwise, with
> support for one of the SoCs disabled, the driver could still bind to
> its compatible value.
> 

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



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

* Re: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver
  2013-10-29 10:14         ` Kamil Debski
@ 2013-10-29 10:51             ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 46+ messages in thread
From: Kishon Vijay Abraham I @ 2013-10-29 10:51 UTC (permalink / raw)
  To: Kamil Debski
  Cc: 'Vivek Gautam',
	linux-kernel, linux-samsung-soc, 'Linux USB Mailing List',
	devicetree, linux-arm, 'Kyungmin Park',
	Tomasz Figa, Sylwester Nawrocki, Marek Szyprowski,
	'Vivek Gautam', 'Mateusz Krawczuk'

Hi,

On Tuesday 29 October 2013 03:44 PM, Kamil Debski wrote:
> Hi,
> 
>> From: Kishon Vijay Abraham I [mailto:kishon@ti.com]
>> Sent: Tuesday, October 29, 2013 10:55 AM
>>
>> Hi,
>>
>> On Monday 28 October 2013 08:11 PM, Vivek Gautam wrote:
>>> Hi Kishon,
>>>
>>>
>>> On Fri, Oct 25, 2013 at 9:13 PM, Kishon Vijay Abraham I
>> <kishon@ti.com> wrote:
>>>> Hi,
>>>>
>>>> On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
>>>>> Add support for Exynos 5250. This is work-in-progress commit. Not
>>>>> for merging.
>>>>>
>>>>> Signed-off-by: Kamil Debski <k.debski@samsung.com>
>>>>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>>>>> ---
>>>>>  drivers/phy/Kconfig              |    7 +
>>>>>  drivers/phy/Makefile             |    1 +
>>>>>  drivers/phy/phy-exynos-usb.c     |   10 +
>>>>>  drivers/phy/phy-exynos-usb.h     |    1 +
>>>>>  drivers/phy/phy-exynos5250-usb.c |  411
>>>>> ++++++++++++++++++++++++++++++++++++++
>>>>>  5 files changed, 430 insertions(+)
>>>>>  create mode 100644 drivers/phy/phy-exynos5250-usb.c
>>>>>
>>>>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index
>>>>> 2f7ac0a..0f598d0 100644
>>>>> --- a/drivers/phy/Kconfig
>>>>> +++ b/drivers/phy/Kconfig
>>>>> @@ -36,4 +36,11 @@ config PHY_EXYNOS4212_USB
>>>>>       help
>>>>>         Enable USB PHY support for Exynos 4212
>>>>>
>>>>> +config PHY_EXYNOS5250_USB
>>>>> +     bool "Support for Exynos 5250"
>>>>> +     depends on PHY_EXYNOS_USB
>>>>
>>>> This should be a separate driver. Not necessary to use
>> PHY_EXYNOS_USB.
>>>>> +     depends on SOC_EXYNOS5250
>>>>> +     help
>>>>> +       Enable USB PHY support for Exynos 5250
>>>>> +
>>>>>  endmenu
>>>>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index
>>>>> ca3dc82..0dff0dd 100644
>>>>> --- a/drivers/phy/Makefile
>>>>> +++ b/drivers/phy/Makefile
>>>>> @@ -6,3 +6,4 @@ obj-$(CONFIG_GENERIC_PHY)     += phy-core.o
>>>>>  obj-$(CONFIG_PHY_EXYNOS_USB)         += phy-exynos-usb.o
>>>>>  obj-$(CONFIG_PHY_EXYNOS4210_USB)     += phy-exynos4210-usb.o
>>>>>  obj-$(CONFIG_PHY_EXYNOS4212_USB)     += phy-exynos4212-usb.o
>>>>> +obj-$(CONFIG_PHY_EXYNOS5250_USB)     += phy-exynos5250-usb.o
>>>>> diff --git a/drivers/phy/phy-exynos-usb.c
>>>>> b/drivers/phy/phy-exynos-usb.c index d4a26df..172b774 100644
>>>>> --- a/drivers/phy/phy-exynos-usb.c
>>>>> +++ b/drivers/phy/phy-exynos-usb.c
>>>>> @@ -212,6 +212,10 @@ extern const struct uphy_config
>>>>> exynos4210_uphy_config;  extern const struct uphy_config
>>>>> exynos4212_uphy_config;  #endif
>>>>>
>>>>> +#ifdef CONFIG_PHY_EXYNOS5250_USB
>>>>> +extern const struct uphy_config exynos5250_uphy_config; #endif
>>>>> +
>>>>>  static const struct of_device_id exynos_uphy_of_match[] =
>> {  #ifdef
>>>>> CONFIG_PHY_EXYNOS4210_USB
>>>>>       {
>>>>> @@ -225,6 +229,12 @@ static const struct of_device_id
>> exynos_uphy_of_match[] = {
>>>>>               .data = &exynos4212_uphy_config,
>>>>>       },
>>>>>  #endif
>>>>> +#ifdef CONFIG_PHY_EXYNOS5250_USB
>>>>> +     {
>>>>> +             .compatible = "samsung,exynos5250-usbphy",
>>>>> +             .data = &exynos5250_uphy_config,
>>>>> +     },
>>>>> +#endif
>>>>>       { },
>>>>>  };
>>>>>
>>>>> diff --git a/drivers/phy/phy-exynos-usb.h
>>>>> b/drivers/phy/phy-exynos-usb.h index f45cb3c..a9febfa 100644
>>>>> --- a/drivers/phy/phy-exynos-usb.h
>>>>> +++ b/drivers/phy/phy-exynos-usb.h
>>>>> @@ -42,6 +42,7 @@ enum samsung_cpu_type {
>>>>>       TYPE_S3C64XX,
>>>>>       TYPE_EXYNOS4210,
>>>>>       TYPE_EXYNOS4212,
>>>>> +     TYPE_EXYNOS5250,
>>>>
>>>> No cpu types here.
>>>
>>> One question here.
>>> In case we move to single driver for Exynos4 SoCs (4210, 4212 and
>> 4412
>>> later) as well as S5PV210,
>>> there will be certain things changing from one SoC to another, how
>>> should we target that in case we don't have CPU types ?
>>> May be i am misinterpreting your suggestion ?
>>
>> We should be using the IP revision register or check for compatible
>> values.
>>
> 
> In case of this driver the compatible is checked. Maybe it is not as
> straight forward, but the choice is based on compatible value.
> Compatible is matched to an appropriate data entry in the of_device_id
> table. The data entry contains a cpu field which contains the information
> which PHY version we have. Maybe the "cpu" name is confusing and should be
> changed to something like "version" or "revision".

you don't have a revision register in the PHY IP which can be used?
> 
> For example:
> "samsung,exynos4212-usbphy" compatible is matched to exynos4212_uphy_config
> via data field of of_device_id, and the cpu field of exynos4212_uphy_config
> is equal to TYPE_EXYNOS4212.
> This way in the code all what is needed is to check the value of cpu field.
> It already got matched through the compatible.
> 
> Still, Tomasz Figa's idea sound good - using a boolean flag
> "has_mode_switch".

hmm, could be.

Thanks
Kishon

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

* Re: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver
@ 2013-10-29 10:51             ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 46+ messages in thread
From: Kishon Vijay Abraham I @ 2013-10-29 10:51 UTC (permalink / raw)
  To: Kamil Debski
  Cc: 'Vivek Gautam',
	linux-kernel, linux-samsung-soc, 'Linux USB Mailing List',
	devicetree, linux-arm, 'Kyungmin Park',
	Tomasz Figa, Sylwester Nawrocki, Marek Szyprowski,
	'Vivek Gautam', 'Mateusz Krawczuk'

Hi,

On Tuesday 29 October 2013 03:44 PM, Kamil Debski wrote:
> Hi,
> 
>> From: Kishon Vijay Abraham I [mailto:kishon@ti.com]
>> Sent: Tuesday, October 29, 2013 10:55 AM
>>
>> Hi,
>>
>> On Monday 28 October 2013 08:11 PM, Vivek Gautam wrote:
>>> Hi Kishon,
>>>
>>>
>>> On Fri, Oct 25, 2013 at 9:13 PM, Kishon Vijay Abraham I
>> <kishon@ti.com> wrote:
>>>> Hi,
>>>>
>>>> On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
>>>>> Add support for Exynos 5250. This is work-in-progress commit. Not
>>>>> for merging.
>>>>>
>>>>> Signed-off-by: Kamil Debski <k.debski@samsung.com>
>>>>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>>>>> ---
>>>>>  drivers/phy/Kconfig              |    7 +
>>>>>  drivers/phy/Makefile             |    1 +
>>>>>  drivers/phy/phy-exynos-usb.c     |   10 +
>>>>>  drivers/phy/phy-exynos-usb.h     |    1 +
>>>>>  drivers/phy/phy-exynos5250-usb.c |  411
>>>>> ++++++++++++++++++++++++++++++++++++++
>>>>>  5 files changed, 430 insertions(+)
>>>>>  create mode 100644 drivers/phy/phy-exynos5250-usb.c
>>>>>
>>>>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index
>>>>> 2f7ac0a..0f598d0 100644
>>>>> --- a/drivers/phy/Kconfig
>>>>> +++ b/drivers/phy/Kconfig
>>>>> @@ -36,4 +36,11 @@ config PHY_EXYNOS4212_USB
>>>>>       help
>>>>>         Enable USB PHY support for Exynos 4212
>>>>>
>>>>> +config PHY_EXYNOS5250_USB
>>>>> +     bool "Support for Exynos 5250"
>>>>> +     depends on PHY_EXYNOS_USB
>>>>
>>>> This should be a separate driver. Not necessary to use
>> PHY_EXYNOS_USB.
>>>>> +     depends on SOC_EXYNOS5250
>>>>> +     help
>>>>> +       Enable USB PHY support for Exynos 5250
>>>>> +
>>>>>  endmenu
>>>>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index
>>>>> ca3dc82..0dff0dd 100644
>>>>> --- a/drivers/phy/Makefile
>>>>> +++ b/drivers/phy/Makefile
>>>>> @@ -6,3 +6,4 @@ obj-$(CONFIG_GENERIC_PHY)     += phy-core.o
>>>>>  obj-$(CONFIG_PHY_EXYNOS_USB)         += phy-exynos-usb.o
>>>>>  obj-$(CONFIG_PHY_EXYNOS4210_USB)     += phy-exynos4210-usb.o
>>>>>  obj-$(CONFIG_PHY_EXYNOS4212_USB)     += phy-exynos4212-usb.o
>>>>> +obj-$(CONFIG_PHY_EXYNOS5250_USB)     += phy-exynos5250-usb.o
>>>>> diff --git a/drivers/phy/phy-exynos-usb.c
>>>>> b/drivers/phy/phy-exynos-usb.c index d4a26df..172b774 100644
>>>>> --- a/drivers/phy/phy-exynos-usb.c
>>>>> +++ b/drivers/phy/phy-exynos-usb.c
>>>>> @@ -212,6 +212,10 @@ extern const struct uphy_config
>>>>> exynos4210_uphy_config;  extern const struct uphy_config
>>>>> exynos4212_uphy_config;  #endif
>>>>>
>>>>> +#ifdef CONFIG_PHY_EXYNOS5250_USB
>>>>> +extern const struct uphy_config exynos5250_uphy_config; #endif
>>>>> +
>>>>>  static const struct of_device_id exynos_uphy_of_match[] =
>> {  #ifdef
>>>>> CONFIG_PHY_EXYNOS4210_USB
>>>>>       {
>>>>> @@ -225,6 +229,12 @@ static const struct of_device_id
>> exynos_uphy_of_match[] = {
>>>>>               .data = &exynos4212_uphy_config,
>>>>>       },
>>>>>  #endif
>>>>> +#ifdef CONFIG_PHY_EXYNOS5250_USB
>>>>> +     {
>>>>> +             .compatible = "samsung,exynos5250-usbphy",
>>>>> +             .data = &exynos5250_uphy_config,
>>>>> +     },
>>>>> +#endif
>>>>>       { },
>>>>>  };
>>>>>
>>>>> diff --git a/drivers/phy/phy-exynos-usb.h
>>>>> b/drivers/phy/phy-exynos-usb.h index f45cb3c..a9febfa 100644
>>>>> --- a/drivers/phy/phy-exynos-usb.h
>>>>> +++ b/drivers/phy/phy-exynos-usb.h
>>>>> @@ -42,6 +42,7 @@ enum samsung_cpu_type {
>>>>>       TYPE_S3C64XX,
>>>>>       TYPE_EXYNOS4210,
>>>>>       TYPE_EXYNOS4212,
>>>>> +     TYPE_EXYNOS5250,
>>>>
>>>> No cpu types here.
>>>
>>> One question here.
>>> In case we move to single driver for Exynos4 SoCs (4210, 4212 and
>> 4412
>>> later) as well as S5PV210,
>>> there will be certain things changing from one SoC to another, how
>>> should we target that in case we don't have CPU types ?
>>> May be i am misinterpreting your suggestion ?
>>
>> We should be using the IP revision register or check for compatible
>> values.
>>
> 
> In case of this driver the compatible is checked. Maybe it is not as
> straight forward, but the choice is based on compatible value.
> Compatible is matched to an appropriate data entry in the of_device_id
> table. The data entry contains a cpu field which contains the information
> which PHY version we have. Maybe the "cpu" name is confusing and should be
> changed to something like "version" or "revision".

you don't have a revision register in the PHY IP which can be used?
> 
> For example:
> "samsung,exynos4212-usbphy" compatible is matched to exynos4212_uphy_config
> via data field of of_device_id, and the cpu field of exynos4212_uphy_config
> is equal to TYPE_EXYNOS4212.
> This way in the code all what is needed is to check the value of cpu field.
> It already got matched through the compatible.
> 
> Still, Tomasz Figa's idea sound good - using a boolean flag
> "has_mode_switch".

hmm, could be.

Thanks
Kishon

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

end of thread, other threads:[~2013-10-29 10:51 UTC | newest]

Thread overview: 46+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-10-25 14:15 [PATCH 0/5] phy: Add new Exynos USB PHY driver Kamil Debski
2013-10-25 14:15 ` Kamil Debski
2013-10-25 14:15 ` [PATCH v2 1/5] " Kamil Debski
2013-10-25 14:15   ` Kamil Debski
2013-10-25 15:39   ` Kishon Vijay Abraham I
2013-10-25 15:39     ` Kishon Vijay Abraham I
2013-10-28 13:52     ` Kamil Debski
2013-10-28 13:52       ` Kamil Debski
2013-10-28 20:00       ` Tomasz Figa
2013-10-29 10:16         ` Kamil Debski
2013-10-29  9:52       ` Kishon Vijay Abraham I
2013-10-29  9:52         ` Kishon Vijay Abraham I
2013-10-25 21:36   ` Kumar Gala
2013-10-25 21:36     ` Kumar Gala
2013-10-28 13:52     ` Kamil Debski
2013-10-25 14:15 ` [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the " Kamil Debski
2013-10-25 15:43   ` Kishon Vijay Abraham I
2013-10-25 15:43     ` Kishon Vijay Abraham I
2013-10-28 13:52     ` Kamil Debski
2013-10-28 13:52       ` Kamil Debski
2013-10-28 14:41     ` Vivek Gautam
2013-10-28 14:41       ` Vivek Gautam
2013-10-29  9:55       ` Kishon Vijay Abraham I
2013-10-29  9:55         ` Kishon Vijay Abraham I
2013-10-29 10:14         ` Kamil Debski
2013-10-29 10:51           ` Kishon Vijay Abraham I
2013-10-29 10:51             ` Kishon Vijay Abraham I
2013-10-25 14:15 ` [PATCH 3/5] phy: Add support for S5PV210 " Kamil Debski
2013-10-25 15:50   ` Kishon Vijay Abraham I
2013-10-25 15:50     ` Kishon Vijay Abraham I
2013-10-26  1:40     ` Jingoo Han
2013-10-26  1:40       ` Jingoo Han
2013-10-25 14:15 ` [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework Kamil Debski
2013-10-25 15:52   ` Kishon Vijay Abraham I
2013-10-25 15:52     ` Kishon Vijay Abraham I
2013-10-26  1:27   ` Jingoo Han
2013-10-28 13:52     ` Kamil Debski
2013-10-28 13:52       ` Kamil Debski
2013-10-26  9:41   ` Vivek Gautam
2013-10-26  9:41     ` Vivek Gautam
2013-10-28 13:53     ` Kamil Debski
2013-10-28 14:36       ` Vivek Gautam
2013-10-28 14:36         ` Vivek Gautam
2013-10-25 14:15 ` [PATCH 5/5] usb: s3c-hsotg: Use the new Exynos USB phy driver with " Kamil Debski
2013-10-25 15:53   ` Kishon Vijay Abraham I
2013-10-25 15:53     ` Kishon Vijay Abraham I

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.