18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * phy-uniphier-usb2.c - PHY driver for UniPhier USB2 controller 48c2ecf20Sopenharmony_ci * Copyright 2015-2018 Socionext Inc. 58c2ecf20Sopenharmony_ci * Author: 68c2ecf20Sopenharmony_ci * Kunihiko Hayashi <hayashi.kunihiko@socionext.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 138c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/regmap.h> 168c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define SG_USBPHY1CTRL 0x500 198c2ecf20Sopenharmony_ci#define SG_USBPHY1CTRL2 0x504 208c2ecf20Sopenharmony_ci#define SG_USBPHY2CTRL 0x508 218c2ecf20Sopenharmony_ci#define SG_USBPHY2CTRL2 0x50c /* LD11 */ 228c2ecf20Sopenharmony_ci#define SG_USBPHY12PLL 0x50c /* Pro4 */ 238c2ecf20Sopenharmony_ci#define SG_USBPHY3CTRL 0x510 248c2ecf20Sopenharmony_ci#define SG_USBPHY3CTRL2 0x514 258c2ecf20Sopenharmony_ci#define SG_USBPHY4CTRL 0x518 /* Pro4 */ 268c2ecf20Sopenharmony_ci#define SG_USBPHY4CTRL2 0x51c /* Pro4 */ 278c2ecf20Sopenharmony_ci#define SG_USBPHY34PLL 0x51c /* Pro4 */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct uniphier_u2phy_param { 308c2ecf20Sopenharmony_ci u32 offset; 318c2ecf20Sopenharmony_ci u32 value; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct uniphier_u2phy_soc_data { 358c2ecf20Sopenharmony_ci struct uniphier_u2phy_param config0; 368c2ecf20Sopenharmony_ci struct uniphier_u2phy_param config1; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct uniphier_u2phy_priv { 408c2ecf20Sopenharmony_ci struct regmap *regmap; 418c2ecf20Sopenharmony_ci struct phy *phy; 428c2ecf20Sopenharmony_ci struct regulator *vbus; 438c2ecf20Sopenharmony_ci const struct uniphier_u2phy_soc_data *data; 448c2ecf20Sopenharmony_ci struct uniphier_u2phy_priv *next; 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int uniphier_u2phy_power_on(struct phy *phy) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy); 508c2ecf20Sopenharmony_ci int ret = 0; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (priv->vbus) 538c2ecf20Sopenharmony_ci ret = regulator_enable(priv->vbus); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return ret; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int uniphier_u2phy_power_off(struct phy *phy) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (priv->vbus) 638c2ecf20Sopenharmony_ci regulator_disable(priv->vbus); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return 0; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int uniphier_u2phy_init(struct phy *phy) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (!priv->data) 738c2ecf20Sopenharmony_ci return 0; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci regmap_write(priv->regmap, priv->data->config0.offset, 768c2ecf20Sopenharmony_ci priv->data->config0.value); 778c2ecf20Sopenharmony_ci regmap_write(priv->regmap, priv->data->config1.offset, 788c2ecf20Sopenharmony_ci priv->data->config1.value); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic struct phy *uniphier_u2phy_xlate(struct device *dev, 848c2ecf20Sopenharmony_ci struct of_phandle_args *args) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct uniphier_u2phy_priv *priv = dev_get_drvdata(dev); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci while (priv && args->np != priv->phy->dev.of_node) 898c2ecf20Sopenharmony_ci priv = priv->next; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (!priv) { 928c2ecf20Sopenharmony_ci dev_err(dev, "Failed to find appropriate phy\n"); 938c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return priv->phy; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic const struct phy_ops uniphier_u2phy_ops = { 1008c2ecf20Sopenharmony_ci .init = uniphier_u2phy_init, 1018c2ecf20Sopenharmony_ci .power_on = uniphier_u2phy_power_on, 1028c2ecf20Sopenharmony_ci .power_off = uniphier_u2phy_power_off, 1038c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int uniphier_u2phy_probe(struct platform_device *pdev) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1098c2ecf20Sopenharmony_ci struct device_node *parent, *child; 1108c2ecf20Sopenharmony_ci struct uniphier_u2phy_priv *priv = NULL, *next = NULL; 1118c2ecf20Sopenharmony_ci struct phy_provider *phy_provider; 1128c2ecf20Sopenharmony_ci struct regmap *regmap; 1138c2ecf20Sopenharmony_ci const struct uniphier_u2phy_soc_data *data; 1148c2ecf20Sopenharmony_ci int ret, data_idx, ndatas; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci data = of_device_get_match_data(dev); 1178c2ecf20Sopenharmony_ci if (WARN_ON(!data)) 1188c2ecf20Sopenharmony_ci return -EINVAL; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* get number of data */ 1218c2ecf20Sopenharmony_ci for (ndatas = 0; data[ndatas].config0.offset; ndatas++) 1228c2ecf20Sopenharmony_ci ; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci parent = of_get_parent(dev->of_node); 1258c2ecf20Sopenharmony_ci regmap = syscon_node_to_regmap(parent); 1268c2ecf20Sopenharmony_ci of_node_put(parent); 1278c2ecf20Sopenharmony_ci if (IS_ERR(regmap)) { 1288c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get regmap\n"); 1298c2ecf20Sopenharmony_ci return PTR_ERR(regmap); 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci for_each_child_of_node(dev->of_node, child) { 1338c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 1348c2ecf20Sopenharmony_ci if (!priv) { 1358c2ecf20Sopenharmony_ci ret = -ENOMEM; 1368c2ecf20Sopenharmony_ci goto out_put_child; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci priv->regmap = regmap; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci priv->vbus = devm_regulator_get_optional(dev, "vbus"); 1418c2ecf20Sopenharmony_ci if (IS_ERR(priv->vbus)) { 1428c2ecf20Sopenharmony_ci if (PTR_ERR(priv->vbus) == -EPROBE_DEFER) { 1438c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->vbus); 1448c2ecf20Sopenharmony_ci goto out_put_child; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci priv->vbus = NULL; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci priv->phy = devm_phy_create(dev, child, &uniphier_u2phy_ops); 1508c2ecf20Sopenharmony_ci if (IS_ERR(priv->phy)) { 1518c2ecf20Sopenharmony_ci dev_err(dev, "Failed to create phy\n"); 1528c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->phy); 1538c2ecf20Sopenharmony_ci goto out_put_child; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = of_property_read_u32(child, "reg", &data_idx); 1578c2ecf20Sopenharmony_ci if (ret) { 1588c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get reg property\n"); 1598c2ecf20Sopenharmony_ci goto out_put_child; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (data_idx < ndatas) 1638c2ecf20Sopenharmony_ci priv->data = &data[data_idx]; 1648c2ecf20Sopenharmony_ci else 1658c2ecf20Sopenharmony_ci dev_warn(dev, "No phy configuration: %s\n", 1668c2ecf20Sopenharmony_ci child->full_name); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci phy_set_drvdata(priv->phy, priv); 1698c2ecf20Sopenharmony_ci priv->next = next; 1708c2ecf20Sopenharmony_ci next = priv; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci dev_set_drvdata(dev, priv); 1748c2ecf20Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, 1758c2ecf20Sopenharmony_ci uniphier_u2phy_xlate); 1768c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ciout_put_child: 1798c2ecf20Sopenharmony_ci of_node_put(child); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return ret; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic const struct uniphier_u2phy_soc_data uniphier_pro4_data[] = { 1858c2ecf20Sopenharmony_ci { 1868c2ecf20Sopenharmony_ci .config0 = { SG_USBPHY1CTRL, 0x05142400 }, 1878c2ecf20Sopenharmony_ci .config1 = { SG_USBPHY12PLL, 0x00010010 }, 1888c2ecf20Sopenharmony_ci }, 1898c2ecf20Sopenharmony_ci { 1908c2ecf20Sopenharmony_ci .config0 = { SG_USBPHY2CTRL, 0x05142400 }, 1918c2ecf20Sopenharmony_ci .config1 = { SG_USBPHY12PLL, 0x00010010 }, 1928c2ecf20Sopenharmony_ci }, 1938c2ecf20Sopenharmony_ci { 1948c2ecf20Sopenharmony_ci .config0 = { SG_USBPHY3CTRL, 0x05142400 }, 1958c2ecf20Sopenharmony_ci .config1 = { SG_USBPHY34PLL, 0x00010010 }, 1968c2ecf20Sopenharmony_ci }, 1978c2ecf20Sopenharmony_ci { 1988c2ecf20Sopenharmony_ci .config0 = { SG_USBPHY4CTRL, 0x05142400 }, 1998c2ecf20Sopenharmony_ci .config1 = { SG_USBPHY34PLL, 0x00010010 }, 2008c2ecf20Sopenharmony_ci }, 2018c2ecf20Sopenharmony_ci { /* sentinel */ } 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic const struct uniphier_u2phy_soc_data uniphier_ld11_data[] = { 2058c2ecf20Sopenharmony_ci { 2068c2ecf20Sopenharmony_ci .config0 = { SG_USBPHY1CTRL, 0x82280000 }, 2078c2ecf20Sopenharmony_ci .config1 = { SG_USBPHY1CTRL2, 0x00000106 }, 2088c2ecf20Sopenharmony_ci }, 2098c2ecf20Sopenharmony_ci { 2108c2ecf20Sopenharmony_ci .config0 = { SG_USBPHY2CTRL, 0x82280000 }, 2118c2ecf20Sopenharmony_ci .config1 = { SG_USBPHY2CTRL2, 0x00000106 }, 2128c2ecf20Sopenharmony_ci }, 2138c2ecf20Sopenharmony_ci { 2148c2ecf20Sopenharmony_ci .config0 = { SG_USBPHY3CTRL, 0x82280000 }, 2158c2ecf20Sopenharmony_ci .config1 = { SG_USBPHY3CTRL2, 0x00000106 }, 2168c2ecf20Sopenharmony_ci }, 2178c2ecf20Sopenharmony_ci { /* sentinel */ } 2188c2ecf20Sopenharmony_ci}; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic const struct of_device_id uniphier_u2phy_match[] = { 2218c2ecf20Sopenharmony_ci { 2228c2ecf20Sopenharmony_ci .compatible = "socionext,uniphier-pro4-usb2-phy", 2238c2ecf20Sopenharmony_ci .data = &uniphier_pro4_data, 2248c2ecf20Sopenharmony_ci }, 2258c2ecf20Sopenharmony_ci { 2268c2ecf20Sopenharmony_ci .compatible = "socionext,uniphier-ld11-usb2-phy", 2278c2ecf20Sopenharmony_ci .data = &uniphier_ld11_data, 2288c2ecf20Sopenharmony_ci }, 2298c2ecf20Sopenharmony_ci { /* sentinel */ } 2308c2ecf20Sopenharmony_ci}; 2318c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, uniphier_u2phy_match); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic struct platform_driver uniphier_u2phy_driver = { 2348c2ecf20Sopenharmony_ci .probe = uniphier_u2phy_probe, 2358c2ecf20Sopenharmony_ci .driver = { 2368c2ecf20Sopenharmony_ci .name = "uniphier-usb2-phy", 2378c2ecf20Sopenharmony_ci .of_match_table = uniphier_u2phy_match, 2388c2ecf20Sopenharmony_ci }, 2398c2ecf20Sopenharmony_ci}; 2408c2ecf20Sopenharmony_cimodule_platform_driver(uniphier_u2phy_driver); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>"); 2438c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("UniPhier PHY driver for USB2 controller"); 2448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 245