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