2008-12-02 22:33:44 +03:00
/*
* MUSB OTG controller driver for Blackfin Processors
*
* Copyright 2006 - 2008 Analog Devices Inc .
*
* Enter bugs at http : //blackfin.uclinux.org/
*
* Licensed under the GPL - 2 or later .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/init.h>
# include <linux/list.h>
# include <linux/gpio.h>
# include <linux/io.h>
2010-12-02 10:21:05 +03:00
# include <linux/platform_device.h>
# include <linux/dma-mapping.h>
2008-12-02 22:33:44 +03:00
# include <asm/cacheflush.h>
# include "musb_core.h"
# include "blackfin.h"
2010-12-02 10:42:50 +03:00
struct bfin_glue {
struct device * dev ;
struct platform_device * musb ;
} ;
2008-12-02 22:33:44 +03:00
/*
* Load an endpoint ' s FIFO
*/
void musb_write_fifo ( struct musb_hw_ep * hw_ep , u16 len , const u8 * src )
{
void __iomem * fifo = hw_ep - > fifo ;
void __iomem * epio = hw_ep - > regs ;
2009-12-21 17:49:52 +03:00
u8 epnum = hw_ep - > epnum ;
2008-12-02 22:33:44 +03:00
prefetch ( ( u8 * ) src ) ;
musb_writew ( epio , MUSB_TXCOUNT , len ) ;
DBG ( 4 , " TX ep%d fifo %p count %d buf %p, epio %p \n " ,
hw_ep - > epnum , fifo , len , src , epio ) ;
dump_fifo_data ( src , len ) ;
2009-12-21 17:49:52 +03:00
if ( ! ANOMALY_05000380 & & epnum ! = 0 ) {
2009-12-28 14:40:39 +03:00
u16 dma_reg ;
flush_dcache_range ( ( unsigned long ) src ,
( unsigned long ) ( src + len ) ) ;
2009-12-21 17:49:52 +03:00
/* Setup DMA address register */
2009-12-28 14:40:39 +03:00
dma_reg = ( u32 ) src ;
2009-12-21 17:49:52 +03:00
bfin_write16 ( USB_DMA_REG ( epnum , USB_DMAx_ADDR_LOW ) , dma_reg ) ;
SSYNC ( ) ;
2009-12-28 14:40:39 +03:00
dma_reg = ( u32 ) src > > 16 ;
2009-12-21 17:49:52 +03:00
bfin_write16 ( USB_DMA_REG ( epnum , USB_DMAx_ADDR_HIGH ) , dma_reg ) ;
SSYNC ( ) ;
/* Setup DMA count register */
bfin_write16 ( USB_DMA_REG ( epnum , USB_DMAx_COUNT_LOW ) , len ) ;
bfin_write16 ( USB_DMA_REG ( epnum , USB_DMAx_COUNT_HIGH ) , 0 ) ;
SSYNC ( ) ;
/* Enable the DMA */
dma_reg = ( epnum < < 4 ) | DMA_ENA | INT_ENA | DIRECTION ;
bfin_write16 ( USB_DMA_REG ( epnum , USB_DMAx_CTRL ) , dma_reg ) ;
SSYNC ( ) ;
/* Wait for compelete */
while ( ! ( bfin_read_USB_DMA_INTERRUPT ( ) & ( 1 < < epnum ) ) )
cpu_relax ( ) ;
/* acknowledge dma interrupt */
bfin_write_USB_DMA_INTERRUPT ( 1 < < epnum ) ;
SSYNC ( ) ;
/* Reset DMA */
bfin_write16 ( USB_DMA_REG ( epnum , USB_DMAx_CTRL ) , 0 ) ;
SSYNC ( ) ;
} else {
SSYNC ( ) ;
if ( unlikely ( ( unsigned long ) src & 0x01 ) )
2009-12-28 14:40:39 +03:00
outsw_8 ( ( unsigned long ) fifo , src , ( len + 1 ) > > 1 ) ;
2009-12-21 17:49:52 +03:00
else
2009-12-28 14:40:39 +03:00
outsw ( ( unsigned long ) fifo , src , ( len + 1 ) > > 1 ) ;
2009-12-21 17:49:52 +03:00
}
2008-12-02 22:33:44 +03:00
}
/*
* Unload an endpoint ' s FIFO
*/
void musb_read_fifo ( struct musb_hw_ep * hw_ep , u16 len , u8 * dst )
{
void __iomem * fifo = hw_ep - > fifo ;
u8 epnum = hw_ep - > epnum ;
2009-12-21 17:49:52 +03:00
if ( ANOMALY_05000467 & & epnum ! = 0 ) {
2009-12-28 14:40:39 +03:00
u16 dma_reg ;
2009-12-21 17:49:52 +03:00
2009-12-28 14:40:39 +03:00
invalidate_dcache_range ( ( unsigned long ) dst ,
( unsigned long ) ( dst + len ) ) ;
2009-12-21 17:49:52 +03:00
/* Setup DMA address register */
2009-12-28 14:40:39 +03:00
dma_reg = ( u32 ) dst ;
2009-12-21 17:49:52 +03:00
bfin_write16 ( USB_DMA_REG ( epnum , USB_DMAx_ADDR_LOW ) , dma_reg ) ;
SSYNC ( ) ;
2009-12-28 14:40:39 +03:00
dma_reg = ( u32 ) dst > > 16 ;
2009-12-21 17:49:52 +03:00
bfin_write16 ( USB_DMA_REG ( epnum , USB_DMAx_ADDR_HIGH ) , dma_reg ) ;
SSYNC ( ) ;
/* Setup DMA count register */
bfin_write16 ( USB_DMA_REG ( epnum , USB_DMAx_COUNT_LOW ) , len ) ;
bfin_write16 ( USB_DMA_REG ( epnum , USB_DMAx_COUNT_HIGH ) , 0 ) ;
SSYNC ( ) ;
/* Enable the DMA */
dma_reg = ( epnum < < 4 ) | DMA_ENA | INT_ENA ;
bfin_write16 ( USB_DMA_REG ( epnum , USB_DMAx_CTRL ) , dma_reg ) ;
SSYNC ( ) ;
/* Wait for compelete */
while ( ! ( bfin_read_USB_DMA_INTERRUPT ( ) & ( 1 < < epnum ) ) )
cpu_relax ( ) ;
/* acknowledge dma interrupt */
bfin_write_USB_DMA_INTERRUPT ( 1 < < epnum ) ;
SSYNC ( ) ;
/* Reset DMA */
bfin_write16 ( USB_DMA_REG ( epnum , USB_DMAx_CTRL ) , 0 ) ;
SSYNC ( ) ;
} else {
SSYNC ( ) ;
/* Read the last byte of packet with odd size from address fifo + 4
* to trigger 1 byte access to EP0 FIFO .
*/
if ( len = = 1 )
* dst = ( u8 ) inw ( ( unsigned long ) fifo + 4 ) ;
else {
if ( unlikely ( ( unsigned long ) dst & 0x01 ) )
insw_8 ( ( unsigned long ) fifo , dst , len > > 1 ) ;
else
insw ( ( unsigned long ) fifo , dst , len > > 1 ) ;
if ( len & 0x01 )
* ( dst + len - 1 ) = ( u8 ) inw ( ( unsigned long ) fifo + 4 ) ;
}
}
2009-11-16 13:49:19 +03:00
DBG ( 4 , " %cX ep%d fifo %p count %d buf %p \n " ,
' R ' , hw_ep - > epnum , fifo , len , dst ) ;
2008-12-02 22:33:44 +03:00
dump_fifo_data ( dst , len ) ;
}
static irqreturn_t blackfin_interrupt ( int irq , void * __hci )
{
unsigned long flags ;
irqreturn_t retval = IRQ_NONE ;
struct musb * musb = __hci ;
spin_lock_irqsave ( & musb - > lock , flags ) ;
musb - > int_usb = musb_readb ( musb - > mregs , MUSB_INTRUSB ) ;
musb - > int_tx = musb_readw ( musb - > mregs , MUSB_INTRTX ) ;
musb - > int_rx = musb_readw ( musb - > mregs , MUSB_INTRRX ) ;
if ( musb - > int_usb | | musb - > int_tx | | musb - > int_rx ) {
musb_writeb ( musb - > mregs , MUSB_INTRUSB , musb - > int_usb ) ;
musb_writew ( musb - > mregs , MUSB_INTRTX , musb - > int_tx ) ;
musb_writew ( musb - > mregs , MUSB_INTRRX , musb - > int_rx ) ;
retval = musb_interrupt ( musb ) ;
}
2010-03-25 14:25:19 +03:00
/* Start sampling ID pin, when plug is removed from MUSB */
if ( is_otg_enabled ( musb ) & & ( musb - > xceiv - > state = = OTG_STATE_B_IDLE
| | musb - > xceiv - > state = = OTG_STATE_A_WAIT_BCON ) ) {
mod_timer ( & musb_conn_timer , jiffies + TIMER_DELAY ) ;
musb - > a_wait_bcon = TIMER_DELAY ;
}
2008-12-02 22:33:44 +03:00
spin_unlock_irqrestore ( & musb - > lock , flags ) ;
2010-03-25 14:14:25 +03:00
return retval ;
2008-12-02 22:33:44 +03:00
}
static void musb_conn_timer_handler ( unsigned long _musb )
{
struct musb * musb = ( void * ) _musb ;
unsigned long flags ;
u16 val ;
2010-03-25 14:25:19 +03:00
static u8 toggle ;
2008-12-02 22:33:44 +03:00
spin_lock_irqsave ( & musb - > lock , flags ) ;
2009-03-31 23:30:04 +04:00
switch ( musb - > xceiv - > state ) {
2008-12-02 22:33:44 +03:00
case OTG_STATE_A_IDLE :
case OTG_STATE_A_WAIT_BCON :
/* Start a new session */
val = musb_readw ( musb - > mregs , MUSB_DEVCTL ) ;
2010-03-25 14:25:19 +03:00
val & = ~ MUSB_DEVCTL_SESSION ;
musb_writew ( musb - > mregs , MUSB_DEVCTL , val ) ;
2008-12-02 22:33:44 +03:00
val | = MUSB_DEVCTL_SESSION ;
musb_writew ( musb - > mregs , MUSB_DEVCTL , val ) ;
2010-03-25 14:25:19 +03:00
/* Check if musb is host or peripheral. */
val = musb_readw ( musb - > mregs , MUSB_DEVCTL ) ;
if ( ! ( val & MUSB_DEVCTL_BDEVICE ) ) {
gpio_set_value ( musb - > config - > gpio_vrsel , 1 ) ;
musb - > xceiv - > state = OTG_STATE_A_WAIT_BCON ;
} else {
gpio_set_value ( musb - > config - > gpio_vrsel , 0 ) ;
/* Ignore VBUSERROR and SUSPEND IRQ */
val = musb_readb ( musb - > mregs , MUSB_INTRUSBE ) ;
val & = ~ MUSB_INTR_VBUSERROR ;
musb_writeb ( musb - > mregs , MUSB_INTRUSBE , val ) ;
val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR ;
musb_writeb ( musb - > mregs , MUSB_INTRUSB , val ) ;
if ( is_otg_enabled ( musb ) )
musb - > xceiv - > state = OTG_STATE_B_IDLE ;
else
musb_writeb ( musb - > mregs , MUSB_POWER , MUSB_POWER_HSENAB ) ;
}
mod_timer ( & musb_conn_timer , jiffies + TIMER_DELAY ) ;
break ;
case OTG_STATE_B_IDLE :
2008-12-02 22:33:44 +03:00
2010-03-25 14:25:19 +03:00
if ( ! is_peripheral_enabled ( musb ) )
break ;
/* Start a new session. It seems that MUSB needs taking
* some time to recognize the type of the plug inserted ?
*/
val = musb_readw ( musb - > mregs , MUSB_DEVCTL ) ;
val | = MUSB_DEVCTL_SESSION ;
musb_writew ( musb - > mregs , MUSB_DEVCTL , val ) ;
2008-12-02 22:33:44 +03:00
val = musb_readw ( musb - > mregs , MUSB_DEVCTL ) ;
2010-03-25 14:25:19 +03:00
2008-12-02 22:33:44 +03:00
if ( ! ( val & MUSB_DEVCTL_BDEVICE ) ) {
gpio_set_value ( musb - > config - > gpio_vrsel , 1 ) ;
2009-03-31 23:30:04 +04:00
musb - > xceiv - > state = OTG_STATE_A_WAIT_BCON ;
2008-12-02 22:33:44 +03:00
} else {
gpio_set_value ( musb - > config - > gpio_vrsel , 0 ) ;
/* Ignore VBUSERROR and SUSPEND IRQ */
val = musb_readb ( musb - > mregs , MUSB_INTRUSBE ) ;
val & = ~ MUSB_INTR_VBUSERROR ;
musb_writeb ( musb - > mregs , MUSB_INTRUSBE , val ) ;
val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR ;
musb_writeb ( musb - > mregs , MUSB_INTRUSB , val ) ;
2010-03-25 14:25:19 +03:00
/* Toggle the Soft Conn bit, so that we can response to
* the inserting of either A - plug or B - plug .
*/
if ( toggle ) {
val = musb_readb ( musb - > mregs , MUSB_POWER ) ;
val & = ~ MUSB_POWER_SOFTCONN ;
musb_writeb ( musb - > mregs , MUSB_POWER , val ) ;
toggle = 0 ;
} else {
val = musb_readb ( musb - > mregs , MUSB_POWER ) ;
val | = MUSB_POWER_SOFTCONN ;
musb_writeb ( musb - > mregs , MUSB_POWER , val ) ;
toggle = 1 ;
}
/* The delay time is set to 1/4 second by default,
* shortening it , if accelerating A - plug detection
* is needed in OTG mode .
*/
mod_timer ( & musb_conn_timer , jiffies + TIMER_DELAY / 4 ) ;
2008-12-02 22:33:44 +03:00
}
break ;
default :
DBG ( 1 , " %s state not handled \n " , otg_state_string ( musb ) ) ;
break ;
}
spin_unlock_irqrestore ( & musb - > lock , flags ) ;
DBG ( 4 , " state is %s \n " , otg_state_string ( musb ) ) ;
}
2010-12-01 14:22:05 +03:00
static void bfin_musb_enable ( struct musb * musb )
2008-12-02 22:33:44 +03:00
{
2010-03-25 14:25:19 +03:00
if ( ! is_otg_enabled ( musb ) & & is_host_enabled ( musb ) ) {
2008-12-02 22:33:44 +03:00
mod_timer ( & musb_conn_timer , jiffies + TIMER_DELAY ) ;
musb - > a_wait_bcon = TIMER_DELAY ;
}
}
2010-12-01 14:22:05 +03:00
static void bfin_musb_disable ( struct musb * musb )
2008-12-02 22:33:44 +03:00
{
}
2010-12-01 14:22:05 +03:00
static void bfin_musb_set_vbus ( struct musb * musb , int is_on )
2008-12-02 22:33:44 +03:00
{
2010-03-12 11:29:10 +03:00
int value = musb - > config - > gpio_vrsel_active ;
if ( ! is_on )
value = ! value ;
gpio_set_value ( musb - > config - > gpio_vrsel , value ) ;
2008-12-02 22:33:44 +03:00
DBG ( 1 , " VBUS %s, devctl %02x "
/* otg %3x conf %08x prcm %08x */ " \n " ,
otg_state_string ( musb ) ,
musb_readb ( musb - > mregs , MUSB_DEVCTL ) ) ;
}
2010-12-01 14:22:05 +03:00
static int bfin_musb_set_power ( struct otg_transceiver * x , unsigned mA )
2008-12-02 22:33:44 +03:00
{
return 0 ;
}
2010-12-01 14:22:05 +03:00
static void bfin_musb_try_idle ( struct musb * musb , unsigned long timeout )
2008-12-02 22:33:44 +03:00
{
2010-03-25 14:25:19 +03:00
if ( ! is_otg_enabled ( musb ) & & is_host_enabled ( musb ) )
2008-12-02 22:33:44 +03:00
mod_timer ( & musb_conn_timer , jiffies + TIMER_DELAY ) ;
}
2010-12-01 14:22:05 +03:00
static int bfin_musb_get_vbus_status ( struct musb * musb )
2008-12-02 22:33:44 +03:00
{
return 0 ;
}
2010-12-01 14:22:05 +03:00
static int bfin_musb_set_mode ( struct musb * musb , u8 musb_mode )
2008-12-02 22:33:44 +03:00
{
2009-11-16 13:49:25 +03:00
return - EIO ;
2008-12-02 22:33:44 +03:00
}
2010-12-01 14:22:05 +03:00
static void bfin_musb_reg_init ( struct musb * musb )
2008-12-02 22:33:44 +03:00
{
2008-12-02 22:33:45 +03:00
if ( ANOMALY_05000346 ) {
bfin_write_USB_APHY_CALIB ( ANOMALY_05000346_value ) ;
SSYNC ( ) ;
}
2008-12-02 22:33:44 +03:00
2008-12-02 22:33:45 +03:00
if ( ANOMALY_05000347 ) {
bfin_write_USB_APHY_CNTRL ( 0x0 ) ;
SSYNC ( ) ;
}
2008-12-02 22:33:44 +03:00
/* Configure PLL oscillator register */
bfin_write_USB_PLLOSC_CTRL ( 0x30a8 ) ;
SSYNC ( ) ;
bfin_write_USB_SRP_CLKDIV ( ( get_sclk ( ) / 1000 ) / 32 - 1 ) ;
SSYNC ( ) ;
bfin_write_USB_EP_NI0_RXMAXP ( 64 ) ;
SSYNC ( ) ;
bfin_write_USB_EP_NI0_TXMAXP ( 64 ) ;
SSYNC ( ) ;
/* Route INTRUSB/INTR_RX/INTR_TX to USB_INT0*/
bfin_write_USB_GLOBINTR ( 0x7 ) ;
SSYNC ( ) ;
bfin_write_USB_GLOBAL_CTL ( GLOBAL_ENA | EP1_TX_ENA | EP2_TX_ENA |
EP3_TX_ENA | EP4_TX_ENA | EP5_TX_ENA |
EP6_TX_ENA | EP7_TX_ENA | EP1_RX_ENA |
EP2_RX_ENA | EP3_RX_ENA | EP4_RX_ENA |
EP5_RX_ENA | EP6_RX_ENA | EP7_RX_ENA ) ;
SSYNC ( ) ;
2010-12-01 14:22:05 +03:00
}
static int bfin_musb_init ( struct musb * musb )
{
/*
* Rev 1.0 BF549 EZ - KITs require PE7 to be high for both DEVICE
* and OTG HOST modes , while rev 1.1 and greater require PE7 to
* be low for DEVICE mode and high for HOST mode . We set it high
* here because we are in host mode
*/
if ( gpio_request ( musb - > config - > gpio_vrsel , " USB_VRSEL " ) ) {
printk ( KERN_ERR " Failed ro request USB_VRSEL GPIO_%d \n " ,
musb - > config - > gpio_vrsel ) ;
return - ENODEV ;
}
gpio_direction_output ( musb - > config - > gpio_vrsel , 0 ) ;
usb_nop_xceiv_register ( ) ;
musb - > xceiv = otg_get_transceiver ( ) ;
if ( ! musb - > xceiv ) {
gpio_free ( musb - > config - > gpio_vrsel ) ;
return - ENODEV ;
}
bfin_musb_reg_init ( musb ) ;
2008-12-02 22:33:44 +03:00
if ( is_host_enabled ( musb ) ) {
2010-12-01 14:22:05 +03:00
musb - > board_set_vbus = bfin_musb_set_vbus ;
2008-12-02 22:33:44 +03:00
setup_timer ( & musb_conn_timer ,
musb_conn_timer_handler , ( unsigned long ) musb ) ;
}
if ( is_peripheral_enabled ( musb ) )
2010-12-01 14:22:05 +03:00
musb - > xceiv - > set_power = bfin_musb_set_power ;
2008-12-02 22:33:44 +03:00
musb - > isr = blackfin_interrupt ;
return 0 ;
}
2010-12-01 14:22:05 +03:00
# ifdef CONFIG_PM
void musb_platform_save_context ( struct musb * musb ,
struct musb_context_registers * musb_context )
{
if ( is_host_active ( musb ) )
/*
* During hibernate gpio_vrsel will change from high to low
* low which will generate wakeup event resume the system
* immediately . Set it to 0 before hibernate to avoid this
* wakeup event .
*/
gpio_set_value ( musb - > config - > gpio_vrsel , 0 ) ;
}
void musb_platform_restore_context ( struct musb * musb ,
struct musb_context_registers * musb_context )
{
bfin_musb_reg_init ( musb ) ;
}
# endif
static int bfin_musb_exit ( struct musb * musb )
2008-12-02 22:33:44 +03:00
{
gpio_free ( musb - > config - > gpio_vrsel ) ;
2010-09-29 10:54:29 +04:00
otg_put_transceiver ( musb - > xceiv ) ;
2010-09-29 10:54:30 +04:00
usb_nop_xceiv_unregister ( ) ;
2008-12-02 22:33:44 +03:00
return 0 ;
}
2010-12-01 14:22:05 +03:00
const struct musb_platform_ops musb_ops = {
. init = bfin_musb_init ,
. exit = bfin_musb_exit ,
. enable = bfin_musb_enable ,
. disable = bfin_musb_disable ,
. set_mode = bfin_musb_set_mode ,
. try_idle = bfin_musb_try_idle ,
. vbus_status = bfin_musb_vbus_status ,
. set_vbus = bfin_musb_set_vbus ,
} ;
2010-12-02 10:21:05 +03:00
static u64 bfin_dmamask = DMA_BIT_MASK ( 32 ) ;
static int __init bfin_probe ( struct platform_device * pdev )
{
struct musb_hdrc_platform_data * pdata = pdev - > dev . platform_data ;
struct platform_device * musb ;
2010-12-02 10:42:50 +03:00
struct bfin_glue * glue ;
2010-12-02 10:21:05 +03:00
int ret = - ENOMEM ;
2010-12-02 10:42:50 +03:00
glue = kzalloc ( sizeof ( * glue ) , GFP_KERNEL ) ;
if ( ! glue ) {
dev_err ( & pdev - > dev , " failed to allocate glue context \n " ) ;
goto err0 ;
}
2010-12-02 10:21:05 +03:00
musb = platform_device_alloc ( " musb-hdrc " , - 1 ) ;
if ( ! musb ) {
dev_err ( & pdev - > dev , " failed to allocate musb device \n " ) ;
2010-12-02 10:42:50 +03:00
goto err1 ;
2010-12-02 10:21:05 +03:00
}
musb - > dev . parent = & pdev - > dev ;
musb - > dev . dma_mask = & bfin_dmamask ;
musb - > dev . coherent_dma_mask = bfin_dmamask ;
2010-12-02 10:42:50 +03:00
glue - > dev = & pdev - > dev ;
glue - > musb = musb ;
platform_set_drvdata ( pdev , glue ) ;
2010-12-02 10:21:05 +03:00
ret = platform_device_add_resources ( musb , pdev - > resource ,
pdev - > num_resources ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to add resources \n " ) ;
2010-12-02 10:42:50 +03:00
goto err2 ;
2010-12-02 10:21:05 +03:00
}
ret = platform_device_add_data ( musb , pdata , sizeof ( * pdata ) ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to add platform_data \n " ) ;
2010-12-02 10:42:50 +03:00
goto err2 ;
2010-12-02 10:21:05 +03:00
}
ret = platform_device_add ( musb ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to register musb device \n " ) ;
2010-12-02 10:42:50 +03:00
goto err2 ;
2010-12-02 10:21:05 +03:00
}
return 0 ;
2010-12-02 10:42:50 +03:00
err2 :
2010-12-02 10:21:05 +03:00
platform_device_put ( musb ) ;
2010-12-02 10:42:50 +03:00
err1 :
kfree ( glue ) ;
2010-12-02 10:21:05 +03:00
err0 :
return ret ;
}
static int __exit bfin_remove ( struct platform_device * pdev )
{
2010-12-02 10:42:50 +03:00
struct bfin_glue * glue = platform_get_drvdata ( pdev ) ;
2010-12-02 10:21:05 +03:00
2010-12-02 10:42:50 +03:00
platform_device_del ( glue - > musb ) ;
platform_device_put ( glue - > musb ) ;
kfree ( glue ) ;
2010-12-02 10:21:05 +03:00
return 0 ;
}
static struct platform_driver bfin_driver = {
. remove = __exit_p ( bfin_remove ) ,
. driver = {
. name = " musb-bfin " ,
} ,
} ;
MODULE_DESCRIPTION ( " Blackfin MUSB Glue Layer " ) ;
MODULE_AUTHOR ( " Bryan Wy <cooloney@kernel.org> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
static int __init bfin_init ( void )
{
return platform_driver_probe ( & bfin_driver , bfin_probe ) ;
}
subsys_initcall ( bfin_init ) ;
static void __exit bfin_exit ( void )
{
platform_driver_unregister ( & bfin_driver ) ;
}
module_exit ( bfin_exit ) ;