linux/drivers/usb/chipidea/otg.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * otg.c - ChipIdea USB IP core OTG driver
   4 *
   5 * Copyright (C) 2013 Freescale Semiconductor, Inc.
   6 *
   7 * Author: Peter Chen
   8 */
   9
  10/*
  11 * This file mainly handles otgsc register, OTG fsm operations for HNP and SRP
  12 * are also included.
  13 */
  14
  15#include <linux/usb/otg.h>
  16#include <linux/usb/gadget.h>
  17#include <linux/usb/chipidea.h>
  18
  19#include "ci.h"
  20#include "bits.h"
  21#include "otg.h"
  22#include "otg_fsm.h"
  23
  24/**
  25 * hw_read_otgsc returns otgsc register bits value.
  26 * @mask: bitfield mask
  27 */
  28u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
  29{
  30        struct ci_hdrc_cable *cable;
  31        u32 val = hw_read(ci, OP_OTGSC, mask);
  32
  33        /*
  34         * If using extcon framework for VBUS and/or ID signal
  35         * detection overwrite OTGSC register value
  36         */
  37        cable = &ci->platdata->vbus_extcon;
  38        if (!IS_ERR(cable->edev)) {
  39                if (cable->changed)
  40                        val |= OTGSC_BSVIS;
  41                else
  42                        val &= ~OTGSC_BSVIS;
  43
  44                if (cable->connected)
  45                        val |= OTGSC_BSV;
  46                else
  47                        val &= ~OTGSC_BSV;
  48
  49                if (cable->enabled)
  50                        val |= OTGSC_BSVIE;
  51                else
  52                        val &= ~OTGSC_BSVIE;
  53        }
  54
  55        cable = &ci->platdata->id_extcon;
  56        if (!IS_ERR(cable->edev)) {
  57                if (cable->changed)
  58                        val |= OTGSC_IDIS;
  59                else
  60                        val &= ~OTGSC_IDIS;
  61
  62                if (cable->connected)
  63                        val &= ~OTGSC_ID; /* host */
  64                else
  65                        val |= OTGSC_ID; /* device */
  66
  67                if (cable->enabled)
  68                        val |= OTGSC_IDIE;
  69                else
  70                        val &= ~OTGSC_IDIE;
  71        }
  72
  73        return val & mask;
  74}
  75
  76/**
  77 * hw_write_otgsc updates target bits of OTGSC register.
  78 * @mask: bitfield mask
  79 * @data: to be written
  80 */
  81void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
  82{
  83        struct ci_hdrc_cable *cable;
  84
  85        cable = &ci->platdata->vbus_extcon;
  86        if (!IS_ERR(cable->edev)) {
  87                if (data & mask & OTGSC_BSVIS)
  88                        cable->changed = false;
  89
  90                /* Don't enable vbus interrupt if using external notifier */
  91                if (data & mask & OTGSC_BSVIE) {
  92                        cable->enabled = true;
  93                        data &= ~OTGSC_BSVIE;
  94                } else if (mask & OTGSC_BSVIE) {
  95                        cable->enabled = false;
  96                }
  97        }
  98
  99        cable = &ci->platdata->id_extcon;
 100        if (!IS_ERR(cable->edev)) {
 101                if (data & mask & OTGSC_IDIS)
 102                        cable->changed = false;
 103
 104                /* Don't enable id interrupt if using external notifier */
 105                if (data & mask & OTGSC_IDIE) {
 106                        cable->enabled = true;
 107                        data &= ~OTGSC_IDIE;
 108                } else if (mask & OTGSC_IDIE) {
 109                        cable->enabled = false;
 110                }
 111        }
 112
 113        hw_write(ci, OP_OTGSC, mask | OTGSC_INT_STATUS_BITS, data);
 114}
 115
 116/**
 117 * ci_otg_role - pick role based on ID pin state
 118 * @ci: the controller
 119 */
 120enum ci_role ci_otg_role(struct ci_hdrc *ci)
 121{
 122        enum ci_role role = hw_read_otgsc(ci, OTGSC_ID)
 123                ? CI_ROLE_GADGET
 124                : CI_ROLE_HOST;
 125
 126        return role;
 127}
 128
 129void ci_handle_vbus_change(struct ci_hdrc *ci)
 130{
 131        if (!ci->is_otg)
 132                return;
 133
 134        if (hw_read_otgsc(ci, OTGSC_BSV) && !ci->vbus_active)
 135                usb_gadget_vbus_connect(&ci->gadget);
 136        else if (!hw_read_otgsc(ci, OTGSC_BSV) && ci->vbus_active)
 137                usb_gadget_vbus_disconnect(&ci->gadget);
 138}
 139
 140/**
 141 * When we switch to device mode, the vbus value should be lower
 142 * than OTGSC_BSV before connecting to host.
 143 *
 144 * @ci: the controller
 145 *
 146 * This function returns an error code if timeout
 147 */
 148static int hw_wait_vbus_lower_bsv(struct ci_hdrc *ci)
 149{
 150        unsigned long elapse = jiffies + msecs_to_jiffies(5000);
 151        u32 mask = OTGSC_BSV;
 152
 153        while (hw_read_otgsc(ci, mask)) {
 154                if (time_after(jiffies, elapse)) {
 155                        dev_err(ci->dev, "timeout waiting for %08x in OTGSC\n",
 156                                        mask);
 157                        return -ETIMEDOUT;
 158                }
 159                msleep(20);
 160        }
 161
 162        return 0;
 163}
 164
 165static void ci_handle_id_switch(struct ci_hdrc *ci)
 166{
 167        enum ci_role role = ci_otg_role(ci);
 168
 169        if (role != ci->role) {
 170                dev_dbg(ci->dev, "switching from %s to %s\n",
 171                        ci_role(ci)->name, ci->roles[role]->name);
 172
 173                ci_role_stop(ci);
 174
 175                if (role == CI_ROLE_GADGET &&
 176                                IS_ERR(ci->platdata->vbus_extcon.edev))
 177                        /*
 178                         * Wait vbus lower than OTGSC_BSV before connecting
 179                         * to host. If connecting status is from an external
 180                         * connector instead of register, we don't need to
 181                         * care vbus on the board, since it will not affect
 182                         * external connector status.
 183                         */
 184                        hw_wait_vbus_lower_bsv(ci);
 185
 186                ci_role_start(ci, role);
 187                /* vbus change may have already occurred */
 188                if (role == CI_ROLE_GADGET)
 189                        ci_handle_vbus_change(ci);
 190        }
 191}
 192/**
 193 * ci_otg_work - perform otg (vbus/id) event handle
 194 * @work: work struct
 195 */
 196static void ci_otg_work(struct work_struct *work)
 197{
 198        struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work);
 199
 200        if (ci_otg_is_fsm_mode(ci) && !ci_otg_fsm_work(ci)) {
 201                enable_irq(ci->irq);
 202                return;
 203        }
 204
 205        pm_runtime_get_sync(ci->dev);
 206        if (ci->id_event) {
 207                ci->id_event = false;
 208                ci_handle_id_switch(ci);
 209        } else if (ci->b_sess_valid_event) {
 210                ci->b_sess_valid_event = false;
 211                ci_handle_vbus_change(ci);
 212        } else
 213                dev_err(ci->dev, "unexpected event occurs at %s\n", __func__);
 214        pm_runtime_put_sync(ci->dev);
 215
 216        enable_irq(ci->irq);
 217}
 218
 219
 220/**
 221 * ci_hdrc_otg_init - initialize otg struct
 222 * ci: the controller
 223 */
 224int ci_hdrc_otg_init(struct ci_hdrc *ci)
 225{
 226        INIT_WORK(&ci->work, ci_otg_work);
 227        ci->wq = create_freezable_workqueue("ci_otg");
 228        if (!ci->wq) {
 229                dev_err(ci->dev, "can't create workqueue\n");
 230                return -ENODEV;
 231        }
 232
 233        if (ci_otg_is_fsm_mode(ci))
 234                return ci_hdrc_otg_fsm_init(ci);
 235
 236        return 0;
 237}
 238
 239/**
 240 * ci_hdrc_otg_destroy - destroy otg struct
 241 * ci: the controller
 242 */
 243void ci_hdrc_otg_destroy(struct ci_hdrc *ci)
 244{
 245        if (ci->wq) {
 246                flush_workqueue(ci->wq);
 247                destroy_workqueue(ci->wq);
 248        }
 249        /* Disable all OTG irq and clear status */
 250        hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
 251                                                OTGSC_INT_STATUS_BITS);
 252        if (ci_otg_is_fsm_mode(ci))
 253                ci_hdrc_otg_fsm_remove(ci);
 254}
 255