[PATCH v4] clk: si570: Add a driver for SI570 oscillators

Sören Brinkmann soren.brinkmann at xilinx.com
Fri Sep 20 14:46:24 EDT 2013


On Fri, Sep 20, 2013 at 11:15:29AM -0700, Guenter Roeck wrote:
> On Fri, Sep 20, 2013 at 10:39:17AM -0700, Soren Brinkmann wrote:
> > Add a driver for SILabs 570, 571, 598, 599 programmable oscillators.
> > The devices generate low-jitter clock signals and are reprogrammable via
> > an I2C interface.
> > 
> > Cc: Guenter Roeck <linux at roeck-us.net>
> > Signed-off-by: Soren Brinkmann <soren.brinkmann at xilinx.com>
> > ---
> > v4:
> >  - actually check and handle error on clk_set_rate() call
> > 
> > v3:
> >  - add delay in the path for small frequency changes, which can take up
> >    to 100us according to the data sheet
> >  - use real range for usleep_range() argument
> >  - use dev_err() over dev_warn() in set_rate()
> >  - add list of applicable devices for 7ppm DT prop
> >  - remove comments regarding platform data
> >  - ignore 7ppm DT prop for incompatible devices
> >  - replace raw numbers with #defines
> >  - convert DT prop 'temperature-stability' from boolean to u32
> >  - make 'temperature-stability' DT prop required (actually not fully
> >    true, for 59x it is ignored and the driver does not require its
> >    presence)
> > 
> > v2:
> >  - document clock-output-names in bindings documentation
> >  - don't use wildcards in compatibility string
> >  - change Kconfig entry to "... 570 and compatible devices"
> >  - change some indentation flaws
> >  - use 10000 as MIN and MAX value in usleep_range
> >  - fail probe() if 'factory-fout' is not provided in DT
> >    - default factory fout #defines removed
> >  - use i2c driver_data instead of OF data
> >    - remove some related structs and data
> >  - rename DT property 'initial-fout' => 'clock-frequency' (like si5351
> >    driver)
> >  - add some more details regarding the 'factory-fout' DT property
> > ---
> >  .../devicetree/bindings/clock/silabs,si570.txt     |  39 ++
> >  drivers/clk/Kconfig                                |  10 +
> >  drivers/clk/Makefile                               |   1 +
> >  drivers/clk/clk-si570.c                            | 536 +++++++++++++++++++++
> >  4 files changed, 586 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/clock/silabs,si570.txt
> >  create mode 100644 drivers/clk/clk-si570.c
> > 
> > diff --git a/Documentation/devicetree/bindings/clock/silabs,si570.txt b/Documentation/devicetree/bindings/clock/silabs,si570.txt
> > new file mode 100644
> > index 0000000..26aab5c
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/clock/silabs,si570.txt
> > @@ -0,0 +1,39 @@
> > +Binding for Silicon Labs 570, 571, 598 and 599 programmable
> > +I2C clock generators.
> > +
> > +Reference
> > +This binding uses the common clock binding[1]. Details about the devices can be
> > +found in the data sheets[2][3].
> > +
> > +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
> > +[2] Si570/571 Data Sheet
> > +    http://www.silabs.com/Support%20Documents/TechnicalDocs/si570.pdf
> > +[3] Si598/599 Data Sheet
> > +    http://www.silabs.com/Support%20Documents/TechnicalDocs/si598-99.pdf
> > +
> > +Required properties:
> > + - compatible: Shall be one of "silabs,si570", "silabs,si571",
> > +			       "silabs,si598", "silabs,si599"
> > + - reg: I2C device address.
> > + - #clock-cells: From common clock bindings: Shall be 0.
> > + - factory-fout: Factory set default frequency. This frequency is part specific.
> > +		 The correct frequency for the part used has to be provided in
> > +		 order to generate the correct output frequencies. For more
> > +		 details, please refer to the data sheet.
> > + - temperature-stability: Temperature stability of the device. Should be one of:
> > +			  7, 20, 50 or 100.
> > +
> It might make sense to specify that this is in microseconds.
ppm, but yes giving the unit is a good idea.

> 
> > +Optional properties:
> > + - clock-output-names: From common clock bindings. Recommended to be "si570".
> > + - 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.
> > +
> > +Example:
> > +	si570: clock-generator at 5d {
> > +		#clock-cells = <0>;
> > +		compatible = "silabs,si570";
> > +		temperatur-stability = <50>;
> 
> temperature
damn typos.

> 
> > +		reg = <0x5d>;
> > +		factory-fout = <156250000>;
> > +	};
[...]
> > diff --git a/drivers/clk/clk-si570.c b/drivers/clk/clk-si570.c
> > new file mode 100644
> > index 0000000..13c2091
> > --- /dev/null
> > +++ b/drivers/clk/clk-si570.c
[...]
> > +static int si570_probe(struct i2c_client *client,
> > +		const struct i2c_device_id *id)
> > +{
> > +	struct clk_si570 *data;
> > +	struct clk_init_data init;
> > +	struct clk *clk;
> > +	u32 initial_fout, factory_fout, stability;
> > +	int err;
> > +	enum clk_si570_variant variant = id->driver_data;
> > +
> > +	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
> > +	if (!data)
> > +		return -ENOMEM;
> > +
> > +	init.ops = &si570_clk_ops;
> > +	init.flags = CLK_IS_ROOT;
> > +	init.num_parents = 0;
> > +	data->hw.init = &init;
> > +	data->i2c_client = client;
> > +
> > +	if (variant == si57x) {
> > +		err = of_property_read_u32(client->dev.of_node,
> > +				"temperature-stability", &stability);
> > +		if (err) {
> > +			dev_err(&client->dev,
> > +				  "'temperature-stability' property missing\n");
> > +			return err;
> > +		}
> > +		/* adjust register offsets for 7ppm devices */
> > +		if (stability == 7)
> > +			data->div_offset = SI570_DIV_OFFSET_7PPM;
> > +
> > +		data->max_freq = SI570_MAX_FREQ;
> > +	} else {
> > +		data->max_freq = SI598_MAX_FREQ;
> > +	}
> > +
> > +	if (of_property_read_string(client->dev.of_node, "clock-output-names",
> > +			&init.name))
> > +		init.name = client->dev.of_node->name;
> > +
> > +	err = of_property_read_u32(client->dev.of_node, "factory-fout",
> > +			&factory_fout);
> > +	if (err) {
> > +		dev_err(&client->dev, "'factory-fout' property missing\n");
> > +		return err;
> > +	}
> > +
> > +	data->regmap = devm_regmap_init_i2c(client, &si570_regmap_config);
> > +	if (IS_ERR(data->regmap)) {
> > +		dev_err(&client->dev, "failed to allocate register map\n");
> > +		return PTR_ERR(data->regmap);
> > +	}
> > +
> > +	i2c_set_clientdata(client, data);
> > +	err = si570_get_defaults(data, factory_fout);
> > +	if (err)
> > +		return err;
> > +
> > +	clk = devm_clk_register(&client->dev, &data->hw);
> > +	if (IS_ERR(clk)) {
> > +		dev_err(&client->dev, "clock registration failed\n");
> > +		return PTR_ERR(clk);
> > +	}
> > +	err = of_clk_add_provider(client->dev.of_node, of_clk_src_simple_get,
> > +			clk);
> > +	if (err) {
> > +		dev_err(&client->dev, "unable to add clk provider\n");
> > +		return err;
> > +	}
> > +
> > +	/* Read the requested initial output frequency from device tree */
> > +	if (!of_property_read_u32(client->dev.of_node, "clock-frequency",
> > +				&initial_fout)) {
> > +		err = clk_set_rate(clk, initial_fout);
> > +		if (err) {
> > +			of_clk_del_provider(client->dev.of_node);
> > +			clk_unregister(data->hw.clk);
> 
> Is that needed ? I would have thought it isn't because you use a devm_ function
> to register it.
> 
> Not that I understand the clk code, but getting clk from the registration
> function and unregistering hw.clk (without seting it) sems a bit odd.
> Is it well defined that you have to unregister hw.clk ?
I have to double check. I thought the managed interface is only saving
you a call to clk_put() but I may be wrong.

	Sören





More information about the linux-arm-kernel mailing list