2012-11-01 06:03:11 +04:00
/*
* Renesas R - Car USB phy driver
*
2013-06-02 01:57:18 +04:00
* Copyright ( C ) 2012 - 2013 Renesas Solutions Corp .
2012-11-01 06:03:11 +04:00
* Kuninori Morimoto < kuninori . morimoto . gx @ renesas . com >
2013-06-02 01:57:18 +04:00
* Copyright ( C ) 2013 Cogent Embedded , Inc .
2012-11-01 06:03:11 +04:00
*
* 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 .
*/
# include <linux/delay.h>
# include <linux/io.h>
# include <linux/usb/otg.h>
# include <linux/platform_device.h>
# include <linux/spinlock.h>
# include <linux/module.h>
2013-06-02 01:57:18 +04:00
# include <linux/platform_data/usb-rcar-phy.h>
2012-11-01 06:03:11 +04:00
2013-06-02 01:50:25 +04:00
/* REGS block */
# define USBPCTRL0 0x00
# define USBPCTRL1 0x04
# define USBST 0x08
# define USBEH0 0x0C
# define USBOH0 0x1C
# define USBCTL0 0x58
2012-11-01 06:03:11 +04:00
2013-06-09 00:34:36 +04:00
/* High-speed signal quality characteristic control registers (R8A7778 only) */
# define HSQCTL1 0x24
# define HSQCTL2 0x28
2013-06-02 01:57:18 +04:00
/* USBPCTRL0 */
2013-06-09 00:34:36 +04:00
# define OVC2 (1 << 10) /* (R8A7779 only) */
/* Switches the OVC input pin for port 2: */
2013-06-02 01:57:18 +04:00
/* 1: USB_OVC2, 0: OVC2 */
# define OVC1_VBUS1 (1 << 9) /* Switches the OVC input pin for port 1: */
/* 1: USB_OVC1, 0: OVC1/VBUS1 */
/* Function mode: set to 0 */
# define OVC0 (1 << 8) /* Switches the OVC input pin for port 0: */
/* 1: USB_OVC0 pin, 0: OVC0 */
2013-06-09 00:34:36 +04:00
# define OVC2_ACT (1 << 6) /* (R8A7779 only) */
/* Host mode: OVC2 polarity: */
2013-06-02 01:57:18 +04:00
/* 1: active-high, 0: active-low */
# define PENC (1 << 4) /* Function mode: output level of PENC1 pin: */
/* 1: high, 0: low */
# define OVC0_ACT (1 << 3) /* Host mode: OVC0 polarity: */
/* 1: active-high, 0: active-low */
# define OVC1_ACT (1 << 1) /* Host mode: OVC1 polarity: */
/* 1: active-high, 0: active-low */
/* Function mode: be sure to set to 1 */
# define PORT1 (1 << 0) /* Selects port 1 mode: */
/* 1: function, 0: host */
2012-11-01 06:03:11 +04:00
/* USBPCTRL1 */
# define PHY_RST (1 << 2)
# define PLL_ENB (1 << 1)
# define PHY_ENB (1 << 0)
/* USBST */
# define ST_ACT (1 << 31)
# define ST_PLL (1 << 30)
struct rcar_usb_phy_priv {
struct usb_phy phy ;
spinlock_t lock ;
void __iomem * reg0 ;
2013-06-09 00:34:36 +04:00
void __iomem * reg1 ;
2012-11-01 06:03:11 +04:00
int counter ;
} ;
# define usb_phy_to_priv(p) container_of(p, struct rcar_usb_phy_priv, phy)
/*
* USB initial / install operation .
*
* This function setup USB phy .
* The used value and setting order came from
* [ USB : : Initial setting ] on datasheet .
*/
static int rcar_usb_phy_init ( struct usb_phy * phy )
{
struct rcar_usb_phy_priv * priv = usb_phy_to_priv ( phy ) ;
struct device * dev = phy - > dev ;
2013-07-30 12:02:13 +04:00
struct rcar_phy_platform_data * pdata = dev_get_platdata ( dev ) ;
2012-11-01 06:03:11 +04:00
void __iomem * reg0 = priv - > reg0 ;
2013-06-09 00:34:36 +04:00
void __iomem * reg1 = priv - > reg1 ;
2013-06-02 01:57:18 +04:00
static const u8 ovcn_act [ ] = { OVC0_ACT , OVC1_ACT , OVC2_ACT } ;
2012-11-01 06:03:11 +04:00
int i ;
u32 val ;
unsigned long flags ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
if ( priv - > counter + + = = 0 ) {
/*
* USB phy start - up
*/
/* (1) USB-PHY standby release */
iowrite32 ( PHY_ENB , ( reg0 + USBPCTRL1 ) ) ;
/* (2) start USB-PHY internal PLL */
iowrite32 ( PHY_ENB | PLL_ENB , ( reg0 + USBPCTRL1 ) ) ;
2013-06-09 00:34:36 +04:00
/* (3) set USB-PHY in accord with the conditions of usage */
if ( reg1 ) {
u32 hsqctl1 = pdata - > ferrite_bead ? 0x41 : 0 ;
u32 hsqctl2 = pdata - > ferrite_bead ? 0x0d : 7 ;
iowrite32 ( hsqctl1 , reg1 + HSQCTL1 ) ;
iowrite32 ( hsqctl2 , reg1 + HSQCTL2 ) ;
}
/* (4) USB module status check */
2012-11-01 06:03:11 +04:00
for ( i = 0 ; i < 1024 ; i + + ) {
udelay ( 10 ) ;
val = ioread32 ( reg0 + USBST ) ;
if ( val = = ( ST_ACT | ST_PLL ) )
break ;
}
if ( val ! = ( ST_ACT | ST_PLL ) ) {
dev_err ( dev , " USB phy not ready \n " ) ;
goto phy_init_end ;
}
2013-06-09 00:34:36 +04:00
/* (5) USB-PHY reset clear */
2012-11-01 06:03:11 +04:00
iowrite32 ( PHY_ENB | PLL_ENB | PHY_RST , ( reg0 + USBPCTRL1 ) ) ;
2013-06-02 01:57:18 +04:00
/* Board specific port settings */
val = 0 ;
if ( pdata - > port1_func )
val | = PORT1 ;
if ( pdata - > penc1 )
val | = PENC ;
for ( i = 0 ; i < 3 ; i + + ) {
/* OVCn bits follow each other in the right order */
if ( pdata - > ovc_pin [ i ] . select_3_3v )
val | = OVC0 < < i ;
/* OVCn_ACT bits are spaced by irregular intervals */
if ( pdata - > ovc_pin [ i ] . active_high )
val | = ovcn_act [ i ] ;
}
iowrite32 ( val , ( reg0 + USBPCTRL0 ) ) ;
2012-11-01 06:03:11 +04:00
/*
* Bus alignment settings
*/
/* (1) EHCI bus alignment (little endian) */
iowrite32 ( 0x00000000 , ( reg0 + USBEH0 ) ) ;
/* (1) OHCI bus alignment (little endian) */
iowrite32 ( 0x00000000 , ( reg0 + USBOH0 ) ) ;
}
phy_init_end :
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
return 0 ;
}
static void rcar_usb_phy_shutdown ( struct usb_phy * phy )
{
struct rcar_usb_phy_priv * priv = usb_phy_to_priv ( phy ) ;
void __iomem * reg0 = priv - > reg0 ;
unsigned long flags ;
spin_lock_irqsave ( & priv - > lock , flags ) ;
2013-06-02 01:57:18 +04:00
if ( priv - > counter - - = = 1 ) /* last user */
2012-11-01 06:03:11 +04:00
iowrite32 ( 0x00000000 , ( reg0 + USBPCTRL1 ) ) ;
spin_unlock_irqrestore ( & priv - > lock , flags ) ;
}
2012-11-19 22:21:48 +04:00
static int rcar_usb_phy_probe ( struct platform_device * pdev )
2012-11-01 06:03:11 +04:00
{
struct rcar_usb_phy_priv * priv ;
2013-06-09 00:34:36 +04:00
struct resource * res0 , * res1 ;
2012-11-01 06:03:11 +04:00
struct device * dev = & pdev - > dev ;
2013-06-09 00:34:36 +04:00
void __iomem * reg0 , * reg1 = NULL ;
2012-11-01 06:03:11 +04:00
int ret ;
2013-07-30 12:02:13 +04:00
if ( ! dev_get_platdata ( & pdev - > dev ) ) {
2013-06-02 01:57:18 +04:00
dev_err ( dev , " No platform data \n " ) ;
return - EINVAL ;
}
2012-11-01 06:03:11 +04:00
res0 = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-06-02 01:50:25 +04:00
reg0 = devm_ioremap_resource ( dev , res0 ) ;
if ( IS_ERR ( reg0 ) )
return PTR_ERR ( reg0 ) ;
2012-11-01 06:03:11 +04:00
2013-06-09 00:34:36 +04:00
res1 = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
if ( res1 ) {
reg1 = devm_ioremap_resource ( dev , res1 ) ;
if ( IS_ERR ( reg1 ) )
return PTR_ERR ( reg1 ) ;
}
2012-11-01 06:03:11 +04:00
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv ) {
dev_err ( dev , " priv data allocation error \n " ) ;
return - ENOMEM ;
}
priv - > reg0 = reg0 ;
2013-06-09 00:34:36 +04:00
priv - > reg1 = reg1 ;
2012-11-01 06:03:11 +04:00
priv - > counter = 0 ;
priv - > phy . dev = dev ;
priv - > phy . label = dev_name ( dev ) ;
priv - > phy . init = rcar_usb_phy_init ;
priv - > phy . shutdown = rcar_usb_phy_shutdown ;
spin_lock_init ( & priv - > lock ) ;
ret = usb_add_phy ( & priv - > phy , USB_PHY_TYPE_USB2 ) ;
if ( ret < 0 ) {
dev_err ( dev , " usb phy addition error \n " ) ;
return ret ;
}
platform_set_drvdata ( pdev , priv ) ;
return ret ;
}
2012-11-19 22:26:20 +04:00
static int rcar_usb_phy_remove ( struct platform_device * pdev )
2012-11-01 06:03:11 +04:00
{
struct rcar_usb_phy_priv * priv = platform_get_drvdata ( pdev ) ;
usb_remove_phy ( & priv - > phy ) ;
return 0 ;
}
static struct platform_driver rcar_usb_phy_driver = {
. driver = {
. name = " rcar_usb_phy " ,
} ,
. probe = rcar_usb_phy_probe ,
2012-11-19 22:21:08 +04:00
. remove = rcar_usb_phy_remove ,
2012-11-01 06:03:11 +04:00
} ;
module_platform_driver ( rcar_usb_phy_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " Renesas R-Car USB phy " ) ;
MODULE_AUTHOR ( " Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> " ) ;