Skip to content

Commit

Permalink
i3c: fix i2c and i3c scl rate by bus mode
Browse files Browse the repository at this point in the history
Currently the I3C framework limits SCL frequency to FM speed when
dealing with a mixed slow bus, even if all I2C devices are FM+ capable.

The core was also not accounting for I3C speed limitations when
operating in mixed slow mode and was erroneously using FM+ speed as the
max I2C speed when operating in mixed fast mode.

Fixes: 3a379bb ("i3c: Add core I3C infrastructure")
Signed-off-by: Vitor Soares <vitor.soares@synopsys.com>
Cc: Boris Brezillon <bbrezillon@kernel.org>
Cc: <stable@vger.kernel.org>
Cc: <linux-kernel@vger.kernel.org>
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
  • Loading branch information
vitor-soares-snps authored and bbrezillon committed Jun 20, 2019
1 parent 5e343fb commit ecc8fb5
Showing 1 changed file with 38 additions and 13 deletions.
51 changes: 38 additions & 13 deletions drivers/i3c/master.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ void i3c_bus_normaluse_unlock(struct i3c_bus *bus)
up_read(&bus->lock);
}

static struct i3c_master_controller *
i3c_bus_to_i3c_master(struct i3c_bus *i3cbus)
{
return container_of(i3cbus, struct i3c_master_controller, bus);
}

static struct i3c_master_controller *dev_to_i3cmaster(struct device *dev)
{
return container_of(dev, struct i3c_master_controller, dev);
Expand Down Expand Up @@ -565,20 +571,38 @@ static const struct device_type i3c_masterdev_type = {
.groups = i3c_masterdev_groups,
};

int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode)
int i3c_bus_set_mode(struct i3c_bus *i3cbus, enum i3c_bus_mode mode,
unsigned long max_i2c_scl_rate)
{
i3cbus->mode = mode;
struct i3c_master_controller *master = i3c_bus_to_i3c_master(i3cbus);

if (!i3cbus->scl_rate.i3c)
i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
i3cbus->mode = mode;

if (!i3cbus->scl_rate.i2c) {
if (i3cbus->mode == I3C_BUS_MODE_MIXED_SLOW)
i3cbus->scl_rate.i2c = I3C_BUS_I2C_FM_SCL_RATE;
else
i3cbus->scl_rate.i2c = I3C_BUS_I2C_FM_PLUS_SCL_RATE;
switch (i3cbus->mode) {
case I3C_BUS_MODE_PURE:
if (!i3cbus->scl_rate.i3c)
i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
break;
case I3C_BUS_MODE_MIXED_FAST:
if (!i3cbus->scl_rate.i3c)
i3cbus->scl_rate.i3c = I3C_BUS_TYP_I3C_SCL_RATE;
if (!i3cbus->scl_rate.i2c)
i3cbus->scl_rate.i2c = max_i2c_scl_rate;
break;
case I3C_BUS_MODE_MIXED_SLOW:
if (!i3cbus->scl_rate.i2c)
i3cbus->scl_rate.i2c = max_i2c_scl_rate;
if (!i3cbus->scl_rate.i3c ||
i3cbus->scl_rate.i3c > i3cbus->scl_rate.i2c)
i3cbus->scl_rate.i3c = i3cbus->scl_rate.i2c;
break;
default:
return -EINVAL;
}

dev_dbg(&master->dev, "i2c-scl = %ld Hz i3c-scl = %ld Hz\n",
i3cbus->scl_rate.i2c, i3cbus->scl_rate.i3c);

/*
* I3C/I2C frequency may have been overridden, check that user-provided
* values are not exceeding max possible frequency.
Expand Down Expand Up @@ -1976,9 +2000,6 @@ of_i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master,
/* LVR is encoded in reg[2]. */
boardinfo->lvr = reg[2];

if (boardinfo->lvr & I3C_LVR_I2C_FM_MODE)
master->bus.scl_rate.i2c = I3C_BUS_I2C_FM_SCL_RATE;

list_add_tail(&boardinfo->node, &master->boardinfo.i2c);
of_node_get(node);

Expand Down Expand Up @@ -2424,6 +2445,7 @@ int i3c_master_register(struct i3c_master_controller *master,
const struct i3c_master_controller_ops *ops,
bool secondary)
{
unsigned long i2c_scl_rate = I3C_BUS_I2C_FM_PLUS_SCL_RATE;
struct i3c_bus *i3cbus = i3c_master_get_bus(master);
enum i3c_bus_mode mode = I3C_BUS_MODE_PURE;
struct i2c_dev_boardinfo *i2cbi;
Expand Down Expand Up @@ -2473,9 +2495,12 @@ int i3c_master_register(struct i3c_master_controller *master,
ret = -EINVAL;
goto err_put_dev;
}

if (i2cbi->lvr & I3C_LVR_I2C_FM_MODE)
i2c_scl_rate = I3C_BUS_I2C_FM_SCL_RATE;
}

ret = i3c_bus_set_mode(i3cbus, mode);
ret = i3c_bus_set_mode(i3cbus, mode, i2c_scl_rate);
if (ret)
goto err_put_dev;

Expand Down

0 comments on commit ecc8fb5

Please sign in to comment.