mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-11-23 18:14:04 +08:00
I3C for 6.7
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 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmWkbYoACgkQY6TcMGxw OjKuNg//acb3t875RGkDIAcxcxfM8TLwsvo7xYSglQxBRfFqKuj+SzqeCg1szO6S x1aoiCNFKMTgwrJuGFuVCawW2PYqZTcSdQnpdli3HzI9bKNc/RxojDD9R1dztZKh nhOtycZM1ScnJ0SgbXAYakb+IcL2L3K2O5DjdtKBXdfTR74RvjWfxYB3LxXjvEEf CJZ5lapcTuJZVCZBB/KQoshZmZYjA9kySgcqFomQ+ShlYRXbMOW6VCceU5HsDy07 7bfs7caGKu1VwUbDmiV3lvbZUkPiGjspStc5zYQtktORCGh6sP6uBZjB/m3vKaJ2 IsnkqIOXHmh3an8MeGVqWSvR54FzfdXSXW6Xr+0XKvoT8rBc4d8c80HO3Kw3t5Rv 4y/ygYaxFxfcCfhk031ftjaux4DLMFvlGV3pa+OGHdxGUYeFawYDsHZtweMspgBx WRvbOY4wc00zrwnni9L8BqpVwIfAvwkwwMvfgJS4mA+h/4YrbXp6QR/iM2ij2zHl azVDVrSOGLIiPxgqqpGWg09hkrkYWuEbhs61ywQJoeDSaGpdxOKC5VBB0ygwIpfY bsSqKhIjOCAkmRF02XX/atNKu+RNeg1N/F9mDCVhOzhPqNjIPlhvLhEGEuNLwEvK DrVBEWyJlD53S3KBm4Fba04+JngJXkxbIQxYWnYLbEBafU2dVjo= =mysu -----END PGP SIGNATURE----- Merge tag 'i3c/for-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux 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
This commit is contained in:
commit
a3f4a07b50
@ -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
|
||||
|
@ -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)
|
||||
goto out;
|
||||
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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -362,6 +362,7 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
|
||||
struct hci_rh_data *rh;
|
||||
unsigned int i, ring, enqueue_ptr;
|
||||
u32 op1_val, op2_val;
|
||||
void *buf;
|
||||
|
||||
/* For now we only use ring 0 */
|
||||
ring = 0;
|
||||
@ -390,9 +391,10 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
|
||||
|
||||
/* 2nd and 3rd words of Data Buffer Descriptor Structure */
|
||||
if (xfer->data) {
|
||||
buf = xfer->bounce_buf ? xfer->bounce_buf : xfer->data;
|
||||
xfer->data_dma =
|
||||
dma_map_single(&hci->master.dev,
|
||||
xfer->data,
|
||||
buf,
|
||||
xfer->data_len,
|
||||
xfer->rnw ?
|
||||
DMA_FROM_DEVICE :
|
||||
|
@ -90,6 +90,7 @@ struct hci_xfer {
|
||||
struct {
|
||||
/* DMA specific */
|
||||
dma_addr_t data_dma;
|
||||
void *bounce_buf;
|
||||
int ring_number;
|
||||
int ring_entry;
|
||||
};
|
||||
|
@ -128,13 +128,17 @@
|
||||
/* This parameter depends on the implementation and may be tuned */
|
||||
#define SVC_I3C_FIFO_SIZE 16
|
||||
|
||||
#define SVC_I3C_EVENT_IBI BIT(0)
|
||||
#define SVC_I3C_EVENT_HOTJOIN BIT(1)
|
||||
|
||||
struct svc_i3c_cmd {
|
||||
u8 addr;
|
||||
bool rnw;
|
||||
u8 *in;
|
||||
const void *out;
|
||||
unsigned int len;
|
||||
unsigned int read_len;
|
||||
unsigned int actual_len;
|
||||
struct i3c_priv_xfer *xfer;
|
||||
bool continued;
|
||||
};
|
||||
|
||||
@ -177,6 +181,7 @@ struct svc_i3c_regs_save {
|
||||
* @ibi.tbq_slot: To be queued IBI slot
|
||||
* @ibi.lock: IBI lock
|
||||
* @lock: Transfer lock, protect between IBI work thread and callbacks from master
|
||||
* @enabled_events: Bit masks for enable events (IBI, HotJoin).
|
||||
*/
|
||||
struct svc_i3c_master {
|
||||
struct i3c_master_controller base;
|
||||
@ -206,6 +211,7 @@ struct svc_i3c_master {
|
||||
spinlock_t lock;
|
||||
} ibi;
|
||||
struct mutex lock;
|
||||
int enabled_events;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -220,6 +226,11 @@ struct svc_i3c_i2c_dev_data {
|
||||
struct i3c_generic_ibi_pool *ibi_pool;
|
||||
};
|
||||
|
||||
static inline bool is_events_enabled(struct svc_i3c_master *master, u32 mask)
|
||||
{
|
||||
return !!(master->enabled_events & mask);
|
||||
}
|
||||
|
||||
static bool svc_i3c_master_error(struct svc_i3c_master *master)
|
||||
{
|
||||
u32 mstatus, merrwarn;
|
||||
@ -429,13 +440,16 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
|
||||
switch (ibitype) {
|
||||
case SVC_I3C_MSTATUS_IBITYPE_IBI:
|
||||
dev = svc_i3c_master_dev_from_addr(master, ibiaddr);
|
||||
if (!dev)
|
||||
if (!dev || !is_events_enabled(master, SVC_I3C_EVENT_IBI))
|
||||
svc_i3c_master_nack_ibi(master);
|
||||
else
|
||||
svc_i3c_master_handle_ibi(master, dev);
|
||||
break;
|
||||
case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
|
||||
svc_i3c_master_ack_ibi(master, false);
|
||||
if (is_events_enabled(master, SVC_I3C_EVENT_HOTJOIN))
|
||||
svc_i3c_master_ack_ibi(master, false);
|
||||
else
|
||||
svc_i3c_master_nack_ibi(master);
|
||||
break;
|
||||
case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
|
||||
svc_i3c_master_nack_ibi(master);
|
||||
@ -472,7 +486,9 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
|
||||
svc_i3c_master_emit_stop(master);
|
||||
break;
|
||||
case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
|
||||
queue_work(master->base.wq, &master->hj_work);
|
||||
svc_i3c_master_emit_stop(master);
|
||||
if (is_events_enabled(master, SVC_I3C_EVENT_HOTJOIN))
|
||||
queue_work(master->base.wq, &master->hj_work);
|
||||
break;
|
||||
case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
|
||||
default:
|
||||
@ -1024,7 +1040,7 @@ static int svc_i3c_master_write(struct svc_i3c_master *master,
|
||||
static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
||||
bool rnw, unsigned int xfer_type, u8 addr,
|
||||
u8 *in, const u8 *out, unsigned int xfer_len,
|
||||
unsigned int *read_len, bool continued)
|
||||
unsigned int *actual_len, bool continued)
|
||||
{
|
||||
u32 reg;
|
||||
int ret;
|
||||
@ -1037,7 +1053,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
||||
SVC_I3C_MCTRL_IBIRESP_NACK |
|
||||
SVC_I3C_MCTRL_DIR(rnw) |
|
||||
SVC_I3C_MCTRL_ADDR(addr) |
|
||||
SVC_I3C_MCTRL_RDTERM(*read_len),
|
||||
SVC_I3C_MCTRL_RDTERM(*actual_len),
|
||||
master->regs + SVC_I3C_MCTRL);
|
||||
|
||||
ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
|
||||
@ -1047,6 +1063,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
||||
|
||||
if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) {
|
||||
ret = -ENXIO;
|
||||
*actual_len = 0;
|
||||
goto emit_stop;
|
||||
}
|
||||
|
||||
@ -1064,6 +1081,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
||||
*/
|
||||
if (SVC_I3C_MSTATUS_IBIWON(reg)) {
|
||||
ret = -ENXIO;
|
||||
*actual_len = 0;
|
||||
goto emit_stop;
|
||||
}
|
||||
|
||||
@ -1075,7 +1093,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
||||
goto emit_stop;
|
||||
|
||||
if (rnw)
|
||||
*read_len = ret;
|
||||
*actual_len = ret;
|
||||
|
||||
ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
|
||||
SVC_I3C_MSTATUS_COMPLETE(reg), 0, 1000);
|
||||
@ -1157,8 +1175,12 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
|
||||
|
||||
ret = svc_i3c_master_xfer(master, cmd->rnw, xfer->type,
|
||||
cmd->addr, cmd->in, cmd->out,
|
||||
cmd->len, &cmd->read_len,
|
||||
cmd->len, &cmd->actual_len,
|
||||
cmd->continued);
|
||||
/* cmd->xfer is NULL if I2C or CCC transfer */
|
||||
if (cmd->xfer)
|
||||
cmd->xfer->actual_len = cmd->actual_len;
|
||||
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
@ -1243,7 +1265,7 @@ static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master,
|
||||
cmd->in = NULL;
|
||||
cmd->out = buf;
|
||||
cmd->len = xfer_len;
|
||||
cmd->read_len = 0;
|
||||
cmd->actual_len = 0;
|
||||
cmd->continued = false;
|
||||
|
||||
mutex_lock(&master->lock);
|
||||
@ -1263,7 +1285,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
|
||||
struct i3c_ccc_cmd *ccc)
|
||||
{
|
||||
unsigned int xfer_len = ccc->dests[0].payload.len;
|
||||
unsigned int read_len = ccc->rnw ? xfer_len : 0;
|
||||
unsigned int actual_len = ccc->rnw ? xfer_len : 0;
|
||||
struct svc_i3c_xfer *xfer;
|
||||
struct svc_i3c_cmd *cmd;
|
||||
int ret;
|
||||
@ -1281,7 +1303,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
|
||||
cmd->in = NULL;
|
||||
cmd->out = &ccc->id;
|
||||
cmd->len = 1;
|
||||
cmd->read_len = 0;
|
||||
cmd->actual_len = 0;
|
||||
cmd->continued = true;
|
||||
|
||||
/* Directed message */
|
||||
@ -1291,7 +1313,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
|
||||
cmd->in = ccc->rnw ? ccc->dests[0].payload.data : NULL;
|
||||
cmd->out = ccc->rnw ? NULL : ccc->dests[0].payload.data,
|
||||
cmd->len = xfer_len;
|
||||
cmd->read_len = read_len;
|
||||
cmd->actual_len = actual_len;
|
||||
cmd->continued = false;
|
||||
|
||||
mutex_lock(&master->lock);
|
||||
@ -1300,8 +1322,8 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
|
||||
svc_i3c_master_dequeue_xfer(master, xfer);
|
||||
mutex_unlock(&master->lock);
|
||||
|
||||
if (cmd->read_len != xfer_len)
|
||||
ccc->dests[0].payload.len = cmd->read_len;
|
||||
if (cmd->actual_len != xfer_len)
|
||||
ccc->dests[0].payload.len = cmd->actual_len;
|
||||
|
||||
ret = xfer->ret;
|
||||
svc_i3c_master_free_xfer(xfer);
|
||||
@ -1346,12 +1368,13 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
|
||||
for (i = 0; i < nxfers; i++) {
|
||||
struct svc_i3c_cmd *cmd = &xfer->cmds[i];
|
||||
|
||||
cmd->xfer = &xfers[i];
|
||||
cmd->addr = master->addrs[data->index];
|
||||
cmd->rnw = xfers[i].rnw;
|
||||
cmd->in = xfers[i].rnw ? xfers[i].data.in : NULL;
|
||||
cmd->out = xfers[i].rnw ? NULL : xfers[i].data.out;
|
||||
cmd->len = xfers[i].len;
|
||||
cmd->read_len = xfers[i].rnw ? xfers[i].len : 0;
|
||||
cmd->actual_len = xfers[i].rnw ? xfers[i].len : 0;
|
||||
cmd->continued = (i + 1) < nxfers;
|
||||
}
|
||||
|
||||
@ -1391,7 +1414,7 @@ static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
|
||||
cmd->in = cmd->rnw ? xfers[i].buf : NULL;
|
||||
cmd->out = cmd->rnw ? NULL : xfers[i].buf;
|
||||
cmd->len = xfers[i].len;
|
||||
cmd->read_len = cmd->rnw ? xfers[i].len : 0;
|
||||
cmd->actual_len = cmd->rnw ? xfers[i].len : 0;
|
||||
cmd->continued = (i + 1 < nxfers);
|
||||
}
|
||||
|
||||
@ -1472,6 +1495,7 @@ static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
master->enabled_events |= SVC_I3C_EVENT_IBI;
|
||||
svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
|
||||
|
||||
return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
|
||||
@ -1483,7 +1507,9 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
|
||||
struct svc_i3c_master *master = to_svc_i3c_master(m);
|
||||
int ret;
|
||||
|
||||
svc_i3c_master_disable_interrupts(master);
|
||||
master->enabled_events &= ~SVC_I3C_EVENT_IBI;
|
||||
if (!master->enabled_events)
|
||||
svc_i3c_master_disable_interrupts(master);
|
||||
|
||||
ret = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
|
||||
|
||||
@ -1493,6 +1519,39 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int svc_i3c_master_enable_hotjoin(struct i3c_master_controller *m)
|
||||
{
|
||||
struct svc_i3c_master *master = to_svc_i3c_master(m);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(master->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
master->enabled_events |= SVC_I3C_EVENT_HOTJOIN;
|
||||
|
||||
svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int svc_i3c_master_disable_hotjoin(struct i3c_master_controller *m)
|
||||
{
|
||||
struct svc_i3c_master *master = to_svc_i3c_master(m);
|
||||
|
||||
master->enabled_events &= ~SVC_I3C_EVENT_HOTJOIN;
|
||||
|
||||
if (!master->enabled_events)
|
||||
svc_i3c_master_disable_interrupts(master);
|
||||
|
||||
pm_runtime_mark_last_busy(master->dev);
|
||||
pm_runtime_put_autosuspend(master->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void svc_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
|
||||
struct i3c_ibi_slot *slot)
|
||||
{
|
||||
@ -1519,6 +1578,8 @@ static const struct i3c_master_controller_ops svc_i3c_master_ops = {
|
||||
.recycle_ibi_slot = svc_i3c_master_recycle_ibi_slot,
|
||||
.enable_ibi = svc_i3c_master_enable_ibi,
|
||||
.disable_ibi = svc_i3c_master_disable_ibi,
|
||||
.enable_hotjoin = svc_i3c_master_enable_hotjoin,
|
||||
.disable_hotjoin = svc_i3c_master_disable_hotjoin,
|
||||
};
|
||||
|
||||
static int svc_i3c_master_prepare_clks(struct svc_i3c_master *master)
|
||||
|
@ -54,6 +54,7 @@ enum i3c_hdr_mode {
|
||||
* struct i3c_priv_xfer - I3C SDR private transfer
|
||||
* @rnw: encodes the transfer direction. true for a read, false for a write
|
||||
* @len: transfer length in bytes of the transfer
|
||||
* @actual_len: actual length in bytes are transferred by the controller
|
||||
* @data: input/output buffer
|
||||
* @data.in: input buffer. Must point to a DMA-able buffer
|
||||
* @data.out: output buffer. Must point to a DMA-able buffer
|
||||
@ -62,6 +63,7 @@ enum i3c_hdr_mode {
|
||||
struct i3c_priv_xfer {
|
||||
u8 rnw;
|
||||
u16 len;
|
||||
u16 actual_len;
|
||||
union {
|
||||
void *in;
|
||||
const void *out;
|
||||
|
@ -76,7 +76,6 @@ struct i2c_dev_boardinfo {
|
||||
/**
|
||||
* struct i2c_dev_desc - I2C device descriptor
|
||||
* @common: common part of the I2C device descriptor
|
||||
* @boardinfo: pointer to the boardinfo attached to this I2C device
|
||||
* @dev: I2C device object registered to the I2C framework
|
||||
* @addr: I2C device address
|
||||
* @lvr: LVR (Legacy Virtual Register) needed by the I3C core to know about
|
||||
@ -434,6 +433,8 @@ struct i3c_bus {
|
||||
* for a future IBI
|
||||
* This method is mandatory only if ->request_ibi is not
|
||||
* NULL.
|
||||
* @enable_hotjoin: enable hot join event detect.
|
||||
* @disable_hotjoin: disable hot join event detect.
|
||||
*/
|
||||
struct i3c_master_controller_ops {
|
||||
int (*bus_init)(struct i3c_master_controller *master);
|
||||
@ -460,6 +461,8 @@ struct i3c_master_controller_ops {
|
||||
int (*disable_ibi)(struct i3c_dev_desc *dev);
|
||||
void (*recycle_ibi_slot)(struct i3c_dev_desc *dev,
|
||||
struct i3c_ibi_slot *slot);
|
||||
int (*enable_hotjoin)(struct i3c_master_controller *master);
|
||||
int (*disable_hotjoin)(struct i3c_master_controller *master);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -473,6 +476,7 @@ struct i3c_master_controller_ops {
|
||||
* @ops: master operations. See &struct i3c_master_controller_ops
|
||||
* @secondary: true if the master is a secondary master
|
||||
* @init_done: true when the bus initialization is done
|
||||
* @hotjoin: true if the master support hotjoin
|
||||
* @boardinfo.i3c: list of I3C boardinfo objects
|
||||
* @boardinfo.i2c: list of I2C boardinfo objects
|
||||
* @boardinfo: board-level information attached to devices connected on the bus
|
||||
@ -495,6 +499,7 @@ struct i3c_master_controller {
|
||||
const struct i3c_master_controller_ops *ops;
|
||||
unsigned int secondary : 1;
|
||||
unsigned int init_done : 1;
|
||||
unsigned int hotjoin: 1;
|
||||
struct {
|
||||
struct list_head i3c;
|
||||
struct list_head i2c;
|
||||
@ -551,6 +556,8 @@ int i3c_master_register(struct i3c_master_controller *master,
|
||||
const struct i3c_master_controller_ops *ops,
|
||||
bool secondary);
|
||||
void i3c_master_unregister(struct i3c_master_controller *master);
|
||||
int i3c_master_enable_hotjoin(struct i3c_master_controller *master);
|
||||
int i3c_master_disable_hotjoin(struct i3c_master_controller *master);
|
||||
|
||||
/**
|
||||
* i3c_dev_get_master_data() - get master private data attached to an I3C
|
||||
|
Loading…
Reference in New Issue
Block a user