root/drivers/usb/chipidea/otg.c

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

DEFINITIONS

This source file includes following definitions.
  1. hw_read_otgsc
  2. hw_write_otgsc
  3. ci_otg_role
  4. ci_handle_vbus_change
  5. hw_wait_vbus_lower_bsv
  6. ci_handle_id_switch
  7. ci_otg_work
  8. ci_hdrc_otg_init
  9. ci_hdrc_otg_destroy

   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  */
  28 u32 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) || ci->role_switch) {
  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) || ci->role_switch) {
  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  */
  81 void 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) || ci->role_switch) {
  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) || ci->role_switch) {
 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  */
 120 enum 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 
 129 void 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  */
 148 static 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 
 165 static 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  */
 196 static 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 
 207         if (ci->id_event) {
 208                 ci->id_event = false;
 209                 ci_handle_id_switch(ci);
 210         }
 211 
 212         if (ci->b_sess_valid_event) {
 213                 ci->b_sess_valid_event = false;
 214                 ci_handle_vbus_change(ci);
 215         }
 216 
 217         pm_runtime_put_sync(ci->dev);
 218 
 219         enable_irq(ci->irq);
 220 }
 221 
 222 
 223 /**
 224  * ci_hdrc_otg_init - initialize otg struct
 225  * ci: the controller
 226  */
 227 int ci_hdrc_otg_init(struct ci_hdrc *ci)
 228 {
 229         INIT_WORK(&ci->work, ci_otg_work);
 230         ci->wq = create_freezable_workqueue("ci_otg");
 231         if (!ci->wq) {
 232                 dev_err(ci->dev, "can't create workqueue\n");
 233                 return -ENODEV;
 234         }
 235 
 236         if (ci_otg_is_fsm_mode(ci))
 237                 return ci_hdrc_otg_fsm_init(ci);
 238 
 239         return 0;
 240 }
 241 
 242 /**
 243  * ci_hdrc_otg_destroy - destroy otg struct
 244  * ci: the controller
 245  */
 246 void ci_hdrc_otg_destroy(struct ci_hdrc *ci)
 247 {
 248         if (ci->wq) {
 249                 flush_workqueue(ci->wq);
 250                 destroy_workqueue(ci->wq);
 251         }
 252         /* Disable all OTG irq and clear status */
 253         hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
 254                                                 OTGSC_INT_STATUS_BITS);
 255         if (ci_otg_is_fsm_mode(ci))
 256                 ci_hdrc_otg_fsm_remove(ci);
 257 }

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