Port over the Linux v5.8-rc4 bits to support i2c on the sama5d2.
This has been tested by reading the i2c EEPROM on the sama5d27-som1-ek.

Signed-off-by: Ahmad Fatoum <a.fat...@pengutronix.de>
---
 drivers/i2c/busses/i2c-at91.c | 135 +++++++++++++++++++++++++++++-----
 1 file changed, 116 insertions(+), 19 deletions(-)

diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c
index 76bb51bf30c2..70e87c1b2cc4 100644
--- a/drivers/i2c/busses/i2c-at91.c
+++ b/drivers/i2c/busses/i2c-at91.c
@@ -48,6 +48,8 @@
 #define        AT91_TWI_IADR           0x000c  /* Internal Address Register */
 
 #define        AT91_TWI_CWGR           0x0010  /* Clock Waveform Generator Reg 
*/
+#define        AT91_TWI_CWGR_HOLD_MAX  0x1f
+#define        AT91_TWI_CWGR_HOLD(x)   (((x) & AT91_TWI_CWGR_HOLD_MAX) << 24)
 
 #define        AT91_TWI_SR             0x0020  /* Status Register */
 #define        AT91_TWI_TXCOMP         0x0001  /* Transmission Complete */
@@ -64,14 +66,27 @@
 #define        AT91_TWI_RHR            0x0030  /* Receive Holding Register */
 #define        AT91_TWI_THR            0x0034  /* Transmit Holding Register */
 
+#define AT91_TWI_FILTR         0x0044
+#define AT91_TWI_FILTR_FILT    BIT(0)
+#define AT91_TWI_FILTR_PADFEN  BIT(1)
+#define AT91_TWI_FILTR_THRES(v)                ((v) << 8)
+#define AT91_TWI_FILTR_THRES_MAX       7
+#define AT91_TWI_FILTR_THRES_MASK      GENMASK(10, 8)
+
 struct at91_twi_pdata {
        unsigned clk_max_div;
        unsigned clk_offset;
        bool has_unre_flag;
+       bool has_alt_cmd;
+       bool has_hold_field;
+       bool has_dig_filtr;
+       bool has_adv_dig_filtr;
+       bool has_ana_filtr;
+       bool has_clear_cmd;
 };
 
 struct at91_twi_dev {
-       struct device *dev;
+       struct device_d *dev;
        void __iomem *base;
        struct clk *clk;
        u8 *buf;
@@ -82,6 +97,10 @@ struct at91_twi_dev {
        struct i2c_adapter adapter;
        unsigned twi_cwgr_reg;
        struct at91_twi_pdata *pdata;
+       u32 filter_width;
+
+       bool enable_dig_filt;
+       bool enable_ana_filt;
 };
 
 #define to_at91_twi_dev(a) container_of(a, struct at91_twi_dev, adapter)
@@ -104,11 +123,31 @@ static void at91_disable_twi_interrupts(struct 
at91_twi_dev *dev)
 
 static void at91_init_twi_bus(struct at91_twi_dev *dev)
 {
+       struct at91_twi_pdata *pdata = dev->pdata;
+       u32 filtr = 0;
+
        at91_disable_twi_interrupts(dev);
        at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SWRST);
        at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSEN);
        at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVDIS);
        at91_twi_write(dev, AT91_TWI_CWGR, dev->twi_cwgr_reg);
+
+       /* enable digital filter */
+       if (pdata->has_dig_filtr && dev->enable_dig_filt)
+               filtr |= AT91_TWI_FILTR_FILT;
+
+       /* enable advanced digital filter */
+       if (pdata->has_adv_dig_filtr && dev->enable_dig_filt)
+               filtr |= AT91_TWI_FILTR_FILT |
+                        (AT91_TWI_FILTR_THRES(dev->filter_width) &
+                        AT91_TWI_FILTR_THRES_MASK);
+
+       /* enable analog filter */
+       if (pdata->has_ana_filtr && dev->enable_ana_filt)
+               filtr |= AT91_TWI_FILTR_PADFEN;
+
+       if (filtr)
+               at91_twi_write(dev, AT91_TWI_FILTR, filtr);
 }
 
 /*
@@ -117,10 +156,13 @@ static void at91_init_twi_bus(struct at91_twi_dev *dev)
  */
 static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
 {
-       int ckdiv, cdiv, div;
+       int ckdiv, cdiv, div, hold = 0, filter_width = 0;
        struct at91_twi_pdata *pdata = dev->pdata;
        int offset = pdata->clk_offset;
        int max_ckdiv = pdata->clk_max_div;
+       struct i2c_timings timings, *t = &timings;
+
+       i2c_parse_fw_timings(dev->dev, t, true);
 
        div = max(0, (int)DIV_ROUND_UP(clk_get_rate(dev->clk),
                                       2 * twi_clk) - offset);
@@ -128,14 +170,54 @@ static void at91_calc_twi_clock(struct at91_twi_dev *dev, 
int twi_clk)
        cdiv = div >> ckdiv;
 
        if (ckdiv > max_ckdiv) {
-               dev_warn(&dev->adapter.dev, "%d exceeds ckdiv max value which 
is %d.\n",
+               dev_warn(dev->dev, "%d exceeds ckdiv max value which is %d.\n",
                         ckdiv, max_ckdiv);
                ckdiv = max_ckdiv;
                cdiv = 255;
        }
 
-       dev->twi_cwgr_reg = (ckdiv << 16) | (cdiv << 8) | cdiv;
-       dev_dbg(&dev->adapter.dev, "cdiv %d ckdiv %d\n", cdiv, ckdiv);
+       if (pdata->has_hold_field) {
+               /*
+                * hold time = HOLD + 3 x T_peripheral_clock
+                * Use clk rate in kHz to prevent overflows when computing
+                * hold.
+                */
+               hold = DIV_ROUND_UP(t->sda_hold_ns
+                                   * (clk_get_rate(dev->clk) / 1000), 1000000);
+               hold -= 3;
+               if (hold < 0)
+                       hold = 0;
+               if (hold > AT91_TWI_CWGR_HOLD_MAX) {
+                       dev_warn(dev->dev,
+                                "HOLD field set to its maximum value (%d 
instead of %d)\n",
+                                AT91_TWI_CWGR_HOLD_MAX, hold);
+                       hold = AT91_TWI_CWGR_HOLD_MAX;
+               }
+       }
+
+       if (pdata->has_adv_dig_filtr) {
+               /*
+                * filter width = 0 to AT91_TWI_FILTR_THRES_MAX
+                * peripheral clocks
+                */
+               filter_width = DIV_ROUND_UP(t->digital_filter_width_ns
+                               * (clk_get_rate(dev->clk) / 1000), 1000000);
+               if (filter_width > AT91_TWI_FILTR_THRES_MAX) {
+                       dev_warn(dev->dev,
+                               "Filter threshold set to its maximum value (%d 
instead of %d)\n",
+                               AT91_TWI_FILTR_THRES_MAX, filter_width);
+                       filter_width = AT91_TWI_FILTR_THRES_MAX;
+               }
+       }
+
+       dev->twi_cwgr_reg = (ckdiv << 16) | (cdiv << 8) | cdiv
+                           | AT91_TWI_CWGR_HOLD(hold);
+
+       dev->filter_width = filter_width;
+
+       dev_dbg(dev->dev, "cdiv %d ckdiv %d hold %d (%d ns), filter_width %d 
(%d ns)\n",
+               cdiv, ckdiv, hold, t->sda_hold_ns, filter_width,
+               t->digital_filter_width_ns);
 }
 
 static void at91_twi_write_next_byte(struct at91_twi_dev *dev)
@@ -149,7 +231,7 @@ static void at91_twi_write_next_byte(struct at91_twi_dev 
*dev)
        if (--dev->buf_len == 0)
                at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
 
-       dev_dbg(&dev->adapter.dev, "wrote 0x%x, to go %d\n", *dev->buf, 
dev->buf_len);
+       dev_dbg(dev->dev, "wrote 0x%x, to go %d\n", *dev->buf, dev->buf_len);
 
        ++dev->buf;
 }
@@ -166,7 +248,7 @@ static void at91_twi_read_next_byte(struct at91_twi_dev 
*dev)
        if (dev->buf_len == 1)
                at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_STOP);
 
-       dev_dbg(&dev->adapter.dev, "read 0x%x, to go %d\n", *dev->buf, 
dev->buf_len);
+       dev_dbg(dev->dev, "read 0x%x, to go %d\n", *dev->buf, dev->buf_len);
 
        ++dev->buf;
 }
@@ -183,7 +265,7 @@ static int at91_twi_wait_completion(struct at91_twi_dev 
*dev)
 
                if (!(status & irqstatus)) {
                        if (is_timeout(start, AT91_I2C_TIMEOUT)) {
-                               dev_warn(&dev->adapter.dev, "timeout waiting 
for bus ready\n");
+                               dev_warn(dev->dev, "timeout waiting for bus 
ready\n");
                                return -ETIMEDOUT;
                        } else {
                                continue;
@@ -195,7 +277,7 @@ static int at91_twi_wait_completion(struct at91_twi_dev 
*dev)
                else if (irqstatus & AT91_TWI_TXRDY)
                        at91_twi_write_next_byte(dev);
                else
-                       dev_warn(&dev->adapter.dev, "neither rx and tx are 
ready\n");
+                       dev_warn(dev->dev, "neither rx and tx are ready\n");
 
                dev->transfer_status |= status;
 
@@ -211,7 +293,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
        int ret;
        bool has_unre_flag = dev->pdata->has_unre_flag;
 
-       dev_dbg(&dev->adapter.dev, "transfer: %s %d bytes.\n",
+       dev_dbg(dev->dev, "transfer: %s %d bytes.\n",
                (dev->msg->flags & I2C_M_RD) ? "read" : "write", dev->buf_len);
 
        dev->transfer_status = 0;
@@ -223,7 +305,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
                unsigned start_flags = AT91_TWI_START;
 
                if (at91_twi_read(dev, AT91_TWI_SR) & AT91_TWI_RXRDY) {
-                       dev_err(&dev->adapter.dev, "RXRDY still set!");
+                       dev_err(dev->dev, "RXRDY still set!");
                        at91_twi_read(dev, AT91_TWI_RHR);
                }
 
@@ -243,27 +325,27 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
 
        ret = at91_twi_wait_completion(dev);
        if (ret < 0) {
-               dev_err(&dev->adapter.dev, "controller timed out\n");
+               dev_err(dev->dev, "controller timed out\n");
                at91_init_twi_bus(dev);
                ret = -ETIMEDOUT;
                goto error;
        }
        if (dev->transfer_status & AT91_TWI_NACK) {
-               dev_dbg(&dev->adapter.dev, "received nack\n");
+               dev_dbg(dev->dev, "received nack\n");
                ret = -EREMOTEIO;
                goto error;
        }
        if (dev->transfer_status & AT91_TWI_OVRE) {
-               dev_err(&dev->adapter.dev, "overrun while reading\n");
+               dev_err(dev->dev, "overrun while reading\n");
                ret = -EIO;
                goto error;
        }
        if (has_unre_flag && dev->transfer_status & AT91_TWI_UNRE) {
-               dev_err(&dev->adapter.dev, "underrun while writing\n");
+               dev_err(dev->dev, "underrun while writing\n");
                ret = -EIO;
                goto error;
        }
-       dev_dbg(&dev->adapter.dev, "transfer complete\n");
+       dev_dbg(dev->dev, "transfer complete\n");
 
        return 0;
 
@@ -285,7 +367,7 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct 
i2c_msg *msg, int num)
         * repeated start via it's internal address feature.
         */
        if (num > 2) {
-               dev_err(&dev->adapter.dev,
+               dev_err(dev->dev,
                        "cannot handle more than two concatenated messages.\n");
                return 0;
        } else if (num == 2) {
@@ -293,11 +375,11 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct 
i2c_msg *msg, int num)
                int i;
 
                if (msg->flags & I2C_M_RD) {
-                       dev_err(&dev->adapter.dev, "first transfer must be 
write.\n");
+                       dev_err(dev->dev, "first transfer must be write.\n");
                        return -EINVAL;
                }
                if (msg->len > 3) {
-                       dev_err(&dev->adapter.dev, "first message size must be 
<= 3.\n");
+                       dev_err(dev->dev, "first message size must be <= 3.\n");
                        return -EINVAL;
                }
 
@@ -360,6 +442,17 @@ static struct at91_twi_pdata at91sam9x5_config = {
        .has_unre_flag = false,
 };
 
+static struct at91_twi_pdata sama5d2_config = {
+       .clk_max_div = 7,
+       .clk_offset = 3,
+       .has_unre_flag = true,
+       .has_alt_cmd = true,
+       .has_hold_field = true,
+       .has_dig_filtr = true,
+       .has_adv_dig_filtr = true,
+       .has_ana_filtr = true,
+};
+
 static struct platform_device_id at91_twi_devtypes[] = {
        {
                .name = "at91rm9200-i2c",
@@ -403,6 +496,9 @@ static struct of_device_id at91_twi_dt_ids[] = {
        }, {
                .compatible = "atmel,at91sam9x5-i2c",
                .data = &at91sam9x5_config,
+       }, {
+               .compatible = "atmel,sama5d2-i2c",
+               .data = &sama5d2_config,
        }, {
                /* sentinel */
        }
@@ -417,6 +513,7 @@ static int at91_twi_probe(struct device_d *dev)
        u32 bus_clk_rate;
 
        i2c_at91 = xzalloc(sizeof(struct at91_twi_dev));
+       i2c_at91->dev = dev;
 
        rc = dev_get_drvdata(dev, (const void **)&i2c_data);
        if (rc < 0) {
-- 
2.27.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

Reply via email to