#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/interrupt.h>
#include <linux/property.h>
#include <linux/dmapool.h>
#include <linux/iopoll.h>
#include "cdns2-gadget.h"
#include "cdns2-trace.h"
static void set_reg_bit_32(void __iomem *ptr, u32 mask)
{
mask = readl(ptr) | mask;
writel(mask, ptr);
}
static void clear_reg_bit_32(void __iomem *ptr, u32 mask)
{
mask = readl(ptr) & ~mask;
writel(mask, ptr);
}
static void clear_reg_bit_8(void __iomem *ptr, u8 mask)
{
mask = readb(ptr) & ~mask;
writeb(mask, ptr);
}
void set_reg_bit_8(void __iomem *ptr, u8 mask)
{
mask = readb(ptr) | mask;
writeb(mask, ptr);
}
static int cdns2_get_dma_pos(struct cdns2_device *pdev,
struct cdns2_endpoint *pep)
{
int dma_index;
dma_index = readl(&pdev->adma_regs->ep_traddr) - pep->ring.dma;
return dma_index / TRB_SIZE;
}
struct cdns2_request *cdns2_next_preq(struct list_head *list)
{
return list_first_entry_or_null(list, struct cdns2_request, list);
}
void cdns2_select_ep(struct cdns2_device *pdev, u32 ep)
{
if (pdev->selected_ep == ep)
return;
pdev->selected_ep = ep;
writel(ep, &pdev->adma_regs->ep_sel);
}
dma_addr_t cdns2_trb_virt_to_dma(struct cdns2_endpoint *pep,
struct cdns2_trb *trb)
{
u32 offset = (char *)trb - (char *)pep->ring.trbs;
return pep->ring.dma + offset;
}
static void cdns2_free_tr_segment(struct cdns2_endpoint *pep)
{
struct cdns2_device *pdev = pep->pdev;
struct cdns2_ring *ring = &pep->ring;
if (pep->ring.trbs) {
dma_pool_free(pdev->eps_dma_pool, ring->trbs, ring->dma);
memset(ring, 0, sizeof(*ring));
}
}
static int cdns2_alloc_tr_segment(struct cdns2_endpoint *pep)
{
struct cdns2_device *pdev = pep->pdev;
struct cdns2_trb *link_trb;
struct cdns2_ring *ring;
ring = &pep->ring;
if (!ring->trbs) {
ring->trbs = dma_pool_alloc(pdev->eps_dma_pool,
GFP_DMA32 | GFP_ATOMIC,
&ring->dma);
if (!ring->trbs)
return -ENOMEM;
}
memset(ring->trbs, 0, TR_SEG_SIZE);
if (!pep->num)
return 0;
link_trb = (ring->trbs + (TRBS_PER_SEGMENT - 1));
link_trb->buffer = cpu_to_le32(TRB_BUFFER(ring->dma));
link_trb->control = cpu_to_le32(TRB_CYCLE | TRB_TYPE(TRB_LINK) |
TRB_TOGGLE);
return 0;
}
static void cdns2_ep_stall_flush(struct cdns2_endpoint *pep)
{
struct cdns2_device *pdev = pep->pdev;
int val;
trace_cdns2_ep_halt(pep, 1, 1);
writel(DMA_EP_CMD_DFLUSH, &pdev->adma_regs->ep_cmd);
readl_poll_timeout_atomic(&pdev->adma_regs->ep_cmd, val,
!(val & DMA_EP_CMD_DFLUSH), 1, 1000);
pep->ep_state |= EP_STALLED;
pep->ep_state &= ~EP_STALL_PENDING;
}
static void cdns2_ep_inc_trb(int *index, u8 *cs, int trb_in_seg)
{
(*index)++;
if (*index == (trb_in_seg - 1)) {
*index = 0;
*cs ^= 1;
}
}
static void cdns2_ep_inc_enq(struct cdns2_ring *ring)
{
ring->free_trbs--;
cdns2_ep_inc_trb(&ring->enqueue, &ring->pcs, TRBS_PER_SEGMENT);
}
static void cdns2_ep_inc_deq(struct cdns2_ring *ring)
{
ring->free_trbs++;
cdns2_ep_inc_trb(&ring->dequeue, &ring->ccs, TRBS_PER_SEGMENT);
}
static void cdns2_enable_l1(struct cdns2_device *pdev, int enable)
{
if (enable) {
clear_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_LPMNYET);
writeb(LPMCLOCK_SLEEP_ENTRY, &pdev->usb_regs->lpmclock);
} else {
set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_LPMNYET);
}
}
static enum usb_device_speed cdns2_get_speed(struct cdns2_device *pdev)
{
u8 speed = readb(&pdev->usb_regs->speedctrl);
if (speed & SPEEDCTRL_HS)
return USB_SPEED_HIGH;
else if (speed & SPEEDCTRL_FS)
return USB_SPEED_FULL;
return USB_SPEED_UNKNOWN;
}
static struct cdns2_trb *cdns2_next_trb(struct cdns2_endpoint *pep,
struct cdns2_trb *trb)
{
if (trb == (pep->ring.trbs + (TRBS_PER_SEGMENT - 1)))
return pep->ring.trbs;
else
return ++trb;
}
void cdns2_gadget_giveback(struct cdns2_endpoint *pep,
struct cdns2_request *preq,
int status)
{
struct usb_request *request = &preq->request;
struct cdns2_device *pdev = pep->pdev;
list_del_init(&preq->list);
if (request->status == -EINPROGRESS)
request->status = status;
usb_gadget_unmap_request_by_dev(pdev->dev, request, pep->dir);
preq->finished_trb = 0;
trace_cdns2_request_giveback(preq);
if (request->complete) {
spin_unlock(&pdev->lock);
usb_gadget_giveback_request(&pep->endpoint, request);
spin_lock(&pdev->lock);
}
if (request->buf == pdev->zlp_buf)
cdns2_gadget_ep_free_request(&pep->endpoint, request);
}
static void cdns2_wa1_restore_cycle_bit(struct cdns2_endpoint *pep)
{
if (pep->wa1_set) {
trace_cdns2_wa1(pep, "restore cycle bit");
pep->wa1_set = 0;
pep->wa1_trb_index = 0xFFFF;
if (pep->wa1_cycle_bit)
pep->wa1_trb->control |= cpu_to_le32(0x1);
else
pep->wa1_trb->control &= cpu_to_le32(~0x1);
}
}
static int cdns2_wa1_update_guard(struct cdns2_endpoint *pep,
struct cdns2_trb *trb)
{
struct cdns2_device *pdev = pep->pdev;
if (!pep->wa1_set) {
u32 doorbell;
doorbell = !!(readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY);
if (doorbell) {
pep->wa1_cycle_bit = pep->ring.pcs ? TRB_CYCLE : 0;
pep->wa1_set = 1;
pep->wa1_trb = trb;
pep->wa1_trb_index = pep->ring.enqueue;
trace_cdns2_wa1(pep, "set guard");
return 0;
}
}
return 1;
}
static void cdns2_wa1_tray_restore_cycle_bit(struct cdns2_device *pdev,
struct cdns2_endpoint *pep)
{
int dma_index;
u32 doorbell;
doorbell = !!(readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY);
dma_index = cdns2_get_dma_pos(pdev, pep);
if (!doorbell || dma_index != pep->wa1_trb_index)
cdns2_wa1_restore_cycle_bit(pep);
}
static int cdns2_prepare_ring(struct cdns2_device *pdev,
struct cdns2_endpoint *pep,
int num_trbs)
{
struct cdns2_trb *link_trb = NULL;
int doorbell, dma_index;
struct cdns2_ring *ring;
u32 ch_bit = 0;
ring = &pep->ring;
if (num_trbs > ring->free_trbs) {
pep->ep_state |= EP_RING_FULL;
trace_cdns2_no_room_on_ring("Ring full\n");
return -ENOBUFS;
}
if ((ring->enqueue + num_trbs) >= (TRBS_PER_SEGMENT - 1)) {
doorbell = !!(readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY);
dma_index = cdns2_get_dma_pos(pdev, pep);
if (doorbell && dma_index == TRBS_PER_SEGMENT - 1) {
pep->ep_state |= EP_DEFERRED_DRDY;
return -ENOBUFS;
}
link_trb = ring->trbs + (TRBS_PER_SEGMENT - 1);
if (pep->type == USB_ENDPOINT_XFER_ISOC || TRBS_PER_SEGMENT > 2)
ch_bit = TRB_CHAIN;
link_trb->control = cpu_to_le32(((ring->pcs) ? TRB_CYCLE : 0) |
TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit);
}
return 0;
}
static void cdns2_dbg_request_trbs(struct cdns2_endpoint *pep,
struct cdns2_request *preq)
{
struct cdns2_trb *link_trb = pep->ring.trbs + (TRBS_PER_SEGMENT - 1);
struct cdns2_trb *trb = preq->trb;
int num_trbs = preq->num_of_trb;
int i = 0;
while (i < num_trbs) {
trace_cdns2_queue_trb(pep, trb + i);
if (trb + i == link_trb) {
trb = pep->ring.trbs;
num_trbs = num_trbs - i;
i = 0;
} else {
i++;
}
}
}
static unsigned int cdns2_count_trbs(struct cdns2_endpoint *pep,
u64 addr, u64 len)
{
unsigned int num_trbs = 1;
if (pep->type == USB_ENDPOINT_XFER_ISOC) {
num_trbs = DIV_ROUND_UP(len +
(addr & (TRB_MAX_ISO_BUFF_SIZE - 1)),
TRB_MAX_ISO_BUFF_SIZE);
if (pep->interval > 1)
num_trbs = pep->dir ? num_trbs * pep->interval : 1;
} else if (pep->dir) {
num_trbs++;
}
return num_trbs;
}
static unsigned int cdns2_count_sg_trbs(struct cdns2_endpoint *pep,
struct usb_request *req)
{
unsigned int i, len, full_len, num_trbs = 0;
struct scatterlist *sg;
int trb_len = 0;
full_len = req->length;
for_each_sg(req->sg, sg, req->num_sgs, i) {
len = sg_dma_len(sg);
num_trbs += cdns2_count_trbs(pep, sg_dma_address(sg), len);
len = min(len, full_len);
if (pep->type == USB_ENDPOINT_XFER_ISOC) {
u8 temp;
trb_len += len;
temp = trb_len >> 10;
if (temp) {
if (trb_len % 1024)
num_trbs = num_trbs + temp;
else
num_trbs = num_trbs + temp - 1;
trb_len = trb_len - (temp << 10);
}
}
full_len -= len;
if (full_len == 0)
break;
}
return num_trbs;
}
static void cdsn2_isoc_burst_opt(struct cdns2_device *pdev)
{
int axi_burst_option[] = {1, 2, 4, 8, 16, 32, 64, 128};
int best_burst;
int array_size;
int opt_burst;
int trb_size;
int i, j;
array_size = ARRAY_SIZE(axi_burst_option);
for (i = 0; i <= MAX_ISO_SIZE; i++) {
trb_size = i / 4;
best_burst = trb_size ? trb_size : 1;
for (j = 0; j < array_size; j++) {
opt_burst = trb_size / axi_burst_option[j];
opt_burst += trb_size % axi_burst_option[j];
if (opt_burst < best_burst) {
best_burst = opt_burst;
pdev->burst_opt[i] = axi_burst_option[j];
}
}
}
}
static void cdns2_ep_tx_isoc(struct cdns2_endpoint *pep,
struct cdns2_request *preq,
int num_trbs)
{
struct scatterlist *sg = NULL;
u32 remaining_packet_size = 0;
struct cdns2_trb *trb;
bool first_trb = true;
dma_addr_t trb_dma;
u32 trb_buff_len;
u32 block_length;
int td_idx = 0;
int split_size;
u32 full_len;
int enqd_len;
int sent_len;
int sg_iter;
u32 control;
int num_tds;
u32 length;
num_tds = pep->dir ? pep->interval : 1;
split_size = preq->request.num_sgs ? 1024 : 3072;
for (td_idx = 0; td_idx < num_tds; td_idx++) {
if (preq->request.num_sgs) {
sg = preq->request.sg;
trb_dma = sg_dma_address(sg);
block_length = sg_dma_len(sg);
} else {
trb_dma = preq->request.dma;
block_length = preq->request.length;
}
full_len = preq->request.length;
sg_iter = preq->request.num_sgs ? preq->request.num_sgs : 1;
remaining_packet_size = split_size;
for (enqd_len = 0; enqd_len < full_len;
enqd_len += trb_buff_len) {
if (remaining_packet_size == 0)
remaining_packet_size = split_size;
trb_buff_len = TRB_BUFF_LEN_UP_TO_BOUNDARY(trb_dma);
trb_buff_len = min(trb_buff_len, remaining_packet_size);
trb_buff_len = min(trb_buff_len, block_length);
if (trb_buff_len > full_len - enqd_len)
trb_buff_len = full_len - enqd_len;
control = TRB_TYPE(TRB_NORMAL);
if (enqd_len + trb_buff_len >= full_len || !pep->dir)
control |= TRB_IOC | TRB_ISP;
if (first_trb) {
first_trb = false;
if (pep->ring.pcs == 0)
control |= TRB_CYCLE;
} else {
control |= pep->ring.pcs;
}
if (enqd_len + trb_buff_len < full_len)
control |= TRB_CHAIN;
length = TRB_LEN(trb_buff_len) |
TRB_BURST(pep->pdev->burst_opt[trb_buff_len]);
trb = pep->ring.trbs + pep->ring.enqueue;
trb->buffer = cpu_to_le32(TRB_BUFFER(trb_dma));
trb->length = cpu_to_le32(length);
trb->control = cpu_to_le32(control);
trb_dma += trb_buff_len;
sent_len = trb_buff_len;
if (sg && sent_len >= block_length) {
--sg_iter;
sent_len -= block_length;
if (sg_iter != 0) {
sg = sg_next(sg);
trb_dma = sg_dma_address(sg);
block_length = sg_dma_len(sg);
}
}
remaining_packet_size -= trb_buff_len;
block_length -= sent_len;
preq->end_trb = pep->ring.enqueue;
cdns2_ep_inc_enq(&pep->ring);
}
}
}
static void cdns2_ep_tx_bulk(struct cdns2_endpoint *pep,
struct cdns2_request *preq,
int trbs_per_td)
{
struct scatterlist *sg = NULL;
struct cdns2_ring *ring;
struct cdns2_trb *trb;
dma_addr_t trb_dma;
int sg_iter = 0;
u32 control;
u32 length;
if (preq->request.num_sgs) {
sg = preq->request.sg;
trb_dma = sg_dma_address(sg);
length = sg_dma_len(sg);
} else {
trb_dma = preq->request.dma;
length = preq->request.length;
}
ring = &pep->ring;
for (sg_iter = 0; sg_iter < trbs_per_td; sg_iter++) {
control = TRB_TYPE(TRB_NORMAL) | ring->pcs | TRB_ISP;
trb = pep->ring.trbs + ring->enqueue;
if (pep->dir && sg_iter == trbs_per_td - 1) {
preq->end_trb = ring->enqueue;
control = ring->pcs | TRB_TYPE(TRB_LINK) | TRB_CHAIN
| TRB_IOC;
cdns2_ep_inc_enq(&pep->ring);
if (ring->enqueue == 0)
control |= TRB_TOGGLE;
trb->buffer = cpu_to_le32(pep->ring.dma +
(ring->enqueue * TRB_SIZE));
trb->length = 0;
trb->control = cpu_to_le32(control);
break;
}
if (sg_iter == 0)
control = control ^ TRB_CYCLE;
if (sg_iter == (trbs_per_td - (pep->dir ? 2 : 1)))
control |= TRB_IOC;
else
control |= TRB_CHAIN;
trb->buffer = cpu_to_le32(trb_dma);
trb->length = cpu_to_le32(TRB_BURST(pep->trb_burst_size) |
TRB_LEN(length));
trb->control = cpu_to_le32(control);
if (sg && sg_iter < (trbs_per_td - 1)) {
sg = sg_next(sg);
trb_dma = sg_dma_address(sg);
length = sg_dma_len(sg);
}
preq->end_trb = ring->enqueue;
cdns2_ep_inc_enq(&pep->ring);
}
}
static void cdns2_set_drdy(struct cdns2_device *pdev,
struct cdns2_endpoint *pep)
{
trace_cdns2_ring(pep);
dma_wmb();
writel(DMA_EP_STS_TRBERR | DMA_EP_STS_DESCMIS,
&pdev->adma_regs->ep_sts);
writel(DMA_EP_CMD_DRDY, &pdev->adma_regs->ep_cmd);
if (readl(&pdev->adma_regs->ep_sts) & DMA_EP_STS_TRBERR) {
writel(DMA_EP_STS_TRBERR, &pdev->adma_regs->ep_sts);
writel(DMA_EP_CMD_DRDY, &pdev->adma_regs->ep_cmd);
}
trace_cdns2_doorbell_epx(pep, readl(&pdev->adma_regs->ep_traddr));
}
static int cdns2_prepare_first_isoc_transfer(struct cdns2_device *pdev,
struct cdns2_endpoint *pep)
{
struct cdns2_trb *trb;
u32 buffer;
u8 hw_ccs;
if ((readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY))
return -EBUSY;
if (!pep->dir) {
set_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE);
writel(pep->ring.dma + pep->ring.dequeue,
&pdev->adma_regs->ep_traddr);
return 0;
}
buffer = pep->ring.dma + pep->ring.dequeue * TRB_SIZE;
hw_ccs = !!DMA_EP_STS_CCS(readl(&pdev->adma_regs->ep_sts));
trb = &pep->ring.trbs[TRBS_PER_SEGMENT];
trb->length = 0;
trb->buffer = cpu_to_le32(TRB_BUFFER(buffer));
trb->control = cpu_to_le32((hw_ccs ? TRB_CYCLE : 0) | TRB_TYPE(TRB_NORMAL));
trb++;
trb->length = 0;
trb->buffer = cpu_to_le32(TRB_BUFFER(buffer));
trb->control = cpu_to_le32((hw_ccs ? TRB_CYCLE : 0) |
TRB_TYPE(TRB_LINK) | TRB_CHAIN);
if (hw_ccs != pep->ring.ccs)
trb->control |= cpu_to_le32(TRB_TOGGLE);
set_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE);
writel(pep->ring.dma + (TRBS_PER_SEGMENT * TRB_SIZE),
&pdev->adma_regs->ep_traddr);
return 0;
}
static int cdns2_ep_run_transfer(struct cdns2_endpoint *pep,
struct cdns2_request *preq)
{
struct cdns2_device *pdev = pep->pdev;
struct cdns2_ring *ring;
u32 togle_pcs = 1;
int num_trbs;
int ret;
cdns2_select_ep(pdev, pep->endpoint.address);
if (preq->request.sg)
num_trbs = cdns2_count_sg_trbs(pep, &preq->request);
else
num_trbs = cdns2_count_trbs(pep, preq->request.dma,
preq->request.length);
ret = cdns2_prepare_ring(pdev, pep, num_trbs);
if (ret)
return ret;
ring = &pep->ring;
preq->start_trb = ring->enqueue;
preq->trb = ring->trbs + ring->enqueue;
if (usb_endpoint_xfer_isoc(pep->endpoint.desc)) {
cdns2_ep_tx_isoc(pep, preq, num_trbs);
} else {
togle_pcs = cdns2_wa1_update_guard(pep, ring->trbs + ring->enqueue);
cdns2_ep_tx_bulk(pep, preq, num_trbs);
}
preq->num_of_trb = num_trbs;
dma_wmb();
if (togle_pcs)
preq->trb->control = preq->trb->control ^ cpu_to_le32(1);
cdns2_wa1_tray_restore_cycle_bit(pdev, pep);
cdns2_dbg_request_trbs(pep, preq);
if (!pep->wa1_set && !(pep->ep_state & EP_STALLED) && !pep->skip) {
if (pep->type == USB_ENDPOINT_XFER_ISOC) {
ret = cdns2_prepare_first_isoc_transfer(pdev, pep);
if (ret)
return 0;
}
cdns2_set_drdy(pdev, pep);
}
return 0;
}
static int cdns2_start_all_request(struct cdns2_device *pdev,
struct cdns2_endpoint *pep)
{
struct cdns2_request *preq;
int ret;
while (!list_empty(&pep->deferred_list)) {
preq = cdns2_next_preq(&pep->deferred_list);
ret = cdns2_ep_run_transfer(pep, preq);
if (ret)
return ret;
list_move_tail(&preq->list, &pep->pending_list);
}
pep->ep_state &= ~EP_RING_FULL;
return 0;
}
static bool cdns2_trb_handled(struct cdns2_endpoint *pep,
struct cdns2_request *preq)
{
struct cdns2_device *pdev = pep->pdev;
struct cdns2_ring *ring;
struct cdns2_trb *trb;
int current_index = 0;
int handled = 0;
int doorbell;
ring = &pep->ring;
current_index = cdns2_get_dma_pos(pdev, pep);
doorbell = !!(readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY);
if (current_index >= TRBS_PER_SEGMENT)
goto finish;
if (preq->start_trb < preq->end_trb) {
if (ring->dequeue > preq->end_trb)
goto finish;
if (ring->dequeue < preq->start_trb)
goto finish;
}
if (preq->start_trb > preq->end_trb && ring->dequeue > preq->end_trb &&
ring->dequeue < preq->start_trb)
goto finish;
if (preq->start_trb == preq->end_trb && ring->dequeue != preq->end_trb)
goto finish;
trb = &ring->trbs[ring->dequeue];
if ((le32_to_cpu(trb->control) & TRB_CYCLE) != ring->ccs)
goto finish;
if (doorbell == 1 && current_index == ring->dequeue)
goto finish;
if (TRBS_PER_SEGMENT == 2 && pep->type != USB_ENDPOINT_XFER_ISOC) {
handled = 1;
goto finish;
}
if (ring->enqueue == ring->dequeue &&
ring->free_trbs == 0) {
handled = 1;
} else if (ring->dequeue < current_index) {
if ((current_index == (TRBS_PER_SEGMENT - 1)) &&
!ring->dequeue)
goto finish;
handled = 1;
} else if (ring->dequeue > current_index) {
handled = 1;
}
finish:
trace_cdns2_request_handled(preq, current_index, handled);
return handled;
}
static void cdns2_skip_isoc_td(struct cdns2_device *pdev,
struct cdns2_endpoint *pep,
struct cdns2_request *preq)
{
struct cdns2_trb *trb;
int i;
trb = pep->ring.trbs + pep->ring.dequeue;
for (i = preq->finished_trb ; i < preq->num_of_trb; i++) {
preq->finished_trb++;
trace_cdns2_complete_trb(pep, trb);
cdns2_ep_inc_deq(&pep->ring);
trb = cdns2_next_trb(pep, trb);
}
cdns2_gadget_giveback(pep, preq, 0);
cdns2_prepare_first_isoc_transfer(pdev, pep);
pep->skip = false;
cdns2_set_drdy(pdev, pep);
}
static void cdns2_transfer_completed(struct cdns2_device *pdev,
struct cdns2_endpoint *pep)
{
struct cdns2_request *preq = NULL;
bool request_handled = false;
struct cdns2_trb *trb;
while (!list_empty(&pep->pending_list)) {
preq = cdns2_next_preq(&pep->pending_list);
trb = pep->ring.trbs + pep->ring.dequeue;
while (TRB_FIELD_TO_TYPE(le32_to_cpu(trb->control)) == TRB_LINK &&
le32_to_cpu(trb->length)) {
trace_cdns2_complete_trb(pep, trb);
cdns2_ep_inc_deq(&pep->ring);
trb = pep->ring.trbs + pep->ring.dequeue;
}
cdns2_select_ep(pdev, pep->endpoint.address);
while (cdns2_trb_handled(pep, preq)) {
preq->finished_trb++;
if (preq->finished_trb >= preq->num_of_trb)
request_handled = true;
trb = pep->ring.trbs + pep->ring.dequeue;
trace_cdns2_complete_trb(pep, trb);
if (pep->dir && pep->type == USB_ENDPOINT_XFER_ISOC)
preq->request.actual = preq->request.length;
else
preq->request.actual +=
TRB_LEN(le32_to_cpu(trb->length));
cdns2_ep_inc_deq(&pep->ring);
}
if (request_handled) {
cdns2_gadget_giveback(pep, preq, 0);
request_handled = false;
} else {
goto prepare_next_td;
}
if (pep->type != USB_ENDPOINT_XFER_ISOC &&
TRBS_PER_SEGMENT == 2)
break;
}
prepare_next_td:
if (pep->skip && preq)
cdns2_skip_isoc_td(pdev, pep, preq);
if (!(pep->ep_state & EP_STALLED) &&
!(pep->ep_state & EP_STALL_PENDING))
cdns2_start_all_request(pdev, pep);
}
static void cdns2_wakeup(struct cdns2_device *pdev)
{
if (!pdev->may_wakeup)
return;
set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_SIGRSUME);
}
static void cdns2_rearm_transfer(struct cdns2_endpoint *pep, u8 rearm)
{
struct cdns2_device *pdev = pep->pdev;
cdns2_wa1_restore_cycle_bit(pep);
if (rearm) {
trace_cdns2_ring(pep);
dma_wmb();
writel(DMA_EP_CMD_DRDY, &pdev->adma_regs->ep_cmd);
cdns2_wakeup(pdev);
trace_cdns2_doorbell_epx(pep,
readl(&pdev->adma_regs->ep_traddr));
}
}
static void cdns2_handle_epx_interrupt(struct cdns2_endpoint *pep)
{
struct cdns2_device *pdev = pep->pdev;
u8 isoerror = 0;
u32 ep_sts_reg;
u32 val;
cdns2_select_ep(pdev, pep->endpoint.address);
trace_cdns2_epx_irq(pdev, pep);
ep_sts_reg = readl(&pdev->adma_regs->ep_sts);
writel(ep_sts_reg, &pdev->adma_regs->ep_sts);
if (pep->type == USB_ENDPOINT_XFER_ISOC) {
u8 mult;
u8 cs;
mult = USB_EP_MAXP_MULT(pep->endpoint.desc->wMaxPacketSize);
cs = pep->dir ? readb(&pdev->epx_regs->ep[pep->num - 1].txcs) :
readb(&pdev->epx_regs->ep[pep->num - 1].rxcs);
if (mult > 0)
isoerror = EPX_CS_ERR(cs);
}
if ((ep_sts_reg & DMA_EP_STS_ISOERR) || isoerror) {
clear_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE);
readl_poll_timeout_atomic(&pdev->adma_regs->ep_sts, val,
!(val & DMA_EP_STS_DBUSY), 1, 125);
writel(DMA_EP_CMD_DFLUSH, &pep->pdev->adma_regs->ep_cmd);
readl_poll_timeout_atomic(&pep->pdev->adma_regs->ep_cmd, val,
!(val & DMA_EP_CMD_DFLUSH), 1, 10);
pep->skip = true;
}
if (ep_sts_reg & DMA_EP_STS_TRBERR || pep->skip) {
if (pep->ep_state & EP_STALL_PENDING &&
!(ep_sts_reg & DMA_EP_STS_DESCMIS))
cdns2_ep_stall_flush(pep);
if (pep->type == USB_ENDPOINT_XFER_ISOC && !pep->wa1_set) {
if (!pep->dir)
clear_reg_bit_32(&pdev->adma_regs->ep_cfg,
DMA_EP_CFG_ENABLE);
cdns2_transfer_completed(pdev, pep);
if (pep->ep_state & EP_DEFERRED_DRDY) {
pep->ep_state &= ~EP_DEFERRED_DRDY;
cdns2_set_drdy(pdev, pep);
}
return;
}
cdns2_transfer_completed(pdev, pep);
if (!(pep->ep_state & EP_STALLED) &&
!(pep->ep_state & EP_STALL_PENDING)) {
if (pep->ep_state & EP_DEFERRED_DRDY) {
pep->ep_state &= ~EP_DEFERRED_DRDY;
cdns2_start_all_request(pdev, pep);
} else {
cdns2_rearm_transfer(pep, pep->wa1_set);
}
}
return;
}
if ((ep_sts_reg & DMA_EP_STS_IOC) || (ep_sts_reg & DMA_EP_STS_ISP))
cdns2_transfer_completed(pdev, pep);
}
static void cdns2_disconnect_gadget(struct cdns2_device *pdev)
{
if (pdev->gadget_driver && pdev->gadget_driver->disconnect)
pdev->gadget_driver->disconnect(&pdev->gadget);
}
static irqreturn_t cdns2_usb_irq_handler(int irq, void *data)
{
struct cdns2_device *pdev = data;
unsigned long reg_ep_ists;
u8 reg_usb_irq_m;
u8 reg_ext_irq_m;
u8 reg_usb_irq;
u8 reg_ext_irq;
if (pdev->in_lpm)
return IRQ_NONE;
reg_usb_irq_m = readb(&pdev->interrupt_regs->usbien);
reg_ext_irq_m = readb(&pdev->interrupt_regs->extien);
writeb(0, &pdev->interrupt_regs->usbien);
writeb(0, &pdev->interrupt_regs->extien);
writel(0, &pdev->adma_regs->ep_ien);
writel(0, &pdev->adma_regs->ep_sts);
writeb(0, &pdev->interrupt_regs->usbirq);
writeb(0, &pdev->interrupt_regs->extirq);
reg_ep_ists = readl(&pdev->adma_regs->ep_ists);
reg_usb_irq = readb(&pdev->interrupt_regs->usbirq);
reg_ext_irq = readb(&pdev->interrupt_regs->extirq);
if (reg_ep_ists || (reg_usb_irq & reg_usb_irq_m) ||
(reg_ext_irq & reg_ext_irq_m))
return IRQ_WAKE_THREAD;
writeb(USB_IEN_INIT, &pdev->interrupt_regs->usbien);
writeb(EXTIRQ_WAKEUP, &pdev->interrupt_regs->extien);
writel(~0, &pdev->adma_regs->ep_ien);
return IRQ_NONE;
}
static irqreturn_t cdns2_thread_usb_irq_handler(struct cdns2_device *pdev)
{
u8 usb_irq, ext_irq;
int speed;
int i;
ext_irq = readb(&pdev->interrupt_regs->extirq) & EXTIRQ_WAKEUP;
writeb(ext_irq, &pdev->interrupt_regs->extirq);
usb_irq = readb(&pdev->interrupt_regs->usbirq) & USB_IEN_INIT;
writeb(usb_irq, &pdev->interrupt_regs->usbirq);
if (!ext_irq && !usb_irq)
return IRQ_NONE;
trace_cdns2_usb_irq(usb_irq, ext_irq);
if (ext_irq & EXTIRQ_WAKEUP) {
if (pdev->gadget_driver && pdev->gadget_driver->resume) {
spin_unlock(&pdev->lock);
pdev->gadget_driver->resume(&pdev->gadget);
spin_lock(&pdev->lock);
}
}
if (usb_irq & USBIRQ_LPM) {
u8 reg = readb(&pdev->usb_regs->lpmctrl);
if (!(reg & LPMCTRLLH_LPMNYET))
writeb(0, &pdev->usb_regs->sleep_clkgate);
}
if (usb_irq & USBIRQ_SUSPEND) {
if (pdev->gadget_driver && pdev->gadget_driver->suspend) {
spin_unlock(&pdev->lock);
pdev->gadget_driver->suspend(&pdev->gadget);
spin_lock(&pdev->lock);
}
}
if (usb_irq & USBIRQ_URESET) {
if (pdev->gadget_driver) {
pdev->dev_address = 0;
spin_unlock(&pdev->lock);
usb_gadget_udc_reset(&pdev->gadget,
pdev->gadget_driver);
spin_lock(&pdev->lock);
for (i = 0; i < 100; i++) {
mdelay(1);
speed = cdns2_get_speed(pdev);
if (speed == USB_SPEED_HIGH)
break;
}
pdev->gadget.speed = speed;
cdns2_enable_l1(pdev, 0);
cdns2_ep0_config(pdev);
pdev->may_wakeup = 0;
}
}
if (usb_irq & USBIRQ_SUDAV) {
pdev->ep0_stage = CDNS2_SETUP_STAGE;
cdns2_handle_setup_packet(pdev);
}
return IRQ_HANDLED;
}
static irqreturn_t cdns2_thread_irq_handler(int irq, void *data)
{
struct cdns2_device *pdev = data;
unsigned long dma_ep_ists;
unsigned long flags;
unsigned int bit;
local_bh_disable();
spin_lock_irqsave(&pdev->lock, flags);
cdns2_thread_usb_irq_handler(pdev);
dma_ep_ists = readl(&pdev->adma_regs->ep_ists);
if (!dma_ep_ists)
goto unlock;
trace_cdns2_dma_ep_ists(dma_ep_ists);
if (dma_ep_ists & DMA_EP_ISTS_EP_OUT0)
cdns2_handle_ep0_interrupt(pdev, USB_DIR_OUT);
if (dma_ep_ists & DMA_EP_ISTS_EP_IN0)
cdns2_handle_ep0_interrupt(pdev, USB_DIR_IN);
dma_ep_ists &= ~(DMA_EP_ISTS_EP_OUT0 | DMA_EP_ISTS_EP_IN0);
for_each_set_bit(bit, &dma_ep_ists, sizeof(u32) * BITS_PER_BYTE) {
u8 ep_idx = bit > 16 ? (bit - 16) * 2 : (bit * 2) - 1;
cdns2_handle_epx_interrupt(&pdev->eps[ep_idx]);
}
unlock:
writel(~0, &pdev->adma_regs->ep_ien);
writeb(USB_IEN_INIT, &pdev->interrupt_regs->usbien);
writeb(EXTIRQ_WAKEUP, &pdev->interrupt_regs->extien);
spin_unlock_irqrestore(&pdev->lock, flags);
local_bh_enable();
return IRQ_HANDLED;
}
static void cdns2_eps_onchip_buffer_init(struct cdns2_device *pdev)
{
struct cdns2_endpoint *pep;
int min_buf_tx = 0;
int min_buf_rx = 0;
u16 tx_offset = 0;
u16 rx_offset = 0;
int free;
int i;
for (i = 0; i < CDNS2_ENDPOINTS_NUM; i++) {
pep = &pdev->eps[i];
if (!(pep->ep_state & EP_CLAIMED))
continue;
if (pep->dir)
min_buf_tx += pep->buffering;
else
min_buf_rx += pep->buffering;
}
for (i = 0; i < CDNS2_ENDPOINTS_NUM; i++) {
pep = &pdev->eps[i];
if (!(pep->ep_state & EP_CLAIMED))
continue;
if (pep->dir) {
free = pdev->onchip_tx_buf - min_buf_tx;
if (free + pep->buffering >= 4)
free = 4;
else
free = free + pep->buffering;
min_buf_tx = min_buf_tx - pep->buffering + free;
pep->buffering = free;
writel(tx_offset,
&pdev->epx_regs->txstaddr[pep->num - 1]);
pdev->epx_regs->txstaddr[pep->num - 1] = tx_offset;
dev_dbg(pdev->dev, "%s onchip address %04x, buffering: %d\n",
pep->name, tx_offset, pep->buffering);
tx_offset += pep->buffering * 1024;
} else {
free = pdev->onchip_rx_buf - min_buf_rx;
if (free + pep->buffering >= 4)
free = 4;
else
free = free + pep->buffering;
min_buf_rx = min_buf_rx - pep->buffering + free;
pep->buffering = free;
writel(rx_offset,
&pdev->epx_regs->rxstaddr[pep->num - 1]);
dev_dbg(pdev->dev, "%s onchip address %04x, buffering: %d\n",
pep->name, rx_offset, pep->buffering);
rx_offset += pep->buffering * 1024;
}
}
}
static int cdns2_ep_config(struct cdns2_endpoint *pep, bool enable)
{
bool is_iso_ep = (pep->type == USB_ENDPOINT_XFER_ISOC);
struct cdns2_device *pdev = pep->pdev;
u32 max_packet_size;
u8 dir = 0;
u8 ep_cfg;
u8 mult;
u32 val;
int ret;
switch (pep->type) {
case USB_ENDPOINT_XFER_INT:
ep_cfg = EPX_CON_TYPE_INT;
break;
case USB_ENDPOINT_XFER_BULK:
ep_cfg = EPX_CON_TYPE_BULK;
break;
default:
mult = USB_EP_MAXP_MULT(pep->endpoint.desc->wMaxPacketSize);
ep_cfg = mult << EPX_CON_ISOD_SHIFT;
ep_cfg |= EPX_CON_TYPE_ISOC;
if (pep->dir) {
set_reg_bit_8(&pdev->epx_regs->isoautoarm, BIT(pep->num));
set_reg_bit_8(&pdev->epx_regs->isoautodump, BIT(pep->num));
set_reg_bit_8(&pdev->epx_regs->isodctrl, BIT(pep->num));
}
}
switch (pdev->gadget.speed) {
case USB_SPEED_FULL:
max_packet_size = is_iso_ep ? 1023 : 64;
break;
case USB_SPEED_HIGH:
max_packet_size = is_iso_ep ? 1024 : 512;
break;
default:
return -EINVAL;
}
ep_cfg |= (EPX_CON_VAL | (pep->buffering - 1));
if (pep->dir) {
dir = FIFOCTRL_IO_TX;
writew(max_packet_size, &pdev->epx_regs->txmaxpack[pep->num - 1]);
writeb(ep_cfg, &pdev->epx_regs->ep[pep->num - 1].txcon);
} else {
writew(max_packet_size, &pdev->epx_regs->rxmaxpack[pep->num - 1]);
writeb(ep_cfg, &pdev->epx_regs->ep[pep->num - 1].rxcon);
}
writeb(pep->num | dir | FIFOCTRL_FIFOAUTO,
&pdev->usb_regs->fifoctrl);
writeb(pep->num | dir, &pdev->epx_regs->endprst);
writeb(pep->num | ENDPRST_FIFORST | ENDPRST_TOGRST | dir,
&pdev->epx_regs->endprst);
if (max_packet_size == 1024)
pep->trb_burst_size = 128;
else if (max_packet_size >= 512)
pep->trb_burst_size = 64;
else
pep->trb_burst_size = 16;
cdns2_select_ep(pdev, pep->num | pep->dir);
writel(DMA_EP_CMD_EPRST | DMA_EP_CMD_DFLUSH, &pdev->adma_regs->ep_cmd);
ret = readl_poll_timeout_atomic(&pdev->adma_regs->ep_cmd, val,
!(val & (DMA_EP_CMD_DFLUSH |
DMA_EP_CMD_EPRST)),
1, 1000);
if (ret)
return ret;
writel(DMA_EP_STS_TRBERR | DMA_EP_STS_ISOERR, &pdev->adma_regs->ep_sts_en);
if (enable)
writel(DMA_EP_CFG_ENABLE, &pdev->adma_regs->ep_cfg);
trace_cdns2_epx_hw_cfg(pdev, pep);
dev_dbg(pdev->dev, "Configure %s: with MPS: %08x, ep con: %02x\n",
pep->name, max_packet_size, ep_cfg);
return 0;
}
struct usb_request *cdns2_gadget_ep_alloc_request(struct usb_ep *ep,
gfp_t gfp_flags)
{
struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep);
struct cdns2_request *preq;
preq = kzalloc(sizeof(*preq), gfp_flags);
if (!preq)
return NULL;
preq->pep = pep;
trace_cdns2_alloc_request(preq);
return &preq->request;
}
void cdns2_gadget_ep_free_request(struct usb_ep *ep,
struct usb_request *request)
{
struct cdns2_request *preq = to_cdns2_request(request);
trace_cdns2_free_request(preq);
kfree(preq);
}
static int cdns2_gadget_ep_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc)
{
u32 reg = DMA_EP_STS_EN_TRBERREN;
struct cdns2_endpoint *pep;
struct cdns2_device *pdev;
unsigned long flags;
int enable = 1;
int ret = 0;
if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT ||
!desc->wMaxPacketSize) {
return -EINVAL;
}
pep = ep_to_cdns2_ep(ep);
pdev = pep->pdev;
if (dev_WARN_ONCE(pdev->dev, pep->ep_state & EP_ENABLED,
"%s is already enabled\n", pep->name))
return 0;
spin_lock_irqsave(&pdev->lock, flags);
pep->type = usb_endpoint_type(desc);
pep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
if (pdev->gadget.speed == USB_SPEED_FULL)
if (pep->type == USB_ENDPOINT_XFER_INT)
pep->interval = desc->bInterval;
if (pep->interval > ISO_MAX_INTERVAL &&
pep->type == USB_ENDPOINT_XFER_ISOC) {
dev_err(pdev->dev, "ISO period is limited to %d (current: %d)\n",
ISO_MAX_INTERVAL, pep->interval);
ret = -EINVAL;
goto exit;
}
if (pep->type == USB_ENDPOINT_XFER_ISOC && !pep->dir)
enable = 0;
ret = cdns2_alloc_tr_segment(pep);
if (ret)
goto exit;
ret = cdns2_ep_config(pep, enable);
if (ret) {
cdns2_free_tr_segment(pep);
ret = -EINVAL;
goto exit;
}
trace_cdns2_gadget_ep_enable(pep);
pep->ep_state &= ~(EP_STALLED | EP_STALL_PENDING);
pep->ep_state |= EP_ENABLED;
pep->wa1_set = 0;
pep->ring.enqueue = 0;
pep->ring.dequeue = 0;
reg = readl(&pdev->adma_regs->ep_sts);
pep->ring.pcs = !!DMA_EP_STS_CCS(reg);
pep->ring.ccs = !!DMA_EP_STS_CCS(reg);
writel(pep->ring.dma, &pdev->adma_regs->ep_traddr);
pep->ring.free_trbs = TRBS_PER_SEGMENT - 1;
exit:
spin_unlock_irqrestore(&pdev->lock, flags);
return ret;
}
static int cdns2_gadget_ep_disable(struct usb_ep *ep)
{
struct cdns2_endpoint *pep;
struct cdns2_request *preq;
struct cdns2_device *pdev;
unsigned long flags;
int val;
if (!ep)
return -EINVAL;
pep = ep_to_cdns2_ep(ep);
pdev = pep->pdev;
if (dev_WARN_ONCE(pdev->dev, !(pep->ep_state & EP_ENABLED),
"%s is already disabled\n", pep->name))
return 0;
spin_lock_irqsave(&pdev->lock, flags);
trace_cdns2_gadget_ep_disable(pep);
cdns2_select_ep(pdev, ep->desc->bEndpointAddress);
clear_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE);
readl_poll_timeout_atomic(&pdev->adma_regs->ep_sts, val,
!(val & DMA_EP_STS_DBUSY), 1, 10);
writel(DMA_EP_CMD_EPRST, &pdev->adma_regs->ep_cmd);
readl_poll_timeout_atomic(&pdev->adma_regs->ep_cmd, val,
!(val & (DMA_EP_CMD_DFLUSH | DMA_EP_CMD_EPRST)),
1, 1000);
while (!list_empty(&pep->pending_list)) {
preq = cdns2_next_preq(&pep->pending_list);
cdns2_gadget_giveback(pep, preq, -ESHUTDOWN);
}
while (!list_empty(&pep->deferred_list)) {
preq = cdns2_next_preq(&pep->deferred_list);
cdns2_gadget_giveback(pep, preq, -ESHUTDOWN);
}
ep->desc = NULL;
pep->ep_state &= ~EP_ENABLED;
spin_unlock_irqrestore(&pdev->lock, flags);
return 0;
}
static int cdns2_ep_enqueue(struct cdns2_endpoint *pep,
struct cdns2_request *preq,
gfp_t gfp_flags)
{
struct cdns2_device *pdev = pep->pdev;
struct usb_request *request;
int ret;
request = &preq->request;
request->actual = 0;
request->status = -EINPROGRESS;
ret = usb_gadget_map_request_by_dev(pdev->dev, request, pep->dir);
if (ret) {
trace_cdns2_request_enqueue_error(preq);
return ret;
}
list_add_tail(&preq->list, &pep->deferred_list);
trace_cdns2_request_enqueue(preq);
if (!(pep->ep_state & EP_STALLED) && !(pep->ep_state & EP_STALL_PENDING))
cdns2_start_all_request(pdev, pep);
return 0;
}
static int cdns2_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
gfp_t gfp_flags)
{
struct usb_request *zlp_request;
struct cdns2_request *preq;
struct cdns2_endpoint *pep;
struct cdns2_device *pdev;
unsigned long flags;
int ret;
if (!request || !ep)
return -EINVAL;
pep = ep_to_cdns2_ep(ep);
pdev = pep->pdev;
if (!(pep->ep_state & EP_ENABLED)) {
dev_err(pdev->dev, "%s: can't queue to disabled endpoint\n",
pep->name);
return -EINVAL;
}
spin_lock_irqsave(&pdev->lock, flags);
preq = to_cdns2_request(request);
ret = cdns2_ep_enqueue(pep, preq, gfp_flags);
if (ret == 0 && request->zero && request->length &&
(request->length % ep->maxpacket == 0)) {
struct cdns2_request *preq;
zlp_request = cdns2_gadget_ep_alloc_request(ep, GFP_ATOMIC);
zlp_request->buf = pdev->zlp_buf;
zlp_request->length = 0;
preq = to_cdns2_request(zlp_request);
ret = cdns2_ep_enqueue(pep, preq, gfp_flags);
}
spin_unlock_irqrestore(&pdev->lock, flags);
return ret;
}
int cdns2_gadget_ep_dequeue(struct usb_ep *ep,
struct usb_request *request)
{
struct cdns2_request *preq, *preq_temp, *cur_preq;
struct cdns2_endpoint *pep;
struct cdns2_trb *link_trb;
u8 req_on_hw_ring = 0;
unsigned long flags;
u32 buffer;
int val, i;
if (!ep || !request || !ep->desc)
return -EINVAL;
pep = ep_to_cdns2_ep(ep);
if (!pep->endpoint.desc) {
dev_err(pep->pdev->dev, "%s: can't dequeue to disabled endpoint\n",
pep->name);
return -ESHUTDOWN;
}
if (!(pep->ep_state & EP_ENABLED))
return 0;
spin_lock_irqsave(&pep->pdev->lock, flags);
cur_preq = to_cdns2_request(request);
trace_cdns2_request_dequeue(cur_preq);
list_for_each_entry_safe(preq, preq_temp, &pep->pending_list, list) {
if (cur_preq == preq) {
req_on_hw_ring = 1;
goto found;
}
}
list_for_each_entry_safe(preq, preq_temp, &pep->deferred_list, list) {
if (cur_preq == preq)
goto found;
}
goto not_found;
found:
link_trb = preq->trb;
if (req_on_hw_ring && link_trb) {
writel(DMA_EP_CMD_DFLUSH, &pep->pdev->adma_regs->ep_cmd);
readl_poll_timeout_atomic(&pep->pdev->adma_regs->ep_cmd, val,
!(val & DMA_EP_CMD_DFLUSH), 1, 1000);
buffer = cpu_to_le32(TRB_BUFFER(pep->ring.dma +
((preq->end_trb + 1) * TRB_SIZE)));
for (i = 0; i < preq->num_of_trb; i++) {
link_trb->buffer = buffer;
link_trb->control = cpu_to_le32((le32_to_cpu(link_trb->control)
& TRB_CYCLE) | TRB_CHAIN |
TRB_TYPE(TRB_LINK));
trace_cdns2_queue_trb(pep, link_trb);
link_trb = cdns2_next_trb(pep, link_trb);
}
if (pep->wa1_trb == preq->trb)
cdns2_wa1_restore_cycle_bit(pep);
}
cdns2_gadget_giveback(pep, cur_preq, -ECONNRESET);
preq = cdns2_next_preq(&pep->pending_list);
if (preq)
cdns2_rearm_transfer(pep, 1);
not_found:
spin_unlock_irqrestore(&pep->pdev->lock, flags);
return 0;
}
int cdns2_halt_endpoint(struct cdns2_device *pdev,
struct cdns2_endpoint *pep,
int value)
{
u8 __iomem *conf;
int dir = 0;
if (!(pep->ep_state & EP_ENABLED))
return -EPERM;
if (pep->dir) {
dir = ENDPRST_IO_TX;
conf = &pdev->epx_regs->ep[pep->num - 1].txcon;
} else {
conf = &pdev->epx_regs->ep[pep->num - 1].rxcon;
}
if (!value) {
struct cdns2_trb *trb = NULL;
struct cdns2_request *preq;
struct cdns2_trb trb_tmp;
preq = cdns2_next_preq(&pep->pending_list);
if (preq) {
trb = preq->trb;
if (trb) {
trb_tmp = *trb;
trb->control = trb->control ^ cpu_to_le32(TRB_CYCLE);
}
}
trace_cdns2_ep_halt(pep, 0, 0);
writeb(dir | pep->num, &pdev->epx_regs->endprst);
writeb(dir | ENDPRST_TOGRST | pep->num,
&pdev->epx_regs->endprst);
clear_reg_bit_8(conf, EPX_CON_STALL);
pep->ep_state &= ~(EP_STALLED | EP_STALL_PENDING);
if (preq) {
if (trb)
*trb = trb_tmp;
cdns2_rearm_transfer(pep, 1);
}
cdns2_start_all_request(pdev, pep);
} else {
trace_cdns2_ep_halt(pep, 1, 0);
set_reg_bit_8(conf, EPX_CON_STALL);
writeb(dir | pep->num, &pdev->epx_regs->endprst);
writeb(dir | ENDPRST_FIFORST | pep->num,
&pdev->epx_regs->endprst);
pep->ep_state |= EP_STALLED;
}
return 0;
}
static int cdns2_gadget_ep_set_halt(struct usb_ep *ep, int value)
{
struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep);
struct cdns2_device *pdev = pep->pdev;
struct cdns2_request *preq;
unsigned long flags = 0;
int ret;
spin_lock_irqsave(&pdev->lock, flags);
preq = cdns2_next_preq(&pep->pending_list);
if (value && preq) {
trace_cdns2_ep_busy_try_halt_again(pep);
ret = -EAGAIN;
goto done;
}
if (!value)
pep->ep_state &= ~EP_WEDGE;
ret = cdns2_halt_endpoint(pdev, pep, value);
done:
spin_unlock_irqrestore(&pdev->lock, flags);
return ret;
}
static int cdns2_gadget_ep_set_wedge(struct usb_ep *ep)
{
struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep);
cdns2_gadget_ep_set_halt(ep, 1);
pep->ep_state |= EP_WEDGE;
return 0;
}
static struct
cdns2_endpoint *cdns2_find_available_ep(struct cdns2_device *pdev,
struct usb_endpoint_descriptor *desc)
{
struct cdns2_endpoint *pep;
struct usb_ep *ep;
int ep_correct;
list_for_each_entry(ep, &pdev->gadget.ep_list, ep_list) {
unsigned long num;
int ret;
char c[2] = {ep->name[2], '\0'};
ret = kstrtoul(c, 10, &num);
if (ret)
return ERR_PTR(ret);
pep = ep_to_cdns2_ep(ep);
if (pep->num != num)
continue;
ep_correct = (pep->endpoint.caps.dir_in &&
usb_endpoint_dir_in(desc)) ||
(pep->endpoint.caps.dir_out &&
usb_endpoint_dir_out(desc));
if (ep_correct && !(pep->ep_state & EP_CLAIMED))
return pep;
}
return ERR_PTR(-ENOENT);
}
static struct
usb_ep *cdns2_gadget_match_ep(struct usb_gadget *gadget,
struct usb_endpoint_descriptor *desc,
struct usb_ss_ep_comp_descriptor *comp_desc)
{
struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
struct cdns2_endpoint *pep;
unsigned long flags;
pep = cdns2_find_available_ep(pdev, desc);
if (IS_ERR(pep)) {
dev_err(pdev->dev, "no available ep\n");
return NULL;
}
spin_lock_irqsave(&pdev->lock, flags);
if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_ISOC)
pep->buffering = 4;
else
pep->buffering = 1;
pep->ep_state |= EP_CLAIMED;
spin_unlock_irqrestore(&pdev->lock, flags);
return &pep->endpoint;
}
static const struct usb_ep_ops cdns2_gadget_ep_ops = {
.enable = cdns2_gadget_ep_enable,
.disable = cdns2_gadget_ep_disable,
.alloc_request = cdns2_gadget_ep_alloc_request,
.free_request = cdns2_gadget_ep_free_request,
.queue = cdns2_gadget_ep_queue,
.dequeue = cdns2_gadget_ep_dequeue,
.set_halt = cdns2_gadget_ep_set_halt,
.set_wedge = cdns2_gadget_ep_set_wedge,
};
static int cdns2_gadget_get_frame(struct usb_gadget *gadget)
{
struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
return readw(&pdev->usb_regs->frmnr);
}
static int cdns2_gadget_wakeup(struct usb_gadget *gadget)
{
struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
unsigned long flags;
spin_lock_irqsave(&pdev->lock, flags);
cdns2_wakeup(pdev);
spin_unlock_irqrestore(&pdev->lock, flags);
return 0;
}
static int cdns2_gadget_set_selfpowered(struct usb_gadget *gadget,
int is_selfpowered)
{
struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
unsigned long flags;
spin_lock_irqsave(&pdev->lock, flags);
pdev->is_selfpowered = !!is_selfpowered;
spin_unlock_irqrestore(&pdev->lock, flags);
return 0;
}
static void cdns2_quiesce(struct cdns2_device *pdev)
{
set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON);
writeb(0, &pdev->interrupt_regs->extien),
writeb(0, &pdev->interrupt_regs->usbien),
writew(0, &pdev->adma_regs->ep_ien);
writeb(0x0, &pdev->interrupt_regs->usbirq);
}
static void cdns2_gadget_config(struct cdns2_device *pdev)
{
cdns2_ep0_config(pdev);
writel(~0x0, &pdev->adma_regs->ep_ien);
cdns2_enable_l1(pdev, 0);
writeb(USB_IEN_INIT, &pdev->interrupt_regs->usbien);
writeb(EXTIRQ_WAKEUP, &pdev->interrupt_regs->extien);
writel(DMA_CONF_DMULT, &pdev->adma_regs->conf);
}
static int cdns2_gadget_pullup(struct usb_gadget *gadget, int is_on)
{
struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
unsigned long flags;
trace_cdns2_pullup(is_on);
disable_irq(pdev->irq);
spin_lock_irqsave(&pdev->lock, flags);
if (is_on) {
cdns2_gadget_config(pdev);
clear_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON);
} else {
cdns2_quiesce(pdev);
}
spin_unlock_irqrestore(&pdev->lock, flags);
enable_irq(pdev->irq);
return 0;
}
static int cdns2_gadget_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
enum usb_device_speed max_speed = driver->max_speed;
unsigned long flags;
spin_lock_irqsave(&pdev->lock, flags);
pdev->gadget_driver = driver;
max_speed = min(driver->max_speed, gadget->max_speed);
switch (max_speed) {
case USB_SPEED_FULL:
writeb(SPEEDCTRL_HSDISABLE, &pdev->usb_regs->speedctrl);
break;
case USB_SPEED_HIGH:
writeb(0, &pdev->usb_regs->speedctrl);
break;
default:
dev_err(pdev->dev, "invalid maximum_speed parameter %d\n",
max_speed);
fallthrough;
case USB_SPEED_UNKNOWN:
max_speed = USB_SPEED_HIGH;
break;
}
writeb(ENDPRST_IO_TX, &pdev->usb_regs->endprst);
writeb(ENDPRST_FIFORST | ENDPRST_TOGRST | ENDPRST_IO_TX,
&pdev->usb_regs->endprst);
writeb(ENDPRST_FIFORST | ENDPRST_TOGRST, &pdev->usb_regs->endprst);
cdns2_eps_onchip_buffer_init(pdev);
cdns2_gadget_config(pdev);
spin_unlock_irqrestore(&pdev->lock, flags);
return 0;
}
static int cdns2_gadget_udc_stop(struct usb_gadget *gadget)
{
struct cdns2_device *pdev = gadget_to_cdns2_device(gadget);
struct cdns2_endpoint *pep;
u32 bEndpointAddress;
struct usb_ep *ep;
int val;
pdev->gadget_driver = NULL;
pdev->gadget.speed = USB_SPEED_UNKNOWN;
list_for_each_entry(ep, &pdev->gadget.ep_list, ep_list) {
pep = ep_to_cdns2_ep(ep);
bEndpointAddress = pep->num | pep->dir;
cdns2_select_ep(pdev, bEndpointAddress);
writel(DMA_EP_CMD_EPRST, &pdev->adma_regs->ep_cmd);
readl_poll_timeout_atomic(&pdev->adma_regs->ep_cmd, val,
!(val & DMA_EP_CMD_EPRST), 1, 100);
}
cdns2_quiesce(pdev);
writeb(ENDPRST_IO_TX, &pdev->usb_regs->endprst);
writeb(ENDPRST_FIFORST | ENDPRST_TOGRST | ENDPRST_IO_TX,
&pdev->epx_regs->endprst);
writeb(ENDPRST_FIFORST | ENDPRST_TOGRST, &pdev->epx_regs->endprst);
return 0;
}
static const struct usb_gadget_ops cdns2_gadget_ops = {
.get_frame = cdns2_gadget_get_frame,
.wakeup = cdns2_gadget_wakeup,
.set_selfpowered = cdns2_gadget_set_selfpowered,
.pullup = cdns2_gadget_pullup,
.udc_start = cdns2_gadget_udc_start,
.udc_stop = cdns2_gadget_udc_stop,
.match_ep = cdns2_gadget_match_ep,
};
static void cdns2_free_all_eps(struct cdns2_device *pdev)
{
int i;
for (i = 0; i < CDNS2_ENDPOINTS_NUM; i++)
cdns2_free_tr_segment(&pdev->eps[i]);
}
static int cdns2_init_eps(struct cdns2_device *pdev)
{
struct cdns2_endpoint *pep;
int i;
for (i = 0; i < CDNS2_ENDPOINTS_NUM; i++) {
bool direction = !(i & 1);
u8 epnum = ((i + 1) >> 1);
if (!CDNS2_IF_EP_EXIST(pdev, epnum, direction))
continue;
pep = &pdev->eps[i];
pep->pdev = pdev;
pep->num = epnum;
pep->dir = direction ? USB_DIR_IN : USB_DIR_OUT;
pep->idx = i;
if (!epnum) {
int ret;
snprintf(pep->name, sizeof(pep->name), "ep%d%s",
epnum, "BiDir");
cdns2_init_ep0(pdev, pep);
ret = cdns2_alloc_tr_segment(pep);
if (ret) {
dev_err(pdev->dev, "Failed to init ep0\n");
return ret;
}
} else {
snprintf(pep->name, sizeof(pep->name), "ep%d%s",
epnum, !!direction ? "in" : "out");
pep->endpoint.name = pep->name;
usb_ep_set_maxpacket_limit(&pep->endpoint, 1024);
pep->endpoint.ops = &cdns2_gadget_ep_ops;
list_add_tail(&pep->endpoint.ep_list, &pdev->gadget.ep_list);
pep->endpoint.caps.dir_in = direction;
pep->endpoint.caps.dir_out = !direction;
pep->endpoint.caps.type_iso = 1;
pep->endpoint.caps.type_bulk = 1;
pep->endpoint.caps.type_int = 1;
}
pep->endpoint.name = pep->name;
pep->ep_state = 0;
dev_dbg(pdev->dev, "Init %s, SupType: CTRL: %s, INT: %s, "
"BULK: %s, ISOC %s, SupDir IN: %s, OUT: %s\n",
pep->name,
(pep->endpoint.caps.type_control) ? "yes" : "no",
(pep->endpoint.caps.type_int) ? "yes" : "no",
(pep->endpoint.caps.type_bulk) ? "yes" : "no",
(pep->endpoint.caps.type_iso) ? "yes" : "no",
(pep->endpoint.caps.dir_in) ? "yes" : "no",
(pep->endpoint.caps.dir_out) ? "yes" : "no");
INIT_LIST_HEAD(&pep->pending_list);
INIT_LIST_HEAD(&pep->deferred_list);
}
return 0;
}
static int cdns2_gadget_start(struct cdns2_device *pdev)
{
u32 max_speed;
void *buf;
int val;
int ret;
pdev->usb_regs = pdev->regs;
pdev->ep0_regs = pdev->regs;
pdev->epx_regs = pdev->regs;
pdev->interrupt_regs = pdev->regs;
pdev->adma_regs = pdev->regs + CDNS2_ADMA_REGS_OFFSET;
set_reg_bit_8(&pdev->usb_regs->cpuctrl, CPUCTRL_SW_RST);
ret = readl_poll_timeout_atomic(&pdev->usb_regs->cpuctrl, val,
!(val & CPUCTRL_SW_RST), 1, 10000);
if (ret) {
dev_err(pdev->dev, "Error: reset controller timeout\n");
return -EINVAL;
}
usb_initialize_gadget(pdev->dev, &pdev->gadget, NULL);
device_property_read_u16(pdev->dev, "cdns,on-chip-tx-buff-size",
&pdev->onchip_tx_buf);
device_property_read_u16(pdev->dev, "cdns,on-chip-rx-buff-size",
&pdev->onchip_rx_buf);
device_property_read_u32(pdev->dev, "cdns,avail-endpoints",
&pdev->eps_supported);
if (!pdev->onchip_tx_buf && !pdev->onchip_rx_buf) {
ret = -EINVAL;
dev_err(pdev->dev, "Invalid on-chip memory configuration\n");
goto put_gadget;
}
if (!(pdev->eps_supported & ~0x00010001)) {
ret = -EINVAL;
dev_err(pdev->dev, "No hardware endpoints available\n");
goto put_gadget;
}
max_speed = usb_get_maximum_speed(pdev->dev);
switch (max_speed) {
case USB_SPEED_FULL:
case USB_SPEED_HIGH:
break;
default:
dev_err(pdev->dev, "invalid maximum_speed parameter %d\n",
max_speed);
fallthrough;
case USB_SPEED_UNKNOWN:
max_speed = USB_SPEED_HIGH;
break;
}
pdev->gadget.max_speed = max_speed;
pdev->gadget.speed = USB_SPEED_UNKNOWN;
pdev->gadget.ops = &cdns2_gadget_ops;
pdev->gadget.name = "usbhs-gadget";
pdev->gadget.quirk_avoids_skb_reserve = 1;
pdev->gadget.irq = pdev->irq;
spin_lock_init(&pdev->lock);
INIT_WORK(&pdev->pending_status_wq, cdns2_pending_setup_status_handler);
INIT_LIST_HEAD(&pdev->gadget.ep_list);
pdev->eps_dma_pool = dma_pool_create("cdns2_eps_dma_pool", pdev->dev,
TR_SEG_SIZE, 8, 0);
if (!pdev->eps_dma_pool) {
dev_err(pdev->dev, "Failed to create TRB dma pool\n");
ret = -ENOMEM;
goto put_gadget;
}
ret = cdns2_init_eps(pdev);
if (ret) {
dev_err(pdev->dev, "Failed to create endpoints\n");
goto destroy_dma_pool;
}
pdev->gadget.sg_supported = 1;
pdev->zlp_buf = kzalloc(CDNS2_EP_ZLP_BUF_SIZE, GFP_KERNEL);
if (!pdev->zlp_buf) {
ret = -ENOMEM;
goto destroy_dma_pool;
}
buf = dma_alloc_coherent(pdev->dev, 8, &pdev->ep0_preq.request.dma,
GFP_DMA);
pdev->ep0_preq.request.buf = buf;
if (!pdev->ep0_preq.request.buf) {
ret = -ENOMEM;
goto free_zlp_buf;
}
ret = usb_add_gadget(&pdev->gadget);
if (ret < 0) {
dev_err(pdev->dev, "Failed to add gadget\n");
goto free_ep0_buf;
}
return 0;
free_ep0_buf:
dma_free_coherent(pdev->dev, 8, pdev->ep0_preq.request.buf,
pdev->ep0_preq.request.dma);
free_zlp_buf:
kfree(pdev->zlp_buf);
destroy_dma_pool:
dma_pool_destroy(pdev->eps_dma_pool);
put_gadget:
usb_put_gadget(&pdev->gadget);
return ret;
}
int cdns2_gadget_suspend(struct cdns2_device *pdev)
{
unsigned long flags;
cdns2_disconnect_gadget(pdev);
spin_lock_irqsave(&pdev->lock, flags);
pdev->gadget.speed = USB_SPEED_UNKNOWN;
trace_cdns2_device_state("notattached");
usb_gadget_set_state(&pdev->gadget, USB_STATE_NOTATTACHED);
cdns2_enable_l1(pdev, 0);
writeb(0, &pdev->interrupt_regs->usbien);
writel(0, &pdev->adma_regs->ep_ien);
spin_unlock_irqrestore(&pdev->lock, flags);
return 0;
}
int cdns2_gadget_resume(struct cdns2_device *pdev, bool hibernated)
{
unsigned long flags;
spin_lock_irqsave(&pdev->lock, flags);
if (!pdev->gadget_driver) {
spin_unlock_irqrestore(&pdev->lock, flags);
return 0;
}
cdns2_gadget_config(pdev);
if (hibernated)
clear_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON);
spin_unlock_irqrestore(&pdev->lock, flags);
return 0;
}
void cdns2_gadget_remove(struct cdns2_device *pdev)
{
pm_runtime_mark_last_busy(pdev->dev);
pm_runtime_put_autosuspend(pdev->dev);
usb_del_gadget(&pdev->gadget);
cdns2_free_all_eps(pdev);
dma_pool_destroy(pdev->eps_dma_pool);
kfree(pdev->zlp_buf);
usb_put_gadget(&pdev->gadget);
}
int cdns2_gadget_init(struct cdns2_device *pdev)
{
int ret;
ret = dma_set_mask_and_coherent(pdev->dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(pdev->dev, "Failed to set dma mask: %d\n", ret);
return ret;
}
pm_runtime_get_sync(pdev->dev);
cdsn2_isoc_burst_opt(pdev);
ret = cdns2_gadget_start(pdev);
if (ret) {
pm_runtime_put_sync(pdev->dev);
return ret;
}
ret = devm_request_threaded_irq(pdev->dev, pdev->irq,
cdns2_usb_irq_handler,
cdns2_thread_irq_handler,
IRQF_SHARED,
dev_name(pdev->dev),
pdev);
if (ret)
goto err0;
return 0;
err0:
cdns2_gadget_remove(pdev);
return ret;
}