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