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/* Default PHY_SEL and REFCLKSEL configuration */ 26#define STIH407_USB_PICOPHY_CTRL_PORT_CONF 0x6 27#define STIH407_USB_PICOPHY_CTRL_PORT_MASK 0x1f 28 29/* ports parameters overriding */ 30#define STIH407_USB_PICOPHY_PARAM_DEF 0x39a4dc 31#define STIH407_USB_PICOPHY_PARAM_MASK 0xffffffff 32 33struct stih407_usb2_picophy { 34 struct phy *phy; 35 struct regmap *regmap; 36 struct device *dev; 37 struct reset_control *rstc; 38 struct reset_control *rstport; 39 int ctrl; 40 int param; 41}; 42 43static int stih407_usb2_pico_ctrl(struct stih407_usb2_picophy *phy_dev) 44{ 45 reset_control_deassert(phy_dev->rstc); 46 47 return regmap_update_bits(phy_dev->regmap, phy_dev->ctrl, 48 STIH407_USB_PICOPHY_CTRL_PORT_MASK, 49 STIH407_USB_PICOPHY_CTRL_PORT_CONF); 50} 51 52static int stih407_usb2_init_port(struct phy *phy) 53{ 54 int ret; 55 struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy); 56 57 stih407_usb2_pico_ctrl(phy_dev); 58 59 ret = regmap_update_bits(phy_dev->regmap, 60 phy_dev->param, 61 STIH407_USB_PICOPHY_PARAM_MASK, 62 STIH407_USB_PICOPHY_PARAM_DEF); 63 if (ret) 64 return ret; 65 66 return reset_control_deassert(phy_dev->rstport); 67} 68 69static int stih407_usb2_exit_port(struct phy *phy) 70{ 71 struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy); 72 73 /* 74 * Only port reset is asserted, phy global reset is kept untouched 75 * as other ports may still be active. When all ports are in reset 76 * state, assumption is made that power will be cut off on the phy, in 77 * case of suspend for instance. Theoretically, asserting individual 78 * reset (like here) or global reset should be equivalent. 79 */ 80 return reset_control_assert(phy_dev->rstport); 81} 82 83static const struct phy_ops stih407_usb2_picophy_data = { 84 .init = stih407_usb2_init_port, 85 .exit = stih407_usb2_exit_port, 86 .owner = THIS_MODULE, 87}; 88 89static int stih407_usb2_picophy_probe(struct platform_device *pdev) 90{ 91 struct stih407_usb2_picophy *phy_dev; 92 struct device *dev = &pdev->dev; 93 struct device_node *np = dev->of_node; 94 struct phy_provider *phy_provider; 95 struct phy *phy; 96 struct resource *res; 97 98 phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); 99 if (!phy_dev) 100 return -ENOMEM; 101 102 phy_dev->dev = dev; 103 dev_set_drvdata(dev, phy_dev); 104 105 phy_dev->rstc = devm_reset_control_get(dev, "global"); 106 if (IS_ERR(phy_dev->rstc)) { 107 dev_err(dev, "failed to ctrl picoPHY reset\n"); 108 return PTR_ERR(phy_dev->rstc); 109 } 110 111 phy_dev->rstport = devm_reset_control_get(dev, "port"); 112 if (IS_ERR(phy_dev->rstport)) { 113 dev_err(dev, "failed to ctrl picoPHY reset\n"); 114 return PTR_ERR(phy_dev->rstport); 115 } 116 117 /* Reset port by default: only deassert it in phy init */ 118 reset_control_assert(phy_dev->rstport); 119 120 phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); 121 if (IS_ERR(phy_dev->regmap)) { 122 dev_err(dev, "No syscfg phandle specified\n"); 123 return PTR_ERR(phy_dev->regmap); 124 } 125 126 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); 127 if (!res) { 128 dev_err(dev, "No ctrl reg found\n"); 129 return -ENXIO; 130 } 131 phy_dev->ctrl = res->start; 132 133 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "param"); 134 if (!res) { 135 dev_err(dev, "No param reg found\n"); 136 return -ENXIO; 137 } 138 phy_dev->param = res->start; 139 140 phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data, NULL); 141 if (IS_ERR(phy)) { 142 dev_err(dev, "failed to create Display Port PHY\n"); 143 return PTR_ERR(phy); 144 } 145 146 phy_dev->phy = phy; 147 phy_set_drvdata(phy, phy_dev); 148 149 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 150 if (IS_ERR(phy_provider)) 151 return PTR_ERR(phy_provider); 152 153 dev_info(dev, "STiH407 USB Generic picoPHY driver probed!"); 154 155 return 0; 156} 157 158static const struct of_device_id stih407_usb2_picophy_of_match[] = { 159 { .compatible = "st,stih407-usb2-phy" }, 160 { /*sentinel */ }, 161}; 162 163MODULE_DEVICE_TABLE(of, stih407_usb2_picophy_of_match); 164 165static struct platform_driver stih407_usb2_picophy_driver = { 166 .probe = stih407_usb2_picophy_probe, 167 .driver = { 168 .name = "stih407-usb-genphy", 169 .of_match_table = stih407_usb2_picophy_of_match, 170 } 171}; 172 173module_platform_driver(stih407_usb2_picophy_driver); 174 175MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>"); 176MODULE_DESCRIPTION("STMicroelectronics Generic picoPHY driver for STiH407"); 177MODULE_LICENSE("GPL v2"); 178