i3c: i3c-master-qcom-geni: Support for OD,PUSH-PULL mode frequency switch

This change is to support both OD,PUSH-PULL mode frequency upto 12.5 Mhz.
Also to make use of same source frequency during both the operations.

Change-Id: I48ecd4a48f11de466b8b15d8454b8e7e8acc6825
Signed-off-by: Subramanian Ananthanarayanan <skananth@codeaurora.org>
This commit is contained in:
Subramanian Ananthanarayanan
2019-11-05 14:38:25 +05:30
parent c3934aa900
commit 137b5f5720

View File

@@ -199,12 +199,18 @@ enum geni_i3c_err_code {
#define TLMM_I3C_MODE 0x24
#define IBI_SW_RESET_MIN_SLEEP 1000
#define IBI_SW_RESET_MAX_SLEEP 2000
#define I3C_OD_CLK_RATE 370000
enum i3c_trans_dir {
WRITE_TRANSACTION = 0,
READ_TRANSACTION = 1
};
enum i3c_bus_phase {
OPEN_DRAIN_MODE = 0,
PUSH_PULL_MODE = 1
};
struct geni_se {
void __iomem *base;
void __iomem *ibi_base;
@@ -262,6 +268,7 @@ struct geni_i3c_dev {
int cur_idx;
unsigned long newaddrslots[(I3C_ADDR_MASK + 1) / BITS_PER_LONG];
const struct geni_i3c_clk_fld *clk_fld;
const struct geni_i3c_clk_fld *clk_od_fld;
struct geni_ibi ibi;
};
@@ -302,9 +309,9 @@ struct geni_i3c_clk_fld {
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;
u32 i2c_t_cycle_cnt;
};
static struct geni_i3c_dev*
@@ -332,11 +339,12 @@ to_geni_i3c_master(struct i3c_master_controller *master)
* 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 },
{ KHZ(100), 19200, 7, 10, 11, 0, 0, 26},
{ KHZ(400), 19200, 2, 5, 12, 0, 0, 24},
{ KHZ(1000), 19200, 1, 3, 9, 7, 0, 18},
{ KHZ(1920), 19200, 1, 4, 9, 7, 8, 19},
{ KHZ(370), 100000, 20, 4, 7, 8, 14, 14},
{ KHZ(12500), 100000, 1, 72, 168, 6, 7, 300},
};
static int geni_i3c_clk_map_idx(struct geni_i3c_dev *gi3c)
@@ -351,11 +359,19 @@ static int geni_i3c_clk_map_idx(struct geni_i3c_dev *gi3c)
itr->clk_freq_out == bus->scl_rate.i3c) &&
KHZ(itr->clk_src_freq) == gi3c->clk_src_freq) {
gi3c->clk_fld = itr;
return 0;
}
if (itr->clk_freq_out == I3C_OD_CLK_RATE)
gi3c->clk_od_fld = itr;
}
return -EINVAL;
if ((!gi3c->clk_fld) || (!gi3c->clk_od_fld)) {
GENI_SE_ERR(gi3c->ipcl, true, gi3c->se.dev,
"%s : clk mapping failed", __func__);
return -EINVAL;
}
return 0;
}
static void set_new_addr_slot(unsigned long *addrslot, u8 addr)
@@ -391,13 +407,17 @@ static bool is_new_addr_slot_set(unsigned long *addrslot, u8 addr)
return ((*ptr & (1 << (addr % BITS_PER_LONG))) != 0);
}
static void qcom_geni_i3c_conf(struct geni_i3c_dev *gi3c)
static void qcom_geni_i3c_conf(struct geni_i3c_dev *gi3c,
enum i3c_bus_phase bus_phase)
{
const struct geni_i3c_clk_fld *itr = gi3c->clk_fld;
u32 val;
unsigned long freq;
int ret = 0;
if (bus_phase == OPEN_DRAIN_MODE)
itr = gi3c->clk_od_fld;
if (gi3c->dfs_idx > DFS_INDEX_MAX)
ret = geni_se_clk_freq_match(&gi3c->se.i3c_rsc,
KHZ(itr->clk_src_freq),
@@ -673,8 +693,6 @@ static int i3c_geni_runtime_get_mutex_lock(struct geni_i3c_dev *gi3c)
return ret;
}
qcom_geni_i3c_conf(gi3c);
return 0; /* return 0 to indicate SUCCESS */
}
@@ -923,6 +941,8 @@ static int geni_i3c_master_send_ccc_cmd
if (ret)
return ret;
qcom_geni_i3c_conf(gi3c, OPEN_DRAIN_MODE);
for (i = 0; i < cmd->ndests; i++) {
int stall = (i < (cmd->ndests - 1)) ||
(cmd->id == I3C_CCC_ENTDAA);
@@ -1000,6 +1020,8 @@ static int geni_i3c_master_priv_xfers
if (ret)
return ret;
qcom_geni_i3c_conf(gi3c, PUSH_PULL_MODE);
for (i = 0; i < nxfers; i++) {
bool stall = (i < (nxfers - 1));
struct i3c_xfer_params xfer = { FIFO_MODE };
@@ -1051,6 +1073,8 @@ static int geni_i3c_master_i2c_xfers
if (ret)
return ret;
qcom_geni_i3c_conf(gi3c, PUSH_PULL_MODE);
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);
@@ -1193,7 +1217,7 @@ static int geni_i3c_master_bus_init(struct i3c_master_controller *m)
goto err_cleanup;
}
qcom_geni_i3c_conf(gi3c);
qcom_geni_i3c_conf(gi3c, OPEN_DRAIN_MODE);
/* Get an address for the master. */
ret = i3c_master_get_free_addr(m, 0);