linux/drivers/usb/phy/phy-ulpi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Generic ULPI USB transceiver support
   4 *
   5 * Copyright (C) 2009 Daniel Mack <daniel@caiaq.de>
   6 *
   7 * Based on sources from
   8 *
   9 *   Sascha Hauer <s.hauer@pengutronix.de>
  10 *   Freescale Semiconductors
  11 */
  12
  13#include <linux/kernel.h>
  14#include <linux/slab.h>
  15#include <linux/export.h>
  16#include <linux/usb.h>
  17#include <linux/usb/otg.h>
  18#include <linux/usb/ulpi.h>
  19
  20
  21struct ulpi_info {
  22        unsigned int    id;
  23        char            *name;
  24};
  25
  26#define ULPI_ID(vendor, product) (((vendor) << 16) | (product))
  27#define ULPI_INFO(_id, _name)           \
  28        {                               \
  29                .id     = (_id),        \
  30                .name   = (_name),      \
  31        }
  32
  33/* ULPI hardcoded IDs, used for probing */
  34static struct ulpi_info ulpi_ids[] = {
  35        ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"),
  36        ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"),
  37        ULPI_INFO(ULPI_ID(0x0424, 0x0007), "SMSC USB3320"),
  38        ULPI_INFO(ULPI_ID(0x0424, 0x0009), "SMSC USB334x"),
  39        ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"),
  40};
  41
  42static int ulpi_set_otg_flags(struct usb_phy *phy)
  43{
  44        unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN |
  45                             ULPI_OTG_CTRL_DM_PULLDOWN;
  46
  47        if (phy->flags & ULPI_OTG_ID_PULLUP)
  48                flags |= ULPI_OTG_CTRL_ID_PULLUP;
  49
  50        /*
  51         * ULPI Specification rev.1.1 default
  52         * for Dp/DmPulldown is enabled.
  53         */
  54        if (phy->flags & ULPI_OTG_DP_PULLDOWN_DIS)
  55                flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN;
  56
  57        if (phy->flags & ULPI_OTG_DM_PULLDOWN_DIS)
  58                flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN;
  59
  60        if (phy->flags & ULPI_OTG_EXTVBUSIND)
  61                flags |= ULPI_OTG_CTRL_EXTVBUSIND;
  62
  63        return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
  64}
  65
  66static int ulpi_set_fc_flags(struct usb_phy *phy)
  67{
  68        unsigned int flags = 0;
  69
  70        /*
  71         * ULPI Specification rev.1.1 default
  72         * for XcvrSelect is Full Speed.
  73         */
  74        if (phy->flags & ULPI_FC_HS)
  75                flags |= ULPI_FUNC_CTRL_HIGH_SPEED;
  76        else if (phy->flags & ULPI_FC_LS)
  77                flags |= ULPI_FUNC_CTRL_LOW_SPEED;
  78        else if (phy->flags & ULPI_FC_FS4LS)
  79                flags |= ULPI_FUNC_CTRL_FS4LS;
  80        else
  81                flags |= ULPI_FUNC_CTRL_FULL_SPEED;
  82
  83        if (phy->flags & ULPI_FC_TERMSEL)
  84                flags |= ULPI_FUNC_CTRL_TERMSELECT;
  85
  86        /*
  87         * ULPI Specification rev.1.1 default
  88         * for OpMode is Normal Operation.
  89         */
  90        if (phy->flags & ULPI_FC_OP_NODRV)
  91                flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
  92        else if (phy->flags & ULPI_FC_OP_DIS_NRZI)
  93                flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI;
  94        else if (phy->flags & ULPI_FC_OP_NSYNC_NEOP)
  95                flags |= ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP;
  96        else
  97                flags |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
  98
  99        /*
 100         * ULPI Specification rev.1.1 default
 101         * for SuspendM is Powered.
 102         */
 103        flags |= ULPI_FUNC_CTRL_SUSPENDM;
 104
 105        return usb_phy_io_write(phy, flags, ULPI_FUNC_CTRL);
 106}
 107
 108static int ulpi_set_ic_flags(struct usb_phy *phy)
 109{
 110        unsigned int flags = 0;
 111
 112        if (phy->flags & ULPI_IC_AUTORESUME)
 113                flags |= ULPI_IFC_CTRL_AUTORESUME;
 114
 115        if (phy->flags & ULPI_IC_EXTVBUS_INDINV)
 116                flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS;
 117
 118        if (phy->flags & ULPI_IC_IND_PASSTHRU)
 119                flags |= ULPI_IFC_CTRL_PASSTHRU;
 120
 121        if (phy->flags & ULPI_IC_PROTECT_DIS)
 122                flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE;
 123
 124        return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
 125}
 126
 127static int ulpi_set_flags(struct usb_phy *phy)
 128{
 129        int ret;
 130
 131        ret = ulpi_set_otg_flags(phy);
 132        if (ret)
 133                return ret;
 134
 135        ret = ulpi_set_ic_flags(phy);
 136        if (ret)
 137                return ret;
 138
 139        return ulpi_set_fc_flags(phy);
 140}
 141
 142static int ulpi_check_integrity(struct usb_phy *phy)
 143{
 144        int ret, i;
 145        unsigned int val = 0x55;
 146
 147        for (i = 0; i < 2; i++) {
 148                ret = usb_phy_io_write(phy, val, ULPI_SCRATCH);
 149                if (ret < 0)
 150                        return ret;
 151
 152                ret = usb_phy_io_read(phy, ULPI_SCRATCH);
 153                if (ret < 0)
 154                        return ret;
 155
 156                if (ret != val) {
 157                        pr_err("ULPI integrity check: failed!");
 158                        return -ENODEV;
 159                }
 160                val = val << 1;
 161        }
 162
 163        pr_info("ULPI integrity check: passed.\n");
 164
 165        return 0;
 166}
 167
 168static int ulpi_init(struct usb_phy *phy)
 169{
 170        int i, vid, pid, ret;
 171        u32 ulpi_id = 0;
 172
 173        for (i = 0; i < 4; i++) {
 174                ret = usb_phy_io_read(phy, ULPI_PRODUCT_ID_HIGH - i);
 175                if (ret < 0)
 176                        return ret;
 177                ulpi_id = (ulpi_id << 8) | ret;
 178        }
 179        vid = ulpi_id & 0xffff;
 180        pid = ulpi_id >> 16;
 181
 182        pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid);
 183
 184        for (i = 0; i < ARRAY_SIZE(ulpi_ids); i++) {
 185                if (ulpi_ids[i].id == ULPI_ID(vid, pid)) {
 186                        pr_info("Found %s ULPI transceiver.\n",
 187                                ulpi_ids[i].name);
 188                        break;
 189                }
 190        }
 191
 192        ret = ulpi_check_integrity(phy);
 193        if (ret)
 194                return ret;
 195
 196        return ulpi_set_flags(phy);
 197}
 198
 199static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host)
 200{
 201        struct usb_phy *phy = otg->usb_phy;
 202        unsigned int flags = usb_phy_io_read(phy, ULPI_IFC_CTRL);
 203
 204        if (!host) {
 205                otg->host = NULL;
 206                return 0;
 207        }
 208
 209        otg->host = host;
 210
 211        flags &= ~(ULPI_IFC_CTRL_6_PIN_SERIAL_MODE |
 212                   ULPI_IFC_CTRL_3_PIN_SERIAL_MODE |
 213                   ULPI_IFC_CTRL_CARKITMODE);
 214
 215        if (phy->flags & ULPI_IC_6PIN_SERIAL)
 216                flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE;
 217        else if (phy->flags & ULPI_IC_3PIN_SERIAL)
 218                flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE;
 219        else if (phy->flags & ULPI_IC_CARKIT)
 220                flags |= ULPI_IFC_CTRL_CARKITMODE;
 221
 222        return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
 223}
 224
 225static int ulpi_set_vbus(struct usb_otg *otg, bool on)
 226{
 227        struct usb_phy *phy = otg->usb_phy;
 228        unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL);
 229
 230        flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT);
 231
 232        if (on) {
 233                if (phy->flags & ULPI_OTG_DRVVBUS)
 234                        flags |= ULPI_OTG_CTRL_DRVVBUS;
 235
 236                if (phy->flags & ULPI_OTG_DRVVBUS_EXT)
 237                        flags |= ULPI_OTG_CTRL_DRVVBUS_EXT;
 238        }
 239
 240        return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
 241}
 242
 243static void otg_ulpi_init(struct usb_phy *phy, struct usb_otg *otg,
 244                          struct usb_phy_io_ops *ops,
 245                          unsigned int flags)
 246{
 247        phy->label      = "ULPI";
 248        phy->flags      = flags;
 249        phy->io_ops     = ops;
 250        phy->otg        = otg;
 251        phy->init       = ulpi_init;
 252
 253        otg->usb_phy    = phy;
 254        otg->set_host   = ulpi_set_host;
 255        otg->set_vbus   = ulpi_set_vbus;
 256}
 257
 258struct usb_phy *
 259otg_ulpi_create(struct usb_phy_io_ops *ops,
 260                unsigned int flags)
 261{
 262        struct usb_phy *phy;
 263        struct usb_otg *otg;
 264
 265        phy = kzalloc(sizeof(*phy), GFP_KERNEL);
 266        if (!phy)
 267                return NULL;
 268
 269        otg = kzalloc(sizeof(*otg), GFP_KERNEL);
 270        if (!otg) {
 271                kfree(phy);
 272                return NULL;
 273        }
 274
 275        otg_ulpi_init(phy, otg, ops, flags);
 276
 277        return phy;
 278}
 279EXPORT_SYMBOL_GPL(otg_ulpi_create);
 280
 281struct usb_phy *
 282devm_otg_ulpi_create(struct device *dev,
 283                     struct usb_phy_io_ops *ops,
 284                     unsigned int flags)
 285{
 286        struct usb_phy *phy;
 287        struct usb_otg *otg;
 288
 289        phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
 290        if (!phy)
 291                return NULL;
 292
 293        otg = devm_kzalloc(dev, sizeof(*otg), GFP_KERNEL);
 294        if (!otg) {
 295                devm_kfree(dev, phy);
 296                return NULL;
 297        }
 298
 299        otg_ulpi_init(phy, otg, ops, flags);
 300
 301        return phy;
 302}
 303EXPORT_SYMBOL_GPL(devm_otg_ulpi_create);
 304