linux/drivers/phy/phy-omap-usb2.c
<<
>>
Prefs
   1/*
   2 * omap-usb2.c - USB PHY, talking to musb controller in OMAP.
   3 *
   4 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License as published by
   7 * the Free Software Foundation; either version 2 of the License, or
   8 * (at your option) any later version.
   9 *
  10 * Author: Kishon Vijay Abraham I <kishon@ti.com>
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/platform_device.h>
  21#include <linux/slab.h>
  22#include <linux/of.h>
  23#include <linux/io.h>
  24#include <linux/usb/omap_usb.h>
  25#include <linux/usb/phy_companion.h>
  26#include <linux/clk.h>
  27#include <linux/err.h>
  28#include <linux/pm_runtime.h>
  29#include <linux/delay.h>
  30#include <linux/usb/omap_control_usb.h>
  31#include <linux/phy/phy.h>
  32#include <linux/of_platform.h>
  33
  34/**
  35 * omap_usb2_set_comparator - links the comparator present in the sytem with
  36 *      this phy
  37 * @comparator - the companion phy(comparator) for this phy
  38 *
  39 * The phy companion driver should call this API passing the phy_companion
  40 * filled with set_vbus and start_srp to be used by usb phy.
  41 *
  42 * For use by phy companion driver
  43 */
  44int omap_usb2_set_comparator(struct phy_companion *comparator)
  45{
  46        struct omap_usb *phy;
  47        struct usb_phy  *x = usb_get_phy(USB_PHY_TYPE_USB2);
  48
  49        if (IS_ERR(x))
  50                return -ENODEV;
  51
  52        phy = phy_to_omapusb(x);
  53        phy->comparator = comparator;
  54        return 0;
  55}
  56EXPORT_SYMBOL_GPL(omap_usb2_set_comparator);
  57
  58static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
  59{
  60        struct omap_usb *phy = phy_to_omapusb(otg->phy);
  61
  62        if (!phy->comparator)
  63                return -ENODEV;
  64
  65        return phy->comparator->set_vbus(phy->comparator, enabled);
  66}
  67
  68static int omap_usb_start_srp(struct usb_otg *otg)
  69{
  70        struct omap_usb *phy = phy_to_omapusb(otg->phy);
  71
  72        if (!phy->comparator)
  73                return -ENODEV;
  74
  75        return phy->comparator->start_srp(phy->comparator);
  76}
  77
  78static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
  79{
  80        struct usb_phy  *phy = otg->phy;
  81
  82        otg->host = host;
  83        if (!host)
  84                phy->state = OTG_STATE_UNDEFINED;
  85
  86        return 0;
  87}
  88
  89static int omap_usb_set_peripheral(struct usb_otg *otg,
  90                struct usb_gadget *gadget)
  91{
  92        struct usb_phy  *phy = otg->phy;
  93
  94        otg->gadget = gadget;
  95        if (!gadget)
  96                phy->state = OTG_STATE_UNDEFINED;
  97
  98        return 0;
  99}
 100
 101static int omap_usb2_suspend(struct usb_phy *x, int suspend)
 102{
 103        struct omap_usb *phy = phy_to_omapusb(x);
 104        int ret;
 105
 106        if (suspend && !phy->is_suspended) {
 107                omap_control_usb_phy_power(phy->control_dev, 0);
 108                pm_runtime_put_sync(phy->dev);
 109                phy->is_suspended = 1;
 110        } else if (!suspend && phy->is_suspended) {
 111                ret = pm_runtime_get_sync(phy->dev);
 112                if (ret < 0) {
 113                        dev_err(phy->dev, "get_sync failed with err %d\n", ret);
 114                        return ret;
 115                }
 116                omap_control_usb_phy_power(phy->control_dev, 1);
 117                phy->is_suspended = 0;
 118        }
 119
 120        return 0;
 121}
 122
 123static int omap_usb_power_off(struct phy *x)
 124{
 125        struct omap_usb *phy = phy_get_drvdata(x);
 126
 127        omap_control_usb_phy_power(phy->control_dev, 0);
 128
 129        return 0;
 130}
 131
 132static int omap_usb_power_on(struct phy *x)
 133{
 134        struct omap_usb *phy = phy_get_drvdata(x);
 135
 136        omap_control_usb_phy_power(phy->control_dev, 1);
 137
 138        return 0;
 139}
 140
 141static struct phy_ops ops = {
 142        .power_on       = omap_usb_power_on,
 143        .power_off      = omap_usb_power_off,
 144        .owner          = THIS_MODULE,
 145};
 146
 147static int omap_usb2_probe(struct platform_device *pdev)
 148{
 149        struct omap_usb *phy;
 150        struct phy *generic_phy;
 151        struct phy_provider *phy_provider;
 152        struct usb_otg *otg;
 153        struct device_node *node = pdev->dev.of_node;
 154        struct device_node *control_node;
 155        struct platform_device *control_pdev;
 156
 157        if (!node)
 158                return -EINVAL;
 159
 160        phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
 161        if (!phy) {
 162                dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n");
 163                return -ENOMEM;
 164        }
 165
 166        otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
 167        if (!otg) {
 168                dev_err(&pdev->dev, "unable to allocate memory for USB OTG\n");
 169                return -ENOMEM;
 170        }
 171
 172        phy->dev                = &pdev->dev;
 173
 174        phy->phy.dev            = phy->dev;
 175        phy->phy.label          = "omap-usb2";
 176        phy->phy.set_suspend    = omap_usb2_suspend;
 177        phy->phy.otg            = otg;
 178        phy->phy.type           = USB_PHY_TYPE_USB2;
 179
 180        control_node = of_parse_phandle(node, "ctrl-module", 0);
 181        if (!control_node) {
 182                dev_err(&pdev->dev, "Failed to get control device phandle\n");
 183                return -EINVAL;
 184        }
 185
 186        control_pdev = of_find_device_by_node(control_node);
 187        if (!control_pdev) {
 188                dev_err(&pdev->dev, "Failed to get control device\n");
 189                return -EINVAL;
 190        }
 191
 192        phy->control_dev = &control_pdev->dev;
 193
 194        phy->is_suspended       = 1;
 195        omap_control_usb_phy_power(phy->control_dev, 0);
 196
 197        otg->set_host           = omap_usb_set_host;
 198        otg->set_peripheral     = omap_usb_set_peripheral;
 199        otg->set_vbus           = omap_usb_set_vbus;
 200        otg->start_srp          = omap_usb_start_srp;
 201        otg->phy                = &phy->phy;
 202
 203        platform_set_drvdata(pdev, phy);
 204        pm_runtime_enable(phy->dev);
 205
 206        generic_phy = devm_phy_create(phy->dev, &ops, NULL);
 207        if (IS_ERR(generic_phy))
 208                return PTR_ERR(generic_phy);
 209
 210        phy_set_drvdata(generic_phy, phy);
 211
 212        phy_provider = devm_of_phy_provider_register(phy->dev,
 213                        of_phy_simple_xlate);
 214        if (IS_ERR(phy_provider))
 215                return PTR_ERR(phy_provider);
 216
 217        phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
 218        if (IS_ERR(phy->wkupclk)) {
 219                dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
 220                return PTR_ERR(phy->wkupclk);
 221        }
 222        clk_prepare(phy->wkupclk);
 223
 224        phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m");
 225        if (IS_ERR(phy->optclk))
 226                dev_vdbg(&pdev->dev, "unable to get refclk960m\n");
 227        else
 228                clk_prepare(phy->optclk);
 229
 230        usb_add_phy_dev(&phy->phy);
 231
 232        return 0;
 233}
 234
 235static int omap_usb2_remove(struct platform_device *pdev)
 236{
 237        struct omap_usb *phy = platform_get_drvdata(pdev);
 238
 239        clk_unprepare(phy->wkupclk);
 240        if (!IS_ERR(phy->optclk))
 241                clk_unprepare(phy->optclk);
 242        usb_remove_phy(&phy->phy);
 243
 244        return 0;
 245}
 246
 247#ifdef CONFIG_PM_RUNTIME
 248
 249static int omap_usb2_runtime_suspend(struct device *dev)
 250{
 251        struct platform_device  *pdev = to_platform_device(dev);
 252        struct omap_usb *phy = platform_get_drvdata(pdev);
 253
 254        clk_disable(phy->wkupclk);
 255        if (!IS_ERR(phy->optclk))
 256                clk_disable(phy->optclk);
 257
 258        return 0;
 259}
 260
 261static int omap_usb2_runtime_resume(struct device *dev)
 262{
 263        struct platform_device  *pdev = to_platform_device(dev);
 264        struct omap_usb *phy = platform_get_drvdata(pdev);
 265        int ret;
 266
 267        ret = clk_enable(phy->wkupclk);
 268        if (ret < 0) {
 269                dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
 270                goto err0;
 271        }
 272
 273        if (!IS_ERR(phy->optclk)) {
 274                ret = clk_enable(phy->optclk);
 275                if (ret < 0) {
 276                        dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
 277                        goto err1;
 278                }
 279        }
 280
 281        return 0;
 282
 283err1:
 284        clk_disable(phy->wkupclk);
 285
 286err0:
 287        return ret;
 288}
 289
 290static const struct dev_pm_ops omap_usb2_pm_ops = {
 291        SET_RUNTIME_PM_OPS(omap_usb2_runtime_suspend, omap_usb2_runtime_resume,
 292                NULL)
 293};
 294
 295#define DEV_PM_OPS     (&omap_usb2_pm_ops)
 296#else
 297#define DEV_PM_OPS     NULL
 298#endif
 299
 300#ifdef CONFIG_OF
 301static const struct of_device_id omap_usb2_id_table[] = {
 302        { .compatible = "ti,omap-usb2" },
 303        {}
 304};
 305MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
 306#endif
 307
 308static struct platform_driver omap_usb2_driver = {
 309        .probe          = omap_usb2_probe,
 310        .remove         = omap_usb2_remove,
 311        .driver         = {
 312                .name   = "omap-usb2",
 313                .owner  = THIS_MODULE,
 314                .pm     = DEV_PM_OPS,
 315                .of_match_table = of_match_ptr(omap_usb2_id_table),
 316        },
 317};
 318
 319module_platform_driver(omap_usb2_driver);
 320
 321MODULE_ALIAS("platform: omap_usb2");
 322MODULE_AUTHOR("Texas Instruments Inc.");
 323MODULE_DESCRIPTION("OMAP USB2 phy driver");
 324MODULE_LICENSE("GPL v2");
 325
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.