OSDN Git Service

clk: si5341: Allow different output VDD_SEL values
authorRobert Hancock <robert.hancock@calian.com>
Thu, 25 Mar 2021 19:26:40 +0000 (13:26 -0600)
committerStephen Boyd <sboyd@kernel.org>
Mon, 28 Jun 2021 02:58:14 +0000 (19:58 -0700)
The driver was not previously programming the VDD_SEL values for each
output to indicate what external VDDO voltage was used for each. Add
ability to specify a regulator supplying the VDDO pin for each output of
the device. The voltage of the regulator is used to automatically set the
VDD_SEL value appropriately. If no regulator is specified and the chip is
being reconfigured, assume 2.5V which appears to be the chip default.

Signed-off-by: Robert Hancock <robert.hancock@calian.com>
Link: https://lore.kernel.org/r/20210325192643.2190069-7-robert.hancock@calian.com
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
drivers/clk/clk-si5341.c

index eb22f4f..28ac708 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/i2c.h>
 #include <linux/module.h>
 #include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <asm/unaligned.h>
 
@@ -59,6 +60,7 @@ struct clk_si5341_synth {
 struct clk_si5341_output {
        struct clk_hw hw;
        struct clk_si5341 *data;
+       struct regulator *vddo_reg;
        u8 index;
 };
 #define to_clk_si5341_output(_hw) \
@@ -84,6 +86,7 @@ struct clk_si5341 {
 struct clk_si5341_output_config {
        u8 out_format_drv_bits;
        u8 out_cm_ampl_bits;
+       u8 vdd_sel_bits;
        bool synth_master;
        bool always_on;
 };
@@ -136,6 +139,8 @@ struct clk_si5341_output_config {
 #define SI5341_OUT_R_REG(output)       \
                        ((output)->data->reg_rdiv_offset[(output)->index])
 
+#define SI5341_OUT_MUX_VDD_SEL_MASK 0x38
+
 /* Synthesize N divider */
 #define SI5341_SYNTH_N_NUM(x)  (0x0302 + ((x) * 11))
 #define SI5341_SYNTH_N_DEN(x)  (0x0308 + ((x) * 11))
@@ -1248,11 +1253,11 @@ static const struct regmap_config si5341_regmap_config = {
        .volatile_table = &si5341_regmap_volatile,
 };
 
-static int si5341_dt_parse_dt(struct i2c_client *client,
-       struct clk_si5341_output_config *config)
+static int si5341_dt_parse_dt(struct clk_si5341 *data,
+                             struct clk_si5341_output_config *config)
 {
        struct device_node *child;
-       struct device_node *np = client->dev.of_node;
+       struct device_node *np = data->i2c_client->dev.of_node;
        u32 num;
        u32 val;
 
@@ -1261,13 +1266,13 @@ static int si5341_dt_parse_dt(struct i2c_client *client,
 
        for_each_child_of_node(np, child) {
                if (of_property_read_u32(child, "reg", &num)) {
-                       dev_err(&client->dev, "missing reg property of %s\n",
+                       dev_err(&data->i2c_client->dev, "missing reg property of %s\n",
                                child->name);
                        goto put_child;
                }
 
                if (num >= SI5341_MAX_NUM_OUTPUTS) {
-                       dev_err(&client->dev, "invalid clkout %d\n", num);
+                       dev_err(&data->i2c_client->dev, "invalid clkout %d\n", num);
                        goto put_child;
                }
 
@@ -1286,7 +1291,7 @@ static int si5341_dt_parse_dt(struct i2c_client *client,
                                config[num].out_format_drv_bits |= 0xc0;
                                break;
                        default:
-                               dev_err(&client->dev,
+                               dev_err(&data->i2c_client->dev,
                                        "invalid silabs,format %u for %u\n",
                                        val, num);
                                goto put_child;
@@ -1299,7 +1304,7 @@ static int si5341_dt_parse_dt(struct i2c_client *client,
 
                if (!of_property_read_u32(child, "silabs,common-mode", &val)) {
                        if (val > 0xf) {
-                               dev_err(&client->dev,
+                               dev_err(&data->i2c_client->dev,
                                        "invalid silabs,common-mode %u\n",
                                        val);
                                goto put_child;
@@ -1310,7 +1315,7 @@ static int si5341_dt_parse_dt(struct i2c_client *client,
 
                if (!of_property_read_u32(child, "silabs,amplitude", &val)) {
                        if (val > 0xf) {
-                               dev_err(&client->dev,
+                               dev_err(&data->i2c_client->dev,
                                        "invalid silabs,amplitude %u\n",
                                        val);
                                goto put_child;
@@ -1327,6 +1332,34 @@ static int si5341_dt_parse_dt(struct i2c_client *client,
 
                config[num].always_on =
                        of_property_read_bool(child, "always-on");
+
+               config[num].vdd_sel_bits = 0x08;
+               if (data->clk[num].vddo_reg) {
+                       int vdd = regulator_get_voltage(data->clk[num].vddo_reg);
+
+                       switch (vdd) {
+                       case 3300000:
+                               config[num].vdd_sel_bits |= 0 << 4;
+                               break;
+                       case 1800000:
+                               config[num].vdd_sel_bits |= 1 << 4;
+                               break;
+                       case 2500000:
+                               config[num].vdd_sel_bits |= 2 << 4;
+                               break;
+                       default:
+                               dev_err(&data->i2c_client->dev,
+                                       "unsupported vddo voltage %d for %s\n",
+                                       vdd, child->name);
+                               goto put_child;
+                       }
+               } else {
+                       /* chip seems to default to 2.5V when not set */
+                       dev_warn(&data->i2c_client->dev,
+                               "no regulator set, defaulting vdd_sel to 2.5V for %s\n",
+                               child->name);
+                       config[num].vdd_sel_bits |= 2 << 4;
+               }
        }
 
        return 0;
@@ -1452,9 +1485,33 @@ static int si5341_probe(struct i2c_client *client,
                }
        }
 
-       err = si5341_dt_parse_dt(client, config);
+       for (i = 0; i < SI5341_MAX_NUM_OUTPUTS; ++i) {
+               char reg_name[10];
+
+               snprintf(reg_name, sizeof(reg_name), "vddo%d", i);
+               data->clk[i].vddo_reg = devm_regulator_get_optional(
+                       &client->dev, reg_name);
+               if (IS_ERR(data->clk[i].vddo_reg)) {
+                       err = PTR_ERR(data->clk[i].vddo_reg);
+                       data->clk[i].vddo_reg = NULL;
+                       if (err == -ENODEV)
+                               continue;
+                       goto cleanup;
+               } else {
+                       err = regulator_enable(data->clk[i].vddo_reg);
+                       if (err) {
+                               dev_err(&client->dev,
+                                       "failed to enable %s regulator: %d\n",
+                                       reg_name, err);
+                               data->clk[i].vddo_reg = NULL;
+                               goto cleanup;
+                       }
+               }
+       }
+
+       err = si5341_dt_parse_dt(data, config);
        if (err)
-               return err;
+               goto cleanup;
 
        if (of_property_read_string(client->dev.of_node, "clock-output-names",
                        &init.name))
@@ -1462,21 +1519,23 @@ static int si5341_probe(struct i2c_client *client,
        root_clock_name = init.name;
 
        data->regmap = devm_regmap_init_i2c(client, &si5341_regmap_config);
-       if (IS_ERR(data->regmap))
-               return PTR_ERR(data->regmap);
+       if (IS_ERR(data->regmap)) {
+               err = PTR_ERR(data->regmap);
+               goto cleanup;
+       }
 
        i2c_set_clientdata(client, data);
 
        err = si5341_probe_chip_id(data);
        if (err < 0)
-               return err;
+               goto cleanup;
 
        if (of_property_read_bool(client->dev.of_node, "silabs,reprogram")) {
                initialization_required = true;
        } else {
                err = si5341_is_programmed_already(data);
                if (err < 0)
-                       return err;
+                       goto cleanup;
 
                initialization_required = !err;
        }
@@ -1485,11 +1544,11 @@ static int si5341_probe(struct i2c_client *client,
                /* Populate the regmap cache in preparation for "cache only" */
                err = si5341_read_settings(data);
                if (err < 0)
-                       return err;
+                       goto cleanup;
 
                err = si5341_send_preamble(data);
                if (err < 0)
-                       return err;
+                       goto cleanup;
 
                /*
                 * We intend to send all 'final' register values in a single
@@ -1502,19 +1561,19 @@ static int si5341_probe(struct i2c_client *client,
                err = si5341_write_multiple(data, si5341_reg_defaults,
                                        ARRAY_SIZE(si5341_reg_defaults));
                if (err < 0)
-                       return err;
+                       goto cleanup;
        }
 
        /* Input must be up and running at this point */
        err = si5341_clk_select_active_input(data);
        if (err < 0)
-               return err;
+               goto cleanup;
 
        if (initialization_required) {
                /* PLL configuration is required */
                err = si5341_initialize_pll(data);
                if (err < 0)
-                       return err;
+                       goto cleanup;
        }
 
        /* Register the PLL */
@@ -1527,7 +1586,7 @@ static int si5341_probe(struct i2c_client *client,
        err = devm_clk_hw_register(&client->dev, &data->hw);
        if (err) {
                dev_err(&client->dev, "clock registration failed\n");
-               return err;
+               goto cleanup;
        }
 
        init.num_parents = 1;
@@ -1564,13 +1623,17 @@ static int si5341_probe(struct i2c_client *client,
                        regmap_write(data->regmap,
                                SI5341_OUT_CM(&data->clk[i]),
                                config[i].out_cm_ampl_bits);
+                       regmap_update_bits(data->regmap,
+                               SI5341_OUT_MUX_SEL(&data->clk[i]),
+                               SI5341_OUT_MUX_VDD_SEL_MASK,
+                               config[i].vdd_sel_bits);
                }
                err = devm_clk_hw_register(&client->dev, &data->clk[i].hw);
                kfree(init.name); /* clock framework made a copy of the name */
                if (err) {
                        dev_err(&client->dev,
                                "output %u registration failed\n", i);
-                       return err;
+                       goto cleanup;
                }
                if (config[i].always_on)
                        clk_prepare(data->clk[i].hw.clk);
@@ -1580,7 +1643,7 @@ static int si5341_probe(struct i2c_client *client,
                        data);
        if (err) {
                dev_err(&client->dev, "unable to add clk provider\n");
-               return err;
+               goto cleanup;
        }
 
        if (initialization_required) {
@@ -1588,11 +1651,11 @@ static int si5341_probe(struct i2c_client *client,
                regcache_cache_only(data->regmap, false);
                err = regcache_sync(data->regmap);
                if (err < 0)
-                       return err;
+                       goto cleanup;
 
                err = si5341_finalize_defaults(data);
                if (err < 0)
-                       return err;
+                       goto cleanup;
        }
 
        /* wait for device to report input clock present and PLL lock */
@@ -1601,14 +1664,14 @@ static int si5341_probe(struct i2c_client *client,
               10000, 250000);
        if (err) {
                dev_err(&client->dev, "Error waiting for input clock or PLL lock\n");
-               return err;
+               goto cleanup;
        }
 
        /* clear sticky alarm bits from initialization */
        err = regmap_write(data->regmap, SI5341_STATUS_STICKY, 0);
        if (err) {
                dev_err(&client->dev, "unable to clear sticky status\n");
-               return err;
+               goto cleanup;
        }
 
        /* Free the names, clk framework makes copies */
@@ -1616,6 +1679,26 @@ static int si5341_probe(struct i2c_client *client,
                 devm_kfree(&client->dev, (void *)synth_clock_names[i]);
 
        return 0;
+
+cleanup:
+       for (i = 0; i < SI5341_MAX_NUM_OUTPUTS; ++i) {
+               if (data->clk[i].vddo_reg)
+                       regulator_disable(data->clk[i].vddo_reg);
+       }
+       return err;
+}
+
+static int si5341_remove(struct i2c_client *client)
+{
+       struct clk_si5341 *data = i2c_get_clientdata(client);
+       int i;
+
+       for (i = 0; i < SI5341_MAX_NUM_OUTPUTS; ++i) {
+               if (data->clk[i].vddo_reg)
+                       regulator_disable(data->clk[i].vddo_reg);
+       }
+
+       return 0;
 }
 
 static const struct i2c_device_id si5341_id[] = {
@@ -1644,6 +1727,7 @@ static struct i2c_driver si5341_driver = {
                .of_match_table = clk_si5341_of_match,
        },
        .probe          = si5341_probe,
+       .remove         = si5341_remove,
        .id_table       = si5341_id,
 };
 module_i2c_driver(si5341_driver);