diff --git a/drivers/i3c/master/Kconfig b/drivers/i3c/master/Kconfig index 4e80a1fcbf91..3163ff524e99 100644 --- a/drivers/i3c/master/Kconfig +++ b/drivers/i3c/master/Kconfig @@ -21,3 +21,15 @@ config DW_I3C_MASTER This driver can also be built as a module. If so, the module will be called dw-i3c-master. + +config I3C_MASTER_MSM_GENI + tristate "Qualcomm Technologies Inc.'s GENI based I3C controller" + depends on ARCH_QCOM + depends on I3C + depends on MSM_GENI_SE + help + If you say yes to this option, support will be included for the + built-in I3C interface on the Qualcomm Technologies Inc SoCs. + + This driver can also be built as a module. If so, the module + will be called i3c-master-msm-geni. diff --git a/drivers/i3c/master/Makefile b/drivers/i3c/master/Makefile index 7eea9e086144..86503c698a82 100644 --- a/drivers/i3c/master/Makefile +++ b/drivers/i3c/master/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_CDNS_I3C_MASTER) += i3c-master-cdns.o obj-$(CONFIG_DW_I3C_MASTER) += dw-i3c-master.o +obj-$(CONFIG_I3C_MASTER_MSM_GENI) += i3c-master-msm-geni.o diff --git a/drivers/i3c/master/i3c-master-msm-geni.c b/drivers/i3c/master/i3c-master-msm-geni.c new file mode 100644 index 000000000000..3e94cfca5d2e --- /dev/null +++ b/drivers/i3c/master/i3c-master-msm-geni.c @@ -0,0 +1,1953 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SE_I3C_SCL_HIGH 0x268 +#define SE_I3C_TX_TRANS_LEN 0x26C +#define SE_I3C_RX_TRANS_LEN 0x270 +#define SE_I3C_DELAY_COUNTER 0x274 +#define SE_I2C_SCL_COUNTERS 0x278 +#define SE_I3C_SCL_CYCLE 0x27C +#define SE_GENI_HW_IRQ_EN 0x920 +#define SE_GENI_HW_IRQ_IGNORE_ON_ACTIVE 0x924 +#define SE_GENI_HW_IRQ_CMD_PARAM_0 0x930 +/* IBI_C registers */ +#define IBI_GEN_CONFIG 0x0000 +#define IBI_SCL_OD_TYPE 0x0004 +#define IBI_SCL_PP_TIMING_CONFIG 0x0008 +#define IBI_GPII_IBI_EN 0x000c +#define IBI_GEN_IRQ_STATUS 0x0010 +#define IBI_GEN_IRQ_EN 0x0014 +#define IBI_GEN_IRQ_CLR 0x0018 +#define IBI_HW_PARAM 0x001c +#define IBI_HW_VERSION 0x0020 +#define IBI_RX_DATA_DELAY 0x0024 +#define IBI_UNEXPECT_IBI_INFO 0x0028 +#define IBI_LEGACY_MODE 0x002c +#define IBI_SW_RESET 0x0030 +#define IBI_TEST_BUS_SEL 0x0100 +#define IBI_TEST_BUS_EN 0x0104 +#define IBI_TEST_BUS_REG 0x0108 +#define IBI_HW_EVENTS_MUX_CFG 0x010c +#define IBI_CHAR_CFG 0x0180 +#define IBI_CHAR_DATA 0x0184 +#define IBI_CHAR_OE 0x0188 +#define IBI_CMD(n) (0x1000 + (0x1000*n)) +#define IBI_IRQ_STATUS(n) (0x1004 + (0x1000*n)) +#define IBI_IRQ_EN(n) (0x1008 + (0x1000*n)) +#define IBI_IRQ_CLR(n) (0x100C + (0x1000*n)) +#define IBI_RCVD_IBI_STATUS(n) (0x1010 + (0x1000*n)) +#define IBI_RCVD_IBI_CLR(n) (0x1014 + (0x1000*n)) +#define IBI_ALLOCATED_ENTRIES_GPII(n) (0x1018 + (0x1000*n)) +#define IBI_CONFIG_ENTRY(n, k) (0x1800 + (0x1000*n) + (0x40*k)) +#define IBI_RCVD_IBI_INFO_ENTRY(n, k) (0x1804 + (0x1000*n) + (0x40*k)) +#define IBI_RCVD_IBI_DATA_ENTRY(n, k) (0x1808 + (0x1000*n) + (0x40*k)) +#define IBI_RCVD_IBI_TS_LSB_ENTRY(n, k) (0x180C + (0x1000*n) + (0x40*k)) +#define IBI_RCVD_IBI_TS_MSB_ENTRY(n, k) (0x1810 + (0x1000*n) + (0x40*k)) + +/* SE_GENI_M_CLK_CFG field shifts */ +#define CLK_DEV_VALUE_SHFT 4 +#define SER_CLK_EN_SHFT 0 + +/* SE_GENI_HW_IRQ_CMD_PARAM_0 field shifts */ +#define M_IBI_IRQ_PARAM_7E_SHFT 0 +#define M_IBI_IRQ_PARAM_STOP_STALL_SHFT 1 + +/* SE_I2C_SCL_COUNTERS field shifts */ +#define I2C_SCL_HIGH_COUNTER_SHFT 20 +#define I2C_SCL_LOW_COUNTER_SHFT 10 + +#define SE_I3C_ERR (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |\ + M_CMD_ABORT_EN | M_GP_IRQ_0_EN | M_GP_IRQ_1_EN | M_GP_IRQ_2_EN | \ + M_GP_IRQ_3_EN | M_GP_IRQ_4_EN) + +/* M_CMD OP codes for I2C/I3C */ +#define I3C_READ_IBI_HW 0 +#define I2C_WRITE 1 +#define I2C_READ 2 +#define I2C_WRITE_READ 3 +#define I2C_ADDR_ONLY 4 +#define I3C_INBAND_RESET 5 +#define I2C_BUS_CLEAR 6 +#define I2C_STOP_ON_BUS 7 +#define I3C_HDR_DDR_EXIT 8 +#define I3C_PRIVATE_WRITE 9 +#define I3C_PRIVATE_READ 10 +#define I3C_HDR_DDR_WRITE 11 +#define I3C_HDR_DDR_READ 12 +#define I3C_DIRECT_CCC_ADDR_ONLY 13 +#define I3C_BCAST_CCC_ADDR_ONLY 14 +#define I3C_READ_IBI 15 +#define I3C_BCAST_CCC_WRITE 16 +#define I3C_DIRECT_CCC_WRITE 17 +#define I3C_DIRECT_CCC_READ 18 +/* M_CMD params for I3C */ +#define PRE_CMD_DELAY BIT(0) +#define TIMESTAMP_BEFORE BIT(1) +#define STOP_STRETCH BIT(2) +#define TIMESTAMP_AFTER BIT(3) +#define POST_COMMAND_DELAY BIT(4) +#define IGNORE_ADD_NACK BIT(6) +#define READ_FINISHED_WITH_ACK BIT(7) +#define CONTINUOUS_MODE_DAA BIT(8) +#define SLV_ADDR_MSK GENMASK(15, 9) +#define SLV_ADDR_SHFT 9 +#define CCC_HDR_CMD_MSK GENMASK(23, 16) +#define CCC_HDR_CMD_SHFT 16 +#define IBI_NACK_TBL_CTRL BIT(24) +#define USE_7E BIT(25) +#define BYPASS_ADDR_PHASE BIT(26) + +/* IBI_HW_PARAM fields */ +#define I3C_IBI_NUM_GPII_MSK (GENMASK(11, 8)) +#define I3C_IBI_NUM_GPII_SHFT (8) +#define I3C_IBI_TABLE_DEPTH_MSK (GENMASK(4, 0)) + +/* IBI_IRQ_STATUS(n) fields */ +#define COMMAND_DONE BIT(0) +#define CFG_TABLE_FULL BIT(2) +#define IBI_RECEIVED BIT(8) +#define ADDR_ASSOCIATED_W_OTHER_GPII BIT(21) + +/* IBI_GEN_IRQ_EN fields */ +#define ENABLE_CHANGE_IRQ_EN BIT(0) +#define UNEXPECT_IBI_ADDR_IRQ_EN BIT(1) +#define HOT_JOIN_IRQ_EN BIT(2) +#define SW_RESET_DONE_EN BIT(3) +#define BUS_ERROR_EN BIT(4) + +/* IBI_IRQ_EN fields */ +#define COMMAND_DONE_IRQ_EN BIT(0) +#define INVALID_I3C_SLAVE_ADDR_IRQ_EN BIT(1) +#define CFG_TABLE_FULL_IRQ_EN BIT(2) +#define CFG_FAIL_IRQ_EN BIT(3) +#define CFG_W_IBI_DIS_IRQ_EN BIT(4) +#define IBI_RECEIVED_IRQ_EN BIT(8) +#define CFG_FAIL_ZERO_NUM_MDB_EN BIT(16) +#define CFG_FAIL_MASK_EN_DIFF_EN BIT(17) +#define CFG_FAIL_NUM_MDB_DIFF_EN BIT(18) +#define CFG_FAIL_NACK_DIFF_EN BIT(19) +#define CFG_FAIL_STALL_DIFF_EN BIT(20) +#define ADDR_ASSOCIATED_W_OTHER_GPII_EN BIT(21) + +/* IBI_CMD fields */ +#define IBI_CMD_OPCODE BIT(0) +#define I3C_SLAVE_RW BIT(15) +#define STALL BIT(21) +#define I3C_SLAVE_ADDR_SHIFT 8 +#define I3C_SLAVE_MASK 0x7f + +/* IBI_GEN_CONFIG fields */ +#define IBI_C_ENABLE BIT(0) + +/* IBI_CONFIG_ENTRY fields */ +#define IBI_VALID BIT(0) + +#define SE_I3C_IBI_ERR (INVALID_I3C_SLAVE_ADDR_IRQ_EN |\ + CFG_TABLE_FULL_IRQ_EN | CFG_FAIL_IRQ_EN |\ + CFG_W_IBI_DIS_IRQ_EN | CFG_FAIL_ZERO_NUM_MDB_EN |\ + CFG_FAIL_MASK_EN_DIFF_EN | CFG_FAIL_NUM_MDB_DIFF_EN |\ + CFG_FAIL_NACK_DIFF_EN | CFG_FAIL_STALL_DIFF_EN |\ + ADDR_ASSOCIATED_W_OTHER_GPII_EN) + +enum geni_i3c_err_code { + RD_TERM, + NACK, + CRC_ERR, + BUS_PROTO, + NACK_7E, + NACK_IBI, + GENI_OVERRUN, + GENI_ILLEGAL_CMD, + GENI_ABORT_DONE, + GENI_TIMEOUT, +}; + +#define DM_I3C_CB_ERR ((BIT(NACK) | BIT(BUS_PROTO) | BIT(NACK_7E)) << 5) + +#define I3C_AUTO_SUSPEND_DELAY 250 +#define KHZ(freq) (1000 * freq) +#define PACKING_BYTES_PW 4 +#define XFER_TIMEOUT HZ +#define DFS_INDEX_MAX 7 +#define I3C_CORE2X_VOTE (960) +#define DEFAULT_BUS_WIDTH (4) +#define DEFAULT_SE_CLK (19200000) + +#define I3C_DDR_READ_CMD BIT(7) +#define I3C_ADDR_MASK 0x7f +#define I3C_MAX_GPII_NUM 12 +#define TLMM_I3C_MODE 0x24 +#define IBI_SW_RESET_MIN_SLEEP 1000 +#define IBI_SW_RESET_MAX_SLEEP 2000 + +enum i3c_trans_dir { + WRITE_TRANSACTION = 0, + READ_TRANSACTION = 1 +}; + +struct geni_se { + void __iomem *base; + void __iomem *ibi_base; + struct device *dev; + struct se_geni_rsc i3c_rsc; +}; + +struct rcvd_ibi_data { + union { + struct { + u32 slave_add : 7; + u32 rw : 1; + u32 num_bytes : 3; + u32 resvd1 : 1; + u32 nack : 1; + u32 resvd2 : 18; + u32 valid : 1; + } fields; + u32 info; + } info; + u32 ts; + u32 payload; +}; + +struct geni_ibi { + bool hw_support; + bool is_init; + unsigned int num_slots; + unsigned int num_gpi; + struct i3c_dev_desc **slots; + spinlock_t lock; + int mngr_irq; + struct completion done; + int gpii_irq[I3C_MAX_GPII_NUM]; + int err; + u8 ctrl_id; + struct rcvd_ibi_data data; +}; + +struct geni_i3c_dev { + struct geni_se se; + unsigned int tx_wm; + int irq; + int err; + struct i3c_master_controller ctrlr; + void *ipcl; + struct completion done; + struct mutex lock; + spinlock_t spinlock; + u32 clk_src_freq; + u32 dfs_idx; + u8 *cur_buf; + enum i3c_trans_dir cur_rnw; + int cur_len; + int cur_idx; + unsigned long newaddrslots[(I3C_ADDR_MASK + 1) / BITS_PER_LONG]; + const struct geni_i3c_clk_fld *clk_fld; + struct geni_ibi ibi; +}; + +struct geni_i3c_i2c_dev_data { + u16 id; + s16 ibi; + struct i3c_generic_ibi_pool *ibi_pool; +}; + +struct i3c_xfer_params { + enum se_xfer_mode mode; + u32 m_cmd; + u32 m_param; +}; + +struct geni_i3c_err_log { + int err; + const char *msg; +}; + +static struct geni_i3c_err_log gi3c_log[] = { + [RD_TERM] = { -EINVAL, "I3C slave early read termination" }, + [NACK] = { -ENOTCONN, "NACK: slave unresponsive, check power/reset" }, + [CRC_ERR] = { -EINVAL, "CRC or parity error" }, + [BUS_PROTO] = { -EPROTO, "Bus proto err, noisy/unexpected start/stop" }, + [NACK_7E] = { -EBUSY, "NACK on 7E, unexpected protocol error" }, + [NACK_IBI] = { -EINVAL, "NACK on IBI" }, + [GENI_OVERRUN] = { -EIO, "Cmd overrun, check GENI cmd-state machine" }, + [GENI_ILLEGAL_CMD] = { -EILSEQ, + "Illegal cmd, check GENI cmd-state machine" }, + [GENI_ABORT_DONE] = { -ETIMEDOUT, "Abort after timeout successful" }, + [GENI_TIMEOUT] = { -ETIMEDOUT, "I3C transaction timed out" }, +}; + +struct geni_i3c_clk_fld { + u32 clk_freq_out; + u32 clk_src_freq; + u8 clk_div; + u8 i2c_t_high_cnt; + u8 i2c_t_low_cnt; + u8 i2c_t_cycle_cnt; + u8 i3c_t_high_cnt; + u8 i3c_t_cycle_cnt; +}; + +static struct geni_i3c_dev* +to_geni_i3c_master(struct i3c_master_controller *master) +{ + return container_of(master, struct geni_i3c_dev, ctrlr); +} + +/* + * Hardware uses the underlying formula to calculate time periods of + * SCL clock cycle. Firmware uses some additional cycles excluded from the + * below formula and it is confirmed that the time periods are within + * specification limits. + * + * time of high period of I2C SCL: + * i2c_t_high = (i2c_t_high_cnt * clk_div) / source_clock + * time of low period of I2C SCL: + * i2c_t_low = (i2c_t_low_cnt * clk_div) / source_clock + * time of full period of I2C SCL: + * i2c_t_cycle = (i2c_t_cycle_cnt * clk_div) / source_clock + * time of high period of I3C SCL: + * i3c_t_high = (i3c_t_high_cnt * clk_div) / source_clock + * time of full period of I3C SCL: + * i3c_t_cycle = (i3c_t_cycle_cnt * clk_div) / source_clock + * clk_freq_out = t / t_cycle + */ +static const struct geni_i3c_clk_fld geni_i3c_clk_map[] = { + { KHZ(100), 19200, 7, 10, 11, 26, 0, 0 }, + { KHZ(400), 19200, 2, 5, 12, 24, 0, 0 }, + { KHZ(1000), 19200, 1, 3, 9, 18, 7, 0 }, + { KHZ(1920), 19200, 1, 4, 9, 19, 7, 8 }, + { KHZ(12500), 100000, 1, 60, 140, 250, 8, 16 }, +}; + +static int geni_i3c_clk_map_idx(struct geni_i3c_dev *gi3c) +{ + int i; + struct i3c_master_controller *m = &gi3c->ctrlr; + const struct geni_i3c_clk_fld *itr = geni_i3c_clk_map; + struct i3c_bus *bus = i3c_master_get_bus(m); + + for (i = 0; i < ARRAY_SIZE(geni_i3c_clk_map); i++, itr++) { + if ((!bus || + itr->clk_freq_out == bus->scl_rate.i3c) && + KHZ(itr->clk_src_freq) == gi3c->clk_src_freq) { + gi3c->clk_fld = itr; + return 0; + } + } + + return -EINVAL; +} + +static void set_new_addr_slot(unsigned long *addrslot, u8 addr) +{ + unsigned long *ptr; + + if (addr > I3C_ADDR_MASK) + return; + + ptr = addrslot + (addr / BITS_PER_LONG); + *ptr |= 1 << (addr % BITS_PER_LONG); +} + +static void clear_new_addr_slot(unsigned long *addrslot, u8 addr) +{ + unsigned long *ptr; + + if (addr > I3C_ADDR_MASK) + return; + + ptr = addrslot + (addr / BITS_PER_LONG); + *ptr &= ~(1 << (addr % BITS_PER_LONG)); +} + +static bool is_new_addr_slot_set(unsigned long *addrslot, u8 addr) +{ + unsigned long *ptr; + + if (addr > I3C_ADDR_MASK) + return false; + + ptr = addrslot + (addr / BITS_PER_LONG); + return ((*ptr & (1 << (addr % BITS_PER_LONG))) != 0); +} + +static void qcom_geni_i3c_conf(struct geni_i3c_dev *gi3c) +{ + const struct geni_i3c_clk_fld *itr = gi3c->clk_fld; + u32 val; + unsigned long freq; + int ret = 0; + + if (gi3c->dfs_idx > DFS_INDEX_MAX) + ret = geni_se_clk_freq_match(&gi3c->se.i3c_rsc, + KHZ(itr->clk_src_freq), + &gi3c->dfs_idx, &freq, false); + if (ret) + gi3c->dfs_idx = 0; + + writel_relaxed(gi3c->dfs_idx, gi3c->se.base + SE_GENI_CLK_SEL); + + val = itr->clk_div << CLK_DEV_VALUE_SHFT; + val |= 1 << SER_CLK_EN_SHFT; + writel_relaxed(val, gi3c->se.base + GENI_SER_M_CLK_CFG); + + val = itr->i2c_t_high_cnt << I2C_SCL_HIGH_COUNTER_SHFT; + val |= itr->i2c_t_low_cnt << I2C_SCL_LOW_COUNTER_SHFT; + val |= itr->i2c_t_cycle_cnt; + writel_relaxed(val, gi3c->se.base + SE_I2C_SCL_COUNTERS); + + writel_relaxed(itr->i3c_t_cycle_cnt, gi3c->se.base + SE_I3C_SCL_CYCLE); + writel_relaxed(itr->i3c_t_high_cnt, gi3c->se.base + SE_I3C_SCL_HIGH); + +} + +static void geni_i3c_err(struct geni_i3c_dev *gi3c, int err) +{ + if (gi3c->cur_rnw == WRITE_TRANSACTION) + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, "len:%d, write\n", + gi3c->cur_len); + else + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, "len:%d, read\n", + gi3c->cur_len); + + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, "%s\n", gi3c_log[err].msg); + gi3c->err = gi3c_log[err].err; + + geni_se_dump_dbg_regs(&gi3c->se.i3c_rsc, gi3c->se.base, gi3c->ipcl); +} + +static void geni_i3c_handle_received_ibi(struct geni_i3c_dev *gi3c) +{ + struct geni_i3c_i2c_dev_data *data; + struct i3c_ibi_slot *slot; + struct i3c_dev_desc *dev = gi3c->ibi.slots[0]; + u32 val, i; + + val = readl_relaxed(gi3c->se.ibi_base + IBI_RCVD_IBI_STATUS(0)); + + data = i3c_dev_get_master_data(dev); + slot = i3c_generic_ibi_get_free_slot(data->ibi_pool); + if (!slot) { + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, "no free slot\n"); + goto no_free_slot; + } + + for (i = 0; i < gi3c->ibi.num_slots; i++) { + if (!(val & (1u << i))) + continue; + + gi3c->ibi.data.info.info = + readl_relaxed(gi3c->se.ibi_base + + IBI_RCVD_IBI_INFO_ENTRY(0, i)); + gi3c->ibi.data.ts = + readl_relaxed(gi3c->se.ibi_base + + IBI_RCVD_IBI_TS_LSB_ENTRY(0, i)); + gi3c->ibi.data.payload = + readl_relaxed(gi3c->se.ibi_base + + IBI_RCVD_IBI_DATA_ENTRY(0, i)); + + if (slot->data) + memcpy(slot->data, &gi3c->ibi.data.payload, + dev->ibi->max_payload_len); + + slot->len = min_t(unsigned int, + gi3c->ibi.data.info.fields.num_bytes, + dev->ibi->max_payload_len); + + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, + "IBI: info: 0x%x, ts: 0x%x, Data: 0x%x\n", + gi3c->ibi.data.info.info, gi3c->ibi.data.ts, + gi3c->ibi.data.payload); + } + + i3c_master_queue_ibi(dev, slot); +no_free_slot: + writel_relaxed(val, gi3c->se.ibi_base + IBI_RCVD_IBI_CLR(0)); +} + +static irqreturn_t geni_i3c_ibi_irq(int irq, void *dev) +{ + struct geni_i3c_dev *gi3c = dev; + unsigned long flags; + u32 m_stat = 0, m_stat_mask = 0; + bool cmd_done = false; + + spin_lock_irqsave(&gi3c->ibi.lock, flags); + + if (irq == gi3c->ibi.mngr_irq) { + m_stat_mask = readl_relaxed(gi3c->se.ibi_base + IBI_GEN_IRQ_EN); + m_stat = readl_relaxed(gi3c->se.ibi_base + + IBI_GEN_IRQ_STATUS) & m_stat_mask; + + if ((m_stat & UNEXPECT_IBI_ADDR_IRQ_EN) || + (m_stat & BUS_ERROR_EN)) + gi3c->ibi.err = m_stat; + + if ((m_stat & ENABLE_CHANGE_IRQ_EN) || + (m_stat & SW_RESET_DONE_EN)) + cmd_done = true; + + /* clear interrupts */ + if (m_stat) + writel_relaxed(m_stat, gi3c->se.ibi_base + + IBI_GEN_IRQ_CLR); + } else if (irq == gi3c->ibi.gpii_irq[0]) { + m_stat = readl_relaxed(gi3c->se.ibi_base + IBI_IRQ_STATUS(0)); + + if (m_stat & SE_I3C_IBI_ERR) + gi3c->ibi.err = m_stat; + + if (m_stat & IBI_RECEIVED) + geni_i3c_handle_received_ibi(gi3c); + + if (m_stat & COMMAND_DONE) + cmd_done = true; + + /* clear interrupts */ + if (m_stat) + writel_relaxed(m_stat, gi3c->se.ibi_base + + IBI_IRQ_CLR(0)); + } + + if (cmd_done) + complete(&gi3c->ibi.done); + spin_unlock_irqrestore(&gi3c->ibi.lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t geni_i3c_irq(int irq, void *dev) +{ + struct geni_i3c_dev *gi3c = dev; + int j; + u32 m_stat, m_stat_mask, rx_st; + u32 dm_tx_st, dm_rx_st, dma; + unsigned long flags; + + spin_lock_irqsave(&gi3c->spinlock, flags); + + m_stat = readl_relaxed(gi3c->se.base + SE_GENI_M_IRQ_STATUS); + m_stat_mask = readl_relaxed(gi3c->se.base + SE_GENI_M_IRQ_EN); + rx_st = readl_relaxed(gi3c->se.base + SE_GENI_RX_FIFO_STATUS); + dm_tx_st = readl_relaxed(gi3c->se.base + SE_DMA_TX_IRQ_STAT); + dm_rx_st = readl_relaxed(gi3c->se.base + SE_DMA_RX_IRQ_STAT); + dma = readl_relaxed(gi3c->se.base + SE_GENI_DMA_MODE_EN); + + if ((m_stat & SE_I3C_ERR) || + (dm_rx_st & DM_I3C_CB_ERR)) { + if (m_stat & M_GP_IRQ_0_EN) + geni_i3c_err(gi3c, RD_TERM); + if (m_stat & M_GP_IRQ_1_EN) + geni_i3c_err(gi3c, NACK); + if (m_stat & M_GP_IRQ_2_EN) + geni_i3c_err(gi3c, CRC_ERR); + if (m_stat & M_GP_IRQ_3_EN) + geni_i3c_err(gi3c, BUS_PROTO); + if (m_stat & M_GP_IRQ_4_EN) + geni_i3c_err(gi3c, NACK_7E); + if (m_stat & M_CMD_OVERRUN_EN) + geni_i3c_err(gi3c, GENI_OVERRUN); + if (m_stat & M_ILLEGAL_CMD_EN) + geni_i3c_err(gi3c, GENI_ILLEGAL_CMD); + if (m_stat & M_CMD_ABORT_EN) + geni_i3c_err(gi3c, GENI_ABORT_DONE); + + /* Disable the TX Watermark interrupt to stop TX */ + if (!dma) + writel_relaxed(0, gi3c->se.base + + SE_GENI_TX_WATERMARK_REG); + goto irqret; + } + + if (dma) { + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, + "i3c dma tx:0x%x, dma rx:0x%x\n", dm_tx_st, dm_rx_st); + goto irqret; + } + + if ((m_stat & (M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN)) && + (gi3c->cur_rnw == READ_TRANSACTION) && + gi3c->cur_buf) { + u32 rxcnt = rx_st & RX_FIFO_WC_MSK; + + for (j = 0; j < rxcnt; j++) { + u32 val; + int p = 0; + + val = readl_relaxed(gi3c->se.base + SE_GENI_RX_FIFOn); + while (gi3c->cur_idx < gi3c->cur_len && + p < sizeof(val)) { + gi3c->cur_buf[gi3c->cur_idx++] = val & 0xff; + val >>= 8; + p++; + } + if (gi3c->cur_idx == gi3c->cur_len) + break; + } + } else if ((m_stat & M_TX_FIFO_WATERMARK_EN) && + (gi3c->cur_rnw == WRITE_TRANSACTION) && + (gi3c->cur_buf)) { + for (j = 0; j < gi3c->tx_wm; j++) { + u32 temp; + u32 val = 0; + int p = 0; + + while (gi3c->cur_idx < gi3c->cur_len && + p < sizeof(val)) { + temp = gi3c->cur_buf[gi3c->cur_idx++]; + val |= temp << (p * 8); + p++; + } + writel_relaxed(val, gi3c->se.base + SE_GENI_TX_FIFOn); + if (gi3c->cur_idx == gi3c->cur_len) { + writel_relaxed(0, gi3c->se.base + + SE_GENI_TX_WATERMARK_REG); + break; + } + } + } +irqret: + if (m_stat) + writel_relaxed(m_stat, gi3c->se.base + SE_GENI_M_IRQ_CLEAR); + + if (dma) { + if (dm_tx_st) + writel_relaxed(dm_tx_st, + gi3c->se.base + SE_DMA_TX_IRQ_CLR); + if (dm_rx_st) + writel_relaxed(dm_rx_st, + gi3c->se.base + SE_DMA_RX_IRQ_CLR); + } + /* if this is err with done-bit not set, handle that through timeout. */ + if (m_stat & M_CMD_DONE_EN || m_stat & M_CMD_ABORT_EN) { + writel_relaxed(0, gi3c->se.base + SE_GENI_TX_WATERMARK_REG); + complete(&gi3c->done); + } else if ((dm_tx_st & TX_DMA_DONE) || + (dm_rx_st & RX_DMA_DONE) || + (dm_rx_st & RX_RESET_DONE)) + complete(&gi3c->done); + + spin_unlock_irqrestore(&gi3c->spinlock, flags); + return IRQ_HANDLED; +} + +static int i3c_geni_runtime_get_mutex_lock(struct geni_i3c_dev *gi3c) +{ + int ret; + + mutex_lock(&gi3c->lock); + + reinit_completion(&gi3c->done); + if (!pm_runtime_enabled(gi3c->se.dev)) + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "PM runtime disabled\n"); + + ret = pm_runtime_get_sync(gi3c->se.dev); + if (ret < 0) { + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "error turning on SE resources:%d\n", ret); + pm_runtime_put_noidle(gi3c->se.dev); + /* Set device in suspended since resume failed */ + pm_runtime_set_suspended(gi3c->se.dev); + + mutex_unlock(&gi3c->lock); + return ret; + } + + qcom_geni_i3c_conf(gi3c); + + return 0; /* return 0 to indicate SUCCESS */ +} + +static void i3c_geni_runtime_put_mutex_unlock(struct geni_i3c_dev *gi3c) +{ + pm_runtime_mark_last_busy(gi3c->se.dev); + pm_runtime_put_autosuspend(gi3c->se.dev); + mutex_unlock(&gi3c->lock); +} + +static int _i3c_geni_execute_command(struct geni_i3c_dev *gi3c, + struct i3c_xfer_params *xfer) +{ + dma_addr_t tx_dma = 0; + dma_addr_t rx_dma = 0; + int ret, time_remaining = 0; + enum i3c_trans_dir rnw = gi3c->cur_rnw; + u32 len = gi3c->cur_len; + + geni_se_select_mode(gi3c->se.base, xfer->mode); + + gi3c->err = 0; + gi3c->cur_idx = 0; + + if (rnw == READ_TRANSACTION) { + writel_relaxed(len, gi3c->se.base + SE_I3C_RX_TRANS_LEN); + geni_setup_m_cmd(gi3c->se.base, xfer->m_cmd, xfer->m_param); + + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, + "I3C cmd:0x%x param:0x%x READ len:%d, m_cmd: 0x%x\n", + xfer->m_cmd, xfer->m_param, len, + geni_read_reg(gi3c->se.base, SE_GENI_M_CMD0)); + + if (xfer->mode == SE_DMA) { + ret = geni_se_rx_dma_prep(gi3c->se.i3c_rsc.wrapper_dev, + gi3c->se.base, gi3c->cur_buf, + len, &rx_dma); + if (ret) { + xfer->mode = FIFO_MODE; + geni_se_select_mode(gi3c->se.base, xfer->mode); + } + } + } else { + writel_relaxed(len, gi3c->se.base + SE_I3C_TX_TRANS_LEN); + geni_setup_m_cmd(gi3c->se.base, xfer->m_cmd, xfer->m_param); + + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, + "I3C cmd:0x%x param:0x%x WRITE len:%d, m_cmd: 0x%x\n", + xfer->m_cmd, xfer->m_param, len, + geni_read_reg(gi3c->se.base, SE_GENI_M_CMD0)); + + if (xfer->mode == SE_DMA) { + ret = geni_se_tx_dma_prep(gi3c->se.i3c_rsc.wrapper_dev, + gi3c->se.base, gi3c->cur_buf, + len, &tx_dma); + if (ret) { + xfer->mode = FIFO_MODE; + geni_se_select_mode(gi3c->se.base, xfer->mode); + } + } + if (xfer->mode == FIFO_MODE && len > 0) /* Get FIFO IRQ */ + writel_relaxed(1, gi3c->se.base + + SE_GENI_TX_WATERMARK_REG); + } + time_remaining = wait_for_completion_timeout(&gi3c->done, + XFER_TIMEOUT); + if (!time_remaining) { + unsigned long flags; + + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "got wait_for_completion timeout\n"); + spin_lock_irqsave(&gi3c->spinlock, flags); + geni_i3c_err(gi3c, GENI_TIMEOUT); + gi3c->cur_buf = NULL; + gi3c->cur_len = gi3c->cur_idx = 0; + gi3c->cur_rnw = 0; + geni_abort_m_cmd(gi3c->se.base); + spin_unlock_irqrestore(&gi3c->spinlock, flags); + time_remaining = wait_for_completion_timeout(&gi3c->done, + XFER_TIMEOUT); + } + if (xfer->mode == SE_DMA) { + if (gi3c->err) { + if (rnw == READ_TRANSACTION) + writel_relaxed(1, gi3c->se.base + + SE_DMA_TX_FSM_RST); + else + writel_relaxed(1, gi3c->se.base + + SE_DMA_RX_FSM_RST); + wait_for_completion_timeout(&gi3c->done, XFER_TIMEOUT); + } + geni_se_rx_dma_unprep(gi3c->se.i3c_rsc.wrapper_dev, + rx_dma, len); + geni_se_tx_dma_unprep(gi3c->se.i3c_rsc.wrapper_dev, + tx_dma, len); + } + ret = gi3c->err; + if (gi3c->err) + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, + "I3C transaction error :%d\n", gi3c->err); + + gi3c->cur_buf = NULL; + gi3c->cur_len = gi3c->cur_idx = 0; + gi3c->cur_rnw = 0; + gi3c->err = 0; + + return ret; +} + +static int i3c_geni_execute_read_command(struct geni_i3c_dev *gi3c, + struct i3c_xfer_params *xfer, u8 *buf, u32 len) +{ + gi3c->cur_rnw = READ_TRANSACTION; + gi3c->cur_buf = buf; + gi3c->cur_len = len; + return _i3c_geni_execute_command(gi3c, xfer); +} + +static int i3c_geni_execute_write_command(struct geni_i3c_dev *gi3c, + struct i3c_xfer_params *xfer, u8 *buf, u32 len) +{ + gi3c->cur_rnw = WRITE_TRANSACTION; + gi3c->cur_buf = buf; + gi3c->cur_len = len; + return _i3c_geni_execute_command(gi3c, xfer); +} + +static void geni_i3c_perform_daa(struct geni_i3c_dev *gi3c) +{ + struct i3c_master_controller *m = &gi3c->ctrlr; + struct i3c_bus *bus = i3c_master_get_bus(m); + u8 last_dyn_addr = 0; + int ret; + + while (1) { + u8 rx_buf[8], tx_buf[8]; + struct i3c_xfer_params xfer = { FIFO_MODE }; + struct i3c_device_info info = { 0 }; + struct i3c_dev_desc *i3cdev; + bool new_device = true; + u64 pid; + u8 bcr, dcr, addr; + + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, + "i3c entdaa read\n"); + + xfer.m_cmd = I2C_READ; + xfer.m_param = STOP_STRETCH | CONTINUOUS_MODE_DAA | USE_7E | + IBI_NACK_TBL_CTRL; + + ret = i3c_geni_execute_read_command(gi3c, &xfer, rx_buf, 8); + if (ret) + break; + + dcr = rx_buf[7]; + bcr = rx_buf[6]; + pid = ((u64)rx_buf[0] << 40) | + ((u64)rx_buf[1] << 32) | + ((u64)rx_buf[2] << 24) | + ((u64)rx_buf[3] << 16) | + ((u64)rx_buf[4] << 8) | + ((u64)rx_buf[5]); + + i3c_bus_for_each_i3cdev(bus, i3cdev) { + + if (!i3cdev->dev) + continue; + + i3c_device_get_info(i3cdev->dev, &info); + if (pid == info.pid && + dcr == info.dcr && + bcr == info.bcr) { + new_device = false; + addr = (info.dyn_addr) ? info.dyn_addr : + info.static_addr; + break; + } + } + + if (new_device) { + ret = i3c_master_get_free_addr(m, + last_dyn_addr + 1); + if (ret < 0) + goto daa_err; + addr = last_dyn_addr = (u8)ret; + set_new_addr_slot(gi3c->newaddrslots, addr); + } + + tx_buf[0] = (addr & I3C_ADDR_MASK) << 1; + tx_buf[0] |= ~(hweight8(addr & I3C_ADDR_MASK) & 1); + + /* calculate crc */ + if (tx_buf[0]) { + u32 slaveid = addr; + u32 ret = slaveid & 1u; + u32 final = 0; + + while (slaveid) { + slaveid >>= 1; + ret = ret ^ (slaveid & 1u); + } + + ret = ret ^ 1u; + final = (addr << 1) | ret; + tx_buf[0] = final; + } + + xfer.m_cmd = I2C_WRITE; + xfer.m_param = STOP_STRETCH | BYPASS_ADDR_PHASE | + IBI_NACK_TBL_CTRL; + + ret = i3c_geni_execute_write_command(gi3c, &xfer, tx_buf, 1); + if (ret) + break; + } +daa_err: + return; +} + +static int geni_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, + struct i3c_ccc_cmd *cmd) +{ + struct geni_i3c_dev *gi3c = to_geni_i3c_master(m); + int i, ret; + + if (!(cmd->id & I3C_CCC_DIRECT) && (cmd->ndests != 1)) + return -EINVAL; + + ret = i3c_geni_runtime_get_mutex_lock(gi3c); + if (ret) + return ret; + + for (i = 0; i < cmd->ndests; i++) { + int stall = (i < (cmd->ndests - 1)) || + (cmd->id == I3C_CCC_ENTDAA); + struct i3c_xfer_params xfer = { FIFO_MODE }; + + xfer.m_param = (stall ? STOP_STRETCH : 0); + xfer.m_param |= (cmd->id << CCC_HDR_CMD_SHFT); + xfer.m_param |= IBI_NACK_TBL_CTRL; + if (cmd->id & I3C_CCC_DIRECT) { + xfer.m_param |= ((cmd->dests[i].addr & I3C_ADDR_MASK) + << SLV_ADDR_SHFT); + if (cmd->rnw) { + if (i == 0) + xfer.m_cmd = I3C_DIRECT_CCC_READ; + else + xfer.m_cmd = I3C_PRIVATE_READ; + } else { + if (i == 0) + xfer.m_cmd = + (cmd->dests[i].payload.len > 0) ? + I3C_DIRECT_CCC_WRITE : + I3C_DIRECT_CCC_ADDR_ONLY; + else + xfer.m_cmd = I3C_PRIVATE_WRITE; + } + } else { + if (cmd->dests[i].payload.len > 0) + xfer.m_cmd = I3C_BCAST_CCC_WRITE; + else + xfer.m_cmd = I3C_BCAST_CCC_ADDR_ONLY; + } + + if (i == 0) + xfer.m_param |= USE_7E; + + if (cmd->rnw) + ret = i3c_geni_execute_read_command(gi3c, &xfer, + cmd->dests[i].payload.data, + cmd->dests[i].payload.len); + else + ret = i3c_geni_execute_write_command(gi3c, &xfer, + cmd->dests[i].payload.data, + cmd->dests[i].payload.len); + if (ret) + break; + + if (cmd->id == I3C_CCC_ENTDAA) + geni_i3c_perform_daa(gi3c); + } + + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, + "i3c ccc: txn ret:%d\n", ret); + + i3c_geni_runtime_put_mutex_unlock(gi3c); + + return ret; +} + +static int geni_i3c_master_priv_xfers(struct i3c_dev_desc *dev, + struct i3c_priv_xfer *xfers, int nxfers) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct geni_i3c_dev *gi3c = to_geni_i3c_master(m); + int i, ret; + bool use_7e = true; + + if (nxfers <= 0) + return 0; + + ret = i3c_geni_runtime_get_mutex_lock(gi3c); + if (ret) + return ret; + + for (i = 0; i < nxfers; i++) { + bool stall = (i < (nxfers - 1)); + struct i3c_xfer_params xfer = { FIFO_MODE }; + + xfer.m_param = (stall ? STOP_STRETCH : 0); + xfer.m_param |= ((dev->info.dyn_addr & I3C_ADDR_MASK) + << SLV_ADDR_SHFT); + xfer.m_param |= (use_7e) ? USE_7E : 0; + + /* Update use_7e status for next loop iteration */ + use_7e = !stall; + + if (xfers[i].rnw) { + xfer.m_cmd = I3C_PRIVATE_READ; + ret = i3c_geni_execute_read_command(gi3c, &xfer, + (u8 *)xfers[i].data.in, + xfers[i].len); + } else { + xfer.m_cmd = I3C_PRIVATE_WRITE; + ret = i3c_geni_execute_write_command(gi3c, &xfer, + (u8 *)xfers[i].data.out, + xfers[i].len); + } + + if (ret) + break; + } + + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, + "i3c priv: txn ret:%d\n", ret); + + i3c_geni_runtime_put_mutex_unlock(gi3c); + + return ret; +} + +static int geni_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, + const struct i2c_msg *msgs, int num) +{ + struct i3c_master_controller *m = i2c_dev_get_master(dev); + struct geni_i3c_dev *gi3c = to_geni_i3c_master(m); + int i, ret; + + ret = i3c_geni_runtime_get_mutex_lock(gi3c); + if (ret) + return ret; + + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, + "i2c xfer:num:%d, msgs:len:%d,flg:%d\n", + num, msgs[0].len, msgs[0].flags); + + for (i = 0; i < num; i++) { + struct i3c_xfer_params xfer; + + xfer.m_cmd = (msgs[i].flags & I2C_M_RD) ? I2C_READ : + I2C_WRITE; + xfer.m_param = (i < (num - 1)) ? STOP_STRETCH : 0; + xfer.m_param |= ((msgs[i].addr & I3C_ADDR_MASK) + << SLV_ADDR_SHFT); + xfer.mode = msgs[i].len > 32 ? SE_DMA : FIFO_MODE; + if (msgs[i].flags & I2C_M_RD) + ret = i3c_geni_execute_read_command(gi3c, &xfer, + msgs[i].buf, msgs[i].len); + else + ret = i3c_geni_execute_write_command(gi3c, &xfer, + msgs[i].buf, msgs[i].len); + if (ret) + break; + } + + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, "i2c: txn ret:%d\n", ret); + + i3c_geni_runtime_put_mutex_unlock(gi3c); + + return ret; +} + +static int geni_i3c_master_attach_i2c_dev(struct i2c_dev_desc *dev) +{ + struct i3c_master_controller *m = i2c_dev_get_master(dev); + struct geni_i3c_dev *gi3c = to_geni_i3c_master(m); + struct geni_i3c_i2c_dev_data *data; + + data = devm_kzalloc(gi3c->se.dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_dev_set_master_data(dev, data); + + return 0; +} + +static void geni_i3c_master_detach_i2c_dev(struct i2c_dev_desc *dev) +{ + struct i3c_master_controller *m = i2c_dev_get_master(dev); + struct geni_i3c_dev *gi3c = to_geni_i3c_master(m); + struct geni_i3c_i2c_dev_data *data = i2c_dev_get_master_data(dev); + + i2c_dev_set_master_data(dev, NULL); + devm_kfree(gi3c->se.dev, data); +} + +static int geni_i3c_master_attach_i3c_dev(struct i3c_dev_desc *dev) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct geni_i3c_dev *gi3c = to_geni_i3c_master(m); + struct geni_i3c_i2c_dev_data *data; + + data = devm_kzalloc(gi3c->se.dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->ibi = -1; + i3c_dev_set_master_data(dev, data); + + return 0; +} + +static int geni_i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev, + u8 old_dyn_addr) +{ + return 0; +} + +static void geni_i3c_master_detach_i3c_dev(struct i3c_dev_desc *dev) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct geni_i3c_dev *gi3c = to_geni_i3c_master(m); + struct geni_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); + + i3c_dev_set_master_data(dev, NULL); + devm_kfree(gi3c->se.dev, data); +} + +static int geni_i3c_master_entdaa_locked(struct geni_i3c_dev *gi3c) +{ + struct i3c_master_controller *m = &gi3c->ctrlr; + u8 addr; + int ret; + + ret = i3c_master_entdaa_locked(m); + if (ret && ret != I3C_ERROR_M2) + return ret; + + for (addr = 0; addr <= I3C_ADDR_MASK; addr++) { + if (is_new_addr_slot_set(gi3c->newaddrslots, addr)) { + clear_new_addr_slot(gi3c->newaddrslots, addr); + i3c_master_add_i3c_dev_locked(m, addr); + } + } + + return 0; +} + +static int geni_i3c_master_do_daa(struct i3c_master_controller *m) +{ + struct geni_i3c_dev *gi3c = to_geni_i3c_master(m); + + return geni_i3c_master_entdaa_locked(gi3c); +} + +static int geni_i3c_master_bus_init(struct i3c_master_controller *m) +{ + struct geni_i3c_dev *gi3c = to_geni_i3c_master(m); + struct i3c_bus *bus = i3c_master_get_bus(m); + struct i3c_device_info info = { }; + int ret; + + ret = pm_runtime_get_sync(gi3c->se.dev); + if (ret < 0) { + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "%s: error turning SE resources:%d\n", __func__, ret); + pm_runtime_put_noidle(gi3c->se.dev); + /* Set device in suspended since resume failed */ + pm_runtime_set_suspended(gi3c->se.dev); + return ret; + } + + ret = geni_i3c_clk_map_idx(gi3c); + if (ret) { + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "Invalid clk frequency %d Hz src or %ld Hz bus: %d\n", + gi3c->clk_src_freq, bus->scl_rate.i3c, + ret); + goto err_cleanup; + } + + qcom_geni_i3c_conf(gi3c); + + /* Get an address for the master. */ + ret = i3c_master_get_free_addr(m, 0); + if (ret < 0) + goto err_cleanup; + + info.dyn_addr = ret; + info.dcr = I3C_DCR_GENERIC_DEVICE; + info.bcr = I3C_BCR_I3C_MASTER | I3C_BCR_HDR_CAP; + info.pid = 0; + + ret = i3c_master_set_info(&gi3c->ctrlr, &info); + +err_cleanup: + /*As framework calls multiple exposed API's after this API, we cannot + *use mutex protected internal put/get sync API. Hence forcefully + *disabling clocks and decrementing usage count. + */ + disable_irq(gi3c->irq); + se_geni_resources_off(&gi3c->se.i3c_rsc); + pm_runtime_disable(gi3c->se.dev); + pm_runtime_put_noidle(gi3c->se.dev); + pm_runtime_set_suspended(gi3c->se.dev); + pm_runtime_enable(gi3c->se.dev); + + return ret; +} + +static void geni_i3c_master_bus_cleanup(struct i3c_master_controller *m) +{ +} + +static bool geni_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m, + const struct i3c_ccc_cmd *cmd) +{ + switch (cmd->id) { + case I3C_CCC_ENEC(true): + /* fallthrough */ + case I3C_CCC_ENEC(false): + /* fallthrough */ + case I3C_CCC_DISEC(true): + /* fallthrough */ + case I3C_CCC_DISEC(false): + /* fallthrough */ + case I3C_CCC_ENTAS(0, true): + /* fallthrough */ + case I3C_CCC_ENTAS(0, false): + /* fallthrough */ + case I3C_CCC_RSTDAA(true): + /* fallthrough */ + case I3C_CCC_RSTDAA(false): + /* fallthrough */ + case I3C_CCC_ENTDAA: + /* fallthrough */ + case I3C_CCC_SETMWL(true): + /* fallthrough */ + case I3C_CCC_SETMWL(false): + /* fallthrough */ + case I3C_CCC_SETMRL(true): + /* fallthrough */ + case I3C_CCC_SETMRL(false): + /* fallthrough */ + case I3C_CCC_DEFSLVS: + /* fallthrough */ + case I3C_CCC_ENTHDR(0): + /* fallthrough */ + case I3C_CCC_SETDASA: + /* fallthrough */ + case I3C_CCC_SETNEWDA: + /* fallthrough */ + case I3C_CCC_GETMWL: + /* fallthrough */ + case I3C_CCC_GETMRL: + /* fallthrough */ + case I3C_CCC_GETPID: + /* fallthrough */ + case I3C_CCC_GETBCR: + /* fallthrough */ + case I3C_CCC_GETDCR: + /* fallthrough */ + case I3C_CCC_GETSTATUS: + /* fallthrough */ + case I3C_CCC_GETACCMST: + /* fallthrough */ + case I3C_CCC_GETMXDS: + /* fallthrough */ + case I3C_CCC_GETHDRCAP: + return true; + default: + break; + } + + return false; +} + +static int geni_i3c_master_enable_ibi(struct i3c_dev_desc *dev) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct geni_i3c_dev *gi3c = to_geni_i3c_master(m); + int ret = 0; + + if (!gi3c->ibi.hw_support && !gi3c->ibi.is_init) + return -EPERM; + + ret = i3c_master_enec_locked(m, dev->info.dyn_addr, + I3C_CCC_EVENT_SIR); + if (ret) + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "%s: error while i3c_master_enec_locked\n", __func__); + + return ret; +} + +static int geni_i3c_master_disable_ibi(struct i3c_dev_desc *dev) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct geni_i3c_dev *gi3c = to_geni_i3c_master(m); + int ret = 0; + + if (!gi3c->ibi.hw_support && !gi3c->ibi.is_init) + return -EPERM; + + ret = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); + if (ret) + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "%s: error while i3c_master_disec_locked\n", __func__); + + return ret; +} + +static void qcom_geni_i3c_ibi_conf(struct geni_i3c_dev *gi3c) +{ + u32 val, timeout; + + gi3c->ibi.err = 0; + reinit_completion(&gi3c->ibi.done); + + /* set the configuration for 100Khz OD speed */ + geni_write_reg(0, gi3c->se.ibi_base, IBI_SCL_OD_TYPE); + geni_write_reg(0x5FD74322, gi3c->se.ibi_base, IBI_SCL_PP_TIMING_CONFIG); + + /* Enable I3C IBI controller */ + val = geni_read_reg(gi3c->se.ibi_base, IBI_GEN_CONFIG); + val |= IBI_C_ENABLE; + geni_write_reg(val, gi3c->se.ibi_base, IBI_GEN_CONFIG); + + /* enable ENABLE_CHANGE */ + val = geni_read_reg(gi3c->se.ibi_base, IBI_GEN_IRQ_EN); + val |= IBI_C_ENABLE; + geni_write_reg(val, gi3c->se.ibi_base, IBI_GEN_IRQ_EN); + + /* wait for ENABLE_CHANGE */ + timeout = wait_for_completion_timeout(&gi3c->ibi.done, XFER_TIMEOUT); + if (!timeout) { + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "timeout while ENABLE_CHANGE bit\n"); + return; + } + + /* enable manager interrupts */ + geni_write_reg(~0u, gi3c->se.ibi_base, IBI_GEN_IRQ_EN); + + /* Enable GPII0 interrupts */ + geni_write_reg(0x1, gi3c->se.ibi_base, IBI_GPII_IBI_EN); + geni_write_reg(~0u, gi3c->se.ibi_base, IBI_IRQ_EN(0)); + gi3c->ibi.is_init = true; +} + +static int geni_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 geni_i3c_dev *gi3c = to_geni_i3c_master(m); + struct geni_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); + unsigned long i, flags; + + if (!gi3c->ibi.hw_support) + return -EPERM; + + if (!gi3c->ibi.is_init) + qcom_geni_i3c_ibi_conf(gi3c); + + data->ibi_pool = i3c_generic_ibi_alloc_pool(dev, req); + if (IS_ERR(data->ibi_pool)) { + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "Error creating a generic IBI pool %d\n", + PTR_ERR(data->ibi_pool)); + return PTR_ERR(data->ibi_pool); + } + + spin_lock_irqsave(&gi3c->ibi.lock, flags); + for (i = 0; i < gi3c->ibi.num_slots; i++) { + if (!gi3c->ibi.slots[i]) { + data->ibi = i; + gi3c->ibi.slots[i] = dev; + break; + } + } + spin_unlock_irqrestore(&gi3c->ibi.lock, flags); + + if (i < gi3c->ibi.num_slots) { + u32 cmd, timeout; + + gi3c->ibi.err = 0; + reinit_completion(&gi3c->ibi.done); + + cmd = ((dev->info.dyn_addr & I3C_SLAVE_MASK) + << I3C_SLAVE_ADDR_SHIFT) | I3C_SLAVE_RW | STALL; + geni_write_reg(cmd, gi3c->se.ibi_base, IBI_CMD(0)); + + /* wait for adding slave IBI */ + timeout = wait_for_completion_timeout(&gi3c->ibi.done, + XFER_TIMEOUT); + if (!timeout) { + gi3c->ibi.err = -ETIMEDOUT; + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "timeout while adding slave IBI\n"); + } + + if (!gi3c->ibi.err) + return 0; + + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "error while adding slave IBI 0x%x\n", gi3c->ibi.err); + } + + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "ibi.num_slots ran out %d: %d\n", i, gi3c->ibi.num_slots); + + i3c_generic_ibi_free_pool(data->ibi_pool); + data->ibi_pool = NULL; + + return -ENOSPC; +} + +static int qcom_deallocate_ibi_table_entry(struct i3c_dev_desc *dev) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct geni_i3c_dev *gi3c = to_geni_i3c_master(m); + u32 i, timeout; + + for (i = 0; i < gi3c->ibi.num_slots; i++) { + u32 entry; + + gi3c->ibi.err = 0; + reinit_completion(&gi3c->ibi.done); + + entry = geni_read_reg(gi3c->se.ibi_base, + IBI_CONFIG_ENTRY(0, i)); + + /* if valid entry */ + if (entry & IBI_VALID) { + /* send remove command */ + entry &= ~IBI_CMD_OPCODE; + geni_write_reg(entry, gi3c->se.ibi_base, IBI_CMD(0)); + + /* wait for removing slave IBI */ + timeout = wait_for_completion_timeout(&gi3c->ibi.done, + XFER_TIMEOUT); + if (!timeout) { + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "timeout while adding slave IBI\n"); + return -ETIMEDOUT; + } + } + } + + return 0; +} + +static void qcom_geni_i3c_ibi_unconf(struct i3c_dev_desc *dev) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct geni_i3c_dev *gi3c = to_geni_i3c_master(m); + u32 val, timeout; + int ret = 0; + + val = geni_read_reg(gi3c->se.ibi_base, IBI_ALLOCATED_ENTRIES_GPII(0)); + if (val) { + ret = qcom_deallocate_ibi_table_entry(dev); + if (ret) + return; + } + + /* disable interrupts */ + geni_write_reg(0, gi3c->se.ibi_base, IBI_GPII_IBI_EN); + geni_write_reg(0, gi3c->se.ibi_base, IBI_IRQ_EN(0)); + + /* check if any IBI is enabled, if not then reset HW */ + val = geni_read_reg(gi3c->se.ibi_base, IBI_GPII_IBI_EN); + if (!val) { + + gi3c->ibi.err = 0; + reinit_completion(&gi3c->ibi.done); + + val = geni_read_reg(gi3c->se.ibi_base, IBI_GEN_CONFIG); + val &= ~IBI_C_ENABLE; + geni_write_reg(val, gi3c->se.ibi_base, IBI_GEN_CONFIG); + + + /* wait for ENABLE change */ + timeout = wait_for_completion_timeout(&gi3c->ibi.done, + XFER_TIMEOUT); + if (!timeout) { + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "timeout while disabling IBI controller\n"); + return; + } + + if (gi3c->ibi.err) { + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "error while disabling IBI controller 0x%x\n", + gi3c->ibi.err); + return; + } + + } + + gi3c->ibi.is_init = false; +} + +static void geni_i3c_master_free_ibi(struct i3c_dev_desc *dev) +{ + struct i3c_master_controller *m = i3c_dev_get_master(dev); + struct geni_i3c_dev *gi3c = to_geni_i3c_master(m); + struct geni_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); + unsigned long flags; + + if (!gi3c->ibi.hw_support && !gi3c->ibi.is_init) + return; + + qcom_geni_i3c_ibi_unconf(dev); + + spin_lock_irqsave(&gi3c->ibi.lock, flags); + gi3c->ibi.slots[data->ibi] = NULL; + data->ibi = -1; + spin_unlock_irqrestore(&gi3c->ibi.lock, flags); + + i3c_generic_ibi_free_pool(data->ibi_pool); +} + +static void geni_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev, + struct i3c_ibi_slot *slot) +{ + struct geni_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 geni_i3c_master_ops = { + .bus_init = geni_i3c_master_bus_init, + .bus_cleanup = geni_i3c_master_bus_cleanup, + .do_daa = geni_i3c_master_do_daa, + .attach_i3c_dev = geni_i3c_master_attach_i3c_dev, + .reattach_i3c_dev = geni_i3c_master_reattach_i3c_dev, + .detach_i3c_dev = geni_i3c_master_detach_i3c_dev, + .attach_i2c_dev = geni_i3c_master_attach_i2c_dev, + .detach_i2c_dev = geni_i3c_master_detach_i2c_dev, + .supports_ccc_cmd = geni_i3c_master_supports_ccc_cmd, + .send_ccc_cmd = geni_i3c_master_send_ccc_cmd, + .priv_xfers = geni_i3c_master_priv_xfers, + .i2c_xfers = geni_i3c_master_i2c_xfers, + .enable_ibi = geni_i3c_master_enable_ibi, + .disable_ibi = geni_i3c_master_disable_ibi, + .request_ibi = geni_i3c_master_request_ibi, + .free_ibi = geni_i3c_master_free_ibi, + .recycle_ibi_slot = geni_i3c_master_recycle_ibi_slot, +}; + +static int i3c_geni_rsrcs_clk_init(struct geni_i3c_dev *gi3c) +{ + int ret; + + gi3c->se.i3c_rsc.ctrl_dev = gi3c->se.dev; + gi3c->se.i3c_rsc.se_clk = devm_clk_get(gi3c->se.dev, "se-clk"); + if (IS_ERR(gi3c->se.i3c_rsc.se_clk)) { + ret = PTR_ERR(gi3c->se.i3c_rsc.se_clk); + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "Error getting SE Core clk %d\n", ret); + return ret; + } + + gi3c->se.i3c_rsc.m_ahb_clk = devm_clk_get(gi3c->se.dev, "m-ahb"); + if (IS_ERR(gi3c->se.i3c_rsc.m_ahb_clk)) { + ret = PTR_ERR(gi3c->se.i3c_rsc.m_ahb_clk); + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "Error getting M AHB clk %d\n", ret); + return ret; + } + + gi3c->se.i3c_rsc.s_ahb_clk = devm_clk_get(gi3c->se.dev, "s-ahb"); + if (IS_ERR(gi3c->se.i3c_rsc.s_ahb_clk)) { + ret = PTR_ERR(gi3c->se.i3c_rsc.s_ahb_clk); + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "Error getting S AHB clk %d\n", ret); + return ret; + } + + return 0; +} + +static int i3c_geni_rsrcs_init(struct geni_i3c_dev *gi3c, + struct platform_device *pdev) +{ + struct resource *res; + struct platform_device *wrapper_pdev; + struct device_node *wrapper_ph_node; + int ret; + + /* base register address */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "Err getting IO region\n"); + return -EINVAL; + } + + gi3c->se.base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gi3c->se.base)) + return PTR_ERR(gi3c->se.base); + + wrapper_ph_node = of_parse_phandle(pdev->dev.of_node, + "qcom,wrapper-core", 0); + if (IS_ERR_OR_NULL(wrapper_ph_node)) { + ret = PTR_ERR(wrapper_ph_node); + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "No wrapper core defined\n"); + return ret; + } + + wrapper_pdev = of_find_device_by_node(wrapper_ph_node); + of_node_put(wrapper_ph_node); + if (IS_ERR_OR_NULL(wrapper_pdev)) { + ret = PTR_ERR(wrapper_pdev); + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "Cannot retrieve wrapper device\n"); + return ret; + } + + gi3c->se.i3c_rsc.wrapper_dev = &wrapper_pdev->dev; + + ret = geni_se_resources_init(&gi3c->se.i3c_rsc, I3C_CORE2X_VOTE, + (DEFAULT_SE_CLK * DEFAULT_BUS_WIDTH)); + if (ret) { + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, + "geni_se_resources_init\n"); + return ret; + } + + ret = device_property_read_u32(&pdev->dev, "se-clock-frequency", + &gi3c->clk_src_freq); + if (ret) { + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, + "SE clk freq not specified, default to 100 MHz.\n"); + gi3c->clk_src_freq = 100000000; + } + + ret = device_property_read_u32(&pdev->dev, "dfs-index", + &gi3c->dfs_idx); + if (ret) + gi3c->dfs_idx = 0xf; + + gi3c->se.i3c_rsc.geni_pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(gi3c->se.i3c_rsc.geni_pinctrl)) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "Error no pinctrl config specified\n"); + ret = PTR_ERR(gi3c->se.i3c_rsc.geni_pinctrl); + return ret; + } + gi3c->se.i3c_rsc.geni_gpio_active = + pinctrl_lookup_state(gi3c->se.i3c_rsc.geni_pinctrl, "default"); + if (IS_ERR(gi3c->se.i3c_rsc.geni_gpio_active)) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "Error no pinctr default config specified\n"); + ret = PTR_ERR(gi3c->se.i3c_rsc.geni_gpio_active); + return ret; + } + gi3c->se.i3c_rsc.geni_gpio_sleep = + pinctrl_lookup_state(gi3c->se.i3c_rsc.geni_pinctrl, "sleep"); + if (IS_ERR(gi3c->se.i3c_rsc.geni_gpio_sleep)) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "Error no pinctrl sleep config specified\n"); + ret = PTR_ERR(gi3c->se.i3c_rsc.geni_gpio_sleep); + return ret; + } + + return 0; +} + +static int i3c_ibi_rsrcs_init(struct geni_i3c_dev *gi3c, + struct platform_device *pdev) +{ + struct resource *res; + int ret; + + if (of_property_read_u8(pdev->dev.of_node, "qcom,ibi-ctrl-id", + &gi3c->ibi.ctrl_id)) { + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "IBI controller instance id is not defined\n"); + return -ENXIO; + } + + /* Enable TLMM I3C MODE registers */ + msm_qup_write(gi3c->ibi.ctrl_id, TLMM_I3C_MODE); + + /* IBI register address */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return -EINVAL; + + gi3c->se.ibi_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gi3c->se.ibi_base)) + return PTR_ERR(gi3c->se.ibi_base); + + gi3c->ibi.hw_support = (geni_read_reg(gi3c->se.base, SE_HW_PARAM_0) + & GEN_I3C_IBI_CTRL); + if (!gi3c->ibi.hw_support) { + GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev, + "IBI controller support not present\n"); + return -ENODEV; + } + + init_completion(&gi3c->ibi.done); + spin_lock_init(&gi3c->ibi.lock); + gi3c->ibi.num_slots = ((geni_read_reg(gi3c->se.ibi_base, IBI_HW_PARAM) + & I3C_IBI_TABLE_DEPTH_MSK)); + gi3c->ibi.slots = devm_kcalloc(&pdev->dev, gi3c->ibi.num_slots, + sizeof(*gi3c->ibi.slots), GFP_KERNEL); + if (!gi3c->ibi.slots) + return -ENOMEM; + + /* Register IBI_C manager interrupt */ + gi3c->ibi.mngr_irq = platform_get_irq(pdev, 1); + if (gi3c->ibi.mngr_irq < 0) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "IRQ error for ibi_c manager\n"); + return gi3c->ibi.mngr_irq; + } + + ret = devm_request_irq(&pdev->dev, gi3c->ibi.mngr_irq, geni_i3c_ibi_irq, + IRQF_TRIGGER_HIGH, dev_name(&pdev->dev), gi3c); + if (ret) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "Request_irq failed:%d: err:%d\n", + gi3c->ibi.mngr_irq, ret); + return ret; + } + + /* set mngr irq as wake-up irq */ + ret = irq_set_irq_wake(gi3c->ibi.mngr_irq, 1); + if (ret) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "Failed to set mngr IRQ(%d) wake: err:%d\n", + gi3c->ibi.mngr_irq, ret); + return ret; + } + + /* Register GPII interrupt */ + gi3c->ibi.gpii_irq[0] = platform_get_irq(pdev, 2); + if (gi3c->ibi.gpii_irq[0] < 0) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "IRQ error for ibi_c gpii\n"); + return gi3c->ibi.gpii_irq[0]; + } + + ret = devm_request_irq(&pdev->dev, gi3c->ibi.gpii_irq[0], + geni_i3c_ibi_irq, IRQF_TRIGGER_HIGH, + dev_name(&pdev->dev), gi3c); + if (ret) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "Request_irq failed:%d: err:%d\n", + gi3c->ibi.gpii_irq[0], ret); + return ret; + } + + /* set gpii irq as wake-up irq */ + ret = irq_set_irq_wake(gi3c->ibi.gpii_irq[0], 1); + if (ret) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "Failed to set gpii IRQ(%d) wake: err:%d\n", + gi3c->ibi.gpii_irq[0], ret); + return ret; + } + + qcom_geni_i3c_ibi_conf(gi3c); + + return 0; +} + +static int geni_i3c_probe(struct platform_device *pdev) +{ + struct geni_i3c_dev *gi3c; + u32 proto, tx_depth; + int ret; + u32 se_mode; + + gi3c = devm_kzalloc(&pdev->dev, sizeof(*gi3c), GFP_KERNEL); + if (!gi3c) + return -ENOMEM; + + gi3c->se.dev = &pdev->dev; + gi3c->ipcl = ipc_log_context_create(4, dev_name(gi3c->se.dev), 0); + + ret = i3c_geni_rsrcs_init(gi3c, pdev); + if (ret) + return ret; + + ret = i3c_geni_rsrcs_clk_init(gi3c); + if (ret) + return ret; + + gi3c->irq = platform_get_irq(pdev, 0); + if (gi3c->irq < 0) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "IRQ error for i3c-master-geni\n"); + return gi3c->irq; + } + + init_completion(&gi3c->done); + mutex_init(&gi3c->lock); + spin_lock_init(&gi3c->spinlock); + platform_set_drvdata(pdev, gi3c); + ret = devm_request_irq(&pdev->dev, gi3c->irq, geni_i3c_irq, + IRQF_TRIGGER_HIGH, dev_name(&pdev->dev), gi3c); + if (ret) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "Request_irq failed:%d: err:%d\n", + gi3c->irq, ret); + return ret; + } + /* Disable the interrupt so that the system can enter low-power mode */ + disable_irq(gi3c->irq); + + ret = se_geni_resources_on(&gi3c->se.i3c_rsc); + if (ret) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "Error turning on resources %d\n", ret); + return ret; + } + + proto = get_se_proto(gi3c->se.base); + if (proto != I3C) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "Invalid proto %d\n", proto); + se_geni_resources_off(&gi3c->se.i3c_rsc); + return -ENXIO; + } + + se_mode = geni_read_reg(gi3c->se.base, GENI_IF_FIFO_DISABLE_RO); + if (se_mode) { + GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, + "Non supported mode %d\n", se_mode); + se_geni_resources_off(&gi3c->se.i3c_rsc); + return -ENXIO; + } + + tx_depth = get_tx_fifo_depth(gi3c->se.base); + gi3c->tx_wm = tx_depth - 1; + geni_se_init(gi3c->se.base, gi3c->tx_wm, tx_depth); + se_config_packing(gi3c->se.base, BITS_PER_BYTE, PACKING_BYTES_PW, true); + + ret = i3c_ibi_rsrcs_init(gi3c, pdev); + if (ret) { + se_geni_resources_off(&gi3c->se.i3c_rsc); + return ret; + } + + se_geni_resources_off(&gi3c->se.i3c_rsc); + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, + "i3c fifo/se-dma mode. fifo depth:%d\n", tx_depth); + + pm_runtime_set_suspended(gi3c->se.dev); + pm_runtime_set_autosuspend_delay(gi3c->se.dev, I3C_AUTO_SUSPEND_DELAY); + pm_runtime_use_autosuspend(gi3c->se.dev); + pm_runtime_enable(gi3c->se.dev); + + ret = i3c_master_register(&gi3c->ctrlr, &pdev->dev, + &geni_i3c_master_ops, false); + + if (ret) + return ret; + + GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, "I3C probed\n"); + return ret; +} + +static int geni_i3c_remove(struct platform_device *pdev) +{ + struct geni_i3c_dev *gi3c = platform_get_drvdata(pdev); + int ret = 0; + + pm_runtime_disable(gi3c->se.dev); + ret = i3c_master_unregister(&gi3c->ctrlr); + if (gi3c->ipcl) + ipc_log_context_destroy(gi3c->ipcl); + return ret; +} + +static int geni_i3c_resume_noirq(struct device *dev) +{ + return 0; +} + +#ifdef CONFIG_PM +static int geni_i3c_runtime_suspend(struct device *dev) +{ + struct geni_i3c_dev *gi3c = dev_get_drvdata(dev); + + disable_irq(gi3c->irq); + se_geni_resources_off(&gi3c->se.i3c_rsc); + return 0; +} + +static int geni_i3c_runtime_resume(struct device *dev) +{ + int ret; + struct geni_i3c_dev *gi3c = dev_get_drvdata(dev); + + ret = se_geni_resources_on(&gi3c->se.i3c_rsc); + if (ret) + return ret; + + enable_irq(gi3c->irq); + + /* Enable TLMM I3C MODE registers */ + return 0; +} + +static int geni_i3c_suspend_noirq(struct device *dev) +{ + if (!pm_runtime_status_suspended(dev)) { + geni_i3c_runtime_suspend(dev); + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + } + return 0; +} +#else +static int geni_i3c_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int geni_i3c_runtime_resume(struct device *dev) +{ + return 0; +} + +static int geni_i3c_suspend_noirq(struct device *dev) +{ + return 0; +} +#endif + +static const struct dev_pm_ops geni_i3c_pm_ops = { + .suspend_noirq = geni_i3c_suspend_noirq, + .resume_noirq = geni_i3c_resume_noirq, + .runtime_suspend = geni_i3c_runtime_suspend, + .runtime_resume = geni_i3c_runtime_resume, +}; + +static const struct of_device_id geni_i3c_dt_match[] = { + { .compatible = "qcom,geni-i3c" }, + { } +}; +MODULE_DEVICE_TABLE(of, geni_i3c_dt_match); + +static struct platform_driver geni_i3c_master = { + .probe = geni_i3c_probe, + .remove = geni_i3c_remove, + .driver = { + .name = "geni_i3c_master", + .pm = &geni_i3c_pm_ops, + .of_match_table = geni_i3c_dt_match, + }, +}; + +module_platform_driver(geni_i3c_master); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:geni_i3c_master");