Re: [PATCH v5 1/3] soc: qcom: Add support for Core Power Reduction v3, v4 and Hardened

From: Bjorn Andersson
Date: Mon May 31 2021 - 19:59:46 EST


On Thu 21 Jan 13:40 CST 2021, AngeloGioacchino Del Regno wrote:

> This commit introduces a new driver, based on the one for cpr v1,
> to enable support for the newer Qualcomm Core Power Reduction
> hardware, known downstream as CPR3, CPR4 and CPRh, and support
> for MSM8998 and SDM630 CPU power reduction.
>
> In these new versions of the hardware, support for various new
> features was introduced, including voltage reduction for the GPU,
> security hardening and a new way of controlling CPU DVFS,
> consisting in internal communication between microcontrollers,
> specifically the CPR-Hardened and the Operating State Manager.
>
> The CPR v3, v4 and CPRh are present in a broad range of SoCs,
> from the mid-range to the high end ones including, but not limited
> to, MSM8953/8996/8998, SDM630/636/660/845.

Why is 845 in this list? I was under the impression that CPR was dealt
with entirely by firmware starting in 845.


Also, you don't happen to have the 8996 data laying around somewhere?

> diff --git a/drivers/soc/qcom/cpr3.c b/drivers/soc/qcom/cpr3.c
[..]
> +/*
> + * cpr_get_ring_osc_factor - Get fuse corner ring oscillator factor
> + *
> + * Not all threads have different scaling factors for each
> + * Fuse Corner: if the RO factors are the same for all corners,
> + * then only one is specified, instead of uselessly repeating
> + * the same array for FC-times.
> + * This function checks for the same and gives back the right
> + * factor for the requested ring oscillator.
> + *
> + * Returns: Ring oscillator factor

This is almost kerneldoc, how about adding another '*', some parenthesis
on the function name, short description of the parameters and dropping
the 's' on Return?

> + */
> +static int cpr_get_ro_factor(const struct cpr_thread_desc *tdesc,
> + int fnum, int ro_idx)
> +{
> + int ro_fnum;
> +
> + if (tdesc->ro_avail_corners == tdesc->num_fuse_corners)
> + ro_fnum = fnum;
> + else
> + ro_fnum = 0;
> +
> + return tdesc->ro_scaling_factor[ro_fnum][ro_idx];
> +}
> +
> +static void cpr_write(struct cpr_thread *thread, u32 offset, u32 value)
> +{
> + writel_relaxed(value, thread->base + offset);

I dislike the fact that we default to the _relaxed() version of
readl/writel. Can we please switch them for non-relaxed versions, or
document why they all need to be _relaxed?

> +}
> +
> +static u32 cpr_read(struct cpr_thread *thread, u32 offset)
> +{
> + return readl_relaxed(thread->base + offset);
> +}
> +
> +static void
> +cpr_masked_write(struct cpr_thread *thread, u32 offset, u32 mask, u32 value)
> +{
> + u32 val;
> +
> + val = readl_relaxed(thread->base + offset);
> + val &= ~mask;
> + val |= value & mask;
> + writel_relaxed(val, thread->base + offset);
> +}
> +
> +static void cpr_irq_clr(struct cpr_thread *thread)
> +{
> + cpr_write(thread, CPR3_REG_IRQ_CLEAR, CPR3_IRQ_ALL);
> +}
> +
> +static void cpr_irq_clr_nack(struct cpr_thread *thread)
> +{
> + cpr_irq_clr(thread);
> + cpr_write(thread, CPR3_REG_CONT_CMD, CPR3_CONT_CMD_NACK);
> +}
> +
> +static void cpr_irq_clr_ack(struct cpr_thread *thread)
> +{
> + cpr_irq_clr(thread);
> + cpr_write(thread, CPR3_REG_CONT_CMD, CPR3_CONT_CMD_ACK);
> +}
> +
> +static void cpr_irq_set(struct cpr_thread *thread, u32 int_bits)
> +{
> + /* On CPR-hardened, interrupts are managed by and on firmware */
> + if (thread->drv->desc->cpr_type == CTRL_TYPE_CPRH)
> + return;
> +
> + cpr_write(thread, CPR3_REG_IRQ_EN, int_bits);
> +}
> +
> +/**
> + * cpr_ctl_enable - Enable CPR thread

() after the function name, here an in all kerneldoc comments below.

> + * @thread: Structure holding CPR thread-specific parameters
> + */
> +static void cpr_ctl_enable(struct cpr_thread *thread)
> +{
> + if (thread->drv->enabled && !thread->restarting)
> + cpr_masked_write(thread, CPR3_REG_CPR_CTL,
> + CPR3_CPR_CTL_LOOP_EN_MASK,
> + CPR3_CPR_CTL_LOOP_EN_MASK);

Please wrap this "block" in {}

> +}
> +
> +/**
> + * cpr_ctl_disable - Disable CPR thread
> + * @thread: Structure holding CPR thread-specific parameters
> + */
> +static void cpr_ctl_disable(struct cpr_thread *thread)
> +{
> + const struct cpr_desc *desc = thread->drv->desc;
> +
> + if (desc->cpr_type != CTRL_TYPE_CPRH) {
> + cpr_irq_set(thread, 0);
> + cpr_irq_clr(thread);
> + }
> +
> + cpr_masked_write(thread, CPR3_REG_CPR_CTL,
> + CPR3_CPR_CTL_LOOP_EN_MASK, 0);
> +}
> +
> +/**
> + * cpr_ctl_is_enabled - Check if thread is enabled
> + * @thread: Structure holding CPR thread-specific parameters
> + *
> + * Returns: true if the CPR is enabled, false if it is disabled.

It's supposed to be "Return:"

> + */
> +static bool cpr_ctl_is_enabled(struct cpr_thread *thread)
> +{
> + u32 reg_val;
> +
> + reg_val = cpr_read(thread, CPR3_REG_CPR_CTL);
> + return reg_val & CPR3_CPR_CTL_LOOP_EN_MASK;
> +}
> +
[..]
> +/**
> + * cpr_configure - Configure main HW parameters
> + * @thread: Structure holding CPR thread-specific parameters
> + *
> + * This function configures the main CPR hardware parameters, such as
> + * internal timers (and delays), sensor ownerships, activates and/or
> + * deactivates cpr-threads and others, as one sequence for all of the
> + * versions supported in this driver. By design, the function may
> + * return a success earlier if the sequence for "a previous version"
> + * has ended.
> + *
> + * NOTE: The CPR must be clocked before calling this function!

I think "Context: " is suitable for this type of comments.

> + *
> + * Returns: Zero for success or negative number on errors.
> + */
> +static int cpr_configure(struct cpr_thread *thread)
> +{
> + struct cpr_drv *drv = thread->drv;
> + const struct cpr_desc *desc = drv->desc;
> + const struct cpr_thread_desc *tdesc = thread->desc;
> + u32 val;
> + int i;
> +
> + /* Disable interrupt and CPR */
> + cpr_irq_set(thread, 0);
> + cpr_write(thread, CPR3_REG_CPR_CTL, 0);
> +
> + /* Init and save gcnt */
> + drv->gcnt = drv->ref_clk_khz * desc->gcnt_us;
> + do_div(drv->gcnt, 1000);
> +
> + /* Program the delay count for the timer */
> + val = drv->ref_clk_khz * desc->timer_delay_us;
> + do_div(val, 1000);
> + if (desc->cpr_type == CTRL_TYPE_CPR3) {
> + cpr_write(thread, CPR3_REG_CPR_TIMER_MID_CONT, val);
> +
> + val = drv->ref_clk_khz * desc->timer_updn_delay_us;
> + do_div(val, 1000);
> + cpr_write(thread, CPR3_REG_CPR_TIMER_UP_DN_CONT, val);
> + } else {
> + cpr_write(thread, CPR3_REG_CPR_TIMER_AUTO_CONT, val);
> + }
> + dev_dbg(drv->dev, "Timer count: %#0x (for %d us)\n", val,
> + desc->timer_delay_us);
> +
> + /* Program the control register */
> + val = desc->idle_clocks << CPR3_CPR_CTL_IDLE_CLOCKS_SHIFT
> + | desc->count_mode << CPR3_CPR_CTL_COUNT_MODE_SHIFT
> + | desc->count_repeat << CPR3_CPR_CTL_COUNT_REPEAT_SHIFT;

I think it's idiomatic to have the | at the end of the previous line,
rather than start with it. And below you repeat val |=, pick one style
and stick with that.

> + cpr_write(thread, CPR3_REG_CPR_CTL, val);
> +
> + /* Configure CPR default step quotients */
> + val = tdesc->step_quot_init_min << CPR3_CPR_STEP_QUOT_MIN_SHIFT
> + | tdesc->step_quot_init_max << CPR3_CPR_STEP_QUOT_MAX_SHIFT;
> +
> + cpr_write(thread, CPR3_REG_CPR_STEP_QUOT, val);
> +
> + /*
> + * Configure the CPR sensor ownership always on thread 0
> + * TODO: SDM845 has different ownership for sensors!!
> + */
> + for (i = tdesc->sensor_range_start; i < tdesc->sensor_range_end; i++)
> + cpr_write(thread, CPR3_REG_SENSOR_OWNER(i), 0);
> +
> + /* Program Consecutive Up & Down */
> + val = desc->timer_cons_up << CPR3_THRESH_CONS_UP_SHIFT;
> + val |= desc->timer_cons_down << CPR3_THRESH_CONS_DOWN_SHIFT;
> + val |= desc->up_threshold << CPR3_THRESH_UP_THRESH_SHIFT;
> + val |= desc->down_threshold << CPR3_THRESH_DOWN_THRESH_SHIFT;
> + cpr_write(thread, CPR3_REG_THRESH(tdesc->hw_tid), val);
> +
> + /* Mask all ring oscillators for all threads initially */
> + cpr_write(thread, CPR3_REG_RO_MASK(tdesc->hw_tid), CPR3_RO_MASK);
> +
> + /* HW Closed-loop control */
> + if (desc->cpr_type == CTRL_TYPE_CPR3)

Some {} here and in various places below, please

> + cpr_write(thread, CPR3_REG_HW_CLOSED_LOOP_DISABLED,
> + !desc->hw_closed_loop_en);
> + else
> + cpr_masked_write(thread, CPR4_REG_MARGIN_ADJ_CTL,
> + CPR4_MARGIN_ADJ_HW_CLOSED_LOOP_EN,
> + desc->hw_closed_loop_en ?
> + CPR4_MARGIN_ADJ_HW_CLOSED_LOOP_EN : 0);

Regards,
Bjorn