summaryrefslogtreecommitdiff
path: root/drivers/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/designware_i2c.c18
-rw-r--r--drivers/i2c/designware_i2c.h9
-rw-r--r--drivers/i2c/i2c-gpio.c170
-rw-r--r--drivers/i2c/i2c-uclass.c44
-rw-r--r--drivers/i2c/stm32f7_i2c.c105
5 files changed, 213 insertions, 133 deletions
diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c
index 0b5e70af59..088a6f3efb 100644
--- a/drivers/i2c/designware_i2c.c
+++ b/drivers/i2c/designware_i2c.c
@@ -203,14 +203,16 @@ static int calc_bus_speed(struct dw_i2c *priv, int speed, ulong bus_clk,
const struct dw_scl_sda_cfg *scl_sda_cfg = NULL;
struct i2c_regs *regs = priv->regs;
enum i2c_speed_mode i2c_spd;
+ u32 comp_param1;
int spk_cnt;
int ret;
+ comp_param1 = readl(&regs->comp_param1);
+
if (priv)
scl_sda_cfg = priv->scl_sda_cfg;
/* Allow high speed if there is no config, or the config allows it */
- if (speed >= I2C_SPEED_HIGH_RATE &&
- (!scl_sda_cfg || scl_sda_cfg->has_high_speed))
+ if (speed >= I2C_SPEED_HIGH_RATE)
i2c_spd = IC_SPEED_MODE_HIGH;
else if (speed >= I2C_SPEED_FAST_PLUS_RATE)
i2c_spd = IC_SPEED_MODE_FAST_PLUS;
@@ -219,6 +221,13 @@ static int calc_bus_speed(struct dw_i2c *priv, int speed, ulong bus_clk,
else
i2c_spd = IC_SPEED_MODE_STANDARD;
+ /* Check is high speed possible and fall back to fast mode if not */
+ if (i2c_spd == IC_SPEED_MODE_HIGH) {
+ if ((comp_param1 & DW_IC_COMP_PARAM_1_SPEED_MODE_MASK)
+ != DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH)
+ i2c_spd = IC_SPEED_MODE_FAST;
+ }
+
/* Get the proper spike-suppression count based on target speed */
if (!priv || !priv->has_spk_cnt)
spk_cnt = 0;
@@ -231,6 +240,9 @@ static int calc_bus_speed(struct dw_i2c *priv, int speed, ulong bus_clk,
if (i2c_spd == IC_SPEED_MODE_STANDARD) {
config->scl_hcnt = scl_sda_cfg->ss_hcnt;
config->scl_lcnt = scl_sda_cfg->ss_lcnt;
+ } else if (i2c_spd == IC_SPEED_MODE_HIGH) {
+ config->scl_hcnt = scl_sda_cfg->hs_hcnt;
+ config->scl_lcnt = scl_sda_cfg->hs_lcnt;
} else {
config->scl_hcnt = scl_sda_cfg->fs_hcnt;
config->scl_lcnt = scl_sda_cfg->fs_lcnt;
@@ -274,7 +286,7 @@ static int _dw_i2c_set_bus_speed(struct dw_i2c *priv, struct i2c_regs *i2c_base,
switch (config.speed_mode) {
case IC_SPEED_MODE_HIGH:
- cntl |= IC_CON_SPD_SS;
+ cntl |= IC_CON_SPD_HS;
writel(config.scl_hcnt, &i2c_base->ic_hs_scl_hcnt);
writel(config.scl_lcnt, &i2c_base->ic_hs_scl_lcnt);
break;
diff --git a/drivers/i2c/designware_i2c.h b/drivers/i2c/designware_i2c.h
index 61a882cb65..7ee236193d 100644
--- a/drivers/i2c/designware_i2c.h
+++ b/drivers/i2c/designware_i2c.h
@@ -138,22 +138,27 @@ struct i2c_regs {
#define IC_STATUS_TFNF 0x0002
#define IC_STATUS_ACT 0x0001
+#define DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH (BIT(2) | BIT(3))
+#define DW_IC_COMP_PARAM_1_SPEED_MODE_MASK (BIT(2) | BIT(3))
+
/**
* struct dw_scl_sda_cfg - I2C timing configuration
*
- * @has_high_speed: Support high speed (3.4Mbps)
* @ss_hcnt: Standard speed high time in ns
* @fs_hcnt: Fast speed high time in ns
+ * @hs_hcnt: High speed high time in ns
* @ss_lcnt: Standard speed low time in ns
* @fs_lcnt: Fast speed low time in ns
+ * @hs_lcnt: High speed low time in ns
* @sda_hold: SDA hold time
*/
struct dw_scl_sda_cfg {
- bool has_high_speed;
u32 ss_hcnt;
u32 fs_hcnt;
+ u32 hs_hcnt;
u32 ss_lcnt;
u32 fs_lcnt;
+ u32 hs_lcnt;
u32 sda_hold;
};
diff --git a/drivers/i2c/i2c-gpio.c b/drivers/i2c/i2c-gpio.c
index 4e8fa21473..07fdd343f2 100644
--- a/drivers/i2c/i2c-gpio.c
+++ b/drivers/i2c/i2c-gpio.c
@@ -32,23 +32,50 @@ struct i2c_gpio_bus {
int udelay;
/* sda, scl */
struct gpio_desc gpios[PIN_COUNT];
+
+ int (*get_sda)(struct i2c_gpio_bus *bus);
+ void (*set_sda)(struct i2c_gpio_bus *bus, int bit);
+ void (*set_scl)(struct i2c_gpio_bus *bus, int bit);
};
-static int i2c_gpio_sda_get(struct gpio_desc *sda)
+static int i2c_gpio_sda_get(struct i2c_gpio_bus *bus)
{
+ struct gpio_desc *sda = &bus->gpios[PIN_SDA];
+
return dm_gpio_get_value(sda);
}
-static void i2c_gpio_sda_set(struct gpio_desc *sda, int bit)
+static void i2c_gpio_sda_set(struct i2c_gpio_bus *bus, int bit)
{
+ struct gpio_desc *sda = &bus->gpios[PIN_SDA];
+
if (bit)
dm_gpio_set_dir_flags(sda, GPIOD_IS_IN);
else
dm_gpio_set_dir_flags(sda, GPIOD_IS_OUT);
}
-static void i2c_gpio_scl_set(struct gpio_desc *scl, int bit)
+static void i2c_gpio_scl_set(struct i2c_gpio_bus *bus, int bit)
{
+ struct gpio_desc *scl = &bus->gpios[PIN_SCL];
+ int count = 0;
+
+ if (bit) {
+ dm_gpio_set_dir_flags(scl, GPIOD_IS_IN);
+ while (!dm_gpio_get_value(scl) && count++ < 100000)
+ udelay(1);
+
+ if (!dm_gpio_get_value(scl))
+ pr_err("timeout waiting on slave to release scl\n");
+ } else {
+ dm_gpio_set_dir_flags(scl, GPIOD_IS_OUT);
+ }
+}
+
+/* variant for output only gpios which cannot support clock stretching */
+static void i2c_gpio_scl_set_output_only(struct i2c_gpio_bus *bus, int bit)
+{
+ struct gpio_desc *scl = &bus->gpios[PIN_SCL];
ulong flags = GPIOD_IS_OUT;
if (bit)
@@ -56,65 +83,60 @@ static void i2c_gpio_scl_set(struct gpio_desc *scl, int bit)
dm_gpio_set_dir_flags(scl, flags);
}
-static void i2c_gpio_write_bit(struct gpio_desc *scl, struct gpio_desc *sda,
- int delay, uchar bit)
+static void i2c_gpio_write_bit(struct i2c_gpio_bus *bus, int delay, uchar bit)
{
- i2c_gpio_scl_set(scl, 0);
+ bus->set_scl(bus, 0);
udelay(delay);
- i2c_gpio_sda_set(sda, bit);
+ bus->set_sda(bus, bit);
udelay(delay);
- i2c_gpio_scl_set(scl, 1);
+ bus->set_scl(bus, 1);
udelay(2 * delay);
}
-static int i2c_gpio_read_bit(struct gpio_desc *scl, struct gpio_desc *sda,
- int delay)
+static int i2c_gpio_read_bit(struct i2c_gpio_bus *bus, int delay)
{
int value;
- i2c_gpio_scl_set(scl, 1);
+ bus->set_scl(bus, 1);
udelay(delay);
- value = i2c_gpio_sda_get(sda);
+ value = bus->get_sda(bus);
udelay(delay);
- i2c_gpio_scl_set(scl, 0);
+ bus->set_scl(bus, 0);
udelay(2 * delay);
return value;
}
/* START: High -> Low on SDA while SCL is High */
-static void i2c_gpio_send_start(struct gpio_desc *scl, struct gpio_desc *sda,
- int delay)
+static void i2c_gpio_send_start(struct i2c_gpio_bus *bus, int delay)
{
udelay(delay);
- i2c_gpio_sda_set(sda, 1);
+ bus->set_sda(bus, 1);
udelay(delay);
- i2c_gpio_scl_set(scl, 1);
+ bus->set_scl(bus, 1);
udelay(delay);
- i2c_gpio_sda_set(sda, 0);
+ bus->set_sda(bus, 0);
udelay(delay);
}
/* STOP: Low -> High on SDA while SCL is High */
-static void i2c_gpio_send_stop(struct gpio_desc *scl, struct gpio_desc *sda,
- int delay)
+static void i2c_gpio_send_stop(struct i2c_gpio_bus *bus, int delay)
{
- i2c_gpio_scl_set(scl, 0);
+ bus->set_scl(bus, 0);
udelay(delay);
- i2c_gpio_sda_set(sda, 0);
+ bus->set_sda(bus, 0);
udelay(delay);
- i2c_gpio_scl_set(scl, 1);
+ bus->set_scl(bus, 1);
udelay(delay);
- i2c_gpio_sda_set(sda, 1);
+ bus->set_sda(bus, 1);
udelay(delay);
}
/* ack should be I2C_ACK or I2C_NOACK */
-static void i2c_gpio_send_ack(struct gpio_desc *scl, struct gpio_desc *sda,
- int delay, int ack)
+static void i2c_gpio_send_ack(struct i2c_gpio_bus *bus, int delay, int ack)
{
- i2c_gpio_write_bit(scl, sda, delay, ack);
- i2c_gpio_scl_set(scl, 0);
+ i2c_gpio_write_bit(bus, delay, ack);
+ bus->set_scl(bus, 0);
udelay(delay);
}
@@ -123,44 +145,41 @@ static void i2c_gpio_send_ack(struct gpio_desc *scl, struct gpio_desc *sda,
* to clock any confused device back into an idle state. Also send a
* <stop> at the end of the sequence for belts & suspenders.
*/
-static void i2c_gpio_send_reset(struct gpio_desc *scl, struct gpio_desc *sda,
- int delay)
+static void i2c_gpio_send_reset(struct i2c_gpio_bus *bus, int delay)
{
int j;
for (j = 0; j < 9; j++)
- i2c_gpio_write_bit(scl, sda, delay, 1);
+ i2c_gpio_write_bit(bus, delay, 1);
- i2c_gpio_send_stop(scl, sda, delay);
+ i2c_gpio_send_stop(bus, delay);
}
/* Set sda high with low clock, before reading slave data */
-static void i2c_gpio_sda_high(struct gpio_desc *scl, struct gpio_desc *sda,
- int delay)
+static void i2c_gpio_sda_high(struct i2c_gpio_bus *bus, int delay)
{
- i2c_gpio_scl_set(scl, 0);
+ bus->set_scl(bus, 0);
udelay(delay);
- i2c_gpio_sda_set(sda, 1);
+ bus->set_sda(bus, 1);
udelay(delay);
}
/* Send 8 bits and look for an acknowledgement */
-static int i2c_gpio_write_byte(struct gpio_desc *scl, struct gpio_desc *sda,
- int delay, uchar data)
+static int i2c_gpio_write_byte(struct i2c_gpio_bus *bus, int delay, uchar data)
{
int j;
int nack;
for (j = 0; j < 8; j++) {
- i2c_gpio_write_bit(scl, sda, delay, data & 0x80);
+ i2c_gpio_write_bit(bus, delay, data & 0x80);
data <<= 1;
}
udelay(delay);
/* Look for an <ACK>(negative logic) and return it */
- i2c_gpio_sda_high(scl, sda, delay);
- nack = i2c_gpio_read_bit(scl, sda, delay);
+ i2c_gpio_sda_high(bus, delay);
+ nack = i2c_gpio_read_bit(bus, delay);
return nack; /* not a nack is an ack */
}
@@ -169,31 +188,29 @@ static int i2c_gpio_write_byte(struct gpio_desc *scl, struct gpio_desc *sda,
* if ack == I2C_ACK, ACK the byte so can continue reading, else
* send I2C_NOACK to end the read.
*/
-static uchar i2c_gpio_read_byte(struct gpio_desc *scl, struct gpio_desc *sda,
- int delay, int ack)
+static uchar i2c_gpio_read_byte(struct i2c_gpio_bus *bus, int delay, int ack)
{
int data;
int j;
- i2c_gpio_sda_high(scl, sda, delay);
+ i2c_gpio_sda_high(bus, delay);
data = 0;
for (j = 0; j < 8; j++) {
data <<= 1;
- data |= i2c_gpio_read_bit(scl, sda, delay);
+ data |= i2c_gpio_read_bit(bus, delay);
}
- i2c_gpio_send_ack(scl, sda, delay, ack);
+ i2c_gpio_send_ack(bus, delay, ack);
return data;
}
/* send start and the slave chip address */
-int i2c_send_slave_addr(struct gpio_desc *scl, struct gpio_desc *sda, int delay,
- uchar chip)
+int i2c_send_slave_addr(struct i2c_gpio_bus *bus, int delay, uchar chip)
{
- i2c_gpio_send_start(scl, sda, delay);
+ i2c_gpio_send_start(bus, delay);
- if (i2c_gpio_write_byte(scl, sda, delay, chip)) {
- i2c_gpio_send_stop(scl, sda, delay);
+ if (i2c_gpio_write_byte(bus, delay, chip)) {
+ i2c_gpio_send_stop(bus, delay);
return -EIO;
}
@@ -204,29 +221,27 @@ static int i2c_gpio_write_data(struct i2c_gpio_bus *bus, uchar chip,
uchar *buffer, int len,
bool end_with_repeated_start)
{
- struct gpio_desc *scl = &bus->gpios[PIN_SCL];
- struct gpio_desc *sda = &bus->gpios[PIN_SDA];
unsigned int delay = bus->udelay;
int failures = 0;
debug("%s: chip %x buffer %p len %d\n", __func__, chip, buffer, len);
- if (i2c_send_slave_addr(scl, sda, delay, chip << 1)) {
+ if (i2c_send_slave_addr(bus, delay, chip << 1)) {
debug("i2c_write, no chip responded %02X\n", chip);
return -EIO;
}
while (len-- > 0) {
- if (i2c_gpio_write_byte(scl, sda, delay, *buffer++))
+ if (i2c_gpio_write_byte(bus, delay, *buffer++))
failures++;
}
if (!end_with_repeated_start) {
- i2c_gpio_send_stop(scl, sda, delay);
+ i2c_gpio_send_stop(bus, delay);
return failures;
}
- if (i2c_send_slave_addr(scl, sda, delay, (chip << 1) | 0x1)) {
+ if (i2c_send_slave_addr(bus, delay, (chip << 1) | 0x1)) {
debug("i2c_write, no chip responded %02X\n", chip);
return -EIO;
}
@@ -237,16 +252,14 @@ static int i2c_gpio_write_data(struct i2c_gpio_bus *bus, uchar chip,
static int i2c_gpio_read_data(struct i2c_gpio_bus *bus, uchar chip,
uchar *buffer, int len)
{
- struct gpio_desc *scl = &bus->gpios[PIN_SCL];
- struct gpio_desc *sda = &bus->gpios[PIN_SDA];
unsigned int delay = bus->udelay;
debug("%s: chip %x buffer: %p len %d\n", __func__, chip, buffer, len);
while (len-- > 0)
- *buffer++ = i2c_gpio_read_byte(scl, sda, delay, len == 0);
+ *buffer++ = i2c_gpio_read_byte(bus, delay, len == 0);
- i2c_gpio_send_stop(scl, sda, delay);
+ i2c_gpio_send_stop(bus, delay);
return 0;
}
@@ -277,14 +290,12 @@ static int i2c_gpio_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
static int i2c_gpio_probe(struct udevice *dev, uint chip, uint chip_flags)
{
struct i2c_gpio_bus *bus = dev_get_priv(dev);
- struct gpio_desc *scl = &bus->gpios[PIN_SCL];
- struct gpio_desc *sda = &bus->gpios[PIN_SDA];
unsigned int delay = bus->udelay;
int ret;
- i2c_gpio_send_start(scl, sda, delay);
- ret = i2c_gpio_write_byte(scl, sda, delay, (chip << 1) | 0);
- i2c_gpio_send_stop(scl, sda, delay);
+ i2c_gpio_send_start(bus, delay);
+ ret = i2c_gpio_write_byte(bus, delay, (chip << 1) | 0);
+ i2c_gpio_send_stop(bus, delay);
debug("%s: bus: %d (%s) chip: %x flags: %x ret: %d\n",
__func__, dev->seq, dev->name, chip, chip_flags, ret);
@@ -295,12 +306,25 @@ static int i2c_gpio_probe(struct udevice *dev, uint chip, uint chip_flags)
static int i2c_gpio_set_bus_speed(struct udevice *dev, unsigned int speed_hz)
{
struct i2c_gpio_bus *bus = dev_get_priv(dev);
- struct gpio_desc *scl = &bus->gpios[PIN_SCL];
- struct gpio_desc *sda = &bus->gpios[PIN_SDA];
bus->udelay = 1000000 / (speed_hz << 2);
- i2c_gpio_send_reset(scl, sda, bus->udelay);
+ i2c_gpio_send_reset(bus, bus->udelay);
+
+ return 0;
+}
+
+static int i2c_gpio_drv_probe(struct udevice *dev)
+{
+ if (dev_read_bool(dev, "i2c-gpio,deblock")) {
+ /* @200kHz 9 clocks = 44us, 62us is ok */
+ const unsigned int DELAY_ABORT_SEQ = 62;
+ struct i2c_gpio_bus *bus = dev_get_priv(dev);
+
+ return i2c_deblock_gpio_loop(&bus->gpios[PIN_SDA],
+ &bus->gpios[PIN_SCL],
+ 16, 5, DELAY_ABORT_SEQ);
+ }
return 0;
}
@@ -320,6 +344,13 @@ static int i2c_gpio_ofdata_to_platdata(struct udevice *dev)
bus->udelay = fdtdec_get_int(blob, node, "i2c-gpio,delay-us",
DEFAULT_UDELAY);
+ bus->get_sda = i2c_gpio_sda_get;
+ bus->set_sda = i2c_gpio_sda_set;
+ if (fdtdec_get_bool(blob, node, "i2c-gpio,scl-output-only"))
+ bus->set_scl = i2c_gpio_scl_set_output_only;
+ else
+ bus->set_scl = i2c_gpio_scl_set;
+
return 0;
error:
pr_err("Can't get %s gpios! Error: %d", dev->name, ret);
@@ -341,6 +372,7 @@ U_BOOT_DRIVER(i2c_gpio) = {
.name = "i2c-gpio",
.id = UCLASS_I2C,
.of_match = i2c_gpio_ids,
+ .probe = i2c_gpio_drv_probe,
.ofdata_to_platdata = i2c_gpio_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct i2c_gpio_bus),
.ops = &i2c_gpio_ops,
diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c
index 2aa3efe8aa..e9ec388576 100644
--- a/drivers/i2c/i2c-uclass.c
+++ b/drivers/i2c/i2c-uclass.c
@@ -501,35 +501,53 @@ static int i2c_gpio_get_pin(struct gpio_desc *pin)
return dm_gpio_get_value(pin);
}
-static int i2c_deblock_gpio_loop(struct gpio_desc *sda_pin,
- struct gpio_desc *scl_pin)
+int i2c_deblock_gpio_loop(struct gpio_desc *sda_pin,
+ struct gpio_desc *scl_pin,
+ unsigned int scl_count,
+ unsigned int start_count,
+ unsigned int delay)
{
- int counter = 9;
- int ret = 0;
+ int i, ret = -EREMOTEIO;
i2c_gpio_set_pin(sda_pin, 1);
i2c_gpio_set_pin(scl_pin, 1);
- udelay(5);
+ udelay(delay);
/* Toggle SCL until slave release SDA */
- while (counter-- >= 0) {
+ while (scl_count-- >= 0) {
i2c_gpio_set_pin(scl_pin, 1);
- udelay(5);
+ udelay(delay);
i2c_gpio_set_pin(scl_pin, 0);
- udelay(5);
- if (i2c_gpio_get_pin(sda_pin))
+ udelay(delay);
+ if (i2c_gpio_get_pin(sda_pin)) {
+ ret = 0;
break;
+ }
+ }
+
+ if (!ret && start_count) {
+ for (i = 0; i < start_count; i++) {
+ /* Send start condition */
+ udelay(delay);
+ i2c_gpio_set_pin(sda_pin, 1);
+ udelay(delay);
+ i2c_gpio_set_pin(scl_pin, 1);
+ udelay(delay);
+ i2c_gpio_set_pin(sda_pin, 0);
+ udelay(delay);
+ i2c_gpio_set_pin(scl_pin, 0);
+ }
}
/* Then, send I2C stop */
i2c_gpio_set_pin(sda_pin, 0);
- udelay(5);
+ udelay(delay);
i2c_gpio_set_pin(scl_pin, 1);
- udelay(5);
+ udelay(delay);
i2c_gpio_set_pin(sda_pin, 1);
- udelay(5);
+ udelay(delay);
if (!i2c_gpio_get_pin(sda_pin) || !i2c_gpio_get_pin(scl_pin))
ret = -EREMOTEIO;
@@ -561,7 +579,7 @@ static int i2c_deblock_gpio(struct udevice *bus)
goto out_no_pinctrl;
}
- ret0 = i2c_deblock_gpio_loop(&gpios[PIN_SDA], &gpios[PIN_SCL]);
+ ret0 = i2c_deblock_gpio_loop(&gpios[PIN_SDA], &gpios[PIN_SCL], 9, 0, 5);
ret = pinctrl_select_state(bus, "default");
if (ret) {
diff --git a/drivers/i2c/stm32f7_i2c.c b/drivers/i2c/stm32f7_i2c.c
index 7d046c1a1e..fc5c1221e1 100644
--- a/drivers/i2c/stm32f7_i2c.c
+++ b/drivers/i2c/stm32f7_i2c.c
@@ -7,10 +7,10 @@
#include <clk.h>
#include <dm.h>
#include <i2c.h>
-#include <malloc.h>
#include <reset.h>
#include <dm/device.h>
+#include <linux/err.h>
#include <linux/io.h>
/* STM32 I2C registers */
@@ -145,7 +145,6 @@ struct stm32_i2c_spec {
/**
* struct stm32_i2c_setup - private I2C timing setup parameters
- * @speed: I2C speed mode (standard, Fast Plus)
* @speed_freq: I2C speed frequency (Hz)
* @clock_src: I2C clock source frequency (Hz)
* @rise_time: Rise time (ns)
@@ -154,7 +153,6 @@ struct stm32_i2c_spec {
* @analog_filter: Analog filter delay (On/Off)
*/
struct stm32_i2c_setup {
- enum i2c_speed_mode speed;
u32 speed_freq;
u32 clock_src;
u32 rise_time;
@@ -184,10 +182,11 @@ struct stm32_i2c_priv {
struct stm32_i2c_regs *regs;
struct clk clk;
struct stm32_i2c_setup *setup;
- int speed;
+ u32 speed;
};
static const struct stm32_i2c_spec i2c_specs[] = {
+ /* Standard speed - 100 KHz */
[IC_SPEED_MODE_STANDARD] = {
.rate = I2C_SPEED_STANDARD_RATE,
.rate_min = 8000,
@@ -200,6 +199,7 @@ static const struct stm32_i2c_spec i2c_specs[] = {
.l_min = 4700,
.h_min = 4000,
},
+ /* Fast speed - 400 KHz */
[IC_SPEED_MODE_FAST] = {
.rate = I2C_SPEED_FAST_RATE,
.rate_min = 320000,
@@ -212,6 +212,7 @@ static const struct stm32_i2c_spec i2c_specs[] = {
.l_min = 1300,
.h_min = 600,
},
+ /* Fast Plus Speed - 1 MHz */
[IC_SPEED_MODE_FAST_PLUS] = {
.rate = I2C_SPEED_FAST_PLUS_RATE,
.rate_min = 800000,
@@ -474,6 +475,7 @@ static int stm32_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
}
static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup,
+ const struct stm32_i2c_spec *specs,
struct list_head *solutions)
{
struct stm32_i2c_timings *v;
@@ -490,13 +492,13 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup,
af_delay_max = setup->analog_filter ?
STM32_I2C_ANALOG_FILTER_DELAY_MAX : 0;
- sdadel_min = i2c_specs[setup->speed].hddat_min + setup->fall_time -
+ sdadel_min = specs->hddat_min + setup->fall_time -
af_delay_min - (setup->dnf + 3) * i2cclk;
- sdadel_max = i2c_specs[setup->speed].vddat_max - setup->rise_time -
+ sdadel_max = specs->vddat_max - setup->rise_time -
af_delay_max - (setup->dnf + 4) * i2cclk;
- scldel_min = setup->rise_time + i2c_specs[setup->speed].sudat_min;
+ scldel_min = setup->rise_time + specs->sudat_min;
if (sdadel_min < 0)
sdadel_min = 0;
@@ -548,6 +550,7 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup,
}
static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
+ const struct stm32_i2c_spec *specs,
struct list_head *solutions,
struct stm32_i2c_timings *s)
{
@@ -570,8 +573,8 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
dnf_delay = setup->dnf * i2cclk;
tsync = af_delay_min + dnf_delay + (2 * i2cclk);
- clk_max = STM32_NSEC_PER_SEC / i2c_specs[setup->speed].rate_min;
- clk_min = STM32_NSEC_PER_SEC / i2c_specs[setup->speed].rate_max;
+ clk_max = STM32_NSEC_PER_SEC / specs->rate_min;
+ clk_min = STM32_NSEC_PER_SEC / specs->rate_max;
/*
* Among Prescaler possibilities discovered above figures out SCL Low
@@ -589,7 +592,7 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
for (l = 0; l < STM32_SCLL_MAX; l++) {
u32 tscl_l = (l + 1) * prescaler + tsync;
- if ((tscl_l < i2c_specs[setup->speed].l_min) ||
+ if (tscl_l < specs->l_min ||
(i2cclk >=
((tscl_l - af_delay_min - dnf_delay) / 4))) {
continue;
@@ -601,7 +604,7 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
setup->rise_time + setup->fall_time;
if ((tscl >= clk_min) && (tscl <= clk_max) &&
- (tscl_h >= i2c_specs[setup->speed].h_min) &&
+ (tscl_h >= specs->h_min) &&
(i2cclk < tscl_h)) {
u32 clk_error;
@@ -630,26 +633,40 @@ static int stm32_i2c_choose_solution(struct stm32_i2c_setup *setup,
return ret;
}
+static const struct stm32_i2c_spec *get_specs(u32 rate)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(i2c_specs); i++)
+ if (rate <= i2c_specs[i].rate)
+ return &i2c_specs[i];
+
+ /* NOT REACHED */
+ return ERR_PTR(-EINVAL);
+}
+
static int stm32_i2c_compute_timing(struct stm32_i2c_priv *i2c_priv,
struct stm32_i2c_setup *setup,
struct stm32_i2c_timings *output)
{
+ const struct stm32_i2c_spec *specs;
struct stm32_i2c_timings *v, *_v;
struct list_head solutions;
int ret;
- if (setup->speed >= ARRAY_SIZE(i2c_specs)) {
- pr_err("%s: speed out of bound {%d/%d}\n", __func__,
- setup->speed, ARRAY_SIZE(i2c_specs) - 1);
+ specs = get_specs(setup->speed_freq);
+ if (specs == ERR_PTR(-EINVAL)) {
+ pr_err("%s: speed out of bound {%d}\n", __func__,
+ setup->speed_freq);
return -EINVAL;
}
- if ((setup->rise_time > i2c_specs[setup->speed].rise_max) ||
- (setup->fall_time > i2c_specs[setup->speed].fall_max)) {
+ if (setup->rise_time > specs->rise_max ||
+ setup->fall_time > specs->fall_max) {
pr_err("%s :timings out of bound Rise{%d>%d}/Fall{%d>%d}\n",
__func__,
- setup->rise_time, i2c_specs[setup->speed].rise_max,
- setup->fall_time, i2c_specs[setup->speed].fall_max);
+ setup->rise_time, specs->rise_max,
+ setup->fall_time, specs->fall_max);
return -EINVAL;
}
@@ -659,18 +676,12 @@ static int stm32_i2c_compute_timing(struct stm32_i2c_priv *i2c_priv,
return -EINVAL;
}
- if (setup->speed_freq > i2c_specs[setup->speed].rate) {
- pr_err("%s: Freq {%d/%d}\n", __func__,
- setup->speed_freq, i2c_specs[setup->speed].rate);
- return -EINVAL;
- }
-
INIT_LIST_HEAD(&solutions);
- ret = stm32_i2c_compute_solutions(setup, &solutions);
+ ret = stm32_i2c_compute_solutions(setup, specs, &solutions);
if (ret)
goto exit;
- ret = stm32_i2c_choose_solution(setup, &solutions, output);
+ ret = stm32_i2c_choose_solution(setup, specs, &solutions, output);
if (ret)
goto exit;
@@ -689,14 +700,24 @@ exit:
return ret;
}
+static u32 get_lower_rate(u32 rate)
+{
+ int i;
+
+ for (i = ARRAY_SIZE(i2c_specs) - 1; i >= 0; i--)
+ if (rate > i2c_specs[i].rate)
+ return i2c_specs[i].rate;
+
+ return i2c_specs[0].rate;
+}
+
static int stm32_i2c_setup_timing(struct stm32_i2c_priv *i2c_priv,
struct stm32_i2c_timings *timing)
{
struct stm32_i2c_setup *setup = i2c_priv->setup;
int ret = 0;
- setup->speed = i2c_priv->speed;
- setup->speed_freq = i2c_specs[setup->speed].rate;
+ setup->speed_freq = i2c_priv->speed;
setup->clock_src = clk_get_rate(&i2c_priv->clk);
if (!setup->clock_src) {
@@ -709,13 +730,11 @@ static int stm32_i2c_setup_timing(struct stm32_i2c_priv *i2c_priv,
if (ret) {
debug("%s: failed to compute I2C timings.\n",
__func__);
- if (i2c_priv->speed > IC_SPEED_MODE_STANDARD) {
- i2c_priv->speed--;
- setup->speed = i2c_priv->speed;
+ if (setup->speed_freq > I2C_SPEED_STANDARD_RATE) {
setup->speed_freq =
- i2c_specs[setup->speed].rate;
+ get_lower_rate(setup->speed_freq);
debug("%s: downgrade I2C Speed Freq to (%i)\n",
- __func__, i2c_specs[setup->speed].rate);
+ __func__, setup->speed_freq);
} else {
break;
}
@@ -727,13 +746,15 @@ static int stm32_i2c_setup_timing(struct stm32_i2c_priv *i2c_priv,
return ret;
}
- debug("%s: I2C Speed(%i), Freq(%i), Clk Source(%i)\n", __func__,
- setup->speed, setup->speed_freq, setup->clock_src);
+ debug("%s: I2C Freq(%i), Clk Source(%i)\n", __func__,
+ setup->speed_freq, setup->clock_src);
debug("%s: I2C Rise(%i) and Fall(%i) Time\n", __func__,
setup->rise_time, setup->fall_time);
debug("%s: I2C Analog Filter(%s), DNF(%i)\n", __func__,
setup->analog_filter ? "On" : "Off", setup->dnf);
+ i2c_priv->speed = setup->speed_freq;
+
return 0;
}
@@ -773,21 +794,13 @@ static int stm32_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
{
struct stm32_i2c_priv *i2c_priv = dev_get_priv(bus);
- switch (speed) {
- case I2C_SPEED_STANDARD_RATE:
- i2c_priv->speed = IC_SPEED_MODE_STANDARD;
- break;
- case I2C_SPEED_FAST_RATE:
- i2c_priv->speed = IC_SPEED_MODE_FAST;
- break;
- case I2C_SPEED_FAST_PLUS_RATE:
- i2c_priv->speed = IC_SPEED_MODE_FAST_PLUS;
- break;
- default:
+ if (speed > I2C_SPEED_FAST_PLUS_RATE) {
debug("%s: Speed %d not supported\n", __func__, speed);
return -EINVAL;
}
+ i2c_priv->speed = speed;
+
return stm32_i2c_hw_config(i2c_priv);
}