1 /* 2 * drivers/usb/otg/nop-usb-xceiv.c 3 * 4 * NOP USB transceiver for all USB transceiver which are either built-in 5 * into USB IP or which are mostly autonomous. 6 * 7 * Copyright (C) 2009 Texas Instruments Inc 8 * Author: Ajay Kumar Gupta <ajay.gupta@ti.com> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 * 24 * Current status: 25 * This provides a "nop" transceiver for PHYs which are 26 * autonomous such as isp1504, isp1707, etc. 27 */ 28 29 #include <linux/module.h> 30 #include <linux/platform_device.h> 31 #include <linux/dma-mapping.h> 32 #include <linux/usb/otg.h> 33 #include <linux/usb/usb_phy_gen_xceiv.h> 34 #include <linux/slab.h> 35 #include <linux/clk.h> 36 #include <linux/regulator/consumer.h> 37 #include <linux/of.h> 38 #include <linux/of_gpio.h> 39 #include <linux/gpio.h> 40 #include <linux/delay.h> 41 42 #include "phy-generic.h" 43 44 static struct platform_device *pd; 45 46 void usb_nop_xceiv_register(void) 47 { 48 if (pd) 49 return; 50 pd = platform_device_register_simple("usb_phy_gen_xceiv", -1, NULL, 0); 51 if (!pd) { 52 pr_err("Unable to register generic usb transceiver\n"); 53 return; 54 } 55 } 56 EXPORT_SYMBOL(usb_nop_xceiv_register); 57 58 void usb_nop_xceiv_unregister(void) 59 { 60 platform_device_unregister(pd); 61 pd = NULL; 62 } 63 EXPORT_SYMBOL(usb_nop_xceiv_unregister); 64 65 static int nop_set_suspend(struct usb_phy *x, int suspend) 66 { 67 return 0; 68 } 69 70 static void nop_reset_set(struct usb_phy_gen_xceiv *nop, int asserted) 71 { 72 int value; 73 74 if (!gpio_is_valid(nop->gpio_reset)) 75 return; 76 77 value = asserted; 78 if (nop->reset_active_low) 79 value = !value; 80 81 gpio_set_value_cansleep(nop->gpio_reset, value); 82 83 if (!asserted) 84 usleep_range(10000, 20000); 85 } 86 87 int usb_gen_phy_init(struct usb_phy *phy) 88 { 89 struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); 90 91 if (!IS_ERR(nop->vcc)) { 92 if (regulator_enable(nop->vcc)) 93 dev_err(phy->dev, "Failed to enable power\n"); 94 } 95 96 if (!IS_ERR(nop->clk)) 97 clk_prepare_enable(nop->clk); 98 99 /* De-assert RESET */ 100 nop_reset_set(nop, 0); 101 102 return 0; 103 } 104 EXPORT_SYMBOL_GPL(usb_gen_phy_init); 105 106 void usb_gen_phy_shutdown(struct usb_phy *phy) 107 { 108 struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); 109 110 /* Assert RESET */ 111 nop_reset_set(nop, 1); 112 113 if (!IS_ERR(nop->clk)) 114 clk_disable_unprepare(nop->clk); 115 116 if (!IS_ERR(nop->vcc)) { 117 if (regulator_disable(nop->vcc)) 118 dev_err(phy->dev, "Failed to disable power\n"); 119 } 120 } 121 EXPORT_SYMBOL_GPL(usb_gen_phy_shutdown); 122 123 static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) 124 { 125 if (!otg) 126 return -ENODEV; 127 128 if (!gadget) { 129 otg->gadget = NULL; 130 return -ENODEV; 131 } 132 133 otg->gadget = gadget; 134 otg->phy->state = OTG_STATE_B_IDLE; 135 return 0; 136 } 137 138 static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) 139 { 140 if (!otg) 141 return -ENODEV; 142 143 if (!host) { 144 otg->host = NULL; 145 return -ENODEV; 146 } 147 148 otg->host = host; 149 return 0; 150 } 151 152 int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, 153 enum usb_phy_type type, u32 clk_rate, bool needs_vcc) 154 { 155 int err; 156 157 nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg), 158 GFP_KERNEL); 159 if (!nop->phy.otg) 160 return -ENOMEM; 161 162 nop->clk = devm_clk_get(dev, "main_clk"); 163 if (IS_ERR(nop->clk)) { 164 dev_dbg(dev, "Can't get phy clock: %ld\n", 165 PTR_ERR(nop->clk)); 166 } 167 168 if (!IS_ERR(nop->clk) && clk_rate) { 169 err = clk_set_rate(nop->clk, clk_rate); 170 if (err) { 171 dev_err(dev, "Error setting clock rate\n"); 172 return err; 173 } 174 } 175 176 nop->vcc = devm_regulator_get(dev, "vcc"); 177 if (IS_ERR(nop->vcc)) { 178 dev_dbg(dev, "Error getting vcc regulator: %ld\n", 179 PTR_ERR(nop->vcc)); 180 if (needs_vcc) 181 return -EPROBE_DEFER; 182 } 183 184 if (gpio_is_valid(nop->gpio_reset)) { 185 unsigned long gpio_flags; 186 187 /* Assert RESET */ 188 if (nop->reset_active_low) 189 gpio_flags = GPIOF_OUT_INIT_LOW; 190 else 191 gpio_flags = GPIOF_OUT_INIT_HIGH; 192 193 err = devm_gpio_request_one(dev, nop->gpio_reset, 194 gpio_flags, dev_name(dev)); 195 if (err) { 196 dev_err(dev, "Error requesting RESET GPIO %d\n", 197 nop->gpio_reset); 198 return err; 199 } 200 } 201 202 nop->dev = dev; 203 nop->phy.dev = nop->dev; 204 nop->phy.label = "nop-xceiv"; 205 nop->phy.set_suspend = nop_set_suspend; 206 nop->phy.state = OTG_STATE_UNDEFINED; 207 nop->phy.type = type; 208 209 nop->phy.otg->phy = &nop->phy; 210 nop->phy.otg->set_host = nop_set_host; 211 nop->phy.otg->set_peripheral = nop_set_peripheral; 212 213 ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); 214 return 0; 215 } 216 EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy); 217 218 static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) 219 { 220 struct device *dev = &pdev->dev; 221 struct usb_phy_gen_xceiv_platform_data *pdata = 222 dev_get_platdata(&pdev->dev); 223 struct usb_phy_gen_xceiv *nop; 224 enum usb_phy_type type = USB_PHY_TYPE_USB2; 225 int err; 226 u32 clk_rate = 0; 227 bool needs_vcc = false; 228 229 nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL); 230 if (!nop) 231 return -ENOMEM; 232 233 nop->reset_active_low = true; /* default behaviour */ 234 235 if (dev->of_node) { 236 struct device_node *node = dev->of_node; 237 enum of_gpio_flags flags; 238 239 if (of_property_read_u32(node, "clock-frequency", &clk_rate)) 240 clk_rate = 0; 241 242 needs_vcc = of_property_read_bool(node, "vcc-supply"); 243 nop->gpio_reset = of_get_named_gpio_flags(node, "reset-gpios", 244 0, &flags); 245 if (nop->gpio_reset == -EPROBE_DEFER) 246 return -EPROBE_DEFER; 247 248 nop->reset_active_low = flags & OF_GPIO_ACTIVE_LOW; 249 250 } else if (pdata) { 251 type = pdata->type; 252 clk_rate = pdata->clk_rate; 253 needs_vcc = pdata->needs_vcc; 254 nop->gpio_reset = pdata->gpio_reset; 255 } 256 257 err = usb_phy_gen_create_phy(dev, nop, type, clk_rate, needs_vcc); 258 if (err) 259 return err; 260 261 nop->phy.init = usb_gen_phy_init; 262 nop->phy.shutdown = usb_gen_phy_shutdown; 263 264 err = usb_add_phy_dev(&nop->phy); 265 if (err) { 266 dev_err(&pdev->dev, "can't register transceiver, err: %d\n", 267 err); 268 return err; 269 } 270 271 platform_set_drvdata(pdev, nop); 272 273 return 0; 274 275 return err; 276 } 277 278 static int usb_phy_gen_xceiv_remove(struct platform_device *pdev) 279 { 280 struct usb_phy_gen_xceiv *nop = platform_get_drvdata(pdev); 281 282 usb_remove_phy(&nop->phy); 283 284 return 0; 285 } 286 287 static const struct of_device_id nop_xceiv_dt_ids[] = { 288 { .compatible = "usb-nop-xceiv" }, 289 { } 290 }; 291 292 MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); 293 294 static struct platform_driver usb_phy_gen_xceiv_driver = { 295 .probe = usb_phy_gen_xceiv_probe, 296 .remove = usb_phy_gen_xceiv_remove, 297 .driver = { 298 .name = "usb_phy_gen_xceiv", 299 .owner = THIS_MODULE, 300 .of_match_table = nop_xceiv_dt_ids, 301 }, 302 }; 303 304 static int __init usb_phy_gen_xceiv_init(void) 305 { 306 return platform_driver_register(&usb_phy_gen_xceiv_driver); 307 } 308 subsys_initcall(usb_phy_gen_xceiv_init); 309 310 static void __exit usb_phy_gen_xceiv_exit(void) 311 { 312 platform_driver_unregister(&usb_phy_gen_xceiv_driver); 313 } 314 module_exit(usb_phy_gen_xceiv_exit); 315 316 MODULE_ALIAS("platform:usb_phy_gen_xceiv"); 317 MODULE_AUTHOR("Texas Instruments Inc"); 318 MODULE_DESCRIPTION("NOP USB Transceiver driver"); 319 MODULE_LICENSE("GPL"); 320