linux_kernel/drivers/phy/tegra/xusb.c
Marc Zyngier eb9c4dd9bd phy: tegra: xusb: Fix dangling pointer on probe failure
If, for some reason, the xusb PHY fails to probe, it leaves
a dangling pointer attached to the platform device structure.

This would normally be harmless, but the Tegra XHCI driver then
goes and extract that pointer from the PHY device. Things go
downhill from there:

    8.752082] [004d554e5145533c] address between user and kernel address ranges
[    8.752085] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[    8.752088] Modules linked in: max77620_regulator(E+) xhci_tegra(E+) sdhci_tegra(E+) xhci_hcd(E) sdhci_pltfm(E) cqhci(E) fixed(E) usbcore(E) scsi_mod(E) sdhci(E) host1x(E+)
[    8.752103] CPU: 4 PID: 158 Comm: systemd-udevd Tainted: G S      W   E     5.9.0-rc7-00298-gf6337624c4fe #1980
[    8.752105] Hardware name: NVIDIA Jetson TX2 Developer Kit (DT)
[    8.752108] pstate: 20000005 (nzCv daif -PAN -UAO BTYPE=--)
[    8.752115] pc : kobject_put+0x1c/0x21c
[    8.752120] lr : put_device+0x20/0x30
[    8.752121] sp : ffffffc012eb3840
[    8.752122] x29: ffffffc012eb3840 x28: ffffffc010e82638
[    8.752125] x27: ffffffc008d56440 x26: 0000000000000000
[    8.752128] x25: ffffff81eb508200 x24: 0000000000000000
[    8.752130] x23: ffffff81eb538800 x22: 0000000000000000
[    8.752132] x21: 00000000fffffdfb x20: ffffff81eb538810
[    8.752134] x19: 3d4d554e51455300 x18: 0000000000000020
[    8.752136] x17: ffffffc008d00270 x16: ffffffc008d00c94
[    8.752138] x15: 0000000000000004 x14: ffffff81ebd4ae90
[    8.752140] x13: 0000000000000000 x12: ffffff81eb86a4e8
[    8.752142] x11: ffffff81eb86a480 x10: ffffff81eb862fea
[    8.752144] x9 : ffffffc01055fb28 x8 : ffffff81eb86a4a8
[    8.752146] x7 : 0000000000000001 x6 : 0000000000000001
[    8.752148] x5 : ffffff81dff8bc38 x4 : 0000000000000000
[    8.752150] x3 : 0000000000000001 x2 : 0000000000000001
[    8.752152] x1 : 0000000000000002 x0 : 3d4d554e51455300
[    8.752155] Call trace:
[    8.752157]  kobject_put+0x1c/0x21c
[    8.752160]  put_device+0x20/0x30
[    8.752164]  tegra_xusb_padctl_put+0x24/0x3c
[    8.752170]  tegra_xusb_probe+0x8b0/0xd10 [xhci_tegra]
[    8.752174]  platform_drv_probe+0x60/0xb4
[    8.752176]  really_probe+0xf0/0x504
[    8.752179]  driver_probe_device+0x100/0x170
[    8.752181]  device_driver_attach+0xcc/0xd4
[    8.752183]  __driver_attach+0xb0/0x17c
[    8.752185]  bus_for_each_dev+0x7c/0xd4
[    8.752187]  driver_attach+0x30/0x3c
[    8.752189]  bus_add_driver+0x154/0x250
[    8.752191]  driver_register+0x84/0x140
[    8.752193]  __platform_driver_register+0x54/0x60
[    8.752197]  tegra_xusb_init+0x40/0x1000 [xhci_tegra]
[    8.752201]  do_one_initcall+0x54/0x2d0
[    8.752205]  do_init_module+0x68/0x29c
[    8.752207]  load_module+0x2178/0x26c0
[    8.752209]  __do_sys_finit_module+0xb0/0x120
[    8.752211]  __arm64_sys_finit_module+0x2c/0x40
[    8.752215]  el0_svc_common.constprop.0+0x80/0x240
[    8.752218]  do_el0_svc+0x30/0xa0
[    8.752220]  el0_svc+0x18/0x50
[    8.752223]  el0_sync_handler+0x90/0x318
[    8.752225]  el0_sync+0x158/0x180
[    8.752230] Code: a9bd7bfd 910003fd a90153f3 aa0003f3 (3940f000)
[    8.752232] ---[ end trace 90f6c89d62d85ff5 ]---

Reset the pointer on probe failure fixes the issue.

Fixes: 53d2a715c2 ("phy: Add Tegra XUSB pad controller support")
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20201013095820.311376-1-maz@kernel.org
Signed-off-by: Vinod Koul <vkoul@kernel.org>
2020-10-28 21:42:18 +05:30

1399 lines
32 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/mailbox_client.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/phy/tegra/xusb.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <soc/tegra/fuse.h>
#include "xusb.h"
static struct phy *tegra_xusb_pad_of_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct tegra_xusb_pad *pad = dev_get_drvdata(dev);
struct phy *phy = NULL;
unsigned int i;
if (args->args_count != 0)
return ERR_PTR(-EINVAL);
for (i = 0; i < pad->soc->num_lanes; i++) {
if (!pad->lanes[i])
continue;
if (pad->lanes[i]->dev.of_node == args->np) {
phy = pad->lanes[i];
break;
}
}
if (phy == NULL)
phy = ERR_PTR(-ENODEV);
return phy;
}
static const struct of_device_id tegra_xusb_padctl_of_match[] = {
#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC)
{
.compatible = "nvidia,tegra124-xusb-padctl",
.data = &tegra124_xusb_padctl_soc,
},
#endif
#if defined(CONFIG_ARCH_TEGRA_210_SOC)
{
.compatible = "nvidia,tegra210-xusb-padctl",
.data = &tegra210_xusb_padctl_soc,
},
#endif
#if defined(CONFIG_ARCH_TEGRA_186_SOC)
{
.compatible = "nvidia,tegra186-xusb-padctl",
.data = &tegra186_xusb_padctl_soc,
},
#endif
#if defined(CONFIG_ARCH_TEGRA_194_SOC)
{
.compatible = "nvidia,tegra194-xusb-padctl",
.data = &tegra194_xusb_padctl_soc,
},
#endif
{ }
};
MODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match);
static struct device_node *
tegra_xusb_find_pad_node(struct tegra_xusb_padctl *padctl, const char *name)
{
struct device_node *pads, *np;
pads = of_get_child_by_name(padctl->dev->of_node, "pads");
if (!pads)
return NULL;
np = of_get_child_by_name(pads, name);
of_node_put(pads);
return np;
}
static struct device_node *
tegra_xusb_pad_find_phy_node(struct tegra_xusb_pad *pad, unsigned int index)
{
struct device_node *np, *lanes;
lanes = of_get_child_by_name(pad->dev.of_node, "lanes");
if (!lanes)
return NULL;
np = of_get_child_by_name(lanes, pad->soc->lanes[index].name);
of_node_put(lanes);
return np;
}
int tegra_xusb_lane_parse_dt(struct tegra_xusb_lane *lane,
struct device_node *np)
{
struct device *dev = &lane->pad->dev;
const char *function;
int err;
err = of_property_read_string(np, "nvidia,function", &function);
if (err < 0)
return err;
err = match_string(lane->soc->funcs, lane->soc->num_funcs, function);
if (err < 0) {
dev_err(dev, "invalid function \"%s\" for lane \"%pOFn\"\n",
function, np);
return err;
}
lane->function = err;
return 0;
}
static void tegra_xusb_lane_destroy(struct phy *phy)
{
if (phy) {
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
lane->pad->ops->remove(lane);
phy_destroy(phy);
}
}
static void tegra_xusb_pad_release(struct device *dev)
{
struct tegra_xusb_pad *pad = to_tegra_xusb_pad(dev);
pad->soc->ops->remove(pad);
}
static struct device_type tegra_xusb_pad_type = {
.release = tegra_xusb_pad_release,
};
int tegra_xusb_pad_init(struct tegra_xusb_pad *pad,
struct tegra_xusb_padctl *padctl,
struct device_node *np)
{
int err;
device_initialize(&pad->dev);
INIT_LIST_HEAD(&pad->list);
pad->dev.parent = padctl->dev;
pad->dev.type = &tegra_xusb_pad_type;
pad->dev.of_node = np;
pad->padctl = padctl;
err = dev_set_name(&pad->dev, "%s", pad->soc->name);
if (err < 0)
goto unregister;
err = device_add(&pad->dev);
if (err < 0)
goto unregister;
return 0;
unregister:
device_unregister(&pad->dev);
return err;
}
int tegra_xusb_pad_register(struct tegra_xusb_pad *pad,
const struct phy_ops *ops)
{
struct device_node *children;
struct phy *lane;
unsigned int i;
int err;
children = of_get_child_by_name(pad->dev.of_node, "lanes");
if (!children)
return -ENODEV;
pad->lanes = devm_kcalloc(&pad->dev, pad->soc->num_lanes, sizeof(lane),
GFP_KERNEL);
if (!pad->lanes) {
of_node_put(children);
return -ENOMEM;
}
for (i = 0; i < pad->soc->num_lanes; i++) {
struct device_node *np = tegra_xusb_pad_find_phy_node(pad, i);
struct tegra_xusb_lane *lane;
/* skip disabled lanes */
if (!np || !of_device_is_available(np)) {
of_node_put(np);
continue;
}
pad->lanes[i] = phy_create(&pad->dev, np, ops);
if (IS_ERR(pad->lanes[i])) {
err = PTR_ERR(pad->lanes[i]);
of_node_put(np);
goto remove;
}
lane = pad->ops->probe(pad, np, i);
if (IS_ERR(lane)) {
phy_destroy(pad->lanes[i]);
err = PTR_ERR(lane);
goto remove;
}
list_add_tail(&lane->list, &pad->padctl->lanes);
phy_set_drvdata(pad->lanes[i], lane);
}
pad->provider = of_phy_provider_register_full(&pad->dev, children,
tegra_xusb_pad_of_xlate);
if (IS_ERR(pad->provider)) {
err = PTR_ERR(pad->provider);
goto remove;
}
return 0;
remove:
while (i--)
tegra_xusb_lane_destroy(pad->lanes[i]);
of_node_put(children);
return err;
}
void tegra_xusb_pad_unregister(struct tegra_xusb_pad *pad)
{
unsigned int i = pad->soc->num_lanes;
of_phy_provider_unregister(pad->provider);
while (i--)
tegra_xusb_lane_destroy(pad->lanes[i]);
device_unregister(&pad->dev);
}
static struct tegra_xusb_pad *
tegra_xusb_pad_create(struct tegra_xusb_padctl *padctl,
const struct tegra_xusb_pad_soc *soc)
{
struct tegra_xusb_pad *pad;
struct device_node *np;
int err;
np = tegra_xusb_find_pad_node(padctl, soc->name);
if (!np || !of_device_is_available(np))
return NULL;
pad = soc->ops->probe(padctl, soc, np);
if (IS_ERR(pad)) {
err = PTR_ERR(pad);
dev_err(padctl->dev, "failed to create pad %s: %d\n",
soc->name, err);
return ERR_PTR(err);
}
/* XXX move this into ->probe() to avoid string comparison */
if (strcmp(soc->name, "pcie") == 0)
padctl->pcie = pad;
if (strcmp(soc->name, "sata") == 0)
padctl->sata = pad;
if (strcmp(soc->name, "usb2") == 0)
padctl->usb2 = pad;
if (strcmp(soc->name, "ulpi") == 0)
padctl->ulpi = pad;
if (strcmp(soc->name, "hsic") == 0)
padctl->hsic = pad;
return pad;
}
static void __tegra_xusb_remove_pads(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_pad *pad, *tmp;
list_for_each_entry_safe_reverse(pad, tmp, &padctl->pads, list) {
list_del(&pad->list);
tegra_xusb_pad_unregister(pad);
}
}
static void tegra_xusb_remove_pads(struct tegra_xusb_padctl *padctl)
{
mutex_lock(&padctl->lock);
__tegra_xusb_remove_pads(padctl);
mutex_unlock(&padctl->lock);
}
static void tegra_xusb_lane_program(struct tegra_xusb_lane *lane)
{
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
const struct tegra_xusb_lane_soc *soc = lane->soc;
u32 value;
/* skip single function lanes */
if (soc->num_funcs < 2)
return;
/* choose function */
value = padctl_readl(padctl, soc->offset);
value &= ~(soc->mask << soc->shift);
value |= lane->function << soc->shift;
padctl_writel(padctl, value, soc->offset);
}
static void tegra_xusb_pad_program(struct tegra_xusb_pad *pad)
{
unsigned int i;
for (i = 0; i < pad->soc->num_lanes; i++) {
struct tegra_xusb_lane *lane;
if (pad->lanes[i]) {
lane = phy_get_drvdata(pad->lanes[i]);
tegra_xusb_lane_program(lane);
}
}
}
static int tegra_xusb_setup_pads(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_pad *pad;
unsigned int i;
mutex_lock(&padctl->lock);
for (i = 0; i < padctl->soc->num_pads; i++) {
const struct tegra_xusb_pad_soc *soc = padctl->soc->pads[i];
int err;
pad = tegra_xusb_pad_create(padctl, soc);
if (IS_ERR(pad)) {
err = PTR_ERR(pad);
dev_err(padctl->dev, "failed to create pad %s: %d\n",
soc->name, err);
__tegra_xusb_remove_pads(padctl);
mutex_unlock(&padctl->lock);
return err;
}
if (!pad)
continue;
list_add_tail(&pad->list, &padctl->pads);
}
list_for_each_entry(pad, &padctl->pads, list)
tegra_xusb_pad_program(pad);
mutex_unlock(&padctl->lock);
return 0;
}
static bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane,
const char *function)
{
const char *func = lane->soc->funcs[lane->function];
return strcmp(function, func) == 0;
}
struct tegra_xusb_lane *tegra_xusb_find_lane(struct tegra_xusb_padctl *padctl,
const char *type,
unsigned int index)
{
struct tegra_xusb_lane *lane, *hit = ERR_PTR(-ENODEV);
char *name;
name = kasprintf(GFP_KERNEL, "%s-%u", type, index);
if (!name)
return ERR_PTR(-ENOMEM);
list_for_each_entry(lane, &padctl->lanes, list) {
if (strcmp(lane->soc->name, name) == 0) {
hit = lane;
break;
}
}
kfree(name);
return hit;
}
struct tegra_xusb_lane *
tegra_xusb_port_find_lane(struct tegra_xusb_port *port,
const struct tegra_xusb_lane_map *map,
const char *function)
{
struct tegra_xusb_lane *lane, *match = ERR_PTR(-ENODEV);
for (; map->type; map++) {
if (port->index != map->port)
continue;
lane = tegra_xusb_find_lane(port->padctl, map->type,
map->index);
if (IS_ERR(lane))
continue;
if (!tegra_xusb_lane_check(lane, function))
continue;
if (!IS_ERR(match))
dev_err(&port->dev, "conflicting match: %s-%u / %s\n",
map->type, map->index, match->soc->name);
else
match = lane;
}
return match;
}
static struct device_node *
tegra_xusb_find_port_node(struct tegra_xusb_padctl *padctl, const char *type,
unsigned int index)
{
struct device_node *ports, *np;
char *name;
ports = of_get_child_by_name(padctl->dev->of_node, "ports");
if (!ports)
return NULL;
name = kasprintf(GFP_KERNEL, "%s-%u", type, index);
if (!name) {
of_node_put(ports);
return ERR_PTR(-ENOMEM);
}
np = of_get_child_by_name(ports, name);
kfree(name);
of_node_put(ports);
return np;
}
struct tegra_xusb_port *
tegra_xusb_find_port(struct tegra_xusb_padctl *padctl, const char *type,
unsigned int index)
{
struct tegra_xusb_port *port;
struct device_node *np;
np = tegra_xusb_find_port_node(padctl, type, index);
if (!np)
return NULL;
list_for_each_entry(port, &padctl->ports, list) {
if (np == port->dev.of_node) {
of_node_put(np);
return port;
}
}
of_node_put(np);
return NULL;
}
struct tegra_xusb_usb2_port *
tegra_xusb_find_usb2_port(struct tegra_xusb_padctl *padctl, unsigned int index)
{
struct tegra_xusb_port *port;
port = tegra_xusb_find_port(padctl, "usb2", index);
if (port)
return to_usb2_port(port);
return NULL;
}
struct tegra_xusb_usb3_port *
tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl, unsigned int index)
{
struct tegra_xusb_port *port;
port = tegra_xusb_find_port(padctl, "usb3", index);
if (port)
return to_usb3_port(port);
return NULL;
}
static void tegra_xusb_port_release(struct device *dev)
{
struct tegra_xusb_port *port = to_tegra_xusb_port(dev);
if (port->ops->release)
port->ops->release(port);
}
static struct device_type tegra_xusb_port_type = {
.release = tegra_xusb_port_release,
};
static int tegra_xusb_port_init(struct tegra_xusb_port *port,
struct tegra_xusb_padctl *padctl,
struct device_node *np,
const char *name,
unsigned int index)
{
int err;
INIT_LIST_HEAD(&port->list);
port->padctl = padctl;
port->index = index;
device_initialize(&port->dev);
port->dev.type = &tegra_xusb_port_type;
port->dev.of_node = of_node_get(np);
port->dev.parent = padctl->dev;
err = dev_set_name(&port->dev, "%s-%u", name, index);
if (err < 0)
goto unregister;
err = device_add(&port->dev);
if (err < 0)
goto unregister;
return 0;
unregister:
device_unregister(&port->dev);
return err;
}
static void tegra_xusb_port_unregister(struct tegra_xusb_port *port)
{
if (!IS_ERR_OR_NULL(port->usb_role_sw)) {
of_platform_depopulate(&port->dev);
usb_role_switch_unregister(port->usb_role_sw);
cancel_work_sync(&port->usb_phy_work);
usb_remove_phy(&port->usb_phy);
}
if (port->ops->remove)
port->ops->remove(port);
device_unregister(&port->dev);
}
static const char *const modes[] = {
[USB_DR_MODE_UNKNOWN] = "",
[USB_DR_MODE_HOST] = "host",
[USB_DR_MODE_PERIPHERAL] = "peripheral",
[USB_DR_MODE_OTG] = "otg",
};
static const char * const usb_roles[] = {
[USB_ROLE_NONE] = "none",
[USB_ROLE_HOST] = "host",
[USB_ROLE_DEVICE] = "device",
};
static enum usb_phy_events to_usb_phy_event(enum usb_role role)
{
switch (role) {
case USB_ROLE_DEVICE:
return USB_EVENT_VBUS;
case USB_ROLE_HOST:
return USB_EVENT_ID;
default:
return USB_EVENT_NONE;
}
}
static void tegra_xusb_usb_phy_work(struct work_struct *work)
{
struct tegra_xusb_port *port = container_of(work,
struct tegra_xusb_port,
usb_phy_work);
enum usb_role role = usb_role_switch_get_role(port->usb_role_sw);
usb_phy_set_event(&port->usb_phy, to_usb_phy_event(role));
dev_dbg(&port->dev, "%s(): calling notifier for role %s\n", __func__,
usb_roles[role]);
atomic_notifier_call_chain(&port->usb_phy.notifier, 0, &port->usb_phy);
}
static int tegra_xusb_role_sw_set(struct usb_role_switch *sw,
enum usb_role role)
{
struct tegra_xusb_port *port = usb_role_switch_get_drvdata(sw);
dev_dbg(&port->dev, "%s(): role %s\n", __func__, usb_roles[role]);
schedule_work(&port->usb_phy_work);
return 0;
}
static int tegra_xusb_set_peripheral(struct usb_otg *otg,
struct usb_gadget *gadget)
{
struct tegra_xusb_port *port = container_of(otg->usb_phy,
struct tegra_xusb_port,
usb_phy);
if (gadget != NULL)
schedule_work(&port->usb_phy_work);
return 0;
}
static int tegra_xusb_set_host(struct usb_otg *otg, struct usb_bus *host)
{
struct tegra_xusb_port *port = container_of(otg->usb_phy,
struct tegra_xusb_port,
usb_phy);
if (host != NULL)
schedule_work(&port->usb_phy_work);
return 0;
}
static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port)
{
struct tegra_xusb_lane *lane;
struct usb_role_switch_desc role_sx_desc = {
.fwnode = dev_fwnode(&port->dev),
.set = tegra_xusb_role_sw_set,
};
int err = 0;
/*
* USB role switch driver needs parent driver owner info. This is a
* suboptimal solution. TODO: Need to revisit this in a follow-up patch
* where an optimal solution is possible with changes to USB role
* switch driver.
*/
port->dev.driver = devm_kzalloc(&port->dev,
sizeof(struct device_driver),
GFP_KERNEL);
port->dev.driver->owner = THIS_MODULE;
port->usb_role_sw = usb_role_switch_register(&port->dev,
&role_sx_desc);
if (IS_ERR(port->usb_role_sw)) {
err = PTR_ERR(port->usb_role_sw);
dev_err(&port->dev, "failed to register USB role switch: %d",
err);
return err;
}
INIT_WORK(&port->usb_phy_work, tegra_xusb_usb_phy_work);
usb_role_switch_set_drvdata(port->usb_role_sw, port);
port->usb_phy.otg = devm_kzalloc(&port->dev, sizeof(struct usb_otg),
GFP_KERNEL);
if (!port->usb_phy.otg)
return -ENOMEM;
lane = tegra_xusb_find_lane(port->padctl, "usb2", port->index);
/*
* Assign phy dev to usb-phy dev. Host/device drivers can use phy
* reference to retrieve usb-phy details.
*/
port->usb_phy.dev = &lane->pad->lanes[port->index]->dev;
port->usb_phy.dev->driver = port->padctl->dev->driver;
port->usb_phy.otg->usb_phy = &port->usb_phy;
port->usb_phy.otg->set_peripheral = tegra_xusb_set_peripheral;
port->usb_phy.otg->set_host = tegra_xusb_set_host;
err = usb_add_phy_dev(&port->usb_phy);
if (err < 0) {
dev_err(&port->dev, "Failed to add USB PHY: %d\n", err);
return err;
}
/* populate connector entry */
of_platform_populate(port->dev.of_node, NULL, NULL, &port->dev);
return err;
}
static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2)
{
struct tegra_xusb_port *port = &usb2->base;
struct device_node *np = port->dev.of_node;
const char *mode;
int err;
usb2->internal = of_property_read_bool(np, "nvidia,internal");
if (!of_property_read_string(np, "mode", &mode)) {
int err = match_string(modes, ARRAY_SIZE(modes), mode);
if (err < 0) {
dev_err(&port->dev, "invalid value %s for \"mode\"\n",
mode);
usb2->mode = USB_DR_MODE_UNKNOWN;
} else {
usb2->mode = err;
}
} else {
usb2->mode = USB_DR_MODE_HOST;
}
/* usb-role-switch property is mandatory for OTG/Peripheral modes */
if (usb2->mode == USB_DR_MODE_PERIPHERAL ||
usb2->mode == USB_DR_MODE_OTG) {
if (of_property_read_bool(np, "usb-role-switch")) {
err = tegra_xusb_setup_usb_role_switch(port);
if (err < 0)
return err;
} else {
dev_err(&port->dev, "usb-role-switch not found for %s mode",
modes[usb2->mode]);
return -EINVAL;
}
}
usb2->supply = regulator_get(&port->dev, "vbus");
return PTR_ERR_OR_ZERO(usb2->supply);
}
static int tegra_xusb_add_usb2_port(struct tegra_xusb_padctl *padctl,
unsigned int index)
{
struct tegra_xusb_usb2_port *usb2;
struct device_node *np;
int err = 0;
/*
* USB2 ports don't require additional properties, but if the port is
* marked as disabled there is no reason to register it.
*/
np = tegra_xusb_find_port_node(padctl, "usb2", index);
if (!np || !of_device_is_available(np))
goto out;
usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL);
if (!usb2) {
err = -ENOMEM;
goto out;
}
err = tegra_xusb_port_init(&usb2->base, padctl, np, "usb2", index);
if (err < 0)
goto out;
usb2->base.ops = padctl->soc->ports.usb2.ops;
usb2->base.lane = usb2->base.ops->map(&usb2->base);
if (IS_ERR(usb2->base.lane)) {
err = PTR_ERR(usb2->base.lane);
goto out;
}
err = tegra_xusb_usb2_port_parse_dt(usb2);
if (err < 0) {
tegra_xusb_port_unregister(&usb2->base);
goto out;
}
list_add_tail(&usb2->base.list, &padctl->ports);
out:
of_node_put(np);
return err;
}
void tegra_xusb_usb2_port_release(struct tegra_xusb_port *port)
{
struct tegra_xusb_usb2_port *usb2 = to_usb2_port(port);
kfree(usb2);
}
void tegra_xusb_usb2_port_remove(struct tegra_xusb_port *port)
{
struct tegra_xusb_usb2_port *usb2 = to_usb2_port(port);
regulator_put(usb2->supply);
}
static int tegra_xusb_ulpi_port_parse_dt(struct tegra_xusb_ulpi_port *ulpi)
{
struct tegra_xusb_port *port = &ulpi->base;
struct device_node *np = port->dev.of_node;
ulpi->internal = of_property_read_bool(np, "nvidia,internal");
return 0;
}
static int tegra_xusb_add_ulpi_port(struct tegra_xusb_padctl *padctl,
unsigned int index)
{
struct tegra_xusb_ulpi_port *ulpi;
struct device_node *np;
int err = 0;
np = tegra_xusb_find_port_node(padctl, "ulpi", index);
if (!np || !of_device_is_available(np))
goto out;
ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL);
if (!ulpi) {
err = -ENOMEM;
goto out;
}
err = tegra_xusb_port_init(&ulpi->base, padctl, np, "ulpi", index);
if (err < 0)
goto out;
ulpi->base.ops = padctl->soc->ports.ulpi.ops;
ulpi->base.lane = ulpi->base.ops->map(&ulpi->base);
if (IS_ERR(ulpi->base.lane)) {
err = PTR_ERR(ulpi->base.lane);
goto out;
}
err = tegra_xusb_ulpi_port_parse_dt(ulpi);
if (err < 0) {
tegra_xusb_port_unregister(&ulpi->base);
goto out;
}
list_add_tail(&ulpi->base.list, &padctl->ports);
out:
of_node_put(np);
return err;
}
void tegra_xusb_ulpi_port_release(struct tegra_xusb_port *port)
{
struct tegra_xusb_ulpi_port *ulpi = to_ulpi_port(port);
kfree(ulpi);
}
static int tegra_xusb_hsic_port_parse_dt(struct tegra_xusb_hsic_port *hsic)
{
/* XXX */
return 0;
}
static int tegra_xusb_add_hsic_port(struct tegra_xusb_padctl *padctl,
unsigned int index)
{
struct tegra_xusb_hsic_port *hsic;
struct device_node *np;
int err = 0;
np = tegra_xusb_find_port_node(padctl, "hsic", index);
if (!np || !of_device_is_available(np))
goto out;
hsic = kzalloc(sizeof(*hsic), GFP_KERNEL);
if (!hsic) {
err = -ENOMEM;
goto out;
}
err = tegra_xusb_port_init(&hsic->base, padctl, np, "hsic", index);
if (err < 0)
goto out;
hsic->base.ops = padctl->soc->ports.hsic.ops;
hsic->base.lane = hsic->base.ops->map(&hsic->base);
if (IS_ERR(hsic->base.lane)) {
err = PTR_ERR(hsic->base.lane);
goto out;
}
err = tegra_xusb_hsic_port_parse_dt(hsic);
if (err < 0) {
tegra_xusb_port_unregister(&hsic->base);
goto out;
}
list_add_tail(&hsic->base.list, &padctl->ports);
out:
of_node_put(np);
return err;
}
void tegra_xusb_hsic_port_release(struct tegra_xusb_port *port)
{
struct tegra_xusb_hsic_port *hsic = to_hsic_port(port);
kfree(hsic);
}
static int tegra_xusb_usb3_port_parse_dt(struct tegra_xusb_usb3_port *usb3)
{
struct tegra_xusb_port *port = &usb3->base;
struct device_node *np = port->dev.of_node;
enum usb_device_speed maximum_speed;
u32 value;
int err;
err = of_property_read_u32(np, "nvidia,usb2-companion", &value);
if (err < 0) {
dev_err(&port->dev, "failed to read port: %d\n", err);
return err;
}
usb3->port = value;
usb3->internal = of_property_read_bool(np, "nvidia,internal");
if (device_property_present(&port->dev, "maximum-speed")) {
maximum_speed = usb_get_maximum_speed(&port->dev);
if (maximum_speed == USB_SPEED_SUPER)
usb3->disable_gen2 = true;
else if (maximum_speed == USB_SPEED_SUPER_PLUS)
usb3->disable_gen2 = false;
else
return -EINVAL;
}
usb3->supply = regulator_get(&port->dev, "vbus");
return PTR_ERR_OR_ZERO(usb3->supply);
}
static int tegra_xusb_add_usb3_port(struct tegra_xusb_padctl *padctl,
unsigned int index)
{
struct tegra_xusb_usb3_port *usb3;
struct device_node *np;
int err = 0;
/*
* If there is no supplemental configuration in the device tree the
* port is unusable. But it is valid to configure only a single port,
* hence return 0 instead of an error to allow ports to be optional.
*/
np = tegra_xusb_find_port_node(padctl, "usb3", index);
if (!np || !of_device_is_available(np))
goto out;
usb3 = kzalloc(sizeof(*usb3), GFP_KERNEL);
if (!usb3) {
err = -ENOMEM;
goto out;
}
err = tegra_xusb_port_init(&usb3->base, padctl, np, "usb3", index);
if (err < 0)
goto out;
usb3->base.ops = padctl->soc->ports.usb3.ops;
usb3->base.lane = usb3->base.ops->map(&usb3->base);
if (IS_ERR(usb3->base.lane)) {
err = PTR_ERR(usb3->base.lane);
goto out;
}
err = tegra_xusb_usb3_port_parse_dt(usb3);
if (err < 0) {
tegra_xusb_port_unregister(&usb3->base);
goto out;
}
list_add_tail(&usb3->base.list, &padctl->ports);
out:
of_node_put(np);
return err;
}
void tegra_xusb_usb3_port_release(struct tegra_xusb_port *port)
{
struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port);
kfree(usb3);
}
void tegra_xusb_usb3_port_remove(struct tegra_xusb_port *port)
{
struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port);
regulator_put(usb3->supply);
}
static void __tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_port *port, *tmp;
list_for_each_entry_safe_reverse(port, tmp, &padctl->ports, list) {
list_del(&port->list);
tegra_xusb_port_unregister(port);
}
}
static int tegra_xusb_find_unused_usb3_port(struct tegra_xusb_padctl *padctl)
{
struct device_node *np;
unsigned int i;
for (i = 0; i < padctl->soc->ports.usb3.count; i++) {
np = tegra_xusb_find_port_node(padctl, "usb3", i);
if (!np || !of_device_is_available(np))
return i;
}
return -ENODEV;
}
static bool tegra_xusb_port_is_companion(struct tegra_xusb_usb2_port *usb2)
{
unsigned int i;
struct tegra_xusb_usb3_port *usb3;
struct tegra_xusb_padctl *padctl = usb2->base.padctl;
for (i = 0; i < padctl->soc->ports.usb3.count; i++) {
usb3 = tegra_xusb_find_usb3_port(padctl, i);
if (usb3 && usb3->port == usb2->base.index)
return true;
}
return false;
}
static int tegra_xusb_update_usb3_fake_port(struct tegra_xusb_usb2_port *usb2)
{
int fake;
/* Disable usb3_port_fake usage by default and assign if needed */
usb2->usb3_port_fake = -1;
if ((usb2->mode == USB_DR_MODE_OTG ||
usb2->mode == USB_DR_MODE_PERIPHERAL) &&
!tegra_xusb_port_is_companion(usb2)) {
fake = tegra_xusb_find_unused_usb3_port(usb2->base.padctl);
if (fake < 0) {
dev_err(&usb2->base.dev, "no unused USB3 ports available\n");
return -ENODEV;
}
dev_dbg(&usb2->base.dev, "Found unused usb3 port: %d\n", fake);
usb2->usb3_port_fake = fake;
}
return 0;
}
static int tegra_xusb_setup_ports(struct tegra_xusb_padctl *padctl)
{
struct tegra_xusb_port *port;
struct tegra_xusb_usb2_port *usb2;
unsigned int i;
int err = 0;
mutex_lock(&padctl->lock);
for (i = 0; i < padctl->soc->ports.usb2.count; i++) {
err = tegra_xusb_add_usb2_port(padctl, i);
if (err < 0)
goto remove_ports;
}
for (i = 0; i < padctl->soc->ports.ulpi.count; i++) {
err = tegra_xusb_add_ulpi_port(padctl, i);
if (err < 0)
goto remove_ports;
}
for (i = 0; i < padctl->soc->ports.hsic.count; i++) {
err = tegra_xusb_add_hsic_port(padctl, i);
if (err < 0)
goto remove_ports;
}
for (i = 0; i < padctl->soc->ports.usb3.count; i++) {
err = tegra_xusb_add_usb3_port(padctl, i);
if (err < 0)
goto remove_ports;
}
if (padctl->soc->need_fake_usb3_port) {
for (i = 0; i < padctl->soc->ports.usb2.count; i++) {
usb2 = tegra_xusb_find_usb2_port(padctl, i);
if (!usb2)
continue;
err = tegra_xusb_update_usb3_fake_port(usb2);
if (err < 0)
goto remove_ports;
}
}
list_for_each_entry(port, &padctl->ports, list) {
err = port->ops->enable(port);
if (err < 0)
dev_err(padctl->dev, "failed to enable port %s: %d\n",
dev_name(&port->dev), err);
}
goto unlock;
remove_ports:
__tegra_xusb_remove_ports(padctl);
unlock:
mutex_unlock(&padctl->lock);
return err;
}
static void tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl)
{
mutex_lock(&padctl->lock);
__tegra_xusb_remove_ports(padctl);
mutex_unlock(&padctl->lock);
}
static int tegra_xusb_padctl_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
const struct tegra_xusb_padctl_soc *soc;
struct tegra_xusb_padctl *padctl;
const struct of_device_id *match;
struct resource *res;
int err;
/* for backwards compatibility with old device trees */
np = of_get_child_by_name(np, "pads");
if (!np) {
dev_warn(&pdev->dev, "deprecated DT, using legacy driver\n");
return tegra_xusb_padctl_legacy_probe(pdev);
}
of_node_put(np);
match = of_match_node(tegra_xusb_padctl_of_match, pdev->dev.of_node);
soc = match->data;
padctl = soc->ops->probe(&pdev->dev, soc);
if (IS_ERR(padctl))
return PTR_ERR(padctl);
platform_set_drvdata(pdev, padctl);
INIT_LIST_HEAD(&padctl->ports);
INIT_LIST_HEAD(&padctl->lanes);
INIT_LIST_HEAD(&padctl->pads);
mutex_init(&padctl->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
padctl->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(padctl->regs)) {
err = PTR_ERR(padctl->regs);
goto remove;
}
padctl->rst = devm_reset_control_get(&pdev->dev, NULL);
if (IS_ERR(padctl->rst)) {
err = PTR_ERR(padctl->rst);
goto remove;
}
padctl->supplies = devm_kcalloc(&pdev->dev, padctl->soc->num_supplies,
sizeof(*padctl->supplies), GFP_KERNEL);
if (!padctl->supplies) {
err = -ENOMEM;
goto remove;
}
regulator_bulk_set_supply_names(padctl->supplies,
padctl->soc->supply_names,
padctl->soc->num_supplies);
err = devm_regulator_bulk_get(&pdev->dev, padctl->soc->num_supplies,
padctl->supplies);
if (err < 0) {
dev_err(&pdev->dev, "failed to get regulators: %d\n", err);
goto remove;
}
err = reset_control_deassert(padctl->rst);
if (err < 0)
goto remove;
err = regulator_bulk_enable(padctl->soc->num_supplies,
padctl->supplies);
if (err < 0) {
dev_err(&pdev->dev, "failed to enable supplies: %d\n", err);
goto reset;
}
err = tegra_xusb_setup_pads(padctl);
if (err < 0) {
dev_err(&pdev->dev, "failed to setup pads: %d\n", err);
goto power_down;
}
err = tegra_xusb_setup_ports(padctl);
if (err) {
const char *level = KERN_ERR;
if (err == -EPROBE_DEFER)
level = KERN_DEBUG;
dev_printk(level, &pdev->dev,
dev_fmt("failed to setup XUSB ports: %d\n"), err);
goto remove_pads;
}
return 0;
remove_pads:
tegra_xusb_remove_pads(padctl);
power_down:
regulator_bulk_disable(padctl->soc->num_supplies, padctl->supplies);
reset:
reset_control_assert(padctl->rst);
remove:
platform_set_drvdata(pdev, NULL);
soc->ops->remove(padctl);
return err;
}
static int tegra_xusb_padctl_remove(struct platform_device *pdev)
{
struct tegra_xusb_padctl *padctl = platform_get_drvdata(pdev);
int err;
tegra_xusb_remove_ports(padctl);
tegra_xusb_remove_pads(padctl);
err = regulator_bulk_disable(padctl->soc->num_supplies,
padctl->supplies);
if (err < 0)
dev_err(&pdev->dev, "failed to disable supplies: %d\n", err);
err = reset_control_assert(padctl->rst);
if (err < 0)
dev_err(&pdev->dev, "failed to assert reset: %d\n", err);
padctl->soc->ops->remove(padctl);
return err;
}
static struct platform_driver tegra_xusb_padctl_driver = {
.driver = {
.name = "tegra-xusb-padctl",
.of_match_table = tegra_xusb_padctl_of_match,
},
.probe = tegra_xusb_padctl_probe,
.remove = tegra_xusb_padctl_remove,
};
module_platform_driver(tegra_xusb_padctl_driver);
struct tegra_xusb_padctl *tegra_xusb_padctl_get(struct device *dev)
{
struct tegra_xusb_padctl *padctl;
struct platform_device *pdev;
struct device_node *np;
np = of_parse_phandle(dev->of_node, "nvidia,xusb-padctl", 0);
if (!np)
return ERR_PTR(-EINVAL);
/*
* This is slightly ugly. A better implementation would be to keep a
* registry of pad controllers, but since there will almost certainly
* only ever be one per SoC that would be a little overkill.
*/
pdev = of_find_device_by_node(np);
if (!pdev) {
of_node_put(np);
return ERR_PTR(-ENODEV);
}
of_node_put(np);
padctl = platform_get_drvdata(pdev);
if (!padctl) {
put_device(&pdev->dev);
return ERR_PTR(-EPROBE_DEFER);
}
return padctl;
}
EXPORT_SYMBOL_GPL(tegra_xusb_padctl_get);
void tegra_xusb_padctl_put(struct tegra_xusb_padctl *padctl)
{
if (padctl)
put_device(padctl->dev);
}
EXPORT_SYMBOL_GPL(tegra_xusb_padctl_put);
int tegra_xusb_padctl_usb3_save_context(struct tegra_xusb_padctl *padctl,
unsigned int port)
{
if (padctl->soc->ops->usb3_save_context)
return padctl->soc->ops->usb3_save_context(padctl, port);
return -ENOSYS;
}
EXPORT_SYMBOL_GPL(tegra_xusb_padctl_usb3_save_context);
int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl,
unsigned int port, bool idle)
{
if (padctl->soc->ops->hsic_set_idle)
return padctl->soc->ops->hsic_set_idle(padctl, port, idle);
return -ENOSYS;
}
EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle);
int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl,
unsigned int port, bool enable)
{
if (padctl->soc->ops->usb3_set_lfps_detect)
return padctl->soc->ops->usb3_set_lfps_detect(padctl, port,
enable);
return -ENOSYS;
}
EXPORT_SYMBOL_GPL(tegra_xusb_padctl_usb3_set_lfps_detect);
int tegra_xusb_padctl_set_vbus_override(struct tegra_xusb_padctl *padctl,
bool val)
{
if (padctl->soc->ops->vbus_override)
return padctl->soc->ops->vbus_override(padctl, val);
return -ENOTSUPP;
}
EXPORT_SYMBOL_GPL(tegra_xusb_padctl_set_vbus_override);
int tegra_phy_xusb_utmi_port_reset(struct phy *phy)
{
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
if (padctl->soc->ops->utmi_port_reset)
return padctl->soc->ops->utmi_port_reset(phy);
return -ENOTSUPP;
}
EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_port_reset);
int tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl,
unsigned int port)
{
struct tegra_xusb_usb2_port *usb2;
struct tegra_xusb_usb3_port *usb3;
int i;
usb2 = tegra_xusb_find_usb2_port(padctl, port);
if (!usb2)
return -EINVAL;
for (i = 0; i < padctl->soc->ports.usb3.count; i++) {
usb3 = tegra_xusb_find_usb3_port(padctl, i);
if (usb3 && usb3->port == usb2->base.index)
return usb3->base.index;
}
return -ENODEV;
}
EXPORT_SYMBOL_GPL(tegra_xusb_padctl_get_usb3_companion);
MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
MODULE_DESCRIPTION("Tegra XUSB Pad Controller driver");
MODULE_LICENSE("GPL v2");