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