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