Skip to content

Commit 6927518

Browse files
MikeLooijmansbebarino
authored andcommitted
clk, clk-si5341: Support multiple input ports
The Si5341 and Si5340 have multiple input clock options. So far, the driver only supported the XTAL input, this adds support for the three external clock inputs as well. If the clock chip isn't programmed at boot, the driver will default to the XTAL input as before. If there is no "xtal" clock input available, it will pick the first connected input (e.g. "in0") as the input clock for the PLL. One can use clock-assigned-parents to select a particular clock as input. Signed-off-by: Mike Looijmans <[email protected]> Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Stephen Boyd <[email protected]>
1 parent e42617b commit 6927518

File tree

1 file changed

+196
-16
lines changed

1 file changed

+196
-16
lines changed

drivers/clk/clk-si5341.c

Lines changed: 196 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include <linux/slab.h>
1717
#include <asm/unaligned.h>
1818

19+
#define SI5341_NUM_INPUTS 4
20+
1921
#define SI5341_MAX_NUM_OUTPUTS 10
2022
#define SI5340_MAX_NUM_OUTPUTS 4
2123

@@ -56,8 +58,8 @@ struct clk_si5341 {
5658
struct i2c_client *i2c_client;
5759
struct clk_si5341_synth synth[SI5341_NUM_SYNTH];
5860
struct clk_si5341_output clk[SI5341_MAX_NUM_OUTPUTS];
59-
struct clk *pxtal;
60-
const char *pxtal_name;
61+
struct clk *input_clk[SI5341_NUM_INPUTS];
62+
const char *input_clk_name[SI5341_NUM_INPUTS];
6163
const u16 *reg_output_offset;
6264
const u16 *reg_rdiv_offset;
6365
u64 freq_vco; /* 13500–14256 MHz */
@@ -78,10 +80,25 @@ struct clk_si5341_output_config {
7880
#define SI5341_DEVICE_REV 0x0005
7981
#define SI5341_STATUS 0x000C
8082
#define SI5341_SOFT_RST 0x001C
83+
#define SI5341_IN_SEL 0x0021
84+
#define SI5341_XAXB_CFG 0x090E
85+
#define SI5341_IN_EN 0x0949
86+
#define SI5341_INX_TO_PFD_EN 0x094A
87+
88+
/* Input selection */
89+
#define SI5341_IN_SEL_MASK 0x06
90+
#define SI5341_IN_SEL_SHIFT 1
91+
#define SI5341_IN_SEL_REGCTRL 0x01
92+
#define SI5341_INX_TO_PFD_SHIFT 4
93+
94+
/* XTAL config bits */
95+
#define SI5341_XAXB_CFG_EXTCLK_EN BIT(0)
96+
#define SI5341_XAXB_CFG_PDNB BIT(1)
8197

8298
/* Input dividers (48-bit) */
8399
#define SI5341_IN_PDIV(x) (0x0208 + ((x) * 10))
84100
#define SI5341_IN_PSET(x) (0x020E + ((x) * 10))
101+
#define SI5341_PX_UPD 0x0230
85102

86103
/* PLL configuration */
87104
#define SI5341_PLL_M_NUM 0x0235
@@ -120,6 +137,10 @@ struct si5341_reg_default {
120137
u8 value;
121138
};
122139

140+
static const char * const si5341_input_clock_names[] = {
141+
"in0", "in1", "in2", "xtal"
142+
};
143+
123144
/* Output configuration registers 0..9 are not quite logically organized */
124145
static const u16 si5341_reg_output_offset[] = {
125146
0x0108,
@@ -390,7 +411,112 @@ static unsigned long si5341_clk_recalc_rate(struct clk_hw *hw,
390411
return (unsigned long)res;
391412
}
392413

414+
static int si5341_clk_get_selected_input(struct clk_si5341 *data)
415+
{
416+
int err;
417+
u32 val;
418+
419+
err = regmap_read(data->regmap, SI5341_IN_SEL, &val);
420+
if (err < 0)
421+
return err;
422+
423+
return (val & SI5341_IN_SEL_MASK) >> SI5341_IN_SEL_SHIFT;
424+
}
425+
426+
static u8 si5341_clk_get_parent(struct clk_hw *hw)
427+
{
428+
struct clk_si5341 *data = to_clk_si5341(hw);
429+
int res = si5341_clk_get_selected_input(data);
430+
431+
if (res < 0)
432+
return 0; /* Apparently we cannot report errors */
433+
434+
return res;
435+
}
436+
437+
static int si5341_clk_reparent(struct clk_si5341 *data, u8 index)
438+
{
439+
int err;
440+
u8 val;
441+
442+
val = (index << SI5341_IN_SEL_SHIFT) & SI5341_IN_SEL_MASK;
443+
/* Enable register-based input selection */
444+
val |= SI5341_IN_SEL_REGCTRL;
445+
446+
err = regmap_update_bits(data->regmap,
447+
SI5341_IN_SEL, SI5341_IN_SEL_REGCTRL | SI5341_IN_SEL_MASK, val);
448+
if (err < 0)
449+
return err;
450+
451+
if (index < 3) {
452+
/* Enable input buffer for selected input */
453+
err = regmap_update_bits(data->regmap,
454+
SI5341_IN_EN, 0x07, BIT(index));
455+
if (err < 0)
456+
return err;
457+
458+
/* Enables the input to phase detector */
459+
err = regmap_update_bits(data->regmap, SI5341_INX_TO_PFD_EN,
460+
0x7 << SI5341_INX_TO_PFD_SHIFT,
461+
BIT(index + SI5341_INX_TO_PFD_SHIFT));
462+
if (err < 0)
463+
return err;
464+
465+
/* Power down XTAL oscillator and buffer */
466+
err = regmap_update_bits(data->regmap, SI5341_XAXB_CFG,
467+
SI5341_XAXB_CFG_PDNB, 0);
468+
if (err < 0)
469+
return err;
470+
471+
/*
472+
* Set the P divider to "1". There's no explanation in the
473+
* datasheet of these registers, but the clockbuilder software
474+
* programs a "1" when the input is being used.
475+
*/
476+
err = regmap_write(data->regmap, SI5341_IN_PDIV(index), 1);
477+
if (err < 0)
478+
return err;
479+
480+
err = regmap_write(data->regmap, SI5341_IN_PSET(index), 1);
481+
if (err < 0)
482+
return err;
483+
484+
/* Set update PDIV bit */
485+
err = regmap_write(data->regmap, SI5341_PX_UPD, BIT(index));
486+
if (err < 0)
487+
return err;
488+
} else {
489+
/* Disable all input buffers */
490+
err = regmap_update_bits(data->regmap, SI5341_IN_EN, 0x07, 0);
491+
if (err < 0)
492+
return err;
493+
494+
/* Disable input to phase detector */
495+
err = regmap_update_bits(data->regmap, SI5341_INX_TO_PFD_EN,
496+
0x7 << SI5341_INX_TO_PFD_SHIFT, 0);
497+
if (err < 0)
498+
return err;
499+
500+
/* Power up XTAL oscillator and buffer */
501+
err = regmap_update_bits(data->regmap, SI5341_XAXB_CFG,
502+
SI5341_XAXB_CFG_PDNB, SI5341_XAXB_CFG_PDNB);
503+
if (err < 0)
504+
return err;
505+
}
506+
507+
return 0;
508+
}
509+
510+
static int si5341_clk_set_parent(struct clk_hw *hw, u8 index)
511+
{
512+
struct clk_si5341 *data = to_clk_si5341(hw);
513+
514+
return si5341_clk_reparent(data, index);
515+
}
516+
393517
static const struct clk_ops si5341_clk_ops = {
518+
.set_parent = si5341_clk_set_parent,
519+
.get_parent = si5341_clk_get_parent,
394520
.recalc_rate = si5341_clk_recalc_rate,
395521
};
396522

@@ -985,7 +1111,8 @@ static const struct regmap_range si5341_regmap_volatile_range[] = {
9851111
regmap_reg_range(0x000C, 0x0012), /* Status */
9861112
regmap_reg_range(0x001C, 0x001E), /* reset, finc/fdec */
9871113
regmap_reg_range(0x00E2, 0x00FE), /* NVM, interrupts, device ready */
988-
/* Update bits for synth config */
1114+
/* Update bits for P divider and synth config */
1115+
regmap_reg_range(SI5341_PX_UPD, SI5341_PX_UPD),
9891116
regmap_reg_range(SI5341_SYNTH_N_UPD(0), SI5341_SYNTH_N_UPD(0)),
9901117
regmap_reg_range(SI5341_SYNTH_N_UPD(1), SI5341_SYNTH_N_UPD(1)),
9911118
regmap_reg_range(SI5341_SYNTH_N_UPD(2), SI5341_SYNTH_N_UPD(2)),
@@ -1122,6 +1249,7 @@ static int si5341_initialize_pll(struct clk_si5341 *data)
11221249
struct device_node *np = data->i2c_client->dev.of_node;
11231250
u32 m_num = 0;
11241251
u32 m_den = 0;
1252+
int sel;
11251253

11261254
if (of_property_read_u32(np, "silabs,pll-m-num", &m_num)) {
11271255
dev_err(&data->i2c_client->dev,
@@ -1135,19 +1263,64 @@ static int si5341_initialize_pll(struct clk_si5341 *data)
11351263
if (!m_num || !m_den) {
11361264
dev_err(&data->i2c_client->dev,
11371265
"PLL configuration invalid, assume 14GHz\n");
1138-
m_den = clk_get_rate(data->pxtal) / 10;
1266+
sel = si5341_clk_get_selected_input(data);
1267+
if (sel < 0)
1268+
return sel;
1269+
1270+
m_den = clk_get_rate(data->input_clk[sel]) / 10;
11391271
m_num = 1400000000;
11401272
}
11411273

11421274
return si5341_encode_44_32(data->regmap,
11431275
SI5341_PLL_M_NUM, m_num, m_den);
11441276
}
11451277

1278+
static int si5341_clk_select_active_input(struct clk_si5341 *data)
1279+
{
1280+
int res;
1281+
int err;
1282+
int i;
1283+
1284+
res = si5341_clk_get_selected_input(data);
1285+
if (res < 0)
1286+
return res;
1287+
1288+
/* If the current register setting is invalid, pick the first input */
1289+
if (!data->input_clk[res]) {
1290+
dev_dbg(&data->i2c_client->dev,
1291+
"Input %d not connected, rerouting\n", res);
1292+
res = -ENODEV;
1293+
for (i = 0; i < SI5341_NUM_INPUTS; ++i) {
1294+
if (data->input_clk[i]) {
1295+
res = i;
1296+
break;
1297+
}
1298+
}
1299+
if (res < 0) {
1300+
dev_err(&data->i2c_client->dev,
1301+
"No clock input available\n");
1302+
return res;
1303+
}
1304+
}
1305+
1306+
/* Make sure the selected clock is also enabled and routed */
1307+
err = si5341_clk_reparent(data, res);
1308+
if (err < 0)
1309+
return err;
1310+
1311+
err = clk_prepare_enable(data->input_clk[res]);
1312+
if (err < 0)
1313+
return err;
1314+
1315+
return res;
1316+
}
1317+
11461318
static int si5341_probe(struct i2c_client *client,
11471319
const struct i2c_device_id *id)
11481320
{
11491321
struct clk_si5341 *data;
11501322
struct clk_init_data init;
1323+
struct clk *input;
11511324
const char *root_clock_name;
11521325
const char *synth_clock_names[SI5341_NUM_SYNTH];
11531326
int err;
@@ -1161,12 +1334,16 @@ static int si5341_probe(struct i2c_client *client,
11611334

11621335
data->i2c_client = client;
11631336

1164-
data->pxtal = devm_clk_get(&client->dev, "xtal");
1165-
if (IS_ERR(data->pxtal)) {
1166-
if (PTR_ERR(data->pxtal) == -EPROBE_DEFER)
1167-
return -EPROBE_DEFER;
1168-
1169-
dev_err(&client->dev, "Missing xtal clock input\n");
1337+
for (i = 0; i < SI5341_NUM_INPUTS; ++i) {
1338+
input = devm_clk_get(&client->dev, si5341_input_clock_names[i]);
1339+
if (IS_ERR(input)) {
1340+
if (PTR_ERR(input) == -EPROBE_DEFER)
1341+
return -EPROBE_DEFER;
1342+
data->input_clk_name[i] = si5341_input_clock_names[i];
1343+
} else {
1344+
data->input_clk[i] = input;
1345+
data->input_clk_name[i] = __clk_get_name(input);
1346+
}
11701347
}
11711348

11721349
err = si5341_dt_parse_dt(client, config);
@@ -1188,9 +1365,6 @@ static int si5341_probe(struct i2c_client *client,
11881365
if (err < 0)
11891366
return err;
11901367

1191-
/* "Activate" the xtal (usually a fixed clock) */
1192-
clk_prepare_enable(data->pxtal);
1193-
11941368
if (of_property_read_bool(client->dev.of_node, "silabs,reprogram")) {
11951369
initialization_required = true;
11961370
} else {
@@ -1223,17 +1397,23 @@ static int si5341_probe(struct i2c_client *client,
12231397
ARRAY_SIZE(si5341_reg_defaults));
12241398
if (err < 0)
12251399
return err;
1400+
}
1401+
1402+
/* Input must be up and running at this point */
1403+
err = si5341_clk_select_active_input(data);
1404+
if (err < 0)
1405+
return err;
12261406

1407+
if (initialization_required) {
12271408
/* PLL configuration is required */
12281409
err = si5341_initialize_pll(data);
12291410
if (err < 0)
12301411
return err;
12311412
}
12321413

12331414
/* Register the PLL */
1234-
data->pxtal_name = __clk_get_name(data->pxtal);
1235-
init.parent_names = &data->pxtal_name;
1236-
init.num_parents = 1; /* For now, only XTAL input supported */
1415+
init.parent_names = data->input_clk_name;
1416+
init.num_parents = SI5341_NUM_INPUTS;
12371417
init.ops = &si5341_clk_ops;
12381418
init.flags = 0;
12391419
data->hw.init = &init;

0 commit comments

Comments
 (0)