/*
* Renesas USB driver
*
* Copyright (C) 2011 Renesas Solutions Corp.
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/scatterlist.h>
#include "./common.h"
#include "./pipe.h"
#define usbhsf_get_cfifo(p) (&((p)->fifo_info.cfifo))
#define usbhsf_get_d0fifo(p) (&((p)->fifo_info.d0fifo))
#define usbhsf_get_d1fifo(p) (&((p)->fifo_info.d1fifo))
#define usbhsf_is_cfifo(p, f) (usbhsf_get_cfifo(p) == f)
#define usbhsf_fifo_is_busy(f) ((f)->pipe) /* see usbhs_pipe_select_fifo */
/*
* packet initialize
*/
void usbhs_pkt_init(struct usbhs_pkt *pkt)
{
pkt->dma = DMA_ADDR_INVALID;
INIT_LIST_HEAD(&pkt->node);
}
/*
* packet control function
*/
static int usbhsf_null_handle(struct usbhs_pkt *pkt, int *is_done)
{
struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
struct device *dev = usbhs_priv_to_dev(priv);
dev_err(dev, "null handler\n");
return -EINVAL;
}
static struct usbhs_pkt_handle usbhsf_null_handler = {
.prepare = usbhsf_null_handle,
.try_run = usbhsf_null_handle,
};
void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
void (*done)(struct usbhs_priv *priv,
struct usbhs_pkt *pkt),
void *buf, int len, int zero, int sequence)
{
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
struct device *dev = usbhs_priv_to_dev(priv);
unsigned long flags;
if (!done) {
dev_err(dev, "no done function\n");
return;
}
/******************** spin lock ********************/
usbhs_lock(priv, flags);
if (!pipe->handler) {
dev_err(dev, "no handler function\n");
pipe->handler = &usbhsf_null_handler;
}
list_move_tail(&pkt->node, &pipe->list);
/*
* each pkt must hold own handler.
* because handler might be changed by its situation.
* dma handler -> pio handler.
*/
pkt->pipe = pipe;
pkt->buf = buf;
pkt->handler = pipe->handler;
pkt->length = len;
pkt->zero = zero;
pkt->actual = 0;
pkt->done = done;
pkt->sequence = sequence;
usbhs_unlock(priv, flags);
/******************** spin unlock ******************/
}
static void __usbhsf_pkt_del(struct usbhs_pkt *pkt)
{
list_del_init(&pkt->node);
}
static struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe)
{
if (list_empty(&pipe->list))
return NULL;
return list_first_entry(&pipe->list, struct usbhs_pkt, node);
}
struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt)
{
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
unsigned long flags;
/******************** spin lock ********************/
usbhs_lock(priv, flags);
if (!pkt)
pkt = __usbhsf_pkt_get(pipe);
if (pkt)
__usbhsf_pkt_del(pkt);
usbhs_unlock(priv, flags);
/******************** spin unlock ******************/
return pkt;
}
enum {
USBHSF_PKT_PREPARE,
USBHSF_PKT_TRY_RUN,
USBHSF_PKT_DMA_DONE,
};
static int usbhsf_pkt_handler(struct usbhs_pipe *pipe, int type)
{
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
struct usbhs_pkt *pkt;
struct device *dev = usbhs_priv_to_dev(priv);
int (*func)(struct usbhs_pkt *pkt, int *is_done);
unsigned long flags;
int ret = 0;
int is_done = 0;
/******************** spin lock ********************/
usbhs_lock(priv, flags);
pkt = __usbhsf_pkt_get(pipe);
if (!pkt)
goto __usbhs_pkt_handler_end;
switch (type) {
case USBHSF_PKT_PREPARE:
func = pkt->handler->prepare;
break;
case USBHSF_PKT_TRY_RUN:
func = pkt->handler->try_run;
break;
case USBHSF_PKT_DMA_DONE:
func = pkt->handler->dma_done;
break;
default:
dev_err(dev, "unknown pkt hander\n");
goto __usbhs_pkt_handler_end;
}
ret = func(pkt, &is_done);
if (is_done)
__usbhsf_pkt_del(pkt);
__usbhs_pkt_handler_end:
usbhs_unlock(priv, flags);
/******************** spin unlock ******************/
if (is_done) {
pkt->done(priv, pkt);
usbhs_pkt_start(pipe);
}
return ret;
}
void usbhs_pkt_start(struct usbhs_pipe *pipe)
{
usbhsf_pkt_handler(pipe, USBHSF_PKT_PREPARE);
}
/*
* irq enable/disable function
*/
#define usbhsf_irq_empty_ctrl(p, e) usbhsf_irq_callback_ctrl(p, bempsts, e)
#define usbhsf_irq_ready_ctrl(p, e) usbhsf_irq_callback_ctrl(p, brdysts, e)
#define usbhsf_irq_callback_ctrl(pipe, status, enable) \
({ \
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); \
struct usbhs_mod *mod = usbhs_mod_get_current(priv); \
u16 status = (1 << usbhs_pipe_number(pipe)); \
if (!mod) \
return; \
if (enable) \
mod->irq_##status |= status; \
else \
mod->irq_##status &= ~status; \
usbhs_irq_callback_update(priv, mod); \
})
static void usbhsf_tx_irq_ctrl(struct usbhs_pipe *pipe, int enable)
{
/*
* And DCP pipe can NOT use "ready interrupt" for "send"
* it should use "empty" interrupt.
* see
* "Operation" - "Interrupt Function" - "BRDY Interrupt"
*
* on the other hand, normal pipe can use "ready interrupt" for "send"
* even though it is single/double buffer
*/
if (usbhs_pipe_is_dcp(pipe))
usbhsf_irq_empty_ctrl(pipe, enable);
else
usbhsf_irq_ready_ctrl(pipe, enable);
}
static void usbhsf_rx_irq_ctrl(struct usbhs_pipe *pipe, int enable)
{
|