2009-08-18 16:23:37 +04:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* Copyright ( C ) 2008 Maxime Bizon < mbizon @ freebox . fr >
*/
2017-01-29 05:05:57 +03:00
# include <linux/init.h>
# include <linux/export.h>
2009-08-18 16:23:37 +04:00
# include <linux/mutex.h>
# include <linux/err.h>
# include <linux/clk.h>
2017-09-20 14:14:01 +03:00
# include <linux/clkdev.h>
2011-11-04 22:09:35 +04:00
# include <linux/delay.h>
2009-08-18 16:23:37 +04:00
# include <bcm63xx_cpu.h>
# include <bcm63xx_io.h>
# include <bcm63xx_regs.h>
2012-10-28 16:17:55 +04:00
# include <bcm63xx_reset.h>
2013-04-06 14:31:02 +04:00
struct clk {
void ( * set ) ( struct clk * , int ) ;
unsigned int rate ;
unsigned int usage ;
int id ;
} ;
2009-08-18 16:23:37 +04:00
static DEFINE_MUTEX ( clocks_mutex ) ;
static void clk_enable_unlocked ( struct clk * clk )
{
if ( clk - > set & & ( clk - > usage + + ) = = 0 )
clk - > set ( clk , 1 ) ;
}
static void clk_disable_unlocked ( struct clk * clk )
{
if ( clk - > set & & ( - - clk - > usage ) = = 0 )
clk - > set ( clk , 0 ) ;
}
static void bcm_hwclock_set ( u32 mask , int enable )
{
u32 reg ;
reg = bcm_perf_readl ( PERF_CKCTL_REG ) ;
if ( enable )
reg | = mask ;
else
reg & = ~ mask ;
bcm_perf_writel ( reg , PERF_CKCTL_REG ) ;
}
/*
* Ethernet MAC " misc " clock : dma clocks and main clock on 6348
*/
static void enet_misc_set ( struct clk * clk , int enable )
{
u32 mask ;
if ( BCMCPU_IS_6338 ( ) )
mask = CKCTL_6338_ENET_EN ;
else if ( BCMCPU_IS_6345 ( ) )
mask = CKCTL_6345_ENET_EN ;
else if ( BCMCPU_IS_6348 ( ) )
mask = CKCTL_6348_ENET_EN ;
else
/* BCMCPU_IS_6358 */
mask = CKCTL_6358_EMUSB_EN ;
bcm_hwclock_set ( mask , enable ) ;
}
static struct clk clk_enet_misc = {
. set = enet_misc_set ,
} ;
/*
* Ethernet MAC clocks : only revelant on 6358 , silently enable misc
* clocks
*/
static void enetx_set ( struct clk * clk , int enable )
{
if ( enable )
clk_enable_unlocked ( & clk_enet_misc ) ;
else
clk_disable_unlocked ( & clk_enet_misc ) ;
2013-06-18 20:55:40 +04:00
if ( BCMCPU_IS_3368 ( ) | | BCMCPU_IS_6358 ( ) ) {
2009-08-18 16:23:37 +04:00
u32 mask ;
if ( clk - > id = = 0 )
mask = CKCTL_6358_ENET0_EN ;
else
mask = CKCTL_6358_ENET1_EN ;
bcm_hwclock_set ( mask , enable ) ;
}
}
static struct clk clk_enet0 = {
. id = 0 ,
. set = enetx_set ,
} ;
static struct clk clk_enet1 = {
. id = 1 ,
. set = enetx_set ,
} ;
/*
* Ethernet PHY clock
*/
static void ephy_set ( struct clk * clk , int enable )
{
2013-06-18 20:55:40 +04:00
if ( BCMCPU_IS_3368 ( ) | | BCMCPU_IS_6358 ( ) )
bcm_hwclock_set ( CKCTL_6358_EPHY_EN , enable ) ;
2009-08-18 16:23:37 +04:00
}
static struct clk clk_ephy = {
. set = ephy_set ,
} ;
2011-11-04 22:09:35 +04:00
/*
* Ethernet switch clock
*/
static void enetsw_set ( struct clk * clk , int enable )
{
2013-04-22 14:57:06 +04:00
if ( BCMCPU_IS_6328 ( ) )
bcm_hwclock_set ( CKCTL_6328_ROBOSW_EN , enable ) ;
else if ( BCMCPU_IS_6362 ( ) )
bcm_hwclock_set ( CKCTL_6362_ROBOSW_EN , enable ) ;
else if ( BCMCPU_IS_6368 ( ) )
bcm_hwclock_set ( CKCTL_6368_ROBOSW_EN |
CKCTL_6368_SWPKT_USB_EN |
CKCTL_6368_SWPKT_SAR_EN ,
enable ) ;
else
2011-11-04 22:09:35 +04:00
return ;
2013-04-22 14:57:06 +04:00
2011-11-04 22:09:35 +04:00
if ( enable ) {
/* reset switch core afer clock change */
2012-10-28 16:17:55 +04:00
bcm63xx_core_set_reset ( BCM63XX_RESET_ENETSW , 1 ) ;
2011-11-04 22:09:35 +04:00
msleep ( 10 ) ;
2012-10-28 16:17:55 +04:00
bcm63xx_core_set_reset ( BCM63XX_RESET_ENETSW , 0 ) ;
2011-11-04 22:09:35 +04:00
msleep ( 10 ) ;
}
}
static struct clk clk_enetsw = {
. set = enetsw_set ,
} ;
2009-08-18 16:23:37 +04:00
/*
* PCM clock
*/
static void pcm_set ( struct clk * clk , int enable )
{
2013-06-18 20:55:40 +04:00
if ( BCMCPU_IS_3368 ( ) )
bcm_hwclock_set ( CKCTL_3368_PCM_EN , enable ) ;
if ( BCMCPU_IS_6358 ( ) )
bcm_hwclock_set ( CKCTL_6358_PCM_EN , enable ) ;
2009-08-18 16:23:37 +04:00
}
static struct clk clk_pcm = {
. set = pcm_set ,
} ;
/*
* USB host clock
*/
static void usbh_set ( struct clk * clk , int enable )
{
2012-06-23 08:14:51 +04:00
if ( BCMCPU_IS_6328 ( ) )
bcm_hwclock_set ( CKCTL_6328_USBH_EN , enable ) ;
else if ( BCMCPU_IS_6348 ( ) )
2011-11-04 22:09:35 +04:00
bcm_hwclock_set ( CKCTL_6348_USBH_EN , enable ) ;
2013-04-22 14:57:06 +04:00
else if ( BCMCPU_IS_6362 ( ) )
bcm_hwclock_set ( CKCTL_6362_USBH_EN , enable ) ;
2011-11-04 22:09:35 +04:00
else if ( BCMCPU_IS_6368 ( ) )
2012-07-04 18:57:09 +04:00
bcm_hwclock_set ( CKCTL_6368_USBH_EN , enable ) ;
2009-08-18 16:23:37 +04:00
}
static struct clk clk_usbh = {
. set = usbh_set ,
} ;
2012-06-23 08:14:51 +04:00
/*
* USB device clock
*/
static void usbd_set ( struct clk * clk , int enable )
{
if ( BCMCPU_IS_6328 ( ) )
bcm_hwclock_set ( CKCTL_6328_USBD_EN , enable ) ;
2013-04-22 14:57:06 +04:00
else if ( BCMCPU_IS_6362 ( ) )
bcm_hwclock_set ( CKCTL_6362_USBD_EN , enable ) ;
2012-06-23 08:14:51 +04:00
else if ( BCMCPU_IS_6368 ( ) )
bcm_hwclock_set ( CKCTL_6368_USBD_EN , enable ) ;
}
static struct clk clk_usbd = {
. set = usbd_set ,
} ;
2009-08-18 16:23:37 +04:00
/*
* SPI clock
*/
static void spi_set ( struct clk * clk , int enable )
{
u32 mask ;
if ( BCMCPU_IS_6338 ( ) )
mask = CKCTL_6338_SPI_EN ;
else if ( BCMCPU_IS_6348 ( ) )
mask = CKCTL_6348_SPI_EN ;
2013-06-18 20:55:40 +04:00
else if ( BCMCPU_IS_3368 ( ) | | BCMCPU_IS_6358 ( ) )
2009-08-18 16:23:37 +04:00
mask = CKCTL_6358_SPI_EN ;
2013-03-21 18:03:18 +04:00
else if ( BCMCPU_IS_6362 ( ) )
mask = CKCTL_6362_SPI_EN ;
2012-07-04 18:58:30 +04:00
else
/* BCMCPU_IS_6368 */
mask = CKCTL_6368_SPI_EN ;
2009-08-18 16:23:37 +04:00
bcm_hwclock_set ( mask , enable ) ;
}
static struct clk clk_spi = {
. set = spi_set ,
} ;
2013-11-30 15:42:02 +04:00
/*
* HSSPI clock
*/
static void hsspi_set ( struct clk * clk , int enable )
{
u32 mask ;
if ( BCMCPU_IS_6328 ( ) )
mask = CKCTL_6328_HSSPI_EN ;
else if ( BCMCPU_IS_6362 ( ) )
mask = CKCTL_6362_HSSPI_EN ;
else
return ;
bcm_hwclock_set ( mask , enable ) ;
}
static struct clk clk_hsspi = {
. set = hsspi_set ,
} ;
2011-11-04 22:09:35 +04:00
/*
* XTM clock
*/
static void xtm_set ( struct clk * clk , int enable )
{
if ( ! BCMCPU_IS_6368 ( ) )
return ;
2012-07-04 18:57:09 +04:00
bcm_hwclock_set ( CKCTL_6368_SAR_EN |
2011-11-04 22:09:35 +04:00
CKCTL_6368_SWPKT_SAR_EN , enable ) ;
if ( enable ) {
/* reset sar core afer clock change */
2012-10-28 16:17:55 +04:00
bcm63xx_core_set_reset ( BCM63XX_RESET_SAR , 1 ) ;
2011-11-04 22:09:35 +04:00
mdelay ( 1 ) ;
2012-10-28 16:17:55 +04:00
bcm63xx_core_set_reset ( BCM63XX_RESET_SAR , 0 ) ;
2011-11-04 22:09:35 +04:00
mdelay ( 1 ) ;
}
}
static struct clk clk_xtm = {
. set = xtm_set ,
} ;
2012-07-24 18:33:09 +04:00
/*
* IPsec clock
*/
static void ipsec_set ( struct clk * clk , int enable )
{
2013-04-22 14:57:06 +04:00
if ( BCMCPU_IS_6362 ( ) )
bcm_hwclock_set ( CKCTL_6362_IPSEC_EN , enable ) ;
else if ( BCMCPU_IS_6368 ( ) )
bcm_hwclock_set ( CKCTL_6368_IPSEC_EN , enable ) ;
2012-07-24 18:33:09 +04:00
}
static struct clk clk_ipsec = {
. set = ipsec_set ,
} ;
2012-10-28 15:49:53 +04:00
/*
* PCIe clock
*/
static void pcie_set ( struct clk * clk , int enable )
{
2013-04-22 14:57:06 +04:00
if ( BCMCPU_IS_6328 ( ) )
bcm_hwclock_set ( CKCTL_6328_PCIE_EN , enable ) ;
else if ( BCMCPU_IS_6362 ( ) )
bcm_hwclock_set ( CKCTL_6362_PCIE_EN , enable ) ;
2012-10-28 15:49:53 +04:00
}
static struct clk clk_pcie = {
. set = pcie_set ,
} ;
2009-08-18 16:23:37 +04:00
/*
* Internal peripheral clock
*/
static struct clk clk_periph = {
. rate = ( 50 * 1000 * 1000 ) ,
} ;
/*
* Linux clock API implementation
*/
int clk_enable ( struct clk * clk )
{
mutex_lock ( & clocks_mutex ) ;
clk_enable_unlocked ( clk ) ;
mutex_unlock ( & clocks_mutex ) ;
return 0 ;
}
EXPORT_SYMBOL ( clk_enable ) ;
void clk_disable ( struct clk * clk )
{
2016-09-18 21:04:35 +03:00
if ( ! clk )
return ;
2009-08-18 16:23:37 +04:00
mutex_lock ( & clocks_mutex ) ;
clk_disable_unlocked ( clk ) ;
mutex_unlock ( & clocks_mutex ) ;
}
EXPORT_SYMBOL ( clk_disable ) ;
unsigned long clk_get_rate ( struct clk * clk )
{
2017-07-18 13:17:27 +03:00
if ( ! clk )
return 0 ;
2009-08-18 16:23:37 +04:00
return clk - > rate ;
}
EXPORT_SYMBOL ( clk_get_rate ) ;
2013-07-02 13:13:44 +04:00
int clk_set_rate ( struct clk * clk , unsigned long rate )
{
return 0 ;
}
EXPORT_SYMBOL_GPL ( clk_set_rate ) ;
long clk_round_rate ( struct clk * clk , unsigned long rate )
{
return 0 ;
}
EXPORT_SYMBOL_GPL ( clk_round_rate ) ;
2017-09-20 14:14:01 +03:00
static struct clk_lookup bcm3368_clks [ ] = {
/* fixed rate clocks */
CLKDEV_INIT ( NULL , " periph " , & clk_periph ) ,
/* gated clocks */
CLKDEV_INIT ( NULL , " enet0 " , & clk_enet0 ) ,
CLKDEV_INIT ( NULL , " enet1 " , & clk_enet1 ) ,
CLKDEV_INIT ( NULL , " ephy " , & clk_ephy ) ,
CLKDEV_INIT ( NULL , " usbh " , & clk_usbh ) ,
CLKDEV_INIT ( NULL , " usbd " , & clk_usbd ) ,
CLKDEV_INIT ( NULL , " spi " , & clk_spi ) ,
CLKDEV_INIT ( NULL , " pcm " , & clk_pcm ) ,
} ;
2009-08-18 16:23:37 +04:00
2017-09-20 14:14:01 +03:00
static struct clk_lookup bcm6328_clks [ ] = {
/* fixed rate clocks */
CLKDEV_INIT ( NULL , " periph " , & clk_periph ) ,
/* gated clocks */
CLKDEV_INIT ( NULL , " enetsw " , & clk_enetsw ) ,
CLKDEV_INIT ( NULL , " usbh " , & clk_usbh ) ,
CLKDEV_INIT ( NULL , " usbd " , & clk_usbd ) ,
CLKDEV_INIT ( NULL , " hsspi " , & clk_hsspi ) ,
CLKDEV_INIT ( NULL , " pcie " , & clk_pcie ) ,
} ;
2009-08-18 16:23:37 +04:00
2017-09-20 14:14:01 +03:00
static struct clk_lookup bcm6338_clks [ ] = {
/* fixed rate clocks */
CLKDEV_INIT ( NULL , " periph " , & clk_periph ) ,
/* gated clocks */
CLKDEV_INIT ( NULL , " enet0 " , & clk_enet0 ) ,
CLKDEV_INIT ( NULL , " enet1 " , & clk_enet1 ) ,
CLKDEV_INIT ( NULL , " ephy " , & clk_ephy ) ,
CLKDEV_INIT ( NULL , " usbh " , & clk_usbh ) ,
CLKDEV_INIT ( NULL , " usbd " , & clk_usbd ) ,
CLKDEV_INIT ( NULL , " spi " , & clk_spi ) ,
} ;
static struct clk_lookup bcm6345_clks [ ] = {
/* fixed rate clocks */
CLKDEV_INIT ( NULL , " periph " , & clk_periph ) ,
/* gated clocks */
CLKDEV_INIT ( NULL , " enet0 " , & clk_enet0 ) ,
CLKDEV_INIT ( NULL , " enet1 " , & clk_enet1 ) ,
CLKDEV_INIT ( NULL , " ephy " , & clk_ephy ) ,
CLKDEV_INIT ( NULL , " usbh " , & clk_usbh ) ,
CLKDEV_INIT ( NULL , " usbd " , & clk_usbd ) ,
CLKDEV_INIT ( NULL , " spi " , & clk_spi ) ,
} ;
static struct clk_lookup bcm6348_clks [ ] = {
/* fixed rate clocks */
CLKDEV_INIT ( NULL , " periph " , & clk_periph ) ,
/* gated clocks */
CLKDEV_INIT ( NULL , " enet0 " , & clk_enet0 ) ,
CLKDEV_INIT ( NULL , " enet1 " , & clk_enet1 ) ,
CLKDEV_INIT ( NULL , " ephy " , & clk_ephy ) ,
CLKDEV_INIT ( NULL , " usbh " , & clk_usbh ) ,
CLKDEV_INIT ( NULL , " usbd " , & clk_usbd ) ,
CLKDEV_INIT ( NULL , " spi " , & clk_spi ) ,
} ;
2009-08-18 16:23:37 +04:00
2017-09-20 14:14:01 +03:00
static struct clk_lookup bcm6358_clks [ ] = {
/* fixed rate clocks */
CLKDEV_INIT ( NULL , " periph " , & clk_periph ) ,
/* gated clocks */
CLKDEV_INIT ( NULL , " enet0 " , & clk_enet0 ) ,
CLKDEV_INIT ( NULL , " enet1 " , & clk_enet1 ) ,
CLKDEV_INIT ( NULL , " ephy " , & clk_ephy ) ,
CLKDEV_INIT ( NULL , " usbh " , & clk_usbh ) ,
CLKDEV_INIT ( NULL , " usbd " , & clk_usbd ) ,
CLKDEV_INIT ( NULL , " spi " , & clk_spi ) ,
CLKDEV_INIT ( NULL , " pcm " , & clk_pcm ) ,
} ;
static struct clk_lookup bcm6362_clks [ ] = {
/* fixed rate clocks */
CLKDEV_INIT ( NULL , " periph " , & clk_periph ) ,
/* gated clocks */
CLKDEV_INIT ( NULL , " enetsw " , & clk_enetsw ) ,
CLKDEV_INIT ( NULL , " usbh " , & clk_usbh ) ,
CLKDEV_INIT ( NULL , " usbd " , & clk_usbd ) ,
CLKDEV_INIT ( NULL , " spi " , & clk_spi ) ,
CLKDEV_INIT ( NULL , " hsspi " , & clk_hsspi ) ,
CLKDEV_INIT ( NULL , " pcie " , & clk_pcie ) ,
CLKDEV_INIT ( NULL , " ipsec " , & clk_ipsec ) ,
} ;
static struct clk_lookup bcm6368_clks [ ] = {
/* fixed rate clocks */
CLKDEV_INIT ( NULL , " periph " , & clk_periph ) ,
/* gated clocks */
CLKDEV_INIT ( NULL , " enetsw " , & clk_enetsw ) ,
CLKDEV_INIT ( NULL , " usbh " , & clk_usbh ) ,
CLKDEV_INIT ( NULL , " usbd " , & clk_usbd ) ,
CLKDEV_INIT ( NULL , " spi " , & clk_spi ) ,
CLKDEV_INIT ( NULL , " xtm " , & clk_xtm ) ,
CLKDEV_INIT ( NULL , " ipsec " , & clk_ipsec ) ,
} ;
2013-11-30 15:42:03 +04:00
# define HSSPI_PLL_HZ_6328 133333333
# define HSSPI_PLL_HZ_6362 400000000
static int __init bcm63xx_clk_init ( void )
{
switch ( bcm63xx_get_cpu_id ( ) ) {
2017-09-20 14:14:01 +03:00
case BCM3368_CPU_ID :
clkdev_add_table ( bcm3368_clks , ARRAY_SIZE ( bcm3368_clks ) ) ;
break ;
2013-11-30 15:42:03 +04:00
case BCM6328_CPU_ID :
clk_hsspi . rate = HSSPI_PLL_HZ_6328 ;
2017-09-20 14:14:01 +03:00
clkdev_add_table ( bcm6328_clks , ARRAY_SIZE ( bcm6328_clks ) ) ;
break ;
case BCM6338_CPU_ID :
clkdev_add_table ( bcm6338_clks , ARRAY_SIZE ( bcm6338_clks ) ) ;
break ;
case BCM6345_CPU_ID :
clkdev_add_table ( bcm6345_clks , ARRAY_SIZE ( bcm6345_clks ) ) ;
break ;
case BCM6348_CPU_ID :
clkdev_add_table ( bcm6348_clks , ARRAY_SIZE ( bcm6348_clks ) ) ;
break ;
case BCM6358_CPU_ID :
clkdev_add_table ( bcm6358_clks , ARRAY_SIZE ( bcm6358_clks ) ) ;
2013-11-30 15:42:03 +04:00
break ;
case BCM6362_CPU_ID :
clk_hsspi . rate = HSSPI_PLL_HZ_6362 ;
2017-09-20 14:14:01 +03:00
clkdev_add_table ( bcm6362_clks , ARRAY_SIZE ( bcm6362_clks ) ) ;
break ;
case BCM6368_CPU_ID :
clkdev_add_table ( bcm6368_clks , ARRAY_SIZE ( bcm6368_clks ) ) ;
2013-11-30 15:42:03 +04:00
break ;
}
return 0 ;
}
arch_initcall ( bcm63xx_clk_init ) ;