18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/delay.h> 78c2ecf20Sopenharmony_ci#include <linux/io.h> 88c2ecf20Sopenharmony_ci#include <linux/mailbox_client.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/of.h> 118c2ecf20Sopenharmony_ci#include <linux/of_device.h> 128c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 138c2ecf20Sopenharmony_ci#include <linux/phy/tegra/xusb.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 168c2ecf20Sopenharmony_ci#include <linux/reset.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <soc/tegra/fuse.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "xusb.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic struct phy *tegra_xusb_pad_of_xlate(struct device *dev, 258c2ecf20Sopenharmony_ci struct of_phandle_args *args) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct tegra_xusb_pad *pad = dev_get_drvdata(dev); 288c2ecf20Sopenharmony_ci struct phy *phy = NULL; 298c2ecf20Sopenharmony_ci unsigned int i; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci if (args->args_count != 0) 328c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci for (i = 0; i < pad->soc->num_lanes; i++) { 358c2ecf20Sopenharmony_ci if (!pad->lanes[i]) 368c2ecf20Sopenharmony_ci continue; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (pad->lanes[i]->dev.of_node == args->np) { 398c2ecf20Sopenharmony_ci phy = pad->lanes[i]; 408c2ecf20Sopenharmony_ci break; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (phy == NULL) 458c2ecf20Sopenharmony_ci phy = ERR_PTR(-ENODEV); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci return phy; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic const struct of_device_id tegra_xusb_padctl_of_match[] = { 518c2ecf20Sopenharmony_ci#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC) 528c2ecf20Sopenharmony_ci { 538c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra124-xusb-padctl", 548c2ecf20Sopenharmony_ci .data = &tegra124_xusb_padctl_soc, 558c2ecf20Sopenharmony_ci }, 568c2ecf20Sopenharmony_ci#endif 578c2ecf20Sopenharmony_ci#if defined(CONFIG_ARCH_TEGRA_210_SOC) 588c2ecf20Sopenharmony_ci { 598c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra210-xusb-padctl", 608c2ecf20Sopenharmony_ci .data = &tegra210_xusb_padctl_soc, 618c2ecf20Sopenharmony_ci }, 628c2ecf20Sopenharmony_ci#endif 638c2ecf20Sopenharmony_ci#if defined(CONFIG_ARCH_TEGRA_186_SOC) 648c2ecf20Sopenharmony_ci { 658c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra186-xusb-padctl", 668c2ecf20Sopenharmony_ci .data = &tegra186_xusb_padctl_soc, 678c2ecf20Sopenharmony_ci }, 688c2ecf20Sopenharmony_ci#endif 698c2ecf20Sopenharmony_ci#if defined(CONFIG_ARCH_TEGRA_194_SOC) 708c2ecf20Sopenharmony_ci { 718c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra194-xusb-padctl", 728c2ecf20Sopenharmony_ci .data = &tegra194_xusb_padctl_soc, 738c2ecf20Sopenharmony_ci }, 748c2ecf20Sopenharmony_ci#endif 758c2ecf20Sopenharmony_ci { } 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic struct device_node * 808c2ecf20Sopenharmony_citegra_xusb_find_pad_node(struct tegra_xusb_padctl *padctl, const char *name) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct device_node *pads, *np; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci pads = of_get_child_by_name(padctl->dev->of_node, "pads"); 858c2ecf20Sopenharmony_ci if (!pads) 868c2ecf20Sopenharmony_ci return NULL; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci np = of_get_child_by_name(pads, name); 898c2ecf20Sopenharmony_ci of_node_put(pads); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return np; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic struct device_node * 958c2ecf20Sopenharmony_citegra_xusb_pad_find_phy_node(struct tegra_xusb_pad *pad, unsigned int index) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct device_node *np, *lanes; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci lanes = of_get_child_by_name(pad->dev.of_node, "lanes"); 1008c2ecf20Sopenharmony_ci if (!lanes) 1018c2ecf20Sopenharmony_ci return NULL; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci np = of_get_child_by_name(lanes, pad->soc->lanes[index].name); 1048c2ecf20Sopenharmony_ci of_node_put(lanes); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return np; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ciint tegra_xusb_lane_parse_dt(struct tegra_xusb_lane *lane, 1108c2ecf20Sopenharmony_ci struct device_node *np) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct device *dev = &lane->pad->dev; 1138c2ecf20Sopenharmony_ci const char *function; 1148c2ecf20Sopenharmony_ci int err; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci err = of_property_read_string(np, "nvidia,function", &function); 1178c2ecf20Sopenharmony_ci if (err < 0) 1188c2ecf20Sopenharmony_ci return err; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci err = match_string(lane->soc->funcs, lane->soc->num_funcs, function); 1218c2ecf20Sopenharmony_ci if (err < 0) { 1228c2ecf20Sopenharmony_ci dev_err(dev, "invalid function \"%s\" for lane \"%pOFn\"\n", 1238c2ecf20Sopenharmony_ci function, np); 1248c2ecf20Sopenharmony_ci return err; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci lane->function = err; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void tegra_xusb_lane_destroy(struct phy *phy) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci if (phy) { 1358c2ecf20Sopenharmony_ci struct tegra_xusb_lane *lane = phy_get_drvdata(phy); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci lane->pad->ops->remove(lane); 1388c2ecf20Sopenharmony_ci phy_destroy(phy); 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void tegra_xusb_pad_release(struct device *dev) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct tegra_xusb_pad *pad = to_tegra_xusb_pad(dev); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci pad->soc->ops->remove(pad); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic struct device_type tegra_xusb_pad_type = { 1508c2ecf20Sopenharmony_ci .release = tegra_xusb_pad_release, 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ciint tegra_xusb_pad_init(struct tegra_xusb_pad *pad, 1548c2ecf20Sopenharmony_ci struct tegra_xusb_padctl *padctl, 1558c2ecf20Sopenharmony_ci struct device_node *np) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci int err; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci device_initialize(&pad->dev); 1608c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pad->list); 1618c2ecf20Sopenharmony_ci pad->dev.parent = padctl->dev; 1628c2ecf20Sopenharmony_ci pad->dev.type = &tegra_xusb_pad_type; 1638c2ecf20Sopenharmony_ci pad->dev.of_node = np; 1648c2ecf20Sopenharmony_ci pad->padctl = padctl; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci err = dev_set_name(&pad->dev, "%s", pad->soc->name); 1678c2ecf20Sopenharmony_ci if (err < 0) 1688c2ecf20Sopenharmony_ci goto unregister; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci err = device_add(&pad->dev); 1718c2ecf20Sopenharmony_ci if (err < 0) 1728c2ecf20Sopenharmony_ci goto unregister; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ciunregister: 1778c2ecf20Sopenharmony_ci device_unregister(&pad->dev); 1788c2ecf20Sopenharmony_ci return err; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ciint tegra_xusb_pad_register(struct tegra_xusb_pad *pad, 1828c2ecf20Sopenharmony_ci const struct phy_ops *ops) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct device_node *children; 1858c2ecf20Sopenharmony_ci struct phy *lane; 1868c2ecf20Sopenharmony_ci unsigned int i; 1878c2ecf20Sopenharmony_ci int err; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci children = of_get_child_by_name(pad->dev.of_node, "lanes"); 1908c2ecf20Sopenharmony_ci if (!children) 1918c2ecf20Sopenharmony_ci return -ENODEV; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci pad->lanes = devm_kcalloc(&pad->dev, pad->soc->num_lanes, sizeof(lane), 1948c2ecf20Sopenharmony_ci GFP_KERNEL); 1958c2ecf20Sopenharmony_ci if (!pad->lanes) { 1968c2ecf20Sopenharmony_ci of_node_put(children); 1978c2ecf20Sopenharmony_ci return -ENOMEM; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci for (i = 0; i < pad->soc->num_lanes; i++) { 2018c2ecf20Sopenharmony_ci struct device_node *np = tegra_xusb_pad_find_phy_node(pad, i); 2028c2ecf20Sopenharmony_ci struct tegra_xusb_lane *lane; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* skip disabled lanes */ 2058c2ecf20Sopenharmony_ci if (!np || !of_device_is_available(np)) { 2068c2ecf20Sopenharmony_ci of_node_put(np); 2078c2ecf20Sopenharmony_ci continue; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci pad->lanes[i] = phy_create(&pad->dev, np, ops); 2118c2ecf20Sopenharmony_ci if (IS_ERR(pad->lanes[i])) { 2128c2ecf20Sopenharmony_ci err = PTR_ERR(pad->lanes[i]); 2138c2ecf20Sopenharmony_ci of_node_put(np); 2148c2ecf20Sopenharmony_ci goto remove; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci lane = pad->ops->probe(pad, np, i); 2188c2ecf20Sopenharmony_ci if (IS_ERR(lane)) { 2198c2ecf20Sopenharmony_ci phy_destroy(pad->lanes[i]); 2208c2ecf20Sopenharmony_ci err = PTR_ERR(lane); 2218c2ecf20Sopenharmony_ci goto remove; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci list_add_tail(&lane->list, &pad->padctl->lanes); 2258c2ecf20Sopenharmony_ci phy_set_drvdata(pad->lanes[i], lane); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci pad->provider = of_phy_provider_register_full(&pad->dev, children, 2298c2ecf20Sopenharmony_ci tegra_xusb_pad_of_xlate); 2308c2ecf20Sopenharmony_ci if (IS_ERR(pad->provider)) { 2318c2ecf20Sopenharmony_ci err = PTR_ERR(pad->provider); 2328c2ecf20Sopenharmony_ci goto remove; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ciremove: 2388c2ecf20Sopenharmony_ci while (i--) 2398c2ecf20Sopenharmony_ci tegra_xusb_lane_destroy(pad->lanes[i]); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci of_node_put(children); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci return err; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_civoid tegra_xusb_pad_unregister(struct tegra_xusb_pad *pad) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci unsigned int i = pad->soc->num_lanes; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci of_phy_provider_unregister(pad->provider); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci while (i--) 2538c2ecf20Sopenharmony_ci tegra_xusb_lane_destroy(pad->lanes[i]); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci device_unregister(&pad->dev); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic struct tegra_xusb_pad * 2598c2ecf20Sopenharmony_citegra_xusb_pad_create(struct tegra_xusb_padctl *padctl, 2608c2ecf20Sopenharmony_ci const struct tegra_xusb_pad_soc *soc) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct tegra_xusb_pad *pad; 2638c2ecf20Sopenharmony_ci struct device_node *np; 2648c2ecf20Sopenharmony_ci int err; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci np = tegra_xusb_find_pad_node(padctl, soc->name); 2678c2ecf20Sopenharmony_ci if (!np || !of_device_is_available(np)) 2688c2ecf20Sopenharmony_ci return NULL; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci pad = soc->ops->probe(padctl, soc, np); 2718c2ecf20Sopenharmony_ci if (IS_ERR(pad)) { 2728c2ecf20Sopenharmony_ci err = PTR_ERR(pad); 2738c2ecf20Sopenharmony_ci dev_err(padctl->dev, "failed to create pad %s: %d\n", 2748c2ecf20Sopenharmony_ci soc->name, err); 2758c2ecf20Sopenharmony_ci return ERR_PTR(err); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* XXX move this into ->probe() to avoid string comparison */ 2798c2ecf20Sopenharmony_ci if (strcmp(soc->name, "pcie") == 0) 2808c2ecf20Sopenharmony_ci padctl->pcie = pad; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (strcmp(soc->name, "sata") == 0) 2838c2ecf20Sopenharmony_ci padctl->sata = pad; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (strcmp(soc->name, "usb2") == 0) 2868c2ecf20Sopenharmony_ci padctl->usb2 = pad; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (strcmp(soc->name, "ulpi") == 0) 2898c2ecf20Sopenharmony_ci padctl->ulpi = pad; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (strcmp(soc->name, "hsic") == 0) 2928c2ecf20Sopenharmony_ci padctl->hsic = pad; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return pad; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic void __tegra_xusb_remove_pads(struct tegra_xusb_padctl *padctl) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct tegra_xusb_pad *pad, *tmp; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci list_for_each_entry_safe_reverse(pad, tmp, &padctl->pads, list) { 3028c2ecf20Sopenharmony_ci list_del(&pad->list); 3038c2ecf20Sopenharmony_ci tegra_xusb_pad_unregister(pad); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic void tegra_xusb_remove_pads(struct tegra_xusb_padctl *padctl) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci mutex_lock(&padctl->lock); 3108c2ecf20Sopenharmony_ci __tegra_xusb_remove_pads(padctl); 3118c2ecf20Sopenharmony_ci mutex_unlock(&padctl->lock); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cistatic void tegra_xusb_lane_program(struct tegra_xusb_lane *lane) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct tegra_xusb_padctl *padctl = lane->pad->padctl; 3178c2ecf20Sopenharmony_ci const struct tegra_xusb_lane_soc *soc = lane->soc; 3188c2ecf20Sopenharmony_ci u32 value; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* skip single function lanes */ 3218c2ecf20Sopenharmony_ci if (soc->num_funcs < 2) 3228c2ecf20Sopenharmony_ci return; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* choose function */ 3258c2ecf20Sopenharmony_ci value = padctl_readl(padctl, soc->offset); 3268c2ecf20Sopenharmony_ci value &= ~(soc->mask << soc->shift); 3278c2ecf20Sopenharmony_ci value |= lane->function << soc->shift; 3288c2ecf20Sopenharmony_ci padctl_writel(padctl, value, soc->offset); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic void tegra_xusb_pad_program(struct tegra_xusb_pad *pad) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci unsigned int i; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci for (i = 0; i < pad->soc->num_lanes; i++) { 3368c2ecf20Sopenharmony_ci struct tegra_xusb_lane *lane; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (pad->lanes[i]) { 3398c2ecf20Sopenharmony_ci lane = phy_get_drvdata(pad->lanes[i]); 3408c2ecf20Sopenharmony_ci tegra_xusb_lane_program(lane); 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic int tegra_xusb_setup_pads(struct tegra_xusb_padctl *padctl) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct tegra_xusb_pad *pad; 3488c2ecf20Sopenharmony_ci unsigned int i; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci mutex_lock(&padctl->lock); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci for (i = 0; i < padctl->soc->num_pads; i++) { 3538c2ecf20Sopenharmony_ci const struct tegra_xusb_pad_soc *soc = padctl->soc->pads[i]; 3548c2ecf20Sopenharmony_ci int err; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci pad = tegra_xusb_pad_create(padctl, soc); 3578c2ecf20Sopenharmony_ci if (IS_ERR(pad)) { 3588c2ecf20Sopenharmony_ci err = PTR_ERR(pad); 3598c2ecf20Sopenharmony_ci dev_err(padctl->dev, "failed to create pad %s: %d\n", 3608c2ecf20Sopenharmony_ci soc->name, err); 3618c2ecf20Sopenharmony_ci __tegra_xusb_remove_pads(padctl); 3628c2ecf20Sopenharmony_ci mutex_unlock(&padctl->lock); 3638c2ecf20Sopenharmony_ci return err; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (!pad) 3678c2ecf20Sopenharmony_ci continue; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci list_add_tail(&pad->list, &padctl->pads); 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci list_for_each_entry(pad, &padctl->pads, list) 3738c2ecf20Sopenharmony_ci tegra_xusb_pad_program(pad); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci mutex_unlock(&padctl->lock); 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane, 3808c2ecf20Sopenharmony_ci const char *function) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci const char *func = lane->soc->funcs[lane->function]; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci return strcmp(function, func) == 0; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistruct tegra_xusb_lane *tegra_xusb_find_lane(struct tegra_xusb_padctl *padctl, 3888c2ecf20Sopenharmony_ci const char *type, 3898c2ecf20Sopenharmony_ci unsigned int index) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct tegra_xusb_lane *lane, *hit = ERR_PTR(-ENODEV); 3928c2ecf20Sopenharmony_ci char *name; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%s-%u", type, index); 3958c2ecf20Sopenharmony_ci if (!name) 3968c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci list_for_each_entry(lane, &padctl->lanes, list) { 3998c2ecf20Sopenharmony_ci if (strcmp(lane->soc->name, name) == 0) { 4008c2ecf20Sopenharmony_ci hit = lane; 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci kfree(name); 4068c2ecf20Sopenharmony_ci return hit; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistruct tegra_xusb_lane * 4108c2ecf20Sopenharmony_citegra_xusb_port_find_lane(struct tegra_xusb_port *port, 4118c2ecf20Sopenharmony_ci const struct tegra_xusb_lane_map *map, 4128c2ecf20Sopenharmony_ci const char *function) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct tegra_xusb_lane *lane, *match = ERR_PTR(-ENODEV); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci for (; map->type; map++) { 4178c2ecf20Sopenharmony_ci if (port->index != map->port) 4188c2ecf20Sopenharmony_ci continue; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci lane = tegra_xusb_find_lane(port->padctl, map->type, 4218c2ecf20Sopenharmony_ci map->index); 4228c2ecf20Sopenharmony_ci if (IS_ERR(lane)) 4238c2ecf20Sopenharmony_ci continue; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (!tegra_xusb_lane_check(lane, function)) 4268c2ecf20Sopenharmony_ci continue; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (!IS_ERR(match)) 4298c2ecf20Sopenharmony_ci dev_err(&port->dev, "conflicting match: %s-%u / %s\n", 4308c2ecf20Sopenharmony_ci map->type, map->index, match->soc->name); 4318c2ecf20Sopenharmony_ci else 4328c2ecf20Sopenharmony_ci match = lane; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return match; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic struct device_node * 4398c2ecf20Sopenharmony_citegra_xusb_find_port_node(struct tegra_xusb_padctl *padctl, const char *type, 4408c2ecf20Sopenharmony_ci unsigned int index) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci struct device_node *ports, *np; 4438c2ecf20Sopenharmony_ci char *name; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci ports = of_get_child_by_name(padctl->dev->of_node, "ports"); 4468c2ecf20Sopenharmony_ci if (!ports) 4478c2ecf20Sopenharmony_ci return NULL; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%s-%u", type, index); 4508c2ecf20Sopenharmony_ci if (!name) { 4518c2ecf20Sopenharmony_ci of_node_put(ports); 4528c2ecf20Sopenharmony_ci return NULL; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci np = of_get_child_by_name(ports, name); 4558c2ecf20Sopenharmony_ci kfree(name); 4568c2ecf20Sopenharmony_ci of_node_put(ports); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci return np; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistruct tegra_xusb_port * 4628c2ecf20Sopenharmony_citegra_xusb_find_port(struct tegra_xusb_padctl *padctl, const char *type, 4638c2ecf20Sopenharmony_ci unsigned int index) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct tegra_xusb_port *port; 4668c2ecf20Sopenharmony_ci struct device_node *np; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci np = tegra_xusb_find_port_node(padctl, type, index); 4698c2ecf20Sopenharmony_ci if (!np) 4708c2ecf20Sopenharmony_ci return NULL; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci list_for_each_entry(port, &padctl->ports, list) { 4738c2ecf20Sopenharmony_ci if (np == port->dev.of_node) { 4748c2ecf20Sopenharmony_ci of_node_put(np); 4758c2ecf20Sopenharmony_ci return port; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci of_node_put(np); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return NULL; 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistruct tegra_xusb_usb2_port * 4858c2ecf20Sopenharmony_citegra_xusb_find_usb2_port(struct tegra_xusb_padctl *padctl, unsigned int index) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct tegra_xusb_port *port; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci port = tegra_xusb_find_port(padctl, "usb2", index); 4908c2ecf20Sopenharmony_ci if (port) 4918c2ecf20Sopenharmony_ci return to_usb2_port(port); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return NULL; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistruct tegra_xusb_usb3_port * 4978c2ecf20Sopenharmony_citegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl, unsigned int index) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct tegra_xusb_port *port; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci port = tegra_xusb_find_port(padctl, "usb3", index); 5028c2ecf20Sopenharmony_ci if (port) 5038c2ecf20Sopenharmony_ci return to_usb3_port(port); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci return NULL; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic void tegra_xusb_port_release(struct device *dev) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci struct tegra_xusb_port *port = to_tegra_xusb_port(dev); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (port->ops->release) 5138c2ecf20Sopenharmony_ci port->ops->release(port); 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic struct device_type tegra_xusb_port_type = { 5178c2ecf20Sopenharmony_ci .release = tegra_xusb_port_release, 5188c2ecf20Sopenharmony_ci}; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic int tegra_xusb_port_init(struct tegra_xusb_port *port, 5218c2ecf20Sopenharmony_ci struct tegra_xusb_padctl *padctl, 5228c2ecf20Sopenharmony_ci struct device_node *np, 5238c2ecf20Sopenharmony_ci const char *name, 5248c2ecf20Sopenharmony_ci unsigned int index) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci int err; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&port->list); 5298c2ecf20Sopenharmony_ci port->padctl = padctl; 5308c2ecf20Sopenharmony_ci port->index = index; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci device_initialize(&port->dev); 5338c2ecf20Sopenharmony_ci port->dev.type = &tegra_xusb_port_type; 5348c2ecf20Sopenharmony_ci port->dev.of_node = of_node_get(np); 5358c2ecf20Sopenharmony_ci port->dev.parent = padctl->dev; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci err = dev_set_name(&port->dev, "%s-%u", name, index); 5388c2ecf20Sopenharmony_ci if (err < 0) 5398c2ecf20Sopenharmony_ci goto unregister; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci err = device_add(&port->dev); 5428c2ecf20Sopenharmony_ci if (err < 0) 5438c2ecf20Sopenharmony_ci goto unregister; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci return 0; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ciunregister: 5488c2ecf20Sopenharmony_ci device_unregister(&port->dev); 5498c2ecf20Sopenharmony_ci return err; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic void tegra_xusb_port_unregister(struct tegra_xusb_port *port) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(port->usb_role_sw)) { 5558c2ecf20Sopenharmony_ci of_platform_depopulate(&port->dev); 5568c2ecf20Sopenharmony_ci usb_role_switch_unregister(port->usb_role_sw); 5578c2ecf20Sopenharmony_ci cancel_work_sync(&port->usb_phy_work); 5588c2ecf20Sopenharmony_ci usb_remove_phy(&port->usb_phy); 5598c2ecf20Sopenharmony_ci port->usb_phy.dev->driver = NULL; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (port->ops->remove) 5638c2ecf20Sopenharmony_ci port->ops->remove(port); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci device_unregister(&port->dev); 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic const char *const modes[] = { 5698c2ecf20Sopenharmony_ci [USB_DR_MODE_UNKNOWN] = "", 5708c2ecf20Sopenharmony_ci [USB_DR_MODE_HOST] = "host", 5718c2ecf20Sopenharmony_ci [USB_DR_MODE_PERIPHERAL] = "peripheral", 5728c2ecf20Sopenharmony_ci [USB_DR_MODE_OTG] = "otg", 5738c2ecf20Sopenharmony_ci}; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic const char * const usb_roles[] = { 5768c2ecf20Sopenharmony_ci [USB_ROLE_NONE] = "none", 5778c2ecf20Sopenharmony_ci [USB_ROLE_HOST] = "host", 5788c2ecf20Sopenharmony_ci [USB_ROLE_DEVICE] = "device", 5798c2ecf20Sopenharmony_ci}; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic enum usb_phy_events to_usb_phy_event(enum usb_role role) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci switch (role) { 5848c2ecf20Sopenharmony_ci case USB_ROLE_DEVICE: 5858c2ecf20Sopenharmony_ci return USB_EVENT_VBUS; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci case USB_ROLE_HOST: 5888c2ecf20Sopenharmony_ci return USB_EVENT_ID; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci default: 5918c2ecf20Sopenharmony_ci return USB_EVENT_NONE; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic void tegra_xusb_usb_phy_work(struct work_struct *work) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci struct tegra_xusb_port *port = container_of(work, 5988c2ecf20Sopenharmony_ci struct tegra_xusb_port, 5998c2ecf20Sopenharmony_ci usb_phy_work); 6008c2ecf20Sopenharmony_ci enum usb_role role = usb_role_switch_get_role(port->usb_role_sw); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci usb_phy_set_event(&port->usb_phy, to_usb_phy_event(role)); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s(): calling notifier for role %s\n", __func__, 6058c2ecf20Sopenharmony_ci usb_roles[role]); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci atomic_notifier_call_chain(&port->usb_phy.notifier, 0, &port->usb_phy); 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic int tegra_xusb_role_sw_set(struct usb_role_switch *sw, 6118c2ecf20Sopenharmony_ci enum usb_role role) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci struct tegra_xusb_port *port = usb_role_switch_get_drvdata(sw); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci dev_dbg(&port->dev, "%s(): role %s\n", __func__, usb_roles[role]); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci schedule_work(&port->usb_phy_work); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci return 0; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic int tegra_xusb_set_peripheral(struct usb_otg *otg, 6238c2ecf20Sopenharmony_ci struct usb_gadget *gadget) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct tegra_xusb_port *port = container_of(otg->usb_phy, 6268c2ecf20Sopenharmony_ci struct tegra_xusb_port, 6278c2ecf20Sopenharmony_ci usb_phy); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (gadget != NULL) 6308c2ecf20Sopenharmony_ci schedule_work(&port->usb_phy_work); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci return 0; 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic int tegra_xusb_set_host(struct usb_otg *otg, struct usb_bus *host) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci struct tegra_xusb_port *port = container_of(otg->usb_phy, 6388c2ecf20Sopenharmony_ci struct tegra_xusb_port, 6398c2ecf20Sopenharmony_ci usb_phy); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci if (host != NULL) 6428c2ecf20Sopenharmony_ci schedule_work(&port->usb_phy_work); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci return 0; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci struct tegra_xusb_lane *lane; 6518c2ecf20Sopenharmony_ci struct usb_role_switch_desc role_sx_desc = { 6528c2ecf20Sopenharmony_ci .fwnode = dev_fwnode(&port->dev), 6538c2ecf20Sopenharmony_ci .set = tegra_xusb_role_sw_set, 6548c2ecf20Sopenharmony_ci }; 6558c2ecf20Sopenharmony_ci int err = 0; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci /* 6588c2ecf20Sopenharmony_ci * USB role switch driver needs parent driver owner info. This is a 6598c2ecf20Sopenharmony_ci * suboptimal solution. TODO: Need to revisit this in a follow-up patch 6608c2ecf20Sopenharmony_ci * where an optimal solution is possible with changes to USB role 6618c2ecf20Sopenharmony_ci * switch driver. 6628c2ecf20Sopenharmony_ci */ 6638c2ecf20Sopenharmony_ci port->dev.driver = devm_kzalloc(&port->dev, 6648c2ecf20Sopenharmony_ci sizeof(struct device_driver), 6658c2ecf20Sopenharmony_ci GFP_KERNEL); 6668c2ecf20Sopenharmony_ci if (!port->dev.driver) 6678c2ecf20Sopenharmony_ci return -ENOMEM; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci port->dev.driver->owner = THIS_MODULE; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci port->usb_role_sw = usb_role_switch_register(&port->dev, 6728c2ecf20Sopenharmony_ci &role_sx_desc); 6738c2ecf20Sopenharmony_ci if (IS_ERR(port->usb_role_sw)) { 6748c2ecf20Sopenharmony_ci err = PTR_ERR(port->usb_role_sw); 6758c2ecf20Sopenharmony_ci dev_err(&port->dev, "failed to register USB role switch: %d", 6768c2ecf20Sopenharmony_ci err); 6778c2ecf20Sopenharmony_ci return err; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci INIT_WORK(&port->usb_phy_work, tegra_xusb_usb_phy_work); 6818c2ecf20Sopenharmony_ci usb_role_switch_set_drvdata(port->usb_role_sw, port); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci port->usb_phy.otg = devm_kzalloc(&port->dev, sizeof(struct usb_otg), 6848c2ecf20Sopenharmony_ci GFP_KERNEL); 6858c2ecf20Sopenharmony_ci if (!port->usb_phy.otg) 6868c2ecf20Sopenharmony_ci return -ENOMEM; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci lane = tegra_xusb_find_lane(port->padctl, "usb2", port->index); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci /* 6918c2ecf20Sopenharmony_ci * Assign phy dev to usb-phy dev. Host/device drivers can use phy 6928c2ecf20Sopenharmony_ci * reference to retrieve usb-phy details. 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_ci port->usb_phy.dev = &lane->pad->lanes[port->index]->dev; 6958c2ecf20Sopenharmony_ci port->usb_phy.dev->driver = port->dev.driver; 6968c2ecf20Sopenharmony_ci port->usb_phy.otg->usb_phy = &port->usb_phy; 6978c2ecf20Sopenharmony_ci port->usb_phy.otg->set_peripheral = tegra_xusb_set_peripheral; 6988c2ecf20Sopenharmony_ci port->usb_phy.otg->set_host = tegra_xusb_set_host; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci err = usb_add_phy_dev(&port->usb_phy); 7018c2ecf20Sopenharmony_ci if (err < 0) { 7028c2ecf20Sopenharmony_ci dev_err(&port->dev, "Failed to add USB PHY: %d\n", err); 7038c2ecf20Sopenharmony_ci return err; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci /* populate connector entry */ 7078c2ecf20Sopenharmony_ci of_platform_populate(port->dev.of_node, NULL, NULL, &port->dev); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci return err; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci struct tegra_xusb_port *port = &usb2->base; 7158c2ecf20Sopenharmony_ci struct device_node *np = port->dev.of_node; 7168c2ecf20Sopenharmony_ci const char *mode; 7178c2ecf20Sopenharmony_ci int err; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci usb2->internal = of_property_read_bool(np, "nvidia,internal"); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci if (!of_property_read_string(np, "mode", &mode)) { 7228c2ecf20Sopenharmony_ci int err = match_string(modes, ARRAY_SIZE(modes), mode); 7238c2ecf20Sopenharmony_ci if (err < 0) { 7248c2ecf20Sopenharmony_ci dev_err(&port->dev, "invalid value %s for \"mode\"\n", 7258c2ecf20Sopenharmony_ci mode); 7268c2ecf20Sopenharmony_ci usb2->mode = USB_DR_MODE_UNKNOWN; 7278c2ecf20Sopenharmony_ci } else { 7288c2ecf20Sopenharmony_ci usb2->mode = err; 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci } else { 7318c2ecf20Sopenharmony_ci usb2->mode = USB_DR_MODE_HOST; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci /* usb-role-switch property is mandatory for OTG/Peripheral modes */ 7358c2ecf20Sopenharmony_ci if (usb2->mode == USB_DR_MODE_PERIPHERAL || 7368c2ecf20Sopenharmony_ci usb2->mode == USB_DR_MODE_OTG) { 7378c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "usb-role-switch")) { 7388c2ecf20Sopenharmony_ci err = tegra_xusb_setup_usb_role_switch(port); 7398c2ecf20Sopenharmony_ci if (err < 0) 7408c2ecf20Sopenharmony_ci return err; 7418c2ecf20Sopenharmony_ci } else { 7428c2ecf20Sopenharmony_ci dev_err(&port->dev, "usb-role-switch not found for %s mode", 7438c2ecf20Sopenharmony_ci modes[usb2->mode]); 7448c2ecf20Sopenharmony_ci return -EINVAL; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci usb2->supply = regulator_get(&port->dev, "vbus"); 7498c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(usb2->supply); 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cistatic int tegra_xusb_add_usb2_port(struct tegra_xusb_padctl *padctl, 7538c2ecf20Sopenharmony_ci unsigned int index) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci struct tegra_xusb_usb2_port *usb2; 7568c2ecf20Sopenharmony_ci struct device_node *np; 7578c2ecf20Sopenharmony_ci int err = 0; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci /* 7608c2ecf20Sopenharmony_ci * USB2 ports don't require additional properties, but if the port is 7618c2ecf20Sopenharmony_ci * marked as disabled there is no reason to register it. 7628c2ecf20Sopenharmony_ci */ 7638c2ecf20Sopenharmony_ci np = tegra_xusb_find_port_node(padctl, "usb2", index); 7648c2ecf20Sopenharmony_ci if (!np || !of_device_is_available(np)) 7658c2ecf20Sopenharmony_ci goto out; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL); 7688c2ecf20Sopenharmony_ci if (!usb2) { 7698c2ecf20Sopenharmony_ci err = -ENOMEM; 7708c2ecf20Sopenharmony_ci goto out; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci err = tegra_xusb_port_init(&usb2->base, padctl, np, "usb2", index); 7748c2ecf20Sopenharmony_ci if (err < 0) 7758c2ecf20Sopenharmony_ci goto out; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci usb2->base.ops = padctl->soc->ports.usb2.ops; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci usb2->base.lane = usb2->base.ops->map(&usb2->base); 7808c2ecf20Sopenharmony_ci if (IS_ERR(usb2->base.lane)) { 7818c2ecf20Sopenharmony_ci err = PTR_ERR(usb2->base.lane); 7828c2ecf20Sopenharmony_ci tegra_xusb_port_unregister(&usb2->base); 7838c2ecf20Sopenharmony_ci goto out; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci err = tegra_xusb_usb2_port_parse_dt(usb2); 7878c2ecf20Sopenharmony_ci if (err < 0) { 7888c2ecf20Sopenharmony_ci tegra_xusb_port_unregister(&usb2->base); 7898c2ecf20Sopenharmony_ci goto out; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci list_add_tail(&usb2->base.list, &padctl->ports); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ciout: 7958c2ecf20Sopenharmony_ci of_node_put(np); 7968c2ecf20Sopenharmony_ci return err; 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_civoid tegra_xusb_usb2_port_release(struct tegra_xusb_port *port) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci struct tegra_xusb_usb2_port *usb2 = to_usb2_port(port); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci kfree(usb2); 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_civoid tegra_xusb_usb2_port_remove(struct tegra_xusb_port *port) 8078c2ecf20Sopenharmony_ci{ 8088c2ecf20Sopenharmony_ci struct tegra_xusb_usb2_port *usb2 = to_usb2_port(port); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci regulator_put(usb2->supply); 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic int tegra_xusb_ulpi_port_parse_dt(struct tegra_xusb_ulpi_port *ulpi) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci struct tegra_xusb_port *port = &ulpi->base; 8168c2ecf20Sopenharmony_ci struct device_node *np = port->dev.of_node; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci ulpi->internal = of_property_read_bool(np, "nvidia,internal"); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci return 0; 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic int tegra_xusb_add_ulpi_port(struct tegra_xusb_padctl *padctl, 8248c2ecf20Sopenharmony_ci unsigned int index) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci struct tegra_xusb_ulpi_port *ulpi; 8278c2ecf20Sopenharmony_ci struct device_node *np; 8288c2ecf20Sopenharmony_ci int err = 0; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci np = tegra_xusb_find_port_node(padctl, "ulpi", index); 8318c2ecf20Sopenharmony_ci if (!np || !of_device_is_available(np)) 8328c2ecf20Sopenharmony_ci goto out; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL); 8358c2ecf20Sopenharmony_ci if (!ulpi) { 8368c2ecf20Sopenharmony_ci err = -ENOMEM; 8378c2ecf20Sopenharmony_ci goto out; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci err = tegra_xusb_port_init(&ulpi->base, padctl, np, "ulpi", index); 8418c2ecf20Sopenharmony_ci if (err < 0) 8428c2ecf20Sopenharmony_ci goto out; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci ulpi->base.ops = padctl->soc->ports.ulpi.ops; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci ulpi->base.lane = ulpi->base.ops->map(&ulpi->base); 8478c2ecf20Sopenharmony_ci if (IS_ERR(ulpi->base.lane)) { 8488c2ecf20Sopenharmony_ci err = PTR_ERR(ulpi->base.lane); 8498c2ecf20Sopenharmony_ci tegra_xusb_port_unregister(&ulpi->base); 8508c2ecf20Sopenharmony_ci goto out; 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci err = tegra_xusb_ulpi_port_parse_dt(ulpi); 8548c2ecf20Sopenharmony_ci if (err < 0) { 8558c2ecf20Sopenharmony_ci tegra_xusb_port_unregister(&ulpi->base); 8568c2ecf20Sopenharmony_ci goto out; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci list_add_tail(&ulpi->base.list, &padctl->ports); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ciout: 8628c2ecf20Sopenharmony_ci of_node_put(np); 8638c2ecf20Sopenharmony_ci return err; 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_civoid tegra_xusb_ulpi_port_release(struct tegra_xusb_port *port) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci struct tegra_xusb_ulpi_port *ulpi = to_ulpi_port(port); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci kfree(ulpi); 8718c2ecf20Sopenharmony_ci} 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_cistatic int tegra_xusb_hsic_port_parse_dt(struct tegra_xusb_hsic_port *hsic) 8748c2ecf20Sopenharmony_ci{ 8758c2ecf20Sopenharmony_ci /* XXX */ 8768c2ecf20Sopenharmony_ci return 0; 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_cistatic int tegra_xusb_add_hsic_port(struct tegra_xusb_padctl *padctl, 8808c2ecf20Sopenharmony_ci unsigned int index) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci struct tegra_xusb_hsic_port *hsic; 8838c2ecf20Sopenharmony_ci struct device_node *np; 8848c2ecf20Sopenharmony_ci int err = 0; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci np = tegra_xusb_find_port_node(padctl, "hsic", index); 8878c2ecf20Sopenharmony_ci if (!np || !of_device_is_available(np)) 8888c2ecf20Sopenharmony_ci goto out; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci hsic = kzalloc(sizeof(*hsic), GFP_KERNEL); 8918c2ecf20Sopenharmony_ci if (!hsic) { 8928c2ecf20Sopenharmony_ci err = -ENOMEM; 8938c2ecf20Sopenharmony_ci goto out; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci err = tegra_xusb_port_init(&hsic->base, padctl, np, "hsic", index); 8978c2ecf20Sopenharmony_ci if (err < 0) 8988c2ecf20Sopenharmony_ci goto out; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci hsic->base.ops = padctl->soc->ports.hsic.ops; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci hsic->base.lane = hsic->base.ops->map(&hsic->base); 9038c2ecf20Sopenharmony_ci if (IS_ERR(hsic->base.lane)) { 9048c2ecf20Sopenharmony_ci err = PTR_ERR(hsic->base.lane); 9058c2ecf20Sopenharmony_ci goto out; 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci err = tegra_xusb_hsic_port_parse_dt(hsic); 9098c2ecf20Sopenharmony_ci if (err < 0) { 9108c2ecf20Sopenharmony_ci tegra_xusb_port_unregister(&hsic->base); 9118c2ecf20Sopenharmony_ci goto out; 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci list_add_tail(&hsic->base.list, &padctl->ports); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ciout: 9178c2ecf20Sopenharmony_ci of_node_put(np); 9188c2ecf20Sopenharmony_ci return err; 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_civoid tegra_xusb_hsic_port_release(struct tegra_xusb_port *port) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct tegra_xusb_hsic_port *hsic = to_hsic_port(port); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci kfree(hsic); 9268c2ecf20Sopenharmony_ci} 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_cistatic int tegra_xusb_usb3_port_parse_dt(struct tegra_xusb_usb3_port *usb3) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci struct tegra_xusb_port *port = &usb3->base; 9318c2ecf20Sopenharmony_ci struct device_node *np = port->dev.of_node; 9328c2ecf20Sopenharmony_ci enum usb_device_speed maximum_speed; 9338c2ecf20Sopenharmony_ci u32 value; 9348c2ecf20Sopenharmony_ci int err; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci err = of_property_read_u32(np, "nvidia,usb2-companion", &value); 9378c2ecf20Sopenharmony_ci if (err < 0) { 9388c2ecf20Sopenharmony_ci dev_err(&port->dev, "failed to read port: %d\n", err); 9398c2ecf20Sopenharmony_ci return err; 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci usb3->port = value; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci usb3->internal = of_property_read_bool(np, "nvidia,internal"); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci if (device_property_present(&port->dev, "maximum-speed")) { 9478c2ecf20Sopenharmony_ci maximum_speed = usb_get_maximum_speed(&port->dev); 9488c2ecf20Sopenharmony_ci if (maximum_speed == USB_SPEED_SUPER) 9498c2ecf20Sopenharmony_ci usb3->disable_gen2 = true; 9508c2ecf20Sopenharmony_ci else if (maximum_speed == USB_SPEED_SUPER_PLUS) 9518c2ecf20Sopenharmony_ci usb3->disable_gen2 = false; 9528c2ecf20Sopenharmony_ci else 9538c2ecf20Sopenharmony_ci return -EINVAL; 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci usb3->supply = regulator_get(&port->dev, "vbus"); 9578c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(usb3->supply); 9588c2ecf20Sopenharmony_ci} 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_cistatic int tegra_xusb_add_usb3_port(struct tegra_xusb_padctl *padctl, 9618c2ecf20Sopenharmony_ci unsigned int index) 9628c2ecf20Sopenharmony_ci{ 9638c2ecf20Sopenharmony_ci struct tegra_xusb_usb3_port *usb3; 9648c2ecf20Sopenharmony_ci struct device_node *np; 9658c2ecf20Sopenharmony_ci int err = 0; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci /* 9688c2ecf20Sopenharmony_ci * If there is no supplemental configuration in the device tree the 9698c2ecf20Sopenharmony_ci * port is unusable. But it is valid to configure only a single port, 9708c2ecf20Sopenharmony_ci * hence return 0 instead of an error to allow ports to be optional. 9718c2ecf20Sopenharmony_ci */ 9728c2ecf20Sopenharmony_ci np = tegra_xusb_find_port_node(padctl, "usb3", index); 9738c2ecf20Sopenharmony_ci if (!np || !of_device_is_available(np)) 9748c2ecf20Sopenharmony_ci goto out; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci usb3 = kzalloc(sizeof(*usb3), GFP_KERNEL); 9778c2ecf20Sopenharmony_ci if (!usb3) { 9788c2ecf20Sopenharmony_ci err = -ENOMEM; 9798c2ecf20Sopenharmony_ci goto out; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci err = tegra_xusb_port_init(&usb3->base, padctl, np, "usb3", index); 9838c2ecf20Sopenharmony_ci if (err < 0) 9848c2ecf20Sopenharmony_ci goto out; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci usb3->base.ops = padctl->soc->ports.usb3.ops; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci usb3->base.lane = usb3->base.ops->map(&usb3->base); 9898c2ecf20Sopenharmony_ci if (IS_ERR(usb3->base.lane)) { 9908c2ecf20Sopenharmony_ci err = PTR_ERR(usb3->base.lane); 9918c2ecf20Sopenharmony_ci goto out; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci err = tegra_xusb_usb3_port_parse_dt(usb3); 9958c2ecf20Sopenharmony_ci if (err < 0) { 9968c2ecf20Sopenharmony_ci tegra_xusb_port_unregister(&usb3->base); 9978c2ecf20Sopenharmony_ci goto out; 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci list_add_tail(&usb3->base.list, &padctl->ports); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ciout: 10038c2ecf20Sopenharmony_ci of_node_put(np); 10048c2ecf20Sopenharmony_ci return err; 10058c2ecf20Sopenharmony_ci} 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_civoid tegra_xusb_usb3_port_release(struct tegra_xusb_port *port) 10088c2ecf20Sopenharmony_ci{ 10098c2ecf20Sopenharmony_ci struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci kfree(usb3); 10128c2ecf20Sopenharmony_ci} 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_civoid tegra_xusb_usb3_port_remove(struct tegra_xusb_port *port) 10158c2ecf20Sopenharmony_ci{ 10168c2ecf20Sopenharmony_ci struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci regulator_put(usb3->supply); 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_cistatic void __tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci struct tegra_xusb_port *port, *tmp; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci list_for_each_entry_safe_reverse(port, tmp, &padctl->ports, list) { 10268c2ecf20Sopenharmony_ci list_del(&port->list); 10278c2ecf20Sopenharmony_ci tegra_xusb_port_unregister(port); 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci} 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_cistatic int tegra_xusb_find_unused_usb3_port(struct tegra_xusb_padctl *padctl) 10328c2ecf20Sopenharmony_ci{ 10338c2ecf20Sopenharmony_ci struct device_node *np; 10348c2ecf20Sopenharmony_ci unsigned int i; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci for (i = 0; i < padctl->soc->ports.usb3.count; i++) { 10378c2ecf20Sopenharmony_ci np = tegra_xusb_find_port_node(padctl, "usb3", i); 10388c2ecf20Sopenharmony_ci if (!np || !of_device_is_available(np)) 10398c2ecf20Sopenharmony_ci return i; 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci return -ENODEV; 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_cistatic bool tegra_xusb_port_is_companion(struct tegra_xusb_usb2_port *usb2) 10468c2ecf20Sopenharmony_ci{ 10478c2ecf20Sopenharmony_ci unsigned int i; 10488c2ecf20Sopenharmony_ci struct tegra_xusb_usb3_port *usb3; 10498c2ecf20Sopenharmony_ci struct tegra_xusb_padctl *padctl = usb2->base.padctl; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci for (i = 0; i < padctl->soc->ports.usb3.count; i++) { 10528c2ecf20Sopenharmony_ci usb3 = tegra_xusb_find_usb3_port(padctl, i); 10538c2ecf20Sopenharmony_ci if (usb3 && usb3->port == usb2->base.index) 10548c2ecf20Sopenharmony_ci return true; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci return false; 10588c2ecf20Sopenharmony_ci} 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_cistatic int tegra_xusb_update_usb3_fake_port(struct tegra_xusb_usb2_port *usb2) 10618c2ecf20Sopenharmony_ci{ 10628c2ecf20Sopenharmony_ci int fake; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci /* Disable usb3_port_fake usage by default and assign if needed */ 10658c2ecf20Sopenharmony_ci usb2->usb3_port_fake = -1; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if ((usb2->mode == USB_DR_MODE_OTG || 10688c2ecf20Sopenharmony_ci usb2->mode == USB_DR_MODE_PERIPHERAL) && 10698c2ecf20Sopenharmony_ci !tegra_xusb_port_is_companion(usb2)) { 10708c2ecf20Sopenharmony_ci fake = tegra_xusb_find_unused_usb3_port(usb2->base.padctl); 10718c2ecf20Sopenharmony_ci if (fake < 0) { 10728c2ecf20Sopenharmony_ci dev_err(&usb2->base.dev, "no unused USB3 ports available\n"); 10738c2ecf20Sopenharmony_ci return -ENODEV; 10748c2ecf20Sopenharmony_ci } 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci dev_dbg(&usb2->base.dev, "Found unused usb3 port: %d\n", fake); 10778c2ecf20Sopenharmony_ci usb2->usb3_port_fake = fake; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci return 0; 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_cistatic int tegra_xusb_setup_ports(struct tegra_xusb_padctl *padctl) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci struct tegra_xusb_port *port; 10868c2ecf20Sopenharmony_ci struct tegra_xusb_usb2_port *usb2; 10878c2ecf20Sopenharmony_ci unsigned int i; 10888c2ecf20Sopenharmony_ci int err = 0; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci mutex_lock(&padctl->lock); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci for (i = 0; i < padctl->soc->ports.usb2.count; i++) { 10938c2ecf20Sopenharmony_ci err = tegra_xusb_add_usb2_port(padctl, i); 10948c2ecf20Sopenharmony_ci if (err < 0) 10958c2ecf20Sopenharmony_ci goto remove_ports; 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci for (i = 0; i < padctl->soc->ports.ulpi.count; i++) { 10998c2ecf20Sopenharmony_ci err = tegra_xusb_add_ulpi_port(padctl, i); 11008c2ecf20Sopenharmony_ci if (err < 0) 11018c2ecf20Sopenharmony_ci goto remove_ports; 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci for (i = 0; i < padctl->soc->ports.hsic.count; i++) { 11058c2ecf20Sopenharmony_ci err = tegra_xusb_add_hsic_port(padctl, i); 11068c2ecf20Sopenharmony_ci if (err < 0) 11078c2ecf20Sopenharmony_ci goto remove_ports; 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci for (i = 0; i < padctl->soc->ports.usb3.count; i++) { 11118c2ecf20Sopenharmony_ci err = tegra_xusb_add_usb3_port(padctl, i); 11128c2ecf20Sopenharmony_ci if (err < 0) 11138c2ecf20Sopenharmony_ci goto remove_ports; 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci if (padctl->soc->need_fake_usb3_port) { 11178c2ecf20Sopenharmony_ci for (i = 0; i < padctl->soc->ports.usb2.count; i++) { 11188c2ecf20Sopenharmony_ci usb2 = tegra_xusb_find_usb2_port(padctl, i); 11198c2ecf20Sopenharmony_ci if (!usb2) 11208c2ecf20Sopenharmony_ci continue; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci err = tegra_xusb_update_usb3_fake_port(usb2); 11238c2ecf20Sopenharmony_ci if (err < 0) 11248c2ecf20Sopenharmony_ci goto remove_ports; 11258c2ecf20Sopenharmony_ci } 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci list_for_each_entry(port, &padctl->ports, list) { 11298c2ecf20Sopenharmony_ci err = port->ops->enable(port); 11308c2ecf20Sopenharmony_ci if (err < 0) 11318c2ecf20Sopenharmony_ci dev_err(padctl->dev, "failed to enable port %s: %d\n", 11328c2ecf20Sopenharmony_ci dev_name(&port->dev), err); 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci goto unlock; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ciremove_ports: 11388c2ecf20Sopenharmony_ci __tegra_xusb_remove_ports(padctl); 11398c2ecf20Sopenharmony_ciunlock: 11408c2ecf20Sopenharmony_ci mutex_unlock(&padctl->lock); 11418c2ecf20Sopenharmony_ci return err; 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_cistatic void tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl) 11458c2ecf20Sopenharmony_ci{ 11468c2ecf20Sopenharmony_ci mutex_lock(&padctl->lock); 11478c2ecf20Sopenharmony_ci __tegra_xusb_remove_ports(padctl); 11488c2ecf20Sopenharmony_ci mutex_unlock(&padctl->lock); 11498c2ecf20Sopenharmony_ci} 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_cistatic int tegra_xusb_padctl_probe(struct platform_device *pdev) 11528c2ecf20Sopenharmony_ci{ 11538c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 11548c2ecf20Sopenharmony_ci const struct tegra_xusb_padctl_soc *soc; 11558c2ecf20Sopenharmony_ci struct tegra_xusb_padctl *padctl; 11568c2ecf20Sopenharmony_ci const struct of_device_id *match; 11578c2ecf20Sopenharmony_ci struct resource *res; 11588c2ecf20Sopenharmony_ci int err; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci /* for backwards compatibility with old device trees */ 11618c2ecf20Sopenharmony_ci np = of_get_child_by_name(np, "pads"); 11628c2ecf20Sopenharmony_ci if (!np) { 11638c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "deprecated DT, using legacy driver\n"); 11648c2ecf20Sopenharmony_ci return tegra_xusb_padctl_legacy_probe(pdev); 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci of_node_put(np); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci match = of_match_node(tegra_xusb_padctl_of_match, pdev->dev.of_node); 11708c2ecf20Sopenharmony_ci soc = match->data; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci padctl = soc->ops->probe(&pdev->dev, soc); 11738c2ecf20Sopenharmony_ci if (IS_ERR(padctl)) 11748c2ecf20Sopenharmony_ci return PTR_ERR(padctl); 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, padctl); 11778c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&padctl->ports); 11788c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&padctl->lanes); 11798c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&padctl->pads); 11808c2ecf20Sopenharmony_ci mutex_init(&padctl->lock); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 11838c2ecf20Sopenharmony_ci padctl->regs = devm_ioremap_resource(&pdev->dev, res); 11848c2ecf20Sopenharmony_ci if (IS_ERR(padctl->regs)) { 11858c2ecf20Sopenharmony_ci err = PTR_ERR(padctl->regs); 11868c2ecf20Sopenharmony_ci goto remove; 11878c2ecf20Sopenharmony_ci } 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci padctl->rst = devm_reset_control_get(&pdev->dev, NULL); 11908c2ecf20Sopenharmony_ci if (IS_ERR(padctl->rst)) { 11918c2ecf20Sopenharmony_ci err = PTR_ERR(padctl->rst); 11928c2ecf20Sopenharmony_ci goto remove; 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci padctl->supplies = devm_kcalloc(&pdev->dev, padctl->soc->num_supplies, 11968c2ecf20Sopenharmony_ci sizeof(*padctl->supplies), GFP_KERNEL); 11978c2ecf20Sopenharmony_ci if (!padctl->supplies) { 11988c2ecf20Sopenharmony_ci err = -ENOMEM; 11998c2ecf20Sopenharmony_ci goto remove; 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci regulator_bulk_set_supply_names(padctl->supplies, 12038c2ecf20Sopenharmony_ci padctl->soc->supply_names, 12048c2ecf20Sopenharmony_ci padctl->soc->num_supplies); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci err = devm_regulator_bulk_get(&pdev->dev, padctl->soc->num_supplies, 12078c2ecf20Sopenharmony_ci padctl->supplies); 12088c2ecf20Sopenharmony_ci if (err < 0) { 12098c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get regulators: %d\n", err); 12108c2ecf20Sopenharmony_ci goto remove; 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci err = reset_control_deassert(padctl->rst); 12148c2ecf20Sopenharmony_ci if (err < 0) 12158c2ecf20Sopenharmony_ci goto remove; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci err = regulator_bulk_enable(padctl->soc->num_supplies, 12188c2ecf20Sopenharmony_ci padctl->supplies); 12198c2ecf20Sopenharmony_ci if (err < 0) { 12208c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to enable supplies: %d\n", err); 12218c2ecf20Sopenharmony_ci goto reset; 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci err = tegra_xusb_setup_pads(padctl); 12258c2ecf20Sopenharmony_ci if (err < 0) { 12268c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to setup pads: %d\n", err); 12278c2ecf20Sopenharmony_ci goto power_down; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci err = tegra_xusb_setup_ports(padctl); 12318c2ecf20Sopenharmony_ci if (err) { 12328c2ecf20Sopenharmony_ci const char *level = KERN_ERR; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci if (err == -EPROBE_DEFER) 12358c2ecf20Sopenharmony_ci level = KERN_DEBUG; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci dev_printk(level, &pdev->dev, 12388c2ecf20Sopenharmony_ci dev_fmt("failed to setup XUSB ports: %d\n"), err); 12398c2ecf20Sopenharmony_ci goto remove_pads; 12408c2ecf20Sopenharmony_ci } 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci return 0; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ciremove_pads: 12458c2ecf20Sopenharmony_ci tegra_xusb_remove_pads(padctl); 12468c2ecf20Sopenharmony_cipower_down: 12478c2ecf20Sopenharmony_ci regulator_bulk_disable(padctl->soc->num_supplies, padctl->supplies); 12488c2ecf20Sopenharmony_cireset: 12498c2ecf20Sopenharmony_ci reset_control_assert(padctl->rst); 12508c2ecf20Sopenharmony_ciremove: 12518c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, NULL); 12528c2ecf20Sopenharmony_ci soc->ops->remove(padctl); 12538c2ecf20Sopenharmony_ci return err; 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_cistatic int tegra_xusb_padctl_remove(struct platform_device *pdev) 12578c2ecf20Sopenharmony_ci{ 12588c2ecf20Sopenharmony_ci struct tegra_xusb_padctl *padctl = platform_get_drvdata(pdev); 12598c2ecf20Sopenharmony_ci int err; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci tegra_xusb_remove_ports(padctl); 12628c2ecf20Sopenharmony_ci tegra_xusb_remove_pads(padctl); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci err = regulator_bulk_disable(padctl->soc->num_supplies, 12658c2ecf20Sopenharmony_ci padctl->supplies); 12668c2ecf20Sopenharmony_ci if (err < 0) 12678c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to disable supplies: %d\n", err); 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci err = reset_control_assert(padctl->rst); 12708c2ecf20Sopenharmony_ci if (err < 0) 12718c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to assert reset: %d\n", err); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci padctl->soc->ops->remove(padctl); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci return err; 12768c2ecf20Sopenharmony_ci} 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_cistatic struct platform_driver tegra_xusb_padctl_driver = { 12798c2ecf20Sopenharmony_ci .driver = { 12808c2ecf20Sopenharmony_ci .name = "tegra-xusb-padctl", 12818c2ecf20Sopenharmony_ci .of_match_table = tegra_xusb_padctl_of_match, 12828c2ecf20Sopenharmony_ci }, 12838c2ecf20Sopenharmony_ci .probe = tegra_xusb_padctl_probe, 12848c2ecf20Sopenharmony_ci .remove = tegra_xusb_padctl_remove, 12858c2ecf20Sopenharmony_ci}; 12868c2ecf20Sopenharmony_cimodule_platform_driver(tegra_xusb_padctl_driver); 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_cistruct tegra_xusb_padctl *tegra_xusb_padctl_get(struct device *dev) 12898c2ecf20Sopenharmony_ci{ 12908c2ecf20Sopenharmony_ci struct tegra_xusb_padctl *padctl; 12918c2ecf20Sopenharmony_ci struct platform_device *pdev; 12928c2ecf20Sopenharmony_ci struct device_node *np; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci np = of_parse_phandle(dev->of_node, "nvidia,xusb-padctl", 0); 12958c2ecf20Sopenharmony_ci if (!np) 12968c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci /* 12998c2ecf20Sopenharmony_ci * This is slightly ugly. A better implementation would be to keep a 13008c2ecf20Sopenharmony_ci * registry of pad controllers, but since there will almost certainly 13018c2ecf20Sopenharmony_ci * only ever be one per SoC that would be a little overkill. 13028c2ecf20Sopenharmony_ci */ 13038c2ecf20Sopenharmony_ci pdev = of_find_device_by_node(np); 13048c2ecf20Sopenharmony_ci if (!pdev) { 13058c2ecf20Sopenharmony_ci of_node_put(np); 13068c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 13078c2ecf20Sopenharmony_ci } 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci of_node_put(np); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci padctl = platform_get_drvdata(pdev); 13128c2ecf20Sopenharmony_ci if (!padctl) { 13138c2ecf20Sopenharmony_ci put_device(&pdev->dev); 13148c2ecf20Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 13158c2ecf20Sopenharmony_ci } 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci return padctl; 13188c2ecf20Sopenharmony_ci} 13198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra_xusb_padctl_get); 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_civoid tegra_xusb_padctl_put(struct tegra_xusb_padctl *padctl) 13228c2ecf20Sopenharmony_ci{ 13238c2ecf20Sopenharmony_ci if (padctl) 13248c2ecf20Sopenharmony_ci put_device(padctl->dev); 13258c2ecf20Sopenharmony_ci} 13268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra_xusb_padctl_put); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ciint tegra_xusb_padctl_usb3_save_context(struct tegra_xusb_padctl *padctl, 13298c2ecf20Sopenharmony_ci unsigned int port) 13308c2ecf20Sopenharmony_ci{ 13318c2ecf20Sopenharmony_ci if (padctl->soc->ops->usb3_save_context) 13328c2ecf20Sopenharmony_ci return padctl->soc->ops->usb3_save_context(padctl, port); 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci return -ENOSYS; 13358c2ecf20Sopenharmony_ci} 13368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra_xusb_padctl_usb3_save_context); 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ciint tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl, 13398c2ecf20Sopenharmony_ci unsigned int port, bool idle) 13408c2ecf20Sopenharmony_ci{ 13418c2ecf20Sopenharmony_ci if (padctl->soc->ops->hsic_set_idle) 13428c2ecf20Sopenharmony_ci return padctl->soc->ops->hsic_set_idle(padctl, port, idle); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci return -ENOSYS; 13458c2ecf20Sopenharmony_ci} 13468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle); 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ciint tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl, 13498c2ecf20Sopenharmony_ci unsigned int port, bool enable) 13508c2ecf20Sopenharmony_ci{ 13518c2ecf20Sopenharmony_ci if (padctl->soc->ops->usb3_set_lfps_detect) 13528c2ecf20Sopenharmony_ci return padctl->soc->ops->usb3_set_lfps_detect(padctl, port, 13538c2ecf20Sopenharmony_ci enable); 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci return -ENOSYS; 13568c2ecf20Sopenharmony_ci} 13578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra_xusb_padctl_usb3_set_lfps_detect); 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ciint tegra_xusb_padctl_set_vbus_override(struct tegra_xusb_padctl *padctl, 13608c2ecf20Sopenharmony_ci bool val) 13618c2ecf20Sopenharmony_ci{ 13628c2ecf20Sopenharmony_ci if (padctl->soc->ops->vbus_override) 13638c2ecf20Sopenharmony_ci return padctl->soc->ops->vbus_override(padctl, val); 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci return -ENOTSUPP; 13668c2ecf20Sopenharmony_ci} 13678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra_xusb_padctl_set_vbus_override); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ciint tegra_phy_xusb_utmi_port_reset(struct phy *phy) 13708c2ecf20Sopenharmony_ci{ 13718c2ecf20Sopenharmony_ci struct tegra_xusb_lane *lane = phy_get_drvdata(phy); 13728c2ecf20Sopenharmony_ci struct tegra_xusb_padctl *padctl = lane->pad->padctl; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci if (padctl->soc->ops->utmi_port_reset) 13758c2ecf20Sopenharmony_ci return padctl->soc->ops->utmi_port_reset(phy); 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci return -ENOTSUPP; 13788c2ecf20Sopenharmony_ci} 13798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_port_reset); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ciint tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl, 13828c2ecf20Sopenharmony_ci unsigned int port) 13838c2ecf20Sopenharmony_ci{ 13848c2ecf20Sopenharmony_ci struct tegra_xusb_usb2_port *usb2; 13858c2ecf20Sopenharmony_ci struct tegra_xusb_usb3_port *usb3; 13868c2ecf20Sopenharmony_ci int i; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci usb2 = tegra_xusb_find_usb2_port(padctl, port); 13898c2ecf20Sopenharmony_ci if (!usb2) 13908c2ecf20Sopenharmony_ci return -EINVAL; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci for (i = 0; i < padctl->soc->ports.usb3.count; i++) { 13938c2ecf20Sopenharmony_ci usb3 = tegra_xusb_find_usb3_port(padctl, i); 13948c2ecf20Sopenharmony_ci if (usb3 && usb3->port == usb2->base.index) 13958c2ecf20Sopenharmony_ci return usb3->base.index; 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci return -ENODEV; 13998c2ecf20Sopenharmony_ci} 14008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tegra_xusb_padctl_get_usb3_companion); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); 14038c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Tegra XUSB Pad Controller driver"); 14048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1405