diff options
Diffstat (limited to 'drivers/i3c/master/dw-i3c-master.c')
| -rw-r--r-- | drivers/i3c/master/dw-i3c-master.c | 121 |
1 files changed, 87 insertions, 34 deletions
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 889e2ed5bc83..7eb09ad10171 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -5,6 +5,7 @@ * Author: Vitor Soares <vitor.soares@synopsys.com> */ +#include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/clk.h> #include <linux/completion.h> @@ -204,12 +205,23 @@ #define EXTENDED_CAPABILITY 0xe8 #define SLAVE_CONFIG 0xec +#define DYN_ADDR_LO_MASK GENMASK(4, 0) +#define DYN_ADDR_HI_MASK GENMASK(6, 5) +#define IBI_SIR_BIT_MOD 32 /* 32-bit vector */ + +#define DW_I3C_DEV_NACK_RETRY_CNT_MAX 0x3 +#define DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK GENMASK(30, 29) +#define DEV_ADDR_TABLE_DYNAMIC_MASK GENMASK(23, 16) +#define DEV_ADDR_TABLE_STATIC_MASK GENMASK(6, 0) #define DEV_ADDR_TABLE_IBI_MDB BIT(12) #define DEV_ADDR_TABLE_SIR_REJECT BIT(13) +#define DEV_ADDR_TABLE_DEV_NACK_RETRY_CNT(x) \ + FIELD_PREP(DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK, (x)) #define DEV_ADDR_TABLE_LEGACY_I2C_DEV BIT(31) -#define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) (((x) << 16) & GENMASK(23, 16)) -#define DEV_ADDR_TABLE_STATIC_ADDR(x) ((x) & GENMASK(6, 0)) +#define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) FIELD_PREP(DEV_ADDR_TABLE_DYNAMIC_MASK, x) +#define DEV_ADDR_TABLE_STATIC_ADDR(x) FIELD_PREP(DEV_ADDR_TABLE_STATIC_MASK, x) #define DEV_ADDR_TABLE_LOC(start, idx) ((start) + ((idx) << 2)) +#define DEV_ADDR_TABLE_GET_DYNAMIC_ADDR(x) FIELD_GET(DEV_ADDR_TABLE_DYNAMIC_MASK, x) #define I3C_BUS_SDR1_SCL_RATE 8000000 #define I3C_BUS_SDR2_SCL_RATE 6000000 @@ -257,6 +269,14 @@ struct dw_i3c_drvdata { u32 flags; }; +static inline u32 get_ibi_sir_bit_index(u8 addr) +{ + u32 lo = FIELD_GET(DYN_ADDR_LO_MASK, addr); + u32 hi = FIELD_GET(DYN_ADDR_HI_MASK, addr); + + return (lo + hi) % IBI_SIR_BIT_MOD; +} + static bool dw_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m, const struct i3c_ccc_cmd *cmd) { @@ -409,17 +429,14 @@ static void dw_i3c_master_start_xfer_locked(struct dw_i3c_master *master) static void dw_i3c_master_enqueue_xfer(struct dw_i3c_master *master, struct dw_i3c_xfer *xfer) { - unsigned long flags; - init_completion(&xfer->comp); - spin_lock_irqsave(&master->xferqueue.lock, flags); + guard(spinlock_irqsave)(&master->xferqueue.lock); if (master->xferqueue.cur) { list_add_tail(&xfer->node, &master->xferqueue.list); } else { master->xferqueue.cur = xfer; dw_i3c_master_start_xfer_locked(master); } - spin_unlock_irqrestore(&master->xferqueue.lock, flags); } static void dw_i3c_master_dequeue_xfer_locked(struct dw_i3c_master *master, @@ -444,11 +461,8 @@ static void dw_i3c_master_dequeue_xfer_locked(struct dw_i3c_master *master, static void dw_i3c_master_dequeue_xfer(struct dw_i3c_master *master, struct dw_i3c_xfer *xfer) { - unsigned long flags; - - spin_lock_irqsave(&master->xferqueue.lock, flags); + guard(spinlock_irqsave)(&master->xferqueue.lock); dw_i3c_master_dequeue_xfer_locked(master, xfer); - spin_unlock_irqrestore(&master->xferqueue.lock, flags); } static void dw_i3c_master_end_xfer_locked(struct dw_i3c_master *master, u32 isr) @@ -1099,6 +1113,7 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, dev_err(master->dev, "<%s> cannot resume i3c bus master, err: %d\n", __func__, ret); + dw_i3c_master_free_xfer(xfer); return ret; } @@ -1187,15 +1202,13 @@ static int dw_i3c_master_request_ibi(struct i3c_dev_desc *dev, struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); struct i3c_master_controller *m = i3c_dev_get_master(dev); struct dw_i3c_master *master = to_dw_i3c_master(m); - unsigned long flags; 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->devs_lock, flags); + guard(spinlock_irqsave)(&master->devs_lock); master->devs[data->index].ibi_dev = dev; - spin_unlock_irqrestore(&master->devs_lock, flags); return 0; } @@ -1205,11 +1218,10 @@ static void dw_i3c_master_free_ibi(struct i3c_dev_desc *dev) struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); struct i3c_master_controller *m = i3c_dev_get_master(dev); struct dw_i3c_master *master = to_dw_i3c_master(m); - unsigned long flags; - spin_lock_irqsave(&master->devs_lock, flags); - master->devs[data->index].ibi_dev = NULL; - spin_unlock_irqrestore(&master->devs_lock, flags); + scoped_guard(spinlock_irqsave, &master->devs_lock) { + master->devs[data->index].ibi_dev = NULL; + } i3c_generic_ibi_free_pool(data->ibi_pool); data->ibi_pool = NULL; @@ -1236,14 +1248,21 @@ static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master, struct i3c_dev_desc *dev, u8 idx, bool enable) { - unsigned long flags; u32 dat_entry, reg; bool global; + u8 dynamic_addr; dat_entry = DEV_ADDR_TABLE_LOC(master->datstartaddr, idx); - spin_lock_irqsave(&master->devs_lock, flags); + guard(spinlock_irqsave)(&master->devs_lock); reg = readl(master->regs + dat_entry); + dynamic_addr = DEV_ADDR_TABLE_GET_DYNAMIC_ADDR(reg); + + if (!dynamic_addr) + dev_warn(master->dev, + "<%s> unassigned slave device, dynamic addr:%x\n", + __func__, dynamic_addr); + if (enable) { reg &= ~DEV_ADDR_TABLE_SIR_REJECT; if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD) @@ -1256,20 +1275,17 @@ static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master, if (enable) { global = (master->sir_rej_mask == IBI_REQ_REJECT_ALL); - master->sir_rej_mask &= ~BIT(idx); + master->sir_rej_mask &= ~BIT(get_ibi_sir_bit_index(dynamic_addr)); } else { bool hj_rejected = !!(readl(master->regs + DEVICE_CTRL) & DEV_CTRL_HOT_JOIN_NACK); - master->sir_rej_mask |= BIT(idx); + master->sir_rej_mask |= BIT(get_ibi_sir_bit_index(dynamic_addr)); global = (master->sir_rej_mask == IBI_REQ_REJECT_ALL) && hj_rejected; } writel(master->sir_rej_mask, master->regs + IBI_SIR_REQ_REJECT); if (global) dw_i3c_master_enable_sir_signal(master, enable); - - - spin_unlock_irqrestore(&master->devs_lock, flags); } static int dw_i3c_master_enable_hotjoin(struct i3c_master_controller *m) @@ -1370,7 +1386,6 @@ static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master, struct dw_i3c_i2c_dev_data *data; struct i3c_ibi_slot *slot; struct i3c_dev_desc *dev; - unsigned long flags; u8 addr, len; int idx; @@ -1388,7 +1403,7 @@ static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master, * a new platform op to validate it. */ - spin_lock_irqsave(&master->devs_lock, flags); + guard(spinlock_irqsave)(&master->devs_lock); idx = dw_i3c_master_get_addr_pos(master, addr); if (idx < 0) { dev_dbg_ratelimited(&master->base.dev, @@ -1424,14 +1439,10 @@ static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master, } i3c_master_queue_ibi(dev, slot); - spin_unlock_irqrestore(&master->devs_lock, flags); - return; err_drain: dw_i3c_master_drain_ibi_queue(master, len); - - spin_unlock_irqrestore(&master->devs_lock, flags); } /* "ibis": referring to In-Band Interrupts, and not @@ -1489,6 +1500,40 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static int dw_i3c_master_set_dev_nack_retry(struct i3c_master_controller *m, + unsigned long dev_nack_retry_cnt) +{ + struct dw_i3c_master *master = to_dw_i3c_master(m); + u32 reg; + int i; + + if (dev_nack_retry_cnt > DW_I3C_DEV_NACK_RETRY_CNT_MAX) { + dev_err(&master->base.dev, + "Value %ld exceeds maximum %d\n", + dev_nack_retry_cnt, DW_I3C_DEV_NACK_RETRY_CNT_MAX); + return -ERANGE; + } + + /* + * Update DAT entries for all currently attached devices. + * We directly iterate through the master's device array. + */ + for (i = 0; i < master->maxdevs; i++) { + /* Skip free/empty slots */ + if (master->free_pos & BIT(i)) + continue; + + reg = readl(master->regs + + DEV_ADDR_TABLE_LOC(master->datstartaddr, i)); + reg &= ~DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK; + reg |= DEV_ADDR_TABLE_DEV_NACK_RETRY_CNT(dev_nack_retry_cnt); + writel(reg, master->regs + + DEV_ADDR_TABLE_LOC(master->datstartaddr, i)); + } + + return 0; +} + static const struct i3c_master_controller_ops dw_mipi_i3c_ops = { .bus_init = dw_i3c_master_bus_init, .bus_cleanup = dw_i3c_master_bus_cleanup, @@ -1509,6 +1554,7 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = { .recycle_ibi_slot = dw_i3c_master_recycle_ibi_slot, .enable_hotjoin = dw_i3c_master_enable_hotjoin, .disable_hotjoin = dw_i3c_master_disable_hotjoin, + .set_dev_nack_retry = dw_i3c_master_set_dev_nack_retry, }; /* default platform ops implementations */ @@ -1570,6 +1616,8 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, spin_lock_init(&master->xferqueue.lock); INIT_LIST_HEAD(&master->xferqueue.list); + spin_lock_init(&master->devs_lock); + writel(INTR_ALL, master->regs + INTR_STATUS); irq = platform_get_irq(pdev, 0); ret = devm_request_irq(&pdev->dev, irq, @@ -1676,11 +1724,16 @@ static void dw_i3c_master_restore_addrs(struct dw_i3c_master *master) if (master->free_pos & BIT(pos)) continue; - if (master->devs[pos].is_i2c_addr) - reg_val = DEV_ADDR_TABLE_LEGACY_I2C_DEV | + reg_val = readl(master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, pos)); + + if (master->devs[pos].is_i2c_addr) { + reg_val &= ~DEV_ADDR_TABLE_STATIC_MASK; + reg_val |= DEV_ADDR_TABLE_LEGACY_I2C_DEV | DEV_ADDR_TABLE_STATIC_ADDR(master->devs[pos].addr); - else - reg_val = DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr); + } else { + reg_val &= ~DEV_ADDR_TABLE_DYNAMIC_MASK; + reg_val |= DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr); + } writel(reg_val, master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, pos)); } |
