clk: si544: add support for si549

Add support for Si549, which only differs from Si544 in internal
oscilator frequency. Si549 also comes in three types A,B and C.
Each having different maximum output frequency.

This patch also fixes maximum frequency of Si544 C. Which should
be 325MHz according to datasheet.

Signed-off-by: Pavel Löbl <pavel@loebl.cz>
Reviewed-by: Brian Masney <bmasney@redhat.com>
Signed-off-by: Brian Masney <bmasney@redhat.com>
This commit is contained in:
Pavel Löbl
2026-07-01 16:51:01 +02:00
committed by Brian Masney
parent d1828369fa
commit bdba63b598
2 changed files with 68 additions and 20 deletions

View File

@@ -155,12 +155,12 @@ config COMMON_CLK_SI514
generator.
config COMMON_CLK_SI544
tristate "Clock driver for SiLabs 544 devices"
tristate "Clock driver for SiLabs 544 and compatible devices"
depends on I2C
select REGMAP_I2C
help
This driver supports the Silicon Labs 544 programmable clock
generator.
This driver supports the Silicon Labs 544/549 programmable clock
generators.
config COMMON_CLK_SI570
tristate "Clock driver for SiLabs 570 and compatible devices"

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for Silicon Labs Si544 Programmable Oscillator
* Driver for Silicon Labs Si544/Si549 Programmable Oscillator
* Copyright (C) 2018 Topic Embedded Products
* Author: Mike Looijmans <mike.looijmans@topic.nl>
*/
@@ -40,7 +40,9 @@
#define SI544_MIN_FREQ 200000U
/* Si544 Internal oscillator runs at 55.05 MHz */
#define FXO 55050000U
#define SI544_XO_FREQ 55050000U
/* Si549 Internal oscilator runs at 152.60 MHz */
#define SI549_XO_FREQ 152600000U
/* VCO range is 10.8 .. 12.1 GHz, max depends on speed grade */
#define FVCO_MIN 10800000000ULL
@@ -56,11 +58,16 @@
#define DELTA_M_FRAC_NUM 19
#define DELTA_M_FRAC_DEN 20000
struct si544_clk_desc {
unsigned long max_freq;
unsigned long xo_freq;
};
struct clk_si544 {
struct clk_hw hw;
struct regmap *regmap;
struct i2c_client *i2c_client;
unsigned long max_freq;
const struct si544_clk_desc *chip_info;
};
#define to_clk_si544(_hw) container_of(_hw, struct clk_si544, hw)
@@ -79,6 +86,7 @@ struct clk_si544_muldiv {
u16 hs_div;
u8 ls_div_bits;
s32 delta_m;
u32 xo_freq;
};
/* Enables or disables the output driver */
@@ -145,6 +153,8 @@ static int si544_get_muldiv(struct clk_si544 *data,
settings->delta_m = reg[0] << 8 | reg[1] << 16 | reg[2] << 24;
settings->delta_m >>= 8;
settings->xo_freq = data->chip_info->xo_freq;
return 0;
}
@@ -193,7 +203,7 @@ static bool is_valid_frequency(const struct clk_si544 *data,
if (frequency < SI544_MIN_FREQ)
return false;
return frequency <= data->max_freq;
return frequency <= data->chip_info->max_freq;
}
/* Calculate divider settings for a given frequency */
@@ -201,6 +211,7 @@ static int si544_calc_muldiv(struct clk_si544_muldiv *settings,
unsigned long frequency)
{
u64 vco;
u32 fxo = settings->xo_freq;
u32 ls_freq;
u32 tmp;
u8 res;
@@ -238,13 +249,13 @@ static int si544_calc_muldiv(struct clk_si544_muldiv *settings,
vco = (u64)ls_freq * settings->hs_div;
/* Calculate the integer part of the feedback divider */
tmp = do_div(vco, FXO);
tmp = do_div(vco, fxo);
settings->fb_div_int = vco;
/* And the fractional bits using the remainder */
vco = (u64)tmp << 32;
vco += FXO / 2; /* Round to nearest multiple */
do_div(vco, FXO);
vco += fxo / 2; /* Round to nearest multiple */
do_div(vco, fxo);
settings->fb_div_frac = vco;
/* Reset the frequency adjustment */
@@ -258,15 +269,16 @@ static unsigned long si544_calc_center_rate(
const struct clk_si544_muldiv *settings)
{
u32 d = settings->hs_div * BIT(settings->ls_div_bits);
u32 fxo = settings->xo_freq;
u64 vco;
/* Calculate VCO from the fractional part */
vco = (u64)settings->fb_div_frac * FXO;
vco += (FXO / 2);
vco = (u64)settings->fb_div_frac * fxo;
vco += (fxo / 2);
vco >>= 32;
/* Add the integer part of the VCO frequency */
vco += (u64)settings->fb_div_int * FXO;
vco += (u64)settings->fb_div_int * fxo;
/* Apply divider to obtain the generated frequency */
do_div(vco, d);
@@ -446,7 +458,7 @@ static int si544_probe(struct i2c_client *client)
init.num_parents = 0;
data->hw.init = &init;
data->i2c_client = client;
data->max_freq = (uintptr_t)i2c_get_match_data(client);
data->chip_info = i2c_get_match_data(client);
if (of_property_read_string(client->dev.of_node, "clock-output-names",
&init.name))
@@ -478,18 +490,54 @@ static int si544_probe(struct i2c_client *client)
return 0;
}
static const struct si544_clk_desc clk_si544a_info = {
.xo_freq = SI544_XO_FREQ,
.max_freq = 1500000000,
};
static const struct si544_clk_desc clk_si544b_info = {
.xo_freq = SI544_XO_FREQ,
.max_freq = 800000000,
};
static const struct si544_clk_desc clk_si544c_info = {
.xo_freq = SI544_XO_FREQ,
.max_freq = 325000000,
};
static const struct si544_clk_desc clk_si549a_info = {
.xo_freq = SI549_XO_FREQ,
.max_freq = 1500000000,
};
static const struct si544_clk_desc clk_si549b_info = {
.xo_freq = SI549_XO_FREQ,
.max_freq = 800000000,
};
static const struct si544_clk_desc clk_si549c_info = {
.xo_freq = SI549_XO_FREQ,
.max_freq = 325000000,
};
static const struct i2c_device_id si544_id[] = {
{ "si544a", 1500000000 },
{ "si544b", 800000000 },
{ "si544c", 350000000 },
{ "si544a", (kernel_ulong_t)&clk_si544a_info },
{ "si544b", (kernel_ulong_t)&clk_si544b_info },
{ "si544c", (kernel_ulong_t)&clk_si544c_info },
{ "si549a", (kernel_ulong_t)&clk_si549a_info },
{ "si549b", (kernel_ulong_t)&clk_si549b_info },
{ "si549c", (kernel_ulong_t)&clk_si549c_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, si544_id);
static const struct of_device_id clk_si544_of_match[] = {
{ .compatible = "silabs,si544a", .data = (void *)1500000000 },
{ .compatible = "silabs,si544b", .data = (void *)800000000 },
{ .compatible = "silabs,si544c", .data = (void *)350000000 },
{ .compatible = "silabs,si544a", .data = &clk_si544a_info },
{ .compatible = "silabs,si544b", .data = &clk_si544b_info },
{ .compatible = "silabs,si544c", .data = &clk_si544c_info },
{ .compatible = "silabs,si549a", .data = &clk_si549a_info },
{ .compatible = "silabs,si549b", .data = &clk_si549b_info },
{ .compatible = "silabs,si549c", .data = &clk_si549c_info },
{ }
};
MODULE_DEVICE_TABLE(of, clk_si544_of_match);