Re: [PATCH v2] Add driver for the si514 clock generator chip

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On 09/17, Mike Looijmans wrote:
> This patch adds the driver and devicetree documentation for the
> Silicon Labs SI514 clock generator chip. This is an I2C controlled
> oscilator capable of generating clock signals ranging from 100kHz

s/oscilator/oscillator/

> to 250MHz.
> 
> Signed-off-by: Mike Looijmans <mike.looijmans@xxxxxxxx>
> ---
> diff --git a/Documentation/devicetree/bindings/clock/silabs,si514.txt b/Documentation/devicetree/bindings/clock/silabs,si514.txt
> new file mode 100644
> index 0000000..05964d1
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/silabs,si514.txt
> @@ -0,0 +1,27 @@
> +Binding for Silicon Labs 514 programmable I2C clock generator.
> +
> +Reference
> +This binding uses the common clock binding[1]. Details about the device can be
> +found in the datasheet[2].
> +
> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> +[2] Si514 datasheet
> +    http://www.silabs.com/Support%20Documents/TechnicalDocs/si514.pdf
> +
> +Required properties:
> + - compatible: Shall be "silabs,si514"
> + - reg: I2C device address.
> + - #clock-cells: From common clock bindings: Shall be 0.
> +
> +Optional properties:
> + - clock-output-names: From common clock bindings. Recommended to be "si514".
> + - clock-frequency: Output frequency to generate. This defines the output
> +		    frequency set during boot. It can be reprogrammed during
> +		    runtime through the common clock framework.

Can we use assigned clock rates instead of this property?

> +
> +Example:
> +	si514: clock-generator@55 {
> +		reg = <0x55>;
> +		#clock-cells = <0>;
> +		compatible = "silabs,si514";
> +	};
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 42f7120..6ac7deb5 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -68,6 +68,16 @@ config COMMON_CLK_SI5351
>  	  This driver supports Silicon Labs 5351A/B/C programmable clock
>  	  generators.
>  
> +config COMMON_CLK_SI514
> +	tristate "Clock driver for SiLabs 514 devices"
> +	depends on I2C
> +	depends on OF

It actually depends on this to build?

> +	select REGMAP_I2C
> +	help
> +	---help---
> +	  This driver supports the Silicon Labs 514 programmable clock
> +	  generator.
> +
>  config COMMON_CLK_SI570
>  	tristate "Clock driver for SiLabs 570 and compatible devices"
>  	depends on I2C
> diff --git a/drivers/clk/clk-si514.c b/drivers/clk/clk-si514.c
> new file mode 100644
> index 0000000..ca70818
> --- /dev/null
> +++ b/drivers/clk/clk-si514.c
> @@ -0,0 +1,393 @@
> +
> +#include <linux/clk-provider.h>

I'd expect some sort of linux/clk.h include here if we're using
clk APIs.

> +#include <linux/delay.h>
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
[...]
> +
> +/* Program clock settings into hardware */

This comment is useless.

> +static int si514_set_muldiv(struct clk_si514 *data,
> +	struct clk_si514_muldiv *settings)
> +{
> +	u8 lp;
> +	u8 reg[7];
> +	int err;
> +
> +	/* Calculate LP1/LP2 according to table 13 in the datasheet */
> +	/* 65.259980246 */
> +	if ((settings->m_int < 65) ||
> +		((settings->m_int == 65) && (settings->m_frac <= 139575831)))
> +		lp = 0x22;
> +	/* 67.859763463 */
> +	else if ((settings->m_int < 67) ||
> +		((settings->m_int == 67) && (settings->m_frac <= 461581994)))
> +		lp = 0x23;
> +	/* 72.937624981 */
> +	else if ((settings->m_int < 72) ||
> +		((settings->m_int == 72) && (settings->m_frac <= 503383578)))
> +		lp = 0x33;
> +	/* 75.843265046 */
> +	else if ((settings->m_int < 75) ||
> +		((settings->m_int == 75) && (settings->m_frac <= 452724474)))
> +		lp = 0x34;

Drop the extra parenthesis on these if statements.

> +	else
> +		lp = 0x44;
> +
> +	err = regmap_write(data->regmap, SI514_REG_LP, lp);
> +	if (err < 0)
> +		return err;
> +
> +	reg[0] = settings->m_frac & 0xff;
> +	reg[1] = (settings->m_frac >> 8) & 0xff;
> +	reg[2] = (settings->m_frac >> 16) & 0xff;
> +	reg[3] = ((settings->m_frac >> 24) | (settings->m_int << 5)) & 0xff;
> +	reg[4] = (settings->m_int >> 3) & 0xff;
> +	reg[5] = (settings->hs_div) & 0xff;
> +	reg[6] = (((settings->hs_div) >> 8) |
> +			(settings->ls_div_bits << 4)) & 0xff;

The & 0xff part is redundant. Assignment truncates for us.

> +
> +	err = regmap_bulk_write(data->regmap, SI514_REG_HS_DIV, reg + 5, 2);
> +	if (err < 0)
> +		return err;
> +	/* Writing to SI514_REG_M_INT_FRAC triggers the clock change, so that
> +	 * must be written last */

Please fix multi-line commenting style.

> +	return regmap_bulk_write(data->regmap, SI514_REG_M_FRAC1, reg, 5);
> +}
> +
> +/* Calculate divider settings for a given frequency */
> +static int si514_calc_muldiv(struct clk_si514_muldiv *settings,
> +	unsigned long frequency)
> +{
> +	u64 m;
> +	u32 ls_freq;
> +	u32 tmp;
> +	u8 res;
> +
> +	if ((frequency < SI514_MIN_FREQ) || (frequency > SI514_MAX_FREQ))
> +		return -EINVAL;
> +
> +	/* Determine the minimum value of LS_DIV and resulting target freq. */
> +	ls_freq = frequency;
> +	if (frequency >= (FVCO_MIN / HS_DIV_MAX))
> +		settings->ls_div_bits = 0;
> +	else {
> +		res = 1;
> +		tmp = 2 * HS_DIV_MAX;
> +		while (tmp <= (HS_DIV_MAX*32)) {

Please add some space here between HS_DIV_MAX and 32.

> +			if ((frequency * tmp) >= FVCO_MIN)
> +				break; /* We're done */

Yep, that's what break in a loop usually means.

> +			++res;
> +			tmp <<= 1;
> +		}
> +		settings->ls_div_bits = res;
> +		ls_freq = frequency << res;
> +	}
> +
> +	/* Determine minimum HS_DIV, round up to even number */
> +	settings->hs_div = DIV_ROUND_UP(FVCO_MIN >> 1, ls_freq) << 1;
> +
> +	/* M = LS_DIV x HS_DIV x frequency / F_XO (in fixed-point) */
> +	m = ((u64)(ls_freq * settings->hs_div) << 29) + (FXO/2);
> +	do_div(m, FXO);
> +	settings->m_frac = (u32)m & (BIT(29) - 1);
> +	settings->m_int = (u32)(m >> 29);
> +
> +	return 0;
> +}
> +
> +/* Calculate resulting frequency given the register settings */
> +static unsigned long si514_calc_rate(struct clk_si514_muldiv *settings)
> +{
> +	u64 m = settings->m_frac | ((u64)settings->m_int << 29);
> +
> +	return ((u32)(((m * FXO) + (FXO/2)) >> 29)) /

Please add space after /

> +		(settings->hs_div * BIT(settings->ls_div_bits));

And drop the extra parenthesis. It would be nice if we didn't do
it all in one line too. Use some local variables.

> +}
> +
[...]
> +
> +	err = si514_calc_muldiv(&settings, rate);
> +	if (err)
> +		return err;
> +
> +	return si514_calc_rate(&settings);
> +}
> +
> +/* Update output frequency for big frequency changes (> 1000 ppm).
> + * The chip supports <1000ppm changes "on the fly", we haven't implemented
> + * that here. */

Please fix comment style.

> +static int si514_set_rate(struct clk_hw *hw, unsigned long rate,
> +		unsigned long parent_rate)
> +{
> +	struct clk_si514 *data = to_clk_si514(hw);
> +	struct clk_si514_muldiv settings;
> +	int err;
> +
> +	err = si514_calc_muldiv(&settings, rate);
> +	if (err)
> +		return err;
> +
> +	si514_enable_output(data, false);
> +
> +	err = si514_set_muldiv(data, &settings);
> +	if (err < 0)
> +		return err; /* Undefined state now, best to leave disabled */
> +
> +	/* Trigger calibration */
> +	err = regmap_write(data->regmap, SI514_REG_CONTROL, SI514_CONTROL_FCAL);
> +
> +	/* Applying a new frequency can take up to 10ms */
> +	usleep_range(10000, 12000);
> +
> +	si514_enable_output(data, true);
> +

Should we enable if regmap_write() failed?

> +	return err;
> +}
> +
> +static const struct clk_ops si514_clk_ops = {
> +	.recalc_rate = si514_recalc_rate,
> +	.round_rate = si514_round_rate,
> +	.set_rate = si514_set_rate,
> +};
> +
> +static struct regmap_config si514_regmap_config = {

const?

> +		}
> +	}
> +
> +	/* Display a message indicating that we've successfully registered */
> +	dev_info(&client->dev, "registered, current frequency %lu Hz\n",
> +			clk_get_rate(clk));

Please remove this.

> +
> +	return 0;
> +}
> +

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux