mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-28 11:49:32 +00:00
ARM: tegra: USB driver cleanup
The Tegra USB driver has a number of issues: 1) The PHY driver isn't a true platform device, and doesn't implement the standard USB PHY API. 2) struct device instance numbers were used to make decisions in the driver, rather than being parameterized by DT or platform data. This pull request solves issue (2), and lays the groundwork for solving issue (1). The work on issue (1) involved introducing new DT nodes for the USB PHYs, which in turn interacted with the Tegra common clock framework changes, due to the move of clock lookups into device tree. Hence, these USB driver changes are taken through the Tegra tree with acks from USB maintainers. This pull request is based on the previous pull request, with tag tegra-for-3.9-soc-ccf. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJRCYlsAAoJEMzrak5tbycxbp8QANOIUl1AD5R9wyHucs3O4RVN xKzI0ch2N4lb8dRBNn5oZ/T0fU5m3Y2w+cAJD9INQZaiqbMsGjm7wcJeuy8IUbQL cjBwYFOySCAIfRJZtLIYax+sB7/Eoa+un2SHtmWJPn/dWrPcJOp1wc+iNJHxVTLu 4kHgG1Rq3A7etorPmvaf5HkATmOKvzXoxrO9LyGdr3/dgEUDa50p4JBRC4V7osFy Va9B4fOq777/k1aR59SMKOwLoVvw/u+YwYIMStmAflLnWduXRUbA400mc+nDVH+x 66r3JUZ5/0vzyMsLW8AnhbZWfAROedl4JfT9ZYVvsQmxSzIoyQl2dtt1GiDr7/j+ LSWgPT9BaY+masNiwxoFMMqLg+w0RUxkCGmVu0yYGK3xNmuSI9zeZxV+o3oPkyjQ /IrOerPE45rd8EACBF9hRLI3mc6+kbS2RPXRl74jre7KHAzumVfZrsNb5M5ZbK1z 8h8ucs/7b62YgCTUhYvbWlvBFgcANq2N9r/AJF4TMMIWtpliHDOHGLu0V1Kd2r8c hWsAB9dgaljcVV7rFXJhjlCJp7U6MkPmKi1lWg9jpYS5nDcCy5smOH1EEYzN6xS8 4e/YLgz/qkChW4cbQQNMRsLhjD+7ouHryTDLNmOZ/n1Rty8mFOqaQsRPl3OA/4DN jPmL9AneLq4RM44Zdxe5 =8pTe -----END PGP SIGNATURE----- Merge tag 'tegra-for-3.9-soc-usb' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra into next/soc From Stephen Warren: ARM: tegra: USB driver cleanup The Tegra USB driver has a number of issues: 1) The PHY driver isn't a true platform device, and doesn't implement the standard USB PHY API. 2) struct device instance numbers were used to make decisions in the driver, rather than being parameterized by DT or platform data. This pull request solves issue (2), and lays the groundwork for solving issue (1). The work on issue (1) involved introducing new DT nodes for the USB PHYs, which in turn interacted with the Tegra common clock framework changes, due to the move of clock lookups into device tree. Hence, these USB driver changes are taken through the Tegra tree with acks from USB maintainers. This pull request is based on the previous pull request, with tag tegra-for-3.9-soc-ccf. * tag 'tegra-for-3.9-soc-usb' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra: usb: host: tegra: make use of PHY pointer of HCD ARM: tegra: Add reset GPIO information to PHY DT node usb: host: tegra: don't touch EMC clock usb: add APIs to access host registers from Tegra PHY USB: PHY: tegra: Get rid of instance number to differentiate PHY type USB: PHY: tegra: get rid of instance number to differentiate legacy controller ARM: tegra: add clocks properties to USB PHY nodes ARM: tegra: add DT nodes for Tegra USB PHY usb: phy: remove unused APIs from Tegra PHY. usb: host: tegra: Resetting PORT0 based on information received via DT. ARM: tegra: Add new DT property to USB node. usb: phy: use kzalloc to allocate struct tegra_usb_phy ARM: tegra: remove USB address related macros from iomap.h
This commit is contained in:
commit
ceca718f21
13 changed files with 192 additions and 139 deletions
|
@ -11,6 +11,7 @@ Required properties :
|
|||
- phy_type : Should be one of "ulpi" or "utmi".
|
||||
- nvidia,vbus-gpio : If present, specifies a gpio that needs to be
|
||||
activated for the bus to be powered.
|
||||
- nvidia,phy : phandle of the PHY instance, the controller is connected to.
|
||||
|
||||
Required properties for phy_type == ulpi:
|
||||
- nvidia,phy-reset-gpio : The GPIO used to reset the PHY.
|
||||
|
@ -27,3 +28,5 @@ Optional properties:
|
|||
registers are accessed through the APB_MISC base address instead of
|
||||
the USB controller. Since this is a legacy issue it probably does not
|
||||
warrant a compatible string of its own.
|
||||
- nvidia,needs-double-reset : boolean is to be set for some of the Tegra2
|
||||
USB ports, which need reset twice due to hardware issues.
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
Tegra SOC USB PHY
|
||||
|
||||
The device node for Tegra SOC USB PHY:
|
||||
|
||||
Required properties :
|
||||
- compatible : Should be "nvidia,tegra20-usb-phy".
|
||||
- reg : Address and length of the register set for the USB PHY interface.
|
||||
- phy_type : Should be one of "ulpi" or "utmi".
|
||||
|
||||
Required properties for phy_type == ulpi:
|
||||
- nvidia,phy-reset-gpio : The GPIO used to reset the PHY.
|
||||
|
||||
Optional properties:
|
||||
- nvidia,has-legacy-mode : boolean indicates whether this controller can
|
||||
operate in legacy mode (as APX 2500 / 2600). In legacy mode some
|
||||
registers are accessed through the APB_MISC base address instead of
|
||||
the USB controller.
|
|
@ -432,6 +432,10 @@ usb@c5008000 {
|
|||
status = "okay";
|
||||
};
|
||||
|
||||
usb-phy@c5004400 {
|
||||
nvidia,phy-reset-gpio = <&gpio 169 0>; /* gpio PV1 */
|
||||
};
|
||||
|
||||
sdhci@c8000200 {
|
||||
status = "okay";
|
||||
cd-gpios = <&gpio 69 0>; /* gpio PI5 */
|
||||
|
|
|
@ -420,6 +420,10 @@ usb@c5008000 {
|
|||
status = "okay";
|
||||
};
|
||||
|
||||
usb-phy@c5004400 {
|
||||
nvidia,phy-reset-gpio = <&gpio 168 0>; /* gpio PV0 */
|
||||
};
|
||||
|
||||
sdhci@c8000000 {
|
||||
status = "okay";
|
||||
cd-gpios = <&gpio 173 0>; /* gpio PV5 */
|
||||
|
|
|
@ -561,6 +561,10 @@ usb@c5008000 {
|
|||
status = "okay";
|
||||
};
|
||||
|
||||
usb-phy@c5004400 {
|
||||
nvidia,phy-reset-gpio = <&gpio 169 0>; /* gpio PV1 */
|
||||
};
|
||||
|
||||
sdhci@c8000000 {
|
||||
status = "okay";
|
||||
power-gpios = <&gpio 86 0>; /* gpio PK6 */
|
||||
|
|
|
@ -310,6 +310,10 @@ usb@c5008000 {
|
|||
status = "okay";
|
||||
};
|
||||
|
||||
usb-phy@c5004400 {
|
||||
nvidia,phy-reset-gpio = <&gpio 168 0>; /* gpio PV0 */
|
||||
};
|
||||
|
||||
sdhci@c8000000 {
|
||||
status = "okay";
|
||||
bus-width = <4>;
|
||||
|
|
|
@ -497,6 +497,10 @@ usb@c5008000 {
|
|||
status = "okay";
|
||||
};
|
||||
|
||||
usb-phy@c5004400 {
|
||||
nvidia,phy-reset-gpio = <&gpio 169 0>; /* gpio PV1 */
|
||||
};
|
||||
|
||||
sdhci@c8000000 {
|
||||
status = "okay";
|
||||
power-gpios = <&gpio 86 0>; /* gpio PK6 */
|
||||
|
|
|
@ -400,6 +400,31 @@ memory-controller@7000f400 {
|
|||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
phy1: usb-phy@c5000400 {
|
||||
compatible = "nvidia,tegra20-usb-phy";
|
||||
reg = <0xc5000400 0x3c00>;
|
||||
phy_type = "utmi";
|
||||
nvidia,has-legacy-mode;
|
||||
clocks = <&tegra_car 22>, <&tegra_car 127>;
|
||||
clock-names = "phy", "pll_u";
|
||||
};
|
||||
|
||||
phy2: usb-phy@c5004400 {
|
||||
compatible = "nvidia,tegra20-usb-phy";
|
||||
reg = <0xc5004400 0x3c00>;
|
||||
phy_type = "ulpi";
|
||||
clocks = <&tegra_car 94>, <&tegra_car 127>;
|
||||
clock-names = "phy", "pll_u";
|
||||
};
|
||||
|
||||
phy3: usb-phy@c5008400 {
|
||||
compatible = "nvidia,tegra20-usb-phy";
|
||||
reg = <0xc5008400 0x3C00>;
|
||||
phy_type = "utmi";
|
||||
clocks = <&tegra_car 22>, <&tegra_car 127>;
|
||||
clock-names = "phy", "pll_u";
|
||||
};
|
||||
|
||||
usb@c5000000 {
|
||||
compatible = "nvidia,tegra20-ehci", "usb-ehci";
|
||||
reg = <0xc5000000 0x4000>;
|
||||
|
@ -407,6 +432,8 @@ usb@c5000000 {
|
|||
phy_type = "utmi";
|
||||
nvidia,has-legacy-mode;
|
||||
clocks = <&tegra_car 22>;
|
||||
nvidia,needs-double-reset;
|
||||
nvidia,phy = <&phy1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -416,6 +443,7 @@ usb@c5004000 {
|
|||
interrupts = <0 21 0x04>;
|
||||
phy_type = "ulpi";
|
||||
clocks = <&tegra_car 58>;
|
||||
nvidia,phy = <&phy2>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -425,6 +453,7 @@ usb@c5008000 {
|
|||
interrupts = <0 97 0x04>;
|
||||
phy_type = "utmi";
|
||||
clocks = <&tegra_car 59>;
|
||||
nvidia,phy = <&phy3>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
|
|
@ -68,11 +68,11 @@ static struct tegra_ehci_platform_data tegra_ehci3_pdata = {
|
|||
};
|
||||
|
||||
static struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = {
|
||||
OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB_BASE, "tegra-ehci.0",
|
||||
OF_DEV_AUXDATA("nvidia,tegra20-ehci", 0xC5000000, "tegra-ehci.0",
|
||||
&tegra_ehci1_pdata),
|
||||
OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB2_BASE, "tegra-ehci.1",
|
||||
OF_DEV_AUXDATA("nvidia,tegra20-ehci", 0xC5004000, "tegra-ehci.1",
|
||||
&tegra_ehci2_pdata),
|
||||
OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB3_BASE, "tegra-ehci.2",
|
||||
OF_DEV_AUXDATA("nvidia,tegra20-ehci", 0xC5008000, "tegra-ehci.2",
|
||||
&tegra_ehci3_pdata),
|
||||
{}
|
||||
};
|
||||
|
|
|
@ -240,15 +240,6 @@
|
|||
#define TEGRA_CSITE_BASE 0x70040000
|
||||
#define TEGRA_CSITE_SIZE SZ_256K
|
||||
|
||||
#define TEGRA_USB_BASE 0xC5000000
|
||||
#define TEGRA_USB_SIZE SZ_16K
|
||||
|
||||
#define TEGRA_USB2_BASE 0xC5004000
|
||||
#define TEGRA_USB2_SIZE SZ_16K
|
||||
|
||||
#define TEGRA_USB3_BASE 0xC5008000
|
||||
#define TEGRA_USB3_SIZE SZ_16K
|
||||
|
||||
#define TEGRA_SDMMC1_BASE 0xC8000000
|
||||
#define TEGRA_SDMMC1_SIZE SZ_512
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* EHCI-compliant USB host controller driver for NVIDIA Tegra SoCs
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Copyright (C) 2009 NVIDIA Corporation
|
||||
* Copyright (C) 2009 - 2013 NVIDIA Corporation
|
||||
*
|
||||
* 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
|
||||
|
@ -26,23 +26,28 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/usb/ehci_def.h>
|
||||
#include <linux/usb/tegra_usb_phy.h>
|
||||
|
||||
#define TEGRA_USB_BASE 0xC5000000
|
||||
#define TEGRA_USB2_BASE 0xC5004000
|
||||
#define TEGRA_USB3_BASE 0xC5008000
|
||||
|
||||
/* PORTSC registers */
|
||||
#define TEGRA_USB_PORTSC1 0x184
|
||||
#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
|
||||
#define TEGRA_USB_PORTSC1_PHCD (1 << 23)
|
||||
|
||||
#define TEGRA_USB_DMA_ALIGN 32
|
||||
|
||||
struct tegra_ehci_hcd {
|
||||
struct ehci_hcd *ehci;
|
||||
struct tegra_usb_phy *phy;
|
||||
struct clk *clk;
|
||||
struct clk *emc_clk;
|
||||
struct usb_phy *transceiver;
|
||||
int host_resumed;
|
||||
int port_resuming;
|
||||
bool needs_double_reset;
|
||||
enum tegra_usb_phy_port_speed port_speed;
|
||||
};
|
||||
|
||||
|
@ -50,9 +55,8 @@ static void tegra_ehci_power_up(struct usb_hcd *hcd)
|
|||
{
|
||||
struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
|
||||
|
||||
clk_prepare_enable(tegra->emc_clk);
|
||||
clk_prepare_enable(tegra->clk);
|
||||
usb_phy_set_suspend(&tegra->phy->u_phy, 0);
|
||||
usb_phy_set_suspend(hcd->phy, 0);
|
||||
tegra->host_resumed = 1;
|
||||
}
|
||||
|
||||
|
@ -61,9 +65,8 @@ static void tegra_ehci_power_down(struct usb_hcd *hcd)
|
|||
struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
|
||||
|
||||
tegra->host_resumed = 0;
|
||||
usb_phy_set_suspend(&tegra->phy->u_phy, 1);
|
||||
usb_phy_set_suspend(hcd->phy, 1);
|
||||
clk_disable_unprepare(tegra->clk);
|
||||
clk_disable_unprepare(tegra->emc_clk);
|
||||
}
|
||||
|
||||
static int tegra_ehci_internal_port_reset(
|
||||
|
@ -156,7 +159,7 @@ static int tegra_ehci_hub_control(
|
|||
if (tegra->port_resuming && !(temp & PORT_SUSPEND)) {
|
||||
/* Resume completed, re-enable disconnect detection */
|
||||
tegra->port_resuming = 0;
|
||||
tegra_usb_phy_postresume(tegra->phy);
|
||||
tegra_usb_phy_postresume(hcd->phy);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,7 +187,7 @@ static int tegra_ehci_hub_control(
|
|||
}
|
||||
|
||||
/* For USB1 port we need to issue Port Reset twice internally */
|
||||
if (tegra->phy->instance == 0 &&
|
||||
if (tegra->needs_double_reset &&
|
||||
(typeReq == SetPortFeature && wValue == USB_PORT_FEAT_RESET)) {
|
||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||
return tegra_ehci_internal_port_reset(ehci, status_reg);
|
||||
|
@ -209,7 +212,7 @@ static int tegra_ehci_hub_control(
|
|||
goto done;
|
||||
|
||||
/* Disable disconnect detection during port resume */
|
||||
tegra_usb_phy_preresume(tegra->phy);
|
||||
tegra_usb_phy_preresume(hcd->phy);
|
||||
|
||||
ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25);
|
||||
|
||||
|
@ -473,7 +476,7 @@ static int controller_resume(struct device *dev)
|
|||
}
|
||||
|
||||
/* Force the phy to keep data lines in suspend state */
|
||||
tegra_ehci_phy_restore_start(tegra->phy, tegra->port_speed);
|
||||
tegra_ehci_phy_restore_start(hcd->phy, tegra->port_speed);
|
||||
|
||||
/* Enable host mode */
|
||||
tdi_reset(ehci);
|
||||
|
@ -540,17 +543,17 @@ static int controller_resume(struct device *dev)
|
|||
}
|
||||
}
|
||||
|
||||
tegra_ehci_phy_restore_end(tegra->phy);
|
||||
tegra_ehci_phy_restore_end(hcd->phy);
|
||||
goto done;
|
||||
|
||||
restart:
|
||||
if (tegra->port_speed <= TEGRA_USB_PHY_PORT_SPEED_HIGH)
|
||||
tegra_ehci_phy_restore_end(tegra->phy);
|
||||
tegra_ehci_phy_restore_end(hcd->phy);
|
||||
|
||||
tegra_ehci_restart(hcd);
|
||||
|
||||
done:
|
||||
tegra_usb_phy_preresume(tegra->phy);
|
||||
tegra_usb_phy_preresume(hcd->phy);
|
||||
tegra->port_resuming = 1;
|
||||
return 0;
|
||||
}
|
||||
|
@ -604,6 +607,37 @@ static const struct dev_pm_ops tegra_ehci_pm_ops = {
|
|||
|
||||
#endif
|
||||
|
||||
/* Bits of PORTSC1, which will get cleared by writing 1 into them */
|
||||
#define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
|
||||
|
||||
void tegra_ehci_set_pts(struct usb_phy *x, u8 pts_val)
|
||||
{
|
||||
unsigned long val;
|
||||
struct usb_hcd *hcd = bus_to_hcd(x->otg->host);
|
||||
void __iomem *base = hcd->regs;
|
||||
|
||||
val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS;
|
||||
val &= ~TEGRA_USB_PORTSC1_PTS(3);
|
||||
val |= TEGRA_USB_PORTSC1_PTS(pts_val & 3);
|
||||
writel(val, base + TEGRA_USB_PORTSC1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_ehci_set_pts);
|
||||
|
||||
void tegra_ehci_set_phcd(struct usb_phy *x, bool enable)
|
||||
{
|
||||
unsigned long val;
|
||||
struct usb_hcd *hcd = bus_to_hcd(x->otg->host);
|
||||
void __iomem *base = hcd->regs;
|
||||
|
||||
val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS;
|
||||
if (enable)
|
||||
val |= TEGRA_USB_PORTSC1_PHCD;
|
||||
else
|
||||
val &= ~TEGRA_USB_PORTSC1_PHCD;
|
||||
writel(val, base + TEGRA_USB_PORTSC1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_ehci_set_phcd);
|
||||
|
||||
static u64 tegra_ehci_dma_mask = DMA_BIT_MASK(32);
|
||||
|
||||
static int tegra_ehci_probe(struct platform_device *pdev)
|
||||
|
@ -615,6 +649,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)
|
|||
int err = 0;
|
||||
int irq;
|
||||
int instance = pdev->id;
|
||||
struct usb_phy *u_phy;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (!pdata) {
|
||||
|
@ -656,15 +691,8 @@ static int tegra_ehci_probe(struct platform_device *pdev)
|
|||
if (err)
|
||||
goto fail_clk;
|
||||
|
||||
tegra->emc_clk = devm_clk_get(&pdev->dev, "emc");
|
||||
if (IS_ERR(tegra->emc_clk)) {
|
||||
dev_err(&pdev->dev, "Can't get emc clock\n");
|
||||
err = PTR_ERR(tegra->emc_clk);
|
||||
goto fail_emc_clk;
|
||||
}
|
||||
|
||||
clk_prepare_enable(tegra->emc_clk);
|
||||
clk_set_rate(tegra->emc_clk, 400000000);
|
||||
tegra->needs_double_reset = of_property_read_bool(pdev->dev.of_node,
|
||||
"nvidia,needs-double-reset");
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
|
@ -712,9 +740,19 @@ static int tegra_ehci_probe(struct platform_device *pdev)
|
|||
goto fail_io;
|
||||
}
|
||||
|
||||
usb_phy_init(&tegra->phy->u_phy);
|
||||
hcd->phy = u_phy = &tegra->phy->u_phy;
|
||||
usb_phy_init(hcd->phy);
|
||||
|
||||
err = usb_phy_set_suspend(&tegra->phy->u_phy, 0);
|
||||
u_phy->otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg),
|
||||
GFP_KERNEL);
|
||||
if (!u_phy->otg) {
|
||||
dev_err(&pdev->dev, "Failed to alloc memory for otg\n");
|
||||
err = -ENOMEM;
|
||||
goto fail_io;
|
||||
}
|
||||
u_phy->otg->host = hcd_to_bus(hcd);
|
||||
|
||||
err = usb_phy_set_suspend(hcd->phy, 0);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to power on the phy\n");
|
||||
goto fail;
|
||||
|
@ -760,10 +798,8 @@ static int tegra_ehci_probe(struct platform_device *pdev)
|
|||
if (!IS_ERR_OR_NULL(tegra->transceiver))
|
||||
otg_set_host(tegra->transceiver->otg, NULL);
|
||||
#endif
|
||||
usb_phy_shutdown(&tegra->phy->u_phy);
|
||||
usb_phy_shutdown(hcd->phy);
|
||||
fail_io:
|
||||
clk_disable_unprepare(tegra->emc_clk);
|
||||
fail_emc_clk:
|
||||
clk_disable_unprepare(tegra->clk);
|
||||
fail_clk:
|
||||
usb_put_hcd(hcd);
|
||||
|
@ -784,15 +820,12 @@ static int tegra_ehci_remove(struct platform_device *pdev)
|
|||
otg_set_host(tegra->transceiver->otg, NULL);
|
||||
#endif
|
||||
|
||||
usb_phy_shutdown(hcd->phy);
|
||||
usb_remove_hcd(hcd);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
usb_phy_shutdown(&tegra->phy->u_phy);
|
||||
|
||||
clk_disable_unprepare(tegra->clk);
|
||||
|
||||
clk_disable_unprepare(tegra->emc_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/ulpi.h>
|
||||
|
@ -35,19 +36,6 @@
|
|||
|
||||
#define ULPI_VIEWPORT 0x170
|
||||
|
||||
#define USB_PORTSC1 0x184
|
||||
#define USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
|
||||
#define USB_PORTSC1_PSPD(x) (((x) & 0x3) << 26)
|
||||
#define USB_PORTSC1_PHCD (1 << 23)
|
||||
#define USB_PORTSC1_WKOC (1 << 22)
|
||||
#define USB_PORTSC1_WKDS (1 << 21)
|
||||
#define USB_PORTSC1_WKCN (1 << 20)
|
||||
#define USB_PORTSC1_PTC(x) (((x) & 0xf) << 16)
|
||||
#define USB_PORTSC1_PP (1 << 12)
|
||||
#define USB_PORTSC1_SUSP (1 << 7)
|
||||
#define USB_PORTSC1_PE (1 << 2)
|
||||
#define USB_PORTSC1_CCS (1 << 0)
|
||||
|
||||
#define USB_SUSP_CTRL 0x400
|
||||
#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3)
|
||||
#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4)
|
||||
|
@ -208,11 +196,6 @@ static struct tegra_utmip_config utmip_default[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static inline bool phy_is_ulpi(struct tegra_usb_phy *phy)
|
||||
{
|
||||
return (phy->instance == 1);
|
||||
}
|
||||
|
||||
static int utmip_pad_open(struct tegra_usb_phy *phy)
|
||||
{
|
||||
phy->pad_clk = clk_get_sys("utmip-pad", NULL);
|
||||
|
@ -221,7 +204,7 @@ static int utmip_pad_open(struct tegra_usb_phy *phy)
|
|||
return PTR_ERR(phy->pad_clk);
|
||||
}
|
||||
|
||||
if (phy->instance == 0) {
|
||||
if (phy->is_legacy_phy) {
|
||||
phy->pad_regs = phy->regs;
|
||||
} else {
|
||||
phy->pad_regs = ioremap(TEGRA_USB_BASE, TEGRA_USB_SIZE);
|
||||
|
@ -236,7 +219,7 @@ static int utmip_pad_open(struct tegra_usb_phy *phy)
|
|||
|
||||
static void utmip_pad_close(struct tegra_usb_phy *phy)
|
||||
{
|
||||
if (phy->instance != 0)
|
||||
if (!phy->is_legacy_phy)
|
||||
iounmap(phy->pad_regs);
|
||||
clk_put(phy->pad_clk);
|
||||
}
|
||||
|
@ -305,7 +288,7 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy)
|
|||
unsigned long val;
|
||||
void __iomem *base = phy->regs;
|
||||
|
||||
if (phy->instance == 0) {
|
||||
if (phy->is_legacy_phy) {
|
||||
val = readl(base + USB_SUSP_CTRL);
|
||||
val |= USB_SUSP_SET;
|
||||
writel(val, base + USB_SUSP_CTRL);
|
||||
|
@ -315,13 +298,8 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy)
|
|||
val = readl(base + USB_SUSP_CTRL);
|
||||
val &= ~USB_SUSP_SET;
|
||||
writel(val, base + USB_SUSP_CTRL);
|
||||
}
|
||||
|
||||
if (phy->instance == 2) {
|
||||
val = readl(base + USB_PORTSC1);
|
||||
val |= USB_PORTSC1_PHCD;
|
||||
writel(val, base + USB_PORTSC1);
|
||||
}
|
||||
} else
|
||||
tegra_ehci_set_phcd(&phy->u_phy, true);
|
||||
|
||||
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0)
|
||||
pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
|
||||
|
@ -332,7 +310,7 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy)
|
|||
unsigned long val;
|
||||
void __iomem *base = phy->regs;
|
||||
|
||||
if (phy->instance == 0) {
|
||||
if (phy->is_legacy_phy) {
|
||||
val = readl(base + USB_SUSP_CTRL);
|
||||
val |= USB_SUSP_CLR;
|
||||
writel(val, base + USB_SUSP_CTRL);
|
||||
|
@ -342,13 +320,8 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy)
|
|||
val = readl(base + USB_SUSP_CTRL);
|
||||
val &= ~USB_SUSP_CLR;
|
||||
writel(val, base + USB_SUSP_CTRL);
|
||||
}
|
||||
|
||||
if (phy->instance == 2) {
|
||||
val = readl(base + USB_PORTSC1);
|
||||
val &= ~USB_PORTSC1_PHCD;
|
||||
writel(val, base + USB_PORTSC1);
|
||||
}
|
||||
} else
|
||||
tegra_ehci_set_phcd(&phy->u_phy, false);
|
||||
|
||||
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
|
||||
USB_PHY_CLK_VALID))
|
||||
|
@ -365,7 +338,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
|
|||
val |= UTMIP_RESET;
|
||||
writel(val, base + USB_SUSP_CTRL);
|
||||
|
||||
if (phy->instance == 0) {
|
||||
if (phy->is_legacy_phy) {
|
||||
val = readl(base + USB1_LEGACY_CTRL);
|
||||
val |= USB1_NO_LEGACY_MODE;
|
||||
writel(val, base + USB1_LEGACY_CTRL);
|
||||
|
@ -440,16 +413,14 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
|
|||
val |= UTMIP_BIAS_PDTRK_COUNT(0x5);
|
||||
writel(val, base + UTMIP_BIAS_CFG1);
|
||||
|
||||
if (phy->instance == 0) {
|
||||
if (phy->is_legacy_phy) {
|
||||
val = readl(base + UTMIP_SPARE_CFG0);
|
||||
if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE)
|
||||
val &= ~FUSE_SETUP_SEL;
|
||||
else
|
||||
val |= FUSE_SETUP_SEL;
|
||||
writel(val, base + UTMIP_SPARE_CFG0);
|
||||
}
|
||||
|
||||
if (phy->instance == 2) {
|
||||
} else {
|
||||
val = readl(base + USB_SUSP_CTRL);
|
||||
val |= UTMIP_PHY_ENABLE;
|
||||
writel(val, base + USB_SUSP_CTRL);
|
||||
|
@ -459,7 +430,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
|
|||
val &= ~UTMIP_RESET;
|
||||
writel(val, base + USB_SUSP_CTRL);
|
||||
|
||||
if (phy->instance == 0) {
|
||||
if (phy->is_legacy_phy) {
|
||||
val = readl(base + USB1_LEGACY_CTRL);
|
||||
val &= ~USB1_VBUS_SENSE_CTL_MASK;
|
||||
val |= USB1_VBUS_SENSE_CTL_A_SESS_VLD;
|
||||
|
@ -472,11 +443,8 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
|
|||
|
||||
utmi_phy_clk_enable(phy);
|
||||
|
||||
if (phy->instance == 2) {
|
||||
val = readl(base + USB_PORTSC1);
|
||||
val &= ~USB_PORTSC1_PTS(~0);
|
||||
writel(val, base + USB_PORTSC1);
|
||||
}
|
||||
if (!phy->is_legacy_phy)
|
||||
tegra_ehci_set_pts(&phy->u_phy, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -621,10 +589,6 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
|
|||
return ret;
|
||||
}
|
||||
|
||||
val = readl(base + USB_PORTSC1);
|
||||
val |= USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN;
|
||||
writel(val, base + USB_PORTSC1);
|
||||
|
||||
val = readl(base + USB_SUSP_CTRL);
|
||||
val |= USB_SUSP_CLR;
|
||||
writel(val, base + USB_SUSP_CTRL);
|
||||
|
@ -639,17 +603,8 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
|
|||
|
||||
static int ulpi_phy_power_off(struct tegra_usb_phy *phy)
|
||||
{
|
||||
unsigned long val;
|
||||
void __iomem *base = phy->regs;
|
||||
struct tegra_ulpi_config *config = phy->config;
|
||||
|
||||
/* Clear WKCN/WKDS/WKOC wake-on events that can cause the USB
|
||||
* Controller to immediately bring the ULPI PHY out of low power
|
||||
*/
|
||||
val = readl(base + USB_PORTSC1);
|
||||
val &= ~(USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN);
|
||||
writel(val, base + USB_PORTSC1);
|
||||
|
||||
clk_disable(phy->clk);
|
||||
return gpio_direction_output(config->reset_gpio, 0);
|
||||
}
|
||||
|
@ -660,7 +615,7 @@ static int tegra_phy_init(struct usb_phy *x)
|
|||
struct tegra_ulpi_config *ulpi_config;
|
||||
int err;
|
||||
|
||||
if (phy_is_ulpi(phy)) {
|
||||
if (phy->is_ulpi_phy) {
|
||||
ulpi_config = phy->config;
|
||||
phy->clk = clk_get_sys(NULL, ulpi_config->clk);
|
||||
if (IS_ERR(phy->clk)) {
|
||||
|
@ -698,7 +653,7 @@ static void tegra_usb_phy_close(struct usb_phy *x)
|
|||
{
|
||||
struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
|
||||
|
||||
if (phy_is_ulpi(phy))
|
||||
if (phy->is_ulpi_phy)
|
||||
clk_put(phy->clk);
|
||||
else
|
||||
utmip_pad_close(phy);
|
||||
|
@ -709,7 +664,7 @@ static void tegra_usb_phy_close(struct usb_phy *x)
|
|||
|
||||
static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
|
||||
{
|
||||
if (phy_is_ulpi(phy))
|
||||
if (phy->is_ulpi_phy)
|
||||
return ulpi_phy_power_on(phy);
|
||||
else
|
||||
return utmi_phy_power_on(phy);
|
||||
|
@ -717,7 +672,7 @@ static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
|
|||
|
||||
static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
|
||||
{
|
||||
if (phy_is_ulpi(phy))
|
||||
if (phy->is_ulpi_phy)
|
||||
return ulpi_phy_power_off(phy);
|
||||
else
|
||||
return utmi_phy_power_off(phy);
|
||||
|
@ -739,8 +694,9 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance,
|
|||
unsigned long parent_rate;
|
||||
int i;
|
||||
int err;
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
phy = kmalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL);
|
||||
phy = kzalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
@ -749,9 +705,16 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance,
|
|||
phy->config = config;
|
||||
phy->mode = phy_mode;
|
||||
phy->dev = dev;
|
||||
phy->is_legacy_phy =
|
||||
of_property_read_bool(np, "nvidia,has-legacy-mode");
|
||||
err = of_property_match_string(np, "phy_type", "ulpi");
|
||||
if (err < 0)
|
||||
phy->is_ulpi_phy = false;
|
||||
else
|
||||
phy->is_ulpi_phy = true;
|
||||
|
||||
if (!phy->config) {
|
||||
if (phy_is_ulpi(phy)) {
|
||||
if (phy->is_ulpi_phy) {
|
||||
pr_err("%s: ulpi phy configuration missing", __func__);
|
||||
err = -EINVAL;
|
||||
goto err0;
|
||||
|
@ -796,45 +759,40 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_usb_phy_open);
|
||||
|
||||
void tegra_usb_phy_preresume(struct tegra_usb_phy *phy)
|
||||
void tegra_usb_phy_preresume(struct usb_phy *x)
|
||||
{
|
||||
if (!phy_is_ulpi(phy))
|
||||
struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
|
||||
|
||||
if (!phy->is_ulpi_phy)
|
||||
utmi_phy_preresume(phy);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_usb_phy_preresume);
|
||||
|
||||
void tegra_usb_phy_postresume(struct tegra_usb_phy *phy)
|
||||
void tegra_usb_phy_postresume(struct usb_phy *x)
|
||||
{
|
||||
if (!phy_is_ulpi(phy))
|
||||
struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
|
||||
|
||||
if (!phy->is_ulpi_phy)
|
||||
utmi_phy_postresume(phy);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_usb_phy_postresume);
|
||||
|
||||
void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy,
|
||||
void tegra_ehci_phy_restore_start(struct usb_phy *x,
|
||||
enum tegra_usb_phy_port_speed port_speed)
|
||||
{
|
||||
if (!phy_is_ulpi(phy))
|
||||
struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
|
||||
|
||||
if (!phy->is_ulpi_phy)
|
||||
utmi_phy_restore_start(phy, port_speed);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_start);
|
||||
|
||||
void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy)
|
||||
void tegra_ehci_phy_restore_end(struct usb_phy *x)
|
||||
{
|
||||
if (!phy_is_ulpi(phy))
|
||||
struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
|
||||
|
||||
if (!phy->is_ulpi_phy)
|
||||
utmi_phy_restore_end(phy);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end);
|
||||
|
||||
void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy)
|
||||
{
|
||||
if (!phy_is_ulpi(phy))
|
||||
utmi_phy_clk_disable(phy);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_usb_phy_clk_disable);
|
||||
|
||||
void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy)
|
||||
{
|
||||
if (!phy_is_ulpi(phy))
|
||||
utmi_phy_clk_enable(phy);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_usb_phy_clk_enable);
|
||||
|
|
|
@ -59,22 +59,24 @@ struct tegra_usb_phy {
|
|||
struct usb_phy *ulpi;
|
||||
struct usb_phy u_phy;
|
||||
struct device *dev;
|
||||
bool is_legacy_phy;
|
||||
bool is_ulpi_phy;
|
||||
};
|
||||
|
||||
struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance,
|
||||
void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode);
|
||||
|
||||
void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy);
|
||||
void tegra_usb_phy_preresume(struct usb_phy *phy);
|
||||
|
||||
void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy);
|
||||
void tegra_usb_phy_postresume(struct usb_phy *phy);
|
||||
|
||||
void tegra_usb_phy_preresume(struct tegra_usb_phy *phy);
|
||||
|
||||
void tegra_usb_phy_postresume(struct tegra_usb_phy *phy);
|
||||
|
||||
void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy,
|
||||
void tegra_ehci_phy_restore_start(struct usb_phy *phy,
|
||||
enum tegra_usb_phy_port_speed port_speed);
|
||||
|
||||
void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy);
|
||||
void tegra_ehci_phy_restore_end(struct usb_phy *phy);
|
||||
|
||||
void tegra_ehci_set_pts(struct usb_phy *x, u8 pts_val);
|
||||
|
||||
void tegra_ehci_set_phcd(struct usb_phy *x, bool enable);
|
||||
|
||||
#endif /* __TEGRA_USB_PHY_H */
|
||||
|
|
Loading…
Reference in a new issue