lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <41b517e9-b5b3-6346-16fd-54e5923684d6@topic.nl>
Date:   Wed, 18 Dec 2019 09:11:06 +0000
From:   Mike Looijmans <mike.looijmans@...ic.nl>
To:     "linux-clk@...r.kernel.org" <linux-clk@...r.kernel.org>
CC:     "linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
        "sboyd@...nel.org" <sboyd@...nel.org>,
        "mturquette@...libre.com" <mturquette@...libre.com>
Subject: Re: [PATCH] clk, clk-si5341: Support multiple input ports

Gentle "ping" for this patch, I haven't seen any reaction or state change..

On 05-12-2019 12:57, Mike Looijmans wrote:
> 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 is'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 <mike.looijmans@...ic.nl>
> ---
>   drivers/clk/clk-si5341.c | 213 +++++++++++++++++++++++++++++++++++----
>   1 file changed, 196 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c
> index 6e780c2a9e6b..f7dba7698083 100644
> --- a/drivers/clk/clk-si5341.c
> +++ b/drivers/clk/clk-si5341.c
> @@ -4,7 +4,6 @@
>    * Copyright (C) 2019 Topic Embedded Products
>    * Author: Mike Looijmans <mike.looijmans@...ic.nl>
>    */
> -
>   #include <linux/clk.h>
>   #include <linux/clk-provider.h>
>   #include <linux/delay.h>
> @@ -16,6 +15,8 @@
>   #include <linux/slab.h>
>   #include <asm/unaligned.h>
>   
> +#define SI5341_NUM_INPUTS 4
> +
>   #define SI5341_MAX_NUM_OUTPUTS 10
>   #define SI5340_MAX_NUM_OUTPUTS 4
>   
> @@ -56,8 +57,8 @@ struct clk_si5341 {
>   	struct i2c_client *i2c_client;
>   	struct clk_si5341_synth synth[SI5341_NUM_SYNTH];
>   	struct clk_si5341_output clk[SI5341_MAX_NUM_OUTPUTS];
> -	struct clk *pxtal;
> -	const char *pxtal_name;
> +	struct clk *input_clk[SI5341_NUM_INPUTS];
> +	const char *input_clk_name[SI5341_NUM_INPUTS];
>   	const u16 *reg_output_offset;
>   	const u16 *reg_rdiv_offset;
>   	u64 freq_vco; /* 13500–14256 MHz */
> @@ -78,10 +79,25 @@ struct clk_si5341_output_config {
>   #define SI5341_DEVICE_REV	0x0005
>   #define SI5341_STATUS		0x000C
>   #define SI5341_SOFT_RST		0x001C
> +#define SI5341_IN_SEL		0x0021
> +#define SI5341_XAXB_CFG		0x090E
> +#define SI5341_IN_EN		0x0949
> +#define SI5341_INX_TO_PFD_EN	0x094A
> +
> +/* Input selection */
> +#define SI5341_IN_SEL_MASK	0x06
> +#define SI5341_IN_SEL_SHIFT	1
> +#define SI5341_IN_SEL_REGCTRL	0x01
> +#define SI5341_INX_TO_PFD_SHIFT	4
> +
> +/* XTAL config bits */
> +#define SI5341_XAXB_CFG_EXTCLK_EN	BIT(0)
> +#define SI5341_XAXB_CFG_PDNB		BIT(1)
>   
>   /* Input dividers (48-bit) */
>   #define SI5341_IN_PDIV(x)	(0x0208 + ((x) * 10))
>   #define SI5341_IN_PSET(x)	(0x020E + ((x) * 10))
> +#define SI5341_PX_UPD		0x0230
>   
>   /* PLL configuration */
>   #define SI5341_PLL_M_NUM	0x0235
> @@ -120,6 +136,10 @@ struct si5341_reg_default {
>   	u8 value;
>   };
>   
> +static const char * const si5341_input_clock_names[] = {
> +	"in0", "in1", "in2", "xtal"
> +};
> +
>   /* Output configuration registers 0..9 are not quite logically organized */
>   static const u16 si5341_reg_output_offset[] = {
>   	0x0108,
> @@ -390,7 +410,112 @@ static unsigned long si5341_clk_recalc_rate(struct clk_hw *hw,
>   	return (unsigned long)res;
>   }
>   
> +static int si5341_clk_get_selected_input(struct clk_si5341 *data)
> +{
> +	int err;
> +	u32 val;
> +
> +	err = regmap_read(data->regmap, SI5341_IN_SEL, &val);
> +	if (err < 0)
> +		return err;
> +
> +	return (val & SI5341_IN_SEL_MASK) >> SI5341_IN_SEL_SHIFT;
> +}
> +
> +static unsigned char si5341_clk_get_parent(struct clk_hw *hw)
> +{
> +	struct clk_si5341 *data = to_clk_si5341(hw);
> +	int res = si5341_clk_get_selected_input(data);
> +
> +	if (res < 0)
> +		return 0; /* Apparently we cannot report errors */
> +
> +	return res;
> +}
> +
> +static int si5341_clk_reparent(struct clk_si5341 *data, u8 index)
> +{
> +	int err;
> +	u8 val;
> +
> +	val = (index << SI5341_IN_SEL_SHIFT) & SI5341_IN_SEL_MASK;
> +	/* Enable register-based input selection */
> +	val |= SI5341_IN_SEL_REGCTRL;
> +
> +	err = regmap_update_bits(data->regmap,
> +		SI5341_IN_SEL, SI5341_IN_SEL_REGCTRL | SI5341_IN_SEL_MASK, val);
> +	if (err < 0)
> +		return err;
> +
> +	if (index < 3) {
> +		/* Enable input buffer for selected input */
> +		err = regmap_update_bits(data->regmap,
> +				SI5341_IN_EN, 0x07, BIT(index));
> +		if (err < 0)
> +			return err;
> +
> +		/* Enables the input to phase detector */
> +		err = regmap_update_bits(data->regmap, SI5341_INX_TO_PFD_EN,
> +				0x7 << SI5341_INX_TO_PFD_SHIFT,
> +				BIT(index + SI5341_INX_TO_PFD_SHIFT));
> +		if (err < 0)
> +			return err;
> +
> +		/* Power down XTAL oscillator and buffer */
> +		err = regmap_update_bits(data->regmap, SI5341_XAXB_CFG,
> +				SI5341_XAXB_CFG_PDNB, 0);
> +		if (err < 0)
> +			return err;
> +
> +		/*
> +		 * Set the P divider to "1". There's no explanation in the
> +		 * datasheet of these registers, but the clockbuilder software
> +		 * programs a "1" when the input is being used.
> +		 */
> +		err = regmap_write(data->regmap, SI5341_IN_PDIV(index), 1);
> +		if (err < 0)
> +			return err;
> +
> +		err = regmap_write(data->regmap, SI5341_IN_PSET(index), 1);
> +		if (err < 0)
> +			return err;
> +
> +		/* Set update PDIV bit */
> +		err = regmap_write(data->regmap, SI5341_PX_UPD, BIT(index));
> +		if (err < 0)
> +			return err;
> +	} else {
> +		/* Disable all input buffers */
> +		err = regmap_update_bits(data->regmap, SI5341_IN_EN, 0x07, 0);
> +		if (err < 0)
> +			return err;
> +
> +		/* Disable input to phase detector */
> +		err = regmap_update_bits(data->regmap, SI5341_INX_TO_PFD_EN,
> +				0x7 << SI5341_INX_TO_PFD_SHIFT, 0);
> +		if (err < 0)
> +			return err;
> +
> +		/* Power up XTAL oscillator and buffer */
> +		err = regmap_update_bits(data->regmap, SI5341_XAXB_CFG,
> +				SI5341_XAXB_CFG_PDNB, SI5341_XAXB_CFG_PDNB);
> +		if (err < 0)
> +			return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static int si5341_clk_set_parent(struct clk_hw *hw, u8 index)
> +{
> +	struct clk_si5341 *data = to_clk_si5341(hw);
> +
> +	return si5341_clk_reparent(data, index);
> +}
> +
>   static const struct clk_ops si5341_clk_ops = {
> +	.set_parent = si5341_clk_set_parent,
> +	.get_parent = si5341_clk_get_parent,
>   	.recalc_rate = si5341_clk_recalc_rate,
>   };
>   
> @@ -985,7 +1110,8 @@ static const struct regmap_range si5341_regmap_volatile_range[] = {
>   	regmap_reg_range(0x000C, 0x0012), /* Status */
>   	regmap_reg_range(0x001C, 0x001E), /* reset, finc/fdec */
>   	regmap_reg_range(0x00E2, 0x00FE), /* NVM, interrupts, device ready */
> -	/* Update bits for synth config */
> +	/* Update bits for P divider and synth config */
> +	regmap_reg_range(SI5341_PX_UPD, SI5341_PX_UPD),
>   	regmap_reg_range(SI5341_SYNTH_N_UPD(0), SI5341_SYNTH_N_UPD(0)),
>   	regmap_reg_range(SI5341_SYNTH_N_UPD(1), SI5341_SYNTH_N_UPD(1)),
>   	regmap_reg_range(SI5341_SYNTH_N_UPD(2), SI5341_SYNTH_N_UPD(2)),
> @@ -1122,6 +1248,7 @@ static int si5341_initialize_pll(struct clk_si5341 *data)
>   	struct device_node *np = data->i2c_client->dev.of_node;
>   	u32 m_num = 0;
>   	u32 m_den = 0;
> +	int sel;
>   
>   	if (of_property_read_u32(np, "silabs,pll-m-num", &m_num)) {
>   		dev_err(&data->i2c_client->dev,
> @@ -1135,7 +1262,11 @@ static int si5341_initialize_pll(struct clk_si5341 *data)
>   	if (!m_num || !m_den) {
>   		dev_err(&data->i2c_client->dev,
>   			"PLL configuration invalid, assume 14GHz\n");
> -		m_den = clk_get_rate(data->pxtal) / 10;
> +		sel = si5341_clk_get_selected_input(data);
> +		if (sel < 0)
> +			return sel;
> +
> +		m_den = clk_get_rate(data->input_clk[sel]) / 10;
>   		m_num = 1400000000;
>   	}
>   
> @@ -1143,11 +1274,52 @@ static int si5341_initialize_pll(struct clk_si5341 *data)
>   			SI5341_PLL_M_NUM, m_num, m_den);
>   }
>   
> +static int si5341_clk_select_active_input(struct clk_si5341 *data)
> +{
> +	int res;
> +	int err;
> +	int i;
> +
> +	res = si5341_clk_get_selected_input(data);
> +	if (res < 0)
> +		return res;
> +
> +	/* If the current register setting is invalid, pick the first input */
> +	if (!data->input_clk[res]) {
> +		dev_dbg(&data->i2c_client->dev,
> +			"Input %d not connected, rerouting\n", res);
> +		res = -ENODEV;
> +		for (i = 0; i < SI5341_NUM_INPUTS; ++i) {
> +			if (data->input_clk[i]) {
> +				res = i;
> +				break;
> +			}
> +		}
> +		if (res < 0) {
> +			dev_err(&data->i2c_client->dev,
> +				"No clock input available\n");
> +			return res;
> +		}
> +	}
> +
> +	/* Make sure the selected clock is also enabled and routed */
> +	err = si5341_clk_reparent(data, res);
> +	if (err < 0)
> +		return err;
> +
> +	err = clk_prepare_enable(data->input_clk[res]);
> +	if (err < 0)
> +		return err;
> +
> +	return res;
> +}
> +
>   static int si5341_probe(struct i2c_client *client,
>   		const struct i2c_device_id *id)
>   {
>   	struct clk_si5341 *data;
>   	struct clk_init_data init;
> +	struct clk *input;
>   	const char *root_clock_name;
>   	const char *synth_clock_names[SI5341_NUM_SYNTH];
>   	int err;
> @@ -1161,12 +1333,16 @@ static int si5341_probe(struct i2c_client *client,
>   
>   	data->i2c_client = client;
>   
> -	data->pxtal = devm_clk_get(&client->dev, "xtal");
> -	if (IS_ERR(data->pxtal)) {
> -		if (PTR_ERR(data->pxtal) == -EPROBE_DEFER)
> -			return -EPROBE_DEFER;
> -
> -		dev_err(&client->dev, "Missing xtal clock input\n");
> +	for (i = 0; i < SI5341_NUM_INPUTS; ++i) {
> +		input = devm_clk_get(&client->dev, si5341_input_clock_names[i]);
> +		if (IS_ERR(input)) {
> +			if (PTR_ERR(input) == -EPROBE_DEFER)
> +				return -EPROBE_DEFER;
> +			data->input_clk_name[i] = si5341_input_clock_names[i];
> +		} else {
> +			data->input_clk[i] = input;
> +			data->input_clk_name[i] = __clk_get_name(input);
> +		}
>   	}
>   
>   	err = si5341_dt_parse_dt(client, config);
> @@ -1188,9 +1364,6 @@ static int si5341_probe(struct i2c_client *client,
>   	if (err < 0)
>   		return err;
>   
> -	/* "Activate" the xtal (usually a fixed clock) */
> -	clk_prepare_enable(data->pxtal);
> -
>   	if (of_property_read_bool(client->dev.of_node, "silabs,reprogram")) {
>   		initialization_required = true;
>   	} else {
> @@ -1223,7 +1396,14 @@ static int si5341_probe(struct i2c_client *client,
>   					ARRAY_SIZE(si5341_reg_defaults));
>   		if (err < 0)
>   			return err;
> +	}
> +
> +	/* Input must be up and running at this point */
> +	err = si5341_clk_select_active_input(data);
> +	if (err < 0)
> +		return err;
>   
> +	if (initialization_required) {
>   		/* PLL configuration is required */
>   		err = si5341_initialize_pll(data);
>   		if (err < 0)
> @@ -1231,9 +1411,8 @@ static int si5341_probe(struct i2c_client *client,
>   	}
>   
>   	/* Register the PLL */
> -	data->pxtal_name = __clk_get_name(data->pxtal);
> -	init.parent_names = &data->pxtal_name;
> -	init.num_parents = 1; /* For now, only XTAL input supported */
> +	init.parent_names = data->input_clk_name;
> +	init.num_parents = SI5341_NUM_INPUTS;
>   	init.ops = &si5341_clk_ops;
>   	init.flags = 0;
>   	data->hw.init = &init;
> 

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ