linux/drivers/usb/phy/phy-omap-otg.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * OMAP OTG controller driver
   4 *
   5 * Based on code from tahvo-usb.c and isp1301_omap.c drivers.
   6 *
   7 * Copyright (C) 2005-2006 Nokia Corporation
   8 * Copyright (C) 2004 Texas Instruments
   9 * Copyright (C) 2004 David Brownell
  10 */
  11
  12#include <linux/io.h>
  13#include <linux/err.h>
  14#include <linux/extcon.h>
  15#include <linux/kernel.h>
  16#include <linux/module.h>
  17#include <linux/interrupt.h>
  18#include <linux/platform_device.h>
  19#include <linux/platform_data/usb-omap1.h>
  20
  21struct otg_device {
  22        void __iomem                    *base;
  23        bool                            id;
  24        bool                            vbus;
  25        struct extcon_dev               *extcon;
  26        struct notifier_block           vbus_nb;
  27        struct notifier_block           id_nb;
  28};
  29
  30#define OMAP_OTG_CTRL           0x0c
  31#define OMAP_OTG_ASESSVLD       (1 << 20)
  32#define OMAP_OTG_BSESSEND       (1 << 19)
  33#define OMAP_OTG_BSESSVLD       (1 << 18)
  34#define OMAP_OTG_VBUSVLD        (1 << 17)
  35#define OMAP_OTG_ID             (1 << 16)
  36#define OMAP_OTG_XCEIV_OUTPUTS \
  37        (OMAP_OTG_ASESSVLD | OMAP_OTG_BSESSEND | OMAP_OTG_BSESSVLD | \
  38         OMAP_OTG_VBUSVLD  | OMAP_OTG_ID)
  39
  40static void omap_otg_ctrl(struct otg_device *otg_dev, u32 outputs)
  41{
  42        u32 l;
  43
  44        l = readl(otg_dev->base + OMAP_OTG_CTRL);
  45        l &= ~OMAP_OTG_XCEIV_OUTPUTS;
  46        l |= outputs;
  47        writel(l, otg_dev->base + OMAP_OTG_CTRL);
  48}
  49
  50static void omap_otg_set_mode(struct otg_device *otg_dev)
  51{
  52        if (!otg_dev->id && otg_dev->vbus)
  53                /* Set B-session valid. */
  54                omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSVLD);
  55        else if (otg_dev->vbus)
  56                /* Set A-session valid. */
  57                omap_otg_ctrl(otg_dev, OMAP_OTG_ASESSVLD);
  58        else if (!otg_dev->id)
  59                /* Set B-session end to indicate no VBUS. */
  60                omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSEND);
  61}
  62
  63static int omap_otg_id_notifier(struct notifier_block *nb,
  64                                unsigned long event, void *ptr)
  65{
  66        struct otg_device *otg_dev = container_of(nb, struct otg_device, id_nb);
  67
  68        otg_dev->id = event;
  69        omap_otg_set_mode(otg_dev);
  70
  71        return NOTIFY_DONE;
  72}
  73
  74static int omap_otg_vbus_notifier(struct notifier_block *nb,
  75                                  unsigned long event, void *ptr)
  76{
  77        struct otg_device *otg_dev = container_of(nb, struct otg_device,
  78                                                  vbus_nb);
  79
  80        otg_dev->vbus = event;
  81        omap_otg_set_mode(otg_dev);
  82
  83        return NOTIFY_DONE;
  84}
  85
  86static int omap_otg_probe(struct platform_device *pdev)
  87{
  88        const struct omap_usb_config *config = pdev->dev.platform_data;
  89        struct otg_device *otg_dev;
  90        struct extcon_dev *extcon;
  91        int ret;
  92        u32 rev;
  93
  94        if (!config || !config->extcon)
  95                return -ENODEV;
  96
  97        extcon = extcon_get_extcon_dev(config->extcon);
  98        if (!extcon)
  99                return -EPROBE_DEFER;
 100
 101        otg_dev = devm_kzalloc(&pdev->dev, sizeof(*otg_dev), GFP_KERNEL);
 102        if (!otg_dev)
 103                return -ENOMEM;
 104
 105        otg_dev->base = devm_ioremap_resource(&pdev->dev, &pdev->resource[0]);
 106        if (IS_ERR(otg_dev->base))
 107                return PTR_ERR(otg_dev->base);
 108
 109        otg_dev->extcon = extcon;
 110        otg_dev->id_nb.notifier_call = omap_otg_id_notifier;
 111        otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier;
 112
 113        ret = devm_extcon_register_notifier(&pdev->dev, extcon,
 114                                        EXTCON_USB_HOST, &otg_dev->id_nb);
 115        if (ret)
 116                return ret;
 117
 118        ret = devm_extcon_register_notifier(&pdev->dev, extcon,
 119                                        EXTCON_USB, &otg_dev->vbus_nb);
 120        if (ret) {
 121                return ret;
 122        }
 123
 124        otg_dev->id = extcon_get_state(extcon, EXTCON_USB_HOST);
 125        otg_dev->vbus = extcon_get_state(extcon, EXTCON_USB);
 126        omap_otg_set_mode(otg_dev);
 127
 128        rev = readl(otg_dev->base);
 129
 130        dev_info(&pdev->dev,
 131                 "OMAP USB OTG controller rev %d.%d (%s, id=%d, vbus=%d)\n",
 132                 (rev >> 4) & 0xf, rev & 0xf, config->extcon, otg_dev->id,
 133                 otg_dev->vbus);
 134
 135        platform_set_drvdata(pdev, otg_dev);
 136
 137        return 0;
 138}
 139
 140static struct platform_driver omap_otg_driver = {
 141        .probe          = omap_otg_probe,
 142        .driver         = {
 143                .name   = "omap_otg",
 144        },
 145};
 146module_platform_driver(omap_otg_driver);
 147
 148MODULE_DESCRIPTION("OMAP USB OTG controller driver");
 149MODULE_LICENSE("GPL");
 150MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
 151