]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
i2c: at91: add support for the HOLD field
authorLudovic Desroches <ludovic.desroches@atmel.com>
Thu, 3 Dec 2015 09:53:50 +0000 (10:53 +0100)
committerWolfram Sang <wsa@the-dreams.de>
Thu, 3 Dec 2015 20:42:37 +0000 (21:42 +0100)
The hold field allows to configure the data hold time which can be set
with the help of the generic binding 'i2c-sda-hold-time-ns'. This
feature has been introduced with SAMA5D4 SoC family.

Signed-off-by: Ludovic Desroches <ludovic.desroches@atmel.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
Documentation/devicetree/bindings/i2c/i2c-at91.txt
drivers/i2c/busses/i2c-at91.c

index 6e81dc153f3b9570f617cf73cac90f238debb392..ef973a0343c7b48a95cd18c3cc44b48b0ae08e62 100644 (file)
@@ -3,7 +3,7 @@ I2C for Atmel platforms
 Required properties :
 - compatible : Must be "atmel,at91rm9200-i2c", "atmel,at91sam9261-i2c",
      "atmel,at91sam9260-i2c", "atmel,at91sam9g20-i2c", "atmel,at91sam9g10-i2c",
-     "atmel,at91sam9x5-i2c" or "atmel,sama5d2-i2c"
+     "atmel,at91sam9x5-i2c", "atmel,sama5d4-i2c" or "atmel,sama5d2-i2c"
 - reg: physical base address of the controller and length of memory mapped
      region.
 - interrupts: interrupt number to the cpu.
@@ -17,6 +17,8 @@ Optional properties:
 - dma-names: should contain "tx" and "rx".
 - atmel,fifo-size: maximum number of data the RX and TX FIFOs can store for FIFO
   capable I2C controllers.
+- i2c-sda-hold-time-ns: TWD hold time, only available for "atmel,sama5d4-i2c"
+  and "atmel,sama5d2-i2c".
 - Child nodes conforming to i2c bus binding
 
 Examples :
@@ -52,6 +54,7 @@ i2c0: i2c@f8034600 {
        #size-cells = <0>;
        clocks = <&flx0>;
        atmel,fifo-size = <16>;
+       i2c-sda-hold-time-ns = <336>;
 
        wm8731: wm8731@1a {
                compatible = "wm8731";
index 10835d1f559ba99f9b0118b19f55b7187a980dea..921d32bfcda8eabbbb3677178734f6b4f614987a 100644 (file)
@@ -64,6 +64,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         BIT(0)  /* Transmission Complete */
@@ -110,6 +112,7 @@ struct at91_twi_pdata {
        unsigned clk_offset;
        bool has_unre_flag;
        bool has_alt_cmd;
+       bool has_hold_field;
        struct at_dma_slave dma_slave;
 };
 
@@ -187,10 +190,11 @@ 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;
        struct at91_twi_pdata *pdata = dev->pdata;
        int offset = pdata->clk_offset;
        int max_ckdiv = pdata->clk_max_div;
+       u32 twd_hold_time_ns = 0;
 
        div = max(0, (int)DIV_ROUND_UP(clk_get_rate(dev->clk),
                                       2 * twi_clk) - offset);
@@ -204,8 +208,33 @@ static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
                cdiv = 255;
        }
 
-       dev->twi_cwgr_reg = (ckdiv << 16) | (cdiv << 8) | cdiv;
-       dev_dbg(dev->dev, "cdiv %d ckdiv %d\n", cdiv, ckdiv);
+       if (pdata->has_hold_field) {
+               of_property_read_u32(dev->dev->of_node, "i2c-sda-hold-time-ns",
+                                    &twd_hold_time_ns);
+
+               /*
+                * hold time = HOLD + 3 x T_peripheral_clock
+                * Use clk rate in kHz to prevent overflows when computing
+                * hold.
+                */
+               hold = DIV_ROUND_UP(twd_hold_time_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;
+               }
+       }
+
+       dev->twi_cwgr_reg = (ckdiv << 16) | (cdiv << 8) | cdiv
+                           | AT91_TWI_CWGR_HOLD(hold);
+
+       dev_dbg(dev->dev, "cdiv %d ckdiv %d hold %d (%d ns)\n",
+               cdiv, ckdiv, hold, twd_hold_time_ns);
 }
 
 static void at91_twi_dma_cleanup(struct at91_twi_dev *dev)
@@ -797,6 +826,7 @@ static struct at91_twi_pdata at91rm9200_config = {
        .clk_offset = 3,
        .has_unre_flag = true,
        .has_alt_cmd = false,
+       .has_hold_field = false,
 };
 
 static struct at91_twi_pdata at91sam9261_config = {
@@ -804,6 +834,7 @@ static struct at91_twi_pdata at91sam9261_config = {
        .clk_offset = 4,
        .has_unre_flag = false,
        .has_alt_cmd = false,
+       .has_hold_field = false,
 };
 
 static struct at91_twi_pdata at91sam9260_config = {
@@ -811,6 +842,7 @@ static struct at91_twi_pdata at91sam9260_config = {
        .clk_offset = 4,
        .has_unre_flag = false,
        .has_alt_cmd = false,
+       .has_hold_field = false,
 };
 
 static struct at91_twi_pdata at91sam9g20_config = {
@@ -818,6 +850,7 @@ static struct at91_twi_pdata at91sam9g20_config = {
        .clk_offset = 4,
        .has_unre_flag = false,
        .has_alt_cmd = false,
+       .has_hold_field = false,
 };
 
 static struct at91_twi_pdata at91sam9g10_config = {
@@ -825,6 +858,7 @@ static struct at91_twi_pdata at91sam9g10_config = {
        .clk_offset = 4,
        .has_unre_flag = false,
        .has_alt_cmd = false,
+       .has_hold_field = false,
 };
 
 static const struct platform_device_id at91_twi_devtypes[] = {
@@ -854,6 +888,15 @@ static struct at91_twi_pdata at91sam9x5_config = {
        .clk_offset = 4,
        .has_unre_flag = false,
        .has_alt_cmd = false,
+       .has_hold_field = false,
+};
+
+static struct at91_twi_pdata sama5d4_config = {
+       .clk_max_div = 7,
+       .clk_offset = 4,
+       .has_unre_flag = false,
+       .has_alt_cmd = false,
+       .has_hold_field = true,
 };
 
 static struct at91_twi_pdata sama5d2_config = {
@@ -861,6 +904,7 @@ static struct at91_twi_pdata sama5d2_config = {
        .clk_offset = 4,
        .has_unre_flag = true,
        .has_alt_cmd = true,
+       .has_hold_field = true,
 };
 
 static const struct of_device_id atmel_twi_dt_ids[] = {
@@ -882,6 +926,9 @@ static const struct of_device_id atmel_twi_dt_ids[] = {
        }, {
                .compatible = "atmel,at91sam9x5-i2c",
                .data = &at91sam9x5_config,
+       }, {
+               .compatible = "atmel,sama5d4-i2c",
+               .data = &sama5d4_config,
        }, {
                .compatible = "atmel,sama5d2-i2c",
                .data = &sama5d2_config,