clk: si5341: Check for input clock presence and PLL lock on startup
authorRobert Hancock <robert.hancock@calian.com>
Thu, 25 Mar 2021 19:26:38 +0000 (13:26 -0600)
committerStephen Boyd <sboyd@kernel.org>
Mon, 28 Jun 2021 02:58:14 +0000 (19:58 -0700)
After initializing the device, wait for it to report that the input
clock is present and the PLL has locked before declaring success.

Fixes: 3044a860fd ("clk: Add Si5341/Si5340 driver")
Signed-off-by: Robert Hancock <robert.hancock@calian.com>
Link: https://lore.kernel.org/r/20210325192643.2190069-5-robert.hancock@calian.com
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
drivers/clk/clk-si5341.c

index ac1ccec..da40b90 100644 (file)
@@ -92,6 +92,9 @@ struct clk_si5341_output_config {
 #define SI5341_PN_BASE         0x0002
 #define SI5341_DEVICE_REV      0x0005
 #define SI5341_STATUS          0x000C
+#define SI5341_LOS             0x000D
+#define SI5341_STATUS_STICKY   0x0011
+#define SI5341_LOS_STICKY      0x0012
 #define SI5341_SOFT_RST                0x001C
 #define SI5341_IN_SEL          0x0021
 #define SI5341_DEVICE_READY    0x00FE
@@ -99,6 +102,12 @@ struct clk_si5341_output_config {
 #define SI5341_IN_EN           0x0949
 #define SI5341_INX_TO_PFD_EN   0x094A
 
+/* Status bits */
+#define SI5341_STATUS_SYSINCAL BIT(0)
+#define SI5341_STATUS_LOSXAXB  BIT(1)
+#define SI5341_STATUS_LOSREF   BIT(2)
+#define SI5341_STATUS_LOL      BIT(3)
+
 /* Input selection */
 #define SI5341_IN_SEL_MASK     0x06
 #define SI5341_IN_SEL_SHIFT    1
@@ -1416,6 +1425,7 @@ static int si5341_probe(struct i2c_client *client,
        unsigned int i;
        struct clk_si5341_output_config config[SI5341_MAX_NUM_OUTPUTS];
        bool initialization_required;
+       u32 status;
 
        data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
        if (!data)
@@ -1583,6 +1593,22 @@ static int si5341_probe(struct i2c_client *client,
                        return err;
        }
 
+       /* wait for device to report input clock present and PLL lock */
+       err = regmap_read_poll_timeout(data->regmap, SI5341_STATUS, status,
+               !(status & (SI5341_STATUS_LOSREF | SI5341_STATUS_LOL)),
+              10000, 250000);
+       if (err) {
+               dev_err(&client->dev, "Error waiting for input clock or PLL lock\n");
+               return err;
+       }
+
+       /* 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;
+       }
+
        /* Free the names, clk framework makes copies */
        for (i = 0; i < data->num_synth; ++i)
                 devm_kfree(&client->dev, (void *)synth_clock_names[i]);