2016-05-13 10:52:40 +03:00
/*
* Purna Chandra Mandal , < purna . mandal @ microchip . com >
* Copyright ( C ) 2015 Microchip Technology Inc . All rights reserved .
*
* This program is free software ; you can distribute it and / or modify it
* under the terms 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 .
*/
# include <dt-bindings/clock/microchip,pic32-clock.h>
# include <linux/clk.h>
# include <linux/clk-provider.h>
# include <linux/clkdev.h>
# include <linux/module.h>
# include <linux/of_address.h>
# include <linux/of_platform.h>
# include <linux/platform_device.h>
# include <asm/traps.h>
# include "clk-core.h"
/* FRC Postscaler */
# define OSC_FRCDIV_MASK 0x07
# define OSC_FRCDIV_SHIFT 24
/* SPLL fields */
# define PLL_ICLK_MASK 0x01
# define PLL_ICLK_SHIFT 7
# define DECLARE_PERIPHERAL_CLOCK(__clk_name, __reg, __flags) \
{ \
. ctrl_reg = ( __reg ) , \
. init_data = { \
. name = ( __clk_name ) , \
. parent_names = ( const char * [ ] ) { \
" sys_clk " \
} , \
. num_parents = 1 , \
. ops = & pic32_pbclk_ops , \
. flags = ( __flags ) , \
} , \
}
# define DECLARE_REFO_CLOCK(__clkid, __reg) \
{ \
. ctrl_reg = ( __reg ) , \
. init_data = { \
. name = " refo " # __clkid " _clk " , \
. parent_names = ( const char * [ ] ) { \
" sys_clk " , " pb1_clk " , " posc_clk " , \
" frc_clk " , " lprc_clk " , " sosc_clk " , \
" sys_pll " , " refi " # __clkid " _clk " , \
" bfrc_clk " , \
} , \
. num_parents = 9 , \
. flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE , \
. ops = & pic32_roclk_ops , \
} , \
. parent_map = ( const u32 [ ] ) { \
0 , 1 , 2 , 3 , 4 , 5 , 7 , 8 , 9 \
} , \
}
static const struct pic32_ref_osc_data ref_clks [ ] = {
DECLARE_REFO_CLOCK ( 1 , 0x80 ) ,
DECLARE_REFO_CLOCK ( 2 , 0xa0 ) ,
DECLARE_REFO_CLOCK ( 3 , 0xc0 ) ,
DECLARE_REFO_CLOCK ( 4 , 0xe0 ) ,
DECLARE_REFO_CLOCK ( 5 , 0x100 ) ,
} ;
static const struct pic32_periph_clk_data periph_clocks [ ] = {
DECLARE_PERIPHERAL_CLOCK ( " pb1_clk " , 0x140 , 0 ) ,
DECLARE_PERIPHERAL_CLOCK ( " pb2_clk " , 0x150 , CLK_IGNORE_UNUSED ) ,
DECLARE_PERIPHERAL_CLOCK ( " pb3_clk " , 0x160 , 0 ) ,
DECLARE_PERIPHERAL_CLOCK ( " pb4_clk " , 0x170 , 0 ) ,
DECLARE_PERIPHERAL_CLOCK ( " pb5_clk " , 0x180 , 0 ) ,
DECLARE_PERIPHERAL_CLOCK ( " pb6_clk " , 0x190 , 0 ) ,
DECLARE_PERIPHERAL_CLOCK ( " cpu_clk " , 0x1a0 , CLK_IGNORE_UNUSED ) ,
} ;
static const struct pic32_sys_clk_data sys_mux_clk = {
. slew_reg = 0x1c0 ,
. slew_div = 2 , /* step of div_4 -> div_2 -> no_div */
. init_data = {
. name = " sys_clk " ,
. parent_names = ( const char * [ ] ) {
" frcdiv_clk " , " sys_pll " , " posc_clk " ,
" sosc_clk " , " lprc_clk " , " frcdiv_clk " ,
} ,
. num_parents = 6 ,
. ops = & pic32_sclk_ops ,
} ,
. parent_map = ( const u32 [ ] ) {
0 , 1 , 2 , 4 , 5 , 7 ,
} ,
} ;
static const struct pic32_sys_pll_data sys_pll = {
. ctrl_reg = 0x020 ,
. status_reg = 0x1d0 ,
. lock_mask = BIT ( 7 ) ,
. init_data = {
. name = " sys_pll " ,
. parent_names = ( const char * [ ] ) {
" spll_mux_clk "
} ,
. num_parents = 1 ,
. ops = & pic32_spll_ops ,
} ,
} ;
static const struct pic32_sec_osc_data sosc_clk = {
. status_reg = 0x1d0 ,
. enable_mask = BIT ( 1 ) ,
. status_mask = BIT ( 4 ) ,
2016-05-17 08:05:51 +03:00
. fixed_rate = 32768 ,
2016-05-13 10:52:40 +03:00
. init_data = {
. name = " sosc_clk " ,
. parent_names = NULL ,
. ops = & pic32_sosc_ops ,
} ,
} ;
static int pic32mzda_critical_clks [ ] = {
PB2CLK , PB7CLK
} ;
/* PIC32MZDA clock data */
struct pic32mzda_clk_data {
struct clk * clks [ MAXCLKS ] ;
struct pic32_clk_common core ;
struct clk_onecell_data onecell_data ;
struct notifier_block failsafe_notifier ;
} ;
static int pic32_fscm_nmi ( struct notifier_block * nb ,
unsigned long action , void * data )
{
struct pic32mzda_clk_data * cd ;
cd = container_of ( nb , struct pic32mzda_clk_data , failsafe_notifier ) ;
/* SYSCLK is now running from BFRCCLK. Report clock failure. */
if ( readl ( cd - > core . iobase ) & BIT ( 2 ) )
pr_alert ( " pic32-clk: FSCM detected clk failure. \n " ) ;
/* TODO: detect reason of failure and recover accordingly */
return NOTIFY_OK ;
}
static int pic32mzda_clk_probe ( struct platform_device * pdev )
{
const char * const pll_mux_parents [ ] = { " posc_clk " , " frc_clk " } ;
struct device_node * np = pdev - > dev . of_node ;
struct pic32mzda_clk_data * cd ;
struct pic32_clk_common * core ;
struct clk * pll_mux_clk , * clk ;
struct clk * * clks ;
int nr_clks , i , ret ;
cd = devm_kzalloc ( & pdev - > dev , sizeof ( * cd ) , GFP_KERNEL ) ;
if ( ! cd )
return - ENOMEM ;
core = & cd - > core ;
core - > iobase = of_io_request_and_map ( np , 0 , of_node_full_name ( np ) ) ;
if ( IS_ERR ( core - > iobase ) ) {
dev_err ( & pdev - > dev , " pic32-clk: failed to map registers \n " ) ;
return PTR_ERR ( core - > iobase ) ;
}
spin_lock_init ( & core - > reg_lock ) ;
core - > dev = & pdev - > dev ;
clks = & cd - > clks [ 0 ] ;
/* register fixed rate clocks */
clks [ POSCCLK ] = clk_register_fixed_rate ( & pdev - > dev , " posc_clk " , NULL ,
2016-06-02 00:52:54 +03:00
0 , 24000000 ) ;
2016-05-13 10:52:40 +03:00
clks [ FRCCLK ] = clk_register_fixed_rate ( & pdev - > dev , " frc_clk " , NULL ,
2016-06-02 00:52:54 +03:00
0 , 8000000 ) ;
2016-05-13 10:52:40 +03:00
clks [ BFRCCLK ] = clk_register_fixed_rate ( & pdev - > dev , " bfrc_clk " , NULL ,
2016-06-02 00:52:54 +03:00
0 , 8000000 ) ;
2016-05-13 10:52:40 +03:00
clks [ LPRCCLK ] = clk_register_fixed_rate ( & pdev - > dev , " lprc_clk " , NULL ,
2016-06-02 00:52:54 +03:00
0 , 32000 ) ;
2016-05-13 10:52:40 +03:00
clks [ UPLLCLK ] = clk_register_fixed_rate ( & pdev - > dev , " usbphy_clk " , NULL ,
2016-06-02 00:52:54 +03:00
0 , 24000000 ) ;
2016-05-13 10:52:40 +03:00
/* fixed rate (optional) clock */
if ( of_find_property ( np , " microchip,pic32mzda-sosc " , NULL ) ) {
pr_info ( " pic32-clk: dt requests SOSC. \n " ) ;
clks [ SOSCCLK ] = pic32_sosc_clk_register ( & sosc_clk , core ) ;
}
/* divider clock */
clks [ FRCDIVCLK ] = clk_register_divider ( & pdev - > dev , " frcdiv_clk " ,
" frc_clk " , 0 ,
core - > iobase ,
OSC_FRCDIV_SHIFT ,
OSC_FRCDIV_MASK ,
CLK_DIVIDER_POWER_OF_TWO ,
& core - > reg_lock ) ;
/* PLL ICLK mux */
pll_mux_clk = clk_register_mux ( & pdev - > dev , " spll_mux_clk " ,
pll_mux_parents , 2 , 0 ,
core - > iobase + 0x020 ,
PLL_ICLK_SHIFT , 1 , 0 , & core - > reg_lock ) ;
if ( IS_ERR ( pll_mux_clk ) )
pr_err ( " spll_mux_clk: clk register failed \n " ) ;
/* PLL */
clks [ PLLCLK ] = pic32_spll_clk_register ( & sys_pll , core ) ;
/* SYSTEM clock */
clks [ SCLK ] = pic32_sys_clk_register ( & sys_mux_clk , core ) ;
/* Peripheral bus clocks */
for ( nr_clks = PB1CLK , i = 0 ; nr_clks < = PB7CLK ; i + + , nr_clks + + )
clks [ nr_clks ] = pic32_periph_clk_register ( & periph_clocks [ i ] ,
core ) ;
/* Reference oscillator clock */
for ( nr_clks = REF1CLK , i = 0 ; nr_clks < = REF5CLK ; i + + , nr_clks + + )
clks [ nr_clks ] = pic32_refo_clk_register ( & ref_clks [ i ] , core ) ;
/* register clkdev */
for ( i = 0 ; i < MAXCLKS ; i + + ) {
if ( IS_ERR ( clks [ i ] ) )
continue ;
clk_register_clkdev ( clks [ i ] , NULL , __clk_get_name ( clks [ i ] ) ) ;
}
/* register clock provider */
cd - > onecell_data . clks = clks ;
cd - > onecell_data . clk_num = MAXCLKS ;
ret = of_clk_add_provider ( np , of_clk_src_onecell_get ,
& cd - > onecell_data ) ;
if ( ret )
return ret ;
/* force enable critical clocks */
for ( i = 0 ; i < ARRAY_SIZE ( pic32mzda_critical_clks ) ; i + + ) {
clk = clks [ pic32mzda_critical_clks [ i ] ] ;
if ( clk_prepare_enable ( clk ) )
dev_err ( & pdev - > dev , " clk_prepare_enable(%s) failed \n " ,
__clk_get_name ( clk ) ) ;
}
/* register NMI for failsafe clock monitor */
cd - > failsafe_notifier . notifier_call = pic32_fscm_nmi ;
return register_nmi_notifier ( & cd - > failsafe_notifier ) ;
}
static const struct of_device_id pic32mzda_clk_match_table [ ] = {
{ . compatible = " microchip,pic32mzda-clk " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , pic32mzda_clk_match_table ) ;
static struct platform_driver pic32mzda_clk_driver = {
. probe = pic32mzda_clk_probe ,
. driver = {
. name = " clk-pic32mzda " ,
. of_match_table = pic32mzda_clk_match_table ,
} ,
} ;
static int __init microchip_pic32mzda_clk_init ( void )
{
return platform_driver_register ( & pic32mzda_clk_driver ) ;
}
core_initcall ( microchip_pic32mzda_clk_init ) ;
MODULE_DESCRIPTION ( " Microchip PIC32MZDA Clock Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:clk-pic32mzda " ) ;