brintos

brintos / linux-shallow public Read only

0
0
Text · 5.7 KiB · 21c2017 Raw
245 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * phy-uniphier-usb2.c - PHY driver for UniPhier USB2 controller4 * Copyright 2015-2018 Socionext Inc.5 * Author:6 *      Kunihiko Hayashi <hayashi.kunihiko@socionext.com>7 */8 9#include <linux/mfd/syscon.h>10#include <linux/module.h>11#include <linux/of.h>12#include <linux/of_platform.h>13#include <linux/phy/phy.h>14#include <linux/platform_device.h>15#include <linux/regmap.h>16#include <linux/regulator/consumer.h>17 18#define SG_USBPHY1CTRL		0x50019#define SG_USBPHY1CTRL2		0x50420#define SG_USBPHY2CTRL		0x50821#define SG_USBPHY2CTRL2		0x50c	/* LD11 */22#define SG_USBPHY12PLL		0x50c	/* Pro4 */23#define SG_USBPHY3CTRL		0x51024#define SG_USBPHY3CTRL2		0x51425#define SG_USBPHY4CTRL		0x518	/* Pro4 */26#define SG_USBPHY4CTRL2		0x51c	/* Pro4 */27#define SG_USBPHY34PLL		0x51c	/* Pro4 */28 29struct uniphier_u2phy_param {30	u32 offset;31	u32 value;32};33 34struct uniphier_u2phy_soc_data {35	struct uniphier_u2phy_param config0;36	struct uniphier_u2phy_param config1;37};38 39struct uniphier_u2phy_priv {40	struct regmap *regmap;41	struct phy *phy;42	struct regulator *vbus;43	const struct uniphier_u2phy_soc_data *data;44	struct uniphier_u2phy_priv *next;45};46 47static int uniphier_u2phy_power_on(struct phy *phy)48{49	struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy);50	int ret = 0;51 52	if (priv->vbus)53		ret = regulator_enable(priv->vbus);54 55	return ret;56}57 58static int uniphier_u2phy_power_off(struct phy *phy)59{60	struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy);61 62	if (priv->vbus)63		regulator_disable(priv->vbus);64 65	return 0;66}67 68static int uniphier_u2phy_init(struct phy *phy)69{70	struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy);71 72	if (!priv->data)73		return 0;74 75	regmap_write(priv->regmap, priv->data->config0.offset,76		     priv->data->config0.value);77	regmap_write(priv->regmap, priv->data->config1.offset,78		     priv->data->config1.value);79 80	return 0;81}82 83static struct phy *uniphier_u2phy_xlate(struct device *dev,84					const struct of_phandle_args *args)85{86	struct uniphier_u2phy_priv *priv = dev_get_drvdata(dev);87 88	while (priv && args->np != priv->phy->dev.of_node)89		priv = priv->next;90 91	if (!priv) {92		dev_err(dev, "Failed to find appropriate phy\n");93		return ERR_PTR(-EINVAL);94	}95 96	return priv->phy;97}98 99static const struct phy_ops uniphier_u2phy_ops = {100	.init      = uniphier_u2phy_init,101	.power_on  = uniphier_u2phy_power_on,102	.power_off = uniphier_u2phy_power_off,103	.owner = THIS_MODULE,104};105 106static int uniphier_u2phy_probe(struct platform_device *pdev)107{108	struct device *dev = &pdev->dev;109	struct device_node *parent, *child;110	struct uniphier_u2phy_priv *priv = NULL, *next = NULL;111	struct phy_provider *phy_provider;112	struct regmap *regmap;113	const struct uniphier_u2phy_soc_data *data;114	int ret, data_idx, ndatas;115 116	data = of_device_get_match_data(dev);117	if (WARN_ON(!data))118		return -EINVAL;119 120	/* get number of data */121	for (ndatas = 0; data[ndatas].config0.offset; ndatas++)122		;123 124	parent = of_get_parent(dev->of_node);125	regmap = syscon_node_to_regmap(parent);126	of_node_put(parent);127	if (IS_ERR(regmap)) {128		dev_err(dev, "Failed to get regmap\n");129		return PTR_ERR(regmap);130	}131 132	for_each_child_of_node(dev->of_node, child) {133		priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);134		if (!priv) {135			ret = -ENOMEM;136			goto out_put_child;137		}138		priv->regmap = regmap;139 140		priv->vbus = devm_regulator_get_optional(dev, "vbus");141		if (IS_ERR(priv->vbus)) {142			if (PTR_ERR(priv->vbus) == -EPROBE_DEFER) {143				ret = PTR_ERR(priv->vbus);144				goto out_put_child;145			}146			priv->vbus = NULL;147		}148 149		priv->phy = devm_phy_create(dev, child, &uniphier_u2phy_ops);150		if (IS_ERR(priv->phy)) {151			dev_err(dev, "Failed to create phy\n");152			ret = PTR_ERR(priv->phy);153			goto out_put_child;154		}155 156		ret = of_property_read_u32(child, "reg", &data_idx);157		if (ret) {158			dev_err(dev, "Failed to get reg property\n");159			goto out_put_child;160		}161 162		if (data_idx < ndatas)163			priv->data = &data[data_idx];164		else165			dev_warn(dev, "No phy configuration: %s\n",166				 child->full_name);167 168		phy_set_drvdata(priv->phy, priv);169		priv->next = next;170		next = priv;171	}172 173	dev_set_drvdata(dev, priv);174	phy_provider = devm_of_phy_provider_register(dev,175						     uniphier_u2phy_xlate);176	return PTR_ERR_OR_ZERO(phy_provider);177 178out_put_child:179	of_node_put(child);180 181	return ret;182}183 184static const struct uniphier_u2phy_soc_data uniphier_pro4_data[] = {185	{186		.config0 = { SG_USBPHY1CTRL, 0x05142400 },187		.config1 = { SG_USBPHY12PLL, 0x00010010 },188	},189	{190		.config0 = { SG_USBPHY2CTRL, 0x05142400 },191		.config1 = { SG_USBPHY12PLL, 0x00010010 },192	},193	{194		.config0 = { SG_USBPHY3CTRL, 0x05142400 },195		.config1 = { SG_USBPHY34PLL, 0x00010010 },196	},197	{198		.config0 = { SG_USBPHY4CTRL, 0x05142400 },199		.config1 = { SG_USBPHY34PLL, 0x00010010 },200	},201	{ /* sentinel */ }202};203 204static const struct uniphier_u2phy_soc_data uniphier_ld11_data[] = {205	{206		.config0 = { SG_USBPHY1CTRL,  0x82280000 },207		.config1 = { SG_USBPHY1CTRL2, 0x00000106 },208	},209	{210		.config0 = { SG_USBPHY2CTRL,  0x82280000 },211		.config1 = { SG_USBPHY2CTRL2, 0x00000106 },212	},213	{214		.config0 = { SG_USBPHY3CTRL,  0x82280000 },215		.config1 = { SG_USBPHY3CTRL2, 0x00000106 },216	},217	{ /* sentinel */ }218};219 220static const struct of_device_id uniphier_u2phy_match[] = {221	{222		.compatible = "socionext,uniphier-pro4-usb2-phy",223		.data = &uniphier_pro4_data,224	},225	{226		.compatible = "socionext,uniphier-ld11-usb2-phy",227		.data = &uniphier_ld11_data,228	},229	{ /* sentinel */ }230};231MODULE_DEVICE_TABLE(of, uniphier_u2phy_match);232 233static struct platform_driver uniphier_u2phy_driver = {234	.probe = uniphier_u2phy_probe,235	.driver = {236		.name = "uniphier-usb2-phy",237		.of_match_table = uniphier_u2phy_match,238	},239};240module_platform_driver(uniphier_u2phy_driver);241 242MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");243MODULE_DESCRIPTION("UniPhier PHY driver for USB2 controller");244MODULE_LICENSE("GPL v2");245