2017-06-21 09:59:52 +02:00
/*
* Cortina Gemini SoC Clock Controller driver
* Copyright ( c ) 2017 Linus Walleij < linus . walleij @ linaro . org >
*/
# define pr_fmt(fmt) "clk-gemini: " fmt
# include <linux/init.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/clk-provider.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
# include <linux/spinlock.h>
# include <linux/reset-controller.h>
# include <dt-bindings/reset/cortina,gemini-reset.h>
# include <dt-bindings/clock/cortina,gemini-clock.h>
/* Globally visible clocks */
static DEFINE_SPINLOCK ( gemini_clk_lock ) ;
# define GEMINI_GLOBAL_STATUS 0x04
# define PLL_OSC_SEL BIT(30)
# define AHBSPEED_SHIFT (15)
# define AHBSPEED_MASK 0x07
# define CPU_AHB_RATIO_SHIFT (18)
# define CPU_AHB_RATIO_MASK 0x03
# define GEMINI_GLOBAL_PLL_CONTROL 0x08
# define GEMINI_GLOBAL_SOFT_RESET 0x0c
# define GEMINI_GLOBAL_MISC_CONTROL 0x30
# define PCI_CLK_66MHZ BIT(18)
# define GEMINI_GLOBAL_CLOCK_CONTROL 0x34
# define PCI_CLKRUN_EN BIT(16)
# define TVC_HALFDIV_SHIFT (24)
# define TVC_HALFDIV_MASK 0x1f
# define SECURITY_CLK_SEL BIT(29)
# define GEMINI_GLOBAL_PCI_DLL_CONTROL 0x44
# define PCI_DLL_BYPASS BIT(31)
# define PCI_DLL_TAP_SEL_MASK 0x1f
/**
* struct gemini_data_data - Gemini gated clocks
* @ bit_idx : the bit used to gate this clock in the clock register
* @ name : the clock name
* @ parent_name : the name of the parent clock
* @ flags : standard clock framework flags
*/
struct gemini_gate_data {
u8 bit_idx ;
const char * name ;
const char * parent_name ;
unsigned long flags ;
} ;
/**
* struct clk_gemini_pci - Gemini PCI clock
* @ hw : corresponding clock hardware entry
* @ map : regmap to access the registers
* @ rate : current rate
*/
struct clk_gemini_pci {
struct clk_hw hw ;
struct regmap * map ;
unsigned long rate ;
} ;
/**
* struct gemini_reset - gemini reset controller
* @ map : regmap to access the containing system controller
* @ rcdev : reset controller device
*/
struct gemini_reset {
struct regmap * map ;
struct reset_controller_dev rcdev ;
} ;
/* Keeps track of all clocks */
static struct clk_hw_onecell_data * gemini_clk_data ;
static const struct gemini_gate_data gemini_gates [ ] = {
{ 1 , " security-gate " , " secdiv " , 0 } ,
{ 2 , " gmac0-gate " , " ahb " , 0 } ,
{ 3 , " gmac1-gate " , " ahb " , 0 } ,
{ 4 , " sata0-gate " , " ahb " , 0 } ,
{ 5 , " sata1-gate " , " ahb " , 0 } ,
{ 6 , " usb0-gate " , " ahb " , 0 } ,
{ 7 , " usb1-gate " , " ahb " , 0 } ,
{ 8 , " ide-gate " , " ahb " , 0 } ,
{ 9 , " pci-gate " , " ahb " , 0 } ,
/*
* The DDR controller may never have a driver , but certainly must
* not be gated off .
*/
{ 10 , " ddr-gate " , " ahb " , CLK_IS_CRITICAL } ,
/*
* The flash controller must be on to access NOR flash through the
* memory map .
*/
{ 11 , " flash-gate " , " ahb " , CLK_IGNORE_UNUSED } ,
{ 12 , " tvc-gate " , " ahb " , 0 } ,
{ 13 , " boot-gate " , " apb " , 0 } ,
} ;
# define to_pciclk(_hw) container_of(_hw, struct clk_gemini_pci, hw)
# define to_gemini_reset(p) container_of((p), struct gemini_reset, rcdev)
static unsigned long gemini_pci_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_gemini_pci * pciclk = to_pciclk ( hw ) ;
u32 val ;
regmap_read ( pciclk - > map , GEMINI_GLOBAL_MISC_CONTROL , & val ) ;
if ( val & PCI_CLK_66MHZ )
return 66000000 ;
return 33000000 ;
}
static long gemini_pci_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * prate )
{
/* We support 33 and 66 MHz */
if ( rate < 48000000 )
return 33000000 ;
return 66000000 ;
}
static int gemini_pci_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_gemini_pci * pciclk = to_pciclk ( hw ) ;
if ( rate = = 33000000 )
return regmap_update_bits ( pciclk - > map ,
GEMINI_GLOBAL_MISC_CONTROL ,
PCI_CLK_66MHZ , 0 ) ;
if ( rate = = 66000000 )
return regmap_update_bits ( pciclk - > map ,
GEMINI_GLOBAL_MISC_CONTROL ,
0 , PCI_CLK_66MHZ ) ;
return - EINVAL ;
}
static int gemini_pci_enable ( struct clk_hw * hw )
{
struct clk_gemini_pci * pciclk = to_pciclk ( hw ) ;
regmap_update_bits ( pciclk - > map , GEMINI_GLOBAL_CLOCK_CONTROL ,
0 , PCI_CLKRUN_EN ) ;
return 0 ;
}
static void gemini_pci_disable ( struct clk_hw * hw )
{
struct clk_gemini_pci * pciclk = to_pciclk ( hw ) ;
regmap_update_bits ( pciclk - > map , GEMINI_GLOBAL_CLOCK_CONTROL ,
PCI_CLKRUN_EN , 0 ) ;
}
static int gemini_pci_is_enabled ( struct clk_hw * hw )
{
struct clk_gemini_pci * pciclk = to_pciclk ( hw ) ;
unsigned int val ;
regmap_read ( pciclk - > map , GEMINI_GLOBAL_CLOCK_CONTROL , & val ) ;
return ! ! ( val & PCI_CLKRUN_EN ) ;
}
static const struct clk_ops gemini_pci_clk_ops = {
. recalc_rate = gemini_pci_recalc_rate ,
. round_rate = gemini_pci_round_rate ,
. set_rate = gemini_pci_set_rate ,
. enable = gemini_pci_enable ,
. disable = gemini_pci_disable ,
. is_enabled = gemini_pci_is_enabled ,
} ;
static struct clk_hw * gemini_pci_clk_setup ( const char * name ,
const char * parent_name ,
struct regmap * map )
{
struct clk_gemini_pci * pciclk ;
struct clk_init_data init ;
int ret ;
pciclk = kzalloc ( sizeof ( * pciclk ) , GFP_KERNEL ) ;
if ( ! pciclk )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & gemini_pci_clk_ops ;
init . flags = 0 ;
init . parent_names = & parent_name ;
init . num_parents = 1 ;
pciclk - > map = map ;
pciclk - > hw . init = & init ;
ret = clk_hw_register ( NULL , & pciclk - > hw ) ;
if ( ret ) {
kfree ( pciclk ) ;
return ERR_PTR ( ret ) ;
}
return & pciclk - > hw ;
}
/*
* This is a self - deasserting reset controller .
*/
static int gemini_reset ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct gemini_reset * gr = to_gemini_reset ( rcdev ) ;
/* Manual says to always set BIT 30 (CPU1) to 1 */
return regmap_write ( gr - > map ,
GEMINI_GLOBAL_SOFT_RESET ,
BIT ( GEMINI_RESET_CPU1 ) | BIT ( id ) ) ;
}
2017-07-11 14:26:01 +02:00
static int gemini_reset_assert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
return 0 ;
}
static int gemini_reset_deassert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
return 0 ;
}
2017-06-21 09:59:52 +02:00
static int gemini_reset_status ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct gemini_reset * gr = to_gemini_reset ( rcdev ) ;
u32 val ;
int ret ;
ret = regmap_read ( gr - > map , GEMINI_GLOBAL_SOFT_RESET , & val ) ;
if ( ret )
return ret ;
return ! ! ( val & BIT ( id ) ) ;
}
static const struct reset_control_ops gemini_reset_ops = {
. reset = gemini_reset ,
2017-07-11 14:26:01 +02:00
. assert = gemini_reset_assert ,
. deassert = gemini_reset_deassert ,
2017-06-21 09:59:52 +02:00
. status = gemini_reset_status ,
} ;
static int gemini_clk_probe ( struct platform_device * pdev )
{
/* Gives the fracions 1x, 1.5x, 1.85x and 2x */
unsigned int cpu_ahb_mult [ 4 ] = { 1 , 3 , 24 , 2 } ;
unsigned int cpu_ahb_div [ 4 ] = { 1 , 2 , 13 , 1 } ;
void __iomem * base ;
struct gemini_reset * gr ;
struct regmap * map ;
struct clk_hw * hw ;
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
unsigned int mult , div ;
struct resource * res ;
u32 val ;
int ret ;
int i ;
gr = devm_kzalloc ( dev , sizeof ( * gr ) , GFP_KERNEL ) ;
if ( ! gr )
return - ENOMEM ;
/* Remap the system controller for the exclusive register */
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
map = syscon_node_to_regmap ( np ) ;
if ( IS_ERR ( map ) ) {
dev_err ( dev , " no syscon regmap \n " ) ;
return PTR_ERR ( map ) ;
}
gr - > map = map ;
gr - > rcdev . owner = THIS_MODULE ;
gr - > rcdev . nr_resets = 32 ;
gr - > rcdev . ops = & gemini_reset_ops ;
gr - > rcdev . of_node = np ;
ret = devm_reset_controller_register ( dev , & gr - > rcdev ) ;
if ( ret ) {
dev_err ( dev , " could not register reset controller \n " ) ;
return ret ;
}
/* RTC clock 32768 Hz */
hw = clk_hw_register_fixed_rate ( NULL , " rtc " , NULL , 0 , 32768 ) ;
gemini_clk_data - > hws [ GEMINI_CLK_RTC ] = hw ;
/* CPU clock derived as a fixed ratio from the AHB clock */
2017-06-29 15:31:07 +09:30
regmap_read ( map , GEMINI_GLOBAL_STATUS , & val ) ;
2017-06-21 09:59:52 +02:00
val > > = CPU_AHB_RATIO_SHIFT ;
val & = CPU_AHB_RATIO_MASK ;
hw = clk_hw_register_fixed_factor ( NULL , " cpu " , " ahb " , 0 ,
cpu_ahb_mult [ val ] ,
cpu_ahb_div [ val ] ) ;
gemini_clk_data - > hws [ GEMINI_CLK_CPU ] = hw ;
/* Security clock is 1:1 or 0.75 of APB */
regmap_read ( map , GEMINI_GLOBAL_CLOCK_CONTROL , & val ) ;
if ( val & SECURITY_CLK_SEL ) {
mult = 1 ;
div = 1 ;
} else {
mult = 3 ;
div = 4 ;
}
hw = clk_hw_register_fixed_factor ( NULL , " secdiv " , " ahb " , 0 , mult , div ) ;
/*
* These are the leaf gates , at boot no clocks are gated .
*/
for ( i = 0 ; i < ARRAY_SIZE ( gemini_gates ) ; i + + ) {
const struct gemini_gate_data * gd ;
gd = & gemini_gates [ i ] ;
gemini_clk_data - > hws [ GEMINI_CLK_GATES + i ] =
clk_hw_register_gate ( NULL , gd - > name ,
gd - > parent_name ,
gd - > flags ,
base + GEMINI_GLOBAL_CLOCK_CONTROL ,
gd - > bit_idx ,
CLK_GATE_SET_TO_DISABLE ,
& gemini_clk_lock ) ;
}
/*
* The TV Interface Controller has a 5 - bit half divider register .
* This clock is supposed to be 27 MHz as this is an exact multiple
* of PAL and NTSC frequencies . The register is undocumented : (
* FIXME : figure out the parent and how the divider works .
*/
mult = 1 ;
div = ( ( val > > TVC_HALFDIV_SHIFT ) & TVC_HALFDIV_MASK ) ;
dev_dbg ( dev , " TVC half divider value = %d \n " , div ) ;
div + = 1 ;
hw = clk_hw_register_fixed_rate ( NULL , " tvcdiv " , " xtal " , 0 , 27000000 ) ;
gemini_clk_data - > hws [ GEMINI_CLK_TVC ] = hw ;
/* FIXME: very unclear what the parent is */
hw = gemini_pci_clk_setup ( " PCI " , " xtal " , map ) ;
gemini_clk_data - > hws [ GEMINI_CLK_PCI ] = hw ;
/* FIXME: very unclear what the parent is */
hw = clk_hw_register_fixed_rate ( NULL , " uart " , " xtal " , 0 , 48000000 ) ;
gemini_clk_data - > hws [ GEMINI_CLK_UART ] = hw ;
return 0 ;
}
static const struct of_device_id gemini_clk_dt_ids [ ] = {
{ . compatible = " cortina,gemini-syscon " , } ,
{ /* sentinel */ } ,
} ;
static struct platform_driver gemini_clk_driver = {
. probe = gemini_clk_probe ,
. driver = {
. name = " gemini-clk " ,
. of_match_table = gemini_clk_dt_ids ,
. suppress_bind_attrs = true ,
} ,
} ;
builtin_platform_driver ( gemini_clk_driver ) ;
static void __init gemini_cc_init ( struct device_node * np )
{
struct regmap * map ;
struct clk_hw * hw ;
unsigned long freq ;
unsigned int mult , div ;
u32 val ;
int ret ;
int i ;
gemini_clk_data = kzalloc ( sizeof ( * gemini_clk_data ) +
sizeof ( * gemini_clk_data - > hws ) * GEMINI_NUM_CLKS ,
GFP_KERNEL ) ;
if ( ! gemini_clk_data )
return ;
/*
* This way all clock fetched before the platform device probes ,
* except those we assign here for early use , will be deferred .
*/
for ( i = 0 ; i < GEMINI_NUM_CLKS ; i + + )
gemini_clk_data - > hws [ i ] = ERR_PTR ( - EPROBE_DEFER ) ;
map = syscon_node_to_regmap ( np ) ;
if ( IS_ERR ( map ) ) {
pr_err ( " no syscon regmap \n " ) ;
return ;
}
/*
* We check that the regmap works on this very first access ,
* but as this is an MMIO - backed regmap , subsequent regmap
* access is not going to fail and we skip error checks from
* this point .
*/
ret = regmap_read ( map , GEMINI_GLOBAL_STATUS , & val ) ;
if ( ret ) {
pr_err ( " failed to read global status register \n " ) ;
return ;
}
/*
* XTAL is the crystal oscillator , 60 or 30 MHz selected from
* strap pin E6
*/
if ( val & PLL_OSC_SEL )
freq = 30000000 ;
else
freq = 60000000 ;
hw = clk_hw_register_fixed_rate ( NULL , " xtal " , NULL , 0 , freq ) ;
pr_debug ( " main crystal @%lu MHz \n " , freq / 1000000 ) ;
/* VCO clock derived from the crystal */
mult = 13 + ( ( val > > AHBSPEED_SHIFT ) & AHBSPEED_MASK ) ;
div = 2 ;
/* If we run on 30 MHz crystal we have to multiply with two */
if ( val & PLL_OSC_SEL )
mult * = 2 ;
hw = clk_hw_register_fixed_factor ( NULL , " vco " , " xtal " , 0 , mult , div ) ;
/* The AHB clock is always 1/3 of the VCO */
hw = clk_hw_register_fixed_factor ( NULL , " ahb " , " vco " , 0 , 1 , 3 ) ;
gemini_clk_data - > hws [ GEMINI_CLK_AHB ] = hw ;
/* The APB clock is always 1/6 of the AHB */
hw = clk_hw_register_fixed_factor ( NULL , " apb " , " ahb " , 0 , 1 , 6 ) ;
gemini_clk_data - > hws [ GEMINI_CLK_APB ] = hw ;
/* Register the clocks to be accessed by the device tree */
gemini_clk_data - > num = GEMINI_NUM_CLKS ;
of_clk_add_hw_provider ( np , of_clk_hw_onecell_get , gemini_clk_data ) ;
}
CLK_OF_DECLARE_DRIVER ( gemini_cc , " cortina,gemini-syscon " , gemini_cc_init ) ;