All of lore.kernel.org
 help / color / mirror / Atom feed
From: Daniel Lezcano <daniel.lezcano@linaro.org>
To: Lina Iyer <lina.iyer@linaro.org>
Cc: khilman@linaro.org, sboyd@codeaurora.org, galak@codeaurora.org,
	linux-arm-msm@vger.kernel.org, linux-pm@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, lorenzo.pieralisi@arm.com,
	msivasub@codeaurora.org, devicetree@vger.kernel.org
Subject: Re: [PATCH v13 03/10] qcom: spm: Add Subsystem Power Manager driver
Date: Tue, 02 Dec 2014 10:53:30 +0100	[thread overview]
Message-ID: <547D8C1A.1010204@linaro.org> (raw)
In-Reply-To: <20141201185023.GB499@linaro.org>

On 12/01/2014 07:50 PM, Lina Iyer wrote:
> On Thu, Nov 27 2014 at 01:52 -0700, Daniel Lezcano wrote:
>> On 11/27/2014 06:24 AM, Lina Iyer wrote:
>
>> +    static bool cpuidle_drv_init;
>>
>>        ^^^^^^^^^
>>
>> As already said in a previous comment, please find a way to remove that.
>>
> I will look into it. Stephen and I wanted the cpuidle driver to be
> probed only after any of the SPMs are ready. And possibly, only for that
> cpu, for which the SPM has been probed.
>
> To achieve the SPM -> CPUIDLE Device relation, I havent found a good way
> to do that. Without using CPUIDLE_MULTIPLE_DRIVERS, initializing each
> cpuidle device, separate from the cpuidle driver, requires that I update
> the cpuidle_driver->cpumask after probing each SPM device, to allow for
> only one driver and cpuidle devices only for the probed cpus. Using the
> cpuidle_register_driver(), resets the drv->refcnt in
> __cpuidle_driver_init.
>
> I may need something like this in the else clause of
> CPUIDLE_MULTIPLE_DRIVERS -
>
> static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
> {
>         struct cpuidle_driver *drv = cpuidle_curr_driver;
>
>         if (drv && !cpumask_test_cpu(cpu, drv->cpumask))
>                  drv = NULL;
>
>         return drv;
> }

You don't have to deal with the drv->cpumask and 
CPUIDLE_MULTIPLE_DRIVERS. This field gives the list of the cpus the 
driver will have to potentially handle.

Just register the driver at the first place with the platform device by 
using cpuidle_register_driver(drv), I suggest somewhere else than the 
spm code.

The underlying code will do:

         if (!drv->cpumask)
                 drv->cpumask = (struct cpumask *)cpu_possible_mask;

Until the cpuidle device is not initialized for a specific cpu, the 
cpuidle code will have no effect for this cpu. If you register the 
driver but without registering the cpuidle devices that will result in 
nothing more than a structure initialized but inoperative.

For each SPM being probed:

struct cpuidle_device *dev = &per_cpu(cpuidle_dev, cpu);
dev->cpu = cpu;
cpuidle_register_device(dev);

Note cpuidle_dev is exported via cpuidle.h, so you don't have to 
redeclare a per cpu cpuidle device by your own.

So rephrasing all that:

(1) in cpuidle-qcom register the driver
(2) in the spm code register the device for each spm probe

(1) being called before (2).

If after that in the code you still have a "static bool 
cpuidle_drv_init", then I guess we have a problem in the init sequence 
somewhere else.

> void cpuidle_update_cpumask(struct cpumask *mask) {
>         struct cpuidle_driver *drv;
>
>         spin_lock(&cpuidle_driver_lock);
>         drv = cpuidle_get_driver();
>      if (drv)
>          drv->cpumask = mask ?: cpu_possible_mask;
>         spin_unlock(&cpuidle_driver_lock);
> }
>
> With that, I could register cpuidle driver the first time when the mask
> changes
> from empty and consequent updates would just update the cpumask. (I am not
> sure, if I missed anything in this change). It just seemed far too
> invasive at
> this time, in lieu of the static bool.
>
>>> +    const struct platform_device_info qcom_cpuidle_info = {
>>> +        .name    = "qcom_cpuidle",
>>> +        .id    = -1,
>>> +        .data = &lpm_ops,
>>> +        .size_data = sizeof(lpm_ops),
>>> +    };
>>> +
>>> +    drv = spm_get_drv(pdev, &cpu);
>>> +    if (!drv || cpu < 0)
>>> +        return -EINVAL;
>>
>> As already said in a previous comment, it is not possible to have "cpu
>> < 0" with "drv != NULL", so except I am missing something the test
>> should be:
>>
>>     if (!drv)
>>         return -EINVAL;
>>
> Sorry, done.
>
>> There is something wrong with the init sequence. Don't you find weird
>> you have to backward search for the cpu belonging to the pdev each
>> time the probe function is called ?
>>
>>
> Well, it was that or the SPM device node pointing to the CPU that it
> references. It seems more common to have an iterator than the doubly
> linked device nodes. I dont have a strong preference either way, just
> chose the way that made device nodes easier.
>
>>> +
>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> +    drv->reg_base = devm_ioremap_resource(&pdev->dev, res);
>>> +    if (IS_ERR(drv->reg_base))
>>> +        return PTR_ERR(drv->reg_base);
>>> +
>>> +    match_id = of_match_node(spm_match_table, pdev->dev.of_node);
>>> +    if (!match_id)
>>> +        return -ENODEV;
>>> +
>>> +    drv->reg_data = match_id->data;
>>> +
>>> +    /* Write the SPM sequences first.. */
>>> +    addr = drv->reg_base +
>>> drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY];
>>> +    __iowrite32_copy(addr, drv->reg_data->seq,
>>> +            ARRAY_SIZE(drv->reg_data->seq) / 4);
>>> +
>>> +    /*
>>> +     * ..and then the control registers.
>>> +     * On some SoC's if the control registers are written first and
>>> if the
>>> +     * CPU was held in reset, the reset signal could trigger the SPM
>>> state
>>> +     * machine, before the sequences are completely written.
>>> +     */
>>> +    spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg);
>>> +    spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly);
>>> +    spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly);
>>> +
>>> +    spm_register_write(drv, SPM_REG_PMIC_DATA_0,
>>> +                drv->reg_data->pmic_data[0]);
>>> +    spm_register_write(drv, SPM_REG_PMIC_DATA_1,
>>> +                drv->reg_data->pmic_data[1]);
>>> +
>>> +    /*
>>> +     * Ensure all observers see the above register writes before the
>>> +     * cpuidle driver is allowed to use the SPM.
>>> +     */
>>> +    wmb();
>>> +    per_cpu(cpu_spm_drv, cpu) = drv;
>>> +
>>> +    if (!cpuidle_drv_init) {
>>> +        platform_device_register_full(&qcom_cpuidle_info);
>>> +        cpuidle_drv_init = true;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static struct platform_driver spm_driver = {
>>> +    .probe = spm_dev_probe,
>>> +    .driver = {
>>> +        .name = "saw",
>>> +        .of_match_table = spm_match_table,
>>> +    },
>>> +};
>>> +
>>> +module_platform_driver(spm_driver);
>>> +
>>> +MODULE_LICENSE("GPL v2");
>>> +MODULE_DESCRIPTION("SAW power controller driver");
>>> +MODULE_ALIAS("platform:saw");
>>> diff --git a/include/soc/qcom/pm.h b/include/soc/qcom/pm.h
>>> new file mode 100644
>>> index 0000000..d9a56d7
>>> --- /dev/null
>>> +++ b/include/soc/qcom/pm.h
>>> @@ -0,0 +1,31 @@
>>> +/*
>>> + * Copyright (c) 2009-2014, The Linux Foundation. All rights reserved.
>>> + *
>>> + * This software is licensed under the terms of the GNU General Public
>>> + * License version 2, as published by the Free Software Foundation, and
>>> + * may be copied, distributed, and modified under those terms.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + */
>>> +
>>> +#ifndef __QCOM_PM_H
>>> +#define __QCOM_PM_H
>>> +
>>> +enum pm_sleep_mode {
>>> +    PM_SLEEP_MODE_STBY,
>>> +    PM_SLEEP_MODE_RET,
>>> +    PM_SLEEP_MODE_SPC,
>>> +    PM_SLEEP_MODE_PC,
>>> +    PM_SLEEP_MODE_NR,
>>> +};
>>> +
>>> +struct qcom_cpu_pm_ops {
>>> +    int (*standby)(void *data);
>>> +    int (*spc)(void *data);
>>> +};
>>> +
>>> +#endif  /* __QCOM_PM_H */
>>>
>>
>>
>> --
>> <http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs
>>
>> Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
>> <http://twitter.com/#!/linaroorg> Twitter |
>> <http://www.linaro.org/linaro-blog/> Blog
>>


-- 
  <http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs

Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog

WARNING: multiple messages have this Message-ID (diff)
From: daniel.lezcano@linaro.org (Daniel Lezcano)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v13 03/10] qcom: spm: Add Subsystem Power Manager driver
Date: Tue, 02 Dec 2014 10:53:30 +0100	[thread overview]
Message-ID: <547D8C1A.1010204@linaro.org> (raw)
In-Reply-To: <20141201185023.GB499@linaro.org>

On 12/01/2014 07:50 PM, Lina Iyer wrote:
> On Thu, Nov 27 2014 at 01:52 -0700, Daniel Lezcano wrote:
>> On 11/27/2014 06:24 AM, Lina Iyer wrote:
>
>> +    static bool cpuidle_drv_init;
>>
>>        ^^^^^^^^^
>>
>> As already said in a previous comment, please find a way to remove that.
>>
> I will look into it. Stephen and I wanted the cpuidle driver to be
> probed only after any of the SPMs are ready. And possibly, only for that
> cpu, for which the SPM has been probed.
>
> To achieve the SPM -> CPUIDLE Device relation, I havent found a good way
> to do that. Without using CPUIDLE_MULTIPLE_DRIVERS, initializing each
> cpuidle device, separate from the cpuidle driver, requires that I update
> the cpuidle_driver->cpumask after probing each SPM device, to allow for
> only one driver and cpuidle devices only for the probed cpus. Using the
> cpuidle_register_driver(), resets the drv->refcnt in
> __cpuidle_driver_init.
>
> I may need something like this in the else clause of
> CPUIDLE_MULTIPLE_DRIVERS -
>
> static inline struct cpuidle_driver *__cpuidle_get_cpu_driver(int cpu)
> {
>         struct cpuidle_driver *drv = cpuidle_curr_driver;
>
>         if (drv && !cpumask_test_cpu(cpu, drv->cpumask))
>                  drv = NULL;
>
>         return drv;
> }

You don't have to deal with the drv->cpumask and 
CPUIDLE_MULTIPLE_DRIVERS. This field gives the list of the cpus the 
driver will have to potentially handle.

Just register the driver at the first place with the platform device by 
using cpuidle_register_driver(drv), I suggest somewhere else than the 
spm code.

The underlying code will do:

         if (!drv->cpumask)
                 drv->cpumask = (struct cpumask *)cpu_possible_mask;

Until the cpuidle device is not initialized for a specific cpu, the 
cpuidle code will have no effect for this cpu. If you register the 
driver but without registering the cpuidle devices that will result in 
nothing more than a structure initialized but inoperative.

For each SPM being probed:

struct cpuidle_device *dev = &per_cpu(cpuidle_dev, cpu);
dev->cpu = cpu;
cpuidle_register_device(dev);

Note cpuidle_dev is exported via cpuidle.h, so you don't have to 
redeclare a per cpu cpuidle device by your own.

So rephrasing all that:

(1) in cpuidle-qcom register the driver
(2) in the spm code register the device for each spm probe

(1) being called before (2).

If after that in the code you still have a "static bool 
cpuidle_drv_init", then I guess we have a problem in the init sequence 
somewhere else.

> void cpuidle_update_cpumask(struct cpumask *mask) {
>         struct cpuidle_driver *drv;
>
>         spin_lock(&cpuidle_driver_lock);
>         drv = cpuidle_get_driver();
>      if (drv)
>          drv->cpumask = mask ?: cpu_possible_mask;
>         spin_unlock(&cpuidle_driver_lock);
> }
>
> With that, I could register cpuidle driver the first time when the mask
> changes
> from empty and consequent updates would just update the cpumask. (I am not
> sure, if I missed anything in this change). It just seemed far too
> invasive at
> this time, in lieu of the static bool.
>
>>> +    const struct platform_device_info qcom_cpuidle_info = {
>>> +        .name    = "qcom_cpuidle",
>>> +        .id    = -1,
>>> +        .data = &lpm_ops,
>>> +        .size_data = sizeof(lpm_ops),
>>> +    };
>>> +
>>> +    drv = spm_get_drv(pdev, &cpu);
>>> +    if (!drv || cpu < 0)
>>> +        return -EINVAL;
>>
>> As already said in a previous comment, it is not possible to have "cpu
>> < 0" with "drv != NULL", so except I am missing something the test
>> should be:
>>
>>     if (!drv)
>>         return -EINVAL;
>>
> Sorry, done.
>
>> There is something wrong with the init sequence. Don't you find weird
>> you have to backward search for the cpu belonging to the pdev each
>> time the probe function is called ?
>>
>>
> Well, it was that or the SPM device node pointing to the CPU that it
> references. It seems more common to have an iterator than the doubly
> linked device nodes. I dont have a strong preference either way, just
> chose the way that made device nodes easier.
>
>>> +
>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> +    drv->reg_base = devm_ioremap_resource(&pdev->dev, res);
>>> +    if (IS_ERR(drv->reg_base))
>>> +        return PTR_ERR(drv->reg_base);
>>> +
>>> +    match_id = of_match_node(spm_match_table, pdev->dev.of_node);
>>> +    if (!match_id)
>>> +        return -ENODEV;
>>> +
>>> +    drv->reg_data = match_id->data;
>>> +
>>> +    /* Write the SPM sequences first.. */
>>> +    addr = drv->reg_base +
>>> drv->reg_data->reg_offset[SPM_REG_SEQ_ENTRY];
>>> +    __iowrite32_copy(addr, drv->reg_data->seq,
>>> +            ARRAY_SIZE(drv->reg_data->seq) / 4);
>>> +
>>> +    /*
>>> +     * ..and then the control registers.
>>> +     * On some SoC's if the control registers are written first and
>>> if the
>>> +     * CPU was held in reset, the reset signal could trigger the SPM
>>> state
>>> +     * machine, before the sequences are completely written.
>>> +     */
>>> +    spm_register_write(drv, SPM_REG_CFG, drv->reg_data->spm_cfg);
>>> +    spm_register_write(drv, SPM_REG_DLY, drv->reg_data->spm_dly);
>>> +    spm_register_write(drv, SPM_REG_PMIC_DLY, drv->reg_data->pmic_dly);
>>> +
>>> +    spm_register_write(drv, SPM_REG_PMIC_DATA_0,
>>> +                drv->reg_data->pmic_data[0]);
>>> +    spm_register_write(drv, SPM_REG_PMIC_DATA_1,
>>> +                drv->reg_data->pmic_data[1]);
>>> +
>>> +    /*
>>> +     * Ensure all observers see the above register writes before the
>>> +     * cpuidle driver is allowed to use the SPM.
>>> +     */
>>> +    wmb();
>>> +    per_cpu(cpu_spm_drv, cpu) = drv;
>>> +
>>> +    if (!cpuidle_drv_init) {
>>> +        platform_device_register_full(&qcom_cpuidle_info);
>>> +        cpuidle_drv_init = true;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static struct platform_driver spm_driver = {
>>> +    .probe = spm_dev_probe,
>>> +    .driver = {
>>> +        .name = "saw",
>>> +        .of_match_table = spm_match_table,
>>> +    },
>>> +};
>>> +
>>> +module_platform_driver(spm_driver);
>>> +
>>> +MODULE_LICENSE("GPL v2");
>>> +MODULE_DESCRIPTION("SAW power controller driver");
>>> +MODULE_ALIAS("platform:saw");
>>> diff --git a/include/soc/qcom/pm.h b/include/soc/qcom/pm.h
>>> new file mode 100644
>>> index 0000000..d9a56d7
>>> --- /dev/null
>>> +++ b/include/soc/qcom/pm.h
>>> @@ -0,0 +1,31 @@
>>> +/*
>>> + * Copyright (c) 2009-2014, The Linux Foundation. All rights reserved.
>>> + *
>>> + * This software is licensed under the terms of the GNU General Public
>>> + * License version 2, as published by the Free Software Foundation, and
>>> + * may be copied, distributed, and modified under those terms.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + */
>>> +
>>> +#ifndef __QCOM_PM_H
>>> +#define __QCOM_PM_H
>>> +
>>> +enum pm_sleep_mode {
>>> +    PM_SLEEP_MODE_STBY,
>>> +    PM_SLEEP_MODE_RET,
>>> +    PM_SLEEP_MODE_SPC,
>>> +    PM_SLEEP_MODE_PC,
>>> +    PM_SLEEP_MODE_NR,
>>> +};
>>> +
>>> +struct qcom_cpu_pm_ops {
>>> +    int (*standby)(void *data);
>>> +    int (*spc)(void *data);
>>> +};
>>> +
>>> +#endif  /* __QCOM_PM_H */
>>>
>>
>>
>> --
>> <http://www.linaro.org/> Linaro.org ? Open source software for ARM SoCs
>>
>> Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
>> <http://twitter.com/#!/linaroorg> Twitter |
>> <http://www.linaro.org/linaro-blog/> Blog
>>


-- 
  <http://www.linaro.org/> Linaro.org ? Open source software for ARM SoCs

Follow Linaro:  <http://www.facebook.com/pages/Linaro> Facebook |
<http://twitter.com/#!/linaroorg> Twitter |
<http://www.linaro.org/linaro-blog/> Blog

  reply	other threads:[~2014-12-02  9:53 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-11-27  5:24 [PATCH v13 00/10] cpuidle driver for QCOM SoCs: 8064, 8074, 8084 Lina Iyer
2014-11-27  5:24 ` Lina Iyer
2014-11-27  5:24 ` [PATCH v13 01/10] qcom: scm: Move scm-boot files to drivers/soc/qcom/ and include/soc/qcom Lina Iyer
2014-11-27  5:24   ` Lina Iyer
2014-11-27  5:24 ` [PATCH v13 02/10] qcom: scm: Add SCM warmboot support for quad core SoCs Lina Iyer
2014-11-27  5:24   ` Lina Iyer
2014-11-27  5:24 ` [PATCH v13 03/10] qcom: spm: Add Subsystem Power Manager driver Lina Iyer
2014-11-27  5:24   ` Lina Iyer
2014-11-27  8:44   ` Ivan T. Ivanov
2014-11-27  8:44     ` Ivan T. Ivanov
2014-12-01 17:57     ` Lina Iyer
2014-12-01 17:57       ` Lina Iyer
2014-11-27  8:52   ` Daniel Lezcano
2014-11-27  8:52     ` Daniel Lezcano
2014-12-01 18:50     ` Lina Iyer
2014-12-01 18:50       ` Lina Iyer
2014-12-02  9:53       ` Daniel Lezcano [this message]
2014-12-02  9:53         ` Daniel Lezcano
2014-12-02 15:35         ` Lina Iyer
2014-12-02 15:35           ` Lina Iyer
2014-12-02 15:47           ` Daniel Lezcano
2014-12-02 15:47             ` Daniel Lezcano
2014-11-27 15:01   ` Lorenzo Pieralisi
2014-11-27 15:01     ` Lorenzo Pieralisi
2014-12-01 18:57     ` Lina Iyer
2014-12-01 18:57       ` Lina Iyer
2014-12-02 11:10       ` Catalin Marinas
2014-12-02 11:10         ` Catalin Marinas
2014-12-02 15:52         ` Lina Iyer
2014-12-02 15:52           ` Lina Iyer
2014-11-27  5:24 ` [PATCH v13 04/10] arm: dts: qcom: Add power-controller device node for 8074 Krait CPUs Lina Iyer
2014-11-27  5:24   ` Lina Iyer
2014-11-27  5:24 ` [PATCH v13 05/10] arm: dts: qcom: Add power-controller device node for 8084 " Lina Iyer
2014-11-27  5:24   ` Lina Iyer
2014-11-27  5:24 ` [PATCH v13 06/10] arm: dts: qcom: Update power-controller device node for 8064 " Lina Iyer
2014-11-27  5:24   ` Lina Iyer
2014-11-27  5:24 ` [PATCH v13 07/10] qcom: cpuidle: Add cpuidle driver for QCOM cpus Lina Iyer
2014-11-27  5:24   ` Lina Iyer
2014-11-27  5:24 ` [PATCH v13 08/10] arm: dts: qcom: Add idle states device nodes for 8074 Lina Iyer
2014-11-27  5:24   ` Lina Iyer
2014-11-27  5:24 ` [PATCH v13 09/10] arm: dts: qcom: Add idle states device nodes for 8084 Lina Iyer
2014-11-27  5:24   ` Lina Iyer
2014-11-27  5:24 ` [PATCH v13 10/10] arm: dts: qcom: Add idle state device nodes for 8064 Lina Iyer
2014-11-27  5:24   ` Lina Iyer
2014-11-27  8:53 ` [PATCH v13 00/10] cpuidle driver for QCOM SoCs: 8064, 8074, 8084 Daniel Lezcano
2014-11-27  8:53   ` Daniel Lezcano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=547D8C1A.1010204@linaro.org \
    --to=daniel.lezcano@linaro.org \
    --cc=devicetree@vger.kernel.org \
    --cc=galak@codeaurora.org \
    --cc=khilman@linaro.org \
    --cc=lina.iyer@linaro.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=lorenzo.pieralisi@arm.com \
    --cc=msivasub@codeaurora.org \
    --cc=sboyd@codeaurora.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.