diff --git a/drivers/soc/qcom/ddr_stats.c b/drivers/soc/qcom/ddr_stats.c index 89a9e5d52e8a..7ff93612b6e1 100644 --- a/drivers/soc/qcom/ddr_stats.c +++ b/drivers/soc/qcom/ddr_stats.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include @@ -22,6 +24,12 @@ #define MAGIC_KEY1 0xA1157A75 #define MAX_NUM_MODES 0x14 #define MSM_ARCH_TIMER_FREQ 19200000 +#define MAX_DRV 18 +#define MAX_MSG_LEN 35 +#define DRV_ABSENT 0xdeaddead +#define DRV_INVALID 0xffffdead +#define VOTE_MASK 0x3fff +#define VOTE_X_SHIFT 14 #define GET_PDATA_OF_ATTR(attr) \ (container_of(attr, struct ddr_stats_kobj_attr, ka)->pd) @@ -41,7 +49,10 @@ struct ddr_stats_platform_data { void __iomem *reg; int freq_count; int entry_count; + bool read_ddr_vote; struct mutex ddr_stats_lock; + struct mbox_client stats_mbox_cl; + struct mbox_chan *stats_mbox_ch; }; struct stats_entry { @@ -199,6 +210,64 @@ int ddr_stats_get_residency(int freq_count, struct ddr_freq_residency *data) } EXPORT_SYMBOL(ddr_stats_get_residency); +int ddr_stats_get_ss_count(void) +{ + return ddr_pdata->read_ddr_vote ? MAX_DRV : -EOPNOTSUPP; +} +EXPORT_SYMBOL(ddr_stats_get_ss_count); + +int ddr_stats_get_ss_vote_info(int ss_count, + struct ddr_stats_ss_vote_info *vote_info) +{ + char buf[MAX_MSG_LEN] = {}; + struct qmp_pkt pkt; + void __iomem *reg; + u32 vote_offset, val[MAX_DRV]; + int ret, i; + + if (!ddr_pdata->read_ddr_vote) + return -EOPNOTSUPP; + + if (!vote_info || !(ss_count == MAX_DRV) || !ddr_pdata) + return -ENODEV; + + mutex_lock(&ddr_pdata->ddr_stats_lock); + ret = scnprintf(buf, MAX_MSG_LEN, "{class: ddr, res: drvs_ddr_votes}"); + pkt.size = (ret + 0x3) & ~0x3; + pkt.data = buf; + + ret = mbox_send_message(ddr_pdata->stats_mbox_ch, &pkt); + if (ret < 0) { + pr_err("Error sending mbox message: %d\n", ret); + mutex_unlock(&ddr_pdata->ddr_stats_lock); + return ret; + } + + vote_offset = sizeof(u32) + sizeof(u32) + + (ddr_pdata->entry_count * sizeof(struct stats_entry)); + reg = ddr_pdata->reg; + + for (i = 0; i < ss_count; i++, reg += sizeof(u32)) { + val[i] = readl_relaxed(reg + vote_offset); + if (val[i] == DRV_ABSENT) { + vote_info[i].ab = DRV_ABSENT; + vote_info[i].ib = DRV_ABSENT; + continue; + } else if (val[i] == DRV_INVALID) { + vote_info[i].ab = DRV_INVALID; + vote_info[i].ib = DRV_INVALID; + continue; + } + + vote_info[i].ab = (val[i] >> VOTE_X_SHIFT) & VOTE_MASK; + vote_info[i].ib = val[i] & VOTE_MASK; + } + + mutex_unlock(&ddr_pdata->ddr_stats_lock); + return 0; +} +EXPORT_SYMBOL(ddr_stats_get_ss_vote_info); + static ssize_t ddr_stats_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -312,6 +381,16 @@ static int ddr_stats_probe(struct platform_device *pdev) reg += sizeof(struct stats_entry); } + ddr_pdata->stats_mbox_cl.dev = &pdev->dev; + ddr_pdata->stats_mbox_cl.tx_block = true; + ddr_pdata->stats_mbox_cl.tx_tout = 1000; + ddr_pdata->stats_mbox_cl.knows_txdone = false; + + ddr_pdata->read_ddr_vote = true; + ddr_pdata->stats_mbox_ch = mbox_request_channel(&ddr_pdata->stats_mbox_cl, 0); + if (IS_ERR(ddr_pdata->stats_mbox_ch)) + ddr_pdata->read_ddr_vote = false; + return ddr_stats_create_sysfs(pdev, ddr_pdata); } @@ -322,6 +401,9 @@ static int ddr_stats_remove(struct platform_device *pdev) ddr_stats_ka = (struct ddr_stats_kobj_attr *) platform_get_drvdata(pdev); + if (ddr_pdata->read_ddr_vote) + mbox_free_channel(ddr_stats_ka->pd->stats_mbox_ch); + sysfs_remove_file(ddr_stats_ka->kobj, &ddr_stats_ka->ka.attr); kobject_put(ddr_stats_ka->kobj); platform_set_drvdata(pdev, NULL); diff --git a/include/linux/soc/qcom/ddr_stats.h b/include/linux/soc/qcom/ddr_stats.h index 41c67ddf0e44..7dec4d922db4 100644 --- a/include/linux/soc/qcom/ddr_stats.h +++ b/include/linux/soc/qcom/ddr_stats.h @@ -11,7 +11,14 @@ struct ddr_freq_residency { uint64_t residency; }; +struct ddr_stats_ss_vote_info { + u32 ab; /* vote_x */ + u32 ib; /* vote_y */ +}; + int ddr_stats_get_freq_count(void); int ddr_stats_get_residency(int freq_count, struct ddr_freq_residency *data); - +int ddr_stats_get_ss_count(void); +int ddr_stats_get_ss_vote_info(int ss_count, + struct ddr_stats_ss_vote_info *vote_info); #endif /*__DDR_STATS_H_ */