1 // SPDX-License-Identifier: GPL-2.0+
3 * aspeed-vhub -- Driver for Aspeed SoC "vHub" USB gadget
5 * core.c - Top level support
7 * Copyright 2017 IBM Corporation
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <linux/platform_device.h>
18 #include <linux/delay.h>
19 #include <linux/ioport.h>
20 #include <linux/slab.h>
21 #include <linux/errno.h>
22 #include <linux/list.h>
23 #include <linux/interrupt.h>
24 #include <linux/proc_fs.h>
25 #include <linux/prefetch.h>
26 #include <linux/clk.h>
27 #include <linux/usb/gadget.h>
29 #include <linux/of_gpio.h>
30 #include <linux/regmap.h>
31 #include <linux/dma-mapping.h>
35 void ast_vhub_done(struct ast_vhub_ep
*ep
, struct ast_vhub_req
*req
,
38 bool internal
= req
->internal
;
40 EPVDBG(ep
, "completing request @%p, status %d\n", req
, status
);
42 list_del_init(&req
->queue
);
44 if (req
->req
.status
== -EINPROGRESS
)
45 req
->req
.status
= status
;
48 if (!WARN_ON(!ep
->dev
))
49 usb_gadget_unmap_request(&ep
->dev
->gadget
,
50 &req
->req
, ep
->epn
.is_in
);
55 * If this isn't an internal EP0 request, call the core
56 * to call the gadget completion.
59 spin_unlock(&ep
->vhub
->lock
);
60 usb_gadget_giveback_request(&ep
->ep
, &req
->req
);
61 spin_lock(&ep
->vhub
->lock
);
65 void ast_vhub_nuke(struct ast_vhub_ep
*ep
, int status
)
67 struct ast_vhub_req
*req
;
70 /* Beware, lock will be dropped & req-acquired by done() */
71 while (!list_empty(&ep
->queue
)) {
72 req
= list_first_entry(&ep
->queue
, struct ast_vhub_req
, queue
);
73 ast_vhub_done(ep
, req
, status
);
77 EPDBG(ep
, "Nuked %d request(s)\n", count
);
80 struct usb_request
*ast_vhub_alloc_request(struct usb_ep
*u_ep
,
83 struct ast_vhub_req
*req
;
85 req
= kzalloc(sizeof(*req
), gfp_flags
);
91 void ast_vhub_free_request(struct usb_ep
*u_ep
, struct usb_request
*u_req
)
93 struct ast_vhub_req
*req
= to_ast_req(u_req
);
98 static irqreturn_t
ast_vhub_irq(int irq
, void *data
)
100 struct ast_vhub
*vhub
= data
;
101 irqreturn_t iret
= IRQ_NONE
;
104 /* Stale interrupt while tearing down */
108 spin_lock(&vhub
->lock
);
110 /* Read and ACK interrupts */
111 istat
= readl(vhub
->regs
+ AST_VHUB_ISR
);
114 writel(istat
, vhub
->regs
+ AST_VHUB_ISR
);
117 UDCVDBG(vhub
, "irq status=%08x, ep_acks=%08x ep_nacks=%08x\n",
119 readl(vhub
->regs
+ AST_VHUB_EP_ACK_ISR
),
120 readl(vhub
->regs
+ AST_VHUB_EP_NACK_ISR
));
122 /* Handle generic EPs first */
123 if (istat
& VHUB_IRQ_EP_POOL_ACK_STALL
) {
124 u32 ep_acks
= readl(vhub
->regs
+ AST_VHUB_EP_ACK_ISR
);
125 writel(ep_acks
, vhub
->regs
+ AST_VHUB_EP_ACK_ISR
);
127 for (i
= 0; ep_acks
&& i
< vhub
->max_epns
; i
++) {
128 u32 mask
= VHUB_EP_IRQ(i
);
129 if (ep_acks
& mask
) {
130 ast_vhub_epn_ack_irq(&vhub
->epns
[i
]);
136 /* Handle device interrupts */
137 if (istat
& vhub
->port_irq_mask
) {
138 for (i
= 0; i
< vhub
->max_ports
; i
++) {
139 if (istat
& VHUB_DEV_IRQ(i
))
140 ast_vhub_dev_irq(&vhub
->ports
[i
].dev
);
144 /* Handle top-level vHub EP0 interrupts */
145 if (istat
& (VHUB_IRQ_HUB_EP0_OUT_ACK_STALL
|
146 VHUB_IRQ_HUB_EP0_IN_ACK_STALL
|
147 VHUB_IRQ_HUB_EP0_SETUP
)) {
148 if (istat
& VHUB_IRQ_HUB_EP0_IN_ACK_STALL
)
149 ast_vhub_ep0_handle_ack(&vhub
->ep0
, true);
150 if (istat
& VHUB_IRQ_HUB_EP0_OUT_ACK_STALL
)
151 ast_vhub_ep0_handle_ack(&vhub
->ep0
, false);
152 if (istat
& VHUB_IRQ_HUB_EP0_SETUP
)
153 ast_vhub_ep0_handle_setup(&vhub
->ep0
);
156 /* Various top level bus events */
157 if (istat
& (VHUB_IRQ_BUS_RESUME
|
158 VHUB_IRQ_BUS_SUSPEND
|
159 VHUB_IRQ_BUS_RESET
)) {
160 if (istat
& VHUB_IRQ_BUS_RESUME
)
161 ast_vhub_hub_resume(vhub
);
162 if (istat
& VHUB_IRQ_BUS_SUSPEND
)
163 ast_vhub_hub_suspend(vhub
);
164 if (istat
& VHUB_IRQ_BUS_RESET
)
165 ast_vhub_hub_reset(vhub
);
169 spin_unlock(&vhub
->lock
);
173 void ast_vhub_init_hw(struct ast_vhub
*vhub
)
175 u32 ctrl
, port_mask
, epn_mask
;
177 UDCDBG(vhub
,"(Re)Starting HW ...\n");
180 ctrl
= VHUB_CTRL_PHY_CLK
|
181 VHUB_CTRL_PHY_RESET_DIS
;
184 * We do *NOT* set the VHUB_CTRL_CLK_STOP_SUSPEND bit
185 * to stop the logic clock during suspend because
186 * it causes the registers to become inaccessible and
187 * we haven't yet figured out a good wayt to bring the
188 * controller back into life to issue a wakeup.
192 * Set some ISO & split control bits according to Aspeed
195 * VHUB_CTRL_ISO_RSP_CTRL: When set tells the HW to respond
196 * with 0 bytes data packet to ISO IN endpoints when no data
199 * VHUB_CTRL_SPLIT_IN: This makes a SOF complete a split IN
202 ctrl
|= VHUB_CTRL_ISO_RSP_CTRL
| VHUB_CTRL_SPLIT_IN
;
203 writel(ctrl
, vhub
->regs
+ AST_VHUB_CTRL
);
206 /* Set descriptor ring size */
207 if (AST_VHUB_DESCS_COUNT
== 256) {
208 ctrl
|= VHUB_CTRL_LONG_DESC
;
209 writel(ctrl
, vhub
->regs
+ AST_VHUB_CTRL
);
211 BUILD_BUG_ON(AST_VHUB_DESCS_COUNT
!= 32);
214 /* Reset all devices */
215 port_mask
= GENMASK(vhub
->max_ports
, 1);
216 writel(VHUB_SW_RESET_ROOT_HUB
|
217 VHUB_SW_RESET_DMA_CONTROLLER
|
218 VHUB_SW_RESET_EP_POOL
|
219 port_mask
, vhub
->regs
+ AST_VHUB_SW_RESET
);
221 writel(0, vhub
->regs
+ AST_VHUB_SW_RESET
);
223 /* Disable and cleanup EP ACK/NACK interrupts */
224 epn_mask
= GENMASK(vhub
->max_epns
- 1, 0);
225 writel(0, vhub
->regs
+ AST_VHUB_EP_ACK_IER
);
226 writel(0, vhub
->regs
+ AST_VHUB_EP_NACK_IER
);
227 writel(epn_mask
, vhub
->regs
+ AST_VHUB_EP_ACK_ISR
);
228 writel(epn_mask
, vhub
->regs
+ AST_VHUB_EP_NACK_ISR
);
230 /* Default settings for EP0, enable HW hub EP1 */
231 writel(0, vhub
->regs
+ AST_VHUB_EP0_CTRL
);
232 writel(VHUB_EP1_CTRL_RESET_TOGGLE
|
233 VHUB_EP1_CTRL_ENABLE
,
234 vhub
->regs
+ AST_VHUB_EP1_CTRL
);
235 writel(0, vhub
->regs
+ AST_VHUB_EP1_STS_CHG
);
237 /* Configure EP0 DMA buffer */
238 writel(vhub
->ep0
.buf_dma
, vhub
->regs
+ AST_VHUB_EP0_DATA
);
241 writel(0, vhub
->regs
+ AST_VHUB_CONF
);
243 /* Pullup hub (activate on host) */
244 if (vhub
->force_usb1
)
245 ctrl
|= VHUB_CTRL_FULL_SPEED_ONLY
;
247 ctrl
|= VHUB_CTRL_UPSTREAM_CONNECT
;
248 writel(ctrl
, vhub
->regs
+ AST_VHUB_CTRL
);
250 /* Enable some interrupts */
251 writel(VHUB_IRQ_HUB_EP0_IN_ACK_STALL
|
252 VHUB_IRQ_HUB_EP0_OUT_ACK_STALL
|
253 VHUB_IRQ_HUB_EP0_SETUP
|
254 VHUB_IRQ_EP_POOL_ACK_STALL
|
255 VHUB_IRQ_BUS_RESUME
|
256 VHUB_IRQ_BUS_SUSPEND
|
258 vhub
->regs
+ AST_VHUB_IER
);
261 static int ast_vhub_remove(struct platform_device
*pdev
)
263 struct ast_vhub
*vhub
= platform_get_drvdata(pdev
);
267 if (!vhub
|| !vhub
->regs
)
271 for (i
= 0; i
< vhub
->max_ports
; i
++)
272 ast_vhub_del_dev(&vhub
->ports
[i
].dev
);
274 spin_lock_irqsave(&vhub
->lock
, flags
);
276 /* Mask & ack all interrupts */
277 writel(0, vhub
->regs
+ AST_VHUB_IER
);
278 writel(VHUB_IRQ_ACK_ALL
, vhub
->regs
+ AST_VHUB_ISR
);
280 /* Pull device, leave PHY enabled */
281 writel(VHUB_CTRL_PHY_CLK
|
282 VHUB_CTRL_PHY_RESET_DIS
,
283 vhub
->regs
+ AST_VHUB_CTRL
);
286 clk_disable_unprepare(vhub
->clk
);
288 spin_unlock_irqrestore(&vhub
->lock
, flags
);
291 dma_free_coherent(&pdev
->dev
,
292 AST_VHUB_EP0_MAX_PACKET
*
293 (vhub
->max_ports
+ 1),
296 vhub
->ep0_bufs
= NULL
;
301 static int ast_vhub_probe(struct platform_device
*pdev
)
303 enum usb_device_speed max_speed
;
304 struct ast_vhub
*vhub
;
305 struct resource
*res
;
307 const struct device_node
*np
= pdev
->dev
.of_node
;
309 vhub
= devm_kzalloc(&pdev
->dev
, sizeof(*vhub
), GFP_KERNEL
);
313 rc
= of_property_read_u32(np
, "aspeed,vhub-downstream-ports",
316 vhub
->max_ports
= AST_VHUB_NUM_PORTS
;
318 vhub
->ports
= devm_kcalloc(&pdev
->dev
, vhub
->max_ports
,
319 sizeof(*vhub
->ports
), GFP_KERNEL
);
323 rc
= of_property_read_u32(np
, "aspeed,vhub-generic-endpoints",
326 vhub
->max_epns
= AST_VHUB_NUM_GEN_EPs
;
328 vhub
->epns
= devm_kcalloc(&pdev
->dev
, vhub
->max_epns
,
329 sizeof(*vhub
->epns
), GFP_KERNEL
);
333 spin_lock_init(&vhub
->lock
);
335 vhub
->port_irq_mask
= GENMASK(VHUB_IRQ_DEV1_BIT
+ vhub
->max_ports
- 1,
338 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
339 vhub
->regs
= devm_ioremap_resource(&pdev
->dev
, res
);
340 if (IS_ERR(vhub
->regs
)) {
341 dev_err(&pdev
->dev
, "Failed to map resources\n");
342 return PTR_ERR(vhub
->regs
);
344 UDCDBG(vhub
, "vHub@%pR mapped @%p\n", res
, vhub
->regs
);
346 platform_set_drvdata(pdev
, vhub
);
348 vhub
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
349 if (IS_ERR(vhub
->clk
)) {
350 rc
= PTR_ERR(vhub
->clk
);
353 rc
= clk_prepare_enable(vhub
->clk
);
355 dev_err(&pdev
->dev
, "Error couldn't enable clock (%d)\n", rc
);
359 /* Check if we need to limit the HW to USB1 */
360 max_speed
= usb_get_maximum_speed(&pdev
->dev
);
361 if (max_speed
!= USB_SPEED_UNKNOWN
&& max_speed
< USB_SPEED_HIGH
)
362 vhub
->force_usb1
= true;
364 /* Mask & ack all interrupts before installing the handler */
365 writel(0, vhub
->regs
+ AST_VHUB_IER
);
366 writel(VHUB_IRQ_ACK_ALL
, vhub
->regs
+ AST_VHUB_ISR
);
368 /* Find interrupt and install handler */
369 vhub
->irq
= platform_get_irq(pdev
, 0);
374 rc
= devm_request_irq(&pdev
->dev
, vhub
->irq
, ast_vhub_irq
, 0,
375 KBUILD_MODNAME
, vhub
);
377 dev_err(&pdev
->dev
, "Failed to request interrupt\n");
382 * Allocate DMA buffers for all EP0s in one chunk,
383 * one per port and one for the vHub itself
385 vhub
->ep0_bufs
= dma_alloc_coherent(&pdev
->dev
,
386 AST_VHUB_EP0_MAX_PACKET
*
387 (vhub
->max_ports
+ 1),
388 &vhub
->ep0_bufs_dma
, GFP_KERNEL
);
389 if (!vhub
->ep0_bufs
) {
390 dev_err(&pdev
->dev
, "Failed to allocate EP0 DMA buffers\n");
394 UDCVDBG(vhub
, "EP0 DMA buffers @%p (DMA 0x%08x)\n",
395 vhub
->ep0_bufs
, (u32
)vhub
->ep0_bufs_dma
);
398 ast_vhub_init_ep0(vhub
, &vhub
->ep0
, NULL
);
401 for (i
= 0; i
< vhub
->max_ports
&& rc
== 0; i
++)
402 rc
= ast_vhub_init_dev(vhub
, i
);
406 /* Init hub emulation */
407 rc
= ast_vhub_init_hub(vhub
);
412 ast_vhub_init_hw(vhub
);
414 dev_info(&pdev
->dev
, "Initialized virtual hub in USB%d mode\n",
415 vhub
->force_usb1
? 1 : 2);
419 ast_vhub_remove(pdev
);
423 static const struct of_device_id ast_vhub_dt_ids
[] = {
425 .compatible
= "aspeed,ast2400-usb-vhub",
428 .compatible
= "aspeed,ast2500-usb-vhub",
431 .compatible
= "aspeed,ast2600-usb-vhub",
435 MODULE_DEVICE_TABLE(of
, ast_vhub_dt_ids
);
437 static struct platform_driver ast_vhub_driver
= {
438 .probe
= ast_vhub_probe
,
439 .remove
= ast_vhub_remove
,
441 .name
= KBUILD_MODNAME
,
442 .of_match_table
= ast_vhub_dt_ids
,
445 module_platform_driver(ast_vhub_driver
);
447 MODULE_DESCRIPTION("Aspeed vHub udc driver");
448 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
449 MODULE_LICENSE("GPL");