Re: [PATCH 2/8] soc: ti: add initial PRM driver with reset control support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 8/20/19 2:37 AM, Tero Kristo wrote:
> On 20.8.2019 2.01, Suman Anna wrote:
>> Hi Tero,
>>
>> On 8/19/19 4:32 AM, Tero Kristo wrote:
>>> On 08/08/2019 08:26, Keerthy wrote:
>>>>
>>>>
>>>> On 07/08/19 1:18 PM, Tero Kristo wrote:
>>>>> Add initial PRM (Power and Reset Management) driver for TI OMAP class
>>>>> SoCs. Initially this driver only supports reset control, but can be
>>>>> extended to support rest of the functionality, like powerdomain
>>>>> control, PRCM irq support etc.
>>>>>
>>>>> Signed-off-by: Tero Kristo <t-kristo@xxxxxx>
>>>>> ---
>>>>>    arch/arm/mach-omap2/Kconfig |   1 +
>>>>>    drivers/soc/ti/Makefile     |   1 +
>>>>>    drivers/soc/ti/omap_prm.c   | 216
>>>>> ++++++++++++++++++++++++++++++++++++++++++++
>>>>>    3 files changed, 218 insertions(+)
>>>>>    create mode 100644 drivers/soc/ti/omap_prm.c
>>>>>
>>>>> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
>>>>> index fdb6743..42ad063 100644
>>>>> --- a/arch/arm/mach-omap2/Kconfig
>>>>> +++ b/arch/arm/mach-omap2/Kconfig
>>>>> @@ -109,6 +109,7 @@ config ARCH_OMAP2PLUS
>>>>>        select TI_SYSC
>>>>>        select OMAP_IRQCHIP
>>>>>        select CLKSRC_TI_32K
>>>>> +    select RESET_CONTROLLER
>>
>> Use ARCH_HAS_RESET_CONTROLLER instead.
> 
> Ok.
> 
>>
>>>>>        help
>>>>>          Systems based on OMAP2, OMAP3, OMAP4 or OMAP5
>>>>> diff --git a/drivers/soc/ti/Makefile b/drivers/soc/ti/Makefile
>>>>> index b3868d3..788b5cd 100644
>>>>> --- a/drivers/soc/ti/Makefile
>>>>> +++ b/drivers/soc/ti/Makefile
>>>>> @@ -6,6 +6,7 @@ obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS)    +=
>>>>> knav_qmss.o
>>>>>    knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o
>>>>>    obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA)    += knav_dma.o
>>>>>    obj-$(CONFIG_AMX3_PM)            += pm33xx.o
>>>>> +obj-$(CONFIG_ARCH_OMAP2PLUS)        += omap_prm.o
>>>>>    obj-$(CONFIG_WKUP_M3_IPC)        += wkup_m3_ipc.o
>>>>>    obj-$(CONFIG_TI_SCI_PM_DOMAINS)        += ti_sci_pm_domains.o
>>>>>    obj-$(CONFIG_TI_SCI_INTA_MSI_DOMAIN)    += ti_sci_inta_msi.o
>>>>> diff --git a/drivers/soc/ti/omap_prm.c b/drivers/soc/ti/omap_prm.c
>>>>> new file mode 100644
>>>>> index 0000000..7c89eb8
>>>>> --- /dev/null
>>>>> +++ b/drivers/soc/ti/omap_prm.c
>>>>> @@ -0,0 +1,216 @@
>>>>> +// SPDX-License-Identifier: GPL-2.0
>>>>> +/*
>>>>> + * OMAP2+ PRM driver
>>>>> + *
>>>>> + * Copyright (C) 2019 Texas Instruments Incorporated -
>>>>> http://www.ti.com/
>>>>> + *    Tero Kristo <t-kristo@xxxxxx>
>>>>> + */
>>>>> +
>>>>> +#include <linux/kernel.h>
>>>>> +#include <linux/module.h>
>>>>> +#include <linux/device.h>
>>>>> +#include <linux/io.h>
>>>>> +#include <linux/of.h>
>>>>> +#include <linux/of_device.h>
>>>>> +#include <linux/platform_device.h>
>>>>> +#include <linux/reset-controller.h>
>>>>> +#include <linux/delay.h>
>>>>> +
>>>>> +struct omap_rst_map {
>>>>> +    s8 rst;
>>>>> +    s8 st;
>>>>> +};
>>>>> +
>>>>> +struct omap_prm_data {
>>>>> +    u32 base;
>>>>> +    const char *name;
>>>>> +    u16 pwstctrl;
>>>>> +    u16 pwstst;
>>>>> +    u16 rstctl;
>>
>> Minor nit, can you use rstctrl instead here so that it is in sync with
>> the other variables and with the register names used in TRM.
> 
> Ok.
> 
>>
>>>>> +    u16 rstst;
>>>>> +    struct omap_rst_map *rstmap;
>>>>> +    u8 flags;
>>>>> +};
>>>>> +
>>>>> +struct omap_prm {
>>>>> +    const struct omap_prm_data *data;
>>>>> +    void __iomem *base;
>>>>> +};
>>>>> +
>>>>> +struct omap_reset_data {
>>>>> +    struct reset_controller_dev rcdev;
>>>>> +    struct omap_prm *prm;
>>>>> +};
>>>>> +
>>>>> +#define to_omap_reset_data(p) container_of((p), struct
>>>>> omap_reset_data, rcdev)
>>>>> +
>>>>> +#define OMAP_MAX_RESETS        8
>>>>> +#define OMAP_RESET_MAX_WAIT    10000
>>>>> +
>>>>> +#define OMAP_PRM_NO_RSTST    BIT(0)
>>>>> +
>>>>> +static const struct of_device_id omap_prm_id_table[] = {
>>>>> +    { },
>>>>> +};
>>>>
>>>> This table is blank and we are doing of_match_device against it.
>>>
>>> Yes, it gets populated with other patches in series, one entry per soc.
>>>
>>>>
>>>>> +
>>>>> +static int omap_reset_status(struct reset_controller_dev *rcdev,
>>>>> +                 unsigned long id)
>>>>> +{
>>>>> +    struct omap_reset_data *reset = to_omap_reset_data(rcdev);
>>>>> +    u32 v;
>>>>> +
>>>>> +    v = readl_relaxed(reset->prm->base + reset->prm->data->rstst);
>>>>> +    v &= 1 << id;
>>>>> +    v >>= id;
>>>>> +
>>>>> +    return v;
>>>>> +}
>>>>> +
>>>>> +static int omap_reset_assert(struct reset_controller_dev *rcdev,
>>>>> +                 unsigned long id)
>>>>> +{
>>>>> +    struct omap_reset_data *reset = to_omap_reset_data(rcdev);
>>>>> +    u32 v;
>>>>> +
>>>>> +    /* assert the reset control line */
>>>>> +    v = readl_relaxed(reset->prm->base + reset->prm->data->rstctl);
>>>>> +    v |= 1 << id;
>>>>> +    writel_relaxed(v, reset->prm->base + reset->prm->data->rstctl);
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>> +static int omap_reset_get_st_bit(struct omap_reset_data *reset,
>>>>> +                 unsigned long id)
>>>>> +{
>>>>> +    struct omap_rst_map *map = reset->prm->data->rstmap;
>>>>> +
>>>>> +    while (map && map->rst >= 0) {
>>>>> +        if (map->rst == id)
>>>>> +            return map->st;
>>>>> +
>>>>> +        map++;
>>>>> +    }
>>>>> +
>>>>> +    return id;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * Note that status will not change until clocks are on, and clocks
>>>>> cannot be
>>>>> + * enabled until reset is deasserted. Consumer drivers must check
>>>>> status
>>>>> + * separately after enabling clocks.
>>>>> + */
>>>>> +static int omap_reset_deassert(struct reset_controller_dev *rcdev,
>>>>> +                   unsigned long id)
>>>>> +{
>>>>> +    struct omap_reset_data *reset = to_omap_reset_data(rcdev);
>>>>> +    u32 v;
>>>>> +    int st_bit = id;
>>
>> No need to initialize this, will always get overwritten below.
> 
> Hmm right, must be a leftover from some earlier code.
> 
>>
>>>>> +    bool has_rstst;
>>>>> +
>>>>> +    /* check the current status to avoid de-asserting the line
>>>>> twice */
>>>>> +    v = readl_relaxed(reset->prm->base + reset->prm->data->rstctl);
>>>>> +    if (!(v & BIT(id)))
>>>>> +        return -EEXIST;
>>>>> +
>>>>> +    has_rstst = !(reset->prm->data->flags & OMAP_PRM_NO_RSTST);
>>>>> +
>>>>> +    if (has_rstst) {
>>>>> +        st_bit = omap_reset_get_st_bit(reset, id);
>>>>> +
>>>>> +        /* Clear the reset status by writing 1 to the status bit */
>>>>> +        v = readl_relaxed(reset->prm->base +
>>>>> reset->prm->data->rstst);
>>>>> +        v |= 1 << st_bit;
>>>>> +        writel_relaxed(v, reset->prm->base +
>>>>> reset->prm->data->rstst);
>>>>> +    }
>>>>> +
>>>>> +    /* de-assert the reset control line */
>>>>> +    v = readl_relaxed(reset->prm->base + reset->prm->data->rstctl);
>>>>> +    v &= ~(1 << id);
>>>>> +    writel_relaxed(v, reset->prm->base + reset->prm->data->rstctl);
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>> +static const struct reset_control_ops omap_reset_ops = {
>>>>> +    .assert        = omap_reset_assert,
>>>>> +    .deassert    = omap_reset_deassert,
>>>>> +    .status        = omap_reset_status,
>>>>> +};
>>>>> +
>>>>> +static int omap_prm_reset_probe(struct platform_device *pdev,
>>>>> +                struct omap_prm *prm)
>>
>> Call this omap_prm_reset_init or something similar to avoid confusion.
> 
> Ok, can change this.
> 
>>
>>>>> +{
>>>>> +    struct omap_reset_data *reset;
>>>>> +
>>>>> +    /*
>>>>> +     * Check if we have resets. If either rstctl or rstst is
>>>>> +     * non-zero, we have reset registers in place. Additionally
>>>>> +     * the flag OMAP_PRM_NO_RSTST implies that we have resets.
>>>>> +     */
>>>>> +    if (!prm->data->rstctl && !prm->data->rstst &&
>>>>> +        !(prm->data->flags & OMAP_PRM_NO_RSTST))
>>>>> +        return 0;
>>>>> +
>>>>> +    reset = devm_kzalloc(&pdev->dev, sizeof(*reset), GFP_KERNEL);
>>>>> +    if (!reset)
>>>>> +        return -ENOMEM;
>>>>> +
>>>>> +    reset->rcdev.owner = THIS_MODULE;
>>>>> +    reset->rcdev.ops = &omap_reset_ops;
>>>>> +    reset->rcdev.of_node = pdev->dev.of_node;
>>>>> +    reset->rcdev.nr_resets = OMAP_MAX_RESETS;
>>
>> Suggest adding a number of resets to prm->data, and using it so that we
>> don't even entertain any resets beyond the actual number of resets.
> 
> Hmm why bother? Accessing a stale reset bit will just cause access to a
> reserved bit in the reset register, doing basically nothing. Also, this
> would not work for am3/am4 wkup, as there is a single reset bit at an
> arbitrary position.

The generic convention seems to be defining a reset id value defined
from include/dt-bindings/reset/ that can be used to match between the
dt-nodes and the reset-controller driver.

Philipp,
Any comments?

regards
Suman

> 
>>
>> You actually seem to be using the bit-position directly in client data
>> instead of a reset number. I am not sure if this is accepted practice
>> with reset controllers, do you incur any memory wastage?
> 
> Reset numbering almost always seems to start from 0, I think the only
> exception to this is wkup_m3 on am3/am4. Introducing an additional
> arbitrary mapping for this doesn't seem to make any sense.
> 
> Also, resets are allocated on-need-basis, so it only allocates one
> instance for the reset control whatever the index is.
> 
>>
>>>>> +
>>>>> +    reset->prm = prm;
>>>>> +
>>>>> +    return devm_reset_controller_register(&pdev->dev, &reset->rcdev);
>>>>> +}
>>>>> +
>>>>> +static int omap_prm_probe(struct platform_device *pdev)
>>>>> +{
>>>>> +    struct resource *res;
>>>>> +    const struct omap_prm_data *data;
>>>>> +    struct omap_prm *prm;
>>>>> +    const struct of_device_id *match;
>>>>> +
>>>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>>> +    if (!res)
>>>>> +        return -ENODEV;
>>>>> +
>>>>> +    match = of_match_device(omap_prm_id_table, &pdev->dev);
>>>>> +    if (!match)
>>>>> +        return -ENOTSUPP;
>>
>> Use of_device_get_match_data() instead to do both match and get the
>> data. That can perhaps be the first block.
>>
>>>>> +
>>>>> +    prm = devm_kzalloc(&pdev->dev, sizeof(*prm), GFP_KERNEL);
>>>>> +    if (!prm)
>>>>> +        return -ENOMEM;
>>
>> Perhaps move the allocate after the match check to streamline.
> 
> Ok, will check these two out.
> 
>>
>>>>> +
>>>>> +    data = match->data;
>>>>> +
>>>>> +    while (data->base != res->start) {
>>>>> +        if (!data->base)
>>>>> +            return -EINVAL;
>>>>> +        data++;
>>>>> +    }
>>>>> +
>>>>> +    prm->data = data;
>>>>> +
>>>>> +    prm->base = devm_ioremap_resource(&pdev->dev, res);
>>>>> +    if (!prm->base)
>>>>> +        return -ENOMEM;
>>>>> +
>>>>> +    return omap_prm_reset_probe(pdev, prm);
>>>>> +}
>>>>> +
>>>>> +static struct platform_driver omap_prm_driver = {
>>>>> +    .probe = omap_prm_probe,
>>>>> +    .driver = {
>>>>> +        .name        = KBUILD_MODNAME,
>>>>> +        .of_match_table    = omap_prm_id_table,
>>>>> +    },
>>>>> +};
>>>>> +builtin_platform_driver(omap_prm_driver);
>>>>> +
>>>>> +MODULE_ALIAS("platform:prm");
>>
>> Drop this and use MODULE_DEVICE_TABLE instead on omap_prm_id_table if
>> retaining, but I don't think these need to be defined.
> 
> Ok, will ditch them.
> 
> -Tero
> 
>>
>> regards
>> Suman
>>
>>>>> +MODULE_LICENSE("GPL v2");
>>>>> +MODULE_DESCRIPTION("omap2+ prm driver");
>>>>
>>>> It is a builtin_platform_driver so do we need the MODULE*?
>>>
>>> Well, thats debatable, however some existing drivers do introduce this
>>> even if they are builtin.
>>>
>>> -Tero
>>> -- 
>>
> 
> -- 
> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux