usb: r8a66597-udc: add support for SUDMAC
PATCH v2 4/4 usb: r8a66597-udc: add support for SUDMAC
SH7757 has a USB function with internal DMA controller (SUDMAC). This patch supports the SUDMAC. The SUDMAC is incompatible with general-purpose DMAC. So, it doesn't use dmaengine.
Signed-off-by Yoshihiro Shimoda <[email protected]>
---
about v2:
- fix balance of braces.
- use dev_err() instead of printk().
drivers/usb/gadget/r8a66597-udc.c | 364 +++++++++++++++++++++++++++++++++++-- drivers/usb/gadget/r8a66597-udc.h | 26 +++- include/linux/usb/r8a66597.h | 60 ++++++ 3 files changed, 430 insertions(+), 20 deletions(-)
drivers/usb/gadget/r8a66597-udc.c +345 -19
@@ -28,13 +28,14 @@#include <linux/clk.h> #include <linux/err.h> #include <linux/slab.h> +#include <linux/dma-mapping.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include "r8a66597-udc.h" -#define DRIVER_VERSION "2009-08-18" +#define DRIVER_VERSION "2011-09-26"static const char udc_name[] ="r8a66597_udc" ;static const char *r8a66597_ep_name[] = {
@@ -194,6 +195,54 @@ static inline void control_reg_sqclr(struct r8a66597 *r8a66597, u16 pipenum) } } +static void control_reg_sqset (struct r8a66597 *r8a66597,u16 pipenum) +{ +unsigned long offset; + +pipe_stop (r8a66597, pipenum); + +if (pipenum =0 ) { +r8a66597_bset (r8a66597,SQSET ,DCPCTR ); + }else if (pipenum <R8A66597_MAX_NUM_PIPE ) { + offset =get_pipectr_addr (pipenum); +r8a66597_bset (r8a66597,SQSET , offset); + }else { +dev_err (r8a66597_to_dev (r8a66597), +"unexpect pipe num(%d)\n" , pipenum); + } +} + +static u16 control_reg_sqmon (struct r8a66597 *r8a66597,u16 pipenum) +{ +unsigned long offset; + +if (pipenum =0 ) { +return r8a66597_read (r8a66597,DCPCTR ) &SQMON ; + }else if (pipenum <R8A66597_MAX_NUM_PIPE ) { + offset =get_pipectr_addr (pipenum); +return r8a66597_read (r8a66597, offset) &SQMON ; + }else { +dev_err (r8a66597_to_dev (r8a66597), +"unexpect pipe num(%d)\n" , pipenum); + } + +return 0 ; +} + +static u16 save_usb_toggle (struct r8a66597 *r8a66597,u16 pipenum) +{ +return control_reg_sqmon (r8a66597, pipenum); +} + +static void restore_usb_toggle (struct r8a66597 *r8a66597,u16 pipenum, +u16 toggle) +{ +if (toggle) +control_reg_sqset (r8a66597, pipenum); +else +control_reg_sqclr (r8a66597, pipenum); +} +static inline int get_buffer_size (struct r8a66597 *r8a66597,u16 pipenum) {u16 tmp;
@@ -230,18 +279,51 @@ static inline unsigned short mbw_value(struct r8a66597 *r8a66597)return MBW_16 ; } +static void r8a66597_change_curpipe (struct r8a66597 *r8a66597,u16 pipenum, +u16 isel,u16 fifosel) +{ +u16 tmp, mask, loop; +int i =0 ; + +if (!pipenum) { + mask =ISEL |CURPIPE ; + loop = isel; + }else { + mask =CURPIPE ; + loop = pipenum; + } +r8a66597_mdfy (r8a66597, loop, mask, fifosel); + +do { + tmp =r8a66597_read (r8a66597, fifosel); +if (i++ >1000000 ) { +dev_err (r8a66597_to_dev (r8a66597), +"r8a66597: register%x, loop %x " +"is timeout\n" , fifosel, loop); +break ; + } +ndelay (1 ); + }while ((tmp & mask) != loop); +} +static inline void pipe_change (struct r8a66597 *r8a66597,u16 pipenum) {struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum];if (ep->use_dma) -return ; +r8a66597_bclr (r8a66597,DREQE , ep->fifosel);r8a66597_mdfy (r8a66597, pipenum,CURPIPE , ep->fifosel);ndelay (450 ); -r8a66597_bset (r8a66597,mbw_value (r8a66597), ep->fifosel); +if (r8a66597_is_sudmac (r8a66597) && ep->use_dma) +r8a66597_bclr (r8a66597,mbw_value (r8a66597), ep->fifosel); +else +r8a66597_bset (r8a66597,mbw_value (r8a66597), ep->fifosel); + +if (ep->use_dma) +r8a66597_bset (r8a66597,DREQE , ep->fifosel); } static int pipe_buffer_setting(struct r8a66597 *r8a66597,
@@ -346,9 +428,15 @@ static void r8a66597_ep_setting(struct r8a66597 *r8a66597, ep->fifoaddr =CFIFO ; ep->fifosel =CFIFOSEL ; ep->fifoctr =CFIFOCTR ; - ep->fifotrn =0 ; ep->pipectr =get_pipectr_addr (pipenum); +if (is_bulk_pipe (pipenum) ||is_isoc_pipe (pipenum)) { + ep->pipetre =get_pipetre_addr (pipenum); + ep->pipetrn =get_pipetrn_addr (pipenum); + }else { + ep->pipetre =0 ; + ep->pipetrn =0 ; + } ep->pipenum = pipenum; ep->ep.maxpacket =le16_to_cpu (desc->wMaxPacketSize); r8a66597->pipenum2ep[pipenum] = ep;
@@ -508,6 +596,124 @@ static void start_ep0_write(struct r8a66597_ep *ep, } } +static void disable_fifosel (struct r8a66597 *r8a66597,u16 pipenum, +u16 fifosel) +{ +u16 tmp; + + tmp =r8a66597_read (r8a66597, fifosel) &CURPIPE ; +if (tmp = pipenum) +r8a66597_change_curpipe (r8a66597,0 ,0 , fifosel); +} + +static void change_bfre_mode (struct r8a66597 *r8a66597,u16 pipenum, +int enable) +{ +struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum]; +u16 tmp, toggle; + +/* check current BFRE bit */ +r8a66597_write (r8a66597, pipenum,PIPESEL ); + tmp =r8a66597_read (r8a66597,PIPECFG ) &R8A66597_BFRE ; +if ((enable && tmp) || (!enable && !tmp)) +return ; + +/* change BFRE bit */ +pipe_stop (r8a66597, pipenum); +disable_fifosel (r8a66597, pipenum,CFIFOSEL ); +disable_fifosel (r8a66597, pipenum,D0FIFOSEL ); +disable_fifosel (r8a66597, pipenum,D1FIFOSEL ); + + toggle =save_usb_toggle (r8a66597, pipenum); + +r8a66597_write (r8a66597, pipenum,PIPESEL ); +if (enable) +r8a66597_bset (r8a66597,R8A66597_BFRE ,PIPECFG ); +else +r8a66597_bclr (r8a66597,R8A66597_BFRE ,PIPECFG ); + +/* initialize for internal BFRE flag */ +r8a66597_bset (r8a66597,ACLRM , ep->pipectr); +r8a66597_bclr (r8a66597,ACLRM , ep->pipectr); + +restore_usb_toggle (r8a66597, pipenum, toggle); +} + +static int sudmac_alloc_channel (struct r8a66597 *r8a66597, +struct r8a66597_ep *ep, +struct r8a66597_request *req) +{ +struct r8a66597_dma *dma; + +if (!r8a66597_is_sudmac (r8a66597)) +return -ENODEV ; + +/* Check transfer type */ +if (!is_bulk_pipe (ep->pipenum)) +return -EIO ; + +if (r8a66597->dma.used) +return -EBUSY ; + +/* set SUDMAC parameters */ + dma = &r8a66597->dma; + dma->used =1 ; +if (ep->desc->bEndpointAddress &USB_DIR_IN ) { + dma->dir =1 ; + }else { + dma->dir =0 ; +change_bfre_mode (r8a66597, ep->pipenum,1 ); + } + +/* set r8a66597_ep paramters */ + ep->use_dma =1 ; + ep->dma = dma; + ep->fifoaddr =D0FIFO ; + ep->fifosel =D0FIFOSEL ; + ep->fifoctr =D0FIFOCTR ; + +/* dma mapping */ + req->req.dma =dma_map_single (r8a66597_to_dev (ep->r8a66597), + req->req.buf, req->req.length, + dma->dir ?DMA_TO_DEVICE :DMA_FROM_DEVICE ); + +return 0 ; +} + +static void sudmac_free_channel (struct r8a66597 *r8a66597, +struct r8a66597_ep *ep, +struct r8a66597_request *req) +{ +if (!r8a66597_is_sudmac (r8a66597)) +return ; + +dma_unmap_single (r8a66597_to_dev (ep->r8a66597), + req->req.dma, req->req.length, + ep->dma->dir ?DMA_TO_DEVICE :DMA_FROM_DEVICE ); + +r8a66597_bclr (r8a66597,DREQE , ep->fifosel); +r8a66597_change_curpipe (r8a66597,0 ,0 , ep->fifosel); + + ep->dma->used =0 ; + ep->use_dma =0 ; + ep->fifoaddr =CFIFO ; + ep->fifosel =CFIFOSEL ; + ep->fifoctr =CFIFOCTR ; +} + +static void sudmac_start (struct r8a66597 *r8a66597,struct r8a66597_ep *ep, +struct r8a66597_request *req) +{ +BUG_ON (req->req.length =0 ); + +r8a66597_sudmac_write (r8a66597,LBA_WAIT ,CH0CFG ); +r8a66597_sudmac_write (r8a66597, req->req.dma,CH0BA ); +r8a66597_sudmac_write (r8a66597, req->req.length,CH0BBC ); +r8a66597_sudmac_write (r8a66597,CH0ENDE ,DINTCTRL ); + +r8a66597_sudmac_write (r8a66597,DEN ,CH0DEN ); +} +static void start_packet_write (struct r8a66597_ep *ep,struct r8a66597_request *req) {
@@ -518,11 +724,29 @@ static void start_packet_write(struct r8a66597_ep *ep,disable_irq_empty (r8a66597, ep->pipenum);pipe_start (r8a66597, ep->pipenum); - tmp =r8a66597_read (r8a66597, ep->fifoctr); -if (unlikely ((tmp &FRDY ) =0 )) -pipe_irq_enable (r8a66597, ep->pipenum); -else -irq_packet_write (ep, req); +if (req->req.length =0 ) { +transfer_complete (ep, req,0 ); + }else { +r8a66597_write (r8a66597, ~(1 << ep->pipenum),BRDYSTS ); +if (sudmac_alloc_channel (r8a66597, ep, req) <0 ) { +/* PIO mode */ +pipe_change (r8a66597, ep->pipenum); +disable_irq_empty (r8a66597, ep->pipenum); +pipe_start (r8a66597, ep->pipenum); + tmp =r8a66597_read (r8a66597, ep->fifoctr); +if (unlikely ((tmp &FRDY ) =0 )) +pipe_irq_enable (r8a66597, ep->pipenum); +else +irq_packet_write (ep, req); + }else { +/* DMA mode */ +pipe_change (r8a66597, ep->pipenum); +disable_irq_nrdy (r8a66597, ep->pipenum); +pipe_start (r8a66597, ep->pipenum); +enable_irq_nrdy (r8a66597, ep->pipenum); +sudmac_start (r8a66597, ep, req); + } + } } static void start_packet_read(struct r8a66597_ep *ep,
@@ -537,17 +761,26 @@ static void start_packet_read(struct r8a66597_ep *ep,pipe_start (r8a66597, pipenum);pipe_irq_enable (r8a66597, pipenum); }else { -if (ep->use_dma) { -r8a66597_bset (r8a66597,TRCLR , ep->fifosel); -pipe_change (r8a66597, pipenum); -r8a66597_bset (r8a66597,TRENB , ep->fifosel); +pipe_stop (r8a66597, pipenum); +if (ep->pipetre) { +enable_irq_nrdy (r8a66597, pipenum); +r8a66597_write (r8a66597,TRCLR , ep->pipetre);r8a66597_write (r8a66597, - (req->req.length + ep->ep.maxpacket -1 ) - / ep->ep.maxpacket, - ep->fifotrn); +DIV_ROUND_UP (req->req.length, ep->ep.maxpacket), + ep->pipetrn); +r8a66597_bset (r8a66597,TRENB , ep->pipetre); + } + +if (sudmac_alloc_channel (r8a66597, ep, req) <0 ) { +/* PIO mode */ +change_bfre_mode (r8a66597, ep->pipenum,0 ); +pipe_start (r8a66597, pipenum);/* trigger once */ +pipe_irq_enable (r8a66597, pipenum); + }else { +pipe_change (r8a66597, pipenum); +sudmac_start (r8a66597, ep, req); +pipe_start (r8a66597, pipenum);/* trigger once */ } -pipe_start (r8a66597, pipenum);/* trigger once */ -pipe_irq_enable (r8a66597, pipenum); } }
@@ -704,6 +937,9 @@ __acquires(r8a66597->lock)if (!list_empty (&ep->queue)) restart =1 ; +if (ep->use_dma) +sudmac_free_channel (ep->r8a66597, ep, req); +spin_unlock (&ep->r8a66597->lock); req->req.complete (&ep->ep, &req->req);spin_lock (&ep->r8a66597->lock);
@@ -1180,6 +1416,65 @@ __acquires(r8a66597->lock) } } +static void sudmac_finish (struct r8a66597 *r8a66597,struct r8a66597_ep *ep) +{ +u16 pipenum; +struct r8a66597_request *req; +u32 len; +int i =0 ; + + pipenum = ep->pipenum; +pipe_change (r8a66597, pipenum); + +while (!(r8a66597_read (r8a66597, ep->fifoctr) &FRDY )) { +udelay (1 ); +if (unlikely (i++ >=10000 )) {/* timeout = 10 msec */ +dev_err (r8a66597_to_dev (r8a66597), +"%s: FRDY was not set (%d)\n" , + __func__, pipenum); +return ; + } + } + +r8a66597_bset (r8a66597,BCLR , ep->fifoctr); + req =get_request_from_ep (ep); + +/* prepare parameters */ + len =r8a66597_sudmac_read (r8a66597,CH0CBC ); + req->req.actual += len; + +/* clear */ +r8a66597_sudmac_write (r8a66597,CH0STCLR ,DSTSCLR ); + +/* check transfer finish */ +if ((!req->req.zero && (req->req.actual = req->req.length)) + || (len % ep->ep.maxpacket)) { +if (ep->dma->dir) { +disable_irq_ready (r8a66597, pipenum); +enable_irq_empty (r8a66597, pipenum); + }else { +/* Clear the interrupt flag for next transfer */ +r8a66597_write (r8a66597, ~(1 << pipenum),BRDYSTS ); +transfer_complete (ep, req,0 ); + } + } +} + +static void r8a66597_sudmac_irq (struct r8a66597 *r8a66597) +{ +u32 irqsts; +struct r8a66597_ep *ep; +u16 pipenum; + + irqsts =r8a66597_sudmac_read (r8a66597,DINTSTS ); +if (irqsts &CH0ENDS ) { +r8a66597_sudmac_write (r8a66597,CH0ENDC ,DINTSTSCLR ); + pipenum = (r8a66597_read (r8a66597,D0FIFOSEL ) &CURPIPE ); + ep = r8a66597->pipenum2ep[pipenum]; +sudmac_finish (r8a66597, ep); + } +} +static irqreturn_t r8a66597_irq (int irq,void *_r8a66597) {struct r8a66597 *r8a66597 = _r8a66597;
@@ -1190,6 +1485,9 @@ static irqreturn_t r8a66597_irq(int irq, void *_r8a66597)u16 savepipe;u16 mask0; +if (r8a66597_is_sudmac (r8a66597)) +r8a66597_sudmac_irq (r8a66597); +spin_lock (&r8a66597->lock); intsts0 =r8a66597_read (r8a66597,INTSTS0 );
@@ -1566,6 +1864,8 @@ static int __exit r8a66597_remove(struct platform_device *pdev)usb_del_gadget_udc (&r8a66597->gadget);del_timer_sync (&r8a66597->timer);iounmap (r8a66597->reg); +if (r8a66597->pdata->sudmac) +iounmap (r8a66597->sudmac_reg);free_irq (platform_get_irq (pdev,0 ), r8a66597);r8a66597_free_request (&r8a66597->ep[0 ].ep, r8a66597->ep0_req);#ifdef CONFIG_HAVE_CLK
@@ -1582,6 +1882,26 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r) { } +static int __init r8a66597_sudmac_ioremap (struct r8a66597 *r8a66597, +struct platform_device *pdev) +{ +struct resource *res; + + res =platform_get_resource_byname (pdev,IORESOURCE_MEM ,"sudmac" ); +if (!res) { +dev_err (&pdev->dev,"platform_get_resource error(sudmac).\n" ); +return -ENODEV ; + } + + r8a66597->sudmac_reg =ioremap (res->start,resource_size (res)); +if (r8a66597->sudmac_reg =NULL ) { +dev_err (&pdev->dev,"ioremap error(sudmac).\n" ); +return -ENOMEM ; + } + +return 0 ; +} +static int __init r8a66597_probe (struct platform_device *pdev) {#ifdef CONFIG_HAVE_CLK
@@ -1659,6 +1979,11 @@ static int __init r8a66597_probe(struct platform_device *pdev) clk_enable(r8a66597->clk); }#endif +if (r8a66597->pdata->sudmac) { + ret =r8a66597_sudmac_ioremap (r8a66597, pdev); +if (ret <0 ) + goto clean_up2; + }disable_controller (r8a66597);/* make sure controller is disabled */
@@ -1691,7 +2016,6 @@ static int __init r8a66597_probe(struct platform_device *pdev) r8a66597->ep[0 ].fifoaddr =CFIFO ; r8a66597->ep[0 ].fifosel =CFIFOSEL ; r8a66597->ep[0 ].fifoctr =CFIFOCTR ; - r8a66597->ep[0 ].fifotrn =0 ; r8a66597->ep[0 ].pipectr =get_pipectr_addr (0 ); r8a66597->pipenum2ep[0 ] = &r8a66597->ep[0 ]; r8a66597->epaddr2ep[0 ] = &r8a66597->ep[0 ];
@@ -1724,6 +2048,8 @@ clean_up2:#endif clean_up:if (r8a66597) { +if (r8a66597->sudmac_reg) +iounmap (r8a66597->sudmac_reg);if (r8a66597->ep0_req)r8a66597_free_request (&r8a66597->ep[0 ].ep, r8a66597->ep0_req);
drivers/usb/gadget/r8a66597-udc.h +25 -1
@@ -53,6 +53,7 @@ ((pipenum >=R8A66597_BASE_PIPENUM_ISOC ) && \ (pipenum < (R8A66597_BASE_PIPENUM_ISOC + R8A66597_MAX_NUM_ISOC))) +#define r8a66597_is_sudmac (r8a66597) (r8a66597->pdata->sudmac)struct r8a66597_pipe_info {u16 pipe;u16 epnum;
@@ -70,6 +71,7 @@ struct r8a66597_request {struct r8a66597_ep {struct usb_ep ep;struct r8a66597 *r8a66597; +struct r8a66597_dma *dma;struct list_head queue;unsigned busy:1 ;
@@ -85,13 +87,20 @@ struct r8a66597_ep {unsigned char fifoaddr;unsigned char fifosel;unsigned char fifoctr; -unsigned char fifotrn;unsigned char pipectr; +unsigned char pipetre; +unsigned char pipetrn; +}; + +struct r8a66597_dma { +unsigned used:1 ; +unsigned dir:1 ;/* 1 = IN(write), 0 = OUT(read) */ };struct r8a66597 {spinlock_t lock;void __iomem *reg; +void __iomem *sudmac_reg;#ifdef CONFIG_HAVE_CLK struct clk *clk;
@@ -104,6 +113,7 @@ struct r8a66597 {struct r8a66597_ep ep[R8A66597_MAX_NUM_PIPE ];struct r8a66597_ep *pipenum2ep[R8A66597_MAX_NUM_PIPE ];struct r8a66597_ep *epaddr2ep[16 ]; +struct r8a66597_dma dma;struct timer_list timer;struct usb_request *ep0_req;/* for internal request */
@@ -261,7 +271,21 @@ static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata)return clock; } +static inline u32 r8a66597_sudmac_read (struct r8a66597 *r8a66597, +unsigned long offset) +{ +return ioread32 (r8a66597->sudmac_reg + offset); +} + +static inline void r8a66597_sudmac_write (struct r8a66597 *r8a66597,u32 val, +unsigned long offset) +{ +iowrite32 (val, r8a66597->sudmac_reg + offset); +} +#define get_pipectr_addr (pipenum) (PIPE1CTR + (pipenum - 1) * 2) +#define get_pipetre_addr (pipenum) (PIPE1TRE + (pipenum - 1) * 4) +#define get_pipetrn_addr (pipenum) (PIPE1TRN + (pipenum - 1) * 4)#define enable_irq_ready (r8a66597, pipenum) \ enable_pipe_irq(r8a66597, pipenum, BRDYENB)
include/linux/usb/r8a66597.h +60 -0
71 lines not on this page · raw message
Signature · 1 line
1.7.1