Re: [PATCH 1/4] usb: phy: add a new driver for usb3 phy

From: ABRAHAM, KISHON VIJAY
Date: Fri Sep 21 2012 - 02:08:17 EST


Hi,

On Wed, Sep 19, 2012 at 8:11 PM, Marc Kleine-Budde <mkl@xxxxxxxxxxxxxx> wrote:
> On 09/19/2012 01:30 PM, Kishon Vijay Abraham I wrote:
>> 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 <kishon@xxxxxx>
>> Signed-off-by: Felipe Balbi <balbi@xxxxxx>
>> Signed-off-by: Moiz Sonasath <m-sonasath@xxxxxx>
>> ---
>> Documentation/devicetree/bindings/usb/usb-phy.txt | 18 +
>> drivers/usb/phy/Kconfig | 9 +
>> drivers/usb/phy/Makefile | 1 +
>> drivers/usb/phy/omap-usb3.c | 369 +++++++++++++++++++++
>> include/linux/usb/omap_usb.h | 72 ++++
>> 5 files changed, 469 insertions(+)
>> create mode 100644 drivers/usb/phy/omap-usb3.c
>>
>> diff --git a/Documentation/devicetree/bindings/usb/usb-phy.txt b/Documentation/devicetree/bindings/usb/usb-phy.txt
>> index 80d4148..7c5fd89 100644
>> --- a/Documentation/devicetree/bindings/usb/usb-phy.txt
>> +++ b/Documentation/devicetree/bindings/usb/usb-phy.txt
>> @@ -15,3 +15,21 @@ usb2phy@4a0ad080 {
>> reg = <0x4a0ad080 0x58>,
>> <0x4a002300 0x4>;
>> };
>> +
>> +OMAP USB3 PHY
>> +
>> +Required properties:
>> + - compatible: Should be "ti,omap-usb3"
>> + - reg : Address and length of the register set for the device. Also
>> +add the address of control module phy power register until a driver for
>> +control module is added
>> +
>> +This is usually a subnode of ocp2scp to which it is connected.
>> +
>> +usb3phy@4a084400 {
>> + compatible = "ti,omap-usb3";
>> + reg = <0x0x4a084400 0x80>,
>> + <0x4a084800 0x64>,
>> + <0x4a084c00 0x40>,
>> + <0x4a002370 0x4>;
>> +};
>> diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
>> index 63c339b..353dc0c 100644
>> --- a/drivers/usb/phy/Kconfig
>> +++ b/drivers/usb/phy/Kconfig
>> @@ -13,6 +13,15 @@ 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
>> + 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.
>> + The USB OTG controller communicates with the comparator using this
>> + driver.
>> +
>> 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 b069f29..973b1e6 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_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-usb3.c b/drivers/usb/phy/omap-usb3.c
>> new file mode 100644
>> index 0000000..4dc84ca
>> --- /dev/null
>> +++ b/drivers/usb/phy/omap-usb3.c
>> @@ -0,0 +1,369 @@
>> +/*
>> + * omap-usb3 - USB PHY, talking to dwc3 controller in OMAP.
>> + *
>> + * Copyright (C) 2012 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 <kishon@xxxxxx>
>> + *
>> + * 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 <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/usb/omap_usb.h>
>> +#include <linux/of.h>
>> +#include <linux/clk.h>
>> +#include <linux/err.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/delay.h>
>> +
>> +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 */
>> +};
>> +
>> +/**
>> + * omap5_usb_phy_power - power on/off the serializer using control module
>> + * @phy: struct omap_usb *
>> + * @on: 0 to off and 1 to on based on powering on or off the PHY
>> + *
>> + * omap_usb3 can call this API to power on or off the PHY.
>> + */
>> +static int omap5_usb_phy_power(struct omap_usb *phy, bool on)
>> +{
>> + u32 val;
>> + unsigned long rate;
>> + struct clk *sys_clk;
>> +
>> + sys_clk = clk_get(NULL, "sys_clkin");
>
> Where's the corresponding clk_put()?

I have missed it. Will fix it in my next version.
>
>> + if (IS_ERR(sys_clk)) {
>> + pr_err("%s: unable to get sys_clkin\n", __func__);
>> + return -EINVAL;
>> + }
>> +
>> + rate = clk_get_rate(sys_clk);
>> + rate = rate/1000000;
>> +
>> + val = readl(phy->control_dev);
>> +
>> + if (on) {
>> + val &= ~(USB_PWRCTL_CLK_CMD_MASK | USB_PWRCTL_CLK_FREQ_MASK);
>> + val |= USB3_PHY_TX_RX_POWERON << USB_PWRCTL_CLK_CMD_SHIFT;
>> + val |= rate << USB_PWRCTL_CLK_FREQ_SHIFT;
>> + } else {
>> + val &= ~USB_PWRCTL_CLK_CMD_MASK;
>> + val |= USB3_PHY_TX_RX_POWEROFF << USB_PWRCTL_CLK_CMD_SHIFT;
>> + }
>> +
>> + writel(val, phy->control_dev);
>> +
>> + return 0;
>> +}
>> +
>> +static int omap_usb3_suspend(struct usb_phy *x, int suspend)
>> +{
>> + struct omap_usb *phy = phy_to_omapusb(x);
>> + int val, ret;
>> + 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);
>> +
>> + omap5_usb_phy_power(phy, 0);
>> + pm_runtime_put_sync(phy->dev);
>> +
>> + phy->is_suspended = 1;
>> + } else if (!suspend && phy->is_suspended) {
>> + phy->is_suspended = 0;
>> + ret = pm_runtime_get_sync(phy->dev);
>> + if (ret < 0) {
>> + dev_err(phy->dev, "get_sync failed with err %d\n",
>> + ret);
>> + return ret;
>> + }
>> +
>> + 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;
>> + struct clk *sys_clk;
>> + unsigned long rate;
>> + enum sys_clk_rate clk_index;
>> +
>> + sys_clk = clk_get(NULL, "sys_clkin");
>
> Where's the corresponding clk_put()?
>
>> + if (IS_ERR(sys_clk)) {
>> + pr_err("unable to get sys_clkin\n");
>> + return PTR_ERR(sys_clk);
>> + }
>> +
>> + rate = clk_get_rate(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);
>> + omap5_usb_phy_power(phy, 1);
>> +
>> + return 0;
>> +}
>> +
>> +static int __devinit 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(pdev, IORESOURCE_MEM, 2);
>> + if (!res) {
>> + dev_err(&pdev->dev, "unable to get base address of pll_ctrl\n");
>> + return -ENODEV;
>> + }
>> +
>> + 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;
>> + }
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
>> + if (!res) {
>> + dev_err(&pdev->dev,
>> + "unable to get address of control phy power\n");
>> + return -ENODEV;
>> + }
>> +
>> + phy->control_dev = devm_request_and_ioremap(&pdev->dev, res);
>> + if (!phy->control_dev) {
>> + dev_err(&pdev->dev, "ioremap of control_dev 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->is_suspended = 1;
>> + omap5_usb_phy_power(phy, 0);
>> +
>> + 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);
>
> Are these clocks only needed on the PM usecase?
clk32k is a wakeup/debounce clock and 960m is a functional reference
clock to usb2 phy.

Thanks
Kishon
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/