Skip to content

Commit

Permalink
usb: gadget: tegra-xudc: Support multiple device modes
Browse files Browse the repository at this point in the history
This change supports limited multiple device modes by:
- At most 4 ports contains OTG/Device capability.
- One port run as device mode at a time.

Signed-off-by: Nagarjuna Kristam <nkristam@nvidia.com>
Acked-by: Felipe Balbi <balbi@kernel.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
  • Loading branch information
Nagarjuna Kristam authored and thierryreding committed Mar 19, 2020
1 parent b9c9fd4 commit b4e1993
Showing 1 changed file with 160 additions and 57 deletions.
217 changes: 160 additions & 57 deletions drivers/usb/gadget/udc/tegra-xudc.c
Original file line number Diff line number Diff line change
Expand Up @@ -482,14 +482,16 @@ struct tegra_xudc {
bool device_mode;
struct work_struct usb_role_sw_work;

struct phy *usb3_phy;
struct phy *utmi_phy;
struct phy **usb3_phy;
struct phy *curr_usb3_phy;
struct phy **utmi_phy;
struct phy *curr_utmi_phy;

struct tegra_xudc_save_regs saved_regs;
bool suspended;
bool powergated;

struct usb_phy *usbphy;
struct usb_phy **usbphy;
struct notifier_block vbus_nb;

struct completion disconnect_complete;
Expand Down Expand Up @@ -521,6 +523,7 @@ struct tegra_xudc_soc {
unsigned int num_supplies;
const char * const *clock_names;
unsigned int num_clks;
unsigned int num_phys;
bool u1_enable;
bool u2_enable;
bool lpm_enable;
Expand Down Expand Up @@ -602,17 +605,18 @@ static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc)

pm_runtime_get_sync(xudc->dev);

err = phy_power_on(xudc->utmi_phy);
err = phy_power_on(xudc->curr_utmi_phy);
if (err < 0)
dev_err(xudc->dev, "utmi power on failed %d\n", err);

err = phy_power_on(xudc->usb3_phy);
err = phy_power_on(xudc->curr_usb3_phy);
if (err < 0)
dev_err(xudc->dev, "usb3 phy power on failed %d\n", err);

dev_dbg(xudc->dev, "device mode on\n");

phy_set_mode_ext(xudc->utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_DEVICE);
phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
USB_ROLE_DEVICE);
}

static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
Expand All @@ -627,7 +631,7 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)

reinit_completion(&xudc->disconnect_complete);

phy_set_mode_ext(xudc->utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);
phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);

pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >>
PORTSC_PLS_SHIFT;
Expand All @@ -652,11 +656,11 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
/* Make sure interrupt handler has completed before powergating. */
synchronize_irq(xudc->irq);

err = phy_power_off(xudc->utmi_phy);
err = phy_power_off(xudc->curr_utmi_phy);
if (err < 0)
dev_err(xudc->dev, "utmi_phy power off failed %d\n", err);

err = phy_power_off(xudc->usb3_phy);
err = phy_power_off(xudc->curr_usb3_phy);
if (err < 0)
dev_err(xudc->dev, "usb3_phy power off failed %d\n", err);

Expand All @@ -674,12 +678,27 @@ static void tegra_xudc_usb_role_sw_work(struct work_struct *work)
tegra_xudc_device_mode_off(xudc);
}

static int tegra_xudc_get_phy_index(struct tegra_xudc *xudc,
struct usb_phy *usbphy)
{
unsigned int i;

for (i = 0; i < xudc->soc->num_phys; i++) {
if (xudc->usbphy[i] && usbphy == xudc->usbphy[i])
return i;
}

dev_info(xudc->dev, "phy index could not be found for shared USB PHY");
return -1;
}

static int tegra_xudc_vbus_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct tegra_xudc *xudc = container_of(nb, struct tegra_xudc,
vbus_nb);
struct usb_phy *usbphy = (struct usb_phy *)data;
int phy_index;

dev_dbg(xudc->dev, "%s(): event is %d\n", __func__, usbphy->last_event);

Expand All @@ -693,8 +712,15 @@ static int tegra_xudc_vbus_notify(struct notifier_block *nb,
xudc->device_mode = (usbphy->last_event == USB_EVENT_VBUS) ? true :
false;

if (!xudc->suspended)
phy_index = tegra_xudc_get_phy_index(xudc, usbphy);
dev_dbg(xudc->dev, "%s(): current phy index is %d\n", __func__,
phy_index);

if (!xudc->suspended && phy_index != -1) {
xudc->curr_utmi_phy = xudc->utmi_phy[phy_index];
xudc->curr_usb3_phy = xudc->usb3_phy[phy_index];
schedule_work(&xudc->usb_role_sw_work);
}

return NOTIFY_OK;
}
Expand All @@ -714,9 +740,9 @@ static void tegra_xudc_plc_reset_work(struct work_struct *work)

if (pls == PORTSC_PLS_INACTIVE) {
dev_info(xudc->dev, "PLS = Inactive. Toggle VBUS\n");
phy_set_mode_ext(xudc->utmi_phy, PHY_MODE_USB_OTG,
phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
USB_ROLE_NONE);
phy_set_mode_ext(xudc->utmi_phy, PHY_MODE_USB_OTG,
phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
USB_ROLE_DEVICE);

xudc->wait_csc = false;
Expand Down Expand Up @@ -745,7 +771,8 @@ static void tegra_xudc_port_reset_war_work(struct work_struct *work)
if (pls == PORTSC_PLS_DISABLED) {
dev_dbg(xudc->dev, "toggle vbus\n");
/* PRC doesn't complete in 100ms, toggle the vbus */
ret = tegra_phy_xusb_utmi_port_reset(xudc->utmi_phy);
ret = tegra_phy_xusb_utmi_port_reset(
xudc->curr_utmi_phy);
if (ret == 1)
xudc->wait_for_sec_prc = 0;
}
Expand Down Expand Up @@ -1934,6 +1961,7 @@ static int tegra_xudc_gadget_start(struct usb_gadget *gadget,
unsigned long flags;
u32 val;
int ret;
unsigned int i;

if (!driver)
return -EINVAL;
Expand Down Expand Up @@ -1969,8 +1997,9 @@ static int tegra_xudc_gadget_start(struct usb_gadget *gadget,
xudc_writel(xudc, val, CTRL);
}

if (xudc->usbphy)
otg_set_peripheral(xudc->usbphy->otg, gadget);
for (i = 0; i < xudc->soc->num_phys; i++)
if (xudc->usbphy[i])
otg_set_peripheral(xudc->usbphy[i]->otg, gadget);

xudc->driver = driver;
unlock:
Expand All @@ -1987,13 +2016,15 @@ static int tegra_xudc_gadget_stop(struct usb_gadget *gadget)
struct tegra_xudc *xudc = to_xudc(gadget);
unsigned long flags;
u32 val;
unsigned int i;

pm_runtime_get_sync(xudc->dev);

spin_lock_irqsave(&xudc->lock, flags);

if (xudc->usbphy)
otg_set_peripheral(xudc->usbphy->otg, NULL);
for (i = 0; i < xudc->soc->num_phys; i++)
if (xudc->usbphy[i])
otg_set_peripheral(xudc->usbphy[i]->otg, NULL);

val = xudc_readl(xudc, CTRL);
val &= ~(CTRL_IE | CTRL_ENABLE);
Expand Down Expand Up @@ -3327,33 +3358,120 @@ static void tegra_xudc_device_params_init(struct tegra_xudc *xudc)
xudc_writel(xudc, val, CFG_DEV_SSPI_XFER);
}

static int tegra_xudc_phy_init(struct tegra_xudc *xudc)
static int tegra_xudc_phy_get(struct tegra_xudc *xudc)
{
int err;
int err = 0, usb3;
unsigned int i;

err = phy_init(xudc->utmi_phy);
if (err < 0) {
dev_err(xudc->dev, "utmi phy init failed: %d\n", err);
return err;
}
xudc->utmi_phy = devm_kcalloc(xudc->dev, xudc->soc->num_phys,
sizeof(*xudc->utmi_phy), GFP_KERNEL);
if (!xudc->utmi_phy)
return -ENOMEM;

err = phy_init(xudc->usb3_phy);
if (err < 0) {
dev_err(xudc->dev, "usb3 phy init failed: %d\n", err);
goto exit_utmi_phy;
xudc->usb3_phy = devm_kcalloc(xudc->dev, xudc->soc->num_phys,
sizeof(*xudc->usb3_phy), GFP_KERNEL);
if (!xudc->usb3_phy)
return -ENOMEM;

xudc->usbphy = devm_kcalloc(xudc->dev, xudc->soc->num_phys,
sizeof(*xudc->usbphy), GFP_KERNEL);
if (!xudc->usbphy)
return -ENOMEM;

xudc->vbus_nb.notifier_call = tegra_xudc_vbus_notify;

for (i = 0; i < xudc->soc->num_phys; i++) {
char phy_name[] = "usb.-.";

/* Get USB2 phy */
snprintf(phy_name, sizeof(phy_name), "usb2-%d", i);
xudc->utmi_phy[i] = devm_phy_optional_get(xudc->dev, phy_name);
if (IS_ERR(xudc->utmi_phy[i])) {
err = PTR_ERR(xudc->utmi_phy[i]);
if (err != -EPROBE_DEFER)
dev_err(xudc->dev, "failed to get usb2-%d phy: %d\n",
i, err);

goto clean_up;
} else if (xudc->utmi_phy[i]) {
/* Get usb-phy, if utmi phy is available */
xudc->usbphy[i] = devm_usb_get_phy_by_node(xudc->dev,
xudc->utmi_phy[i]->dev.of_node,
&xudc->vbus_nb);
if (IS_ERR(xudc->usbphy[i])) {
err = PTR_ERR(xudc->usbphy[i]);
dev_err(xudc->dev, "failed to get usbphy-%d: %d\n",
i, err);
goto clean_up;
}
} else if (!xudc->utmi_phy[i]) {
/* if utmi phy is not available, ignore USB3 phy get */
continue;
}

/* Get USB3 phy */
usb3 = tegra_xusb_padctl_get_usb3_companion(xudc->padctl, i);
if (usb3 < 0)
continue;

snprintf(phy_name, sizeof(phy_name), "usb3-%d", usb3);
xudc->usb3_phy[i] = devm_phy_optional_get(xudc->dev, phy_name);
if (IS_ERR(xudc->usb3_phy[i])) {
err = PTR_ERR(xudc->usb3_phy[i]);
if (err != -EPROBE_DEFER)
dev_err(xudc->dev, "failed to get usb3-%d phy: %d\n",
usb3, err);

goto clean_up;
} else if (xudc->usb3_phy[i])
dev_dbg(xudc->dev, "usb3_phy-%d registered", usb3);
}

return 0;
return err;

clean_up:
for (i = 0; i < xudc->soc->num_phys; i++) {
xudc->usb3_phy[i] = NULL;
xudc->utmi_phy[i] = NULL;
xudc->usbphy[i] = NULL;
}

exit_utmi_phy:
phy_exit(xudc->utmi_phy);
return err;
}

static void tegra_xudc_phy_exit(struct tegra_xudc *xudc)
{
phy_exit(xudc->usb3_phy);
phy_exit(xudc->utmi_phy);
unsigned int i;

for (i = 0; i < xudc->soc->num_phys; i++) {
phy_exit(xudc->usb3_phy[i]);
phy_exit(xudc->utmi_phy[i]);
}
}

static int tegra_xudc_phy_init(struct tegra_xudc *xudc)
{
int err;
unsigned int i;

for (i = 0; i < xudc->soc->num_phys; i++) {
err = phy_init(xudc->utmi_phy[i]);
if (err < 0) {
dev_err(xudc->dev, "utmi phy init failed: %d\n", err);
goto exit_phy;
}

err = phy_init(xudc->usb3_phy[i]);
if (err < 0) {
dev_err(xudc->dev, "usb3 phy init failed: %d\n", err);
goto exit_phy;
}
}
return 0;

exit_phy:
tegra_xudc_phy_exit(xudc);
return err;
}

static const char * const tegra210_xudc_supply_names[] = {
Expand Down Expand Up @@ -3381,6 +3499,7 @@ static struct tegra_xudc_soc tegra210_xudc_soc_data = {
.num_supplies = ARRAY_SIZE(tegra210_xudc_supply_names),
.clock_names = tegra210_xudc_clock_names,
.num_clks = ARRAY_SIZE(tegra210_xudc_clock_names),
.num_phys = 4,
.u1_enable = false,
.u2_enable = true,
.lpm_enable = false,
Expand All @@ -3393,6 +3512,7 @@ static struct tegra_xudc_soc tegra210_xudc_soc_data = {
static struct tegra_xudc_soc tegra186_xudc_soc_data = {
.clock_names = tegra186_xudc_clock_names,
.num_clks = ARRAY_SIZE(tegra186_xudc_clock_names),
.num_phys = 4,
.u1_enable = true,
.u2_enable = true,
.lpm_enable = false,
Expand Down Expand Up @@ -3555,19 +3675,9 @@ static int tegra_xudc_probe(struct platform_device *pdev)
goto put_padctl;
}

xudc->usb3_phy = devm_phy_optional_get(&pdev->dev, "usb3");
if (IS_ERR(xudc->usb3_phy)) {
err = PTR_ERR(xudc->usb3_phy);
dev_err(xudc->dev, "failed to get usb3 phy: %d\n", err);
goto disable_regulator;
}

xudc->utmi_phy = devm_phy_optional_get(&pdev->dev, "usb2");
if (IS_ERR(xudc->utmi_phy)) {
err = PTR_ERR(xudc->utmi_phy);
dev_err(xudc->dev, "failed to get usb2 phy: %d\n", err);
err = tegra_xudc_phy_get(xudc);
if (err)
goto disable_regulator;
}

err = tegra_xudc_powerdomain_init(xudc);
if (err)
Expand Down Expand Up @@ -3596,16 +3706,6 @@ static int tegra_xudc_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&xudc->port_reset_war_work,
tegra_xudc_port_reset_war_work);

xudc->vbus_nb.notifier_call = tegra_xudc_vbus_notify;
xudc->usbphy = devm_usb_get_phy_by_node(xudc->dev,
xudc->utmi_phy->dev.of_node,
&xudc->vbus_nb);
if (IS_ERR(xudc->usbphy)) {
err = PTR_ERR(xudc->usbphy);
dev_err(xudc->dev, "failed to get USB PHY: %d\n", err);
goto free_eps;
}

pm_runtime_enable(&pdev->dev);

xudc->gadget.ops = &tegra_xudc_gadget_ops;
Expand Down Expand Up @@ -3640,6 +3740,7 @@ static int tegra_xudc_probe(struct platform_device *pdev)
static int tegra_xudc_remove(struct platform_device *pdev)
{
struct tegra_xudc *xudc = platform_get_drvdata(pdev);
unsigned int i;

pm_runtime_get_sync(xudc->dev);

Expand All @@ -3655,8 +3756,10 @@ static int tegra_xudc_remove(struct platform_device *pdev)

regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies);

phy_power_off(xudc->utmi_phy);
phy_power_off(xudc->usb3_phy);
for (i = 0; i < xudc->soc->num_phys; i++) {
phy_power_off(xudc->utmi_phy[i]);
phy_power_off(xudc->usb3_phy[i]);
}

tegra_xudc_phy_exit(xudc);

Expand Down

0 comments on commit b4e1993

Please sign in to comment.