diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2011-12-13 09:37:40 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-12-13 09:37:40 -0800 |
commit | 121a8cdd79e2c68ae78c7633f2a46ee65a177ff6 (patch) | |
tree | 03793bef35f590718ebc6ae6110eb0c507ae60bf /drivers/usb/renesas_usbhs | |
parent | a1016ce33ce23296ad030e5276fcfdf9cb27cb6a (diff) | |
parent | 15a3838b101b292c2e40824d843a4d8871ac4010 (diff) |
Merge branch 'for-next/gadget' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
* 'for-next/gadget' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb: (50 commits)
usb: renesas_usbhs: show error reason on usbhsh_urb_enqueu()
usb: renesas_usbhs: add force packet remove method
usb: renesas_usbhs: care usb_hcd_giveback_urb() status
usb: renesas_usbhs: add usbhsh_is_running()
usb: renesas_usbhs: disable attch irq after device attached
usb: renesas_usbhs: care pipe sequence
usb: renesas_usbhs: add usbhs_pipe_attach() method
usb: renesas_usbhs: add usbhsh_endpoint_detach_all() for error case
usb: renesas_usbhs: modify device attach method
usb: renesas_usbhs: pop packet when urb dequeued
usb: renesas_usbhs: add lost error value when enqueue
usb: gadget: mv_udc: replace some debug info
usb: gadget: mv_udc: refine suspend/resume function
usb: gadget: mv_udc: refine the clock relative code
usb: gadget: mv_udc: disable ISR when stopped
usb: gadget: mv_udc: add otg relative code
usb: gadget: Use kcalloc instead of kzalloc to allocate array
usb: renesas_usbhs: remove the_controller_link
usb: renesas_usbhs: add test-mode support
usb: renesas_usbhs: call usbhsg_queue_pop() when pipe disable.
...
Diffstat (limited to 'drivers/usb/renesas_usbhs')
-rw-r--r-- | drivers/usb/renesas_usbhs/common.c | 39 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/common.h | 9 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/fifo.c | 9 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/fifo.h | 3 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/mod.c | 4 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/mod_gadget.c | 193 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/mod_host.c | 953 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/pipe.c | 28 | ||||
-rw-r--r-- | drivers/usb/renesas_usbhs/pipe.h | 1 |
9 files changed, 814 insertions, 425 deletions
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index aa94e33e688..e9a5b1d2615 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -95,25 +95,15 @@ struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev) /* * syscfg functions */ -void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable) +static void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable) { usbhs_bset(priv, SYSCFG, SCKE, enable ? SCKE : 0); } -void usbhs_sys_hispeed_ctrl(struct usbhs_priv *priv, int enable) -{ - usbhs_bset(priv, SYSCFG, HSE, enable ? HSE : 0); -} - -void usbhs_sys_usb_ctrl(struct usbhs_priv *priv, int enable) -{ - usbhs_bset(priv, SYSCFG, USBE, enable ? USBE : 0); -} - void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable) { - u16 mask = DCFM | DRPD | DPRPU; - u16 val = DCFM | DRPD; + u16 mask = DCFM | DRPD | DPRPU | HSE | USBE; + u16 val = DCFM | DRPD | HSE | USBE; int has_otg = usbhs_get_dparam(priv, has_otg); if (has_otg) @@ -130,8 +120,8 @@ void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable) void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable) { - u16 mask = DCFM | DRPD | DPRPU; - u16 val = DPRPU; + u16 mask = DCFM | DRPD | DPRPU | HSE | USBE; + u16 val = DPRPU | HSE | USBE; /* * if enable @@ -142,6 +132,11 @@ void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable) usbhs_bset(priv, SYSCFG, mask, enable ? val : 0); } +void usbhs_sys_set_test_mode(struct usbhs_priv *priv, u16 mode) +{ + usbhs_write(priv, TESTMODE, mode); +} + /* * frame functions */ @@ -229,7 +224,7 @@ static void usbhsc_bus_init(struct usbhs_priv *priv) /* * device configuration */ -int usbhs_set_device_speed(struct usbhs_priv *priv, int devnum, +int usbhs_set_device_config(struct usbhs_priv *priv, int devnum, u16 upphub, u16 hubport, u16 speed) { struct device *dev = usbhs_priv_to_dev(priv); @@ -301,18 +296,25 @@ static u32 usbhsc_default_pipe_type[] = { */ static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable) { + struct platform_device *pdev = usbhs_priv_to_pdev(priv); struct device *dev = usbhs_priv_to_dev(priv); if (enable) { /* enable PM */ pm_runtime_get_sync(dev); + /* enable platform power */ + usbhs_platform_call(priv, power_ctrl, pdev, priv->base, enable); + /* USB on */ usbhs_sys_clock_ctrl(priv, enable); } else { /* USB off */ usbhs_sys_clock_ctrl(priv, enable); + /* disable platform power */ + usbhs_platform_call(priv, power_ctrl, pdev, priv->base, enable); + /* disable PM */ pm_runtime_put_sync(dev); } @@ -388,7 +390,7 @@ static void usbhsc_notify_hotplug(struct work_struct *work) usbhsc_hotplug(priv); } -int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) +static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) { struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); int delay = usbhs_get_dparam(priv, detection_delay); @@ -398,7 +400,8 @@ int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) * To make sure safety context, * use workqueue for usbhs_notify_hotplug */ - schedule_delayed_work(&priv->notify_hotplug_work, delay); + schedule_delayed_work(&priv->notify_hotplug_work, + msecs_to_jiffies(delay)); return 0; } diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index 8729da5c3be..d79b3e27db9 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -33,6 +33,7 @@ struct usbhs_priv; #define SYSCFG 0x0000 #define BUSWAIT 0x0002 #define DVSTCTR 0x0008 +#define TESTMODE 0x000C #define CFIFO 0x0014 #define CFIFOSEL 0x0020 #define CFIFOCTR 0x0022 @@ -275,19 +276,15 @@ u16 usbhs_read(struct usbhs_priv *priv, u32 reg); void usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data); void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data); -int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev); - #define usbhs_lock(p, f) spin_lock_irqsave(usbhs_priv_to_lock(p), f) #define usbhs_unlock(p, f) spin_unlock_irqrestore(usbhs_priv_to_lock(p), f) /* * sysconfig */ -void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable); -void usbhs_sys_hispeed_ctrl(struct usbhs_priv *priv, int enable); -void usbhs_sys_usb_ctrl(struct usbhs_priv *priv, int enable); void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable); void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable); +void usbhs_sys_set_test_mode(struct usbhs_priv *priv, u16 mode); /* * usb request @@ -311,7 +308,7 @@ int usbhs_frame_get_num(struct usbhs_priv *priv); /* * device config */ -int usbhs_set_device_speed(struct usbhs_priv *priv, int devnum, u16 upphub, +int usbhs_set_device_config(struct usbhs_priv *priv, int devnum, u16 upphub, u16 hubport, u16 speed); /* diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index ffdf5d15085..b51fcd80d24 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -56,7 +56,7 @@ static struct usbhs_pkt_handle usbhsf_null_handler = { void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, void (*done)(struct usbhs_priv *priv, struct usbhs_pkt *pkt), - void *buf, int len, int zero) + void *buf, int len, int zero, int sequence) { struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); struct device *dev = usbhs_priv_to_dev(priv); @@ -90,6 +90,7 @@ void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, pkt->zero = zero; pkt->actual = 0; pkt->done = done; + pkt->sequence = sequence; usbhs_unlock(priv, flags); /******************** spin unlock ******************/ @@ -481,6 +482,9 @@ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done) int i, ret, len; int is_short; + usbhs_pipe_data_sequence(pipe, pkt->sequence); + pkt->sequence = -1; /* -1 sequence will be ignored */ + ret = usbhsf_fifo_select(pipe, fifo, 1); if (ret < 0) return 0; @@ -584,6 +588,8 @@ static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done) /* * pipe enable to prepare packet receive */ + usbhs_pipe_data_sequence(pipe, pkt->sequence); + pkt->sequence = -1; /* -1 sequence will be ignored */ usbhs_pipe_enable(pipe); usbhsf_rx_irq_ctrl(pipe, 1); @@ -641,6 +647,7 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done) * "Operation" - "FIFO Buffer Memory" - "FIFO Port Function" */ if (0 == rcv_len) { + pkt->zero = 1; usbhsf_fifo_clear(pipe, fifo); goto usbhs_fifo_read_end; } diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index 32a7b246b28..f68609c0f48 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -59,6 +59,7 @@ struct usbhs_pkt { int trans; int actual; int zero; + int sequence; }; struct usbhs_pkt_handle { @@ -95,7 +96,7 @@ void usbhs_pkt_init(struct usbhs_pkt *pkt); void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, void (*done)(struct usbhs_priv *priv, struct usbhs_pkt *pkt), - void *buf, int len, int zero); + void *buf, int len, int zero, int sequence); struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt); void usbhs_pkt_start(struct usbhs_pipe *pipe); diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 053f86d7000..f382e431436 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -50,7 +50,9 @@ static int usbhsm_autonomy_irq_vbus(struct usbhs_priv *priv, { struct platform_device *pdev = usbhs_priv_to_pdev(priv); - return usbhsc_drvcllbck_notify_hotplug(pdev); + renesas_usbhs_call_notify_hotplug(pdev); + + return 0; } void usbhs_mod_autonomy_mode(struct usbhs_priv *priv) diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 7f4e8033857..db2a1c6a086 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -14,6 +14,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ +#include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/io.h> #include <linux/module.h> @@ -44,7 +45,6 @@ struct usbhsg_uep { struct usbhsg_gpriv { struct usb_gadget gadget; struct usbhs_mod mod; - struct list_head link; struct usbhsg_uep *uep; int uep_size; @@ -114,16 +114,6 @@ struct usbhsg_recip_handle { #define usbhsg_status_clr(gp, b) (gp->status &= ~b) #define usbhsg_status_has(gp, b) (gp->status & b) -/* controller */ -LIST_HEAD(the_controller_link); - -#define usbhsg_for_each_controller(gpriv)\ - list_for_each_entry(gpriv, &the_controller_link, link) -#define usbhsg_controller_register(gpriv)\ - list_add_tail(&(gpriv)->link, &the_controller_link) -#define usbhsg_controller_unregister(gpriv)\ - list_del_init(&(gpriv)->link) - /* * queue push/pop */ @@ -164,7 +154,7 @@ static void usbhsg_queue_push(struct usbhsg_uep *uep, req->actual = 0; req->status = -EINPROGRESS; usbhs_pkt_push(pipe, pkt, usbhsg_queue_done, - req->buf, req->length, req->zero); + req->buf, req->length, req->zero, -1); usbhs_pkt_start(pipe); dev_dbg(dev, "pipe %d : queue push (%d)\n", @@ -271,6 +261,8 @@ static int usbhsg_recip_handler_std_clear_endpoint(struct usbhs_priv *priv, usbhsg_recip_handler_std_control_done(priv, uep, ctrl); + usbhs_pkt_start(pipe); + return 0; } @@ -282,6 +274,145 @@ struct usbhsg_recip_handle req_clear_feature = { }; /* + * USB_TYPE_STANDARD / set feature functions + */ +static int usbhsg_recip_handler_std_set_device(struct usbhs_priv *priv, + struct usbhsg_uep *uep, + struct usb_ctrlrequest *ctrl) +{ + switch (le16_to_cpu(ctrl->wValue)) { + case USB_DEVICE_TEST_MODE: + usbhsg_recip_handler_std_control_done(priv, uep, ctrl); + udelay(100); + usbhs_sys_set_test_mode(priv, le16_to_cpu(ctrl->wIndex >> 8)); + break; + default: + usbhsg_recip_handler_std_control_done(priv, uep, ctrl); + break; + } + + return 0; +} + +static int usbhsg_recip_handler_std_set_endpoint(struct usbhs_priv *priv, + struct usbhsg_uep *uep, + struct usb_ctrlrequest *ctrl) +{ + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + + usbhs_pipe_stall(pipe); + + usbhsg_recip_handler_std_control_done(priv, uep, ctrl); + + return 0; +} + +struct usbhsg_recip_handle req_set_feature = { + .name = "set feature", + .device = usbhsg_recip_handler_std_set_device, + .interface = usbhsg_recip_handler_std_control_done, + .endpoint = usbhsg_recip_handler_std_set_endpoint, +}; + +/* + * USB_TYPE_STANDARD / get status functions + */ +static void __usbhsg_recip_send_complete(struct usb_ep *ep, + struct usb_request *req) +{ + struct usbhsg_request *ureq = usbhsg_req_to_ureq(req); + + /* free allocated recip-buffer/usb_request */ + kfree(ureq->pkt.buf); + usb_ep_free_request(ep, req); +} + +static void __usbhsg_recip_send_status(struct usbhsg_gpriv *gpriv, + unsigned short status) +{ + struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv); + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(dcp); + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + struct usb_request *req; + unsigned short *buf; + + /* alloc new usb_request for recip */ + req = usb_ep_alloc_request(&dcp->ep, GFP_ATOMIC); + if (!req) { + dev_err(dev, "recip request allocation fail\n"); + return; + } + + /* alloc recip data buffer */ + buf = kmalloc(sizeof(*buf), GFP_ATOMIC); + if (!buf) { + usb_ep_free_request(&dcp->ep, req); + dev_err(dev, "recip data allocation fail\n"); + return; + } + + /* recip data is status */ + *buf = cpu_to_le16(status); + + /* allocated usb_request/buffer will be freed */ + req->complete = __usbhsg_recip_send_complete; + req->buf = buf; + req->length = sizeof(*buf); + req->zero = 0; + + /* push packet */ + pipe->handler = &usbhs_fifo_pio_push_handler; + usbhsg_queue_push(dcp, usbhsg_req_to_ureq(req)); +} + +static int usbhsg_recip_handler_std_get_device(struct usbhs_priv *priv, + struct usbhsg_uep *uep, + struct usb_ctrlrequest *ctrl) +{ + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + unsigned short status = 1 << USB_DEVICE_SELF_POWERED; + + __usbhsg_recip_send_status(gpriv, status); + + return 0; +} + +static int usbhsg_recip_handler_std_get_interface(struct usbhs_priv *priv, + struct usbhsg_uep *uep, + struct usb_ctrlrequest *ctrl) +{ + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + unsigned short status = 0; + + __usbhsg_recip_send_status(gpriv, status); + + return 0; +} + +static int usbhsg_recip_handler_std_get_endpoint(struct usbhs_priv *priv, + struct usbhsg_uep *uep, + struct usb_ctrlrequest *ctrl) +{ + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + unsigned short status = 0; + + if (usbhs_pipe_is_stall(pipe)) + status = 1 << USB_ENDPOINT_HALT; + + __usbhsg_recip_send_status(gpriv, status); + + return 0; +} + +struct usbhsg_recip_handle req_get_status = { + .name = "get status", + .device = usbhsg_recip_handler_std_get_device, + .interface = usbhsg_recip_handler_std_get_interface, + .endpoint = usbhsg_recip_handler_std_get_endpoint, +}; + +/* * USB_TYPE handler */ static int usbhsg_recip_run_handle(struct usbhs_priv *priv, @@ -303,8 +434,7 @@ static int usbhsg_recip_run_handle(struct usbhs_priv *priv, pipe = usbhsg_uep_to_pipe(uep); if (!pipe) { dev_err(dev, "wrong recip request\n"); - ret = -EINVAL; - goto usbhsg_recip_run_handle_end; + return -EINVAL; } switch (recip) { @@ -327,20 +457,10 @@ static int usbhsg_recip_run_handle(struct usbhs_priv *priv, } if (func) { - unsigned long flags; - dev_dbg(dev, "%s (pipe %d :%s)\n", handler->name, nth, msg); - - /******************** spin lock ********************/ - usbhs_lock(priv, flags); ret = func(priv, uep, ctrl); - usbhs_unlock(priv, flags); - /******************** spin unlock ******************/ } -usbhsg_recip_run_handle_end: - usbhs_pkt_start(pipe); - return ret; } @@ -412,6 +532,12 @@ static int usbhsg_irq_ctrl_stage(struct usbhs_priv *priv, case USB_REQ_CLEAR_FEATURE: recip_handler = &req_clear_feature; break; + case USB_REQ_SET_FEATURE: + recip_handler = &req_set_feature; + break; + case USB_REQ_GET_STATUS: + recip_handler = &req_get_status; + break; } } @@ -439,14 +565,16 @@ static int usbhsg_pipe_disable(struct usbhsg_uep *uep) struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); struct usbhs_pkt *pkt; - usbhs_pipe_disable(pipe); - while (1) { pkt = usbhs_pkt_pop(pipe, NULL); if (!pkt) break; + + usbhsg_queue_pop(uep, usbhsg_pkt_to_ureq(pkt), -ECONNRESET); } + usbhs_pipe_disable(pipe); + return 0; } @@ -681,9 +809,7 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) * - function * - usb module */ - usbhs_sys_hispeed_ctrl(priv, 1); usbhs_sys_function_ctrl(priv, 1); - usbhs_sys_usb_ctrl(priv, 1); /* * enable irq callback @@ -731,9 +857,8 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) gpriv->gadget.speed = USB_SPEED_UNKNOWN; /* disable sys */ - usbhs_sys_hispeed_ctrl(priv, 0); + usbhs_sys_set_test_mode(priv, 0); usbhs_sys_function_ctrl(priv, 0); - usbhs_sys_usb_ctrl(priv, 0); usbhsg_pipe_disable(dcp); @@ -755,7 +880,7 @@ static int usbhsg_gadget_start(struct usb_gadget *gadget, if (!driver || !driver->setup || - driver->speed < USB_SPEED_FULL) + driver->max_speed < USB_SPEED_FULL) return -EINVAL; /* first hook up the driver ... */ @@ -866,7 +991,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) gpriv->gadget.dev.parent = dev; gpriv->gadget.name = "renesas_usbhs_udc"; gpriv->gadget.ops = &usbhsg_gadget_ops; - gpriv->gadget.is_dualspeed = 1; + gpriv->gadget.max_speed = USB_SPEED_HIGH; ret = device_register(&gpriv->gadget.dev); if (ret < 0) goto err_add_udc; @@ -896,8 +1021,6 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) } } - usbhsg_controller_register(gpriv); - ret = usb_add_gadget_udc(dev, &gpriv->gadget); if (ret) goto err_register; @@ -926,8 +1049,6 @@ void usbhs_mod_gadget_remove(struct usbhs_priv *priv) device_unregister(&gpriv->gadget.dev); - usbhsg_controller_unregister(gpriv); - kfree(gpriv->uep); kfree(gpriv); } diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c index 75659e0c735..aa50eaaffcb 100644 --- a/drivers/usb/renesas_usbhs/mod_host.c +++ b/drivers/usb/renesas_usbhs/mod_host.c @@ -45,36 +45,34 @@ * * +--------+ pipes are reused for each uep. * | udev 1 |-+- [uep 0 (dcp) ] --+ pipe will be switched when - * +--------+ | | target device was changed + * +--------+ | | other device requested * +- [uep 1 (bulk)] --|---+ +--------------+ * | +--------------> | pipe0 (dcp) | - * +- [uep 2 (bulk)] --|---|---+ +--------------+ - * | | | | pipe1 (isoc) | - * +--------+ | | | +--------------+ - * | udev 2 |-+- [uep 0 (dcp) ] --+ +-- |------> | pipe2 (bulk) | - * +--------+ | | | | +--------------+ - * +- [uep 1 (int) ] --|-+ | +------> | pipe3 (bulk) | - * | | | | +--------------+ - * +--------+ | +-|---|------> | pipe4 (int) | - * | udev 3 |-+- [uep 0 (dcp) ] --+ | | +--------------+ - * +--------+ | | | | .... | - * +- [uep 1 (bulk)] ------+ | | .... | + * +- [uep 2 (bulk)] -@ | +--------------+ + * | | pipe1 (isoc) | + * +--------+ | +--------------+ + * | udev 2 |-+- [uep 0 (dcp) ] -@ +----------> | pipe2 (bulk) | + * +--------+ | +--------------+ + * +- [uep 1 (int) ] ----+ +------> | pipe3 (bulk) | + * | | +--------------+ + * +--------+ +-----|------> | pipe4 (int) | + * | udev 3 |-+- [uep 0 (dcp) ] -@ | +--------------+ + * +--------+ | | | .... | + * +- [uep 1 (bulk)] -@ | | .... | * | | * +- [uep 2 (bulk)]-----------+ + * + * @ : uep requested free pipe, but all have been used. + * now it is waiting for free pipe */ /* * struct */ -struct usbhsh_pipe_info { - unsigned int usr_cnt; /* see usbhsh_endpoint_alloc() */ -}; - struct usbhsh_request { struct urb *urb; struct usbhs_pkt pkt; - struct list_head ureq_link; /* see hpriv :: ureq_link_xxx */ }; struct usbhsh_device { @@ -83,11 +81,10 @@ struct usbhsh_device { }; struct usbhsh_ep { - struct usbhs_pipe *pipe; + struct usbhs_pipe *pipe; /* attached pipe */ struct usbhsh_device *udev; /* attached udev */ + struct usb_host_endpoint *ep; struct list_head ep_list; /* list to usbhsh_device */ - - int maxp; }; #define USBHSH_DEVICE_MAX 10 /* see DEVADDn / DCPMAXP / PIPEMAXP */ @@ -98,16 +95,9 @@ struct usbhsh_hpriv { struct usbhsh_device udev[USBHSH_DEVICE_MAX]; - struct usbhsh_pipe_info *pipe_info; - int pipe_size; - u32 port_stat; /* USB_PORT_STAT_xxx */ struct completion setup_ack_done; - - /* see usbhsh_req_alloc/free */ - struct list_head ureq_link_active; - struct list_head ureq_link_free; }; @@ -119,17 +109,6 @@ static const char usbhsh_hcd_name[] = "renesas_usbhs host"; #define usbhsh_priv_to_hpriv(priv) \ container_of(usbhs_mod_get(priv, USBHS_HOST), struct usbhsh_hpriv, mod) -#define __usbhsh_for_each_hpipe(start, pos, h, i) \ - for (i = start, pos = (h)->hpipe + i; \ - i < (h)->hpipe_size; \ - i++, pos = (h)->hpipe + i) - -#define usbhsh_for_each_hpipe(pos, hpriv, i) \ - __usbhsh_for_each_hpipe(1, pos, hpriv, i) - -#define usbhsh_for_each_hpipe_with_dcp(pos, hpriv, i) \ - __usbhsh_for_each_hpipe(0, pos, hpriv, i) - #define __usbhsh_for_each_udev(start, pos, h, i) \ for (i = start, pos = (h)->udev + i; \ i < USBHSH_DEVICE_MAX; \ @@ -152,15 +131,20 @@ static const char usbhsh_hcd_name[] = "renesas_usbhs host"; #define usbhsh_ep_to_uep(u) ((u)->hcpriv) #define usbhsh_uep_to_pipe(u) ((u)->pipe) #define usbhsh_uep_to_udev(u) ((u)->udev) +#define usbhsh_uep_to_ep(u) ((u)->ep) + #define usbhsh_urb_to_ureq(u) ((u)->hcpriv) #define usbhsh_urb_to_usbv(u) ((u)->dev) #define usbhsh_usbv_to_udev(d) dev_get_drvdata(&(d)->dev) #define usbhsh_udev_to_usbv(h) ((h)->usbv) +#define usbhsh_udev_is_used(h) usbhsh_udev_to_usbv(h) -#define usbhsh_pipe_info(p) ((p)->mod_private) +#define usbhsh_pipe_to_uep(p) ((p)->mod_private) +#define usbhsh_device_parent(d) (usbhsh_usbv_to_udev((d)->usbv->parent)) +#define usbhsh_device_hubport(d) ((d)->usbv->portnum) #define usbhsh_device_number(h, d) ((int)((d) - (h)->udev)) #define usbhsh_device_nth(h, d) ((h)->udev + d) #define usbhsh_device0(h) usbhsh_device_nth(h, 0) @@ -170,38 +154,13 @@ static const char usbhsh_hcd_name[] = "renesas_usbhs host"; #define usbhsh_port_stat_clear(h, s) ((h)->port_stat &= ~(s)) #define usbhsh_port_stat_get(h) ((h)->port_stat) -#define usbhsh_pkt_to_req(p) \ +#define usbhsh_pkt_to_ureq(p) \ container_of((void *)p, struct usbhsh_request, pkt) /* * req alloc/free */ -static void usbhsh_req_list_init(struct usbhsh_hpriv *hpriv) -{ - INIT_LIST_HEAD(&hpriv->ureq_link_active); - INIT_LIST_HEAD(&hpriv->ureq_link_free); -} - -static void usbhsh_req_list_quit(struct usbhsh_hpriv *hpriv) -{ - struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); - struct device *dev = usbhsh_hcd_to_dev(hcd); - struct usbhsh_request *ureq, *next; - - /* kfree all active ureq */ - list_for_each_entry_safe(ureq, next, - &hpriv->ureq_link_active, - ureq_link) { - dev_err(dev, "active ureq (%p) is force freed\n", ureq); - kfree(ureq); - } - - /* kfree all free ureq */ - list_for_each_entry_safe(ureq, next, &hpriv->ureq_link_free, ureq_link) - kfree(ureq); -} - -static struct usbhsh_request *usbhsh_req_alloc(struct usbhsh_hpriv *hpriv, +static struct usbhsh_request *usbhsh_ureq_alloc(struct usbhsh_hpriv *hpriv, struct urb *urb, gfp_t mem_flags) { @@ -209,270 +168,460 @@ static struct usbhsh_request *usbhsh_req_alloc(struct usbhsh_hpriv *hpriv, struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); struct device *dev = usbhs_priv_to_dev(priv); - if (list_empty(&hpriv->ureq_link_free)) { - /* - * create new one if there is no free ureq - */ - ureq = kzalloc(sizeof(struct usbhsh_request), mem_flags); - if (ureq) - INIT_LIST_HEAD(&ureq->ureq_link); - } else { - /* - * reuse "free" ureq if exist - */ - ureq = list_entry(hpriv->ureq_link_free.next, - struct usbhsh_request, - ureq_link); - if (ureq) - list_del_init(&ureq->ureq_link); - } - + ureq = kzalloc(sizeof(struct usbhsh_request), mem_flags); if (!ureq) { dev_err(dev, "ureq alloc fail\n"); return NULL; } usbhs_pkt_init(&ureq->pkt); - - /* - * push it to "active" list - */ - list_add_tail(&ureq->ureq_link, &hpriv->ureq_link_active); ureq->urb = urb; + usbhsh_urb_to_ureq(urb) = ureq; return ureq; } -static void usbhsh_req_free(struct usbhsh_hpriv *hpriv, +static void usbhsh_ureq_free(struct usbhsh_hpriv *hpriv, struct usbhsh_request *ureq) { - struct usbhs_pkt *pkt = &ureq->pkt; + usbhsh_urb_to_ureq(ureq->urb) = NULL; + ureq->urb = NULL; - usbhs_pkt_init(pkt); + kfree(ureq); +} +/* + * status + */ +static int usbhsh_is_running(struct usbhsh_hpriv *hpriv) +{ /* - * removed from "active" list, - * and push it to "free" list + * we can decide some device is attached or not + * by checking mod.irq_attch + * see + * usbhsh_irq_attch() + * usbhsh_irq_dtch() */ - ureq->urb = NULL; - list_del_init(&ureq->ureq_link); - list_add_tail(&ureq->ureq_link, &hpriv->ureq_link_free); + return (hpriv->mod.irq_attch == NULL); } /* - * device control + * pipe control */ - -static int usbhsh_device_has_endpoint(struct usbhsh_device *udev) +static void usbhsh_endpoint_sequence_save(struct usbhsh_hpriv *hpriv, + struct urb *urb, + struct usbhs_pkt *pkt) { - return !list_empty(&udev->ep_list_head); -} + int len = urb->actual_length; + int maxp = usb_endpoint_maxp(&urb->ep->desc); + int t = 0; -static struct usbhsh_device *usbhsh_device_alloc(struct usbhsh_hpriv *hpriv, - struct urb *urb) -{ - struct usbhsh_device *udev = NULL; - struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); - struct device *dev = usbhsh_hcd_to_dev(hcd); - struct usb_device *usbv = usbhsh_urb_to_usbv(urb); - struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); - int i; + /* DCP is out of sequence control */ + if (usb_pipecontrol(urb->pipe)) + return; /* - * device 0 + * renesas_usbhs pipe has a limitation in a number. + * So, driver should re-use the limited pipe for each device/endpoint. + * DATA0/1 sequence should be saved for it. + * see [image of mod_host] + * [HARDWARE LIMITATION] */ - if (0 == usb_pipedevice(urb->pipe)) { - udev = usbhsh_device0(hpriv); - goto usbhsh_device_find; - } /* - * find unused device + * next sequence depends on actual_length + * + * ex) actual_length = 1147, maxp = 512 + * data0 : 512 + * data1 : 512 + * data0 : 123 + * data1 is the next sequence */ - usbhsh_for_each_udev(udev, hpriv, i) { - if (usbhsh_udev_to_usbv(udev)) - continue; - goto usbhsh_device_find; + t = len / maxp; + if (len % maxp) + t++; + if (pkt->zero) + t++; + t %= 2; + + if (t) + usb_dotoggle(urb->dev, + usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); +} + +static struct usbhsh_device *usbhsh_device_get(struct usbhsh_hpriv *hpriv, + struct urb *urb); + +static int usbhsh_pipe_attach(struct usbhsh_hpriv *hpriv, + struct urb *urb) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct usbhsh_ep *uep = usbhsh_ep_to_uep(urb->ep); + struct usbhsh_device *udev = usbhsh_device_get(hpriv, urb); + struct usbhs_pipe *pipe; + struct usb_endpoint_descriptor *desc = &urb->ep->desc; + struct device *dev = usbhs_priv_to_dev(priv); + unsigned long flags; + int dir_in_req = !!usb_pipein(urb->pipe); + int is_dcp = usb_endpoint_xfer_control(desc); + int i, dir_in; + int ret = -EBUSY; + + /******************** spin lock ********************/ + usbhs_lock(priv, flags); + + if (unlikely(usbhsh_uep_to_pipe(uep))) { + dev_err(dev, "uep already has pipe\n"); + goto usbhsh_pipe_attach_done; } - dev_err(dev, "no free usbhsh_device\n"); + usbhs_for_each_pipe_with_dcp(pipe, priv, i) { - return NULL; + /* check pipe type */ + if (!usbhs_pipe_type_is(pipe, usb_endpoint_type(desc))) + continue; -usbhsh_device_find: - if (usbhsh_device_has_endpoint(udev)) - dev_warn(dev, "udev have old endpoint\n"); + /* check pipe direction if normal pipe */ + if (!is_dcp) { + dir_in = !!usbhs_pipe_is_dir_in(pipe); + if (0 != (dir_in - dir_in_req)) + continue; + } - /* uep will be attached */ - INIT_LIST_HEAD(&udev->ep_list_head); + /* check pipe is free */ + if (usbhsh_pipe_to_uep(pipe)) + continue; - /* - * usbhsh_usbv_to_udev() - * usbhsh_udev_to_usbv() - * will be enable - */ - dev_set_drvdata(&usbv->dev, udev); - udev->usbv = usbv; + /* + * attach pipe to uep + * + * usbhs_pipe_config_update() should be called after + * usbhs_set_device_config() + * see + * DCPMAXP/PIPEMAXP + */ + usbhsh_uep_to_pipe(uep) = pipe; + usbhsh_pipe_to_uep(pipe) = uep; - /* set device config */ - usbhs_set_device_speed(priv, - usbhsh_device_number(hpriv, udev), - usbhsh_device_number(hpriv, udev), - 0, /* FIXME no parent */ - usbv->speed); + usbhs_pipe_config_update(pipe, + usbhsh_device_number(hpriv, udev), + usb_endpoint_num(desc), + usb_endpoint_maxp(desc)); - dev_dbg(dev, "%s [%d](%p)\n", __func__, - usbhsh_device_number(hpriv, udev), udev); + dev_dbg(dev, "%s [%d-%d(%s:%s)]\n", __func__, + usbhsh_device_number(hpriv, udev), + usb_endpoint_num(desc), + usbhs_pipe_name(pipe), + dir_in_req ? "in" : "out"); - return udev; + ret = 0; + break; + } + +usbhsh_pipe_attach_done: + usbhs_unlock(priv, flags); + /******************** spin unlock ******************/ + + return ret; } -static void usbhsh_device_free(struct usbhsh_hpriv *hpriv, - struct usbhsh_device *udev) +static void usbhsh_pipe_detach(struct usbhsh_hpriv *hpriv, + struct usbhsh_ep *uep) { - struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); - struct device *dev = usbhsh_hcd_to_dev(hcd); - struct usb_device *usbv = usbhsh_udev_to_usbv(udev); + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct usbhs_pipe *pipe; + struct device *dev = usbhs_priv_to_dev(priv); + unsigned long flags; - dev_dbg(dev, "%s [%d](%p)\n", __func__, - usbhsh_device_number(hpriv, udev), udev); + /******************** spin lock ********************/ + usbhs_lock(priv, flags); - if (usbhsh_device_has_endpoint(udev)) - dev_warn(dev, "udev still have endpoint\n"); + pipe = usbhsh_uep_to_pipe(uep); - /* - * usbhsh_usbv_to_udev() - * usbhsh_udev_to_usbv() - * will be disable - */ - dev_set_drvdata(&usbv->dev, NULL); - udev->usbv = NULL; + if (unlikely(!pipe)) { + dev_err(dev, "uep doens't have pipe\n"); + } else { + struct usb_host_endpoint *ep = usbhsh_uep_to_ep(uep); + struct usbhsh_device *udev = usbhsh_uep_to_udev(uep); + + /* detach pipe from uep */ + usbhsh_uep_to_pipe(uep) = NULL; + usbhsh_pipe_to_uep(pipe) = NULL; + + dev_dbg(dev, "%s [%d-%d(%s)]\n", __func__, + usbhsh_device_number(hpriv, udev), + usb_endpoint_num(&ep->desc), + |