// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014-2022, 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 const 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_lan
|