| From 28683e847e2f20eed22cdd24f185d7783db396d3 Mon Sep 17 00:00:00 2001 |
| From: Jerome Brunet <jbrunet@baylibre.com> |
| Date: Wed, 7 Oct 2020 10:07:49 +0200 |
| Subject: i2c: meson: fix clock setting overwrite |
| |
| From: Jerome Brunet <jbrunet@baylibre.com> |
| |
| commit 28683e847e2f20eed22cdd24f185d7783db396d3 upstream. |
| |
| When the slave address is written in do_start(), SLAVE_ADDR is written |
| completely. This may overwrite some setting related to the clock rate |
| or signal filtering. |
| |
| Fix this by writing only the bits related to slave address. To avoid |
| causing unexpected changed, explicitly disable filtering or high/low |
| clock mode which may have been left over by the bootloader. |
| |
| Fixes: 30021e3707a7 ("i2c: add support for Amlogic Meson I2C controller") |
| Signed-off-by: Jerome Brunet <jbrunet@baylibre.com> |
| Signed-off-by: Wolfram Sang <wsa@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/i2c/busses/i2c-meson.c | 19 ++++++++++++++++++- |
| 1 file changed, 18 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/i2c/busses/i2c-meson.c |
| +++ b/drivers/i2c/busses/i2c-meson.c |
| @@ -5,6 +5,7 @@ |
| * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> |
| */ |
| |
| +#include <linux/bitfield.h> |
| #include <linux/clk.h> |
| #include <linux/completion.h> |
| #include <linux/i2c.h> |
| @@ -38,6 +39,12 @@ |
| #define REG_CTRL_CLKDIVEXT_SHIFT 28 |
| #define REG_CTRL_CLKDIVEXT_MASK GENMASK(29, 28) |
| |
| +#define REG_SLV_ADDR GENMASK(7, 0) |
| +#define REG_SLV_SDA_FILTER GENMASK(10, 8) |
| +#define REG_SLV_SCL_FILTER GENMASK(13, 11) |
| +#define REG_SLV_SCL_LOW GENMASK(27, 16) |
| +#define REG_SLV_SCL_LOW_EN BIT(28) |
| + |
| #define I2C_TIMEOUT_MS 500 |
| |
| enum { |
| @@ -147,6 +154,9 @@ static void meson_i2c_set_clk_div(struct |
| meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_CLKDIVEXT_MASK, |
| (div >> 10) << REG_CTRL_CLKDIVEXT_SHIFT); |
| |
| + /* Disable HIGH/LOW mode */ |
| + meson_i2c_set_mask(i2c, REG_SLAVE_ADDR, REG_SLV_SCL_LOW_EN, 0); |
| + |
| dev_dbg(i2c->dev, "%s: clk %lu, freq %u, div %u\n", __func__, |
| clk_rate, freq, div); |
| } |
| @@ -280,7 +290,10 @@ static void meson_i2c_do_start(struct me |
| token = (msg->flags & I2C_M_RD) ? TOKEN_SLAVE_ADDR_READ : |
| TOKEN_SLAVE_ADDR_WRITE; |
| |
| - writel(msg->addr << 1, i2c->regs + REG_SLAVE_ADDR); |
| + |
| + meson_i2c_set_mask(i2c, REG_SLAVE_ADDR, REG_SLV_ADDR, |
| + FIELD_PREP(REG_SLV_ADDR, msg->addr << 1)); |
| + |
| meson_i2c_add_token(i2c, TOKEN_START); |
| meson_i2c_add_token(i2c, token); |
| } |
| @@ -461,6 +474,10 @@ static int meson_i2c_probe(struct platfo |
| return ret; |
| } |
| |
| + /* Disable filtering */ |
| + meson_i2c_set_mask(i2c, REG_SLAVE_ADDR, |
| + REG_SLV_SDA_FILTER | REG_SLV_SCL_FILTER, 0); |
| + |
| meson_i2c_set_clk_div(i2c, timings.bus_freq_hz); |
| |
| return 0; |