2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2013-10-17 18:55:41 +02:00
/*
* Copyright ( C ) 2013 Boris BREZILLON < b . brezillon @ overkiz . com >
*/
# 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 SAM9X5_USB_DIV_SHIFT 8
# define SAM9X5_USB_MAX_DIV 0xf
# define RM9200_USB_DIV_SHIFT 28
# define RM9200_USB_DIV_TAB_SIZE 4
2019-04-02 14:50:52 +02:00
# define SAM9X5_USBS_MASK GENMASK(0, 0)
# define SAM9X60_USBS_MASK GENMASK(1, 0)
2013-10-17 18:55:41 +02:00
struct at91sam9x5_clk_usb {
struct clk_hw hw ;
2014-09-07 08:14:29 +02:00
struct regmap * regmap ;
2019-04-02 14:50:52 +02:00
u32 usbs_mask ;
2020-01-17 13:36:49 +02:00
u8 num_parents ;
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 ) ;
2020-01-17 13:36:46 +02:00
if ( ! tmp_parent_rate )
continue ;
2015-03-29 03:45:33 +02:00
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 ) ;
2020-01-17 13:36:49 +02:00
if ( index > = usb - > num_parents )
2013-10-17 18:55:41 +02:00
return - EINVAL ;
2014-09-07 08:14:29 +02:00
2019-04-02 14:50:52 +02:00
regmap_update_bits ( usb - > regmap , AT91_PMC_USB , usb - > usbs_mask , index ) ;
2014-09-07 08:14:29 +02:00
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 ) ;
2019-04-02 14:50:52 +02:00
return usbr & usb - > usbs_mask ;
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 ,
} ;
2019-04-02 14:50:52 +02:00
static struct clk_hw * __init
_at91sam9x5_clk_register_usb ( struct regmap * regmap , const char * name ,
const char * * parent_names , u8 num_parents ,
u32 usbs_mask )
2013-10-17 18:55:41 +02:00
{
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 ;
2020-01-17 13:36:48 +02:00
usb - > usbs_mask = usbs_mask ;
2020-01-17 13:36:49 +02:00
usb - > num_parents = num_parents ;
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
}
2019-04-02 14:50:52 +02:00
struct clk_hw * __init
at91sam9x5_clk_register_usb ( struct regmap * regmap , const char * name ,
const char * * parent_names , u8 num_parents )
{
return _at91sam9x5_clk_register_usb ( regmap , name , parent_names ,
num_parents , SAM9X5_USBS_MASK ) ;
}
struct clk_hw * __init
sam9x60_clk_register_usb ( struct regmap * regmap , const char * name ,
const char * * parent_names , u8 num_parents )
{
return _at91sam9x5_clk_register_usb ( regmap , name , parent_names ,
num_parents , SAM9X60_USBS_MASK ) ;
}
2018-10-16 16:21:44 +02:00
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 ,
} ;
2018-10-16 16:21:44 +02:00
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
}