On Sun 13 May 00:01 PDT 2018, Rohit kumar wrote: > This adds Qualcomm ADSP PIL driver support for SDM845 with ADSP bootup > and shutdown operation handled from Application Processor SubSystem(APSS). > > Signed-off-by: Rohit kumar <rohitkr@xxxxxxxxxxxxxx> > Signed-off-by: RajendraBabu Medisetti <rajendrabm@xxxxxxxxxxxxxx> > Signed-off-by: Krishnamurthy Renu <krishnamurthy.renu@xxxxxxxxxxxxxx> > --- > .../devicetree/bindings/remoteproc/qcom,adsp.txt | 1 + > drivers/remoteproc/Makefile | 3 +- > drivers/remoteproc/qcom_adsp_pil.c | 122 ++++----- > drivers/remoteproc/qcom_adsp_pil.h | 86 ++++++ > drivers/remoteproc/qcom_adsp_pil_sdm845.c | 304 +++++++++++++++++++++ > 5 files changed, 454 insertions(+), 62 deletions(-) > create mode 100644 drivers/remoteproc/qcom_adsp_pil.h > create mode 100644 drivers/remoteproc/qcom_adsp_pil_sdm845.c > > diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt > index 728e419..a9fe033 100644 > --- a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt > +++ b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt > @@ -10,6 +10,7 @@ on the Qualcomm ADSP Hexagon core. > "qcom,msm8974-adsp-pil" > "qcom,msm8996-adsp-pil" > "qcom,msm8996-slpi-pil" > + "qcom,sdm845-apss-adsp-pil" Afaict there's nothing in this binding that ties this to the apss, so I don't think we should base the compatible on this. The differentiation is PAS vs non-PAS; so let's start naming the PAS variants "qcom,platform-subsystem-pas" and the non-PAS "qcom,platform-subsystem-pil" instead. I.e. please make this "qcom,sdm845-adsp-pil". More importantly, any resources such as clocks or reset lines should come from DT and as such you need to extend the binding quite a bit - which I suggest you do by introducing a new binding document. > > - interrupts-extended: > Usage: required > diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile > index 02627ed..759831b 100644 > --- a/drivers/remoteproc/Makefile > +++ b/drivers/remoteproc/Makefile > @@ -14,7 +14,8 @@ obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o > obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o > obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o > obj-$(CONFIG_KEYSTONE_REMOTEPROC) += keystone_remoteproc.o > -obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o > +obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp.o > +qcom_adsp-objs += qcom_adsp_pil.o qcom_adsp_pil_sdm845.o > obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o > obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o > obj-$(CONFIG_QCOM_SYSMON) += qcom_sysmon.o > diff --git a/drivers/remoteproc/qcom_adsp_pil.c b/drivers/remoteproc/qcom_adsp_pil.c I get the feeling that the main reason for modifying this file is its name, not that it reduces the complexity of the final solution. I definitely think it's cleaner to have some structural duplication and keep this driver handling the various PAS based remoteprocs. Please see the RFC series I posted reducing the duplication between the various "Q6V5 drivers". [..] > diff --git a/drivers/remoteproc/qcom_adsp_pil.h b/drivers/remoteproc/qcom_adsp_pil.h [..] > +static inline void update_bits(void *reg, u32 mask_val, u32 set_val, u32 shift) > +{ > + u32 reg_val = 0; > + > + reg_val = ((readl(reg)) & ~mask_val) | ((set_val << shift) & mask_val); > + writel(reg_val, reg); > +} > + > +static inline unsigned int read_bit(void *reg, u32 mask, int shift) > +{ > + return ((readl(reg) & mask) >> shift); > +} I don't like these helper functions, their prototype is nonstandard and makes it really hard to read all the calling code. I would prefer if you just inline the operations directly, to make it clearer what's going on in each case - if not then at least follow the prototype of e.g. regmap_udpate_bits(), which people might be used to. > + > +#endif > diff --git a/drivers/remoteproc/qcom_adsp_pil_sdm845.c b/drivers/remoteproc/qcom_adsp_pil_sdm845.c > new file mode 100644 > index 0000000..7518385 > --- /dev/null > +++ b/drivers/remoteproc/qcom_adsp_pil_sdm845.c > @@ -0,0 +1,304 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Qualcomm APSS Based ADSP bootup/shutdown ops for SDM845. > + * Copyright (c) 2018, The Linux Foundation. All rights reserved. > + */ > + > +#include <linux/delay.h> > +#include <linux/io.h> > + > +#include "qcom_adsp_pil.h" > + > +/* set values */ > +#define CLK_ENABLE 0x1 > +#define CLK_DISABLE 0x0 Just write 0 and 1 in the code, it saves future readers the trouble of having to remember if these are special in any way. > +/* time out value */ > +#define ACK_TIMEOUT 200000 This is currently given in the rather awkward unit of 5uS. As it's input to what should have been a call to readl_poll_timeout() please express it in micro seconds. > +/* mask values */ > +#define CLK_MASK GENMASK(4, 0) > +#define EVB_MASK GENMASK(27, 4) > +#define SPIN_CLKOFF_MASK BIT(31) > +#define AUDIO_SYNC_RESET_MASK BIT(2) > +#define CLK_ENABLE_MASK BIT(0) > +#define HAL_CLK_MASK BIT(1) > +/* GCC register offsets */ > +#define GCC_BASE 0x00147000 This doesn't belong here, expose the resource from the gcc driver using existing frameworks. > +#define SWAY_CBCR_OFFSET 0x00000008 > +/*LPASS register base address and offsets*/ > +#define LPASS_BASE 0x17000000 This should be in the lpass clock driver. > +#define AON_CBCR_OFFSET 0x00014098 > +#define CMD_RCGR_OFFSET 0x00014000 > +#define CFG_RCGR_OFFSET 0x00014004 > +#define AHBS_AON_CBCR_OFFSET 0x00033000 > +#define AHBM_AON_CBCR_OFFSET 0x00026000 > +/*QDSP6SS register base address and offsets*/ > +#define QDSP6SS_BASE 0x17300000 This should come from the reg property in DT. > +#define RST_EVB_OFFSET 0x00000010 > +#define SLEEP_CBCR_OFFSET 0x0000003C > +#define XO_CBCR_OFFSET 0x00000038 > +#define CORE_CBCR_OFFSET 0x00000020 > +#define CORE_START_OFFSET 0x00000400 > +#define BOOT_CMD_OFFSET 0x00000404 > +#define BOOT_STATUS_OFFSET 0x00000408 > +#define RET_CFG_OFFSET 0x0000001C > +/*TCSR register base address and offsets*/ > +#define TCSR_BASE 0x01F62000 Look at how we deal with TCSR in the MSS driver instead. > +#define TCSR_LPASS_MASTER_IDLE_OFFSET 0x00000008 > +#define TCSR_LPASS_HALTACK_OFFSET 0x00000004 > +#define TCSR_LPASS_PWR_ON_OFFSET 0x00000010 > +#define TCSR_LPASS_HALTREQ_OFFSET 0X00000000 > + > +#define RPMH_PDC_SYNC_RESET_ADDR 0x0B2E0100 > +#define AOSS_CC_LPASS_RESTART_ADDR 0x0C2D0000 Please expose these from an appropriate driver using appropriate frameworks. > + > +struct sdm845_reg { > + void __iomem *gcc_base; > + void __iomem *lpass_base; > + void __iomem *qdsp6ss_base; > + void __iomem *tcsr_base; > + void __iomem *pdc_sync; > + void __iomem *cc_lpass; I expect to see only qdsp6ss_base remain here. > +}; > + > +static int sdm845_map_registers(struct qcom_adsp *adsp, > + struct platform_device *pdev) > +{ > + struct sdm845_reg *reg; > + > + adsp->priv_reg = devm_kzalloc(&pdev->dev, sizeof(struct sdm845_reg), > + GFP_KERNEL); > + if (!adsp->priv_reg) > + return -ENOMEM; > + > + reg = adsp->priv_reg; > + > + reg->gcc_base = devm_ioremap(adsp->dev, GCC_BASE, 0xc); > + if (!reg->gcc_base) { > + dev_err(adsp->dev, "%s: failed to map GCC base registers\n", > + __func__); > + return -ENOMEM; > + } > + > + reg->lpass_base = devm_ioremap(adsp->dev, LPASS_BASE, 0x8E004); > + if (!reg->lpass_base) { > + dev_err(adsp->dev, "%s: failed to map LPASS base registers\n", > + __func__); > + return -ENOMEM; > + } > + reg->qdsp6ss_base = devm_ioremap(adsp->dev, QDSP6SS_BASE, 0x40c); > + if (!reg->qdsp6ss_base) { > + dev_err(adsp->dev, "%s: failed to map QDSP6SS base registers\n", > + __func__); > + return -ENOMEM; > + } > + reg->tcsr_base = devm_ioremap(adsp->dev, TCSR_BASE, 0x14); > + if (!reg->tcsr_base) { > + dev_err(adsp->dev, "%s: failed to map TCSR base registers\n", > + __func__); > + return -ENOMEM; > + } > + reg->pdc_sync = devm_ioremap(adsp->dev, RPMH_PDC_SYNC_RESET_ADDR, 0x4); > + if (!reg->pdc_sync) { > + dev_err(adsp->dev, "%s: failed to map RPMH_PDC_SYNC_RESET register\n", > + __func__); > + return -ENOMEM; > + } > + reg->cc_lpass = devm_ioremap(adsp->dev, AOSS_CC_LPASS_RESTART_ADDR, > + 0x4); > + if (!reg->cc_lpass) { > + dev_err(adsp->dev, "%s:failed to map AOSS_CC_LPASS_RESTART register\n", > + __func__); > + return -ENOMEM; > + } > + > + return 0; > +} > + > +static int clk_enable_spin(void *reg, int read_shift, int write_shift) This should be in the appropriate clock drivers. > +{ > + u32 maxDelay = 500; > + u32 val; > + > + update_bits(reg, CLK_ENABLE_MASK, CLK_ENABLE, write_shift); > + val = readl(reg); > + if (!(readl(reg) & HAL_CLK_MASK)) { > + /* > + * wait for disabling of HW signal CLK_OFF to confirm that > + * clock is actually ON. > + */ > + while (maxDelay-- && read_bit(reg, SPIN_CLKOFF_MASK, > + read_shift)) > + udelay(1); > + } > + if (!maxDelay) { > + pr_err("%s: fail to update register = %p\n", __func__, reg); > + return -ETIMEDOUT; > + } > + return 0; > +} > + > +static int sdm845_adsp_clk_enable(struct qcom_adsp *adsp) > +{ > + u32 ret; > + u32 maxDelay = 100; > + struct sdm845_reg *reg = adsp->priv_reg; > + > + /* Enable SWAY clock */ > + ret = clk_enable_spin(reg->gcc_base + SWAY_CBCR_OFFSET, CLK_MASK, 0x0); > + if (ret) > + return ret; > + > + /* Enable LPASS AHB AON Bus */ > + ret = clk_enable_spin(reg->lpass_base + AON_CBCR_OFFSET, CLK_MASK, 0x0); > + if (ret) > + return ret; > + > + /* Set the AON clock root to be sourced by XO */ > + writel(CLK_DISABLE, reg->lpass_base + CFG_RCGR_OFFSET); > + writel(CLK_ENABLE, reg->lpass_base + CMD_RCGR_OFFSET); > + > + while (read_bit((reg->lpass_base + CMD_RCGR_OFFSET), CLK_ENABLE, 0) > + && maxDelay--) > + udelay(2); > + > + if (!maxDelay) { > + pr_err("%s: fail to enable CMD_RCGR clock\n", __func__); > + return -ETIMEDOUT; > + } > + > + /* Enable the QDSP6SS AHBM and AHBS clocks */ > + ret = clk_enable_spin(reg->lpass_base + AHBS_AON_CBCR_OFFSET, > + CLK_MASK, 0x0); > + if (ret) > + return ret; > + ret = clk_enable_spin(reg->lpass_base + AHBM_AON_CBCR_OFFSET, > + CLK_MASK, 0x0); > + if (ret) > + return ret; Above should be calls to the clock framework. > + > + /* Turn on the XO clock, required to boot FSM */ > + update_bits(reg->qdsp6ss_base + XO_CBCR_OFFSET, CLK_ENABLE_MASK, > + CLK_ENABLE, 0x0); > + > + /* Enable the QDSP6SS sleep clock for the QDSP6 watchdog enablement */ > + update_bits(reg->qdsp6ss_base + SLEEP_CBCR_OFFSET, > + CLK_ENABLE_MASK, CLK_ENABLE, 0x0); > + > + /* Configure QDSP6 core CBC to enable clock */ > + update_bits(reg->qdsp6ss_base + CORE_CBCR_OFFSET, CLK_ENABLE_MASK, > + CLK_ENABLE, 0x0); > + return 0; > +} > + > +static int sdm845_adsp_reset(struct qcom_adsp *adsp) > +{ > + u32 timeout = ACK_TIMEOUT; > + struct sdm845_reg *reg = adsp->priv_reg; > + > + /* De-assert QDSP6 stop core. QDSP6 will execute after out of reset */ > + update_bits(reg->qdsp6ss_base + CORE_START_OFFSET, > + CLK_ENABLE_MASK, CLK_ENABLE, 0x0); > + /* Trigger boot FSM to start QDSP6 */ > + writel(CLK_ENABLE, reg->qdsp6ss_base + BOOT_CMD_OFFSET); > + > + /* Wait for core to come out of reset */ > + while ((!(readl(reg->qdsp6ss_base + > + BOOT_STATUS_OFFSET))) && (timeout-- > 0)) > + udelay(5); Use readl_poll_timeout() from linux/iopoll.h > + > + if (!timeout) > + return -ETIMEDOUT; > + > + return 0; > +} > + > +static int sdm845_bringup(struct qcom_adsp *adsp) This is called "start" in other places, please use existing naming convention to make your code feel familiar to people reading other drivers. > +{ > + u32 ret; ret is exclusively used to store data of the type "int". > + struct sdm845_reg *reg = adsp->priv_reg; > + > + ret = sdm845_adsp_clk_enable(adsp); > + if (ret) { > + dev_err(adsp->dev, "%s: sdm845_adsp_clk_enable failed\n", > + __func__); > + return ret; > + } > + /* Program boot address */ > + update_bits(reg->qdsp6ss_base + RST_EVB_OFFSET, > + EVB_MASK, (adsp->mem_phys) >> 8, 0x4); In the WCSS PIL driver this is: writel(rproc->bootaddr >> 4, wcss->reg_base + QDSP6SS_RST_EVB); Which I think is the same as you're doing here, although you're shifting 8 bits right and then 4 (base 16) to the left. > + > + /* Wait for addresses to be programmed before starting adsp */ That's not what mb() does, it just ensures that any read and writes coming after this point isn't reordered with any operations before it. And as sdm845_adsp_reset() used writel() there is already a wmb() there, so you can drop this one. > + mb(); > + ret = sdm845_adsp_reset(adsp); > + if (ret) > + dev_err(adsp->dev, "%s: De-assert QDSP6 out of reset failed\n", > + __func__); This string is unique in the kernel, so you don't need __func__. > + return ret; > +} > + > +static int sdm845_bringdown(struct qcom_adsp *adsp) > +{ > + u32 acktimeout = ACK_TIMEOUT; > + u32 temp; We know this is a temporary variable, name it "val" as we do in the other functions. > + struct sdm845_reg *reg = adsp->priv_reg; > + > + /* Reset the retention logic */ > + update_bits(reg->qdsp6ss_base + RET_CFG_OFFSET, > + CLK_ENABLE_MASK, CLK_ENABLE, 0x0); > + /* Disable the slave way clock to LPASS */ > + update_bits(reg->gcc_base + SWAY_CBCR_OFFSET, > + CLK_ENABLE_MASK, CLK_DISABLE, 0x0); > + > + /* QDSP6 master port needs to be explicitly halted */ > + temp = read_bit(reg->tcsr_base + TCSR_LPASS_PWR_ON_OFFSET, > + CLK_ENABLE, 0x0); > + temp = temp && !read_bit(reg->tcsr_base + TCSR_LPASS_MASTER_IDLE_OFFSET, > + CLK_ENABLE, 0x0); > + if (temp) { > + writel(CLK_ENABLE, reg->tcsr_base + TCSR_LPASS_HALTREQ_OFFSET); CLK_ENABLE happens to have the right value, but the value you write is "request halt" not "enable clock". > + /* Wait for halt ACK from QDSP6 */ > + while ((read_bit(reg->tcsr_base + TCSR_LPASS_HALTACK_OFFSET, > + CLK_DISABLE, 0x0) == 0) && (acktimeout-- > 0)) > + udelay(5); > + > + if (acktimeout) { > + if (read_bit(reg->tcsr_base + > + TCSR_LPASS_MASTER_IDLE_OFFSET, > + CLK_ENABLE, 0x0) != 1) > + dev_warn(adsp->dev, > + "%s: failed to receive %s\n", > + __func__, "TCSR MASTER ACK"); > + } else { > + dev_err(adsp->dev, "%s: failed to receive halt ack\n", > + __func__); > + return -ETIMEDOUT; > + } > + } Take a look at q6v5proc_halt_axi_port() in qcom_q6v5_pil.c instead of this thing. > + > + /* Assert the LPASS PDC Reset */ > + update_bits(reg->pdc_sync, AUDIO_SYNC_RESET_MASK, > + CLK_ENABLE, 0x2); Use https://patchwork.kernel.org/patch/10415991/ reset_control_assert(); > + /* Place the LPASS processor into reset */ > + writel(CLK_ENABLE, reg->cc_lpass); > + /* wait after asserting subsystem restart from AOSS */ > + udelay(200); > + > + /* Clear the halt request for the AXIM and AHBM for Q6 */ > + writel(CLK_DISABLE, reg->tcsr_base + TCSR_LPASS_HALTREQ_OFFSET); Disable the clock that is the halt request register? > + > + /* De-assert the LPASS PDC Reset */ > + update_bits(reg->pdc_sync, AUDIO_SYNC_RESET_MASK, > + CLK_DISABLE, 0x2); reset_control_deassert(); > + /* Remove the LPASS reset */ > + writel(CLK_DISABLE, reg->cc_lpass); > + /* wait after de-asserting subsystem restart from AOSS */ > + udelay(200); > + > + return 0; > +} Regards, Bjorn -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html