162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Renesas USB driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 Renesas Solutions Corp. 662306a36Sopenharmony_ci * Copyright (C) 2019 Renesas Electronics Corporation 762306a36Sopenharmony_ci * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/usb/ch9.h> 1562306a36Sopenharmony_ci#include <linux/usb/gadget.h> 1662306a36Sopenharmony_ci#include <linux/usb/otg.h> 1762306a36Sopenharmony_ci#include "common.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * struct 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_cistruct usbhsg_request { 2362306a36Sopenharmony_ci struct usb_request req; 2462306a36Sopenharmony_ci struct usbhs_pkt pkt; 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define EP_NAME_SIZE 8 2862306a36Sopenharmony_cistruct usbhsg_gpriv; 2962306a36Sopenharmony_cistruct usbhsg_uep { 3062306a36Sopenharmony_ci struct usb_ep ep; 3162306a36Sopenharmony_ci struct usbhs_pipe *pipe; 3262306a36Sopenharmony_ci spinlock_t lock; /* protect the pipe */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci char ep_name[EP_NAME_SIZE]; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct usbhsg_gpriv { 4062306a36Sopenharmony_ci struct usb_gadget gadget; 4162306a36Sopenharmony_ci struct usbhs_mod mod; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci struct usbhsg_uep *uep; 4462306a36Sopenharmony_ci int uep_size; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci struct usb_gadget_driver *driver; 4762306a36Sopenharmony_ci struct usb_phy *transceiver; 4862306a36Sopenharmony_ci bool vbus_active; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci u32 status; 5162306a36Sopenharmony_ci#define USBHSG_STATUS_STARTED (1 << 0) 5262306a36Sopenharmony_ci#define USBHSG_STATUS_REGISTERD (1 << 1) 5362306a36Sopenharmony_ci#define USBHSG_STATUS_WEDGE (1 << 2) 5462306a36Sopenharmony_ci#define USBHSG_STATUS_SELF_POWERED (1 << 3) 5562306a36Sopenharmony_ci#define USBHSG_STATUS_SOFT_CONNECT (1 << 4) 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistruct usbhsg_recip_handle { 5962306a36Sopenharmony_ci char *name; 6062306a36Sopenharmony_ci int (*device)(struct usbhs_priv *priv, struct usbhsg_uep *uep, 6162306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl); 6262306a36Sopenharmony_ci int (*interface)(struct usbhs_priv *priv, struct usbhsg_uep *uep, 6362306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl); 6462306a36Sopenharmony_ci int (*endpoint)(struct usbhs_priv *priv, struct usbhsg_uep *uep, 6562306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl); 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* 6962306a36Sopenharmony_ci * macro 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci#define usbhsg_priv_to_gpriv(priv) \ 7262306a36Sopenharmony_ci container_of( \ 7362306a36Sopenharmony_ci usbhs_mod_get(priv, USBHS_GADGET), \ 7462306a36Sopenharmony_ci struct usbhsg_gpriv, mod) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define __usbhsg_for_each_uep(start, pos, g, i) \ 7762306a36Sopenharmony_ci for ((i) = start; \ 7862306a36Sopenharmony_ci ((i) < (g)->uep_size) && ((pos) = (g)->uep + (i)); \ 7962306a36Sopenharmony_ci (i)++) 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define usbhsg_for_each_uep(pos, gpriv, i) \ 8262306a36Sopenharmony_ci __usbhsg_for_each_uep(1, pos, gpriv, i) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define usbhsg_for_each_uep_with_dcp(pos, gpriv, i) \ 8562306a36Sopenharmony_ci __usbhsg_for_each_uep(0, pos, gpriv, i) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define usbhsg_gadget_to_gpriv(g)\ 8862306a36Sopenharmony_ci container_of(g, struct usbhsg_gpriv, gadget) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define usbhsg_req_to_ureq(r)\ 9162306a36Sopenharmony_ci container_of(r, struct usbhsg_request, req) 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define usbhsg_ep_to_uep(e) container_of(e, struct usbhsg_uep, ep) 9462306a36Sopenharmony_ci#define usbhsg_gpriv_to_dev(gp) usbhs_priv_to_dev((gp)->mod.priv) 9562306a36Sopenharmony_ci#define usbhsg_gpriv_to_priv(gp) ((gp)->mod.priv) 9662306a36Sopenharmony_ci#define usbhsg_gpriv_to_dcp(gp) ((gp)->uep) 9762306a36Sopenharmony_ci#define usbhsg_gpriv_to_nth_uep(gp, i) ((gp)->uep + i) 9862306a36Sopenharmony_ci#define usbhsg_uep_to_gpriv(u) ((u)->gpriv) 9962306a36Sopenharmony_ci#define usbhsg_uep_to_pipe(u) ((u)->pipe) 10062306a36Sopenharmony_ci#define usbhsg_pipe_to_uep(p) ((p)->mod_private) 10162306a36Sopenharmony_ci#define usbhsg_is_dcp(u) ((u) == usbhsg_gpriv_to_dcp((u)->gpriv)) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define usbhsg_ureq_to_pkt(u) (&(u)->pkt) 10462306a36Sopenharmony_ci#define usbhsg_pkt_to_ureq(i) \ 10562306a36Sopenharmony_ci container_of(i, struct usbhsg_request, pkt) 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci#define usbhsg_is_not_connected(gp) ((gp)->gadget.speed == USB_SPEED_UNKNOWN) 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* status */ 11062306a36Sopenharmony_ci#define usbhsg_status_init(gp) do {(gp)->status = 0; } while (0) 11162306a36Sopenharmony_ci#define usbhsg_status_set(gp, b) (gp->status |= b) 11262306a36Sopenharmony_ci#define usbhsg_status_clr(gp, b) (gp->status &= ~b) 11362306a36Sopenharmony_ci#define usbhsg_status_has(gp, b) (gp->status & b) 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* 11662306a36Sopenharmony_ci * queue push/pop 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_cistatic void __usbhsg_queue_pop(struct usbhsg_uep *uep, 11962306a36Sopenharmony_ci struct usbhsg_request *ureq, 12062306a36Sopenharmony_ci int status) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); 12362306a36Sopenharmony_ci struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); 12462306a36Sopenharmony_ci struct device *dev = usbhsg_gpriv_to_dev(gpriv); 12562306a36Sopenharmony_ci struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (pipe) 12862306a36Sopenharmony_ci dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe)); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci ureq->req.status = status; 13162306a36Sopenharmony_ci spin_unlock(usbhs_priv_to_lock(priv)); 13262306a36Sopenharmony_ci usb_gadget_giveback_request(&uep->ep, &ureq->req); 13362306a36Sopenharmony_ci spin_lock(usbhs_priv_to_lock(priv)); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void usbhsg_queue_pop(struct usbhsg_uep *uep, 13762306a36Sopenharmony_ci struct usbhsg_request *ureq, 13862306a36Sopenharmony_ci int status) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); 14162306a36Sopenharmony_ci struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); 14262306a36Sopenharmony_ci unsigned long flags; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci usbhs_lock(priv, flags); 14562306a36Sopenharmony_ci __usbhsg_queue_pop(uep, ureq, status); 14662306a36Sopenharmony_ci usbhs_unlock(priv, flags); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct usbhs_pipe *pipe = pkt->pipe; 15262306a36Sopenharmony_ci struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe); 15362306a36Sopenharmony_ci struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt); 15462306a36Sopenharmony_ci unsigned long flags; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ureq->req.actual = pkt->actual; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci usbhs_lock(priv, flags); 15962306a36Sopenharmony_ci if (uep) 16062306a36Sopenharmony_ci __usbhsg_queue_pop(uep, ureq, 0); 16162306a36Sopenharmony_ci usbhs_unlock(priv, flags); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void usbhsg_queue_push(struct usbhsg_uep *uep, 16562306a36Sopenharmony_ci struct usbhsg_request *ureq) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); 16862306a36Sopenharmony_ci struct device *dev = usbhsg_gpriv_to_dev(gpriv); 16962306a36Sopenharmony_ci struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); 17062306a36Sopenharmony_ci struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq); 17162306a36Sopenharmony_ci struct usb_request *req = &ureq->req; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci req->actual = 0; 17462306a36Sopenharmony_ci req->status = -EINPROGRESS; 17562306a36Sopenharmony_ci usbhs_pkt_push(pipe, pkt, usbhsg_queue_done, 17662306a36Sopenharmony_ci req->buf, req->length, req->zero, -1); 17762306a36Sopenharmony_ci usbhs_pkt_start(pipe); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci dev_dbg(dev, "pipe %d : queue push (%d)\n", 18062306a36Sopenharmony_ci usbhs_pipe_number(pipe), 18162306a36Sopenharmony_ci req->length); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/* 18562306a36Sopenharmony_ci * dma map/unmap 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_cistatic int usbhsg_dma_map_ctrl(struct device *dma_dev, struct usbhs_pkt *pkt, 18862306a36Sopenharmony_ci int map) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt); 19162306a36Sopenharmony_ci struct usb_request *req = &ureq->req; 19262306a36Sopenharmony_ci struct usbhs_pipe *pipe = pkt->pipe; 19362306a36Sopenharmony_ci enum dma_data_direction dir; 19462306a36Sopenharmony_ci int ret = 0; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci dir = usbhs_pipe_is_dir_host(pipe); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (map) { 19962306a36Sopenharmony_ci /* it can not use scatter/gather */ 20062306a36Sopenharmony_ci WARN_ON(req->num_sgs); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci ret = usb_gadget_map_request_by_dev(dma_dev, req, dir); 20362306a36Sopenharmony_ci if (ret < 0) 20462306a36Sopenharmony_ci return ret; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci pkt->dma = req->dma; 20762306a36Sopenharmony_ci } else { 20862306a36Sopenharmony_ci usb_gadget_unmap_request_by_dev(dma_dev, req, dir); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return ret; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/* 21562306a36Sopenharmony_ci * USB_TYPE_STANDARD / clear feature functions 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_cistatic int usbhsg_recip_handler_std_control_done(struct usbhs_priv *priv, 21862306a36Sopenharmony_ci struct usbhsg_uep *uep, 21962306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); 22262306a36Sopenharmony_ci struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv); 22362306a36Sopenharmony_ci struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(dcp); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci usbhs_dcp_control_transfer_done(pipe); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return 0; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic int usbhsg_recip_handler_std_clear_endpoint(struct usbhs_priv *priv, 23162306a36Sopenharmony_ci struct usbhsg_uep *uep, 23262306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); 23562306a36Sopenharmony_ci struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (!usbhsg_status_has(gpriv, USBHSG_STATUS_WEDGE)) { 23862306a36Sopenharmony_ci usbhs_pipe_disable(pipe); 23962306a36Sopenharmony_ci usbhs_pipe_sequence_data0(pipe); 24062306a36Sopenharmony_ci usbhs_pipe_enable(pipe); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci usbhsg_recip_handler_std_control_done(priv, uep, ctrl); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci usbhs_pkt_start(pipe); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic struct usbhsg_recip_handle req_clear_feature = { 25162306a36Sopenharmony_ci .name = "clear feature", 25262306a36Sopenharmony_ci .device = usbhsg_recip_handler_std_control_done, 25362306a36Sopenharmony_ci .interface = usbhsg_recip_handler_std_control_done, 25462306a36Sopenharmony_ci .endpoint = usbhsg_recip_handler_std_clear_endpoint, 25562306a36Sopenharmony_ci}; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/* 25862306a36Sopenharmony_ci * USB_TYPE_STANDARD / set feature functions 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_cistatic int usbhsg_recip_handler_std_set_device(struct usbhs_priv *priv, 26162306a36Sopenharmony_ci struct usbhsg_uep *uep, 26262306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci switch (le16_to_cpu(ctrl->wValue)) { 26562306a36Sopenharmony_ci case USB_DEVICE_TEST_MODE: 26662306a36Sopenharmony_ci usbhsg_recip_handler_std_control_done(priv, uep, ctrl); 26762306a36Sopenharmony_ci udelay(100); 26862306a36Sopenharmony_ci usbhs_sys_set_test_mode(priv, le16_to_cpu(ctrl->wIndex) >> 8); 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci default: 27162306a36Sopenharmony_ci usbhsg_recip_handler_std_control_done(priv, uep, ctrl); 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return 0; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic int usbhsg_recip_handler_std_set_endpoint(struct usbhs_priv *priv, 27962306a36Sopenharmony_ci struct usbhsg_uep *uep, 28062306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci usbhs_pipe_stall(pipe); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci usbhsg_recip_handler_std_control_done(priv, uep, ctrl); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return 0; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic struct usbhsg_recip_handle req_set_feature = { 29262306a36Sopenharmony_ci .name = "set feature", 29362306a36Sopenharmony_ci .device = usbhsg_recip_handler_std_set_device, 29462306a36Sopenharmony_ci .interface = usbhsg_recip_handler_std_control_done, 29562306a36Sopenharmony_ci .endpoint = usbhsg_recip_handler_std_set_endpoint, 29662306a36Sopenharmony_ci}; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci/* 29962306a36Sopenharmony_ci * USB_TYPE_STANDARD / get status functions 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_cistatic void __usbhsg_recip_send_complete(struct usb_ep *ep, 30262306a36Sopenharmony_ci struct usb_request *req) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct usbhsg_request *ureq = usbhsg_req_to_ureq(req); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* free allocated recip-buffer/usb_request */ 30762306a36Sopenharmony_ci kfree(ureq->pkt.buf); 30862306a36Sopenharmony_ci usb_ep_free_request(ep, req); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic void __usbhsg_recip_send_status(struct usbhsg_gpriv *gpriv, 31262306a36Sopenharmony_ci unsigned short status) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv); 31562306a36Sopenharmony_ci struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(dcp); 31662306a36Sopenharmony_ci struct device *dev = usbhsg_gpriv_to_dev(gpriv); 31762306a36Sopenharmony_ci struct usb_request *req; 31862306a36Sopenharmony_ci __le16 *buf; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* alloc new usb_request for recip */ 32162306a36Sopenharmony_ci req = usb_ep_alloc_request(&dcp->ep, GFP_ATOMIC); 32262306a36Sopenharmony_ci if (!req) { 32362306a36Sopenharmony_ci dev_err(dev, "recip request allocation fail\n"); 32462306a36Sopenharmony_ci return; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* alloc recip data buffer */ 32862306a36Sopenharmony_ci buf = kmalloc(sizeof(*buf), GFP_ATOMIC); 32962306a36Sopenharmony_ci if (!buf) { 33062306a36Sopenharmony_ci usb_ep_free_request(&dcp->ep, req); 33162306a36Sopenharmony_ci return; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* recip data is status */ 33562306a36Sopenharmony_ci *buf = cpu_to_le16(status); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* allocated usb_request/buffer will be freed */ 33862306a36Sopenharmony_ci req->complete = __usbhsg_recip_send_complete; 33962306a36Sopenharmony_ci req->buf = buf; 34062306a36Sopenharmony_ci req->length = sizeof(*buf); 34162306a36Sopenharmony_ci req->zero = 0; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* push packet */ 34462306a36Sopenharmony_ci pipe->handler = &usbhs_fifo_pio_push_handler; 34562306a36Sopenharmony_ci usbhsg_queue_push(dcp, usbhsg_req_to_ureq(req)); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic int usbhsg_recip_handler_std_get_device(struct usbhs_priv *priv, 34962306a36Sopenharmony_ci struct usbhsg_uep *uep, 35062306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); 35362306a36Sopenharmony_ci unsigned short status = 0; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (usbhsg_status_has(gpriv, USBHSG_STATUS_SELF_POWERED)) 35662306a36Sopenharmony_ci status = 1 << USB_DEVICE_SELF_POWERED; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci __usbhsg_recip_send_status(gpriv, status); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic int usbhsg_recip_handler_std_get_interface(struct usbhs_priv *priv, 36462306a36Sopenharmony_ci struct usbhsg_uep *uep, 36562306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); 36862306a36Sopenharmony_ci unsigned short status = 0; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci __usbhsg_recip_send_status(gpriv, status); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic int usbhsg_recip_handler_std_get_endpoint(struct usbhs_priv *priv, 37662306a36Sopenharmony_ci struct usbhsg_uep *uep, 37762306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); 38062306a36Sopenharmony_ci struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); 38162306a36Sopenharmony_ci unsigned short status = 0; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (usbhs_pipe_is_stall(pipe)) 38462306a36Sopenharmony_ci status = 1 << USB_ENDPOINT_HALT; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci __usbhsg_recip_send_status(gpriv, status); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return 0; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic struct usbhsg_recip_handle req_get_status = { 39262306a36Sopenharmony_ci .name = "get status", 39362306a36Sopenharmony_ci .device = usbhsg_recip_handler_std_get_device, 39462306a36Sopenharmony_ci .interface = usbhsg_recip_handler_std_get_interface, 39562306a36Sopenharmony_ci .endpoint = usbhsg_recip_handler_std_get_endpoint, 39662306a36Sopenharmony_ci}; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci/* 39962306a36Sopenharmony_ci * USB_TYPE handler 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_cistatic int usbhsg_recip_run_handle(struct usbhs_priv *priv, 40262306a36Sopenharmony_ci struct usbhsg_recip_handle *handler, 40362306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); 40662306a36Sopenharmony_ci struct device *dev = usbhsg_gpriv_to_dev(gpriv); 40762306a36Sopenharmony_ci struct usbhsg_uep *uep; 40862306a36Sopenharmony_ci struct usbhs_pipe *pipe; 40962306a36Sopenharmony_ci int recip = ctrl->bRequestType & USB_RECIP_MASK; 41062306a36Sopenharmony_ci int nth = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; 41162306a36Sopenharmony_ci int ret = 0; 41262306a36Sopenharmony_ci int (*func)(struct usbhs_priv *priv, struct usbhsg_uep *uep, 41362306a36Sopenharmony_ci struct usb_ctrlrequest *ctrl); 41462306a36Sopenharmony_ci char *msg; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci uep = usbhsg_gpriv_to_nth_uep(gpriv, nth); 41762306a36Sopenharmony_ci pipe = usbhsg_uep_to_pipe(uep); 41862306a36Sopenharmony_ci if (!pipe) { 41962306a36Sopenharmony_ci dev_err(dev, "wrong recip request\n"); 42062306a36Sopenharmony_ci return -EINVAL; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci switch (recip) { 42462306a36Sopenharmony_ci case USB_RECIP_DEVICE: 42562306a36Sopenharmony_ci msg = "DEVICE"; 42662306a36Sopenharmony_ci func = handler->device; 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci case USB_RECIP_INTERFACE: 42962306a36Sopenharmony_ci msg = "INTERFACE"; 43062306a36Sopenharmony_ci func = handler->interface; 43162306a36Sopenharmony_ci break; 43262306a36Sopenharmony_ci case USB_RECIP_ENDPOINT: 43362306a36Sopenharmony_ci msg = "ENDPOINT"; 43462306a36Sopenharmony_ci func = handler->endpoint; 43562306a36Sopenharmony_ci break; 43662306a36Sopenharmony_ci default: 43762306a36Sopenharmony_ci dev_warn(dev, "unsupported RECIP(%d)\n", recip); 43862306a36Sopenharmony_ci func = NULL; 43962306a36Sopenharmony_ci ret = -EINVAL; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (func) { 44362306a36Sopenharmony_ci dev_dbg(dev, "%s (pipe %d :%s)\n", handler->name, nth, msg); 44462306a36Sopenharmony_ci ret = func(priv, uep, ctrl); 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return ret; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci/* 45162306a36Sopenharmony_ci * irq functions 45262306a36Sopenharmony_ci * 45362306a36Sopenharmony_ci * it will be called from usbhs_interrupt 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_cistatic int usbhsg_irq_dev_state(struct usbhs_priv *priv, 45662306a36Sopenharmony_ci struct usbhs_irq_state *irq_state) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); 45962306a36Sopenharmony_ci struct device *dev = usbhsg_gpriv_to_dev(gpriv); 46062306a36Sopenharmony_ci int state = usbhs_status_get_device_state(irq_state); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci gpriv->gadget.speed = usbhs_bus_get_speed(priv); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci dev_dbg(dev, "state = %x : speed : %d\n", state, gpriv->gadget.speed); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (gpriv->gadget.speed != USB_SPEED_UNKNOWN && 46762306a36Sopenharmony_ci (state & SUSPENDED_STATE)) { 46862306a36Sopenharmony_ci if (gpriv->driver && gpriv->driver->suspend) 46962306a36Sopenharmony_ci gpriv->driver->suspend(&gpriv->gadget); 47062306a36Sopenharmony_ci usb_gadget_set_state(&gpriv->gadget, USB_STATE_SUSPENDED); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int usbhsg_irq_ctrl_stage(struct usbhs_priv *priv, 47762306a36Sopenharmony_ci struct usbhs_irq_state *irq_state) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); 48062306a36Sopenharmony_ci struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv); 48162306a36Sopenharmony_ci struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(dcp); 48262306a36Sopenharmony_ci struct device *dev = usbhsg_gpriv_to_dev(gpriv); 48362306a36Sopenharmony_ci struct usb_ctrlrequest ctrl; 48462306a36Sopenharmony_ci struct usbhsg_recip_handle *recip_handler = NULL; 48562306a36Sopenharmony_ci int stage = usbhs_status_get_ctrl_stage(irq_state); 48662306a36Sopenharmony_ci int ret = 0; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci dev_dbg(dev, "stage = %d\n", stage); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* 49162306a36Sopenharmony_ci * see Manual 49262306a36Sopenharmony_ci * 49362306a36Sopenharmony_ci * "Operation" 49462306a36Sopenharmony_ci * - "Interrupt Function" 49562306a36Sopenharmony_ci * - "Control Transfer Stage Transition Interrupt" 49662306a36Sopenharmony_ci * - Fig. "Control Transfer Stage Transitions" 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci switch (stage) { 50062306a36Sopenharmony_ci case READ_DATA_STAGE: 50162306a36Sopenharmony_ci pipe->handler = &usbhs_fifo_pio_push_handler; 50262306a36Sopenharmony_ci break; 50362306a36Sopenharmony_ci case WRITE_DATA_STAGE: 50462306a36Sopenharmony_ci pipe->handler = &usbhs_fifo_pio_pop_handler; 50562306a36Sopenharmony_ci break; 50662306a36Sopenharmony_ci case NODATA_STATUS_STAGE: 50762306a36Sopenharmony_ci pipe->handler = &usbhs_ctrl_stage_end_handler; 50862306a36Sopenharmony_ci break; 50962306a36Sopenharmony_ci case READ_STATUS_STAGE: 51062306a36Sopenharmony_ci case WRITE_STATUS_STAGE: 51162306a36Sopenharmony_ci usbhs_dcp_control_transfer_done(pipe); 51262306a36Sopenharmony_ci fallthrough; 51362306a36Sopenharmony_ci default: 51462306a36Sopenharmony_ci return ret; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* 51862306a36Sopenharmony_ci * get usb request 51962306a36Sopenharmony_ci */ 52062306a36Sopenharmony_ci usbhs_usbreq_get_val(priv, &ctrl); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci switch (ctrl.bRequestType & USB_TYPE_MASK) { 52362306a36Sopenharmony_ci case USB_TYPE_STANDARD: 52462306a36Sopenharmony_ci switch (ctrl.bRequest) { 52562306a36Sopenharmony_ci case USB_REQ_CLEAR_FEATURE: 52662306a36Sopenharmony_ci recip_handler = &req_clear_feature; 52762306a36Sopenharmony_ci break; 52862306a36Sopenharmony_ci case USB_REQ_SET_FEATURE: 52962306a36Sopenharmony_ci recip_handler = &req_set_feature; 53062306a36Sopenharmony_ci break; 53162306a36Sopenharmony_ci case USB_REQ_GET_STATUS: 53262306a36Sopenharmony_ci recip_handler = &req_get_status; 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* 53862306a36Sopenharmony_ci * setup stage / run recip 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_ci if (recip_handler) 54162306a36Sopenharmony_ci ret = usbhsg_recip_run_handle(priv, recip_handler, &ctrl); 54262306a36Sopenharmony_ci else 54362306a36Sopenharmony_ci ret = gpriv->driver->setup(&gpriv->gadget, &ctrl); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (ret < 0) 54662306a36Sopenharmony_ci usbhs_pipe_stall(pipe); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci return ret; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci/* 55262306a36Sopenharmony_ci * 55362306a36Sopenharmony_ci * usb_dcp_ops 55462306a36Sopenharmony_ci * 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_cistatic int usbhsg_pipe_disable(struct usbhsg_uep *uep) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); 55962306a36Sopenharmony_ci struct usbhs_pkt *pkt; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci while (1) { 56262306a36Sopenharmony_ci pkt = usbhs_pkt_pop(pipe, NULL); 56362306a36Sopenharmony_ci if (!pkt) 56462306a36Sopenharmony_ci break; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci usbhsg_queue_pop(uep, usbhsg_pkt_to_ureq(pkt), -ESHUTDOWN); 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci usbhs_pipe_disable(pipe); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return 0; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci/* 57562306a36Sopenharmony_ci * 57662306a36Sopenharmony_ci * usb_ep_ops 57762306a36Sopenharmony_ci * 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_cistatic int usbhsg_ep_enable(struct usb_ep *ep, 58062306a36Sopenharmony_ci const struct usb_endpoint_descriptor *desc) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); 58362306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); 58462306a36Sopenharmony_ci struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); 58562306a36Sopenharmony_ci struct usbhs_pipe *pipe; 58662306a36Sopenharmony_ci int ret = -EIO; 58762306a36Sopenharmony_ci unsigned long flags; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci usbhs_lock(priv, flags); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* 59262306a36Sopenharmony_ci * if it already have pipe, 59362306a36Sopenharmony_ci * nothing to do 59462306a36Sopenharmony_ci */ 59562306a36Sopenharmony_ci if (uep->pipe) { 59662306a36Sopenharmony_ci usbhs_pipe_clear(uep->pipe); 59762306a36Sopenharmony_ci usbhs_pipe_sequence_data0(uep->pipe); 59862306a36Sopenharmony_ci ret = 0; 59962306a36Sopenharmony_ci goto usbhsg_ep_enable_end; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci pipe = usbhs_pipe_malloc(priv, 60362306a36Sopenharmony_ci usb_endpoint_type(desc), 60462306a36Sopenharmony_ci usb_endpoint_dir_in(desc)); 60562306a36Sopenharmony_ci if (pipe) { 60662306a36Sopenharmony_ci uep->pipe = pipe; 60762306a36Sopenharmony_ci pipe->mod_private = uep; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* set epnum / maxp */ 61062306a36Sopenharmony_ci usbhs_pipe_config_update(pipe, 0, 61162306a36Sopenharmony_ci usb_endpoint_num(desc), 61262306a36Sopenharmony_ci usb_endpoint_maxp(desc)); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* 61562306a36Sopenharmony_ci * usbhs_fifo_dma_push/pop_handler try to 61662306a36Sopenharmony_ci * use dmaengine if possible. 61762306a36Sopenharmony_ci * It will use pio handler if impossible. 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_ci if (usb_endpoint_dir_in(desc)) { 62062306a36Sopenharmony_ci pipe->handler = &usbhs_fifo_dma_push_handler; 62162306a36Sopenharmony_ci } else { 62262306a36Sopenharmony_ci pipe->handler = &usbhs_fifo_dma_pop_handler; 62362306a36Sopenharmony_ci usbhs_xxxsts_clear(priv, BRDYSTS, 62462306a36Sopenharmony_ci usbhs_pipe_number(pipe)); 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci ret = 0; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ciusbhsg_ep_enable_end: 63162306a36Sopenharmony_ci usbhs_unlock(priv, flags); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci return ret; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic int usbhsg_ep_disable(struct usb_ep *ep) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); 63962306a36Sopenharmony_ci struct usbhs_pipe *pipe; 64062306a36Sopenharmony_ci unsigned long flags; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci spin_lock_irqsave(&uep->lock, flags); 64362306a36Sopenharmony_ci pipe = usbhsg_uep_to_pipe(uep); 64462306a36Sopenharmony_ci if (!pipe) 64562306a36Sopenharmony_ci goto out; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci usbhsg_pipe_disable(uep); 64862306a36Sopenharmony_ci usbhs_pipe_free(pipe); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci uep->pipe->mod_private = NULL; 65162306a36Sopenharmony_ci uep->pipe = NULL; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ciout: 65462306a36Sopenharmony_ci spin_unlock_irqrestore(&uep->lock, flags); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci return 0; 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic struct usb_request *usbhsg_ep_alloc_request(struct usb_ep *ep, 66062306a36Sopenharmony_ci gfp_t gfp_flags) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct usbhsg_request *ureq; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci ureq = kzalloc(sizeof *ureq, gfp_flags); 66562306a36Sopenharmony_ci if (!ureq) 66662306a36Sopenharmony_ci return NULL; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci usbhs_pkt_init(usbhsg_ureq_to_pkt(ureq)); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci return &ureq->req; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic void usbhsg_ep_free_request(struct usb_ep *ep, 67462306a36Sopenharmony_ci struct usb_request *req) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci struct usbhsg_request *ureq = usbhsg_req_to_ureq(req); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci WARN_ON(!list_empty(&ureq->pkt.node)); 67962306a36Sopenharmony_ci kfree(ureq); 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic int usbhsg_ep_queue(struct usb_ep *ep, struct usb_request *req, 68362306a36Sopenharmony_ci gfp_t gfp_flags) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); 68662306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); 68762306a36Sopenharmony_ci struct usbhsg_request *ureq = usbhsg_req_to_ureq(req); 68862306a36Sopenharmony_ci struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* param check */ 69162306a36Sopenharmony_ci if (usbhsg_is_not_connected(gpriv) || 69262306a36Sopenharmony_ci unlikely(!gpriv->driver) || 69362306a36Sopenharmony_ci unlikely(!pipe)) 69462306a36Sopenharmony_ci return -ESHUTDOWN; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci usbhsg_queue_push(uep, ureq); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci return 0; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); 70462306a36Sopenharmony_ci struct usbhsg_request *ureq = usbhsg_req_to_ureq(req); 70562306a36Sopenharmony_ci struct usbhs_pipe *pipe; 70662306a36Sopenharmony_ci unsigned long flags; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci spin_lock_irqsave(&uep->lock, flags); 70962306a36Sopenharmony_ci pipe = usbhsg_uep_to_pipe(uep); 71062306a36Sopenharmony_ci if (pipe) 71162306a36Sopenharmony_ci usbhs_pkt_pop(pipe, usbhsg_ureq_to_pkt(ureq)); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* 71462306a36Sopenharmony_ci * To dequeue a request, this driver should call the usbhsg_queue_pop() 71562306a36Sopenharmony_ci * even if the pipe is NULL. 71662306a36Sopenharmony_ci */ 71762306a36Sopenharmony_ci usbhsg_queue_pop(uep, ureq, -ECONNRESET); 71862306a36Sopenharmony_ci spin_unlock_irqrestore(&uep->lock, flags); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci return 0; 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic int __usbhsg_ep_set_halt_wedge(struct usb_ep *ep, int halt, int wedge) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); 72662306a36Sopenharmony_ci struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); 72762306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); 72862306a36Sopenharmony_ci struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); 72962306a36Sopenharmony_ci struct device *dev = usbhsg_gpriv_to_dev(gpriv); 73062306a36Sopenharmony_ci unsigned long flags; 73162306a36Sopenharmony_ci int ret = 0; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci dev_dbg(dev, "set halt %d (pipe %d)\n", 73462306a36Sopenharmony_ci halt, usbhs_pipe_number(pipe)); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /******************** spin lock ********************/ 73762306a36Sopenharmony_ci usbhs_lock(priv, flags); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* 74062306a36Sopenharmony_ci * According to usb_ep_set_halt()'s description, this function should 74162306a36Sopenharmony_ci * return -EAGAIN if the IN endpoint has any queue or data. Note 74262306a36Sopenharmony_ci * that the usbhs_pipe_is_dir_in() returns false if the pipe is an 74362306a36Sopenharmony_ci * IN endpoint in the gadget mode. 74462306a36Sopenharmony_ci */ 74562306a36Sopenharmony_ci if (!usbhs_pipe_is_dir_in(pipe) && (__usbhsf_pkt_get(pipe) || 74662306a36Sopenharmony_ci usbhs_pipe_contains_transmittable_data(pipe))) { 74762306a36Sopenharmony_ci ret = -EAGAIN; 74862306a36Sopenharmony_ci goto out; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci if (halt) 75262306a36Sopenharmony_ci usbhs_pipe_stall(pipe); 75362306a36Sopenharmony_ci else 75462306a36Sopenharmony_ci usbhs_pipe_disable(pipe); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (halt && wedge) 75762306a36Sopenharmony_ci usbhsg_status_set(gpriv, USBHSG_STATUS_WEDGE); 75862306a36Sopenharmony_ci else 75962306a36Sopenharmony_ci usbhsg_status_clr(gpriv, USBHSG_STATUS_WEDGE); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ciout: 76262306a36Sopenharmony_ci usbhs_unlock(priv, flags); 76362306a36Sopenharmony_ci /******************** spin unlock ******************/ 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return ret; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic int usbhsg_ep_set_halt(struct usb_ep *ep, int value) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci return __usbhsg_ep_set_halt_wedge(ep, value, 0); 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic int usbhsg_ep_set_wedge(struct usb_ep *ep) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci return __usbhsg_ep_set_halt_wedge(ep, 1, 1); 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic const struct usb_ep_ops usbhsg_ep_ops = { 77962306a36Sopenharmony_ci .enable = usbhsg_ep_enable, 78062306a36Sopenharmony_ci .disable = usbhsg_ep_disable, 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci .alloc_request = usbhsg_ep_alloc_request, 78362306a36Sopenharmony_ci .free_request = usbhsg_ep_free_request, 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci .queue = usbhsg_ep_queue, 78662306a36Sopenharmony_ci .dequeue = usbhsg_ep_dequeue, 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci .set_halt = usbhsg_ep_set_halt, 78962306a36Sopenharmony_ci .set_wedge = usbhsg_ep_set_wedge, 79062306a36Sopenharmony_ci}; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci/* 79362306a36Sopenharmony_ci * pullup control 79462306a36Sopenharmony_ci */ 79562306a36Sopenharmony_cistatic int usbhsg_can_pullup(struct usbhs_priv *priv) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci return gpriv->driver && 80062306a36Sopenharmony_ci usbhsg_status_has(gpriv, USBHSG_STATUS_SOFT_CONNECT); 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic void usbhsg_update_pullup(struct usbhs_priv *priv) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci if (usbhsg_can_pullup(priv)) 80662306a36Sopenharmony_ci usbhs_sys_function_pullup(priv, 1); 80762306a36Sopenharmony_ci else 80862306a36Sopenharmony_ci usbhs_sys_function_pullup(priv, 0); 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci/* 81262306a36Sopenharmony_ci * usb module start/end 81362306a36Sopenharmony_ci */ 81462306a36Sopenharmony_cistatic int usbhsg_try_start(struct usbhs_priv *priv, u32 status) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); 81762306a36Sopenharmony_ci struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv); 81862306a36Sopenharmony_ci struct usbhs_mod *mod = usbhs_mod_get_current(priv); 81962306a36Sopenharmony_ci struct device *dev = usbhs_priv_to_dev(priv); 82062306a36Sopenharmony_ci unsigned long flags; 82162306a36Sopenharmony_ci int ret = 0; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci /******************** spin lock ********************/ 82462306a36Sopenharmony_ci usbhs_lock(priv, flags); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci usbhsg_status_set(gpriv, status); 82762306a36Sopenharmony_ci if (!(usbhsg_status_has(gpriv, USBHSG_STATUS_STARTED) && 82862306a36Sopenharmony_ci usbhsg_status_has(gpriv, USBHSG_STATUS_REGISTERD))) 82962306a36Sopenharmony_ci ret = -1; /* not ready */ 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci usbhs_unlock(priv, flags); 83262306a36Sopenharmony_ci /******************** spin unlock ********************/ 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (ret < 0) 83562306a36Sopenharmony_ci return 0; /* not ready is not error */ 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* 83862306a36Sopenharmony_ci * enable interrupt and systems if ready 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_ci dev_dbg(dev, "start gadget\n"); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci /* 84362306a36Sopenharmony_ci * pipe initialize and enable DCP 84462306a36Sopenharmony_ci */ 84562306a36Sopenharmony_ci usbhs_fifo_init(priv); 84662306a36Sopenharmony_ci usbhs_pipe_init(priv, 84762306a36Sopenharmony_ci usbhsg_dma_map_ctrl); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* dcp init instead of usbhsg_ep_enable() */ 85062306a36Sopenharmony_ci dcp->pipe = usbhs_dcp_malloc(priv); 85162306a36Sopenharmony_ci dcp->pipe->mod_private = dcp; 85262306a36Sopenharmony_ci usbhs_pipe_config_update(dcp->pipe, 0, 0, 64); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci /* 85562306a36Sopenharmony_ci * system config enble 85662306a36Sopenharmony_ci * - HI speed 85762306a36Sopenharmony_ci * - function 85862306a36Sopenharmony_ci * - usb module 85962306a36Sopenharmony_ci */ 86062306a36Sopenharmony_ci usbhs_sys_function_ctrl(priv, 1); 86162306a36Sopenharmony_ci usbhsg_update_pullup(priv); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci /* 86462306a36Sopenharmony_ci * enable irq callback 86562306a36Sopenharmony_ci */ 86662306a36Sopenharmony_ci mod->irq_dev_state = usbhsg_irq_dev_state; 86762306a36Sopenharmony_ci mod->irq_ctrl_stage = usbhsg_irq_ctrl_stage; 86862306a36Sopenharmony_ci usbhs_irq_callback_update(priv, mod); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci return 0; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); 87662306a36Sopenharmony_ci struct usbhs_mod *mod = usbhs_mod_get_current(priv); 87762306a36Sopenharmony_ci struct usbhsg_uep *uep; 87862306a36Sopenharmony_ci struct device *dev = usbhs_priv_to_dev(priv); 87962306a36Sopenharmony_ci unsigned long flags; 88062306a36Sopenharmony_ci int ret = 0, i; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci /******************** spin lock ********************/ 88362306a36Sopenharmony_ci usbhs_lock(priv, flags); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci usbhsg_status_clr(gpriv, status); 88662306a36Sopenharmony_ci if (!usbhsg_status_has(gpriv, USBHSG_STATUS_STARTED) && 88762306a36Sopenharmony_ci !usbhsg_status_has(gpriv, USBHSG_STATUS_REGISTERD)) 88862306a36Sopenharmony_ci ret = -1; /* already done */ 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci usbhs_unlock(priv, flags); 89162306a36Sopenharmony_ci /******************** spin unlock ********************/ 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci if (ret < 0) 89462306a36Sopenharmony_ci return 0; /* already done is not error */ 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci /* 89762306a36Sopenharmony_ci * disable interrupt and systems if 1st try 89862306a36Sopenharmony_ci */ 89962306a36Sopenharmony_ci usbhs_fifo_quit(priv); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci /* disable all irq */ 90262306a36Sopenharmony_ci mod->irq_dev_state = NULL; 90362306a36Sopenharmony_ci mod->irq_ctrl_stage = NULL; 90462306a36Sopenharmony_ci usbhs_irq_callback_update(priv, mod); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci gpriv->gadget.speed = USB_SPEED_UNKNOWN; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci /* disable sys */ 90962306a36Sopenharmony_ci usbhs_sys_set_test_mode(priv, 0); 91062306a36Sopenharmony_ci usbhs_sys_function_ctrl(priv, 0); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci /* disable all eps */ 91362306a36Sopenharmony_ci usbhsg_for_each_uep_with_dcp(uep, gpriv, i) 91462306a36Sopenharmony_ci usbhsg_ep_disable(&uep->ep); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci dev_dbg(dev, "stop gadget\n"); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci return 0; 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci/* 92262306a36Sopenharmony_ci * VBUS provided by the PHY 92362306a36Sopenharmony_ci */ 92462306a36Sopenharmony_cistatic int usbhsm_phy_get_vbus(struct platform_device *pdev) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); 92762306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci return gpriv->vbus_active; 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic void usbhs_mod_phy_mode(struct usbhs_priv *priv) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci struct usbhs_mod_info *info = &priv->mod_info; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci info->irq_vbus = NULL; 93762306a36Sopenharmony_ci info->get_vbus = usbhsm_phy_get_vbus; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci usbhs_irq_callback_update(priv, NULL); 94062306a36Sopenharmony_ci} 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci/* 94362306a36Sopenharmony_ci * 94462306a36Sopenharmony_ci * linux usb function 94562306a36Sopenharmony_ci * 94662306a36Sopenharmony_ci */ 94762306a36Sopenharmony_cistatic int usbhsg_gadget_start(struct usb_gadget *gadget, 94862306a36Sopenharmony_ci struct usb_gadget_driver *driver) 94962306a36Sopenharmony_ci{ 95062306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget); 95162306a36Sopenharmony_ci struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); 95262306a36Sopenharmony_ci struct device *dev = usbhs_priv_to_dev(priv); 95362306a36Sopenharmony_ci int ret; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (!driver || 95662306a36Sopenharmony_ci !driver->setup || 95762306a36Sopenharmony_ci driver->max_speed < USB_SPEED_FULL) 95862306a36Sopenharmony_ci return -EINVAL; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci /* connect to bus through transceiver */ 96162306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(gpriv->transceiver)) { 96262306a36Sopenharmony_ci ret = otg_set_peripheral(gpriv->transceiver->otg, 96362306a36Sopenharmony_ci &gpriv->gadget); 96462306a36Sopenharmony_ci if (ret) { 96562306a36Sopenharmony_ci dev_err(dev, "%s: can't bind to transceiver\n", 96662306a36Sopenharmony_ci gpriv->gadget.name); 96762306a36Sopenharmony_ci return ret; 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci /* get vbus using phy versions */ 97162306a36Sopenharmony_ci usbhs_mod_phy_mode(priv); 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci /* first hook up the driver ... */ 97562306a36Sopenharmony_ci gpriv->driver = driver; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci return usbhsg_try_start(priv, USBHSG_STATUS_REGISTERD); 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic int usbhsg_gadget_stop(struct usb_gadget *gadget) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget); 98362306a36Sopenharmony_ci struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci usbhsg_try_stop(priv, USBHSG_STATUS_REGISTERD); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(gpriv->transceiver)) 98862306a36Sopenharmony_ci otg_set_peripheral(gpriv->transceiver->otg, NULL); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci gpriv->driver = NULL; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci return 0; 99362306a36Sopenharmony_ci} 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci/* 99662306a36Sopenharmony_ci * usb gadget ops 99762306a36Sopenharmony_ci */ 99862306a36Sopenharmony_cistatic int usbhsg_get_frame(struct usb_gadget *gadget) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget); 100162306a36Sopenharmony_ci struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci return usbhs_frame_get_num(priv); 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic int usbhsg_pullup(struct usb_gadget *gadget, int is_on) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget); 100962306a36Sopenharmony_ci struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); 101062306a36Sopenharmony_ci unsigned long flags; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci usbhs_lock(priv, flags); 101362306a36Sopenharmony_ci if (is_on) 101462306a36Sopenharmony_ci usbhsg_status_set(gpriv, USBHSG_STATUS_SOFT_CONNECT); 101562306a36Sopenharmony_ci else 101662306a36Sopenharmony_ci usbhsg_status_clr(gpriv, USBHSG_STATUS_SOFT_CONNECT); 101762306a36Sopenharmony_ci usbhsg_update_pullup(priv); 101862306a36Sopenharmony_ci usbhs_unlock(priv, flags); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci return 0; 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cistatic int usbhsg_set_selfpowered(struct usb_gadget *gadget, int is_self) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci if (is_self) 102862306a36Sopenharmony_ci usbhsg_status_set(gpriv, USBHSG_STATUS_SELF_POWERED); 102962306a36Sopenharmony_ci else 103062306a36Sopenharmony_ci usbhsg_status_clr(gpriv, USBHSG_STATUS_SELF_POWERED); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci gadget->is_selfpowered = (is_self != 0); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci return 0; 103562306a36Sopenharmony_ci} 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_cistatic int usbhsg_vbus_session(struct usb_gadget *gadget, int is_active) 103862306a36Sopenharmony_ci{ 103962306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget); 104062306a36Sopenharmony_ci struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); 104162306a36Sopenharmony_ci struct platform_device *pdev = usbhs_priv_to_pdev(priv); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci gpriv->vbus_active = !!is_active; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci usbhsc_schedule_notify_hotplug(pdev); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci return 0; 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_cistatic const struct usb_gadget_ops usbhsg_gadget_ops = { 105162306a36Sopenharmony_ci .get_frame = usbhsg_get_frame, 105262306a36Sopenharmony_ci .set_selfpowered = usbhsg_set_selfpowered, 105362306a36Sopenharmony_ci .udc_start = usbhsg_gadget_start, 105462306a36Sopenharmony_ci .udc_stop = usbhsg_gadget_stop, 105562306a36Sopenharmony_ci .pullup = usbhsg_pullup, 105662306a36Sopenharmony_ci .vbus_session = usbhsg_vbus_session, 105762306a36Sopenharmony_ci}; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic int usbhsg_start(struct usbhs_priv *priv) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci return usbhsg_try_start(priv, USBHSG_STATUS_STARTED); 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cistatic int usbhsg_stop(struct usbhs_priv *priv) 106562306a36Sopenharmony_ci{ 106662306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci /* cable disconnect */ 106962306a36Sopenharmony_ci if (gpriv->driver && 107062306a36Sopenharmony_ci gpriv->driver->disconnect) 107162306a36Sopenharmony_ci gpriv->driver->disconnect(&gpriv->gadget); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci return usbhsg_try_stop(priv, USBHSG_STATUS_STARTED); 107462306a36Sopenharmony_ci} 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ciint usbhs_mod_gadget_probe(struct usbhs_priv *priv) 107762306a36Sopenharmony_ci{ 107862306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv; 107962306a36Sopenharmony_ci struct usbhsg_uep *uep; 108062306a36Sopenharmony_ci struct device *dev = usbhs_priv_to_dev(priv); 108162306a36Sopenharmony_ci struct renesas_usbhs_driver_pipe_config *pipe_configs = 108262306a36Sopenharmony_ci usbhs_get_dparam(priv, pipe_configs); 108362306a36Sopenharmony_ci int pipe_size = usbhs_get_dparam(priv, pipe_size); 108462306a36Sopenharmony_ci int i; 108562306a36Sopenharmony_ci int ret; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci gpriv = kzalloc(sizeof(struct usbhsg_gpriv), GFP_KERNEL); 108862306a36Sopenharmony_ci if (!gpriv) 108962306a36Sopenharmony_ci return -ENOMEM; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci uep = kcalloc(pipe_size, sizeof(struct usbhsg_uep), GFP_KERNEL); 109262306a36Sopenharmony_ci if (!uep) { 109362306a36Sopenharmony_ci ret = -ENOMEM; 109462306a36Sopenharmony_ci goto usbhs_mod_gadget_probe_err_gpriv; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED); 109862306a36Sopenharmony_ci dev_info(dev, "%stransceiver found\n", 109962306a36Sopenharmony_ci !IS_ERR(gpriv->transceiver) ? "" : "no "); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci /* 110262306a36Sopenharmony_ci * CAUTION 110362306a36Sopenharmony_ci * 110462306a36Sopenharmony_ci * There is no guarantee that it is possible to access usb module here. 110562306a36Sopenharmony_ci * Don't accesses to it. 110662306a36Sopenharmony_ci * The accesse will be enable after "usbhsg_start" 110762306a36Sopenharmony_ci */ 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci /* 111062306a36Sopenharmony_ci * register itself 111162306a36Sopenharmony_ci */ 111262306a36Sopenharmony_ci usbhs_mod_register(priv, &gpriv->mod, USBHS_GADGET); 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci /* init gpriv */ 111562306a36Sopenharmony_ci gpriv->mod.name = "gadget"; 111662306a36Sopenharmony_ci gpriv->mod.start = usbhsg_start; 111762306a36Sopenharmony_ci gpriv->mod.stop = usbhsg_stop; 111862306a36Sopenharmony_ci gpriv->uep = uep; 111962306a36Sopenharmony_ci gpriv->uep_size = pipe_size; 112062306a36Sopenharmony_ci usbhsg_status_init(gpriv); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci /* 112362306a36Sopenharmony_ci * init gadget 112462306a36Sopenharmony_ci */ 112562306a36Sopenharmony_ci gpriv->gadget.dev.parent = dev; 112662306a36Sopenharmony_ci gpriv->gadget.name = "renesas_usbhs_udc"; 112762306a36Sopenharmony_ci gpriv->gadget.ops = &usbhsg_gadget_ops; 112862306a36Sopenharmony_ci gpriv->gadget.max_speed = USB_SPEED_HIGH; 112962306a36Sopenharmony_ci gpriv->gadget.quirk_avoids_skb_reserve = usbhs_get_dparam(priv, 113062306a36Sopenharmony_ci has_usb_dmac); 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci INIT_LIST_HEAD(&gpriv->gadget.ep_list); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* 113562306a36Sopenharmony_ci * init usb_ep 113662306a36Sopenharmony_ci */ 113762306a36Sopenharmony_ci usbhsg_for_each_uep_with_dcp(uep, gpriv, i) { 113862306a36Sopenharmony_ci uep->gpriv = gpriv; 113962306a36Sopenharmony_ci uep->pipe = NULL; 114062306a36Sopenharmony_ci snprintf(uep->ep_name, EP_NAME_SIZE, "ep%d", i); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci uep->ep.name = uep->ep_name; 114362306a36Sopenharmony_ci uep->ep.ops = &usbhsg_ep_ops; 114462306a36Sopenharmony_ci INIT_LIST_HEAD(&uep->ep.ep_list); 114562306a36Sopenharmony_ci spin_lock_init(&uep->lock); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci /* init DCP */ 114862306a36Sopenharmony_ci if (usbhsg_is_dcp(uep)) { 114962306a36Sopenharmony_ci gpriv->gadget.ep0 = &uep->ep; 115062306a36Sopenharmony_ci usb_ep_set_maxpacket_limit(&uep->ep, 64); 115162306a36Sopenharmony_ci uep->ep.caps.type_control = true; 115262306a36Sopenharmony_ci } else { 115362306a36Sopenharmony_ci /* init normal pipe */ 115462306a36Sopenharmony_ci if (pipe_configs[i].type == USB_ENDPOINT_XFER_ISOC) 115562306a36Sopenharmony_ci uep->ep.caps.type_iso = true; 115662306a36Sopenharmony_ci if (pipe_configs[i].type == USB_ENDPOINT_XFER_BULK) 115762306a36Sopenharmony_ci uep->ep.caps.type_bulk = true; 115862306a36Sopenharmony_ci if (pipe_configs[i].type == USB_ENDPOINT_XFER_INT) 115962306a36Sopenharmony_ci uep->ep.caps.type_int = true; 116062306a36Sopenharmony_ci usb_ep_set_maxpacket_limit(&uep->ep, 116162306a36Sopenharmony_ci pipe_configs[i].bufsize); 116262306a36Sopenharmony_ci list_add_tail(&uep->ep.ep_list, &gpriv->gadget.ep_list); 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci uep->ep.caps.dir_in = true; 116562306a36Sopenharmony_ci uep->ep.caps.dir_out = true; 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci ret = usb_add_gadget_udc(dev, &gpriv->gadget); 116962306a36Sopenharmony_ci if (ret) 117062306a36Sopenharmony_ci goto err_add_udc; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci dev_info(dev, "gadget probed\n"); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci return 0; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_cierr_add_udc: 117862306a36Sopenharmony_ci kfree(gpriv->uep); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ciusbhs_mod_gadget_probe_err_gpriv: 118162306a36Sopenharmony_ci kfree(gpriv); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci return ret; 118462306a36Sopenharmony_ci} 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_civoid usbhs_mod_gadget_remove(struct usbhs_priv *priv) 118762306a36Sopenharmony_ci{ 118862306a36Sopenharmony_ci struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci usb_del_gadget_udc(&gpriv->gadget); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci kfree(gpriv->uep); 119362306a36Sopenharmony_ci kfree(gpriv); 119462306a36Sopenharmony_ci} 1195