Merge "i3c: i3c-master-qcom-geni: Fix IBI and Hot join related issues"

This commit is contained in:
qctecmdr
2020-10-23 05:30:15 -07:00
committed by Gerrit - the friendly Code Review server

View File

@@ -151,12 +151,17 @@
#define CFG_FAIL_STALL_DIFF_EN BIT(20)
#define ADDR_ASSOCIATED_W_OTHER_GPII_EN BIT(21)
/* Enable bits for GPIIn, n:[0-11] */
#define GPIIn_IBI_EN(n) BIT(n)
/* 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
#define NUM_OF_MDB_SHIFT 16
#define IBI_NUM_OF_MDB_MSK GENMASK(18, 16)
/* IBI_GEN_CONFIG fields */
#define IBI_C_ENABLE BIT(0)
@@ -319,6 +324,9 @@ struct geni_i3c_clk_fld {
u32 i2c_t_cycle_cnt;
};
static void geni_i3c_enable_ibi_ctrl(struct geni_i3c_dev *gi3c, bool enable);
static void geni_i3c_enable_ibi_irq(struct geni_i3c_dev *gi3c, bool enable);
static struct geni_i3c_dev*
to_geni_i3c_master(struct i3c_master_controller *master)
{
@@ -1456,38 +1464,14 @@ static int geni_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
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(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(0x1B, 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));
geni_i3c_enable_ibi_ctrl(gi3c, true);
geni_i3c_enable_ibi_irq(gi3c, true);
gi3c->ibi.is_init = true;
}
@@ -1498,6 +1482,7 @@ static int geni_i3c_master_request_ibi(struct i3c_dev_desc *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;
unsigned int payload_len = req->max_payload_len;
if (!gi3c->ibi.hw_support)
return -EPERM;
@@ -1531,6 +1516,7 @@ static int geni_i3c_master_request_ibi(struct i3c_dev_desc *dev,
cmd = ((dev->info.dyn_addr & I3C_SLAVE_MASK)
<< I3C_SLAVE_ADDR_SHIFT) | I3C_SLAVE_RW | STALL;
cmd |= ((payload_len << NUM_OF_MDB_SHIFT) & IBI_NUM_OF_MDB_MSK);
geni_write_reg(cmd, gi3c->se.ibi_base, IBI_CMD(0));
/* wait for adding slave IBI */
@@ -1591,9 +1577,103 @@ static int qcom_deallocate_ibi_table_entry(struct geni_i3c_dev *gi3c)
return 0;
}
static void qcom_geni_i3c_ibi_unconf(struct geni_i3c_dev *gi3c)
static void geni_i3c_enable_hotjoin_irq(struct geni_i3c_dev *gi3c, bool enable)
{
u32 val;
//Disable hot-join, until next probe happens
val = geni_read_reg(gi3c->se.ibi_base, IBI_GEN_IRQ_EN);
if (enable)
val |= HOT_JOIN_IRQ_EN;
else
val &= ~HOT_JOIN_IRQ_EN;
geni_write_reg(val, gi3c->se.ibi_base, IBI_GEN_IRQ_EN);
GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev,
"%s:%s\n", __func__, (enable) ? "Enabled" : "Disabled");
}
static void geni_i3c_enable_ibi_irq(struct geni_i3c_dev *gi3c, bool enable)
{
u32 val;
if (enable) {
/* enable manager interrupts : HPG sec 4.1 */
val = geni_read_reg(gi3c->se.ibi_base, IBI_GEN_IRQ_EN);
val |= (val & 0x1B);
geni_write_reg(val, gi3c->se.ibi_base, IBI_GEN_IRQ_EN);
/* Enable GPII0 interrupts */
geni_write_reg(GPIIn_IBI_EN(0), gi3c->se.ibi_base,
IBI_GPII_IBI_EN);
geni_write_reg(~0u, gi3c->se.ibi_base, IBI_IRQ_EN(0));
} else {
geni_write_reg(0, gi3c->se.ibi_base, IBI_GPII_IBI_EN);
geni_write_reg(0, gi3c->se.ibi_base, IBI_IRQ_EN(0));
geni_write_reg(0, gi3c->se.ibi_base, IBI_GEN_IRQ_EN);
}
}
static void geni_i3c_enable_ibi_ctrl(struct geni_i3c_dev *gi3c, bool enable)
{
u32 val, timeout;
if (enable) {
reinit_completion(&gi3c->ibi.done);
/* 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);
/* Enable I3C IBI controller, if not in enabled state */
val = geni_read_reg(gi3c->se.ibi_base, IBI_GEN_CONFIG);
if (!(val & IBI_C_ENABLE)) {
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 ENABLE_CHANGE bit\n");
return;
}
GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev,
"%s: IBI ctrl enabled\n", __func__);
}
} else {
/* Disable IBI controller */
/* check if any IBI is enabled, if not then disable IBI ctrl */
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 disabling IBI: 0x%x\n", gi3c->ibi.err);
return;
}
GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev,
"%s: IBI ctrl disabled\n", __func__);
}
}
}
static void qcom_geni_i3c_ibi_unconf(struct geni_i3c_dev *gi3c)
{
u32 val;
int ret = 0;
val = geni_read_reg(gi3c->se.ibi_base, IBI_ALLOCATED_ENTRIES_GPII(0));
@@ -1603,40 +1683,6 @@ static void qcom_geni_i3c_ibi_unconf(struct geni_i3c_dev *gi3c)
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;
}
@@ -1992,17 +2038,6 @@ static int geni_i3c_probe(struct platform_device *pdev)
geni_se_init(gi3c->se.base, gi3c->tx_wm, tx_depth);
se_config_packing(gi3c->se.base, BITS_PER_BYTE, PACKING_BYTES_PW, true);
gi3c->hj_wl = wakeup_source_register(gi3c->se.dev,
dev_name(gi3c->se.dev));
if (!gi3c->hj_wl) {
GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev, "wakeup source registration failed\n");
se_geni_resources_off(&gi3c->se.i3c_rsc);
return -ENOMEM;
}
INIT_WORK(&gi3c->hj_wd, geni_i3c_hotjoin);
gi3c->hj_wq = alloc_workqueue("%s", 0, 0, dev_name(gi3c->se.dev));
ret = i3c_ibi_rsrcs_init(gi3c, pdev);
if (ret) {
se_geni_resources_off(&gi3c->se.i3c_rsc);
@@ -2025,8 +2060,19 @@ static int geni_i3c_probe(struct platform_device *pdev)
GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev,
"i3c_master_register failed:%d\n", ret);
//enable hot-join IRQ also
geni_write_reg(~0u, gi3c->se.ibi_base, IBI_GEN_IRQ_EN);
// hot-join
gi3c->hj_wl = wakeup_source_register(gi3c->se.dev,
dev_name(gi3c->se.dev));
if (!gi3c->hj_wl) {
GENI_SE_ERR(gi3c->ipcl, false, gi3c->se.dev,
"wakeup source registration failed\n");
se_geni_resources_off(&gi3c->se.i3c_rsc);
return -ENOMEM;
}
INIT_WORK(&gi3c->hj_wd, geni_i3c_hotjoin);
gi3c->hj_wq = alloc_workqueue("%s", 0, 0, dev_name(gi3c->se.dev));
geni_i3c_enable_hotjoin_irq(gi3c, true);
GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev, "I3C probed\n");
@@ -2036,17 +2082,20 @@ static int geni_i3c_probe(struct platform_device *pdev)
static int geni_i3c_remove(struct platform_device *pdev)
{
struct geni_i3c_dev *gi3c = platform_get_drvdata(pdev);
int ret = 0, val = 0;
int ret = 0;
//Disable hot-join, until next probe happens
val = geni_read_reg(gi3c->se.ibi_base, IBI_GEN_IRQ_EN);
val &= ~HOT_JOIN_IRQ_EN;
geni_write_reg(val, gi3c->se.ibi_base, IBI_GEN_IRQ_EN);
geni_i3c_enable_hotjoin_irq(gi3c, false);
destroy_workqueue(gi3c->hj_wq);
wakeup_source_unregister(gi3c->hj_wl);
if (gi3c->ibi.is_init)
qcom_geni_i3c_ibi_unconf(gi3c);
destroy_workqueue(gi3c->hj_wq);
wakeup_source_unregister(gi3c->hj_wl);
geni_i3c_enable_ibi_ctrl(gi3c, false);
/* Potentially to be done before pinctrl change */
geni_i3c_enable_ibi_irq(gi3c, false);
/*force suspend to avoid the auto suspend caused by driver removal*/
pm_runtime_force_suspend(gi3c->se.dev);
ret = pinctrl_select_state(gi3c->se.i3c_rsc.geni_pinctrl,
@@ -2054,7 +2103,9 @@ static int geni_i3c_remove(struct platform_device *pdev)
if (ret)
GENI_SE_DBG(gi3c->ipcl, false, gi3c->se.dev,
" i3c: pinctrl_select_state failed\n");
ret = i3c_master_unregister(&gi3c->ctrlr);
/* TBD : If we need debug for previous session, Don't delete logs */
if (gi3c->ipcl)
ipc_log_context_destroy(gi3c->ipcl);
return ret;