162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * phy-uniphier-usb2.c - PHY driver for UniPhier USB2 controller
462306a36Sopenharmony_ci * Copyright 2015-2018 Socionext Inc.
562306a36Sopenharmony_ci * Author:
662306a36Sopenharmony_ci *      Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/of_platform.h>
1362306a36Sopenharmony_ci#include <linux/phy/phy.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/regmap.h>
1662306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define SG_USBPHY1CTRL		0x500
1962306a36Sopenharmony_ci#define SG_USBPHY1CTRL2		0x504
2062306a36Sopenharmony_ci#define SG_USBPHY2CTRL		0x508
2162306a36Sopenharmony_ci#define SG_USBPHY2CTRL2		0x50c	/* LD11 */
2262306a36Sopenharmony_ci#define SG_USBPHY12PLL		0x50c	/* Pro4 */
2362306a36Sopenharmony_ci#define SG_USBPHY3CTRL		0x510
2462306a36Sopenharmony_ci#define SG_USBPHY3CTRL2		0x514
2562306a36Sopenharmony_ci#define SG_USBPHY4CTRL		0x518	/* Pro4 */
2662306a36Sopenharmony_ci#define SG_USBPHY4CTRL2		0x51c	/* Pro4 */
2762306a36Sopenharmony_ci#define SG_USBPHY34PLL		0x51c	/* Pro4 */
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct uniphier_u2phy_param {
3062306a36Sopenharmony_ci	u32 offset;
3162306a36Sopenharmony_ci	u32 value;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistruct uniphier_u2phy_soc_data {
3562306a36Sopenharmony_ci	struct uniphier_u2phy_param config0;
3662306a36Sopenharmony_ci	struct uniphier_u2phy_param config1;
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistruct uniphier_u2phy_priv {
4062306a36Sopenharmony_ci	struct regmap *regmap;
4162306a36Sopenharmony_ci	struct phy *phy;
4262306a36Sopenharmony_ci	struct regulator *vbus;
4362306a36Sopenharmony_ci	const struct uniphier_u2phy_soc_data *data;
4462306a36Sopenharmony_ci	struct uniphier_u2phy_priv *next;
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int uniphier_u2phy_power_on(struct phy *phy)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy);
5062306a36Sopenharmony_ci	int ret = 0;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (priv->vbus)
5362306a36Sopenharmony_ci		ret = regulator_enable(priv->vbus);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return ret;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic int uniphier_u2phy_power_off(struct phy *phy)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (priv->vbus)
6362306a36Sopenharmony_ci		regulator_disable(priv->vbus);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return 0;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int uniphier_u2phy_init(struct phy *phy)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if (!priv->data)
7362306a36Sopenharmony_ci		return 0;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	regmap_write(priv->regmap, priv->data->config0.offset,
7662306a36Sopenharmony_ci		     priv->data->config0.value);
7762306a36Sopenharmony_ci	regmap_write(priv->regmap, priv->data->config1.offset,
7862306a36Sopenharmony_ci		     priv->data->config1.value);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic struct phy *uniphier_u2phy_xlate(struct device *dev,
8462306a36Sopenharmony_ci					struct of_phandle_args *args)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct uniphier_u2phy_priv *priv = dev_get_drvdata(dev);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	while (priv && args->np != priv->phy->dev.of_node)
8962306a36Sopenharmony_ci		priv = priv->next;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (!priv) {
9262306a36Sopenharmony_ci		dev_err(dev, "Failed to find appropriate phy\n");
9362306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return priv->phy;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic const struct phy_ops uniphier_u2phy_ops = {
10062306a36Sopenharmony_ci	.init      = uniphier_u2phy_init,
10162306a36Sopenharmony_ci	.power_on  = uniphier_u2phy_power_on,
10262306a36Sopenharmony_ci	.power_off = uniphier_u2phy_power_off,
10362306a36Sopenharmony_ci	.owner = THIS_MODULE,
10462306a36Sopenharmony_ci};
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int uniphier_u2phy_probe(struct platform_device *pdev)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
10962306a36Sopenharmony_ci	struct device_node *parent, *child;
11062306a36Sopenharmony_ci	struct uniphier_u2phy_priv *priv = NULL, *next = NULL;
11162306a36Sopenharmony_ci	struct phy_provider *phy_provider;
11262306a36Sopenharmony_ci	struct regmap *regmap;
11362306a36Sopenharmony_ci	const struct uniphier_u2phy_soc_data *data;
11462306a36Sopenharmony_ci	int ret, data_idx, ndatas;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	data = of_device_get_match_data(dev);
11762306a36Sopenharmony_ci	if (WARN_ON(!data))
11862306a36Sopenharmony_ci		return -EINVAL;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* get number of data */
12162306a36Sopenharmony_ci	for (ndatas = 0; data[ndatas].config0.offset; ndatas++)
12262306a36Sopenharmony_ci		;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	parent = of_get_parent(dev->of_node);
12562306a36Sopenharmony_ci	regmap = syscon_node_to_regmap(parent);
12662306a36Sopenharmony_ci	of_node_put(parent);
12762306a36Sopenharmony_ci	if (IS_ERR(regmap)) {
12862306a36Sopenharmony_ci		dev_err(dev, "Failed to get regmap\n");
12962306a36Sopenharmony_ci		return PTR_ERR(regmap);
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	for_each_child_of_node(dev->of_node, child) {
13362306a36Sopenharmony_ci		priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
13462306a36Sopenharmony_ci		if (!priv) {
13562306a36Sopenharmony_ci			ret = -ENOMEM;
13662306a36Sopenharmony_ci			goto out_put_child;
13762306a36Sopenharmony_ci		}
13862306a36Sopenharmony_ci		priv->regmap = regmap;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci		priv->vbus = devm_regulator_get_optional(dev, "vbus");
14162306a36Sopenharmony_ci		if (IS_ERR(priv->vbus)) {
14262306a36Sopenharmony_ci			if (PTR_ERR(priv->vbus) == -EPROBE_DEFER) {
14362306a36Sopenharmony_ci				ret = PTR_ERR(priv->vbus);
14462306a36Sopenharmony_ci				goto out_put_child;
14562306a36Sopenharmony_ci			}
14662306a36Sopenharmony_ci			priv->vbus = NULL;
14762306a36Sopenharmony_ci		}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		priv->phy = devm_phy_create(dev, child, &uniphier_u2phy_ops);
15062306a36Sopenharmony_ci		if (IS_ERR(priv->phy)) {
15162306a36Sopenharmony_ci			dev_err(dev, "Failed to create phy\n");
15262306a36Sopenharmony_ci			ret = PTR_ERR(priv->phy);
15362306a36Sopenharmony_ci			goto out_put_child;
15462306a36Sopenharmony_ci		}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		ret = of_property_read_u32(child, "reg", &data_idx);
15762306a36Sopenharmony_ci		if (ret) {
15862306a36Sopenharmony_ci			dev_err(dev, "Failed to get reg property\n");
15962306a36Sopenharmony_ci			goto out_put_child;
16062306a36Sopenharmony_ci		}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		if (data_idx < ndatas)
16362306a36Sopenharmony_ci			priv->data = &data[data_idx];
16462306a36Sopenharmony_ci		else
16562306a36Sopenharmony_ci			dev_warn(dev, "No phy configuration: %s\n",
16662306a36Sopenharmony_ci				 child->full_name);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci		phy_set_drvdata(priv->phy, priv);
16962306a36Sopenharmony_ci		priv->next = next;
17062306a36Sopenharmony_ci		next = priv;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	dev_set_drvdata(dev, priv);
17462306a36Sopenharmony_ci	phy_provider = devm_of_phy_provider_register(dev,
17562306a36Sopenharmony_ci						     uniphier_u2phy_xlate);
17662306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(phy_provider);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ciout_put_child:
17962306a36Sopenharmony_ci	of_node_put(child);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	return ret;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic const struct uniphier_u2phy_soc_data uniphier_pro4_data[] = {
18562306a36Sopenharmony_ci	{
18662306a36Sopenharmony_ci		.config0 = { SG_USBPHY1CTRL, 0x05142400 },
18762306a36Sopenharmony_ci		.config1 = { SG_USBPHY12PLL, 0x00010010 },
18862306a36Sopenharmony_ci	},
18962306a36Sopenharmony_ci	{
19062306a36Sopenharmony_ci		.config0 = { SG_USBPHY2CTRL, 0x05142400 },
19162306a36Sopenharmony_ci		.config1 = { SG_USBPHY12PLL, 0x00010010 },
19262306a36Sopenharmony_ci	},
19362306a36Sopenharmony_ci	{
19462306a36Sopenharmony_ci		.config0 = { SG_USBPHY3CTRL, 0x05142400 },
19562306a36Sopenharmony_ci		.config1 = { SG_USBPHY34PLL, 0x00010010 },
19662306a36Sopenharmony_ci	},
19762306a36Sopenharmony_ci	{
19862306a36Sopenharmony_ci		.config0 = { SG_USBPHY4CTRL, 0x05142400 },
19962306a36Sopenharmony_ci		.config1 = { SG_USBPHY34PLL, 0x00010010 },
20062306a36Sopenharmony_ci	},
20162306a36Sopenharmony_ci	{ /* sentinel */ }
20262306a36Sopenharmony_ci};
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic const struct uniphier_u2phy_soc_data uniphier_ld11_data[] = {
20562306a36Sopenharmony_ci	{
20662306a36Sopenharmony_ci		.config0 = { SG_USBPHY1CTRL,  0x82280000 },
20762306a36Sopenharmony_ci		.config1 = { SG_USBPHY1CTRL2, 0x00000106 },
20862306a36Sopenharmony_ci	},
20962306a36Sopenharmony_ci	{
21062306a36Sopenharmony_ci		.config0 = { SG_USBPHY2CTRL,  0x82280000 },
21162306a36Sopenharmony_ci		.config1 = { SG_USBPHY2CTRL2, 0x00000106 },
21262306a36Sopenharmony_ci	},
21362306a36Sopenharmony_ci	{
21462306a36Sopenharmony_ci		.config0 = { SG_USBPHY3CTRL,  0x82280000 },
21562306a36Sopenharmony_ci		.config1 = { SG_USBPHY3CTRL2, 0x00000106 },
21662306a36Sopenharmony_ci	},
21762306a36Sopenharmony_ci	{ /* sentinel */ }
21862306a36Sopenharmony_ci};
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic const struct of_device_id uniphier_u2phy_match[] = {
22162306a36Sopenharmony_ci	{
22262306a36Sopenharmony_ci		.compatible = "socionext,uniphier-pro4-usb2-phy",
22362306a36Sopenharmony_ci		.data = &uniphier_pro4_data,
22462306a36Sopenharmony_ci	},
22562306a36Sopenharmony_ci	{
22662306a36Sopenharmony_ci		.compatible = "socionext,uniphier-ld11-usb2-phy",
22762306a36Sopenharmony_ci		.data = &uniphier_ld11_data,
22862306a36Sopenharmony_ci	},
22962306a36Sopenharmony_ci	{ /* sentinel */ }
23062306a36Sopenharmony_ci};
23162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, uniphier_u2phy_match);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic struct platform_driver uniphier_u2phy_driver = {
23462306a36Sopenharmony_ci	.probe = uniphier_u2phy_probe,
23562306a36Sopenharmony_ci	.driver = {
23662306a36Sopenharmony_ci		.name = "uniphier-usb2-phy",
23762306a36Sopenharmony_ci		.of_match_table = uniphier_u2phy_match,
23862306a36Sopenharmony_ci	},
23962306a36Sopenharmony_ci};
24062306a36Sopenharmony_cimodule_platform_driver(uniphier_u2phy_driver);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ciMODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
24362306a36Sopenharmony_ciMODULE_DESCRIPTION("UniPhier PHY driver for USB2 controller");
24462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
245