From 04a6221c509e90b5f921d408bbf0afcf91147280 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Thu, 10 Jan 2013 16:35:53 +0800 Subject: usb: phy: mxs-phy: add set_suspend API It needs to call set_suspend during USB suspend/resume Signed-off-by: Peter Chen Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/mxs-phy.c b/drivers/usb/otg/mxs-phy.c index 7630272..5158332 100644 --- a/drivers/usb/otg/mxs-phy.c +++ b/drivers/usb/otg/mxs-phy.c @@ -76,6 +76,25 @@ static void mxs_phy_shutdown(struct usb_phy *phy) clk_disable_unprepare(mxs_phy->clk); } +static int mxs_phy_suspend(struct usb_phy *x, int suspend) +{ + struct mxs_phy *mxs_phy = to_mxs_phy(x); + + if (suspend) { + writel_relaxed(0xffffffff, x->io_priv + HW_USBPHY_PWD); + writel_relaxed(BM_USBPHY_CTRL_CLKGATE, + x->io_priv + HW_USBPHY_CTRL_SET); + clk_disable_unprepare(mxs_phy->clk); + } else { + clk_prepare_enable(mxs_phy->clk); + writel_relaxed(BM_USBPHY_CTRL_CLKGATE, + x->io_priv + HW_USBPHY_CTRL_CLR); + writel_relaxed(0, x->io_priv + HW_USBPHY_PWD); + } + + return 0; +} + static int mxs_phy_on_connect(struct usb_phy *phy, enum usb_device_speed speed) { @@ -137,6 +156,7 @@ static int mxs_phy_probe(struct platform_device *pdev) mxs_phy->phy.label = DRIVER_NAME; mxs_phy->phy.init = mxs_phy_init; mxs_phy->phy.shutdown = mxs_phy_shutdown; + mxs_phy->phy.set_suspend = mxs_phy_suspend; mxs_phy->phy.notify_connect = mxs_phy_on_connect; mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect; -- cgit v0.10.2 From 337dc3a7684cf6577b5595b9bb96e1af06baec41 Mon Sep 17 00:00:00 2001 From: Praveen Paneri Date: Fri, 23 Nov 2012 16:03:06 +0530 Subject: usb: phy: samsung: Introducing usb phy driver for hsotg This driver uses usb_phy interface to interact with s3c-hsotg. Supports phy_init and phy_shutdown functions to enable/disable usb phy. Support will be extended to host controllers and more Samsung SoCs. Signed-off-by: Praveen Paneri Acked-by: Heiko Stuebner Acked-by: Kyungmin Park Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt new file mode 100644 index 0000000..7b26e2d --- /dev/null +++ b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt @@ -0,0 +1,11 @@ +* Samsung's usb phy transceiver + +The Samsung's phy transceiver is used for controlling usb otg phy for +s3c-hsotg usb device controller. +TODO: Adding the PHY binding with controller(s) according to the under +developement generic PHY driver. + +Required properties: +- compatible : should be "samsung,exynos4210-usbphy" +- reg : base physical address of the phy registers and length of memory mapped + region. diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 5de6e7f..36a85b6 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -45,3 +45,11 @@ config USB_RCAR_PHY To compile this driver as a module, choose M here: the module will be called rcar-phy. + +config SAMSUNG_USBPHY + bool "Samsung USB PHY controller Driver" + depends on USB_S3C_HSOTG + select USB_OTG_UTILS + help + Enable this to support Samsung USB phy controller for samsung + SoCs. diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 1a579a8..ec304f6 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_USB_ISP1301) += isp1301.o obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o obj-$(CONFIG_USB_EHCI_TEGRA) += tegra_usb_phy.o obj-$(CONFIG_USB_RCAR_PHY) += rcar-phy.o +obj-$(CONFIG_SAMSUNG_USBPHY) += samsung-usbphy.o diff --git a/drivers/usb/phy/samsung-usbphy.c b/drivers/usb/phy/samsung-usbphy.c new file mode 100644 index 0000000..5c5e1bb5 --- /dev/null +++ b/drivers/usb/phy/samsung-usbphy.c @@ -0,0 +1,354 @@ +/* linux/drivers/usb/phy/samsung-usbphy.c + * + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Praveen Paneri + * + * Samsung USB2.0 High-speed OTG transceiver, talks to S3C HS OTG controller + * + * 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. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ + +#define SAMSUNG_PHYPWR (0x00) + +#define PHYPWR_NORMAL_MASK (0x19 << 0) +#define PHYPWR_OTG_DISABLE (0x1 << 4) +#define PHYPWR_ANALOG_POWERDOWN (0x1 << 3) +#define PHYPWR_FORCE_SUSPEND (0x1 << 1) +/* For Exynos4 */ +#define PHYPWR_NORMAL_MASK_PHY0 (0x39 << 0) +#define PHYPWR_SLEEP_PHY0 (0x1 << 5) + +#define SAMSUNG_PHYCLK (0x04) + +#define PHYCLK_MODE_USB11 (0x1 << 6) +#define PHYCLK_EXT_OSC (0x1 << 5) +#define PHYCLK_COMMON_ON_N (0x1 << 4) +#define PHYCLK_ID_PULL (0x1 << 2) +#define PHYCLK_CLKSEL_MASK (0x3 << 0) +#define PHYCLK_CLKSEL_48M (0x0 << 0) +#define PHYCLK_CLKSEL_12M (0x2 << 0) +#define PHYCLK_CLKSEL_24M (0x3 << 0) + +#define SAMSUNG_RSTCON (0x08) + +#define RSTCON_PHYLINK_SWRST (0x1 << 2) +#define RSTCON_HLINK_SWRST (0x1 << 1) +#define RSTCON_SWRST (0x1 << 0) + +#ifndef MHZ +#define MHZ (1000*1000) +#endif + +enum samsung_cpu_type { + TYPE_S3C64XX, + TYPE_EXYNOS4210, +}; + +/* + * struct samsung_usbphy - transceiver driver state + * @phy: transceiver structure + * @plat: platform data + * @dev: The parent device supplied to the probe function + * @clk: usb phy clock + * @regs: usb phy register memory base + * @ref_clk_freq: reference clock frequency selection + * @cpu_type: machine identifier + */ +struct samsung_usbphy { + struct usb_phy phy; + struct samsung_usbphy_data *plat; + struct device *dev; + struct clk *clk; + void __iomem *regs; + int ref_clk_freq; + int cpu_type; +}; + +#define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) + +/* + * Returns reference clock frequency selection value + */ +static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) +{ + struct clk *ref_clk; + int refclk_freq = 0; + + ref_clk = clk_get(sphy->dev, "xusbxti"); + if (IS_ERR(ref_clk)) { + dev_err(sphy->dev, "Failed to get reference clock\n"); + return PTR_ERR(ref_clk); + } + + switch (clk_get_rate(ref_clk)) { + case 12 * MHZ: + refclk_freq = PHYCLK_CLKSEL_12M; + break; + case 24 * MHZ: + refclk_freq = PHYCLK_CLKSEL_24M; + break; + case 48 * MHZ: + refclk_freq = PHYCLK_CLKSEL_48M; + break; + default: + if (sphy->cpu_type == TYPE_S3C64XX) + refclk_freq = PHYCLK_CLKSEL_48M; + else + refclk_freq = PHYCLK_CLKSEL_24M; + break; + } + clk_put(ref_clk); + + return refclk_freq; +} + +static void samsung_usbphy_enable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phypwr; + u32 phyclk; + u32 rstcon; + + /* set clock frequency for PLL */ + phyclk = sphy->ref_clk_freq; + phypwr = readl(regs + SAMSUNG_PHYPWR); + rstcon = readl(regs + SAMSUNG_RSTCON); + + switch (sphy->cpu_type) { + case TYPE_S3C64XX: + phyclk &= ~PHYCLK_COMMON_ON_N; + phypwr &= ~PHYPWR_NORMAL_MASK; + rstcon |= RSTCON_SWRST; + break; + case TYPE_EXYNOS4210: + phypwr &= ~PHYPWR_NORMAL_MASK_PHY0; + rstcon |= RSTCON_SWRST; + default: + break; + } + + writel(phyclk, regs + SAMSUNG_PHYCLK); + /* Configure PHY0 for normal operation*/ + writel(phypwr, regs + SAMSUNG_PHYPWR); + /* reset all ports of PHY and Link */ + writel(rstcon, regs + SAMSUNG_RSTCON); + udelay(10); + rstcon &= ~RSTCON_SWRST; + writel(rstcon, regs + SAMSUNG_RSTCON); +} + +static void samsung_usbphy_disable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phypwr; + + phypwr = readl(regs + SAMSUNG_PHYPWR); + + switch (sphy->cpu_type) { + case TYPE_S3C64XX: + phypwr |= PHYPWR_NORMAL_MASK; + break; + case TYPE_EXYNOS4210: + phypwr |= PHYPWR_NORMAL_MASK_PHY0; + default: + break; + } + + /* Disable analog and otg block power */ + writel(phypwr, regs + SAMSUNG_PHYPWR); +} + +/* + * The function passed to the usb driver for phy initialization + */ +static int samsung_usbphy_init(struct usb_phy *phy) +{ + struct samsung_usbphy *sphy; + int ret = 0; + + sphy = phy_to_sphy(phy); + + /* Enable the phy clock */ + ret = clk_prepare_enable(sphy->clk); + if (ret) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return ret; + } + + /* Disable phy isolation */ + if (sphy->plat && sphy->plat->pmu_isolation) + sphy->plat->pmu_isolation(false); + + /* Initialize usb phy registers */ + samsung_usbphy_enable(sphy); + + /* Disable the phy clock */ + clk_disable_unprepare(sphy->clk); + return ret; +} + +/* + * The function passed to the usb driver for phy shutdown + */ +static void samsung_usbphy_shutdown(struct usb_phy *phy) +{ + struct samsung_usbphy *sphy; + + sphy = phy_to_sphy(phy); + + if (clk_prepare_enable(sphy->clk)) { + dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); + return; + } + + /* De-initialize usb phy registers */ + samsung_usbphy_disable(sphy); + + /* Enable phy isolation */ + if (sphy->plat && sphy->plat->pmu_isolation) + sphy->plat->pmu_isolation(true); + + clk_disable_unprepare(sphy->clk); +} + +static const struct of_device_id samsung_usbphy_dt_match[]; + +static inline int samsung_usbphy_get_driver_data(struct platform_device *pdev) +{ + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(samsung_usbphy_dt_match, + pdev->dev.of_node); + return (int) match->data; + } + + return platform_get_device_id(pdev)->driver_data; +} + +static int __devinit samsung_usbphy_probe(struct platform_device *pdev) +{ + struct samsung_usbphy *sphy; + struct samsung_usbphy_data *pdata; + struct device *dev = &pdev->dev; + struct resource *phy_mem; + void __iomem *phy_base; + struct clk *clk; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "%s: no platform data defined\n", __func__); + return -EINVAL; + } + + phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!phy_mem) { + dev_err(dev, "%s: missing mem resource\n", __func__); + return -ENODEV; + } + + phy_base = devm_request_and_ioremap(dev, phy_mem); + if (!phy_base) { + dev_err(dev, "%s: register mapping failed\n", __func__); + return -ENXIO; + } + + sphy = devm_kzalloc(dev, sizeof(*sphy), GFP_KERNEL); + if (!sphy) + return -ENOMEM; + + clk = devm_clk_get(dev, "otg"); + if (IS_ERR(clk)) { + dev_err(dev, "Failed to get otg clock\n"); + return PTR_ERR(clk); + } + + sphy->dev = &pdev->dev; + sphy->plat = pdata; + sphy->regs = phy_base; + sphy->clk = clk; + sphy->phy.dev = sphy->dev; + sphy->phy.label = "samsung-usbphy"; + sphy->phy.init = samsung_usbphy_init; + sphy->phy.shutdown = samsung_usbphy_shutdown; + sphy->cpu_type = samsung_usbphy_get_driver_data(pdev); + sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); + + platform_set_drvdata(pdev, sphy); + + return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); +} + +static int __exit samsung_usbphy_remove(struct platform_device *pdev) +{ + struct samsung_usbphy *sphy = platform_get_drvdata(pdev); + + usb_remove_phy(&sphy->phy); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id samsung_usbphy_dt_match[] = { + { + .compatible = "samsung,s3c64xx-usbphy", + .data = (void *)TYPE_S3C64XX, + }, { + .compatible = "samsung,exynos4210-usbphy", + .data = (void *)TYPE_EXYNOS4210, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); +#endif + +static struct platform_device_id samsung_usbphy_driver_ids[] = { + { + .name = "s3c64xx-usbphy", + .driver_data = TYPE_S3C64XX, + }, { + .name = "exynos4210-usbphy", + .driver_data = TYPE_EXYNOS4210, + }, + {}, +}; + +MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); + +static struct platform_driver samsung_usbphy_driver = { + .probe = samsung_usbphy_probe, + .remove = __devexit_p(samsung_usbphy_remove), + .id_table = samsung_usbphy_driver_ids, + .driver = { + .name = "samsung-usbphy", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(samsung_usbphy_dt_match), + }, +}; + +module_platform_driver(samsung_usbphy_driver); + +MODULE_DESCRIPTION("Samsung USB phy controller"); +MODULE_AUTHOR("Praveen Paneri "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:samsung-usbphy"); diff --git a/include/linux/platform_data/samsung-usbphy.h b/include/linux/platform_data/samsung-usbphy.h new file mode 100644 index 0000000..1bd24cb --- /dev/null +++ b/include/linux/platform_data/samsung-usbphy.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * http://www.samsung.com/ + * Author: Praveen Paneri + * + * Defines platform data for samsung usb phy driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __SAMSUNG_USBPHY_PLATFORM_H +#define __SAMSUNG_USBPHY_PLATFORM_H + +/** + * samsung_usbphy_data - Platform data for USB PHY driver. + * @pmu_isolation: Function to control usb phy isolation in PMU. + */ +struct samsung_usbphy_data { + void (*pmu_isolation)(int on); +}; + +extern void samsung_usbphy_set_pdata(struct samsung_usbphy_data *pd); + +#endif /* __SAMSUNG_USBPHY_PLATFORM_H */ -- cgit v0.10.2 From b2e587dbb7a7554b56d2f38e284ad975d2f00181 Mon Sep 17 00:00:00 2001 From: Praveen Paneri Date: Wed, 14 Nov 2012 15:57:16 +0530 Subject: usb: phy: s3c-hsotg: adding phy driver support Adding the transceiver to hsotg driver. Keeping the platform data for continuing the smooth operation for boards which still uses it Signed-off-by: Praveen Paneri Acked-by: Kyungmin Park Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 141971d..2143df3 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -133,7 +134,9 @@ struct s3c_hsotg_ep { * struct s3c_hsotg - driver state. * @dev: The parent device supplied to the probe function * @driver: USB gadget driver - * @plat: The platform specific configuration data. + * @phy: The otg phy transceiver structure for phy control. + * @plat: The platform specific configuration data. This can be removed once + * all SoCs support usb transceiver. * @regs: The memory area mapped for accessing registers. * @irq: The IRQ number we are using * @supplies: Definition of USB power supplies @@ -153,6 +156,7 @@ struct s3c_hsotg_ep { struct s3c_hsotg { struct device *dev; struct usb_gadget_driver *driver; + struct usb_phy *phy; struct s3c_hsotg_plat *plat; spinlock_t lock; @@ -2854,7 +2858,10 @@ static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg) struct platform_device *pdev = to_platform_device(hsotg->dev); dev_dbg(hsotg->dev, "pdev 0x%p\n", pdev); - if (hsotg->plat->phy_init) + + if (hsotg->phy) + usb_phy_init(hsotg->phy); + else if (hsotg->plat->phy_init) hsotg->plat->phy_init(pdev, hsotg->plat->phy_type); } @@ -2869,7 +2876,9 @@ static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg) { struct platform_device *pdev = to_platform_device(hsotg->dev); - if (hsotg->plat->phy_exit) + if (hsotg->phy) + usb_phy_shutdown(hsotg->phy); + else if (hsotg->plat->phy_exit) hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type); } @@ -3493,6 +3502,7 @@ static void s3c_hsotg_release(struct device *dev) static int s3c_hsotg_probe(struct platform_device *pdev) { struct s3c_hsotg_plat *plat = pdev->dev.platform_data; + struct usb_phy *phy; struct device *dev = &pdev->dev; struct s3c_hsotg_ep *eps; struct s3c_hsotg *hsotg; @@ -3501,20 +3511,27 @@ static int s3c_hsotg_probe(struct platform_device *pdev) int ret; int i; - plat = pdev->dev.platform_data; - if (!plat) { - dev_err(&pdev->dev, "no platform data defined\n"); - return -EINVAL; - } - hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL); if (!hsotg) { dev_err(dev, "cannot get memory\n"); return -ENOMEM; } + phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + if (IS_ERR_OR_NULL(phy)) { + /* Fallback for pdata */ + plat = pdev->dev.platform_data; + if (!plat) { + dev_err(&pdev->dev, "no platform data or transceiver defined\n"); + return -EPROBE_DEFER; + } else { + hsotg->plat = plat; + } + } else { + hsotg->phy = phy; + } + hsotg->dev = dev; - hsotg->plat = plat; hsotg->clk = devm_clk_get(&pdev->dev, "otg"); if (IS_ERR(hsotg->clk)) { -- cgit v0.10.2 From 63a1307930867a45f86a1a69f80315b2df7b7b49 Mon Sep 17 00:00:00 2001 From: Cesar Eduardo Barros Date: Tue, 4 Dec 2012 20:21:12 -0200 Subject: usb: phy: mv-otg: use to_delayed_work instead of cast Directly casting a work_struct pointer to a delayed_work is risky if the work member of struct delayed_work is ever moved from being the first member. Instead, use the inline function to_delayed_work(), which does the same cast in a safer way (using container_of). Signed-off-by: Cesar Eduardo Barros Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/mv_otg.c b/drivers/usb/otg/mv_otg.c index 1dd5750..5104bc2 100644 --- a/drivers/usb/otg/mv_otg.c +++ b/drivers/usb/otg/mv_otg.c @@ -420,7 +420,7 @@ static void mv_otg_work(struct work_struct *work) struct usb_otg *otg; int old_state; - mvotg = container_of((struct delayed_work *)work, struct mv_otg, work); + mvotg = container_of(to_delayed_work(work), struct mv_otg, work); run: /* work queue is single thread, or we need spin_lock to protect */ -- cgit v0.10.2 From 69f0946a8db75d6de377c6a5796aa2e417c3c83a Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Tue, 15 Jan 2013 11:40:25 +0530 Subject: usb: phy: samsung: Add support to set pmu isolation Adding support to parse device node data in order to get required properties to set pmu isolation for usb-phy. Signed-off-by: Vivek Gautam Acked-by: Kukjin Kim Reviewed-by: Sylwester Nawrocki Reviewed-by: Doug Anderson Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt index 7b26e2d..22d06cf 100644 --- a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt +++ b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt @@ -9,3 +9,39 @@ Required properties: - compatible : should be "samsung,exynos4210-usbphy" - reg : base physical address of the phy registers and length of memory mapped region. + +Optional properties: +- #address-cells: should be '1' when usbphy node has a child node with 'reg' + property. +- #size-cells: should be '1' when usbphy node has a child node with 'reg' + property. +- ranges: allows valid translation between child's address space and parent's + address space. + +- The child node 'usbphy-sys' to the node 'usbphy' is for the system controller + interface for usb-phy. It should provide the following information required by + usb-phy controller to control phy. + - reg : base physical address of PHY_CONTROL registers. + The size of this register is the total sum of size of all PHY_CONTROL + registers that the SoC has. For example, the size will be + '0x4' in case we have only one PHY_CONTROL register (e.g. + OTHERS register in S3C64XX or USB_PHY_CONTROL register in S5PV210) + and, '0x8' in case we have two PHY_CONTROL registers (e.g. + USBDEVICE_PHY_CONTROL and USBHOST_PHY_CONTROL registers in exynos4x). + and so on. + +Example: + - Exynos4210 + + usbphy@125B0000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "samsung,exynos4210-usbphy"; + reg = <0x125B0000 0x100>; + ranges; + + usbphy-sys { + /* USB device and host PHY_CONTROL registers */ + reg = <0x10020704 0x8>; + }; + }; diff --git a/drivers/usb/phy/samsung-usbphy.c b/drivers/usb/phy/samsung-usbphy.c index 5c5e1bb5..30aebb5 100644 --- a/drivers/usb/phy/samsung-usbphy.c +++ b/drivers/usb/phy/samsung-usbphy.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -60,20 +61,46 @@ #define MHZ (1000*1000) #endif +#define S3C64XX_USBPHY_ENABLE (0x1 << 16) +#define EXYNOS_USBPHY_ENABLE (0x1 << 0) + enum samsung_cpu_type { TYPE_S3C64XX, TYPE_EXYNOS4210, }; /* + * struct samsung_usbphy_drvdata - driver data for various SoC variants + * @cpu_type: machine identifier + * @devphy_en_mask: device phy enable mask for PHY CONTROL register + * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from + * mapped address of system controller. + * + * Here we have a separate mask for device type phy. + * Having different masks for host and device type phy helps + * in setting independent masks in case of SoCs like S5PV210, + * in which PHY0 and PHY1 enable bits belong to same register + * placed at position 0 and 1 respectively. + * Although for newer SoCs like exynos these bits belong to + * different registers altogether placed at position 0. + */ +struct samsung_usbphy_drvdata { + int cpu_type; + int devphy_en_mask; + u32 devphy_reg_offset; +}; + +/* * struct samsung_usbphy - transceiver driver state * @phy: transceiver structure * @plat: platform data * @dev: The parent device supplied to the probe function * @clk: usb phy clock - * @regs: usb phy register memory base + * @regs: usb phy controller registers memory base + * @pmuregs: USB device PHY_CONTROL register memory base * @ref_clk_freq: reference clock frequency selection - * @cpu_type: machine identifier + * @drv_data: driver data available for different SoCs + * @lock: lock for phy operations */ struct samsung_usbphy { struct usb_phy phy; @@ -81,12 +108,66 @@ struct samsung_usbphy { struct device *dev; struct clk *clk; void __iomem *regs; + void __iomem *pmuregs; int ref_clk_freq; - int cpu_type; + const struct samsung_usbphy_drvdata *drv_data; + spinlock_t lock; }; #define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) +static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) +{ + struct device_node *usbphy_sys; + + /* Getting node for system controller interface for usb-phy */ + usbphy_sys = of_get_child_by_name(sphy->dev->of_node, "usbphy-sys"); + if (!usbphy_sys) { + dev_err(sphy->dev, "No sys-controller interface for usb-phy\n"); + return -ENODEV; + } + + sphy->pmuregs = of_iomap(usbphy_sys, 0); + + of_node_put(usbphy_sys); + + if (sphy->pmuregs == NULL) { + dev_err(sphy->dev, "Can't get usb-phy pmu control register\n"); + return -ENODEV; + } + + return 0; +} + +/* + * Set isolation here for phy. + * Here 'on = true' would mean USB PHY block is isolated, hence + * de-activated and vice-versa. + */ +static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) +{ + void __iomem *reg; + u32 reg_val; + u32 en_mask; + + if (!sphy->pmuregs) { + dev_warn(sphy->dev, "Can't set pmu isolation\n"); + return; + } + + reg = sphy->pmuregs + sphy->drv_data->devphy_reg_offset; + en_mask = sphy->drv_data->devphy_en_mask; + + reg_val = readl(reg); + + if (on) + reg_val &= ~en_mask; + else + reg_val |= en_mask; + + writel(reg_val, reg); +} + /* * Returns reference clock frequency selection value */ @@ -112,7 +193,7 @@ static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) refclk_freq = PHYCLK_CLKSEL_48M; break; default: - if (sphy->cpu_type == TYPE_S3C64XX) + if (sphy->drv_data->cpu_type == TYPE_S3C64XX) refclk_freq = PHYCLK_CLKSEL_48M; else refclk_freq = PHYCLK_CLKSEL_24M; @@ -135,7 +216,7 @@ static void samsung_usbphy_enable(struct samsung_usbphy *sphy) phypwr = readl(regs + SAMSUNG_PHYPWR); rstcon = readl(regs + SAMSUNG_RSTCON); - switch (sphy->cpu_type) { + switch (sphy->drv_data->cpu_type) { case TYPE_S3C64XX: phyclk &= ~PHYCLK_COMMON_ON_N; phypwr &= ~PHYPWR_NORMAL_MASK; @@ -165,7 +246,7 @@ static void samsung_usbphy_disable(struct samsung_usbphy *sphy) phypwr = readl(regs + SAMSUNG_PHYPWR); - switch (sphy->cpu_type) { + switch (sphy->drv_data->cpu_type) { case TYPE_S3C64XX: phypwr |= PHYPWR_NORMAL_MASK; break; @@ -185,6 +266,7 @@ static void samsung_usbphy_disable(struct samsung_usbphy *sphy) static int samsung_usbphy_init(struct usb_phy *phy) { struct samsung_usbphy *sphy; + unsigned long flags; int ret = 0; sphy = phy_to_sphy(phy); @@ -196,13 +278,19 @@ static int samsung_usbphy_init(struct usb_phy *phy) return ret; } + spin_lock_irqsave(&sphy->lock, flags); + /* Disable phy isolation */ if (sphy->plat && sphy->plat->pmu_isolation) sphy->plat->pmu_isolation(false); + else + samsung_usbphy_set_isolation(sphy, false); /* Initialize usb phy registers */ samsung_usbphy_enable(sphy); + spin_unlock_irqrestore(&sphy->lock, flags); + /* Disable the phy clock */ clk_disable_unprepare(sphy->clk); return ret; @@ -214,6 +302,7 @@ static int samsung_usbphy_init(struct usb_phy *phy) static void samsung_usbphy_shutdown(struct usb_phy *phy) { struct samsung_usbphy *sphy; + unsigned long flags; sphy = phy_to_sphy(phy); @@ -222,44 +311,47 @@ static void samsung_usbphy_shutdown(struct usb_phy *phy) return; } + spin_lock_irqsave(&sphy->lock, flags); + /* De-initialize usb phy registers */ samsung_usbphy_disable(sphy); /* Enable phy isolation */ if (sphy->plat && sphy->plat->pmu_isolation) sphy->plat->pmu_isolation(true); + else + samsung_usbphy_set_isolation(sphy, true); + + spin_unlock_irqrestore(&sphy->lock, flags); clk_disable_unprepare(sphy->clk); } static const struct of_device_id samsung_usbphy_dt_match[]; -static inline int samsung_usbphy_get_driver_data(struct platform_device *pdev) +static inline const struct samsung_usbphy_drvdata +*samsung_usbphy_get_driver_data(struct platform_device *pdev) { if (pdev->dev.of_node) { const struct of_device_id *match; match = of_match_node(samsung_usbphy_dt_match, pdev->dev.of_node); - return (int) match->data; + return match->data; } - return platform_get_device_id(pdev)->driver_data; + return (struct samsung_usbphy_drvdata *) + platform_get_device_id(pdev)->driver_data; } static int __devinit samsung_usbphy_probe(struct platform_device *pdev) { struct samsung_usbphy *sphy; - struct samsung_usbphy_data *pdata; + struct samsung_usbphy_data *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct resource *phy_mem; void __iomem *phy_base; struct clk *clk; - - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "%s: no platform data defined\n", __func__); - return -EINVAL; - } + int ret; phy_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!phy_mem) { @@ -283,7 +375,19 @@ static int __devinit samsung_usbphy_probe(struct platform_device *pdev) return PTR_ERR(clk); } - sphy->dev = &pdev->dev; + sphy->dev = dev; + + if (dev->of_node) { + ret = samsung_usbphy_parse_dt(sphy); + if (ret < 0) + return ret; + } else { + if (!pdata) { + dev_err(dev, "no platform data specified\n"); + return -EINVAL; + } + } + sphy->plat = pdata; sphy->regs = phy_base; sphy->clk = clk; @@ -291,9 +395,11 @@ static int __devinit samsung_usbphy_probe(struct platform_device *pdev) sphy->phy.label = "samsung-usbphy"; sphy->phy.init = samsung_usbphy_init; sphy->phy.shutdown = samsung_usbphy_shutdown; - sphy->cpu_type = samsung_usbphy_get_driver_data(pdev); + sphy->drv_data = samsung_usbphy_get_driver_data(pdev); sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); + spin_lock_init(&sphy->lock); + platform_set_drvdata(pdev, sphy); return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); @@ -305,17 +411,30 @@ static int __exit samsung_usbphy_remove(struct platform_device *pdev) usb_remove_phy(&sphy->phy); + if (sphy->pmuregs) + iounmap(sphy->pmuregs); + return 0; } +static const struct samsung_usbphy_drvdata usbphy_s3c64xx = { + .cpu_type = TYPE_S3C64XX, + .devphy_en_mask = S3C64XX_USBPHY_ENABLE, +}; + +static const struct samsung_usbphy_drvdata usbphy_exynos4 = { + .cpu_type = TYPE_EXYNOS4210, + .devphy_en_mask = EXYNOS_USBPHY_ENABLE, +}; + #ifdef CONFIG_OF static const struct of_device_id samsung_usbphy_dt_match[] = { { .compatible = "samsung,s3c64xx-usbphy", - .data = (void *)TYPE_S3C64XX, + .data = &usbphy_s3c64xx, }, { .compatible = "samsung,exynos4210-usbphy", - .data = (void *)TYPE_EXYNOS4210, + .data = &usbphy_exynos4, }, {}, }; @@ -325,10 +444,10 @@ MODULE_DEVICE_TABLE(of, samsung_usbphy_dt_match); static struct platform_device_id samsung_usbphy_driver_ids[] = { { .name = "s3c64xx-usbphy", - .driver_data = TYPE_S3C64XX, + .driver_data = (unsigned long)&usbphy_s3c64xx, }, { .name = "exynos4210-usbphy", - .driver_data = TYPE_EXYNOS4210, + .driver_data = (unsigned long)&usbphy_exynos4, }, {}, }; -- cgit v0.10.2 From b506eebc504caaf863b5c5a68a1e1d304d610482 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Tue, 22 Jan 2013 18:30:40 +0530 Subject: ARM: EXYNOS: Update & move usb-phy types to generic include layer Updating the names of usb-phy types to more generic names: USB_PHY_TYPE_DEIVCE & USB_PHY_TYPE_HOST; and further update its dependencies. Signed-off-by: Praveen Paneri Signed-off-by: Vivek Gautam Acked-by: Kukjin Kim Signed-off-by: Felipe Balbi diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 319dcfa..46ca5ef 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #define EHCI_INSNREG00(base) (base + 0x90) @@ -164,7 +165,7 @@ static int s5p_ehci_probe(struct platform_device *pdev) } if (pdata->phy_init) - pdata->phy_init(pdev, S5P_USB_PHY_HOST); + pdata->phy_init(pdev, USB_PHY_TYPE_HOST); ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; @@ -198,7 +199,7 @@ static int s5p_ehci_remove(struct platform_device *pdev) usb_remove_hcd(hcd); if (pdata && pdata->phy_exit) - pdata->phy_exit(pdev, S5P_USB_PHY_HOST); + pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); clk_disable_unprepare(s5p_ehci->clk); @@ -229,7 +230,7 @@ static int s5p_ehci_suspend(struct device *dev) rc = ehci_suspend(hcd, do_wakeup); if (pdata && pdata->phy_exit) - pdata->phy_exit(pdev, S5P_USB_PHY_HOST); + pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); clk_disable_unprepare(s5p_ehci->clk); @@ -246,7 +247,7 @@ static int s5p_ehci_resume(struct device *dev) clk_prepare_enable(s5p_ehci->clk); if (pdata && pdata->phy_init) - pdata->phy_init(pdev, S5P_USB_PHY_HOST); + pdata->phy_init(pdev, USB_PHY_TYPE_HOST); /* DMA burst Enable */ writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs)); diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index aa3b884..804fb62 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -15,6 +15,7 @@ #include #include #include +#include #include struct exynos_ohci_hcd { @@ -153,7 +154,7 @@ static int exynos_ohci_probe(struct platform_device *pdev) } if (pdata->phy_init) - pdata->phy_init(pdev, S5P_USB_PHY_HOST); + pdata->phy_init(pdev, USB_PHY_TYPE_HOST); ohci = hcd_to_ohci(hcd); ohci_hcd_init(ohci); @@ -184,7 +185,7 @@ static int exynos_ohci_remove(struct platform_device *pdev) usb_remove_hcd(hcd); if (pdata && pdata->phy_exit) - pdata->phy_exit(pdev, S5P_USB_PHY_HOST); + pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); clk_disable_unprepare(exynos_ohci->clk); @@ -229,7 +230,7 @@ static int exynos_ohci_suspend(struct device *dev) clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); if (pdata && pdata->phy_exit) - pdata->phy_exit(pdev, S5P_USB_PHY_HOST); + pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); clk_disable_unprepare(exynos_ohci->clk); @@ -249,7 +250,7 @@ static int exynos_ohci_resume(struct device *dev) clk_prepare_enable(exynos_ohci->clk); if (pdata && pdata->phy_init) - pdata->phy_init(pdev, S5P_USB_PHY_HOST); + pdata->phy_init(pdev, USB_PHY_TYPE_HOST); ohci_resume(hcd, false); diff --git a/include/linux/usb/samsung_usb_phy.h b/include/linux/usb/samsung_usb_phy.h new file mode 100644 index 0000000..9167826 --- /dev/null +++ b/include/linux/usb/samsung_usb_phy.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * http://www.samsung.com/ + * + * Defines phy types for samsung usb phy controllers - HOST or DEIVCE. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +enum samsung_usb_phy_type { + USB_PHY_TYPE_DEVICE, + USB_PHY_TYPE_HOST, +}; -- cgit v0.10.2 From 8c1b3e16e902b010f79e2d299927ec43b495f1c7 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Tue, 22 Jan 2013 18:30:41 +0530 Subject: usb: phy: samsung: Add host phy support to samsung-phy driver This patch adds host phy support to samsung-usbphy driver and further adds support for samsung's exynos5250 usb-phy. Signed-off-by: Praveen Paneri Signed-off-by: Vivek Gautam Acked-by: Kukjin Kim Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt index 22d06cf..0331949 100644 --- a/Documentation/devicetree/bindings/usb/samsung-usbphy.txt +++ b/Documentation/devicetree/bindings/usb/samsung-usbphy.txt @@ -1,15 +1,23 @@ * Samsung's usb phy transceiver -The Samsung's phy transceiver is used for controlling usb otg phy for -s3c-hsotg usb device controller. +The Samsung's phy transceiver is used for controlling usb phy for +s3c-hsotg as well as ehci-s5p and ohci-exynos usb controllers +across Samsung SOCs. TODO: Adding the PHY binding with controller(s) according to the under developement generic PHY driver. Required properties: + +Exynos4210: - compatible : should be "samsung,exynos4210-usbphy" - reg : base physical address of the phy registers and length of memory mapped region. +Exynos5250: +- compatible : should be "samsung,exynos5250-usbphy" +- reg : base physical address of the phy registers and length of memory mapped + region. + Optional properties: - #address-cells: should be '1' when usbphy node has a child node with 'reg' property. diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 36a85b6..fae4d08 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -48,7 +48,7 @@ config USB_RCAR_PHY config SAMSUNG_USBPHY bool "Samsung USB PHY controller Driver" - depends on USB_S3C_HSOTG + depends on USB_S3C_HSOTG || USB_EHCI_S5P || USB_OHCI_EXYNOS select USB_OTG_UTILS help Enable this to support Samsung USB phy controller for samsung diff --git a/drivers/usb/phy/samsung-usbphy.c b/drivers/usb/phy/samsung-usbphy.c index 30aebb5..9e9861c 100644 --- a/drivers/usb/phy/samsung-usbphy.c +++ b/drivers/usb/phy/samsung-usbphy.c @@ -5,7 +5,8 @@ * * Author: Praveen Paneri * - * Samsung USB2.0 High-speed OTG transceiver, talks to S3C HS OTG controller + * Samsung USB2.0 PHY transceiver; talks to S3C HS OTG controller, EHCI-S5P and + * OHCI-EXYNOS controllers. * * 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 @@ -21,11 +22,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include /* Register definitions */ @@ -57,24 +60,132 @@ #define RSTCON_HLINK_SWRST (0x1 << 1) #define RSTCON_SWRST (0x1 << 0) +/* EXYNOS5 */ +#define EXYNOS5_PHY_HOST_CTRL0 (0x00) + +#define HOST_CTRL0_PHYSWRSTALL (0x1 << 31) + +#define HOST_CTRL0_REFCLKSEL_MASK (0x3 << 19) +#define HOST_CTRL0_REFCLKSEL_XTAL (0x0 << 19) +#define HOST_CTRL0_REFCLKSEL_EXTL (0x1 << 19) +#define HOST_CTRL0_REFCLKSEL_CLKCORE (0x2 << 19) + +#define HOST_CTRL0_FSEL_MASK (0x7 << 16) +#define HOST_CTRL0_FSEL(_x) ((_x) << 16) + +#define FSEL_CLKSEL_50M (0x7) +#define FSEL_CLKSEL_24M (0x5) +#define FSEL_CLKSEL_20M (0x4) +#define FSEL_CLKSEL_19200K (0x3) +#define FSEL_CLKSEL_12M (0x2) +#define FSEL_CLKSEL_10M (0x1) +#define FSEL_CLKSEL_9600K (0x0) + +#define HOST_CTRL0_TESTBURNIN (0x1 << 11) +#define HOST_CTRL0_RETENABLE (0x1 << 10) +#define HOST_CTRL0_COMMONON_N (0x1 << 9) +#define HOST_CTRL0_SIDDQ (0x1 << 6) +#define HOST_CTRL0_FORCESLEEP (0x1 << 5) +#define HOST_CTRL0_FORCESUSPEND (0x1 << 4) +#define HOST_CTRL0_WORDINTERFACE (0x1 << 3) +#define HOST_CTRL0_UTMISWRST (0x1 << 2) +#define HOST_CTRL0_LINKSWRST (0x1 << 1) +#define HOST_CTRL0_PHYSWRST (0x1 << 0) + +#define EXYNOS5_PHY_HOST_TUNE0 (0x04) + +#define EXYNOS5_PHY_HSIC_CTRL1 (0x10) + +#define EXYNOS5_PHY_HSIC_TUNE1 (0x14) + +#define EXYNOS5_PHY_HSIC_CTRL2 (0x20) + +#define EXYNOS5_PHY_HSIC_TUNE2 (0x24) + +#define HSIC_CTRL_REFCLKSEL_MASK (0x3 << 23) +#define HSIC_CTRL_REFCLKSEL (0x2 << 23) + +#define HSIC_CTRL_REFCLKDIV_MASK (0x7f << 16) +#define HSIC_CTRL_REFCLKDIV(_x) ((_x) << 16) +#define HSIC_CTRL_REFCLKDIV_12 (0x24 << 16) +#define HSIC_CTRL_REFCLKDIV_15 (0x1c << 16) +#define HSIC_CTRL_REFCLKDIV_16 (0x1a << 16) +#define HSIC_CTRL_REFCLKDIV_19_2 (0x15 << 16) +#define HSIC_CTRL_REFCLKDIV_20 (0x14 << 16) + +#define HSIC_CTRL_SIDDQ (0x1 << 6) +#define HSIC_CTRL_FORCESLEEP (0x1 << 5) +#define HSIC_CTRL_FORCESUSPEND (0x1 << 4) +#define HSIC_CTRL_WORDINTERFACE (0x1 << 3) +#define HSIC_CTRL_UTMISWRST (0x1 << 2) +#define HSIC_CTRL_PHYSWRST (0x1 << 0) + +#define EXYNOS5_PHY_HOST_EHCICTRL (0x30) + +#define HOST_EHCICTRL_ENAINCRXALIGN (0x1 << 29) +#define HOST_EHCICTRL_ENAINCR4 (0x1 << 28) +#define HOST_EHCICTRL_ENAINCR8 (0x1 << 27) +#define HOST_EHCICTRL_ENAINCR16 (0x1 << 26) + +#define EXYNOS5_PHY_HOST_OHCICTRL (0x34) + +#define HOST_OHCICTRL_SUSPLGCY (0x1 << 3) +#define HOST_OHCICTRL_APPSTARTCLK (0x1 << 2) +#define HOST_OHCICTRL_CNTSEL (0x1 << 1) +#define HOST_OHCICTRL_CLKCKTRST (0x1 << 0) + +#define EXYNOS5_PHY_OTG_SYS (0x38) + +#define OTG_SYS_PHYLINK_SWRESET (0x1 << 14) +#define OTG_SYS_LINKSWRST_UOTG (0x1 << 13) +#define OTG_SYS_PHY0_SWRST (0x1 << 12) + +#define OTG_SYS_REFCLKSEL_MASK (0x3 << 9) +#define OTG_SYS_REFCLKSEL_XTAL (0x0 << 9) +#define OTG_SYS_REFCLKSEL_EXTL (0x1 << 9) +#define OTG_SYS_REFCLKSEL_CLKCORE (0x2 << 9) + +#define OTG_SYS_IDPULLUP_UOTG (0x1 << 8) +#define OTG_SYS_COMMON_ON (0x1 << 7) + +#define OTG_SYS_FSEL_MASK (0x7 << 4) +#define OTG_SYS_FSEL(_x) ((_x) << 4) + +#define OTG_SYS_FORCESLEEP (0x1 << 3) +#define OTG_SYS_OTGDISABLE (0x1 << 2) +#define OTG_SYS_SIDDQ_UOTG (0x1 << 1) +#define OTG_SYS_FORCESUSPEND (0x1 << 0) + +#define EXYNOS5_PHY_OTG_TUNE (0x40) + #ifndef MHZ #define MHZ (1000*1000) #endif +#ifndef KHZ +#define KHZ (1000) +#endif + +#define EXYNOS_USBHOST_PHY_CTRL_OFFSET (0x4) #define S3C64XX_USBPHY_ENABLE (0x1 << 16) #define EXYNOS_USBPHY_ENABLE (0x1 << 0) +#define EXYNOS_USB20PHY_CFG_HOST_LINK (0x1 << 0) enum samsung_cpu_type { TYPE_S3C64XX, TYPE_EXYNOS4210, + TYPE_EXYNOS5250, }; /* * struct samsung_usbphy_drvdata - driver data for various SoC variants * @cpu_type: machine identifier * @devphy_en_mask: device phy enable mask for PHY CONTROL register + * @hostphy_en_mask: host phy enable mask for PHY CONTROL register * @devphy_reg_offset: offset to DEVICE PHY CONTROL register from * mapped address of system controller. + * @hostphy_reg_offset: offset to HOST PHY CONTROL register from + * mapped address of system controller. * * Here we have a separate mask for device type phy. * Having different masks for host and device type phy helps @@ -87,7 +198,9 @@ enum samsung_cpu_type { struct samsung_usbphy_drvdata { int cpu_type; int devphy_en_mask; + int hostphy_en_mask; u32 devphy_reg_offset; + u32 hostphy_reg_offset; }; /* @@ -98,8 +211,12 @@ struct samsung_usbphy_drvdata { * @clk: usb phy clock * @regs: usb phy controller registers memory base * @pmuregs: USB device PHY_CONTROL register memory base + * @sysreg: USB2.0 PHY_CFG register memory base * @ref_clk_freq: reference clock frequency selection * @drv_data: driver data available for different SoCs + * @phy_type: Samsung SoCs specific phy types: #HOST + * #DEVICE + * @phy_usage: usage count for phy * @lock: lock for phy operations */ struct samsung_usbphy { @@ -109,13 +226,27 @@ struct samsung_usbphy { struct clk *clk; void __iomem *regs; void __iomem *pmuregs; + void __iomem *sysreg; int ref_clk_freq; const struct samsung_usbphy_drvdata *drv_data; + enum samsung_usb_phy_type phy_type; + atomic_t phy_usage; spinlock_t lock; }; #define phy_to_sphy(x) container_of((x), struct samsung_usbphy, phy) +int samsung_usbphy_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + if (!otg) + return -ENODEV; + + if (!otg->host) + otg->host = host; + + return 0; +} + static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) { struct device_node *usbphy_sys; @@ -129,14 +260,27 @@ static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) sphy->pmuregs = of_iomap(usbphy_sys, 0); - of_node_put(usbphy_sys); - if (sphy->pmuregs == NULL) { dev_err(sphy->dev, "Can't get usb-phy pmu control register\n"); - return -ENODEV; + goto err0; } + sphy->sysreg = of_iomap(usbphy_sys, 1); + + /* + * Not returning error code here, since this situation is not fatal. + * Few SoCs may not have this switch available + */ + if (sphy->sysreg == NULL) + dev_warn(sphy->dev, "Can't get usb-phy sysreg cfg register\n"); + + of_node_put(usbphy_sys); + return 0; + +err0: + of_node_put(usbphy_sys); + return -ENXIO; } /* @@ -146,17 +290,42 @@ static int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy) */ static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) { - void __iomem *reg; + void __iomem *reg = NULL; u32 reg_val; - u32 en_mask; + u32 en_mask = 0; if (!sphy->pmuregs) { dev_warn(sphy->dev, "Can't set pmu isolation\n"); return; } - reg = sphy->pmuregs + sphy->drv_data->devphy_reg_offset; - en_mask = sphy->drv_data->devphy_en_mask; + switch (sphy->drv_data->cpu_type) { + case TYPE_S3C64XX: + /* + * Do nothing: We will add here once S3C64xx goes for DT support + */ + break; + case TYPE_EXYNOS4210: + /* + * Fall through since exynos4210 and exynos5250 have similar + * register architecture: two separate registers for host and + * device phy control with enable bit at position 0. + */ + case TYPE_EXYNOS5250: + if (sphy->phy_type == USB_PHY_TYPE_DEVICE) { + reg = sphy->pmuregs + + sphy->drv_data->devphy_reg_offset; + en_mask = sphy->drv_data->devphy_en_mask; + } else if (sphy->phy_type == USB_PHY_TYPE_HOST) { + reg = sphy->pmuregs + + sphy->drv_data->hostphy_reg_offset; + en_mask = sphy->drv_data->hostphy_en_mask; + } + break; + default: + dev_err(sphy->dev, "Invalid SoC type\n"); + return; + } reg_val = readl(reg); @@ -169,6 +338,43 @@ static void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) } /* + * Configure the mode of working of usb-phy here: HOST/DEVICE. + */ +static void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy) +{ + u32 reg; + + if (!sphy->sysreg) { + dev_warn(sphy->dev, "Can't configure specified phy mode\n"); + return; + } + + reg = readl(sphy->sysreg); + + if (sphy->phy_type == USB_PHY_TYPE_DEVICE) + reg &= ~EXYNOS_USB20PHY_CFG_HOST_LINK; + else if (sphy->phy_type == USB_PHY_TYPE_HOST) + reg |= EXYNOS_USB20PHY_CFG_HOST_LINK; + + writel(reg, sphy->sysreg); +} + +/* + * PHYs are different for USB Device and USB Host. + * This make sure that correct PHY type is selected before + * any operation on PHY. + */ +static int samsung_usbphy_set_type(struct usb_phy *phy, + enum samsung_usb_phy_type phy_type) +{ + struct samsung_usbphy *sphy = phy_to_sphy(phy); + + sphy->phy_type = phy_type; + + return 0; +} + +/* * Returns reference clock frequency selection value */ static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) @@ -176,34 +382,185 @@ static int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) struct clk *ref_clk; int refclk_freq = 0; - ref_clk = clk_get(sphy->dev, "xusbxti"); + /* + * In exynos5250 USB host and device PHY use + * external crystal clock XXTI + */ + if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) + ref_clk = clk_get(sphy->dev, "ext_xtal"); + else + ref_clk = clk_get(sphy->dev, "xusbxti"); if (IS_ERR(ref_clk)) { dev_err(sphy->dev, "Failed to get reference clock\n"); return PTR_ERR(ref_clk); } - switch (clk_get_rate(ref_clk)) { - case 12 * MHZ: - refclk_freq = PHYCLK_CLKSEL_12M; - break; - case 24 * MHZ: - refclk_freq = PHYCLK_CLKSEL_24M; - break; - case 48 * MHZ: - refclk_freq = PHYCLK_CLKSEL_48M; - break; - default: - if (sphy->drv_data->cpu_type == TYPE_S3C64XX) - refclk_freq = PHYCLK_CLKSEL_48M; - else + if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) { + /* set clock frequency for PLL */ + switch (clk_get_rate(ref_clk)) { + case 9600 * KHZ: + refclk_freq = FSEL_CLKSEL_9600K; + break; + case 10 * MHZ: + refclk_freq = FSEL_CLKSEL_10M; + break; + case 12 * MHZ: + refclk_freq = FSEL_CLKSEL_12M; + break; + case 19200 * KHZ: + refclk_freq = FSEL_CLKSEL_19200K; + break; + case 20 * MHZ: + refclk_freq = FSEL_CLKSEL_20M; + break; + case 50 * MHZ: + refclk_freq = FSEL_CLKSEL_50M; + break; + case 24 * MHZ: + default: + /* default reference clock */ + refclk_freq = FSEL_CLKSEL_24M; + break; + } + } else { + switch (clk_get_rate(ref_clk)) { + case 12 * MHZ: + refclk_freq = PHYCLK_CLKSEL_12M; + break; + case 24 * MHZ: refclk_freq = PHYCLK_CLKSEL_24M; - break; + break; + case 48 * MHZ: + refclk_freq = PHYCLK_CLKSEL_48M; + break; + default: + if (sphy->drv_data->cpu_type == TYPE_S3C64XX) + refclk_freq = PHYCLK_CLKSEL_48M; + else + refclk_freq = PHYCLK_CLKSEL_24M; + break; + } } clk_put(ref_clk); return refclk_freq; } +static bool exynos5_phyhost_is_on(void *regs) +{ + u32 reg; + + reg = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + + return !(reg & HOST_CTRL0_SIDDQ); +} + +static void samsung_exynos5_usbphy_enable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phyclk = sphy->ref_clk_freq; + u32 phyhost; + u32 phyotg; + u32 phyhsic; + u32 ehcictrl; + u32 ohcictrl; + + /* + * phy_usage helps in keeping usage count for phy + * so that the first consumer enabling the phy is also + * the last consumer to disable it. + */ + + atomic_inc(&sphy->phy_usage); + + if (exynos5_phyhost_is_on(regs)) { + dev_info(sphy->dev, "Already power on PHY\n"); + return; + } + + /* Host configuration */ + phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + + /* phy reference clock configuration */ + phyhost &= ~HOST_CTRL0_FSEL_MASK; + phyhost |= HOST_CTRL0_FSEL(phyclk); + + /* host phy reset */ + phyhost &= ~(HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL | + HOST_CTRL0_SIDDQ | + /* Enable normal mode of operation */ + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP); + + /* Link reset */ + phyhost |= (HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST | + /* COMMON Block configuration during suspend */ + HOST_CTRL0_COMMONON_N); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + udelay(10); + phyhost &= ~(HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + + /* OTG configuration */ + phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); + + /* phy reference clock configuration */ + phyotg &= ~OTG_SYS_FSEL_MASK; + phyotg |= OTG_SYS_FSEL(phyclk); + + /* Enable normal mode of operation */ + phyotg &= ~(OTG_SYS_FORCESUSPEND | + OTG_SYS_SIDDQ_UOTG | + OTG_SYS_FORCESLEEP | + OTG_SYS_REFCLKSEL_MASK | + /* COMMON Block configuration during suspend */ + OTG_SYS_COMMON_ON); + + /* OTG phy & link reset */ + phyotg |= (OTG_SYS_PHY0_SWRST | + OTG_SYS_LINKSWRST_UOTG | + OTG_SYS_PHYLINK_SWRESET | + OTG_SYS_OTGDISABLE | + /* Set phy refclk */ + OTG_SYS_REFCLKSEL_CLKCORE); + + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); + udelay(10); + phyotg &= ~(OTG_SYS_PHY0_SWRST | + OTG_SYS_LINKSWRST_UOTG | + OTG_SYS_PHYLINK_SWRESET); + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); + + /* HSIC phy configuration */ + phyhsic = (HSIC_CTRL_REFCLKDIV_12 | + HSIC_CTRL_REFCLKSEL | + HSIC_CTRL_PHYSWRST); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + udelay(10); + phyhsic &= ~HSIC_CTRL_PHYSWRST; + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + + udelay(80); + + /* enable EHCI DMA burst */ + ehcictrl = readl(regs + EXYNOS5_PHY_HOST_EHCICTRL); + ehcictrl |= (HOST_EHCICTRL_ENAINCRXALIGN | + HOST_EHCICTRL_ENAINCR4 | + HOST_EHCICTRL_ENAINCR8 | + HOST_EHCICTRL_ENAINCR16); + writel(ehcictrl, regs + EXYNOS5_PHY_HOST_EHCICTRL); + + /* set ohci_suspend_on_n */ + ohcictrl = readl(regs + EXYNOS5_PHY_HOST_OHCICTRL); + ohcictrl |= HOST_OHCICTRL_SUSPLGCY; + writel(ohcictrl, regs + EXYNOS5_PHY_HOST_OHCICTRL); +} + static void samsung_usbphy_enable(struct samsung_usbphy *sphy) { void __iomem *regs = sphy->regs; @@ -239,6 +596,41 @@ static void samsung_usbphy_enable(struct samsung_usbphy *sphy) writel(rstcon, regs + SAMSUNG_RSTCON); } +static void samsung_exynos5_usbphy_disable(struct samsung_usbphy *sphy) +{ + void __iomem *regs = sphy->regs; + u32 phyhost; + u32 phyotg; + u32 phyhsic; + + if (atomic_dec_return(&sphy->phy_usage) > 0) { + dev_info(sphy->dev, "still being used\n"); + return; + } + + phyhsic = (HSIC_CTRL_REFCLKDIV_12 | + HSIC_CTRL_REFCLKSEL | + HSIC_CTRL_SIDDQ | + HSIC_CTRL_FORCESLEEP | + HSIC_CTRL_FORCESUSPEND); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL1); + writel(phyhsic, regs + EXYNOS5_PHY_HSIC_CTRL2); + + phyhost = readl(regs + EXYNOS5_PHY_HOST_CTRL0); + phyhost |= (HOST_CTRL0_SIDDQ | + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP | + HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL); + writel(phyhost, regs + EXYNOS5_PHY_HOST_CTRL0); + + phyotg = readl(regs + EXYNOS5_PHY_OTG_SYS); + phyotg |= (OTG_SYS_FORCESUSPEND | + OTG_SYS_SIDDQ_UOTG | + OTG_SYS_FORCESLEEP); + writel(phyotg, regs + EXYNOS5_PHY_OTG_SYS); +} + static void samsung_usbphy_disable(struct samsung_usbphy *sphy) { void __iomem *regs = sphy->regs; @@ -266,11 +658,14 @@ static void samsung_usbphy_disable(struct samsung_usbphy *sphy) static int samsung_usbphy_init(struct usb_phy *phy) { struct samsung_usbphy *sphy; + struct usb_bus *host = NULL; unsigned long flags; int ret = 0; sphy = phy_to_sphy(phy); + host = phy->otg->host; + /* Enable the phy clock */ ret = clk_prepare_enable(sphy->clk); if (ret) { @@ -280,19 +675,35 @@ static int samsung_usbphy_init(struct usb_phy *phy) spin_lock_irqsave(&sphy->lock, flags); + if (host) { + /* setting default phy-type for USB 2.0 */ + if (!strstr(dev_name(host->controller), "ehci") || + !strstr(dev_name(host->controller), "ohci")) + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); + } else { + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); + } + /* Disable phy isolation */ if (sphy->plat && sphy->plat->pmu_isolation) sphy->plat->pmu_isolation(false); else samsung_usbphy_set_isolation(sphy, false); + /* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */ + samsung_usbphy_cfg_sel(sphy); + /* Initialize usb phy registers */ - samsung_usbphy_enable(sphy); + if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) + samsung_exynos5_usbphy_enable(sphy); + else + samsung_usbphy_enable(sphy); spin_unlock_irqrestore(&sphy->lock, flags); /* Disable the phy clock */ clk_disable_unprepare(sphy->clk); + return ret; } @@ -302,10 +713,13 @@ static int samsung_usbphy_init(struct usb_phy *phy) static void samsung_usbphy_shutdown(struct usb_phy *phy) { struct samsung_usbphy *sphy; + struct usb_bus *host = NULL; unsigned long flags; sphy = phy_to_sphy(phy); + host = phy->otg->host; + if (clk_prepare_enable(sphy->clk)) { dev_err(sphy->dev, "%s: clk_prepare_enable failed\n", __func__); return; @@ -313,8 +727,20 @@ static void samsung_usbphy_shutdown(struct usb_phy *phy) spin_lock_irqsave(&sphy->lock, flags); + if (host) { + /* setting default phy-type for USB 2.0 */ + if (!strstr(dev_name(host->controller), "ehci") || + !strstr(dev_name(host->controller), "ohci")) + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_HOST); + } else { + samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE); + } + /* De-initialize usb phy registers */ - samsung_usbphy_disable(sphy); + if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) + samsung_exynos5_usbphy_disable(sphy); + else + samsung_usbphy_disable(sphy); /* Enable phy isolation */ if (sphy->plat && sphy->plat->pmu_isolation) @@ -346,7 +772,9 @@ static inline const struct samsung_usbphy_drvdata static int __devinit samsung_usbphy_probe(struct platform_device *pdev) { struct samsung_usbphy *sphy; + struct usb_otg *otg; struct samsung_usbphy_data *pdata = pdev->dev.platform_data; + const struct samsung_usbphy_drvdata *drv_data; struct device *dev = &pdev->dev; struct resource *phy_mem; void __iomem *phy_base; @@ -369,7 +797,17 @@ static int __devinit samsung_usbphy_probe(struct platform_device *pdev) if (!sphy) return -ENOMEM; - clk = devm_clk_get(dev, "otg"); + otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL); + if (!otg) + return -ENOMEM; + + drv_data = samsung_usbphy_get_driver_data(pdev); + + if (drv_data->cpu_type == TYPE_EXYNOS5250) + clk = devm_clk_get(dev, "usbhost"); + else + clk = devm_clk_get(dev, "otg"); + if (IS_ERR(clk)) { dev_err(dev, "Failed to get otg clock\n"); return PTR_ERR(clk); @@ -391,13 +829,17 @@ static int __devinit samsung_usbphy_probe(struct platform_device *pdev) sphy->plat = pdata; sphy->regs = phy_base; sphy->clk = clk; + sphy->drv_data = drv_data; sphy->phy.dev = sphy->dev; sphy->phy.label = "samsung-usbphy"; sphy->phy.init = samsung_usbphy_init; sphy->phy.shutdown = samsung_usbphy_shutdown; - sphy->drv_data = samsung_usbphy_get_driver_data(pdev); sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy); + sphy->phy.otg = otg; + sphy->phy.otg->phy = &sphy->phy; + sphy->phy.otg->set_host = samsung_usbphy_set_host; + spin_lock_init(&sphy->lock); platform_set_drvdata(pdev, sphy); @@ -413,6 +855,8 @@ static int __exit samsung_usbphy_remove(struct platform_device *pdev) if (sphy->pmuregs) iounmap(sphy->pmuregs); + if (sphy->sysreg) + iounmap(sphy->sysreg); return 0; } @@ -425,6 +869,13 @@ static const struct samsung_usbphy_drvdata usbphy_s3c64xx = { static const struct samsung_usbphy_drvdata usbphy_exynos4 = { .cpu_type = TYPE_EXYNOS4210, .devphy_en_mask = EXYNOS_USBPHY_ENABLE, + .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, +}; + +static struct samsung_usbphy_drvdata usbphy_exynos5 = { + .cpu_type = TYPE_EXYNOS5250, + .hostphy_en_mask = EXYNOS_USBPHY_ENABLE, + .hostphy_reg_offset = EXYNOS_USBHOST_PHY_CTRL_OFFSET, }; #ifdef CONFIG_OF @@ -435,6 +886,9 @@ static const struct of_device_id samsung_usbphy_dt_match[] = { }, { .compatible = "samsung,exynos4210-usbphy", .data = &usbphy_exynos4, + }, { + .compatible = "samsung,exynos5250-usbphy", + .data = &usbphy_exynos5 }, {}, }; @@ -448,6 +902,9 @@ static struct platform_device_id samsung_usbphy_driver_ids[] = { }, { .name = "exynos4210-usbphy", .driver_data = (unsigned long)&usbphy_exynos4, + }, { + .name = "exynos5250-usbphy", + .driver_data = (unsigned long)&usbphy_exynos5, }, {}, }; -- cgit v0.10.2 From d233c196ce1a3a33901b75ca818c85825683afba Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Tue, 22 Jan 2013 18:30:42 +0530 Subject: USB: ehci-s5p: Add phy driver support Adding the phy driver to ehci-s5p. Keeping the platform data for continuing the smooth operation for boards which still uses it Signed-off-by: Vivek Gautam Acked-by: Jingoo Han Acked-by: Alan Stern Acked-by: Kukjin Kim Signed-off-by: Felipe Balbi diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 46ca5ef..d603e6e 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,9 @@ struct s5p_ehci_hcd { struct device *dev; struct usb_hcd *hcd; struct clk *clk; + struct usb_phy *phy; + struct usb_otg *otg; + struct s5p_ehci_platdata *pdata; }; static const struct hc_driver s5p_ehci_hc_driver = { @@ -66,6 +70,26 @@ static const struct hc_driver s5p_ehci_hc_driver = { .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; +static void s5p_ehci_phy_enable(struct s5p_ehci_hcd *s5p_ehci) +{ + struct platform_device *pdev = to_platform_device(s5p_ehci->dev); + + if (s5p_ehci->phy) + usb_phy_init(s5p_ehci->phy); + else if (s5p_ehci->pdata->phy_init) + s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST); +} + +static void s5p_ehci_phy_disable(struct s5p_ehci_hcd *s5p_ehci) +{ + struct platform_device *pdev = to_platform_device(s5p_ehci->dev); + + if (s5p_ehci->phy) + usb_phy_shutdown(s5p_ehci->phy); + else if (s5p_ehci->pdata->phy_exit) + s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); +} + static void s5p_setup_vbus_gpio(struct platform_device *pdev) { int err; @@ -88,20 +112,15 @@ static u64 ehci_s5p_dma_mask = DMA_BIT_MASK(32); static int s5p_ehci_probe(struct platform_device *pdev) { - struct s5p_ehci_platdata *pdata; + struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; struct s5p_ehci_hcd *s5p_ehci; struct usb_hcd *hcd; struct ehci_hcd *ehci; struct resource *res; + struct usb_phy *phy; int irq; int err; - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "No platform data defined\n"); - return -EINVAL; - } - /* * Right now device-tree probed devices don't get dma_mask set. * Since shared usb code relies on it, set it here for now. @@ -119,6 +138,20 @@ static int s5p_ehci_probe(struct platform_device *pdev) if (!s5p_ehci) return -ENOMEM; + phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); + if (IS_ERR_OR_NULL(phy)) { + /* Fallback to pdata */ + if (!pdata) { + dev_warn(&pdev->dev, "no platform data or transceiver defined\n"); + return -EPROBE_DEFER; + } else { + s5p_ehci->pdata = pdata; + } + } else { + s5p_ehci->phy = phy; + s5p_ehci->otg = phy->otg; + } + s5p_ehci->dev = &pdev->dev; hcd = usb_create_hcd(&s5p_ehci_hc_driver, &pdev->dev, @@ -164,8 +197,10 @@ static int s5p_ehci_probe(struct platform_device *pdev) goto fail_io; } - if (pdata->phy_init) - pdata->phy_init(pdev, USB_PHY_TYPE_HOST); + if (s5p_ehci->otg) + s5p_ehci->otg->set_host(s5p_ehci->otg, &s5p_ehci->hcd->self); + + s5p_ehci_phy_enable(s5p_ehci); ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; @@ -176,13 +211,15 @@ static int s5p_ehci_probe(struct platform_device *pdev) err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); - goto fail_io; + goto fail_add_hcd; } platform_set_drvdata(pdev, s5p_ehci); return 0; +fail_add_hcd: + s5p_ehci_phy_disable(s5p_ehci); fail_io: clk_disable_unprepare(s5p_ehci->clk); fail_clk: @@ -192,14 +229,15 @@ fail_clk: static int s5p_ehci_remove(struct platform_device *pdev) { - struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev); struct usb_hcd *hcd = s5p_ehci->hcd; usb_remove_hcd(hcd); - if (pdata && pdata->phy_exit) - pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); + if (s5p_ehci->otg) + s5p_ehci->otg->set_host(s5p_ehci->otg, &s5p_ehci->hcd->self); + + s5p_ehci_phy_disable(s5p_ehci); clk_disable_unprepare(s5p_ehci->clk); @@ -223,14 +261,14 @@ static int s5p_ehci_suspend(struct device *dev) struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev); struct usb_hcd *hcd = s5p_ehci->hcd; bool do_wakeup = device_may_wakeup(dev); - struct platform_device *pdev = to_platform_device(dev); - struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; int rc; rc = ehci_suspend(hcd, do_wakeup); - if (pdata && pdata->phy_exit) - pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); + if (s5p_ehci->otg) + s5p_ehci->otg->set_host(s5p_ehci->otg, &s5p_ehci->hcd->self); + + s5p_ehci_phy_disable(s5p_ehci); clk_disable_unprepare(s5p_ehci->clk); @@ -241,13 +279,13 @@ static int s5p_ehci_resume(struct device *dev) { struct s5p_ehci_hcd *s5p_ehci = dev_get_drvdata(dev); struct usb_hcd *hcd = s5p_ehci->hcd; - struct platform_device *pdev = to_platform_device(dev); - struct s5p_ehci_platdata *pdata = pdev->dev.platform_data; clk_prepare_enable(s5p_ehci->clk); - if (pdata && pdata->phy_init) - pdata->phy_init(pdev, USB_PHY_TYPE_HOST); + if (s5p_ehci->otg) + s5p_ehci->otg->set_host(s5p_ehci->otg, &s5p_ehci->hcd->self); + + s5p_ehci_phy_enable(s5p_ehci); /* DMA burst Enable */ writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs)); -- cgit v0.10.2 From ed993bf19b98fdb0d364913174b5001fc3ac199b Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Tue, 22 Jan 2013 18:30:43 +0530 Subject: USB: ohci-exynos: Add phy driver support Adding the phy-driver to ohci-exynos. Keeping the platform data for continuing the smooth operation for boards which still uses it Signed-off-by: Vivek Gautam Acked-by: Jingoo Han Acked-by: Alan Stern Acked-by: Kukjin Kim Signed-off-by: Felipe Balbi diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 804fb62..1b38409 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -22,8 +23,31 @@ struct exynos_ohci_hcd { struct device *dev; struct usb_hcd *hcd; struct clk *clk; + struct usb_phy *phy; + struct usb_otg *otg; + struct exynos4_ohci_platdata *pdata; }; +static void exynos_ohci_phy_enable(struct exynos_ohci_hcd *exynos_ohci) +{ + struct platform_device *pdev = to_platform_device(exynos_ohci->dev); + + if (exynos_ohci->phy) + usb_phy_init(exynos_ohci->phy); + else if (exynos_ohci->pdata->phy_init) + exynos_ohci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST); +} + +static void exynos_ohci_phy_disable(struct exynos_ohci_hcd *exynos_ohci) +{ + struct platform_device *pdev = to_platform_device(exynos_ohci->dev); + + if (exynos_ohci->phy) + usb_phy_shutdown(exynos_ohci->phy); + else if (exynos_ohci->pdata->phy_exit) + exynos_ohci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); +} + static int ohci_exynos_reset(struct usb_hcd *hcd) { return ohci_init(hcd_to_ohci(hcd)); @@ -79,20 +103,15 @@ static u64 ohci_exynos_dma_mask = DMA_BIT_MASK(32); static int exynos_ohci_probe(struct platform_device *pdev) { - struct exynos4_ohci_platdata *pdata; + struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data; struct exynos_ohci_hcd *exynos_ohci; struct usb_hcd *hcd; struct ohci_hcd *ohci; struct resource *res; + struct usb_phy *phy; int irq; int err; - pdata = pdev->dev.platform_data; - if (!pdata) { - dev_err(&pdev->dev, "No platform data defined\n"); - return -EINVAL; - } - /* * Right now device-tree probed devices don't get dma_mask set. * Since shared usb code relies on it, set it here for now. @@ -108,6 +127,20 @@ static int exynos_ohci_probe(struct platform_device *pdev) if (!exynos_ohci) return -ENOMEM; + phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); + if (IS_ERR_OR_NULL(phy)) { + /* Fallback to pdata */ + if (!pdata) { + dev_warn(&pdev->dev, "no platform data or transceiver defined\n"); + return -EPROBE_DEFER; + } else { + exynos_ohci->pdata = pdata; + } + } else { + exynos_ohci->phy = phy; + exynos_ohci->otg = phy->otg; + } + exynos_ohci->dev = &pdev->dev; hcd = usb_create_hcd(&exynos_ohci_hc_driver, &pdev->dev, @@ -153,8 +186,11 @@ static int exynos_ohci_probe(struct platform_device *pdev) goto fail_io; } - if (pdata->phy_init) - pdata->phy_init(pdev, USB_PHY_TYPE_HOST); + if (exynos_ohci->otg) + exynos_ohci->otg->set_host(exynos_ohci->otg, + &exynos_ohci->hcd->self); + + exynos_ohci_phy_enable(exynos_ohci); ohci = hcd_to_ohci(hcd); ohci_hcd_init(ohci); @@ -162,13 +198,15 @@ static int exynos_ohci_probe(struct platform_device *pdev) err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); - goto fail_io; + goto fail_add_hcd; } platform_set_drvdata(pdev, exynos_ohci); return 0; +fail_add_hcd: + exynos_ohci_phy_disable(exynos_ohci); fail_io: clk_disable_unprepare(exynos_ohci->clk); fail_clk: @@ -178,14 +216,16 @@ fail_clk: static int exynos_ohci_remove(struct platform_device *pdev) { - struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data; struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev); struct usb_hcd *hcd = exynos_ohci->hcd; usb_remove_hcd(hcd); - if (pdata && pdata->phy_exit) - pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); + if (exynos_ohci->otg) + exynos_ohci->otg->set_host(exynos_ohci->otg, + &exynos_ohci->hcd->self); + + exynos_ohci_phy_disable(exynos_ohci); clk_disable_unprepare(exynos_ohci->clk); @@ -209,8 +249,6 @@ static int exynos_ohci_suspend(struct device *dev) struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev); struct usb_hcd *hcd = exynos_ohci->hcd; struct ohci_hcd *ohci = hcd_to_ohci(hcd); - struct platform_device *pdev = to_platform_device(dev); - struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data; unsigned long flags; int rc = 0; @@ -229,8 +267,11 @@ static int exynos_ohci_suspend(struct device *dev) clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - if (pdata && pdata->phy_exit) - pdata->phy_exit(pdev, USB_PHY_TYPE_HOST); + if (exynos_ohci->otg) + exynos_ohci->otg->set_host(exynos_ohci->otg, + &exynos_ohci->hcd->self); + + exynos_ohci_phy_disable(exynos_ohci); clk_disable_unprepare(exynos_ohci->clk); @@ -244,13 +285,14 @@ static int exynos_ohci_resume(struct device *dev) { struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev); struct usb_hcd *hcd = exynos_ohci->hcd; - struct platform_device *pdev = to_platform_device(dev); - struct exynos4_ohci_platdata *pdata = pdev->dev.platform_data; clk_prepare_enable(exynos_ohci->clk); - if (pdata && pdata->phy_init) - pdata->phy_init(pdev, USB_PHY_TYPE_HOST); + if (exynos_ohci->otg) + exynos_ohci->otg->set_host(exynos_ohci->otg, + &exynos_ohci->hcd->self); + + exynos_ohci_phy_enable(exynos_ohci); ohci_resume(hcd, false); -- cgit v0.10.2 From 5a1a174c2c7f51b562071e06c9b52c5fd8adf6da Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Mon, 21 Jan 2013 19:30:12 +0530 Subject: usb: phy: samsung: Remove __devinit, __devexit_p and __exit annotations Dropping __devinit, __devexit_p, __exit annotations since they are nop and no longer supported. Signed-off-by: Vivek Gautam Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/samsung-usbphy.c b/drivers/usb/phy/samsung-usbphy.c index 9e9861c..6ea5537 100644 --- a/drivers/usb/phy/samsung-usbphy.c +++ b/drivers/usb/phy/samsung-usbphy.c @@ -769,7 +769,7 @@ static inline const struct samsung_usbphy_drvdata platform_get_device_id(pdev)->driver_data; } -static int __devinit samsung_usbphy_probe(struct platform_device *pdev) +static int samsung_usbphy_probe(struct platform_device *pdev) { struct samsung_usbphy *sphy; struct usb_otg *otg; @@ -847,7 +847,7 @@ static int __devinit samsung_usbphy_probe(struct platform_device *pdev) return usb_add_phy(&sphy->phy, USB_PHY_TYPE_USB2); } -static int __exit samsung_usbphy_remove(struct platform_device *pdev) +static int samsung_usbphy_remove(struct platform_device *pdev) { struct samsung_usbphy *sphy = platform_get_drvdata(pdev); @@ -913,7 +913,7 @@ MODULE_DEVICE_TABLE(platform, samsung_usbphy_driver_ids); static struct platform_driver samsung_usbphy_driver = { .probe = samsung_usbphy_probe, - .remove = __devexit_p(samsung_usbphy_remove), + .remove = samsung_usbphy_remove, .id_table = samsung_usbphy_driver_ids, .driver = { .name = "samsung-usbphy", -- cgit v0.10.2 From b4a83e4df1bc864e89d3bb90e97f9caab656545d Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 08:03:21 +0530 Subject: usb: otg: add an api to bind the usb controller and phy In order to support platforms which has multiple PHY's (of same type) and which has multiple USB controllers, a new design is adopted wherin the binding information (between the PHY and the USB controller) should be passed to the PHY library from platform specific file (board file). So added a new API to pass the binding information. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index a30c041..8e756d9 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -18,6 +18,7 @@ #include static LIST_HEAD(phy_list); +static LIST_HEAD(phy_bind_list); static DEFINE_SPINLOCK(phy_lock); static struct usb_phy *__usb_find_phy(struct list_head *list, @@ -201,6 +202,42 @@ void usb_remove_phy(struct usb_phy *x) } EXPORT_SYMBOL(usb_remove_phy); +/** + * usb_bind_phy - bind the phy and the controller that uses the phy + * @dev_name: the device name of the device that will bind to the phy + * @index: index to specify the port number + * @phy_dev_name: the device name of the phy + * + * Fills the phy_bind structure with the dev_name and phy_dev_name. This will + * be used when the phy driver registers the phy and when the controller + * requests this phy. + * + * To be used by platform specific initialization code. + */ +int __init usb_bind_phy(const char *dev_name, u8 index, + const char *phy_dev_name) +{ + struct usb_phy_bind *phy_bind; + unsigned long flags; + + phy_bind = kzalloc(sizeof(*phy_bind), GFP_KERNEL); + if (!phy_bind) { + pr_err("phy_bind(): No memory for phy_bind"); + return -ENOMEM; + } + + phy_bind->dev_name = dev_name; + phy_bind->phy_dev_name = phy_dev_name; + phy_bind->index = index; + + spin_lock_irqsave(&phy_lock, flags); + list_add_tail(&phy_bind->list, &phy_bind_list); + spin_unlock_irqrestore(&phy_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(usb_bind_phy); + const char *otg_state_string(enum usb_otg_state state) { switch (state) { diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index a29ae1e..a812ed5 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -106,6 +106,21 @@ struct usb_phy { enum usb_device_speed speed); }; +/** + * struct usb_phy_bind - represent the binding for the phy + * @dev_name: the device name of the device that will bind to the phy + * @phy_dev_name: the device name of the phy + * @index: used if a single controller uses multiple phys + * @phy: reference to the phy + * @list: to maintain a linked list of the binding information + */ +struct usb_phy_bind { + const char *dev_name; + const char *phy_dev_name; + u8 index; + struct usb_phy *phy; + struct list_head list; +}; /* for board-specific init logic */ extern int usb_add_phy(struct usb_phy *, enum usb_phy_type type); @@ -151,6 +166,8 @@ extern struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type); extern void usb_put_phy(struct usb_phy *); extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x); +extern int usb_bind_phy(const char *dev_name, u8 index, + const char *phy_dev_name); #else static inline struct usb_phy *usb_get_phy(enum usb_phy_type type) { @@ -171,6 +188,11 @@ static inline void devm_usb_put_phy(struct device *dev, struct usb_phy *x) { } +static inline int usb_bind_phy(const char *dev_name, u8 index, + const char *phy_dev_name) +{ + return -EOPNOTSUPP; +} #endif static inline int -- cgit v0.10.2 From 0fa4fab4ee46470ccd463c83be95434936942e05 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 08:03:22 +0530 Subject: usb: otg: utils: add facilities in phy lib to support multiple PHYs of same type In order to add support for multipe PHY's of the same type, new API's for adding PHY and getting PHY has been added. Now the binding information for the PHY and controller should be done in platform file using usb_bind_phy API. And for getting a PHY, the device pointer of the USB controller and an index should be passed. Based on the binding information that is added in the platform file, usb_get_phy_dev will return the appropriate PHY. Already existing API's to add and get phy by type is not removed. These API's are deprecated and will be removed once all the platforms start to use the new API. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 8e756d9..4bb4333 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -36,6 +36,24 @@ static struct usb_phy *__usb_find_phy(struct list_head *list, return ERR_PTR(-ENODEV); } +static struct usb_phy *__usb_find_phy_dev(struct device *dev, + struct list_head *list, u8 index) +{ + struct usb_phy_bind *phy_bind = NULL; + + list_for_each_entry(phy_bind, list, list) { + if (!(strcmp(phy_bind->dev_name, dev_name(dev))) && + phy_bind->index == index) { + if (phy_bind->phy) + return phy_bind->phy; + else + return ERR_PTR(-EPROBE_DEFER); + } + } + + return ERR_PTR(-ENODEV); +} + static void devm_usb_phy_release(struct device *dev, void *res) { struct usb_phy *phy = *(struct usb_phy **)res; @@ -112,6 +130,69 @@ err0: EXPORT_SYMBOL(usb_get_phy); /** + * usb_get_phy_dev - find the USB PHY + * @dev - device that requests this phy + * @index - the index of the phy + * + * Returns the phy driver, after getting a refcount to it; or + * -ENODEV if there is no such phy. The caller is responsible for + * calling usb_put_phy() to release that count. + * + * For use by USB host and peripheral drivers. + */ +struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index) +{ + struct usb_phy *phy = NULL; + unsigned long flags; + + spin_lock_irqsave(&phy_lock, flags); + + phy = __usb_find_phy_dev(dev, &phy_bind_list, index); + if (IS_ERR(phy)) { + pr_err("unable to find transceiver\n"); + goto err0; + } + + get_device(phy->dev); + +err0: + spin_unlock_irqrestore(&phy_lock, flags); + + return phy; +} +EXPORT_SYMBOL(usb_get_phy_dev); + +/** + * devm_usb_get_phy_dev - find the USB PHY using device ptr and index + * @dev - device that requests this phy + * @index - the index of the phy + * + * Gets the phy using usb_get_phy_dev(), and associates a device with it using + * devres. On driver detach, release function is invoked on the devres data, + * then, devres data is freed. + * + * For use by USB host and peripheral drivers. + */ +struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index) +{ + struct usb_phy **ptr, *phy; + + ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + phy = usb_get_phy_dev(dev, index); + if (!IS_ERR(phy)) { + *ptr = phy; + devres_add(dev, ptr); + } else + devres_free(ptr); + + return phy; +} +EXPORT_SYMBOL(devm_usb_get_phy_dev); + +/** * devm_usb_put_phy - release the USB PHY * @dev - device that wants to release this phy * @phy - the phy returned by devm_usb_get_phy() @@ -186,6 +267,36 @@ out: EXPORT_SYMBOL(usb_add_phy); /** + * usb_add_phy_dev - declare the USB PHY + * @x: the USB phy to be used; or NULL + * + * This call is exclusively for use by phy drivers, which + * coordinate the activities of drivers for host and peripheral + * controllers, and in some cases for VBUS current regulation. + */ +int usb_add_phy_dev(struct usb_phy *x) +{ + struct usb_phy_bind *phy_bind; + unsigned long flags; + + if (!x->dev) { + dev_err(x->dev, "no device provided for PHY\n"); + return -EINVAL; + } + + spin_lock_irqsave(&phy_lock, flags); + list_for_each_entry(phy_bind, &phy_bind_list, list) + if (!(strcmp(phy_bind->phy_dev_name, dev_name(x->dev)))) + phy_bind->phy = x; + + list_add_tail(&x->head, &phy_list); + + spin_unlock_irqrestore(&phy_lock, flags); + return 0; +} +EXPORT_SYMBOL(usb_add_phy_dev); + +/** * usb_remove_phy - remove the OTG PHY * @x: the USB OTG PHY to be removed; * @@ -194,10 +305,15 @@ EXPORT_SYMBOL(usb_add_phy); void usb_remove_phy(struct usb_phy *x) { unsigned long flags; + struct usb_phy_bind *phy_bind; spin_lock_irqsave(&phy_lock, flags); - if (x) + if (x) { + list_for_each_entry(phy_bind, &phy_bind_list, list) + if (phy_bind->phy == x) + phy_bind->phy = NULL; list_del(&x->head); + } spin_unlock_irqrestore(&phy_lock, flags); } EXPORT_SYMBOL(usb_remove_phy); diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index a812ed5..359db7d 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -124,6 +124,7 @@ struct usb_phy_bind { /* for board-specific init logic */ extern int usb_add_phy(struct usb_phy *, enum usb_phy_type type); +extern int usb_add_phy_dev(struct usb_phy *); extern void usb_remove_phy(struct usb_phy *); /* helpers for direct access thru low-level io interface */ @@ -164,6 +165,8 @@ usb_phy_shutdown(struct usb_phy *x) extern struct usb_phy *usb_get_phy(enum usb_phy_type type); extern struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type); +extern struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index); +extern struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index); extern void usb_put_phy(struct usb_phy *); extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x); extern int usb_bind_phy(const char *dev_name, u8 index, @@ -180,6 +183,16 @@ static inline struct usb_phy *devm_usb_get_phy(struct device *dev, return NULL; } +static inline struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index) +{ + return NULL; +} + +static inline struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index) +{ + return NULL; +} + static inline void usb_put_phy(struct usb_phy *x) { } -- cgit v0.10.2 From 5d3c28b5a42df5ceaa854901ba2cccb76883c77e Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 08:03:25 +0530 Subject: usb: otg: add device tree support to otg library Added an API devm_usb_get_phy_by_phandle(), to get usb phy by passing a device node phandle value. This function will return a pointer to the phy on success, -EPROBE_DEFER if there is a device_node for the phandle, but the phy has not been added, or a ERR_PTR() otherwise. Cc: Marc Kleine-Budde Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 4bb4333..e181439 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -13,7 +13,9 @@ #include #include #include +#include #include +#include #include @@ -54,6 +56,20 @@ static struct usb_phy *__usb_find_phy_dev(struct device *dev, return ERR_PTR(-ENODEV); } +static struct usb_phy *__of_usb_find_phy(struct device_node *node) +{ + struct usb_phy *phy; + + list_for_each_entry(phy, &phy_list, head) { + if (node != phy->dev->of_node) + continue; + + return phy; + } + + return ERR_PTR(-ENODEV); +} + static void devm_usb_phy_release(struct device *dev, void *res) { struct usb_phy *phy = *(struct usb_phy **)res; @@ -129,6 +145,70 @@ err0: } EXPORT_SYMBOL(usb_get_phy); + /** + * devm_usb_get_phy_by_phandle - find the USB PHY by phandle + * @dev - device that requests this phy + * @phandle - name of the property holding the phy phandle value + * @index - the index of the phy + * + * Returns the phy driver associated with the given phandle value, + * after getting a refcount to it, -ENODEV if there is no such phy or + * -EPROBE_DEFER if there is a phandle to the phy, but the device is + * not yet loaded. While at that, it also associates the device with + * the phy using devres. On driver detach, release function is invoked + * on the devres data, then, devres data is freed. + * + * For use by USB host and peripheral drivers. + */ +struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, + const char *phandle, u8 index) +{ + struct usb_phy *phy = ERR_PTR(-ENOMEM), **ptr; + unsigned long flags; + struct device_node *node; + + if (!dev->of_node) { + dev_dbg(dev, "device does not have a device node entry\n"); + return ERR_PTR(-EINVAL); + } + + node = of_parse_phandle(dev->of_node, phandle, index); + if (!node) { + dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, + dev->of_node->full_name); + return ERR_PTR(-ENODEV); + } + + ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) { + dev_dbg(dev, "failed to allocate memory for devres\n"); + goto err0; + } + + spin_lock_irqsave(&phy_lock, flags); + + phy = __of_usb_find_phy(node); + if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) { + phy = ERR_PTR(-EPROBE_DEFER); + devres_free(ptr); + goto err1; + } + + *ptr = phy; + devres_add(dev, ptr); + + get_device(phy->dev); + +err1: + spin_unlock_irqrestore(&phy_lock, flags); + +err0: + of_node_put(node); + + return phy; +} +EXPORT_SYMBOL(devm_usb_get_phy_by_phandle); + /** * usb_get_phy_dev - find the USB PHY * @dev - device that requests this phy diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index 359db7d..15847cb 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -167,6 +167,8 @@ extern struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type); extern struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index); extern struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index); +extern struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, + const char *phandle, u8 index); extern void usb_put_phy(struct usb_phy *); extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x); extern int usb_bind_phy(const char *dev_name, u8 index, @@ -193,6 +195,12 @@ static inline struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index) return NULL; } +static inline struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, + const char *phandle, u8 index) +{ + return NULL; +} + static inline void usb_put_phy(struct usb_phy *x) { } -- cgit v0.10.2 From 01658f0f8d1322dbf94f289aa610731d539bf888 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 15:53:57 +0530 Subject: usb: phy: add a new driver for usb part of control module Added a new driver for the usb part of control module. This has an API to power on the USB2 phy and an API to write to the mailbox depending on whether MUSB has to act in host mode or in device mode. Writing to control module registers for doing the above task which was previously done in omap glue and in omap-usb2 phy will be removed. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt index 29a043e..3d78cc2 100644 --- a/Documentation/devicetree/bindings/usb/omap-usb.txt +++ b/Documentation/devicetree/bindings/usb/omap-usb.txt @@ -1,4 +1,4 @@ -OMAP GLUE +OMAP GLUE AND OTHER OMAP SPECIFIC COMPONENTS OMAP MUSB GLUE - compatible : Should be "ti,omap4-musb" or "ti,omap3-musb" @@ -16,6 +16,10 @@ OMAP MUSB GLUE - power : Should be "50". This signifies the controller can supply upto 100mA when operating in host mode. +Optional properties: + - ctrl-module : phandle of the control module this glue uses to write to + mailbox + SOC specific device node entry usb_otg_hs: usb_otg_hs@4a0ab000 { compatible = "ti,omap4-musb"; @@ -23,6 +27,7 @@ usb_otg_hs: usb_otg_hs@4a0ab000 { multipoint = <1>; num_eps = <16>; ram_bits = <12>; + ctrl-module = <&omap_control_usb>; }; Board specific device node entry @@ -31,3 +36,26 @@ Board specific device node entry mode = <3>; power = <50>; }; + +OMAP CONTROL USB + +Required properties: + - compatible: Should be "ti,omap-control-usb" + - reg : Address and length of the register set for the device. It contains + the address of "control_dev_conf" and "otghs_control" or "phy_power_usb" + depending upon omap4 or omap5. + - reg-names: The names of the register addresses corresponding to the registers + filled in "reg". + - ti,type: This is used to differentiate whether the control module has + usb mailbox or usb3 phy power. omap4 has usb mailbox in control module to + notify events to the musb core and omap5 has usb3 phy power register to + power on usb3 phy. Should be "1" if it has mailbox and "2" if it has usb3 + phy power. + +omap_control_usb: omap-control-usb@4a002300 { + compatible = "ti,omap-control-usb"; + reg = <0x4a002300 0x4>, + <0x4a00233c 0x4>; + reg-names = "control_dev_conf", "otghs_control"; + ti,type = <1>; +}; diff --git a/Documentation/devicetree/bindings/usb/usb-phy.txt b/Documentation/devicetree/bindings/usb/usb-phy.txt index 80d4148..4234105 100644 --- a/Documentation/devicetree/bindings/usb/usb-phy.txt +++ b/Documentation/devicetree/bindings/usb/usb-phy.txt @@ -8,10 +8,15 @@ Required properties: add the address of control module dev conf register until a driver for control module is added +Optional properties: + - ctrl-module : phandle of the control module used by PHY driver to power on + the PHY. + This is usually a subnode of ocp2scp to which it is connected. usb2phy@4a0ad080 { compatible = "ti,omap-usb2"; reg = <0x4a0ad080 0x58>, <0x4a002300 0x4>; + ctrl-module = <&omap_control_usb>; }; diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index fae4d08..053696a 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -14,6 +14,16 @@ config OMAP_USB2 The USB OTG controller communicates with the comparator using this driver. +config OMAP_CONTROL_USB + tristate "OMAP CONTROL USB Driver" + depends on ARCH_OMAP2PLUS + help + Enable this to add support for the USB part present in the control + module. This driver has API to power on the USB2 PHY and to write to + the mailbox. The mailbox is present only in omap4 and the register to + power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an + additional register to power on USB3 PHY. + config USB_ISP1301 tristate "NXP ISP1301 USB transceiver support" depends on USB || USB_GADGET diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index ec304f6..ee97767 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -5,6 +5,7 @@ ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG obj-$(CONFIG_OMAP_USB2) += omap-usb2.o +obj-$(CONFIG_OMAP_CONTROL_USB) += omap-control-usb.o obj-$(CONFIG_USB_ISP1301) += isp1301.o obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o obj-$(CONFIG_USB_EHCI_TEGRA) += tegra_usb_phy.o diff --git a/drivers/usb/phy/omap-control-usb.c b/drivers/usb/phy/omap-control-usb.c new file mode 100644 index 0000000..5323b71 --- /dev/null +++ b/drivers/usb/phy/omap-control-usb.c @@ -0,0 +1,295 @@ +/* + * omap-control-usb.c - The USB part of control module. + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Author: Kishon Vijay Abraham I + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct omap_control_usb *control_usb; + +/** + * omap_get_control_dev - returns the device pointer for this control device + * + * This API should be called to get the device pointer for this control + * module device. This device pointer should be used for called other + * exported API's in this driver. + * + * To be used by PHY driver and glue driver. + */ +struct device *omap_get_control_dev(void) +{ + if (!control_usb) + return ERR_PTR(-ENODEV); + + return control_usb->dev; +} +EXPORT_SYMBOL_GPL(omap_get_control_dev); + +/** + * omap_control_usb3_phy_power - power on/off the serializer using control + * module + * @dev: the control module device + * @on: 0 to off and 1 to on based on powering on or off the PHY + * + * usb3 PHY driver should call this API to power on or off the PHY. + */ +void omap_control_usb3_phy_power(struct device *dev, bool on) +{ + u32 val; + unsigned long rate; + struct omap_control_usb *control_usb = dev_get_drvdata(dev); + + if (control_usb->type != OMAP_CTRL_DEV_TYPE2) + return; + + rate = clk_get_rate(control_usb->sys_clk); + rate = rate/1000000; + + val = readl(control_usb->phy_power); + + if (on) { + val &= ~(OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK | + OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK); + val |= OMAP_CTRL_USB3_PHY_TX_RX_POWERON << + OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT; + val |= rate << OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT; + } else { + val &= ~OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK; + val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF << + OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT; + } + + writel(val, control_usb->phy_power); +} +EXPORT_SYMBOL_GPL(omap_control_usb3_phy_power); + +/** + * omap_control_usb_phy_power - power on/off the phy using control module reg + * @dev: the control module device + * @on: 0 or 1, based on powering on or off the PHY + */ +void omap_control_usb_phy_power(struct device *dev, int on) +{ + u32 val; + struct omap_control_usb *control_usb = dev_get_drvdata(dev); + + val = readl(control_usb->dev_conf); + + if (on) + val &= ~OMAP_CTRL_DEV_PHY_PD; + else + val |= OMAP_CTRL_DEV_PHY_PD; + + writel(val, control_usb->dev_conf); +} +EXPORT_SYMBOL_GPL(omap_control_usb_phy_power); + +/** + * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded + * @ctrl_usb: struct omap_control_usb * + * + * Writes to the mailbox register to notify the usb core that a usb + * device has been connected. + */ +static void omap_control_usb_host_mode(struct omap_control_usb *ctrl_usb) +{ + u32 val; + + val = readl(ctrl_usb->otghs_control); + val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND); + val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID; + writel(val, ctrl_usb->otghs_control); +} + +/** + * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high + * impedance + * @ctrl_usb: struct omap_control_usb * + * + * Writes to the mailbox register to notify the usb core that it has been + * connected to a usb host. + */ +static void omap_control_usb_device_mode(struct omap_control_usb *ctrl_usb) +{ + u32 val; + + val = readl(ctrl_usb->otghs_control); + val &= ~OMAP_CTRL_DEV_SESSEND; + val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID | + OMAP_CTRL_DEV_VBUSVALID; + writel(val, ctrl_usb->otghs_control); +} + +/** + * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high + * impedance + * @ctrl_usb: struct omap_control_usb * + * + * Writes to the mailbox register to notify the usb core it's now in + * disconnected state. + */ +static void omap_control_usb_set_sessionend(struct omap_control_usb *ctrl_usb) +{ + u32 val; + + val = readl(ctrl_usb->otghs_control); + val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID); + val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND; + writel(val, ctrl_usb->otghs_control); +} + +/** + * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode + * or device mode or to denote disconnected state + * @dev: the control module device + * @mode: The mode to which usb should be configured + * + * This is an API to write to the mailbox register to notify the usb core that + * a usb device has been connected. + */ +void omap_control_usb_set_mode(struct device *dev, + enum omap_control_usb_mode mode) +{ + struct omap_control_usb *ctrl_usb; + + if (IS_ERR(dev) || control_usb->type != OMAP_CTRL_DEV_TYPE1) + return; + + ctrl_usb = dev_get_drvdata(dev); + + switch (mode) { + case USB_MODE_HOST: + omap_control_usb_host_mode(ctrl_usb); + break; + case USB_MODE_DEVICE: + omap_control_usb_device_mode(ctrl_usb); + break; + case USB_MODE_DISCONNECT: + omap_control_usb_set_sessionend(ctrl_usb); + break; + default: + dev_vdbg(dev, "invalid omap control usb mode\n"); + } +} +EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); + +static int omap_control_usb_probe(struct platform_device *pdev) +{ + struct resource *res; + struct device_node *np = pdev->dev.of_node; + struct omap_control_usb_platform_data *pdata = pdev->dev.platform_data; + + control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb), + GFP_KERNEL); + if (!control_usb) { + dev_err(&pdev->dev, "unable to alloc memory for control usb\n"); + return -ENOMEM; + } + + if (np) { + of_property_read_u32(np, "ti,type", &control_usb->type); + } else if (pdata) { + control_usb->type = pdata->type; + } else { + dev_err(&pdev->dev, "no pdata present\n"); + return -EINVAL; + } + + control_usb->dev = &pdev->dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "control_dev_conf"); + control_usb->dev_conf = devm_request_and_ioremap(&pdev->dev, res); + if (!control_usb->dev_conf) { + dev_err(&pdev->dev, "Failed to obtain io memory\n"); + return -EADDRNOTAVAIL; + } + + if (control_usb->type == OMAP_CTRL_DEV_TYPE1) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "otghs_control"); + control_usb->otghs_control = devm_request_and_ioremap( + &pdev->dev, res); + if (!control_usb->otghs_control) { + dev_err(&pdev->dev, "Failed to obtain io memory\n"); + return -EADDRNOTAVAIL; + } + } + + if (control_usb->type == OMAP_CTRL_DEV_TYPE2) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "phy_power_usb"); + control_usb->phy_power = devm_request_and_ioremap( + &pdev->dev, res); + if (!control_usb->phy_power) { + dev_dbg(&pdev->dev, "Failed to obtain io memory\n"); + return -EADDRNOTAVAIL; + } + + control_usb->sys_clk = devm_clk_get(control_usb->dev, + "sys_clkin"); + if (IS_ERR(control_usb->sys_clk)) { + pr_err("%s: unable to get sys_clkin\n", __func__); + return -EINVAL; + } + } + + + dev_set_drvdata(control_usb->dev, control_usb); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id omap_control_usb_id_table[] = { + { .compatible = "ti,omap-control-usb" }, + {} +}; +MODULE_DEVICE_TABLE(of, omap_control_usb_id_table); +#endif + +static struct platform_driver omap_control_usb_driver = { + .probe = omap_control_usb_probe, + .driver = { + .name = "omap-control-usb", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(omap_control_usb_id_table), + }, +}; + +static int __init omap_control_usb_init(void) +{ + return platform_driver_register(&omap_control_usb_driver); +} +subsys_initcall(omap_control_usb_init); + +static void __exit omap_control_usb_exit(void) +{ + platform_driver_unregister(&omap_control_usb_driver); +} +module_exit(omap_control_usb_exit); + +MODULE_ALIAS("platform: omap_control_usb"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("OMAP Control Module USB Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/usb/omap_control_usb.h b/include/linux/usb/omap_control_usb.h new file mode 100644 index 0000000..f306db7 --- /dev/null +++ b/include/linux/usb/omap_control_usb.h @@ -0,0 +1,92 @@ +/* + * omap_control_usb.h - Header file for the USB part of control module. + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Author: Kishon Vijay Abraham I + * + * 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. + * + */ + +#ifndef __OMAP_CONTROL_USB_H__ +#define __OMAP_CONTROL_USB_H__ + +struct omap_control_usb { + struct device *dev; + + u32 __iomem *dev_conf; + u32 __iomem *otghs_control; + u32 __iomem *phy_power; + + struct clk *sys_clk; + + u32 type; +}; + +struct omap_control_usb_platform_data { + u8 type; +}; + +enum omap_control_usb_mode { + USB_MODE_UNDEFINED = 0, + USB_MODE_HOST, + USB_MODE_DEVICE, + USB_MODE_DISCONNECT, +}; + +/* To differentiate ctrl module IP having either mailbox or USB3 PHY power */ +#define OMAP_CTRL_DEV_TYPE1 0x1 +#define OMAP_CTRL_DEV_TYPE2 0x2 + +#define OMAP_CTRL_DEV_PHY_PD BIT(0) + +#define OMAP_CTRL_DEV_AVALID BIT(0) +#define OMAP_CTRL_DEV_BVALID BIT(1) +#define OMAP_CTRL_DEV_VBUSVALID BIT(2) +#define OMAP_CTRL_DEV_SESSEND BIT(3) +#define OMAP_CTRL_DEV_IDDIG BIT(4) + +#define OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK 0x003FC000 +#define OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT 0xE + +#define OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK 0xFFC00000 +#define OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT 0x16 + +#define OMAP_CTRL_USB3_PHY_TX_RX_POWERON 0x3 +#define OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF 0x0 + +#if IS_ENABLED(CONFIG_OMAP_CONTROL_USB) +extern struct device *omap_get_control_dev(void); +extern void omap_control_usb_phy_power(struct device *dev, int on); +extern void omap_control_usb3_phy_power(struct device *dev, bool on); +extern void omap_control_usb_set_mode(struct device *dev, + enum omap_control_usb_mode mode); +#else +static inline struct device *omap_get_control_dev() +{ + return ERR_PTR(-ENODEV); +} + +static inline void omap_control_usb_phy_power(struct device *dev, int on) +{ +} + +static inline void omap_control_usb3_phy_power(struct device *dev, int on) +{ +} + +static inline void omap_control_usb_set_mode(struct device *dev, + enum omap_control_usb_mode mode) +{ +} +#endif + +#endif /* __OMAP_CONTROL_USB_H__ */ -- cgit v0.10.2 From ca784be36cc725bca9b526eba342de7550329731 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 15:54:00 +0530 Subject: usb: start using the control module driver Start using the control module driver for powering on the PHY and for writing to the mailbox instead of writing to the control module registers on their own. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt index 3d78cc2..1ef0ce7 100644 --- a/Documentation/devicetree/bindings/usb/omap-usb.txt +++ b/Documentation/devicetree/bindings/usb/omap-usb.txt @@ -3,6 +3,9 @@ OMAP GLUE AND OTHER OMAP SPECIFIC COMPONENTS OMAP MUSB GLUE - compatible : Should be "ti,omap4-musb" or "ti,omap3-musb" - ti,hwmods : must be "usb_otg_hs" + - ti,has-mailbox : to specify that omap uses an external mailbox + (in control module) to communicate with the musb core during device connect + and disconnect. - multipoint : Should be "1" indicating the musb controller supports multipoint. This is a MUSB configuration-specific setting. - num_eps : Specifies the number of endpoints. This is also a @@ -24,6 +27,7 @@ SOC specific device node entry usb_otg_hs: usb_otg_hs@4a0ab000 { compatible = "ti,omap4-musb"; ti,hwmods = "usb_otg_hs"; + ti,has-mailbox; multipoint = <1>; num_eps = <16>; ram_bits = <12>; diff --git a/Documentation/devicetree/bindings/usb/usb-phy.txt b/Documentation/devicetree/bindings/usb/usb-phy.txt index 4234105..b4b86bb 100644 --- a/Documentation/devicetree/bindings/usb/usb-phy.txt +++ b/Documentation/devicetree/bindings/usb/usb-phy.txt @@ -4,9 +4,7 @@ OMAP USB2 PHY Required properties: - compatible: Should be "ti,omap-usb2" - - reg : Address and length of the register set for the device. Also -add the address of control module dev conf register until a driver for -control module is added + - reg : Address and length of the register set for the device. Optional properties: - ctrl-module : phandle of the control module used by PHY driver to power on @@ -16,7 +14,6 @@ This is usually a subnode of ocp2scp to which it is connected. usb2phy@4a0ad080 { compatible = "ti,omap-usb2"; - reg = <0x4a0ad080 0x58>, - <0x4a002300 0x4>; + reg = <0x4a0ad080 0x58>; ctrl-module = <&omap_control_usb>; }; diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 23a0b7f..de6e5ce 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -11,6 +11,7 @@ config USB_MUSB_HDRC select NOP_USB_XCEIV if (SOC_TI81XX || SOC_AM33XX) select TWL4030_USB if MACH_OMAP_3430SDP select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA + select OMAP_CONTROL_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA select USB_OTG_UTILS help Say Y here if your system has a dual role high speed USB diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index da00af4..779862d 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "musb_core.h" #include "omap2430.h" @@ -46,7 +47,7 @@ struct omap2430_glue { struct platform_device *musb; enum omap_musb_vbus_id_status status; struct work_struct omap_musb_mailbox_work; - u32 __iomem *control_otghs; + struct device *control_otghs; }; #define glue_to_musb(g) platform_get_drvdata(g->musb) @@ -54,26 +55,6 @@ struct omap2430_glue *_glue; static struct timer_list musb_idle_timer; -/** - * omap4_usb_phy_mailbox - write to usb otg mailbox - * @glue: struct omap2430_glue * - * @val: the value to be written to the mailbox - * - * On detection of a device (ID pin is grounded), this API should be called - * to set AVALID, VBUSVALID and ID pin is grounded. - * - * When OMAP is connected to a host (OMAP in device mode), this API - * is called to set AVALID, VBUSVALID and ID pin in high impedance. - * - * XXX: This function will be removed once we have a seperate driver for - * control module - */ -static void omap4_usb_phy_mailbox(struct omap2430_glue *glue, u32 val) -{ - if (glue->control_otghs) - writel(val, glue->control_otghs); -} - static void musb_do_idle(unsigned long _musb) { struct musb *musb = (void *)_musb; @@ -269,7 +250,6 @@ EXPORT_SYMBOL_GPL(omap_musb_mailbox); static void omap_musb_set_mailbox(struct omap2430_glue *glue) { - u32 val; struct musb *musb = glue_to_musb(glue); struct device *dev = musb->controller; struct musb_hdrc_platform_data *pdata = dev->platform_data; @@ -285,8 +265,8 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) musb->xceiv->last_event = USB_EVENT_ID; if (musb->gadget_driver) { pm_runtime_get_sync(dev); - val = AVALID | VBUSVALID; - omap4_usb_phy_mailbox(glue, val); + omap_control_usb_set_mode(glue->control_otghs, + USB_MODE_HOST); omap2430_musb_set_vbus(musb, 1); } break; @@ -299,8 +279,7 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) musb->xceiv->last_event = USB_EVENT_VBUS; if (musb->gadget_driver) pm_runtime_get_sync(dev); - val = IDDIG | AVALID | VBUSVALID; - omap4_usb_phy_mailbox(glue, val); + omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DEVICE); break; case OMAP_MUSB_ID_FLOAT: @@ -317,8 +296,8 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) if (musb->xceiv->otg->set_vbus) otg_set_vbus(musb->xceiv->otg, 0); } - val = SESSEND | IDDIG; - omap4_usb_phy_mailbox(glue, val); + omap_control_usb_set_mode(glue->control_otghs, + USB_MODE_DISCONNECT); break; default: dev_dbg(dev, "ID float\n"); @@ -415,7 +394,6 @@ err1: static void omap2430_musb_enable(struct musb *musb) { u8 devctl; - u32 val; unsigned long timeout = jiffies + msecs_to_jiffies(1000); struct device *dev = musb->controller; struct omap2430_glue *glue = dev_get_drvdata(dev->parent); @@ -425,8 +403,7 @@ static void omap2430_musb_enable(struct musb *musb) switch (glue->status) { case OMAP_MUSB_ID_GROUND: - val = AVALID | VBUSVALID; - omap4_usb_phy_mailbox(glue, val); + omap_control_usb_set_mode(glue->control_otghs, USB_MODE_HOST); if (data->interface_type != MUSB_INTERFACE_UTMI) break; devctl = musb_readb(musb->mregs, MUSB_DEVCTL); @@ -445,8 +422,7 @@ static void omap2430_musb_enable(struct musb *musb) break; case OMAP_MUSB_VBUS_VALID: - val = IDDIG | AVALID | VBUSVALID; - omap4_usb_phy_mailbox(glue, val); + omap_control_usb_set_mode(glue->control_otghs, USB_MODE_DEVICE); break; default: @@ -456,14 +432,12 @@ static void omap2430_musb_enable(struct musb *musb) static void omap2430_musb_disable(struct musb *musb) { - u32 val; struct device *dev = musb->controller; struct omap2430_glue *glue = dev_get_drvdata(dev->parent); - if (glue->status != OMAP_MUSB_UNKNOWN) { - val = SESSEND | IDDIG; - omap4_usb_phy_mailbox(glue, val); - } + if (glue->status != OMAP_MUSB_UNKNOWN) + omap_control_usb_set_mode(glue->control_otghs, + USB_MODE_DISCONNECT); } static int omap2430_musb_exit(struct musb *musb) @@ -498,7 +472,6 @@ static int omap2430_probe(struct platform_device *pdev) struct omap2430_glue *glue; struct device_node *np = pdev->dev.of_node; struct musb_hdrc_config *config; - struct resource *res; int ret = -ENOMEM; glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); @@ -521,12 +494,6 @@ static int omap2430_probe(struct platform_device *pdev) glue->musb = musb; glue->status = OMAP_MUSB_UNKNOWN; - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - - glue->control_otghs = devm_request_and_ioremap(&pdev->dev, res); - if (glue->control_otghs == NULL) - dev_dbg(&pdev->dev, "Failed to obtain control memory\n"); - if (np) { pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { @@ -558,11 +525,22 @@ static int omap2430_probe(struct platform_device *pdev) of_property_read_u32(np, "ram_bits", (u32 *)&config->ram_bits); of_property_read_u32(np, "power", (u32 *)&pdata->power); config->multipoint = of_property_read_bool(np, "multipoint"); + pdata->has_mailbox = of_property_read_bool(np, + "ti,has-mailbox"); pdata->board_data = data; pdata->config = config; } + if (pdata->has_mailbox) { + glue->control_otghs = omap_get_control_dev(); + if (IS_ERR(glue->control_otghs)) { + dev_vdbg(&pdev->dev, "Failed to get control device\n"); + return -ENODEV; + } + } else { + glue->control_otghs = ERR_PTR(-ENODEV); + } pdata->platform_ops = &omap2430_ops; platform_set_drvdata(pdev, glue); diff --git a/drivers/usb/musb/omap2430.h b/drivers/usb/musb/omap2430.h index 8ef6566..1b5e83a 100644 --- a/drivers/usb/musb/omap2430.h +++ b/drivers/usb/musb/omap2430.h @@ -49,13 +49,4 @@ #define OTG_FORCESTDBY 0x414 # define ENABLEFORCE (1 << 0) -/* - * Control Module bit definitions - * XXX: Will be removed once we have a driver for control module. - */ -#define AVALID BIT(0) -#define BVALID BIT(1) -#define VBUSVALID BIT(2) -#define SESSEND BIT(3) -#define IDDIG BIT(4) #endif /* __MUSB_OMAP243X_H__ */ diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 053696a..f989511 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -8,6 +8,7 @@ config OMAP_USB2 tristate "OMAP USB2 PHY Driver" depends on ARCH_OMAP2PLUS select USB_OTG_UTILS + select OMAP_CONTROL_USB help Enable this to support the transceiver that is part of SOC. This driver takes care of all the PHY functionality apart from comparator. diff --git a/drivers/usb/phy/omap-usb2.c b/drivers/usb/phy/omap-usb2.c index 26ae8f4..c2b4c8e 100644 --- a/drivers/usb/phy/omap-usb2.c +++ b/drivers/usb/phy/omap-usb2.c @@ -27,6 +27,7 @@ #include #include #include +#include /** * omap_usb2_set_comparator - links the comparator present in the sytem with @@ -52,29 +53,6 @@ int omap_usb2_set_comparator(struct phy_companion *comparator) } EXPORT_SYMBOL_GPL(omap_usb2_set_comparator); -/** - * omap_usb_phy_power - power on/off the phy using control module reg - * @phy: struct omap_usb * - * @on: 0 or 1, based on powering on or off the PHY - * - * XXX: Remove this function once control module driver gets merged - */ -static void omap_usb_phy_power(struct omap_usb *phy, int on) -{ - u32 val; - - if (on) { - val = readl(phy->control_dev); - if (val & PHY_PD) { - writel(~PHY_PD, phy->control_dev); - /* XXX: add proper documentation for this delay */ - mdelay(200); - } - } else { - writel(PHY_PD, phy->control_dev); - } -} - static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled) { struct omap_usb *phy = phy_to_omapusb(otg->phy); @@ -124,7 +102,7 @@ static int omap_usb2_suspend(struct usb_phy *x, int suspend) struct omap_usb *phy = phy_to_omapusb(x); if (suspend && !phy->is_suspended) { - omap_usb_phy_power(phy, 0); + omap_control_usb_phy_power(phy->control_dev, 0); pm_runtime_put_sync(phy->dev); phy->is_suspended = 1; } else if (!suspend && phy->is_suspended) { @@ -134,7 +112,7 @@ static int omap_usb2_suspend(struct usb_phy *x, int suspend) ret); return ret; } - omap_usb_phy_power(phy, 1); + omap_control_usb_phy_power(phy->control_dev, 1); phy->is_suspended = 0; } @@ -145,7 +123,6 @@ static int omap_usb2_probe(struct platform_device *pdev) { struct omap_usb *phy; struct usb_otg *otg; - struct resource *res; phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); if (!phy) { @@ -166,16 +143,14 @@ static int omap_usb2_probe(struct platform_device *pdev) phy->phy.set_suspend = omap_usb2_suspend; phy->phy.otg = otg; - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - - phy->control_dev = devm_request_and_ioremap(&pdev->dev, res); - if (phy->control_dev == NULL) { - dev_err(&pdev->dev, "Failed to obtain io memory\n"); - return -ENXIO; + phy->control_dev = omap_get_control_dev(); + if (IS_ERR(phy->control_dev)) { + dev_dbg(&pdev->dev, "Failed to get control device\n"); + return -ENODEV; } phy->is_suspended = 1; - omap_usb_phy_power(phy, 0); + omap_control_usb_phy_power(phy->control_dev, 0); otg->set_host = omap_usb_set_host; otg->set_peripheral = omap_usb_set_peripheral; diff --git a/include/linux/usb/omap_usb.h b/include/linux/usb/omap_usb.h index 0ea17f8..3db9b53 100644 --- a/include/linux/usb/omap_usb.h +++ b/include/linux/usb/omap_usb.h @@ -25,13 +25,11 @@ struct omap_usb { struct usb_phy phy; struct phy_companion *comparator; struct device *dev; - u32 __iomem *control_dev; + struct device *control_dev; struct clk *wkupclk; u8 is_suspended:1; }; -#define PHY_PD 0x1 - #define phy_to_omapusb(x) container_of((x), struct omap_usb, phy) #if defined(CONFIG_OMAP_USB2) || defined(CONFIG_OMAP_USB2_MODULE) -- cgit v0.10.2 From 57f6ce072e35770a63be0c5d5e82f90d8da7d665 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 08:21:48 +0530 Subject: usb: phy: add a new driver for usb3 phy Added a driver for usb3 phy that handles the interaction between usb phy device and dwc3 controller. This also includes device tree support for usb3 phy driver and the documentation with device tree binding information is updated. Currently writing to control module register is taken care in this driver which will be removed once the control module driver is in place. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi Signed-off-by: Moiz Sonasath Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/usb-phy.txt b/Documentation/devicetree/bindings/usb/usb-phy.txt index b4b86bb..61496f5 100644 --- a/Documentation/devicetree/bindings/usb/usb-phy.txt +++ b/Documentation/devicetree/bindings/usb/usb-phy.txt @@ -17,3 +17,26 @@ usb2phy@4a0ad080 { reg = <0x4a0ad080 0x58>; ctrl-module = <&omap_control_usb>; }; + +OMAP USB3 PHY + +Required properties: + - compatible: Should be "ti,omap-usb3" + - reg : Address and length of the register set for the device. + - reg-names: The names of the register addresses corresponding to the registers + filled in "reg". + +Optional properties: + - ctrl-module : phandle of the control module used by PHY driver to power on + the PHY. + +This is usually a subnode of ocp2scp to which it is connected. + +usb3phy@4a084400 { + compatible = "ti,omap-usb3"; + reg = <0x4a084400 0x80>, + <0x4a084800 0x64>, + <0x4a084c00 0x40>; + reg-names = "phy_rx", "phy_tx", "pll_ctrl"; + ctrl-module = <&omap_control_usb>; +}; diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index f989511..9bbedec 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -15,6 +15,16 @@ config OMAP_USB2 The USB OTG controller communicates with the comparator using this driver. +config OMAP_USB3 + tristate "OMAP USB3 PHY Driver" + select USB_OTG_UTILS + select OMAP_CONTROL_USB + help + Enable this to support the USB3 PHY that is part of SOC. This + driver takes care of all the PHY functionality apart from comparator. + This driver interacts with the "OMAP Control USB Driver" to power + on/off the PHY. + config OMAP_CONTROL_USB tristate "OMAP CONTROL USB Driver" depends on ARCH_OMAP2PLUS diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index ee97767..b13faa1 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -5,6 +5,7 @@ ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG obj-$(CONFIG_OMAP_USB2) += omap-usb2.o +obj-$(CONFIG_OMAP_USB3) += omap-usb3.o obj-$(CONFIG_OMAP_CONTROL_USB) += omap-control-usb.o obj-$(CONFIG_USB_ISP1301) += isp1301.o obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o diff --git a/drivers/usb/phy/omap-usb3.c b/drivers/usb/phy/omap-usb3.c new file mode 100644 index 0000000..fadc0c2 --- /dev/null +++ b/drivers/usb/phy/omap-usb3.c @@ -0,0 +1,355 @@ +/* + * omap-usb3 - USB PHY, talking to dwc3 controller in OMAP. + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Author: Kishon Vijay Abraham I + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUM_SYS_CLKS 5 +#define PLL_STATUS 0x00000004 +#define PLL_GO 0x00000008 +#define PLL_CONFIGURATION1 0x0000000C +#define PLL_CONFIGURATION2 0x00000010 +#define PLL_CONFIGURATION3 0x00000014 +#define PLL_CONFIGURATION4 0x00000020 + +#define PLL_REGM_MASK 0x001FFE00 +#define PLL_REGM_SHIFT 0x9 +#define PLL_REGM_F_MASK 0x0003FFFF +#define PLL_REGM_F_SHIFT 0x0 +#define PLL_REGN_MASK 0x000001FE +#define PLL_REGN_SHIFT 0x1 +#define PLL_SELFREQDCO_MASK 0x0000000E +#define PLL_SELFREQDCO_SHIFT 0x1 +#define PLL_SD_MASK 0x0003FC00 +#define PLL_SD_SHIFT 0x9 +#define SET_PLL_GO 0x1 +#define PLL_TICOPWDN 0x10000 +#define PLL_LOCK 0x2 +#define PLL_IDLE 0x1 + +/* + * This is an Empirical value that works, need to confirm the actual + * value required for the USB3PHY_PLL_CONFIGURATION2.PLL_IDLE status + * to be correctly reflected in the USB3PHY_PLL_STATUS register. + */ +# define PLL_IDLE_TIME 100; + +enum sys_clk_rate { + CLK_RATE_UNDEFINED = -1, + CLK_RATE_12MHZ, + CLK_RATE_16MHZ, + CLK_RATE_19MHZ, + CLK_RATE_26MHZ, + CLK_RATE_38MHZ +}; + +static struct usb_dpll_params omap_usb3_dpll_params[NUM_SYS_CLKS] = { + {1250, 5, 4, 20, 0}, /* 12 MHz */ + {3125, 20, 4, 20, 0}, /* 16.8 MHz */ + {1172, 8, 4, 20, 65537}, /* 19.2 MHz */ + {1250, 12, 4, 20, 0}, /* 26 MHz */ + {3125, 47, 4, 20, 92843}, /* 38.4 MHz */ +}; + +static int omap_usb3_suspend(struct usb_phy *x, int suspend) +{ + struct omap_usb *phy = phy_to_omapusb(x); + int val; + int timeout = PLL_IDLE_TIME; + + if (suspend && !phy->is_suspended) { + val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); + val |= PLL_IDLE; + omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); + + do { + val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS); + if (val & PLL_TICOPWDN) + break; + udelay(1); + } while (--timeout); + + omap_control_usb3_phy_power(phy->control_dev, 0); + + phy->is_suspended = 1; + } else if (!suspend && phy->is_suspended) { + phy->is_suspended = 0; + + val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); + val &= ~PLL_IDLE; + omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); + + do { + val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS); + if (!(val & PLL_TICOPWDN)) + break; + udelay(1); + } while (--timeout); + } + + return 0; +} + +static inline enum sys_clk_rate __get_sys_clk_index(unsigned long rate) +{ + switch (rate) { + case 12000000: + return CLK_RATE_12MHZ; + case 16800000: + return CLK_RATE_16MHZ; + case 19200000: + return CLK_RATE_19MHZ; + case 26000000: + return CLK_RATE_26MHZ; + case 38400000: + return CLK_RATE_38MHZ; + default: + return CLK_RATE_UNDEFINED; + } +} + +static void omap_usb_dpll_relock(struct omap_usb *phy) +{ + u32 val; + unsigned long timeout; + + omap_usb_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO); + + timeout = jiffies + msecs_to_jiffies(20); + do { + val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS); + if (val & PLL_LOCK) + break; + } while (!WARN_ON(time_after(jiffies, timeout))); +} + +static int omap_usb_dpll_lock(struct omap_usb *phy) +{ + u32 val; + unsigned long rate; + enum sys_clk_rate clk_index; + + rate = clk_get_rate(phy->sys_clk); + clk_index = __get_sys_clk_index(rate); + + if (clk_index == CLK_RATE_UNDEFINED) { + pr_err("dpll cannot be locked for sys clk freq:%luHz\n", rate); + return -EINVAL; + } + + val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); + val &= ~PLL_REGN_MASK; + val |= omap_usb3_dpll_params[clk_index].n << PLL_REGN_SHIFT; + omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); + + val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); + val &= ~PLL_SELFREQDCO_MASK; + val |= omap_usb3_dpll_params[clk_index].freq << PLL_SELFREQDCO_SHIFT; + omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); + + val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); + val &= ~PLL_REGM_MASK; + val |= omap_usb3_dpll_params[clk_index].m << PLL_REGM_SHIFT; + omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); + + val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4); + val &= ~PLL_REGM_F_MASK; + val |= omap_usb3_dpll_params[clk_index].mf << PLL_REGM_F_SHIFT; + omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val); + + val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3); + val &= ~PLL_SD_MASK; + val |= omap_usb3_dpll_params[clk_index].sd << PLL_SD_SHIFT; + omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val); + + omap_usb_dpll_relock(phy); + + return 0; +} + +static int omap_usb3_init(struct usb_phy *x) +{ + struct omap_usb *phy = phy_to_omapusb(x); + + omap_usb_dpll_lock(phy); + omap_control_usb3_phy_power(phy->control_dev, 1); + + return 0; +} + +static int omap_usb3_probe(struct platform_device *pdev) +{ + struct omap_usb *phy; + struct resource *res; + + phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); + if (!phy) { + dev_err(&pdev->dev, "unable to alloc mem for OMAP USB3 PHY\n"); + return -ENOMEM; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl"); + phy->pll_ctrl_base = devm_request_and_ioremap(&pdev->dev, res); + if (!phy->pll_ctrl_base) { + dev_err(&pdev->dev, "ioremap of pll_ctrl failed\n"); + return -ENOMEM; + } + + phy->dev = &pdev->dev; + + phy->phy.dev = phy->dev; + phy->phy.label = "omap-usb3"; + phy->phy.init = omap_usb3_init; + phy->phy.set_suspend = omap_usb3_suspend; + phy->phy.type = USB_PHY_TYPE_USB3; + + phy->is_suspended = 1; + phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k"); + if (IS_ERR(phy->wkupclk)) { + dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); + return PTR_ERR(phy->wkupclk); + } + clk_prepare(phy->wkupclk); + + phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m"); + if (IS_ERR(phy->optclk)) { + dev_err(&pdev->dev, "unable to get usb_otg_ss_refclk960m\n"); + return PTR_ERR(phy->optclk); + } + clk_prepare(phy->optclk); + + phy->sys_clk = devm_clk_get(phy->dev, "sys_clkin"); + if (IS_ERR(phy->sys_clk)) { + pr_err("%s: unable to get sys_clkin\n", __func__); + return -EINVAL; + } + + phy->control_dev = omap_get_control_dev(); + if (IS_ERR(phy->control_dev)) { + dev_dbg(&pdev->dev, "Failed to get control device\n"); + return -ENODEV; + } + + omap_control_usb3_phy_power(phy->control_dev, 0); + usb_add_phy_dev(&phy->phy); + + platform_set_drvdata(pdev, phy); + + pm_runtime_enable(phy->dev); + pm_runtime_get(&pdev->dev); + + return 0; +} + +static int omap_usb3_remove(struct platform_device *pdev) +{ + struct omap_usb *phy = platform_get_drvdata(pdev); + + clk_unprepare(phy->wkupclk); + clk_unprepare(phy->optclk); + usb_remove_phy(&phy->phy); + if (!pm_runtime_suspended(&pdev->dev)) + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM_RUNTIME + +static int omap_usb3_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omap_usb *phy = platform_get_drvdata(pdev); + + clk_disable(phy->wkupclk); + clk_disable(phy->optclk); + + return 0; +} + +static int omap_usb3_runtime_resume(struct device *dev) +{ + u32 ret = 0; + struct platform_device *pdev = to_platform_device(dev); + struct omap_usb *phy = platform_get_drvdata(pdev); + + ret = clk_enable(phy->optclk); + if (ret) { + dev_err(phy->dev, "Failed to enable optclk %d\n", ret); + goto err1; + } + + ret = clk_enable(phy->wkupclk); + if (ret) { + dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); + goto err2; + } + + return 0; + +err2: + clk_disable(phy->optclk); + +err1: + return ret; +} + +static const struct dev_pm_ops omap_usb3_pm_ops = { + SET_RUNTIME_PM_OPS(omap_usb3_runtime_suspend, omap_usb3_runtime_resume, + NULL) +}; + +#define DEV_PM_OPS (&omap_usb3_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + +#ifdef CONFIG_OF +static const struct of_device_id omap_usb3_id_table[] = { + { .compatible = "ti,omap-usb3" }, + {} +}; +MODULE_DEVICE_TABLE(of, omap_usb3_id_table); +#endif + +static struct platform_driver omap_usb3_driver = { + .probe = omap_usb3_probe, + .remove = omap_usb3_remove, + .driver = { + .name = "omap-usb3", + .owner = THIS_MODULE, + .pm = DEV_PM_OPS, + .of_match_table = of_match_ptr(omap_usb3_id_table), + }, +}; + +module_platform_driver(omap_usb3_driver); + +MODULE_ALIAS("platform: omap_usb3"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("OMAP USB3 phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/usb/omap_usb.h b/include/linux/usb/omap_usb.h index 3db9b53..6ae2936 100644 --- a/include/linux/usb/omap_usb.h +++ b/include/linux/usb/omap_usb.h @@ -19,14 +19,26 @@ #ifndef __DRIVERS_OMAP_USB2_H #define __DRIVERS_OMAP_USB2_H +#include #include +struct usb_dpll_params { + u16 m; + u8 n; + u8 freq:3; + u8 sd; + u32 mf; +}; + struct omap_usb { struct usb_phy phy; struct phy_companion *comparator; + void __iomem *pll_ctrl_base; struct device *dev; struct device *control_dev; struct clk *wkupclk; + struct clk *sys_clk; + struct clk *optclk; u8 is_suspended:1; }; @@ -41,4 +53,15 @@ static inline int omap_usb2_set_comparator(struct phy_companion *comparator) } #endif +static inline u32 omap_usb_readl(void __iomem *addr, unsigned offset) +{ + return __raw_readl(addr + offset); +} + +static inline void omap_usb_writel(void __iomem *addr, unsigned offset, + u32 data) +{ + __raw_writel(data, addr + offset); +} + #endif /* __DRIVERS_OMAP_USB_H */ -- cgit v0.10.2 From c11747f6ce70253dbf73709bb0a5ff19acc48ec8 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 08:03:24 +0530 Subject: usb: musb: omap: make use of the new PHY lib APIs New PHY lib APIs like usb_add_phy_dev() and devm_usb_get_phy_dev() are used in MUSB (OMAP), in order to make use of the binding information provided in the board file (of OMAP platforms). All the platforms should be modified similar to this to add and get the PHY. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 779862d..23df9d7 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -345,7 +345,7 @@ static int omap2430_musb_init(struct musb *musb) * up through ULPI. TWL4030-family PMICs include one, * which needs a driver, drivers aren't always needed. */ - musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + musb->xceiv = devm_usb_get_phy_dev(dev, 0); if (IS_ERR_OR_NULL(musb->xceiv)) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index 0a70193..a994715 100644 --- a/drivers/usb/otg/twl4030-usb.c +++ b/drivers/usb/otg/twl4030-usb.c @@ -610,6 +610,7 @@ static int twl4030_usb_probe(struct platform_device *pdev) twl->phy.dev = twl->dev; twl->phy.label = "twl4030"; twl->phy.otg = otg; + twl->phy.type = USB_PHY_TYPE_USB2; twl->phy.set_suspend = twl4030_set_suspend; otg->phy = &twl->phy; @@ -624,7 +625,7 @@ static int twl4030_usb_probe(struct platform_device *pdev) dev_err(&pdev->dev, "ldo init failed\n"); return err; } - usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2); + usb_add_phy_dev(&twl->phy); platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) diff --git a/drivers/usb/phy/omap-usb2.c b/drivers/usb/phy/omap-usb2.c index c2b4c8e..0cd88ac 100644 --- a/drivers/usb/phy/omap-usb2.c +++ b/drivers/usb/phy/omap-usb2.c @@ -142,6 +142,7 @@ static int omap_usb2_probe(struct platform_device *pdev) phy->phy.label = "omap-usb2"; phy->phy.set_suspend = omap_usb2_suspend; phy->phy.otg = otg; + phy->phy.type = USB_PHY_TYPE_USB2; phy->control_dev = omap_get_control_dev(); if (IS_ERR(phy->control_dev)) { @@ -165,7 +166,7 @@ static int omap_usb2_probe(struct platform_device *pdev) } clk_prepare(phy->wkupclk); - usb_add_phy(&phy->phy, USB_PHY_TYPE_USB2); + usb_add_phy_dev(&phy->phy); platform_set_drvdata(pdev, phy); -- cgit v0.10.2 From b16604f2c1dc3f0a326818b282e6bb5f363f725e Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 08:03:26 +0530 Subject: usb: musb: omap: get phy by phandle for dt boot The OMAP glue has been modified to get PHY by phandle for dt boot. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 23df9d7..d100360 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -345,7 +345,12 @@ static int omap2430_musb_init(struct musb *musb) * up through ULPI. TWL4030-family PMICs include one, * which needs a driver, drivers aren't always needed. */ - musb->xceiv = devm_usb_get_phy_dev(dev, 0); + if (dev->parent->of_node) + musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent, + "usb-phy", 0); + else + musb->xceiv = devm_usb_get_phy_dev(dev, 0); + if (IS_ERR_OR_NULL(musb->xceiv)) { pr_err("HS USB OTG: no transceiver configured\n"); return -ENODEV; -- cgit v0.10.2 From 0c4c8bbbfdfc61c2ab2cc1026b5a05ae52396c93 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 08:21:49 +0530 Subject: usb: phy: omap-usb2: enable 960Mhz clock for omap5 "usb_otg_ss_refclk960m" is needed for usb2 phy present in omap5. For omap4, the clk_get of this clock will fail since it does not have this clock. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/drivers/usb/phy/omap-usb2.c b/drivers/usb/phy/omap-usb2.c index 0cd88ac..844ab68 100644 --- a/drivers/usb/phy/omap-usb2.c +++ b/drivers/usb/phy/omap-usb2.c @@ -166,6 +166,12 @@ static int omap_usb2_probe(struct platform_device *pdev) } clk_prepare(phy->wkupclk); + phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m"); + if (IS_ERR(phy->optclk)) + dev_vdbg(&pdev->dev, "unable to get refclk960m\n"); + else + clk_prepare(phy->optclk); + usb_add_phy_dev(&phy->phy); platform_set_drvdata(pdev, phy); @@ -180,6 +186,8 @@ static int omap_usb2_remove(struct platform_device *pdev) struct omap_usb *phy = platform_get_drvdata(pdev); clk_unprepare(phy->wkupclk); + if (!IS_ERR(phy->optclk)) + clk_unprepare(phy->optclk); usb_remove_phy(&phy->phy); return 0; @@ -193,6 +201,8 @@ static int omap_usb2_runtime_suspend(struct device *dev) struct omap_usb *phy = platform_get_drvdata(pdev); clk_disable(phy->wkupclk); + if (!IS_ERR(phy->optclk)) + clk_disable(phy->optclk); return 0; } @@ -204,9 +214,25 @@ static int omap_usb2_runtime_resume(struct device *dev) struct omap_usb *phy = platform_get_drvdata(pdev); ret = clk_enable(phy->wkupclk); - if (ret < 0) + if (ret < 0) { dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); + goto err0; + } + + if (!IS_ERR(phy->optclk)) { + ret = clk_enable(phy->optclk); + if (ret < 0) { + dev_err(phy->dev, "Failed to enable optclk %d\n", ret); + goto err1; + } + } + + return 0; + +err1: + clk_disable(phy->wkupclk); +err0: return ret; } -- cgit v0.10.2 From 5088b6f5bcf1747345ef9fe217fc80935b1b07df Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 25 Jan 2013 16:36:53 +0530 Subject: usb: dwc3: core: add dt support for dwc3 core Added dt support for dwc3 core and update the documentation with device tree binding information. Getting a PHY is now done using devm_usb_get_phy_by_phandle() for dt boot. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt new file mode 100644 index 0000000..7a95c65 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -0,0 +1,22 @@ +synopsys DWC3 CORE + +DWC3- USB3 CONTROLLER + +Required properties: + - compatible: must be "synopsys,dwc3" + - reg : Address and length of the register set for the device + - interrupts: Interrupts used by the dwc3 controller. + - usb-phy : array of phandle for the PHY device + +Optional properties: + - tx-fifo-resize: determines if the FIFO *has* to be reallocated. + +This is usually a subnode to DWC3 glue to which it is connected. + +dwc3@4a030000 { + compatible = "synopsys,dwc3"; + reg = <0x4a030000 0xcfff>; + interrupts = <0 92 4> + usb-phy = <&usb2_phy>, <&usb3,phy>; + tx-fifo-resize; +}; diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 3a4004a..8044025 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -420,13 +420,19 @@ static int dwc3_probe(struct platform_device *pdev) return -ENOMEM; } - dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + if (node) { + dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); + dwc->usb3_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 1); + } else { + dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3); + } + if (IS_ERR_OR_NULL(dwc->usb2_phy)) { dev_err(dev, "no usb2 phy configured\n"); return -EPROBE_DEFER; } - dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3); if (IS_ERR_OR_NULL(dwc->usb3_phy)) { dev_err(dev, "no usb3 phy configured\n"); return -EPROBE_DEFER; @@ -450,8 +456,7 @@ static int dwc3_probe(struct platform_device *pdev) else dwc->maximum_speed = DWC3_DCFG_SUPERSPEED; - if (of_get_property(node, "tx-fifo-resize", NULL)) - dwc->needs_fifo_resize = true; + dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize"); pm_runtime_enable(dev); pm_runtime_get_sync(dev); @@ -580,11 +585,22 @@ static int dwc3_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id of_dwc3_match[] = { + { + .compatible = "synopsys,dwc3" + }, + { }, +}; +MODULE_DEVICE_TABLE(of, of_dwc3_match); +#endif + static struct platform_driver dwc3_driver = { .probe = dwc3_probe, .remove = dwc3_remove, .driver = { .name = "dwc3", + .of_match_table = of_match_ptr(of_dwc3_match), }, }; -- cgit v0.10.2