1*0b56e9a7SVivek Gautam /* 2*0b56e9a7SVivek Gautam * Copyright (C) 2014 STMicroelectronics 3*0b56e9a7SVivek Gautam * 4*0b56e9a7SVivek Gautam * STMicroelectronics Generic PHY driver for STiH407 USB2. 5*0b56e9a7SVivek Gautam * 6*0b56e9a7SVivek Gautam * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> 7*0b56e9a7SVivek Gautam * 8*0b56e9a7SVivek Gautam * This program is free software; you can redistribute it and/or modify 9*0b56e9a7SVivek Gautam * it under the terms of the GNU General Public License version 2, as 10*0b56e9a7SVivek Gautam * published by the Free Software Foundation. 11*0b56e9a7SVivek Gautam * 12*0b56e9a7SVivek Gautam */ 13*0b56e9a7SVivek Gautam #include <linux/platform_device.h> 14*0b56e9a7SVivek Gautam #include <linux/io.h> 15*0b56e9a7SVivek Gautam #include <linux/kernel.h> 16*0b56e9a7SVivek Gautam #include <linux/module.h> 17*0b56e9a7SVivek Gautam #include <linux/of.h> 18*0b56e9a7SVivek Gautam #include <linux/of_platform.h> 19*0b56e9a7SVivek Gautam #include <linux/clk.h> 20*0b56e9a7SVivek Gautam #include <linux/regmap.h> 21*0b56e9a7SVivek Gautam #include <linux/reset.h> 22*0b56e9a7SVivek Gautam #include <linux/mfd/syscon.h> 23*0b56e9a7SVivek Gautam #include <linux/phy/phy.h> 24*0b56e9a7SVivek Gautam 25*0b56e9a7SVivek Gautam #define PHYPARAM_REG 1 26*0b56e9a7SVivek Gautam #define PHYCTRL_REG 2 27*0b56e9a7SVivek Gautam 28*0b56e9a7SVivek Gautam /* Default PHY_SEL and REFCLKSEL configuration */ 29*0b56e9a7SVivek Gautam #define STIH407_USB_PICOPHY_CTRL_PORT_CONF 0x6 30*0b56e9a7SVivek Gautam #define STIH407_USB_PICOPHY_CTRL_PORT_MASK 0x1f 31*0b56e9a7SVivek Gautam 32*0b56e9a7SVivek Gautam /* ports parameters overriding */ 33*0b56e9a7SVivek Gautam #define STIH407_USB_PICOPHY_PARAM_DEF 0x39a4dc 34*0b56e9a7SVivek Gautam #define STIH407_USB_PICOPHY_PARAM_MASK 0xffffffff 35*0b56e9a7SVivek Gautam 36*0b56e9a7SVivek Gautam struct stih407_usb2_picophy { 37*0b56e9a7SVivek Gautam struct phy *phy; 38*0b56e9a7SVivek Gautam struct regmap *regmap; 39*0b56e9a7SVivek Gautam struct device *dev; 40*0b56e9a7SVivek Gautam struct reset_control *rstc; 41*0b56e9a7SVivek Gautam struct reset_control *rstport; 42*0b56e9a7SVivek Gautam int ctrl; 43*0b56e9a7SVivek Gautam int param; 44*0b56e9a7SVivek Gautam }; 45*0b56e9a7SVivek Gautam 46*0b56e9a7SVivek Gautam static int stih407_usb2_pico_ctrl(struct stih407_usb2_picophy *phy_dev) 47*0b56e9a7SVivek Gautam { 48*0b56e9a7SVivek Gautam reset_control_deassert(phy_dev->rstc); 49*0b56e9a7SVivek Gautam 50*0b56e9a7SVivek Gautam return regmap_update_bits(phy_dev->regmap, phy_dev->ctrl, 51*0b56e9a7SVivek Gautam STIH407_USB_PICOPHY_CTRL_PORT_MASK, 52*0b56e9a7SVivek Gautam STIH407_USB_PICOPHY_CTRL_PORT_CONF); 53*0b56e9a7SVivek Gautam } 54*0b56e9a7SVivek Gautam 55*0b56e9a7SVivek Gautam static int stih407_usb2_init_port(struct phy *phy) 56*0b56e9a7SVivek Gautam { 57*0b56e9a7SVivek Gautam int ret; 58*0b56e9a7SVivek Gautam struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy); 59*0b56e9a7SVivek Gautam 60*0b56e9a7SVivek Gautam stih407_usb2_pico_ctrl(phy_dev); 61*0b56e9a7SVivek Gautam 62*0b56e9a7SVivek Gautam ret = regmap_update_bits(phy_dev->regmap, 63*0b56e9a7SVivek Gautam phy_dev->param, 64*0b56e9a7SVivek Gautam STIH407_USB_PICOPHY_PARAM_MASK, 65*0b56e9a7SVivek Gautam STIH407_USB_PICOPHY_PARAM_DEF); 66*0b56e9a7SVivek Gautam if (ret) 67*0b56e9a7SVivek Gautam return ret; 68*0b56e9a7SVivek Gautam 69*0b56e9a7SVivek Gautam return reset_control_deassert(phy_dev->rstport); 70*0b56e9a7SVivek Gautam } 71*0b56e9a7SVivek Gautam 72*0b56e9a7SVivek Gautam static int stih407_usb2_exit_port(struct phy *phy) 73*0b56e9a7SVivek Gautam { 74*0b56e9a7SVivek Gautam struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy); 75*0b56e9a7SVivek Gautam 76*0b56e9a7SVivek Gautam /* 77*0b56e9a7SVivek Gautam * Only port reset is asserted, phy global reset is kept untouched 78*0b56e9a7SVivek Gautam * as other ports may still be active. When all ports are in reset 79*0b56e9a7SVivek Gautam * state, assumption is made that power will be cut off on the phy, in 80*0b56e9a7SVivek Gautam * case of suspend for instance. Theoretically, asserting individual 81*0b56e9a7SVivek Gautam * reset (like here) or global reset should be equivalent. 82*0b56e9a7SVivek Gautam */ 83*0b56e9a7SVivek Gautam return reset_control_assert(phy_dev->rstport); 84*0b56e9a7SVivek Gautam } 85*0b56e9a7SVivek Gautam 86*0b56e9a7SVivek Gautam static const struct phy_ops stih407_usb2_picophy_data = { 87*0b56e9a7SVivek Gautam .init = stih407_usb2_init_port, 88*0b56e9a7SVivek Gautam .exit = stih407_usb2_exit_port, 89*0b56e9a7SVivek Gautam .owner = THIS_MODULE, 90*0b56e9a7SVivek Gautam }; 91*0b56e9a7SVivek Gautam 92*0b56e9a7SVivek Gautam static int stih407_usb2_picophy_probe(struct platform_device *pdev) 93*0b56e9a7SVivek Gautam { 94*0b56e9a7SVivek Gautam struct stih407_usb2_picophy *phy_dev; 95*0b56e9a7SVivek Gautam struct device *dev = &pdev->dev; 96*0b56e9a7SVivek Gautam struct device_node *np = dev->of_node; 97*0b56e9a7SVivek Gautam struct phy_provider *phy_provider; 98*0b56e9a7SVivek Gautam struct phy *phy; 99*0b56e9a7SVivek Gautam int ret; 100*0b56e9a7SVivek Gautam 101*0b56e9a7SVivek Gautam phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); 102*0b56e9a7SVivek Gautam if (!phy_dev) 103*0b56e9a7SVivek Gautam return -ENOMEM; 104*0b56e9a7SVivek Gautam 105*0b56e9a7SVivek Gautam phy_dev->dev = dev; 106*0b56e9a7SVivek Gautam dev_set_drvdata(dev, phy_dev); 107*0b56e9a7SVivek Gautam 108*0b56e9a7SVivek Gautam phy_dev->rstc = devm_reset_control_get_shared(dev, "global"); 109*0b56e9a7SVivek Gautam if (IS_ERR(phy_dev->rstc)) { 110*0b56e9a7SVivek Gautam dev_err(dev, "failed to ctrl picoPHY reset\n"); 111*0b56e9a7SVivek Gautam return PTR_ERR(phy_dev->rstc); 112*0b56e9a7SVivek Gautam } 113*0b56e9a7SVivek Gautam 114*0b56e9a7SVivek Gautam phy_dev->rstport = devm_reset_control_get_exclusive(dev, "port"); 115*0b56e9a7SVivek Gautam if (IS_ERR(phy_dev->rstport)) { 116*0b56e9a7SVivek Gautam dev_err(dev, "failed to ctrl picoPHY reset\n"); 117*0b56e9a7SVivek Gautam return PTR_ERR(phy_dev->rstport); 118*0b56e9a7SVivek Gautam } 119*0b56e9a7SVivek Gautam 120*0b56e9a7SVivek Gautam /* Reset port by default: only deassert it in phy init */ 121*0b56e9a7SVivek Gautam reset_control_assert(phy_dev->rstport); 122*0b56e9a7SVivek Gautam 123*0b56e9a7SVivek Gautam phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); 124*0b56e9a7SVivek Gautam if (IS_ERR(phy_dev->regmap)) { 125*0b56e9a7SVivek Gautam dev_err(dev, "No syscfg phandle specified\n"); 126*0b56e9a7SVivek Gautam return PTR_ERR(phy_dev->regmap); 127*0b56e9a7SVivek Gautam } 128*0b56e9a7SVivek Gautam 129*0b56e9a7SVivek Gautam ret = of_property_read_u32_index(np, "st,syscfg", PHYPARAM_REG, 130*0b56e9a7SVivek Gautam &phy_dev->param); 131*0b56e9a7SVivek Gautam if (ret) { 132*0b56e9a7SVivek Gautam dev_err(dev, "can't get phyparam offset (%d)\n", ret); 133*0b56e9a7SVivek Gautam return ret; 134*0b56e9a7SVivek Gautam } 135*0b56e9a7SVivek Gautam 136*0b56e9a7SVivek Gautam ret = of_property_read_u32_index(np, "st,syscfg", PHYCTRL_REG, 137*0b56e9a7SVivek Gautam &phy_dev->ctrl); 138*0b56e9a7SVivek Gautam if (ret) { 139*0b56e9a7SVivek Gautam dev_err(dev, "can't get phyctrl offset (%d)\n", ret); 140*0b56e9a7SVivek Gautam return ret; 141*0b56e9a7SVivek Gautam } 142*0b56e9a7SVivek Gautam 143*0b56e9a7SVivek Gautam phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data); 144*0b56e9a7SVivek Gautam if (IS_ERR(phy)) { 145*0b56e9a7SVivek Gautam dev_err(dev, "failed to create Display Port PHY\n"); 146*0b56e9a7SVivek Gautam return PTR_ERR(phy); 147*0b56e9a7SVivek Gautam } 148*0b56e9a7SVivek Gautam 149*0b56e9a7SVivek Gautam phy_dev->phy = phy; 150*0b56e9a7SVivek Gautam phy_set_drvdata(phy, phy_dev); 151*0b56e9a7SVivek Gautam 152*0b56e9a7SVivek Gautam phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 153*0b56e9a7SVivek Gautam if (IS_ERR(phy_provider)) 154*0b56e9a7SVivek Gautam return PTR_ERR(phy_provider); 155*0b56e9a7SVivek Gautam 156*0b56e9a7SVivek Gautam dev_info(dev, "STiH407 USB Generic picoPHY driver probed!"); 157*0b56e9a7SVivek Gautam 158*0b56e9a7SVivek Gautam return 0; 159*0b56e9a7SVivek Gautam } 160*0b56e9a7SVivek Gautam 161*0b56e9a7SVivek Gautam static const struct of_device_id stih407_usb2_picophy_of_match[] = { 162*0b56e9a7SVivek Gautam { .compatible = "st,stih407-usb2-phy" }, 163*0b56e9a7SVivek Gautam { /*sentinel */ }, 164*0b56e9a7SVivek Gautam }; 165*0b56e9a7SVivek Gautam 166*0b56e9a7SVivek Gautam MODULE_DEVICE_TABLE(of, stih407_usb2_picophy_of_match); 167*0b56e9a7SVivek Gautam 168*0b56e9a7SVivek Gautam static struct platform_driver stih407_usb2_picophy_driver = { 169*0b56e9a7SVivek Gautam .probe = stih407_usb2_picophy_probe, 170*0b56e9a7SVivek Gautam .driver = { 171*0b56e9a7SVivek Gautam .name = "stih407-usb-genphy", 172*0b56e9a7SVivek Gautam .of_match_table = stih407_usb2_picophy_of_match, 173*0b56e9a7SVivek Gautam } 174*0b56e9a7SVivek Gautam }; 175*0b56e9a7SVivek Gautam 176*0b56e9a7SVivek Gautam module_platform_driver(stih407_usb2_picophy_driver); 177*0b56e9a7SVivek Gautam 178*0b56e9a7SVivek Gautam MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>"); 179*0b56e9a7SVivek Gautam MODULE_DESCRIPTION("STMicroelectronics Generic picoPHY driver for STiH407"); 180*0b56e9a7SVivek Gautam MODULE_LICENSE("GPL v2"); 181