1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
|
/* Copyright (c) 2013-2014 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/kernel.h>
#include <linux/usb/hbm.h>
#include <linux/usb_bam.h>
/**
* USB HBM Hardware registers.
*
*/
#define USB_OTG_HS_HBM_CFG (0x00000290)
#define USB_OTG_HS_HBM_QH_MAP_PIPE(n) (0x00000294 + 4 * (n))
#define USB_OTG_HS_HBM_PIPE_PRODUCER (0x00000314)
#define USB_OTG_HS_HBM_PARK_MODE_DISABLE (0x00000318)
#define USB_OTG_HS_HBM_PIPE_ZLT_DISABLE (0x0000031C)
#define USB_OTG_HS_HBM_PIPE_EN (0x00000310)
#define USB_OTG_HS_HBM_SW_RST (0x00000324)
#define USB_OTG_HS_HBM_SB_SW_RST (0x00000320)
#define USB_OTG_HS_USBCMD (0x00000140)
#define USB_OTG_HS_USBSTS (0x00000144)
/**
* USB HBM Hardware registers bitmask.
*/
#define HBM_EN 0x00000001
#define ASE 0x20
#define AS 0x8000
#define PIPE_PRODUCER 1
#define MAX_PIPE_NUM 16
#define HBM_QH_MAP_PIPE 0xffffffc0
#define QTD_CERR_MASK 0xfffff3ff
struct hbm_msm {
u32 *base;
struct usb_hcd *hcd;
bool disable_park_mode;
};
static struct hbm_msm *hbm_ctx;
/**
* Read register masked field.
*
* @base - hbm base virtual address.
* @offset - register offset.
* @mask - register bitmask.
*
* @return u32
*/
static inline u32 hbm_msm_read_reg_field(void *base,
u32 offset, const u32 mask)
{
u32 shift = find_first_bit((void *)&mask, 32);
u32 val = ioread32(base + offset);
val &= mask; /* clear other bits */
val >>= shift;
return val;
}
/**
* Write register field.
*
* @base - hbm base virtual address.
* @offset - register offset.
* @val - value to be written.
*
*/
static inline void hbm_msm_write_reg(void *base, u32 offset, u32 val)
{
iowrite32(val, base + offset);
}
/**
* Write register masked field.
*
* @base - hbm base virtual address.
* @offset - register offset.
* @mask - register bitmask.
* @val - value to write.
*
*/
static inline void hbm_msm_write_reg_field(void *base, u32 offset,
const u32 mask, u32 val)
{
u32 shift = find_first_bit((void *)&mask, 32);
u32 tmp = ioread32(base + offset);
tmp &= ~mask;
val = tmp | (val << shift);
iowrite32(val, base + offset);
}
/**
* Enable/disable park mode. Park mode enables executing up to 3 usb packets
* from each QH.
*
* @pipe_num - Connection index.
*
* @disable_park_mode - Enable/disable park mode.
*
*/
int set_disable_park_mode(u8 pipe_num, bool disable_park_mode)
{
if (pipe_num >= MAX_PIPE_NUM) {
pr_err("%s: illegal pipe num %d", __func__, pipe_num);
return -EINVAL;
}
/* enable/disable park mode */
hbm_msm_write_reg_field(hbm_ctx->base,
USB_OTG_HS_HBM_PARK_MODE_DISABLE, 1 << pipe_num,
(disable_park_mode ? 1 : 0));
return 0;
}
/**
* Enable/disable zero length transfer.
*
* @pipe_num - Connection index.
*
* @disable_zlt - Enable/disable zlt.
*
*/
int set_disable_zlt(u8 pipe_num, bool disable_zlt)
{
if (pipe_num >= MAX_PIPE_NUM) {
pr_err("%s: illegal pipe num %d", __func__, pipe_num);
return -EINVAL;
}
/* enable/disable zlt */
hbm_msm_write_reg_field(hbm_ctx->base,
USB_OTG_HS_HBM_PIPE_ZLT_DISABLE, 1 << pipe_num,
(disable_zlt ? 1 : 0));
return 0;
}
static void hbm_reset(bool reset)
{
hbm_msm_write_reg_field(hbm_ctx->base, USB_OTG_HS_HBM_SW_RST, 1 << 0,
reset ? 1 : 0);
}
static void hbm_config(bool enable)
{
hbm_msm_write_reg_field(hbm_ctx->base, USB_OTG_HS_HBM_CFG, HBM_EN,
enable ? 1 : 0);
}
int hbm_pipe_init(u32 QH_addr, u32 pipe_num, bool is_consumer)
{
if (pipe_num >= MAX_PIPE_NUM) {
pr_err("%s: illegal pipe num %d", __func__, pipe_num);
return -EINVAL;
}
/* map QH(ep) <> pipe */
hbm_msm_write_reg(hbm_ctx->base,
USB_OTG_HS_HBM_QH_MAP_PIPE(pipe_num), QH_addr);
/* set pipe producer/consumer mode - (IN EP is producer) */
hbm_msm_write_reg_field(hbm_ctx->base,
USB_OTG_HS_HBM_PIPE_PRODUCER, 1 << pipe_num,
(is_consumer ? 0 : 1));
/* set park mode */
set_disable_park_mode(pipe_num, hbm_ctx->disable_park_mode);
/* enable zlt as default*/
set_disable_zlt(pipe_num, false);
/* activate pipe */
hbm_msm_write_reg_field(hbm_ctx->base, USB_OTG_HS_HBM_PIPE_EN,
1 << pipe_num, 1);
return 0;
}
void hbm_init(struct usb_hcd *hcd, bool disable_park_mode)
{
pr_info("%s\n", __func__);
hbm_ctx = kzalloc(sizeof(*hbm_ctx), GFP_KERNEL);
if (!hbm_ctx) {
pr_err("%s: hbm_ctx alloc failed\n", __func__);
return;
}
hbm_ctx->base = hcd->regs;
hbm_ctx->hcd = hcd;
hbm_ctx->disable_park_mode = disable_park_mode;
/* reset hbm */
hbm_reset(true);
/* delay was added to allow the reset process the end */
udelay(1000);
hbm_reset(false);
hbm_config(true);
}
void hbm_uninit(void)
{
hbm_config(false);
kfree(hbm_ctx);
}
static int hbm_submit_async(struct ehci_hcd *ehci, struct urb *urb,
struct list_head *qtd_list, gfp_t mem_flags)
{
int epnum;
unsigned long flags;
struct ehci_qh *qh = NULL;
int rc;
struct usb_host_bam_type *bam =
(struct usb_host_bam_type *)urb->priv_data;
epnum = urb->ep->desc.bEndpointAddress;
spin_lock_irqsave(&ehci->lock, flags);
if (unlikely(!HCD_HW_ACCESSIBLE(ehci_to_hcd(ehci)))) {
rc = -ESHUTDOWN;
goto done;
}
rc = usb_hcd_link_urb_to_ep(ehci_to_hcd(ehci), urb);
if (unlikely(rc))
goto done;
qh = qh_append_tds(ehci, urb, qtd_list, epnum, &urb->ep->hcpriv);
if (unlikely(qh == NULL)) {
usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
rc = -ENOMEM;
goto done;
}
hbm_pipe_init(qh->qh_dma, bam->pipe_num, bam->dir);
if (likely(qh->qh_state == QH_STATE_IDLE))
qh_link_async(ehci, qh);
done:
spin_unlock_irqrestore(&ehci->lock, flags);
if (unlikely(qh == NULL))
qtd_list_free(ehci, urb, qtd_list);
return rc;
}
int hbm_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
struct list_head qtd_list;
struct ehci_qtd *qtd;
INIT_LIST_HEAD(&qtd_list);
if (usb_pipetype(urb->pipe) != PIPE_BULK) {
pr_err("%s pipe type is not BULK\n", __func__);
return -EINVAL;
}
/*no sg support*/
urb->transfer_buffer_length = 0;
urb->transfer_dma = 0;
urb->transfer_flags |= URB_NO_INTERRUPT;
if (!qh_urb_transaction(ehci, urb, &qtd_list, mem_flags))
return -ENOMEM;
/* set err counter in qTD token to zero */
qtd = list_entry(qtd_list.next, struct ehci_qtd, qtd_list);
if (qtd != NULL)
qtd->hw_token &= QTD_CERR_MASK;
return hbm_submit_async(ehci, urb, &qtd_list, mem_flags);
}
|