* [PATCH 6/9] ARM: dts: wheat: Drop MTD partitioning from DT
From: Geert Uytterhoeven @ 2018-05-23 6:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <fc1cfd7e-db27-9277-dbed-cc2274c614a6@gmail.com>
Hi Marek,
On Wed, May 23, 2018 at 12:01 AM, Marek Vasut <marek.vasut@gmail.com> wrote:
> On 05/22/2018 04:43 PM, Geert Uytterhoeven wrote:
>> On Tue, May 22, 2018 at 2:02 PM, Marek Vasut <marek.vasut@gmail.com> wrote:
>>> Drop the MTD partitioning from DT, since it does not describe HW
>>> and to give way to a more flexible kernel command line partition
>>> passing.
>>>
>>> To retain the original partitioning, assure you have enabled
>>> CONFIG_MTD_CMDLINE_PARTS in your kernel config and add the
>>> following to your kernel command line:
>>>
>>> mtdparts=spi0.0:256k at 0(loader),4096k(user),-(flash)
>>
>> I think the "@0" can be dropped, as it's optional?
>> 4m?
>
> My take on this is that the loader is actually at offset 0x0 of the MTD
> device and we explicitly state that in the mtdparts to anchor the first
> partition within the MTD device and all the other partitions are at
> offset +(sum of the sizes of all partitions listed before the current
> one) relative to that first partition.
Where is this explicitly states for the first partition?
> Removing the @0 feels fragile at best and it seems to depend on the
> current behavior of the code.
Better, it also depends on the documented behavior:
Documentation/admin-guide/kernel-parameters.txt refers to
drivers/mtd/cmdlinepart.c, which states:
* <offset> := standard linux memsize
* if omitted the part will immediately follow the previous part
* or 0 if the first part
None of the examples listed there or under the MTD_CMDLINE_PARTS Kconfig
help text, or in a defconfig bundled with the kernel, use @0 for the first
partition.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* [PATCH v3] arm64: allwinner: a64: Add Amarula A64-Relic initial support
From: Jagan Teki @ 2018-05-23 6:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180522143044.pgwawojiwnk2lzxb@flea>
On Tue, May 22, 2018 at 8:00 PM, Maxime Ripard
<maxime.ripard@bootlin.com> wrote:
> On Tue, May 22, 2018 at 06:52:28PM +0530, Jagan Teki wrote:
>> Amarula A64-Relic is Allwinner A64 based IoT device, which support
>> - Allwinner A64 Cortex-A53
>> - Mali-400MP2 GPU
>> - AXP803 PMIC
>> - 1GB DDR3 RAM
>> - 8GB eMMC
>> - AP6330 Wifi/BLE
>> - MIPI-DSI
>> - CSI: OV5640 sensor
>> - USB OTG
>
> You claim that this is doing OTG...
>
> [..]
>
>> +&usb_otg {
>> + dr_mode = "peripheral";
>> + status = "okay";
>> +};
>
> ... and yet you're setting it as peripheral...
Though it claims OTG, board doesn't have any USB ports to operate(not
even Mini-AB) the only way to use the board as peripheral to transfer
images from host.
^ permalink raw reply
* [PATCH 8/9] PM / Domains: Add support for multi PM domains per device to genpd
From: Ulf Hansson @ 2018-05-23 6:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <51f7de26-579a-8b9e-4e79-f4eee923ab38@codeaurora.org>
Rajendra, Jon,
On 23 May 2018 at 06:51, Rajendra Nayak <rnayak@codeaurora.org> wrote:
>
>
> On 05/23/2018 02:25 AM, Jon Hunter wrote:
>>
>> On 22/05/18 15:47, Ulf Hansson wrote:
>>> [...]
>>>
>>>>>
>>>>> +/**
>>>>> + * genpd_dev_pm_attach_by_id() - Attach a device to one of its PM domain.
>>>>> + * @dev: Device to attach.
>>>>> + * @index: The index of the PM domain.
>>>>> + *
>>>>> + * Parse device's OF node to find a PM domain specifier at the provided @index.
>>>>> + * If such is found, allocates a new device and attaches it to retrieved
>>>>> + * pm_domain ops.
>>>>> + *
>>>>> + * Returns the allocated device if successfully attached PM domain, NULL when
>>>>> + * the device don't need a PM domain or have a single PM domain, else PTR_ERR()
>>>>> + * in case of failures. Note that if a power-domain exists for the device, but
>>>>> + * cannot be found or turned on, then return PTR_ERR(-EPROBE_DEFER) to ensure
>>>>> + * that the device is not probed and to re-try again later.
>>>>> + */
>>>>> +struct device *genpd_dev_pm_attach_by_id(struct device *dev,
>>>>> + unsigned int index)
>>>>> +{
>>>>> + struct device *genpd_dev;
>>>>> + int num_domains;
>>>>> + int ret;
>>>>> +
>>>>> + if (!dev->of_node)
>>>>> + return NULL;
>>>>> +
>>>>> + /* Deal only with devices using multiple PM domains. */
>>>>> + num_domains = of_count_phandle_with_args(dev->of_node, "power-domains",
>>>>> + "#power-domain-cells");
>>>>> + if (num_domains < 2 || index >= num_domains)
>>>>> + return NULL;
>>>>> +
>>>>> + /* Allocate and register device on the genpd bus. */
>>>>> + genpd_dev = kzalloc(sizeof(*genpd_dev), GFP_KERNEL);
>>>>> + if (!genpd_dev)
>>>>> + return ERR_PTR(-ENOMEM);
>>>>> +
>>>>> + dev_set_name(genpd_dev, "genpd:%u:%s", index, dev_name(dev));
>>>>> + genpd_dev->bus = &genpd_bus_type;
>>>>> + genpd_dev->release = genpd_release_dev;
>>>>> +
>>>>> + ret = device_register(genpd_dev);
>>>>> + if (ret) {
>>>>> + kfree(genpd_dev);
>>>>> + return ERR_PTR(ret);
>>>>> + }
>>>>> +
>>>>> + /* Try to attach the device to the PM domain at the specified index. */
>>>>> + ret = __genpd_dev_pm_attach(genpd_dev, dev->of_node, index);
>>>>> + if (ret < 1) {
>>>>> + device_unregister(genpd_dev);
>>>>> + return ret ? ERR_PTR(ret) : NULL;
>>>>> + }
>>>>> +
>>>>> + pm_runtime_set_active(genpd_dev);
>>>>> + pm_runtime_enable(genpd_dev);
>>>>> +
>>>>> + return genpd_dev;
>>>>> +}
>>>>> +EXPORT_SYMBOL_GPL(genpd_dev_pm_attach_by_id);
>>>>
>>>> Thanks for sending this. Believe it or not this has still been on my to-do list
>>>> and so we definitely need a solution for Tegra.
>>>>
>>>> Looking at the above it appears that additional power-domains exposed as devices
>>>> to the client device. So I assume that this means that the drivers for devices
>>>> with multiple power-domains will need to call RPM APIs for each of these
>>>> additional power-domains. Is that correct?
>>>
>>> They can, but should not!
>>>
>>> Instead, the driver shall use device_link_add() and device_link_del(),
>>> dynamically, depending on what PM domain that their original device
>>> needs for the current running use case.
>>>
>>> In that way, they keep existing runtime PM deployment, operating on
>>> its original device.
>>
>> OK, sounds good. Any reason why the linking cannot be handled by the above API? Is there a use-case where you would not want it linked?
>
> I am guessing the linking is what would give the driver the ability to decide which subset of powerdomains it actually wants to control
> at any point using runtime PM. If we have cases wherein the driver would want to turn on/off _all_ its associated powerdomains _always_
> then a default linking of all would help.
First, I think we need to decide on *where* the linking should be
done, not at both places, as that would just mess up synchronization
of who is responsible for calling the device_link_del() at detach.
Second, It would in principle be fine to call device_link_add() and
device_link_del() as a part of the attach/detach APIs. However, there
is a downside to such solution, which would be that the driver then
needs call the detach API, just to do device_link_del(). Of course
then it would also needs to call the attach API later if/when needed.
Doing this adds unnecessary overhead - comparing to just let the
driver call device_link_add|del() when needed. On the upside, yes, it
would put less burden on the drivers as it then only needs to care
about using one set of functions.
Which solution do you prefer?
Kind regards
Uffe
^ permalink raw reply
* [PATCH v2 4/4] drm/mediatek: add connection from OD1 to RDMA1
From: CK Hu @ 2018-05-23 6:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180523022517.12103-5-stu.hsieh@mediatek.com>
Hi, Stu:
On Wed, 2018-05-23 at 10:25 +0800, Stu Hsieh wrote:
> This patch add the connection from OD1 to RDMA1 for ext path.
>
I would like to apply this patch before the patch 'Add support for
mediatek SOC MT2712' because this patch is necessary for mt2712.
Regards,
CK
> Signed-off-by: Stu Hsieh <stu.hsieh@mediatek.com>
> ---
> drivers/gpu/drm/mediatek/mtk_drm_ddp.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> index 3c7bd453cf42..0450ecbbc356 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> @@ -193,6 +193,9 @@ static unsigned int mtk_ddp_mout_en(enum mtk_ddp_comp_id cur,
> } else if (cur == DDP_COMPONENT_GAMMA && next == DDP_COMPONENT_RDMA1) {
> *addr = DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN;
> value = GAMMA_MOUT_EN_RDMA1;
> + } else if (cur == DDP_COMPONENT_OD1 && next == DDP_COMPONENT_RDMA1) {
> + *addr = DISP_REG_CONFIG_DISP_OD_MOUT_EN;
> + value = OD1_MOUT_EN_RDMA1;
> } else if (cur == DDP_COMPONENT_RDMA1 && next == DDP_COMPONENT_DPI0) {
> *addr = DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN;
> value = RDMA1_MOUT_DPI0;
^ permalink raw reply
* [RFC 12/13] ARM: dts: ti: add dra71-evm FIT description file
From: Tero Kristo @ 2018-05-23 5:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180522200100.GA23937@rob-hp-laptop>
On 22/05/18 23:01, Rob Herring wrote:
> On Mon, May 21, 2018 at 09:57:54AM +0300, Tero Kristo wrote:
>> On 17/04/18 17:49, Tony Lindgren wrote:
>>> * Tero Kristo <t-kristo@ti.com> [180417 09:36]:
>>>> In typical setup, you can boot a large number of different configs via:
>>>>
>>>> bootm 0x82000000#dra71-evm#nand#lcd-auo-g101evn01.0
>>>>
>>>> ... assuming the configs were named like that, and assuming they would be
>>>> compatible with each other. The am57xx-evm example provided is better, as
>>>> you can chain the different cameras to the available evm configs.
>>>
>>> Why not just do it in the bootloader to put together the dtb?
>>>
>>> Then for external devices, you could just pass info on the
>>> kernel cmdline with lcd=foo camera=bar if they cannot be
>>> detected over I2C.
>>
>> (Added Linux ARM list to CC, this was not part of the original delivery.)
>>
>> Ok trying to resurrect this thread a bit. Is there any kind of consensus how
>> things like this should be handled? Should we add the DT overlay files to
>> kernel tree or not?
>
> IMO, yes.
>
>> Should we add any kind of build infra to kernel tree, and at what level
>> would this be? Just DT overlay file building support, and drop the FIT build
>> support as was proposed in this RFC series or...?
>
> I think I mentioned this already, but I expect that this is going to
> cause a number of conversions of dtsi + dtsi -> dtb into base dts and
> overlay(s) dts files. In doing so, we still need to be able to build the
> original, full dtb.
So you mean like breaking apart the existing .dts files? Are there any
plans to get that done (I know the android folks talk about this but I
don't like their idea.) If we do the split, how are we going to
determine which dts + overlay files are required to get a specific DTB
done? I actually wrote a tool for this purpose which parses the FIT
image configurations and generates plain dtb files out of the info there
if needed, but assuming FIT is abandoned then what...?
>
>> U-boot can obviously parse the base DTB + overlay DTB:s into a single DTB,
>> but this is somewhat clumsy approach and is relatively error prone to get it
>> right.
>
> Why? How is the kernel better?
I am mostly speaking about runtime applying of the overlays. If done
build time, it is obviously on same level. If you apply the base DTS +
overlays from u-boot prompt, it is not too much fun, and if there are
any failures it just won't work, but don't really tell you why not.
>
>> Building the FIT image post kernel build would also be possible, but who
>> would be doing this, is there any need to get this done in generic manner or
>> shall we just add SoC vendor specific tools for this?
>
> I'll tell you up front, I'm not a fan of FIT image (nor uImage,
> Android boot image, $bootloader image). If you want a collection of
> files and some configuration data, use a filesystem and a text file.
Ok, thanks for your frank comments. I believe based on this feedback
I'll try to modify this series into bare minimal overlay support to
kernel, and have the post processing done elsewhere (either u-boot build
or possibly completely separate tool.)
-Tero
--
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
^ permalink raw reply
* [PATCH] cpufreq: Add Kryo CPU scaling driver
From: Viresh Kumar @ 2018-05-23 5:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180522130704.GA31065@e107155-lin>
On 22-05-18, 14:07, Sudeep Holla wrote:
> On Tue, May 22, 2018 at 02:29:45PM +0300, Ilia Lin wrote:
> > In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors,
> > the CPU frequency subset and voltage value of each OPP varies
> > based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables
> > defines the voltage and frequency value based on the msm-id in SMEM
> > and speedbin blown in the efuse combination.
> > The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
> > to provide the OPP framework with required information.
> > This is used to determine the voltage and frequency value for each OPP of
> > operating-points-v2 table when it is parsed by the OPP framework.
> >
> > Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
> > Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
>
> [...]
>
> > +
> > + switch (msm8996_version) {
> > + case MSM8996_V3:
> > + versions = 1 << (unsigned int)(*speedbin);
> > + break;
> > + case MSM8996_SG:
> > + versions = 1 << ((unsigned int)(*speedbin) + 4);
> > + break;
> > + default:
> > + BUG();
> > + break;
> > + }
> > +
> > + for_each_possible_cpu(cpu) {
> > + cpu_dev = get_cpu_device(cpu);
> > + if (NULL == cpu_dev) {
> > + ret = -ENODEV;
> > + goto free_opp;
> > + }
> > +
> > + opp_tables[cpu] = dev_pm_opp_set_supported_hw(cpu_dev,
> > + &versions, 1);
>
> Will be not get NULL for all CPUs except 0 ?
With my patches, we will get the OPP table again with refcount
incremented. And on failures, we need to call put-supported-hw helper
only for the CPUs for which it passed previously.
--
viresh
^ permalink raw reply
* [PATCH v2 2/4] drm/mediatek: Add support for mediatek SOC MT2712
From: CK Hu @ 2018-05-23 5:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180523022517.12103-3-stu.hsieh@mediatek.com>
Hi, Stu:
I've some inline comment.
On Wed, 2018-05-23 at 10:25 +0800, Stu Hsieh wrote:
> This patch add support for the Mediatek MT2712 DISP subsystem.
> There are two OVL engine and three disp output in MT2712.
>
> Signed-off-by: Stu Hsieh <stu.hsieh@mediatek.com>
> ---
> drivers/gpu/drm/mediatek/mtk_drm_ddp.c | 50 +++++++++++++++++++++++++++--
> drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c | 8 +++--
> drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h | 7 ++--
> drivers/gpu/drm/mediatek/mtk_drm_drv.c | 47 +++++++++++++++++++++++++--
> drivers/gpu/drm/mediatek/mtk_drm_drv.h | 7 ++--
> 5 files changed, 108 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> index 8130f3dab661..e563dedd1999 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> @@ -29,6 +29,8 @@
> #define DISP_REG_CONFIG_DISP_COLOR0_SEL_IN 0x084
> #define DISP_REG_CONFIG_DISP_COLOR1_SEL_IN 0x088
> #define DISP_REG_CONFIG_DPI_SEL_IN 0x0ac
> +#define DISP_REG_CONFIG_DISP_RDMA2_SOUT 0x0b8
> +#define DISP_REG_CONFIG_DISP_RDMA0_MOUT_EN 0x0c4
These two definition are useless, so remove it.
> #define DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN 0x0c8
> #define DISP_REG_CONFIG_MMSYS_CG_CON0 0x100
>
> @@ -41,6 +43,7 @@
> #define DISP_REG_MUTEX_RST(n) (0x28 + 0x20 * (n))
> #define DISP_REG_MUTEX_MOD(n) (0x2c + 0x20 * (n))
> #define DISP_REG_MUTEX_SOF(n) (0x30 + 0x20 * (n))
> +#define DISP_REG_MUTEX_MOD2(n) (0x34 + 0x20 * (n))
Move this to the patch 'drm/mediatek: support maximum 64 mutex mod' and
that patch should be applied before this patch.
>
> #define INT_MUTEX BIT(1)
>
> @@ -60,6 +63,25 @@
> #define MT8173_MUTEX_MOD_DISP_PWM1 BIT(24)
> #define MT8173_MUTEX_MOD_DISP_OD BIT(25)
>
> +#define MT2712_MUTEX_MOD_DISP_OVL0 BIT(11)
> +#define MT2712_MUTEX_MOD_DISP_OVL1 BIT(12)
> +#define MT2712_MUTEX_MOD_DISP_RDMA0 BIT(13)
> +#define MT2712_MUTEX_MOD_DISP_RDMA1 BIT(14)
> +#define MT2712_MUTEX_MOD_DISP_RDMA2 BIT(15)
> +#define MT2712_MUTEX_MOD_DISP_WDMA0 BIT(16)
> +#define MT2712_MUTEX_MOD_DISP_WDMA1 BIT(17)
> +#define MT2712_MUTEX_MOD_DISP_COLOR0 BIT(18)
> +#define MT2712_MUTEX_MOD_DISP_COLOR1 BIT(19)
> +#define MT2712_MUTEX_MOD_DISP_AAL0 BIT(20)
> +#define MT2712_MUTEX_MOD_DISP_UFOE BIT(22)
> +#define MT2712_MUTEX_MOD_DISP_PWM0 BIT(23)
> +#define MT2712_MUTEX_MOD_DISP_PWM1 BIT(24)
> +#define MT2712_MUTEX_MOD_DISP_PWM2 BIT(10)
> +#define MT2712_MUTEX_MOD_DISP_OD0 BIT(25)
> +/* modules more than 32, add BIT(31) when using DISP_REG_MUTEX_MOD2 bit */
> +#define MT2712_MUTEX_MOD2_DISP_AAL1 (BIT(1) | BIT(31))
I think a better definition is
#define MT2712_MUTEX_MOD2_DISP_AAL1 BIT(33)
when you need to access this register,
if (ddp->mutex_mod[id] < BIT(32)) {
offset = DISP_REG_MUTEX_MOD(mutex->id);
reg = readl_relaxed(ddp->regs + offset);
reg |= ddp->mutex_mod[id];
writel_relaxed(reg, ddp->regs + offset);
} else {
offset = DISP_REG_MUTEX_MOD2(mutex->id);
reg = readl_relaxed(ddp->regs + offset);
reg |= (ddp->mutex_mod[id] >> 32);
writel_relaxed(reg, ddp->regs + offset);
}
because DISP_REG_MUTEX_MOD BIT(31) could be used for some module.
> +#define MT2712_MUTEX_MOD2_DISP_OD1 (BIT(2) | BIT(31))
> +
> #define MT2701_MUTEX_MOD_DISP_OVL BIT(3)
> #define MT2701_MUTEX_MOD_DISP_WDMA BIT(6)
> #define MT2701_MUTEX_MOD_DISP_COLOR BIT(7)
> @@ -74,6 +96,7 @@
>
> #define OVL0_MOUT_EN_COLOR0 0x1
> #define OD_MOUT_EN_RDMA0 0x1
> +#define OD1_MOUT_EN_RDMA1 BIT(16)
> #define UFOE_MOUT_EN_DSI0 0x1
> #define COLOR0_SEL_IN_OVL0 0x1
> #define OVL1_MOUT_EN_COLOR1 0x1
> @@ -108,12 +131,32 @@ static const unsigned int mt2701_mutex_mod[DDP_COMPONENT_ID_MAX] = {
> [DDP_COMPONENT_WDMA0] = MT2701_MUTEX_MOD_DISP_WDMA,
> };
>
> +static const unsigned int mt2712_mutex_mod[DDP_COMPONENT_ID_MAX] = {
> + [DDP_COMPONENT_AAL0] = MT2712_MUTEX_MOD_DISP_AAL0,
> + [DDP_COMPONENT_AAL1] = MT2712_MUTEX_MOD2_DISP_AAL1,
> + [DDP_COMPONENT_COLOR0] = MT2712_MUTEX_MOD_DISP_COLOR0,
> + [DDP_COMPONENT_COLOR1] = MT2712_MUTEX_MOD_DISP_COLOR1,
> + [DDP_COMPONENT_OD0] = MT2712_MUTEX_MOD_DISP_OD0,
> + [DDP_COMPONENT_OD1] = MT2712_MUTEX_MOD2_DISP_OD1,
> + [DDP_COMPONENT_OVL0] = MT2712_MUTEX_MOD_DISP_OVL0,
> + [DDP_COMPONENT_OVL1] = MT2712_MUTEX_MOD_DISP_OVL1,
> + [DDP_COMPONENT_PWM0] = MT2712_MUTEX_MOD_DISP_PWM0,
> + [DDP_COMPONENT_PWM1] = MT2712_MUTEX_MOD_DISP_PWM1,
> + [DDP_COMPONENT_PWM2] = MT2712_MUTEX_MOD_DISP_PWM2,
> + [DDP_COMPONENT_RDMA0] = MT2712_MUTEX_MOD_DISP_RDMA0,
> + [DDP_COMPONENT_RDMA1] = MT2712_MUTEX_MOD_DISP_RDMA1,
> + [DDP_COMPONENT_RDMA2] = MT2712_MUTEX_MOD_DISP_RDMA2,
> + [DDP_COMPONENT_UFOE] = MT2712_MUTEX_MOD_DISP_UFOE,
> + [DDP_COMPONENT_WDMA0] = MT2712_MUTEX_MOD_DISP_WDMA0,
> + [DDP_COMPONENT_WDMA1] = MT2712_MUTEX_MOD_DISP_WDMA1,
> +};
> +
> static const unsigned int mt8173_mutex_mod[DDP_COMPONENT_ID_MAX] = {
> - [DDP_COMPONENT_AAL] = MT8173_MUTEX_MOD_DISP_AAL,
> + [DDP_COMPONENT_AAL0] = MT8173_MUTEX_MOD_DISP_AAL,
> [DDP_COMPONENT_COLOR0] = MT8173_MUTEX_MOD_DISP_COLOR0,
> [DDP_COMPONENT_COLOR1] = MT8173_MUTEX_MOD_DISP_COLOR1,
> [DDP_COMPONENT_GAMMA] = MT8173_MUTEX_MOD_DISP_GAMMA,
> - [DDP_COMPONENT_OD] = MT8173_MUTEX_MOD_DISP_OD,
> + [DDP_COMPONENT_OD0] = MT8173_MUTEX_MOD_DISP_OD,
> [DDP_COMPONENT_OVL0] = MT8173_MUTEX_MOD_DISP_OVL0,
> [DDP_COMPONENT_OVL1] = MT8173_MUTEX_MOD_DISP_OVL1,
> [DDP_COMPONENT_PWM0] = MT8173_MUTEX_MOD_DISP_PWM0,
> @@ -138,7 +181,7 @@ static unsigned int mtk_ddp_mout_en(enum mtk_ddp_comp_id cur,
> } else if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_RDMA0) {
> *addr = DISP_REG_CONFIG_DISP_OVL_MOUT_EN;
> value = OVL_MOUT_EN_RDMA;
> - } else if (cur == DDP_COMPONENT_OD && next == DDP_COMPONENT_RDMA0) {
> + } else if (cur == DDP_COMPONENT_OD0 && next == DDP_COMPONENT_RDMA0) {
> *addr = DISP_REG_CONFIG_DISP_OD_MOUT_EN;
> value = OD_MOUT_EN_RDMA0;
> } else if (cur == DDP_COMPONENT_UFOE && next == DDP_COMPONENT_DSI0) {
> @@ -407,6 +450,7 @@ static int mtk_ddp_remove(struct platform_device *pdev)
>
> static const struct of_device_id ddp_driver_dt_match[] = {
> { .compatible = "mediatek,mt2701-disp-mutex", .data = mt2701_mutex_mod},
> + { .compatible = "mediatek,mt2712-disp-mutex", .data = mt2712_mutex_mod},
> { .compatible = "mediatek,mt8173-disp-mutex", .data = mt8173_mutex_mod},
> {},
> };
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> index 4672317e3ad1..86e8c9e5df41 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> @@ -218,7 +218,8 @@ struct mtk_ddp_comp_match {
> };
>
> static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
> - [DDP_COMPONENT_AAL] = { MTK_DISP_AAL, 0, &ddp_aal },
> + [DDP_COMPONENT_AAL0] = { MTK_DISP_AAL, 0, &ddp_aal },
> + [DDP_COMPONENT_AAL1] = { MTK_DISP_AAL, 1, &ddp_aal },
> [DDP_COMPONENT_BLS] = { MTK_DISP_BLS, 0, NULL },
> [DDP_COMPONENT_COLOR0] = { MTK_DISP_COLOR, 0, NULL },
> [DDP_COMPONENT_COLOR1] = { MTK_DISP_COLOR, 1, NULL },
> @@ -226,10 +227,13 @@ static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
> [DDP_COMPONENT_DSI0] = { MTK_DSI, 0, NULL },
> [DDP_COMPONENT_DSI1] = { MTK_DSI, 1, NULL },
> [DDP_COMPONENT_GAMMA] = { MTK_DISP_GAMMA, 0, &ddp_gamma },
> - [DDP_COMPONENT_OD] = { MTK_DISP_OD, 0, &ddp_od },
> + [DDP_COMPONENT_OD0] = { MTK_DISP_OD, 0, &ddp_od },
> + [DDP_COMPONENT_OD1] = { MTK_DISP_OD, 1, &ddp_od },
> [DDP_COMPONENT_OVL0] = { MTK_DISP_OVL, 0, NULL },
> [DDP_COMPONENT_OVL1] = { MTK_DISP_OVL, 1, NULL },
> [DDP_COMPONENT_PWM0] = { MTK_DISP_PWM, 0, NULL },
> + [DDP_COMPONENT_PWM1] = { MTK_DISP_PWM, 1, NULL },
> + [DDP_COMPONENT_PWM2] = { MTK_DISP_PWM, 2, NULL },
> [DDP_COMPONENT_RDMA0] = { MTK_DISP_RDMA, 0, NULL },
> [DDP_COMPONENT_RDMA1] = { MTK_DISP_RDMA, 1, NULL },
> [DDP_COMPONENT_RDMA2] = { MTK_DISP_RDMA, 2, NULL },
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> index 0828cf8bf85c..e00c2e798abd 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> @@ -41,7 +41,8 @@ enum mtk_ddp_comp_type {
> };
>
> enum mtk_ddp_comp_id {
> - DDP_COMPONENT_AAL,
> + DDP_COMPONENT_AAL0,
> + DDP_COMPONENT_AAL1,
Move this to a patch 'add ddp component AAL1'.
> DDP_COMPONENT_BLS,
> DDP_COMPONENT_COLOR0,
> DDP_COMPONENT_COLOR1,
> @@ -49,11 +50,13 @@ enum mtk_ddp_comp_id {
> DDP_COMPONENT_DSI0,
> DDP_COMPONENT_DSI1,
> DDP_COMPONENT_GAMMA,
> - DDP_COMPONENT_OD,
> + DDP_COMPONENT_OD0,
> + DDP_COMPONENT_OD1,
Move this to a patch 'add ddp component OD1'.
> DDP_COMPONENT_OVL0,
> DDP_COMPONENT_OVL1,
> DDP_COMPONENT_PWM0,
> DDP_COMPONENT_PWM1,
> + DDP_COMPONENT_PWM2,
Move this to a patch 'add ddp component PWM2'.
> DDP_COMPONENT_RDMA0,
> DDP_COMPONENT_RDMA1,
> DDP_COMPONENT_RDMA2,
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> index a2ca90fc403c..3a866e1d6af4 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> @@ -146,11 +146,37 @@ static const enum mtk_ddp_comp_id mt2701_mtk_ddp_ext[] = {
> DDP_COMPONENT_DPI0,
> };
>
> +static const enum mtk_ddp_comp_id mt2712_mtk_ddp_main[] = {
> + DDP_COMPONENT_OVL0,
> + DDP_COMPONENT_COLOR0,
> + DDP_COMPONENT_AAL0,
> + DDP_COMPONENT_OD0,
> + DDP_COMPONENT_RDMA0,
> + DDP_COMPONENT_DPI0,
> + DDP_COMPONENT_PWM0,
> +};
> +
> +static const enum mtk_ddp_comp_id mt2712_mtk_ddp_ext[] = {
> + DDP_COMPONENT_OVL1,
> + DDP_COMPONENT_COLOR1,
> + DDP_COMPONENT_AAL1,
> + DDP_COMPONENT_OD1,
> + DDP_COMPONENT_RDMA1,
> + DDP_COMPONENT_DPI1,
> + DDP_COMPONENT_PWM1,
> +};
> +
> +static const enum mtk_ddp_comp_id mt2712_mtk_ddp_third[] = {
> + DDP_COMPONENT_RDMA2,
> + DDP_COMPONENT_DSI2,
> + DDP_COMPONENT_PWM2,
> +};
> +
> static const enum mtk_ddp_comp_id mt8173_mtk_ddp_main[] = {
> DDP_COMPONENT_OVL0,
> DDP_COMPONENT_COLOR0,
> - DDP_COMPONENT_AAL,
> - DDP_COMPONENT_OD,
> + DDP_COMPONENT_AAL0,
> + DDP_COMPONENT_OD0,
> DDP_COMPONENT_RDMA0,
> DDP_COMPONENT_UFOE,
> DDP_COMPONENT_DSI0,
> @@ -173,6 +199,15 @@ static const struct mtk_mmsys_driver_data mt2701_mmsys_driver_data = {
> .shadow_register = true,
> };
>
> +static const struct mtk_mmsys_driver_data mt2712_mmsys_driver_data = {
> + .main_path = mt2712_mtk_ddp_main,
> + .main_len = ARRAY_SIZE(mt2712_mtk_ddp_main),
> + .ext_path = mt2712_mtk_ddp_ext,
> + .ext_len = ARRAY_SIZE(mt2712_mtk_ddp_ext),
> + .third_path = mt2712_mtk_ddp_third,
> + .third_len = ARRAY_SIZE(mt2712_mtk_ddp_third),
> +};
> +
> static const struct mtk_mmsys_driver_data mt8173_mmsys_driver_data = {
> .main_path = mt8173_mtk_ddp_main,
> .main_len = ARRAY_SIZE(mt8173_mtk_ddp_main),
> @@ -232,6 +267,11 @@ static int mtk_drm_kms_init(struct drm_device *drm)
> if (ret < 0)
> goto err_component_unbind;
>
> + ret = mtk_drm_crtc_create(drm, private->data->third_path,
> + private->data->third_len);
> + if (ret < 0)
> + goto err_component_unbind;
> +
Move this to a patch 'add third ddp path'.
Regards,
CK
> /* Use OVL device for all DMA memory allocations */
> np = private->comp_node[private->data->main_path[0]] ?:
> private->comp_node[private->data->ext_path[0]];
> @@ -374,6 +414,7 @@ static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
> { .compatible = "mediatek,mt8173-dsi", .data = (void *)MTK_DSI },
> { .compatible = "mediatek,mt8173-dpi", .data = (void *)MTK_DPI },
> { .compatible = "mediatek,mt2701-disp-mutex", .data = (void *)MTK_DISP_MUTEX },
> + { .compatible = "mediatek,mt2712-disp-mutex", .data = (void *)MTK_DISP_MUTEX },
> { .compatible = "mediatek,mt8173-disp-mutex", .data = (void *)MTK_DISP_MUTEX },
> { .compatible = "mediatek,mt2701-disp-pwm", .data = (void *)MTK_DISP_BLS },
> { .compatible = "mediatek,mt8173-disp-pwm", .data = (void *)MTK_DISP_PWM },
> @@ -552,6 +593,8 @@ static SIMPLE_DEV_PM_OPS(mtk_drm_pm_ops, mtk_drm_sys_suspend,
> static const struct of_device_id mtk_drm_of_ids[] = {
> { .compatible = "mediatek,mt2701-mmsys",
> .data = &mt2701_mmsys_driver_data},
> + { .compatible = "mediatek,mt2712-mmsys",
> + .data = &mt2712_mmsys_driver_data},
> { .compatible = "mediatek,mt8173-mmsys",
> .data = &mt8173_mmsys_driver_data},
> { }
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
> index c3378c452c0a..e821342bc2d3 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
> @@ -17,8 +17,8 @@
> #include <linux/io.h>
> #include "mtk_drm_ddp_comp.h"
>
> -#define MAX_CRTC 2
> -#define MAX_CONNECTOR 2
> +#define MAX_CRTC 3
> +#define MAX_CONNECTOR 3
>
> struct device;
> struct device_node;
> @@ -33,6 +33,9 @@ struct mtk_mmsys_driver_data {
> unsigned int main_len;
> const enum mtk_ddp_comp_id *ext_path;
> unsigned int ext_len;
> + enum mtk_ddp_comp_id *third_path;
> + unsigned int third_len;
> +
> bool shadow_register;
> };
>
^ permalink raw reply
* [PATCH v3 1/2] regulator: dt-bindings: add QCOM RPMh regulator bindings
From: Doug Anderson @ 2018-05-23 5:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <b5078a17-4cbb-905e-4cd1-f252a02f97e9@codeaurora.org>
Hi,
On Tue, May 22, 2018 at 6:19 PM, David Collins <collinsd@codeaurora.org> wrote:
>>>> OK, so how's this for a proposal then:
>>>>
>>>> 1. For RPMh-regulator whenever we see a "set voltage" but Linux hasn't
>>>> specified that the regulator is enabled then we don't send the
>>>> voltage, we just cache it.
>>>>
>>>> 2. When we see the first enable then we first send the cached voltage
>>>> and then do the enable.
>>>>
>>>> 3. We don't need an "initial voltage" because any rails that are
>>>> expected to be variable voltage the client should be choosing a
>>>> voltage.
>>>>
>>>>
>>>> ...taking the SD card case as an example: if the regulator framework
>>>> says "set to the minimum: 1.8V" we'll cache this but not apply it yet
>>>> because the rail is off as far as Linux is concerned. Then when the
>>>> SD card framework starts up it will set the voltage to 3.3V which will
>>>> overwrite the cache. Then the SD card framework will enable the
>>>> regulator and RPMh will set the voltage to 3.3V and enable.
>>>
>>> I am ok with implementing this feature.
>>>
>>> However, should the voltage be cached instead of sent immediately any time
>>> that Linux has not explicitly requested the regulator to be enabled, or
>>> only before the first time that an enable request is sent?
>>>
>>> 1. regulator_set_voltage(reg, 2960000, 2960000)
>>> --> cache voltage=2960 mV
>>> 2. regulator_enable(reg)
>>> --> Send voltage=2960 then enable=1
>>> 3. regulator_disable(reg)
>>> --> Send enable=0
>>> 4. regulator_set_voltage(reg, 1800000, 2960000)
>>> --> A. Send voltage=1800 OR B. cache voltage=1800?
>>>
>>> Option A is used on the downstream rpmh-regulator driver in order to avoid
>>> cases where AP votes to disable a regulator that is kept enabled by
>>> another subsystem but then does not lower the voltage requested thanks to
>>> regulator_set_voltage() being called after regulator_disable(). I plan to
>>> go with option A for qcom-rpmh-regulator unless there are objections.
>>
>> So one client's vote for a voltage continues to be in effect even if
>> that client votes to have the regulator disabled? That seems
>> fundamentally broken in RPMh. I guess my take would be to work around
>> this RPMh misfeature by saying that whenever Linux votes to disable a
>> regulator it also votes for a voltage of 0. Then another client of
>> RPMh won't be affected.
>>
>> That seems like it would be beneficial in any case. If the AP always
>> asks for 1.3 V and the modem always asks for 1.1 V for the same rail
>> then the rail should go down to 1.1 V when the AP says to disable the
>> rail.
>
> The VRM in RPMh hardware aggregates enable state, output voltage, mode,
> and headroom voltage requests independently for each regulator. This
> allows for maximum request flexibility and makes no assumptions about
> consumer use cases. There is nothing inherently wrong about this approach.
Just to confirm I'm understanding correctly:
1. AP: request that regulator A be at 1.3 V and enabled
==> actual output: regulator A is 1.3 V and enabled
2. modem: requests that regulator A be at 1.1 V and enabled
==> actual output: regulator A is 1.3 V and enabled
3. AP: request that regulator A be disabled
You're saying that here the output of regulator A will be "1.3 V" and
"enabled".
I just can't see how that can be correct behavior. A given client's
vote for the voltage should really only make sense if that client
wants the regulator enabled. In the case above the kernel would have
no idea that someone else might have the regulator enabled. Why would
it care if that other user gets it at 1.3 V or at 1.1 V?
You have some use case in mind where the kernel would need to have
control over the voltage of a regulator that it thinks is disabled?
Now obviously if the kernel re-enables the regulator then its old
voltage vote should be re-instated right away, but until then its vote
about the voltage shouldn't count. If that means that the kernel has
to "vote" for 0V then I guess that's the way the API works.
> I'm concerned about a blanket policy of setting voltage=0 when issuing
> disable requests. That seems to violate the semantics of the
> regulator_set_voltage() API which enforces the requested voltage range
> until a new range is specified. There may be workarounds that require a
> regulator to operate at a specific voltage even when no Linux consumers
> explicitly need the regulator enabled.
>
> Given that not masking the voltage on disable is guaranteed to be safe and
> does not silently break potential use cases, I plan to stick with this
> approach. Therefore, the question about option A or B for voltage caching
> is still relevant. I think that option A is safer/more API conforming so
> I plan to implement that.
I still can't understand how it ever makes sense to vote for a voltage
for a regulator that you think is disabled. ...but if there's some
reason it does then I guess I'm OK with A.
>>>> This whole thing makes me worry about another problem, though. You
>>>> say that the bootloader left the SD card rail on, right? ...but as
>>>> far as Linux is concerned the SD card rail is off (argh, it can't read
>>>> the state that the bootloader left the rail in!)
>>>>
>>>> ...now imagine any of the following:
>>>>
>>>> * The user boots up a kernel where the SD card driver is disabled.
>>>>
>>>> * The user ejects the SD card right after the bootloader used it but
>>>> before the kernel driver started.
>>>>
>>>> When the kernel comes up it will believe that the SD card rail is
>>>> disabled so it won't try to disable it. So the voltage will be left
>>>> on.
>>>
>>> We have not encountered issues with regulators getting left on
>>> indefinitely because Linux devices failed to take control of them during
>>> boot. I don't think that this hypothetical issue needs to be addressed in
>>> the first qcom-rpmh-regulator driver patch if at all.
>>
>> I don't think it hypothetical at all. Linux takes control of a
>> regulator and then at bootup it disables all regulators that aren't
>> currently used/enabled. In your case you will report that regulators
>> are already disabled and thus you'll neuter the kernel's attempt to
>> disable regulators that nobody is using but that might have been left
>> on by the bootloader. It seemed like Mark agreed here.
>
> I did not consider regulator_late_cleanup(). I'll initialize the enabled
> value in qcom-rpmh-regulator to -EINVAL to handle this case and also so
> that _regulator_enable() succeeds without further core modification.
That's weird that the regulator code has a special case for -EINVAL in
_regulator_enable(). Given how most of the code in the
regulator/core.c doesn't seem to check for error codes I wonder if
anyone is using that... I guess I'd leave it to Mark whether he's
happy with -EINVAL for this case even though it's asymmetric to using
-ENOTRECOVERABLE for getting the voltage.
Are we really sure there aren't any places that the regulator code
needs to handle an error from _regulator_enable()? An an example, in
regulator_resolve_supply() are we sure we should be passing a
regulator_enable() on to our parent supply even if
_regulator_is_enabled() returns an error? I haven't looked in depth
of all use cases, though...
I see you posted a new version. Thanks! I'll try to find some time
soon to review it, but I'll be a bit busy over the next few days.
-Doug
^ permalink raw reply
* [PATCH 8/9] PM / Domains: Add support for multi PM domains per device to genpd
From: Rajendra Nayak @ 2018-05-23 4:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <5a79d3a2-d090-645b-da69-524b7e7a4d90@nvidia.com>
On 05/23/2018 02:25 AM, Jon Hunter wrote:
>
> On 22/05/18 15:47, Ulf Hansson wrote:
>> [...]
>>
>>>>
>>>> +/**
>>>> + * genpd_dev_pm_attach_by_id() - Attach a device to one of its PM domain.
>>>> + * @dev: Device to attach.
>>>> + * @index: The index of the PM domain.
>>>> + *
>>>> + * Parse device's OF node to find a PM domain specifier at the provided @index.
>>>> + * If such is found, allocates a new device and attaches it to retrieved
>>>> + * pm_domain ops.
>>>> + *
>>>> + * Returns the allocated device if successfully attached PM domain, NULL when
>>>> + * the device don't need a PM domain or have a single PM domain, else PTR_ERR()
>>>> + * in case of failures. Note that if a power-domain exists for the device, but
>>>> + * cannot be found or turned on, then return PTR_ERR(-EPROBE_DEFER) to ensure
>>>> + * that the device is not probed and to re-try again later.
>>>> + */
>>>> +struct device *genpd_dev_pm_attach_by_id(struct device *dev,
>>>> +????????????????????????????????????? unsigned int index)
>>>> +{
>>>> +???? struct device *genpd_dev;
>>>> +???? int num_domains;
>>>> +???? int ret;
>>>> +
>>>> +???? if (!dev->of_node)
>>>> +???????????? return NULL;
>>>> +
>>>> +???? /* Deal only with devices using multiple PM domains. */
>>>> +???? num_domains = of_count_phandle_with_args(dev->of_node, "power-domains",
>>>> +????????????????????????????????????????????? "#power-domain-cells");
>>>> +???? if (num_domains < 2 || index >= num_domains)
>>>> +???????????? return NULL;
>>>> +
>>>> +???? /* Allocate and register device on the genpd bus. */
>>>> +???? genpd_dev = kzalloc(sizeof(*genpd_dev), GFP_KERNEL);
>>>> +???? if (!genpd_dev)
>>>> +???????????? return ERR_PTR(-ENOMEM);
>>>> +
>>>> +???? dev_set_name(genpd_dev, "genpd:%u:%s", index, dev_name(dev));
>>>> +???? genpd_dev->bus = &genpd_bus_type;
>>>> +???? genpd_dev->release = genpd_release_dev;
>>>> +
>>>> +???? ret = device_register(genpd_dev);
>>>> +???? if (ret) {
>>>> +???????????? kfree(genpd_dev);
>>>> +???????????? return ERR_PTR(ret);
>>>> +???? }
>>>> +
>>>> +???? /* Try to attach the device to the PM domain at the specified index. */
>>>> +???? ret = __genpd_dev_pm_attach(genpd_dev, dev->of_node, index);
>>>> +???? if (ret < 1) {
>>>> +???????????? device_unregister(genpd_dev);
>>>> +???????????? return ret ? ERR_PTR(ret) : NULL;
>>>> +???? }
>>>> +
>>>> +???? pm_runtime_set_active(genpd_dev);
>>>> +???? pm_runtime_enable(genpd_dev);
>>>> +
>>>> +???? return genpd_dev;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(genpd_dev_pm_attach_by_id);
>>>
>>> Thanks for sending this. Believe it or not this has still been on my to-do list
>>> and so we definitely need a solution for Tegra.
>>>
>>> Looking at the above it appears that additional power-domains exposed as devices
>>> to the client device. So I assume that this means that the drivers for devices
>>> with multiple power-domains will need to call RPM APIs for each of these
>>> additional power-domains. Is that correct?
>>
>> They can, but should not!
>>
>> Instead, the driver shall use device_link_add() and device_link_del(),
>> dynamically, depending on what PM domain that their original device
>> needs for the current running use case.
>>
>> In that way, they keep existing runtime PM deployment, operating on
>> its original device.
>
> OK, sounds good. Any reason why the linking cannot be handled by the above API? Is there a use-case where you would not want it linked?
I am guessing the linking is what would give the driver the ability to decide which subset of powerdomains it actually wants to control
at any point using runtime PM. If we have cases wherein the driver would want to turn on/off _all_ its associated powerdomains _always_
then a default linking of all would help.
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply
* [PATCH v3 3/3] ARM: dts: imx6ull-colibri-wifi: remove operating points
From: Viresh Kumar @ 2018-05-23 4:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180522062853.24799-3-sebastien.szymanski@armadeus.com>
On 22-05-18, 08:28, S?bastien Szymanski wrote:
> Operating points are now defined in the imx6ull.dtsi file so remove
> them from board device trees.
>
> Signed-off-by: S?bastien Szymanski <sebastien.szymanski@armadeus.com>
> ---
> arch/arm/boot/dts/imx6ull-colibri-wifi.dtsi | 14 --------------
> 1 file changed, 14 deletions(-)
>
> diff --git a/arch/arm/boot/dts/imx6ull-colibri-wifi.dtsi b/arch/arm/boot/dts/imx6ull-colibri-wifi.dtsi
> index 3dffbcd50bf6..183193e8580d 100644
> --- a/arch/arm/boot/dts/imx6ull-colibri-wifi.dtsi
> +++ b/arch/arm/boot/dts/imx6ull-colibri-wifi.dtsi
> @@ -20,20 +20,6 @@
>
> &cpu0 {
> clock-frequency = <792000000>;
> - operating-points = <
> - /* kHz uV */
> - 792000 1225000
> - 528000 1175000
> - 396000 1025000
> - 198000 950000
> - >;
> - fsl,soc-operating-points = <
> - /* KHz uV */
> - 792000 1175000
> - 528000 1175000
> - 396000 1175000
> - 198000 1175000
> - >;
> };
>
> &iomuxc {
Maybe you should merge this with the previous patch itself.
--
viresh
^ permalink raw reply
* [PATCH v3 1/3] cpufreq: imx6q: check speed grades for i.MX6ULL
From: Viresh Kumar @ 2018-05-23 4:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180522062853.24799-1-sebastien.szymanski@armadeus.com>
On 22-05-18, 08:28, S?bastien Szymanski wrote:
> Check the max speed supported from the fuses for i.MX6ULL and update the
> operating points table accordingly.
>
> Signed-off-by: S?bastien Szymanski <sebastien.szymanski@armadeus.com>
> ---
>
> Changes for v3:
> - none
>
> Changes for v2:
> - none
>
> drivers/cpufreq/imx6q-cpufreq.c | 29 +++++++++++++++++++++++------
> 1 file changed, 23 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c
> index 83cf631fc9bc..f094687cae52 100644
> --- a/drivers/cpufreq/imx6q-cpufreq.c
> +++ b/drivers/cpufreq/imx6q-cpufreq.c
> @@ -266,6 +266,8 @@ static void imx6q_opp_check_speed_grading(struct device *dev)
> }
>
> #define OCOTP_CFG3_6UL_SPEED_696MHZ 0x2
> +#define OCOTP_CFG3_6ULL_SPEED_792MHZ 0x2
> +#define OCOTP_CFG3_6ULL_SPEED_900MHZ 0x3
>
> static void imx6ul_opp_check_speed_grading(struct device *dev)
> {
> @@ -287,16 +289,30 @@ static void imx6ul_opp_check_speed_grading(struct device *dev)
> * Speed GRADING[1:0] defines the max speed of ARM:
> * 2b'00: Reserved;
> * 2b'01: 528000000Hz;
> - * 2b'10: 696000000Hz;
> - * 2b'11: Reserved;
> + * 2b'10: 696000000Hz on i.MX6UL, 792000000Hz on i.MX6ULL;
> + * 2b'11: 900000000Hz on i.MX6ULL only;
> * We need to set the max speed of ARM according to fuse map.
> */
> val = readl_relaxed(base + OCOTP_CFG3);
> val >>= OCOTP_CFG3_SPEED_SHIFT;
> val &= 0x3;
> - if (val != OCOTP_CFG3_6UL_SPEED_696MHZ)
> - if (dev_pm_opp_disable(dev, 696000000))
> - dev_warn(dev, "failed to disable 696MHz OPP\n");
> +
> + if (of_machine_is_compatible("fsl,imx6ul")) {
> + if (val != OCOTP_CFG3_6UL_SPEED_696MHZ)
> + if (dev_pm_opp_disable(dev, 696000000))
> + dev_warn(dev, "failed to disable 696MHz OPP\n");
> + }
> +
> + if (of_machine_is_compatible("fsl,imx6ull")) {
> + if (val != OCOTP_CFG3_6ULL_SPEED_792MHZ)
> + if (dev_pm_opp_disable(dev, 792000000))
> + dev_warn(dev, "failed to disable 792MHz OPP\n");
> +
> + if (val != OCOTP_CFG3_6ULL_SPEED_900MHZ)
> + if (dev_pm_opp_disable(dev, 900000000))
> + dev_warn(dev, "failed to disable 900MHz OPP\n");
> + }
> +
> iounmap(base);
> put_node:
> of_node_put(np);
> @@ -356,7 +372,8 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
> goto put_reg;
> }
>
> - if (of_machine_is_compatible("fsl,imx6ul"))
> + if (of_machine_is_compatible("fsl,imx6ul") ||
> + of_machine_is_compatible("fsl,imx6ull"))
> imx6ul_opp_check_speed_grading(cpu_dev);
> else
> imx6q_opp_check_speed_grading(cpu_dev);
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
--
viresh
^ permalink raw reply
* [PATCH v3 1/3] cpufreq: imx6q: check speed grades for i.MX6ULL
From: Viresh Kumar @ 2018-05-23 4:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180522062853.24799-1-sebastien.szymanski@armadeus.com>
On 22-05-18, 08:28, S?bastien Szymanski wrote:
> Check the max speed supported from the fuses for i.MX6ULL and update the
> operating points table accordingly.
>
> Signed-off-by: S?bastien Szymanski <sebastien.szymanski@armadeus.com>
> ---
>
> Changes for v3:
> - none
@Sascha and Shawn: Can you guys please Ack this series if there is
nothing wrong with it ?
--
viresh
^ permalink raw reply
* [PATCH v2 1/4] drm/mediatek: update dt-bindings for mt2712
From: CK Hu @ 2018-05-23 3:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180523022517.12103-2-stu.hsieh@mediatek.com>
Hi, Stu:
On Wed, 2018-05-23 at 10:25 +0800, Stu Hsieh wrote:
> Update device tree binding documentation for the display subsystem for
> Mediatek MT2712 SoCs.
>
Acked-by: CK Hu <ck.hu@mediatek.com>
> Signed-off-by: Stu Hsieh <stu.hsieh@mediatek.com>
> ---
> Documentation/devicetree/bindings/display/mediatek/mediatek,disp.txt | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,disp.txt b/Documentation/devicetree/bindings/display/mediatek/mediatek,disp.txt
> index 383183a89164..8469de510001 100644
> --- a/Documentation/devicetree/bindings/display/mediatek/mediatek,disp.txt
> +++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,disp.txt
> @@ -40,7 +40,7 @@ Required properties (all function blocks):
> "mediatek,<chip>-dpi" - DPI controller, see mediatek,dpi.txt
> "mediatek,<chip>-disp-mutex" - display mutex
> "mediatek,<chip>-disp-od" - overdrive
> - the supported chips are mt2701 and mt8173.
> + the supported chips are mt2701, mt2712 and mt8173.
> - reg: Physical base address and length of the function block register space
> - interrupts: The interrupt signal from the function block (required, except for
> merge and split function blocks).
^ permalink raw reply
* Difference between IOVA and bus address when SMMU is enabled
From: valmiki @ 2018-05-23 3:18 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180514105303.GE16141@n2100.armlinux.org.uk>
> On Sat, May 12, 2018 at 06:25:13PM +0530, valmiki wrote:
>> Hi All,
>>
>> What is the difference between IOVA address and bus address
>> when SMMU is enabled ?
>>
>> Is IOVA address term used only when hypervisor is present ?
>
> IOVA = IO virtual address. IOVA is the term normally used to describe
> the address used on the _device_ side of an IOMMU.
>
> For any general setup:
>
> RAM ----- MMU ----- DEVICE
> ^ ^
> physical virtual
> address address
>
> where "device" can be an IO device or a CPU, the terms still apply.
>
> If you have something like this:
>
> RAM ----- PCI bridge ----- MMU ----- DEVICE
> ^ ^ ^
> physical bus virtual
> address address address
>
> You could also have (eg, in the case of a system MMU):
>
> RAM ----- MMU ----- PCI bridge ----- DEVICE
> ^ ^ ^
> physical virtual bus
> address address address
> (this can also be
> considered a bus
> address!)
>
> In both of the above two cases, the PCI bridge may perform some address
> translation, meaning that the bus address is different from the address
> seen on the other side of the bridge.
>
> So, the terms used depend exactly on the overall bus topology.
>
> In the case of a system MMU, where the system MMU sits between peripheral
> devices and RAM, then the bus addresses are the same as the
> _IOVA of the system MMU_.
>
Thanks Russell.
---
This email has been checked for viruses by Avast antivirus software.
https://www.avast.com/antivirus
^ permalink raw reply
* [PATCH V2] PCI/portdrv: do not disable device on reboot/shutdown
From: Sinan Kaya @ 2018-05-23 2:44 UTC (permalink / raw)
To: linux-arm-kernel
'Commit cc27b735ad3a ("PCI/portdrv: Turn off PCIe services during
shutdown")' has been added to kernel to shutdown pending PCIe port
service interrupts during reboot so that a newly started kexec kernel
wouldn't observe pending interrupts.
pcie_port_device_remove() is disabling the root port and switches by
calling pci_disable_device() after all PCIe service drivers are shutdown.
pci_disable_device() has a much wider impact then port service itself and
it prevents all inbound transactions to reach to the system and impacts
the entire PCI traffic behind the bridge.
Issue is that pcie_port_device_remove() doesn't maintain any coordination
with the rest of the PCI device drivers in the system before clearing the
bus master bit.
This has been found to cause crashes on HP DL360 Gen9 machines during
reboot. Besides, kexec is already clearing the bus master bit in
pci_device_shutdown() after all PCI drivers are removed.
Just remove the extra clear in shutdown path by seperating the remove and
shutdown APIs in the PORTDRV.
Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
Link: https://bugzilla.kernel.org/show_bug.cgi?id=199779
Fixes: cc27b735ad3a ("PCI/portdrv: Turn off PCIe services during shutdown")
Cc: stable at vger.kernel.org
Reported-by: Ryan Finnie <ryan@finnie.org>
---
drivers/pci/pcie/portdrv.h | 2 +-
drivers/pci/pcie/portdrv_core.c | 5 +++--
drivers/pci/pcie/portdrv_pci.c | 16 +++++++++++++---
3 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index d0c6783..f6e88fe 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -86,7 +86,7 @@ int pcie_port_device_register(struct pci_dev *dev);
int pcie_port_device_suspend(struct device *dev);
int pcie_port_device_resume(struct device *dev);
#endif
-void pcie_port_device_remove(struct pci_dev *dev);
+void pcie_port_device_remove(struct pci_dev *dev, bool disable);
int __must_check pcie_port_bus_register(void);
void pcie_port_bus_unregister(void);
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index c9c0663..f35341e 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -405,11 +405,12 @@ static int remove_iter(struct device *dev, void *data)
* Remove PCI Express port service devices associated with given port and
* disable MSI-X or MSI for the port.
*/
-void pcie_port_device_remove(struct pci_dev *dev)
+void pcie_port_device_remove(struct pci_dev *dev, bool disable)
{
device_for_each_child(&dev->dev, NULL, remove_iter);
pci_free_irq_vectors(dev);
- pci_disable_device(dev);
+ if (disable)
+ pci_disable_device(dev);
}
/**
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index 973f1b8..29afaf3 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -137,7 +137,7 @@ static int pcie_portdrv_probe(struct pci_dev *dev,
return 0;
}
-static void pcie_portdrv_remove(struct pci_dev *dev)
+static void __pcie_portdrv_remove(struct pci_dev *dev, bool disable)
{
if (pci_bridge_d3_possible(dev)) {
pm_runtime_forbid(&dev->dev);
@@ -145,7 +145,17 @@ static void pcie_portdrv_remove(struct pci_dev *dev)
pm_runtime_dont_use_autosuspend(&dev->dev);
}
- pcie_port_device_remove(dev);
+ pcie_port_device_remove(dev, disable);
+}
+
+static void pcie_portdrv_remove(struct pci_dev *dev)
+{
+ return __pcie_portdrv_remove(dev, true);
+}
+
+static void pcie_portdrv_shutdown(struct pci_dev *dev)
+{
+ return __pcie_portdrv_remove(dev, false);
}
static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev,
@@ -218,7 +228,7 @@ static struct pci_driver pcie_portdriver = {
.probe = pcie_portdrv_probe,
.remove = pcie_portdrv_remove,
- .shutdown = pcie_portdrv_remove,
+ .shutdown = pcie_portdrv_shutdown,
.err_handler = &pcie_portdrv_err_handler,
--
2.7.4
^ permalink raw reply related
* [PATCH v4 2/2] regulator: add QCOM RPMh regulator driver
From: David Collins @ 2018-05-23 2:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1527040878.git.collinsd@codeaurora.org>
Add the QCOM RPMh regulator driver to manage PMIC regulators
which are controlled via RPMh on some Qualcomm Technologies, Inc.
SoCs. RPMh is a hardware block which contains several
accelerators which are used to manage various hardware resources
that are shared between the processors of the SoC. The final
hardware state of a regulator is determined within RPMh by
performing max aggregation of the requests made by all of the
processors.
Add support for PMIC regulator control via the voltage regulator
manager (VRM) and oscillator buffer (XOB) RPMh accelerators.
VRM supports manipulation of enable state, voltage, and mode.
XOB supports manipulation of enable state.
Signed-off-by: David Collins <collinsd@codeaurora.org>
---
drivers/regulator/Kconfig | 9 +
drivers/regulator/Makefile | 1 +
drivers/regulator/qcom-rpmh-regulator.c | 865 ++++++++++++++++++++++++++++++++
3 files changed, 875 insertions(+)
create mode 100644 drivers/regulator/qcom-rpmh-regulator.c
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 4efae3b..1a69bdc 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -671,6 +671,15 @@ config REGULATOR_QCOM_RPM
Qualcomm RPM as a module. The module will be named
"qcom_rpm-regulator".
+config REGULATOR_QCOM_RPMH
+ tristate "Qualcomm Technologies, Inc. RPMh regulator driver"
+ depends on QCOM_RPMH || COMPILE_TEST
+ help
+ This driver supports control of PMIC regulators via the RPMh hardware
+ block found on Qualcomm Technologies Inc. SoCs. RPMh regulator
+ control allows for voting on regulator state between multiple
+ processors within the SoC.
+
config REGULATOR_QCOM_SMD_RPM
tristate "Qualcomm SMD based RPM regulator driver"
depends on QCOM_SMD_RPM
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index d81fb02..906f048 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o
obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o
obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
+obj-$(CONFIG_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_SMD_RPM) += qcom_smd-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c
new file mode 100644
index 0000000..0217dcc
--- /dev/null
+++ b/drivers/regulator/qcom-rpmh-regulator.c
@@ -0,0 +1,865 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved. */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#include <soc/qcom/cmd-db.h>
+#include <soc/qcom/rpmh.h>
+
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+
+/**
+ * enum rpmh_regulator_type - supported RPMh accelerator types
+ * %VRM: RPMh VRM accelerator which supports voting on enable, voltage,
+ * and mode of LDO, SMPS, and BOB type PMIC regulators.
+ * %XOB: RPMh XOB accelerator which supports voting on the enable state
+ * of PMIC regulators.
+ */
+enum rpmh_regulator_type {
+ VRM,
+ XOB,
+};
+
+#define RPMH_VRM_HEADROOM_MAX_UV 511000
+
+#define RPMH_REGULATOR_REG_VRM_VOLTAGE 0x0
+#define RPMH_REGULATOR_REG_ENABLE 0x4
+#define RPMH_REGULATOR_REG_VRM_MODE 0x8
+#define RPMH_REGULATOR_REG_VRM_HEADROOM 0xC
+
+#define RPMH_REGULATOR_MODE_COUNT 4
+
+#define PMIC4_LDO_MODE_RETENTION 4
+#define PMIC4_LDO_MODE_LPM 5
+#define PMIC4_LDO_MODE_HPM 7
+
+#define PMIC4_SMPS_MODE_RETENTION 4
+#define PMIC4_SMPS_MODE_PFM 5
+#define PMIC4_SMPS_MODE_AUTO 6
+#define PMIC4_SMPS_MODE_PWM 7
+
+#define PMIC4_BOB_MODE_PASS 0
+#define PMIC4_BOB_MODE_PFM 1
+#define PMIC4_BOB_MODE_AUTO 2
+#define PMIC4_BOB_MODE_PWM 3
+
+/**
+ * struct rpmh_vreg_hw_data - RPMh regulator hardware configurations
+ * @regulator_type: RPMh accelerator type used to manage this
+ * regulator
+ * @ops: Pointer to regulator ops callback structure
+ * @voltage_range: The single range of voltages supported by this
+ * PMIC regulator type
+ * @n_voltages: The number of unique voltage set points defined
+ * by voltage_range
+ * @pmic_mode_map: Array indexed by regulator framework mode
+ * containing PMIC hardware modes. Must be large
+ * enough to index all framework modes supported
+ * by this regulator hardware type.
+ * @of_map_mode: Maps an RPMH_REGULATOR_MODE_* mode value defined
+ * in device tree to a regulator framework mode
+ */
+struct rpmh_vreg_hw_data {
+ enum rpmh_regulator_type regulator_type;
+ const struct regulator_ops *ops;
+ const struct regulator_linear_range voltage_range;
+ int n_voltages;
+ const int *pmic_mode_map;
+ unsigned int (*of_map_mode)(unsigned int mode);
+};
+
+/**
+ * struct rpmh_vreg - individual RPMh regulator data structure encapsulating a
+ * single regulator device
+ * @dev: Device pointer for the top-level PMIC RPMh
+ * regulator parent device. This is used as a
+ * handle in RPMh write requests.
+ * @addr: Base address of the regulator resource within
+ * an RPMh accelerator
+ * @rdesc: Regulator descriptor
+ * @hw_data: PMIC regulator configuration data for this RPMh
+ * regulator
+ * @always_wait_for_ack: Boolean flag indicating if a request must always
+ * wait for an ACK from RPMh before continuing even
+ * if it corresponds to a strictly lower power
+ * state (e.g. enabled --> disabled).
+ * @drms_modes: Array of regulator framework modes which can
+ * be configured dynamically for this regulator
+ * via the set_load() callback.
+ * @drms_mode_max_uAs: Array of maximum load currents in microamps
+ * supported by the corresponding modes in
+ * drms_mode. Elements must be specified in
+ * strictly increasing order.
+ * @drms_mode_count: The number of elements in drms_mode array.
+ * @enabled: Flag indicating if the regulator is enabled or
+ * not
+ * @ever_enabled: Boolean indicating that the regulator has been
+ * explicitly enabled at least once. Voltage
+ * requests should be cached when this flag is not
+ * set.
+ * @bypassed: Boolean indicating if the regulator is in
+ * bypass (pass-through) mode or not. This is
+ * only used by BOB rpmh-regulator resources.
+ * @voltage_selector: Selector used for get_voltage_sel() and
+ * set_voltage_sel() callbacks
+ * @mode: RPMh VRM regulator current framework mode
+ */
+struct rpmh_vreg {
+ struct device *dev;
+ u32 addr;
+ struct regulator_desc rdesc;
+ const struct rpmh_vreg_hw_data *hw_data;
+ bool always_wait_for_ack;
+ unsigned int *drms_modes;
+ int *drms_mode_max_uAs;
+ size_t drms_mode_count;
+
+ int enabled;
+ bool ever_enabled;
+ bool bypassed;
+ int voltage_selector;
+ unsigned int mode;
+};
+
+/**
+ * struct rpmh_vreg_init_data - initialization data for an RPMh regulator
+ * @name: Name for the regulator which also corresponds
+ * to the device tree subnode name of the regulator
+ * @resource_name: RPMh regulator resource name format string.
+ * This must include exactly one field: '%s' which
+ * is filled at run-time with the PMIC ID provided
+ * by device tree property qcom,pmic-id. Example:
+ * "ldo%s1" for RPMh resource "ldoa1".
+ * @supply_name: Parent supply regulator name
+ * @hw_data: Configuration data for this PMIC regulator type
+ */
+struct rpmh_vreg_init_data {
+ const char *name;
+ const char *resource_name;
+ const char *supply_name;
+ const struct rpmh_vreg_hw_data *hw_data;
+};
+
+/**
+ * rpmh_regulator_send_request() - send the request to RPMh
+ * @vreg: Pointer to the RPMh regulator
+ * @cmd: RPMh commands to send
+ * @count: Size of cmd array
+ * @wait_for_ack: Boolean indicating if execution must wait until the
+ * request has been acknowledged as complete
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_send_request(struct rpmh_vreg *vreg,
+ struct tcs_cmd *cmd, int count, bool wait_for_ack)
+{
+ int ret;
+
+ if (wait_for_ack || vreg->always_wait_for_ack)
+ ret = rpmh_write(vreg->dev, RPMH_ACTIVE_ONLY_STATE, cmd, count);
+ else
+ ret = rpmh_write_async(vreg->dev, RPMH_ACTIVE_ONLY_STATE, cmd,
+ count);
+
+ return ret;
+}
+
+static int _rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int selector, bool wait_for_ack)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+ struct tcs_cmd cmd = {
+ .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE,
+ };
+ int ret;
+
+ /* VRM voltage control register is set with voltage in millivolts. */
+ cmd.data = DIV_ROUND_UP(regulator_list_voltage_linear_range(rdev,
+ selector), 1000);
+
+ ret = rpmh_regulator_send_request(vreg, &cmd, 1, wait_for_ack);
+ if (!ret)
+ vreg->voltage_selector = selector;
+
+ return 0;
+}
+
+static int rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+ if (vreg->ever_enabled) {
+ return _rpmh_regulator_vrm_set_voltage_sel(rdev, selector,
+ selector > vreg->voltage_selector);
+ } else {
+ /*
+ * Cache the voltage and send it later when the regulator is
+ * enabled.
+ */
+ vreg->voltage_selector = selector;
+ }
+
+ return 0;
+}
+
+static int rpmh_regulator_vrm_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+ return vreg->voltage_selector;
+}
+
+static int rpmh_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+ return vreg->enabled;
+}
+
+static int rpmh_regulator_enable(struct regulator_dev *rdev)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+ struct tcs_cmd cmd = {
+ .addr = vreg->addr + RPMH_REGULATOR_REG_ENABLE,
+ .data = 1,
+ };
+ int ret;
+
+ if (!vreg->ever_enabled && vreg->voltage_selector != -ENOTRECOVERABLE) {
+ ret = _rpmh_regulator_vrm_set_voltage_sel(rdev,
+ vreg->voltage_selector, true);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = rpmh_regulator_send_request(vreg, &cmd, 1, true);
+ if (!ret) {
+ vreg->enabled = true;
+ vreg->ever_enabled = true;
+ }
+
+ return ret;
+}
+
+static int rpmh_regulator_disable(struct regulator_dev *rdev)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+ struct tcs_cmd cmd = {
+ .addr = vreg->addr + RPMH_REGULATOR_REG_ENABLE,
+ .data = 0,
+ };
+ int ret;
+
+ ret = rpmh_regulator_send_request(vreg, &cmd, 1, false);
+ if (!ret)
+ vreg->enabled = false;
+
+ return ret;
+}
+
+static int rpmh_regulator_vrm_set_mode_bypass(struct rpmh_vreg *vreg,
+ unsigned int mode, bool bypassed)
+{
+ struct tcs_cmd cmd = {
+ .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE,
+ };
+ int pmic_mode;
+
+ if (mode > REGULATOR_MODE_STANDBY)
+ return -EINVAL;
+
+ pmic_mode = vreg->hw_data->pmic_mode_map[mode];
+ if (pmic_mode < 0)
+ return pmic_mode;
+
+ cmd.data = bypassed ? PMIC4_BOB_MODE_PASS : pmic_mode;
+
+ return rpmh_regulator_send_request(vreg, &cmd, 1, true);
+}
+
+static int rpmh_regulator_vrm_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (mode == vreg->mode)
+ return 0;
+
+ ret = rpmh_regulator_vrm_set_mode_bypass(vreg, mode, vreg->bypassed);
+ if (!ret)
+ vreg->mode = mode;
+
+ return ret;
+}
+
+static unsigned int rpmh_regulator_vrm_get_mode(struct regulator_dev *rdev)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+ return vreg->mode;
+}
+
+/**
+ * rpmh_regulator_vrm_set_load() - set the regulator mode based upon the load
+ * current requested
+ * @rdev: Regulator device pointer for the rpmh-regulator
+ * @load_uA: Aggregated load current in microamps
+ *
+ * This function is used in the regulator_ops for VRM type RPMh regulator
+ * devices. It updates the mode of the regulator using a table of maximum
+ * load currents per mode specified in device tree properties.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_vrm_set_load(struct regulator_dev *rdev, int load_uA)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+ int i;
+
+ for (i = 0; i < vreg->drms_mode_count - 1; i++)
+ if (vreg->drms_mode_max_uAs[i] >= load_uA)
+ break;
+ if (load_uA > vreg->drms_mode_max_uAs[vreg->drms_mode_count - 1])
+ dev_warn(vreg->dev, "%s: requested load=%d uA greater than max=%d uA\n",
+ vreg->rdesc.name, load_uA,
+ vreg->drms_mode_max_uAs[vreg->drms_mode_count - 1]);
+
+ return rpmh_regulator_vrm_set_mode(rdev, vreg->drms_modes[i]);
+}
+
+static int rpmh_regulator_vrm_set_bypass(struct regulator_dev *rdev,
+ bool enable)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (vreg->bypassed == enable)
+ return 0;
+
+ ret = rpmh_regulator_vrm_set_mode_bypass(vreg, vreg->mode, enable);
+ if (!ret)
+ vreg->bypassed = enable;
+
+ return ret;
+}
+
+static int rpmh_regulator_vrm_get_bypass(struct regulator_dev *rdev,
+ bool *enable)
+{
+ struct rpmh_vreg *vreg = rdev_get_drvdata(rdev);
+
+ *enable = vreg->bypassed;
+
+ return 0;
+}
+
+static const struct regulator_ops rpmh_regulator_vrm_ops = {
+ .enable = rpmh_regulator_enable,
+ .disable = rpmh_regulator_disable,
+ .is_enabled = rpmh_regulator_is_enabled,
+ .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel,
+ .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .set_mode = rpmh_regulator_vrm_set_mode,
+ .get_mode = rpmh_regulator_vrm_get_mode,
+ .set_load = rpmh_regulator_vrm_set_load,
+};
+
+static const struct regulator_ops rpmh_regulator_vrm_bypass_ops = {
+ .enable = rpmh_regulator_enable,
+ .disable = rpmh_regulator_disable,
+ .is_enabled = rpmh_regulator_is_enabled,
+ .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel,
+ .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .set_mode = rpmh_regulator_vrm_set_mode,
+ .get_mode = rpmh_regulator_vrm_get_mode,
+ .set_load = rpmh_regulator_vrm_set_load,
+ .set_bypass = rpmh_regulator_vrm_set_bypass,
+ .get_bypass = rpmh_regulator_vrm_get_bypass,
+};
+
+static const struct regulator_ops rpmh_regulator_xob_ops = {
+ .enable = rpmh_regulator_enable,
+ .disable = rpmh_regulator_disable,
+ .is_enabled = rpmh_regulator_is_enabled,
+};
+
+/**
+ * rpmh_regulator_parse_vrm_modes() - parse the supported mode configurations
+ * for a VRM RPMh resource from device tree
+ * vreg: Pointer to the individual rpmh-regulator resource
+ * dev: Pointer to the top level rpmh-regulator PMIC device
+ * node: Pointer to the individual rpmh-regulator resource
+ * device node
+ *
+ * This function initializes the drms_modes[] and drms_mode_max_uAs[] arrays of
+ * vreg based upon the values of optional device tree properties.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_parse_vrm_modes(struct rpmh_vreg *vreg,
+ struct device *dev, struct device_node *node)
+{
+ const char *prop;
+ int i, len, ret, mode;
+ u32 *buf;
+
+ /* qcom,regulator-drms-modes is optional */
+ prop = "qcom,regulator-drms-modes";
+ len = of_property_count_elems_of_size(node, prop, sizeof(u32));
+ if (len < 0)
+ return 0;
+
+ vreg->drms_modes = devm_kcalloc(dev, len, sizeof(*vreg->drms_modes),
+ GFP_KERNEL);
+ vreg->drms_mode_max_uAs = devm_kcalloc(dev, len,
+ sizeof(*vreg->drms_mode_max_uAs),
+ GFP_KERNEL);
+ if (!vreg->drms_modes || !vreg->drms_mode_max_uAs)
+ return -ENOMEM;
+ vreg->drms_mode_count = len;
+
+ buf = kcalloc(len, sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(node, prop, buf, len);
+ if (ret < 0) {
+ dev_err(dev, "%s: unable to read %s, ret=%d\n",
+ node->name, prop, ret);
+ goto done;
+ }
+
+ for (i = 0; i < len; i++) {
+ mode = vreg->hw_data->of_map_mode(buf[i]);
+ if (mode == REGULATOR_MODE_INVALID) {
+ dev_err(dev, "%s: element %d of %s = %u is invalid for this regulator\n",
+ node->name, i, prop, buf[i]);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ vreg->drms_modes[i] = mode;
+ }
+
+ prop = "qcom,drms-mode-max-microamps";
+ len = of_property_count_elems_of_size(node, prop, sizeof(u32));
+ if (len != vreg->drms_mode_count) {
+ dev_err(dev, "%s: invalid element count=%d for %s\n",
+ node->name, len, prop);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = of_property_read_u32_array(node, prop, buf, len);
+ if (ret < 0) {
+ dev_err(dev, "%s: unable to read %s, ret=%d\n",
+ node->name, prop, ret);
+ goto done;
+ }
+
+ for (i = 0; i < len; i++) {
+ vreg->drms_mode_max_uAs[i] = buf[i];
+
+ if (i > 0 && vreg->drms_mode_max_uAs[i]
+ <= vreg->drms_mode_max_uAs[i - 1]) {
+ dev_err(dev, "%s: %s elements are not in ascending order\n",
+ node->name, prop);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+done:
+ kfree(buf);
+ return ret;
+}
+
+/**
+ * rpmh_regulator_init_vreg() - initialize all attributes of an rpmh-regulator
+ * vreg: Pointer to the individual rpmh-regulator resource
+ * dev: Pointer to the top level rpmh-regulator PMIC device
+ * node: Pointer to the individual rpmh-regulator resource
+ * device node
+ * pmic_id: String used to identify the top level rpmh-regulator
+ * PMIC device on the board
+ * rpmh_data: Pointer to a null-terminated array of rpmh-regulator
+ * resources defined for the top level PMIC device
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg, struct device *dev,
+ struct device_node *node, const char *pmic_id,
+ const struct rpmh_vreg_init_data *rpmh_data)
+{
+ struct regulator_config reg_config = {};
+ char rpmh_resource_name[20] = "";
+ struct regulator_dev *rdev;
+ enum rpmh_regulator_type type;
+ struct regulator_init_data *init_data;
+ int ret;
+
+ vreg->dev = dev;
+
+ for (; rpmh_data->name; rpmh_data++)
+ if (!strcmp(rpmh_data->name, node->name))
+ break;
+
+ if (!rpmh_data->name) {
+ dev_err(dev, "Unknown regulator %s\n", node->name);
+ return -EINVAL;
+ }
+
+ scnprintf(rpmh_resource_name, sizeof(rpmh_resource_name),
+ rpmh_data->resource_name, pmic_id);
+
+ vreg->addr = cmd_db_read_addr(rpmh_resource_name);
+ if (!vreg->addr) {
+ dev_err(dev, "%s: could not find RPMh address for resource %s\n",
+ node->name, rpmh_resource_name);
+ return -ENODEV;
+ }
+
+ vreg->rdesc.name = rpmh_data->name;
+ vreg->rdesc.supply_name = rpmh_data->supply_name;
+ vreg->hw_data = rpmh_data->hw_data;
+
+ vreg->enabled = -EINVAL;
+ vreg->voltage_selector = -ENOTRECOVERABLE;
+ vreg->mode = REGULATOR_MODE_INVALID;
+
+ if (rpmh_data->hw_data->n_voltages) {
+ vreg->rdesc.linear_ranges = &rpmh_data->hw_data->voltage_range;
+ vreg->rdesc.n_linear_ranges = 1;
+ vreg->rdesc.n_voltages = rpmh_data->hw_data->n_voltages;
+ }
+
+ type = rpmh_data->hw_data->regulator_type;
+ if (type == VRM) {
+ ret = rpmh_regulator_parse_vrm_modes(vreg, dev, node);
+ if (ret < 0)
+ return ret;
+ }
+
+ vreg->always_wait_for_ack = of_property_read_bool(node,
+ "qcom,always-wait-for-ack");
+
+ vreg->rdesc.owner = THIS_MODULE;
+ vreg->rdesc.type = REGULATOR_VOLTAGE;
+ vreg->rdesc.ops = vreg->hw_data->ops;
+ vreg->rdesc.of_map_mode = vreg->hw_data->of_map_mode;
+
+ init_data = of_get_regulator_init_data(dev, node, &vreg->rdesc);
+ if (!init_data)
+ return -ENOMEM;
+
+ if (type == XOB && init_data->constraints.min_uV &&
+ init_data->constraints.min_uV == init_data->constraints.max_uV) {
+ vreg->rdesc.fixed_uV = init_data->constraints.min_uV;
+ vreg->rdesc.n_voltages = 1;
+ }
+
+ reg_config.dev = dev;
+ reg_config.init_data = init_data;
+ reg_config.of_node = node;
+ reg_config.driver_data = vreg;
+
+ rdev = devm_regulator_register(dev, &vreg->rdesc, ®_config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ rdev = NULL;
+ dev_err(dev, "%s: devm_regulator_register() failed, ret=%d\n",
+ node->name, ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "%s regulator registered for RPMh resource %s @ 0x%05X\n",
+ node->name, rpmh_resource_name, vreg->addr);
+
+ return ret;
+}
+
+static const int pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = {
+ [REGULATOR_MODE_INVALID] = -EINVAL,
+ [REGULATOR_MODE_STANDBY] = PMIC4_LDO_MODE_RETENTION,
+ [REGULATOR_MODE_IDLE] = PMIC4_LDO_MODE_LPM,
+ [REGULATOR_MODE_NORMAL] = -EINVAL,
+ [REGULATOR_MODE_FAST] = PMIC4_LDO_MODE_HPM,
+};
+
+static unsigned int rpmh_regulator_pmic4_ldo_of_map_mode(unsigned int mode)
+{
+ static const unsigned int of_mode_map[RPMH_REGULATOR_MODE_COUNT] = {
+ [RPMH_REGULATOR_MODE_RET] = REGULATOR_MODE_STANDBY,
+ [RPMH_REGULATOR_MODE_LPM] = REGULATOR_MODE_IDLE,
+ [RPMH_REGULATOR_MODE_AUTO] = REGULATOR_MODE_INVALID,
+ [RPMH_REGULATOR_MODE_HPM] = REGULATOR_MODE_FAST,
+ };
+
+ if (mode >= RPMH_REGULATOR_MODE_COUNT)
+ return -EINVAL;
+
+ return of_mode_map[mode];
+}
+
+static const int pmic_mode_map_pmic4_smps[REGULATOR_MODE_STANDBY + 1] = {
+ [REGULATOR_MODE_INVALID] = -EINVAL,
+ [REGULATOR_MODE_STANDBY] = PMIC4_SMPS_MODE_RETENTION,
+ [REGULATOR_MODE_IDLE] = PMIC4_SMPS_MODE_PFM,
+ [REGULATOR_MODE_NORMAL] = PMIC4_SMPS_MODE_AUTO,
+ [REGULATOR_MODE_FAST] = PMIC4_SMPS_MODE_PWM,
+};
+
+static unsigned int rpmh_regulator_pmic4_smps_of_map_mode(unsigned int mode)
+{
+ static const unsigned int of_mode_map[RPMH_REGULATOR_MODE_COUNT] = {
+ [RPMH_REGULATOR_MODE_RET] = REGULATOR_MODE_STANDBY,
+ [RPMH_REGULATOR_MODE_LPM] = REGULATOR_MODE_IDLE,
+ [RPMH_REGULATOR_MODE_AUTO] = REGULATOR_MODE_NORMAL,
+ [RPMH_REGULATOR_MODE_HPM] = REGULATOR_MODE_FAST,
+ };
+
+ if (mode >= RPMH_REGULATOR_MODE_COUNT)
+ return -EINVAL;
+
+ return of_mode_map[mode];
+}
+
+static const int pmic_mode_map_pmic4_bob[REGULATOR_MODE_STANDBY + 1] = {
+ [REGULATOR_MODE_INVALID] = -EINVAL,
+ [REGULATOR_MODE_STANDBY] = -EINVAL,
+ [REGULATOR_MODE_IDLE] = PMIC4_BOB_MODE_PFM,
+ [REGULATOR_MODE_NORMAL] = PMIC4_BOB_MODE_AUTO,
+ [REGULATOR_MODE_FAST] = PMIC4_BOB_MODE_PWM,
+};
+
+static unsigned int rpmh_regulator_pmic4_bob_of_map_mode(unsigned int mode)
+{
+ static const unsigned int of_mode_map[RPMH_REGULATOR_MODE_COUNT] = {
+ [RPMH_REGULATOR_MODE_RET] = REGULATOR_MODE_INVALID,
+ [RPMH_REGULATOR_MODE_LPM] = REGULATOR_MODE_IDLE,
+ [RPMH_REGULATOR_MODE_AUTO] = REGULATOR_MODE_NORMAL,
+ [RPMH_REGULATOR_MODE_HPM] = REGULATOR_MODE_FAST,
+ };
+
+ if (mode >= RPMH_REGULATOR_MODE_COUNT)
+ return -EINVAL;
+
+ return of_mode_map[mode];
+}
+
+static const struct rpmh_vreg_hw_data pmic4_pldo = {
+ .regulator_type = VRM,
+ .ops = &rpmh_regulator_vrm_ops,
+ .voltage_range = REGULATOR_LINEAR_RANGE(1664000, 0, 255, 8000),
+ .n_voltages = 256,
+ .pmic_mode_map = pmic_mode_map_pmic4_ldo,
+ .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_pldo_lv = {
+ .regulator_type = VRM,
+ .ops = &rpmh_regulator_vrm_ops,
+ .voltage_range = REGULATOR_LINEAR_RANGE(1256000, 0, 127, 8000),
+ .n_voltages = 128,
+ .pmic_mode_map = pmic_mode_map_pmic4_ldo,
+ .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_nldo = {
+ .regulator_type = VRM,
+ .ops = &rpmh_regulator_vrm_ops,
+ .voltage_range = REGULATOR_LINEAR_RANGE(312000, 0, 127, 8000),
+ .n_voltages = 128,
+ .pmic_mode_map = pmic_mode_map_pmic4_ldo,
+ .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_hfsmps3 = {
+ .regulator_type = VRM,
+ .ops = &rpmh_regulator_vrm_ops,
+ .voltage_range = REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000),
+ .n_voltages = 216,
+ .pmic_mode_map = pmic_mode_map_pmic4_smps,
+ .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_ftsmps426 = {
+ .regulator_type = VRM,
+ .ops = &rpmh_regulator_vrm_ops,
+ .voltage_range = REGULATOR_LINEAR_RANGE(320000, 0, 258, 4000),
+ .n_voltages = 259,
+ .pmic_mode_map = pmic_mode_map_pmic4_smps,
+ .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_bob = {
+ .regulator_type = VRM,
+ .ops = &rpmh_regulator_vrm_bypass_ops,
+ .voltage_range = REGULATOR_LINEAR_RANGE(1824000, 0, 83, 32000),
+ .n_voltages = 84,
+ .pmic_mode_map = pmic_mode_map_pmic4_bob,
+ .of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode,
+};
+
+static const struct rpmh_vreg_hw_data pmic4_lvs = {
+ .regulator_type = XOB,
+ .ops = &rpmh_regulator_xob_ops,
+ /* LVS hardware does not support voltage or mode configuration. */
+};
+
+#define RPMH_VREG(_name, _resource_name, _hw_data, _supply_name) \
+{ \
+ .name = _name, \
+ .resource_name = _resource_name, \
+ .hw_data = _hw_data, \
+ .supply_name = _supply_name, \
+}
+
+static const struct rpmh_vreg_init_data pm8998_vreg_data[] = {
+ RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"),
+ RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"),
+ RPMH_VREG("smps3", "smp%s3", &pmic4_hfsmps3, "vdd-s3"),
+ RPMH_VREG("smps4", "smp%s4", &pmic4_hfsmps3, "vdd-s4"),
+ RPMH_VREG("smps5", "smp%s5", &pmic4_hfsmps3, "vdd-s5"),
+ RPMH_VREG("smps6", "smp%s6", &pmic4_ftsmps426, "vdd-s6"),
+ RPMH_VREG("smps7", "smp%s7", &pmic4_ftsmps426, "vdd-s7"),
+ RPMH_VREG("smps8", "smp%s8", &pmic4_ftsmps426, "vdd-s8"),
+ RPMH_VREG("smps9", "smp%s9", &pmic4_ftsmps426, "vdd-s9"),
+ RPMH_VREG("smps10", "smp%s10", &pmic4_ftsmps426, "vdd-s10"),
+ RPMH_VREG("smps11", "smp%s11", &pmic4_ftsmps426, "vdd-s11"),
+ RPMH_VREG("smps12", "smp%s12", &pmic4_ftsmps426, "vdd-s12"),
+ RPMH_VREG("smps13", "smp%s13", &pmic4_ftsmps426, "vdd-s13"),
+ RPMH_VREG("ldo1", "ldo%s1", &pmic4_nldo, "vdd-l1-l27"),
+ RPMH_VREG("ldo2", "ldo%s2", &pmic4_nldo, "vdd-l2-l8-l17"),
+ RPMH_VREG("ldo3", "ldo%s3", &pmic4_nldo, "vdd-l3-l11"),
+ RPMH_VREG("ldo4", "ldo%s4", &pmic4_nldo, "vdd-l4-l5"),
+ RPMH_VREG("ldo5", "ldo%s5", &pmic4_nldo, "vdd-l4-l5"),
+ RPMH_VREG("ldo6", "ldo%s6", &pmic4_pldo, "vdd-l6"),
+ RPMH_VREG("ldo7", "ldo%s7", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"),
+ RPMH_VREG("ldo8", "ldo%s8", &pmic4_nldo, "vdd-l2-l8-l17"),
+ RPMH_VREG("ldo9", "ldo%s9", &pmic4_pldo, "vdd-l9"),
+ RPMH_VREG("ldo10", "ldo%s10", &pmic4_pldo, "vdd-l10-l23-l25"),
+ RPMH_VREG("ldo11", "ldo%s11", &pmic4_nldo, "vdd-l3-l11"),
+ RPMH_VREG("ldo12", "ldo%s12", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"),
+ RPMH_VREG("ldo13", "ldo%s13", &pmic4_pldo, "vdd-l13-l19-l21"),
+ RPMH_VREG("ldo14", "ldo%s14", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"),
+ RPMH_VREG("ldo15", "ldo%s15", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"),
+ RPMH_VREG("ldo16", "ldo%s16", &pmic4_pldo, "vdd-l16-l28"),
+ RPMH_VREG("ldo17", "ldo%s17", &pmic4_nldo, "vdd-l2-l8-l17"),
+ RPMH_VREG("ldo18", "ldo%s18", &pmic4_pldo, "vdd-l18-l22"),
+ RPMH_VREG("ldo19", "ldo%s19", &pmic4_pldo, "vdd-l13-l19-l21"),
+ RPMH_VREG("ldo20", "ldo%s20", &pmic4_pldo, "vdd-l20-l24"),
+ RPMH_VREG("ldo21", "ldo%s21", &pmic4_pldo, "vdd-l13-l19-l21"),
+ RPMH_VREG("ldo22", "ldo%s22", &pmic4_pldo, "vdd-l18-l22"),
+ RPMH_VREG("ldo23", "ldo%s23", &pmic4_pldo, "vdd-l10-l23-l25"),
+ RPMH_VREG("ldo24", "ldo%s24", &pmic4_pldo, "vdd-l20-l24"),
+ RPMH_VREG("ldo25", "ldo%s25", &pmic4_pldo, "vdd-l10-l23-l25"),
+ RPMH_VREG("ldo26", "ldo%s26", &pmic4_nldo, "vdd-l26"),
+ RPMH_VREG("ldo27", "ldo%s27", &pmic4_nldo, "vdd-l1-l27"),
+ RPMH_VREG("ldo28", "ldo%s28", &pmic4_pldo, "vdd-l16-l28"),
+ RPMH_VREG("lvs1", "vs%s1", &pmic4_lvs, "vin-lvs-1-2"),
+ RPMH_VREG("lvs2", "vs%s2", &pmic4_lvs, "vin-lvs-1-2"),
+ {},
+};
+
+static const struct rpmh_vreg_init_data pmi8998_vreg_data[] = {
+ RPMH_VREG("bob", "bob%s1", &pmic4_bob, "vdd-bob"),
+ {},
+};
+
+static const struct rpmh_vreg_init_data pm8005_vreg_data[] = {
+ RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"),
+ RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"),
+ RPMH_VREG("smps3", "smp%s3", &pmic4_ftsmps426, "vdd-s3"),
+ RPMH_VREG("smps4", "smp%s4", &pmic4_ftsmps426, "vdd-s4"),
+ {},
+};
+
+static int rpmh_regulator_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct rpmh_vreg_init_data *vreg_data;
+ struct device_node *node;
+ struct rpmh_vreg *vreg;
+ const char *pmic_id;
+ int ret;
+
+ ret = cmd_db_ready();
+ if (ret < 0) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Command DB not available, ret=%d\n", ret);
+ return ret;
+ }
+
+ vreg_data = of_device_get_match_data(dev);
+ if (!vreg_data)
+ return -ENODEV;
+
+ ret = of_property_read_string(dev->of_node, "qcom,pmic-id", &pmic_id);
+ if (ret < 0) {
+ dev_err(dev, "qcom,pmic-id missing in DT node\n");
+ return ret;
+ }
+
+ for_each_available_child_of_node(dev->of_node, node) {
+ vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+ if (!vreg) {
+ of_node_put(node);
+ return -ENOMEM;
+ }
+
+ ret = rpmh_regulator_init_vreg(vreg, dev, node, pmic_id,
+ vreg_data);
+ if (ret < 0) {
+ of_node_put(node);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct of_device_id rpmh_regulator_match_table[] = {
+ {
+ .compatible = "qcom,pm8998-rpmh-regulators",
+ .data = pm8998_vreg_data,
+ },
+ {
+ .compatible = "qcom,pmi8998-rpmh-regulators",
+ .data = pmi8998_vreg_data,
+ },
+ {
+ .compatible = "qcom,pm8005-rpmh-regulators",
+ .data = pm8005_vreg_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table);
+
+static struct platform_driver rpmh_regulator_driver = {
+ .driver = {
+ .name = "qcom-rpmh-regulator",
+ .of_match_table = of_match_ptr(rpmh_regulator_match_table),
+ },
+ .probe = rpmh_regulator_probe,
+};
+module_platform_driver(rpmh_regulator_driver);
+
+MODULE_DESCRIPTION("Qualcomm RPMh regulator driver");
+MODULE_LICENSE("GPL v2");
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v4 1/2] regulator: dt-bindings: add QCOM RPMh regulator bindings
From: David Collins @ 2018-05-23 2:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1527040878.git.collinsd@codeaurora.org>
Introduce bindings for RPMh regulator devices found on some
Qualcomm Technlogies, Inc. SoCs. These devices allow a given
processor within the SoC to make PMIC regulator requests which
are aggregated within the RPMh hardware block along with requests
from other processors in the SoC to determine the final PMIC
regulator hardware state.
Signed-off-by: David Collins <collinsd@codeaurora.org>
---
.../bindings/regulator/qcom,rpmh-regulator.txt | 185 +++++++++++++++++++++
.../dt-bindings/regulator/qcom,rpmh-regulator.h | 36 ++++
2 files changed, 221 insertions(+)
create mode 100644 Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
create mode 100644 include/dt-bindings/regulator/qcom,rpmh-regulator.h
diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
new file mode 100644
index 0000000..aaac975
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
@@ -0,0 +1,185 @@
+Qualcomm Technologies, Inc. RPMh Regulators
+
+rpmh-regulator devices support PMIC regulator management via the Voltage
+Regulator Manager (VRM) and Oscillator Buffer (XOB) RPMh accelerators. The APPS
+processor communicates with these hardware blocks via a Resource State
+Coordinator (RSC) using command packets. The VRM allows changing three
+parameters for a given regulator: enable state, output voltage, and operating
+mode. The XOB allows changing only a single parameter for a given regulator:
+its enable state. Despite its name, the XOB is capable of controlling the
+enable state of any PMIC peripheral. It is used for clock buffers, low-voltage
+switches, and LDO/SMPS regulators which have a fixed voltage and mode.
+
+=======================
+Required Node Structure
+=======================
+
+RPMh regulators must be described in two levels of device nodes. The first
+level describes the PMIC containing the regulators and must reside within an
+RPMh device node. The second level describes each regulator within the PMIC
+which is to be used on the board. Each of these regulators maps to a single
+RPMh resource.
+
+The names used for regulator nodes must match those supported by a given PMIC.
+Supported regulator node names:
+ PM8998: smps1 - smps13, ldo1 - ldo28, lvs1 - lvs2
+ PMI8998: bob
+ PM8005: smps1 - smps4
+
+========================
+First Level Nodes - PMIC
+========================
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: Must be one of: "qcom,pm8998-rpmh-regulators",
+ "qcom,pmi8998-rpmh-regulators" or
+ "qcom,pm8005-rpmh-regulators".
+
+- qcom,pmic-id
+ Usage: required
+ Value type: <string>
+ Definition: RPMh resource name suffix used for the regulators found on
+ this PMIC. Typical values: "a", "b", "c", "d", "e", "f".
+
+- vdd-s1-supply
+- vdd-s2-supply
+- vdd-s3-supply
+- vdd-s4-supply
+ Usage: optional (PM8998 and PM8005 only)
+ Value type: <phandle>
+ Definition: phandle of the parent supply regulator of one or more of the
+ regulators for this PMIC.
+
+- vdd-s5-supply
+- vdd-s6-supply
+- vdd-s7-supply
+- vdd-s8-supply
+- vdd-s9-supply
+- vdd-s10-supply
+- vdd-s11-supply
+- vdd-s12-supply
+- vdd-s13-supply
+- vdd-l1-l27-supply
+- vdd-l2-l8-l17-supply
+- vdd-l3-l11-supply
+- vdd-l4-l5-supply
+- vdd-l6-supply
+- vdd-l7-l12-l14-l15-supply
+- vdd-l9-supply
+- vdd-l10-l23-l25-supply
+- vdd-l13-l19-l21-supply
+- vdd-l16-l28-supply
+- vdd-l18-l22-supply
+- vdd-l20-l24-supply
+- vdd-l26-supply
+- vin-lvs-1-2-supply
+ Usage: optional (PM8998 only)
+ Value type: <phandle>
+ Definition: phandle of the parent supply regulator of one or more of the
+ regulators for this PMIC.
+
+- vdd-bob-supply
+ Usage: optional (PMI8998 only)
+ Value type: <phandle>
+ Definition: BOB regulator parent supply phandle
+
+===============================
+Second Level Nodes - Regulators
+===============================
+
+- qcom,regulator-drms-modes
+ Usage: required if regulator-allow-set-load is specified;
+ VRM regulators only
+ Value type: <prop-encoded-array>
+ Definition: A list of integers specifying the PMIC regulator modes which
+ can be configured at runtime based upon consumer load needs.
+ Supported values are RPMH_REGULATOR_MODE_* which are defined
+ in [1] (i.e. 0 to 3).
+
+- qcom,drms-mode-max-microamps
+ Usage: required if regulator-allow-set-load is specified;
+ VRM regulators only
+ Value type: <prop-encoded-array>
+ Definition: A list of integers specifying the maximum allowed load
+ current in microamps for each of the modes listed in
+ qcom,regulator-drms-modes (matched 1-to-1 in order).
+ Elements must be specified in order from lowest to highest
+ value.
+
+- qcom,always-wait-for-ack
+ Usage: optional
+ Value type: <empty>
+ Definition: Boolean flag which indicates that the application processor
+ must wait for an ACK or a NACK from RPMh for every request
+ sent for this regulator including those which are for a
+ strictly lower power state.
+
+Other properties defined in Documentation/devicetree/bindings/regulator.txt
+may also be used. regulator-initial-mode and regulator-allowed-modes may be
+specified for VRM regulators using mode values from [1]. regulator-allow-bypass
+may be specified for BOB type regulators managed via VRM.
+regulator-allow-set-load may be specified for VRM regulators. It is used in
+conjunction with qcom,regulator-drms-modes and qcom,drms-mode-max-microamps to
+define a dynamic total load current to mode mapping.
+
+[1] include/dt-bindings/regulator/qcom,rpmh-regulator.h
+
+========
+Examples
+========
+
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+
+&apps_rsc {
+ pm8998-rpmh-regulators {
+ compatible = "qcom,pm8998-rpmh-regulators";
+ qcom,pmic-id = "a";
+
+ vdd-l7-l12-l14-l15-supply = <&pm8998_s5>;
+
+ smps2 {
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ };
+
+ pm8998_s5: smps5 {
+ regulator-min-microvolt = <1904000>;
+ regulator-max-microvolt = <2040000>;
+ };
+
+ ldo7 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_LPM>;
+ regulator-allowed-modes =
+ <RPMH_REGULATOR_MODE_LPM
+ RPMH_REGULATOR_MODE_HPM>;
+ regulator-allow-set-load;
+ qcom,regulator-drms-modes =
+ <RPMH_REGULATOR_MODE_LPM
+ RPMH_REGULATOR_MODE_HPM>;
+ qcom,drms-mode-max-microamps = <10000 1000000>;
+ };
+
+ lvs1 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+ };
+
+ pmi8998-rpmh-regulators {
+ compatible = "qcom,pmi8998-rpmh-regulators";
+ qcom,pmic-id = "b";
+
+ bob {
+ regulator-min-microvolt = <3312000>;
+ regulator-max-microvolt = <3600000>;
+ regulator-allowed-modes =
+ <RPMH_REGULATOR_MODE_AUTO
+ RPMH_REGULATOR_MODE_HPM>;
+ regulator-initial-mode = <RPMH_REGULATOR_MODE_AUTO>;
+ };
+ };
+};
diff --git a/include/dt-bindings/regulator/qcom,rpmh-regulator.h b/include/dt-bindings/regulator/qcom,rpmh-regulator.h
new file mode 100644
index 0000000..86713dc
--- /dev/null
+++ b/include/dt-bindings/regulator/qcom,rpmh-regulator.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, The Linux Foundation. All rights reserved. */
+
+#ifndef __QCOM_RPMH_REGULATOR_H
+#define __QCOM_RPMH_REGULATOR_H
+
+/*
+ * These mode constants may be used to specify modes for various RPMh regulator
+ * device tree properties (e.g. regulator-initial-mode). Each type of regulator
+ * supports a subset of the possible modes.
+ *
+ * %RPMH_REGULATOR_MODE_RET: Retention mode in which only an extremely small
+ * load current is allowed. This mode is supported
+ * by LDO and SMPS type regulators.
+ * %RPMH_REGULATOR_MODE_LPM: Low power mode in which a small load current is
+ * allowed. This mode corresponds to PFM for SMPS
+ * and BOB type regulators. This mode is supported
+ * by LDO, HFSMPS, BOB, and PMIC4 FTSMPS type
+ * regulators.
+ * %RPMH_REGULATOR_MODE_AUTO: Auto mode in which the regulator hardware
+ * automatically switches between LPM and HPM based
+ * upon the real-time load current. This mode is
+ * supported by HFSMPS, BOB, and PMIC4 FTSMPS type
+ * regulators.
+ * %RPMH_REGULATOR_MODE_HPM: High power mode in which the full rated current
+ * of the regulator is allowed. This mode
+ * corresponds to PWM for SMPS and BOB type
+ * regulators. This mode is supported by all types
+ * of regulators.
+ */
+#define RPMH_REGULATOR_MODE_RET 0
+#define RPMH_REGULATOR_MODE_LPM 1
+#define RPMH_REGULATOR_MODE_AUTO 2
+#define RPMH_REGULATOR_MODE_HPM 3
+
+#endif
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply related
* [PATCH v4 0/2] regulator: add QCOM RPMh regulator driver
From: David Collins @ 2018-05-23 2:43 UTC (permalink / raw)
To: linux-arm-kernel
This patch series adds a driver and device tree binding documentation for
PMIC regulator control via Resource Power Manager-hardened (RPMh) on some
Qualcomm Technologies, Inc. SoCs such as SDM845. RPMh is a hardware block
which contains several accelerators which are used to manage various
hardware resources that are shared between the processors of the SoC. The
final hardware state of a regulator is determined within RPMh by performing
max aggregation of the requests made by all of the processors.
The RPMh regulator driver depends upon the RPMh driver [1] and command DB
driver [2] which are both still undergoing review. It also depends upon
three recent regulator changes: [3], [4], and [5].
Changes since v3 [6]:
- Removed support for DT properties qcom,regulator-initial-microvolt
and qcom,headroom-microvolt
- Renamed DT property qcom,allowed-drms-modes to be
qcom,regulator-drms-modes
- Updated DT binding documentation to mention which common regulator
bindings can be used for qcom-rpmh-regulator devices
- Added voltage caching so that voltage requests are only sent to RPMh
after the regulator has been enabled at least once
- Changed 'voltage_selector' default value to be -ENOTRECOVERABLE to
interact with [5]
- Initialized 'enabled' to -EINVAL so that unused regulators are
disabled by regulator_late_cleanup()
- Removed rpmh_regulator_load_default_parameters() as it is no longer
needed
- Updated the mode usage description in qcom,rpmh-regulator.h
Changes since v2 [7]:
- Replaced '_' with '-' in device tree supply property names
- Renamed qcom_rpmh-regulator.c to be qcom-rpmh-regulator.c
- Updated various DT property names to use "microvolt" and "microamp"
- Moved allowed modes constraint specification out of the driver [4]
- Replaced rpmh_client with device pointer to match new RPMh API [1]
- Corrected drms mode threshold checking
- Initialized voltage_selector to -EINVAL when not specified in DT
- Added constants for PMIC regulator hardware modes
- Corrected type sign of mode mapping tables
- Made variable names for mode arrays plural
- Simplified Kconfig depends on
- Removed unnecessary constants and struct fields
- Added some descriptive comments
Changes since v1 [8]:
- Addressed review feedback from Doug, Mark, and Stephen
- Replaced set_voltage()/get_voltage() callbacks with set_voltage_sel()/
get_voltage_sel()
- Added set_bypass()/get_bypass() callbacks for BOB pass-through mode
control
- Removed top-level PMIC data structures
- Removed initialization variables from structs and passed them as
function parameters
- Removed various comments and error messages
- Simplified mode handling
- Refactored per-PMIC rpmh-regulator data specification
- Simplified probe function
- Moved header into DT patch
- Removed redundant property listings from DT binding documentation
[1]: https://lkml.org/lkml/2018/5/9/729
[2]: https://lkml.org/lkml/2018/4/10/714
[3]: https://lkml.org/lkml/2018/4/18/556
[4]: https://lkml.org/lkml/2018/5/11/696
[5]: https://lkml.org/lkml/2018/5/15/1005
[6]: https://lkml.org/lkml/2018/5/11/701
[7]: https://lkml.org/lkml/2018/4/13/687
[8]: https://lkml.org/lkml/2018/3/16/1431
David Collins (2):
regulator: dt-bindings: add QCOM RPMh regulator bindings
regulator: add QCOM RPMh regulator driver
.../bindings/regulator/qcom,rpmh-regulator.txt | 185 +++++
drivers/regulator/Kconfig | 9 +
drivers/regulator/Makefile | 1 +
drivers/regulator/qcom-rpmh-regulator.c | 865 +++++++++++++++++++++
.../dt-bindings/regulator/qcom,rpmh-regulator.h | 36 +
5 files changed, 1096 insertions(+)
create mode 100644 Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
create mode 100644 drivers/regulator/qcom-rpmh-regulator.c
create mode 100644 include/dt-bindings/regulator/qcom,rpmh-regulator.h
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* [PATCH] PCI/portdrv: do not disable device on remove()
From: Sinan Kaya @ 2018-05-23 2:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180523022431.GA15069@wunner.de>
On 5/22/2018 10:24 PM, Lukas Wunner wrote:
> On Tue, May 22, 2018 at 01:58:00PM -0400, Sinan Kaya wrote:
>> --- a/drivers/pci/pcie/portdrv_core.c
>> +++ b/drivers/pci/pcie/portdrv_core.c
>> @@ -409,7 +409,6 @@ void pcie_port_device_remove(struct pci_dev *dev)
>> {
>> device_for_each_child(&dev->dev, NULL, remove_iter);
>> pci_free_irq_vectors(dev);
>> - pci_disable_device(dev);
>> }
>
> Shutdown aside, pci_disable_device() is also not called in the ->remove
> path with this patch, right? Seems wrong. E.g. when unbinding the driver
> from the root port device, or when unplugging a port (happens all the time
> with Thunderbolt).
Agreed. I'll spin another version where I skip disable on shutdown path only.
>
> Thanks,
>
> Lukas
>
--
Sinan Kaya
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply
* [PATCH v2 2/5] gpio: syscon: Add gpio-syscon for rockchip
From: Levin Du @ 2018-05-23 2:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180522180238.GA7328@rob-hp-laptop>
On 2018-05-23 2:02 AM, Rob Herring wrote:
> On Fri, May 18, 2018 at 11:52:05AM +0800, djw at t-chip.com.cn wrote:
>> From: Levin Du <djw@t-chip.com.cn>
>>
>> Some GPIOs sit in the GRF_SOC_CON registers of Rockchip SoCs,
>> which do not belong to the general pinctrl.
>>
>> Adding gpio-syscon support makes controlling regulator or
>> LED using these special pins very easy by reusing existing
>> drivers, such as gpio-regulator and led-gpio.
>>
>> Signed-off-by: Levin Du <djw@t-chip.com.cn>
>>
>> ---
>>
>> Changes in v2:
>> - Rename gpio_syscon10 to gpio_mute in doc
>>
>> Changes in v1:
>> - Refactured for general gpio-syscon usage for Rockchip SoCs.
>> - Add doc rockchip,gpio-syscon.txt
>>
>> .../bindings/gpio/rockchip,gpio-syscon.txt | 41 ++++++++++++++++++++++
>> drivers/gpio/gpio-syscon.c | 30 ++++++++++++++++
>> 2 files changed, 71 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/gpio/rockchip,gpio-syscon.txt
>>
>> diff --git a/Documentation/devicetree/bindings/gpio/rockchip,gpio-syscon.txt b/Documentation/devicetree/bindings/gpio/rockchip,gpio-syscon.txt
>> new file mode 100644
>> index 0000000..b1b2a67
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/gpio/rockchip,gpio-syscon.txt
>> @@ -0,0 +1,41 @@
>> +* Rockchip GPIO support for GRF_SOC_CON registers
>> +
>> +Required properties:
>> +- compatible: Should contain "rockchip,gpio-syscon".
>> +- gpio-controller: Marks the device node as a gpio controller.
>> +- #gpio-cells: Should be two. The first cell is the pin number and
>> + the second cell is used to specify the gpio polarity:
>> + 0 = Active high,
>> + 1 = Active low.
> There's no need for this child node. Just make the parent node a gpio
> controller.
>
> Rob
Hi Rob, it is not clear to me. Do you suggest that the grf node should
be a gpio controller,
like below?
+??? grf: syscon at ff100000 {
+??? ??? compatible = "rockchip,gpio-syscon", "rockchip,rk3328-grf",
"syscon", "simple-mfd";
+??? ??? //...
+??? ??? gpio-controller;
+??? ??? #gpio-cells = <2>;
+??? ??? gpio,syscon-dev = <&grf 0x0428 0>;
+??? };
or just reserve the following case in the doc?
+??? grf: syscon at ff100000 {
+??? ??? compatible = "rockchip,rk3328-grf", "syscon", "simple-mfd";
+??? ??? //...
+??? };
+
+??? gpio_mute: gpio-mute {
+??? ??? compatible = "rockchip,gpio-syscon";
+??? ??? gpio-controller;
+??? ??? #gpio-cells = <2>;
+??? ??? gpio,syscon-dev = <&grf 0x0428 0>;
+??? };
Thanks
Levin
^ permalink raw reply
* [PATCH net] net: phy: broadcom: Fix bcm_write_exp()
From: Florian Fainelli @ 2018-05-23 1:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180523001503.GA16062@lunn.ch>
Hi Andrew,
On 05/22/2018 05:15 PM, Andrew Lunn wrote:
> On Tue, May 22, 2018 at 05:04:49PM -0700, Florian Fainelli wrote:
>> On newer PHYs, we need to select the expansion register to write with
>> setting bits [11:8] to 0xf. This was done correctly by bcm7xxx.c prior
>> to being migrated to generic code under bcm-phy-lib.c which
>> unfortunately used the older implementation from the BCM54xx days.
>
> Hi Florian
>
> Does selecting the expansion register affect access to the standard
> registers? Does this need locking like the Marvell PHY has when
> changing pages?
We should probably convert this to the page accessors since the
expansion, misc and other shadow 0x1c accesses are all indirection
layers to poke into a different address space of the PHY. That would be
a separate fix though for a number of reasons.
--
Florian
^ permalink raw reply
* [PATCH v3 1/2] regulator: dt-bindings: add QCOM RPMh regulator bindings
From: David Collins @ 2018-05-23 1:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAD=FV=W=BquL5ojORz4HNP889s=-uNJ-QEzUfAxwXj2Oa3cGew@mail.gmail.com>
On 05/22/2018 05:08 PM, Doug Anderson wrote:
> On Tue, May 22, 2018 at 3:46 PM, David Collins <collinsd@codeaurora.org> wrote:
>> On 05/22/2018 09:43 AM, Doug Anderson wrote:
>>> On Mon, May 21, 2018 at 5:00 PM, David Collins <collinsd@codeaurora.org> wrote:
...
>> However, if the voltage caching feature is acceptable for upstream usage,
>> then I could add it. With that in place, I see less of a need for the
>> qcom,regulator-initial-microvolt property and would be ok with removing it
>> for now.
>
> I think it's the right thing to do an Mark didn't seem to yell, so I'd
> say go for it.
I'll remove qcom,regulator-initial-microvolt support and add voltage caching.
>>> OK, so how's this for a proposal then:
>>>
>>> 1. For RPMh-regulator whenever we see a "set voltage" but Linux hasn't
>>> specified that the regulator is enabled then we don't send the
>>> voltage, we just cache it.
>>>
>>> 2. When we see the first enable then we first send the cached voltage
>>> and then do the enable.
>>>
>>> 3. We don't need an "initial voltage" because any rails that are
>>> expected to be variable voltage the client should be choosing a
>>> voltage.
>>>
>>>
>>> ...taking the SD card case as an example: if the regulator framework
>>> says "set to the minimum: 1.8V" we'll cache this but not apply it yet
>>> because the rail is off as far as Linux is concerned. Then when the
>>> SD card framework starts up it will set the voltage to 3.3V which will
>>> overwrite the cache. Then the SD card framework will enable the
>>> regulator and RPMh will set the voltage to 3.3V and enable.
>>
>> I am ok with implementing this feature.
>>
>> However, should the voltage be cached instead of sent immediately any time
>> that Linux has not explicitly requested the regulator to be enabled, or
>> only before the first time that an enable request is sent?
>>
>> 1. regulator_set_voltage(reg, 2960000, 2960000)
>> --> cache voltage=2960 mV
>> 2. regulator_enable(reg)
>> --> Send voltage=2960 then enable=1
>> 3. regulator_disable(reg)
>> --> Send enable=0
>> 4. regulator_set_voltage(reg, 1800000, 2960000)
>> --> A. Send voltage=1800 OR B. cache voltage=1800?
>>
>> Option A is used on the downstream rpmh-regulator driver in order to avoid
>> cases where AP votes to disable a regulator that is kept enabled by
>> another subsystem but then does not lower the voltage requested thanks to
>> regulator_set_voltage() being called after regulator_disable(). I plan to
>> go with option A for qcom-rpmh-regulator unless there are objections.
>
> So one client's vote for a voltage continues to be in effect even if
> that client votes to have the regulator disabled? That seems
> fundamentally broken in RPMh. I guess my take would be to work around
> this RPMh misfeature by saying that whenever Linux votes to disable a
> regulator it also votes for a voltage of 0. Then another client of
> RPMh won't be affected.
>
> That seems like it would be beneficial in any case. If the AP always
> asks for 1.3 V and the modem always asks for 1.1 V for the same rail
> then the rail should go down to 1.1 V when the AP says to disable the
> rail.
The VRM in RPMh hardware aggregates enable state, output voltage, mode,
and headroom voltage requests independently for each regulator. This
allows for maximum request flexibility and makes no assumptions about
consumer use cases. There is nothing inherently wrong about this approach.
I'm concerned about a blanket policy of setting voltage=0 when issuing
disable requests. That seems to violate the semantics of the
regulator_set_voltage() API which enforces the requested voltage range
until a new range is specified. There may be workarounds that require a
regulator to operate at a specific voltage even when no Linux consumers
explicitly need the regulator enabled.
Given that not masking the voltage on disable is guaranteed to be safe and
does not silently break potential use cases, I plan to stick with this
approach. Therefore, the question about option A or B for voltage caching
is still relevant. I think that option A is safer/more API conforming so
I plan to implement that.
>>> This whole thing makes me worry about another problem, though. You
>>> say that the bootloader left the SD card rail on, right? ...but as
>>> far as Linux is concerned the SD card rail is off (argh, it can't read
>>> the state that the bootloader left the rail in!)
>>>
>>> ...now imagine any of the following:
>>>
>>> * The user boots up a kernel where the SD card driver is disabled.
>>>
>>> * The user ejects the SD card right after the bootloader used it but
>>> before the kernel driver started.
>>>
>>> When the kernel comes up it will believe that the SD card rail is
>>> disabled so it won't try to disable it. So the voltage will be left
>>> on.
>>
>> We have not encountered issues with regulators getting left on
>> indefinitely because Linux devices failed to take control of them during
>> boot. I don't think that this hypothetical issue needs to be addressed in
>> the first qcom-rpmh-regulator driver patch if at all.
>
> I don't think it hypothetical at all. Linux takes control of a
> regulator and then at bootup it disables all regulators that aren't
> currently used/enabled. In your case you will report that regulators
> are already disabled and thus you'll neuter the kernel's attempt to
> disable regulators that nobody is using but that might have been left
> on by the bootloader. It seemed like Mark agreed here.
I did not consider regulator_late_cleanup(). I'll initialize the enabled
value in qcom-rpmh-regulator to -EINVAL to handle this case and also so
that _regulator_enable() succeeds without further core modification.
>>> You can even come up with a less contrived use case here. One could
>>> argue that the SD card framework really ought to be ensuring VMMC and
>>> VQMMC are off before it starts tweaking with them just in case the
>>> bootloader left them on. Thus, it should do:
>>>
>>> A) Turn off VMMC and VQMMC
>>> B) Program VMMC and VQMMC to defaults
>>> C) Turn on VMMC and VQMMC
>>>
>>> ...right now we bootup and pretend to Linux that VMMC and VQMMC start
>>> off, so step A) will be no-op. Sigh.
>>
>> Step A) would not work because the regulator's use_count would be 0 and
>> regulator_disable() can only be called successfully if use_count > 0. The
>> call would have no impact and it would return an error.
>
> Are you sure regulator_force_disable() won't do the trick on most
> boards (which will report the regulator being enabled at bootup)? I
> haven't tried it, but it seems like it might.
regulator_force_disable() would indeed call the disable() callback.
However, this function is designed for emergency uses only and will cause
the use_count to become out of sync with the requested regulator state. I
don't think that we want to suggest to consumers that they call
regulator_force_disable(). It would completely break any shared regulator
uses cases between multiple Linux consumers.
Take care,
David
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* [PATCH v6 5/5] drm/rockchip: support dp training outside dp firmware
From: hl @ 2018-05-23 1:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAFqH_52A6n9fuCFLbDaaAZB1YjyWLMVMViam5BvfYyA75eYr5w@mail.gmail.com>
Hi Enric,
On Wednesday, May 23, 2018 01:06 AM, Enric Balletbo Serra wrote:
> Lin,
>
> 2018-05-22 9:41 GMT+02:00 Enric Balletbo Serra <eballetbo@gmail.com>:
>> Hi Lin
>>
>> 2018-05-22 3:08 GMT+02:00 hl <hl@rock-chips.com>:
>>> Hi Enric,
>>>
>>>
>>>
>>>
>>> On Monday, May 21, 2018 11:22 PM, Enric Balletbo Serra wrote:
>>>> Hi Lin,
>>>>
>>>> 2018-05-21 11:37 GMT+02:00 Lin Huang <hl@rock-chips.com>:
>>>>> DP firmware uses fixed phy config values to do training, but some
>>>>> boards need to adjust these values to fit for their unique hardware
>>>>> design. So get phy config values from dts and use software link training
>>>>> instead of relying on firmware, if software training fail, keep firmware
>>>>> training as a fallback if sw training fails.
>>>>>
>>>>> Signed-off-by: Chris Zhong <zyw@rock-chips.com>
>>>>> Signed-off-by: Lin Huang <hl@rock-chips.com>
>>>>> Reviewed-by: Sean Paul <seanpaul@chromium.org>
>>>>> ---
>>>>> Changes in v2:
>>>>> - update patch following Enric suggest
>>>>> Changes in v3:
>>>>> - use variable fw_training instead sw_training_success
>>>>> - base on DP SPCE, if training fail use lower link rate to retry training
>>>>> Changes in v4:
>>>>> - improve cdn_dp_get_lower_link_rate() and cdn_dp_software_train_link()
>>>>> follow Sean suggest
>>>>> Changes in v5:
>>>>> - fix some whitespcae issue
>>>>> Changes in v6:
>>>>> - None
>>>>>
>>>>> drivers/gpu/drm/rockchip/Makefile | 3 +-
>>>>> drivers/gpu/drm/rockchip/cdn-dp-core.c | 24 +-
>>>>> drivers/gpu/drm/rockchip/cdn-dp-core.h | 2 +
>>>>> drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 420
>>>>> ++++++++++++++++++++++++
>>>>> drivers/gpu/drm/rockchip/cdn-dp-reg.c | 31 +-
>>>>> drivers/gpu/drm/rockchip/cdn-dp-reg.h | 38 ++-
>>>>> 6 files changed, 505 insertions(+), 13 deletions(-)
>>>>> create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>>>>>
>>>>> diff --git a/drivers/gpu/drm/rockchip/Makefile
>>>>> b/drivers/gpu/drm/rockchip/Makefile
>>>>> index a314e21..b932f62 100644
>>>>> --- a/drivers/gpu/drm/rockchip/Makefile
>>>>> +++ b/drivers/gpu/drm/rockchip/Makefile
>>>>> @@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>>>>> rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>>>>>
>>>>> rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>>>>> -rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
>>>>> +rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \
>>>>> + cdn-dp-link-training.o
>>>>> rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>>>>> rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
>>>>> rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
>>>>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c
>>>>> b/drivers/gpu/drm/rockchip/cdn-dp-core.c
>>>>> index cce64c1..d9d0d4d 100644
>>>>> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
>>>>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
>>>>> @@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct
>>>>> drm_encoder *encoder)
>>>>> goto out;
>>>>> }
>>>>> }
>>>>> -
>>>>> - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
>>>>> - if (ret) {
>>>>> - DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret);
>>>>> - goto out;
>>>>> + if (dp->use_fw_training == true) {
>>>> You don't need to compare to true. Simply do:
>>>>
>>>> if (dp->use_fw_training)
>>>>
>>>>> + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
>>>>> + if (ret) {
>>>>> + DRM_DEV_ERROR(dp->dev,
>>>>> + "Failed to idle video %d\n", ret);
>>>>> + goto out;
>>>>> + }
>>>>> }
>>>>>
>>>>> ret = cdn_dp_config_video(dp);
>>>>> @@ -642,11 +644,15 @@ static void cdn_dp_encoder_enable(struct
>>>>> drm_encoder *encoder)
>>>>> goto out;
>>>>> }
>>>>>
>>>>> - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
>>>>> - if (ret) {
>>>>> - DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n",
>>>>> ret);
>>>>> - goto out;
>>>>> + if (dp->use_fw_training == true) {
>>>> if (dp->use_fw_training)
>>>>
>>>>> + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
>>>>> + if (ret) {
>>>>> + DRM_DEV_ERROR(dp->dev,
>>>>> + "Failed to valid video %d\n", ret);
>>>>> + goto out;
>>>>> + }
>>>>> }
>>>>> +
>>>>> out:
>>>>> mutex_unlock(&dp->lock);
>>>>> }
>>>>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h
>>>>> b/drivers/gpu/drm/rockchip/cdn-dp-core.h
>>>>> index 46159b2..77a9793 100644
>>>>> --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
>>>>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
>>>>> @@ -84,6 +84,7 @@ struct cdn_dp_device {
>>>>> bool connected;
>>>>> bool active;
>>>>> bool suspended;
>>>>> + bool use_fw_training;
>>>>>
>>>>> const struct firmware *fw; /* cdn dp firmware */
>>>>> unsigned int fw_version; /* cdn fw version */
>>>>> @@ -106,6 +107,7 @@ struct cdn_dp_device {
>>>>> u8 ports;
>>>>> u8 lanes;
>>>>> int active_port;
>>>>> + u8 train_set[4];
>>>>>
>>>>> u8 dpcd[DP_RECEIVER_CAP_SIZE];
>>>>> bool sink_has_audio;
>>>>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>>>>> b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>>>>> new file mode 100644
>>>>> index 0000000..73c3290
>>>>> --- /dev/null
>>>>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
>>>>> @@ -0,0 +1,420 @@
>>>>> +// SPDX-License-Identifier: GPL-2.0
>>>>> +/*
>>>>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>>>>> + * Author: Chris Zhong <zyw@rock-chips.com>
>>>>> + */
> Oh, I just noticed that this is not the preferred format for .c files,
> see the discussion here
>
> https://lkml.org/lkml/2017/11/25/133
So what is the standard? the document:
https://www.kernel.org/doc/html/latest/process/license-rules.html
or Linus's word, do we have official about it?
>
>>>>> +
>>>>> +#include <linux/device.h>
>>>>> +#include <linux/delay.h>
>>>>> +#include <linux/phy/phy.h>
>>>>> +#include <soc/rockchip/rockchip_phy_typec.h>
>>>>> +
>>>>> +#include "cdn-dp-core.h"
>>>>> +#include "cdn-dp-reg.h"
>>>>> +
>>>>> +static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp)
>>>>> +{
>>>>> + struct cdn_dp_port *port = dp->port[dp->active_port];
>>>>> + struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy);
>>>>> +
>>>>> + int rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
>>>>> + u8 swing = (dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) >>
>>>>> + DP_TRAIN_VOLTAGE_SWING_SHIFT;
>>>>> + u8 pre_emphasis = (dp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
>>>>> + >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
>>>>> +
>>>>> + tcphy->typec_phy_config(port->phy, rate, dp->link.num_lanes,
>>>>> + swing, pre_emphasis);
>>>>> +}
>>>>> +
>>>>> +static int cdn_dp_set_pattern(struct cdn_dp_device *dp, uint8_t
>>>>> dp_train_pat)
>>>>> +{
>>>>> + u32 phy_config, global_config;
>>>>> + int ret;
>>>>> + uint8_t pattern = dp_train_pat & DP_TRAINING_PATTERN_MASK;
>>>>> +
>>>>> + global_config = NUM_LANES(dp->link.num_lanes - 1) | SST_MODE |
>>>>> + GLOBAL_EN | RG_EN | ENC_RST_DIS | WR_VHSYNC_FALL;
>>>>> +
>>>>> + phy_config = DP_TX_PHY_ENCODER_BYPASS(0) |
>>>>> + DP_TX_PHY_SKEW_BYPASS(0) |
>>>>> + DP_TX_PHY_DISPARITY_RST(0) |
>>>>> + DP_TX_PHY_LANE0_SKEW(0) |
>>>>> + DP_TX_PHY_LANE1_SKEW(1) |
>>>>> + DP_TX_PHY_LANE2_SKEW(2) |
>>>>> + DP_TX_PHY_LANE3_SKEW(3) |
>>>>> + DP_TX_PHY_10BIT_ENABLE(0);
>>>>> +
>>>>> + if (pattern != DP_TRAINING_PATTERN_DISABLE) {
>>>>> + global_config |= NO_VIDEO;
>>>>> + phy_config |= DP_TX_PHY_TRAINING_ENABLE(1) |
>>>>> + DP_TX_PHY_SCRAMBLER_BYPASS(1) |
>>>>> + DP_TX_PHY_TRAINING_PATTERN(pattern);
>>>>> + }
>>>>> +
>>>>> + ret = cdn_dp_reg_write(dp, DP_FRAMER_GLOBAL_CONFIG,
>>>>> global_config);
>>>>> + if (ret) {
>>>>> + DRM_ERROR("fail to set DP_FRAMER_GLOBAL_CONFIG, error:
>>>>> %d\n",
>>>>> + ret);
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + ret = cdn_dp_reg_write(dp, DP_TX_PHY_CONFIG_REG, phy_config);
>>>>> + if (ret) {
>>>>> + DRM_ERROR("fail to set DP_TX_PHY_CONFIG_REG, error:
>>>>> %d\n",
>>>>> + ret);
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + ret = cdn_dp_reg_write(dp, DPTX_LANE_EN, BIT(dp->link.num_lanes)
>>>>> - 1);
>>>>> + if (ret) {
>>>>> + DRM_ERROR("fail to set DPTX_LANE_EN, error: %d\n", ret);
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + if (drm_dp_enhanced_frame_cap(dp->dpcd))
>>>>> + ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 1);
>>>>> + else
>>>>> + ret = cdn_dp_reg_write(dp, DPTX_ENHNCD, 0);
>>>>> + if (ret)
>>>>> + DRM_ERROR("failed to set DPTX_ENHNCD, error: %x\n", ret);
>>>>> +
>>>>> + return ret;
>>>>> +}
>>>>> +
>>>>> +static u8 cdn_dp_pre_emphasis_max(u8 voltage_swing)
>>>>> +{
>>>>> + switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>>>>> + case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>>>>> + return DP_TRAIN_PRE_EMPH_LEVEL_3;
>>>>> + case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>>>>> + return DP_TRAIN_PRE_EMPH_LEVEL_2;
>>>>> + case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>>>>> + return DP_TRAIN_PRE_EMPH_LEVEL_1;
>>>>> + default:
>>>>> + return DP_TRAIN_PRE_EMPH_LEVEL_0;
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +static void cdn_dp_get_adjust_train(struct cdn_dp_device *dp,
>>>>> + uint8_t
>>>>> link_status[DP_LINK_STATUS_SIZE])
>>>>> +{
>>>>> + int i;
>>>>> + uint8_t v = 0, p = 0;
>>>>> + uint8_t preemph_max;
>>>>> +
>>>>> + for (i = 0; i < dp->link.num_lanes; i++) {
>>>>> + v = max(v, drm_dp_get_adjust_request_voltage(link_status,
>>>>> i));
>>>>> + p = max(p,
>>>>> drm_dp_get_adjust_request_pre_emphasis(link_status,
>>>>> + i));
>>>>> + }
>>>>> +
>>>>> + if (v >= VOLTAGE_LEVEL_2)
>>>>> + v = VOLTAGE_LEVEL_2 | DP_TRAIN_MAX_SWING_REACHED;
>>>>> +
>>>>> + preemph_max = cdn_dp_pre_emphasis_max(v);
>>>>> + if (p >= preemph_max)
>>>>> + p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
>>>>> +
>>>>> + for (i = 0; i < dp->link.num_lanes; i++)
>>>>> + dp->train_set[i] = v | p;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * Pick training pattern for channel equalization. Training Pattern 3
>>>>> for HBR2
>>>>> + * or 1.2 devices that support it, Training Pattern 2 otherwise.
>>>>> + */
>>>>> +static u32 cdn_dp_select_chaneq_pattern(struct cdn_dp_device *dp)
>>>>> +{
>>>>> + u32 training_pattern = DP_TRAINING_PATTERN_2;
>>>>> +
>>>>> + /*
>>>>> + * cdn dp support HBR2 also support TPS3. TPS3 support is also
>>>>> mandatory
>>>>> + * for downstream devices that support HBR2. However, not all
>>>>> sinks
>>>>> + * follow the spec.
>>>>> + */
>>>>> + if (drm_dp_tps3_supported(dp->dpcd))
>>>>> + training_pattern = DP_TRAINING_PATTERN_3;
>>>>> + else
>>>>> + DRM_DEBUG_KMS("5.4 Gbps link rate without sink TPS3
>>>>> support\n");
>>>>> +
>>>>> + return training_pattern;
>>>>> +}
>>>>> +
>>>>> +
>>>>> +static bool cdn_dp_link_max_vswing_reached(struct cdn_dp_device *dp)
>>>>> +{
>>>>> + int lane;
>>>>> +
>>>>> + for (lane = 0; lane < dp->link.num_lanes; lane++)
>>>>> + if ((dp->train_set[lane] & DP_TRAIN_MAX_SWING_REACHED) ==
>>>>> 0)
>>>>> + return false;
>>>>> +
>>>>> + return true;
>>>>> +}
>>>>> +
>>>>> +static int cdn_dp_update_link_train(struct cdn_dp_device *dp)
>>>>> +{
>>>>> + int ret;
>>>>> +
>>>>> + cdn_dp_set_signal_levels(dp);
>>>>> +
>>>>> + ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
>>>>> + dp->train_set, dp->link.num_lanes);
>>>>> + if (ret != dp->link.num_lanes)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static int cdn_dp_set_link_train(struct cdn_dp_device *dp,
>>>>> + uint8_t dp_train_pat)
>>>>> +{
>>>>> + uint8_t buf[sizeof(dp->train_set) + 1];
>>>>> + int ret, len;
>>>>> +
>>>>> + buf[0] = dp_train_pat;
>>>>> + if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) ==
>>>>> + DP_TRAINING_PATTERN_DISABLE) {
>>>>> + /* don't write DP_TRAINING_LANEx_SET on disable */
>>>>> + len = 1;
>>>>> + } else {
>>>>> + /* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET
>>>>> */
>>>>> + memcpy(buf + 1, dp->train_set, dp->link.num_lanes);
>>>>> + len = dp->link.num_lanes + 1;
>>>>> + }
>>>>> +
>>>>> + ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_PATTERN_SET,
>>>>> + buf, len);
>>>>> + if (ret != len)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +static int cdn_dp_reset_link_train(struct cdn_dp_device *dp,
>>>>> + uint8_t dp_train_pat)
>>>>> +{
>>>>> + int ret;
>>>>> +
>>>>> + memset(dp->train_set, 0, sizeof(dp->train_set));
>>>>> +
>>>>> + cdn_dp_set_signal_levels(dp);
>>>>> +
>>>>> + ret = cdn_dp_set_pattern(dp, dp_train_pat);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + return cdn_dp_set_link_train(dp, dp_train_pat);
>>>>> +}
>>>>> +
>>>>> +/* Enable corresponding port and start training pattern 1 */
>>>>> +static int cdn_dp_link_training_clock_recovery(struct cdn_dp_device *dp)
>>>>> +{
>>>>> + u8 voltage;
>>>>> + u8 link_status[DP_LINK_STATUS_SIZE];
>>>>> + u32 voltage_tries, max_vswing_tries;
>>>>> + int ret;
>>>>> +
>>>>> + /* clock recovery */
>>>>> + ret = cdn_dp_reset_link_train(dp, DP_TRAINING_PATTERN_1 |
>>>>> + DP_LINK_SCRAMBLING_DISABLE);
>>>>> + if (ret) {
>>>>> + DRM_ERROR("failed to start link train\n");
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + voltage_tries = 1;
>>>>> + max_vswing_tries = 0;
>>>>> + for (;;) {
>>>>> + drm_dp_link_train_clock_recovery_delay(dp->dpcd);
>>>>> + if (drm_dp_dpcd_read_link_status(&dp->aux, link_status)
>>>>> !=
>>>>> + DP_LINK_STATUS_SIZE) {
>>>>> + DRM_ERROR("failed to get link status\n");
>>>>> + return -EINVAL;
>>>>> + }
>>>>> +
>>>>> + if (drm_dp_clock_recovery_ok(link_status,
>>>>> dp->link.num_lanes)) {
>>>>> + DRM_DEBUG_KMS("clock recovery OK\n");
>>>>> + return 0;
>>>>> + }
>>>>> +
>>>>> + if (voltage_tries >= 5) {
>>>>> + DRM_DEBUG_KMS("Same voltage tried 5 times\n");
>>>>> + return -EINVAL;
>>>>> + }
>>>>> +
>>>>> + if (max_vswing_tries >= 1) {
>>>>> + DRM_DEBUG_KMS("Max Voltage Swing reached\n");
>>>>> + return -EINVAL;
>>>>> + }
>>>>> +
>>>>> + voltage = dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
>>>>> +
>>>>> + /* Update training set as requested by target */
>>>>> + cdn_dp_get_adjust_train(dp, link_status);
>>>>> + if (cdn_dp_update_link_train(dp)) {
>>>>> + DRM_ERROR("failed to update link training\n");
>>>>> + return -EINVAL;
>>>>> + }
>>>>> +
>>>>> + if ((dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) ==
>>>>> + voltage)
>>>>> + ++voltage_tries;
>>>>> + else
>>>>> + voltage_tries = 1;
>>>>> +
>>>>> + if (cdn_dp_link_max_vswing_reached(dp))
>>>>> + ++max_vswing_tries;
>>>>> + }
>>>>> +}
>>>>> +
>>>>> +static int cdn_dp_link_training_channel_equalization(struct
>>>>> cdn_dp_device *dp)
>>>>> +{
>>>>> + int tries, ret;
>>>>> + u32 training_pattern;
>>>>> + uint8_t link_status[DP_LINK_STATUS_SIZE];
>>>>> +
>>>>> + training_pattern = cdn_dp_select_chaneq_pattern(dp);
>>>>> + training_pattern |= DP_LINK_SCRAMBLING_DISABLE;
>>>>> +
>>>>> + ret = cdn_dp_set_pattern(dp, training_pattern);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + ret = cdn_dp_set_link_train(dp, training_pattern);
>>>>> + if (ret) {
>>>>> + DRM_ERROR("failed to start channel equalization\n");
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + for (tries = 0; tries < 5; tries++) {
>>>>> + drm_dp_link_train_channel_eq_delay(dp->dpcd);
>>>>> + if (drm_dp_dpcd_read_link_status(&dp->aux, link_status)
>>>>> !=
>>>>> + DP_LINK_STATUS_SIZE) {
>>>>> + DRM_ERROR("failed to get link status\n");
>>>>> + break;
>>>>> + }
>>>>> +
>>>>> + /* Make sure clock is still ok */
>>>>> + if (!drm_dp_clock_recovery_ok(link_status,
>>>>> + dp->link.num_lanes)) {
>>>>> + DRM_DEBUG_KMS("Clock recovery check failed\n");
>>>>> + break;
>>>>> + }
>>>>> +
>>>>> + if (drm_dp_channel_eq_ok(link_status,
>>>>> dp->link.num_lanes)) {
>>>>> + DRM_DEBUG_KMS("Channel EQ done\n");
>>>>> + return 0;
>>>>> + }
>>>>> +
>>>>> + /* Update training set as requested by target */
>>>>> + cdn_dp_get_adjust_train(dp, link_status);
>>>>> + if (cdn_dp_update_link_train(dp)) {
>>>>> + DRM_ERROR("failed to update link training\n");
>>>>> + break;
>>>>> + }
>>>>> + }
>>>>> +
>>>>> + /* Try 5 times, else fail and try at lower BW */
>>>>> + if (tries == 5)
>>>>> + DRM_DEBUG_KMS("Channel equalization failed 5 times\n");
>>>>> +
>>>>> + return -EINVAL;
>>>>> +}
>>>>> +
>>>>> +static int cdn_dp_stop_link_train(struct cdn_dp_device *dp)
>>>>> +{
>>>>> + int ret = cdn_dp_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE);
>>>>> +
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + return cdn_dp_set_link_train(dp, DP_TRAINING_PATTERN_DISABLE);
>>>>> +}
>>>>> +
>>>>> +static int cdn_dp_get_lower_link_rate(struct cdn_dp_device *dp)
>>>>> +{
>>>>> + switch (dp->link.rate) {
>>>>> + case DP_LINK_BW_1_62:
>>>>> + return -EINVAL;
>>>>> + case DP_LINK_BW_2_7:
>>>>> + dp->link.rate = DP_LINK_BW_1_62;
>>>>> + break;
>>>>> + case DP_LINK_BW_5_4:
>>>>> + dp->link.rate = DP_LINK_BW_2_7;
>>>>> + break;
>>>>> + default:
>>>>> + dp->link.rate = DP_LINK_BW_5_4;
>>>>> + break;
>>>>> + }
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> +int cdn_dp_software_train_link(struct cdn_dp_device *dp)
>>>>> +{
>>>>> + int ret, stop_err;
>>>>> + u8 link_config[2];
>>>>> + u32 rate, sink_max, source_max;
>>>>> +
>>>>> + ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
>>>>> + sizeof(dp->dpcd));
>>>>> + if (ret < 0) {
>>>>> + DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + source_max = dp->lanes;
>>>>> + sink_max = drm_dp_max_lane_count(dp->dpcd);
>>>>> + dp->link.num_lanes = min(source_max, sink_max);
>>>>> +
>>>>> + source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
>>>>> + sink_max = drm_dp_max_link_rate(dp->dpcd);
>>>>> + rate = min(source_max, sink_max);
>>>>> + dp->link.rate = drm_dp_link_rate_to_bw_code(rate);
>>>>> +
>>>>> + link_config[0] = 0;
>>>>> + link_config[1] = 0;
>>>>> + if (dp->dpcd[DP_MAIN_LINK_CHANNEL_CODING] & 0x01)
>>>>> + link_config[1] = DP_SET_ANSI_8B10B;
>>>>> + drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
>>>>> +
>>>>> + while (true) {
>>>>> +
>>>>> + /* Write the link configuration data */
>>>>> + link_config[0] = dp->link.rate;
>>>>> + link_config[1] = dp->link.num_lanes;
>>>>> + if (drm_dp_enhanced_frame_cap(dp->dpcd))
>>>>> + link_config[1] |=
>>>>> DP_LANE_COUNT_ENHANCED_FRAME_EN;
>>>>> + drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, link_config,
>>>>> 2);
>>>>> +
>>>>> + ret = cdn_dp_link_training_clock_recovery(dp);
>>>>> + if (ret) {
>>>>> + if (!cdn_dp_get_lower_link_rate(dp))
>>>>> + continue;
>>>>> +
>>>>> + DRM_ERROR("training clock recovery failed: %d\n",
>>>>> ret);
>>>>> + break;
>>>>> + }
>>>>> +
>>>>> + ret = cdn_dp_link_training_channel_equalization(dp);
>>>>> + if (ret) {
>>>>> + if (!cdn_dp_get_lower_link_rate(dp))
>>>>> + continue;
>>>>> +
>>>>> + DRM_ERROR("training channel eq failed: %d\n",
>>>>> ret);
>>>>> + break;
>>>>> + }
>>>>> +
>>>>> + break;
>>>>> + }
>>>>> +
>>>>> + stop_err = cdn_dp_stop_link_train(dp);
>>>>> + if (stop_err) {
>>>>> + DRM_ERROR("stop training fail, error: %d\n", stop_err);
>>>>> + return stop_err;
>>>>> + }
>>>>> +
>>>>> + return ret;
>>>>> +}
>>>>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>>>>> b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>>>>> index 979355d..e1273e6 100644
>>>>> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>>>>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>>>>> @@ -17,7 +17,9 @@
>>>>> #include <linux/delay.h>
>>>>> #include <linux/io.h>
>>>>> #include <linux/iopoll.h>
>>>>> +#include <linux/phy/phy.h>
>>>>> #include <linux/reset.h>
>>>>> +#include <soc/rockchip/rockchip_phy_typec.h>
>>>>>
>>>>> #include "cdn-dp-core.h"
>>>>> #include "cdn-dp-reg.h"
>>>>> @@ -189,7 +191,7 @@ static int cdn_dp_mailbox_send(struct cdn_dp_device
>>>>> *dp, u8 module_id,
>>>>> return 0;
>>>>> }
>>>>>
>>>>> -static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
>>>>> +int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
>>>>> {
>>>>> u8 msg[6];
>>>>>
>>>>> @@ -609,6 +611,31 @@ int cdn_dp_train_link(struct cdn_dp_device *dp)
>>>>> {
>>>>> int ret;
>>>>>
>>>>> + /*
>>>>> + * DP firmware uses fixed phy config values to do training, but
>>>>> some
>>>>> + * boards need to adjust these values to fit for their unique
>>>>> hardware
>>>>> + * design. So if the phy is using custom config values, do
>>>>> software
>>>>> + * link training instead of relying on firmware, if software
>>>>> training
>>>>> + * fail, keep firmware training as a fallback if sw training
>>>>> fails.
>>>>> + */
>>>>> + ret = cdn_dp_software_train_link(dp);
>>>>> + if (ret) {
>>>>> + DRM_DEV_ERROR(dp->dev,
>>>>> + "Failed to do software training %d\n", ret);
>>>>> + goto do_fw_training;
>>>>> + }
>>>> If I understand correctly you are changing current behavior. Before
>>>> this patch, we use always firmware link training, and after this
>>>> patch, we always use software link training. If fails we use the
>>>> firmware link training.
>>>>
>>>> AFAIK my Samsung Chromebook Plus works well with firmware link
>>>> training, so there are any benefits of use software link training
>>>> instead of firmware link training?
>>>>
>>>> Looks to me that we should only do software link training on these
>>>> platforms that need it, so on those that define the phy-config
>>>> property in their DT and use by default firmware link training.
>>> Sean and me have discussed about that, and we all agree to use sw training
>>> instead the
>>> fw training to keep training process consistent, and we do not need add a
>>> varialbe in
>>> struct rockchip_typec_phy(like use_sw_training before) to distinguish sw and
>>> fw training.
>>> If training process implement correctly, sw training not different with fw
>>> training, and as my
>>> test so far, sw training work well on Kevin, ofcourse, we need keep testing
>>> it.
>>>
>> Ok, I can also confirm that software training works well on kevin.
>> Could you mention this change of behavior in the commit message? I
>> think that after these patches the firmware training is unlikely to
>> happen.
>>
>> Thanks,
>> Enric
>>
>>
>>>>> + ret = cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);
>>>>> + if (ret) {
>>>>> + DRM_DEV_ERROR(dp->dev,
>>>>> + "Failed to write SOURCE_HDTX_CAR register %d\n", ret);
>>>>> + goto do_fw_training;
>>>>> + }
>>>>> + dp->use_fw_training = false;
>>>>> + return 0;
>>>>> +
>>>>> +do_fw_training:
>>>>> + dp->use_fw_training = true;
>>>>> + DRM_DEV_DEBUG_KMS(dp->dev, "use fw training\n");
>>>>> ret = cdn_dp_training_start(dp);
>>>>> if (ret) {
>>>>> DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n",
>>>>> ret);
>>>>> @@ -623,7 +650,7 @@ int cdn_dp_train_link(struct cdn_dp_device *dp)
>>>>>
>>>>> DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n",
>>>>> dp->link.rate,
>>>>> dp->link.num_lanes);
>>>>> - return ret;
>>>>> + return 0;
>>>>> }
>>>>>
>>>>> int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
>>>>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>>>>> b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>>>>> index 6580b11..3420771 100644
>>>>> --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>>>>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>>>>> @@ -137,7 +137,7 @@
>>>>> #define HPD_EVENT_MASK 0x211c
>>>>> #define HPD_EVENT_DET 0x2120
>>>>>
>>>>> -/* dpyx framer addr */
>>>>> +/* dptx framer addr */
>>>>> #define DP_FRAMER_GLOBAL_CONFIG 0x2200
>>>>> #define DP_SW_RESET 0x2204
>>>>> #define DP_FRAMER_TU 0x2208
>>>>> @@ -431,6 +431,40 @@
>>>>> /* Reference cycles when using lane clock as reference */
>>>>> #define LANE_REF_CYC 0x8000
>>>>>
>>>>> +/* register CM_VID_CTRL */
>>>>> +#define LANE_VID_REF_CYC(x) (((x) & (BIT(24) - 1)) <<
>>>>> 0)
>>>>> +#define NMVID_MEAS_TOLERANCE(x) (((x) & 0xf) <<
>>>>> 24)
>>>>> +
>>>>> +/* register DP_TX_PHY_CONFIG_REG */
>>>>> +#define DP_TX_PHY_TRAINING_ENABLE(x) ((x) & 1)
>>>>> +#define DP_TX_PHY_TRAINING_TYPE_PRBS7 (0 << 1)
>>>>> +#define DP_TX_PHY_TRAINING_TYPE_TPS1 (1 << 1)
>>>>> +#define DP_TX_PHY_TRAINING_TYPE_TPS2 (2 << 1)
>>>>> +#define DP_TX_PHY_TRAINING_TYPE_TPS3 (3 << 1)
>>>>> +#define DP_TX_PHY_TRAINING_TYPE_TPS4 (4 << 1)
>>>>> +#define DP_TX_PHY_TRAINING_TYPE_PLTPAT (5 << 1)
>>>>> +#define DP_TX_PHY_TRAINING_TYPE_D10_2 (6 << 1)
>>>>> +#define DP_TX_PHY_TRAINING_TYPE_HBR2CPAT (8 << 1)
>>>>> +#define DP_TX_PHY_TRAINING_PATTERN(x) ((x) << 1)
>>>>> +#define DP_TX_PHY_SCRAMBLER_BYPASS(x) (((x) & 1) << 5)
>>>>> +#define DP_TX_PHY_ENCODER_BYPASS(x) (((x) & 1) << 6)
>>>>> +#define DP_TX_PHY_SKEW_BYPASS(x) (((x) & 1) << 7)
>>>>> +#define DP_TX_PHY_DISPARITY_RST(x) (((x) & 1) << 8)
>>>>> +#define DP_TX_PHY_LANE0_SKEW(x) (((x) & 7) << 9)
>>>>> +#define DP_TX_PHY_LANE1_SKEW(x) (((x) & 7) << 12)
>>>>> +#define DP_TX_PHY_LANE2_SKEW(x) (((x) & 7) << 15)
>>>>> +#define DP_TX_PHY_LANE3_SKEW(x) (((x) & 7) << 18)
>>>>> +#define DP_TX_PHY_10BIT_ENABLE(x) (((x) & 1) << 21)
>>>>> +
>>>>> +/* register DP_FRAMER_GLOBAL_CONFIG */
>>>>> +#define NUM_LANES(x) ((x) & 3)
>>>>> +#define SST_MODE (0 << 2)
>>>>> +#define RG_EN (0 << 4)
>>>>> +#define GLOBAL_EN BIT(3)
>>>>> +#define NO_VIDEO BIT(5)
>>>>> +#define ENC_RST_DIS BIT(6)
>>>>> +#define WR_VHSYNC_FALL BIT(7)
>>>>> +
>>>>> enum voltage_swing_level {
>>>>> VOLTAGE_LEVEL_0,
>>>>> VOLTAGE_LEVEL_1,
>>>>> @@ -476,6 +510,7 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8
>>>>> lanes, bool flip);
>>>>> int cdn_dp_event_config(struct cdn_dp_device *dp);
>>>>> u32 cdn_dp_get_event(struct cdn_dp_device *dp);
>>>>> int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
>>>>> +int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val);
>>>>> ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
>>>>> u8 *data, u16 len);
>>>>> ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
>>>>> @@ -489,4 +524,5 @@ int cdn_dp_config_video(struct cdn_dp_device *dp);
>>>>> int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info
>>>>> *audio);
>>>>> int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
>>>>> int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info
>>>>> *audio);
>>>>> +int cdn_dp_software_train_link(struct cdn_dp_device *dp);
>>>>> #endif /* _CDN_DP_REG_H */
>>>>> --
>>>>> 2.7.4
>>>>>
>>>>
>>>
>
>
^ permalink raw reply
* [PATCH v3 2/2] media: helene: support IF frequency of ISDB-S
From: Katsuhiro Suzuki @ 2018-05-23 0:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180523002750.27136-1-suzuki.katsuhiro@socionext.com>
This patch enhances maximum IF frequency to 2.072GHz of this tuner
for supporting ISDB-S in Japan.
Maimum RF center frequency of ISDB-S for right-handed circularly
polarized.
BSAT
BS-23 12.14944GHz
N-SAT-110
ND-24 12.731GHz
Local frequency of BS/CS converter is typically 10.678GHz.
---
Changes since v2:
- Newly added
Signed-off-by: Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>
---
drivers/media/dvb-frontends/helene.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c
index 04033f0c278b..7d02a9ea7d95 100644
--- a/drivers/media/dvb-frontends/helene.c
+++ b/drivers/media/dvb-frontends/helene.c
@@ -874,7 +874,7 @@ static const struct dvb_tuner_ops helene_tuner_ops_s = {
.info = {
.name = "Sony HELENE Sat tuner",
.frequency_min = 500000,
- .frequency_max = 2500000,
+ .frequency_max = 2072000000,
.frequency_step = 1000,
},
.init = helene_init,
@@ -888,7 +888,7 @@ static const struct dvb_tuner_ops helene_tuner_ops = {
.info = {
.name = "Sony HELENE Sat/Ter tuner",
.frequency_min = 500000,
- .frequency_max = 1200000000,
+ .frequency_max = 2072000000,
.frequency_step = 1000,
},
.init = helene_init,
--
2.17.0
^ permalink raw reply related
* [PATCH v3 1/2] media: helene: add I2C device probe function
From: Katsuhiro Suzuki @ 2018-05-23 0:27 UTC (permalink / raw)
To: linux-arm-kernel
This patch adds I2C probe function to use dvb_module_probe()
with this driver.
Signed-off-by: Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>
---
Changes since v2:
- Nothing
Changes since v1:
- Add documents for dvb_frontend member of helene_config
---
drivers/media/dvb-frontends/helene.c | 88 ++++++++++++++++++++++++++--
drivers/media/dvb-frontends/helene.h | 3 +
2 files changed, 87 insertions(+), 4 deletions(-)
diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c
index a0d0b53c91d7..04033f0c278b 100644
--- a/drivers/media/dvb-frontends/helene.c
+++ b/drivers/media/dvb-frontends/helene.c
@@ -666,7 +666,7 @@ static int helene_set_params_s(struct dvb_frontend *fe)
return 0;
}
-static int helene_set_params(struct dvb_frontend *fe)
+static int helene_set_params_t(struct dvb_frontend *fe)
{
u8 data[MAX_WRITE_REGSIZE];
u32 frequency;
@@ -835,6 +835,19 @@ static int helene_set_params(struct dvb_frontend *fe)
return 0;
}
+static int helene_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ if (p->delivery_system == SYS_DVBT ||
+ p->delivery_system == SYS_DVBT2 ||
+ p->delivery_system == SYS_ISDBT ||
+ p->delivery_system == SYS_DVBC_ANNEX_A)
+ return helene_set_params_t(fe);
+
+ return helene_set_params_s(fe);
+}
+
static int helene_get_frequency(struct dvb_frontend *fe, u32 *frequency)
{
struct helene_priv *priv = fe->tuner_priv;
@@ -843,7 +856,7 @@ static int helene_get_frequency(struct dvb_frontend *fe, u32 *frequency)
return 0;
}
-static const struct dvb_tuner_ops helene_tuner_ops = {
+static const struct dvb_tuner_ops helene_tuner_ops_t = {
.info = {
.name = "Sony HELENE Ter tuner",
.frequency_min = 1000000,
@@ -853,7 +866,7 @@ static const struct dvb_tuner_ops helene_tuner_ops = {
.init = helene_init,
.release = helene_release,
.sleep = helene_sleep,
- .set_params = helene_set_params,
+ .set_params = helene_set_params_t,
.get_frequency = helene_get_frequency,
};
@@ -871,6 +884,20 @@ static const struct dvb_tuner_ops helene_tuner_ops_s = {
.get_frequency = helene_get_frequency,
};
+static const struct dvb_tuner_ops helene_tuner_ops = {
+ .info = {
+ .name = "Sony HELENE Sat/Ter tuner",
+ .frequency_min = 500000,
+ .frequency_max = 1200000000,
+ .frequency_step = 1000,
+ },
+ .init = helene_init,
+ .release = helene_release,
+ .sleep = helene_sleep,
+ .set_params = helene_set_params,
+ .get_frequency = helene_get_frequency,
+};
+
/* power-on tuner
* call once after reset
*/
@@ -1032,7 +1059,7 @@ struct dvb_frontend *helene_attach(struct dvb_frontend *fe,
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
- memcpy(&fe->ops.tuner_ops, &helene_tuner_ops,
+ memcpy(&fe->ops.tuner_ops, &helene_tuner_ops_t,
sizeof(struct dvb_tuner_ops));
fe->tuner_priv = priv;
dev_info(&priv->i2c->dev,
@@ -1042,6 +1069,59 @@ struct dvb_frontend *helene_attach(struct dvb_frontend *fe,
}
EXPORT_SYMBOL(helene_attach);
+static int helene_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct helene_config *config = client->dev.platform_data;
+ struct dvb_frontend *fe = config->fe;
+ struct device *dev = &client->dev;
+ struct helene_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->i2c_address = client->addr;
+ priv->i2c = client->adapter;
+ priv->set_tuner_data = config->set_tuner_priv;
+ priv->set_tuner = config->set_tuner_callback;
+ priv->xtal = config->xtal;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ if (helene_x_pon(priv) != 0)
+ return -EINVAL;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ memcpy(&fe->ops.tuner_ops, &helene_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+ fe->tuner_priv = priv;
+ i2c_set_clientdata(client, priv);
+
+ dev_info(dev, "Sony HELENE attached on addr=%x at I2C adapter %p\n",
+ priv->i2c_address, priv->i2c);
+
+ return 0;
+}
+
+static const struct i2c_device_id helene_id[] = {
+ { "helene", },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, helene_id);
+
+static struct i2c_driver helene_driver = {
+ .driver = {
+ .name = "helene",
+ },
+ .probe = helene_probe,
+ .id_table = helene_id,
+};
+module_i2c_driver(helene_driver);
+
MODULE_DESCRIPTION("Sony HELENE Sat/Ter tuner driver");
MODULE_AUTHOR("Abylay Ospan <aospan@netup.ru>");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/helene.h b/drivers/media/dvb-frontends/helene.h
index c9fc81c7e4e7..8562d01bc93e 100644
--- a/drivers/media/dvb-frontends/helene.h
+++ b/drivers/media/dvb-frontends/helene.h
@@ -39,6 +39,7 @@ enum helene_xtal {
* @set_tuner_callback: Callback function that notifies the parent driver
* which tuner is active now
* @xtal: Cristal frequency as described by &enum helene_xtal
+ * @fe: Frontend for which connects this tuner
*/
struct helene_config {
u8 i2c_address;
@@ -46,6 +47,8 @@ struct helene_config {
void *set_tuner_priv;
int (*set_tuner_callback)(void *, int);
enum helene_xtal xtal;
+
+ struct dvb_frontend *fe;
};
#if IS_REACHABLE(CONFIG_DVB_HELENE)
--
2.17.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox