/*
- * Copyright (C) 2013 NVIDIA Corporation
+ * Copyright (C) 2013 NVIDIA Corporation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, pmc_data->instance);
spin_lock_irqsave(&pmc_lock, flags);
+ val = readl(pmc_base + PMC_SLEEP_CFG);
+ if (val & UTMIP_MASTER_ENABLE(inst)) {
+ DBG("%s(%d) inst:[%d] pmc already enabled\n",
+ __func__, __LINE__, pmc_data->instance);
+ spin_unlock_irqrestore(&pmc_lock, flags);
+ return;
+ }
/*Set PMC MASTER bits to do the following
* a. Take over the UTMI drivers
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, pmc_data->instance);
spin_lock_irqsave(&pmc_lock, flags);
+ val = readl(pmc_base + PMC_SLEEP_CFG);
+ if (!(val & UTMIP_MASTER_ENABLE(inst))) {
+ DBG("%s(%d) inst:[%d] pmc already disabled\n",
+ __func__, __LINE__, pmc_data->instance);
+ spin_unlock_irqrestore(&pmc_lock, flags);
+ return;
+ }
if (pmc_data->controller_type == TEGRA_USB_2_0 && usb_base) {
/* disable PMC master control */
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, pmc_data->instance);
spin_lock_irqsave(&pmc_lock, flags);
-
+ val = readl(pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
+ if (val & UHSIC_MASTER_ENABLE(inst)) {
+ DBG("%s(%d) inst:[%d] pmc already enabled\n",
+ __func__, __LINE__, pmc_data->instance);
+ spin_unlock_irqrestore(&pmc_lock, flags);
+ return;
+ }
/*Set PMC MASTER bits to do the following
* a. Take over the hsic drivers
* b. set up such that it will take over resume
spin_lock_irqsave(&pmc_lock, flags);
+ val = readl(pmc_base + PMC_UHSIC_SLEEP_CFG(inst));
+ if (!(val & UHSIC_MASTER_ENABLE(inst))) {
+ DBG("%s(%d) inst:[%d] pmc already disabled\n",
+ __func__, __LINE__, pmc_data->instance);
+ spin_unlock_irqrestore(&pmc_lock, flags);
+ return;
+ }
+
if (pmc_data->controller_type == TEGRA_USB_2_0 && usb_base) {
/* disable PMC master control */
val = readl(usb_base + UTMIP_PMC_WAKEUP0);
struct tegra_xhci_firmware_log log;
};
-static struct tegra_usb_pmc_data pmc_data;
+static struct tegra_usb_pmc_data pmc_data[XUSB_UTMI_COUNT];
static struct tegra_usb_pmc_data pmc_hsic_data[XUSB_HSIC_COUNT];
/* functions */
}
static void tegra_xhci_war_for_tctrl_rctrl(struct tegra_xhci_hcd *tegra);
-static void update_speed(struct tegra_xhci_hcd *tegra, u8 port, bool setup_wake)
+static int update_speed(struct tegra_xhci_hcd *tegra, u8 port)
{
struct usb_hcd *hcd = xhci_to_hcd(tegra->xhci);
u32 portsc;
- /* we don't need speed information to disable PMC */
- if (!setup_wake)
- return;
-
portsc = readl(hcd->regs + BAR0_XHCI_OP_PORTSC(port));
if (DEV_FULLSPEED(portsc))
- pmc_data.port_speed = USB_PMC_PORT_SPEED_FULL;
+ return USB_PMC_PORT_SPEED_FULL;
else if (DEV_HIGHSPEED(portsc))
- pmc_data.port_speed = USB_PMC_PORT_SPEED_HIGH;
+ return USB_PMC_PORT_SPEED_HIGH;
else if (DEV_LOWSPEED(portsc))
- pmc_data.port_speed = USB_PMC_PORT_SPEED_LOW;
+ return USB_PMC_PORT_SPEED_LOW;
else if (DEV_SUPERSPEED(portsc))
- pmc_data.port_speed = USB_PMC_PORT_SPEED_SUPER;
- else
- pmc_data.port_speed = USB_PMC_PORT_SPEED_UNKNOWN;
-}
-
-static void setup_wake_detect(bool setup_wake)
-{
- if (setup_wake)
- pmc_data.pmc_ops->setup_pmc_wake_detect(&pmc_data);
+ return USB_PMC_PORT_SPEED_SUPER;
else
- pmc_data.pmc_ops->disable_pmc_bus_ctrl(&pmc_data, 0);
-}
-
-static void pmc_setup(struct tegra_xhci_hcd *tegra, bool setup_wake)
-{
- u32 portmap = tegra->bdata->portmap;
-
- pmc_data.controller_type = TEGRA_USB_3_0;
- if (portmap & TEGRA_XUSB_USB2_P0) {
- pmc_data.instance = (tegra->pdata->pmc_portmap >> 0) & 0xf;
- pmc_data.phy_type = TEGRA_USB_PHY_INTF_UTMI;
- update_speed(tegra, pmc_data.instance, setup_wake);
- tegra_usb_pmc_init(&pmc_data);
- setup_wake_detect(setup_wake);
- }
- if (portmap & TEGRA_XUSB_USB2_P1) {
- pmc_data.instance = (tegra->pdata->pmc_portmap >> 4) & 0xf;
- pmc_data.phy_type = TEGRA_USB_PHY_INTF_UTMI;
- update_speed(tegra, pmc_data.instance, setup_wake);
- tegra_usb_pmc_init(&pmc_data);
- setup_wake_detect(setup_wake);
- }
- if (portmap & TEGRA_XUSB_USB2_P2) {
- pmc_data.instance = (tegra->pdata->pmc_portmap >> 8) & 0xf;
- pmc_data.phy_type = TEGRA_USB_PHY_INTF_UTMI;
- update_speed(tegra, pmc_data.instance, setup_wake);
- tegra_usb_pmc_init(&pmc_data);
- setup_wake_detect(setup_wake);
- }
+ return USB_PMC_PORT_SPEED_UNKNOWN;
}
static void pmc_init(struct tegra_xhci_hcd *tegra)
struct device *dev = &tegra->pdev->dev;
int pad;
+ for (pad = 0; pad < XUSB_UTMI_COUNT; pad++) {
+ if (BIT(XUSB_UTMI_INDEX + pad) & tegra->bdata->portmap) {
+ dev_dbg(dev, "%s utmi pad %d\n", __func__, pad);
+ pmc = &pmc_data[pad];
+ pmc->instance = pad;
+ pmc->phy_type = TEGRA_USB_PHY_INTF_UTMI;
+ pmc->port_speed = USB_PMC_PORT_SPEED_HIGH;
+ pmc->controller_type = TEGRA_USB_3_0;
+ tegra_usb_pmc_init(pmc);
+ }
+ }
+
for (pad = 0; pad < XUSB_HSIC_COUNT; pad++) {
if (BIT(XUSB_HSIC_INDEX + pad) & tegra->bdata->portmap) {
dev_dbg(dev, "%s hsic pad %d\n", __func__, pad);
pmc->pmc_ops->setup_pmc_wake_detect(pmc);
}
}
+
+ for (pad = 0; pad < XUSB_UTMI_COUNT; pad++) {
+ if (BIT(XUSB_UTMI_INDEX + pad) & tegra->bdata->portmap) {
+ dev_dbg(dev, "%s utmi pad %d\n", __func__, pad);
+ pmc = &pmc_data[pad];
+ pmc->port_speed = update_speed(tegra, pad);
+ pmc->pmc_ops->setup_pmc_wake_detect(pmc);
+ }
+ }
}
static void pmc_disable_bus_ctrl(struct tegra_xhci_hcd *tegra)
pmc->pmc_ops->disable_pmc_bus_ctrl(pmc, 0);
}
}
+
+ for (pad = 0; pad < XUSB_UTMI_COUNT; pad++) {
+ if (BIT(XUSB_UTMI_INDEX + pad) & tegra->bdata->portmap) {
+ dev_dbg(dev, "%s utmi pad %d\n", __func__, pad);
+ pmc = &pmc_data[pad];
+ pmc->pmc_ops->disable_pmc_bus_ctrl(pmc, 0);
+ }
+ }
}
u32 csb_read(struct tegra_xhci_hcd *tegra, u32 addr)
}
}
- tegra->xusb_s5p0v_reg = devm_regulator_get(&pdev->dev, supply->s5p0v);
- if (IS_ERR(tegra->xusb_s5p0v_reg)) {
- dev_err(&pdev->dev, "5p0v regulator not found: %ld."
- , PTR_ERR(tegra->xusb_s5p0v_reg));
- err = PTR_ERR(tegra->xusb_s5p0v_reg);
- goto err_put_s3p3v_reg;
- } else {
- err = regulator_enable(tegra->xusb_s5p0v_reg);
- if (err < 0) {
- dev_err(&pdev->dev,
- "5p0v: regulator enable failed:%d\n", err);
+ if ((tegra->bdata->portmap & TEGRA_XUSB_USB2_P0)) {
+ tegra->xusb_s5p0v_reg = devm_regulator_get(&pdev->dev,
+ supply->s5p0v);
+ if (IS_ERR(tegra->xusb_s5p0v_reg)) {
+ dev_err(&pdev->dev, "5p0v regulator not found: %ld."
+ , PTR_ERR(tegra->xusb_s5p0v_reg));
+ err = PTR_ERR(tegra->xusb_s5p0v_reg);
goto err_put_s3p3v_reg;
+ } else {
+ err = regulator_enable(tegra->xusb_s5p0v_reg);
+ if (err < 0) {
+ dev_err(&pdev->dev,
+ "5p0v: regulator enable failed:%d\n",
+ err);
+ goto err_put_s3p3v_reg;
+ }
}
}
err = PTR_ERR(tegra->xusb_s5p0v1_reg);
goto err_put_s1p05v_reg;
} else {
- err = regulator_enable(tegra->xusb_s5p0v1_reg);
+ if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P1)
+ err = regulator_enable(tegra->xusb_s5p0v1_reg);
if (err < 0) {
dev_err(&pdev->dev,
"5p0v1: regulator enable failed:%d\n", err);
err = PTR_ERR(tegra->xusb_s5p0v2_reg);
goto err_put_s1p5v1_reg;
} else {
- err = regulator_enable(tegra->xusb_s5p0v2_reg);
+ if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P2)
+ err = regulator_enable(tegra->xusb_s5p0v2_reg);
if (err < 0) {
dev_err(&pdev->dev,
"5p0v2: regulator enable failed:%d\n", err);
return err;
err_put_s1p5v1_reg:
- if (tegra->bdata->uses_different_vbus_per_port)
+ if (tegra->bdata->uses_different_vbus_per_port &&
+ tegra->bdata->portmap & TEGRA_XUSB_USB2_P1)
regulator_disable(tegra->xusb_s5p0v1_reg);
err_put_s1p05v_reg:
regulator_disable(tegra->xusb_s1p05v_reg);
err_put_s1p8v_reg:
regulator_disable(tegra->xusb_s1p8v_reg);
err_put_s5p0v_reg:
- regulator_disable(tegra->xusb_s5p0v_reg);
+ if ((tegra->bdata->portmap & TEGRA_XUSB_USB2_P0))
+ regulator_disable(tegra->xusb_s5p0v_reg);
err_put_s3p3v_reg:
regulator_disable(tegra->xusb_s3p3v_reg);
err_null_regulator:
{
regulator_disable(tegra->xusb_s1p05v_reg);
regulator_disable(tegra->xusb_s1p8v_reg);
- regulator_disable(tegra->xusb_s5p0v_reg);
+ if ((tegra->bdata->portmap & TEGRA_XUSB_USB2_P0))
+ regulator_disable(tegra->xusb_s5p0v_reg);
regulator_disable(tegra->xusb_s3p3v_reg);
if (tegra->bdata->uses_different_vbus_per_port) {
- regulator_disable(tegra->xusb_s5p0v1_reg);
- regulator_disable(tegra->xusb_s5p0v2_reg);
+ if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P1)
+ regulator_disable(tegra->xusb_s5p0v1_reg);
+ if (tegra->bdata->portmap & TEGRA_XUSB_USB2_P2)
+ regulator_disable(tegra->xusb_s5p0v2_reg);
}
tegra->xusb_s1p05v_reg = NULL;
/* calculate rctrl_val and tctrl_val */
tegra_xhci_war_for_tctrl_rctrl(tegra);
- pmc_setup(tegra, 1);
pmc_setup_wake_detect(tegra);
tegra_xhci_hs_wake_on_interrupts(tegra->bdata->portmap, true);
{
struct tegra_xusb_padctl_regs *padregs = tegra->padregs;
u32 reg, utmip_rctrl_val, utmip_tctrl_val, pad_mux, portmux, portowner;
+ int port;
portmux = USB2_OTG_PAD_PORT_MASK(0) | USB2_OTG_PAD_PORT_MASK(1);
portowner = USB2_OTG_PAD_PORT_OWNER_XUSB(0) |
* tctrl_val = 0x1f - (16 - ffz(utmip_tctrl_val)
* rctrl_val = 0x1f - (16 - ffz(utmip_rctrl_val)
*/
- pmc_data.utmip_rctrl_val = 0xf + ffz(utmip_rctrl_val);
- pmc_data.utmip_tctrl_val = 0xf + ffz(utmip_tctrl_val);
-
- xhci_dbg(tegra->xhci, "rctrl_val = 0x%x, tctrl_val = 0x%x\n",
- pmc_data.utmip_rctrl_val, pmc_data.utmip_tctrl_val);
+ for (port = 0; port < XUSB_UTMI_COUNT; port++) {
+ pmc_data[port].utmip_rctrl_val =
+ 0xf + ffz(utmip_rctrl_val);
+ pmc_data[port].utmip_tctrl_val =
+ 0xf + ffz(utmip_tctrl_val);
+ xhci_dbg(tegra->xhci, "rctrl_val = 0x%x, tctrl_val = 0x%x\n",
+ pmc_data[port].utmip_rctrl_val,
+ pmc_data[port].utmip_tctrl_val);
+ }
/* XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_0::PD = 1 and
* XUSB_PADCTL_USB2_BIAS_PAD_CTL_0_0::PD_TRK = 1
writel(reg, tegra->padctl_base + padregs->usb2_bias_pad_ctl0_0);
/* Program these values into PMC regiseter and program the
- * PMC override
+ * PMC override. This will be done as part of pmc setup
*/
- reg = PMC_TCTRL_VAL(pmc_data.utmip_tctrl_val) |
- PMC_RCTRL_VAL(pmc_data.utmip_rctrl_val);
- tegra_usb_pmc_reg_update(PMC_UTMIP_TERM_PAD_CFG,
- 0xffffffff, reg);
-
- reg = UTMIP_RCTRL_USE_PMC_P2 | UTMIP_TCTRL_USE_PMC_P2;
- tegra_usb_pmc_reg_update(PMC_SLEEP_CFG, reg, reg);
} else {
/* TODO use common PMC API to use SNPS register space */
}
goto out;
}
- pmc_setup(tegra, 0);
pmc_disable_bus_ctrl(tegra);
tegra->hc_in_elpg = false;
return;
if (tegra->hc_in_elpg) {
- mutex_lock(&tegra->sync_lock);
- pmc_setup(tegra, 0);
- mutex_unlock(&tegra->sync_lock);
+ pmc_disable_bus_ctrl(tegra);
} else {
xhci = tegra->xhci;
hcd = xhci_to_hcd(xhci);