Skip to content

Commit b4e1993

Browse files
Nagarjuna Kristamthierryreding
authored andcommitted
usb: gadget: tegra-xudc: Support multiple device modes
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 <[email protected]> Acked-by: Felipe Balbi <[email protected]> Signed-off-by: Thierry Reding <[email protected]>
1 parent b9c9fd4 commit b4e1993

File tree

1 file changed

+160
-57
lines changed

1 file changed

+160
-57
lines changed

drivers/usb/gadget/udc/tegra-xudc.c

Lines changed: 160 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -482,14 +482,16 @@ struct tegra_xudc {
482482
bool device_mode;
483483
struct work_struct usb_role_sw_work;
484484

485-
struct phy *usb3_phy;
486-
struct phy *utmi_phy;
485+
struct phy **usb3_phy;
486+
struct phy *curr_usb3_phy;
487+
struct phy **utmi_phy;
488+
struct phy *curr_utmi_phy;
487489

488490
struct tegra_xudc_save_regs saved_regs;
489491
bool suspended;
490492
bool powergated;
491493

492-
struct usb_phy *usbphy;
494+
struct usb_phy **usbphy;
493495
struct notifier_block vbus_nb;
494496

495497
struct completion disconnect_complete;
@@ -521,6 +523,7 @@ struct tegra_xudc_soc {
521523
unsigned int num_supplies;
522524
const char * const *clock_names;
523525
unsigned int num_clks;
526+
unsigned int num_phys;
524527
bool u1_enable;
525528
bool u2_enable;
526529
bool lpm_enable;
@@ -602,17 +605,18 @@ static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc)
602605

603606
pm_runtime_get_sync(xudc->dev);
604607

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

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

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

615-
phy_set_mode_ext(xudc->utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_DEVICE);
618+
phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
619+
USB_ROLE_DEVICE);
616620
}
617621

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

628632
reinit_completion(&xudc->disconnect_complete);
629633

630-
phy_set_mode_ext(xudc->utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);
634+
phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);
631635

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

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

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

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

681+
static int tegra_xudc_get_phy_index(struct tegra_xudc *xudc,
682+
struct usb_phy *usbphy)
683+
{
684+
unsigned int i;
685+
686+
for (i = 0; i < xudc->soc->num_phys; i++) {
687+
if (xudc->usbphy[i] && usbphy == xudc->usbphy[i])
688+
return i;
689+
}
690+
691+
dev_info(xudc->dev, "phy index could not be found for shared USB PHY");
692+
return -1;
693+
}
694+
677695
static int tegra_xudc_vbus_notify(struct notifier_block *nb,
678696
unsigned long action, void *data)
679697
{
680698
struct tegra_xudc *xudc = container_of(nb, struct tegra_xudc,
681699
vbus_nb);
682700
struct usb_phy *usbphy = (struct usb_phy *)data;
701+
int phy_index;
683702

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

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

696-
if (!xudc->suspended)
715+
phy_index = tegra_xudc_get_phy_index(xudc, usbphy);
716+
dev_dbg(xudc->dev, "%s(): current phy index is %d\n", __func__,
717+
phy_index);
718+
719+
if (!xudc->suspended && phy_index != -1) {
720+
xudc->curr_utmi_phy = xudc->utmi_phy[phy_index];
721+
xudc->curr_usb3_phy = xudc->usb3_phy[phy_index];
697722
schedule_work(&xudc->usb_role_sw_work);
723+
}
698724

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

715741
if (pls == PORTSC_PLS_INACTIVE) {
716742
dev_info(xudc->dev, "PLS = Inactive. Toggle VBUS\n");
717-
phy_set_mode_ext(xudc->utmi_phy, PHY_MODE_USB_OTG,
743+
phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
718744
USB_ROLE_NONE);
719-
phy_set_mode_ext(xudc->utmi_phy, PHY_MODE_USB_OTG,
745+
phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
720746
USB_ROLE_DEVICE);
721747

722748
xudc->wait_csc = false;
@@ -745,7 +771,8 @@ static void tegra_xudc_port_reset_war_work(struct work_struct *work)
745771
if (pls == PORTSC_PLS_DISABLED) {
746772
dev_dbg(xudc->dev, "toggle vbus\n");
747773
/* PRC doesn't complete in 100ms, toggle the vbus */
748-
ret = tegra_phy_xusb_utmi_port_reset(xudc->utmi_phy);
774+
ret = tegra_phy_xusb_utmi_port_reset(
775+
xudc->curr_utmi_phy);
749776
if (ret == 1)
750777
xudc->wait_for_sec_prc = 0;
751778
}
@@ -1934,6 +1961,7 @@ static int tegra_xudc_gadget_start(struct usb_gadget *gadget,
19341961
unsigned long flags;
19351962
u32 val;
19361963
int ret;
1964+
unsigned int i;
19371965

19381966
if (!driver)
19391967
return -EINVAL;
@@ -1969,8 +1997,9 @@ static int tegra_xudc_gadget_start(struct usb_gadget *gadget,
19691997
xudc_writel(xudc, val, CTRL);
19701998
}
19711999

1972-
if (xudc->usbphy)
1973-
otg_set_peripheral(xudc->usbphy->otg, gadget);
2000+
for (i = 0; i < xudc->soc->num_phys; i++)
2001+
if (xudc->usbphy[i])
2002+
otg_set_peripheral(xudc->usbphy[i]->otg, gadget);
19742003

19752004
xudc->driver = driver;
19762005
unlock:
@@ -1987,13 +2016,15 @@ static int tegra_xudc_gadget_stop(struct usb_gadget *gadget)
19872016
struct tegra_xudc *xudc = to_xudc(gadget);
19882017
unsigned long flags;
19892018
u32 val;
2019+
unsigned int i;
19902020

19912021
pm_runtime_get_sync(xudc->dev);
19922022

19932023
spin_lock_irqsave(&xudc->lock, flags);
19942024

1995-
if (xudc->usbphy)
1996-
otg_set_peripheral(xudc->usbphy->otg, NULL);
2025+
for (i = 0; i < xudc->soc->num_phys; i++)
2026+
if (xudc->usbphy[i])
2027+
otg_set_peripheral(xudc->usbphy[i]->otg, NULL);
19972028

19982029
val = xudc_readl(xudc, CTRL);
19992030
val &= ~(CTRL_IE | CTRL_ENABLE);
@@ -3327,33 +3358,120 @@ static void tegra_xudc_device_params_init(struct tegra_xudc *xudc)
33273358
xudc_writel(xudc, val, CFG_DEV_SSPI_XFER);
33283359
}
33293360

3330-
static int tegra_xudc_phy_init(struct tegra_xudc *xudc)
3361+
static int tegra_xudc_phy_get(struct tegra_xudc *xudc)
33313362
{
3332-
int err;
3363+
int err = 0, usb3;
3364+
unsigned int i;
33333365

3334-
err = phy_init(xudc->utmi_phy);
3335-
if (err < 0) {
3336-
dev_err(xudc->dev, "utmi phy init failed: %d\n", err);
3337-
return err;
3338-
}
3366+
xudc->utmi_phy = devm_kcalloc(xudc->dev, xudc->soc->num_phys,
3367+
sizeof(*xudc->utmi_phy), GFP_KERNEL);
3368+
if (!xudc->utmi_phy)
3369+
return -ENOMEM;
33393370

3340-
err = phy_init(xudc->usb3_phy);
3341-
if (err < 0) {
3342-
dev_err(xudc->dev, "usb3 phy init failed: %d\n", err);
3343-
goto exit_utmi_phy;
3371+
xudc->usb3_phy = devm_kcalloc(xudc->dev, xudc->soc->num_phys,
3372+
sizeof(*xudc->usb3_phy), GFP_KERNEL);
3373+
if (!xudc->usb3_phy)
3374+
return -ENOMEM;
3375+
3376+
xudc->usbphy = devm_kcalloc(xudc->dev, xudc->soc->num_phys,
3377+
sizeof(*xudc->usbphy), GFP_KERNEL);
3378+
if (!xudc->usbphy)
3379+
return -ENOMEM;
3380+
3381+
xudc->vbus_nb.notifier_call = tegra_xudc_vbus_notify;
3382+
3383+
for (i = 0; i < xudc->soc->num_phys; i++) {
3384+
char phy_name[] = "usb.-.";
3385+
3386+
/* Get USB2 phy */
3387+
snprintf(phy_name, sizeof(phy_name), "usb2-%d", i);
3388+
xudc->utmi_phy[i] = devm_phy_optional_get(xudc->dev, phy_name);
3389+
if (IS_ERR(xudc->utmi_phy[i])) {
3390+
err = PTR_ERR(xudc->utmi_phy[i]);
3391+
if (err != -EPROBE_DEFER)
3392+
dev_err(xudc->dev, "failed to get usb2-%d phy: %d\n",
3393+
i, err);
3394+
3395+
goto clean_up;
3396+
} else if (xudc->utmi_phy[i]) {
3397+
/* Get usb-phy, if utmi phy is available */
3398+
xudc->usbphy[i] = devm_usb_get_phy_by_node(xudc->dev,
3399+
xudc->utmi_phy[i]->dev.of_node,
3400+
&xudc->vbus_nb);
3401+
if (IS_ERR(xudc->usbphy[i])) {
3402+
err = PTR_ERR(xudc->usbphy[i]);
3403+
dev_err(xudc->dev, "failed to get usbphy-%d: %d\n",
3404+
i, err);
3405+
goto clean_up;
3406+
}
3407+
} else if (!xudc->utmi_phy[i]) {
3408+
/* if utmi phy is not available, ignore USB3 phy get */
3409+
continue;
3410+
}
3411+
3412+
/* Get USB3 phy */
3413+
usb3 = tegra_xusb_padctl_get_usb3_companion(xudc->padctl, i);
3414+
if (usb3 < 0)
3415+
continue;
3416+
3417+
snprintf(phy_name, sizeof(phy_name), "usb3-%d", usb3);
3418+
xudc->usb3_phy[i] = devm_phy_optional_get(xudc->dev, phy_name);
3419+
if (IS_ERR(xudc->usb3_phy[i])) {
3420+
err = PTR_ERR(xudc->usb3_phy[i]);
3421+
if (err != -EPROBE_DEFER)
3422+
dev_err(xudc->dev, "failed to get usb3-%d phy: %d\n",
3423+
usb3, err);
3424+
3425+
goto clean_up;
3426+
} else if (xudc->usb3_phy[i])
3427+
dev_dbg(xudc->dev, "usb3_phy-%d registered", usb3);
33443428
}
33453429

3346-
return 0;
3430+
return err;
3431+
3432+
clean_up:
3433+
for (i = 0; i < xudc->soc->num_phys; i++) {
3434+
xudc->usb3_phy[i] = NULL;
3435+
xudc->utmi_phy[i] = NULL;
3436+
xudc->usbphy[i] = NULL;
3437+
}
33473438

3348-
exit_utmi_phy:
3349-
phy_exit(xudc->utmi_phy);
33503439
return err;
33513440
}
33523441

33533442
static void tegra_xudc_phy_exit(struct tegra_xudc *xudc)
33543443
{
3355-
phy_exit(xudc->usb3_phy);
3356-
phy_exit(xudc->utmi_phy);
3444+
unsigned int i;
3445+
3446+
for (i = 0; i < xudc->soc->num_phys; i++) {
3447+
phy_exit(xudc->usb3_phy[i]);
3448+
phy_exit(xudc->utmi_phy[i]);
3449+
}
3450+
}
3451+
3452+
static int tegra_xudc_phy_init(struct tegra_xudc *xudc)
3453+
{
3454+
int err;
3455+
unsigned int i;
3456+
3457+
for (i = 0; i < xudc->soc->num_phys; i++) {
3458+
err = phy_init(xudc->utmi_phy[i]);
3459+
if (err < 0) {
3460+
dev_err(xudc->dev, "utmi phy init failed: %d\n", err);
3461+
goto exit_phy;
3462+
}
3463+
3464+
err = phy_init(xudc->usb3_phy[i]);
3465+
if (err < 0) {
3466+
dev_err(xudc->dev, "usb3 phy init failed: %d\n", err);
3467+
goto exit_phy;
3468+
}
3469+
}
3470+
return 0;
3471+
3472+
exit_phy:
3473+
tegra_xudc_phy_exit(xudc);
3474+
return err;
33573475
}
33583476

33593477
static const char * const tegra210_xudc_supply_names[] = {
@@ -3381,6 +3499,7 @@ static struct tegra_xudc_soc tegra210_xudc_soc_data = {
33813499
.num_supplies = ARRAY_SIZE(tegra210_xudc_supply_names),
33823500
.clock_names = tegra210_xudc_clock_names,
33833501
.num_clks = ARRAY_SIZE(tegra210_xudc_clock_names),
3502+
.num_phys = 4,
33843503
.u1_enable = false,
33853504
.u2_enable = true,
33863505
.lpm_enable = false,
@@ -3393,6 +3512,7 @@ static struct tegra_xudc_soc tegra210_xudc_soc_data = {
33933512
static struct tegra_xudc_soc tegra186_xudc_soc_data = {
33943513
.clock_names = tegra186_xudc_clock_names,
33953514
.num_clks = ARRAY_SIZE(tegra186_xudc_clock_names),
3515+
.num_phys = 4,
33963516
.u1_enable = true,
33973517
.u2_enable = true,
33983518
.lpm_enable = false,
@@ -3555,19 +3675,9 @@ static int tegra_xudc_probe(struct platform_device *pdev)
35553675
goto put_padctl;
35563676
}
35573677

3558-
xudc->usb3_phy = devm_phy_optional_get(&pdev->dev, "usb3");
3559-
if (IS_ERR(xudc->usb3_phy)) {
3560-
err = PTR_ERR(xudc->usb3_phy);
3561-
dev_err(xudc->dev, "failed to get usb3 phy: %d\n", err);
3562-
goto disable_regulator;
3563-
}
3564-
3565-
xudc->utmi_phy = devm_phy_optional_get(&pdev->dev, "usb2");
3566-
if (IS_ERR(xudc->utmi_phy)) {
3567-
err = PTR_ERR(xudc->utmi_phy);
3568-
dev_err(xudc->dev, "failed to get usb2 phy: %d\n", err);
3678+
err = tegra_xudc_phy_get(xudc);
3679+
if (err)
35693680
goto disable_regulator;
3570-
}
35713681

35723682
err = tegra_xudc_powerdomain_init(xudc);
35733683
if (err)
@@ -3596,16 +3706,6 @@ static int tegra_xudc_probe(struct platform_device *pdev)
35963706
INIT_DELAYED_WORK(&xudc->port_reset_war_work,
35973707
tegra_xudc_port_reset_war_work);
35983708

3599-
xudc->vbus_nb.notifier_call = tegra_xudc_vbus_notify;
3600-
xudc->usbphy = devm_usb_get_phy_by_node(xudc->dev,
3601-
xudc->utmi_phy->dev.of_node,
3602-
&xudc->vbus_nb);
3603-
if (IS_ERR(xudc->usbphy)) {
3604-
err = PTR_ERR(xudc->usbphy);
3605-
dev_err(xudc->dev, "failed to get USB PHY: %d\n", err);
3606-
goto free_eps;
3607-
}
3608-
36093709
pm_runtime_enable(&pdev->dev);
36103710

36113711
xudc->gadget.ops = &tegra_xudc_gadget_ops;
@@ -3640,6 +3740,7 @@ static int tegra_xudc_probe(struct platform_device *pdev)
36403740
static int tegra_xudc_remove(struct platform_device *pdev)
36413741
{
36423742
struct tegra_xudc *xudc = platform_get_drvdata(pdev);
3743+
unsigned int i;
36433744

36443745
pm_runtime_get_sync(xudc->dev);
36453746

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

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

3658-
phy_power_off(xudc->utmi_phy);
3659-
phy_power_off(xudc->usb3_phy);
3759+
for (i = 0; i < xudc->soc->num_phys; i++) {
3760+
phy_power_off(xudc->utmi_phy[i]);
3761+
phy_power_off(xudc->usb3_phy[i]);
3762+
}
36603763

36613764
tegra_xudc_phy_exit(xudc);
36623765

0 commit comments

Comments
 (0)