diff --git a/drivers/i3c/master/adi-i3c-master.c b/drivers/i3c/master/adi-i3c-master.c index 23c45f609e92b..a0167f6235b42 100644 --- a/drivers/i3c/master/adi-i3c-master.c +++ b/drivers/i3c/master/adi-i3c-master.c @@ -78,7 +78,7 @@ #define DEV_CHAR_IS_I2C BIT(0) #define DEV_CHAR_IS_ATTACHED BIT(1) -#define DEV_CHAR_IS_IBI_CAPABLE BIT(2) +#define DEV_CHAR_BCR_IBI(x) (((x) & GENMASK(2, 1)) << 1) #define DEV_CHAR_HAS_IBI_MDB BIT(3) #define DEV_CHAR_WEN BIT(8) #define DEV_CHAR_ADDR(x) (((x) & GENMASK(6, 0)) << 9) @@ -86,6 +86,9 @@ #define REG_OPS_SET_SG(x) ((x) << 5) #define REG_OPS_PP_SG_MASK GENMASK(6, 5) +#define REG_IBI_CONFIG_LISTEN BIT(1) +#define REG_IBI_CONFIG_ENABLE BIT(0) + enum speed_grade {PP_SG_UNSET, PP_SG_1MHZ, PP_SG_3MHZ, PP_SG_6MHZ, PP_SG_12MHZ}; struct adi_i3c_cmd { u32 cmd0; @@ -109,6 +112,11 @@ struct adi_i3c_master { struct i3c_master_controller base; u32 free_rr_slots; unsigned int maxdevs; + struct { + unsigned int num_slots; + struct i3c_dev_desc **slots; + spinlock_t lock; /* Protect IBI slot access */ + } ibi; struct { struct list_head list; struct adi_i3c_xfer *cur; @@ -186,6 +194,9 @@ static bool adi_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m, static int adi_i3c_master_disable(struct adi_i3c_master *master) { + writel(~REG_IBI_CONFIG_LISTEN | ~REG_IBI_CONFIG_ENABLE, + master->regs + REG_IBI_CONFIG); + return 0; } @@ -437,6 +448,8 @@ static int adi_i3c_master_priv_xfers(struct i3c_dev_desc *dev, struct adi_i3c_i2c_dev_data { u16 id; + s16 ibi; + struct i3c_generic_ibi_pool *ibi_pool; }; static int adi_i3c_master_get_rr_slot(struct adi_i3c_master *master, @@ -487,6 +500,7 @@ static int adi_i3c_master_attach_i3c_dev(struct i3c_dev_desc *dev) return slot; } + data->ibi = -1; data->id = slot; i3c_dev_set_master_data(dev, data); master->free_rr_slots &= ~BIT(slot); @@ -501,6 +515,22 @@ static int adi_i3c_master_attach_i3c_dev(struct i3c_dev_desc *dev) return 0; } +static void adi_i3c_master_sync_dev_char(struct i3c_master_controller *m) +{ + struct adi_i3c_master *master = to_adi_i3c_master(m); + struct i3c_dev_desc *i3cdev; + u8 addr; + + i3c_bus_for_each_i3cdev(&m->bus, i3cdev) { + addr = i3cdev->info.dyn_addr ? + i3cdev->info.dyn_addr : i3cdev->info.static_addr; + writel(DEV_CHAR_ADDR(addr), master->regs + REG_DEV_CHAR); + writel(readl(master->regs + REG_DEV_CHAR) | + DEV_CHAR_BCR_IBI(i3cdev->info.bcr) | DEV_CHAR_WEN, + master->regs + REG_DEV_CHAR); + } +} + static void adi_i3c_master_detach_i3c_dev(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); @@ -581,6 +611,7 @@ static void adi_i3c_master_upd_i3c_scl_lim(struct adi_i3c_master *master) max_fscl = max(I3C_CCC_MAX_SDR_FSCL(dev->info.max_read_ds), I3C_CCC_MAX_SDR_FSCL(dev->info.max_write_ds)); + switch (max_fscl) { case I3C_SDR1_FSCL_8MHZ: max_fscl = PP_SG_6MHZ; @@ -634,6 +665,7 @@ static int adi_i3c_master_do_daa(struct i3c_master_controller *m) { struct adi_i3c_master *master = to_adi_i3c_master(m); int ret; + u32 irq_mask; master->daa.index = 0x8; for (u8 i = 0; i < MAX_DEVS; i++) { @@ -647,13 +679,14 @@ static int adi_i3c_master_do_daa(struct i3c_master_controller *m) /* Will be reused as index for daa.addrs */ master->daa.index = 0; - /* Enable CMDR and DA IRQ */ - writel(IRQ_PENDING_DAA_PENDING | IRQ_PENDING_CMDR_PENDING, master->regs + REG_IRQ_MASK); + irq_mask = readl(master->regs + REG_IRQ_MASK); + writel(irq_mask | IRQ_PENDING_DAA_PENDING, + master->regs + REG_IRQ_MASK); + //sleep(3000); ret = i3c_master_entdaa_locked(&master->base); - /* Disable DA IRQ */ - writel(IRQ_PENDING_CMDR_PENDING, master->regs + REG_IRQ_MASK); + writel(irq_mask, master->regs + REG_IRQ_MASK); /* DAA always finishes with CE2_ERROR or NACK_RESP */ if (ret && ret != I3C_ERROR_M2) @@ -662,6 +695,8 @@ static int adi_i3c_master_do_daa(struct i3c_master_controller *m) /* Add I3C devices discovered */ for (u8 i = 0; i < master->daa.index; i++) i3c_master_add_i3c_dev_locked(m, master->daa.addrs[i]); + /* Sync retrieved devs info with the IP */ + adi_i3c_master_sync_dev_char(m); i3c_master_defslvs_locked(&master->base); @@ -685,9 +720,64 @@ static int adi_i3c_master_bus_init(struct i3c_master_controller *m) if (ret) return ret; + writel(REG_IBI_CONFIG_LISTEN | ~REG_IBI_CONFIG_ENABLE, + master->regs + REG_IBI_CONFIG); + return 0; } +static void adi_i3c_master_handle_ibi(struct adi_i3c_master *master, + u32 ibi) +{ + struct adi_i3c_i2c_dev_data *data; + bool data_consumed = false; + struct i3c_ibi_slot *slot; + u32 id; + struct i3c_dev_desc *dev; + u8 *buf; + + id = adi_i3c_master_get_rr_slot(master, (ibi >> 17) & GENMASK(6, 0)); + for (id = 0; id < master->ibi.num_slots; id++) { + if (master->ibi.slots[id] && + master->ibi.slots[id]->info.dyn_addr == ((ibi >> 17) & GENMASK(6, 0))) + break; + } + + if (id >= master->ibi.num_slots) + return; + + dev = master->ibi.slots[id]; + spin_lock(&master->ibi.lock); + + data = i3c_dev_get_master_data(dev); + slot = i3c_generic_ibi_get_free_slot(data->ibi_pool); + if (!slot) + goto out_unlock; + + buf = slot->data; + buf[0] = (ibi >> 8) & GENMASK(7, 0); + + slot->len = 1; + i3c_master_queue_ibi(dev, slot); + data_consumed = true; + +out_unlock: + spin_unlock(&master->ibi.lock); +} + +static void adi_i3c_master_demux_ibis(struct adi_i3c_master *master) +{ + u32 status0; + + for (status0 = readl(master->regs + REG_FIFO_STATUS); + !(status0 & FIFO_STATUS_IBI_EMPTY); + status0 = readl(master->regs + REG_FIFO_STATUS)) { + u32 ibi = readl(master->regs + REG_IBI_FIFO); + + adi_i3c_master_handle_ibi(master, ibi); + } +} + static int adi_i3c_master_handle_da_req(struct adi_i3c_master *master) { int addr; @@ -723,6 +813,11 @@ static irqreturn_t adi_i3c_master_irq(int irq, void *data) adi_i3c_master_end_xfer_locked(master, pending); spin_unlock(&master->xferqueue.lock); + if (pending & IRQ_PENDING_IBI_PENDING) { + adi_i3c_master_demux_ibis(master); + clear |= IRQ_PENDING_IBI_PENDING; + } + if (pending & IRQ_PENDING_DAA_PENDING) { if (!adi_i3c_master_handle_da_req(master)) clear |= IRQ_PENDING_DAA_PENDING; @@ -785,6 +880,96 @@ static int adi_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, return ret; } +static int adi_i3c_master_disable_ibi(struct i3c_dev_desc *dev) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct adi_i3c_master *master = to_adi_i3c_master(m); + int ret; + + ret = i3c_master_disec_locked(m, dev->info.dyn_addr, + I3C_CCC_EVENT_SIR); + + writel(REG_IBI_CONFIG_LISTEN | ~REG_IBI_CONFIG_ENABLE, + master->regs + REG_IBI_CONFIG); + + writel(readl(master->regs + REG_IRQ_MASK) | ~IRQ_PENDING_IBI_PENDING, + master->regs + REG_IRQ_MASK); + + return ret; +} + +static int adi_i3c_master_enable_ibi(struct i3c_dev_desc *dev) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct adi_i3c_master *master = to_adi_i3c_master(m); + int ret; + + writel(REG_IBI_CONFIG_LISTEN | REG_IBI_CONFIG_ENABLE, + master->regs + REG_IBI_CONFIG); + + writel(readl(master->regs + REG_IRQ_MASK) | IRQ_PENDING_IBI_PENDING, + master->regs + REG_IRQ_MASK); + + ret = i3c_master_enec_locked(m, dev->info.dyn_addr, + I3C_CCC_EVENT_SIR); + return ret; +} + +static int adi_i3c_master_request_ibi(struct i3c_dev_desc *dev, + const struct i3c_ibi_setup *req) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct adi_i3c_master *master = to_adi_i3c_master(m); + struct adi_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); + unsigned long flags; + unsigned int i; + + data->ibi_pool = i3c_generic_ibi_alloc_pool(dev, req); + if (IS_ERR(data->ibi_pool)) + return PTR_ERR(data->ibi_pool); + + spin_lock_irqsave(&master->ibi.lock, flags); + for (i = 0; i < master->ibi.num_slots; i++) { + if (!master->ibi.slots[i]) { + data->ibi = i; + master->ibi.slots[i] = dev; + break; + } + } + spin_unlock_irqrestore(&master->ibi.lock, flags); + + if (i < master->ibi.num_slots) + return 0; + + i3c_generic_ibi_free_pool(data->ibi_pool); + data->ibi_pool = NULL; + + return -ENOSPC; +} + +static void adi_i3c_master_free_ibi(struct i3c_dev_desc *dev) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct adi_i3c_master *master = to_adi_i3c_master(m); + struct adi_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); + unsigned long flags; + + spin_lock_irqsave(&master->ibi.lock, flags); + master->ibi.slots[data->ibi] = NULL; + data->ibi = -1; + spin_unlock_irqrestore(&master->ibi.lock, flags); + + i3c_generic_ibi_free_pool(data->ibi_pool); +} + +static void adi_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev, + struct i3c_ibi_slot *slot) +{ + struct adi_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); + + i3c_generic_ibi_recycle_slot(data->ibi_pool, slot); +} + static const struct i3c_master_controller_ops adi_i3c_master_ops = { .bus_init = adi_i3c_master_bus_init, .bus_cleanup = adi_i3c_master_bus_cleanup, @@ -798,6 +983,11 @@ static const struct i3c_master_controller_ops adi_i3c_master_ops = { .send_ccc_cmd = adi_i3c_master_send_ccc_cmd, .priv_xfers = adi_i3c_master_priv_xfers, .i2c_xfers = adi_i3c_master_i2c_xfers, + .request_ibi = adi_i3c_master_request_ibi, + .enable_ibi = adi_i3c_master_enable_ibi, + .disable_ibi = adi_i3c_master_disable_ibi, + .free_ibi = adi_i3c_master_free_ibi, + .recycle_ibi_slot = adi_i3c_master_recycle_ibi_slot, }; static const struct of_device_id adi_i3c_master_of_match[] = { @@ -857,6 +1047,16 @@ static int adi_i3c_master_probe(struct platform_device *pdev) writel(IRQ_PENDING_CMDR_PENDING, master->regs + REG_IRQ_MASK); + spin_lock_init(&master->ibi.lock); + master->ibi.num_slots = 15; + master->ibi.slots = devm_kcalloc(&pdev->dev, master->ibi.num_slots, + sizeof(*master->ibi.slots), + GFP_KERNEL); + if (!master->ibi.slots) { + ret = -ENOMEM; + goto err_clk_disable; + } + ret = i3c_master_register(&master->base, &pdev->dev, &adi_i3c_master_ops, false); if (ret)