1f6ce6072SVignesh Raghavendra // SPDX-License-Identifier: GPL-2.0
2f6ce6072SVignesh Raghavendra /*
3f6ce6072SVignesh Raghavendra * Cadence USBSS DRD Driver - gadget side.
4f6ce6072SVignesh Raghavendra *
5f6ce6072SVignesh Raghavendra * Copyright (C) 2018 Cadence Design Systems.
6f6ce6072SVignesh Raghavendra * Copyright (C) 2017-2018 NXP
7f6ce6072SVignesh Raghavendra *
8f6ce6072SVignesh Raghavendra * Authors: Pawel Jez <pjez@cadence.com>,
9f6ce6072SVignesh Raghavendra * Pawel Laszczak <pawell@cadence.com>
10f6ce6072SVignesh Raghavendra * Peter Chen <peter.chen@nxp.com>
11f6ce6072SVignesh Raghavendra */
12f6ce6072SVignesh Raghavendra
13*6a2b8f48SVignesh Raghavendra #include <cpu_func.h>
14f6ce6072SVignesh Raghavendra #include <linux/usb/composite.h>
15f6ce6072SVignesh Raghavendra #include <linux/iopoll.h>
16f6ce6072SVignesh Raghavendra
17f6ce6072SVignesh Raghavendra #include "gadget.h"
18f6ce6072SVignesh Raghavendra #include "trace.h"
19f6ce6072SVignesh Raghavendra
20f6ce6072SVignesh Raghavendra #define readl_poll_timeout_atomic readl_poll_timeout
21f6ce6072SVignesh Raghavendra #define usleep_range(a, b) udelay((b))
22f6ce6072SVignesh Raghavendra
23f6ce6072SVignesh Raghavendra static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
24f6ce6072SVignesh Raghavendra .bLength = USB_DT_ENDPOINT_SIZE,
25f6ce6072SVignesh Raghavendra .bDescriptorType = USB_DT_ENDPOINT,
26f6ce6072SVignesh Raghavendra .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
27f6ce6072SVignesh Raghavendra };
28f6ce6072SVignesh Raghavendra
29f6ce6072SVignesh Raghavendra /**
30f6ce6072SVignesh Raghavendra * cdns3_ep0_run_transfer - Do transfer on default endpoint hardware
31f6ce6072SVignesh Raghavendra * @priv_dev: extended gadget object
32f6ce6072SVignesh Raghavendra * @dma_addr: physical address where data is/will be stored
33f6ce6072SVignesh Raghavendra * @length: data length
34f6ce6072SVignesh Raghavendra * @erdy: set it to 1 when ERDY packet should be sent -
35f6ce6072SVignesh Raghavendra * exit from flow control state
36f6ce6072SVignesh Raghavendra */
cdns3_ep0_run_transfer(struct cdns3_device * priv_dev,dma_addr_t dma_addr,unsigned int length,int erdy,int zlp)37f6ce6072SVignesh Raghavendra static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
38f6ce6072SVignesh Raghavendra dma_addr_t dma_addr,
39f6ce6072SVignesh Raghavendra unsigned int length, int erdy, int zlp)
40f6ce6072SVignesh Raghavendra {
41f6ce6072SVignesh Raghavendra struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
42f6ce6072SVignesh Raghavendra struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
43f6ce6072SVignesh Raghavendra
44f6ce6072SVignesh Raghavendra priv_ep->trb_pool[0].buffer = TRB_BUFFER(dma_addr);
45f6ce6072SVignesh Raghavendra priv_ep->trb_pool[0].length = TRB_LEN(length);
46f6ce6072SVignesh Raghavendra
47f6ce6072SVignesh Raghavendra if (zlp) {
48f6ce6072SVignesh Raghavendra priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_TYPE(TRB_NORMAL);
49f6ce6072SVignesh Raghavendra priv_ep->trb_pool[1].buffer = TRB_BUFFER(dma_addr);
50f6ce6072SVignesh Raghavendra priv_ep->trb_pool[1].length = TRB_LEN(0);
51f6ce6072SVignesh Raghavendra priv_ep->trb_pool[1].control = TRB_CYCLE | TRB_IOC |
52f6ce6072SVignesh Raghavendra TRB_TYPE(TRB_NORMAL);
53f6ce6072SVignesh Raghavendra } else {
54f6ce6072SVignesh Raghavendra priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_IOC |
55f6ce6072SVignesh Raghavendra TRB_TYPE(TRB_NORMAL);
56f6ce6072SVignesh Raghavendra priv_ep->trb_pool[1].control = 0;
57f6ce6072SVignesh Raghavendra }
58f6ce6072SVignesh Raghavendra
59f6ce6072SVignesh Raghavendra /* Flush both TRBs */
60f6ce6072SVignesh Raghavendra flush_dcache_range((unsigned long)priv_ep->trb_pool,
61f6ce6072SVignesh Raghavendra (unsigned long)priv_ep->trb_pool +
62f6ce6072SVignesh Raghavendra ROUND(sizeof(struct cdns3_trb) * 2,
63f6ce6072SVignesh Raghavendra CONFIG_SYS_CACHELINE_SIZE));
64f6ce6072SVignesh Raghavendra
65f6ce6072SVignesh Raghavendra trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
66f6ce6072SVignesh Raghavendra
67f6ce6072SVignesh Raghavendra cdns3_select_ep(priv_dev, priv_dev->ep0_data_dir);
68f6ce6072SVignesh Raghavendra
69f6ce6072SVignesh Raghavendra writel(EP_STS_TRBERR, ®s->ep_sts);
70f6ce6072SVignesh Raghavendra writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), ®s->ep_traddr);
71f6ce6072SVignesh Raghavendra trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
72f6ce6072SVignesh Raghavendra readl(®s->ep_traddr));
73f6ce6072SVignesh Raghavendra
74f6ce6072SVignesh Raghavendra /* TRB should be prepared before starting transfer. */
75f6ce6072SVignesh Raghavendra writel(EP_CMD_DRDY, ®s->ep_cmd);
76f6ce6072SVignesh Raghavendra
77f6ce6072SVignesh Raghavendra /* Resume controller before arming transfer. */
78f6ce6072SVignesh Raghavendra __cdns3_gadget_wakeup(priv_dev);
79f6ce6072SVignesh Raghavendra
80f6ce6072SVignesh Raghavendra if (erdy)
81f6ce6072SVignesh Raghavendra writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
82f6ce6072SVignesh Raghavendra }
83f6ce6072SVignesh Raghavendra
84f6ce6072SVignesh Raghavendra /**
85f6ce6072SVignesh Raghavendra * cdns3_ep0_delegate_req - Returns status of handling setup packet
86f6ce6072SVignesh Raghavendra * Setup is handled by gadget driver
87f6ce6072SVignesh Raghavendra * @priv_dev: extended gadget object
88f6ce6072SVignesh Raghavendra * @ctrl_req: pointer to received setup packet
89f6ce6072SVignesh Raghavendra *
90f6ce6072SVignesh Raghavendra * Returns zero on success or negative value on failure
91f6ce6072SVignesh Raghavendra */
cdns3_ep0_delegate_req(struct cdns3_device * priv_dev,struct usb_ctrlrequest * ctrl_req)92f6ce6072SVignesh Raghavendra static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
93f6ce6072SVignesh Raghavendra struct usb_ctrlrequest *ctrl_req)
94f6ce6072SVignesh Raghavendra {
95f6ce6072SVignesh Raghavendra int ret;
96f6ce6072SVignesh Raghavendra
97f6ce6072SVignesh Raghavendra spin_unlock(&priv_dev->lock);
98f6ce6072SVignesh Raghavendra priv_dev->setup_pending = 1;
99f6ce6072SVignesh Raghavendra ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
100f6ce6072SVignesh Raghavendra priv_dev->setup_pending = 0;
101f6ce6072SVignesh Raghavendra spin_lock(&priv_dev->lock);
102f6ce6072SVignesh Raghavendra return ret;
103f6ce6072SVignesh Raghavendra }
104f6ce6072SVignesh Raghavendra
cdns3_prepare_setup_packet(struct cdns3_device * priv_dev)105f6ce6072SVignesh Raghavendra static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
106f6ce6072SVignesh Raghavendra {
107f6ce6072SVignesh Raghavendra priv_dev->ep0_data_dir = 0;
108f6ce6072SVignesh Raghavendra priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
109f6ce6072SVignesh Raghavendra cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
110f6ce6072SVignesh Raghavendra sizeof(struct usb_ctrlrequest), 0, 0);
111f6ce6072SVignesh Raghavendra }
112f6ce6072SVignesh Raghavendra
cdns3_ep0_complete_setup(struct cdns3_device * priv_dev,u8 send_stall,u8 send_erdy)113f6ce6072SVignesh Raghavendra static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
114f6ce6072SVignesh Raghavendra u8 send_stall, u8 send_erdy)
115f6ce6072SVignesh Raghavendra {
116f6ce6072SVignesh Raghavendra struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
117f6ce6072SVignesh Raghavendra struct usb_request *request;
118f6ce6072SVignesh Raghavendra
119f6ce6072SVignesh Raghavendra request = cdns3_next_request(&priv_ep->pending_req_list);
120f6ce6072SVignesh Raghavendra if (request)
121f6ce6072SVignesh Raghavendra list_del_init(&request->list);
122f6ce6072SVignesh Raghavendra
123f6ce6072SVignesh Raghavendra if (send_stall) {
124f6ce6072SVignesh Raghavendra trace_cdns3_halt(priv_ep, send_stall, 0);
125f6ce6072SVignesh Raghavendra /* set_stall on ep0 */
126f6ce6072SVignesh Raghavendra cdns3_select_ep(priv_dev, 0x00);
127f6ce6072SVignesh Raghavendra writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
128f6ce6072SVignesh Raghavendra } else {
129f6ce6072SVignesh Raghavendra cdns3_prepare_setup_packet(priv_dev);
130f6ce6072SVignesh Raghavendra }
131f6ce6072SVignesh Raghavendra
132f6ce6072SVignesh Raghavendra priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
133f6ce6072SVignesh Raghavendra writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
134f6ce6072SVignesh Raghavendra &priv_dev->regs->ep_cmd);
135f6ce6072SVignesh Raghavendra
136f6ce6072SVignesh Raghavendra cdns3_allow_enable_l1(priv_dev, 1);
137f6ce6072SVignesh Raghavendra }
138f6ce6072SVignesh Raghavendra
139f6ce6072SVignesh Raghavendra /**
140f6ce6072SVignesh Raghavendra * cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request
141f6ce6072SVignesh Raghavendra * @priv_dev: extended gadget object
142f6ce6072SVignesh Raghavendra * @ctrl_req: pointer to received setup packet
143f6ce6072SVignesh Raghavendra *
144f6ce6072SVignesh Raghavendra * Returns 0 if success, USB_GADGET_DELAYED_STATUS on deferred status stage,
145f6ce6072SVignesh Raghavendra * error code on error
146f6ce6072SVignesh Raghavendra */
cdns3_req_ep0_set_configuration(struct cdns3_device * priv_dev,struct usb_ctrlrequest * ctrl_req)147f6ce6072SVignesh Raghavendra static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev,
148f6ce6072SVignesh Raghavendra struct usb_ctrlrequest *ctrl_req)
149f6ce6072SVignesh Raghavendra {
150f6ce6072SVignesh Raghavendra enum usb_device_state device_state = priv_dev->gadget.state;
151f6ce6072SVignesh Raghavendra struct cdns3_endpoint *priv_ep;
152f6ce6072SVignesh Raghavendra u32 config = le16_to_cpu(ctrl_req->wValue);
153f6ce6072SVignesh Raghavendra int result = 0;
154f6ce6072SVignesh Raghavendra int i;
155f6ce6072SVignesh Raghavendra
156f6ce6072SVignesh Raghavendra switch (device_state) {
157f6ce6072SVignesh Raghavendra case USB_STATE_ADDRESS:
158f6ce6072SVignesh Raghavendra /* Configure non-control EPs */
159f6ce6072SVignesh Raghavendra for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
160f6ce6072SVignesh Raghavendra priv_ep = priv_dev->eps[i];
161f6ce6072SVignesh Raghavendra if (!priv_ep)
162f6ce6072SVignesh Raghavendra continue;
163f6ce6072SVignesh Raghavendra
164f6ce6072SVignesh Raghavendra if (priv_ep->flags & EP_CLAIMED)
165f6ce6072SVignesh Raghavendra cdns3_ep_config(priv_ep);
166f6ce6072SVignesh Raghavendra }
167f6ce6072SVignesh Raghavendra
168f6ce6072SVignesh Raghavendra result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
169f6ce6072SVignesh Raghavendra
170f6ce6072SVignesh Raghavendra if (result)
171f6ce6072SVignesh Raghavendra return result;
172f6ce6072SVignesh Raghavendra
173f6ce6072SVignesh Raghavendra if (config) {
174f6ce6072SVignesh Raghavendra cdns3_set_hw_configuration(priv_dev);
175f6ce6072SVignesh Raghavendra } else {
176f6ce6072SVignesh Raghavendra cdns3_hw_reset_eps_config(priv_dev);
177f6ce6072SVignesh Raghavendra usb_gadget_set_state(&priv_dev->gadget,
178f6ce6072SVignesh Raghavendra USB_STATE_ADDRESS);
179f6ce6072SVignesh Raghavendra }
180f6ce6072SVignesh Raghavendra break;
181f6ce6072SVignesh Raghavendra case USB_STATE_CONFIGURED:
182f6ce6072SVignesh Raghavendra result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
183f6ce6072SVignesh Raghavendra
184f6ce6072SVignesh Raghavendra if (!config && !result) {
185f6ce6072SVignesh Raghavendra cdns3_hw_reset_eps_config(priv_dev);
186f6ce6072SVignesh Raghavendra usb_gadget_set_state(&priv_dev->gadget,
187f6ce6072SVignesh Raghavendra USB_STATE_ADDRESS);
188f6ce6072SVignesh Raghavendra }
189f6ce6072SVignesh Raghavendra break;
190f6ce6072SVignesh Raghavendra default:
191f6ce6072SVignesh Raghavendra result = -EINVAL;
192f6ce6072SVignesh Raghavendra }
193f6ce6072SVignesh Raghavendra
194f6ce6072SVignesh Raghavendra return result;
195f6ce6072SVignesh Raghavendra }
196f6ce6072SVignesh Raghavendra
197f6ce6072SVignesh Raghavendra /**
198f6ce6072SVignesh Raghavendra * cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request
199f6ce6072SVignesh Raghavendra * @priv_dev: extended gadget object
200f6ce6072SVignesh Raghavendra * @ctrl_req: pointer to received setup packet
201f6ce6072SVignesh Raghavendra *
202f6ce6072SVignesh Raghavendra * Returns 0 if success, error code on error
203f6ce6072SVignesh Raghavendra */
cdns3_req_ep0_set_address(struct cdns3_device * priv_dev,struct usb_ctrlrequest * ctrl_req)204f6ce6072SVignesh Raghavendra static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev,
205f6ce6072SVignesh Raghavendra struct usb_ctrlrequest *ctrl_req)
206f6ce6072SVignesh Raghavendra {
207f6ce6072SVignesh Raghavendra enum usb_device_state device_state = priv_dev->gadget.state;
208f6ce6072SVignesh Raghavendra u32 reg;
209f6ce6072SVignesh Raghavendra u32 addr;
210f6ce6072SVignesh Raghavendra
211f6ce6072SVignesh Raghavendra addr = le16_to_cpu(ctrl_req->wValue);
212f6ce6072SVignesh Raghavendra
213f6ce6072SVignesh Raghavendra if (addr > USB_DEVICE_MAX_ADDRESS) {
214f6ce6072SVignesh Raghavendra dev_err(priv_dev->dev,
215f6ce6072SVignesh Raghavendra "Device address (%d) cannot be greater than %d\n",
216f6ce6072SVignesh Raghavendra addr, USB_DEVICE_MAX_ADDRESS);
217f6ce6072SVignesh Raghavendra return -EINVAL;
218f6ce6072SVignesh Raghavendra }
219f6ce6072SVignesh Raghavendra
220f6ce6072SVignesh Raghavendra if (device_state == USB_STATE_CONFIGURED) {
221f6ce6072SVignesh Raghavendra dev_err(priv_dev->dev,
222f6ce6072SVignesh Raghavendra "can't set_address from configured state\n");
223f6ce6072SVignesh Raghavendra return -EINVAL;
224f6ce6072SVignesh Raghavendra }
225f6ce6072SVignesh Raghavendra
226f6ce6072SVignesh Raghavendra reg = readl(&priv_dev->regs->usb_cmd);
227f6ce6072SVignesh Raghavendra
228f6ce6072SVignesh Raghavendra writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR,
229f6ce6072SVignesh Raghavendra &priv_dev->regs->usb_cmd);
230f6ce6072SVignesh Raghavendra
231f6ce6072SVignesh Raghavendra usb_gadget_set_state(&priv_dev->gadget,
232f6ce6072SVignesh Raghavendra (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));
233f6ce6072SVignesh Raghavendra
234f6ce6072SVignesh Raghavendra return 0;
235f6ce6072SVignesh Raghavendra }
236f6ce6072SVignesh Raghavendra
237f6ce6072SVignesh Raghavendra /**
238f6ce6072SVignesh Raghavendra * cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request
239f6ce6072SVignesh Raghavendra * @priv_dev: extended gadget object
240f6ce6072SVignesh Raghavendra * @ctrl_req: pointer to received setup packet
241f6ce6072SVignesh Raghavendra *
242f6ce6072SVignesh Raghavendra * Returns 0 if success, error code on error
243f6ce6072SVignesh Raghavendra */
cdns3_req_ep0_get_status(struct cdns3_device * priv_dev,struct usb_ctrlrequest * ctrl)244f6ce6072SVignesh Raghavendra static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
245f6ce6072SVignesh Raghavendra struct usb_ctrlrequest *ctrl)
246f6ce6072SVignesh Raghavendra {
247f6ce6072SVignesh Raghavendra __le16 *response_pkt;
248f6ce6072SVignesh Raghavendra u16 usb_status = 0;
249f6ce6072SVignesh Raghavendra u32 recip;
250f6ce6072SVignesh Raghavendra
251f6ce6072SVignesh Raghavendra recip = ctrl->bRequestType & USB_RECIP_MASK;
252f6ce6072SVignesh Raghavendra
253f6ce6072SVignesh Raghavendra switch (recip) {
254f6ce6072SVignesh Raghavendra case USB_RECIP_DEVICE:
255f6ce6072SVignesh Raghavendra /* self powered */
256f6ce6072SVignesh Raghavendra if (priv_dev->is_selfpowered)
257f6ce6072SVignesh Raghavendra usb_status = BIT(USB_DEVICE_SELF_POWERED);
258f6ce6072SVignesh Raghavendra
259f6ce6072SVignesh Raghavendra if (priv_dev->wake_up_flag)
260f6ce6072SVignesh Raghavendra usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP);
261f6ce6072SVignesh Raghavendra
262f6ce6072SVignesh Raghavendra if (priv_dev->gadget.speed != USB_SPEED_SUPER)
263f6ce6072SVignesh Raghavendra break;
264f6ce6072SVignesh Raghavendra
265f6ce6072SVignesh Raghavendra if (priv_dev->u1_allowed)
266f6ce6072SVignesh Raghavendra usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
267f6ce6072SVignesh Raghavendra
268f6ce6072SVignesh Raghavendra if (priv_dev->u2_allowed)
269f6ce6072SVignesh Raghavendra usb_status |= BIT(USB_DEV_STAT_U2_ENABLED);
270f6ce6072SVignesh Raghavendra
271f6ce6072SVignesh Raghavendra break;
272f6ce6072SVignesh Raghavendra case USB_RECIP_INTERFACE:
273f6ce6072SVignesh Raghavendra return cdns3_ep0_delegate_req(priv_dev, ctrl);
274f6ce6072SVignesh Raghavendra case USB_RECIP_ENDPOINT:
275f6ce6072SVignesh Raghavendra /* check if endpoint is stalled */
276f6ce6072SVignesh Raghavendra cdns3_select_ep(priv_dev, ctrl->wIndex);
277f6ce6072SVignesh Raghavendra if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
278f6ce6072SVignesh Raghavendra usb_status = BIT(USB_ENDPOINT_HALT);
279f6ce6072SVignesh Raghavendra break;
280f6ce6072SVignesh Raghavendra default:
281f6ce6072SVignesh Raghavendra return -EINVAL;
282f6ce6072SVignesh Raghavendra }
283f6ce6072SVignesh Raghavendra
284f6ce6072SVignesh Raghavendra response_pkt = (__le16 *)priv_dev->setup_buf;
285f6ce6072SVignesh Raghavendra *response_pkt = cpu_to_le16(usb_status);
286f6ce6072SVignesh Raghavendra
287f6ce6072SVignesh Raghavendra /* Flush setup response */
288f6ce6072SVignesh Raghavendra flush_dcache_range((unsigned long)priv_dev->setup_buf,
289f6ce6072SVignesh Raghavendra (unsigned long)priv_dev->setup_buf +
290f6ce6072SVignesh Raghavendra ROUND(sizeof(struct usb_ctrlrequest),
291f6ce6072SVignesh Raghavendra CONFIG_SYS_CACHELINE_SIZE));
292f6ce6072SVignesh Raghavendra
293f6ce6072SVignesh Raghavendra cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
294f6ce6072SVignesh Raghavendra sizeof(*response_pkt), 1, 0);
295f6ce6072SVignesh Raghavendra return 0;
296f6ce6072SVignesh Raghavendra }
297f6ce6072SVignesh Raghavendra
cdns3_ep0_feature_handle_device(struct cdns3_device * priv_dev,struct usb_ctrlrequest * ctrl,int set)298f6ce6072SVignesh Raghavendra static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
299f6ce6072SVignesh Raghavendra struct usb_ctrlrequest *ctrl,
300f6ce6072SVignesh Raghavendra int set)
301f6ce6072SVignesh Raghavendra {
302f6ce6072SVignesh Raghavendra enum usb_device_state state;
303f6ce6072SVignesh Raghavendra enum usb_device_speed speed;
304f6ce6072SVignesh Raghavendra int ret = 0;
305f6ce6072SVignesh Raghavendra u16 tmode;
306f6ce6072SVignesh Raghavendra
307f6ce6072SVignesh Raghavendra state = priv_dev->gadget.state;
308f6ce6072SVignesh Raghavendra speed = priv_dev->gadget.speed;
309f6ce6072SVignesh Raghavendra
310f6ce6072SVignesh Raghavendra switch (ctrl->wValue) {
311f6ce6072SVignesh Raghavendra case USB_DEVICE_REMOTE_WAKEUP:
312f6ce6072SVignesh Raghavendra priv_dev->wake_up_flag = !!set;
313f6ce6072SVignesh Raghavendra break;
314f6ce6072SVignesh Raghavendra case USB_DEVICE_U1_ENABLE:
315f6ce6072SVignesh Raghavendra if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
316f6ce6072SVignesh Raghavendra return -EINVAL;
317f6ce6072SVignesh Raghavendra
318f6ce6072SVignesh Raghavendra priv_dev->u1_allowed = !!set;
319f6ce6072SVignesh Raghavendra break;
320f6ce6072SVignesh Raghavendra case USB_DEVICE_U2_ENABLE:
321f6ce6072SVignesh Raghavendra if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
322f6ce6072SVignesh Raghavendra return -EINVAL;
323f6ce6072SVignesh Raghavendra
324f6ce6072SVignesh Raghavendra priv_dev->u2_allowed = !!set;
325f6ce6072SVignesh Raghavendra break;
326f6ce6072SVignesh Raghavendra case USB_DEVICE_LTM_ENABLE:
327f6ce6072SVignesh Raghavendra ret = -EINVAL;
328f6ce6072SVignesh Raghavendra break;
329f6ce6072SVignesh Raghavendra case USB_DEVICE_TEST_MODE:
330f6ce6072SVignesh Raghavendra if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
331f6ce6072SVignesh Raghavendra return -EINVAL;
332f6ce6072SVignesh Raghavendra
333f6ce6072SVignesh Raghavendra tmode = le16_to_cpu(ctrl->wIndex);
334f6ce6072SVignesh Raghavendra
335f6ce6072SVignesh Raghavendra if (!set || (tmode & 0xff) != 0)
336f6ce6072SVignesh Raghavendra return -EINVAL;
337f6ce6072SVignesh Raghavendra
338f6ce6072SVignesh Raghavendra switch (tmode >> 8) {
339f6ce6072SVignesh Raghavendra case TEST_J:
340f6ce6072SVignesh Raghavendra case TEST_K:
341f6ce6072SVignesh Raghavendra case TEST_SE0_NAK:
342f6ce6072SVignesh Raghavendra case TEST_PACKET:
343f6ce6072SVignesh Raghavendra cdns3_ep0_complete_setup(priv_dev, 0, 1);
344f6ce6072SVignesh Raghavendra /**
345f6ce6072SVignesh Raghavendra * Little delay to give the controller some time
346f6ce6072SVignesh Raghavendra * for sending status stage.
347f6ce6072SVignesh Raghavendra * This time should be less then 3ms.
348f6ce6072SVignesh Raghavendra */
349f6ce6072SVignesh Raghavendra usleep_range(1000, 2000);
350f6ce6072SVignesh Raghavendra cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
351f6ce6072SVignesh Raghavendra USB_CMD_STMODE |
352f6ce6072SVignesh Raghavendra USB_STS_TMODE_SEL(tmode - 1));
353f6ce6072SVignesh Raghavendra break;
354f6ce6072SVignesh Raghavendra default:
355f6ce6072SVignesh Raghavendra ret = -EINVAL;
356f6ce6072SVignesh Raghavendra }
357f6ce6072SVignesh Raghavendra break;
358f6ce6072SVignesh Raghavendra default:
359f6ce6072SVignesh Raghavendra ret = -EINVAL;
360f6ce6072SVignesh Raghavendra }
361f6ce6072SVignesh Raghavendra
362f6ce6072SVignesh Raghavendra return ret;
363f6ce6072SVignesh Raghavendra }
364f6ce6072SVignesh Raghavendra
cdns3_ep0_feature_handle_intf(struct cdns3_device * priv_dev,struct usb_ctrlrequest * ctrl,int set)365f6ce6072SVignesh Raghavendra static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev,
366f6ce6072SVignesh Raghavendra struct usb_ctrlrequest *ctrl,
367f6ce6072SVignesh Raghavendra int set)
368f6ce6072SVignesh Raghavendra {
369f6ce6072SVignesh Raghavendra u32 wValue;
370f6ce6072SVignesh Raghavendra int ret = 0;
371f6ce6072SVignesh Raghavendra
372f6ce6072SVignesh Raghavendra wValue = le16_to_cpu(ctrl->wValue);
373f6ce6072SVignesh Raghavendra
374f6ce6072SVignesh Raghavendra switch (wValue) {
375f6ce6072SVignesh Raghavendra case USB_INTRF_FUNC_SUSPEND:
376f6ce6072SVignesh Raghavendra break;
377f6ce6072SVignesh Raghavendra default:
378f6ce6072SVignesh Raghavendra ret = -EINVAL;
379f6ce6072SVignesh Raghavendra }
380f6ce6072SVignesh Raghavendra
381f6ce6072SVignesh Raghavendra return ret;
382f6ce6072SVignesh Raghavendra }
383f6ce6072SVignesh Raghavendra
cdns3_ep0_feature_handle_endpoint(struct cdns3_device * priv_dev,struct usb_ctrlrequest * ctrl,int set)384f6ce6072SVignesh Raghavendra static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
385f6ce6072SVignesh Raghavendra struct usb_ctrlrequest *ctrl,
386f6ce6072SVignesh Raghavendra int set)
387f6ce6072SVignesh Raghavendra {
388f6ce6072SVignesh Raghavendra struct cdns3_endpoint *priv_ep;
389f6ce6072SVignesh Raghavendra int ret = 0;
390f6ce6072SVignesh Raghavendra u8 index;
391f6ce6072SVignesh Raghavendra
392f6ce6072SVignesh Raghavendra if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
393f6ce6072SVignesh Raghavendra return -EINVAL;
394f6ce6072SVignesh Raghavendra
395f6ce6072SVignesh Raghavendra if (!(ctrl->wIndex & ~USB_DIR_IN))
396f6ce6072SVignesh Raghavendra return 0;
397f6ce6072SVignesh Raghavendra
398f6ce6072SVignesh Raghavendra index = cdns3_ep_addr_to_index(ctrl->wIndex);
399f6ce6072SVignesh Raghavendra priv_ep = priv_dev->eps[index];
400f6ce6072SVignesh Raghavendra
401f6ce6072SVignesh Raghavendra cdns3_select_ep(priv_dev, ctrl->wIndex);
402f6ce6072SVignesh Raghavendra
403f6ce6072SVignesh Raghavendra if (set)
404f6ce6072SVignesh Raghavendra __cdns3_gadget_ep_set_halt(priv_ep);
405f6ce6072SVignesh Raghavendra else if (!(priv_ep->flags & EP_WEDGE))
406f6ce6072SVignesh Raghavendra ret = __cdns3_gadget_ep_clear_halt(priv_ep);
407f6ce6072SVignesh Raghavendra
408f6ce6072SVignesh Raghavendra cdns3_select_ep(priv_dev, 0x00);
409f6ce6072SVignesh Raghavendra
410f6ce6072SVignesh Raghavendra return ret;
411f6ce6072SVignesh Raghavendra }
412f6ce6072SVignesh Raghavendra
413f6ce6072SVignesh Raghavendra /**
414f6ce6072SVignesh Raghavendra * cdns3_req_ep0_handle_feature -
415f6ce6072SVignesh Raghavendra * Handling of GET/SET_FEATURE standard USB request
416f6ce6072SVignesh Raghavendra *
417f6ce6072SVignesh Raghavendra * @priv_dev: extended gadget object
418f6ce6072SVignesh Raghavendra * @ctrl_req: pointer to received setup packet
419f6ce6072SVignesh Raghavendra * @set: must be set to 1 for SET_FEATURE request
420f6ce6072SVignesh Raghavendra *
421f6ce6072SVignesh Raghavendra * Returns 0 if success, error code on error
422f6ce6072SVignesh Raghavendra */
cdns3_req_ep0_handle_feature(struct cdns3_device * priv_dev,struct usb_ctrlrequest * ctrl,int set)423f6ce6072SVignesh Raghavendra static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev,
424f6ce6072SVignesh Raghavendra struct usb_ctrlrequest *ctrl,
425f6ce6072SVignesh Raghavendra int set)
426f6ce6072SVignesh Raghavendra {
427f6ce6072SVignesh Raghavendra int ret = 0;
428f6ce6072SVignesh Raghavendra u32 recip;
429f6ce6072SVignesh Raghavendra
430f6ce6072SVignesh Raghavendra recip = ctrl->bRequestType & USB_RECIP_MASK;
431f6ce6072SVignesh Raghavendra
432f6ce6072SVignesh Raghavendra switch (recip) {
433f6ce6072SVignesh Raghavendra case USB_RECIP_DEVICE:
434f6ce6072SVignesh Raghavendra ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set);
435f6ce6072SVignesh Raghavendra break;
436f6ce6072SVignesh Raghavendra case USB_RECIP_INTERFACE:
437f6ce6072SVignesh Raghavendra ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set);
438f6ce6072SVignesh Raghavendra break;
439f6ce6072SVignesh Raghavendra case USB_RECIP_ENDPOINT:
440f6ce6072SVignesh Raghavendra ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set);
441f6ce6072SVignesh Raghavendra break;
442f6ce6072SVignesh Raghavendra default:
443f6ce6072SVignesh Raghavendra return -EINVAL;
444f6ce6072SVignesh Raghavendra }
445f6ce6072SVignesh Raghavendra
446f6ce6072SVignesh Raghavendra return ret;
447f6ce6072SVignesh Raghavendra }
448f6ce6072SVignesh Raghavendra
449f6ce6072SVignesh Raghavendra /**
450f6ce6072SVignesh Raghavendra * cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request
451f6ce6072SVignesh Raghavendra * @priv_dev: extended gadget object
452f6ce6072SVignesh Raghavendra * @ctrl_req: pointer to received setup packet
453f6ce6072SVignesh Raghavendra *
454f6ce6072SVignesh Raghavendra * Returns 0 if success, error code on error
455f6ce6072SVignesh Raghavendra */
cdns3_req_ep0_set_sel(struct cdns3_device * priv_dev,struct usb_ctrlrequest * ctrl_req)456f6ce6072SVignesh Raghavendra static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
457f6ce6072SVignesh Raghavendra struct usb_ctrlrequest *ctrl_req)
458f6ce6072SVignesh Raghavendra {
459f6ce6072SVignesh Raghavendra if (priv_dev->gadget.state < USB_STATE_ADDRESS)
460f6ce6072SVignesh Raghavendra return -EINVAL;
461f6ce6072SVignesh Raghavendra
462f6ce6072SVignesh Raghavendra if (ctrl_req->wLength != 6) {
463f6ce6072SVignesh Raghavendra dev_err(priv_dev->dev, "Set SEL should be 6 bytes, got %d\n",
464f6ce6072SVignesh Raghavendra ctrl_req->wLength);
465f6ce6072SVignesh Raghavendra return -EINVAL;
466f6ce6072SVignesh Raghavendra }
467f6ce6072SVignesh Raghavendra
468f6ce6072SVignesh Raghavendra cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1, 0);
469f6ce6072SVignesh Raghavendra return 0;
470f6ce6072SVignesh Raghavendra }
471f6ce6072SVignesh Raghavendra
472f6ce6072SVignesh Raghavendra /**
473f6ce6072SVignesh Raghavendra * cdns3_req_ep0_set_isoch_delay -
474f6ce6072SVignesh Raghavendra * Handling of GET_ISOCH_DELAY standard USB request
475f6ce6072SVignesh Raghavendra * @priv_dev: extended gadget object
476f6ce6072SVignesh Raghavendra * @ctrl_req: pointer to received setup packet
477f6ce6072SVignesh Raghavendra *
478f6ce6072SVignesh Raghavendra * Returns 0 if success, error code on error
479f6ce6072SVignesh Raghavendra */
cdns3_req_ep0_set_isoch_delay(struct cdns3_device * priv_dev,struct usb_ctrlrequest * ctrl_req)480f6ce6072SVignesh Raghavendra static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev,
481f6ce6072SVignesh Raghavendra struct usb_ctrlrequest *ctrl_req)
482f6ce6072SVignesh Raghavendra {
483f6ce6072SVignesh Raghavendra if (ctrl_req->wIndex || ctrl_req->wLength)
484f6ce6072SVignesh Raghavendra return -EINVAL;
485f6ce6072SVignesh Raghavendra
486f6ce6072SVignesh Raghavendra priv_dev->isoch_delay = ctrl_req->wValue;
487f6ce6072SVignesh Raghavendra
488f6ce6072SVignesh Raghavendra return 0;
489f6ce6072SVignesh Raghavendra }
490f6ce6072SVignesh Raghavendra
491f6ce6072SVignesh Raghavendra /**
492f6ce6072SVignesh Raghavendra * cdns3_ep0_standard_request - Handling standard USB requests
493f6ce6072SVignesh Raghavendra * @priv_dev: extended gadget object
494f6ce6072SVignesh Raghavendra * @ctrl_req: pointer to received setup packet
495f6ce6072SVignesh Raghavendra *
496f6ce6072SVignesh Raghavendra * Returns 0 if success, error code on error
497f6ce6072SVignesh Raghavendra */
cdns3_ep0_standard_request(struct cdns3_device * priv_dev,struct usb_ctrlrequest * ctrl_req)498f6ce6072SVignesh Raghavendra static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev,
499f6ce6072SVignesh Raghavendra struct usb_ctrlrequest *ctrl_req)
500f6ce6072SVignesh Raghavendra {
501f6ce6072SVignesh Raghavendra int ret;
502f6ce6072SVignesh Raghavendra
503f6ce6072SVignesh Raghavendra switch (ctrl_req->bRequest) {
504f6ce6072SVignesh Raghavendra case USB_REQ_SET_ADDRESS:
505f6ce6072SVignesh Raghavendra ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req);
506f6ce6072SVignesh Raghavendra break;
507f6ce6072SVignesh Raghavendra case USB_REQ_SET_CONFIGURATION:
508f6ce6072SVignesh Raghavendra ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req);
509f6ce6072SVignesh Raghavendra break;
510f6ce6072SVignesh Raghavendra case USB_REQ_GET_STATUS:
511f6ce6072SVignesh Raghavendra ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req);
512f6ce6072SVignesh Raghavendra break;
513f6ce6072SVignesh Raghavendra case USB_REQ_CLEAR_FEATURE:
514f6ce6072SVignesh Raghavendra ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0);
515f6ce6072SVignesh Raghavendra break;
516f6ce6072SVignesh Raghavendra case USB_REQ_SET_FEATURE:
517f6ce6072SVignesh Raghavendra ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1);
518f6ce6072SVignesh Raghavendra break;
519f6ce6072SVignesh Raghavendra case USB_REQ_SET_SEL:
520f6ce6072SVignesh Raghavendra ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req);
521f6ce6072SVignesh Raghavendra break;
522f6ce6072SVignesh Raghavendra case USB_REQ_SET_ISOCH_DELAY:
523f6ce6072SVignesh Raghavendra ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req);
524f6ce6072SVignesh Raghavendra break;
525f6ce6072SVignesh Raghavendra default:
526f6ce6072SVignesh Raghavendra ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
527f6ce6072SVignesh Raghavendra break;
528f6ce6072SVignesh Raghavendra }
529f6ce6072SVignesh Raghavendra
530f6ce6072SVignesh Raghavendra return ret;
531f6ce6072SVignesh Raghavendra }
532f6ce6072SVignesh Raghavendra
__pending_setup_status_handler(struct cdns3_device * priv_dev)533f6ce6072SVignesh Raghavendra static void __pending_setup_status_handler(struct cdns3_device *priv_dev)
534f6ce6072SVignesh Raghavendra {
535f6ce6072SVignesh Raghavendra struct usb_request *request = priv_dev->pending_status_request;
536f6ce6072SVignesh Raghavendra
537f6ce6072SVignesh Raghavendra if (priv_dev->status_completion_no_call && request &&
538f6ce6072SVignesh Raghavendra request->complete) {
539f6ce6072SVignesh Raghavendra request->complete(&priv_dev->eps[0]->endpoint, request);
540f6ce6072SVignesh Raghavendra priv_dev->status_completion_no_call = 0;
541f6ce6072SVignesh Raghavendra }
542f6ce6072SVignesh Raghavendra }
543f6ce6072SVignesh Raghavendra
cdns3_pending_setup_status_handler(struct work_struct * work)544f6ce6072SVignesh Raghavendra void cdns3_pending_setup_status_handler(struct work_struct *work)
545f6ce6072SVignesh Raghavendra {
546f6ce6072SVignesh Raghavendra struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
547f6ce6072SVignesh Raghavendra pending_status_wq);
548f6ce6072SVignesh Raghavendra unsigned long flags;
549f6ce6072SVignesh Raghavendra
550f6ce6072SVignesh Raghavendra spin_lock_irqsave(&priv_dev->lock, flags);
551f6ce6072SVignesh Raghavendra __pending_setup_status_handler(priv_dev);
552f6ce6072SVignesh Raghavendra spin_unlock_irqrestore(&priv_dev->lock, flags);
553f6ce6072SVignesh Raghavendra }
554f6ce6072SVignesh Raghavendra
555f6ce6072SVignesh Raghavendra /**
556f6ce6072SVignesh Raghavendra * cdns3_ep0_setup_phase - Handling setup USB requests
557f6ce6072SVignesh Raghavendra * @priv_dev: extended gadget object
558f6ce6072SVignesh Raghavendra */
cdns3_ep0_setup_phase(struct cdns3_device * priv_dev)559f6ce6072SVignesh Raghavendra static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev)
560f6ce6072SVignesh Raghavendra {
561f6ce6072SVignesh Raghavendra struct usb_ctrlrequest *ctrl = priv_dev->setup_buf;
562f6ce6072SVignesh Raghavendra struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
563f6ce6072SVignesh Raghavendra int result;
564f6ce6072SVignesh Raghavendra
565f6ce6072SVignesh Raghavendra priv_dev->ep0_data_dir = ctrl->bRequestType & USB_DIR_IN;
566f6ce6072SVignesh Raghavendra
567f6ce6072SVignesh Raghavendra trace_cdns3_ctrl_req(ctrl);
568f6ce6072SVignesh Raghavendra
569f6ce6072SVignesh Raghavendra if (!list_empty(&priv_ep->pending_req_list)) {
570f6ce6072SVignesh Raghavendra struct usb_request *request;
571f6ce6072SVignesh Raghavendra
572f6ce6072SVignesh Raghavendra request = cdns3_next_request(&priv_ep->pending_req_list);
573f6ce6072SVignesh Raghavendra priv_ep->dir = priv_dev->ep0_data_dir;
574f6ce6072SVignesh Raghavendra cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
575f6ce6072SVignesh Raghavendra -ECONNRESET);
576f6ce6072SVignesh Raghavendra }
577f6ce6072SVignesh Raghavendra
578f6ce6072SVignesh Raghavendra if (le16_to_cpu(ctrl->wLength))
579f6ce6072SVignesh Raghavendra priv_dev->ep0_stage = CDNS3_DATA_STAGE;
580f6ce6072SVignesh Raghavendra else
581f6ce6072SVignesh Raghavendra priv_dev->ep0_stage = CDNS3_STATUS_STAGE;
582f6ce6072SVignesh Raghavendra
583f6ce6072SVignesh Raghavendra if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
584f6ce6072SVignesh Raghavendra result = cdns3_ep0_standard_request(priv_dev, ctrl);
585f6ce6072SVignesh Raghavendra else
586f6ce6072SVignesh Raghavendra result = cdns3_ep0_delegate_req(priv_dev, ctrl);
587f6ce6072SVignesh Raghavendra
588f6ce6072SVignesh Raghavendra if (result == USB_GADGET_DELAYED_STATUS)
589f6ce6072SVignesh Raghavendra return;
590f6ce6072SVignesh Raghavendra
591f6ce6072SVignesh Raghavendra if (result < 0)
592f6ce6072SVignesh Raghavendra cdns3_ep0_complete_setup(priv_dev, 1, 1);
593f6ce6072SVignesh Raghavendra else if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE)
594f6ce6072SVignesh Raghavendra cdns3_ep0_complete_setup(priv_dev, 0, 1);
595f6ce6072SVignesh Raghavendra }
596f6ce6072SVignesh Raghavendra
cdns3_transfer_completed(struct cdns3_device * priv_dev)597f6ce6072SVignesh Raghavendra static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
598f6ce6072SVignesh Raghavendra {
599f6ce6072SVignesh Raghavendra struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
600f6ce6072SVignesh Raghavendra
601f6ce6072SVignesh Raghavendra if (!list_empty(&priv_ep->pending_req_list)) {
602f6ce6072SVignesh Raghavendra struct usb_request *request;
603f6ce6072SVignesh Raghavendra
604f6ce6072SVignesh Raghavendra trace_cdns3_complete_trb(priv_ep, priv_ep->trb_pool);
605f6ce6072SVignesh Raghavendra request = cdns3_next_request(&priv_ep->pending_req_list);
606f6ce6072SVignesh Raghavendra
607f6ce6072SVignesh Raghavendra /* Invalidate TRB before accessing it */
608f6ce6072SVignesh Raghavendra invalidate_dcache_range((unsigned long)priv_ep->trb_pool,
609f6ce6072SVignesh Raghavendra (unsigned long)priv_ep->trb_pool +
610f6ce6072SVignesh Raghavendra ROUND(sizeof(struct cdns3_trb),
611f6ce6072SVignesh Raghavendra CONFIG_SYS_CACHELINE_SIZE));
612f6ce6072SVignesh Raghavendra
613f6ce6072SVignesh Raghavendra request->actual =
614f6ce6072SVignesh Raghavendra TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
615f6ce6072SVignesh Raghavendra
616f6ce6072SVignesh Raghavendra priv_ep->dir = priv_dev->ep0_data_dir;
617f6ce6072SVignesh Raghavendra cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0);
618f6ce6072SVignesh Raghavendra }
619f6ce6072SVignesh Raghavendra
620f6ce6072SVignesh Raghavendra cdns3_ep0_complete_setup(priv_dev, 0, 0);
621f6ce6072SVignesh Raghavendra }
622f6ce6072SVignesh Raghavendra
623f6ce6072SVignesh Raghavendra /**
624f6ce6072SVignesh Raghavendra * cdns3_check_new_setup - Check if controller receive new SETUP packet.
625f6ce6072SVignesh Raghavendra * @priv_dev: extended gadget object
626f6ce6072SVignesh Raghavendra *
627f6ce6072SVignesh Raghavendra * The SETUP packet can be kept in on-chip memory or in system memory.
628f6ce6072SVignesh Raghavendra */
cdns3_check_new_setup(struct cdns3_device * priv_dev)629f6ce6072SVignesh Raghavendra static bool cdns3_check_new_setup(struct cdns3_device *priv_dev)
630f6ce6072SVignesh Raghavendra {
631f6ce6072SVignesh Raghavendra u32 ep_sts_reg;
632f6ce6072SVignesh Raghavendra
633f6ce6072SVignesh Raghavendra cdns3_select_ep(priv_dev, 0 | USB_DIR_OUT);
634f6ce6072SVignesh Raghavendra ep_sts_reg = readl(&priv_dev->regs->ep_sts);
635f6ce6072SVignesh Raghavendra
636f6ce6072SVignesh Raghavendra return !!(ep_sts_reg & (EP_STS_SETUP | EP_STS_STPWAIT));
637f6ce6072SVignesh Raghavendra }
638f6ce6072SVignesh Raghavendra
639f6ce6072SVignesh Raghavendra /**
640f6ce6072SVignesh Raghavendra * cdns3_check_ep0_interrupt_proceed - Processes interrupt related to endpoint 0
641f6ce6072SVignesh Raghavendra * @priv_dev: extended gadget object
642f6ce6072SVignesh Raghavendra * @dir: USB_DIR_IN for IN direction, USB_DIR_OUT for OUT direction
643f6ce6072SVignesh Raghavendra */
cdns3_check_ep0_interrupt_proceed(struct cdns3_device * priv_dev,int dir)644f6ce6072SVignesh Raghavendra void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
645f6ce6072SVignesh Raghavendra {
646f6ce6072SVignesh Raghavendra u32 ep_sts_reg;
647f6ce6072SVignesh Raghavendra
648f6ce6072SVignesh Raghavendra cdns3_select_ep(priv_dev, dir);
649f6ce6072SVignesh Raghavendra
650f6ce6072SVignesh Raghavendra ep_sts_reg = readl(&priv_dev->regs->ep_sts);
651f6ce6072SVignesh Raghavendra writel(ep_sts_reg, &priv_dev->regs->ep_sts);
652f6ce6072SVignesh Raghavendra
653f6ce6072SVignesh Raghavendra trace_cdns3_ep0_irq(priv_dev, ep_sts_reg);
654f6ce6072SVignesh Raghavendra
655f6ce6072SVignesh Raghavendra __pending_setup_status_handler(priv_dev);
656f6ce6072SVignesh Raghavendra
657f6ce6072SVignesh Raghavendra if (ep_sts_reg & EP_STS_SETUP)
658f6ce6072SVignesh Raghavendra priv_dev->wait_for_setup = 1;
659f6ce6072SVignesh Raghavendra
660f6ce6072SVignesh Raghavendra if (priv_dev->wait_for_setup && ep_sts_reg & EP_STS_IOC) {
661f6ce6072SVignesh Raghavendra priv_dev->wait_for_setup = 0;
662f6ce6072SVignesh Raghavendra cdns3_allow_enable_l1(priv_dev, 0);
663f6ce6072SVignesh Raghavendra cdns3_ep0_setup_phase(priv_dev);
664f6ce6072SVignesh Raghavendra } else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
665f6ce6072SVignesh Raghavendra priv_dev->ep0_data_dir = dir;
666f6ce6072SVignesh Raghavendra cdns3_transfer_completed(priv_dev);
667f6ce6072SVignesh Raghavendra }
668f6ce6072SVignesh Raghavendra
669f6ce6072SVignesh Raghavendra if (ep_sts_reg & EP_STS_DESCMIS) {
670f6ce6072SVignesh Raghavendra if (dir == 0 && !priv_dev->setup_pending)
671f6ce6072SVignesh Raghavendra cdns3_prepare_setup_packet(priv_dev);
672f6ce6072SVignesh Raghavendra }
673f6ce6072SVignesh Raghavendra }
674f6ce6072SVignesh Raghavendra
675f6ce6072SVignesh Raghavendra /**
676f6ce6072SVignesh Raghavendra * cdns3_gadget_ep0_enable
677f6ce6072SVignesh Raghavendra * Function shouldn't be called by gadget driver,
678f6ce6072SVignesh Raghavendra * endpoint 0 is allways active
679f6ce6072SVignesh Raghavendra */
cdns3_gadget_ep0_enable(struct usb_ep * ep,const struct usb_endpoint_descriptor * desc)680f6ce6072SVignesh Raghavendra static int cdns3_gadget_ep0_enable(struct usb_ep *ep,
681f6ce6072SVignesh Raghavendra const struct usb_endpoint_descriptor *desc)
682f6ce6072SVignesh Raghavendra {
683f6ce6072SVignesh Raghavendra return -EINVAL;
684f6ce6072SVignesh Raghavendra }
685f6ce6072SVignesh Raghavendra
686f6ce6072SVignesh Raghavendra /**
687f6ce6072SVignesh Raghavendra * cdns3_gadget_ep0_disable
688f6ce6072SVignesh Raghavendra * Function shouldn't be called by gadget driver,
689f6ce6072SVignesh Raghavendra * endpoint 0 is allways active
690f6ce6072SVignesh Raghavendra */
cdns3_gadget_ep0_disable(struct usb_ep * ep)691f6ce6072SVignesh Raghavendra static int cdns3_gadget_ep0_disable(struct usb_ep *ep)
692f6ce6072SVignesh Raghavendra {
693f6ce6072SVignesh Raghavendra return -EINVAL;
694f6ce6072SVignesh Raghavendra }
695f6ce6072SVignesh Raghavendra
696f6ce6072SVignesh Raghavendra /**
697f6ce6072SVignesh Raghavendra * cdns3_gadget_ep0_set_halt
698f6ce6072SVignesh Raghavendra * @ep: pointer to endpoint zero object
699f6ce6072SVignesh Raghavendra * @value: 1 for set stall, 0 for clear stall
700f6ce6072SVignesh Raghavendra *
701f6ce6072SVignesh Raghavendra * Returns 0
702f6ce6072SVignesh Raghavendra */
cdns3_gadget_ep0_set_halt(struct usb_ep * ep,int value)703f6ce6072SVignesh Raghavendra static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
704f6ce6072SVignesh Raghavendra {
705f6ce6072SVignesh Raghavendra /* TODO */
706f6ce6072SVignesh Raghavendra return 0;
707f6ce6072SVignesh Raghavendra }
708f6ce6072SVignesh Raghavendra
709f6ce6072SVignesh Raghavendra /**
710f6ce6072SVignesh Raghavendra * cdns3_gadget_ep0_queue Transfer data on endpoint zero
711f6ce6072SVignesh Raghavendra * @ep: pointer to endpoint zero object
712f6ce6072SVignesh Raghavendra * @request: pointer to request object
713f6ce6072SVignesh Raghavendra * @gfp_flags: gfp flags
714f6ce6072SVignesh Raghavendra *
715f6ce6072SVignesh Raghavendra * Returns 0 on success, error code elsewhere
716f6ce6072SVignesh Raghavendra */
cdns3_gadget_ep0_queue(struct usb_ep * ep,struct usb_request * request,gfp_t gfp_flags)717f6ce6072SVignesh Raghavendra static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
718f6ce6072SVignesh Raghavendra struct usb_request *request,
719f6ce6072SVignesh Raghavendra gfp_t gfp_flags)
720f6ce6072SVignesh Raghavendra {
721f6ce6072SVignesh Raghavendra struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
722f6ce6072SVignesh Raghavendra struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
723f6ce6072SVignesh Raghavendra unsigned long flags;
724f6ce6072SVignesh Raghavendra int erdy_sent = 0;
725f6ce6072SVignesh Raghavendra int ret = 0;
726f6ce6072SVignesh Raghavendra u8 zlp = 0;
727f6ce6072SVignesh Raghavendra
728f6ce6072SVignesh Raghavendra trace_cdns3_ep0_queue(priv_dev, request);
729f6ce6072SVignesh Raghavendra
730f6ce6072SVignesh Raghavendra /* cancel the request if controller receive new SETUP packet. */
731f6ce6072SVignesh Raghavendra if (cdns3_check_new_setup(priv_dev))
732f6ce6072SVignesh Raghavendra return -ECONNRESET;
733f6ce6072SVignesh Raghavendra
734f6ce6072SVignesh Raghavendra /* send STATUS stage. Should be called only for SET_CONFIGURATION */
735f6ce6072SVignesh Raghavendra if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
736f6ce6072SVignesh Raghavendra spin_lock_irqsave(&priv_dev->lock, flags);
737f6ce6072SVignesh Raghavendra cdns3_select_ep(priv_dev, 0x00);
738f6ce6072SVignesh Raghavendra
739f6ce6072SVignesh Raghavendra erdy_sent = !priv_dev->hw_configured_flag;
740f6ce6072SVignesh Raghavendra cdns3_set_hw_configuration(priv_dev);
741f6ce6072SVignesh Raghavendra
742f6ce6072SVignesh Raghavendra if (!erdy_sent)
743f6ce6072SVignesh Raghavendra cdns3_ep0_complete_setup(priv_dev, 0, 1);
744f6ce6072SVignesh Raghavendra
745f6ce6072SVignesh Raghavendra cdns3_allow_enable_l1(priv_dev, 1);
746f6ce6072SVignesh Raghavendra
747f6ce6072SVignesh Raghavendra request->actual = 0;
748f6ce6072SVignesh Raghavendra priv_dev->status_completion_no_call = true;
749f6ce6072SVignesh Raghavendra priv_dev->pending_status_request = request;
750f6ce6072SVignesh Raghavendra spin_unlock_irqrestore(&priv_dev->lock, flags);
751f6ce6072SVignesh Raghavendra
752f6ce6072SVignesh Raghavendra /*
753f6ce6072SVignesh Raghavendra * Since there is no completion interrupt for status stage,
754f6ce6072SVignesh Raghavendra * it needs to call ->completion in software after
755f6ce6072SVignesh Raghavendra * ep0_queue is back.
756f6ce6072SVignesh Raghavendra */
757f6ce6072SVignesh Raghavendra #ifndef __UBOOT__
758f6ce6072SVignesh Raghavendra queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
759f6ce6072SVignesh Raghavendra #else
760f6ce6072SVignesh Raghavendra __pending_setup_status_handler(priv_dev);
761f6ce6072SVignesh Raghavendra #endif
762f6ce6072SVignesh Raghavendra return 0;
763f6ce6072SVignesh Raghavendra }
764f6ce6072SVignesh Raghavendra
765f6ce6072SVignesh Raghavendra spin_lock_irqsave(&priv_dev->lock, flags);
766f6ce6072SVignesh Raghavendra if (!list_empty(&priv_ep->pending_req_list)) {
767f6ce6072SVignesh Raghavendra dev_err(priv_dev->dev,
768f6ce6072SVignesh Raghavendra "can't handle multiple requests for ep0\n");
769f6ce6072SVignesh Raghavendra spin_unlock_irqrestore(&priv_dev->lock, flags);
770f6ce6072SVignesh Raghavendra return -EBUSY;
771f6ce6072SVignesh Raghavendra }
772f6ce6072SVignesh Raghavendra
773f6ce6072SVignesh Raghavendra ret = usb_gadget_map_request(&priv_dev->gadget, request,
774f6ce6072SVignesh Raghavendra priv_dev->ep0_data_dir);
775f6ce6072SVignesh Raghavendra if (ret) {
776f6ce6072SVignesh Raghavendra spin_unlock_irqrestore(&priv_dev->lock, flags);
777f6ce6072SVignesh Raghavendra dev_err(priv_dev->dev, "failed to map request\n");
778f6ce6072SVignesh Raghavendra return -EINVAL;
779f6ce6072SVignesh Raghavendra }
780f6ce6072SVignesh Raghavendra
781f6ce6072SVignesh Raghavendra request->status = -EINPROGRESS;
782f6ce6072SVignesh Raghavendra list_add_tail(&request->list, &priv_ep->pending_req_list);
783f6ce6072SVignesh Raghavendra
784f6ce6072SVignesh Raghavendra if (request->zero && request->length &&
785f6ce6072SVignesh Raghavendra (request->length % ep->maxpacket == 0))
786f6ce6072SVignesh Raghavendra zlp = 1;
787f6ce6072SVignesh Raghavendra
788f6ce6072SVignesh Raghavendra cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1, zlp);
789f6ce6072SVignesh Raghavendra
790f6ce6072SVignesh Raghavendra spin_unlock_irqrestore(&priv_dev->lock, flags);
791f6ce6072SVignesh Raghavendra
792f6ce6072SVignesh Raghavendra return ret;
793f6ce6072SVignesh Raghavendra }
794f6ce6072SVignesh Raghavendra
795f6ce6072SVignesh Raghavendra /**
796f6ce6072SVignesh Raghavendra * cdns3_gadget_ep_set_wedge Set wedge on selected endpoint
797f6ce6072SVignesh Raghavendra * @ep: endpoint object
798f6ce6072SVignesh Raghavendra *
799f6ce6072SVignesh Raghavendra * Returns 0
800f6ce6072SVignesh Raghavendra */
cdns3_gadget_ep_set_wedge(struct usb_ep * ep)801f6ce6072SVignesh Raghavendra int cdns3_gadget_ep_set_wedge(struct usb_ep *ep)
802f6ce6072SVignesh Raghavendra {
803f6ce6072SVignesh Raghavendra struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
804f6ce6072SVignesh Raghavendra
805f6ce6072SVignesh Raghavendra dev_dbg(priv_dev->dev, "Wedge for %s\n", ep->name);
806f6ce6072SVignesh Raghavendra cdns3_gadget_ep_set_halt(ep, 1);
807f6ce6072SVignesh Raghavendra priv_ep->flags |= EP_WEDGE;
808f6ce6072SVignesh Raghavendra
809f6ce6072SVignesh Raghavendra return 0;
810f6ce6072SVignesh Raghavendra }
811f6ce6072SVignesh Raghavendra
812f6ce6072SVignesh Raghavendra const struct usb_ep_ops cdns3_gadget_ep0_ops = {
813f6ce6072SVignesh Raghavendra .enable = cdns3_gadget_ep0_enable,
814f6ce6072SVignesh Raghavendra .disable = cdns3_gadget_ep0_disable,
815f6ce6072SVignesh Raghavendra .alloc_request = cdns3_gadget_ep_alloc_request,
816f6ce6072SVignesh Raghavendra .free_request = cdns3_gadget_ep_free_request,
817f6ce6072SVignesh Raghavendra .queue = cdns3_gadget_ep0_queue,
818f6ce6072SVignesh Raghavendra .dequeue = cdns3_gadget_ep_dequeue,
819f6ce6072SVignesh Raghavendra .set_halt = cdns3_gadget_ep0_set_halt,
820f6ce6072SVignesh Raghavendra .set_wedge = cdns3_gadget_ep_set_wedge,
821f6ce6072SVignesh Raghavendra };
822f6ce6072SVignesh Raghavendra
823f6ce6072SVignesh Raghavendra /**
824f6ce6072SVignesh Raghavendra * cdns3_ep0_config - Configures default endpoint
825f6ce6072SVignesh Raghavendra * @priv_dev: extended gadget object
826f6ce6072SVignesh Raghavendra *
827f6ce6072SVignesh Raghavendra * Functions sets parameters: maximal packet size and enables interrupts
828f6ce6072SVignesh Raghavendra */
cdns3_ep0_config(struct cdns3_device * priv_dev)829f6ce6072SVignesh Raghavendra void cdns3_ep0_config(struct cdns3_device *priv_dev)
830f6ce6072SVignesh Raghavendra {
831f6ce6072SVignesh Raghavendra struct cdns3_usb_regs __iomem *regs;
832f6ce6072SVignesh Raghavendra struct cdns3_endpoint *priv_ep;
833f6ce6072SVignesh Raghavendra u32 max_packet_size = 64;
834f6ce6072SVignesh Raghavendra
835f6ce6072SVignesh Raghavendra regs = priv_dev->regs;
836f6ce6072SVignesh Raghavendra
837f6ce6072SVignesh Raghavendra if (priv_dev->gadget.speed == USB_SPEED_SUPER)
838f6ce6072SVignesh Raghavendra max_packet_size = 512;
839f6ce6072SVignesh Raghavendra
840f6ce6072SVignesh Raghavendra priv_ep = priv_dev->eps[0];
841f6ce6072SVignesh Raghavendra
842f6ce6072SVignesh Raghavendra if (!list_empty(&priv_ep->pending_req_list)) {
843f6ce6072SVignesh Raghavendra struct usb_request *request;
844f6ce6072SVignesh Raghavendra
845f6ce6072SVignesh Raghavendra request = cdns3_next_request(&priv_ep->pending_req_list);
846f6ce6072SVignesh Raghavendra list_del_init(&request->list);
847f6ce6072SVignesh Raghavendra }
848f6ce6072SVignesh Raghavendra
849f6ce6072SVignesh Raghavendra priv_dev->u1_allowed = 0;
850f6ce6072SVignesh Raghavendra priv_dev->u2_allowed = 0;
851f6ce6072SVignesh Raghavendra
852f6ce6072SVignesh Raghavendra priv_dev->gadget.ep0->maxpacket = max_packet_size;
853f6ce6072SVignesh Raghavendra cdns3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
854f6ce6072SVignesh Raghavendra
855f6ce6072SVignesh Raghavendra /* init ep out */
856f6ce6072SVignesh Raghavendra cdns3_select_ep(priv_dev, USB_DIR_OUT);
857f6ce6072SVignesh Raghavendra
858f6ce6072SVignesh Raghavendra if (priv_dev->dev_ver >= DEV_VER_V3) {
859f6ce6072SVignesh Raghavendra cdns3_set_register_bit(&priv_dev->regs->dtrans,
860f6ce6072SVignesh Raghavendra BIT(0) | BIT(16));
861f6ce6072SVignesh Raghavendra cdns3_set_register_bit(&priv_dev->regs->tdl_from_trb,
862f6ce6072SVignesh Raghavendra BIT(0) | BIT(16));
863f6ce6072SVignesh Raghavendra }
864f6ce6072SVignesh Raghavendra
865f6ce6072SVignesh Raghavendra writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
866f6ce6072SVignesh Raghavendra ®s->ep_cfg);
867f6ce6072SVignesh Raghavendra
868f6ce6072SVignesh Raghavendra writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN,
869f6ce6072SVignesh Raghavendra ®s->ep_sts_en);
870f6ce6072SVignesh Raghavendra
871f6ce6072SVignesh Raghavendra /* init ep in */
872f6ce6072SVignesh Raghavendra cdns3_select_ep(priv_dev, USB_DIR_IN);
873f6ce6072SVignesh Raghavendra
874f6ce6072SVignesh Raghavendra writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
875f6ce6072SVignesh Raghavendra ®s->ep_cfg);
876f6ce6072SVignesh Raghavendra
877f6ce6072SVignesh Raghavendra writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, ®s->ep_sts_en);
878f6ce6072SVignesh Raghavendra
879f6ce6072SVignesh Raghavendra cdns3_set_register_bit(®s->usb_conf, USB_CONF_U1DS | USB_CONF_U2DS);
880f6ce6072SVignesh Raghavendra }
881f6ce6072SVignesh Raghavendra
882f6ce6072SVignesh Raghavendra /**
883f6ce6072SVignesh Raghavendra * cdns3_init_ep0 Initializes software endpoint 0 of gadget
884f6ce6072SVignesh Raghavendra * @priv_dev: extended gadget object
885f6ce6072SVignesh Raghavendra * @ep_priv: extended endpoint object
886f6ce6072SVignesh Raghavendra *
887f6ce6072SVignesh Raghavendra * Returns 0 on success else error code.
888f6ce6072SVignesh Raghavendra */
cdns3_init_ep0(struct cdns3_device * priv_dev,struct cdns3_endpoint * priv_ep)889f6ce6072SVignesh Raghavendra int cdns3_init_ep0(struct cdns3_device *priv_dev,
890f6ce6072SVignesh Raghavendra struct cdns3_endpoint *priv_ep)
891f6ce6072SVignesh Raghavendra {
892f6ce6072SVignesh Raghavendra sprintf(priv_ep->name, "ep0");
893f6ce6072SVignesh Raghavendra
894f6ce6072SVignesh Raghavendra /* fill linux fields */
895f6ce6072SVignesh Raghavendra priv_ep->endpoint.ops = &cdns3_gadget_ep0_ops;
896f6ce6072SVignesh Raghavendra priv_ep->endpoint.maxburst = 1;
897f6ce6072SVignesh Raghavendra usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
898f6ce6072SVignesh Raghavendra CDNS3_EP0_MAX_PACKET_LIMIT);
899f6ce6072SVignesh Raghavendra #ifndef __UBOOT__
900f6ce6072SVignesh Raghavendra priv_ep->endpoint.address = 0;
901f6ce6072SVignesh Raghavendra #endif
902f6ce6072SVignesh Raghavendra priv_ep->endpoint.caps.type_control = 1;
903f6ce6072SVignesh Raghavendra priv_ep->endpoint.caps.dir_in = 1;
904f6ce6072SVignesh Raghavendra priv_ep->endpoint.caps.dir_out = 1;
905f6ce6072SVignesh Raghavendra priv_ep->endpoint.name = priv_ep->name;
906f6ce6072SVignesh Raghavendra priv_ep->endpoint.desc = &cdns3_gadget_ep0_desc;
907f6ce6072SVignesh Raghavendra priv_dev->gadget.ep0 = &priv_ep->endpoint;
908f6ce6072SVignesh Raghavendra priv_ep->type = USB_ENDPOINT_XFER_CONTROL;
909f6ce6072SVignesh Raghavendra
910f6ce6072SVignesh Raghavendra return cdns3_allocate_trb_pool(priv_ep);
911f6ce6072SVignesh Raghavendra }
912