aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>2019-06-12 12:00:56 +0530
committerManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>2019-06-12 12:00:56 +0530
commitbd970cfc7a821e9cb2cbd1bdce8acaafa4043869 (patch)
treee3e2f19059437715153f72add574c7b157594263
parentf1f9d6738d95ddfcf2eb2dff5125f940923d159e (diff)
download96b-common-b96-usb.tar.gz
-rw-r--r--arch/arm64/boot/dts/actions/s900-bubblegum-96.dts8
-rw-r--r--arch/arm64/boot/dts/actions/s900.dtsi48
-rw-r--r--drivers/soc/actions/owl-sps.c2
-rw-r--r--drivers/usb/dwc3/Kconfig9
-rw-r--r--drivers/usb/dwc3/Makefile1
-rw-r--r--drivers/usb/dwc3/dwc3-of-simple.c12
-rw-r--r--drivers/usb/dwc3/dwc3-owl.c634
-rw-r--r--drivers/usb/phy/Kconfig12
-rw-r--r--drivers/usb/phy/Makefile2
-rw-r--r--drivers/usb/phy/phy-owl-usb.h48
-rw-r--r--drivers/usb/phy/phy-owl-usb2.c266
-rw-r--r--drivers/usb/phy/phy-owl-usb3.c183
12 files changed, 1223 insertions, 2 deletions
diff --git a/arch/arm64/boot/dts/actions/s900-bubblegum-96.dts b/arch/arm64/boot/dts/actions/s900-bubblegum-96.dts
index 3b596d72de25..dee55f8f6b39 100644
--- a/arch/arm64/boot/dts/actions/s900-bubblegum-96.dts
+++ b/arch/arm64/boot/dts/actions/s900-bubblegum-96.dts
@@ -300,3 +300,11 @@
&uart5 {
status = "okay";
};
+
+&usbdrd3_0 {
+ status = "okay";
+};
+
+&usbdrd_dwc3_0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/actions/s900.dtsi b/arch/arm64/boot/dts/actions/s900.dtsi
index eb35cf78ab73..4dbc5717934c 100644
--- a/arch/arm64/boot/dts/actions/s900.dtsi
+++ b/arch/arm64/boot/dts/actions/s900.dtsi
@@ -6,6 +6,7 @@
#include <dt-bindings/clock/actions,s900-cmu.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/power/owl-s900-powergate.h>
#include <dt-bindings/reset/actions,s900-reset.h>
/ {
@@ -176,7 +177,7 @@
};
sps: power-controller@e012e000 {
- compatible = "actions,s900-sps";
+ compatible = "actions,s900-sps", "simple-pm-bus";
reg = <0x0 0xe012e000 0x0 0x2000>;
#power-domain-cells = <1>;
};
@@ -329,5 +330,50 @@
dma-names = "mmc";
status = "disabled";
};
+
+ usbdrd3_0: usb@e0400000 {
+ compatible = "actions,s900-dwc3";
+ reg = <0x0 0xe040cd00 0x0 0x0c>;
+ clocks = <&cmu CLK_USB3_480MPLL0>,
+ <&cmu CLK_USB3_480MPHY0>,
+ <&cmu CLK_USB3_5GPHY>,
+ <&cmu CLK_USB3_CCE>,
+ <&cmu CLK_USB3_MAC>;
+ clock-names = "usb3_480mpll0", "usb3_480mphy0",
+ "usb3_5gphy", "usb3_cce",
+ "usb3_mac";
+ resets = <&cmu RESET_USB3>;
+ reset-names = "usb3";
+ power-domains = <&sps S900_PD_USB3>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ status = "disabled";
+
+ usbdrd_dwc3_0: dwc3 {
+ compatible = "snps,dwc3";
+ reg = <0x0 0xe0400000 0x0 0xcd00>;
+ interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
+// phys = <&usb2_phy>, <&usb3_phy>;
+ usb-phy = <&usb2_phy>, <&usb3_phy>;
+// phy-names = "usb2-phy", "usb3-phy";
+// phy_type = "utmi_wide";
+ dr_mode = "host";
+ power-domains = <&sps S900_PD_USB3>;
+ snps,dis_u3_susphy_quirk;
+ snps,dis_u2_susphy_quirk;
+ status = "disabled";
+ };
+ };
+
+ usb2_phy: usb2phy@0xe040cd0c {
+ compatible = "actions,s900-usb2phy";
+ reg = <0 0xe040cd0c 0 0x4>;
+ };
+
+ usb3_phy: usb3phy@0xe040cf00 {
+ compatible = "actions,s900-usb3phy";
+ reg = <0 0xe040cf00 0 0x100>;
+ };
};
};
diff --git a/drivers/soc/actions/owl-sps.c b/drivers/soc/actions/owl-sps.c
index 73a9e0bb7e8e..205d5adfb584 100644
--- a/drivers/soc/actions/owl-sps.c
+++ b/drivers/soc/actions/owl-sps.c
@@ -51,6 +51,7 @@ static int owl_sps_set_power(struct owl_sps_domain *pd, bool enable)
ack_mask = BIT(pd->info->ack_bit);
pwr_mask = BIT(pd->info->pwr_bit);
+ pr_info("%s power on", pd->info->name);
return owl_sps_set_pg(pd->sps->base, pwr_mask, ack_mask, enable);
}
@@ -89,6 +90,7 @@ static int owl_sps_init_domain(struct owl_sps *sps, int index)
pd->genpd.flags = pd->info->genpd_flags;
pm_genpd_init(&pd->genpd, NULL, false);
+ owl_sps_set_power(pd, true);
sps->genpd_data.domains[index] = &pd->genpd;
return 0;
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 4a62045cc812..53b429e0f5cf 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -137,4 +137,13 @@ config USB_DWC3_QCOM
for peripheral mode support.
Say 'Y' or 'M' if you have one such device.
+config USB_DWC3_OWL
+ tristate "STMicroelectronics Platforms"
+ depends on (ARCH_ACTIONS || COMPILE_TEST) && OF
+ default USB_DWC3
+ help
+ STMicroelectronics SoCs with one DesignWare Core USB3 IP
+ inside (i.e. STiH407).
+ Say 'Y' or 'M' if you have one such device.
+
endif
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index ae86da0dc5bd..7e170df2b885 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -51,3 +51,4 @@ obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o
obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o
obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o
+obj-$(CONFIG_USB_DWC3_OWL) += dwc3-owl.o
diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
index bdac3e7d7b18..c7e7eed4b704 100644
--- a/drivers/usb/dwc3/dwc3-of-simple.c
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
@@ -40,6 +40,7 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
int ret;
bool shared_resets = false;
+ pr_info("%s: %d", __func__, __LINE__);
simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
if (!simple)
return -ENOMEM;
@@ -51,8 +52,10 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
* Some controllers need to toggle the usb3-otg reset before trying to
* initialize the PHY, otherwise the PHY times out.
*/
- if (of_device_is_compatible(np, "rockchip,rk3399-dwc3"))
+ if (of_device_is_compatible(np, "rockchip,rk3399-dwc3")) {
simple->need_reset = true;
+ simple->pulse_resets = true;
+ }
if (of_device_is_compatible(np, "amlogic,meson-axg-dwc3") ||
of_device_is_compatible(np, "amlogic,meson-gxl-dwc3")) {
@@ -87,6 +90,11 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
if (ret)
goto err_resetc_assert;
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+
+ pr_info("%s: %d", __func__, __LINE__);
ret = of_platform_populate(np, NULL, NULL, dev);
if (ret)
goto err_clk_put;
@@ -95,6 +103,7 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
+ pr_info("DWC3 probed!!!");
return 0;
err_clk_put:
@@ -183,6 +192,7 @@ static const struct of_device_id of_dwc3_simple_match[] = {
{ .compatible = "amlogic,meson-axg-dwc3" },
{ .compatible = "amlogic,meson-gxl-dwc3" },
{ .compatible = "allwinner,sun50i-h6-dwc3" },
+ { .compatible = "actions,owl-dwc3" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_dwc3_simple_match);
diff --git a/drivers/usb/dwc3/dwc3-owl.c b/drivers/usb/dwc3/dwc3-owl.c
new file mode 100644
index 000000000000..85f4c646b070
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-owl.c
@@ -0,0 +1,634 @@
+/**
+ * Actions OWL SoCs dwc3 driver
+ *
+ * Copyright (c) 2015 Actions Semiconductor Co., ltd.
+ * tangshaoqing <tangshaoqing@actions-semi.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <asm/uaccess.h>
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+
+#include "core.h"
+
+#ifdef DEBUG_S700_CLK
+void dwc3_owl_printk_power_usage_count(void);
+void dwc3_owl_printk_clk_reg(void);
+
+#define PWR_USB3 (1<<10)
+#define ACK_USB3 (1<<10)
+
+#define SPS_PG_BASE 0xE01B0100
+#define SPS_PG_CTL (SPS_PG_BASE+0x0000)
+#define SPS_PG_ACK (SPS_PG_BASE+0x0018)
+
+#define USB3_CONTROLLER_CLOCK_ENABLE (1 << 25)
+#define USB3_MOD_RST (1 << 25)
+
+#define CMU_BASE 0xE0168000
+#define CMU_DEVCLKEN0 (CMU_BASE+0x00a0)
+#define CMU_DEVRST0 (CMU_BASE+0x00A8)
+#define CMU_USBPLL (CMU_BASE+0x00b0)
+
+#define PLL_LDO_EN (1<<31)
+#define SOFT_VBUS_VALUE (1<<7)
+#define SOFT_VBUS_EN (1<<6)
+#endif
+
+#ifdef DEBUG_S900_CLK
+void dwc3_owl_printk_power_usage_count(void);
+void dwc3_owl_printk_clk_reg(void);
+
+#define USB3_MOD_RST (1 << 14)
+
+#define PWR_USB3 (1<<8)
+#define ACK_USB3 (1<<8)
+#define USB3_AVDD_EN (1<<17)
+
+#define ASSIST_PLL_EN (1<<0)
+#define USB3_MAC_DIV(n) ((n)<<12)
+#define USB3_MAC_DIV_MASK (3<<12)
+
+#define SPS_PG_BASE 0xE012e000
+#define SPS_PG_CTL (SPS_PG_BASE+0x0000)
+#define SPS_PG_ACK (SPS_PG_BASE+0x0004)
+#define SPS_LDO_CTL (SPS_PG_BASE+0x0014)
+
+#define CMU_BASE 0xE0160000
+#define CMU_USBPLL (CMU_BASE+0x0080)
+#define CMU_ASSISTPLL (CMU_BASE+0x0084)
+#define CMU_DEVRST1 (CMU_BASE+0x00AC)
+#endif
+
+#ifdef FPGA_VERIFY
+#define S700_SOFTVBUS (1<<7)
+#define S700_SOFTVBUSEN (1<<6)
+#define S700_TIMER_BASE 0xE024C000
+#define S700_USB3_ECS (S700_TIMER_BASE+0x0090)
+
+#define S900_SOFTVBUS (1<<25)
+#define S900_SOFTVBUSEN (1<<24)
+#define S900_TIMER_BASE 0xE0228000
+#define S900_USB3_ECS (S900_TIMER_BASE+0x0090)
+#endif
+
+struct dwc3_owl_regs {
+#ifdef DEBUG_S700_CLK
+ void __iomem *sps_pg_ctl;
+ void __iomem *sps_pg_ack;
+
+ void __iomem *usbpll;
+ void __iomem *cmu_devclken0;
+ void __iomem *devrst;
+#endif
+
+#ifdef DEBUG_S900_CLK
+ void __iomem *sps_pg_ctl;
+ void __iomem *sps_pg_ack;
+ void __iomem *sps_ldo_ctl;
+
+ void __iomem *usbpll;
+ void __iomem *cmu_assistpll;
+ void __iomem *devrst;
+#endif
+
+#ifdef FPGA_VERIFY
+ void __iomem *usbecs;
+#endif
+};
+
+
+#define USB3_BACKDOOR 0x0
+
+enum {
+ DWC3_S700 = 0,
+ DWC3_S900 = 0x100
+};
+
+struct dwc3_owl_data {
+ int ic_type;
+};
+
+struct dwc3_owl {
+ struct platform_device *dwc3;
+ struct device *dev;
+
+ struct dwc3_owl_data *data;
+
+ struct clk * clk_usb3_480mpll0;
+ struct clk * clk_usb3_480mphy0;
+ struct clk * clk_usb3_5gphy;
+ struct clk * clk_usb3_cce;
+
+ struct clk * clk_usb3_mac;
+
+ struct dwc3_owl_regs regs;
+
+ void __iomem *base;
+};
+
+struct dwc3_owl *_dwc3_owl = 0;
+
+
+#ifdef FPGA_VERIFY
+static void dwc3_owl_set_softvbus_for_fpga_verify(struct dwc3_owl *owl)
+{
+ u32 reg;
+
+ reg = readl(owl->regs.usbecs);
+
+ if (owl->data->ic_type == DWC3_S700)
+ reg |= (S700_SOFTVBUS | S700_SOFTVBUSEN);
+ else if (owl->data->ic_type == DWC3_S900)
+ reg |= (S900_SOFTVBUS | S900_SOFTVBUSEN);
+
+ writel(reg, owl->regs.usbecs);
+}
+#endif
+
+static void dwc3_owl_clk_init(struct dwc3_owl *owl)
+{
+ struct device *dev = owl->dev;
+
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+
+ clk_prepare_enable(owl->clk_usb3_480mpll0);
+ clk_prepare_enable(owl->clk_usb3_480mphy0);
+ clk_prepare_enable(owl->clk_usb3_5gphy);
+ clk_prepare_enable(owl->clk_usb3_cce);
+
+ clk_prepare_enable(owl->clk_usb3_mac);
+
+ pr_info("USB3 MAC clk req: %ld", clk_round_rate(owl->clk_usb3_mac, (500000000/4)));
+ if (owl->data->ic_type == DWC3_S900)
+ clk_set_rate(owl->clk_usb3_mac, clk_round_rate(owl->clk_usb3_mac, (500000000/4)));
+
+ pr_info("USB3 MAC clk req: %ld", clk_get_rate(owl->clk_usb3_mac));
+ if (owl->data->ic_type == DWC3_S700) {
+ int val;
+ val = readl(owl->base + USB3_BACKDOOR);
+ val |= (0x1<<27);
+ writel(val, owl->base + USB3_BACKDOOR);
+ }
+
+#ifdef FPGA_VERIFY
+ dwc3_owl_set_softvbus_for_fpga_verify(owl);
+#endif
+}
+
+static void dwc3_owl_clk_exit(struct dwc3_owl *owl)
+{
+ struct device *dev = owl->dev;
+
+ clk_disable_unprepare(owl->clk_usb3_mac);
+
+ clk_disable_unprepare(owl->clk_usb3_cce);
+ clk_disable_unprepare(owl->clk_usb3_5gphy);
+ clk_disable_unprepare(owl->clk_usb3_480mphy0);
+ clk_disable_unprepare(owl->clk_usb3_480mpll0);
+
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+}
+
+void dwc3_owl_clock_init(void)
+{
+ if (_dwc3_owl)
+ dwc3_owl_clk_init(_dwc3_owl);
+}
+EXPORT_SYMBOL_GPL(dwc3_owl_clock_init);
+
+void dwc3_owl_clock_exit(void)
+{
+ if (_dwc3_owl)
+ dwc3_owl_clk_exit(_dwc3_owl);
+}
+EXPORT_SYMBOL_GPL(dwc3_owl_clock_exit);
+
+void dwc3_actions_clock_init(void)
+{
+ dwc3_owl_clock_init();
+}
+EXPORT_SYMBOL_GPL(dwc3_actions_clock_init);
+
+void dwc3_actions_clock_exit(void)
+{
+ dwc3_owl_clock_exit();
+}
+EXPORT_SYMBOL_GPL(dwc3_actions_clock_exit);
+
+
+
+static int __dwc3_owl_suspend(struct dwc3_owl *owl)
+{
+ printk("%s\n", __func__);
+
+ clk_disable_unprepare(owl->clk_usb3_mac);
+
+ clk_disable_unprepare(owl->clk_usb3_cce);
+ clk_disable_unprepare(owl->clk_usb3_5gphy);
+ clk_disable_unprepare(owl->clk_usb3_480mphy0);
+ clk_disable_unprepare(owl->clk_usb3_480mpll0);
+
+ return 0;
+}
+
+static int __dwc3_owl_resume(struct dwc3_owl *owl)
+{
+ struct device *dev = owl->dev;
+
+ printk("%s\n", __func__);
+
+ clk_prepare_enable(owl->clk_usb3_480mpll0);
+ clk_prepare_enable(owl->clk_usb3_480mphy0);
+ clk_prepare_enable(owl->clk_usb3_5gphy);
+ clk_prepare_enable(owl->clk_usb3_cce);
+
+ clk_prepare_enable(owl->clk_usb3_mac);
+ clk_set_rate(owl->clk_usb3_mac, clk_round_rate(owl->clk_usb3_mac, (500000000/4)) );
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+
+void dwc3_owl_plug_suspend(void)
+{
+ if (_dwc3_owl)
+ __dwc3_owl_suspend(_dwc3_owl);
+
+}
+EXPORT_SYMBOL_GPL(dwc3_owl_plug_suspend);
+
+void dwc3_owl_plug_resume(void)
+{
+ if (_dwc3_owl)
+ __dwc3_owl_resume(_dwc3_owl);
+}
+EXPORT_SYMBOL_GPL(dwc3_owl_plug_resume);
+
+
+#ifdef DEBUG_S700_CLK
+static int dwc3_owl_debug_regs_remap(struct dwc3_owl *owl)
+{
+ struct device *dev = owl->dev;
+
+ owl->regs.sps_pg_ctl = devm_ioremap_nocache(dev, SPS_PG_CTL, 4);
+ owl->regs.sps_pg_ack = devm_ioremap_nocache(dev, SPS_PG_ACK, 4);
+
+ owl->regs.usbpll = devm_ioremap_nocache(dev, CMU_USBPLL, 4);
+ owl->regs.cmu_devclken0 = devm_ioremap_nocache(dev, CMU_DEVCLKEN0, 4);
+ owl->regs.devrst = devm_ioremap_nocache(dev, CMU_DEVRST0, 4);
+
+ if ((!owl->regs.sps_pg_ctl) || (!owl->regs.sps_pg_ack)
+ || (!owl->regs.usbpll) || (!owl->regs.cmu_devclken0) || (!owl->regs.devrst)) {
+ dev_err(dev, "%s %d ioremap failed\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void dwc3_owl_printk_power_usage_count(void)
+{
+ struct device *dev;
+
+ if (!_dwc3_owl)
+ return;
+
+ dev = _dwc3_owl->dev;
+
+ printk("dwc3 owl dev: disable_depth=%d usage_count=%d\n",
+ dev->power.disable_depth, dev->power.usage_count.counter);
+}
+EXPORT_SYMBOL_GPL(dwc3_owl_printk_power_usage_count);
+
+void dwc3_owl_printk_clk_reg(void)
+{
+ struct dwc3_owl *owl = _dwc3_owl;
+
+ if (!owl)
+ return;
+
+ printk("owl->regs.sps_pg_ctl:0x%x\n", readl(owl->regs.sps_pg_ctl));
+ printk("owl->regs.sps_pg_ack:0x%x\n", readl(owl->regs.sps_pg_ack));
+
+ printk("owl->regs.usbpll:0x%x\n", readl(owl->regs.usbpll));
+ printk("owl->regs.cmu_devclken0:0x%x\n", readl(owl->regs.cmu_devclken0));
+ printk("owl->regs.devrst:0x%x\n", readl(owl->regs.devrst));
+}
+EXPORT_SYMBOL_GPL(dwc3_owl_printk_clk_reg);
+#endif
+
+
+#ifdef DEBUG_S900_CLK
+static int dwc3_owl_debug_regs_remap(struct dwc3_owl *owl)
+{
+ struct device *dev = owl->dev;
+
+ owl->regs.sps_pg_ctl = devm_ioremap_nocache(dev, SPS_PG_CTL, 4);
+ owl->regs.sps_pg_ack = devm_ioremap_nocache(dev, SPS_PG_ACK, 4);
+ owl->regs.sps_ldo_ctl = devm_ioremap_nocache(dev, SPS_LDO_CTL, 4);
+
+ owl->regs.usbpll = devm_ioremap_nocache(dev, CMU_USBPLL, 4);
+ owl->regs.cmu_assistpll = devm_ioremap_nocache(dev, CMU_ASSISTPLL, 4);
+ owl->regs.devrst = devm_ioremap_nocache(dev, CMU_DEVRST1, 4);
+
+ if ((!owl->regs.sps_pg_ctl) || (!owl->regs.sps_pg_ack) || (!owl->regs.sps_ldo_ctl)
+ || (!owl->regs.usbpll) || (!owl->regs.cmu_assistpll) || (!owl->regs.devrst)) {
+ dev_err(dev, "%s %d ioremap failed\n", __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void dwc3_owl_printk_power_usage_count(void)
+{
+ struct device *dev;
+
+ if (!_dwc3_owl)
+ return;
+
+ dev = _dwc3_owl->dev;
+
+ printk("dwc3 owl dev: disable_depth=%d usage_count=%d\n",
+ dev->power.disable_depth, dev->power.usage_count.counter);
+}
+EXPORT_SYMBOL_GPL(dwc3_owl_printk_power_usage_count);
+
+void dwc3_owl_printk_clk_reg(void)
+{
+ struct dwc3_owl *owl = _dwc3_owl;
+
+ if (!owl)
+ return;
+
+ printk("owl->regs.sps_pg_ctl:0x%x\n", readl(owl->regs.sps_pg_ctl));
+ printk("owl->regs.sps_pg_ack:0x%x\n", readl(owl->regs.sps_pg_ack));
+ printk("owl->regs.sps_ldo_ctl:0x%x\n", readl(owl->regs.sps_ldo_ctl));
+
+ printk("owl->regs.usbpll:0x%x\n", readl(owl->regs.usbpll));
+ printk("owl->regs.cmu_assistpll:0x%x\n", readl(owl->regs.cmu_assistpll));
+ printk("owl->regs.devrst:0x%x\n", readl(owl->regs.devrst));
+}
+EXPORT_SYMBOL_GPL(dwc3_owl_printk_clk_reg);
+#endif
+
+
+
+
+static const struct of_device_id owl_dwc3_match[];
+static u64 dwc3_dma_mask = DMA_BIT_MASK(32);
+static int dwc3_owl_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(owl_dwc3_match, &pdev->dev);
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct dwc3_owl *owl;
+ struct resource *res;
+ void __iomem *base;
+ int ret = -ENOMEM;
+
+
+ if (!node) {
+ dev_err(dev, "device node not found\n");
+ return -EINVAL;
+ }
+
+ if (!of_id) {
+ dev_err(dev, "no compatible OF match\n");
+ return -EINVAL;
+ }
+
+ dev->dma_mask = &dwc3_dma_mask;
+ dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ owl = devm_kzalloc(dev,sizeof(*owl), GFP_KERNEL);
+ if (!owl) {
+ dev_err(dev, "not enough memory\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, owl);
+ owl->dwc3 = pdev;
+ owl->dev = &pdev->dev;
+ owl->data = (struct dwc3_owl_data *)of_id->data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "missing memory base resource\n");
+ return -EINVAL;
+ }
+
+ res = devm_request_mem_region(dev, res->start,resource_size(res),
+ dev_name(dev));
+ if (!res) {
+ dev_err(dev, "can't request mem region\n");
+ return -ENOMEM;
+ }
+
+ base = devm_ioremap_nocache(dev, res->start, resource_size(res));
+ if (!base) {
+ dev_err(dev, "ioremap failed\n");
+ return -ENOMEM;
+ }
+
+ owl->base = base;
+
+#ifdef FPGA_VERIFY
+ if (owl->data->ic_type == DWC3_S700)
+ owl->regs.usbecs = devm_ioremap_nocache(dev, S700_USB3_ECS, 4);
+ else if (owl->data->ic_type == DWC3_S900)
+ owl->regs.usbecs = devm_ioremap_nocache(dev, S900_USB3_ECS, 4);
+
+ if (!owl->regs.usbecs) {
+ dev_err(dev, "ioremap failed\n");
+ return -ENOMEM;
+ }
+#endif
+
+#if defined(DEBUG_S700_CLK) || defined(DEBUG_S900_CLK)
+ if (dwc3_owl_debug_regs_remap(owl))
+ return -ENOMEM;
+#endif
+
+ //"usb3_480mpll0", "usb3_480mphy0", "usb3_5gphy", "usb3_cce", "usb3_mac";
+ owl->clk_usb3_480mpll0 = devm_clk_get(dev, "usb3_480mpll0");
+ if (IS_ERR(owl->clk_usb3_480mpll0)) {
+ dev_err(&pdev->dev, "unable to get clk_usb3_480mpll0\n");
+ return -EINVAL;
+ }
+
+ owl->clk_usb3_480mphy0 = devm_clk_get(dev, "usb3_480mphy0");
+ if (IS_ERR(owl->clk_usb3_480mphy0)) {
+ dev_err(&pdev->dev, "unable to get usb3_480mphy0\n");
+ return -EINVAL;
+ }
+
+ owl->clk_usb3_5gphy = devm_clk_get(dev, "usb3_5gphy");
+ if (IS_ERR(owl->clk_usb3_5gphy)) {
+ dev_err(&pdev->dev, "unable to get usb3_5gphy\n");
+ return -EINVAL;
+ }
+
+ owl->clk_usb3_cce = devm_clk_get(dev, "usb3_cce");
+ if (IS_ERR(owl->clk_usb3_cce)) {
+ dev_err(&pdev->dev, "unable to get usb3_cce\n");
+ return -EINVAL;
+ }
+
+ owl->clk_usb3_mac = devm_clk_get(dev, "usb3_mac");
+ if (IS_ERR(owl->clk_usb3_mac)) {
+ dev_err(&pdev->dev, "unable to get usb3_mac\n");
+ return -EINVAL;
+ }
+
+ dwc3_owl_clk_init(owl);
+
+ ret = of_platform_populate(node, NULL, NULL, dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to create dwc3 core\n");
+ goto err;
+ }
+
+ _dwc3_owl = owl;
+
+ printk("%s %d success\n", __FUNCTION__, __LINE__);
+
+ return 0;
+
+err:
+ dwc3_owl_clk_exit(owl);
+
+ return ret;
+}
+
+
+static int dwc3_owl_remove_core(struct device *dev, void *c)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ platform_device_unregister(pdev);
+
+ return 0;
+}
+
+static int dwc3_owl_remove(struct platform_device *pdev)
+{
+ struct dwc3_owl *owl = platform_get_drvdata(pdev);
+
+ _dwc3_owl = 0;
+
+ dwc3_owl_clk_exit(owl);
+ device_for_each_child(&pdev->dev, NULL, dwc3_owl_remove_core);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PM_SLEEP
+static int dwc3_owl_suspend(struct device *dev)
+{
+ printk("%s\n", __func__);
+
+ return 0;
+}
+
+static int dwc3_owl_resume(struct device *dev)
+{
+ printk("%s\n", __func__);
+
+ return 0;
+}
+
+static const struct dev_pm_ops dwc3_owl_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(dwc3_owl_suspend, dwc3_owl_resume)
+};
+
+#define DEV_PM_OPS (&dwc3_owl_dev_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif /* CONFIG_PM_SLEEP */
+
+
+#ifdef CONFIG_OF
+static struct dwc3_owl_data dwc3_owl_s700_data = {
+ .ic_type = DWC3_S700,
+};
+
+static struct dwc3_owl_data dwc3_owl_s900_data = {
+ .ic_type = DWC3_S900,
+};
+
+static const struct of_device_id owl_dwc3_match[] = {
+ { .compatible = "actions,s700-dwc3", .data = &dwc3_owl_s700_data,},
+ { .compatible = "actions,s900-dwc3", .data = &dwc3_owl_s900_data,},
+ {},
+};
+MODULE_DEVICE_TABLE(of, owl_dwc3_match);
+#endif
+
+
+static struct platform_driver dwc3_owl_driver = {
+ .probe = dwc3_owl_probe,
+ .remove = dwc3_owl_remove,
+ .driver = {
+ .name = "owl-dwc3",
+ .of_match_table = of_match_ptr(owl_dwc3_match),
+ .pm = DEV_PM_OPS,
+ },
+};
+
+module_platform_driver(dwc3_owl_driver);
+
+#if 0
+static int __init dwc3_owl_init(void)
+{
+ printk("%s %d %s %s\n", __FUNCTION__, __LINE__, __DATE__, __TIME__);
+ return platform_driver_register(&dwc3_owl_driver);
+}
+module_init(dwc3_owl_init);
+
+static void __exit dwc3_owl_exit(void)
+{
+ platform_driver_unregister(&dwc3_owl_driver);
+}
+module_exit(dwc3_owl_exit);
+#endif
+
+MODULE_ALIAS("platform:owl-dwc3");
+MODULE_AUTHOR("tangshaoqing <tangshaoqing@actions-semi.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DesignWare USB3 actions Glue Layer");
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 24b4f091acb8..00f96f86011d 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -160,6 +160,18 @@ config USB_MXS_PHY
MXS Phy is used by some of the i.MX SoCs, for example imx23/28/6x.
+config OWL_USB2PHY
+ tristate "ACTIONS OWL USB2 PHY Driver"
+ help
+ Enable this to support the USB2 PHY that is part of SOC. This
+ driver takes care of all the PHY functionality.
+
+config OWL_USB3PHY
+ tristate "ACTIONS OWL USB3 PHY Driver"
+ help
+ Enable this to support the USB3 PHY that is part of SOC. This
+ driver takes care of all the PHY functionality.
+
config USB_TEGRA_PHY
tristate "NVIDIA Tegra USB PHY Driver"
depends on ARCH_TEGRA
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index df1d99010079..401718750c2b 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -24,3 +24,5 @@ obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o
obj-$(CONFIG_USB_ULPI) += phy-ulpi.o
obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o
obj-$(CONFIG_KEYSTONE_USB_PHY) += phy-keystone.o
+obj-$(CONFIG_OWL_USB2PHY) += phy-owl-usb2.o
+obj-$(CONFIG_OWL_USB3PHY) += phy-owl-usb3.o
diff --git a/drivers/usb/phy/phy-owl-usb.h b/drivers/usb/phy/phy-owl-usb.h
new file mode 100644
index 000000000000..a72b2920b27b
--- /dev/null
+++ b/drivers/usb/phy/phy-owl-usb.h
@@ -0,0 +1,48 @@
+/*
+ * Actions OWL SoCs phy driver
+ *
+ * Copyright (c) 2015 Actions Semiconductor Co., ltd.
+ * tangshaoqing <tangshaoqing@actions-semi.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __PHY_OWL_USB_H
+#define __PHY_OWL_USB_H
+
+
+#include <linux/usb/phy.h>
+
+enum {
+ PHY_S700 = 0,
+ PHY_S900 = 0x100
+};
+
+struct phy_owl_data {
+ int ic_type;
+};
+
+
+struct owl_usbphy {
+ struct usb_phy phy;
+ struct device *dev;
+ struct phy_owl_data *data;
+ void __iomem *regs;
+};
+
+#define phy_to_sphy(x) container_of((x), struct owl_usbphy, phy)
+
+
+#endif /* __PHY_OWL_USB_H */
+
diff --git a/drivers/usb/phy/phy-owl-usb2.c b/drivers/usb/phy/phy-owl-usb2.c
new file mode 100644
index 000000000000..51abc89f5387
--- /dev/null
+++ b/drivers/usb/phy/phy-owl-usb2.c
@@ -0,0 +1,266 @@
+/*
+ * Actions OWL SoCs phy driver
+ *
+ * Copyright (c) 2015 Actions Semiconductor Co., ltd.
+ * tangshaoqing <tangshaoqing@actions-semi.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include "phy-owl-usb.h"
+
+struct owl_usbphy *usb2_sphy;
+
+static void setphy(struct owl_usbphy *sphy, unsigned char reg_add, unsigned char value)
+{
+ void __iomem *usb3_usb_vcon = sphy->regs;
+ volatile unsigned char addr_low;
+ volatile unsigned char addr_high;
+ volatile unsigned int vstate;
+
+ addr_low = reg_add & 0x0f;
+ addr_high = (reg_add >> 4) & 0x0f;
+
+ vstate = value;
+ vstate = vstate << 8;
+
+ addr_low |= 0x10;
+ writel(vstate | addr_low, usb3_usb_vcon);
+ mb();
+
+ addr_low &= 0x0f;
+ writel(vstate | addr_low, usb3_usb_vcon);
+ mb();
+
+ addr_low |= 0x10;
+ writel(vstate | addr_low, usb3_usb_vcon);
+ mb();
+
+ addr_high |= 0x10;
+ writel(vstate | addr_high, usb3_usb_vcon);
+ mb();
+
+ addr_high &= 0x0f;
+ writel(vstate | addr_high, usb3_usb_vcon);
+ mb();
+
+ addr_high |= 0x10;
+ writel(vstate | addr_high, usb3_usb_vcon);
+ mb();
+ return;
+}
+
+
+#define SET_PHY_FROM_CONFIG_FILE
+#undef SET_PHY_FROM_CONFIG_FILE
+
+#ifdef SET_PHY_FROM_CONFIG_FILE
+void phy_debug_setphy(unsigned char reg_add, unsigned char value)
+{
+ if (usb2_sphy)
+ setphy(usb2_sphy, reg_add, value);
+}
+
+extern int set_phy_from_config_file(char *file_path);
+static void dwc3_phy_setup_from_config_file(int is_device_mode)
+{
+ if (is_device_mode)
+ set_phy_from_config_file("/misc/modules/phy_config_dwc3");
+ else
+ set_phy_from_config_file("/misc/modules/phy_config_xhci");
+
+ udelay(100);
+
+ return;
+}
+#endif
+
+
+static int s900_usb2phy_param_setup(struct owl_usbphy *sphy, int is_device_mode)
+{
+#ifdef SET_PHY_FROM_CONFIG_FILE
+ dwc3_phy_setup_from_config_file(is_device_mode);
+ return 0;
+#endif
+
+ if (is_device_mode) {
+ printk("%s device mode\n", __FUNCTION__);
+
+ setphy(sphy, 0xe7, 0x1b);
+ setphy(sphy, 0xe7,0x1f);
+
+ udelay(10);
+
+ setphy(sphy, 0xe2,0x48);
+ /* setphy(sphy, 0xe5, 0x00); */
+ setphy(sphy, 0xe0, 0xa3);
+ setphy(sphy, 0x87, 0x1f);
+ } else {
+ printk("%s host mode\n", __FUNCTION__);
+
+ setphy(sphy, 0xe7, 0x1b);
+ setphy(sphy, 0xe7, 0x1f);
+
+ udelay(10);
+
+ setphy(sphy, 0xe2,0x46);
+ /* setphy(sphy, 0xe5, 0x00); */
+ setphy(sphy, 0xe0, 0xa3);
+ setphy(sphy, 0x87, 0x1f);
+ }
+
+ return 0;
+}
+
+int owl_dwc3_usb2phy_param_setup(int is_device_mode)
+{
+ int ret = 0;
+
+ if (!usb2_sphy)
+ return 0;
+
+ if (usb2_sphy->data->ic_type == PHY_S900)
+ ret = s900_usb2phy_param_setup(usb2_sphy, is_device_mode);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(owl_dwc3_usb2phy_param_setup);
+
+
+static int owl_usb2phy_init(struct usb_phy *phy)
+{
+ return 0;
+}
+
+static void owl_usb2phy_shutdown(struct usb_phy *phy)
+{
+}
+
+static const struct of_device_id owl_usbphy_dt_match[];
+static int owl_usb2phy_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(owl_usbphy_dt_match, &pdev->dev);
+ struct owl_usbphy *sphy;
+ struct device *dev = &pdev->dev;
+ struct resource *phy_mem;
+ void __iomem *phy_base;
+ int ret = 0;
+
+ printk("%s %d\n", __func__, __LINE__);
+
+ if (!of_id) {
+ dev_err(dev, "no compatible OF match\n");
+ return -EINVAL;
+ }
+
+ phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ phy_base = devm_ioremap_nocache(dev, phy_mem->start, resource_size(phy_mem));
+ if (!phy_base) {
+ dev_err(dev, "ioremap failed\n");
+ return -ENOMEM;
+ }
+
+ sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL);
+ if (!sphy)
+ return -ENOMEM;
+
+
+ sphy->dev = dev;
+ sphy->data = (struct phy_owl_data *)of_id->data;
+
+ sphy->regs = phy_base;
+ sphy->phy.dev = sphy->dev;
+ sphy->phy.label = "owl-usb2phy";
+ sphy->phy.init = owl_usb2phy_init;
+ sphy->phy.shutdown = owl_usb2phy_shutdown;
+
+
+ platform_set_drvdata(pdev, sphy);
+
+ ATOMIC_INIT_NOTIFIER_HEAD(&sphy->phy.notifier);
+
+
+ ret = usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2);
+ if(ret) {
+ return ret;
+ }
+
+ usb2_sphy = sphy;
+
+ return ret;
+
+}
+
+static int owl_usb2phy_remove(struct platform_device *pdev)
+{
+ struct owl_usbphy *sphy = platform_get_drvdata(pdev);
+
+ usb2_sphy = 0;
+ usb_remove_phy(&sphy->phy);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_OF
+static struct phy_owl_data phy_owl_s700_data = {
+ .ic_type = PHY_S700,
+};
+
+static struct phy_owl_data phy_owl_s900_data = {
+ .ic_type = PHY_S900,
+};
+
+static const struct of_device_id owl_usbphy_dt_match[] = {
+ {
+ .compatible = "actions,s700-usb2phy",
+ .data = &phy_owl_s700_data,
+ },
+ {
+ .compatible = "actions,s900-usb2phy",
+ .data = &phy_owl_s900_data,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, owl_usbphy_dt_match);
+#endif
+
+
+static struct platform_driver owl_usb2phy_driver = {
+ .probe = owl_usb2phy_probe,
+ .remove = owl_usb2phy_remove,
+ .driver = {
+ .name = "owl-usb2phy",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(owl_usbphy_dt_match),
+ },
+};
+
+module_platform_driver(owl_usb2phy_driver);
+
+MODULE_DESCRIPTION("Actions owl USB 2.0 phy controller");
+MODULE_AUTHOR("tangshaoqing <tangshaoqing@actions-semi.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:owl-usb2phy");
diff --git a/drivers/usb/phy/phy-owl-usb3.c b/drivers/usb/phy/phy-owl-usb3.c
new file mode 100644
index 000000000000..5256ecc2d665
--- /dev/null
+++ b/drivers/usb/phy/phy-owl-usb3.c
@@ -0,0 +1,183 @@
+/*
+ * Actions OWL SoCs phy driver
+ *
+ * Copyright (c) 2015 Actions Semiconductor Co., ltd.
+ * tangshaoqing <tangshaoqing@actions-semi.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License v2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include "phy-owl-usb.h"
+
+
+
+static inline u8 owl_phy_readb(void __iomem *base, u32 offset)
+{
+ return readb(base + offset);
+}
+
+static inline void owl_phy_writeb(void __iomem *base, u32 offset, u8 value)
+{
+ writeb(value, base + offset);
+}
+
+
+
+#define USB3_TX_DATA_PATH_CTRL (0X5D)
+#define USB3_RX_DATA_PATH_CTRL1 (0X87)
+
+static int s900_usb3phy_init(struct usb_phy *phy)
+{
+ struct owl_usbphy *sphy = phy_to_sphy(phy);
+ void __iomem *base = sphy->regs;
+ u8 reg;
+ u32 offset;
+
+
+ /* IO_OR_U8(USB3_TX_DATA_PATH_CTRL, 0x02); */
+ offset = USB3_TX_DATA_PATH_CTRL;
+ reg = owl_phy_readb(base, offset);
+ reg |= 0x02;
+ owl_phy_writeb(base, offset, reg);
+ printk("%s 0x%x:0x%x\n",__FUNCTION__, offset, owl_phy_readb(base, offset));
+
+ /* IO_OR_U8(USB3_RX_DATA_PATH_CTRL1, 0x20); */
+ offset = USB3_RX_DATA_PATH_CTRL1;
+ reg = owl_phy_readb(base, offset);
+ reg |= 0x20;
+ owl_phy_writeb(base, offset, reg);
+ printk("%s 0x%x:0x%x\n",__FUNCTION__, offset, owl_phy_readb(base, offset));
+
+ return 0;
+}
+
+static int owl_usb3phy_init(struct usb_phy *phy)
+{
+ struct owl_usbphy *sphy = phy_to_sphy(phy);
+
+ if (sphy->data->ic_type == PHY_S900)
+ s900_usb3phy_init(phy);
+
+ return 0;
+}
+
+static void owl_usb3phy_shutdown(struct usb_phy *phy)
+{
+}
+
+static const struct of_device_id owl_usbphy_dt_match[];
+static int owl_usb3phy_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(owl_usbphy_dt_match, &pdev->dev);
+ struct owl_usbphy *sphy;
+ struct device *dev = &pdev->dev;
+ struct resource *phy_mem;
+ void __iomem *phy_base;
+
+ if (!of_id) {
+ dev_err(dev, "no compatible OF match\n");
+ return -EINVAL;
+ }
+
+ phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ phy_base = devm_ioremap_nocache(dev, phy_mem->start, resource_size(phy_mem));
+ if (!phy_base) {
+ dev_err(dev, "ioremap failed\n");
+ return -ENOMEM;
+ }
+
+ sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL);
+ if (!sphy)
+ return -ENOMEM;
+
+
+ sphy->dev = dev;
+ sphy->data = (struct phy_owl_data *)of_id->data;
+
+
+ sphy->regs = phy_base;
+ sphy->phy.dev = sphy->dev;
+ sphy->phy.label = "owl-usb3phy";
+ sphy->phy.init = owl_usb3phy_init;
+ sphy->phy.shutdown = owl_usb3phy_shutdown;
+
+
+ platform_set_drvdata(pdev, sphy);
+
+ printk("%s %d\n", __func__, __LINE__);
+
+ return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB3);
+}
+
+static int owl_usb3phy_remove(struct platform_device *pdev)
+{
+ struct owl_usbphy *sphy = platform_get_drvdata(pdev);
+
+ usb_remove_phy(&sphy->phy);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_OF
+static struct phy_owl_data phy_owl_s700_data = {
+ .ic_type = PHY_S700,
+};
+
+static struct phy_owl_data phy_owl_s900_data = {
+ .ic_type = PHY_S900,
+};
+
+static const struct of_device_id owl_usbphy_dt_match[] = {
+ {
+ .compatible = "actions,s700-usb3phy",
+ .data = &phy_owl_s700_data,
+ },
+ {
+ .compatible = "actions,s900-usb3phy",
+ .data = &phy_owl_s900_data,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, owl_usbphy_dt_match);
+#endif
+
+
+static struct platform_driver owl_usb3phy_driver = {
+ .probe = owl_usb3phy_probe,
+ .remove = owl_usb3phy_remove,
+ .driver = {
+ .name = "owl-usb3phy",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(owl_usbphy_dt_match),
+ },
+};
+
+module_platform_driver(owl_usb3phy_driver);
+
+MODULE_DESCRIPTION("Actions owl USB 3.0 phy controller");
+MODULE_AUTHOR("tangshaoqing <tangshaoqing@actions-semi.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:owl-usb3phy");