usb: dwc3: Add support for GSI operations to gadget framework

Targets supporting h/w accelerated path over GSI require
GSI specific configuration of USB controller.

Add support to enable h/w accelerated path to IPA.
Include operations to configure USB endpoints as
GSI capable, allocate TRBs that are associated with
GSI capable endpoints, perform operations on these
endpoints, and enable the GSI wrapper in DWC3 controller.
This allows a function driver to use GSI based h/w
accelerated data path.

Change-Id: I62688c70a04b1fdab3e522e0af759ebab69d92d7
Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
This commit is contained in:
Hemant Kumar 2015-10-09 19:36:21 -07:00 committed by David Keitel
parent b4fc5ee2ec
commit 972f9346a1

View file

@ -24,9 +24,81 @@
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/usb/ch9.h>
#include <linux/pm_runtime.h>
struct usb_ep;
enum ep_type {
EP_TYPE_NORMAL = 0,
EP_TYPE_GSI,
};
/* Operations codes for GSI enabled EPs */
enum gsi_ep_op {
GSI_EP_OP_CONFIG = 0,
GSI_EP_OP_STARTXFER,
GSI_EP_OP_STORE_DBL_INFO,
GSI_EP_OP_ENABLE_GSI,
GSI_EP_OP_UPDATEXFER,
GSI_EP_OP_RING_IN_DB,
GSI_EP_OP_ENDXFER,
GSI_EP_OP_GET_CH_INFO,
GSI_EP_OP_GET_XFER_IDX,
GSI_EP_OP_PREPARE_TRBS,
GSI_EP_OP_FREE_TRBS,
GSI_EP_OP_SET_CLR_BLOCK_DBL,
GSI_EP_OP_CHECK_FOR_SUSPEND,
};
/*
* @buf_base_addr: Base pointer to buffer allocated for each GSI enabled EP.
* TRBs point to buffers that are split from this pool. The size of the
* buffer is num_bufs times buf_len. num_bufs and buf_len are determined
based on desired performance and aggregation size.
* @dma: DMA address corresponding to buf_base_addr.
* @num_bufs: Number of buffers associated with the GSI enabled EP. This
* corresponds to the number of non-zlp TRBs allocated for the EP.
* The value is determined based on desired performance for the EP.
* @buf_len: Size of each individual buffer is determined based on aggregation
* negotiated as per the protocol. In case of no aggregation supported by
* the protocol, we use default values.
*/
struct usb_gsi_request {
void *buf_base_addr;
dma_addr_t dma;
size_t num_bufs;
size_t buf_len;
};
/*
* @last_trb_addr: Address (LSB - based on alignment restrictions) of
* last TRB in queue. Used to identify rollover case.
* @const_buffer_size: TRB buffer size in KB (similar to IPA aggregation
* configuration). Must be aligned to Max USB Packet Size.
* Should be 1 <= const_buffer_size <= 31.
* @depcmd_low_addr: Used by GSI hardware to write "Update Transfer" cmd
* @depcmd_hi_addr: Used to write "Update Transfer" command.
* @gevntcount_low_addr: GEVNCOUNT low address for GSI hardware to read and
* clear processed events.
* @gevntcount_hi_addr: GEVNCOUNT high address.
* @xfer_ring_len: length of transfer ring in bytes (must be integral
* multiple of TRB size - 16B for xDCI).
* @xfer_ring_base_addr: physical base address of transfer ring. Address must
* be aligned to xfer_ring_len rounded to power of two.
* @ch_req: Used to pass request specific info for certain operations on GSI EP
*/
struct gsi_channel_info {
u16 last_trb_addr;
u8 const_buffer_size;
u32 depcmd_low_addr;
u8 depcmd_hi_addr;
u32 gevntcount_low_addr;
u8 gevntcount_hi_addr;
u16 xfer_ring_len;
u64 xfer_ring_base_addr;
struct usb_gsi_request *ch_req;
};
/**
* struct usb_request - describes one i/o request
* @buf: Buffer used for data. Always provide this; some controllers
@ -138,6 +210,8 @@ struct usb_ep_ops {
int (*fifo_status) (struct usb_ep *ep);
void (*fifo_flush) (struct usb_ep *ep);
int (*gsi_ep_op)(struct usb_ep *ep, void *op_data,
enum gsi_ep_op op);
};
/**
@ -201,6 +275,10 @@ struct usb_ep_caps {
* enabled and remains valid until the endpoint is disabled.
* @comp_desc: In case of SuperSpeed support, this is the endpoint companion
* descriptor that is used to configure the endpoint
* @ep_type: Used to specify type of EP eg. normal vs h/w accelerated.
* @ep_intr_num: Interrupter number for EP.
* @endless: In case where endless transfer is being initiated, this is set
* to disable usb event interrupt for few events.
*
* the bus controller driver lists all the general purpose endpoints in
* gadget->ep_list. the control endpoint (gadget->ep0) is not in that list,
@ -224,6 +302,9 @@ struct usb_ep {
u8 address;
const struct usb_endpoint_descriptor *desc;
const struct usb_ss_ep_comp_descriptor *comp_desc;
enum ep_type ep_type;
u8 ep_intr_num;
bool endless;
};
/*-------------------------------------------------------------------------*/
@ -526,7 +607,20 @@ static inline void usb_ep_fifo_flush(struct usb_ep *ep)
ep->ops->fifo_flush(ep);
}
/**
* usb_gsi_ep_op - performs operation on GSI accelerated EP based on EP op code
*
* Operations such as EP configuration, TRB allocation, StartXfer etc.
* See gsi_ep_op for more details.
*/
static inline int usb_gsi_ep_op(struct usb_ep *ep,
struct usb_gsi_request *req, enum gsi_ep_op op)
{
if (ep->ops->gsi_ep_op)
return ep->ops->gsi_ep_op(ep, req, op);
return -EOPNOTSUPP;
}
/*-------------------------------------------------------------------------*/
struct usb_dcd_config_params {
@ -1010,6 +1104,129 @@ static inline int usb_gadget_activate(struct usb_gadget *gadget)
return 0;
}
/**
* usb_gadget_autopm_get - increment PM-usage counter of usb gadget's parent
* device.
* @gadget: usb gadget whose parent device counter is incremented
*
* This routine should be called by function driver when it wants to use
* gadget's parent device and needs to guarantee that it is not suspended. In
* addition, the routine prevents subsequent autosuspends of gadget's parent
* device. However if the autoresume fails then the counter is re-decremented.
*
* This routine can run only in process context.
*/
static inline int usb_gadget_autopm_get(struct usb_gadget *gadget)
{
int status = -ENODEV;
if (!gadget || !gadget->dev.parent)
return status;
status = pm_runtime_get_sync(gadget->dev.parent);
if (status < 0)
pm_runtime_put_sync(gadget->dev.parent);
if (status > 0)
status = 0;
return status;
}
/**
* usb_gadget_autopm_get_async - increment PM-usage counter of usb gadget's
* parent device.
* @gadget: usb gadget whose parent device counter is incremented
*
* This routine increments @gadget parent device PM usage counter and queue an
* autoresume request if the device is suspended. It does not autoresume device
* directly (it only queues a request). After a successful call, the device may
* not yet be resumed.
*
* This routine can run in atomic context.
*/
static inline int usb_gadget_autopm_get_async(struct usb_gadget *gadget)
{
int status = -ENODEV;
if (!gadget || !gadget->dev.parent)
return status;
status = pm_runtime_get(gadget->dev.parent);
if (status < 0 && status != -EINPROGRESS)
pm_runtime_put_noidle(gadget->dev.parent);
if (status > 0 || status == -EINPROGRESS)
status = 0;
return status;
}
/**
* usb_gadget_autopm_get_noresume - increment PM-usage counter of usb gadget's
* parent device.
* @gadget: usb gadget whose parent device counter is incremented
*
* This routine increments PM-usage count of @gadget parent device but does not
* carry out an autoresume.
*
* This routine can run in atomic context.
*/
static inline void usb_gadget_autopm_get_noresume(struct usb_gadget *gadget)
{
if (gadget && gadget->dev.parent)
pm_runtime_get_noresume(gadget->dev.parent);
}
/**
* usb_gadget_autopm_put - decrement PM-usage counter of usb gadget's parent
* device.
* @gadget: usb gadget whose parent device counter is decremented.
*
* This routine should be called by function driver when it is finished using
* @gadget parent device and wants to allow it to autosuspend. It decrements
* PM-usage counter of @gadget parent device, when the counter reaches 0, a
* delayed autosuspend request is attempted.
*
* This routine can run only in process context.
*/
static inline void usb_gadget_autopm_put(struct usb_gadget *gadget)
{
if (gadget && gadget->dev.parent)
pm_runtime_put_sync(gadget->dev.parent);
}
/**
* usb_gadget_autopm_put_async - decrement PM-usage counter of usb gadget's
* parent device.
* @gadget: usb gadget whose parent device counter is decremented.
*
* This routine decrements PM-usage counter of @gadget parent device and
* schedules a delayed autosuspend request if the counter is <= 0.
*
* This routine can run in atomic context.
*/
static inline void usb_gadget_autopm_put_async(struct usb_gadget *gadget)
{
if (gadget && gadget->dev.parent)
pm_runtime_put(gadget->dev.parent);
}
/**
* usb_gadget_autopm_put_no_suspend - decrement PM-usage counter of usb gadget
's
* parent device.
* @gadget: usb gadget whose parent device counter is decremented.
*
* This routine decrements PM-usage counter of @gadget parent device but does
* not carry out an autosuspend.
*
* This routine can run in atomic context.
*/
static inline void usb_gadget_autopm_put_no_suspend(struct usb_gadget *gadget)
{
if (gadget && gadget->dev.parent)
pm_runtime_put_noidle(gadget->dev.parent);
}
/*-------------------------------------------------------------------------*/
/**