linux/drivers/usb/phy/phy-generic.c
<<
>>
Prefs
   1/*
   2 * NOP USB transceiver for all USB transceiver which are either built-in
   3 * into USB IP or which are mostly autonomous.
   4 *
   5 * Copyright (C) 2009 Texas Instruments Inc
   6 * Author: Ajay Kumar Gupta <ajay.gupta@ti.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 as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21 *
  22 * Current status:
  23 *      This provides a "nop" transceiver for PHYs which are
  24 *      autonomous such as isp1504, isp1707, etc.
  25 */
  26
  27#include <linux/module.h>
  28#include <linux/platform_device.h>
  29#include <linux/dma-mapping.h>
  30#include <linux/usb/gadget.h>
  31#include <linux/usb/otg.h>
  32#include <linux/usb/usb_phy_generic.h>
  33#include <linux/slab.h>
  34#include <linux/clk.h>
  35#include <linux/regulator/consumer.h>
  36#include <linux/of.h>
  37#include <linux/of_gpio.h>
  38#include <linux/gpio.h>
  39#include <linux/delay.h>
  40
  41#include "phy-generic.h"
  42
  43#define VBUS_IRQ_FLAGS \
  44        (IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | \
  45                IRQF_ONESHOT)
  46
  47struct platform_device *usb_phy_generic_register(void)
  48{
  49        return platform_device_register_simple("usb_phy_generic",
  50                        PLATFORM_DEVID_AUTO, NULL, 0);
  51}
  52EXPORT_SYMBOL_GPL(usb_phy_generic_register);
  53
  54void usb_phy_generic_unregister(struct platform_device *pdev)
  55{
  56        platform_device_unregister(pdev);
  57}
  58EXPORT_SYMBOL_GPL(usb_phy_generic_unregister);
  59
  60static int nop_set_suspend(struct usb_phy *x, int suspend)
  61{
  62        return 0;
  63}
  64
  65static void nop_reset(struct usb_phy_generic *nop)
  66{
  67        if (!nop->gpiod_reset)
  68                return;
  69
  70        gpiod_set_value(nop->gpiod_reset, 1);
  71        usleep_range(10000, 20000);
  72        gpiod_set_value(nop->gpiod_reset, 0);
  73}
  74
  75/* interface to regulator framework */
  76static void nop_set_vbus_draw(struct usb_phy_generic *nop, unsigned mA)
  77{
  78        struct regulator *vbus_draw = nop->vbus_draw;
  79        int enabled;
  80        int ret;
  81
  82        if (!vbus_draw)
  83                return;
  84
  85        enabled = nop->vbus_draw_enabled;
  86        if (mA) {
  87                regulator_set_current_limit(vbus_draw, 0, 1000 * mA);
  88                if (!enabled) {
  89                        ret = regulator_enable(vbus_draw);
  90                        if (ret < 0)
  91                                return;
  92                        nop->vbus_draw_enabled = 1;
  93                }
  94        } else {
  95                if (enabled) {
  96                        ret = regulator_disable(vbus_draw);
  97                        if (ret < 0)
  98                                return;
  99                        nop->vbus_draw_enabled = 0;
 100                }
 101        }
 102        nop->mA = mA;
 103}
 104
 105
 106static irqreturn_t nop_gpio_vbus_thread(int irq, void *data)
 107{
 108        struct usb_phy_generic *nop = data;
 109        struct usb_otg *otg = nop->phy.otg;
 110        int vbus, status;
 111
 112        vbus = gpiod_get_value(nop->gpiod_vbus);
 113        if ((vbus ^ nop->vbus) == 0)
 114                return IRQ_HANDLED;
 115        nop->vbus = vbus;
 116
 117        if (vbus) {
 118                status = USB_EVENT_VBUS;
 119                otg->state = OTG_STATE_B_PERIPHERAL;
 120                nop->phy.last_event = status;
 121                if (otg->gadget)
 122                        usb_gadget_vbus_connect(otg->gadget);
 123
 124                /* drawing a "unit load" is *always* OK, except for OTG */
 125                nop_set_vbus_draw(nop, 100);
 126
 127                atomic_notifier_call_chain(&nop->phy.notifier, status,
 128                                           otg->gadget);
 129        } else {
 130                nop_set_vbus_draw(nop, 0);
 131
 132                if (otg->gadget)
 133                        usb_gadget_vbus_disconnect(otg->gadget);
 134                status = USB_EVENT_NONE;
 135                otg->state = OTG_STATE_B_IDLE;
 136                nop->phy.last_event = status;
 137
 138                atomic_notifier_call_chain(&nop->phy.notifier, status,
 139                                           otg->gadget);
 140        }
 141        return IRQ_HANDLED;
 142}
 143
 144int usb_gen_phy_init(struct usb_phy *phy)
 145{
 146        struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
 147
 148        if (!IS_ERR(nop->vcc)) {
 149                if (regulator_enable(nop->vcc))
 150                        dev_err(phy->dev, "Failed to enable power\n");
 151        }
 152
 153        if (!IS_ERR(nop->clk))
 154                clk_prepare_enable(nop->clk);
 155
 156        nop_reset(nop);
 157
 158        return 0;
 159}
 160EXPORT_SYMBOL_GPL(usb_gen_phy_init);
 161
 162void usb_gen_phy_shutdown(struct usb_phy *phy)
 163{
 164        struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
 165
 166        gpiod_set_value(nop->gpiod_reset, 1);
 167
 168        if (!IS_ERR(nop->clk))
 169                clk_disable_unprepare(nop->clk);
 170
 171        if (!IS_ERR(nop->vcc)) {
 172                if (regulator_disable(nop->vcc))
 173                        dev_err(phy->dev, "Failed to disable power\n");
 174        }
 175}
 176EXPORT_SYMBOL_GPL(usb_gen_phy_shutdown);
 177
 178static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
 179{
 180        if (!otg)
 181                return -ENODEV;
 182
 183        if (!gadget) {
 184                otg->gadget = NULL;
 185                return -ENODEV;
 186        }
 187
 188        otg->gadget = gadget;
 189        if (otg->state == OTG_STATE_B_PERIPHERAL)
 190                usb_gadget_vbus_connect(gadget);
 191        else
 192                otg->state = OTG_STATE_B_IDLE;
 193        return 0;
 194}
 195
 196static int nop_set_host(struct usb_otg *otg, struct usb_bus *host)
 197{
 198        if (!otg)
 199                return -ENODEV;
 200
 201        if (!host) {
 202                otg->host = NULL;
 203                return -ENODEV;
 204        }
 205
 206        otg->host = host;
 207        return 0;
 208}
 209
 210int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
 211                struct usb_phy_generic_platform_data *pdata)
 212{
 213        enum usb_phy_type type = USB_PHY_TYPE_USB2;
 214        int err = 0;
 215
 216        u32 clk_rate = 0;
 217        bool needs_vcc = false;
 218
 219        if (dev->of_node) {
 220                struct device_node *node = dev->of_node;
 221
 222                if (of_property_read_u32(node, "clock-frequency", &clk_rate))
 223                        clk_rate = 0;
 224
 225                needs_vcc = of_property_read_bool(node, "vcc-supply");
 226                nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
 227                                                           GPIOD_ASIS);
 228                err = PTR_ERR_OR_ZERO(nop->gpiod_reset);
 229                if (!err) {
 230                        nop->gpiod_vbus = devm_gpiod_get_optional(dev,
 231                                                         "vbus-detect",
 232                                                         GPIOD_ASIS);
 233                        err = PTR_ERR_OR_ZERO(nop->gpiod_vbus);
 234                }
 235        } else if (pdata) {
 236                type = pdata->type;
 237                clk_rate = pdata->clk_rate;
 238                needs_vcc = pdata->needs_vcc;
 239                if (gpio_is_valid(pdata->gpio_reset)) {
 240                        err = devm_gpio_request_one(dev, pdata->gpio_reset,
 241                                                    GPIOF_ACTIVE_LOW,
 242                                                    dev_name(dev));
 243                        if (!err)
 244                                nop->gpiod_reset =
 245                                        gpio_to_desc(pdata->gpio_reset);
 246                }
 247                nop->gpiod_vbus = pdata->gpiod_vbus;
 248        }
 249
 250        if (err == -EPROBE_DEFER)
 251                return -EPROBE_DEFER;
 252        if (err) {
 253                dev_err(dev, "Error requesting RESET or VBUS GPIO\n");
 254                return err;
 255        }
 256        if (nop->gpiod_reset)
 257                gpiod_direction_output(nop->gpiod_reset, 1);
 258
 259        nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg),
 260                        GFP_KERNEL);
 261        if (!nop->phy.otg)
 262                return -ENOMEM;
 263
 264        nop->clk = devm_clk_get(dev, "main_clk");
 265        if (IS_ERR(nop->clk)) {
 266                dev_dbg(dev, "Can't get phy clock: %ld\n",
 267                                        PTR_ERR(nop->clk));
 268        }
 269
 270        if (!IS_ERR(nop->clk) && clk_rate) {
 271                err = clk_set_rate(nop->clk, clk_rate);
 272                if (err) {
 273                        dev_err(dev, "Error setting clock rate\n");
 274                        return err;
 275                }
 276        }
 277
 278        nop->vcc = devm_regulator_get(dev, "vcc");
 279        if (IS_ERR(nop->vcc)) {
 280                dev_dbg(dev, "Error getting vcc regulator: %ld\n",
 281                                        PTR_ERR(nop->vcc));
 282                if (needs_vcc)
 283                        return -EPROBE_DEFER;
 284        }
 285
 286        nop->dev                = dev;
 287        nop->phy.dev            = nop->dev;
 288        nop->phy.label          = "nop-xceiv";
 289        nop->phy.set_suspend    = nop_set_suspend;
 290        nop->phy.type           = type;
 291
 292        nop->phy.otg->state             = OTG_STATE_UNDEFINED;
 293        nop->phy.otg->usb_phy           = &nop->phy;
 294        nop->phy.otg->set_host          = nop_set_host;
 295        nop->phy.otg->set_peripheral    = nop_set_peripheral;
 296
 297        return 0;
 298}
 299EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy);
 300
 301static int usb_phy_generic_probe(struct platform_device *pdev)
 302{
 303        struct device *dev = &pdev->dev;
 304        struct usb_phy_generic  *nop;
 305        int err;
 306
 307        nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL);
 308        if (!nop)
 309                return -ENOMEM;
 310
 311        err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev));
 312        if (err)
 313                return err;
 314        if (nop->gpiod_vbus) {
 315                err = devm_request_threaded_irq(&pdev->dev,
 316                                                gpiod_to_irq(nop->gpiod_vbus),
 317                                                NULL, nop_gpio_vbus_thread,
 318                                                VBUS_IRQ_FLAGS, "vbus_detect",
 319                                                nop);
 320                if (err) {
 321                        dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
 322                                gpiod_to_irq(nop->gpiod_vbus), err);
 323                        return err;
 324                }
 325        }
 326
 327        nop->phy.init           = usb_gen_phy_init;
 328        nop->phy.shutdown       = usb_gen_phy_shutdown;
 329
 330        err = usb_add_phy_dev(&nop->phy);
 331        if (err) {
 332                dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
 333                        err);
 334                return err;
 335        }
 336
 337        platform_set_drvdata(pdev, nop);
 338
 339        return 0;
 340}
 341
 342static int usb_phy_generic_remove(struct platform_device *pdev)
 343{
 344        struct usb_phy_generic *nop = platform_get_drvdata(pdev);
 345
 346        usb_remove_phy(&nop->phy);
 347
 348        return 0;
 349}
 350
 351static const struct of_device_id nop_xceiv_dt_ids[] = {
 352        { .compatible = "usb-nop-xceiv" },
 353        { }
 354};
 355
 356MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids);
 357
 358static struct platform_driver usb_phy_generic_driver = {
 359        .probe          = usb_phy_generic_probe,
 360        .remove         = usb_phy_generic_remove,
 361        .driver         = {
 362                .name   = "usb_phy_generic",
 363                .of_match_table = nop_xceiv_dt_ids,
 364        },
 365};
 366
 367static int __init usb_phy_generic_init(void)
 368{
 369        return platform_driver_register(&usb_phy_generic_driver);
 370}
 371subsys_initcall(usb_phy_generic_init);
 372
 373static void __exit usb_phy_generic_exit(void)
 374{
 375        platform_driver_unregister(&usb_phy_generic_driver);
 376}
 377module_exit(usb_phy_generic_exit);
 378
 379MODULE_ALIAS("platform:usb_phy_generic");
 380MODULE_AUTHOR("Texas Instruments Inc");
 381MODULE_DESCRIPTION("NOP USB Transceiver driver");
 382MODULE_LICENSE("GPL");
 383