// SPDX-License-Identifier: GPL-2.0 /* * udc.c - ChipIdea UDC driver * * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved. * * Author: David Lopo */ #include <linux/delay.h> #include <linux/device.h> #include <linux/dmapool.h> #include <linux/err.h> #include <linux/irqreturn.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/pm_runtime.h> #include <linux/pinctrl/consumer.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/otg-fsm.h> #include <linux/usb/chipidea.h> #include "ci.h" #include "udc.h" #include "bits.h" #include "otg.h" #include "otg_fsm.h" #include "trace.h" /* control endpoint description */ static const struct usb_endpoint_descriptor ctrl_endpt_out_desc = …; static const struct usb_endpoint_descriptor ctrl_endpt_in_desc = …; static int reprime_dtd(struct ci_hdrc *ci, struct ci_hw_ep *hwep, struct td_node *node); /** * hw_ep_bit: calculates the bit number * @num: endpoint number * @dir: endpoint direction * * This function returns bit number */ static inline int hw_ep_bit(int num, int dir) { … } static inline int ep_to_bit(struct ci_hdrc *ci, int n) { … } /** * hw_device_state: enables/disables interrupts (execute without interruption) * @ci: the controller * @dma: 0 => disable, !0 => enable and set dma engine * * This function returns an error code */ static int hw_device_state(struct ci_hdrc *ci, u32 dma) { … } /** * hw_ep_flush: flush endpoint fifo (execute without interruption) * @ci: the controller * @num: endpoint number * @dir: endpoint direction * * This function returns an error code */ static int hw_ep_flush(struct ci_hdrc *ci, int num, int dir) { … } /** * hw_ep_disable: disables endpoint (execute without interruption) * @ci: the controller * @num: endpoint number * @dir: endpoint direction * * This function returns an error code */ static int hw_ep_disable(struct ci_hdrc *ci, int num, int dir) { … } /** * hw_ep_enable: enables endpoint (execute without interruption) * @ci: the controller * @num: endpoint number * @dir: endpoint direction * @type: endpoint type * * This function returns an error code */ static int hw_ep_enable(struct ci_hdrc *ci, int num, int dir, int type) { … } /** * hw_ep_get_halt: return endpoint halt status * @ci: the controller * @num: endpoint number * @dir: endpoint direction * * This function returns 1 if endpoint halted */ static int hw_ep_get_halt(struct ci_hdrc *ci, int num, int dir) { … } /** * hw_ep_prime: primes endpoint (execute without interruption) * @ci: the controller * @num: endpoint number * @dir: endpoint direction * @is_ctrl: true if control endpoint * * This function returns an error code */ static int hw_ep_prime(struct ci_hdrc *ci, int num, int dir, int is_ctrl) { … } /** * hw_ep_set_halt: configures ep halt & resets data toggle after clear (execute * without interruption) * @ci: the controller * @num: endpoint number * @dir: endpoint direction * @value: true => stall, false => unstall * * This function returns an error code */ static int hw_ep_set_halt(struct ci_hdrc *ci, int num, int dir, int value) { … } /** * hw_port_is_high_speed: test if port is high speed * @ci: the controller * * This function returns true if high speed port */ static int hw_port_is_high_speed(struct ci_hdrc *ci) { … } /** * hw_test_and_clear_complete: test & clear complete status (execute without * interruption) * @ci: the controller * @n: endpoint number * * This function returns complete status */ static int hw_test_and_clear_complete(struct ci_hdrc *ci, int n) { … } /** * hw_test_and_clear_intr_active: test & clear active interrupts (execute * without interruption) * @ci: the controller * * This function returns active interrutps */ static u32 hw_test_and_clear_intr_active(struct ci_hdrc *ci) { … } /** * hw_test_and_clear_setup_guard: test & clear setup guard (execute without * interruption) * @ci: the controller * * This function returns guard value */ static int hw_test_and_clear_setup_guard(struct ci_hdrc *ci) { … } /** * hw_test_and_set_setup_guard: test & set setup guard (execute without * interruption) * @ci: the controller * * This function returns guard value */ static int hw_test_and_set_setup_guard(struct ci_hdrc *ci) { … } /** * hw_usb_set_address: configures USB address (execute without interruption) * @ci: the controller * @value: new USB address * * This function explicitly sets the address, without the "USBADRA" (advance) * feature, which is not supported by older versions of the controller. */ static void hw_usb_set_address(struct ci_hdrc *ci, u8 value) { … } /** * hw_usb_reset: restart device after a bus reset (execute without * interruption) * @ci: the controller * * This function returns an error code */ static int hw_usb_reset(struct ci_hdrc *ci) { … } /****************************************************************************** * UTIL block *****************************************************************************/ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq, unsigned int length, struct scatterlist *s) { … } /** * _usb_addr: calculates endpoint address from direction & number * @ep: endpoint */ static inline u8 _usb_addr(struct ci_hw_ep *ep) { … } static int prepare_td_for_non_sg(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) { … } static int prepare_td_per_sg(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq, struct scatterlist *s) { … } static void ci_add_buffer_entry(struct td_node *node, struct scatterlist *s) { … } static int prepare_td_for_sg(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) { … } /** * _hardware_enqueue: configures a request at hardware level * @hwep: endpoint * @hwreq: request * * This function returns an error code */ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) { … } /** * free_pending_td: remove a pending request for the endpoint * @hwep: endpoint */ static void free_pending_td(struct ci_hw_ep *hwep) { … } static int reprime_dtd(struct ci_hdrc *ci, struct ci_hw_ep *hwep, struct td_node *node) { … } /** * _hardware_dequeue: handles a request at hardware level * @hwep: endpoint * @hwreq: request * * This function returns an error code */ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) { … } /** * _ep_nuke: dequeues all endpoint requests * @hwep: endpoint * * This function returns an error code * Caller must hold lock */ static int _ep_nuke(struct ci_hw_ep *hwep) __releases(hwep->lock) __acquires(hwep->lock) { … } static int _ep_set_halt(struct usb_ep *ep, int value, bool check_transfer) { … } /** * _gadget_stop_activity: stops all USB activity, flushes & disables all endpts * @gadget: gadget * * This function returns an error code */ static int _gadget_stop_activity(struct usb_gadget *gadget) { … } /****************************************************************************** * ISR block *****************************************************************************/ /** * isr_reset_handler: USB reset interrupt handler * @ci: UDC device * * This function resets USB engine after a bus reset occurred */ static void isr_reset_handler(struct ci_hdrc *ci) __releases(ci->lock) __acquires(ci->lock) { … } /** * isr_get_status_complete: get_status request complete function * @ep: endpoint * @req: request handled * * Caller must release lock */ static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req) { … } /** * _ep_queue: queues (submits) an I/O request to an endpoint * @ep: endpoint * @req: request * @gfp_flags: GFP flags (not used) * * Caller must hold lock * This function returns an error code */ static int _ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t __maybe_unused gfp_flags) { … } /** * isr_get_status_response: get_status request response * @ci: ci struct * @setup: setup request packet * * This function returns an error code */ static int isr_get_status_response(struct ci_hdrc *ci, struct usb_ctrlrequest *setup) __releases(hwep->lock) __acquires(hwep->lock) { … } /** * isr_setup_status_complete: setup_status request complete function * @ep: endpoint * @req: request handled * * Caller must release lock. Put the port in test mode if test mode * feature is selected. */ static void isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req) { … } /** * isr_setup_status_phase: queues the status phase of a setup transation * @ci: ci struct * * This function returns an error code */ static int isr_setup_status_phase(struct ci_hdrc *ci) { … } /** * isr_tr_complete_low: transaction complete low level handler * @hwep: endpoint * * This function returns an error code * Caller must hold lock */ static int isr_tr_complete_low(struct ci_hw_ep *hwep) __releases(hwep->lock) __acquires(hwep->lock) { … } static int otg_a_alt_hnp_support(struct ci_hdrc *ci) { … } /** * isr_setup_packet_handler: setup packet handler * @ci: UDC descriptor * * This function handles setup packet */ static void isr_setup_packet_handler(struct ci_hdrc *ci) __releases(ci->lock) __acquires(ci->lock) { … } /** * isr_tr_complete_handler: transaction complete interrupt handler * @ci: UDC descriptor * * This function handles traffic events */ static void isr_tr_complete_handler(struct ci_hdrc *ci) __releases(ci->lock) __acquires(ci->lock) { … } /****************************************************************************** * ENDPT block *****************************************************************************/ /* * ep_enable: configure endpoint, making it usable * * Check usb_ep_enable() at "usb_gadget.h" for details */ static int ep_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc) { … } /* * ep_disable: endpoint is no longer usable * * Check usb_ep_disable() at "usb_gadget.h" for details */ static int ep_disable(struct usb_ep *ep) { … } /* * ep_alloc_request: allocate a request object to use with this endpoint * * Check usb_ep_alloc_request() at "usb_gadget.h" for details */ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) { … } /* * ep_free_request: frees a request object * * Check usb_ep_free_request() at "usb_gadget.h" for details */ static void ep_free_request(struct usb_ep *ep, struct usb_request *req) { … } /* * ep_queue: queues (submits) an I/O request to an endpoint * * Check usb_ep_queue()* at usb_gadget.h" for details */ static int ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t __maybe_unused gfp_flags) { … } /* * ep_dequeue: dequeues (cancels, unlinks) an I/O request from an endpoint * * Check usb_ep_dequeue() at "usb_gadget.h" for details */ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) { … } /* * ep_set_halt: sets the endpoint halt feature * * Check usb_ep_set_halt() at "usb_gadget.h" for details */ static int ep_set_halt(struct usb_ep *ep, int value) { … } /* * ep_set_wedge: sets the halt feature and ignores clear requests * * Check usb_ep_set_wedge() at "usb_gadget.h" for details */ static int ep_set_wedge(struct usb_ep *ep) { … } /* * ep_fifo_flush: flushes contents of a fifo * * Check usb_ep_fifo_flush() at "usb_gadget.h" for details */ static void ep_fifo_flush(struct usb_ep *ep) { … } /* * Endpoint-specific part of the API to the USB controller hardware * Check "usb_gadget.h" for details */ static const struct usb_ep_ops usb_ep_ops = …; /****************************************************************************** * GADGET block *****************************************************************************/ static int ci_udc_get_frame(struct usb_gadget *_gadget) { … } /* * ci_hdrc_gadget_connect: caller makes sure gadget driver is binded */ static void ci_hdrc_gadget_connect(struct usb_gadget *_gadget, int is_active) { … } static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) { … } static int ci_udc_wakeup(struct usb_gadget *_gadget) { … } static int ci_udc_vbus_draw(struct usb_gadget *_gadget, unsigned ma) { … } static int ci_udc_selfpowered(struct usb_gadget *_gadget, int is_on) { … } /* Change Data+ pullup status * this func is used by usb_gadget_connect/disconnect */ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on) { … } static int ci_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver); static int ci_udc_stop(struct usb_gadget *gadget); /* Match ISOC IN from the highest endpoint */ static struct usb_ep *ci_udc_match_ep(struct usb_gadget *gadget, struct usb_endpoint_descriptor *desc, struct usb_ss_ep_comp_descriptor *comp_desc) { … } /* * Device operations part of the API to the USB controller hardware, * which don't involve endpoints (or i/o) * Check "usb_gadget.h" for details */ static const struct usb_gadget_ops usb_gadget_ops = …; static int init_eps(struct ci_hdrc *ci) { … } static void destroy_eps(struct ci_hdrc *ci) { … } /** * ci_udc_start: register a gadget driver * @gadget: our gadget * @driver: the driver being registered * * Interrupts are enabled here. */ static int ci_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { … } static void ci_udc_stop_for_otg_fsm(struct ci_hdrc *ci) { … } /* * ci_udc_stop: unregister a gadget driver */ static int ci_udc_stop(struct usb_gadget *gadget) { … } /****************************************************************************** * BUS block *****************************************************************************/ /* * udc_irq: ci interrupt handler * * This function returns IRQ_HANDLED if the IRQ has been handled * It locks access to registers */ static irqreturn_t udc_irq(struct ci_hdrc *ci) { … } /** * udc_start: initialize gadget role * @ci: chipidea controller */ static int udc_start(struct ci_hdrc *ci) { … } /* * ci_hdrc_gadget_destroy: parent remove must call this to remove UDC * * No interrupts active, the IRQ has been released */ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) { … } static int udc_id_switch_for_device(struct ci_hdrc *ci) { … } static void udc_id_switch_for_host(struct ci_hdrc *ci) { … } #ifdef CONFIG_PM_SLEEP static void udc_suspend(struct ci_hdrc *ci) { … } static void udc_resume(struct ci_hdrc *ci, bool power_lost) { … } #endif /** * ci_hdrc_gadget_init - initialize device related bits * @ci: the controller * * This function initializes the gadget, if the device is "device capable". */ int ci_hdrc_gadget_init(struct ci_hdrc *ci) { … }