Commit 18517746 authored by Arnd Bergmann's avatar Arnd Bergmann

Merge tag 'qcom-drivers-for-5.9' of...

Merge tag 'qcom-drivers-for-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux into arm/drivers

Qualcomm driver updates for v5.9

For RPMh this fixes an issue where ktime was used during suspend, allows
the driver to be used on ARM targets and some minor cleanups.

It adds support for the latest format version in the socinfo driver and
adds identifiers for SM8250 and SDM630.

SMD-RPM gains compatibles for MSM8994 and MSM8936 and the Qualcomm SCM
gains compatibles MSM8994 and IPQ8074.

The GENI core code gains interconnect path voting and performance level
support, with subsequent patches integrating this with the SPI, I2C,
UART and QSPI drivers.

Following this the KGDB support for the GENI serial driver is improved,
the performance related to chip-select is improved for SPI and QSPI.

* tag 'qcom-drivers-for-5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux: (35 commits)
  soc: qcom: geni: Fix NULL pointer dereference
  tty: serial: qcom-geni-serial: Drop the icc bw votes in suspend for console
  serial: qcom_geni_serial: Always use 4 bytes per TX FIFO word
  serial: qcom_geni_serial: Make kgdb work even if UART isn't console
  spi: spi-geni-qcom: Get rid of most overhead in prepare_message()
  spi: spi-geni-qcom: Set the clock properly at runtime resume
  spi: spi-geni-qcom: Avoid clock setting if not needed
  spi: spi-qcom-qspi: Set an autosuspend delay of 250 ms
  spi: spi-qcom-qspi: Avoid clock setting if not needed
  spi: spi-qcom-qspi: Use OPP API to set clk/perf state
  firmware: qcom_scm: Add msm8994 compatible
  firmware: qcom_scm: Fix legacy convention SCM accessors
  <linux/of.h>: add stub for of_get_next_parent() to fix qcom build error
  dt-bindings: firmware: qcom: Add compatible for IPQ8074 SoC
  spi: spi-geni-qcom: Use OPP API to set clk/perf state
  tty: serial: qcom_geni_serial: Use OPP API to set clk/perf state
  spi: spi-qcom-qspi: Add interconnect support
  spi: spi-geni-qcom: Add interconnect support
  spi: spi-geni-qcom: Combine the clock setting code
  tty: serial: qcom_geni_serial: Add interconnect support
  ...

Link: https://lore.kernel.org/r/20200721044812.3429652-1-bjorn.andersson@linaro.orgSigned-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents e2837df6 03c900b3
......@@ -11,10 +11,12 @@ Required properties:
* "qcom,scm-apq8084"
* "qcom,scm-ipq4019"
* "qcom,scm-ipq806x"
* "qcom,scm-ipq8074"
* "qcom,scm-msm8660"
* "qcom,scm-msm8916"
* "qcom,scm-msm8960"
* "qcom,scm-msm8974"
* "qcom,scm-msm8994"
* "qcom,scm-msm8996"
* "qcom,scm-msm8998"
* "qcom,scm-sc7180"
......
......@@ -21,8 +21,10 @@ resources.
Definition: must be one of:
"qcom,rpm-apq8084"
"qcom,rpm-msm8916"
"qcom,rpm-msm8936"
"qcom,rpm-msm8974"
"qcom,rpm-msm8976"
"qcom,rpm-msm8994"
"qcom,rpm-msm8998"
"qcom,rpm-sdm660"
"qcom,rpm-qcs404"
......
......@@ -391,7 +391,7 @@ static int __qcom_scm_set_dload_mode(struct device *dev, bool enable)
desc.args[1] = enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0;
return qcom_scm_call(__scm->dev, &desc, NULL);
return qcom_scm_call_atomic(__scm->dev, &desc, NULL);
}
static void qcom_scm_set_download_mode(bool enable)
......@@ -650,7 +650,7 @@ int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val)
int ret;
ret = qcom_scm_call(__scm->dev, &desc, &res);
ret = qcom_scm_call_atomic(__scm->dev, &desc, &res);
if (ret >= 0)
*val = res.result[0];
......@@ -669,8 +669,7 @@ int qcom_scm_io_writel(phys_addr_t addr, unsigned int val)
.owner = ARM_SMCCC_OWNER_SIP,
};
return qcom_scm_call(__scm->dev, &desc, NULL);
return qcom_scm_call_atomic(__scm->dev, &desc, NULL);
}
EXPORT_SYMBOL(qcom_scm_io_writel);
......@@ -1151,6 +1150,7 @@ static const struct of_device_id qcom_scm_dt_match[] = {
SCM_HAS_IFACE_CLK |
SCM_HAS_BUS_CLK)
},
{ .compatible = "qcom,scm-msm8994" },
{ .compatible = "qcom,scm-msm8996" },
{ .compatible = "qcom,scm" },
{}
......
......@@ -557,6 +557,22 @@ static int geni_i2c_probe(struct platform_device *pdev)
gi2c->adap.dev.of_node = dev->of_node;
strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
ret = geni_icc_get(&gi2c->se, "qup-memory");
if (ret)
return ret;
/*
* Set the bus quota for core and cpu to a reasonable value for
* register access.
* Set quota for DDR based on bus speed.
*/
gi2c->se.icc_paths[GENI_TO_CORE].avg_bw = GENI_DEFAULT_BW;
gi2c->se.icc_paths[CPU_TO_GENI].avg_bw = GENI_DEFAULT_BW;
gi2c->se.icc_paths[GENI_TO_DDR].avg_bw = Bps_to_icc(gi2c->clk_freq_out);
ret = geni_icc_set_bw(&gi2c->se);
if (ret)
return ret;
ret = geni_se_resources_on(&gi2c->se);
if (ret) {
dev_err(dev, "Error turning on resources %d\n", ret);
......@@ -579,6 +595,10 @@ static int geni_i2c_probe(struct platform_device *pdev)
return ret;
}
ret = geni_icc_disable(&gi2c->se);
if (ret)
return ret;
dev_dbg(dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth);
gi2c->suspended = 1;
......@@ -623,7 +643,7 @@ static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev)
gi2c->suspended = 1;
}
return 0;
return geni_icc_disable(&gi2c->se);
}
static int __maybe_unused geni_i2c_runtime_resume(struct device *dev)
......@@ -631,6 +651,10 @@ static int __maybe_unused geni_i2c_runtime_resume(struct device *dev)
int ret;
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
ret = geni_icc_enable(&gi2c->se);
if (ret)
return ret;
ret = geni_se_resources_on(&gi2c->se);
if (ret)
return ret;
......
......@@ -266,11 +266,7 @@ int qcom_icc_bcm_voter_commit(struct bcm_voter *voter)
if (!commit_idx[0])
goto out;
ret = rpmh_invalidate(voter->dev);
if (ret) {
pr_err("Error invalidating RPMH client (%d)\n", ret);
goto out;
}
rpmh_invalidate(voter->dev);
ret = rpmh_write_batch(voter->dev, RPMH_ACTIVE_ONLY_STATE,
cmds, commit_idx);
......
......@@ -89,7 +89,7 @@ config QCOM_RMTFS_MEM
config QCOM_RPMH
bool "Qualcomm RPM-Hardened (RPMH) Communication"
depends on ARCH_QCOM && ARM64 || COMPILE_TEST
depends on ARCH_QCOM || COMPILE_TEST
help
Support for communication with the hardened-RPM blocks in
Qualcomm Technologies Inc (QTI) SoCs. RPMH communication uses an
......
......@@ -3,6 +3,7 @@
#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/console.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
......@@ -90,8 +91,14 @@ struct geni_wrapper {
struct device *dev;
void __iomem *base;
struct clk_bulk_data ahb_clks[NUM_AHB_CLKS];
struct geni_icc_path to_core;
};
static const char * const icc_path_names[] = {"qup-core", "qup-config",
"qup-memory"};
static struct geni_wrapper *earlycon_wrapper;
#define QUP_HW_VER_REG 0x4
/* Common SE registers */
......@@ -720,11 +727,132 @@ void geni_se_rx_dma_unprep(struct geni_se *se, dma_addr_t iova, size_t len)
}
EXPORT_SYMBOL(geni_se_rx_dma_unprep);
int geni_icc_get(struct geni_se *se, const char *icc_ddr)
{
int i, err;
const char *icc_names[] = {"qup-core", "qup-config", icc_ddr};
for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++) {
if (!icc_names[i])
continue;
se->icc_paths[i].path = devm_of_icc_get(se->dev, icc_names[i]);
if (IS_ERR(se->icc_paths[i].path))
goto err;
}
return 0;
err:
err = PTR_ERR(se->icc_paths[i].path);
if (err != -EPROBE_DEFER)
dev_err_ratelimited(se->dev, "Failed to get ICC path '%s': %d\n",
icc_names[i], err);
return err;
}
EXPORT_SYMBOL(geni_icc_get);
int geni_icc_set_bw(struct geni_se *se)
{
int i, ret;
for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++) {
ret = icc_set_bw(se->icc_paths[i].path,
se->icc_paths[i].avg_bw, se->icc_paths[i].avg_bw);
if (ret) {
dev_err_ratelimited(se->dev, "ICC BW voting failed on path '%s': %d\n",
icc_path_names[i], ret);
return ret;
}
}
return 0;
}
EXPORT_SYMBOL(geni_icc_set_bw);
void geni_icc_set_tag(struct geni_se *se, u32 tag)
{
int i;
for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++)
icc_set_tag(se->icc_paths[i].path, tag);
}
EXPORT_SYMBOL(geni_icc_set_tag);
/* To do: Replace this by icc_bulk_enable once it's implemented in ICC core */
int geni_icc_enable(struct geni_se *se)
{
int i, ret;
for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++) {
ret = icc_enable(se->icc_paths[i].path);
if (ret) {
dev_err_ratelimited(se->dev, "ICC enable failed on path '%s': %d\n",
icc_path_names[i], ret);
return ret;
}
}
return 0;
}
EXPORT_SYMBOL(geni_icc_enable);
int geni_icc_disable(struct geni_se *se)
{
int i, ret;
for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++) {
ret = icc_disable(se->icc_paths[i].path);
if (ret) {
dev_err_ratelimited(se->dev, "ICC disable failed on path '%s': %d\n",
icc_path_names[i], ret);
return ret;
}
}
return 0;
}
EXPORT_SYMBOL(geni_icc_disable);
void geni_remove_earlycon_icc_vote(void)
{
struct platform_device *pdev;
struct geni_wrapper *wrapper;
struct device_node *parent;
struct device_node *child;
if (!earlycon_wrapper)
return;
wrapper = earlycon_wrapper;
parent = of_get_next_parent(wrapper->dev->of_node);
for_each_child_of_node(parent, child) {
if (!of_device_is_compatible(child, "qcom,geni-se-qup"))
continue;
pdev = of_find_device_by_node(child);
if (!pdev)
continue;
wrapper = platform_get_drvdata(pdev);
icc_put(wrapper->to_core.path);
wrapper->to_core.path = NULL;
}
of_node_put(parent);
earlycon_wrapper = NULL;
}
EXPORT_SYMBOL(geni_remove_earlycon_icc_vote);
static int geni_se_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct geni_wrapper *wrapper;
struct console __maybe_unused *bcon;
bool __maybe_unused has_earlycon = false;
int ret;
wrapper = devm_kzalloc(dev, sizeof(*wrapper), GFP_KERNEL);
......@@ -747,6 +875,43 @@ static int geni_se_probe(struct platform_device *pdev)
}
}
#ifdef CONFIG_SERIAL_EARLYCON
for_each_console(bcon) {
if (!strcmp(bcon->name, "qcom_geni")) {
has_earlycon = true;
break;
}
}
if (!has_earlycon)
goto exit;
wrapper->to_core.path = devm_of_icc_get(dev, "qup-core");
if (IS_ERR(wrapper->to_core.path))
return PTR_ERR(wrapper->to_core.path);
/*
* Put minmal BW request on core clocks on behalf of early console.
* The vote will be removed earlycon exit function.
*
* Note: We are putting vote on each QUP wrapper instead only to which
* earlycon is connected because QUP core clock of different wrapper
* share same voltage domain. If core1 is put to 0, then core2 will
* also run at 0, if not voted. Default ICC vote will be removed ASA
* we touch any of the core clock.
* core1 = core2 = max(core1, core2)
*/
ret = icc_set_bw(wrapper->to_core.path, GENI_DEFAULT_BW,
GENI_DEFAULT_BW);
if (ret) {
dev_err(&pdev->dev, "%s: ICC BW voting failed for core: %d\n",
__func__, ret);
return ret;
}
if (of_get_compatible_child(pdev->dev.of_node, "qcom,geni-debug-uart"))
earlycon_wrapper = wrapper;
of_node_put(pdev->dev.of_node);
#endif
exit:
dev_set_drvdata(dev, wrapper);
dev_dbg(dev, "GENI SE Driver probed\n");
return devm_of_platform_populate(dev);
......
......@@ -175,13 +175,21 @@ static void write_tcs_reg(const struct rsc_drv *drv, int reg, int tcs_id,
static void write_tcs_reg_sync(const struct rsc_drv *drv, int reg, int tcs_id,
u32 data)
{
u32 new_data;
int i;
writel(data, tcs_reg_addr(drv, reg, tcs_id));
if (readl_poll_timeout_atomic(tcs_reg_addr(drv, reg, tcs_id), new_data,
new_data == data, 1, USEC_PER_SEC))
pr_err("%s: error writing %#x to %d:%#x\n", drv->name,
data, tcs_id, reg);
/*
* Wait until we read back the same value. Use a counter rather than
* ktime for timeout since this may be called after timekeeping stops.
*/
for (i = 0; i < USEC_PER_SEC; i++) {
if (readl(tcs_reg_addr(drv, reg, tcs_id)) == data)
return;
udelay(1);
}
pr_err("%s: error writing %#x to %d:%#x\n", drv->name,
data, tcs_id, reg);
}
/**
......@@ -1023,6 +1031,7 @@ static struct platform_driver rpmh_driver = {
.driver = {
.name = "rpmh",
.of_match_table = rpmh_drv_match,
.suppress_bind_attrs = true,
},
};
......
......@@ -497,7 +497,7 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr)
*
* Invalidate the sleep and wake values in batch_cache.
*/
int rpmh_invalidate(const struct device *dev)
void rpmh_invalidate(const struct device *dev)
{
struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);
struct batch_cache_req *req, *tmp;
......@@ -509,7 +509,5 @@ int rpmh_invalidate(const struct device *dev)
INIT_LIST_HEAD(&ctrlr->batch_cache);
ctrlr->dirty = true;
spin_unlock_irqrestore(&ctrlr->cache_lock, flags);
return 0;
}
EXPORT_SYMBOL(rpmh_invalidate);
......@@ -231,8 +231,10 @@ static void qcom_smd_rpm_remove(struct rpmsg_device *rpdev)
static const struct of_device_id qcom_smd_rpm_of_match[] = {
{ .compatible = "qcom,rpm-apq8084" },
{ .compatible = "qcom,rpm-msm8916" },
{ .compatible = "qcom,rpm-msm8936" },
{ .compatible = "qcom,rpm-msm8974" },
{ .compatible = "qcom,rpm-msm8976" },
{ .compatible = "qcom,rpm-msm8994" },
{ .compatible = "qcom,rpm-msm8996" },
{ .compatible = "qcom,rpm-msm8998" },
{ .compatible = "qcom,rpm-sdm660" },
......
......@@ -24,6 +24,7 @@
#define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff))
#define SMEM_SOCINFO_BUILD_ID_LENGTH 32
#define SMEM_SOCINFO_CHIP_ID_LENGTH 32
/*
* SMEM item id, used to acquire handles to respective
......@@ -121,6 +122,16 @@ struct socinfo {
__le32 chip_family;
__le32 raw_device_family;
__le32 raw_device_num;
/* Version 13 */
__le32 nproduct_id;
char chip_id[SMEM_SOCINFO_CHIP_ID_LENGTH];
/* Version 14 */
__le32 num_clusters;
__le32 ncluster_array_offset;
__le32 num_defective_parts;
__le32 ndefective_parts_array_offset;
/* Version 15 */
__le32 nmodem_supported;
};
#ifdef CONFIG_DEBUG_FS
......@@ -135,6 +146,12 @@ struct socinfo_params {
u32 raw_ver;
u32 hw_plat;
u32 fmt;
u32 nproduct_id;
u32 num_clusters;
u32 ncluster_array_offset;
u32 num_defective_parts;
u32 ndefective_parts_array_offset;
u32 nmodem_supported;
};
struct smem_image_version {
......@@ -202,8 +219,10 @@ static const struct soc_id soc_id[] = {
{ 310, "MSM8996AU" },
{ 311, "APQ8096AU" },
{ 312, "APQ8096SG" },
{ 318, "SDM630" },
{ 321, "SDM845" },
{ 341, "SDA845" },
{ 356, "SM8250" },
};
static const char *socinfo_machine(struct device *dev, unsigned int id)
......@@ -256,7 +275,10 @@ static int qcom_show_pmic_model(struct seq_file *seq, void *p)
if (model < 0)
return -EINVAL;
seq_printf(seq, "%s\n", pmic_models[model]);
if (model <= ARRAY_SIZE(pmic_models) && pmic_models[model])
seq_printf(seq, "%s\n", pmic_models[model]);
else
seq_printf(seq, "unknown (%d)\n", model);
return 0;
}
......@@ -272,9 +294,19 @@ static int qcom_show_pmic_die_revision(struct seq_file *seq, void *p)
return 0;
}
static int qcom_show_chip_id(struct seq_file *seq, void *p)
{
struct socinfo *socinfo = seq->private;
seq_printf(seq, "%s\n", socinfo->chip_id);
return 0;
}
QCOM_OPEN(build_id, qcom_show_build_id);
QCOM_OPEN(pmic_model, qcom_show_pmic_model);
QCOM_OPEN(pmic_die_rev, qcom_show_pmic_die_revision);
QCOM_OPEN(chip_id, qcom_show_chip_id);
#define DEFINE_IMAGE_OPS(type) \
static int show_image_##type(struct seq_file *seq, void *p) \
......@@ -312,7 +344,38 @@ static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo,
qcom_socinfo->info.fmt = __le32_to_cpu(info->fmt);
debugfs_create_x32("info_fmt", 0400, qcom_socinfo->dbg_root,
&qcom_socinfo->info.fmt);
switch (qcom_socinfo->info.fmt) {
case SOCINFO_VERSION(0, 15):
qcom_socinfo->info.nmodem_supported = __le32_to_cpu(info->nmodem_supported);
debugfs_create_u32("nmodem_supported", 0400, qcom_socinfo->dbg_root,
&qcom_socinfo->info.nmodem_supported);
/* Fall through */
case SOCINFO_VERSION(0, 14):
qcom_socinfo->info.num_clusters = __le32_to_cpu(info->num_clusters);
qcom_socinfo->info.ncluster_array_offset = __le32_to_cpu(info->ncluster_array_offset);
qcom_socinfo->info.num_defective_parts = __le32_to_cpu(info->num_defective_parts);
qcom_socinfo->info.ndefective_parts_array_offset = __le32_to_cpu(info->ndefective_parts_array_offset);
debugfs_create_u32("num_clusters", 0400, qcom_socinfo->dbg_root,
&qcom_socinfo->info.num_clusters);
debugfs_create_u32("ncluster_array_offset", 0400, qcom_socinfo->dbg_root,
&qcom_socinfo->info.ncluster_array_offset);
debugfs_create_u32("num_defective_parts", 0400, qcom_socinfo->dbg_root,
&qcom_socinfo->info.num_defective_parts);
debugfs_create_u32("ndefective_parts_array_offset", 0400, qcom_socinfo->dbg_root,
&qcom_socinfo->info.ndefective_parts_array_offset);
/* Fall through */
case SOCINFO_VERSION(0, 13):
qcom_socinfo->info.nproduct_id = __le32_to_cpu(info->nproduct_id);
debugfs_create_u32("nproduct_id", 0400, qcom_socinfo->dbg_root,
&qcom_socinfo->info.nproduct_id);
DEBUGFS_ADD(info, chip_id);
/* Fall through */
case SOCINFO_VERSION(0, 12):
qcom_socinfo->info.chip_family =
__le32_to_cpu(info->chip_family);
......
......@@ -7,6 +7,7 @@
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/qcom-geni-se.h>
#include <linux/spi/spi.h>
......@@ -76,7 +77,9 @@ struct spi_geni_master {
u32 tx_fifo_depth;
u32 fifo_width_bits;
u32 tx_wm;
u32 last_mode;
unsigned long cur_speed_hz;
unsigned long cur_sclk_hz;
unsigned int cur_bits_per_word;
unsigned int tx_rem_bytes;
unsigned int rx_rem_bytes;
......@@ -95,7 +98,6 @@ static int get_spi_clk_cfg(unsigned int speed_hz,
{
unsigned long sclk_freq;
unsigned int actual_hz;
struct geni_se *se = &mas->se;
int ret;
ret = geni_se_clk_freq_match(&mas->se,
......@@ -112,9 +114,12 @@ static int get_spi_clk_cfg(unsigned int speed_hz,
dev_dbg(mas->dev, "req %u=>%u sclk %lu, idx %d, div %d\n", speed_hz,
actual_hz, sclk_freq, *clk_idx, *clk_div);
ret = clk_set_rate(se->clk, sclk_freq);
ret = dev_pm_opp_set_rate(mas->dev, sclk_freq);
if (ret)
dev_err(mas->dev, "clk_set_rate failed %d\n", ret);
dev_err(mas->dev, "dev_pm_opp_set_rate failed %d\n", ret);
else
mas->cur_sclk_hz = sclk_freq;
return ret;
}
......@@ -177,8 +182,6 @@ static void spi_setup_word_len(struct spi_geni_master *mas, u16 mode,
struct geni_se *se = &mas->se;
u32 word_len;
word_len = readl(se->base + SE_SPI_WORD_LEN);
/*
* If bits_per_word isn't a byte aligned value, set the packing to be
* 1 SPI word per FIFO word.
......@@ -187,74 +190,94 @@ static void spi_setup_word_len(struct spi_geni_master *mas, u16 mode,
pack_words = mas->fifo_width_bits / bits_per_word;
else
pack_words = 1;
word_len &= ~WORD_LEN_MSK;
word_len |= ((bits_per_word - MIN_WORD_LEN) & WORD_LEN_MSK);
geni_se_config_packing(&mas->se, bits_per_word, pack_words, msb_first,
true, true);
word_len = (bits_per_word - MIN_WORD_LEN) & WORD_LEN_MSK;
writel(word_len, se->base + SE_SPI_WORD_LEN);
}
static int setup_fifo_params(struct spi_device *spi_slv,
struct spi_master *spi)
static int geni_spi_set_clock_and_bw(struct spi_geni_master *mas,
unsigned long clk_hz)
{
struct spi_geni_master *mas = spi_master_get_devdata(spi);
u32 clk_sel, m_clk_cfg, idx, div;
struct geni_se *se = &mas->se;
u32 loopback_cfg, cpol, cpha, demux_output_inv;
u32 demux_sel, clk_sel, m_clk_cfg, idx, div;
int ret;
loopback_cfg = readl(se->base + SE_SPI_LOOPBACK);
cpol = readl(se->base + SE_SPI_CPOL);
cpha = readl(se->base + SE_SPI_CPHA);
demux_output_inv = 0;
loopback_cfg &= ~LOOPBACK_MSK;
cpol &= ~CPOL;
cpha &= ~CPHA;
if (spi_slv->mode & SPI_LOOP)
loopback_cfg |= LOOPBACK_ENABLE;
if (spi_slv->mode & SPI_CPOL)
cpol |= CPOL;
if (spi_slv->mode & SPI_CPHA)
cpha |= CPHA;
if (spi_slv->mode & SPI_CS_HIGH)
demux_output_inv = BIT(spi_slv->chip_select);
demux_sel = spi_slv->chip_select;
mas->cur_speed_hz = spi_slv->max_speed_hz;
mas->cur_bits_per_word = spi_slv->bits_per_word;
if (clk_hz == mas->cur_speed_hz)
return 0;
ret = get_spi_clk_cfg(mas->cur_speed_hz, mas, &idx, &div);
ret = get_spi_clk_cfg(clk_hz, mas, &idx, &div);
if (ret) {
dev_err(mas->dev, "Err setting clks ret(%d) for %ld\n",
ret, mas->cur_speed_hz);
dev_err(mas->dev, "Err setting clk to %lu: %d\n", clk_hz, ret);
return ret;
}
/*
* SPI core clock gets configured with the requested frequency
* or the frequency closer to the requested frequency.
* For that reason requested frequency is stored in the
* cur_speed_hz and referred in the consecutive transfer instead
* of calling clk_get_rate() API.
*/
mas->cur_speed_hz = clk_hz;
clk_sel = idx & CLK_SEL_MSK;
m_clk_cfg = (div << CLK_DIV_SHFT) | SER_CLK_EN;
spi_setup_word_len(mas, spi_slv->mode, spi_slv->bits_per_word);
writel(loopback_cfg, se->base + SE_SPI_LOOPBACK);
writel(demux_sel, se->base + SE_SPI_DEMUX_SEL);
writel(cpha, se->base + SE_SPI_CPHA);
writel(cpol, se->base + SE_SPI_CPOL);
writel(demux_output_inv, se->base + SE_SPI_DEMUX_OUTPUT_INV);
writel(clk_sel, se->base + SE_GENI_CLK_SEL);
writel(m_clk_cfg, se->base + GENI_SER_M_CLK_CFG);
/* Set BW quota for CPU as driver supports FIFO mode only. */
se->icc_paths[CPU_TO_GENI].avg_bw = Bps_to_icc(mas->cur_speed_hz);
ret = geni_icc_set_bw(se);
if (ret)
return ret;
return 0;
}
static int setup_fifo_params(struct spi_device *spi_slv,
struct spi_master *spi)
{
struct spi_geni_master *mas = spi_master_get_devdata(spi);
struct geni_se *se = &mas->se;
u32 loopback_cfg = 0, cpol = 0, cpha = 0, demux_output_inv = 0;
u32 demux_sel;
if (mas->last_mode != spi_slv->mode) {
if (spi_slv->mode & SPI_LOOP)
loopback_cfg = LOOPBACK_ENABLE;
if (spi_slv->mode & SPI_CPOL)
cpol = CPOL;
if (spi_slv->mode & SPI_CPHA)
cpha = CPHA;
if (spi_slv->mode & SPI_CS_HIGH)
demux_output_inv = BIT(spi_slv->chip_select);
demux_sel = spi_slv->chip_select;
mas->cur_bits_per_word = spi_slv->bits_per_word;
spi_setup_word_len(mas, spi_slv->mode, spi_slv->bits_per_word);
writel(loopback_cfg, se->base + SE_SPI_LOOPBACK);
writel(demux_sel, se->base + SE_SPI_DEMUX_SEL);
writel(cpha, se->base + SE_SPI_CPHA);
writel(cpol, se->base + SE_SPI_CPOL);
writel(demux_output_inv, se->base + SE_SPI_DEMUX_OUTPUT_INV);
mas->last_mode = spi_slv->mode;
}
return geni_spi_set_clock_and_bw(mas, spi_slv->max_speed_hz);
}
static int spi_geni_prepare_message(struct spi_master *spi,
struct spi_message *spi_msg)
{
int ret;
struct spi_geni_master *mas = spi_master_get_devdata(spi);
struct geni_se *se = &mas->se;
geni_se_select_mode(se, GENI_SE_FIFO);
ret = setup_fifo_params(spi_msg->spi, spi);
if (ret)
dev_err(mas->dev, "Couldn't select mode %d\n", ret);
......@@ -295,6 +318,8 @@ static int spi_geni_init(struct spi_geni_master *mas)
else
mas->oversampling = 1;
geni_se_select_mode(se, GENI_SE_FIFO);
pm_runtime_put(mas->dev);
return 0;
}
......@@ -306,6 +331,7 @@ static void setup_fifo_xfer(struct spi_transfer *xfer,
u32 m_cmd = 0;
u32 spi_tx_cfg, len;
struct geni_se *se = &mas->se;
int ret;
spi_tx_cfg = readl(se->base + SE_SPI_TRANS_CFG);
if (xfer->bits_per_word != mas->cur_bits_per_word) {
......@@ -314,29 +340,9 @@ static void setup_fifo_xfer(struct spi_transfer *xfer,
}
/* Speed and bits per word can be overridden per transfer */
if (xfer->speed_hz != mas->cur_speed_hz) {
int ret;
u32 clk_sel, m_clk_cfg;
unsigned int idx, div;
ret = get_spi_clk_cfg(xfer->speed_hz, mas, &idx, &div);
if (ret) {
dev_err(mas->dev, "Err setting clks:%d\n", ret);
return;
}
/*
* SPI core clock gets configured with the requested frequency
* or the frequency closer to the requested frequency.
* For that reason requested frequency is stored in the
* cur_speed_hz and referred in the consecutive transfer instead
* of calling clk_get_rate() API.
*/
mas->cur_speed_hz = xfer->speed_hz;
clk_sel = idx & CLK_SEL_MSK;
m_clk_cfg = (div << CLK_DIV_SHFT) | SER_CLK_EN;
writel(clk_sel, se->base + SE_GENI_CLK_SEL);
writel(m_clk_cfg, se->base + GENI_SER_M_CLK_CFG);
}
ret = geni_spi_set_clock_and_bw(mas, xfer->speed_hz);
if (ret)
return;
mas->tx_rem_bytes = 0;
mas->rx_rem_bytes = 0;
......@@ -561,6 +567,17 @@ static int spi_geni_probe(struct platform_device *pdev)
mas->se.wrapper = dev_get_drvdata(dev->parent);
mas->se.base = base;
mas->se.clk = clk;
mas->se.opp_table = dev_pm_opp_set_clkname(&pdev->dev, "se");
if (IS_ERR(mas->se.opp_table))
return PTR_ERR(mas->se.opp_table);
/* OPP table is optional */
ret = dev_pm_opp_of_add_table(&pdev->dev);
if (!ret) {
mas->se.has_opp_table = true;
} else if (ret != -ENODEV) {
dev_err(&pdev->dev, "invalid OPP table in device tree\n");
return ret;
}
spi->bus_num = -1;
spi->dev.of_node = dev->of_node;
......@@ -578,6 +595,17 @@ static int spi_geni_probe(struct platform_device *pdev)
spin_lock_init(&mas->lock);
pm_runtime_enable(dev);
ret = geni_icc_get(&mas->se, NULL);
if (ret)
goto spi_geni_probe_runtime_disable;
/* Set the bus quota to a reasonable value for register access */
mas->se.icc_paths[GENI_TO_CORE].avg_bw = Bps_to_icc(CORE_2X_50_MHZ);
mas->se.icc_paths[CPU_TO_GENI].avg_bw = GENI_DEFAULT_BW;
ret = geni_icc_set_bw(&mas->se);
if (ret)
goto spi_geni_probe_runtime_disable;
ret = spi_geni_init(mas);
if (ret)
goto spi_geni_probe_runtime_disable;
......@@ -596,6 +624,9 @@ static int spi_geni_probe(struct platform_device *pdev)
spi_geni_probe_runtime_disable:
pm_runtime_disable(dev);
spi_master_put(spi);
if (mas->se.has_opp_table)
dev_pm_opp_of_remove_table(&pdev->dev);
dev_pm_opp_put_clkname(mas->se.opp_table);
return ret;
}
......@@ -609,6 +640,9 @@ static int spi_geni_remove(struct platform_device *pdev)
free_irq(mas->irq, spi);
pm_runtime_disable(&pdev->dev);
if (mas->se.has_opp_table)
dev_pm_opp_of_remove_table(&pdev->dev);
dev_pm_opp_put_clkname(mas->se.opp_table);
return 0;
}
......@@ -616,16 +650,33 @@ static int __maybe_unused spi_geni_runtime_suspend(struct device *dev)
{
struct spi_master *spi = dev_get_drvdata(dev);
struct spi_geni_master *mas = spi_master_get_devdata(spi);
int ret;
/* Drop the performance state vote */
dev_pm_opp_set_rate(dev, 0);
ret = geni_se_resources_off(&mas->se);
if (ret)
return ret;
return geni_se_resources_off(&mas->se);
return geni_icc_disable(&mas->se);
}
static int __maybe_unused spi_geni_runtime_resume(struct device *dev)
{
struct spi_master *spi = dev_get_drvdata(dev);
struct spi_geni_master *mas = spi_master_get_devdata(spi);
int ret;
ret = geni_icc_enable(&mas->se);
if (ret)
return ret;
ret = geni_se_resources_on(&mas->se);
if (ret)
return ret;
return geni_se_resources_on(&mas->se);
return dev_pm_opp_set_rate(mas->dev, mas->cur_sclk_hz);
}
static int __maybe_unused spi_geni_suspend(struct device *dev)
......
......@@ -2,12 +2,14 @@
// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
#include <linux/clk.h>
#include <linux/interconnect.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <linux/pm_opp.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
......@@ -139,7 +141,11 @@ struct qcom_qspi {
struct device *dev;
struct clk_bulk_data *clks;
struct qspi_xfer xfer;
/* Lock to protect xfer and IRQ accessed registers */
struct icc_path *icc_path_cpu_to_qspi;
struct opp_table *opp_table;
bool has_opp_table;
unsigned long last_speed;
/* Lock to protect data accessed by IRQs */
spinlock_t lock;
};
......@@ -221,6 +227,38 @@ static void qcom_qspi_handle_err(struct spi_master *master,
spin_unlock_irqrestore(&ctrl->lock, flags);
}
static int qcom_qspi_set_speed(struct qcom_qspi *ctrl, unsigned long speed_hz)
{
int ret;
unsigned int avg_bw_cpu;
if (speed_hz == ctrl->last_speed)
return 0;
/* In regular operation (SBL_EN=1) core must be 4x transfer clock */
ret = dev_pm_opp_set_rate(ctrl->dev, speed_hz * 4);
if (ret) {
dev_err(ctrl->dev, "Failed to set core clk %d\n", ret);
return ret;
}
/*
* Set BW quota for CPU as driver supports FIFO mode only.
* We don't have explicit peak requirement so keep it equal to avg_bw.
*/
avg_bw_cpu = Bps_to_icc(speed_hz);
ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, avg_bw_cpu, avg_bw_cpu);
if (ret) {
dev_err(ctrl->dev, "%s: ICC BW voting failed for cpu: %d\n",
__func__, ret);
return ret;
}
ctrl->last_speed = speed_hz;
return 0;
}
static int qcom_qspi_transfer_one(struct spi_master *master,
struct spi_device *slv,
struct spi_transfer *xfer)
......@@ -234,12 +272,9 @@ static int qcom_qspi_transfer_one(struct spi_master *master,
if (xfer->speed_hz)
speed_hz = xfer->speed_hz;
/* In regular operation (SBL_EN=1) core must be 4x transfer clock */
ret = clk_set_rate(ctrl->clks[QSPI_CLK_CORE].clk, speed_hz * 4);
if (ret) {
dev_err(ctrl->dev, "Failed to set core clk %d\n", ret);
ret = qcom_qspi_set_speed(ctrl, speed_hz);
if (ret)
return ret;
}
spin_lock_irqsave(&ctrl->lock, flags);
......@@ -458,6 +493,29 @@ static int qcom_qspi_probe(struct platform_device *pdev)
if (ret)
goto exit_probe_master_put;
ctrl->icc_path_cpu_to_qspi = devm_of_icc_get(dev, "qspi-config");
if (IS_ERR(ctrl->icc_path_cpu_to_qspi)) {
ret = PTR_ERR(ctrl->icc_path_cpu_to_qspi);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get cpu path: %d\n", ret);
goto exit_probe_master_put;
}
/* Set BW vote for register access */
ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, Bps_to_icc(1000),
Bps_to_icc(1000));
if (ret) {
dev_err(ctrl->dev, "%s: ICC BW voting failed for cpu: %d\n",
__func__, ret);
goto exit_probe_master_put;
}
ret = icc_disable(ctrl->icc_path_cpu_to_qspi);
if (ret) {
dev_err(ctrl->dev, "%s: ICC disable failed for cpu: %d\n",
__func__, ret);
goto exit_probe_master_put;
}
ret = platform_get_irq(pdev, 0);
if (ret < 0)
goto exit_probe_master_put;
......@@ -481,6 +539,22 @@ static int qcom_qspi_probe(struct platform_device *pdev)
master->handle_err = qcom_qspi_handle_err;
master->auto_runtime_pm = true;
ctrl->opp_table = dev_pm_opp_set_clkname(&pdev->dev, "core");
if (IS_ERR(ctrl->opp_table)) {
ret = PTR_ERR(ctrl->opp_table);
goto exit_probe_master_put;
}
/* OPP table is optional */
ret = dev_pm_opp_of_add_table(&pdev->dev);
if (!ret) {
ctrl->has_opp_table = true;
} else if (ret != -ENODEV) {
dev_err(&pdev->dev, "invalid OPP table in device tree\n");
goto exit_probe_master_put;
}
pm_runtime_use_autosuspend(dev);
pm_runtime_set_autosuspend_delay(dev, 250);
pm_runtime_enable(dev);
ret = spi_register_master(master);
......@@ -488,6 +562,9 @@ static int qcom_qspi_probe(struct platform_device *pdev)
return 0;
pm_runtime_disable(dev);
if (ctrl->has_opp_table)
dev_pm_opp_of_remove_table(&pdev->dev);
dev_pm_opp_put_clkname(ctrl->opp_table);
exit_probe_master_put:
spi_master_put(master);
......@@ -498,11 +575,15 @@ static int qcom_qspi_probe(struct platform_device *pdev)
static int qcom_qspi_remove(struct platform_device *pdev)
{
struct spi_master *master = platform_get_drvdata(pdev);
struct qcom_qspi *ctrl = spi_master_get_devdata(master);
/* Unregister _before_ disabling pm_runtime() so we stop transfers */
spi_unregister_master(master);
pm_runtime_disable(&pdev->dev);
if (ctrl->has_opp_table)
dev_pm_opp_of_remove_table(&pdev->dev);
dev_pm_opp_put_clkname(ctrl->opp_table);
return 0;
}
......@@ -511,9 +592,19 @@ static int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev)
{
struct spi_master *master = dev_get_drvdata(dev);
struct qcom_qspi *ctrl = spi_master_get_devdata(master);
int ret;
/* Drop the performance state vote */
dev_pm_opp_set_rate(dev, 0);
clk_bulk_disable_unprepare(QSPI_NUM_CLKS, ctrl->clks);
ret = icc_disable(ctrl->icc_path_cpu_to_qspi);
if (ret) {
dev_err_ratelimited(ctrl->dev, "%s: ICC disable failed for cpu: %d\n",
__func__, ret);
return ret;
}
return 0;
}
......@@ -521,8 +612,20 @@ static int __maybe_unused qcom_qspi_runtime_resume(struct device *dev)
{
struct spi_master *master = dev_get_drvdata(dev);
struct qcom_qspi *ctrl = spi_master_get_devdata(master);
int ret;
ret = icc_enable(ctrl->icc_path_cpu_to_qspi);
if (ret) {
dev_err_ratelimited(ctrl->dev, "%s: ICC enable failed for cpu: %d\n",
__func__, ret);
return ret;
}
ret = clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks);
if (ret)
return ret;
return clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks);
return dev_pm_opp_set_rate(dev, ctrl->last_speed * 4);
}
static int __maybe_unused qcom_qspi_suspend(struct device *dev)
......
This diff is collapsed.
......@@ -630,6 +630,11 @@ static inline struct device_node *of_get_parent(const struct device_node *node)
return NULL;
}
static inline struct device_node *of_get_next_parent(struct device_node *node)
{
return NULL;
}
static inline struct device_node *of_get_next_child(
const struct device_node *node, struct device_node *prev)
{
......
......@@ -6,6 +6,8 @@
#ifndef _LINUX_QCOM_GENI_SE
#define _LINUX_QCOM_GENI_SE
#include <linux/interconnect.h>
/* Transfer mode supported by GENI Serial Engines */
enum geni_se_xfer_mode {
GENI_SE_INVALID,
......@@ -25,6 +27,17 @@ enum geni_se_protocol_type {
struct geni_wrapper;
struct clk;
enum geni_icc_path_index {
GENI_TO_CORE,
CPU_TO_GENI,
GENI_TO_DDR
};
struct geni_icc_path {
struct icc_path *path;
unsigned int avg_bw;
};
/**
* struct geni_se - GENI Serial Engine
* @base: Base Address of the Serial Engine's register block
......@@ -33,6 +46,9 @@ struct clk;
* @clk: Handle to the core serial engine clock
* @num_clk_levels: Number of valid clock levels in clk_perf_tbl
* @clk_perf_tbl: Table of clock frequency input to serial engine clock
* @icc_paths: Array of ICC paths for SE
* @opp_table: Pointer to the OPP table
* @has_opp_table: Specifies if the SE has an OPP table
*/
struct geni_se {
void __iomem *base;
......@@ -41,6 +57,9 @@ struct geni_se {
struct clk *clk;
unsigned int num_clk_levels;
unsigned long *clk_perf_tbl;
struct geni_icc_path icc_paths[3];
struct opp_table *opp_table;
bool has_opp_table;
};
/* Common SE registers */
......@@ -229,6 +248,21 @@ struct geni_se {
#define GENI_SE_VERSION_MINOR(ver) ((ver & HW_VER_MINOR_MASK) >> HW_VER_MINOR_SHFT)
#define GENI_SE_VERSION_STEP(ver) (ver & HW_VER_STEP_MASK)
/*
* Define bandwidth thresholds that cause the underlying Core 2X interconnect
* clock to run at the named frequency. These baseline values are recommended
* by the hardware team, and are not dynamically scaled with GENI bandwidth
* beyond basic on/off.
*/
#define CORE_2X_19_2_MHZ 960
#define CORE_2X_50_MHZ 2500
#define CORE_2X_100_MHZ 5000
#define CORE_2X_150_MHZ 7500
#define CORE_2X_200_MHZ 10000
#define CORE_2X_236_MHZ 16383
#define GENI_DEFAULT_BW Bps_to_icc(1000)
#if IS_ENABLED(CONFIG_QCOM_GENI_SE)
u32 geni_se_get_qup_hw_version(struct geni_se *se);
......@@ -416,5 +450,16 @@ int geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len,
void geni_se_tx_dma_unprep(struct geni_se *se, dma_addr_t iova, size_t len);
void geni_se_rx_dma_unprep(struct geni_se *se, dma_addr_t iova, size_t len);
int geni_icc_get(struct geni_se *se, const char *icc_ddr);
int geni_icc_set_bw(struct geni_se *se);
void geni_icc_set_tag(struct geni_se *se, u32 tag);
int geni_icc_enable(struct geni_se *se);
int geni_icc_disable(struct geni_se *se);
void geni_remove_earlycon_icc_vote(void);
#endif
#endif
......@@ -20,7 +20,7 @@ int rpmh_write_async(const struct device *dev, enum rpmh_state state,
int rpmh_write_batch(const struct device *dev, enum rpmh_state state,
const struct tcs_cmd *cmd, u32 *n);
int rpmh_invalidate(const struct device *dev);
void rpmh_invalidate(const struct device *dev);
#else
......@@ -38,8 +38,9 @@ static inline int rpmh_write_batch(const struct device *dev,
const struct tcs_cmd *cmd, u32 *n)
{ return -ENODEV; }
static inline int rpmh_invalidate(const struct device *dev)
{ return -ENODEV; }
static inline void rpmh_invalidate(const struct device *dev)
{
}
#endif /* CONFIG_QCOM_RPMH */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment