2015-01-20 12:23:02 +03:00
/*
* Copyright ( c ) 2014 Oleksij Rempel < linux @ rempel - privat . de > .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/clk.h>
# include <linux/clkdev.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/clk-provider.h>
# include <linux/spinlock.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <dt-bindings/clock/alphascale,asm9260.h>
# define HW_AHBCLKCTRL0 0x0020
# define HW_AHBCLKCTRL1 0x0030
# define HW_SYSPLLCTRL 0x0100
# define HW_MAINCLKSEL 0x0120
# define HW_MAINCLKUEN 0x0124
# define HW_UARTCLKSEL 0x0128
# define HW_UARTCLKUEN 0x012c
# define HW_I2S0CLKSEL 0x0130
# define HW_I2S0CLKUEN 0x0134
# define HW_I2S1CLKSEL 0x0138
# define HW_I2S1CLKUEN 0x013c
# define HW_WDTCLKSEL 0x0160
# define HW_WDTCLKUEN 0x0164
# define HW_CLKOUTCLKSEL 0x0170
# define HW_CLKOUTCLKUEN 0x0174
# define HW_CPUCLKDIV 0x017c
# define HW_SYSAHBCLKDIV 0x0180
# define HW_I2S0MCLKDIV 0x0190
# define HW_I2S0SCLKDIV 0x0194
# define HW_I2S1MCLKDIV 0x0188
# define HW_I2S1SCLKDIV 0x018c
# define HW_UART0CLKDIV 0x0198
# define HW_UART1CLKDIV 0x019c
# define HW_UART2CLKDIV 0x01a0
# define HW_UART3CLKDIV 0x01a4
# define HW_UART4CLKDIV 0x01a8
# define HW_UART5CLKDIV 0x01ac
# define HW_UART6CLKDIV 0x01b0
# define HW_UART7CLKDIV 0x01b4
# define HW_UART8CLKDIV 0x01b8
# define HW_UART9CLKDIV 0x01bc
# define HW_SPI0CLKDIV 0x01c0
# define HW_SPI1CLKDIV 0x01c4
# define HW_QUADSPICLKDIV 0x01c8
# define HW_SSP0CLKDIV 0x01d0
# define HW_NANDCLKDIV 0x01d4
# define HW_TRACECLKDIV 0x01e0
# define HW_CAMMCLKDIV 0x01e8
# define HW_WDTCLKDIV 0x01ec
# define HW_CLKOUTCLKDIV 0x01f4
# define HW_MACCLKDIV 0x01f8
# define HW_LCDCLKDIV 0x01fc
# define HW_ADCANACLKDIV 0x0200
2016-06-02 02:15:07 +03:00
static struct clk_hw_onecell_data * clk_data ;
2015-01-20 12:23:02 +03:00
static DEFINE_SPINLOCK ( asm9260_clk_lock ) ;
struct asm9260_div_clk {
unsigned int idx ;
const char * name ;
const char * parent_name ;
u32 reg ;
} ;
struct asm9260_gate_data {
unsigned int idx ;
const char * name ;
const char * parent_name ;
u32 reg ;
u8 bit_idx ;
unsigned long flags ;
} ;
struct asm9260_mux_clock {
u8 mask ;
u32 * table ;
const char * name ;
const char * * parent_names ;
u8 num_parents ;
unsigned long offset ;
unsigned long flags ;
} ;
static void __iomem * base ;
static const struct asm9260_div_clk asm9260_div_clks [ ] __initconst = {
{ CLKID_SYS_CPU , " cpu_div " , " main_gate " , HW_CPUCLKDIV } ,
{ CLKID_SYS_AHB , " ahb_div " , " cpu_div " , HW_SYSAHBCLKDIV } ,
/* i2s has two deviders: one for only external mclk and internal
* devider for all clks . */
{ CLKID_SYS_I2S0M , " i2s0m_div " , " i2s0_mclk " , HW_I2S0MCLKDIV } ,
{ CLKID_SYS_I2S1M , " i2s1m_div " , " i2s1_mclk " , HW_I2S1MCLKDIV } ,
{ CLKID_SYS_I2S0S , " i2s0s_div " , " i2s0_gate " , HW_I2S0SCLKDIV } ,
{ CLKID_SYS_I2S1S , " i2s1s_div " , " i2s0_gate " , HW_I2S1SCLKDIV } ,
{ CLKID_SYS_UART0 , " uart0_div " , " uart_gate " , HW_UART0CLKDIV } ,
{ CLKID_SYS_UART1 , " uart1_div " , " uart_gate " , HW_UART1CLKDIV } ,
{ CLKID_SYS_UART2 , " uart2_div " , " uart_gate " , HW_UART2CLKDIV } ,
{ CLKID_SYS_UART3 , " uart3_div " , " uart_gate " , HW_UART3CLKDIV } ,
{ CLKID_SYS_UART4 , " uart4_div " , " uart_gate " , HW_UART4CLKDIV } ,
{ CLKID_SYS_UART5 , " uart5_div " , " uart_gate " , HW_UART5CLKDIV } ,
{ CLKID_SYS_UART6 , " uart6_div " , " uart_gate " , HW_UART6CLKDIV } ,
{ CLKID_SYS_UART7 , " uart7_div " , " uart_gate " , HW_UART7CLKDIV } ,
{ CLKID_SYS_UART8 , " uart8_div " , " uart_gate " , HW_UART8CLKDIV } ,
{ CLKID_SYS_UART9 , " uart9_div " , " uart_gate " , HW_UART9CLKDIV } ,
{ CLKID_SYS_SPI0 , " spi0_div " , " main_gate " , HW_SPI0CLKDIV } ,
{ CLKID_SYS_SPI1 , " spi1_div " , " main_gate " , HW_SPI1CLKDIV } ,
{ CLKID_SYS_QUADSPI , " quadspi_div " , " main_gate " , HW_QUADSPICLKDIV } ,
{ CLKID_SYS_SSP0 , " ssp0_div " , " main_gate " , HW_SSP0CLKDIV } ,
{ CLKID_SYS_NAND , " nand_div " , " main_gate " , HW_NANDCLKDIV } ,
{ CLKID_SYS_TRACE , " trace_div " , " main_gate " , HW_TRACECLKDIV } ,
{ CLKID_SYS_CAMM , " camm_div " , " main_gate " , HW_CAMMCLKDIV } ,
{ CLKID_SYS_MAC , " mac_div " , " main_gate " , HW_MACCLKDIV } ,
{ CLKID_SYS_LCD , " lcd_div " , " main_gate " , HW_LCDCLKDIV } ,
{ CLKID_SYS_ADCANA , " adcana_div " , " main_gate " , HW_ADCANACLKDIV } ,
{ CLKID_SYS_WDT , " wdt_div " , " wdt_gate " , HW_WDTCLKDIV } ,
{ CLKID_SYS_CLKOUT , " clkout_div " , " clkout_gate " , HW_CLKOUTCLKDIV } ,
} ;
static const struct asm9260_gate_data asm9260_mux_gates [ ] __initconst = {
{ 0 , " main_gate " , " main_mux " , HW_MAINCLKUEN , 0 } ,
{ 0 , " uart_gate " , " uart_mux " , HW_UARTCLKUEN , 0 } ,
{ 0 , " i2s0_gate " , " i2s0_mux " , HW_I2S0CLKUEN , 0 } ,
{ 0 , " i2s1_gate " , " i2s1_mux " , HW_I2S1CLKUEN , 0 } ,
{ 0 , " wdt_gate " , " wdt_mux " , HW_WDTCLKUEN , 0 } ,
{ 0 , " clkout_gate " , " clkout_mux " , HW_CLKOUTCLKUEN , 0 } ,
} ;
static const struct asm9260_gate_data asm9260_ahb_gates [ ] __initconst = {
/* ahb gates */
{ CLKID_AHB_ROM , " rom " , " ahb_div " ,
HW_AHBCLKCTRL0 , 1 , CLK_IGNORE_UNUSED } ,
{ CLKID_AHB_RAM , " ram " , " ahb_div " ,
HW_AHBCLKCTRL0 , 2 , CLK_IGNORE_UNUSED } ,
{ CLKID_AHB_GPIO , " gpio " , " ahb_div " ,
HW_AHBCLKCTRL0 , 4 } ,
{ CLKID_AHB_MAC , " mac " , " ahb_div " ,
HW_AHBCLKCTRL0 , 5 } ,
{ CLKID_AHB_EMI , " emi " , " ahb_div " ,
HW_AHBCLKCTRL0 , 6 , CLK_IGNORE_UNUSED } ,
{ CLKID_AHB_USB0 , " usb0 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 7 } ,
{ CLKID_AHB_USB1 , " usb1 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 8 } ,
{ CLKID_AHB_DMA0 , " dma0 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 9 } ,
{ CLKID_AHB_DMA1 , " dma1 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 10 } ,
{ CLKID_AHB_UART0 , " uart0 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 11 } ,
{ CLKID_AHB_UART1 , " uart1 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 12 } ,
{ CLKID_AHB_UART2 , " uart2 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 13 } ,
{ CLKID_AHB_UART3 , " uart3 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 14 } ,
{ CLKID_AHB_UART4 , " uart4 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 15 } ,
{ CLKID_AHB_UART5 , " uart5 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 16 } ,
{ CLKID_AHB_UART6 , " uart6 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 17 } ,
{ CLKID_AHB_UART7 , " uart7 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 18 } ,
{ CLKID_AHB_UART8 , " uart8 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 19 } ,
{ CLKID_AHB_UART9 , " uart9 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 20 } ,
{ CLKID_AHB_I2S0 , " i2s0 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 21 } ,
{ CLKID_AHB_I2C0 , " i2c0 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 22 } ,
{ CLKID_AHB_I2C1 , " i2c1 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 23 } ,
{ CLKID_AHB_SSP0 , " ssp0 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 24 } ,
{ CLKID_AHB_IOCONFIG , " ioconf " , " ahb_div " ,
HW_AHBCLKCTRL0 , 25 } ,
{ CLKID_AHB_WDT , " wdt " , " ahb_div " ,
HW_AHBCLKCTRL0 , 26 } ,
{ CLKID_AHB_CAN0 , " can0 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 27 } ,
{ CLKID_AHB_CAN1 , " can1 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 28 } ,
{ CLKID_AHB_MPWM , " mpwm " , " ahb_div " ,
HW_AHBCLKCTRL0 , 29 } ,
{ CLKID_AHB_SPI0 , " spi0 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 30 } ,
{ CLKID_AHB_SPI1 , " spi1 " , " ahb_div " ,
HW_AHBCLKCTRL0 , 31 } ,
{ CLKID_AHB_QEI , " qei " , " ahb_div " ,
HW_AHBCLKCTRL1 , 0 } ,
{ CLKID_AHB_QUADSPI0 , " quadspi0 " , " ahb_div " ,
HW_AHBCLKCTRL1 , 1 } ,
{ CLKID_AHB_CAMIF , " capmif " , " ahb_div " ,
HW_AHBCLKCTRL1 , 2 } ,
{ CLKID_AHB_LCDIF , " lcdif " , " ahb_div " ,
HW_AHBCLKCTRL1 , 3 } ,
{ CLKID_AHB_TIMER0 , " timer0 " , " ahb_div " ,
HW_AHBCLKCTRL1 , 4 } ,
{ CLKID_AHB_TIMER1 , " timer1 " , " ahb_div " ,
HW_AHBCLKCTRL1 , 5 } ,
{ CLKID_AHB_TIMER2 , " timer2 " , " ahb_div " ,
HW_AHBCLKCTRL1 , 6 } ,
{ CLKID_AHB_TIMER3 , " timer3 " , " ahb_div " ,
HW_AHBCLKCTRL1 , 7 } ,
{ CLKID_AHB_IRQ , " irq " , " ahb_div " ,
HW_AHBCLKCTRL1 , 8 , CLK_IGNORE_UNUSED } ,
{ CLKID_AHB_RTC , " rtc " , " ahb_div " ,
HW_AHBCLKCTRL1 , 9 } ,
{ CLKID_AHB_NAND , " nand " , " ahb_div " ,
HW_AHBCLKCTRL1 , 10 } ,
{ CLKID_AHB_ADC0 , " adc0 " , " ahb_div " ,
HW_AHBCLKCTRL1 , 11 } ,
{ CLKID_AHB_LED , " led " , " ahb_div " ,
HW_AHBCLKCTRL1 , 12 } ,
{ CLKID_AHB_DAC0 , " dac0 " , " ahb_div " ,
HW_AHBCLKCTRL1 , 13 } ,
{ CLKID_AHB_LCD , " lcd " , " ahb_div " ,
HW_AHBCLKCTRL1 , 14 } ,
{ CLKID_AHB_I2S1 , " i2s1 " , " ahb_div " ,
HW_AHBCLKCTRL1 , 15 } ,
{ CLKID_AHB_MAC1 , " mac1 " , " ahb_div " ,
HW_AHBCLKCTRL1 , 16 } ,
} ;
static const char __initdata * main_mux_p [ ] = { NULL , NULL } ;
static const char __initdata * i2s0_mux_p [ ] = { NULL , NULL , " i2s0m_div " } ;
static const char __initdata * i2s1_mux_p [ ] = { NULL , NULL , " i2s1m_div " } ;
static const char __initdata * clkout_mux_p [ ] = { NULL , NULL , " rtc " } ;
static u32 three_mux_table [ ] = { 0 , 1 , 3 } ;
static struct asm9260_mux_clock asm9260_mux_clks [ ] __initdata = {
{ 1 , three_mux_table , " main_mux " , main_mux_p ,
ARRAY_SIZE ( main_mux_p ) , HW_MAINCLKSEL , } ,
{ 1 , three_mux_table , " uart_mux " , main_mux_p ,
ARRAY_SIZE ( main_mux_p ) , HW_UARTCLKSEL , } ,
{ 1 , three_mux_table , " wdt_mux " , main_mux_p ,
ARRAY_SIZE ( main_mux_p ) , HW_WDTCLKSEL , } ,
{ 3 , three_mux_table , " i2s0_mux " , i2s0_mux_p ,
ARRAY_SIZE ( i2s0_mux_p ) , HW_I2S0CLKSEL , } ,
{ 3 , three_mux_table , " i2s1_mux " , i2s1_mux_p ,
ARRAY_SIZE ( i2s1_mux_p ) , HW_I2S1CLKSEL , } ,
{ 3 , three_mux_table , " clkout_mux " , clkout_mux_p ,
ARRAY_SIZE ( clkout_mux_p ) , HW_CLKOUTCLKSEL , } ,
} ;
static void __init asm9260_acc_init ( struct device_node * np )
{
2016-06-02 02:15:07 +03:00
struct clk_hw * hw ;
struct clk_hw * * hws ;
2015-01-20 12:23:02 +03:00
const char * ref_clk , * pll_clk = " pll " ;
u32 rate ;
int n ;
u32 accuracy = 0 ;
2016-06-02 02:15:07 +03:00
clk_data = kzalloc ( sizeof ( * clk_data ) +
sizeof ( * clk_data - > hws ) * MAX_CLKS , GFP_KERNEL ) ;
if ( ! clk_data )
return ;
clk_data - > num = MAX_CLKS ;
hws = clk_data - > hws ;
2015-01-20 12:23:02 +03:00
base = of_io_request_and_map ( np , 0 , np - > name ) ;
2015-05-02 18:03:21 +03:00
if ( IS_ERR ( base ) )
2015-01-20 12:23:02 +03:00
panic ( " %s: unable to map resource " , np - > name ) ;
/* register pll */
rate = ( ioread32 ( base + HW_SYSPLLCTRL ) & 0xffff ) * 1000000 ;
ref_clk = of_clk_get_parent_name ( np , 0 ) ;
accuracy = clk_get_accuracy ( __clk_lookup ( ref_clk ) ) ;
2016-06-02 02:15:07 +03:00
hw = clk_hw_register_fixed_rate_with_accuracy ( NULL , pll_clk ,
2015-01-20 12:23:02 +03:00
ref_clk , 0 , rate , accuracy ) ;
2016-06-02 02:15:07 +03:00
if ( IS_ERR ( hw ) )
2015-01-20 12:23:02 +03:00
panic ( " %s: can't register REFCLK. Check DT! " , np - > name ) ;
for ( n = 0 ; n < ARRAY_SIZE ( asm9260_mux_clks ) ; n + + ) {
const struct asm9260_mux_clock * mc = & asm9260_mux_clks [ n ] ;
mc - > parent_names [ 0 ] = ref_clk ;
mc - > parent_names [ 1 ] = pll_clk ;
2016-06-02 02:15:07 +03:00
hw = clk_hw_register_mux_table ( NULL , mc - > name , mc - > parent_names ,
2015-01-20 12:23:02 +03:00
mc - > num_parents , mc - > flags , base + mc - > offset ,
0 , mc - > mask , 0 , mc - > table , & asm9260_clk_lock ) ;
}
/* clock mux gate cells */
for ( n = 0 ; n < ARRAY_SIZE ( asm9260_mux_gates ) ; n + + ) {
const struct asm9260_gate_data * gd = & asm9260_mux_gates [ n ] ;
2016-06-02 02:15:07 +03:00
hw = clk_hw_register_gate ( NULL , gd - > name ,
2015-01-20 12:23:02 +03:00
gd - > parent_name , gd - > flags | CLK_SET_RATE_PARENT ,
base + gd - > reg , gd - > bit_idx , 0 , & asm9260_clk_lock ) ;
}
/* clock div cells */
for ( n = 0 ; n < ARRAY_SIZE ( asm9260_div_clks ) ; n + + ) {
const struct asm9260_div_clk * dc = & asm9260_div_clks [ n ] ;
2016-06-02 02:15:07 +03:00
hws [ dc - > idx ] = clk_hw_register_divider ( NULL , dc - > name ,
2015-01-20 12:23:02 +03:00
dc - > parent_name , CLK_SET_RATE_PARENT ,
base + dc - > reg , 0 , 8 , CLK_DIVIDER_ONE_BASED ,
& asm9260_clk_lock ) ;
}
/* clock ahb gate cells */
for ( n = 0 ; n < ARRAY_SIZE ( asm9260_ahb_gates ) ; n + + ) {
const struct asm9260_gate_data * gd = & asm9260_ahb_gates [ n ] ;
2016-06-02 02:15:07 +03:00
hws [ gd - > idx ] = clk_hw_register_gate ( NULL , gd - > name ,
2015-01-20 12:23:02 +03:00
gd - > parent_name , gd - > flags , base + gd - > reg ,
gd - > bit_idx , 0 , & asm9260_clk_lock ) ;
}
/* check for errors on leaf clocks */
for ( n = 0 ; n < MAX_CLKS ; n + + ) {
2016-06-02 02:15:07 +03:00
if ( ! IS_ERR ( hws [ n ] ) )
2015-01-20 12:23:02 +03:00
continue ;
pr_err ( " %s: Unable to register leaf clock %d \n " ,
np - > full_name , n ) ;
goto fail ;
}
/* register clk-provider */
2016-06-02 02:15:07 +03:00
of_clk_add_hw_provider ( np , of_clk_hw_onecell_get , clk_data ) ;
2015-01-20 12:23:02 +03:00
return ;
fail :
iounmap ( base ) ;
}
CLK_OF_DECLARE ( asm9260_acc , " alphascale,asm9260-clock-controller " ,
asm9260_acc_init ) ;