2013-10-25 14:15:54

by Kamil Debski

[permalink] [raw]
Subject: [PATCH 0/5] phy: Add new Exynos USB PHY driver

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


2013-10-25 14:16:06

by Kamil Debski

[permalink] [raw]
Subject: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver

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 <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
---
.../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 <[email protected]>
+ *
+ * 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 <[email protected]>");
+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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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

2013-10-25 14:16:23

by Kamil Debski

[permalink] [raw]
Subject: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework

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 <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
---
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

2013-10-25 14:16:16

by Kamil Debski

[permalink] [raw]
Subject: [PATCH 3/5] phy: Add support for S5PV210 to the Exynos USB PHY driver

From: Mateusz Krawczuk <[email protected]>

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

Signed-off-by: Mateusz Krawczuk <[email protected]>
[[email protected]: whitespace cleanup and commit description]
Signed-off-by: Kamil Debski <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2013-10-25 14:16:28

by Kamil Debski

[permalink] [raw]
Subject: [PATCH 5/5] usb: s3c-hsotg: Use the new Exynos USB phy driver with the generic phy framework

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

Signed-off-by: Kamil Debski <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
---
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

2013-10-25 14:17:54

by Kamil Debski

[permalink] [raw]
Subject: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver

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

Signed-off-by: Kamil Debski <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
---
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 <[email protected]>
+ *
+ * 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

2013-10-25 15:39:52

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver

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 <[email protected]>
> Signed-off-by: Kyungmin Park <[email protected]>
> ---
> .../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 <[email protected]>
> + *
> + * 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 <[email protected]>");
> +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 <[email protected]>
> + *
> + * 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 <[email protected]>
> + *
> + * 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 <[email protected]>
> + *
> + * 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

2013-10-25 15:44:07

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver

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 <[email protected]>
> Signed-off-by: Kyungmin Park <[email protected]>
> ---
> 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 <[email protected]>
> + *
> + * 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

2013-10-25 15:50:32

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [PATCH 3/5] phy: Add support for S5PV210 to the Exynos USB PHY driver

Hi,

On Friday 25 October 2013 07:45 PM, Kamil Debski wrote:
> From: Mateusz Krawczuk <[email protected]>
>
> Add support for the Samsung's S5PV210 SoC to the Exynos USB PHY driver.
>
> Signed-off-by: Mateusz Krawczuk <[email protected]>
> [[email protected]: whitespace cleanup and commit description]
> Signed-off-by: Kamil Debski <[email protected]>
> ---
> 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 <[email protected]>
> + *
> + * 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

2013-10-25 15:52:49

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework

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 <[email protected]>
> Signed-off-by: Kyungmin Park <[email protected]>
> ---
> 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);
>
>

2013-10-25 15:54:07

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [PATCH 5/5] usb: s3c-hsotg: Use the new Exynos USB phy driver with the generic phy framework

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 <[email protected]>
> Signed-off-by: Kyungmin Park <[email protected]>
> ---
> 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

2013-10-25 21:36:12

by Kumar Gala

[permalink] [raw]
Subject: Re: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver


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 <[email protected]>
> Signed-off-by: Kyungmin Park <[email protected]>
> ---
> .../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

2013-10-26 01:27:29

by Jingoo Han

[permalink] [raw]
Subject: Re: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework

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 <[email protected]>
> Signed-off-by: Kyungmin Park <[email protected]>
> ---
> 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

2013-10-26 01:40:09

by Jingoo Han

[permalink] [raw]
Subject: Re: [PATCH 3/5] phy: Add support for S5PV210 to the Exynos USB PHY driver

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 <[email protected]>
> >
> > Add support for the Samsung's S5PV210 SoC to the Exynos USB PHY driver.
> >
> > Signed-off-by: Mateusz Krawczuk <[email protected]>
> > [[email protected]: whitespace cleanup and commit description]
> > Signed-off-by: Kamil Debski <[email protected]>
> > ---
> > 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

2013-10-26 09:41:35

by Vivek Gautam

[permalink] [raw]
Subject: Re: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework

Hi Kamil,


On Fri, Oct 25, 2013 at 7:45 PM, Kamil Debski <[email protected]> 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 <[email protected]>
> Signed-off-by: Kyungmin Park <[email protected]>
> ---
> 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

2013-10-28 13:52:27

by Kamil Debski

[permalink] [raw]
Subject: RE: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver

Hi Kishon,

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

> From: Kishon Vijay Abraham I [mailto:[email protected]]
> 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 <[email protected]>
> > Signed-off-by: Kyungmin Park <[email protected]>
> > ---
> > .../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 <[email protected]>
> > + *
> > + * 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 <[email protected]>");
> > +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 <[email protected]>
> > + *
> > + * 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 <[email protected]>
> > + *
> > + * 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 <[email protected]>
> > + *
> > + * 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

2013-10-28 13:52:36

by Kamil Debski

[permalink] [raw]
Subject: RE: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver

Hi Kishon,

> From: Kishon Vijay Abraham I [mailto:[email protected]]
> 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 <[email protected]>
> > Signed-off-by: Kyungmin Park <[email protected]>
> > ---
> > 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 <[email protected]>
> > + *
> > + * 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


2013-10-28 13:52:46

by Kamil Debski

[permalink] [raw]
Subject: RE: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver

Hi Kumar Gala,

> From: Kumar Gala [mailto:[email protected]]
> 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 <[email protected]>
> > Signed-off-by: Kyungmin Park <[email protected]>
> > ---
> > .../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

2013-10-28 13:52:57

by Kamil Debski

[permalink] [raw]
Subject: RE: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework

Hi Jingoo,

> From: Jingoo Han [mailto:[email protected]]
> 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 <[email protected]>
> > Signed-off-by: Kyungmin Park <[email protected]>
> > ---
> > 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

2013-10-28 13:53:12

by Kamil Debski

[permalink] [raw]
Subject: RE: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework

Hi Vivek,

> From: Vivek Gautam [mailto:[email protected]]
> Sent: Saturday, October 26, 2013 11:41 AM
>
> Hi Kamil,
>
>
> On Fri, Oct 25, 2013 at 7:45 PM, Kamil Debski <[email protected]>
> 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 <[email protected]>
> > Signed-off-by: Kyungmin Park <[email protected]>
> > ---
> > 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

2013-10-28 14:36:11

by Vivek Gautam

[permalink] [raw]
Subject: Re: [PATCH 4/5] usb: ehci-s5p: Change to use phy provided by the generic phy framework

Hi Kamil,


On Mon, Oct 28, 2013 at 7:23 PM, Kamil Debski <[email protected]> wrote:
> Hi Vivek,
>
>> From: Vivek Gautam [mailto:[email protected]]
>> Sent: Saturday, October 26, 2013 11:41 AM
>>
>> Hi Kamil,
>>
>>
>> On Fri, Oct 25, 2013 at 7:45 PM, Kamil Debski <[email protected]>
>> 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 <[email protected]>
>> > Signed-off-by: Kyungmin Park <[email protected]>
>> > ---

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

2013-10-28 14:41:25

by Vivek Gautam

[permalink] [raw]
Subject: Re: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver

Hi Kishon,


On Fri, Oct 25, 2013 at 9:13 PM, Kishon Vijay Abraham I <[email protected]> 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 <[email protected]>
>> Signed-off-by: Kyungmin Park <[email protected]>
>> ---
>> 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 <[email protected]>
>> + *
>> + * 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 [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html



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

2013-10-28 20:00:07

by Tomasz Figa

[permalink] [raw]
Subject: Re: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver

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

2013-10-29 09:53:04

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver

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:[email protected]]
>> 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 <[email protected]>
>>> Signed-off-by: Kyungmin Park <[email protected]>
>>> ---
>>> .../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 <[email protected]>
>>> + *
>>> + * 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 <[email protected]>");
>>> +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 <[email protected]>
>>> + *
>>> + * 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

2013-10-29 09:55:33

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver

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 <[email protected]> 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 <[email protected]>
>>> Signed-off-by: Kyungmin Park <[email protected]>
>>> ---
>>> 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

2013-10-29 10:14:51

by Kamil Debski

[permalink] [raw]
Subject: RE: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver

Hi,

> From: Kishon Vijay Abraham I [mailto:[email protected]]
> 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
> <[email protected]> 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 <[email protected]>
> >>> Signed-off-by: Kyungmin Park <[email protected]>
> >>> ---
> >>> 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

2013-10-29 10:16:13

by Kamil Debski

[permalink] [raw]
Subject: RE: [PATCH v2 1/5] phy: Add new Exynos USB PHY driver

Hi,

> From: Tomasz Figa [mailto:[email protected]]
> 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

2013-10-29 10:51:33

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [RFC PATCH 2/5] phy: Add WIP Exynos 5250 support to the Exynos USB PHY driver

Hi,

On Tuesday 29 October 2013 03:44 PM, Kamil Debski wrote:
> Hi,
>
>> From: Kishon Vijay Abraham I [mailto:[email protected]]
>> 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
>> <[email protected]> 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 <[email protected]>
>>>>> Signed-off-by: Kyungmin Park <[email protected]>
>>>>> ---
>>>>> 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