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 36struct 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 46static 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 55static 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 72static 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 86static 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 92static 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(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(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 161static const struct of_device_id stih407_usb2_picophy_of_match[] = { 162 { .compatible = "st,stih407-usb2-phy" }, 163 { /*sentinel */ }, 164}; 165 166MODULE_DEVICE_TABLE(of, stih407_usb2_picophy_of_match); 167 168static 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 176module_platform_driver(stih407_usb2_picophy_driver); 177 178MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>"); 179MODULE_DESCRIPTION("STMicroelectronics Generic picoPHY driver for STiH407"); 180MODULE_LICENSE("GPL v2"); 181