mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-29 09:13:38 +00:00
I3C for 6.13
Core: - avoid possible deadlock on probe - ensured preferred address is used on hot-join Drivers: - dw: add AMD I3C controller support - mipi-i3c-hci: fix SETDASA, DMA interrupts fixes - svc: many fixes for IBI and hotjoin -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmdE9XkACgkQY6TcMGxw OjJemA//RXLJuJgTsOSnOIEOg/DrHMg1tY7QiuqWagp68i6IuV+a0m0Os0k3Ak0h EiZ5U8NkXAtAsf1dmA8CjZ5BnFswIJAm7rJI20vvFmaoNAQmqx1DQ5J2aSHm+pFK LZYyM/p3dbNsdDu4MNSiFHwoFi/ZubTQWaqhpdxE6cCKVhRvSUznL51M4dpXEbcE wgbAl+ivhqpiHbqBvfu2KDiZOi+Bb9Dn1voy4PZivuDI+C3SM3mB8K088GigFlkn Q1zMF4I7UdfAffF1Aj1e5YRn+hJcBEu1+UCf+Jj2W7Y2I9r8KQyBzxH0Xt9MVsua WoHT5np/Zc0pw4UykwMmg+qroOM6cXHgPnAq8EYmkY6Ab6XWEPLPY/VIPIn+tq7Y RQs90RKJUKQT8kV07taQ2KUgBSPsl/8iING12xNjfDQjjOY4ebhE3Ou9EIOec1dG rF3+5G8dTSyJfV+5PcgeGFGSIMAqKNRrRUNEfANE2uIh9VS7wcKUemto96uPOySR Ct5GhE21Xrdm+ZievHOTjLvlaSbDv5RU5qFezf15sD8ypF2RrEVE2mZj9bdynBth Qp6I5NDfjF0fW1dRGB44ZAWS82KWia+1D67I3Sy0R/VG5wax1OtQktmC6fA1iqKX wZD/d+zh5zs10wyTjmvEoN2UCoLlzXN/VvA+HQllOcDara9V9Jw= =B7J8 -----END PGP SIGNATURE----- Merge tag 'i3c/for-6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux Pull i3c updates from Alexandre Belloni: "Core: - avoid possible deadlock on probe - ensured preferred address is used on hot-join Drivers: - dw: add AMD I3C controller support - mipi-i3c-hci: fix SETDASA, DMA interrupts fixes - svc: many fixes for IBI and hotjoin" * tag 'i3c/for-6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux: i3c: Use i3cdev->desc->info instead of calling i3c_device_get_info() to avoid deadlock i3c: mipi-i3c-hci: Support SETDASA CCC i3c: dw: Add quirk to address OD/PP timing issue on AMD platform i3c: dw: Add support for AMDI0015 ACPI ID i3c: master: svc: Modify enabled_events bit 7:0 to act as IBI enable counter i3c: Document I3C_ADDR_SLOT_EXT_STATUS_MASK i3c: master: svc: Fix pm_runtime_set_suspended() with runtime pm enabled i3c: mipi-i3c-hci: Handle interrupts according to current specifications i3c: mipi-i3c-hci: Mask ring interrupts before ring stop request i3c: master: Fix miss free init_dyn_addr at i3c_master_put_i3c_addrs() i3c: master: Remove i3c_dev_disable_ibi_locked(olddev) on device hotjoin i3c: master: svc: fix possible assignment of the same address to two devices i3c: master: svc: wait for Manual ACK/NACK Done before next step i3c: master: svc: use spin_lock_irqsave at svc_i3c_master_ibi_work() i3c: master: svc: need check IBIWON for dynamic address assignment i3c: master: svc: manually emit NACK/ACK for hotjoin i3c: master: svc: use repeat start when IBI WIN happens i3c: master: Fix dynamic address leak when 'assigned-address' is present i3c: master: Extend address status bit to 4 and add I3C_ADDR_SLOT_EXT_DESIRED i3c: master: Replace hard code 2 with macro I3C_ADDR_SLOT_STATUS_BITS
This commit is contained in:
commit
750909d55a
@ -282,7 +282,8 @@ static int i3c_device_uevent(const struct device *dev, struct kobj_uevent_env *e
|
||||
struct i3c_device_info devinfo;
|
||||
u16 manuf, part, ext;
|
||||
|
||||
i3c_device_get_info(i3cdev, &devinfo);
|
||||
if (i3cdev->desc)
|
||||
devinfo = i3cdev->desc->info;
|
||||
manuf = I3C_PID_MANUF_ID(devinfo.pid);
|
||||
part = I3C_PID_PART_ID(devinfo.pid);
|
||||
ext = I3C_PID_EXTRA_INFO(devinfo.pid);
|
||||
@ -345,10 +346,10 @@ const struct bus_type i3c_bus_type = {
|
||||
EXPORT_SYMBOL_GPL(i3c_bus_type);
|
||||
|
||||
static enum i3c_addr_slot_status
|
||||
i3c_bus_get_addr_slot_status(struct i3c_bus *bus, u16 addr)
|
||||
i3c_bus_get_addr_slot_status_mask(struct i3c_bus *bus, u16 addr, u32 mask)
|
||||
{
|
||||
unsigned long status;
|
||||
int bitpos = addr * 2;
|
||||
int bitpos = addr * I3C_ADDR_SLOT_STATUS_BITS;
|
||||
|
||||
if (addr > I2C_MAX_ADDR)
|
||||
return I3C_ADDR_SLOT_RSVD;
|
||||
@ -356,22 +357,33 @@ i3c_bus_get_addr_slot_status(struct i3c_bus *bus, u16 addr)
|
||||
status = bus->addrslots[bitpos / BITS_PER_LONG];
|
||||
status >>= bitpos % BITS_PER_LONG;
|
||||
|
||||
return status & I3C_ADDR_SLOT_STATUS_MASK;
|
||||
return status & mask;
|
||||
}
|
||||
|
||||
static void i3c_bus_set_addr_slot_status(struct i3c_bus *bus, u16 addr,
|
||||
enum i3c_addr_slot_status status)
|
||||
static enum i3c_addr_slot_status
|
||||
i3c_bus_get_addr_slot_status(struct i3c_bus *bus, u16 addr)
|
||||
{
|
||||
int bitpos = addr * 2;
|
||||
return i3c_bus_get_addr_slot_status_mask(bus, addr, I3C_ADDR_SLOT_STATUS_MASK);
|
||||
}
|
||||
|
||||
static void i3c_bus_set_addr_slot_status_mask(struct i3c_bus *bus, u16 addr,
|
||||
enum i3c_addr_slot_status status, u32 mask)
|
||||
{
|
||||
int bitpos = addr * I3C_ADDR_SLOT_STATUS_BITS;
|
||||
unsigned long *ptr;
|
||||
|
||||
if (addr > I2C_MAX_ADDR)
|
||||
return;
|
||||
|
||||
ptr = bus->addrslots + (bitpos / BITS_PER_LONG);
|
||||
*ptr &= ~((unsigned long)I3C_ADDR_SLOT_STATUS_MASK <<
|
||||
(bitpos % BITS_PER_LONG));
|
||||
*ptr |= (unsigned long)status << (bitpos % BITS_PER_LONG);
|
||||
*ptr &= ~((unsigned long)mask << (bitpos % BITS_PER_LONG));
|
||||
*ptr |= ((unsigned long)status & mask) << (bitpos % BITS_PER_LONG);
|
||||
}
|
||||
|
||||
static void i3c_bus_set_addr_slot_status(struct i3c_bus *bus, u16 addr,
|
||||
enum i3c_addr_slot_status status)
|
||||
{
|
||||
i3c_bus_set_addr_slot_status_mask(bus, addr, status, I3C_ADDR_SLOT_STATUS_MASK);
|
||||
}
|
||||
|
||||
static bool i3c_bus_dev_addr_is_avail(struct i3c_bus *bus, u8 addr)
|
||||
@ -383,13 +395,44 @@ static bool i3c_bus_dev_addr_is_avail(struct i3c_bus *bus, u8 addr)
|
||||
return status == I3C_ADDR_SLOT_FREE;
|
||||
}
|
||||
|
||||
/*
|
||||
* ┌────┬─────────────┬───┬─────────┬───┐
|
||||
* │S/Sr│ 7'h7E RnW=0 │ACK│ ENTDAA │ T ├────┐
|
||||
* └────┴─────────────┴───┴─────────┴───┘ │
|
||||
* ┌─────────────────────────────────────────┘
|
||||
* │ ┌──┬─────────────┬───┬─────────────────┬────────────────┬───┬─────────┐
|
||||
* └─►│Sr│7'h7E RnW=1 │ACK│48bit UID BCR DCR│Assign 7bit Addr│PAR│ ACK/NACK│
|
||||
* └──┴─────────────┴───┴─────────────────┴────────────────┴───┴─────────┘
|
||||
* Some master controllers (such as HCI) need to prepare the entire above transaction before
|
||||
* sending it out to the I3C bus. This means that a 7-bit dynamic address needs to be allocated
|
||||
* before knowing the target device's UID information.
|
||||
*
|
||||
* However, some I3C targets may request specific addresses (called as "init_dyn_addr"), which is
|
||||
* typically specified by the DT-'s assigned-address property. Lower addresses having higher IBI
|
||||
* priority. If it is available, i3c_bus_get_free_addr() preferably return a free address that is
|
||||
* not in the list of desired addresses (called as "init_dyn_addr"). This allows the device with
|
||||
* the "init_dyn_addr" to switch to its "init_dyn_addr" when it hot-joins the I3C bus. Otherwise,
|
||||
* if the "init_dyn_addr" is already in use by another I3C device, the target device will not be
|
||||
* able to switch to its desired address.
|
||||
*
|
||||
* If the previous step fails, fallback returning one of the remaining unassigned address,
|
||||
* regardless of its state in the desired list.
|
||||
*/
|
||||
static int i3c_bus_get_free_addr(struct i3c_bus *bus, u8 start_addr)
|
||||
{
|
||||
enum i3c_addr_slot_status status;
|
||||
u8 addr;
|
||||
|
||||
for (addr = start_addr; addr < I3C_MAX_ADDR; addr++) {
|
||||
status = i3c_bus_get_addr_slot_status(bus, addr);
|
||||
status = i3c_bus_get_addr_slot_status_mask(bus, addr,
|
||||
I3C_ADDR_SLOT_EXT_STATUS_MASK);
|
||||
if (status == I3C_ADDR_SLOT_FREE)
|
||||
return addr;
|
||||
}
|
||||
|
||||
for (addr = start_addr; addr < I3C_MAX_ADDR; addr++) {
|
||||
status = i3c_bus_get_addr_slot_status_mask(bus, addr,
|
||||
I3C_ADDR_SLOT_STATUS_MASK);
|
||||
if (status == I3C_ADDR_SLOT_FREE)
|
||||
return addr;
|
||||
}
|
||||
@ -1417,7 +1460,7 @@ static void i3c_master_put_i3c_addrs(struct i3c_dev_desc *dev)
|
||||
I3C_ADDR_SLOT_FREE);
|
||||
|
||||
if (dev->boardinfo && dev->boardinfo->init_dyn_addr)
|
||||
i3c_bus_set_addr_slot_status(&master->bus, dev->info.dyn_addr,
|
||||
i3c_bus_set_addr_slot_status(&master->bus, dev->boardinfo->init_dyn_addr,
|
||||
I3C_ADDR_SLOT_FREE);
|
||||
}
|
||||
|
||||
@ -1506,16 +1549,9 @@ static int i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
|
||||
u8 old_dyn_addr)
|
||||
{
|
||||
struct i3c_master_controller *master = i3c_dev_get_master(dev);
|
||||
enum i3c_addr_slot_status status;
|
||||
int ret;
|
||||
|
||||
if (dev->info.dyn_addr != old_dyn_addr &&
|
||||
(!dev->boardinfo ||
|
||||
dev->info.dyn_addr != dev->boardinfo->init_dyn_addr)) {
|
||||
status = i3c_bus_get_addr_slot_status(&master->bus,
|
||||
dev->info.dyn_addr);
|
||||
if (status != I3C_ADDR_SLOT_FREE)
|
||||
return -EBUSY;
|
||||
if (dev->info.dyn_addr != old_dyn_addr) {
|
||||
i3c_bus_set_addr_slot_status(&master->bus,
|
||||
dev->info.dyn_addr,
|
||||
I3C_ADDR_SLOT_I3C_DEV);
|
||||
@ -1918,9 +1954,11 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
|
||||
goto err_rstdaa;
|
||||
}
|
||||
|
||||
i3c_bus_set_addr_slot_status(&master->bus,
|
||||
i3cboardinfo->init_dyn_addr,
|
||||
I3C_ADDR_SLOT_I3C_DEV);
|
||||
/* Do not mark as occupied until real device exist in bus */
|
||||
i3c_bus_set_addr_slot_status_mask(&master->bus,
|
||||
i3cboardinfo->init_dyn_addr,
|
||||
I3C_ADDR_SLOT_EXT_DESIRED,
|
||||
I3C_ADDR_SLOT_EXT_STATUS_MASK);
|
||||
|
||||
/*
|
||||
* Only try to create/attach devices that have a static
|
||||
@ -2051,11 +2089,16 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
|
||||
ibireq.max_payload_len = olddev->ibi->max_payload_len;
|
||||
ibireq.num_slots = olddev->ibi->num_slots;
|
||||
|
||||
if (olddev->ibi->enabled) {
|
||||
if (olddev->ibi->enabled)
|
||||
enable_ibi = true;
|
||||
i3c_dev_disable_ibi_locked(olddev);
|
||||
}
|
||||
|
||||
/*
|
||||
* The olddev should not receive any commands on the
|
||||
* i3c bus as it does not exist and has been assigned
|
||||
* a new address. This will result in NACK or timeout.
|
||||
* So, update the olddev->ibi->enabled flag to false
|
||||
* to avoid DISEC with OldAddr.
|
||||
*/
|
||||
olddev->ibi->enabled = false;
|
||||
i3c_dev_free_ibi_locked(olddev);
|
||||
}
|
||||
mutex_unlock(&olddev->ibi_lock);
|
||||
@ -2083,7 +2126,8 @@ int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
|
||||
else
|
||||
expected_dyn_addr = newdev->info.dyn_addr;
|
||||
|
||||
if (newdev->info.dyn_addr != expected_dyn_addr) {
|
||||
if (newdev->info.dyn_addr != expected_dyn_addr &&
|
||||
i3c_bus_get_addr_slot_status(&master->bus, expected_dyn_addr) == I3C_ADDR_SLOT_FREE) {
|
||||
/*
|
||||
* Try to apply the expected dynamic address. If it fails, keep
|
||||
* the address assigned by the master.
|
||||
|
@ -220,6 +220,14 @@
|
||||
|
||||
#define XFER_TIMEOUT (msecs_to_jiffies(1000))
|
||||
#define RPM_AUTOSUSPEND_TIMEOUT 1000 /* ms */
|
||||
|
||||
/* Timing values to configure 12.5MHz frequency */
|
||||
#define AMD_I3C_OD_TIMING 0x4C007C
|
||||
#define AMD_I3C_PP_TIMING 0x8001A
|
||||
|
||||
/* List of quirks */
|
||||
#define AMD_I3C_OD_PP_TIMING BIT(1)
|
||||
|
||||
struct dw_i3c_cmd {
|
||||
u32 cmd_lo;
|
||||
u32 cmd_hi;
|
||||
@ -794,6 +802,12 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void amd_configure_od_pp_quirk(struct dw_i3c_master *master)
|
||||
{
|
||||
master->i3c_od_timing = AMD_I3C_OD_TIMING;
|
||||
master->i3c_pp_timing = AMD_I3C_PP_TIMING;
|
||||
}
|
||||
|
||||
static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
|
||||
struct i3c_ccc_cmd *ccc)
|
||||
{
|
||||
@ -803,6 +817,13 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
|
||||
if (ccc->id == I3C_CCC_ENTDAA)
|
||||
return -EINVAL;
|
||||
|
||||
/* AMD platform specific OD and PP timings */
|
||||
if (master->quirks & AMD_I3C_OD_PP_TIMING) {
|
||||
amd_configure_od_pp_quirk(master);
|
||||
writel(master->i3c_pp_timing, master->regs + SCL_I3C_PP_TIMING);
|
||||
writel(master->i3c_od_timing, master->regs + SCL_I3C_OD_TIMING);
|
||||
}
|
||||
|
||||
ret = pm_runtime_resume_and_get(master->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(master->dev,
|
||||
@ -1602,6 +1623,8 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
|
||||
master->maxdevs = ret >> 16;
|
||||
master->free_pos = GENMASK(master->maxdevs - 1, 0);
|
||||
|
||||
master->quirks = (unsigned long)device_get_match_data(&pdev->dev);
|
||||
|
||||
INIT_WORK(&master->hj_work, dw_i3c_hj_work);
|
||||
ret = i3c_master_register(&master->base, &pdev->dev,
|
||||
&dw_mipi_i3c_ops, false);
|
||||
@ -1675,6 +1698,10 @@ static void dw_i3c_master_restore_addrs(struct dw_i3c_master *master)
|
||||
|
||||
static void dw_i3c_master_restore_timing_regs(struct dw_i3c_master *master)
|
||||
{
|
||||
/* AMD platform specific OD and PP timings */
|
||||
if (master->quirks & AMD_I3C_OD_PP_TIMING)
|
||||
amd_configure_od_pp_quirk(master);
|
||||
|
||||
writel(master->i3c_pp_timing, master->regs + SCL_I3C_PP_TIMING);
|
||||
writel(master->bus_free_timing, master->regs + BUS_FREE_TIMING);
|
||||
writel(master->i3c_od_timing, master->regs + SCL_I3C_OD_TIMING);
|
||||
@ -1748,12 +1775,19 @@ static const struct of_device_id dw_i3c_master_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_i3c_master_of_match);
|
||||
|
||||
static const struct acpi_device_id amd_i3c_device_match[] = {
|
||||
{ "AMDI0015", AMD_I3C_OD_PP_TIMING },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, amd_i3c_device_match);
|
||||
|
||||
static struct platform_driver dw_i3c_driver = {
|
||||
.probe = dw_i3c_probe,
|
||||
.remove_new = dw_i3c_remove,
|
||||
.driver = {
|
||||
.name = "dw-i3c-master",
|
||||
.of_match_table = dw_i3c_master_of_match,
|
||||
.acpi_match_table = amd_i3c_device_match,
|
||||
.pm = &dw_i3c_pm_ops,
|
||||
},
|
||||
};
|
||||
|
@ -50,6 +50,7 @@ struct dw_i3c_master {
|
||||
u32 bus_free_timing;
|
||||
u32 i2c_fm_timing;
|
||||
u32 i2c_fmp_timing;
|
||||
u32 quirks;
|
||||
/*
|
||||
* Per-device hardware data, used to manage the device address table
|
||||
* (DAT)
|
||||
|
@ -80,8 +80,6 @@
|
||||
#define INTR_HC_CMD_SEQ_UFLOW_STAT BIT(12) /* Cmd Sequence Underflow */
|
||||
#define INTR_HC_RESET_CANCEL BIT(11) /* HC Cancelled Reset */
|
||||
#define INTR_HC_INTERNAL_ERR BIT(10) /* HC Internal Error */
|
||||
#define INTR_HC_PIO BIT(8) /* cascaded PIO interrupt */
|
||||
#define INTR_HC_RINGS GENMASK(7, 0)
|
||||
|
||||
#define DAT_SECTION 0x30 /* Device Address Table */
|
||||
#define DAT_ENTRY_SIZE GENMASK(31, 28)
|
||||
@ -438,7 +436,8 @@ static int i3c_hci_attach_i3c_dev(struct i3c_dev_desc *dev)
|
||||
kfree(dev_data);
|
||||
return ret;
|
||||
}
|
||||
mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, ret, dev->info.dyn_addr);
|
||||
mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, ret,
|
||||
dev->info.dyn_addr ?: dev->info.static_addr);
|
||||
dev_data->dat_idx = ret;
|
||||
}
|
||||
i3c_dev_set_master_data(dev, dev_data);
|
||||
@ -597,9 +596,6 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
|
||||
|
||||
if (val) {
|
||||
reg_write(INTR_STATUS, val);
|
||||
} else {
|
||||
/* v1.0 does not have PIO cascaded notification bits */
|
||||
val |= INTR_HC_PIO;
|
||||
}
|
||||
|
||||
if (val & INTR_HC_RESET_CANCEL) {
|
||||
@ -610,14 +606,9 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
|
||||
dev_err(&hci->master.dev, "Host Controller Internal Error\n");
|
||||
val &= ~INTR_HC_INTERNAL_ERR;
|
||||
}
|
||||
if (val & INTR_HC_PIO) {
|
||||
hci->io->irq_handler(hci, 0);
|
||||
val &= ~INTR_HC_PIO;
|
||||
}
|
||||
if (val & INTR_HC_RINGS) {
|
||||
hci->io->irq_handler(hci, val & INTR_HC_RINGS);
|
||||
val &= ~INTR_HC_RINGS;
|
||||
}
|
||||
|
||||
hci->io->irq_handler(hci);
|
||||
|
||||
if (val)
|
||||
dev_err(&hci->master.dev, "unexpected INTR_STATUS %#x\n", val);
|
||||
else
|
||||
|
@ -159,10 +159,10 @@ static void hci_dma_cleanup(struct i3c_hci *hci)
|
||||
for (i = 0; i < rings->total; i++) {
|
||||
rh = &rings->headers[i];
|
||||
|
||||
rh_reg_write(INTR_SIGNAL_ENABLE, 0);
|
||||
rh_reg_write(RING_CONTROL, 0);
|
||||
rh_reg_write(CR_SETUP, 0);
|
||||
rh_reg_write(IBI_SETUP, 0);
|
||||
rh_reg_write(INTR_SIGNAL_ENABLE, 0);
|
||||
|
||||
if (rh->xfer)
|
||||
dma_free_coherent(&hci->master.dev,
|
||||
@ -733,20 +733,16 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
|
||||
rh_reg_write(CHUNK_CONTROL, rh_reg_read(CHUNK_CONTROL) + ibi_chunks);
|
||||
}
|
||||
|
||||
static bool hci_dma_irq_handler(struct i3c_hci *hci, unsigned int mask)
|
||||
static bool hci_dma_irq_handler(struct i3c_hci *hci)
|
||||
{
|
||||
struct hci_rings_data *rings = hci->io_data;
|
||||
unsigned int i;
|
||||
bool handled = false;
|
||||
|
||||
for (i = 0; mask && i < rings->total; i++) {
|
||||
for (i = 0; i < rings->total; i++) {
|
||||
struct hci_rh_data *rh;
|
||||
u32 status;
|
||||
|
||||
if (!(mask & BIT(i)))
|
||||
continue;
|
||||
mask &= ~BIT(i);
|
||||
|
||||
rh = &rings->headers[i];
|
||||
status = rh_reg_read(INTR_STATUS);
|
||||
DBG("rh%d status: %#x", i, status);
|
||||
|
@ -115,7 +115,7 @@ static inline void hci_free_xfer(struct hci_xfer *xfer, unsigned int n)
|
||||
|
||||
/* This abstracts PIO vs DMA operations */
|
||||
struct hci_io_ops {
|
||||
bool (*irq_handler)(struct i3c_hci *hci, unsigned int mask);
|
||||
bool (*irq_handler)(struct i3c_hci *hci);
|
||||
int (*queue_xfer)(struct i3c_hci *hci, struct hci_xfer *xfer, int n);
|
||||
bool (*dequeue_xfer)(struct i3c_hci *hci, struct hci_xfer *xfer, int n);
|
||||
int (*request_ibi)(struct i3c_hci *hci, struct i3c_dev_desc *dev,
|
||||
|
@ -979,7 +979,7 @@ static void hci_pio_recycle_ibi_slot(struct i3c_hci *hci,
|
||||
i3c_generic_ibi_recycle_slot(dev_ibi->pool, slot);
|
||||
}
|
||||
|
||||
static bool hci_pio_irq_handler(struct i3c_hci *hci, unsigned int unused)
|
||||
static bool hci_pio_irq_handler(struct i3c_hci *hci)
|
||||
{
|
||||
struct hci_pio_data *pio = hci->io_data;
|
||||
u32 status;
|
||||
|
@ -130,8 +130,8 @@
|
||||
#define SVC_I3C_PPBAUD_MAX 15
|
||||
#define SVC_I3C_QUICK_I2C_CLK 4170000
|
||||
|
||||
#define SVC_I3C_EVENT_IBI BIT(0)
|
||||
#define SVC_I3C_EVENT_HOTJOIN BIT(1)
|
||||
#define SVC_I3C_EVENT_IBI GENMASK(7, 0)
|
||||
#define SVC_I3C_EVENT_HOTJOIN BIT(31)
|
||||
|
||||
struct svc_i3c_cmd {
|
||||
u8 addr;
|
||||
@ -214,7 +214,7 @@ struct svc_i3c_master {
|
||||
spinlock_t lock;
|
||||
} ibi;
|
||||
struct mutex lock;
|
||||
int enabled_events;
|
||||
u32 enabled_events;
|
||||
u32 mctrl_config;
|
||||
};
|
||||
|
||||
@ -388,10 +388,11 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void svc_i3c_master_ack_ibi(struct svc_i3c_master *master,
|
||||
static int svc_i3c_master_ack_ibi(struct svc_i3c_master *master,
|
||||
bool mandatory_byte)
|
||||
{
|
||||
unsigned int ibi_ack_nack;
|
||||
u32 reg;
|
||||
|
||||
ibi_ack_nack = SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK;
|
||||
if (mandatory_byte)
|
||||
@ -400,13 +401,43 @@ static void svc_i3c_master_ack_ibi(struct svc_i3c_master *master,
|
||||
ibi_ack_nack |= SVC_I3C_MCTRL_IBIRESP_ACK_WITHOUT_BYTE;
|
||||
|
||||
writel(ibi_ack_nack, master->regs + SVC_I3C_MCTRL);
|
||||
|
||||
return readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, reg,
|
||||
SVC_I3C_MSTATUS_MCTRLDONE(reg), 1, 1000);
|
||||
|
||||
}
|
||||
|
||||
static void svc_i3c_master_nack_ibi(struct svc_i3c_master *master)
|
||||
static int svc_i3c_master_nack_ibi(struct svc_i3c_master *master)
|
||||
{
|
||||
int ret;
|
||||
u32 reg;
|
||||
|
||||
writel(SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK |
|
||||
SVC_I3C_MCTRL_IBIRESP_NACK,
|
||||
master->regs + SVC_I3C_MCTRL);
|
||||
|
||||
ret = readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, reg,
|
||||
SVC_I3C_MSTATUS_MCTRLDONE(reg), 1, 1000);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int svc_i3c_master_handle_ibi_won(struct svc_i3c_master *master, u32 mstatus)
|
||||
{
|
||||
u32 ibitype;
|
||||
int ret = 0;
|
||||
|
||||
ibitype = SVC_I3C_MSTATUS_IBITYPE(mstatus);
|
||||
|
||||
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
|
||||
|
||||
/* Hardware can't auto emit NACK for hot join and master request */
|
||||
switch (ibitype) {
|
||||
case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
|
||||
case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
|
||||
ret = svc_i3c_master_nack_ibi(master);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void svc_i3c_master_ibi_work(struct work_struct *work)
|
||||
@ -418,7 +449,16 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
|
||||
u32 status, val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&master->lock);
|
||||
/*
|
||||
* According to I3C spec ver 1.1, 09-Jun-2021, section 5.1.2.5:
|
||||
*
|
||||
* The I3C Controller shall hold SCL low while the Bus is in ACK/NACK Phase of I3C/I2C
|
||||
* transfer. But maximum stall time is 100us. The IRQs have to be disabled to prevent
|
||||
* schedule during the whole I3C transaction, otherwise, the I3C bus timeout may happen if
|
||||
* any irq or schedule happen during transaction.
|
||||
*/
|
||||
guard(spinlock_irqsave)(&master->xferqueue.lock);
|
||||
|
||||
/*
|
||||
* IBIWON may be set before SVC_I3C_MCTRL_REQUEST_AUTO_IBI, causing
|
||||
* readl_relaxed_poll_timeout() to return immediately. Consequently,
|
||||
@ -438,8 +478,8 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
|
||||
master->regs + SVC_I3C_MCTRL);
|
||||
|
||||
/* Wait for IBIWON, should take approximately 100us */
|
||||
ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
|
||||
SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
|
||||
ret = readl_relaxed_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, val,
|
||||
SVC_I3C_MSTATUS_IBIWON(val), 0, 100);
|
||||
if (ret) {
|
||||
dev_err(master->dev, "Timeout when polling for IBIWON\n");
|
||||
svc_i3c_master_emit_stop(master);
|
||||
@ -511,7 +551,6 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
|
||||
|
||||
reenable_ibis:
|
||||
svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
|
||||
mutex_unlock(&master->lock);
|
||||
}
|
||||
|
||||
static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
|
||||
@ -854,6 +893,9 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
|
||||
int ret, i;
|
||||
|
||||
while (true) {
|
||||
/* clean SVC_I3C_MINT_IBIWON w1c bits */
|
||||
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
|
||||
|
||||
/* SVC_I3C_MCTRL_REQUEST_PROC_DAA have two mode, ENTER DAA or PROCESS DAA.
|
||||
*
|
||||
* ENTER DAA:
|
||||
@ -905,6 +947,11 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
|
||||
ret = svc_i3c_master_readb(master, data, 2);
|
||||
if (ret)
|
||||
break;
|
||||
} else if (SVC_I3C_MSTATUS_IBIWON(reg)) {
|
||||
ret = svc_i3c_master_handle_ibi_won(master, reg);
|
||||
if (ret)
|
||||
break;
|
||||
continue;
|
||||
} else if (SVC_I3C_MSTATUS_MCTRLDONE(reg)) {
|
||||
if (SVC_I3C_MSTATUS_STATE_IDLE(reg) &&
|
||||
SVC_I3C_MSTATUS_COMPLETE(reg)) {
|
||||
@ -1056,12 +1103,27 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
|
||||
if (ret)
|
||||
goto rpm_out;
|
||||
|
||||
/* Register all devices who participated to the core */
|
||||
for (i = 0; i < dev_nb; i++) {
|
||||
ret = i3c_master_add_i3c_dev_locked(m, addrs[i]);
|
||||
if (ret)
|
||||
goto rpm_out;
|
||||
}
|
||||
/*
|
||||
* Register all devices who participated to the core
|
||||
*
|
||||
* If two devices (A and B) are detected in DAA and address 0xa is assigned to
|
||||
* device A and 0xb to device B, a failure in i3c_master_add_i3c_dev_locked()
|
||||
* for device A (addr: 0xa) could prevent device B (addr: 0xb) from being
|
||||
* registered on the bus. The I3C stack might still consider 0xb a free
|
||||
* address. If a subsequent Hotjoin occurs, 0xb might be assigned to Device A,
|
||||
* causing both devices A and B to use the same address 0xb, violating the I3C
|
||||
* specification.
|
||||
*
|
||||
* The return value for i3c_master_add_i3c_dev_locked() should not be checked
|
||||
* because subsequent steps will scan the entire I3C bus, independent of
|
||||
* whether i3c_master_add_i3c_dev_locked() returns success.
|
||||
*
|
||||
* If device A registration fails, there is still a chance to register device
|
||||
* B. i3c_master_add_i3c_dev_locked() can reset DAA if a failure occurs while
|
||||
* retrieving device information.
|
||||
*/
|
||||
for (i = 0; i < dev_nb; i++)
|
||||
i3c_master_add_i3c_dev_locked(m, addrs[i]);
|
||||
|
||||
/* Configure IBI auto-rules */
|
||||
ret = svc_i3c_update_ibirules(master);
|
||||
@ -1163,6 +1225,26 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
||||
if (ret)
|
||||
goto emit_stop;
|
||||
|
||||
/*
|
||||
* According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a
|
||||
* Frame with I3C Target Address.
|
||||
*
|
||||
* The I3C Controller normally should start a Frame, the Address may be arbitrated,
|
||||
* and so the Controller shall monitor to see whether an In-Band Interrupt request,
|
||||
* a Controller Role Request (i.e., Secondary Controller requests to become the
|
||||
* Active Controller), or a Hot-Join Request has been made.
|
||||
*
|
||||
* If missed IBIWON check, the wrong data will be return. When IBIWON happen, issue
|
||||
* repeat start. Address arbitrate only happen at START, never happen at REPEAT
|
||||
* start.
|
||||
*/
|
||||
if (SVC_I3C_MSTATUS_IBIWON(reg)) {
|
||||
ret = svc_i3c_master_handle_ibi_won(master, reg);
|
||||
if (ret)
|
||||
goto emit_stop;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) {
|
||||
/*
|
||||
* According to I3C Spec 1.1.1, 11-Jun-2021, section: 5.1.2.2.3.
|
||||
@ -1196,24 +1278,6 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a Frame
|
||||
* with I3C Target Address.
|
||||
*
|
||||
* The I3C Controller normally should start a Frame, the Address may be arbitrated, and so
|
||||
* the Controller shall monitor to see whether an In-Band Interrupt request, a Controller
|
||||
* Role Request (i.e., Secondary Controller requests to become the Active Controller), or
|
||||
* a Hot-Join Request has been made.
|
||||
*
|
||||
* If missed IBIWON check, the wrong data will be return. When IBIWON happen, return failure
|
||||
* and yield the above events handler.
|
||||
*/
|
||||
if (SVC_I3C_MSTATUS_IBIWON(reg)) {
|
||||
ret = -EAGAIN;
|
||||
*actual_len = 0;
|
||||
goto emit_stop;
|
||||
}
|
||||
|
||||
if (rnw)
|
||||
ret = svc_i3c_master_read(master, in, xfer_len);
|
||||
else
|
||||
@ -1624,7 +1688,7 @@ static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
master->enabled_events |= SVC_I3C_EVENT_IBI;
|
||||
master->enabled_events++;
|
||||
svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
|
||||
|
||||
return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
|
||||
@ -1636,7 +1700,7 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
|
||||
struct svc_i3c_master *master = to_svc_i3c_master(m);
|
||||
int ret;
|
||||
|
||||
master->enabled_events &= ~SVC_I3C_EVENT_IBI;
|
||||
master->enabled_events--;
|
||||
if (!master->enabled_events)
|
||||
svc_i3c_master_disable_interrupts(master);
|
||||
|
||||
@ -1827,8 +1891,8 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
|
||||
rpm_disable:
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
|
||||
err_disable_clks:
|
||||
svc_i3c_master_unprepare_clks(master);
|
||||
|
@ -298,7 +298,9 @@ enum i3c_open_drain_speed {
|
||||
* @I3C_ADDR_SLOT_I2C_DEV: address is assigned to an I2C device
|
||||
* @I3C_ADDR_SLOT_I3C_DEV: address is assigned to an I3C device
|
||||
* @I3C_ADDR_SLOT_STATUS_MASK: address slot mask
|
||||
*
|
||||
* @I3C_ADDR_SLOT_EXT_STATUS_MASK: address slot mask with extended information
|
||||
* @I3C_ADDR_SLOT_EXT_DESIRED: the bitmask represents addresses that are preferred by some devices,
|
||||
* such as the "assigned-address" property in a device tree source.
|
||||
* On an I3C bus, addresses are assigned dynamically, and we need to know which
|
||||
* addresses are free to use and which ones are already assigned.
|
||||
*
|
||||
@ -311,8 +313,12 @@ enum i3c_addr_slot_status {
|
||||
I3C_ADDR_SLOT_I2C_DEV,
|
||||
I3C_ADDR_SLOT_I3C_DEV,
|
||||
I3C_ADDR_SLOT_STATUS_MASK = 3,
|
||||
I3C_ADDR_SLOT_EXT_STATUS_MASK = 7,
|
||||
I3C_ADDR_SLOT_EXT_DESIRED = BIT(2),
|
||||
};
|
||||
|
||||
#define I3C_ADDR_SLOT_STATUS_BITS 4
|
||||
|
||||
/**
|
||||
* struct i3c_bus - I3C bus object
|
||||
* @cur_master: I3C master currently driving the bus. Since I3C is multi-master
|
||||
@ -354,7 +360,7 @@ enum i3c_addr_slot_status {
|
||||
struct i3c_bus {
|
||||
struct i3c_dev_desc *cur_master;
|
||||
int id;
|
||||
unsigned long addrslots[((I2C_MAX_ADDR + 1) * 2) / BITS_PER_LONG];
|
||||
unsigned long addrslots[((I2C_MAX_ADDR + 1) * I3C_ADDR_SLOT_STATUS_BITS) / BITS_PER_LONG];
|
||||
enum i3c_bus_mode mode;
|
||||
struct {
|
||||
unsigned long i3c;
|
||||
|
Loading…
Reference in New Issue
Block a user