Skip to content

Commit ae1221b

Browse files
committed
clk: at91: utmi: set the mainck rate
By default, it is assumed that the UTMI clock is generated from a 12 MHz reference clock (MAINCK). If it's not the case, the FREQ field of the SFR_UTMICKTRIM has to be updated to generate the UTMI clock in the proper way. The UTMI clock has a fixed rate of 480 MHz. In fact, there is no multiplier we can configure. The multiplier is managed internally, depending on the reference clock frequency, to achieve the target of 480 MHz. Signed-off-by: Ludovic Desroches <ludovic.desroches@microchip.com>
1 parent 0ee5b98 commit ae1221b

2 files changed

Lines changed: 70 additions & 14 deletions

File tree

drivers/clk/at91/clk-utmi.c

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,20 @@
1414
#include <linux/of.h>
1515
#include <linux/mfd/syscon.h>
1616
#include <linux/regmap.h>
17+
#include <soc/at91/atmel-sfr.h>
1718

1819
#include "pmc.h"
1920

20-
#define UTMI_FIXED_MUL 40
21+
/*
22+
* The purpose of this clock is to generate a 480 MHz signal. A different
23+
* rate can't be configured.
24+
*/
25+
#define UTMI_RATE 480000000
2126

2227
struct clk_utmi {
2328
struct clk_hw hw;
24-
struct regmap *regmap;
29+
struct regmap *regmap_pmc;
30+
struct regmap *regmap_sfr;
2531
};
2632

2733
#define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw)
@@ -37,13 +43,54 @@ static inline bool clk_utmi_ready(struct regmap *regmap)
3743

3844
static int clk_utmi_prepare(struct clk_hw *hw)
3945
{
46+
struct clk_hw *hw_parent;
4047
struct clk_utmi *utmi = to_clk_utmi(hw);
4148
unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT |
4249
AT91_PMC_BIASEN;
50+
unsigned int utmi_ref_clk_freq;
51+
unsigned long parent_rate;
52+
53+
/*
54+
* If mainck rate is different from 12 MHz, we have to configure the
55+
* FREQ field of the SFR_UTMICKTRIM register to generate properly
56+
* the utmi clock.
57+
*/
58+
hw_parent = clk_hw_get_parent(hw);
59+
parent_rate = clk_hw_get_rate(hw_parent);
60+
61+
switch (parent_rate) {
62+
case 12000000:
63+
utmi_ref_clk_freq = 0;
64+
break;
65+
case 16000000:
66+
utmi_ref_clk_freq = 1;
67+
break;
68+
case 24000000:
69+
utmi_ref_clk_freq = 2;
70+
break;
71+
/*
72+
* Not supported on SAMA5D2 but it's not an issue since MAINCK
73+
* maximum value is 24 MHz.
74+
*/
75+
case 48000000:
76+
utmi_ref_clk_freq = 3;
77+
break;
78+
default:
79+
pr_err("UTMICK: unsupported mainck rate\n");
80+
return -EINVAL;
81+
}
4382

44-
regmap_update_bits(utmi->regmap, AT91_CKGR_UCKR, uckr, uckr);
83+
if (utmi->regmap_sfr) {
84+
regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM,
85+
AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq);
86+
} else if (utmi_ref_clk_freq) {
87+
pr_err("UTMICK: sfr node required\n");
88+
return -EINVAL;
89+
}
90+
91+
regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr);
4592

46-
while (!clk_utmi_ready(utmi->regmap))
93+
while (!clk_utmi_ready(utmi->regmap_pmc))
4794
cpu_relax();
4895

4996
return 0;
@@ -53,21 +100,22 @@ static int clk_utmi_is_prepared(struct clk_hw *hw)
53100
{
54101
struct clk_utmi *utmi = to_clk_utmi(hw);
55102

56-
return clk_utmi_ready(utmi->regmap);
103+
return clk_utmi_ready(utmi->regmap_pmc);
57104
}
58105

59106
static void clk_utmi_unprepare(struct clk_hw *hw)
60107
{
61108
struct clk_utmi *utmi = to_clk_utmi(hw);
62109

63-
regmap_update_bits(utmi->regmap, AT91_CKGR_UCKR, AT91_PMC_UPLLEN, 0);
110+
regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR,
111+
AT91_PMC_UPLLEN, 0);
64112
}
65113

66114
static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw,
67115
unsigned long parent_rate)
68116
{
69-
/* UTMI clk is a fixed clk multiplier */
70-
return parent_rate * UTMI_FIXED_MUL;
117+
/* UTMI clk rate is fixed. */
118+
return UTMI_RATE;
71119
}
72120

73121
static const struct clk_ops utmi_ops = {
@@ -78,7 +126,7 @@ static const struct clk_ops utmi_ops = {
78126
};
79127

80128
static struct clk_hw * __init
81-
at91_clk_register_utmi(struct regmap *regmap,
129+
at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
82130
const char *name, const char *parent_name)
83131
{
84132
struct clk_utmi *utmi;
@@ -97,7 +145,8 @@ at91_clk_register_utmi(struct regmap *regmap,
97145
init.flags = CLK_SET_RATE_GATE;
98146

99147
utmi->hw.init = &init;
100-
utmi->regmap = regmap;
148+
utmi->regmap_pmc = regmap_pmc;
149+
utmi->regmap_sfr = regmap_sfr;
101150

102151
hw = &utmi->hw;
103152
ret = clk_hw_register(NULL, &utmi->hw);
@@ -114,17 +163,22 @@ static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np)
114163
struct clk_hw *hw;
115164
const char *parent_name;
116165
const char *name = np->name;
117-
struct regmap *regmap;
166+
struct regmap *regmap_pmc, *regmap_sfr;
118167

119168
parent_name = of_clk_get_parent_name(np, 0);
120169

121170
of_property_read_string(np, "clock-output-names", &name);
122171

123-
regmap = syscon_node_to_regmap(of_get_parent(np));
124-
if (IS_ERR(regmap))
172+
regmap_pmc = syscon_node_to_regmap(of_get_parent(np));
173+
if (IS_ERR(regmap_pmc))
125174
return;
126175

127-
hw = at91_clk_register_utmi(regmap, name, parent_name);
176+
/* SFR node missing is not necessarily an issue. */
177+
regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr");
178+
if (IS_ERR(regmap_sfr))
179+
regmap_sfr = NULL;
180+
181+
hw = at91_clk_register_utmi(regmap_pmc, regmap_sfr, name, parent_name);
128182
if (IS_ERR(hw))
129183
return;
130184

include/soc/at91/atmel-sfr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
/* 0x08 ~ 0x0c: Reserved */
1818
#define AT91_SFR_OHCIICR 0x10 /* OHCI INT Configuration Register */
1919
#define AT91_SFR_OHCIISR 0x14 /* OHCI INT Status Register */
20+
#define AT91_SFR_UTMICKTRIM 0x30 /* UTMI Clock Trimming Register */
2021
#define AT91_SFR_I2SCLKSEL 0x90 /* I2SC Register */
2122

2223
/* Field definitions */
@@ -28,5 +29,6 @@
2829
AT91_OHCIICR_SUSPEND_B | \
2930
AT91_OHCIICR_SUSPEND_C)
3031

32+
#define AT91_UTMICKTRIM_FREQ GENMASK(1, 0)
3133

3234
#endif /* _LINUX_MFD_SYSCON_ATMEL_SFR_H */

0 commit comments

Comments
 (0)