2012-09-12 14:58:05 +03:00
/*
* Copyright 2012 Freescale Semiconductor , Inc .
*
* The code contained herein is licensed under the GNU General Public
* License . You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations :
*
* http : //www.opensource.org/licenses/gpl-license.html
* http : //www.gnu.org/copyleft/gpl.html
*/
# include <linux/module.h>
# include <linux/of_platform.h>
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/io.h>
2013-03-30 12:54:01 +02:00
# include <linux/delay.h>
2012-09-12 14:58:05 +03:00
2013-06-24 14:46:36 +03:00
# include "ci_hdrc_imx.h"
2012-09-12 14:58:05 +03:00
2013-03-30 12:54:01 +02:00
# define MX25_USB_PHY_CTRL_OFFSET 0x08
# define MX25_BM_EXTERNAL_VBUS_DIVIDER BIT(23)
2014-04-23 15:56:36 +08:00
# define MX25_EHCI_INTERFACE_SINGLE_UNI (2 << 0)
# define MX25_EHCI_INTERFACE_DIFF_UNI (0 << 0)
# define MX25_EHCI_INTERFACE_MASK (0xf)
# define MX25_OTG_SIC_SHIFT 29
# define MX25_OTG_SIC_MASK (0x3 << MX25_OTG_SIC_SHIFT)
# define MX25_OTG_PM_BIT BIT(24)
# define MX25_OTG_PP_BIT BIT(11)
# define MX25_OTG_OCPOL_BIT BIT(3)
# define MX25_H1_SIC_SHIFT 21
# define MX25_H1_SIC_MASK (0x3 << MX25_H1_SIC_SHIFT)
# define MX25_H1_PP_BIT BIT(18)
# define MX25_H1_PM_BIT BIT(16)
# define MX25_H1_IPPUE_UP_BIT BIT(7)
# define MX25_H1_IPPUE_DOWN_BIT BIT(6)
# define MX25_H1_TLL_BIT BIT(5)
# define MX25_H1_USBTE_BIT BIT(4)
# define MX25_H1_OCPOL_BIT BIT(2)
2013-12-06 16:35:14 +08:00
# define MX27_H1_PM_BIT BIT(8)
# define MX27_H2_PM_BIT BIT(16)
# define MX27_OTG_PM_BIT BIT(24)
2013-03-30 12:54:00 +02:00
# define MX53_USB_OTG_PHY_CTRL_0_OFFSET 0x08
2014-05-04 09:24:39 +08:00
# define MX53_USB_OTG_PHY_CTRL_1_OFFSET 0x0c
2013-03-30 12:54:00 +02:00
# define MX53_USB_UH2_CTRL_OFFSET 0x14
# define MX53_USB_UH3_CTRL_OFFSET 0x18
# define MX53_BM_OVER_CUR_DIS_H1 BIT(5)
# define MX53_BM_OVER_CUR_DIS_OTG BIT(8)
# define MX53_BM_OVER_CUR_DIS_UHx BIT(30)
2014-05-04 09:24:39 +08:00
# define MX53_USB_PHYCTRL1_PLLDIV_MASK 0x3
# define MX53_USB_PLL_DIV_24_MHZ 0x01
2013-03-30 12:54:00 +02:00
2013-03-30 12:53:59 +02:00
# define MX6_BM_OVER_CUR_DIS BIT(7)
2012-09-12 14:58:05 +03:00
2014-09-22 08:14:15 +08:00
# define VF610_OVER_CUR_DIS BIT(7)
2013-08-14 12:44:16 +03:00
struct usbmisc_ops {
/* It's called once when probe a usb device */
int ( * init ) ( struct imx_usbmisc_data * data ) ;
/* It's called once after adding a usb device */
int ( * post ) ( struct imx_usbmisc_data * data ) ;
} ;
2013-03-30 12:53:56 +02:00
struct imx_usbmisc {
2012-09-12 14:58:05 +03:00
void __iomem * base ;
spinlock_t lock ;
struct clk * clk ;
2013-03-30 12:53:59 +02:00
const struct usbmisc_ops * ops ;
2012-09-12 14:58:05 +03:00
} ;
2014-04-23 15:56:36 +08:00
static int usbmisc_imx25_init ( struct imx_usbmisc_data * data )
{
2014-09-22 08:14:15 +08:00
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
2014-04-23 15:56:36 +08:00
unsigned long flags ;
u32 val = 0 ;
if ( data - > index > 1 )
return - EINVAL ;
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
switch ( data - > index ) {
case 0 :
val = readl ( usbmisc - > base ) ;
val & = ~ ( MX25_OTG_SIC_MASK | MX25_OTG_PP_BIT ) ;
val | = ( MX25_EHCI_INTERFACE_DIFF_UNI & MX25_EHCI_INTERFACE_MASK ) < < MX25_OTG_SIC_SHIFT ;
val | = ( MX25_OTG_PM_BIT | MX25_OTG_OCPOL_BIT ) ;
writel ( val , usbmisc - > base ) ;
break ;
case 1 :
val = readl ( usbmisc - > base ) ;
val & = ~ ( MX25_H1_SIC_MASK | MX25_H1_PP_BIT | MX25_H1_IPPUE_UP_BIT ) ;
val | = ( MX25_EHCI_INTERFACE_SINGLE_UNI & MX25_EHCI_INTERFACE_MASK ) < < MX25_H1_SIC_SHIFT ;
val | = ( MX25_H1_PM_BIT | MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT |
MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT ) ;
writel ( val , usbmisc - > base ) ;
break ;
}
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
return 0 ;
}
2013-08-14 12:44:16 +03:00
static int usbmisc_imx25_post ( struct imx_usbmisc_data * data )
2013-03-30 12:54:01 +02:00
{
2014-09-22 08:14:15 +08:00
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
2013-03-30 12:54:01 +02:00
void __iomem * reg ;
unsigned long flags ;
u32 val ;
2013-08-14 12:44:16 +03:00
if ( data - > index > 2 )
return - EINVAL ;
2013-03-30 12:54:01 +02:00
2013-08-14 12:44:16 +03:00
if ( data - > evdo ) {
2013-03-30 12:54:01 +02:00
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
2014-11-26 13:44:25 +08:00
reg = usbmisc - > base + MX25_USB_PHY_CTRL_OFFSET ;
2013-03-30 12:54:01 +02:00
val = readl ( reg ) ;
writel ( val | MX25_BM_EXTERNAL_VBUS_DIVIDER , reg ) ;
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
usleep_range ( 5000 , 10000 ) ; /* needed to stabilize voltage */
}
return 0 ;
}
2013-12-06 16:35:14 +08:00
static int usbmisc_imx27_init ( struct imx_usbmisc_data * data )
{
2014-09-22 08:14:15 +08:00
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
2013-12-06 16:35:14 +08:00
unsigned long flags ;
u32 val ;
switch ( data - > index ) {
case 0 :
val = MX27_OTG_PM_BIT ;
break ;
case 1 :
val = MX27_H1_PM_BIT ;
break ;
case 2 :
val = MX27_H2_PM_BIT ;
break ;
default :
return - EINVAL ;
} ;
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
if ( data - > disable_oc )
val = readl ( usbmisc - > base ) | val ;
else
val = readl ( usbmisc - > base ) & ~ val ;
writel ( val , usbmisc - > base ) ;
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
return 0 ;
}
2013-08-14 12:44:16 +03:00
static int usbmisc_imx53_init ( struct imx_usbmisc_data * data )
2013-03-30 12:54:00 +02:00
{
2014-09-22 08:14:15 +08:00
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
2013-03-30 12:54:00 +02:00
void __iomem * reg = NULL ;
unsigned long flags ;
u32 val = 0 ;
2013-08-14 12:44:16 +03:00
if ( data - > index > 3 )
return - EINVAL ;
2013-03-30 12:54:00 +02:00
2014-05-04 09:24:39 +08:00
/* Select a 24 MHz reference clock for the PHY */
2014-11-26 13:44:26 +08:00
val = readl ( usbmisc - > base + MX53_USB_OTG_PHY_CTRL_1_OFFSET ) ;
2014-05-04 09:24:39 +08:00
val & = ~ MX53_USB_PHYCTRL1_PLLDIV_MASK ;
val | = MX53_USB_PLL_DIV_24_MHZ ;
writel ( val , usbmisc - > base + MX53_USB_OTG_PHY_CTRL_1_OFFSET ) ;
2013-08-14 12:44:16 +03:00
if ( data - > disable_oc ) {
2013-03-30 12:54:00 +02:00
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
2013-08-14 12:44:16 +03:00
switch ( data - > index ) {
2013-03-30 12:54:00 +02:00
case 0 :
reg = usbmisc - > base + MX53_USB_OTG_PHY_CTRL_0_OFFSET ;
val = readl ( reg ) | MX53_BM_OVER_CUR_DIS_OTG ;
break ;
case 1 :
reg = usbmisc - > base + MX53_USB_OTG_PHY_CTRL_0_OFFSET ;
val = readl ( reg ) | MX53_BM_OVER_CUR_DIS_H1 ;
break ;
case 2 :
reg = usbmisc - > base + MX53_USB_UH2_CTRL_OFFSET ;
val = readl ( reg ) | MX53_BM_OVER_CUR_DIS_UHx ;
break ;
case 3 :
reg = usbmisc - > base + MX53_USB_UH3_CTRL_OFFSET ;
val = readl ( reg ) | MX53_BM_OVER_CUR_DIS_UHx ;
break ;
}
if ( reg & & val )
writel ( val , reg ) ;
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
}
return 0 ;
}
2013-08-14 12:44:16 +03:00
static int usbmisc_imx6q_init ( struct imx_usbmisc_data * data )
2012-09-12 14:58:05 +03:00
{
2014-09-22 08:14:15 +08:00
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
2012-09-12 14:58:05 +03:00
unsigned long flags ;
u32 reg ;
2013-08-14 12:44:16 +03:00
if ( data - > index > 3 )
return - EINVAL ;
2012-09-12 14:58:05 +03:00
2013-08-14 12:44:16 +03:00
if ( data - > disable_oc ) {
2012-09-12 14:58:05 +03:00
spin_lock_irqsave ( & usbmisc - > lock , flags ) ;
2013-08-14 12:44:16 +03:00
reg = readl ( usbmisc - > base + data - > index * 4 ) ;
2013-03-30 12:53:59 +02:00
writel ( reg | MX6_BM_OVER_CUR_DIS ,
2013-08-14 12:44:16 +03:00
usbmisc - > base + data - > index * 4 ) ;
2012-09-12 14:58:05 +03:00
spin_unlock_irqrestore ( & usbmisc - > lock , flags ) ;
}
return 0 ;
}
2014-09-22 08:14:15 +08:00
static int usbmisc_vf610_init ( struct imx_usbmisc_data * data )
{
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
u32 reg ;
/*
* Vybrid only has one misc register set , but in two different
* areas . These is reflected in two instances of this driver .
*/
if ( data - > index > = 1 )
return - EINVAL ;
if ( data - > disable_oc ) {
reg = readl ( usbmisc - > base ) ;
writel ( reg | VF610_OVER_CUR_DIS , usbmisc - > base ) ;
}
return 0 ;
}
2013-03-30 12:54:01 +02:00
static const struct usbmisc_ops imx25_usbmisc_ops = {
2014-04-23 15:56:36 +08:00
. init = usbmisc_imx25_init ,
2013-03-30 12:54:01 +02:00
. post = usbmisc_imx25_post ,
} ;
2013-12-06 16:35:14 +08:00
static const struct usbmisc_ops imx27_usbmisc_ops = {
. init = usbmisc_imx27_init ,
} ;
2013-03-30 12:54:00 +02:00
static const struct usbmisc_ops imx53_usbmisc_ops = {
. init = usbmisc_imx53_init ,
} ;
2012-09-12 14:58:05 +03:00
static const struct usbmisc_ops imx6q_usbmisc_ops = {
. init = usbmisc_imx6q_init ,
} ;
2014-09-22 08:14:15 +08:00
static const struct usbmisc_ops vf610_usbmisc_ops = {
. init = usbmisc_vf610_init ,
} ;
2013-08-14 12:44:16 +03:00
int imx_usbmisc_init ( struct imx_usbmisc_data * data )
{
2014-09-22 08:14:15 +08:00
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
2013-08-14 12:44:16 +03:00
if ( ! usbmisc - > ops - > init )
return 0 ;
return usbmisc - > ops - > init ( data ) ;
}
EXPORT_SYMBOL_GPL ( imx_usbmisc_init ) ;
int imx_usbmisc_init_post ( struct imx_usbmisc_data * data )
{
2014-09-22 08:14:15 +08:00
struct imx_usbmisc * usbmisc = dev_get_drvdata ( data - > dev ) ;
2013-08-14 12:44:16 +03:00
if ( ! usbmisc - > ops - > post )
return 0 ;
return usbmisc - > ops - > post ( data ) ;
}
EXPORT_SYMBOL_GPL ( imx_usbmisc_init_post ) ;
2013-03-30 12:53:56 +02:00
static const struct of_device_id usbmisc_imx_dt_ids [ ] = {
2013-03-30 12:54:01 +02:00
{
. compatible = " fsl,imx25-usbmisc " ,
. data = & imx25_usbmisc_ops ,
} ,
2014-04-23 15:56:36 +08:00
{
. compatible = " fsl,imx35-usbmisc " ,
. data = & imx25_usbmisc_ops ,
} ,
2013-12-06 16:35:14 +08:00
{
. compatible = " fsl,imx27-usbmisc " ,
. data = & imx27_usbmisc_ops ,
} ,
2013-12-06 16:35:15 +08:00
{
. compatible = " fsl,imx51-usbmisc " ,
. data = & imx53_usbmisc_ops ,
} ,
2013-03-30 12:54:00 +02:00
{
. compatible = " fsl,imx53-usbmisc " ,
. data = & imx53_usbmisc_ops ,
} ,
2013-03-30 12:53:59 +02:00
{
. compatible = " fsl,imx6q-usbmisc " ,
. data = & imx6q_usbmisc_ops ,
} ,
2014-09-22 08:14:15 +08:00
{
. compatible = " fsl,vf610-usbmisc " ,
. data = & vf610_usbmisc_ops ,
} ,
2012-09-12 14:58:05 +03:00
{ /* sentinel */ }
} ;
2013-06-20 23:33:25 +02:00
MODULE_DEVICE_TABLE ( of , usbmisc_imx_dt_ids ) ;
2012-09-12 14:58:05 +03:00
2013-03-30 12:53:56 +02:00
static int usbmisc_imx_probe ( struct platform_device * pdev )
2012-09-12 14:58:05 +03:00
{
struct resource * res ;
2013-03-30 12:53:56 +02:00
struct imx_usbmisc * data ;
2012-09-12 14:58:05 +03:00
int ret ;
2013-03-30 12:53:59 +02:00
struct of_device_id * tmp_dev ;
2012-09-12 14:58:05 +03:00
data = devm_kzalloc ( & pdev - > dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
spin_lock_init ( & data - > lock ) ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-01-21 11:09:22 +01:00
data - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( data - > base ) )
return PTR_ERR ( data - > base ) ;
2012-09-12 14:58:05 +03:00
data - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( data - > clk ) ) {
dev_err ( & pdev - > dev ,
" failed to get clock, err=%ld \n " , PTR_ERR ( data - > clk ) ) ;
return PTR_ERR ( data - > clk ) ;
}
ret = clk_prepare_enable ( data - > clk ) ;
if ( ret ) {
dev_err ( & pdev - > dev ,
" clk_prepare_enable failed, err=%d \n " , ret ) ;
return ret ;
}
2013-03-30 12:53:59 +02:00
tmp_dev = ( struct of_device_id * )
of_match_device ( usbmisc_imx_dt_ids , & pdev - > dev ) ;
data - > ops = ( const struct usbmisc_ops * ) tmp_dev - > data ;
2014-09-22 08:14:15 +08:00
platform_set_drvdata ( pdev , data ) ;
2012-09-12 14:58:05 +03:00
return 0 ;
}
2013-03-30 12:53:56 +02:00
static int usbmisc_imx_remove ( struct platform_device * pdev )
2012-09-12 14:58:05 +03:00
{
2014-09-22 08:14:15 +08:00
struct imx_usbmisc * usbmisc = dev_get_drvdata ( & pdev - > dev ) ;
2012-09-12 14:58:05 +03:00
clk_disable_unprepare ( usbmisc - > clk ) ;
return 0 ;
}
2013-03-30 12:53:56 +02:00
static struct platform_driver usbmisc_imx_driver = {
. probe = usbmisc_imx_probe ,
. remove = usbmisc_imx_remove ,
2012-09-12 14:58:05 +03:00
. driver = {
2013-03-30 12:53:56 +02:00
. name = " usbmisc_imx " ,
. of_match_table = usbmisc_imx_dt_ids ,
2012-09-12 14:58:05 +03:00
} ,
} ;
2013-06-13 17:59:59 +03:00
module_platform_driver ( usbmisc_imx_driver ) ;
2012-09-12 14:58:05 +03:00
2013-03-30 12:53:56 +02:00
MODULE_ALIAS ( " platform:usbmisc-imx " ) ;
2012-09-12 14:58:05 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;
2013-03-30 12:53:56 +02:00
MODULE_DESCRIPTION ( " driver for imx usb non-core registers " ) ;
2012-09-12 14:58:05 +03:00
MODULE_AUTHOR ( " Richard Zhao <richard.zhao@freescale.com> " ) ;