root/drivers/usb/phy/phy-tahvo.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. vbus_show
  2. check_vbus_state
  3. tahvo_usb_become_host
  4. tahvo_usb_stop_host
  5. tahvo_usb_become_peripheral
  6. tahvo_usb_stop_peripheral
  7. tahvo_usb_power_off
  8. tahvo_usb_set_suspend
  9. tahvo_usb_set_host
  10. tahvo_usb_set_peripheral
  11. tahvo_usb_vbus_interrupt
  12. otg_mode_show
  13. otg_mode_store
  14. tahvo_usb_probe
  15. tahvo_usb_remove

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Tahvo USB transceiver driver
   4  *
   5  * Copyright (C) 2005-2006 Nokia Corporation
   6  *
   7  * Parts copied from isp1301_omap.c.
   8  * Copyright (C) 2004 Texas Instruments
   9  * Copyright (C) 2004 David Brownell
  10  *
  11  * Original driver written by Juha Yrjölä, Tony Lindgren and Timo Teräs.
  12  * Modified for Retu/Tahvo MFD by Aaro Koskinen.
  13  */
  14 
  15 #include <linux/io.h>
  16 #include <linux/clk.h>
  17 #include <linux/usb.h>
  18 #include <linux/extcon-provider.h>
  19 #include <linux/kernel.h>
  20 #include <linux/module.h>
  21 #include <linux/usb/otg.h>
  22 #include <linux/mfd/retu.h>
  23 #include <linux/usb/gadget.h>
  24 #include <linux/platform_device.h>
  25 
  26 #define DRIVER_NAME     "tahvo-usb"
  27 
  28 #define TAHVO_REG_IDSR  0x02
  29 #define TAHVO_REG_USBR  0x06
  30 
  31 #define USBR_SLAVE_CONTROL      (1 << 8)
  32 #define USBR_VPPVIO_SW          (1 << 7)
  33 #define USBR_SPEED              (1 << 6)
  34 #define USBR_REGOUT             (1 << 5)
  35 #define USBR_MASTER_SW2         (1 << 4)
  36 #define USBR_MASTER_SW1         (1 << 3)
  37 #define USBR_SLAVE_SW           (1 << 2)
  38 #define USBR_NSUSPEND           (1 << 1)
  39 #define USBR_SEMODE             (1 << 0)
  40 
  41 #define TAHVO_MODE_HOST         0
  42 #define TAHVO_MODE_PERIPHERAL   1
  43 
  44 struct tahvo_usb {
  45         struct platform_device  *pt_dev;
  46         struct usb_phy          phy;
  47         int                     vbus_state;
  48         struct mutex            serialize;
  49         struct clk              *ick;
  50         int                     irq;
  51         int                     tahvo_mode;
  52         struct extcon_dev       *extcon;
  53 };
  54 
  55 static const unsigned int tahvo_cable[] = {
  56         EXTCON_USB,
  57         EXTCON_USB_HOST,
  58 
  59         EXTCON_NONE,
  60 };
  61 
  62 static ssize_t vbus_show(struct device *device,
  63                                struct device_attribute *attr, char *buf)
  64 {
  65         struct tahvo_usb *tu = dev_get_drvdata(device);
  66         return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off");
  67 }
  68 static DEVICE_ATTR_RO(vbus);
  69 
  70 static void check_vbus_state(struct tahvo_usb *tu)
  71 {
  72         struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
  73         int reg, prev_state;
  74 
  75         reg = retu_read(rdev, TAHVO_REG_IDSR);
  76         if (reg & TAHVO_STAT_VBUS) {
  77                 switch (tu->phy.otg->state) {
  78                 case OTG_STATE_B_IDLE:
  79                         /* Enable the gadget driver */
  80                         if (tu->phy.otg->gadget)
  81                                 usb_gadget_vbus_connect(tu->phy.otg->gadget);
  82                         tu->phy.otg->state = OTG_STATE_B_PERIPHERAL;
  83                         usb_phy_set_event(&tu->phy, USB_EVENT_ENUMERATED);
  84                         break;
  85                 case OTG_STATE_A_IDLE:
  86                         /*
  87                          * Session is now valid assuming the USB hub is driving
  88                          * Vbus.
  89                          */
  90                         tu->phy.otg->state = OTG_STATE_A_HOST;
  91                         break;
  92                 default:
  93                         break;
  94                 }
  95                 dev_info(&tu->pt_dev->dev, "USB cable connected\n");
  96         } else {
  97                 switch (tu->phy.otg->state) {
  98                 case OTG_STATE_B_PERIPHERAL:
  99                         if (tu->phy.otg->gadget)
 100                                 usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
 101                         tu->phy.otg->state = OTG_STATE_B_IDLE;
 102                         usb_phy_set_event(&tu->phy, USB_EVENT_NONE);
 103                         break;
 104                 case OTG_STATE_A_HOST:
 105                         tu->phy.otg->state = OTG_STATE_A_IDLE;
 106                         break;
 107                 default:
 108                         break;
 109                 }
 110                 dev_info(&tu->pt_dev->dev, "USB cable disconnected\n");
 111         }
 112 
 113         prev_state = tu->vbus_state;
 114         tu->vbus_state = reg & TAHVO_STAT_VBUS;
 115         if (prev_state != tu->vbus_state) {
 116                 extcon_set_state_sync(tu->extcon, EXTCON_USB, tu->vbus_state);
 117                 sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state");
 118         }
 119 }
 120 
 121 static void tahvo_usb_become_host(struct tahvo_usb *tu)
 122 {
 123         struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
 124 
 125         extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST, true);
 126 
 127         /* Power up the transceiver in USB host mode */
 128         retu_write(rdev, TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND |
 129                    USBR_MASTER_SW2 | USBR_MASTER_SW1);
 130         tu->phy.otg->state = OTG_STATE_A_IDLE;
 131 
 132         check_vbus_state(tu);
 133 }
 134 
 135 static void tahvo_usb_stop_host(struct tahvo_usb *tu)
 136 {
 137         tu->phy.otg->state = OTG_STATE_A_IDLE;
 138 }
 139 
 140 static void tahvo_usb_become_peripheral(struct tahvo_usb *tu)
 141 {
 142         struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
 143 
 144         extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST, false);
 145 
 146         /* Power up transceiver and set it in USB peripheral mode */
 147         retu_write(rdev, TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT |
 148                    USBR_NSUSPEND | USBR_SLAVE_SW);
 149         tu->phy.otg->state = OTG_STATE_B_IDLE;
 150 
 151         check_vbus_state(tu);
 152 }
 153 
 154 static void tahvo_usb_stop_peripheral(struct tahvo_usb *tu)
 155 {
 156         if (tu->phy.otg->gadget)
 157                 usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
 158         tu->phy.otg->state = OTG_STATE_B_IDLE;
 159 }
 160 
 161 static void tahvo_usb_power_off(struct tahvo_usb *tu)
 162 {
 163         struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
 164 
 165         /* Disable gadget controller if any */
 166         if (tu->phy.otg->gadget)
 167                 usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
 168 
 169         /* Power off transceiver */
 170         retu_write(rdev, TAHVO_REG_USBR, 0);
 171         tu->phy.otg->state = OTG_STATE_UNDEFINED;
 172 }
 173 
 174 static int tahvo_usb_set_suspend(struct usb_phy *dev, int suspend)
 175 {
 176         struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, phy);
 177         struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
 178         u16 w;
 179 
 180         dev_dbg(&tu->pt_dev->dev, "%s\n", __func__);
 181 
 182         w = retu_read(rdev, TAHVO_REG_USBR);
 183         if (suspend)
 184                 w &= ~USBR_NSUSPEND;
 185         else
 186                 w |= USBR_NSUSPEND;
 187         retu_write(rdev, TAHVO_REG_USBR, w);
 188 
 189         return 0;
 190 }
 191 
 192 static int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
 193 {
 194         struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb,
 195                                             phy);
 196 
 197         dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, host);
 198 
 199         mutex_lock(&tu->serialize);
 200 
 201         if (host == NULL) {
 202                 if (tu->tahvo_mode == TAHVO_MODE_HOST)
 203                         tahvo_usb_power_off(tu);
 204                 otg->host = NULL;
 205                 mutex_unlock(&tu->serialize);
 206                 return 0;
 207         }
 208 
 209         if (tu->tahvo_mode == TAHVO_MODE_HOST) {
 210                 otg->host = NULL;
 211                 tahvo_usb_become_host(tu);
 212         }
 213 
 214         otg->host = host;
 215 
 216         mutex_unlock(&tu->serialize);
 217 
 218         return 0;
 219 }
 220 
 221 static int tahvo_usb_set_peripheral(struct usb_otg *otg,
 222                                     struct usb_gadget *gadget)
 223 {
 224         struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb,
 225                                             phy);
 226 
 227         dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, gadget);
 228 
 229         mutex_lock(&tu->serialize);
 230 
 231         if (!gadget) {
 232                 if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
 233                         tahvo_usb_power_off(tu);
 234                 tu->phy.otg->gadget = NULL;
 235                 mutex_unlock(&tu->serialize);
 236                 return 0;
 237         }
 238 
 239         tu->phy.otg->gadget = gadget;
 240         if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
 241                 tahvo_usb_become_peripheral(tu);
 242 
 243         mutex_unlock(&tu->serialize);
 244 
 245         return 0;
 246 }
 247 
 248 static irqreturn_t tahvo_usb_vbus_interrupt(int irq, void *_tu)
 249 {
 250         struct tahvo_usb *tu = _tu;
 251 
 252         mutex_lock(&tu->serialize);
 253         check_vbus_state(tu);
 254         mutex_unlock(&tu->serialize);
 255 
 256         return IRQ_HANDLED;
 257 }
 258 
 259 static ssize_t otg_mode_show(struct device *device,
 260                              struct device_attribute *attr, char *buf)
 261 {
 262         struct tahvo_usb *tu = dev_get_drvdata(device);
 263 
 264         switch (tu->tahvo_mode) {
 265         case TAHVO_MODE_HOST:
 266                 return sprintf(buf, "host\n");
 267         case TAHVO_MODE_PERIPHERAL:
 268                 return sprintf(buf, "peripheral\n");
 269         }
 270 
 271         return -EINVAL;
 272 }
 273 
 274 static ssize_t otg_mode_store(struct device *device,
 275                               struct device_attribute *attr,
 276                               const char *buf, size_t count)
 277 {
 278         struct tahvo_usb *tu = dev_get_drvdata(device);
 279         int r;
 280 
 281         mutex_lock(&tu->serialize);
 282         if (count >= 4 && strncmp(buf, "host", 4) == 0) {
 283                 if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
 284                         tahvo_usb_stop_peripheral(tu);
 285                 tu->tahvo_mode = TAHVO_MODE_HOST;
 286                 if (tu->phy.otg->host) {
 287                         dev_info(device, "HOST mode: host controller present\n");
 288                         tahvo_usb_become_host(tu);
 289                 } else {
 290                         dev_info(device, "HOST mode: no host controller, powering off\n");
 291                         tahvo_usb_power_off(tu);
 292                 }
 293                 r = strlen(buf);
 294         } else if (count >= 10 && strncmp(buf, "peripheral", 10) == 0) {
 295                 if (tu->tahvo_mode == TAHVO_MODE_HOST)
 296                         tahvo_usb_stop_host(tu);
 297                 tu->tahvo_mode = TAHVO_MODE_PERIPHERAL;
 298                 if (tu->phy.otg->gadget) {
 299                         dev_info(device, "PERIPHERAL mode: gadget driver present\n");
 300                         tahvo_usb_become_peripheral(tu);
 301                 } else {
 302                         dev_info(device, "PERIPHERAL mode: no gadget driver, powering off\n");
 303                         tahvo_usb_power_off(tu);
 304                 }
 305                 r = strlen(buf);
 306         } else {
 307                 r = -EINVAL;
 308         }
 309         mutex_unlock(&tu->serialize);
 310 
 311         return r;
 312 }
 313 static DEVICE_ATTR_RW(otg_mode);
 314 
 315 static struct attribute *tahvo_attrs[] = {
 316         &dev_attr_vbus.attr,
 317         &dev_attr_otg_mode.attr,
 318         NULL
 319 };
 320 ATTRIBUTE_GROUPS(tahvo);
 321 
 322 static int tahvo_usb_probe(struct platform_device *pdev)
 323 {
 324         struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent);
 325         struct tahvo_usb *tu;
 326         int ret;
 327 
 328         tu = devm_kzalloc(&pdev->dev, sizeof(*tu), GFP_KERNEL);
 329         if (!tu)
 330                 return -ENOMEM;
 331 
 332         tu->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*tu->phy.otg),
 333                                    GFP_KERNEL);
 334         if (!tu->phy.otg)
 335                 return -ENOMEM;
 336 
 337         tu->pt_dev = pdev;
 338 
 339         /* Default mode */
 340 #ifdef CONFIG_TAHVO_USB_HOST_BY_DEFAULT
 341         tu->tahvo_mode = TAHVO_MODE_HOST;
 342 #else
 343         tu->tahvo_mode = TAHVO_MODE_PERIPHERAL;
 344 #endif
 345 
 346         mutex_init(&tu->serialize);
 347 
 348         tu->ick = devm_clk_get(&pdev->dev, "usb_l4_ick");
 349         if (!IS_ERR(tu->ick))
 350                 clk_enable(tu->ick);
 351 
 352         /*
 353          * Set initial state, so that we generate kevents only on state changes.
 354          */
 355         tu->vbus_state = retu_read(rdev, TAHVO_REG_IDSR) & TAHVO_STAT_VBUS;
 356 
 357         tu->extcon = devm_extcon_dev_allocate(&pdev->dev, tahvo_cable);
 358         if (IS_ERR(tu->extcon)) {
 359                 dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
 360                 ret = PTR_ERR(tu->extcon);
 361                 goto err_disable_clk;
 362         }
 363 
 364         ret = devm_extcon_dev_register(&pdev->dev, tu->extcon);
 365         if (ret) {
 366                 dev_err(&pdev->dev, "could not register extcon device: %d\n",
 367                         ret);
 368                 goto err_disable_clk;
 369         }
 370 
 371         /* Set the initial cable state. */
 372         extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST,
 373                                tu->tahvo_mode == TAHVO_MODE_HOST);
 374         extcon_set_state_sync(tu->extcon, EXTCON_USB, tu->vbus_state);
 375 
 376         /* Create OTG interface */
 377         tahvo_usb_power_off(tu);
 378         tu->phy.dev = &pdev->dev;
 379         tu->phy.otg->state = OTG_STATE_UNDEFINED;
 380         tu->phy.label = DRIVER_NAME;
 381         tu->phy.set_suspend = tahvo_usb_set_suspend;
 382 
 383         tu->phy.otg->usb_phy = &tu->phy;
 384         tu->phy.otg->set_host = tahvo_usb_set_host;
 385         tu->phy.otg->set_peripheral = tahvo_usb_set_peripheral;
 386 
 387         ret = usb_add_phy(&tu->phy, USB_PHY_TYPE_USB2);
 388         if (ret < 0) {
 389                 dev_err(&pdev->dev, "cannot register USB transceiver: %d\n",
 390                         ret);
 391                 goto err_disable_clk;
 392         }
 393 
 394         dev_set_drvdata(&pdev->dev, tu);
 395 
 396         tu->irq = platform_get_irq(pdev, 0);
 397         ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt,
 398                                    IRQF_ONESHOT,
 399                                    "tahvo-vbus", tu);
 400         if (ret) {
 401                 dev_err(&pdev->dev, "could not register tahvo-vbus irq: %d\n",
 402                         ret);
 403                 goto err_remove_phy;
 404         }
 405 
 406         return 0;
 407 
 408 err_remove_phy:
 409         usb_remove_phy(&tu->phy);
 410 err_disable_clk:
 411         if (!IS_ERR(tu->ick))
 412                 clk_disable(tu->ick);
 413 
 414         return ret;
 415 }
 416 
 417 static int tahvo_usb_remove(struct platform_device *pdev)
 418 {
 419         struct tahvo_usb *tu = platform_get_drvdata(pdev);
 420 
 421         free_irq(tu->irq, tu);
 422         usb_remove_phy(&tu->phy);
 423         if (!IS_ERR(tu->ick))
 424                 clk_disable(tu->ick);
 425 
 426         return 0;
 427 }
 428 
 429 static struct platform_driver tahvo_usb_driver = {
 430         .probe          = tahvo_usb_probe,
 431         .remove         = tahvo_usb_remove,
 432         .driver         = {
 433                 .name   = "tahvo-usb",
 434                 .dev_groups = tahvo_groups,
 435         },
 436 };
 437 module_platform_driver(tahvo_usb_driver);
 438 
 439 MODULE_DESCRIPTION("Tahvo USB transceiver driver");
 440 MODULE_LICENSE("GPL");
 441 MODULE_AUTHOR("Juha Yrjölä, Tony Lindgren, and Timo Teräs");
 442 MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");

/* [<][>][^][v][top][bottom][index][help] */