Commit a3f4a07b authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull i3c updates from Alexandre Belloni:
 "We are continuing to see more fixes as hardware is available and code
  is actually getting tested.

  Core:
   - Add a sysfs control for hotjoin
   - Add fallback method for GETMXDS CCC

  Drivers:
   - cdns: fix prescale for i2c clock
   - mipi-i3c-hci: more fixes now that the driver is used
   - svc: hotjoin enabling/disabling support"

* tag 'i3c/for-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux:
  i3c: document hotjoin sysfs entry
  i3c: master: fix kernel-doc check warning
  i3c: master: cdns: Update maximum prescaler value for i2c clock
  i3c: master: fix Excess kernel-doc description warning
  i3c: master: svc: return actual transfer data len
  i3c: master: svc: rename read_len as actual_len
  i3c: add actual_len in i3c_priv_xfer
  i3c: master: svc: add hot join support
  i3c: master: add enable(disable) hot join in sys entry
  i3c: master: Fix build error
  i3c: Add fallback method for GETMXDS CCC
  i3c: mipi-i3c-hci: Add DMA bounce buffer for private transfers
  i3c: mipi-i3c-hci: Handle I3C address header error in hci_cmd_v1_daa()
  i3c: mipi-i3c-hci: Do not overallocate transfers in hci_cmd_v1_daa()
  i3c: mipi-i3c-hci: Report NACK response from CCC command to core
parents ed6c23b1 4fa0888f
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -88,6 +88,21 @@ Description:
		This entry describes the HDRCAP of the master controller
		driving the bus.

What:		/sys/bus/i3c/devices/i3c-<bus-id>/hotjoin
KernelVersion:  6.8
Contact:	linux-i3c@vger.kernel.org
Description:
		I3C’s Hot-Join mechanism allows an I3C Device to inform the
		Active Controller that a newly-joined Target is present on the
		I3C Bus and is ready to receive a Dynamic Address, in order to
		become fully functional on the Bus. Hot-Join is used when the
		Target is mounted on the same I3C bus and remains depowered
		until needed or until the Target is physically inserted into the
		I3C bus

		This entry allows to enable or disable Hot-join of the Current
		Controller driving the bus.

What:		/sys/bus/i3c/devices/i3c-<bus-id>/<bus-id>-<device-pid>
KernelVersion:  5.0
Contact:	linux-i3c@vger.kernel.org
+93 −2
Original line number Diff line number Diff line
@@ -557,6 +557,88 @@ static ssize_t i2c_scl_frequency_show(struct device *dev,
}
static DEVICE_ATTR_RO(i2c_scl_frequency);

static int i3c_set_hotjoin(struct i3c_master_controller *master, bool enable)
{
	int ret;

	if (!master || !master->ops)
		return -EINVAL;

	if (!master->ops->enable_hotjoin || !master->ops->disable_hotjoin)
		return -EINVAL;

	i3c_bus_normaluse_lock(&master->bus);

	if (enable)
		ret = master->ops->enable_hotjoin(master);
	else
		ret = master->ops->disable_hotjoin(master);

	master->hotjoin = enable;

	i3c_bus_normaluse_unlock(&master->bus);

	return ret;
}

static ssize_t hotjoin_store(struct device *dev, struct device_attribute *attr,
			     const char *buf, size_t count)
{
	struct i3c_bus *i3cbus = dev_to_i3cbus(dev);
	int ret;
	bool res;

	if (!i3cbus->cur_master)
		return -EINVAL;

	if (kstrtobool(buf, &res))
		return -EINVAL;

	ret = i3c_set_hotjoin(i3cbus->cur_master->common.master, res);
	if (ret)
		return ret;

	return count;
}

/*
 * i3c_master_enable_hotjoin - Enable hotjoin
 * @master: I3C master object
 *
 * Return: a 0 in case of success, an negative error code otherwise.
 */
int i3c_master_enable_hotjoin(struct i3c_master_controller *master)
{
	return i3c_set_hotjoin(master, true);
}
EXPORT_SYMBOL_GPL(i3c_master_enable_hotjoin);

/*
 * i3c_master_disable_hotjoin - Disable hotjoin
 * @master: I3C master object
 *
 * Return: a 0 in case of success, an negative error code otherwise.
 */
int i3c_master_disable_hotjoin(struct i3c_master_controller *master)
{
	return i3c_set_hotjoin(master, false);
}
EXPORT_SYMBOL_GPL(i3c_master_disable_hotjoin);

static ssize_t hotjoin_show(struct device *dev, struct device_attribute *da, char *buf)
{
	struct i3c_bus *i3cbus = dev_to_i3cbus(dev);
	ssize_t ret;

	i3c_bus_normaluse_lock(i3cbus);
	ret = sysfs_emit(buf, "%d\n", i3cbus->cur_master->common.master->hotjoin);
	i3c_bus_normaluse_unlock(i3cbus);

	return ret;
}

static DEVICE_ATTR_RW(hotjoin);

static struct attribute *i3c_masterdev_attrs[] = {
	&dev_attr_mode.attr,
	&dev_attr_current_master.attr,
@@ -567,6 +649,7 @@ static struct attribute *i3c_masterdev_attrs[] = {
	&dev_attr_pid.attr,
	&dev_attr_dynamic_address.attr,
	&dev_attr_hdrcap.attr,
	&dev_attr_hotjoin.attr,
	NULL,
};
ATTRIBUTE_GROUPS(i3c_masterdev);
@@ -1130,8 +1213,16 @@ static int i3c_master_getmxds_locked(struct i3c_master_controller *master,

	i3c_ccc_cmd_init(&cmd, true, I3C_CCC_GETMXDS, &dest, 1);
	ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
	if (ret) {
		/*
		 * Retry when the device does not support max read turnaround
		 * while expecting shorter length from this CCC command.
		 */
		dest.payload.len -= 3;
		ret = i3c_master_send_ccc_cmd_locked(master, &cmd);
		if (ret)
			goto out;
	}

	if (dest.payload.len != 2 && dest.payload.len != 5) {
		ret = -EIO;
+4 −3
Original line number Diff line number Diff line
@@ -76,7 +76,8 @@
#define PRESCL_CTRL0			0x14
#define PRESCL_CTRL0_I2C(x)		((x) << 16)
#define PRESCL_CTRL0_I3C(x)		(x)
#define PRESCL_CTRL0_MAX		GENMASK(9, 0)
#define PRESCL_CTRL0_I3C_MAX		GENMASK(9, 0)
#define PRESCL_CTRL0_I2C_MAX		GENMASK(15, 0)

#define PRESCL_CTRL1			0x18
#define PRESCL_CTRL1_PP_LOW_MASK	GENMASK(15, 8)
@@ -1233,7 +1234,7 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
		return -EINVAL;

	pres = DIV_ROUND_UP(sysclk_rate, (bus->scl_rate.i3c * 4)) - 1;
	if (pres > PRESCL_CTRL0_MAX)
	if (pres > PRESCL_CTRL0_I3C_MAX)
		return -ERANGE;

	bus->scl_rate.i3c = sysclk_rate / ((pres + 1) * 4);
@@ -1246,7 +1247,7 @@ static int cdns_i3c_master_bus_init(struct i3c_master_controller *m)
	max_i2cfreq = bus->scl_rate.i2c;

	pres = (sysclk_rate / (max_i2cfreq * 5)) - 1;
	if (pres > PRESCL_CTRL0_MAX)
	if (pres > PRESCL_CTRL0_I2C_MAX)
		return -ERANGE;

	bus->scl_rate.i2c = sysclk_rate / ((pres + 1) * 5);
+4 −3
Original line number Diff line number Diff line
@@ -298,7 +298,7 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci)
	unsigned int dcr, bcr;
	DECLARE_COMPLETION_ONSTACK(done);

	xfer = hci_alloc_xfer(2);
	xfer = hci_alloc_xfer(1);
	if (!xfer)
		return -ENOMEM;

@@ -339,12 +339,13 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci)
			ret = -ETIME;
			break;
		}
		if (RESP_STATUS(xfer[0].response) == RESP_ERR_NACK &&
		if ((RESP_STATUS(xfer->response) == RESP_ERR_ADDR_HEADER ||
		     RESP_STATUS(xfer->response) == RESP_ERR_NACK) &&
		    RESP_DATA_LENGTH(xfer->response) == 1) {
			ret = 0;  /* no more devices to be assigned */
			break;
		}
		if (RESP_STATUS(xfer[0].response) != RESP_SUCCESS) {
		if (RESP_STATUS(xfer->response) != RESP_SUCCESS) {
			ret = -EIO;
			break;
		}
+48 −1
Original line number Diff line number Diff line
@@ -245,7 +245,14 @@ static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m,
		if (ccc->rnw)
			ccc->dests[i - prefixed].payload.len =
				RESP_DATA_LENGTH(xfer[i].response);
		if (RESP_STATUS(xfer[i].response) != RESP_SUCCESS) {
		switch (RESP_STATUS(xfer[i].response)) {
		case RESP_SUCCESS:
			continue;
		case RESP_ERR_ADDR_HEADER:
		case RESP_ERR_NACK:
			ccc->err = I3C_ERROR_M2;
			fallthrough;
		default:
			ret = -EIO;
			goto out;
		}
@@ -269,6 +276,34 @@ static int i3c_hci_daa(struct i3c_master_controller *m)
	return hci->cmd->perform_daa(hci);
}

static int i3c_hci_alloc_safe_xfer_buf(struct i3c_hci *hci,
				       struct hci_xfer *xfer)
{
	if (hci->io != &mipi_i3c_hci_dma ||
	    xfer->data == NULL || !is_vmalloc_addr(xfer->data))
		return 0;

	if (xfer->rnw)
		xfer->bounce_buf = kzalloc(xfer->data_len, GFP_KERNEL);
	else
		xfer->bounce_buf = kmemdup(xfer->data,
					   xfer->data_len, GFP_KERNEL);

	return xfer->bounce_buf == NULL ? -ENOMEM : 0;
}

static void i3c_hci_free_safe_xfer_buf(struct i3c_hci *hci,
				       struct hci_xfer *xfer)
{
	if (hci->io != &mipi_i3c_hci_dma || xfer->bounce_buf == NULL)
		return;

	if (xfer->rnw)
		memcpy(xfer->data, xfer->bounce_buf, xfer->data_len);

	kfree(xfer->bounce_buf);
}

static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev,
			      struct i3c_priv_xfer *i3c_xfers,
			      int nxfers)
@@ -302,6 +337,9 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev,
		}
		hci->cmd->prep_i3c_xfer(hci, dev, &xfer[i]);
		xfer[i].cmd_desc[0] |= CMD_0_ROC;
		ret = i3c_hci_alloc_safe_xfer_buf(hci, &xfer[i]);
		if (ret)
			goto out;
	}
	last = i - 1;
	xfer[last].cmd_desc[0] |= CMD_0_TOC;
@@ -325,6 +363,9 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev,
	}

out:
	for (i = 0; i < nxfers; i++)
		i3c_hci_free_safe_xfer_buf(hci, &xfer[i]);

	hci_free_xfer(xfer, nxfers);
	return ret;
}
@@ -350,6 +391,9 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
		xfer[i].rnw = i2c_xfers[i].flags & I2C_M_RD;
		hci->cmd->prep_i2c_xfer(hci, dev, &xfer[i]);
		xfer[i].cmd_desc[0] |= CMD_0_ROC;
		ret = i3c_hci_alloc_safe_xfer_buf(hci, &xfer[i]);
		if (ret)
			goto out;
	}
	last = i - 1;
	xfer[last].cmd_desc[0] |= CMD_0_TOC;
@@ -371,6 +415,9 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
	}

out:
	for (i = 0; i < nxfers; i++)
		i3c_hci_free_safe_xfer_buf(hci, &xfer[i]);

	hci_free_xfer(xfer, nxfers);
	return ret;
}
Loading