13fa4d734SSebastian Andrzej Siewior /* 23fa4d734SSebastian Andrzej Siewior * drivers/usb/otg/nop-usb-xceiv.c 33fa4d734SSebastian Andrzej Siewior * 43fa4d734SSebastian Andrzej Siewior * NOP USB transceiver for all USB transceiver which are either built-in 53fa4d734SSebastian Andrzej Siewior * into USB IP or which are mostly autonomous. 63fa4d734SSebastian Andrzej Siewior * 73fa4d734SSebastian Andrzej Siewior * Copyright (C) 2009 Texas Instruments Inc 83fa4d734SSebastian Andrzej Siewior * Author: Ajay Kumar Gupta <ajay.gupta@ti.com> 93fa4d734SSebastian Andrzej Siewior * 103fa4d734SSebastian Andrzej Siewior * This program is free software; you can redistribute it and/or modify 113fa4d734SSebastian Andrzej Siewior * it under the terms of the GNU General Public License as published by 123fa4d734SSebastian Andrzej Siewior * the Free Software Foundation; either version 2 of the License, or 133fa4d734SSebastian Andrzej Siewior * (at your option) any later version. 143fa4d734SSebastian Andrzej Siewior * 153fa4d734SSebastian Andrzej Siewior * This program is distributed in the hope that it will be useful, 163fa4d734SSebastian Andrzej Siewior * but WITHOUT ANY WARRANTY; without even the implied warranty of 173fa4d734SSebastian Andrzej Siewior * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 183fa4d734SSebastian Andrzej Siewior * GNU General Public License for more details. 193fa4d734SSebastian Andrzej Siewior * 203fa4d734SSebastian Andrzej Siewior * You should have received a copy of the GNU General Public License 213fa4d734SSebastian Andrzej Siewior * along with this program; if not, write to the Free Software 223fa4d734SSebastian Andrzej Siewior * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 233fa4d734SSebastian Andrzej Siewior * 243fa4d734SSebastian Andrzej Siewior * Current status: 253fa4d734SSebastian Andrzej Siewior * This provides a "nop" transceiver for PHYs which are 263fa4d734SSebastian Andrzej Siewior * autonomous such as isp1504, isp1707, etc. 273fa4d734SSebastian Andrzej Siewior */ 283fa4d734SSebastian Andrzej Siewior 293fa4d734SSebastian Andrzej Siewior #include <linux/module.h> 303fa4d734SSebastian Andrzej Siewior #include <linux/platform_device.h> 313fa4d734SSebastian Andrzej Siewior #include <linux/dma-mapping.h> 323fa4d734SSebastian Andrzej Siewior #include <linux/usb/otg.h> 33d7078df6SFelipe Balbi #include <linux/usb/usb_phy_generic.h> 343fa4d734SSebastian Andrzej Siewior #include <linux/slab.h> 353fa4d734SSebastian Andrzej Siewior #include <linux/clk.h> 363fa4d734SSebastian Andrzej Siewior #include <linux/regulator/consumer.h> 373fa4d734SSebastian Andrzej Siewior #include <linux/of.h> 38bd27fa44SRoger Quadros #include <linux/of_gpio.h> 39bd27fa44SRoger Quadros #include <linux/gpio.h> 40bd27fa44SRoger Quadros #include <linux/delay.h> 413fa4d734SSebastian Andrzej Siewior 4253b6fc28SSebastian Andrzej Siewior #include "phy-generic.h" 433fa4d734SSebastian Andrzej Siewior 442f36ff69SFelipe Balbi struct platform_device *usb_phy_generic_register(void) 453fa4d734SSebastian Andrzej Siewior { 462f36ff69SFelipe Balbi return platform_device_register_simple("usb_phy_generic", 472f36ff69SFelipe Balbi PLATFORM_DEVID_AUTO, NULL, 0); 483fa4d734SSebastian Andrzej Siewior } 494525beebSFelipe Balbi EXPORT_SYMBOL_GPL(usb_phy_generic_register); 503fa4d734SSebastian Andrzej Siewior 512f36ff69SFelipe Balbi void usb_phy_generic_unregister(struct platform_device *pdev) 523fa4d734SSebastian Andrzej Siewior { 532f36ff69SFelipe Balbi platform_device_unregister(pdev); 543fa4d734SSebastian Andrzej Siewior } 554525beebSFelipe Balbi EXPORT_SYMBOL_GPL(usb_phy_generic_unregister); 563fa4d734SSebastian Andrzej Siewior 573fa4d734SSebastian Andrzej Siewior static int nop_set_suspend(struct usb_phy *x, int suspend) 583fa4d734SSebastian Andrzej Siewior { 593fa4d734SSebastian Andrzej Siewior return 0; 603fa4d734SSebastian Andrzej Siewior } 613fa4d734SSebastian Andrzej Siewior 624525beebSFelipe Balbi static void nop_reset_set(struct usb_phy_generic *nop, int asserted) 63bd27fa44SRoger Quadros { 64bd27fa44SRoger Quadros int value; 65bd27fa44SRoger Quadros 66bd27fa44SRoger Quadros if (!gpio_is_valid(nop->gpio_reset)) 67bd27fa44SRoger Quadros return; 68bd27fa44SRoger Quadros 69bd27fa44SRoger Quadros value = asserted; 70bd27fa44SRoger Quadros if (nop->reset_active_low) 71bd27fa44SRoger Quadros value = !value; 72bd27fa44SRoger Quadros 73bd27fa44SRoger Quadros gpio_set_value_cansleep(nop->gpio_reset, value); 74bd27fa44SRoger Quadros 75bd27fa44SRoger Quadros if (!asserted) 76bd27fa44SRoger Quadros usleep_range(10000, 20000); 77bd27fa44SRoger Quadros } 78bd27fa44SRoger Quadros 7953b6fc28SSebastian Andrzej Siewior int usb_gen_phy_init(struct usb_phy *phy) 803fa4d734SSebastian Andrzej Siewior { 814525beebSFelipe Balbi struct usb_phy_generic *nop = dev_get_drvdata(phy->dev); 823fa4d734SSebastian Andrzej Siewior 833fa4d734SSebastian Andrzej Siewior if (!IS_ERR(nop->vcc)) { 843fa4d734SSebastian Andrzej Siewior if (regulator_enable(nop->vcc)) 853fa4d734SSebastian Andrzej Siewior dev_err(phy->dev, "Failed to enable power\n"); 863fa4d734SSebastian Andrzej Siewior } 873fa4d734SSebastian Andrzej Siewior 883fa4d734SSebastian Andrzej Siewior if (!IS_ERR(nop->clk)) 894d175f34SMark Brown clk_prepare_enable(nop->clk); 903fa4d734SSebastian Andrzej Siewior 913fa4d734SSebastian Andrzej Siewior /* De-assert RESET */ 92bd27fa44SRoger Quadros nop_reset_set(nop, 0); 933fa4d734SSebastian Andrzej Siewior 943fa4d734SSebastian Andrzej Siewior return 0; 953fa4d734SSebastian Andrzej Siewior } 9653b6fc28SSebastian Andrzej Siewior EXPORT_SYMBOL_GPL(usb_gen_phy_init); 973fa4d734SSebastian Andrzej Siewior 9853b6fc28SSebastian Andrzej Siewior void usb_gen_phy_shutdown(struct usb_phy *phy) 993fa4d734SSebastian Andrzej Siewior { 1004525beebSFelipe Balbi struct usb_phy_generic *nop = dev_get_drvdata(phy->dev); 1013fa4d734SSebastian Andrzej Siewior 1023fa4d734SSebastian Andrzej Siewior /* Assert RESET */ 103bd27fa44SRoger Quadros nop_reset_set(nop, 1); 1043fa4d734SSebastian Andrzej Siewior 1053fa4d734SSebastian Andrzej Siewior if (!IS_ERR(nop->clk)) 1064d175f34SMark Brown clk_disable_unprepare(nop->clk); 1073fa4d734SSebastian Andrzej Siewior 1083fa4d734SSebastian Andrzej Siewior if (!IS_ERR(nop->vcc)) { 1093fa4d734SSebastian Andrzej Siewior if (regulator_disable(nop->vcc)) 1103fa4d734SSebastian Andrzej Siewior dev_err(phy->dev, "Failed to disable power\n"); 1113fa4d734SSebastian Andrzej Siewior } 1123fa4d734SSebastian Andrzej Siewior } 11353b6fc28SSebastian Andrzej Siewior EXPORT_SYMBOL_GPL(usb_gen_phy_shutdown); 1143fa4d734SSebastian Andrzej Siewior 1153fa4d734SSebastian Andrzej Siewior static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) 1163fa4d734SSebastian Andrzej Siewior { 1173fa4d734SSebastian Andrzej Siewior if (!otg) 1183fa4d734SSebastian Andrzej Siewior return -ENODEV; 1193fa4d734SSebastian Andrzej Siewior 1203fa4d734SSebastian Andrzej Siewior if (!gadget) { 1213fa4d734SSebastian Andrzej Siewior otg->gadget = NULL; 1223fa4d734SSebastian Andrzej Siewior return -ENODEV; 1233fa4d734SSebastian Andrzej Siewior } 1243fa4d734SSebastian Andrzej Siewior 1253fa4d734SSebastian Andrzej Siewior otg->gadget = gadget; 126e47d9254SAntoine Tenart otg->state = OTG_STATE_B_IDLE; 1273fa4d734SSebastian Andrzej Siewior return 0; 1283fa4d734SSebastian Andrzej Siewior } 1293fa4d734SSebastian Andrzej Siewior 1303fa4d734SSebastian Andrzej Siewior static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) 1313fa4d734SSebastian Andrzej Siewior { 1323fa4d734SSebastian Andrzej Siewior if (!otg) 1333fa4d734SSebastian Andrzej Siewior return -ENODEV; 1343fa4d734SSebastian Andrzej Siewior 1353fa4d734SSebastian Andrzej Siewior if (!host) { 1363fa4d734SSebastian Andrzej Siewior otg->host = NULL; 1373fa4d734SSebastian Andrzej Siewior return -ENODEV; 1383fa4d734SSebastian Andrzej Siewior } 1393fa4d734SSebastian Andrzej Siewior 1403fa4d734SSebastian Andrzej Siewior otg->host = host; 1413fa4d734SSebastian Andrzej Siewior return 0; 1423fa4d734SSebastian Andrzej Siewior } 1433fa4d734SSebastian Andrzej Siewior 1444525beebSFelipe Balbi int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, 1454525beebSFelipe Balbi struct usb_phy_generic_platform_data *pdata) 14653b6fc28SSebastian Andrzej Siewior { 147af9f51c5SFelipe Balbi enum usb_phy_type type = USB_PHY_TYPE_USB2; 14853b6fc28SSebastian Andrzej Siewior int err; 14953b6fc28SSebastian Andrzej Siewior 150af9f51c5SFelipe Balbi u32 clk_rate = 0; 151af9f51c5SFelipe Balbi bool needs_vcc = false; 152af9f51c5SFelipe Balbi 153af9f51c5SFelipe Balbi nop->reset_active_low = true; /* default behaviour */ 154af9f51c5SFelipe Balbi 155af9f51c5SFelipe Balbi if (dev->of_node) { 156af9f51c5SFelipe Balbi struct device_node *node = dev->of_node; 15737cfbc42SHeikki Krogerus enum of_gpio_flags flags = 0; 158af9f51c5SFelipe Balbi 159af9f51c5SFelipe Balbi if (of_property_read_u32(node, "clock-frequency", &clk_rate)) 160af9f51c5SFelipe Balbi clk_rate = 0; 161af9f51c5SFelipe Balbi 162af9f51c5SFelipe Balbi needs_vcc = of_property_read_bool(node, "vcc-supply"); 163af9f51c5SFelipe Balbi nop->gpio_reset = of_get_named_gpio_flags(node, "reset-gpios", 164af9f51c5SFelipe Balbi 0, &flags); 165af9f51c5SFelipe Balbi if (nop->gpio_reset == -EPROBE_DEFER) 166af9f51c5SFelipe Balbi return -EPROBE_DEFER; 167af9f51c5SFelipe Balbi 168af9f51c5SFelipe Balbi nop->reset_active_low = flags & OF_GPIO_ACTIVE_LOW; 169af9f51c5SFelipe Balbi 170af9f51c5SFelipe Balbi } else if (pdata) { 171af9f51c5SFelipe Balbi type = pdata->type; 172af9f51c5SFelipe Balbi clk_rate = pdata->clk_rate; 173af9f51c5SFelipe Balbi needs_vcc = pdata->needs_vcc; 174af9f51c5SFelipe Balbi nop->gpio_reset = pdata->gpio_reset; 175dc52c574SAaro Koskinen } else { 176dc52c574SAaro Koskinen nop->gpio_reset = -1; 177af9f51c5SFelipe Balbi } 178af9f51c5SFelipe Balbi 17953b6fc28SSebastian Andrzej Siewior nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg), 18053b6fc28SSebastian Andrzej Siewior GFP_KERNEL); 18153b6fc28SSebastian Andrzej Siewior if (!nop->phy.otg) 18253b6fc28SSebastian Andrzej Siewior return -ENOMEM; 18353b6fc28SSebastian Andrzej Siewior 18453b6fc28SSebastian Andrzej Siewior nop->clk = devm_clk_get(dev, "main_clk"); 18553b6fc28SSebastian Andrzej Siewior if (IS_ERR(nop->clk)) { 18653b6fc28SSebastian Andrzej Siewior dev_dbg(dev, "Can't get phy clock: %ld\n", 18753b6fc28SSebastian Andrzej Siewior PTR_ERR(nop->clk)); 18853b6fc28SSebastian Andrzej Siewior } 18953b6fc28SSebastian Andrzej Siewior 19053b6fc28SSebastian Andrzej Siewior if (!IS_ERR(nop->clk) && clk_rate) { 19153b6fc28SSebastian Andrzej Siewior err = clk_set_rate(nop->clk, clk_rate); 19253b6fc28SSebastian Andrzej Siewior if (err) { 19353b6fc28SSebastian Andrzej Siewior dev_err(dev, "Error setting clock rate\n"); 19453b6fc28SSebastian Andrzej Siewior return err; 19553b6fc28SSebastian Andrzej Siewior } 19653b6fc28SSebastian Andrzej Siewior } 19753b6fc28SSebastian Andrzej Siewior 19853b6fc28SSebastian Andrzej Siewior nop->vcc = devm_regulator_get(dev, "vcc"); 19953b6fc28SSebastian Andrzej Siewior if (IS_ERR(nop->vcc)) { 20053b6fc28SSebastian Andrzej Siewior dev_dbg(dev, "Error getting vcc regulator: %ld\n", 20153b6fc28SSebastian Andrzej Siewior PTR_ERR(nop->vcc)); 20253b6fc28SSebastian Andrzej Siewior if (needs_vcc) 20353b6fc28SSebastian Andrzej Siewior return -EPROBE_DEFER; 20453b6fc28SSebastian Andrzej Siewior } 20553b6fc28SSebastian Andrzej Siewior 206bd27fa44SRoger Quadros if (gpio_is_valid(nop->gpio_reset)) { 207bd27fa44SRoger Quadros unsigned long gpio_flags; 208bd27fa44SRoger Quadros 209bd27fa44SRoger Quadros /* Assert RESET */ 210bd27fa44SRoger Quadros if (nop->reset_active_low) 211bd27fa44SRoger Quadros gpio_flags = GPIOF_OUT_INIT_LOW; 212bd27fa44SRoger Quadros else 213bd27fa44SRoger Quadros gpio_flags = GPIOF_OUT_INIT_HIGH; 214bd27fa44SRoger Quadros 215bd27fa44SRoger Quadros err = devm_gpio_request_one(dev, nop->gpio_reset, 216bd27fa44SRoger Quadros gpio_flags, dev_name(dev)); 217bd27fa44SRoger Quadros if (err) { 218bd27fa44SRoger Quadros dev_err(dev, "Error requesting RESET GPIO %d\n", 219bd27fa44SRoger Quadros nop->gpio_reset); 220bd27fa44SRoger Quadros return err; 221bd27fa44SRoger Quadros } 22253b6fc28SSebastian Andrzej Siewior } 22353b6fc28SSebastian Andrzej Siewior 22453b6fc28SSebastian Andrzej Siewior nop->dev = dev; 22553b6fc28SSebastian Andrzej Siewior nop->phy.dev = nop->dev; 22653b6fc28SSebastian Andrzej Siewior nop->phy.label = "nop-xceiv"; 22753b6fc28SSebastian Andrzej Siewior nop->phy.set_suspend = nop_set_suspend; 22853b6fc28SSebastian Andrzej Siewior nop->phy.type = type; 22953b6fc28SSebastian Andrzej Siewior 230e47d9254SAntoine Tenart nop->phy.otg->state = OTG_STATE_UNDEFINED; 23153b6fc28SSebastian Andrzej Siewior nop->phy.otg->phy = &nop->phy; 23253b6fc28SSebastian Andrzej Siewior nop->phy.otg->set_host = nop_set_host; 23353b6fc28SSebastian Andrzej Siewior nop->phy.otg->set_peripheral = nop_set_peripheral; 23453b6fc28SSebastian Andrzej Siewior 23553b6fc28SSebastian Andrzej Siewior return 0; 23653b6fc28SSebastian Andrzej Siewior } 23753b6fc28SSebastian Andrzej Siewior EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy); 23853b6fc28SSebastian Andrzej Siewior 2394525beebSFelipe Balbi static int usb_phy_generic_probe(struct platform_device *pdev) 2403fa4d734SSebastian Andrzej Siewior { 2413fa4d734SSebastian Andrzej Siewior struct device *dev = &pdev->dev; 2424525beebSFelipe Balbi struct usb_phy_generic *nop; 2433fa4d734SSebastian Andrzej Siewior int err; 2443fa4d734SSebastian Andrzej Siewior 24553b6fc28SSebastian Andrzej Siewior nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL); 24653b6fc28SSebastian Andrzej Siewior if (!nop) 24753b6fc28SSebastian Andrzej Siewior return -ENOMEM; 2483fa4d734SSebastian Andrzej Siewior 249af9f51c5SFelipe Balbi err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev)); 25053b6fc28SSebastian Andrzej Siewior if (err) 2513fa4d734SSebastian Andrzej Siewior return err; 2523fa4d734SSebastian Andrzej Siewior 25353b6fc28SSebastian Andrzej Siewior nop->phy.init = usb_gen_phy_init; 25453b6fc28SSebastian Andrzej Siewior nop->phy.shutdown = usb_gen_phy_shutdown; 2553fa4d734SSebastian Andrzej Siewior 2563fa4d734SSebastian Andrzej Siewior err = usb_add_phy_dev(&nop->phy); 2573fa4d734SSebastian Andrzej Siewior if (err) { 2583fa4d734SSebastian Andrzej Siewior dev_err(&pdev->dev, "can't register transceiver, err: %d\n", 2593fa4d734SSebastian Andrzej Siewior err); 2604d175f34SMark Brown return err; 2613fa4d734SSebastian Andrzej Siewior } 2623fa4d734SSebastian Andrzej Siewior 2633fa4d734SSebastian Andrzej Siewior platform_set_drvdata(pdev, nop); 2643fa4d734SSebastian Andrzej Siewior 2653fa4d734SSebastian Andrzej Siewior return 0; 2663fa4d734SSebastian Andrzej Siewior } 2673fa4d734SSebastian Andrzej Siewior 2684525beebSFelipe Balbi static int usb_phy_generic_remove(struct platform_device *pdev) 2693fa4d734SSebastian Andrzej Siewior { 2704525beebSFelipe Balbi struct usb_phy_generic *nop = platform_get_drvdata(pdev); 2713fa4d734SSebastian Andrzej Siewior 2723fa4d734SSebastian Andrzej Siewior usb_remove_phy(&nop->phy); 2733fa4d734SSebastian Andrzej Siewior 2743fa4d734SSebastian Andrzej Siewior return 0; 2753fa4d734SSebastian Andrzej Siewior } 2763fa4d734SSebastian Andrzej Siewior 2773fa4d734SSebastian Andrzej Siewior static const struct of_device_id nop_xceiv_dt_ids[] = { 2783fa4d734SSebastian Andrzej Siewior { .compatible = "usb-nop-xceiv" }, 2793fa4d734SSebastian Andrzej Siewior { } 2803fa4d734SSebastian Andrzej Siewior }; 2813fa4d734SSebastian Andrzej Siewior 2823fa4d734SSebastian Andrzej Siewior MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); 2833fa4d734SSebastian Andrzej Siewior 2844525beebSFelipe Balbi static struct platform_driver usb_phy_generic_driver = { 2854525beebSFelipe Balbi .probe = usb_phy_generic_probe, 2864525beebSFelipe Balbi .remove = usb_phy_generic_remove, 2873fa4d734SSebastian Andrzej Siewior .driver = { 2884525beebSFelipe Balbi .name = "usb_phy_generic", 2893fa4d734SSebastian Andrzej Siewior .owner = THIS_MODULE, 2903fa4d734SSebastian Andrzej Siewior .of_match_table = nop_xceiv_dt_ids, 2913fa4d734SSebastian Andrzej Siewior }, 2923fa4d734SSebastian Andrzej Siewior }; 2933fa4d734SSebastian Andrzej Siewior 2944525beebSFelipe Balbi static int __init usb_phy_generic_init(void) 2953fa4d734SSebastian Andrzej Siewior { 2964525beebSFelipe Balbi return platform_driver_register(&usb_phy_generic_driver); 2973fa4d734SSebastian Andrzej Siewior } 2984525beebSFelipe Balbi subsys_initcall(usb_phy_generic_init); 2993fa4d734SSebastian Andrzej Siewior 3004525beebSFelipe Balbi static void __exit usb_phy_generic_exit(void) 3013fa4d734SSebastian Andrzej Siewior { 3024525beebSFelipe Balbi platform_driver_unregister(&usb_phy_generic_driver); 3033fa4d734SSebastian Andrzej Siewior } 3044525beebSFelipe Balbi module_exit(usb_phy_generic_exit); 3053fa4d734SSebastian Andrzej Siewior 3064525beebSFelipe Balbi MODULE_ALIAS("platform:usb_phy_generic"); 3073fa4d734SSebastian Andrzej Siewior MODULE_AUTHOR("Texas Instruments Inc"); 3083fa4d734SSebastian Andrzej Siewior MODULE_DESCRIPTION("NOP USB Transceiver driver"); 3093fa4d734SSebastian Andrzej Siewior MODULE_LICENSE("GPL"); 310