static int cdns_torrent_phy_probe()

in cadence/phy-cadence-torrent.c [2526:2779]


static int cdns_torrent_phy_probe(struct platform_device *pdev)
{
	struct cdns_torrent_phy *cdns_phy;
	struct device *dev = &pdev->dev;
	struct phy_provider *phy_provider;
	const struct cdns_torrent_data *data;
	struct device_node *child;
	int ret, subnodes, node = 0, i;
	u32 total_num_lanes = 0;
	int already_configured;
	u8 init_dp_regmap = 0;
	u32 phy_type;

	/* Get init data for this PHY */
	data = of_device_get_match_data(dev);
	if (!data)
		return -EINVAL;

	cdns_phy = devm_kzalloc(dev, sizeof(*cdns_phy), GFP_KERNEL);
	if (!cdns_phy)
		return -ENOMEM;

	dev_set_drvdata(dev, cdns_phy);
	cdns_phy->dev = dev;
	cdns_phy->init_data = data;

	cdns_phy->sd_base = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(cdns_phy->sd_base))
		return PTR_ERR(cdns_phy->sd_base);

	subnodes = of_get_available_child_count(dev->of_node);
	if (subnodes == 0) {
		dev_err(dev, "No available link subnodes found\n");
		return -EINVAL;
	}

	ret = cdns_torrent_regmap_init(cdns_phy);
	if (ret)
		return ret;

	ret = cdns_torrent_regfield_init(cdns_phy);
	if (ret)
		return ret;

	ret = cdns_torrent_clk_register(cdns_phy);
	if (ret)
		return ret;

	regmap_field_read(cdns_phy->phy_pma_cmn_ctrl_1, &already_configured);

	if (!already_configured) {
		ret = cdns_torrent_reset(cdns_phy);
		if (ret)
			goto clk_cleanup;

		ret = cdns_torrent_clk(cdns_phy);
		if (ret)
			goto clk_cleanup;

		/* Enable APB */
		reset_control_deassert(cdns_phy->apb_rst);
	}

	for_each_available_child_of_node(dev->of_node, child) {
		struct phy *gphy;

		/* PHY subnode name must be 'phy'. */
		if (!(of_node_name_eq(child, "phy")))
			continue;

		cdns_phy->phys[node].lnk_rst =
				of_reset_control_array_get_exclusive(child);
		if (IS_ERR(cdns_phy->phys[node].lnk_rst)) {
			dev_err(dev, "%s: failed to get reset\n",
				child->full_name);
			ret = PTR_ERR(cdns_phy->phys[node].lnk_rst);
			goto put_lnk_rst;
		}

		if (of_property_read_u32(child, "reg",
					 &cdns_phy->phys[node].mlane)) {
			dev_err(dev, "%s: No \"reg\"-property.\n",
				child->full_name);
			ret = -EINVAL;
			goto put_child;
		}

		if (of_property_read_u32(child, "cdns,phy-type", &phy_type)) {
			dev_err(dev, "%s: No \"cdns,phy-type\"-property.\n",
				child->full_name);
			ret = -EINVAL;
			goto put_child;
		}

		switch (phy_type) {
		case PHY_TYPE_PCIE:
			cdns_phy->phys[node].phy_type = TYPE_PCIE;
			break;
		case PHY_TYPE_DP:
			cdns_phy->phys[node].phy_type = TYPE_DP;
			break;
		case PHY_TYPE_SGMII:
			cdns_phy->phys[node].phy_type = TYPE_SGMII;
			break;
		case PHY_TYPE_QSGMII:
			cdns_phy->phys[node].phy_type = TYPE_QSGMII;
			break;
		case PHY_TYPE_USB3:
			cdns_phy->phys[node].phy_type = TYPE_USB;
			break;
		default:
			dev_err(dev, "Unsupported protocol\n");
			ret = -EINVAL;
			goto put_child;
		}

		if (of_property_read_u32(child, "cdns,num-lanes",
					 &cdns_phy->phys[node].num_lanes)) {
			dev_err(dev, "%s: No \"cdns,num-lanes\"-property.\n",
				child->full_name);
			ret = -EINVAL;
			goto put_child;
		}

		total_num_lanes += cdns_phy->phys[node].num_lanes;

		/* Get SSC mode */
		cdns_phy->phys[node].ssc_mode = NO_SSC;
		of_property_read_u32(child, "cdns,ssc-mode",
				     &cdns_phy->phys[node].ssc_mode);

		if (!already_configured)
			gphy = devm_phy_create(dev, child, &cdns_torrent_phy_ops);
		else
			gphy = devm_phy_create(dev, child, &noop_ops);
		if (IS_ERR(gphy)) {
			ret = PTR_ERR(gphy);
			goto put_child;
		}

		if (cdns_phy->phys[node].phy_type == TYPE_DP) {
			switch (cdns_phy->phys[node].num_lanes) {
			case 1:
			case 2:
			case 4:
			/* valid number of lanes */
				break;
			default:
				dev_err(dev, "unsupported number of lanes: %d\n",
					cdns_phy->phys[node].num_lanes);
				ret = -EINVAL;
				goto put_child;
			}

			cdns_phy->max_bit_rate = DEFAULT_MAX_BIT_RATE;
			of_property_read_u32(child, "cdns,max-bit-rate",
					     &cdns_phy->max_bit_rate);

			switch (cdns_phy->max_bit_rate) {
			case 1620:
			case 2160:
			case 2430:
			case 2700:
			case 3240:
			case 4320:
			case 5400:
			case 8100:
			/* valid bit rate */
				break;
			default:
				dev_err(dev, "unsupported max bit rate: %dMbps\n",
					cdns_phy->max_bit_rate);
				ret = -EINVAL;
				goto put_child;
			}

			/* DPTX registers */
			cdns_phy->base = devm_platform_ioremap_resource(pdev, 1);
			if (IS_ERR(cdns_phy->base)) {
				ret = PTR_ERR(cdns_phy->base);
				goto put_child;
			}

			if (!init_dp_regmap) {
				ret = cdns_torrent_dp_regmap_init(cdns_phy);
				if (ret)
					goto put_child;

				ret = cdns_torrent_dp_regfield_init(cdns_phy);
				if (ret)
					goto put_child;

				init_dp_regmap++;
			}

			dev_dbg(dev, "DP max bit rate %d.%03d Gbps\n",
				cdns_phy->max_bit_rate / 1000,
				cdns_phy->max_bit_rate % 1000);

			gphy->attrs.bus_width = cdns_phy->phys[node].num_lanes;
			gphy->attrs.max_link_rate = cdns_phy->max_bit_rate;
			gphy->attrs.mode = PHY_MODE_DP;
		}

		cdns_phy->phys[node].phy = gphy;
		phy_set_drvdata(gphy, &cdns_phy->phys[node]);

		node++;
	}
	cdns_phy->nsubnodes = node;

	if (total_num_lanes > MAX_NUM_LANES) {
		dev_err(dev, "Invalid lane configuration\n");
		ret = -EINVAL;
		goto put_lnk_rst;
	}

	if (cdns_phy->nsubnodes > 1 && !already_configured) {
		ret = cdns_torrent_phy_configure_multilink(cdns_phy);
		if (ret)
			goto put_lnk_rst;
	}

	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
	if (IS_ERR(phy_provider)) {
		ret = PTR_ERR(phy_provider);
		goto put_lnk_rst;
	}

	if (cdns_phy->nsubnodes > 1)
		dev_dbg(dev, "Multi-link: %s (%d lanes) & %s (%d lanes)",
			cdns_torrent_get_phy_type(cdns_phy->phys[0].phy_type),
			cdns_phy->phys[0].num_lanes,
			cdns_torrent_get_phy_type(cdns_phy->phys[1].phy_type),
			cdns_phy->phys[1].num_lanes);
	else
		dev_dbg(dev, "Single link: %s (%d lanes)",
			cdns_torrent_get_phy_type(cdns_phy->phys[0].phy_type),
			cdns_phy->phys[0].num_lanes);

	return 0;

put_child:
	node++;
put_lnk_rst:
	for (i = 0; i < node; i++)
		reset_control_put(cdns_phy->phys[i].lnk_rst);
	of_node_put(child);
	reset_control_assert(cdns_phy->apb_rst);
	clk_disable_unprepare(cdns_phy->clk);
clk_cleanup:
	cdns_torrent_clk_cleanup(cdns_phy);
	return ret;
}