Add support for 32MHz GENI SE clocks through dedicated timing tables
selected via the qcom,geni-se-clk-32mhz DT property. Also add chip
probing support.

Depends on:
misc: qcom_geni: Add minicore support
Link: 
https://lore.kernel.org/u-boot/[email protected]/

Signed-off-by: Gurumoorthy Santhakumar 
<[email protected]>
---
 drivers/i2c/geni_i2c.c | 76 ++++++++++++++++++++++++++++++++++++------
 1 file changed, 66 insertions(+), 10 deletions(-)

diff --git a/drivers/i2c/geni_i2c.c b/drivers/i2c/geni_i2c.c
index 7498cbcb41a..9edba3dd551 100644
--- a/drivers/i2c/geni_i2c.c
+++ b/drivers/i2c/geni_i2c.c
@@ -96,18 +96,35 @@ struct geni_i2c_priv {
  * clk_freq_out = t / t_cycle
  * source_clock = 19.2 MHz
  */
-static const struct geni_i2c_clk_fld geni_i2c_clk_map[] = {
-       {I2C_SPEED_STANDARD_RATE, 7, 10, 11, 26},
-       {I2C_SPEED_FAST_RATE, 2,  5, 12, 24},
-       {I2C_SPEED_FAST_PLUS_RATE, 1, 3,  9, 18},
+static const struct geni_i2c_clk_fld geni_i2c_clk_map_19p2mhz[] = {
+       {I2C_SPEED_STANDARD_RATE, 7, 10, 12, 26},
+       {I2C_SPEED_FAST_RATE, 2,  5, 11, 22},
+       {I2C_SPEED_FAST_PLUS_RATE, 1, 2,  8, 18},
 };
 
-static int geni_i2c_clk_map_idx(struct geni_i2c_priv *geni, unsigned int 
clk_freq)
+/* source_clock = 32 MHz */
+static const struct geni_i2c_clk_fld geni_i2c_clk_map_32mhz[] = {
+       {I2C_SPEED_STANDARD_RATE, 12, 9, 10, 26},
+       {I2C_SPEED_FAST_RATE, 4,  3, 9, 19},
+       {I2C_SPEED_FAST_PLUS_RATE, 2, 3, 5, 15},
+};
+
+static int geni_i2c_clk_map_idx(struct geni_i2c_priv *geni,
+                               unsigned int clk_freq, bool use_32mhz)
 {
-       const struct geni_i2c_clk_fld *itr = geni_i2c_clk_map;
+       const struct geni_i2c_clk_fld *itr;
+       int array_size;
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(geni_i2c_clk_map); i++, itr++) {
+       if (use_32mhz) {
+               itr = geni_i2c_clk_map_32mhz;
+               array_size = ARRAY_SIZE(geni_i2c_clk_map_32mhz);
+       } else {
+               itr = geni_i2c_clk_map_19p2mhz;
+               array_size = ARRAY_SIZE(geni_i2c_clk_map_19p2mhz);
+       }
+
+       for (i = 0; i < array_size; i++, itr++) {
                if (itr->clk_freq_out == clk_freq) {
                        geni->clk_fld = itr;
                        return 0;
@@ -262,6 +279,38 @@ static int geni_i2c_xfer_rx(struct geni_i2c_priv *geni, 
struct i2c_msg *msg, u32
        return geni_i2c_fifo_rx_drain(geni, msg);
 }
 
+/* Probe to see if a chip is present. */
+static int geni_i2c_probe_chip(struct udevice *dev, uint chip_addr,
+                              uint chip_flags)
+{
+       struct geni_i2c_priv *geni = dev_get_priv(dev);
+       u32 m_param = (chip_addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK;
+       u32 status;
+       ulong start;
+
+       (void)chip_flags;
+
+       /* Add delay before chip detect to prevent the bus going undefined. */
+       mdelay(1);
+
+       qcom_geni_i2c_conf(geni);
+       writel(0, geni->base + SE_I2C_TX_TRANS_LEN);
+       geni_i2c_setup_m_cmd(geni, I2C_ADDR_ONLY, m_param);
+       writel(1, geni->base + SE_GENI_TX_WATERMARK_REG);
+
+       start = get_timer(0);
+       while (get_timer(start) < I2C_TIMEOUT_MS) {
+               status = readl(geni->base + SE_GENI_M_IRQ_STATUS);
+               if (status & M_CMD_DONE_EN) {
+                       writel(status, geni->base + SE_GENI_M_IRQ_CLEAR);
+                       return (status & SE_I2C_ERR) ? -ENODEV : 0;
+               }
+               udelay(10);
+       }
+
+       return -ETIMEDOUT;
+}
+
 static int geni_i2c_xfer(struct udevice *bus, struct i2c_msg msgs[], int num)
 {
        struct geni_i2c_priv *geni = dev_get_priv(bus);
@@ -468,6 +517,8 @@ static int geni_i2c_probe(struct udevice *dev)
        ofnode parent_node = ofnode_get_parent(dev_ofnode(dev));
        struct geni_i2c_priv *geni = dev_get_priv(dev);
        u32 proto, tx_depth, fifo_disable;
+       u32 clk_freq;
+       bool use_32mhz;
        int ret;
 
        geni->is_master_hub = dev_get_driver_data(dev) & GENI_I2C_IS_MASTER_HUB;
@@ -538,19 +589,24 @@ static int geni_i2c_probe(struct udevice *dev)
        geni_i2c_config_packing(geni, BITS_PER_BYTE,
                                PACKING_BYTES_PW, true, true, true);
 
-       /* Setup for standard rate */
-       return geni_i2c_clk_map_idx(geni, I2C_SPEED_STANDARD_RATE);
+       clk_freq = dev_read_u32_default(dev, "clock-frequency",
+                                       I2C_SPEED_STANDARD_RATE);
+       use_32mhz = dev_read_bool(dev, "qcom,geni-se-clk-32mhz");
+
+       return geni_i2c_clk_map_idx(geni, clk_freq, use_32mhz);
 }
 
 static int geni_i2c_set_bus_speed(struct udevice *dev, unsigned int clk_freq)
 {
        struct geni_i2c_priv *geni = dev_get_priv(dev);
+       bool use_32mhz = dev_read_bool(dev, "qcom,geni-se-clk-32mhz");
 
-       return geni_i2c_clk_map_idx(geni, clk_freq);
+       return geni_i2c_clk_map_idx(geni, clk_freq, use_32mhz);
 }
 
 static const struct dm_i2c_ops geni_i2c_ops = {
        .xfer           = geni_i2c_xfer,
+       .probe_chip     = geni_i2c_probe_chip,
        .set_bus_speed  = geni_i2c_set_bus_speed,
 };
 
-- 
2.34.1

Reply via email to