Add support for I2C high-speed mode (3.4 MHz).

Signed-off-by: Tim Kryger <tim.kry...@linaro.org>
Reviewed-by: Matt Porter <matt.por...@linaro.org>
Reviewed-by: Markus Mayer <markus.ma...@linaro.org>
---
 drivers/i2c/busses/i2c-bcm-kona.c | 116 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 116 insertions(+)

diff --git a/drivers/i2c/busses/i2c-bcm-kona.c 
b/drivers/i2c/busses/i2c-bcm-kona.c
index 2936acf..458714f 100644
--- a/drivers/i2c/busses/i2c-bcm-kona.c
+++ b/drivers/i2c/busses/i2c-bcm-kona.c
@@ -77,6 +77,9 @@
 
 #define HSTIM_OFFSET                           0x00000058
 #define HSTIM_HS_MODE_MASK                     0x00008000
+#define HSTIM_HS_HOLD_SHIFT                    10
+#define HSTIM_HS_HIGH_PHASE_SHIFT              5
+#define HSTIM_HS_SETUP_SHIFT                   0
 
 #define PADCTL_OFFSET                          0x0000005c
 #define PADCTL_PAD_OUT_EN_MASK                 0x00000004
@@ -91,6 +94,9 @@
 #define MAX_TX_FIFO_SIZE               64U /* bytes */
 
 #define STD_EXT_CLK_FREQ               13000000UL
+#define HS_EXT_CLK_FREQ                        104000000UL
+
+#define MASTERCODE                     0x08 /* Mastercodes are 0000_1xxxb */
 
 #define I2C_TIMEOUT                    100 /* msecs */
 
@@ -108,6 +114,10 @@ enum bus_speed_index {
        BCM_SPD_1MHZ,
 };
 
+enum hs_bus_speed_index {
+       BCM_SPD_3P4MHZ = 0,
+};
+
 /* Internal divider settings for standard mode, fast mode and fast mode plus */
 struct bus_speed_cfg {
        uint8_t time_m;         /* Number of cycles for setup time */
@@ -118,12 +128,30 @@ struct bus_speed_cfg {
        uint8_t time_div;       /* Post-prescale divider */
 };
 
+/* Internal divider settings for high-speed mode */
+struct hs_bus_speed_cfg {
+       uint8_t hs_hold;        /* Number of clock cycles SCL stays low until
+                                  the end of bit period */
+       uint8_t hs_high_phase;  /* Number of clock cycles SCL stays high
+                                  before it falls */
+       uint8_t hs_setup;       /* Number of clock cycles SCL stays low
+                                  before it rises  */
+       uint8_t prescale;       /* Prescale divider */
+       uint8_t time_p;         /* Timing coefficient */
+       uint8_t no_div;         /* Disable clock divider */
+       uint8_t time_div;       /* Post-prescale divider */
+};
+
 static const struct bus_speed_cfg std_cfg_table[] = {
        [BCM_SPD_100K] = {0x01, 0x01, 0x03, 0x06, 0x00, 0x02},
        [BCM_SPD_400K] = {0x05, 0x01, 0x03, 0x05, 0x01, 0x02},
        [BCM_SPD_1MHZ] = {0x01, 0x01, 0x03, 0x01, 0x01, 0x03},
 };
 
+static const struct hs_bus_speed_cfg hs_cfg_table[] = {
+       [BCM_SPD_3P4MHZ] = {0x01, 0x08, 0x14, 0x00, 0x06, 0x01, 0x00},
+};
+
 struct bcm_kona_i2c_dev {
        /* Pointer to linux device struct */
        struct device *device;
@@ -137,6 +165,9 @@ struct bcm_kona_i2c_dev {
        /* Standard Speed configuration */
        const struct bus_speed_cfg *std_cfg;
 
+       /* High Speed configuration (if applicable) */
+       const struct hs_bus_speed_cfg *hs_cfg;
+
        /* Linux I2C adapter struct */
        struct i2c_adapter adapter;
 
@@ -520,6 +551,72 @@ static void bcm_kona_i2c_config_timing(struct 
bcm_kona_i2c_dev *dev)
               dev->base + CLKEN_OFFSET);
 }
 
+static void bcm_kona_i2c_config_timing_hs(struct bcm_kona_i2c_dev *dev)
+{
+       writel((dev->hs_cfg->prescale << TIM_PRESCALE_SHIFT) |
+              (dev->hs_cfg->time_p << TIM_P_SHIFT) |
+              (dev->hs_cfg->no_div << TIM_NO_DIV_SHIFT) |
+              (dev->hs_cfg->time_div << TIM_DIV_SHIFT),
+              dev->base + TIM_OFFSET);
+
+       writel((dev->hs_cfg->hs_hold << HSTIM_HS_HOLD_SHIFT) |
+              (dev->hs_cfg->hs_high_phase << HSTIM_HS_HIGH_PHASE_SHIFT) |
+              (dev->hs_cfg->hs_setup << HSTIM_HS_SETUP_SHIFT),
+              dev->base + HSTIM_OFFSET);
+
+       writel(readl(dev->base + HSTIM_OFFSET) | HSTIM_HS_MODE_MASK,
+              dev->base + HSTIM_OFFSET);
+}
+
+static int bcm_kona_i2c_switch_to_hs(struct bcm_kona_i2c_dev *dev)
+{
+       int rc;
+
+       /* Send mastercode at standard speed */
+       rc = bcm_kona_i2c_write_byte(dev, MASTERCODE, 1);
+       if (rc < 0) {
+               pr_err("High speed handshake failed\n");
+               return rc;
+       }
+
+       /* Configure external clock to higher frequency */
+       rc = clk_set_rate(dev->external_clk, HS_EXT_CLK_FREQ);
+       if (rc) {
+               dev_err(dev->device, "%s: clk_set_rate returned %d\n",
+                       __func__, rc);
+               return rc;
+       }
+
+       /* Reconfigure internal dividers */
+       bcm_kona_i2c_config_timing_hs(dev);
+
+       /* Send a restart command */
+       rc = bcm_kona_send_i2c_cmd(dev, BCM_CMD_RESTART);
+       if (rc < 0) {
+               dev_err(dev->device,
+               "High speed restart command failed rc = %d\n", rc);
+       }
+
+       return rc;
+}
+
+static int bcm_kona_i2c_switch_to_std(struct bcm_kona_i2c_dev *dev)
+{
+       int rc;
+
+       /* Reconfigure internal dividers */
+       bcm_kona_i2c_config_timing(dev);
+
+       /* Configure external clock to lower frequency */
+       rc = clk_set_rate(dev->external_clk, STD_EXT_CLK_FREQ);
+       if (rc) {
+               dev_err(dev->device, "%s: clk_set_rate returned %d\n",
+                       __func__, rc);
+       }
+
+       return rc;
+}
+
 /* Master transfer function */
 static int bcm_kona_i2c_xfer(struct i2c_adapter *adapter,
                             struct i2c_msg msgs[], int num)
@@ -551,6 +648,13 @@ static int bcm_kona_i2c_xfer(struct i2c_adapter *adapter,
                goto xfer_disable_pad;
        }
 
+       /* Switch to high speed if applicable */
+       if (dev->hs_cfg) {
+               rc = bcm_kona_i2c_switch_to_hs(dev);
+               if (rc < 0)
+                       goto xfer_send_stop;
+       }
+
        /* Loop through all messages */
        for (i = 0; i < num; i++) {
                pmsg = &msgs[i];
@@ -598,6 +702,13 @@ xfer_send_stop:
        /* Send a STOP command */
        bcm_kona_send_i2c_cmd(dev, BCM_CMD_STOP);
 
+       /* Return from high speed if applicable */
+       if (dev->hs_cfg) {
+               int hs_rc = bcm_kona_i2c_switch_to_std(dev);
+               if (hs_rc)
+                       rc = hs_rc;
+       }
+
 xfer_disable_pad:
        /* Disable pad output */
        writel(PADCTL_PAD_OUT_EN_MASK, dev->base + PADCTL_OFFSET);
@@ -642,6 +753,11 @@ static int bcm_kona_i2c_assign_bus_speed(struct 
bcm_kona_i2c_dev *dev)
        case 1000000:
                dev->std_cfg = &std_cfg_table[BCM_SPD_1MHZ];
                break;
+       case 3400000:
+               /* Send mastercode at 100k */
+               dev->std_cfg = &std_cfg_table[BCM_SPD_100K];
+               dev->hs_cfg = &hs_cfg_table[BCM_SPD_3P4MHZ];
+               break;
        default:
                pr_err("%d hz bus speed not supported\n", bus_speed);
                return -EINVAL;
-- 
1.8.0.1


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to