From: Ludovic Desroches Date: Thu, 3 Dec 2015 09:53:50 +0000 (+0100) Subject: i2c: at91: add support for the HOLD field X-Git-Tag: v4.5-rc1~103^2~45 X-Git-Url: https://git.kernelconcepts.de/?a=commitdiff_plain;h=cc018e3612e4a8244790521e88f9b1b7eb60dab0;p=karo-tx-linux.git i2c: at91: add support for the HOLD field 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 Acked-by: Nicolas Ferre Acked-by: Rob Herring Signed-off-by: Wolfram Sang --- diff --git a/Documentation/devicetree/bindings/i2c/i2c-at91.txt b/Documentation/devicetree/bindings/i2c/i2c-at91.txt index 6e81dc153f3b..ef973a0343c7 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-at91.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-at91.txt @@ -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"; diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 10835d1f559b..921d32bfcda8 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -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,