1551 lines · 35.4 KB · C
RawDownload
// 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_platform.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,					   const 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#if defined(CONFIG_ARCH_TEGRA_234_SOC)	{		.compatible = "nvidia,tegra234-xusb-padctl",		.data = &tegra234_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_lane_soc *soc = lane->soc;	u32 value; 	/* skip single function lanes */	if (soc->num_funcs < 2)		return; 	if (lane->pad->ops->iddq_enable)		lane->pad->ops->iddq_enable(lane); 	/* choose function */	value = padctl_readl(padctl, soc->offset);	value &= ~(soc->mask << soc->shift);	value |= lane->function << soc->shift;	padctl_writel(padctl, value, soc->offset); 	if (lane->pad->ops->iddq_disable)		lane->pad->ops->iddq_disable(lane);} 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;} 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 NULL;	}	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; 

Lines 1–500 of 1551.