2010-12-09 13:05:01 +01:00
/*
* Copyright ( C ) 2010 ST - Ericsson AB
* Mian Yousaf Kaukab < mian . yousaf . kaukab @ stericsson . com >
*
* Based on omap2430 . c
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/clk.h>
2012-06-26 17:40:32 +05:30
# include <linux/err.h>
2010-12-09 13:05:01 +01:00
# include <linux/io.h>
2013-05-15 10:51:48 +01:00
# include <linux/of.h>
2010-12-09 13:05:01 +01:00
# include <linux/platform_device.h>
2013-03-08 10:27:09 +08:00
# include <linux/usb/musb-ux500.h>
2010-12-09 13:05:01 +01:00
# include "musb_core.h"
2013-05-15 10:51:44 +01:00
static struct musb_hdrc_config ux500_musb_hdrc_config = {
. multipoint = true ,
. dyn_fifo = true ,
. num_eps = 16 ,
. ram_bits = 16 ,
} ;
2010-12-09 13:05:01 +01:00
struct ux500_glue {
struct device * dev ;
struct platform_device * musb ;
struct clk * clk ;
} ;
# define glue_to_musb(g) platform_get_drvdata(g->musb)
2013-03-08 10:27:06 +08:00
static void ux500_musb_set_vbus ( struct musb * musb , int is_on )
{
u8 devctl ;
unsigned long timeout = jiffies + msecs_to_jiffies ( 1000 ) ;
/* HDRC controls CPEN, but beware current surges during device
* connect . They can trigger transient overcurrent conditions
* that must be ignored .
*/
devctl = musb_readb ( musb - > mregs , MUSB_DEVCTL ) ;
if ( is_on ) {
2014-10-30 18:41:13 +01:00
if ( musb - > xceiv - > otg - > state = = OTG_STATE_A_IDLE ) {
2013-03-08 10:27:06 +08:00
/* start the session */
devctl | = MUSB_DEVCTL_SESSION ;
musb_writeb ( musb - > mregs , MUSB_DEVCTL , devctl ) ;
/*
* Wait for the musb to set as A device to enable the
* VBUS
*/
while ( musb_readb ( musb - > mregs , MUSB_DEVCTL ) & 0x80 ) {
if ( time_after ( jiffies , timeout ) ) {
dev_err ( musb - > controller ,
" configured as A device timeout " ) ;
break ;
}
}
} else {
musb - > is_active = 1 ;
musb - > xceiv - > otg - > default_a = 1 ;
2014-10-30 18:41:13 +01:00
musb - > xceiv - > otg - > state = OTG_STATE_A_WAIT_VRISE ;
2013-03-08 10:27:06 +08:00
devctl | = MUSB_DEVCTL_SESSION ;
MUSB_HST_MODE ( musb ) ;
}
} else {
musb - > is_active = 0 ;
/* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and jumping
* right to B_IDLE . . .
*/
musb - > xceiv - > otg - > default_a = 0 ;
devctl & = ~ MUSB_DEVCTL_SESSION ;
MUSB_DEV_MODE ( musb ) ;
}
musb_writeb ( musb - > mregs , MUSB_DEVCTL , devctl ) ;
/*
* Devctl values will be updated after vbus goes below
* session_valid . The time taken depends on the capacitance
* on VBUS line . The max discharge time can be upto 1 sec
* as per the spec . Typically on our platform , it is 200 ms
*/
if ( ! is_on )
mdelay ( 200 ) ;
dev_dbg ( musb - > controller , " VBUS %s, devctl %02x \n " ,
2014-10-30 18:41:13 +01:00
usb_otg_state_string ( musb - > xceiv - > otg - > state ) ,
2013-03-08 10:27:06 +08:00
musb_readb ( musb - > mregs , MUSB_DEVCTL ) ) ;
}
2013-03-08 10:27:07 +08:00
static int musb_otg_notifications ( struct notifier_block * nb ,
unsigned long event , void * unused )
{
struct musb * musb = container_of ( nb , struct musb , nb ) ;
dev_dbg ( musb - > controller , " musb_otg_notifications %ld %s \n " ,
2014-10-30 18:41:13 +01:00
event , usb_otg_state_string ( musb - > xceiv - > otg - > state ) ) ;
2013-03-08 10:27:07 +08:00
switch ( event ) {
2013-03-08 10:27:09 +08:00
case UX500_MUSB_ID :
2013-03-08 10:27:07 +08:00
dev_dbg ( musb - > controller , " ID GND \n " ) ;
ux500_musb_set_vbus ( musb , 1 ) ;
break ;
2013-03-08 10:27:09 +08:00
case UX500_MUSB_VBUS :
2013-03-08 10:27:07 +08:00
dev_dbg ( musb - > controller , " VBUS Connect \n " ) ;
break ;
2013-03-08 10:27:09 +08:00
case UX500_MUSB_NONE :
2013-03-08 10:27:07 +08:00
dev_dbg ( musb - > controller , " VBUS Disconnect \n " ) ;
if ( is_host_active ( musb ) )
ux500_musb_set_vbus ( musb , 0 ) ;
else
2014-10-30 18:41:13 +01:00
musb - > xceiv - > otg - > state = OTG_STATE_B_IDLE ;
2013-03-08 10:27:07 +08:00
break ;
default :
dev_dbg ( musb - > controller , " ID float \n " ) ;
return NOTIFY_DONE ;
}
return NOTIFY_OK ;
}
2012-11-06 15:32:13 +02:00
static irqreturn_t ux500_musb_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 )
retval = musb_interrupt ( musb ) ;
spin_unlock_irqrestore ( & musb - > lock , flags ) ;
return retval ;
}
2010-12-09 13:05:01 +01:00
static int ux500_musb_init ( struct musb * musb )
{
2013-03-08 10:27:07 +08:00
int status ;
2012-06-22 17:02:46 +05:30
musb - > xceiv = usb_get_phy ( USB_PHY_TYPE_USB2 ) ;
2012-06-26 17:40:32 +05:30
if ( IS_ERR_OR_NULL ( musb - > xceiv ) ) {
2010-12-09 13:05:01 +01:00
pr_err ( " HS USB OTG: no transceiver configured \n " ) ;
2013-01-04 23:13:58 +08:00
return - EPROBE_DEFER ;
2010-12-09 13:05:01 +01:00
}
2013-03-08 10:27:07 +08:00
musb - > nb . notifier_call = musb_otg_notifications ;
status = usb_register_notifier ( musb - > xceiv , & musb - > nb ) ;
if ( status < 0 ) {
dev_dbg ( musb - > controller , " notification register failed \n " ) ;
return status ;
}
2012-11-06 15:32:13 +02:00
musb - > isr = ux500_musb_interrupt ;
2010-12-09 13:05:01 +01:00
return 0 ;
}
static int ux500_musb_exit ( struct musb * musb )
{
2013-03-08 10:27:07 +08:00
usb_unregister_notifier ( musb - > xceiv , & musb - > nb ) ;
2012-06-22 17:02:45 +05:30
usb_put_phy ( musb - > xceiv ) ;
2010-12-09 13:05:01 +01:00
return 0 ;
}
static const struct musb_platform_ops ux500_ops = {
2014-11-24 11:05:03 -08:00
. quirks = MUSB_INDEXED_EP ,
2010-12-09 13:05:01 +01:00
. init = ux500_musb_init ,
. exit = ux500_musb_exit ,
2014-11-24 11:05:04 -08:00
. fifo_mode = 5 ,
2013-03-08 10:27:06 +08:00
. set_vbus = ux500_musb_set_vbus ,
2010-12-09 13:05:01 +01:00
} ;
2013-05-15 10:51:48 +01:00
static struct musb_hdrc_platform_data *
ux500_of_probe ( struct platform_device * pdev , struct device_node * np )
{
struct musb_hdrc_platform_data * pdata ;
const char * mode ;
int strlen ;
pdata = devm_kzalloc ( & pdev - > dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return NULL ;
mode = of_get_property ( np , " dr_mode " , & strlen ) ;
if ( ! mode ) {
dev_err ( & pdev - > dev , " No 'dr_mode' property found \n " ) ;
return NULL ;
}
if ( strlen > 0 ) {
if ( ! strcmp ( mode , " host " ) )
pdata - > mode = MUSB_HOST ;
if ( ! strcmp ( mode , " otg " ) )
pdata - > mode = MUSB_OTG ;
if ( ! strcmp ( mode , " peripheral " ) )
pdata - > mode = MUSB_PERIPHERAL ;
}
return pdata ;
}
2012-11-19 13:21:48 -05:00
static int ux500_probe ( struct platform_device * pdev )
2010-12-09 13:05:01 +01:00
{
2013-04-24 17:21:42 +03:00
struct resource musb_resources [ 2 ] ;
2013-07-30 17:03:12 +09:00
struct musb_hdrc_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
2013-05-15 10:51:48 +01:00
struct device_node * np = pdev - > dev . of_node ;
2010-12-09 13:05:01 +01:00
struct platform_device * musb ;
struct ux500_glue * glue ;
struct clk * clk ;
int ret = - ENOMEM ;
2013-05-15 10:51:48 +01:00
if ( ! pdata ) {
if ( np ) {
pdata = ux500_of_probe ( pdev , np ) ;
if ( ! pdata )
goto err0 ;
pdev - > dev . platform_data = pdata ;
} else {
dev_err ( & pdev - > dev , " no pdata or device tree found \n " ) ;
goto err0 ;
}
}
2014-06-21 02:10:25 +05:30
glue = devm_kzalloc ( & pdev - > dev , sizeof ( * glue ) , GFP_KERNEL ) ;
2014-10-14 15:56:07 +08:00
if ( ! glue )
2010-12-09 13:05:01 +01:00
goto err0 ;
2012-10-31 16:12:43 +01:00
musb = platform_device_alloc ( " musb-hdrc " , PLATFORM_DEVID_AUTO ) ;
2010-12-09 13:05:01 +01:00
if ( ! musb ) {
dev_err ( & pdev - > dev , " failed to allocate musb device \n " ) ;
2014-06-21 02:10:25 +05:30
goto err0 ;
2010-12-09 13:05:01 +01:00
}
2014-06-21 02:10:25 +05:30
clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2010-12-09 13:05:01 +01:00
if ( IS_ERR ( clk ) ) {
dev_err ( & pdev - > dev , " failed to get clock \n " ) ;
ret = PTR_ERR ( clk ) ;
2014-06-21 02:10:25 +05:30
goto err1 ;
2010-12-09 13:05:01 +01:00
}
2013-01-07 17:47:41 +01:00
ret = clk_prepare_enable ( clk ) ;
2010-12-09 13:05:01 +01:00
if ( ret ) {
dev_err ( & pdev - > dev , " failed to enable clock \n " ) ;
2014-06-21 02:10:25 +05:30
goto err1 ;
2010-12-09 13:05:01 +01:00
}
musb - > dev . parent = & pdev - > dev ;
2013-05-15 10:51:45 +01:00
musb - > dev . dma_mask = & pdev - > dev . coherent_dma_mask ;
2011-03-15 16:24:29 +01:00
musb - > dev . coherent_dma_mask = pdev - > dev . coherent_dma_mask ;
2010-12-09 13:05:01 +01:00
glue - > dev = & pdev - > dev ;
glue - > musb = musb ;
glue - > clk = clk ;
pdata - > platform_ops = & ux500_ops ;
2013-05-15 10:51:44 +01:00
pdata - > config = & ux500_musb_hdrc_config ;
2010-12-09 13:05:01 +01:00
platform_set_drvdata ( pdev , glue ) ;
2013-04-24 17:21:42 +03:00
memset ( musb_resources , 0x00 , sizeof ( * musb_resources ) *
ARRAY_SIZE ( musb_resources ) ) ;
musb_resources [ 0 ] . name = pdev - > resource [ 0 ] . name ;
musb_resources [ 0 ] . start = pdev - > resource [ 0 ] . start ;
musb_resources [ 0 ] . end = pdev - > resource [ 0 ] . end ;
musb_resources [ 0 ] . flags = pdev - > resource [ 0 ] . flags ;
musb_resources [ 1 ] . name = pdev - > resource [ 1 ] . name ;
musb_resources [ 1 ] . start = pdev - > resource [ 1 ] . start ;
musb_resources [ 1 ] . end = pdev - > resource [ 1 ] . end ;
musb_resources [ 1 ] . flags = pdev - > resource [ 1 ] . flags ;
ret = platform_device_add_resources ( musb , musb_resources ,
ARRAY_SIZE ( musb_resources ) ) ;
2010-12-09 13:05:01 +01:00
if ( ret ) {
dev_err ( & pdev - > dev , " failed to add resources \n " ) ;
2014-06-21 02:10:25 +05:30
goto err2 ;
2010-12-09 13:05:01 +01:00
}
ret = platform_device_add_data ( musb , pdata , sizeof ( * pdata ) ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to add platform_data \n " ) ;
2014-06-21 02:10:25 +05:30
goto err2 ;
2010-12-09 13:05:01 +01:00
}
ret = platform_device_add ( musb ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " failed to register musb device \n " ) ;
2014-06-21 02:10:25 +05:30
goto err2 ;
2010-12-09 13:05:01 +01:00
}
return 0 ;
2014-06-21 02:10:25 +05:30
err2 :
2013-01-07 17:47:41 +01:00
clk_disable_unprepare ( clk ) ;
2010-12-09 13:05:01 +01:00
err1 :
2014-06-21 02:10:25 +05:30
platform_device_put ( musb ) ;
2010-12-09 13:05:01 +01:00
err0 :
return ret ;
}
2012-11-19 13:26:20 -05:00
static int ux500_remove ( struct platform_device * pdev )
2010-12-09 13:05:01 +01:00
{
struct ux500_glue * glue = platform_get_drvdata ( pdev ) ;
2012-10-23 13:36:43 +08:00
platform_device_unregister ( glue - > musb ) ;
2013-01-07 17:47:41 +01:00
clk_disable_unprepare ( glue - > clk ) ;
2010-12-09 13:05:01 +01:00
return 0 ;
}
# ifdef CONFIG_PM
static int ux500_suspend ( struct device * dev )
{
struct ux500_glue * glue = dev_get_drvdata ( dev ) ;
struct musb * musb = glue_to_musb ( glue ) ;
2012-02-13 13:24:18 +02:00
usb_phy_set_suspend ( musb - > xceiv , 1 ) ;
2013-01-07 17:47:41 +01:00
clk_disable_unprepare ( glue - > clk ) ;
2010-12-09 13:05:01 +01:00
return 0 ;
}
static int ux500_resume ( struct device * dev )
{
struct ux500_glue * glue = dev_get_drvdata ( dev ) ;
struct musb * musb = glue_to_musb ( glue ) ;
int ret ;
2013-01-07 17:47:41 +01:00
ret = clk_prepare_enable ( glue - > clk ) ;
2010-12-09 13:05:01 +01:00
if ( ret ) {
dev_err ( dev , " failed to enable clock \n " ) ;
return ret ;
}
2012-02-13 13:24:18 +02:00
usb_phy_set_suspend ( musb - > xceiv , 0 ) ;
2010-12-09 13:05:01 +01:00
return 0 ;
}
# endif
2013-09-30 21:02:09 +02:00
static SIMPLE_DEV_PM_OPS ( ux500_pm_ops , ux500_suspend , ux500_resume ) ;
2013-05-15 10:51:48 +01:00
static const struct of_device_id ux500_match [ ] = {
{ . compatible = " stericsson,db8500-musb " , } ,
{ }
} ;
2010-12-09 13:05:01 +01:00
static struct platform_driver ux500_driver = {
2012-01-26 12:40:23 +02:00
. probe = ux500_probe ,
2012-11-19 13:21:08 -05:00
. remove = ux500_remove ,
2010-12-09 13:05:01 +01:00
. driver = {
. name = " musb-ux500 " ,
2013-09-30 21:02:09 +02:00
. pm = & ux500_pm_ops ,
2013-05-15 10:51:48 +01:00
. of_match_table = ux500_match ,
2010-12-09 13:05:01 +01:00
} ,
} ;
MODULE_DESCRIPTION ( " UX500 MUSB Glue Layer " ) ;
MODULE_AUTHOR ( " Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
2012-10-10 19:37:21 +01:00
module_platform_driver ( ux500_driver ) ;