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/module.h>
  17#include <linux/of.h>
  18#include <linux/io.h>
  19#include <linux/of_address.h>
  20#include <linux/of_device.h>
  21#include <linux/platform_device.h>
  22#include <linux/usb.h>
  23#include <linux/usb/otg.h>
  24#include <linux/usb/ulpi.h>
  25#include <linux/usb/phy.h>
  26
  27
  28struct ulpi_info {
  29        unsigned int    id;
  30        char            *name;
  31};
  32
  33#define ULPI_ID(vendor, product) (((vendor) << 16) | (product))
  34#define ULPI_INFO(_id, _name)           \
  35        {                               \
  36                .id     = (_id),        \
  37                .name   = (_name),      \
  38        }
  39
  40/* ULPI hardcoded IDs, used for probing */
  41static struct ulpi_info ulpi_ids[] = {
  42        ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"),
  43        ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"),
  44        ULPI_INFO(ULPI_ID(0x0424, 0x0007), "SMSC USB3320"),
  45        ULPI_INFO(ULPI_ID(0x0424, 0x0009), "SMSC USB334x"),
  46        ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"),
  47};
  48
  49struct ulpi_phy {
  50        struct usb_phy  *usb_phy;
  51        void __iomem *regs;
  52        unsigned int vp_offset;
  53        unsigned int flags;
  54};
  55
  56static int ulpi_set_otg_flags(struct usb_phy *phy)
  57{
  58        unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN |
  59                             ULPI_OTG_CTRL_DM_PULLDOWN;
  60
  61        if (phy->flags & ULPI_OTG_ID_PULLUP)
  62                flags |= ULPI_OTG_CTRL_ID_PULLUP;
  63
  64        /*
  65         * ULPI Specification rev.1.1 default
  66         * for Dp/DmPulldown is enabled.
  67         */
  68        if (phy->flags & ULPI_OTG_DP_PULLDOWN_DIS)
  69                flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN;
  70
  71        if (phy->flags & ULPI_OTG_DM_PULLDOWN_DIS)
  72                flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN;
  73
  74        if (phy->flags & ULPI_OTG_EXTVBUSIND)
  75                flags |= ULPI_OTG_CTRL_EXTVBUSIND;
  76
  77        return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
  78}
  79
  80static int ulpi_set_fc_flags(struct usb_phy *phy)
  81{
  82        unsigned int flags = 0;
  83
  84        /*
  85         * ULPI Specification rev.1.1 default
  86         * for XcvrSelect is Full Speed.
  87         */
  88        if (phy->flags & ULPI_FC_HS)
  89                flags |= ULPI_FUNC_CTRL_HIGH_SPEED;
  90        else if (phy->flags & ULPI_FC_LS)
  91                flags |= ULPI_FUNC_CTRL_LOW_SPEED;
  92        else if (phy->flags & ULPI_FC_FS4LS)
  93                flags |= ULPI_FUNC_CTRL_FS4LS;
  94        else
  95                flags |= ULPI_FUNC_CTRL_FULL_SPEED;
  96
  97        if (phy->flags & ULPI_FC_TERMSEL)
  98                flags |= ULPI_FUNC_CTRL_TERMSELECT;
  99
 100        /*
 101         * ULPI Specification rev.1.1 default
 102         * for OpMode is Normal Operation.
 103         */
 104        if (phy->flags & ULPI_FC_OP_NODRV)
 105                flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
 106        else if (phy->flags & ULPI_FC_OP_DIS_NRZI)
 107                flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI;
 108        else if (phy->flags & ULPI_FC_OP_NSYNC_NEOP)
 109                flags |= ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP;
 110        else
 111                flags |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
 112
 113        /*
 114         * ULPI Specification rev.1.1 default
 115         * for SuspendM is Powered.
 116         */
 117        flags |= ULPI_FUNC_CTRL_SUSPENDM;
 118
 119        return usb_phy_io_write(phy, flags, ULPI_FUNC_CTRL);
 120}
 121
 122static int ulpi_set_ic_flags(struct usb_phy *phy)
 123{
 124        unsigned int flags = 0;
 125
 126        if (phy->flags & ULPI_IC_AUTORESUME)
 127                flags |= ULPI_IFC_CTRL_AUTORESUME;
 128
 129        if (phy->flags & ULPI_IC_EXTVBUS_INDINV)
 130                flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS;
 131
 132        if (phy->flags & ULPI_IC_IND_PASSTHRU)
 133                flags |= ULPI_IFC_CTRL_PASSTHRU;
 134
 135        if (phy->flags & ULPI_IC_PROTECT_DIS)
 136                flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE;
 137
 138        return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
 139}
 140
 141static int ulpi_set_flags(struct usb_phy *phy)
 142{
 143        int ret;
 144
 145        ret = ulpi_set_otg_flags(phy);
 146        if (ret)
 147                return ret;
 148
 149        ret = ulpi_set_ic_flags(phy);
 150        if (ret)
 151                return ret;
 152
 153        return ulpi_set_fc_flags(phy);
 154}
 155
 156static int ulpi_check_integrity(struct usb_phy *phy)
 157{
 158        int ret, i;
 159        unsigned int val = 0x55;
 160
 161        for (i = 0; i < 2; i++) {
 162                ret = usb_phy_io_write(phy, val, ULPI_SCRATCH);
 163                if (ret < 0)
 164                        return ret;
 165
 166                ret = usb_phy_io_read(phy, ULPI_SCRATCH);
 167                if (ret < 0)
 168                        return ret;
 169
 170                if (ret != val) {
 171                        pr_err("ULPI integrity check: failed!");
 172                        return -ENODEV;
 173                }
 174                val = val << 1;
 175        }
 176
 177        pr_info("ULPI integrity check: passed.\n");
 178
 179        return 0;
 180}
 181
 182static int ulpi_init(struct usb_phy *phy)
 183{
 184        int i, vid, pid, ret;
 185        u32 ulpi_id = 0;
 186
 187        for (i = 0; i < 4; i++) {
 188                ret = usb_phy_io_read(phy, ULPI_PRODUCT_ID_HIGH - i);
 189                if (ret < 0)
 190                        return ret;
 191                ulpi_id = (ulpi_id << 8) | ret;
 192        }
 193        vid = ulpi_id & 0xffff;
 194        pid = ulpi_id >> 16;
 195
 196        pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid);
 197
 198        for (i = 0; i < ARRAY_SIZE(ulpi_ids); i++) {
 199                if (ulpi_ids[i].id == ULPI_ID(vid, pid)) {
 200                        pr_info("Found %s ULPI transceiver.\n",
 201                                ulpi_ids[i].name);
 202                        break;
 203                }
 204        }
 205
 206        ret = ulpi_check_integrity(phy);
 207        if (ret)
 208                return ret;
 209
 210        return ulpi_set_flags(phy);
 211}
 212
 213static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host)
 214{
 215        struct usb_phy *phy = otg->usb_phy;
 216        unsigned int flags = usb_phy_io_read(phy, ULPI_IFC_CTRL);
 217
 218        if (!host) {
 219                otg->host = NULL;
 220                return 0;
 221        }
 222
 223        otg->host = host;
 224
 225        flags &= ~(ULPI_IFC_CTRL_6_PIN_SERIAL_MODE |
 226                   ULPI_IFC_CTRL_3_PIN_SERIAL_MODE |
 227                   ULPI_IFC_CTRL_CARKITMODE);
 228
 229        if (phy->flags & ULPI_IC_6PIN_SERIAL)
 230                flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE;
 231        else if (phy->flags & ULPI_IC_3PIN_SERIAL)
 232                flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE;
 233        else if (phy->flags & ULPI_IC_CARKIT)
 234                flags |= ULPI_IFC_CTRL_CARKITMODE;
 235
 236        return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
 237}
 238
 239static int ulpi_set_vbus(struct usb_otg *otg, bool on)
 240{
 241        struct usb_phy *phy = otg->usb_phy;
 242        unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL);
 243
 244        flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT);
 245
 246        if (on) {
 247                if (phy->flags & ULPI_OTG_DRVVBUS)
 248                        flags |= ULPI_OTG_CTRL_DRVVBUS;
 249
 250                if (phy->flags & ULPI_OTG_DRVVBUS_EXT)
 251                        flags |= ULPI_OTG_CTRL_DRVVBUS_EXT;
 252        }
 253
 254        return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
 255}
 256
 257static int usbphy_set_vbus(struct usb_phy *phy, int on)
 258{
 259        unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL);
 260
 261        flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT);
 262
 263        if (on) {
 264                if (phy->flags & ULPI_OTG_DRVVBUS)
 265                        flags |= ULPI_OTG_CTRL_DRVVBUS;
 266
 267                if (phy->flags & ULPI_OTG_DRVVBUS_EXT)
 268                        flags |= ULPI_OTG_CTRL_DRVVBUS_EXT;
 269        }
 270
 271        return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
 272}
 273
 274struct usb_phy *
 275otg_ulpi_create(struct usb_phy_io_ops *ops,
 276                unsigned int flags)
 277{
 278        struct usb_phy *phy;
 279        struct usb_otg *otg;
 280
 281        phy = kzalloc(sizeof(*phy), GFP_KERNEL);
 282        if (!phy)
 283                return NULL;
 284
 285        otg = kzalloc(sizeof(*otg), GFP_KERNEL);
 286        if (!otg) {
 287                kfree(phy);
 288                return NULL;
 289        }
 290
 291        phy->label      = "ULPI";
 292        phy->flags      = flags;
 293        phy->io_ops     = ops;
 294        phy->otg        = otg;
 295        phy->init       = ulpi_init;
 296        phy->set_vbus   = usbphy_set_vbus;
 297
 298        otg->usb_phy    = phy;
 299        otg->set_host   = ulpi_set_host;
 300        otg->set_vbus   = ulpi_set_vbus;
 301
 302        return phy;
 303}
 304EXPORT_SYMBOL_GPL(otg_ulpi_create);
 305
 306static int ulpi_phy_probe(struct platform_device *pdev)
 307{
 308        struct device_node *np = pdev->dev.of_node;
 309        struct resource *res;
 310        struct ulpi_phy *uphy;
 311        bool flag;
 312        int ret;
 313
 314        uphy = devm_kzalloc(&pdev->dev, sizeof(*uphy), GFP_KERNEL);
 315        if (!uphy)
 316                return -ENOMEM;
 317
 318        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 319        uphy->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
 320        if (IS_ERR(uphy->regs))
 321                return PTR_ERR(uphy->regs);
 322
 323        ret = of_property_read_u32(np, "view-port", &uphy->vp_offset);
 324        if (IS_ERR(uphy->regs)) {
 325                dev_err(&pdev->dev, "view-port register not specified\n");
 326                return PTR_ERR(uphy->regs);
 327        }
 328
 329        flag = of_property_read_bool(np, "drv-vbus");
 330        if (flag)
 331                uphy->flags |= ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT;
 332
 333        uphy->usb_phy = otg_ulpi_create(&ulpi_viewport_access_ops, uphy->flags);
 334
 335        uphy->usb_phy->dev = &pdev->dev;
 336
 337        uphy->usb_phy->io_priv = uphy->regs + uphy->vp_offset;
 338
 339        ret = usb_add_phy_dev(uphy->usb_phy);
 340        if (ret < 0)
 341                return ret;
 342
 343        return 0;
 344}
 345
 346static int ulpi_phy_remove(struct platform_device *pdev)
 347{
 348        struct ulpi_phy *uphy = platform_get_drvdata(pdev);
 349
 350        usb_remove_phy(uphy->usb_phy);
 351
 352        return 0;
 353}
 354
 355static const struct of_device_id ulpi_phy_table[] = {
 356        { .compatible = "ulpi-phy" },
 357        { },
 358};
 359MODULE_DEVICE_TABLE(of, ulpi_phy_table);
 360
 361static struct platform_driver ulpi_phy_driver = {
 362        .probe          = ulpi_phy_probe,
 363        .remove         = ulpi_phy_remove,
 364        .driver         = {
 365                .name   = "ulpi-phy",
 366                .of_match_table = ulpi_phy_table,
 367        },
 368};
 369module_platform_driver(ulpi_phy_driver);
 370
 371MODULE_DESCRIPTION("ULPI PHY driver");
 372MODULE_LICENSE("GPL v2");
 373