usb: chipidea: add OTG fsm operation functions implementation
[linux-2.6-block.git] / drivers / usb / chipidea / otg_fsm.c
CommitLineData
57677be5
LJ
1/*
2 * otg_fsm.c - ChipIdea USB IP core OTG FSM driver
3 *
4 * Copyright (C) 2014 Freescale Semiconductor, Inc.
5 *
6 * Author: Jun Li
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 OTG fsm, it includes OTG fsm operations
15 * for HNP and SRP.
16 */
17
18#include <linux/usb/otg.h>
19#include <linux/usb/gadget.h>
20#include <linux/usb/hcd.h>
21#include <linux/usb/chipidea.h>
826cfe75 22#include <linux/regulator/consumer.h>
57677be5
LJ
23
24#include "ci.h"
25#include "bits.h"
26#include "otg.h"
27#include "otg_fsm.h"
28
826cfe75
LJ
29/*
30 * Add timer to active timer list
31 */
32static void ci_otg_add_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t)
33{
34 struct ci_otg_fsm_timer *tmp_timer;
35 struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t];
36 struct list_head *active_timers = &ci->fsm_timer->active_timers;
37
38 if (t >= NUM_CI_OTG_FSM_TIMERS)
39 return;
40
41 /*
42 * Check if the timer is already in the active list,
43 * if so update timer count
44 */
45 list_for_each_entry(tmp_timer, active_timers, list)
46 if (tmp_timer == timer) {
47 timer->count = timer->expires;
48 return;
49 }
50
51 timer->count = timer->expires;
52 list_add_tail(&timer->list, active_timers);
53
54 /* Enable 1ms irq */
55 if (!(hw_read_otgsc(ci, OTGSC_1MSIE)))
56 hw_write_otgsc(ci, OTGSC_1MSIE, OTGSC_1MSIE);
57}
58
59/*
60 * Remove timer from active timer list
61 */
62static void ci_otg_del_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t)
63{
64 struct ci_otg_fsm_timer *tmp_timer, *del_tmp;
65 struct ci_otg_fsm_timer *timer = ci->fsm_timer->timer_list[t];
66 struct list_head *active_timers = &ci->fsm_timer->active_timers;
67
68 if (t >= NUM_CI_OTG_FSM_TIMERS)
69 return;
70
71 list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list)
72 if (tmp_timer == timer)
73 list_del(&timer->list);
74
75 /* Disable 1ms irq if there is no any active timer */
76 if (list_empty(active_timers))
77 hw_write_otgsc(ci, OTGSC_1MSIE, 0);
78}
79
80/* -------------------------------------------------------------*/
81/* Operations that will be called from OTG Finite State Machine */
82/* -------------------------------------------------------------*/
83static void ci_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
84{
85 struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
86
87 if (t < NUM_OTG_FSM_TIMERS)
88 ci_otg_add_timer(ci, t);
89 return;
90}
91
92static void ci_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
93{
94 struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
95
96 if (t < NUM_OTG_FSM_TIMERS)
97 ci_otg_del_timer(ci, t);
98 return;
99}
100
101/*
102 * A-device drive vbus: turn on vbus regulator and enable port power
103 * Data pulse irq should be disabled while vbus is on.
104 */
105static void ci_otg_drv_vbus(struct otg_fsm *fsm, int on)
106{
107 int ret;
108 struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
109
110 if (on) {
111 /* Enable power power */
112 hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP,
113 PORTSC_PP);
114 if (ci->platdata->reg_vbus) {
115 ret = regulator_enable(ci->platdata->reg_vbus);
116 if (ret) {
117 dev_err(ci->dev,
118 "Failed to enable vbus regulator, ret=%d\n",
119 ret);
120 return;
121 }
122 }
123 /* Disable data pulse irq */
124 hw_write_otgsc(ci, OTGSC_DPIE, 0);
125
126 fsm->a_srp_det = 0;
127 fsm->power_up = 0;
128 } else {
129 if (ci->platdata->reg_vbus)
130 regulator_disable(ci->platdata->reg_vbus);
131
132 fsm->a_bus_drop = 1;
133 fsm->a_bus_req = 0;
134 }
135}
136
137/*
138 * Control data line by Run Stop bit.
139 */
140static void ci_otg_loc_conn(struct otg_fsm *fsm, int on)
141{
142 struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
143
144 if (on)
145 hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
146 else
147 hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
148}
149
150/*
151 * Generate SOF by host.
152 * This is controlled through suspend/resume the port.
153 * In host mode, controller will automatically send SOF.
154 * Suspend will block the data on the port.
155 */
156static void ci_otg_loc_sof(struct otg_fsm *fsm, int on)
157{
158 struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
159
160 if (on)
161 hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_FPR,
162 PORTSC_FPR);
163 else
164 hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_SUSP,
165 PORTSC_SUSP);
166}
167
168/*
169 * Start SRP pulsing by data-line pulsing,
170 * no v-bus pulsing followed
171 */
172static void ci_otg_start_pulse(struct otg_fsm *fsm)
173{
174 struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
175
176 /* Hardware Assistant Data pulse */
177 hw_write_otgsc(ci, OTGSC_HADP, OTGSC_HADP);
178
179 ci_otg_add_timer(ci, B_DATA_PLS);
180}
181
182static int ci_otg_start_host(struct otg_fsm *fsm, int on)
183{
184 struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
185
186 mutex_unlock(&fsm->lock);
187 if (on) {
188 ci_role_stop(ci);
189 ci_role_start(ci, CI_ROLE_HOST);
190 } else {
191 ci_role_stop(ci);
192 hw_device_reset(ci, USBMODE_CM_DC);
193 ci_role_start(ci, CI_ROLE_GADGET);
194 }
195 mutex_lock(&fsm->lock);
196 return 0;
197}
198
199static int ci_otg_start_gadget(struct otg_fsm *fsm, int on)
200{
201 struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
202
203 mutex_unlock(&fsm->lock);
204 if (on)
205 usb_gadget_vbus_connect(&ci->gadget);
206 else
207 usb_gadget_vbus_disconnect(&ci->gadget);
208 mutex_lock(&fsm->lock);
209
210 return 0;
211}
212
213static struct otg_fsm_ops ci_otg_ops = {
214 .drv_vbus = ci_otg_drv_vbus,
215 .loc_conn = ci_otg_loc_conn,
216 .loc_sof = ci_otg_loc_sof,
217 .start_pulse = ci_otg_start_pulse,
218 .add_timer = ci_otg_fsm_add_timer,
219 .del_timer = ci_otg_fsm_del_timer,
220 .start_host = ci_otg_start_host,
221 .start_gadget = ci_otg_start_gadget,
222};
223
57677be5
LJ
224int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
225{
226 struct usb_otg *otg;
227
228 otg = devm_kzalloc(ci->dev,
229 sizeof(struct usb_otg), GFP_KERNEL);
230 if (!otg) {
231 dev_err(ci->dev,
232 "Failed to allocate usb_otg structure for ci hdrc otg!\n");
233 return -ENOMEM;
234 }
235
236 otg->phy = ci->transceiver;
237 otg->gadget = &ci->gadget;
238 ci->fsm.otg = otg;
239 ci->transceiver->otg = ci->fsm.otg;
240 ci->fsm.power_up = 1;
241 ci->fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0;
242 ci->transceiver->state = OTG_STATE_UNDEFINED;
826cfe75 243 ci->fsm.ops = &ci_otg_ops;
57677be5
LJ
244
245 mutex_init(&ci->fsm.lock);
246
247 /* Enable A vbus valid irq */
248 hw_write_otgsc(ci, OTGSC_AVVIE, OTGSC_AVVIE);
249
250 if (ci->fsm.id) {
251 ci->fsm.b_ssend_srp =
252 hw_read_otgsc(ci, OTGSC_BSV) ? 0 : 1;
253 ci->fsm.b_sess_vld =
254 hw_read_otgsc(ci, OTGSC_BSV) ? 1 : 0;
255 /* Enable BSV irq */
256 hw_write_otgsc(ci, OTGSC_BSVIE, OTGSC_BSVIE);
257 }
258
259 return 0;
260}