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>
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 )
{
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 ) ;
2009-08-18 16:23:37 +04:00
struct clk * clk_get ( struct device * dev , const char * id )
{
if ( ! strcmp ( id , " enet0 " ) )
return & clk_enet0 ;
if ( ! strcmp ( id , " enet1 " ) )
return & clk_enet1 ;
2011-11-04 22:09:35 +04:00
if ( ! strcmp ( id , " enetsw " ) )
return & clk_enetsw ;
2009-08-18 16:23:37 +04:00
if ( ! strcmp ( id , " ephy " ) )
return & clk_ephy ;
if ( ! strcmp ( id , " usbh " ) )
return & clk_usbh ;
2012-06-23 08:14:51 +04:00
if ( ! strcmp ( id , " usbd " ) )
return & clk_usbd ;
2009-08-18 16:23:37 +04:00
if ( ! strcmp ( id , " spi " ) )
return & clk_spi ;
2013-11-30 15:42:02 +04:00
if ( ! strcmp ( id , " hsspi " ) )
return & clk_hsspi ;
2011-11-04 22:09:35 +04:00
if ( ! strcmp ( id , " xtm " ) )
return & clk_xtm ;
2009-08-18 16:23:37 +04:00
if ( ! strcmp ( id , " periph " ) )
return & clk_periph ;
2013-06-18 20:55:40 +04:00
if ( ( BCMCPU_IS_3368 ( ) | | BCMCPU_IS_6358 ( ) ) & & ! strcmp ( id , " pcm " ) )
2009-08-18 16:23:37 +04:00
return & clk_pcm ;
2013-04-22 14:57:06 +04:00
if ( ( BCMCPU_IS_6362 ( ) | | BCMCPU_IS_6368 ( ) ) & & ! strcmp ( id , " ipsec " ) )
2012-07-24 18:33:09 +04:00
return & clk_ipsec ;
2013-04-22 14:57:06 +04:00
if ( ( BCMCPU_IS_6328 ( ) | | BCMCPU_IS_6362 ( ) ) & & ! strcmp ( id , " pcie " ) )
2012-10-28 15:49:53 +04:00
return & clk_pcie ;
2009-08-18 16:23:37 +04:00
return ERR_PTR ( - ENOENT ) ;
}
EXPORT_SYMBOL ( clk_get ) ;
void clk_put ( struct clk * clk )
{
}
EXPORT_SYMBOL ( clk_put ) ;
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 ( ) ) {
case BCM6328_CPU_ID :
clk_hsspi . rate = HSSPI_PLL_HZ_6328 ;
break ;
case BCM6362_CPU_ID :
clk_hsspi . rate = HSSPI_PLL_HZ_6362 ;
break ;
}
return 0 ;
}
arch_initcall ( bcm63xx_clk_init ) ;