1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 20b56e9a7SVivek Gautam /* 30b56e9a7SVivek Gautam * Copyright (C) 2014 STMicroelectronics 40b56e9a7SVivek Gautam * 50b56e9a7SVivek Gautam * STMicroelectronics Generic PHY driver for STiH407 USB2. 60b56e9a7SVivek Gautam * 70b56e9a7SVivek Gautam * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> 80b56e9a7SVivek Gautam */ 90b56e9a7SVivek Gautam #include <linux/platform_device.h> 100b56e9a7SVivek Gautam #include <linux/io.h> 110b56e9a7SVivek Gautam #include <linux/kernel.h> 120b56e9a7SVivek Gautam #include <linux/module.h> 130b56e9a7SVivek Gautam #include <linux/of.h> 140b56e9a7SVivek Gautam #include <linux/of_platform.h> 150b56e9a7SVivek Gautam #include <linux/clk.h> 160b56e9a7SVivek Gautam #include <linux/regmap.h> 170b56e9a7SVivek Gautam #include <linux/reset.h> 180b56e9a7SVivek Gautam #include <linux/mfd/syscon.h> 190b56e9a7SVivek Gautam #include <linux/phy/phy.h> 200b56e9a7SVivek Gautam 210b56e9a7SVivek Gautam #define PHYPARAM_REG 1 220b56e9a7SVivek Gautam #define PHYCTRL_REG 2 230b56e9a7SVivek Gautam 240b56e9a7SVivek Gautam /* Default PHY_SEL and REFCLKSEL configuration */ 250b56e9a7SVivek Gautam #define STIH407_USB_PICOPHY_CTRL_PORT_CONF 0x6 260b56e9a7SVivek Gautam #define STIH407_USB_PICOPHY_CTRL_PORT_MASK 0x1f 270b56e9a7SVivek Gautam 280b56e9a7SVivek Gautam /* ports parameters overriding */ 290b56e9a7SVivek Gautam #define STIH407_USB_PICOPHY_PARAM_DEF 0x39a4dc 300b56e9a7SVivek Gautam #define STIH407_USB_PICOPHY_PARAM_MASK 0xffffffff 310b56e9a7SVivek Gautam 320b56e9a7SVivek Gautam struct stih407_usb2_picophy { 330b56e9a7SVivek Gautam struct phy *phy; 340b56e9a7SVivek Gautam struct regmap *regmap; 350b56e9a7SVivek Gautam struct device *dev; 360b56e9a7SVivek Gautam struct reset_control *rstc; 370b56e9a7SVivek Gautam struct reset_control *rstport; 380b56e9a7SVivek Gautam int ctrl; 390b56e9a7SVivek Gautam int param; 400b56e9a7SVivek Gautam }; 410b56e9a7SVivek Gautam 420b56e9a7SVivek Gautam static int stih407_usb2_pico_ctrl(struct stih407_usb2_picophy *phy_dev) 430b56e9a7SVivek Gautam { 440b56e9a7SVivek Gautam reset_control_deassert(phy_dev->rstc); 450b56e9a7SVivek Gautam 460b56e9a7SVivek Gautam return regmap_update_bits(phy_dev->regmap, phy_dev->ctrl, 470b56e9a7SVivek Gautam STIH407_USB_PICOPHY_CTRL_PORT_MASK, 480b56e9a7SVivek Gautam STIH407_USB_PICOPHY_CTRL_PORT_CONF); 490b56e9a7SVivek Gautam } 500b56e9a7SVivek Gautam 510b56e9a7SVivek Gautam static int stih407_usb2_init_port(struct phy *phy) 520b56e9a7SVivek Gautam { 530b56e9a7SVivek Gautam int ret; 540b56e9a7SVivek Gautam struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy); 550b56e9a7SVivek Gautam 560b56e9a7SVivek Gautam stih407_usb2_pico_ctrl(phy_dev); 570b56e9a7SVivek Gautam 580b56e9a7SVivek Gautam ret = regmap_update_bits(phy_dev->regmap, 590b56e9a7SVivek Gautam phy_dev->param, 600b56e9a7SVivek Gautam STIH407_USB_PICOPHY_PARAM_MASK, 610b56e9a7SVivek Gautam STIH407_USB_PICOPHY_PARAM_DEF); 620b56e9a7SVivek Gautam if (ret) 630b56e9a7SVivek Gautam return ret; 640b56e9a7SVivek Gautam 650b56e9a7SVivek Gautam return reset_control_deassert(phy_dev->rstport); 660b56e9a7SVivek Gautam } 670b56e9a7SVivek Gautam 680b56e9a7SVivek Gautam static int stih407_usb2_exit_port(struct phy *phy) 690b56e9a7SVivek Gautam { 700b56e9a7SVivek Gautam struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy); 710b56e9a7SVivek Gautam 720b56e9a7SVivek Gautam /* 730b56e9a7SVivek Gautam * Only port reset is asserted, phy global reset is kept untouched 740b56e9a7SVivek Gautam * as other ports may still be active. When all ports are in reset 750b56e9a7SVivek Gautam * state, assumption is made that power will be cut off on the phy, in 760b56e9a7SVivek Gautam * case of suspend for instance. Theoretically, asserting individual 770b56e9a7SVivek Gautam * reset (like here) or global reset should be equivalent. 780b56e9a7SVivek Gautam */ 790b56e9a7SVivek Gautam return reset_control_assert(phy_dev->rstport); 800b56e9a7SVivek Gautam } 810b56e9a7SVivek Gautam 820b56e9a7SVivek Gautam static const struct phy_ops stih407_usb2_picophy_data = { 830b56e9a7SVivek Gautam .init = stih407_usb2_init_port, 840b56e9a7SVivek Gautam .exit = stih407_usb2_exit_port, 850b56e9a7SVivek Gautam .owner = THIS_MODULE, 860b56e9a7SVivek Gautam }; 870b56e9a7SVivek Gautam 880b56e9a7SVivek Gautam static int stih407_usb2_picophy_probe(struct platform_device *pdev) 890b56e9a7SVivek Gautam { 900b56e9a7SVivek Gautam struct stih407_usb2_picophy *phy_dev; 910b56e9a7SVivek Gautam struct device *dev = &pdev->dev; 920b56e9a7SVivek Gautam struct device_node *np = dev->of_node; 930b56e9a7SVivek Gautam struct phy_provider *phy_provider; 940b56e9a7SVivek Gautam struct phy *phy; 950b56e9a7SVivek Gautam int ret; 960b56e9a7SVivek Gautam 970b56e9a7SVivek Gautam phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); 980b56e9a7SVivek Gautam if (!phy_dev) 990b56e9a7SVivek Gautam return -ENOMEM; 1000b56e9a7SVivek Gautam 1010b56e9a7SVivek Gautam phy_dev->dev = dev; 1020b56e9a7SVivek Gautam dev_set_drvdata(dev, phy_dev); 1030b56e9a7SVivek Gautam 1040b56e9a7SVivek Gautam phy_dev->rstc = devm_reset_control_get_shared(dev, "global"); 1050b56e9a7SVivek Gautam if (IS_ERR(phy_dev->rstc)) { 1060b56e9a7SVivek Gautam dev_err(dev, "failed to ctrl picoPHY reset\n"); 1070b56e9a7SVivek Gautam return PTR_ERR(phy_dev->rstc); 1080b56e9a7SVivek Gautam } 1090b56e9a7SVivek Gautam 1100b56e9a7SVivek Gautam phy_dev->rstport = devm_reset_control_get_exclusive(dev, "port"); 1110b56e9a7SVivek Gautam if (IS_ERR(phy_dev->rstport)) { 1120b56e9a7SVivek Gautam dev_err(dev, "failed to ctrl picoPHY reset\n"); 1130b56e9a7SVivek Gautam return PTR_ERR(phy_dev->rstport); 1140b56e9a7SVivek Gautam } 1150b56e9a7SVivek Gautam 1160b56e9a7SVivek Gautam /* Reset port by default: only deassert it in phy init */ 1170b56e9a7SVivek Gautam reset_control_assert(phy_dev->rstport); 1180b56e9a7SVivek Gautam 1190b56e9a7SVivek Gautam phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); 1200b56e9a7SVivek Gautam if (IS_ERR(phy_dev->regmap)) { 1210b56e9a7SVivek Gautam dev_err(dev, "No syscfg phandle specified\n"); 1220b56e9a7SVivek Gautam return PTR_ERR(phy_dev->regmap); 1230b56e9a7SVivek Gautam } 1240b56e9a7SVivek Gautam 1250b56e9a7SVivek Gautam ret = of_property_read_u32_index(np, "st,syscfg", PHYPARAM_REG, 1260b56e9a7SVivek Gautam &phy_dev->param); 1270b56e9a7SVivek Gautam if (ret) { 1280b56e9a7SVivek Gautam dev_err(dev, "can't get phyparam offset (%d)\n", ret); 1290b56e9a7SVivek Gautam return ret; 1300b56e9a7SVivek Gautam } 1310b56e9a7SVivek Gautam 1320b56e9a7SVivek Gautam ret = of_property_read_u32_index(np, "st,syscfg", PHYCTRL_REG, 1330b56e9a7SVivek Gautam &phy_dev->ctrl); 1340b56e9a7SVivek Gautam if (ret) { 1350b56e9a7SVivek Gautam dev_err(dev, "can't get phyctrl offset (%d)\n", ret); 1360b56e9a7SVivek Gautam return ret; 1370b56e9a7SVivek Gautam } 1380b56e9a7SVivek Gautam 1390b56e9a7SVivek Gautam phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data); 1400b56e9a7SVivek Gautam if (IS_ERR(phy)) { 1410b56e9a7SVivek Gautam dev_err(dev, "failed to create Display Port PHY\n"); 1420b56e9a7SVivek Gautam return PTR_ERR(phy); 1430b56e9a7SVivek Gautam } 1440b56e9a7SVivek Gautam 1450b56e9a7SVivek Gautam phy_dev->phy = phy; 1460b56e9a7SVivek Gautam phy_set_drvdata(phy, phy_dev); 1470b56e9a7SVivek Gautam 1480b56e9a7SVivek Gautam phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 1490b56e9a7SVivek Gautam if (IS_ERR(phy_provider)) 1500b56e9a7SVivek Gautam return PTR_ERR(phy_provider); 1510b56e9a7SVivek Gautam 1520b56e9a7SVivek Gautam dev_info(dev, "STiH407 USB Generic picoPHY driver probed!"); 1530b56e9a7SVivek Gautam 1540b56e9a7SVivek Gautam return 0; 1550b56e9a7SVivek Gautam } 1560b56e9a7SVivek Gautam 1570b56e9a7SVivek Gautam static const struct of_device_id stih407_usb2_picophy_of_match[] = { 1580b56e9a7SVivek Gautam { .compatible = "st,stih407-usb2-phy" }, 1590b56e9a7SVivek Gautam { /*sentinel */ }, 1600b56e9a7SVivek Gautam }; 1610b56e9a7SVivek Gautam 1620b56e9a7SVivek Gautam MODULE_DEVICE_TABLE(of, stih407_usb2_picophy_of_match); 1630b56e9a7SVivek Gautam 1640b56e9a7SVivek Gautam static struct platform_driver stih407_usb2_picophy_driver = { 1650b56e9a7SVivek Gautam .probe = stih407_usb2_picophy_probe, 1660b56e9a7SVivek Gautam .driver = { 1670b56e9a7SVivek Gautam .name = "stih407-usb-genphy", 1680b56e9a7SVivek Gautam .of_match_table = stih407_usb2_picophy_of_match, 1690b56e9a7SVivek Gautam } 1700b56e9a7SVivek Gautam }; 1710b56e9a7SVivek Gautam 1720b56e9a7SVivek Gautam module_platform_driver(stih407_usb2_picophy_driver); 1730b56e9a7SVivek Gautam 1740b56e9a7SVivek Gautam MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>"); 1750b56e9a7SVivek Gautam MODULE_DESCRIPTION("STMicroelectronics Generic picoPHY driver for STiH407"); 1760b56e9a7SVivek Gautam MODULE_LICENSE("GPL v2"); 177