2012-05-11 18:25:46 +04:00
/*
* core . c - ChipIdea USB IP core family device controller
*
* Copyright ( C ) 2008 Chipidea - MIPS Technologies , Inc . All rights reserved .
*
* Author : David Lopo
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
/*
* Description : ChipIdea USB IP core family device controller
*
* This driver is composed of several blocks :
* - HW : hardware interface
* - DBG : debug facilities ( optional )
* - UTIL : utilities
* - ISR : interrupts handling
* - ENDPT : endpoint operations ( Gadget API )
* - GADGET : gadget operations ( Gadget API )
* - BUS : bus glue code , bus abstraction layer
*
* Compile Options
2014-05-23 04:12:47 +04:00
* - CONFIG_USB_CHIPIDEA_DEBUG : enable debug facilities
2012-05-11 18:25:46 +04:00
* - STALL_IN : non - empty bulk - in pipes cannot be halted
* if defined mass storage compliance succeeds but with warnings
* = > case 4 : Hi > Dn
* = > case 5 : Hi > Di
* = > case 8 : Hi < > Do
* if undefined usbtest 13 fails
* - TRACE : enable function tracing ( depends on DEBUG )
*
* Main Features
* - Chapter 9 & Mass Storage Compliance with Gadget File Storage
* - Chapter 9 Compliance with Gadget Zero ( STALL_IN undefined )
* - Normal & LPM support
*
* USBTEST Report
* - OK : 0 - 12 , 13 ( STALL_IN defined ) & 14
* - Not Supported : 15 & 16 ( ISO )
*
* TODO List
* - Suspend & Remote Wakeup
*/
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/dma-mapping.h>
# include <linux/platform_device.h>
# include <linux/module.h>
2012-07-07 18:56:42 +04:00
# include <linux/idr.h>
2012-05-11 18:25:46 +04:00
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/pm_runtime.h>
# include <linux/usb/ch9.h>
# include <linux/usb/gadget.h>
# include <linux/usb/otg.h>
# include <linux/usb/chipidea.h>
2013-06-13 18:59:56 +04:00
# include <linux/usb/of.h>
2014-02-19 09:41:43 +04:00
# include <linux/of.h>
2013-06-13 18:59:56 +04:00
# include <linux/phy.h>
2013-08-14 13:44:03 +04:00
# include <linux/regulator/consumer.h>
2012-05-11 18:25:46 +04:00
# include "ci.h"
# include "udc.h"
# include "bits.h"
2012-05-11 18:25:54 +04:00
# include "host.h"
2012-05-11 18:25:46 +04:00
# include "debug.h"
2013-08-14 13:44:06 +04:00
# include "otg.h"
2014-04-23 11:56:50 +04:00
# include "otg_fsm.h"
2012-05-11 18:25:46 +04:00
2012-05-11 18:25:47 +04:00
/* Controller register map */
2014-01-06 06:10:39 +04:00
static const u8 ci_regs_nolpm [ ] = {
[ CAP_CAPLENGTH ] = 0x00U ,
[ CAP_HCCPARAMS ] = 0x08U ,
[ CAP_DCCPARAMS ] = 0x24U ,
[ CAP_TESTMODE ] = 0x38U ,
[ OP_USBCMD ] = 0x00U ,
[ OP_USBSTS ] = 0x04U ,
[ OP_USBINTR ] = 0x08U ,
[ OP_DEVICEADDR ] = 0x14U ,
[ OP_ENDPTLISTADDR ] = 0x18U ,
[ OP_PORTSC ] = 0x44U ,
[ OP_DEVLC ] = 0x84U ,
[ OP_OTGSC ] = 0x64U ,
[ OP_USBMODE ] = 0x68U ,
[ OP_ENDPTSETUPSTAT ] = 0x6CU ,
[ OP_ENDPTPRIME ] = 0x70U ,
[ OP_ENDPTFLUSH ] = 0x74U ,
[ OP_ENDPTSTAT ] = 0x78U ,
[ OP_ENDPTCOMPLETE ] = 0x7CU ,
[ OP_ENDPTCTRL ] = 0x80U ,
2012-05-11 18:25:46 +04:00
} ;
2014-01-06 06:10:39 +04:00
static const u8 ci_regs_lpm [ ] = {
[ CAP_CAPLENGTH ] = 0x00U ,
[ CAP_HCCPARAMS ] = 0x08U ,
[ CAP_DCCPARAMS ] = 0x24U ,
[ CAP_TESTMODE ] = 0xFCU ,
[ OP_USBCMD ] = 0x00U ,
[ OP_USBSTS ] = 0x04U ,
[ OP_USBINTR ] = 0x08U ,
[ OP_DEVICEADDR ] = 0x14U ,
[ OP_ENDPTLISTADDR ] = 0x18U ,
[ OP_PORTSC ] = 0x44U ,
[ OP_DEVLC ] = 0x84U ,
[ OP_OTGSC ] = 0xC4U ,
[ OP_USBMODE ] = 0xC8U ,
[ OP_ENDPTSETUPSTAT ] = 0xD8U ,
[ OP_ENDPTPRIME ] = 0xDCU ,
[ OP_ENDPTFLUSH ] = 0xE0U ,
[ OP_ENDPTSTAT ] = 0xE4U ,
[ OP_ENDPTCOMPLETE ] = 0xE8U ,
[ OP_ENDPTCTRL ] = 0xECU ,
2012-05-11 18:25:46 +04:00
} ;
2013-06-24 15:46:36 +04:00
static int hw_alloc_regmap ( struct ci_hdrc * ci , bool is_lpm )
2012-05-11 18:25:46 +04:00
{
int i ;
for ( i = 0 ; i < OP_ENDPTCTRL ; i + + )
2012-05-11 18:25:47 +04:00
ci - > hw_bank . regmap [ i ] =
( i < = CAP_LAST ? ci - > hw_bank . cap : ci - > hw_bank . op ) +
2012-05-11 18:25:46 +04:00
( is_lpm ? ci_regs_lpm [ i ] : ci_regs_nolpm [ i ] ) ;
for ( ; i < = OP_LAST ; i + + )
2012-05-11 18:25:47 +04:00
ci - > hw_bank . regmap [ i ] = ci - > hw_bank . op +
2012-05-11 18:25:46 +04:00
4 * ( i - OP_ENDPTCTRL ) +
( is_lpm
? ci_regs_lpm [ OP_ENDPTCTRL ]
: ci_regs_nolpm [ OP_ENDPTCTRL ] ) ;
return 0 ;
}
2014-04-23 11:56:39 +04:00
/**
* hw_read_intr_enable : returns interrupt enable register
*
* This function returns register data
*/
u32 hw_read_intr_enable ( struct ci_hdrc * ci )
{
return hw_read ( ci , OP_USBINTR , ~ 0 ) ;
}
/**
* hw_read_intr_status : returns interrupt status register
*
* This function returns register data
*/
u32 hw_read_intr_status ( struct ci_hdrc * ci )
{
return hw_read ( ci , OP_USBSTS , ~ 0 ) ;
}
2012-05-11 18:25:46 +04:00
/**
* hw_port_test_set : writes port test mode ( execute without interruption )
* @ mode : new value
*
* This function returns an error code
*/
2013-06-24 15:46:36 +04:00
int hw_port_test_set ( struct ci_hdrc * ci , u8 mode )
2012-05-11 18:25:46 +04:00
{
const u8 TEST_MODE_MAX = 7 ;
if ( mode > TEST_MODE_MAX )
return - EINVAL ;
2013-03-30 14:53:55 +04:00
hw_write ( ci , OP_PORTSC , PORTSC_PTC , mode < < __ffs ( PORTSC_PTC ) ) ;
2012-05-11 18:25:46 +04:00
return 0 ;
}
/**
* hw_port_test_get : reads port test mode value
*
* This function returns port test mode value
*/
2013-06-24 15:46:36 +04:00
u8 hw_port_test_get ( struct ci_hdrc * ci )
2012-05-11 18:25:46 +04:00
{
2013-03-30 14:53:55 +04:00
return hw_read ( ci , OP_PORTSC , PORTSC_PTC ) > > __ffs ( PORTSC_PTC ) ;
2012-05-11 18:25:46 +04:00
}
2013-09-24 08:47:55 +04:00
/* The PHY enters/leaves low power mode */
static void ci_hdrc_enter_lpm ( struct ci_hdrc * ci , bool enable )
{
enum ci_hw_regs reg = ci - > hw_bank . lpm ? OP_DEVLC : OP_PORTSC ;
bool lpm = ! ! ( hw_read ( ci , reg , PORTSC_PHCD ( ci - > hw_bank . lpm ) ) ) ;
if ( enable & & ! lpm ) {
hw_write ( ci , reg , PORTSC_PHCD ( ci - > hw_bank . lpm ) ,
PORTSC_PHCD ( ci - > hw_bank . lpm ) ) ;
} else if ( ! enable & & lpm ) {
hw_write ( ci , reg , PORTSC_PHCD ( ci - > hw_bank . lpm ) ,
0 ) ;
/*
2014-04-23 11:56:41 +04:00
* the PHY needs some time ( less
2013-09-24 08:47:55 +04:00
* than 1 ms ) to leave low power mode .
*/
2014-04-23 11:56:41 +04:00
usleep_range ( 1000 , 1100 ) ;
2013-09-24 08:47:55 +04:00
}
}
2013-06-24 15:46:36 +04:00
static int hw_device_init ( struct ci_hdrc * ci , void __iomem * base )
2012-05-11 18:25:46 +04:00
{
u32 reg ;
/* bank is a module variable */
2012-05-11 18:25:47 +04:00
ci - > hw_bank . abs = base ;
2012-05-11 18:25:46 +04:00
2012-05-11 18:25:47 +04:00
ci - > hw_bank . cap = ci - > hw_bank . abs ;
2012-06-29 13:48:53 +04:00
ci - > hw_bank . cap + = ci - > platdata - > capoffset ;
2013-03-30 14:54:03 +04:00
ci - > hw_bank . op = ci - > hw_bank . cap + ( ioread32 ( ci - > hw_bank . cap ) & 0xff ) ;
2012-05-11 18:25:46 +04:00
2012-05-11 18:25:47 +04:00
hw_alloc_regmap ( ci , false ) ;
reg = hw_read ( ci , CAP_HCCPARAMS , HCCPARAMS_LEN ) > >
2013-03-30 14:53:55 +04:00
__ffs ( HCCPARAMS_LEN ) ;
2012-05-11 18:25:47 +04:00
ci - > hw_bank . lpm = reg ;
2013-12-06 12:35:12 +04:00
if ( reg )
hw_alloc_regmap ( ci , ! ! reg ) ;
2012-05-11 18:25:47 +04:00
ci - > hw_bank . size = ci - > hw_bank . op - ci - > hw_bank . abs ;
ci - > hw_bank . size + = OP_LAST ;
ci - > hw_bank . size / = sizeof ( u32 ) ;
2012-05-11 18:25:46 +04:00
2012-05-11 18:25:47 +04:00
reg = hw_read ( ci , CAP_DCCPARAMS , DCCPARAMS_DEN ) > >
2013-03-30 14:53:55 +04:00
__ffs ( DCCPARAMS_DEN ) ;
2012-05-11 18:25:47 +04:00
ci - > hw_ep_max = reg * 2 ; /* cache hw ENDPT_MAX */
2012-05-11 18:25:46 +04:00
2012-05-15 17:58:18 +04:00
if ( ci - > hw_ep_max > ENDPT_MAX )
2012-05-11 18:25:46 +04:00
return - ENODEV ;
2013-09-24 08:47:55 +04:00
ci_hdrc_enter_lpm ( ci , false ) ;
2013-08-14 13:44:09 +04:00
/* Disable all interrupts bits */
hw_write ( ci , OP_USBINTR , 0xffffffff , 0 ) ;
/* Clear all interrupts status bits*/
hw_write ( ci , OP_USBSTS , 0xffffffff , 0xffffffff ) ;
2012-05-11 18:25:47 +04:00
dev_dbg ( ci - > dev , " ChipIdea HDRC found, lpm: %d; cap: %p op: %p \n " ,
ci - > hw_bank . lpm , ci - > hw_bank . cap , ci - > hw_bank . op ) ;
2012-05-11 18:25:46 +04:00
/* setup lock mode ? */
/* ENDPTSETUPSTAT is '0' by default */
/* HCSPARAMS.bf.ppc SHOULD BE zero for device */
return 0 ;
}
2013-06-24 15:46:36 +04:00
static void hw_phymode_configure ( struct ci_hdrc * ci )
2013-06-13 18:59:56 +04:00
{
2014-01-10 09:51:29 +04:00
u32 portsc , lpm , sts = 0 ;
2013-06-13 18:59:56 +04:00
switch ( ci - > platdata - > phy_mode ) {
case USBPHY_INTERFACE_MODE_UTMI :
portsc = PORTSC_PTS ( PTS_UTMI ) ;
lpm = DEVLC_PTS ( PTS_UTMI ) ;
break ;
case USBPHY_INTERFACE_MODE_UTMIW :
portsc = PORTSC_PTS ( PTS_UTMI ) | PORTSC_PTW ;
lpm = DEVLC_PTS ( PTS_UTMI ) | DEVLC_PTW ;
break ;
case USBPHY_INTERFACE_MODE_ULPI :
portsc = PORTSC_PTS ( PTS_ULPI ) ;
lpm = DEVLC_PTS ( PTS_ULPI ) ;
break ;
case USBPHY_INTERFACE_MODE_SERIAL :
portsc = PORTSC_PTS ( PTS_SERIAL ) ;
lpm = DEVLC_PTS ( PTS_SERIAL ) ;
sts = 1 ;
break ;
case USBPHY_INTERFACE_MODE_HSIC :
portsc = PORTSC_PTS ( PTS_HSIC ) ;
lpm = DEVLC_PTS ( PTS_HSIC ) ;
break ;
default :
return ;
}
if ( ci - > hw_bank . lpm ) {
hw_write ( ci , OP_DEVLC , DEVLC_PTS ( 7 ) | DEVLC_PTW , lpm ) ;
2014-01-10 09:51:29 +04:00
if ( sts )
hw_write ( ci , OP_DEVLC , DEVLC_STS , DEVLC_STS ) ;
2013-06-13 18:59:56 +04:00
} else {
hw_write ( ci , OP_PORTSC , PORTSC_PTS ( 7 ) | PORTSC_PTW , portsc ) ;
2014-01-10 09:51:29 +04:00
if ( sts )
hw_write ( ci , OP_PORTSC , PORTSC_STS , PORTSC_STS ) ;
2013-06-13 18:59:56 +04:00
}
}
2014-04-23 11:56:37 +04:00
/**
* ci_usb_phy_init : initialize phy according to different phy type
* @ ci : the controller
*
* This function returns an error code if usb_phy_init has failed
*/
static int ci_usb_phy_init ( struct ci_hdrc * ci )
{
int ret ;
switch ( ci - > platdata - > phy_mode ) {
case USBPHY_INTERFACE_MODE_UTMI :
case USBPHY_INTERFACE_MODE_UTMIW :
case USBPHY_INTERFACE_MODE_HSIC :
ret = usb_phy_init ( ci - > transceiver ) ;
if ( ret )
return ret ;
hw_phymode_configure ( ci ) ;
break ;
case USBPHY_INTERFACE_MODE_ULPI :
case USBPHY_INTERFACE_MODE_SERIAL :
hw_phymode_configure ( ci ) ;
ret = usb_phy_init ( ci - > transceiver ) ;
if ( ret )
return ret ;
break ;
default :
ret = usb_phy_init ( ci - > transceiver ) ;
}
return ret ;
}
2012-05-11 18:25:46 +04:00
/**
* hw_device_reset : resets chip ( execute without interruption )
* @ ci : the controller
*
* This function returns an error code
*/
2013-06-24 15:46:36 +04:00
int hw_device_reset ( struct ci_hdrc * ci , u32 mode )
2012-05-11 18:25:46 +04:00
{
/* should flush & stop before reset */
hw_write ( ci , OP_ENDPTFLUSH , ~ 0 , ~ 0 ) ;
hw_write ( ci , OP_USBCMD , USBCMD_RS , 0 ) ;
hw_write ( ci , OP_USBCMD , USBCMD_RST , USBCMD_RST ) ;
while ( hw_read ( ci , OP_USBCMD , USBCMD_RST ) )
udelay ( 10 ) ; /* not RTOS friendly */
2012-06-29 13:48:53 +04:00
if ( ci - > platdata - > notify_event )
ci - > platdata - > notify_event ( ci ,
2013-06-24 15:46:36 +04:00
CI_HDRC_CONTROLLER_RESET_EVENT ) ;
2012-05-11 18:25:46 +04:00
2013-06-24 15:46:36 +04:00
if ( ci - > platdata - > flags & CI_HDRC_DISABLE_STREAMING )
2012-05-11 18:25:53 +04:00
hw_write ( ci , OP_USBMODE , USBMODE_CI_SDIS , USBMODE_CI_SDIS ) ;
2012-05-11 18:25:46 +04:00
2014-02-19 09:41:43 +04:00
if ( ci - > platdata - > flags & CI_HDRC_FORCE_FULLSPEED ) {
if ( ci - > hw_bank . lpm )
hw_write ( ci , OP_DEVLC , DEVLC_PFSC , DEVLC_PFSC ) ;
else
hw_write ( ci , OP_PORTSC , PORTSC_PFSC , PORTSC_PFSC ) ;
}
2012-05-11 18:25:46 +04:00
/* USBMODE should be configured step by step */
hw_write ( ci , OP_USBMODE , USBMODE_CM , USBMODE_CM_IDLE ) ;
2012-05-11 18:25:54 +04:00
hw_write ( ci , OP_USBMODE , USBMODE_CM , mode ) ;
2012-05-11 18:25:46 +04:00
/* HW >= 2.3 */
hw_write ( ci , OP_USBMODE , USBMODE_SLOM , USBMODE_SLOM ) ;
2012-05-11 18:25:54 +04:00
if ( hw_read ( ci , OP_USBMODE , USBMODE_CM ) ! = mode ) {
pr_err ( " cannot enter in %s mode " , ci_role ( ci ) - > name ) ;
2012-05-11 18:25:46 +04:00
pr_err ( " lpm = %i " , ci - > hw_bank . lpm ) ;
return - ENODEV ;
}
return 0 ;
}
2013-08-14 13:44:12 +04:00
/**
* hw_wait_reg : wait the register value
*
* Sometimes , it needs to wait register value before going on .
* Eg , when switch to device mode , the vbus value should be lower
* than OTGSC_BSV before connects to host .
*
* @ ci : the controller
* @ reg : register index
* @ mask : mast bit
* @ value : the bit value to wait
* @ timeout_ms : timeout in millisecond
*
* This function returns an error code if timeout
*/
int hw_wait_reg ( struct ci_hdrc * ci , enum ci_hw_regs reg , u32 mask ,
u32 value , unsigned int timeout_ms )
{
unsigned long elapse = jiffies + msecs_to_jiffies ( timeout_ms ) ;
while ( hw_read ( ci , reg , mask ) ! = value ) {
if ( time_after ( jiffies , elapse ) ) {
dev_err ( ci - > dev , " timeout waiting for %08x in %d \n " ,
mask , reg ) ;
return - ETIMEDOUT ;
}
msleep ( 20 ) ;
}
return 0 ;
}
2012-05-11 18:25:47 +04:00
static irqreturn_t ci_irq ( int irq , void * data )
{
2013-06-24 15:46:36 +04:00
struct ci_hdrc * ci = data ;
2012-05-11 18:25:47 +04:00
irqreturn_t ret = IRQ_NONE ;
2012-09-12 15:58:11 +04:00
u32 otgsc = 0 ;
2012-05-11 18:25:47 +04:00
2014-04-23 11:56:50 +04:00
if ( ci - > is_otg ) {
2014-04-23 11:56:38 +04:00
otgsc = hw_read_otgsc ( ci , ~ 0 ) ;
2014-04-23 11:56:50 +04:00
if ( ci_otg_is_fsm_mode ( ci ) ) {
ret = ci_otg_fsm_irq ( ci ) ;
if ( ret = = IRQ_HANDLED )
return ret ;
}
}
2012-05-11 18:25:47 +04:00
2013-08-14 13:44:11 +04:00
/*
* Handle id change interrupt , it indicates device / host function
* switch .
*/
if ( ci - > is_otg & & ( otgsc & OTGSC_IDIE ) & & ( otgsc & OTGSC_IDIS ) ) {
ci - > id_event = true ;
2014-04-23 11:56:38 +04:00
/* Clear ID change irq status */
hw_write_otgsc ( ci , OTGSC_IDIS , OTGSC_IDIS ) ;
2013-08-14 13:44:11 +04:00
disable_irq_nosync ( ci - > irq ) ;
queue_work ( ci - > wq , & ci - > work ) ;
return IRQ_HANDLED ;
}
2012-09-12 15:58:11 +04:00
2013-08-14 13:44:11 +04:00
/*
* Handle vbus change interrupt , it indicates device connection
* and disconnection events .
*/
if ( ci - > is_otg & & ( otgsc & OTGSC_BSVIE ) & & ( otgsc & OTGSC_BSVIS ) ) {
ci - > b_sess_valid_event = true ;
2014-04-23 11:56:38 +04:00
/* Clear BSV irq */
hw_write_otgsc ( ci , OTGSC_BSVIS , OTGSC_BSVIS ) ;
2012-09-12 15:58:11 +04:00
disable_irq_nosync ( ci - > irq ) ;
queue_work ( ci - > wq , & ci - > work ) ;
2013-08-14 13:44:11 +04:00
return IRQ_HANDLED ;
2012-05-11 18:25:47 +04:00
}
2013-08-14 13:44:11 +04:00
/* Handle device/host interrupt */
if ( ci - > role ! = CI_ROLE_END )
ret = ci_role ( ci ) - > irq ( ci ) ;
2012-09-12 15:58:11 +04:00
return ret ;
2012-05-11 18:25:47 +04:00
}
2013-08-14 13:44:03 +04:00
static int ci_get_platdata ( struct device * dev ,
struct ci_hdrc_platform_data * platdata )
{
2013-09-17 08:37:22 +04:00
if ( ! platdata - > phy_mode )
platdata - > phy_mode = of_usb_get_phy_mode ( dev - > of_node ) ;
if ( ! platdata - > dr_mode )
platdata - > dr_mode = of_usb_get_dr_mode ( dev - > of_node ) ;
if ( platdata - > dr_mode = = USB_DR_MODE_UNKNOWN )
platdata - > dr_mode = USB_DR_MODE_OTG ;
2013-10-30 05:19:29 +04:00
if ( platdata - > dr_mode ! = USB_DR_MODE_PERIPHERAL ) {
/* Get the vbus regulator */
platdata - > reg_vbus = devm_regulator_get ( dev , " vbus " ) ;
if ( PTR_ERR ( platdata - > reg_vbus ) = = - EPROBE_DEFER ) {
return - EPROBE_DEFER ;
} else if ( PTR_ERR ( platdata - > reg_vbus ) = = - ENODEV ) {
/* no vbus regualator is needed */
platdata - > reg_vbus = NULL ;
} else if ( IS_ERR ( platdata - > reg_vbus ) ) {
dev_err ( dev , " Getting regulator error: %ld \n " ,
PTR_ERR ( platdata - > reg_vbus ) ) ;
return PTR_ERR ( platdata - > reg_vbus ) ;
}
}
2014-02-19 09:41:43 +04:00
if ( of_usb_get_maximum_speed ( dev - > of_node ) = = USB_SPEED_FULL )
platdata - > flags | = CI_HDRC_FORCE_FULLSPEED ;
2013-08-14 13:44:03 +04:00
return 0 ;
}
2012-07-07 18:56:42 +04:00
static DEFINE_IDA ( ci_ida ) ;
2013-06-24 15:46:36 +04:00
struct platform_device * ci_hdrc_add_device ( struct device * dev ,
2012-07-07 18:56:41 +04:00
struct resource * res , int nres ,
2013-06-24 15:46:36 +04:00
struct ci_hdrc_platform_data * platdata )
2012-07-07 18:56:41 +04:00
{
struct platform_device * pdev ;
2012-07-07 18:56:42 +04:00
int id , ret ;
2012-07-07 18:56:41 +04:00
2013-08-14 13:44:03 +04:00
ret = ci_get_platdata ( dev , platdata ) ;
if ( ret )
return ERR_PTR ( ret ) ;
2012-07-07 18:56:42 +04:00
id = ida_simple_get ( & ci_ida , 0 , 0 , GFP_KERNEL ) ;
if ( id < 0 )
return ERR_PTR ( id ) ;
pdev = platform_device_alloc ( " ci_hdrc " , id ) ;
if ( ! pdev ) {
ret = - ENOMEM ;
goto put_id ;
}
2012-07-07 18:56:41 +04:00
pdev - > dev . parent = dev ;
pdev - > dev . dma_mask = dev - > dma_mask ;
pdev - > dev . dma_parms = dev - > dma_parms ;
dma_set_coherent_mask ( & pdev - > dev , dev - > coherent_dma_mask ) ;
ret = platform_device_add_resources ( pdev , res , nres ) ;
if ( ret )
goto err ;
ret = platform_device_add_data ( pdev , platdata , sizeof ( * platdata ) ) ;
if ( ret )
goto err ;
ret = platform_device_add ( pdev ) ;
if ( ret )
goto err ;
return pdev ;
err :
platform_device_put ( pdev ) ;
2012-07-07 18:56:42 +04:00
put_id :
ida_simple_remove ( & ci_ida , id ) ;
2012-07-07 18:56:41 +04:00
return ERR_PTR ( ret ) ;
}
2013-06-24 15:46:36 +04:00
EXPORT_SYMBOL_GPL ( ci_hdrc_add_device ) ;
2012-07-07 18:56:41 +04:00
2013-06-24 15:46:36 +04:00
void ci_hdrc_remove_device ( struct platform_device * pdev )
2012-07-07 18:56:41 +04:00
{
2012-11-22 13:11:25 +04:00
int id = pdev - > id ;
2012-07-07 18:56:41 +04:00
platform_device_unregister ( pdev ) ;
2012-11-22 13:11:25 +04:00
ida_simple_remove ( & ci_ida , id ) ;
2012-07-07 18:56:41 +04:00
}
2013-06-24 15:46:36 +04:00
EXPORT_SYMBOL_GPL ( ci_hdrc_remove_device ) ;
2012-07-07 18:56:41 +04:00
2013-08-14 13:44:07 +04:00
static inline void ci_role_destroy ( struct ci_hdrc * ci )
{
ci_hdrc_gadget_destroy ( ci ) ;
ci_hdrc_host_destroy ( ci ) ;
2013-08-14 13:44:10 +04:00
if ( ci - > is_otg )
ci_hdrc_otg_destroy ( ci ) ;
2013-08-14 13:44:07 +04:00
}
2013-08-14 13:44:08 +04:00
static void ci_get_otg_capable ( struct ci_hdrc * ci )
{
if ( ci - > platdata - > flags & CI_HDRC_DUAL_ROLE_NOT_OTG )
ci - > is_otg = false ;
else
ci - > is_otg = ( hw_read ( ci , CAP_DCCPARAMS ,
DCCPARAMS_DC | DCCPARAMS_HC )
= = ( DCCPARAMS_DC | DCCPARAMS_HC ) ) ;
2014-04-23 11:56:41 +04:00
if ( ci - > is_otg )
2013-08-14 13:44:08 +04:00
dev_dbg ( ci - > dev , " It is OTG capable controller \n " ) ;
}
2012-11-19 22:21:48 +04:00
static int ci_hdrc_probe ( struct platform_device * pdev )
2012-05-11 18:25:46 +04:00
{
struct device * dev = & pdev - > dev ;
2013-06-24 15:46:36 +04:00
struct ci_hdrc * ci ;
2012-05-11 18:25:46 +04:00
struct resource * res ;
void __iomem * base ;
int ret ;
2013-06-13 18:59:57 +04:00
enum usb_dr_mode dr_mode ;
2012-05-11 18:25:46 +04:00
2014-02-19 09:41:42 +04:00
if ( ! dev_get_platdata ( dev ) ) {
2012-05-11 18:25:46 +04:00
dev_err ( dev , " platform data missing \n " ) ;
return - ENODEV ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-03-30 04:46:27 +04:00
base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
2012-05-11 18:25:46 +04:00
2012-05-11 18:25:47 +04:00
ci = devm_kzalloc ( dev , sizeof ( * ci ) , GFP_KERNEL ) ;
if ( ! ci ) {
dev_err ( dev , " can't allocate device \n " ) ;
return - ENOMEM ;
}
ci - > dev = dev ;
2014-02-19 09:41:42 +04:00
ci - > platdata = dev_get_platdata ( dev ) ;
2014-01-10 09:51:27 +04:00
ci - > imx28_write_fix = ! ! ( ci - > platdata - > flags &
CI_HDRC_IMX28_WRITE_FIX ) ;
2012-05-11 18:25:47 +04:00
ret = hw_device_init ( ci , base ) ;
if ( ret < 0 ) {
dev_err ( dev , " can't initialize hardware \n " ) ;
return - ENODEV ;
}
2012-05-11 18:25:46 +04:00
2014-02-19 09:41:40 +04:00
if ( ci - > platdata - > phy )
ci - > transceiver = ci - > platdata - > phy ;
else
ci - > transceiver = devm_usb_get_phy ( dev , USB_PHY_TYPE_USB2 ) ;
if ( IS_ERR ( ci - > transceiver ) ) {
ret = PTR_ERR ( ci - > transceiver ) ;
/*
* if - ENXIO is returned , it means PHY layer wasn ' t
* enabled , so it makes no sense to return - EPROBE_DEFER
* in that case , since no PHY driver will ever probe .
*/
if ( ret = = - ENXIO )
return ret ;
dev_err ( dev , " no usb2 phy configured \n " ) ;
return - EPROBE_DEFER ;
}
2014-04-23 11:56:37 +04:00
ret = ci_usb_phy_init ( ci ) ;
2013-09-24 08:47:53 +04:00
if ( ret ) {
dev_err ( dev , " unable to init phy: %d \n " , ret ) ;
return ret ;
2014-04-23 11:56:41 +04:00
} else {
/*
* The delay to sync PHY ' s status , the maximum delay is
* 2 ms since the otgsc uses 1 ms timer to debounce the
* PHY ' s input
*/
usleep_range ( 2000 , 2500 ) ;
2013-09-24 08:47:53 +04:00
}
2012-05-11 18:25:54 +04:00
ci - > hw_bank . phys = res - > start ;
2012-05-11 18:25:47 +04:00
ci - > irq = platform_get_irq ( pdev , 0 ) ;
if ( ci - > irq < 0 ) {
2012-05-11 18:25:46 +04:00
dev_err ( dev , " missing IRQ \n " ) ;
2014-02-19 09:41:44 +04:00
ret = ci - > irq ;
2014-02-19 09:41:40 +04:00
goto deinit_phy ;
2012-05-11 18:25:47 +04:00
}
2013-08-14 13:44:08 +04:00
ci_get_otg_capable ( ci ) ;
2013-06-13 18:59:57 +04:00
dr_mode = ci - > platdata - > dr_mode ;
2012-05-11 18:25:47 +04:00
/* initialize role(s) before the interrupt is requested */
2013-06-13 18:59:57 +04:00
if ( dr_mode = = USB_DR_MODE_OTG | | dr_mode = = USB_DR_MODE_HOST ) {
ret = ci_hdrc_host_init ( ci ) ;
if ( ret )
dev_info ( dev , " doesn't support host \n " ) ;
}
2012-05-11 18:25:54 +04:00
2013-06-13 18:59:57 +04:00
if ( dr_mode = = USB_DR_MODE_OTG | | dr_mode = = USB_DR_MODE_PERIPHERAL ) {
ret = ci_hdrc_gadget_init ( ci ) ;
if ( ret )
dev_info ( dev , " doesn't support gadget \n " ) ;
}
2012-05-11 18:25:47 +04:00
if ( ! ci - > roles [ CI_ROLE_HOST ] & & ! ci - > roles [ CI_ROLE_GADGET ] ) {
dev_err ( dev , " no supported roles \n " ) ;
2013-09-24 08:47:53 +04:00
ret = - ENODEV ;
2014-02-19 09:41:40 +04:00
goto deinit_phy ;
2013-08-14 13:44:10 +04:00
}
if ( ci - > is_otg ) {
2014-04-23 11:56:41 +04:00
/* Disable and clear all OTG irq */
hw_write_otgsc ( ci , OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS ,
OTGSC_INT_STATUS_BITS ) ;
2013-08-14 13:44:10 +04:00
ret = ci_hdrc_otg_init ( ci ) ;
if ( ret ) {
dev_err ( dev , " init otg fails, ret = %d \n " , ret ) ;
goto stop ;
}
2012-05-11 18:25:47 +04:00
}
if ( ci - > roles [ CI_ROLE_HOST ] & & ci - > roles [ CI_ROLE_GADGET ] ) {
2013-08-14 13:44:08 +04:00
if ( ci - > is_otg ) {
ci - > role = ci_otg_role ( ci ) ;
2014-04-23 11:56:38 +04:00
/* Enable ID change irq */
hw_write_otgsc ( ci , OTGSC_IDIE , OTGSC_IDIE ) ;
2013-08-14 13:44:08 +04:00
} else {
/*
* If the controller is not OTG capable , but support
* role switch , the defalt role is gadget , and the
* user can switch it through debugfs .
*/
ci - > role = CI_ROLE_GADGET ;
}
2012-05-11 18:25:47 +04:00
} else {
ci - > role = ci - > roles [ CI_ROLE_HOST ]
? CI_ROLE_HOST
: CI_ROLE_GADGET ;
}
2013-12-05 11:20:50 +04:00
/* only update vbus status for peripheral */
if ( ci - > role = = CI_ROLE_GADGET )
ci_handle_vbus_change ( ci ) ;
2014-04-23 11:56:50 +04:00
if ( ! ci_otg_is_fsm_mode ( ci ) ) {
ret = ci_role_start ( ci , ci - > role ) ;
if ( ret ) {
dev_err ( dev , " can't start %s role \n " ,
ci_role ( ci ) - > name ) ;
goto stop ;
}
2012-05-11 18:25:46 +04:00
}
2012-05-11 18:25:47 +04:00
platform_set_drvdata ( pdev , ci ) ;
2012-06-29 13:48:53 +04:00
ret = request_irq ( ci - > irq , ci_irq , IRQF_SHARED , ci - > platdata - > name ,
2012-05-11 18:25:47 +04:00
ci ) ;
if ( ret )
goto stop ;
2012-05-11 18:25:46 +04:00
2014-04-23 11:56:50 +04:00
if ( ci_otg_is_fsm_mode ( ci ) )
ci_hdrc_otg_fsm_start ( ci ) ;
2013-03-30 14:53:53 +04:00
ret = dbg_create_files ( ci ) ;
if ( ! ret )
return 0 ;
2012-05-11 18:25:47 +04:00
2013-03-30 14:53:53 +04:00
free_irq ( ci - > irq , ci ) ;
2012-05-11 18:25:47 +04:00
stop :
2013-08-14 13:44:07 +04:00
ci_role_destroy ( ci ) ;
2014-02-19 09:41:40 +04:00
deinit_phy :
usb_phy_shutdown ( ci - > transceiver ) ;
2012-05-11 18:25:46 +04:00
return ret ;
}
2012-11-19 22:26:20 +04:00
static int ci_hdrc_remove ( struct platform_device * pdev )
2012-05-11 18:25:46 +04:00
{
2013-06-24 15:46:36 +04:00
struct ci_hdrc * ci = platform_get_drvdata ( pdev ) ;
2012-05-11 18:25:46 +04:00
2013-03-30 14:53:53 +04:00
dbg_remove_files ( ci ) ;
2012-05-11 18:25:47 +04:00
free_irq ( ci - > irq , ci ) ;
2013-08-14 13:44:07 +04:00
ci_role_destroy ( ci ) ;
2013-09-24 08:47:55 +04:00
ci_hdrc_enter_lpm ( ci , true ) ;
2014-02-19 09:41:40 +04:00
usb_phy_shutdown ( ci - > transceiver ) ;
kfree ( ci - > hw_bank . regmap ) ;
2012-05-11 18:25:46 +04:00
return 0 ;
}
2012-05-11 18:25:47 +04:00
static struct platform_driver ci_hdrc_driver = {
. probe = ci_hdrc_probe ,
2012-11-19 22:21:08 +04:00
. remove = ci_hdrc_remove ,
2012-05-11 18:25:46 +04:00
. driver = {
2012-05-11 18:25:47 +04:00
. name = " ci_hdrc " ,
2014-04-23 11:56:42 +04:00
. owner = THIS_MODULE ,
2012-05-11 18:25:46 +04:00
} ,
} ;
2012-05-11 18:25:47 +04:00
module_platform_driver ( ci_hdrc_driver ) ;
2012-05-11 18:25:46 +04:00
2012-05-11 18:25:47 +04:00
MODULE_ALIAS ( " platform:ci_hdrc " ) ;
2012-05-11 18:25:46 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " David Lopo <dlopo@chipidea.mips.com> " ) ;
2012-05-11 18:25:47 +04:00
MODULE_DESCRIPTION ( " ChipIdea HDRC Driver " ) ;