1/* 2 * otg.c - ChipIdea USB IP core OTG driver 3 * 4 * Copyright (C) 2013 Freescale Semiconductor, Inc. 5 * 6 * Author: Peter Chen 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12 13/* 14 * This file mainly handles otgsc register, OTG fsm operations for HNP and SRP 15 * are also included. 16 */ 17 18#include <linux/usb/otg.h> 19#include <linux/usb/gadget.h> 20#include <linux/usb/chipidea.h> 21 22#include "ci.h" 23#include "bits.h" 24#include "otg.h" 25#include "otg_fsm.h" 26 27/** 28 * hw_read_otgsc returns otgsc register bits value. 29 * @mask: bitfield mask 30 */ 31u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask) 32{ 33 struct ci_hdrc_cable *cable; 34 u32 val = hw_read(ci, OP_OTGSC, mask); 35 36 /* 37 * If using extcon framework for VBUS and/or ID signal 38 * detection overwrite OTGSC register value 39 */ 40 cable = &ci->platdata->vbus_extcon; 41 if (!IS_ERR(cable->edev)) { 42 if (cable->changed) 43 val |= OTGSC_BSVIS; 44 else 45 val &= ~OTGSC_BSVIS; 46 47 cable->changed = false; 48 49 if (cable->state) 50 val |= OTGSC_BSV; 51 else 52 val &= ~OTGSC_BSV; 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 cable->changed = false; 63 64 if (cable->state) 65 val |= OTGSC_ID; 66 else 67 val &= ~OTGSC_ID; 68 } 69 70 return val; 71} 72 73/** 74 * hw_write_otgsc updates target bits of OTGSC register. 75 * @mask: bitfield mask 76 * @data: to be written 77 */ 78void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data) 79{ 80 hw_write(ci, OP_OTGSC, mask | OTGSC_INT_STATUS_BITS, data); 81} 82 83/** 84 * ci_otg_role - pick role based on ID pin state 85 * @ci: the controller 86 */ 87enum ci_role ci_otg_role(struct ci_hdrc *ci) 88{ 89 enum ci_role role = hw_read_otgsc(ci, OTGSC_ID) 90 ? CI_ROLE_GADGET 91 : CI_ROLE_HOST; 92 93 return role; 94} 95 96void ci_handle_vbus_change(struct ci_hdrc *ci) 97{ 98 if (!ci->is_otg) 99 return; 100 101 if (hw_read_otgsc(ci, OTGSC_BSV)) 102 usb_gadget_vbus_connect(&ci->gadget); 103 else 104 usb_gadget_vbus_disconnect(&ci->gadget); 105} 106 107#define CI_VBUS_STABLE_TIMEOUT_MS 5000 108static void ci_handle_id_switch(struct ci_hdrc *ci) 109{ 110 enum ci_role role = ci_otg_role(ci); 111 112 if (role != ci->role) { 113 dev_dbg(ci->dev, "switching from %s to %s\n", 114 ci_role(ci)->name, ci->roles[role]->name); 115 116 ci_role_stop(ci); 117 118 if (role == CI_ROLE_GADGET) 119 /* wait vbus lower than OTGSC_BSV */ 120 hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0, 121 CI_VBUS_STABLE_TIMEOUT_MS); 122 123 ci_role_start(ci, role); 124 } 125} 126/** 127 * ci_otg_work - perform otg (vbus/id) event handle 128 * @work: work struct 129 */ 130static void ci_otg_work(struct work_struct *work) 131{ 132 struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work); 133 134 if (ci_otg_is_fsm_mode(ci) && !ci_otg_fsm_work(ci)) { 135 enable_irq(ci->irq); 136 return; 137 } 138 139 pm_runtime_get_sync(ci->dev); 140 if (ci->id_event) { 141 ci->id_event = false; 142 ci_handle_id_switch(ci); 143 } else if (ci->b_sess_valid_event) { 144 ci->b_sess_valid_event = false; 145 ci_handle_vbus_change(ci); 146 } else 147 dev_err(ci->dev, "unexpected event occurs at %s\n", __func__); 148 pm_runtime_put_sync(ci->dev); 149 150 enable_irq(ci->irq); 151} 152 153 154/** 155 * ci_hdrc_otg_init - initialize otg struct 156 * ci: the controller 157 */ 158int ci_hdrc_otg_init(struct ci_hdrc *ci) 159{ 160 INIT_WORK(&ci->work, ci_otg_work); 161 ci->wq = create_freezable_workqueue("ci_otg"); 162 if (!ci->wq) { 163 dev_err(ci->dev, "can't create workqueue\n"); 164 return -ENODEV; 165 } 166 167 if (ci_otg_is_fsm_mode(ci)) 168 return ci_hdrc_otg_fsm_init(ci); 169 170 return 0; 171} 172 173/** 174 * ci_hdrc_otg_destroy - destroy otg struct 175 * ci: the controller 176 */ 177void ci_hdrc_otg_destroy(struct ci_hdrc *ci) 178{ 179 if (ci->wq) { 180 flush_workqueue(ci->wq); 181 destroy_workqueue(ci->wq); 182 } 183 /* Disable all OTG irq and clear status */ 184 hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS, 185 OTGSC_INT_STATUS_BITS); 186 if (ci_otg_is_fsm_mode(ci)) 187 ci_hdrc_otg_fsm_remove(ci); 188} 189