2013-10-17 18:55:41 +02:00
/*
* Copyright ( C ) 2013 Boris BREZILLON < b . brezillon @ overkiz . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
*/
# include <linux/clk-provider.h>
# include <linux/clkdev.h>
# include <linux/clk/at91_pmc.h>
# include <linux/of.h>
2014-09-07 08:14:29 +02:00
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
2013-10-17 18:55:41 +02:00
# include "pmc.h"
# define USB_SOURCE_MAX 2
# define SAM9X5_USB_DIV_SHIFT 8
# define SAM9X5_USB_MAX_DIV 0xf
# define RM9200_USB_DIV_SHIFT 28
# define RM9200_USB_DIV_TAB_SIZE 4
struct at91sam9x5_clk_usb {
struct clk_hw hw ;
2014-09-07 08:14:29 +02:00
struct regmap * regmap ;
2013-10-17 18:55:41 +02:00
} ;
# define to_at91sam9x5_clk_usb(hw) \
container_of ( hw , struct at91sam9x5_clk_usb , hw )
struct at91rm9200_clk_usb {
struct clk_hw hw ;
2014-09-07 08:14:29 +02:00
struct regmap * regmap ;
2013-10-17 18:55:41 +02:00
u32 divisors [ 4 ] ;
} ;
# define to_at91rm9200_clk_usb(hw) \
container_of ( hw , struct at91rm9200_clk_usb , hw )
static unsigned long at91sam9x5_clk_usb_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct at91sam9x5_clk_usb * usb = to_at91sam9x5_clk_usb ( hw ) ;
2014-09-07 08:14:29 +02:00
unsigned int usbr ;
u8 usbdiv ;
2013-10-17 18:55:41 +02:00
2014-09-07 08:14:29 +02:00
regmap_read ( usb - > regmap , AT91_PMC_USB , & usbr ) ;
usbdiv = ( usbr & AT91_PMC_OHCIUSBDIV ) > > SAM9X5_USB_DIV_SHIFT ;
2014-11-17 14:16:56 +01:00
return DIV_ROUND_CLOSEST ( parent_rate , ( usbdiv + 1 ) ) ;
2013-10-17 18:55:41 +02:00
}
2015-07-07 20:48:08 +02:00
static int at91sam9x5_clk_usb_determine_rate ( struct clk_hw * hw ,
struct clk_rate_request * req )
2013-10-17 18:55:41 +02:00
{
2015-07-30 17:20:57 -07:00
struct clk_hw * parent ;
2015-03-29 03:45:33 +02:00
long best_rate = - EINVAL ;
unsigned long tmp_rate ;
int best_diff = - 1 ;
int tmp_diff ;
int i ;
2015-06-25 16:53:23 -07:00
for ( i = 0 ; i < clk_hw_get_num_parents ( hw ) ; i + + ) {
2015-03-29 03:45:33 +02:00
int div ;
2015-07-30 17:20:57 -07:00
parent = clk_hw_get_parent_by_index ( hw , i ) ;
2015-03-29 03:45:33 +02:00
if ( ! parent )
continue ;
for ( div = 1 ; div < SAM9X5_USB_MAX_DIV + 2 ; div + + ) {
unsigned long tmp_parent_rate ;
2015-07-07 20:48:08 +02:00
tmp_parent_rate = req - > rate * div ;
2015-07-30 17:20:57 -07:00
tmp_parent_rate = clk_hw_round_rate ( parent ,
2015-03-29 03:45:33 +02:00
tmp_parent_rate ) ;
tmp_rate = DIV_ROUND_CLOSEST ( tmp_parent_rate , div ) ;
2015-07-07 20:48:08 +02:00
if ( tmp_rate < req - > rate )
tmp_diff = req - > rate - tmp_rate ;
2015-03-29 03:45:33 +02:00
else
2015-07-07 20:48:08 +02:00
tmp_diff = tmp_rate - req - > rate ;
2015-03-29 03:45:33 +02:00
if ( best_diff < 0 | | best_diff > tmp_diff ) {
best_rate = tmp_rate ;
best_diff = tmp_diff ;
2015-07-07 20:48:08 +02:00
req - > best_parent_rate = tmp_parent_rate ;
2015-07-30 17:20:57 -07:00
req - > best_parent_hw = parent ;
2015-03-29 03:45:33 +02:00
}
2015-07-07 20:48:08 +02:00
if ( ! best_diff | | tmp_rate < req - > rate )
2015-03-29 03:45:33 +02:00
break ;
}
if ( ! best_diff )
break ;
}
2015-07-07 20:48:08 +02:00
if ( best_rate < 0 )
return best_rate ;
req - > rate = best_rate ;
return 0 ;
2013-10-17 18:55:41 +02:00
}
static int at91sam9x5_clk_usb_set_parent ( struct clk_hw * hw , u8 index )
{
struct at91sam9x5_clk_usb * usb = to_at91sam9x5_clk_usb ( hw ) ;
if ( index > 1 )
return - EINVAL ;
2014-09-07 08:14:29 +02:00
regmap_update_bits ( usb - > regmap , AT91_PMC_USB , AT91_PMC_USBS ,
index ? AT91_PMC_USBS : 0 ) ;
2013-10-17 18:55:41 +02:00
return 0 ;
}
static u8 at91sam9x5_clk_usb_get_parent ( struct clk_hw * hw )
{
struct at91sam9x5_clk_usb * usb = to_at91sam9x5_clk_usb ( hw ) ;
2014-09-07 08:14:29 +02:00
unsigned int usbr ;
2013-10-17 18:55:41 +02:00
2014-09-07 08:14:29 +02:00
regmap_read ( usb - > regmap , AT91_PMC_USB , & usbr ) ;
return usbr & AT91_PMC_USBS ;
2013-10-17 18:55:41 +02:00
}
static int at91sam9x5_clk_usb_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct at91sam9x5_clk_usb * usb = to_at91sam9x5_clk_usb ( hw ) ;
2014-11-17 14:16:56 +01:00
unsigned long div ;
if ( ! rate )
return - EINVAL ;
2013-10-17 18:55:41 +02:00
2014-11-17 14:16:56 +01:00
div = DIV_ROUND_CLOSEST ( parent_rate , rate ) ;
if ( div > SAM9X5_USB_MAX_DIV + 1 | | ! div )
2013-10-17 18:55:41 +02:00
return - EINVAL ;
2014-09-07 08:14:29 +02:00
regmap_update_bits ( usb - > regmap , AT91_PMC_USB , AT91_PMC_OHCIUSBDIV ,
( div - 1 ) < < SAM9X5_USB_DIV_SHIFT ) ;
2013-10-17 18:55:41 +02:00
return 0 ;
}
static const struct clk_ops at91sam9x5_usb_ops = {
. recalc_rate = at91sam9x5_clk_usb_recalc_rate ,
2015-03-29 03:45:33 +02:00
. determine_rate = at91sam9x5_clk_usb_determine_rate ,
2013-10-17 18:55:41 +02:00
. get_parent = at91sam9x5_clk_usb_get_parent ,
. set_parent = at91sam9x5_clk_usb_set_parent ,
. set_rate = at91sam9x5_clk_usb_set_rate ,
} ;
static int at91sam9n12_clk_usb_enable ( struct clk_hw * hw )
{
struct at91sam9x5_clk_usb * usb = to_at91sam9x5_clk_usb ( hw ) ;
2014-09-07 08:14:29 +02:00
regmap_update_bits ( usb - > regmap , AT91_PMC_USB , AT91_PMC_USBS ,
AT91_PMC_USBS ) ;
2013-10-17 18:55:41 +02:00
return 0 ;
}
static void at91sam9n12_clk_usb_disable ( struct clk_hw * hw )
{
struct at91sam9x5_clk_usb * usb = to_at91sam9x5_clk_usb ( hw ) ;
2014-09-07 08:14:29 +02:00
regmap_update_bits ( usb - > regmap , AT91_PMC_USB , AT91_PMC_USBS , 0 ) ;
2013-10-17 18:55:41 +02:00
}
static int at91sam9n12_clk_usb_is_enabled ( struct clk_hw * hw )
{
struct at91sam9x5_clk_usb * usb = to_at91sam9x5_clk_usb ( hw ) ;
2014-09-07 08:14:29 +02:00
unsigned int usbr ;
2013-10-17 18:55:41 +02:00
2014-09-07 08:14:29 +02:00
regmap_read ( usb - > regmap , AT91_PMC_USB , & usbr ) ;
return usbr & AT91_PMC_USBS ;
2013-10-17 18:55:41 +02:00
}
static const struct clk_ops at91sam9n12_usb_ops = {
. enable = at91sam9n12_clk_usb_enable ,
. disable = at91sam9n12_clk_usb_disable ,
. is_enabled = at91sam9n12_clk_usb_is_enabled ,
. recalc_rate = at91sam9x5_clk_usb_recalc_rate ,
2015-03-29 03:45:33 +02:00
. determine_rate = at91sam9x5_clk_usb_determine_rate ,
2013-10-17 18:55:41 +02:00
. set_rate = at91sam9x5_clk_usb_set_rate ,
} ;
2016-06-01 14:31:22 -07:00
static struct clk_hw * __init
2014-09-07 08:14:29 +02:00
at91sam9x5_clk_register_usb ( struct regmap * regmap , const char * name ,
2013-10-17 18:55:41 +02:00
const char * * parent_names , u8 num_parents )
{
struct at91sam9x5_clk_usb * usb ;
2016-06-01 14:31:22 -07:00
struct clk_hw * hw ;
2013-10-17 18:55:41 +02:00
struct clk_init_data init ;
2016-06-01 14:31:22 -07:00
int ret ;
2013-10-17 18:55:41 +02:00
usb = kzalloc ( sizeof ( * usb ) , GFP_KERNEL ) ;
if ( ! usb )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & at91sam9x5_usb_ops ;
init . parent_names = parent_names ;
init . num_parents = num_parents ;
2015-03-29 03:45:33 +02:00
init . flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
CLK_SET_RATE_PARENT ;
2013-10-17 18:55:41 +02:00
usb - > hw . init = & init ;
2014-09-07 08:14:29 +02:00
usb - > regmap = regmap ;
2013-10-17 18:55:41 +02:00
2016-06-01 14:31:22 -07:00
hw = & usb - > hw ;
ret = clk_hw_register ( NULL , & usb - > hw ) ;
if ( ret ) {
2013-10-17 18:55:41 +02:00
kfree ( usb ) ;
2016-06-01 14:31:22 -07:00
hw = ERR_PTR ( ret ) ;
}
2013-10-17 18:55:41 +02:00
2016-06-01 14:31:22 -07:00
return hw ;
2013-10-17 18:55:41 +02:00
}
2016-06-01 14:31:22 -07:00
static struct clk_hw * __init
2014-09-07 08:14:29 +02:00
at91sam9n12_clk_register_usb ( struct regmap * regmap , const char * name ,
2013-10-17 18:55:41 +02:00
const char * parent_name )
{
struct at91sam9x5_clk_usb * usb ;
2016-06-01 14:31:22 -07:00
struct clk_hw * hw ;
2013-10-17 18:55:41 +02:00
struct clk_init_data init ;
2016-06-01 14:31:22 -07:00
int ret ;
2013-10-17 18:55:41 +02:00
usb = kzalloc ( sizeof ( * usb ) , GFP_KERNEL ) ;
if ( ! usb )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & at91sam9n12_usb_ops ;
init . parent_names = & parent_name ;
init . num_parents = 1 ;
2015-03-29 03:45:33 +02:00
init . flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT ;
2013-10-17 18:55:41 +02:00
usb - > hw . init = & init ;
2014-09-07 08:14:29 +02:00
usb - > regmap = regmap ;
2013-10-17 18:55:41 +02:00
2016-06-01 14:31:22 -07:00
hw = & usb - > hw ;
ret = clk_hw_register ( NULL , & usb - > hw ) ;
if ( ret ) {
2013-10-17 18:55:41 +02:00
kfree ( usb ) ;
2016-06-01 14:31:22 -07:00
hw = ERR_PTR ( ret ) ;
}
2013-10-17 18:55:41 +02:00
2016-06-01 14:31:22 -07:00
return hw ;
2013-10-17 18:55:41 +02:00
}
static unsigned long at91rm9200_clk_usb_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct at91rm9200_clk_usb * usb = to_at91rm9200_clk_usb ( hw ) ;
2014-09-07 08:14:29 +02:00
unsigned int pllbr ;
2013-10-17 18:55:41 +02:00
u8 usbdiv ;
2014-09-07 08:14:29 +02:00
regmap_read ( usb - > regmap , AT91_CKGR_PLLBR , & pllbr ) ;
usbdiv = ( pllbr & AT91_PMC_USBDIV ) > > RM9200_USB_DIV_SHIFT ;
2013-10-17 18:55:41 +02:00
if ( usb - > divisors [ usbdiv ] )
return parent_rate / usb - > divisors [ usbdiv ] ;
return 0 ;
}
static long at91rm9200_clk_usb_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * parent_rate )
{
struct at91rm9200_clk_usb * usb = to_at91rm9200_clk_usb ( hw ) ;
2015-07-30 17:20:57 -07:00
struct clk_hw * parent = clk_hw_get_parent ( hw ) ;
2013-10-17 18:55:41 +02:00
unsigned long bestrate = 0 ;
int bestdiff = - 1 ;
unsigned long tmprate ;
int tmpdiff ;
int i = 0 ;
2014-09-02 09:50:17 +02:00
for ( i = 0 ; i < RM9200_USB_DIV_TAB_SIZE ; i + + ) {
unsigned long tmp_parent_rate ;
2013-10-17 18:55:41 +02:00
if ( ! usb - > divisors [ i ] )
continue ;
2014-09-02 09:50:17 +02:00
tmp_parent_rate = rate * usb - > divisors [ i ] ;
2015-07-30 17:20:57 -07:00
tmp_parent_rate = clk_hw_round_rate ( parent , tmp_parent_rate ) ;
2014-11-14 19:54:49 +01:00
tmprate = DIV_ROUND_CLOSEST ( tmp_parent_rate , usb - > divisors [ i ] ) ;
2013-10-17 18:55:41 +02:00
if ( tmprate < rate )
tmpdiff = rate - tmprate ;
else
tmpdiff = tmprate - rate ;
if ( bestdiff < 0 | | bestdiff > tmpdiff ) {
bestrate = tmprate ;
bestdiff = tmpdiff ;
2014-09-02 09:50:17 +02:00
* parent_rate = tmp_parent_rate ;
2013-10-17 18:55:41 +02:00
}
if ( ! bestdiff )
break ;
}
return bestrate ;
}
static int at91rm9200_clk_usb_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
int i ;
struct at91rm9200_clk_usb * usb = to_at91rm9200_clk_usb ( hw ) ;
2014-09-02 09:50:18 +02:00
unsigned long div ;
2013-10-17 18:55:41 +02:00
2014-11-14 19:54:49 +01:00
if ( ! rate )
2013-10-17 18:55:41 +02:00
return - EINVAL ;
2014-09-02 09:50:18 +02:00
2014-11-14 19:54:49 +01:00
div = DIV_ROUND_CLOSEST ( parent_rate , rate ) ;
2014-09-02 09:50:18 +02:00
2013-10-17 18:55:41 +02:00
for ( i = 0 ; i < RM9200_USB_DIV_TAB_SIZE ; i + + ) {
if ( usb - > divisors [ i ] = = div ) {
2014-09-07 08:14:29 +02:00
regmap_update_bits ( usb - > regmap , AT91_CKGR_PLLBR ,
AT91_PMC_USBDIV ,
i < < RM9200_USB_DIV_SHIFT ) ;
2013-10-17 18:55:41 +02:00
return 0 ;
}
}
return - EINVAL ;
}
static const struct clk_ops at91rm9200_usb_ops = {
. recalc_rate = at91rm9200_clk_usb_recalc_rate ,
. round_rate = at91rm9200_clk_usb_round_rate ,
. set_rate = at91rm9200_clk_usb_set_rate ,
} ;
2016-06-01 14:31:22 -07:00
static struct clk_hw * __init
2014-09-07 08:14:29 +02:00
at91rm9200_clk_register_usb ( struct regmap * regmap , const char * name ,
2013-10-17 18:55:41 +02:00
const char * parent_name , const u32 * divisors )
{
struct at91rm9200_clk_usb * usb ;
2016-06-01 14:31:22 -07:00
struct clk_hw * hw ;
2013-10-17 18:55:41 +02:00
struct clk_init_data init ;
2016-06-01 14:31:22 -07:00
int ret ;
2013-10-17 18:55:41 +02:00
usb = kzalloc ( sizeof ( * usb ) , GFP_KERNEL ) ;
if ( ! usb )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . ops = & at91rm9200_usb_ops ;
init . parent_names = & parent_name ;
init . num_parents = 1 ;
2014-09-02 09:50:17 +02:00
init . flags = CLK_SET_RATE_PARENT ;
2013-10-17 18:55:41 +02:00
usb - > hw . init = & init ;
2014-09-07 08:14:29 +02:00
usb - > regmap = regmap ;
2013-10-17 18:55:41 +02:00
memcpy ( usb - > divisors , divisors , sizeof ( usb - > divisors ) ) ;
2016-06-01 14:31:22 -07:00
hw = & usb - > hw ;
ret = clk_hw_register ( NULL , & usb - > hw ) ;
if ( ret ) {
2013-10-17 18:55:41 +02:00
kfree ( usb ) ;
2016-06-01 14:31:22 -07:00
hw = ERR_PTR ( ret ) ;
}
2013-10-17 18:55:41 +02:00
2016-06-01 14:31:22 -07:00
return hw ;
2013-10-17 18:55:41 +02:00
}
2014-09-07 08:14:29 +02:00
static void __init of_at91sam9x5_clk_usb_setup ( struct device_node * np )
2013-10-17 18:55:41 +02:00
{
2016-06-01 14:31:22 -07:00
struct clk_hw * hw ;
2016-02-19 17:29:17 -08:00
unsigned int num_parents ;
2013-10-17 18:55:41 +02:00
const char * parent_names [ USB_SOURCE_MAX ] ;
const char * name = np - > name ;
2014-09-07 08:14:29 +02:00
struct regmap * regmap ;
2013-10-17 18:55:41 +02:00
2015-05-29 11:25:45 +02:00
num_parents = of_clk_get_parent_count ( np ) ;
2016-02-19 17:29:17 -08:00
if ( num_parents = = 0 | | num_parents > USB_SOURCE_MAX )
2013-10-17 18:55:41 +02:00
return ;
2015-07-06 22:59:01 -05:00
of_clk_parent_fill ( np , parent_names , num_parents ) ;
2013-10-17 18:55:41 +02:00
of_property_read_string ( np , " clock-output-names " , & name ) ;
2014-09-07 08:14:29 +02:00
regmap = syscon_node_to_regmap ( of_get_parent ( np ) ) ;
if ( IS_ERR ( regmap ) )
return ;
2016-06-01 14:31:22 -07:00
hw = at91sam9x5_clk_register_usb ( regmap , name , parent_names ,
num_parents ) ;
if ( IS_ERR ( hw ) )
2013-10-17 18:55:41 +02:00
return ;
2016-06-01 14:31:22 -07:00
of_clk_add_hw_provider ( np , of_clk_hw_simple_get , hw ) ;
2013-10-17 18:55:41 +02:00
}
2014-09-07 08:14:29 +02:00
CLK_OF_DECLARE ( at91sam9x5_clk_usb , " atmel,at91sam9x5-clk-usb " ,
of_at91sam9x5_clk_usb_setup ) ;
2013-10-17 18:55:41 +02:00
2014-09-07 08:14:29 +02:00
static void __init of_at91sam9n12_clk_usb_setup ( struct device_node * np )
2013-10-17 18:55:41 +02:00
{
2016-06-01 14:31:22 -07:00
struct clk_hw * hw ;
2013-10-17 18:55:41 +02:00
const char * parent_name ;
const char * name = np - > name ;
2014-09-07 08:14:29 +02:00
struct regmap * regmap ;
2013-10-17 18:55:41 +02:00
parent_name = of_clk_get_parent_name ( np , 0 ) ;
if ( ! parent_name )
return ;
of_property_read_string ( np , " clock-output-names " , & name ) ;
2014-09-07 08:14:29 +02:00
regmap = syscon_node_to_regmap ( of_get_parent ( np ) ) ;
if ( IS_ERR ( regmap ) )
return ;
2016-06-01 14:31:22 -07:00
hw = at91sam9n12_clk_register_usb ( regmap , name , parent_name ) ;
if ( IS_ERR ( hw ) )
2013-10-17 18:55:41 +02:00
return ;
2016-06-01 14:31:22 -07:00
of_clk_add_hw_provider ( np , of_clk_hw_simple_get , hw ) ;
2013-10-17 18:55:41 +02:00
}
2014-09-07 08:14:29 +02:00
CLK_OF_DECLARE ( at91sam9n12_clk_usb , " atmel,at91sam9n12-clk-usb " ,
of_at91sam9n12_clk_usb_setup ) ;
2013-10-17 18:55:41 +02:00
2014-09-07 08:14:29 +02:00
static void __init of_at91rm9200_clk_usb_setup ( struct device_node * np )
2013-10-17 18:55:41 +02:00
{
2016-06-01 14:31:22 -07:00
struct clk_hw * hw ;
2013-10-17 18:55:41 +02:00
const char * parent_name ;
const char * name = np - > name ;
u32 divisors [ 4 ] = { 0 , 0 , 0 , 0 } ;
2014-09-07 08:14:29 +02:00
struct regmap * regmap ;
2013-10-17 18:55:41 +02:00
parent_name = of_clk_get_parent_name ( np , 0 ) ;
if ( ! parent_name )
return ;
of_property_read_u32_array ( np , " atmel,clk-divisors " , divisors , 4 ) ;
if ( ! divisors [ 0 ] )
return ;
of_property_read_string ( np , " clock-output-names " , & name ) ;
2014-09-07 08:14:29 +02:00
regmap = syscon_node_to_regmap ( of_get_parent ( np ) ) ;
if ( IS_ERR ( regmap ) )
return ;
2016-06-01 14:31:22 -07:00
hw = at91rm9200_clk_register_usb ( regmap , name , parent_name , divisors ) ;
if ( IS_ERR ( hw ) )
2013-10-17 18:55:41 +02:00
return ;
2016-06-01 14:31:22 -07:00
of_clk_add_hw_provider ( np , of_clk_hw_simple_get , hw ) ;
2013-10-17 18:55:41 +02:00
}
2014-09-07 08:14:29 +02:00
CLK_OF_DECLARE ( at91rm9200_clk_usb , " atmel,at91rm9200-clk-usb " ,
of_at91rm9200_clk_usb_setup ) ;