diff options
author | Li Jun <B47624@freescale.com> | 2014-01-01 00:35:34 -0500 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-16 12:02:34 -0400 |
commit | c777a32b4bf03edb888bb5831f1af2a5a5d58472 (patch) | |
tree | 907bd24bb9fa0090ad6ec271c64a587ceedc8480 | |
parent | 8e1d8d5e1bce096257e64625e27a2c3b6a50a151 (diff) |
ENGR00307558-5 usb: chipidea: add OTG fsm operation functions implemenation.
Add OTG HNP and SRP operation functions implementation:
- charge vbus
- drive vbus
- connection signaling
- drive sof
- start data pulse
- add fsm timer
- delete fsm timer
- start host
- start gadget
Signed-off-by: Li Jun <b47624@freescale.com>
-rw-r--r-- | drivers/usb/chipidea/bits.h | 9 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci.h | 2 | ||||
-rw-r--r-- | drivers/usb/chipidea/otg_fsm.c | 197 | ||||
-rw-r--r-- | drivers/usb/chipidea/otg_fsm.h | 25 |
4 files changed, 233 insertions, 0 deletions
diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h index aa82c8517bcd..49d2dc1ab476 100644 --- a/drivers/usb/chipidea/bits.h +++ b/drivers/usb/chipidea/bits.h | |||
@@ -44,9 +44,14 @@ | |||
44 | #define DEVICEADDR_USBADR (0x7FUL << 25) | 44 | #define DEVICEADDR_USBADR (0x7FUL << 25) |
45 | 45 | ||
46 | /* PORTSC */ | 46 | /* PORTSC */ |
47 | #define PORTSC_CCS BIT(0) | ||
48 | #define PORTSC_CSC BIT(1) | ||
49 | #define PORTSC_PEC BIT(3) | ||
50 | #define PORTSC_OCC BIT(5) | ||
47 | #define PORTSC_FPR BIT(6) | 51 | #define PORTSC_FPR BIT(6) |
48 | #define PORTSC_SUSP BIT(7) | 52 | #define PORTSC_SUSP BIT(7) |
49 | #define PORTSC_HSP BIT(9) | 53 | #define PORTSC_HSP BIT(9) |
54 | #define PORTSC_PP BIT(12) | ||
50 | #define PORTSC_PTC (0x0FUL << 16) | 55 | #define PORTSC_PTC (0x0FUL << 16) |
51 | #define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23)) | 56 | #define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23)) |
52 | /* PTS and PTW for non lpm version only */ | 57 | /* PTS and PTW for non lpm version only */ |
@@ -56,6 +61,9 @@ | |||
56 | #define PORTSC_PTW BIT(28) | 61 | #define PORTSC_PTW BIT(28) |
57 | #define PORTSC_STS BIT(29) | 62 | #define PORTSC_STS BIT(29) |
58 | 63 | ||
64 | #define PORTSC_W1C_BITS \ | ||
65 | (PORTSC_CSC | PORTSC_PEC | PORTSC_OCC) | ||
66 | |||
59 | /* DEVLC */ | 67 | /* DEVLC */ |
60 | #define DEVLC_PFSC BIT(23) | 68 | #define DEVLC_PFSC BIT(23) |
61 | #define DEVLC_PSPD (0x03UL << 25) | 69 | #define DEVLC_PSPD (0x03UL << 25) |
@@ -72,6 +80,7 @@ | |||
72 | 80 | ||
73 | /* OTGSC */ | 81 | /* OTGSC */ |
74 | #define OTGSC_IDPU BIT(5) | 82 | #define OTGSC_IDPU BIT(5) |
83 | #define OTGSC_HADP BIT(6) | ||
75 | #define OTGSC_ID BIT(8) | 84 | #define OTGSC_ID BIT(8) |
76 | #define OTGSC_AVV BIT(9) | 85 | #define OTGSC_AVV BIT(9) |
77 | #define OTGSC_ASV BIT(10) | 86 | #define OTGSC_ASV BIT(10) |
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 2120e6dc1ef7..cbd333edbec0 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h | |||
@@ -112,6 +112,7 @@ struct hw_bank { | |||
112 | * @role: current role | 112 | * @role: current role |
113 | * @is_otg: if the device is otg-capable | 113 | * @is_otg: if the device is otg-capable |
114 | * @fsm: otg finite state machine | 114 | * @fsm: otg finite state machine |
115 | * @fsm_timer: pointer to timer list of otg fsm | ||
115 | * @work: work for role changing | 116 | * @work: work for role changing |
116 | * @wq: workqueue thread | 117 | * @wq: workqueue thread |
117 | * @qh_pool: allocation pool for queue heads | 118 | * @qh_pool: allocation pool for queue heads |
@@ -152,6 +153,7 @@ struct ci_hdrc { | |||
152 | enum ci_role role; | 153 | enum ci_role role; |
153 | bool is_otg; | 154 | bool is_otg; |
154 | struct otg_fsm fsm; | 155 | struct otg_fsm fsm; |
156 | struct ci_otg_fsm_timer_list *fsm_timer; | ||
155 | struct work_struct work; | 157 | struct work_struct work; |
156 | struct workqueue_struct *wq; | 158 | struct workqueue_struct *wq; |
157 | 159 | ||
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index f4bb7c83b708..4a40399df9e9 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c | |||
@@ -19,12 +19,208 @@ | |||
19 | #include <linux/usb/gadget.h> | 19 | #include <linux/usb/gadget.h> |
20 | #include <linux/usb/hcd.h> | 20 | #include <linux/usb/hcd.h> |
21 | #include <linux/usb/chipidea.h> | 21 | #include <linux/usb/chipidea.h> |
22 | #include <linux/regulator/consumer.h> | ||
22 | 23 | ||
23 | #include "ci.h" | 24 | #include "ci.h" |
24 | #include "bits.h" | 25 | #include "bits.h" |
25 | #include "otg.h" | 26 | #include "otg.h" |
26 | #include "otg_fsm.h" | 27 | #include "otg_fsm.h" |
27 | 28 | ||
29 | /* | ||
30 | * Add timer to active timer list | ||
31 | */ | ||
32 | static 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 | */ | ||
62 | static 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 | /* -------------------------------------------------------------*/ | ||
83 | static 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 | |||
92 | static 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 | */ | ||
105 | static 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 | */ | ||
140 | static 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 | */ | ||
156 | static 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 | */ | ||
172 | static 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 | |||
182 | static 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 | |||
199 | static 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 | |||
213 | static 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 | |||
28 | int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) | 224 | int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) |
29 | { | 225 | { |
30 | struct usb_otg *otg; | 226 | struct usb_otg *otg; |
@@ -46,6 +242,7 @@ int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) | |||
46 | ci->fsm.power_up = 1; | 242 | ci->fsm.power_up = 1; |
47 | ci->fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0; | 243 | ci->fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0; |
48 | ci->transceiver->state = OTG_STATE_UNDEFINED; | 244 | ci->transceiver->state = OTG_STATE_UNDEFINED; |
245 | ci->fsm.ops = &ci_otg_ops; | ||
49 | 246 | ||
50 | mutex_init(&ci->fsm.lock); | 247 | mutex_init(&ci->fsm.lock); |
51 | 248 | ||
diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h index bf20a851b601..4d0dfe6900d6 100644 --- a/drivers/usb/chipidea/otg_fsm.h +++ b/drivers/usb/chipidea/otg_fsm.h | |||
@@ -13,6 +13,31 @@ | |||
13 | 13 | ||
14 | #include <linux/usb/otg-fsm.h> | 14 | #include <linux/usb/otg-fsm.h> |
15 | 15 | ||
16 | enum ci_otg_fsm_timer_index { | ||
17 | /* | ||
18 | * CI specific timers, start from the end | ||
19 | * of standard and auxiliary OTG timers | ||
20 | */ | ||
21 | B_DATA_PLS = NUM_OTG_FSM_TIMERS, | ||
22 | B_SSEND_SRP, | ||
23 | B_SESS_VLD, | ||
24 | |||
25 | NUM_CI_OTG_FSM_TIMERS, | ||
26 | }; | ||
27 | |||
28 | struct ci_otg_fsm_timer { | ||
29 | unsigned long expires; /* Number of count increase to timeout */ | ||
30 | unsigned long count; /* Tick counter */ | ||
31 | void (*function)(void *, unsigned long); /* Timeout function */ | ||
32 | unsigned long data; /* Data passed to function */ | ||
33 | struct list_head list; | ||
34 | }; | ||
35 | |||
36 | struct ci_otg_fsm_timer_list { | ||
37 | struct ci_otg_fsm_timer *timer_list[NUM_CI_OTG_FSM_TIMERS]; | ||
38 | struct list_head active_timers; | ||
39 | }; | ||
40 | |||
16 | #ifdef CONFIG_USB_OTG_FSM | 41 | #ifdef CONFIG_USB_OTG_FSM |
17 | 42 | ||
18 | int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci); | 43 | int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci); |