* Re: [PATCH v2 4/6] irqchip/irq-pruss-intc: Add helper functions to configure internal mapping
From: Suman Anna @ 2019-08-12 19:39 UTC (permalink / raw)
To: David Lechner, Marc Zyngier, Thomas Gleixner, Jason Cooper
Cc: devicetree, Grygorii Strashko, Tony Lindgren, Sekhar Nori,
linux-kernel, Andrew F. Davis, Lokesh Vutla, Rob Herring,
Murali Karicheri, linux-omap, linux-arm-kernel, Roger Quadros
In-Reply-To: <124b03b8-f8e7-682b-8767-13a739329da2@lechnology.com>
Hi David,
On 8/8/19 12:09 PM, David Lechner wrote:
> On 8/2/19 4:26 PM, Suman Anna wrote:
>> Point is different applications might use mapping differently as per
>> their firmware and driver/application design and their split across one
>> or more PRUs (design by contract). And we need to set this up at runtime
>> when the application driver is getting run. We will have either the Soft
>> UART or the Ethernet running at a time depending on the end goal desired
>>
>>> I have an idea that we can use multiple struct irq_domains to make
>>> this work in the existing IRQ framework, but it would be helpful to
>>> know more about the bigger picture first.
>>
>> Yeah, would be great if there is a way this can be solved without having
>> to introduce additional API.
>>
>
>
> Here is what I came up with to use existing IRQ APIs to implement event
> mapping.
> Basically it is the same as my previous suggestion [1], with the
> addition of
> multiple IRQ domains.
First of all, many thanks for looking into the problem and providing
patches for the alternate solutions. If we were to not use any exported
functions, this approach does seem to be a viable solution. I am going
to play around with both [1] and this patch with all our existing
usecases and see if I run into any issues.
So, w.r.t this patch compared to [1], is the multiple IRQ domain solving
anything specifically? Our main issue is the re-purposing of a event
(and its mapping depending on the application), and the same issue will
remain whether we have multiple domains or not. Also, now we would
expect an event to migrate between different domains based on its usage.
>
> The idea is that each external interrupt controller (or DMA controller,
> etc.)
> that is connected to the PRUSS interrupt controller is considered an
> interrupt
> domain. One of the objections to my previous patch was that we could
> only have
> one IRQ descriptor per event. Now we can have one descriptor per event per
> domain.
>
> I am still proposing that we use the interrupt-cells and identical vendor
> resource data structures in the PRU firmware be used to provide the mapping
> information. (As a side note, I still think it is important to include
> EVTSEL
> on AM18xx in order to fully describe the event.)
W.r.t EVTSEL, it is a global value and applies to a range of events. I
have another equivalent register/functionality on most of the other SoCs
as well (a register in PRUSS_CFG space) that muxes standard events vs
MII_RT events. Again, that is limited to only a subset of all the system
events. So, should this continue to be a per event specifier, it will be
yet another mapping configuration data item (my idea was to manage this
once per application within the PRU remoteproc driver along with the
fwspec mapping).
regards
Suman
>
> The bindings will have N = 4 cells (or N = 5 when EVTSEL is required to
> fully
> describe the event):
>
> Cell 0: The PRUSS event number, e.g. 0 to 64 for most PRUSSs
> Cell 1: The EVTSEL value (omitted when N == 4), e.g. 0, 1 or
> TI_PRUSS_INTC_EVTSEL_ANY if the event is the same for all EVTSEL
> values. On AM18xx, external events will all require 0 or 1 while
> system events will always be TI_PRUSS_INTC_EVTSEL_ANY.
> Cell N-3: The channel that the event gets mapped to, e.g. 0 to 9
> Cell N-2: The host that the channel gets mapped to, e.g. 0 to 9
> Cell N-1: The interrupt domain, e.g. TI_PRUSS_INTC_DOMAIN_PRU or
> TI_PRUSS_INTC_DOMAIN_MCU
>
> The TI_PRUSS_INTC_DOMAIN_* values are just arbitrary numbers assigned to
> the
> possible domains. For example, on AM18xx and AM33xx, there are just two
> domains,
> the PRU domain for host 0 and host 1 and the MCU domain for host 2 thru 9.
> Looking at the AM65xx manual, it looks like it would have 4 domains, the
> PRU
> domain, the RTU PRU domain, the MCU domain and a task manager domain.
> (And I
> suppose that domains could even be more granular if needed, e.g. we
> could drop
> the arbitrary domain number and treat each host interrupt/event as an
> interrupt
> domain, then there would be an IRQ descriptor per PRU INTC event per host.)
>
> The AM18xx example I have been using will look like this in the device
> tree:
>
> interrupts = <63 TI_PRUSS_INTC_EVTSEL_ANY 0 0
> TI_PRUSS_INTC_DOMAIN_PRU>,
> <62 TI_PRUSS_INTC_EVTSEL_ANY 2 2 TI_PRUSS_INTC_DOMAIN_MCU>;
>
> To keep parsing simple, the PRU firmware can include vendor resources
> that have
> essentially the same format as the device tree bindings. For example:
>
> enum {
> /* IRQ descriptor without EVTSEL */
> TI_PRU_VENDOR_RESOURCE_IRQ = RSC_VENDOR_START,
> /* IRQ descriptor with EVTSEL */
> TI_PRU_VENDOR_RESOURCE_IRQ2,
> };
>
> struct ti_pru_vendor_resource_irq {
> __le32 event;
> __le32 channel;
> __le32 host;
> __le32 domain;
> };
>
> struct ti_pru_vendor_resource_irq2 {
> __le32 event;
> __le32 evt_sel;
> __le32 channel;
> __le32 host;
> __le32 domain;
> };
>
> Then we can provide a vendor resource hook in the remoteproc driver to
> handle
> these resources:
>
> static int ti_pru_rproc_handle_rsc(struct rproc *rproc, u32 rsc_type,
> void *rsc,
> int offset, int avail)
> {
> struct ti_pru_data *pru = rproc->priv;
> struct irq_fwspec fwspec;
> unsigned int virq;
>
> switch (rsc_type) {
> case TI_PRU_VENDOR_RESOURCE_IRQ:
> {
> struct ti_pru_vendor_resource_irq *rsc_irq = rsc;
>
> fwspec.fwnode = pru->intc_fwnode;
> fwspec.param[0] = le32_to_cpu(rsc_irq->event);
> fwspec.param[1] = le32_to_cpu(rsc_irq->channel);
> fwspec.param[2] = le32_to_cpu(rsc_irq->host);
> fwspec.param[3] = le32_to_cpu(rsc_irq->domain);
> fwspec.param_count = 4;
> }
> break;
> case TI_PRU_VENDOR_RESOURCE_IRQ2:
> {
> struct ti_pru_vendor_resource_irq2 *rsc_irq2 = rsc;
>
> fwspec.fwnode = pru->intc_fwnode;
> fwspec.param[0] = le32_to_cpu(rsc_irq2->event);
> fwspec.param[1] = le32_to_cpu(rsc_irq2->evt_sel);
> fwspec.param[2] = le32_to_cpu(rsc_irq2->channel);
> fwspec.param[3] = le32_to_cpu(rsc_irq2->host);
> fwspec.param[4] = le32_to_cpu(rsc_irq2->domain);
> fwspec.param_count = 5;
> break;
> }
> default:
> return RSC_IGNORED;
> }
>
> virq = irq_create_fwspec_mapping(&fwspec);
> if (!virq)
> return -EINVAL;
>
> /* TODO: save virq (and other metadata) for later use */
>
> return RSC_HANDLED;
> }
>
> static const struct rproc_ops ti_pru_rproc_ops = {
> .start = ti_pru_rproc_start,
> .stop = ti_pru_rproc_stop,
> .kick = ti_pru_rproc_kick,
> .da_to_va = ti_pru_rproc_da_to_va,
> .handle_rsc = ti_pru_rproc_handle_rsc,
> };
>
> The handle_rsc callback is called for each resource when the PRU is booted.
> The function irq_create_fwspec_mapping() causes the IRQ to be mapped in
> hardware. From what I understand from the previous discussions, this is
> exactly
> when we want this to happen.
>
> This patch applies on top of "irqchip/irq-pruss-intc: Add a PRUSS
> irqchip driver
> for PRUSS interrupts", "irqchip/irq-pruss-intc: Add support for shared and
> invalid interrupts" and "irqchip/irq-pruss-intc: Implement irq_{get,set}
> _irqchip_state ops" from [PATCH v2 0/6] "Add TI PRUSS Local Interrupt
> Controller
> IRQChip driver" [2].
>
> A working copy along with some remoteproc and rpmsg hacks can be found
> on my
> GitHub [3].
>
> [1]:
> https://lore.kernel.org/lkml/fb2bdb7b-4d4d-508f-722a-554888280145@lechnology.com/
>
> [2]: https://lore.kernel.org/lkml/20190731224149.11153-1-s-anna@ti.com/
> [3]: https://github.com/dlech/linux/commits/pruss-2019-08-08
>
> Signed-off-by: Suman Anna <s-anna@ti.com>
> Signed-off-by: Andrew F. Davis <afd@ti.com>
> Signed-off-by: Roger Quadros <rogerq@ti.com>
> Signed-off-by: David Lechner <david@lechnology.com>
> ---
> drivers/irqchip/irq-pruss-intc.c | 387 +++++++++++++++++-
> .../interrupt-controller/ti-pruss.h | 27 ++
> 2 files changed, 396 insertions(+), 18 deletions(-)
> create mode 100644 include/dt-bindings/interrupt-controller/ti-pruss.h
>
> diff --git a/drivers/irqchip/irq-pruss-intc.c
> b/drivers/irqchip/irq-pruss-intc.c
> index c1fd6c09f2f2..da4349df08c3 100644
> --- a/drivers/irqchip/irq-pruss-intc.c
> +++ b/drivers/irqchip/irq-pruss-intc.c
> @@ -5,6 +5,8 @@
> * Copyright (C) 2016-2019 Texas Instruments Incorporated -
> http://www.ti.com/
> * Andrew F. Davis <afd@ti.com>
> * Suman Anna <s-anna@ti.com>
> + *
> + * Copyright (C) 2019 David Lechner <david@lechnology.com>
> */
>
> #include <linux/interrupt.h>
> @@ -14,6 +16,14 @@
> #include <linux/module.h>
> #include <linux/of_device.h>
> #include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +
> +#include <dt-bindings/interrupt-controller/ti-pruss.h>
> +
> +/* The number of possible interrupt domains, see TI_PRUSS_INTC_DOMAIN_* in
> + * dt-bindings/interrupt-controller/ti-pruss.h
> + */
> +#define NUM_TI_PRUSS_INTC_DOMAIN 5
>
> /*
> * Number of host interrupts reaching the main MPU sub-system. Note
> that this
> @@ -25,6 +35,12 @@
> /* minimum starting host interrupt number for MPU */
> #define MIN_PRU_HOST_INT 2
>
> +/* maximum number of host interrupts */
> +#define MAX_PRU_HOST_INT 10
> +
> +/* maximum number of interrupt channels */
> +#define MAX_PRU_CHANNELS 10
> +
> /* maximum number of system events */
> #define MAX_PRU_SYS_EVENTS 64
>
> @@ -57,27 +73,83 @@
> #define PRU_INTC_HINLR(x) (0x1100 + (x) * 4)
> #define PRU_INTC_HIER 0x1500
>
> +/* CMR register bit-field macros */
> +#define CMR_EVT_MAP_MASK 0xf
> +#define CMR_EVT_MAP_BITS 8
> +#define CMR_EVT_PER_REG 4
> +
> +/* HMR register bit-field macros */
> +#define HMR_CH_MAP_MASK 0xf
> +#define HMR_CH_MAP_BITS 8
> +#define HMR_CH_PER_REG 4
> +
> /* HIPIR register bit-fields */
> #define INTC_HIPIR_NONE_HINT 0x80000000
>
> +/**
> + * struct pruss_intc_hwirq_data - additional metadata associated with a
> PRU
> + * system event
> + * @evtsel: The event select index (AM18xx only)
> + * @channel: The PRU INTC channel that the system event should be
> mapped to
> + * @host: The PRU INTC host that the channel should be mapped to
> + */
> +struct pruss_intc_hwirq_data {
> + u8 evtsel;
> + u8 channel;
> + u8 host;
> +};
> +
> +/**
> + * struct pruss_intc_map_record - keeps track of actual mapping state
> + * @value: The currently mapped value (evtsel, channel or host)
> + * @ref_count: Keeps track of number of current users of this resource
> + */
> +struct pruss_intc_map_record {
> + u8 value;
> + u8 ref_count;
> +};
> +
> +/**
> + * struct pruss_intc_domain - information specific to an external IRQ
> domain
> + * @hwirq_data: Table of additional mapping data received from device tree
> + * or PRU firmware
> + * @domain: irq domain
> + * @intc: the interrupt controller
> + * @id: Unique domain identifier (from device tree bindings)
> + */
> +struct pruss_intc_domain {
> + struct pruss_intc_hwirq_data hwirq_data[MAX_PRU_SYS_EVENTS];
> + struct irq_domain *domain;
> + struct pruss_intc *intc;
> + u32 id;
> +};
> +
> /**
> * struct pruss_intc - PRUSS interrupt controller structure
> + * @domain: External interrupt domains
> + * @evtsel: Tracks the current state of CFGCHIP3[3].PRUSSEVTSEL (AM18xx
> only)
> + * @event_channel: Tracks the current state of system event to channel
> mappings
> + * @channel_host: Tracks the current state of channel to host mappings
> * @irqs: kernel irq numbers corresponding to PRUSS host interrupts
> * @base: base virtual address of INTC register space
> * @irqchip: irq chip for this interrupt controller
> - * @domain: irq domain for this interrupt controller
> * @lock: mutex to serialize access to INTC
> * @shared_intr: bit-map denoting if the MPU host interrupt is shared
> * @invalid_intr: bit-map denoting if host interrupt is not connected
> to MPU
> + * @has_evtsel: indicates that the chip has an event select mux
> */
> struct pruss_intc {
> + struct pruss_intc_domain domain[NUM_ISA_INTERRUPTS];
> + struct pruss_intc_map_record evtsel;
> + struct pruss_intc_map_record event_channel[MAX_PRU_SYS_EVENTS];
> + struct pruss_intc_map_record channel_host[MAX_PRU_CHANNELS];
> unsigned int irqs[MAX_NUM_HOST_IRQS];
> void __iomem *base;
> struct irq_chip *irqchip;
> - struct irq_domain *domain;
> struct mutex lock; /* PRUSS INTC lock */
> u16 shared_intr;
> u16 invalid_intr;
> + bool has_evtsel;
> };
>
> static inline u32 pruss_intc_read_reg(struct pruss_intc *intc, unsigned
> int reg)
> @@ -105,6 +177,172 @@ static int pruss_intc_check_write(struct
> pruss_intc *intc, unsigned int reg,
> return 0;
> }
>
> +/**
> + * pruss_intc_map() - configure the PRUSS INTC
> + * @domain: pru intc domain pointer
> + * @hwirq: the system event number
> + *
> + * Configures the PRUSS INTC with the provided configuration from the one
> + * parsed in the xlate function. Any existing event to channel mappings or
> + * channel to host interrupt mappings are checked to make sure there
> are no
> + * conflicting configuration between both the PRU cores.
> + *
> + * Returns 0 on success, or a suitable error code otherwise
> + */
> +static int pruss_intc_map(struct pruss_intc_domain *domain, unsigned
> long hwirq)
> +{
> + struct pruss_intc *intc = domain->intc;
> + struct device* dev = intc->irqchip->parent_device;
> + u32 val;
> + int idx, ret;
> + u8 evtsel, ch, host;
> +
> + if (hwirq >= MAX_PRU_SYS_EVENTS)
> + return -EINVAL;
> +
> + mutex_lock(&intc->lock);
> +
> + evtsel = domain->hwirq_data[hwirq].evtsel;
> + ch = domain->hwirq_data[hwirq].channel;
> + host = domain->hwirq_data[hwirq].host;
> +
> + if (intc->has_evtsel && intc->evtsel.ref_count > 0 &&
> + intc->evtsel.value != evtsel) {
> + dev_err(dev, "event %lu (req. evtsel %d) already assigned to
> evtsel %d\n",
> + hwirq, evtsel, intc->evtsel.value);
> + ret = -EBUSY;
> + goto unlock;
> + }
> +
> + /* check if sysevent already assigned */
> + if (intc->event_channel[hwirq].ref_count > 0 &&
> + intc->event_channel[hwirq].value != ch) {
> + dev_err(dev, "event %lu (req. channel %d) already assigned to
> channel %d\n",
> + hwirq, ch, intc->event_channel[hwirq].value);
> + ret = -EBUSY;
> + goto unlock;
> + }
> +
> + /* check if channel already assigned */
> + if (intc->channel_host[ch].ref_count > 0 &&
> + intc->channel_host[ch].value != host) {
> + dev_err(dev, "channel %d (req. host %d) already assigned to
> host %d\n",
> + ch, host, intc->channel_host[ch].value);
> + ret = -EBUSY;
> + goto unlock;
> + }
> +
> + if (++intc->evtsel.ref_count == 1) {
> + intc->evtsel.value = evtsel;
> +
> + /* TODO: need to implement CFGCHIP3[3].PRUSSEVTSEL */
> + }
> +
> + if (++intc->event_channel[hwirq].ref_count == 1) {
> + intc->event_channel[hwirq].value = ch;
> +
> + /*
> + * configure channel map registers - each register holds map
> + * info for 4 events, with each event occupying the lower nibble
> + * in a register byte address in little-endian fashion
> + */
> + idx = hwirq / CMR_EVT_PER_REG;
> +
> + val = pruss_intc_read_reg(intc, PRU_INTC_CMR(idx));
> + val &= ~(CMR_EVT_MAP_MASK <<
> + ((hwirq % CMR_EVT_PER_REG) * CMR_EVT_MAP_BITS));
> + val |= ch << ((hwirq % CMR_EVT_PER_REG) * CMR_EVT_MAP_BITS);
> + pruss_intc_write_reg(intc, PRU_INTC_CMR(idx), val);
> +
> + dev_dbg(dev, "SYSEV%lu -> CH%d (CMR%d 0x%08x)\n", hwirq, ch,
> + idx, pruss_intc_read_reg(intc, PRU_INTC_CMR(idx)));
> +
> + /* clear and enable system event */
> + pruss_intc_write_reg(intc, PRU_INTC_SICR, hwirq);
> + pruss_intc_write_reg(intc, PRU_INTC_EISR, hwirq);
> + }
> +
> + if (++intc->channel_host[ch].ref_count == 1) {
> + intc->channel_host[ch].value = host;
> +
> + /*
> + * set host map registers - each register holds map info for
> + * 4 channels, with each channel occupying the lower nibble in
> + * a register byte address in little-endian fashion
> + */
> + idx = ch / HMR_CH_PER_REG;
> +
> + val = pruss_intc_read_reg(intc, PRU_INTC_HMR(idx));
> + val &= ~(HMR_CH_MAP_MASK <<
> + ((ch % HMR_CH_PER_REG) * HMR_CH_MAP_BITS));
> + val |= host << ((ch % HMR_CH_PER_REG) * HMR_CH_MAP_BITS);
> + pruss_intc_write_reg(intc, PRU_INTC_HMR(idx), val);
> +
> + dev_dbg(dev, "CH%d -> HOST%d (HMR%d 0x%08x)\n", ch, host, idx,
> + pruss_intc_read_reg(intc, PRU_INTC_HMR(idx)));
> +
> + /* enable host interrupts */
> + pruss_intc_write_reg(intc, PRU_INTC_HIEISR, host);
> + }
> +
> + dev_info(dev, "mapped system_event = %lu channel = %d host = %d
> domain = %u\n",
> + hwirq, ch, host, domain->id);
> +
> + /* global interrupt enable */
> + pruss_intc_write_reg(intc, PRU_INTC_GER, 1);
> +
> + mutex_unlock(&intc->lock);
> + return 0;
> +
> +unlock:
> + mutex_unlock(&intc->lock);
> + return ret;
> +}
> +
> +/**
> + * pruss_intc_unmap() - unconfigure the PRUSS INTC
> + * @domain: pru intc domain pointer
> + * @hwirq: the system event number
> + *
> + * Undo whatever was done in pruss_intc_map() for a PRU core.
> + * Mappings are reference counted, so resources are only disabled when
> there
> + * are no longer any users.
> + */
> +static void pruss_intc_unmap(struct pruss_intc_domain *domain, unsigned
> long hwirq)
> +{
> + struct pruss_intc *intc = domain->intc;
> + struct device* dev = intc->irqchip->parent_device;
> + u8 ch, host;
> +
> + if (hwirq >= MAX_PRU_SYS_EVENTS)
> + return;
> +
> + mutex_lock(&intc->lock);
> +
> + ch = intc->event_channel[hwirq].value;
> + host = intc->channel_host[ch].value;
> +
> + if (--intc->channel_host[ch].ref_count == 0) {
> + /* disable host interrupts */
> + pruss_intc_write_reg(intc, PRU_INTC_HIDISR, host);
> + }
> +
> + if (--intc->event_channel[hwirq].ref_count == 0) {
> + /* disable system events */
> + pruss_intc_write_reg(intc, PRU_INTC_EICR, hwirq);
> + /* clear any pending status */
> + pruss_intc_write_reg(intc, PRU_INTC_SICR, hwirq);
> + }
> +
> + if (intc->has_evtsel)
> + intc->evtsel.ref_count--;
> +
> + dev_info(dev, "unmapped system_event = %lu channel = %d host = %d\n",
> + hwirq, ch, host);
> +
> + mutex_unlock(&intc->lock);
> +}
> +
> static void pruss_intc_init(struct pruss_intc *intc)
> {
> int i;
> @@ -198,10 +436,83 @@ static int pruss_intc_irq_set_irqchip_state(struct
> irq_data *data,
> return pruss_intc_check_write(intc, PRU_INTC_SICR, data->hwirq);
> }
>
> +static int pruss_intc_irq_domain_select(struct irq_domain *d,
> + struct irq_fwspec *fwspec,
> + enum irq_domain_bus_token bus_token)
> +{
> + struct pruss_intc_domain *domain = d->host_data;
> + int num_cells = domain->intc->has_evtsel ? 5 : 4;
> + u32 domain_id;
> +
> + if (!fwspec || fwspec->fwnode != domain->domain->fwnode)
> + return 0;
> +
> + if (bus_token != DOMAIN_BUS_ANY && bus_token !=
> domain->domain->bus_token)
> + return 0;
> +
> + if (WARN_ON_ONCE(fwspec->param_count != num_cells))
> + return 0;
> +
> + domain_id = fwspec->param[fwspec->param_count - 1];
> + if (domain_id != domain->id)
> + return 0;
> +
> + return 1;
> +}
> +
> +static int
> +pruss_intc_irq_domain_xlate(struct irq_domain *d, struct device_node
> *node,
> + const u32 *intspec, unsigned int intsize,
> + unsigned long *out_hwirq, unsigned int *out_type)
> +{
> + struct pruss_intc_domain *domain = d->host_data;
> + struct pruss_intc *intc = domain->intc;
> + int num_cells = intc->has_evtsel ? 5 : 4;
> + u32 sys_event, channel, host, domain_id;
> + u32 evtsel = 0;
> +
> + if (WARN_ON_ONCE(intsize != num_cells))
> + return -EINVAL;
> +
> + sys_event = intspec[0];
> + if (sys_event >= MAX_PRU_SYS_EVENTS)
> + return -EINVAL;
> +
> + if (intc->has_evtsel)
> + evtsel = intspec[1];
> +
> + channel = intspec[intsize - 3];
> + if (channel >= MAX_PRU_CHANNELS)
> + return -EINVAL;
> +
> + host = intspec[intsize - 2];
> + if (host >= MAX_PRU_HOST_INT)
> + return -EINVAL;
> +
> + domain_id = intspec[intsize - 1];
> + if (domain_id != domain->id)
> + return -EINVAL;
> +
> + domain->hwirq_data[sys_event].evtsel = evtsel;
> + domain->hwirq_data[sys_event].channel = channel;
> + domain->hwirq_data[sys_event].host = host;
> +
> + *out_hwirq = sys_event;
> + *out_type = IRQ_TYPE_NONE;
> +
> + return 0;
> +}
> +
> static int pruss_intc_irq_domain_map(struct irq_domain *d, unsigned int
> virq,
> irq_hw_number_t hw)
> {
> - struct pruss_intc *intc = d->host_data;
> + struct pruss_intc_domain *domain = d->host_data;
> + struct pruss_intc *intc = domain->intc;
> + int err;
> +
> + err = pruss_intc_map(domain, hw);
> + if (err < 0)
> + return err;
>
> irq_set_chip_data(virq, intc);
> irq_set_chip_and_handler(virq, intc->irqchip, handle_level_irq);
> @@ -211,12 +522,17 @@ static int pruss_intc_irq_domain_map(struct
> irq_domain *d, unsigned int virq,
>
> static void pruss_intc_irq_domain_unmap(struct irq_domain *d, unsigned
> int virq)
> {
> + struct pruss_intc_domain *domain = d->host_data;
> + unsigned long hwirq = irqd_to_hwirq(irq_get_irq_data(virq));
> +
> irq_set_chip_and_handler(virq, NULL, NULL);
> irq_set_chip_data(virq, NULL);
> + pruss_intc_unmap(domain, hwirq);
> }
>
> static const struct irq_domain_ops pruss_intc_irq_domain_ops = {
> - .xlate = irq_domain_xlate_onecell,
> + .select = pruss_intc_irq_domain_select,
> + .xlate = pruss_intc_irq_domain_xlate,
> .map = pruss_intc_irq_domain_map,
> .unmap = pruss_intc_irq_domain_unmap,
> };
> @@ -245,7 +561,8 @@ static void pruss_intc_irq_handler(struct irq_desc
> *desc)
> hipir = pruss_intc_read_reg(intc, PRU_INTC_HIPIR(i));
> while (!(hipir & INTC_HIPIR_NONE_HINT)) {
> hwirq = hipir & GENMASK(9, 0);
> - virq = irq_linear_revmap(intc->domain, hwirq);
> + virq = irq_linear_revmap(
> + intc->domain[TI_PRUSS_INTC_DOMAIN_MCU].domain, hwirq);
>
> /*
> * NOTE: manually ACK any system events that do not have a
> @@ -272,7 +589,8 @@ static int pruss_intc_probe(struct platform_device
> *pdev)
> struct pruss_intc *intc;
> struct resource *res;
> struct irq_chip *irqchip;
> - int i, irq, count;
> + int i, err, irq, count;
> + u32 num_cells;
> u8 temp_intr[MAX_NUM_HOST_IRQS] = { 0 };
>
> intc = devm_kzalloc(dev, sizeof(*intc), GFP_KERNEL);
> @@ -323,13 +641,22 @@ static int pruss_intc_probe(struct platform_device
> *pdev)
> }
> }
>
> + err = of_property_read_u32(dev->of_node, "#interrupt-cells",
> &num_cells);
> + if (!err && num_cells == 5)
> + intc->has_evtsel = true;
> +
> mutex_init(&intc->lock);
>
> + pm_runtime_enable(dev);
> + pm_runtime_get_sync(dev);
> +
> pruss_intc_init(intc);
>
> irqchip = devm_kzalloc(dev, sizeof(*irqchip), GFP_KERNEL);
> - if (!irqchip)
> - return -ENOMEM;
> + if (!irqchip) {
> + err = -ENOMEM;
> + goto fail_alloc;
> + }
>
> irqchip->irq_ack = pruss_intc_irq_ack;
> irqchip->irq_mask = pruss_intc_irq_mask;
> @@ -338,14 +665,24 @@ static int pruss_intc_probe(struct platform_device
> *pdev)
> irqchip->irq_release_resources = pruss_intc_irq_relres;
> irqchip->irq_get_irqchip_state = pruss_intc_irq_get_irqchip_state;
> irqchip->irq_set_irqchip_state = pruss_intc_irq_set_irqchip_state;
> + irqchip->parent_device = dev;
> irqchip->name = dev_name(dev);
> intc->irqchip = irqchip;
>
> - /* always 64 events */
> - intc->domain = irq_domain_add_linear(dev->of_node, MAX_PRU_SYS_EVENTS,
> - &pruss_intc_irq_domain_ops, intc);
> - if (!intc->domain)
> - return -ENOMEM;
> + for (i = 0; i < NUM_TI_PRUSS_INTC_DOMAIN; i++) {
> + intc->domain[i].intc = intc;
> + intc->domain[i].id = i;
> + /* always 64 events */
> + intc->domain[i].domain = irq_domain_add_linear(dev->of_node,
> + MAX_PRU_SYS_EVENTS, &pruss_intc_irq_domain_ops,
> + &intc->domain[i]);
> + if (!intc->domain[i].domain) {
> + while (--i >= 0)
> + irq_domain_remove(intc->domain[i].domain);
> + err = -ENOMEM;
> + goto fail_alloc;
> + }
> + }
>
> for (i = 0; i < MAX_NUM_HOST_IRQS; i++) {
> irq = platform_get_irq_byname(pdev, irq_names[i]);
> @@ -356,6 +693,7 @@ static int pruss_intc_probe(struct platform_device
> *pdev)
>
> dev_err(dev, "platform_get_irq_byname failed for %s : %d\n",
> irq_names[i], irq);
> + err = irq;
> goto fail_irq;
> }
>
> @@ -372,13 +710,20 @@ static int pruss_intc_probe(struct platform_device
> *pdev)
> irq_set_chained_handler_and_data(intc->irqs[i], NULL,
> NULL);
> }
> - irq_domain_remove(intc->domain);
> - return irq;
> + for (i = 0; i < NUM_TI_PRUSS_INTC_DOMAIN; i++)
> + irq_domain_remove(intc->domain[i].domain);
> +
> +fail_alloc:
> + pm_runtime_put(dev);
> + pm_runtime_disable(dev);
> +
> + return err;
> }
>
> static int pruss_intc_remove(struct platform_device *pdev)
> {
> struct pruss_intc *intc = platform_get_drvdata(pdev);
> + struct device *dev = &pdev->dev;
> unsigned int hwirq;
> int i;
>
> @@ -388,9 +733,15 @@ static int pruss_intc_remove(struct platform_device
> *pdev)
> NULL);
> }
>
> - for (hwirq = 0; hwirq < MAX_PRU_SYS_EVENTS; hwirq++)
> - irq_dispose_mapping(irq_find_mapping(intc->domain, hwirq));
> - irq_domain_remove(intc->domain);
> + for (i = 0; i < NUM_TI_PRUSS_INTC_DOMAIN; i++) {
> + for (hwirq = 0; hwirq < MAX_PRU_SYS_EVENTS; hwirq++)
> + irq_dispose_mapping(irq_find_mapping(
> + intc->domain[i].domain, hwirq));
> + irq_domain_remove(intc->domain[i].domain);
> + }
> +
> + pm_runtime_put(dev);
> + pm_runtime_disable(dev);
>
> return 0;
> }
> diff --git a/include/dt-bindings/interrupt-controller/ti-pruss.h
> b/include/dt-bindings/interrupt-controller/ti-pruss.h
> new file mode 100644
> index 000000000000..326a68c31bce
> --- /dev/null
> +++ b/include/dt-bindings/interrupt-controller/ti-pruss.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0 OR MIT */
> +/*
> + * This header provides constants for the Texas Instruments Programmable
> + * Realtime Unit Subsystem (PRUSS) interrupt controller.
> + */
> +
> +#ifndef _DT_BINDINGS_INTERRUPT_CONTROLLER_TI_PRUSS_H
> +#define _DT_BINDINGS_INTERRUPT_CONTROLLER_TI_PRUSS_H
> +
> +/* interrupt specifier for optional cell 1 */
> +
> +#define TI_PRUSS_INTC_EVTSEL_ANY 0xffffffff
> +
> +/* interrupt specifier for cell #interrupt-cells - 1 */
> +
> +/* host interrupt is connected to PRU cores, e.g. host events 0 and 1 */
> +#define TI_PRUSS_INTC_DOMAIN_PRU 0
> +/* host interrupt is connected to MCU's interrupt controller */
> +#define TI_PRUSS_INTC_DOMAIN_MCU 1
> +/* host interrupt is connected to DSP's interrupt controller */
> +#define TI_PRUSS_INTC_DOMAIN_DSP 2
> +/* host interrupt is connected to the auxillary PRU cores */
> +#define TI_PRUSS_INTC_DOMAIN_RTU_PRU 3
> +/* host interrupt is connected to the task managers */
> +#define TI_PRUSS_INTC_DOMAIN_TASK 4
> +
> +#endif /* _DT_BINDINGS_INTERRUPT_CONTROLLER_TI_PRUSS_H */
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v2] ARM: dts: add device tree for Mecer Xtreme Mini S6
From: Rob Herring @ 2019-08-12 19:11 UTC (permalink / raw)
To: Justin Swartz
Cc: Mark Rutland, devicetree, Heiko Stuebner,
linux-kernel@vger.kernel.org, open list:ARM/Rockchip SoC...,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE
In-Reply-To: <20190811230015.28349-1-justin.swartz@risingedge.co.za>
On Sun, Aug 11, 2019 at 5:01 PM Justin Swartz
<justin.swartz@risingedge.co.za> wrote:
>
> The Mecer Xtreme Mini S6 features a Rockchip RK3229 SoC,
> 1GB DDR3 RAM, 8GB eMMC, MicroSD port, 10/100Mbps Ethernet,
> Realtek 8723BS WLAN module, 2 x USB 2.0 ports, HDMI output,
> and S/PDIF output.
>
> Signed-off-by: Justin Swartz <justin.swartz@risingedge.co.za>
> ---
> .../devicetree/bindings/arm/rockchip.yaml | 5 +
> .../devicetree/bindings/vendor-prefixes.yaml | 2 +
> arch/arm/boot/dts/Makefile | 1 +
> arch/arm/boot/dts/rk3229-xms6.dts | 283 +++++++++++++++++++++
> 4 files changed, 291 insertions(+)
> create mode 100644 arch/arm/boot/dts/rk3229-xms6.dts
Reviewed-by: Rob Herring <robh@kernel.org>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH v2] coresight: tmc-etr: Fix perf_data check.
From: Yabin Cui @ 2019-08-12 19:03 UTC (permalink / raw)
To: Mathieu Poirier, Suzuki K Poulose, Alexander Shishkin
Cc: Yabin Cui, linux-kernel, linux-arm-kernel
When tracing etm data of multiple threads on multiple cpus through
perf interface, each cpu has a unique etr_perf_buffer while sharing
the same etr device. There is no guarantee that the last cpu starts
etm tracing also stops last. This makes perf_data check fail.
Fix it by checking etr_buf instead of etr_perf_buffer.
Fixes: 3147da92a8a8 ("coresight: tmc-etr: Allocate and free ETR memory buffers for CPU-wide scenarios")
Signed-off-by: Yabin Cui <yabinc@google.com>
---
v1 -> v2: rename perf_data to perf_buf. Add fixes tag.
---
drivers/hwtracing/coresight/coresight-tmc-etr.c | 6 +++---
drivers/hwtracing/coresight/coresight-tmc.h | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 17006705287a..90d1548ad268 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1484,7 +1484,7 @@ tmc_update_etr_buffer(struct coresight_device *csdev,
goto out;
}
- if (WARN_ON(drvdata->perf_data != etr_perf)) {
+ if (WARN_ON(drvdata->perf_buf != etr_buf)) {
lost = true;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
goto out;
@@ -1497,7 +1497,7 @@ tmc_update_etr_buffer(struct coresight_device *csdev,
CS_LOCK(drvdata->base);
/* Reset perf specific data */
- drvdata->perf_data = NULL;
+ drvdata->perf_buf = NULL;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
size = etr_buf->len;
@@ -1556,7 +1556,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
}
etr_perf->head = PERF_IDX2OFF(handle->head, etr_perf);
- drvdata->perf_data = etr_perf;
+ drvdata->perf_buf = etr_perf->etr_buf;
/*
* No HW configuration is needed if the sink is already in
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 1ed50411cc3c..f9a0c95e9ba2 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -178,8 +178,8 @@ struct etr_buf {
* device configuration register (DEVID)
* @idr: Holds etr_bufs allocated for this ETR.
* @idr_mutex: Access serialisation for idr.
- * @perf_data: PERF buffer for ETR.
- * @sysfs_data: SYSFS buffer for ETR.
+ * @sysfs_buf: SYSFS buffer for ETR.
+ * @perf_buf: PERF buffer for ETR.
*/
struct tmc_drvdata {
void __iomem *base;
@@ -202,7 +202,7 @@ struct tmc_drvdata {
struct idr idr;
struct mutex idr_mutex;
struct etr_buf *sysfs_buf;
- void *perf_data;
+ struct etr_buf *perf_buf;
};
struct etr_buf_operations {
--
2.23.0.rc1.153.gdeed80330f-goog
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* Re: [PATCH v2 1/6] dt-bindings: arm: fsl: Add the S32V234-EVB board
From: Rob Herring @ 2019-08-12 18:58 UTC (permalink / raw)
To: Stefan-gabriel Mirea
Cc: mark.rutland@arm.com, devicetree@vger.kernel.org, Eddy Petrisor,
will@kernel.org, corbet@lwn.net, gregkh@linuxfoundation.org,
jslaby@suse.com, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org, Leo Li,
linux-serial@vger.kernel.org, catalin.marinas@arm.com,
shawnguo@kernel.org, linux-arm-kernel@lists.infradead.org
In-Reply-To: <20190809112853.15846-2-stefan-gabriel.mirea@nxp.com>
On Fri, Aug 9, 2019 at 5:29 AM Stefan-gabriel Mirea
<stefan-gabriel.mirea@nxp.com> wrote:
>
> From: Eddy Petrișor <eddy.petrisor@nxp.com>
>
> Add entry for the NXP S32V234 Customer Evaluation Board to the board/SoC
> bindings.
>
> Signed-off-by: Eddy Petrișor <eddy.petrisor@nxp.com>
> Signed-off-by: Stefan-Gabriel Mirea <stefan-gabriel.mirea@nxp.com>
> ---
> Documentation/devicetree/bindings/arm/fsl.yaml | 6 ++++++
> 1 file changed, 6 insertions(+)
Reviewed-by: Rob Herring <robh@kernel.org>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH 01/19] dt-bindings: arm: cpu: Add Marvell MMP3 SMP enable method
From: Rob Herring @ 2019-08-12 18:57 UTC (permalink / raw)
To: Lubomir Rintel
Cc: Mark Rutland, devicetree, Jason Cooper, Stephen Boyd,
Marc Zyngier, Michael Turquette, Russell King,
Kishon Vijay Abraham I,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
Olof Johansson, Thomas Gleixner, linux-clk,
linux-kernel@vger.kernel.org
In-Reply-To: <20190809093158.7969-2-lkundrak@v3.sk>
On Fri, Aug 9, 2019 at 3:32 AM Lubomir Rintel <lkundrak@v3.sk> wrote:
>
> Add the enable method for the second PJ4B core of the Marvell MMP3 SoC.
>
> Signed-off-by: Lubomir Rintel <lkundrak@v3.sk>
> ---
> Documentation/devicetree/bindings/arm/cpus.yaml | 1 +
> 1 file changed, 1 insertion(+)
Reviewed-by: Rob Herring <robh@kernel.org>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH 7/7] arm64: dts: imx8mm: Add devfreq nodes
From: Leonard Crestez @ 2019-08-12 18:49 UTC (permalink / raw)
To: Stephen Boyd, MyungJoo Ham, Kyungmin Park, Rob Herring
Cc: Dong Aisheng, Artur Świgoń, Abel Vesa, Saravana Kannan,
Anson Huang, linux-arm-kernel, Viresh Kumar, Michael Turquette,
linux-pm, linux-imx, Krzysztof Kozlowski, Chanwoo Choi,
Will Deacon, Alexandre Bailon, kernel, Fabio Estevam,
Mark Rutland, Shawn Guo, Georgi Djakov, devicetree, Jacky Bai
In-Reply-To: <cover.1565633880.git.leonard.crestez@nxp.com>
Add initial support for bus scaling on imx8m, starting with noc and ddrc
because they're the biggest power hogs.
Add a devfreq-event link to the PMU in order to support on-demand
scaling of ddrc based on measured dram bandwith usage. Make ddrc a
parent of the NOC because all traffic to ddrc goes through
Support for proactive scaling via interconnect and support for scaling
additional NICs will come later. The high-performance bus masters which
need that (display, vpu, gpu) are not yet enabled in upstream anyway.
Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
---
arch/arm64/boot/dts/freescale/imx8mm.dtsi | 51 ++++++++++++++++++++++-
1 file changed, 50 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi b/arch/arm64/boot/dts/freescale/imx8mm.dtsi
index e8560d15c130..a37d82133c06 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi
@@ -122,10 +122,38 @@
clock-latency-ns = <150000>;
opp-suspend;
};
};
+ ddrc_opp_table: ddrc-opp-table {
+ compatible = "operating-points-v2";
+
+ opp-25M {
+ opp-hz = /bits/ 64 <25000000>;
+ };
+ opp-100M {
+ opp-hz = /bits/ 64 <100000000>;
+ };
+ opp-750M {
+ opp-hz = /bits/ 64 <750000000>;
+ };
+ };
+
+ noc_opp_table: noc-opp-table {
+ compatible = "operating-points-v2";
+
+ opp-150M {
+ opp-hz = /bits/ 64 <150000000>;
+ };
+ opp-375M {
+ opp-hz = /bits/ 64 <375000000>;
+ };
+ opp-750M {
+ opp-hz = /bits/ 64 <750000000>;
+ };
+ };
+
memory@40000000 {
device_type = "memory";
reg = <0x0 0x40000000 0 0x80000000>;
};
@@ -748,10 +776,18 @@
status = "disabled";
};
};
+ noc: noc@32700000 {
+ compatible = "fsl,imx8mm-noc", "fsl,imx8m-noc";
+ reg = <0x32700000 0x100000>;
+ clocks = <&clk IMX8MM_CLK_NOC>;
+ devfreq = <&ddrc>;
+ operating-points-v2 = <&noc_opp_table>;
+ };
+
aips4: bus@32c00000 {
compatible = "fsl,aips-bus", "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x32c00000 0x32c00000 0x400000>;
@@ -832,11 +868,24 @@
#interrupt-cells = <3>;
interrupt-controller;
interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
};
- ddr-pmu@3d800000 {
+ ddrc: dram-controller@3d400000 {
+ compatible = "fsl,imx8mm-ddrc", "fsl,imx8m-ddrc";
+ reg = <0x3d400000 0x400000>;
+ clock-names = "dram_core", "dram_pll", "dram_alt_root", "dram_alt", "dram_apb";
+ clocks = <&clk IMX8MM_CLK_DRAM_CORE>,
+ <&clk IMX8MM_DRAM_PLL>,
+ <&clk IMX8MM_CLK_DRAM_ALT_ROOT>,
+ <&clk IMX8MM_CLK_DRAM_ALT>,
+ <&clk IMX8MM_CLK_DRAM_APB>;
+ devfreq-events = <&ddr_pmu>;
+ operating-points-v2 = <&ddrc_opp_table>;
+ };
+
+ ddr_pmu: ddr-pmu@3d800000 {
compatible = "fsl,imx8mm-ddr-pmu", "fsl,imx8m-ddr-pmu";
reg = <0x3d800000 0x400000>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
};
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH 5/7] PM / devfreq: Add dynamic scaling for imx ddr controller
From: Leonard Crestez @ 2019-08-12 18:49 UTC (permalink / raw)
To: Stephen Boyd, MyungJoo Ham, Kyungmin Park, Rob Herring
Cc: Dong Aisheng, Artur Świgoń, Abel Vesa, Saravana Kannan,
Anson Huang, linux-arm-kernel, Viresh Kumar, Michael Turquette,
linux-pm, linux-imx, Krzysztof Kozlowski, Chanwoo Choi,
Will Deacon, Alexandre Bailon, kernel, Fabio Estevam,
Mark Rutland, Shawn Guo, Georgi Djakov, devicetree, Jacky Bai
In-Reply-To: <cover.1565633880.git.leonard.crestez@nxp.com>
Add driver for dynamic scaling the DDR Controller on imx8m chips. Actual
frequency switching is implemented in TF-A, this driver just wraps the
SMC calls and updates the clk tree.
Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
---
drivers/devfreq/Makefile | 2 +-
drivers/devfreq/imx-ddrc.c | 379 +++++++++++++++++++++++++++++++++++++
2 files changed, 380 insertions(+), 1 deletion(-)
create mode 100644 drivers/devfreq/imx-ddrc.c
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
index c2463ed4c934..eba138914c74 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -7,11 +7,11 @@ obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o
obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o
obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o
# DEVFREQ Drivers
obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o
-obj-$(CONFIG_ARM_IMX_DEVFREQ) += imx-devfreq.o
+obj-$(CONFIG_ARM_IMX_DEVFREQ) += imx-devfreq.o imx-ddrc.o
obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o
obj-$(CONFIG_ARM_TEGRA20_DEVFREQ) += tegra20-devfreq.o
# DEVFREQ Event Drivers
diff --git a/drivers/devfreq/imx-ddrc.c b/drivers/devfreq/imx-ddrc.c
new file mode 100644
index 000000000000..e46a1e711bc6
--- /dev/null
+++ b/drivers/devfreq/imx-ddrc.c
@@ -0,0 +1,379 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 NXP
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/devfreq.h>
+#include <linux/pm_opp.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/arm-smccc.h>
+
+#define IMX_SIP_DDR_DVFS 0xc2000004
+
+/* Values starting from 0 switch to specific frequency */
+#define IMX_SIP_DDR_FREQ_SET_HIGH 0x00
+
+/* Deprecated after moving IRQ handling to ATF */
+#define IMX_SIP_DDR_DVFS_WAIT_CHANGE 0x0F
+
+/* Query available frequencies. */
+#define IMX_SIP_DDR_DVFS_GET_FREQ_COUNT 0x10
+#define IMX_SIP_DDR_DVFS_GET_FREQ_INFO 0x11
+
+/*
+ * This should be in a 1:1 mapping with devicetree OPPs but
+ * firmware provides additional info.
+ */
+struct imx_ddrc_freq {
+ unsigned long rate;
+ unsigned long smcarg;
+ int dram_core_parent_index;
+ int dram_alt_parent_index;
+ int dram_apb_parent_index;
+};
+
+/* Hardware limitation */
+#define IMX_DDRC_MAX_FREQ_COUNT 4
+
+/*
+ * imx DRAM controller
+ *
+ * imx DRAM controller clocks have the following structure (abridged):
+ *
+ * +----------+ |\ +------+
+ * | dram_pll |-------|M| dram_core | |
+ * +----------+ |U|---------->| D |
+ * /--|X| | D |
+ * dram_alt_root | |/ | R |
+ * | | C |
+ * +---------+ | |
+ * |FIX DIV/4| | |
+ * +---------+ | |
+ * composite: | | |
+ * +----------+ | | |
+ * | dram_alt |----/ | |
+ * +----------+ | |
+ * | dram_apb |-------------------->| |
+ * +----------+ +------+
+ *
+ * The dram_pll is used for higher rates and dram_alt is used for lower rates.
+ *
+ * Frequency switching is implemented in TF-A (via SMC call) and can change the
+ * configuration of the clocks, including mux parents. The dram_alt and
+ * dram_apb clocks are "imx composite" and their parent can change too.
+ *
+ * We need to prepare/enable the new mux parents head of switching and update
+ * their information afterwards.
+ */
+struct imx_ddrc {
+ struct devfreq_dev_profile profile;
+ struct devfreq *devfreq;
+
+ /* For frequency switching: */
+ struct clk *dram_core;
+ struct clk *dram_pll;
+ struct clk *dram_alt_root;
+ struct clk *dram_alt;
+ struct clk *dram_apb;
+
+ int freq_count;
+ struct imx_ddrc_freq freq_table[IMX_DDRC_MAX_FREQ_COUNT];
+};
+
+static struct imx_ddrc_freq* imx_ddrc_find_freq(struct imx_ddrc* priv,
+ unsigned long rate)
+{
+ int i;
+
+ for (i = 0; i < priv->freq_count; ++i)
+ if (priv->freq_table[i].rate == rate)
+ return &priv->freq_table[i];
+
+ return NULL;
+}
+
+static void imx_ddrc_smc_set_freq(int target_freq)
+{
+ struct arm_smccc_res res;
+ u32 online_cpus = 0;
+ int cpu;
+
+ local_irq_disable();
+
+ for_each_online_cpu(cpu)
+ online_cpus |= (1 << (cpu * 8));
+
+ /* change the ddr freqency */
+ arm_smccc_smc(IMX_SIP_DDR_DVFS, target_freq, online_cpus,
+ 0, 0, 0, 0, 0, &res);
+
+ local_irq_enable();
+}
+
+struct clk *clk_get_parent_by_index(struct clk* clk, int index)
+{
+ struct clk_hw *hw;
+
+ hw = clk_hw_get_parent_by_index(__clk_get_hw(clk), index);
+
+ return hw ? hw->clk : NULL;
+}
+
+static int imx_ddrc_set_freq(struct device *dev, struct imx_ddrc_freq* freq)
+{
+ struct imx_ddrc *priv = dev_get_drvdata(dev);
+ struct clk* new_dram_core_parent;
+ struct clk* new_dram_alt_parent;
+ struct clk* new_dram_apb_parent;
+ int ret;
+
+ new_dram_core_parent = clk_get_parent_by_index(
+ priv->dram_core, freq->dram_core_parent_index - 1);
+ new_dram_alt_parent = clk_get_parent_by_index(
+ priv->dram_alt, freq->dram_alt_parent_index - 1);
+ new_dram_apb_parent = clk_get_parent_by_index(
+ priv->dram_apb, freq->dram_apb_parent_index - 1);
+
+ /* increase reference counts and ensure clks are ON before switch */
+ ret = clk_prepare_enable(new_dram_core_parent);
+ if (ret) {
+ dev_err(dev, "failed enable new dram_core parent: %d\n", ret);
+ goto out;
+ }
+ ret = clk_prepare_enable(new_dram_alt_parent);
+ if (ret) {
+ dev_err(dev, "failed enable new dram_alt parent: %d\n", ret);
+ goto out_dis_core;
+ }
+ ret = clk_prepare_enable(new_dram_apb_parent);
+ if (ret) {
+ dev_err(dev, "failed enable new dram_apb parent: %d\n", ret);
+ goto out_dis_alt;
+ }
+
+ imx_ddrc_smc_set_freq(freq->smcarg);
+
+ /* update parents in clk tree after switch. */
+ ret = clk_set_parent(priv->dram_core, new_dram_core_parent);
+ if (ret)
+ dev_err(dev, "failed clk_set_parent dram_core: %d\n", ret);
+ if (new_dram_alt_parent) {
+ ret = clk_set_parent(priv->dram_alt, new_dram_alt_parent);
+ if (ret)
+ dev_err(dev, "failed clk_set_parent dram_alt_parent: %d\n", ret);
+ }
+ if (new_dram_apb_parent) {
+ ret = clk_set_parent(priv->dram_apb, new_dram_apb_parent);
+ if (ret)
+ dev_err(dev, "failed clk_set_parent dram_apb_parent: %d\n", ret);
+ }
+
+ /*
+ * clk_set_parent transfer the reference count from old parent.
+ * now we drop extra reference counts used during the switch
+ */
+ clk_disable_unprepare(new_dram_apb_parent);
+out_dis_alt:
+ clk_disable_unprepare(new_dram_alt_parent);
+out_dis_core:
+ clk_disable_unprepare(new_dram_core_parent);
+out:
+ return ret;
+}
+
+static int imx_ddrc_target(struct device *dev, unsigned long *freq, u32 flags)
+{
+ struct imx_ddrc *priv = dev_get_drvdata(dev);
+ struct imx_ddrc_freq *freq_info;
+ struct dev_pm_opp *new_opp;
+ unsigned long new_freq, cur_freq;
+ int ret;
+
+ new_opp = devfreq_recommended_opp(dev, freq, flags);
+ if (IS_ERR(new_opp)) {
+ ret = PTR_ERR(new_opp);
+ dev_err(dev, "failed to get recommended opp: %d\n", ret);
+ return ret;
+ }
+ new_freq = dev_pm_opp_get_freq(new_opp);
+ dev_pm_opp_put(new_opp);
+ cur_freq = clk_get_rate(priv->dram_core);
+
+ if (new_freq == cur_freq)
+ return 0;
+
+ freq_info = imx_ddrc_find_freq(priv, new_freq);
+ if (!freq_info)
+ return -EINVAL;
+ ret = imx_ddrc_set_freq(dev, freq_info);
+ if (ret)
+ dev_err(dev, "ddrc failed to change freq %lu to %lu\n",
+ cur_freq, new_freq);
+ else
+ dev_dbg(dev, "ddrc changed freq %lu to %lu\n",
+ cur_freq, new_freq);
+
+ return ret;
+}
+
+static int imx_ddrc_get_cur_freq(struct device *dev, unsigned long *freq)
+{
+ struct imx_ddrc *priv = dev_get_drvdata(dev);
+
+ *freq = clk_get_rate(priv->dram_core);
+
+ return 0;
+}
+
+static int imx_ddrc_get_dev_status(struct device *dev,
+ struct devfreq_dev_status *stat)
+{
+ struct imx_ddrc *priv = dev_get_drvdata(dev);
+
+ stat->busy_time = 0;
+ stat->total_time = 0;
+ stat->current_frequency = clk_get_rate(priv->dram_core);
+
+ return 0;
+}
+
+static int imx_ddrc_init_freq_info(struct device *dev)
+{
+ struct imx_ddrc *priv = dev_get_drvdata(dev);
+ struct arm_smccc_res res;
+ int index;
+
+ /*
+ * An error here means DDR DVFS API not supported by firmware
+ */
+ arm_smccc_smc(IMX_SIP_DDR_DVFS, IMX_SIP_DDR_DVFS_GET_FREQ_COUNT,
+ 0, 0, 0, 0, 0, 0, &res);
+ priv->freq_count = res.a0;
+ if (priv->freq_count <= 0 || priv->freq_count > IMX_DDRC_MAX_FREQ_COUNT)
+ return -ENOSYS;
+
+ for (index = 0; index < priv->freq_count; ++index) {
+ struct imx_ddrc_freq *freq = &priv->freq_table[index];
+
+ arm_smccc_smc(IMX_SIP_DDR_DVFS, IMX_SIP_DDR_DVFS_GET_FREQ_INFO,
+ index, 0, 0, 0, 0, 0, &res);
+ /* Result should be strictly positive */
+ if ((long)res.a0 <= 0)
+ return -ENOSYS;
+
+ freq->rate = res.a0 * 250000;
+ freq->smcarg = index;
+ freq->dram_core_parent_index = res.a1;
+ freq->dram_alt_parent_index = res.a2;
+ freq->dram_apb_parent_index = res.a3;
+
+ /* dram_core has 2 options: dram_pll or dram_alt_root */
+ if (freq->dram_core_parent_index != 1 &&
+ freq->dram_core_parent_index != 2)
+ return -ENOSYS;
+ /* dram_apb and dram_alt have exactly 8 possible parents */
+ if (freq->dram_alt_parent_index > 8 ||
+ freq->dram_apb_parent_index > 8)
+ return -ENOSYS;
+ /* dram_core from alt requires explicit dram_alt parent */
+ if (freq->dram_core_parent_index == 2 &&
+ freq->dram_alt_parent_index == 0)
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+
+static void imx_ddrc_exit(struct device *dev)
+{
+ struct imx_ddrc *priv = dev_get_drvdata(dev);
+
+ return dev_pm_opp_of_remove_table(dev);
+}
+
+static int imx_ddrc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imx_ddrc *priv;
+ struct device_node *events_node;
+ const char *gov = DEVFREQ_GOV_USERSPACE;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
+ ret = imx_ddrc_init_freq_info(dev);
+ if (ret) {
+ dev_err(dev, "failed to init firmware freq info: %d\n", ret);
+ return ret;
+ }
+
+ priv->dram_core = devm_clk_get(dev, "dram_core");
+ priv->dram_pll = devm_clk_get(dev, "dram_pll");
+ priv->dram_alt = devm_clk_get(dev, "dram_alt");
+ priv->dram_alt_root = devm_clk_get(dev, "dram_alt_root");
+ priv->dram_apb = devm_clk_get(dev, "dram_apb");
+ if (IS_ERR(priv->dram_core) ||
+ IS_ERR(priv->dram_pll) ||
+ IS_ERR(priv->dram_alt) ||
+ IS_ERR(priv->dram_alt_root) ||
+ IS_ERR(priv->dram_apb)) {
+ ret = PTR_ERR(priv->devfreq);
+ dev_err(dev, "failed to fetch clocks: %d\n", ret);
+ return ret;
+ }
+
+ ret = dev_pm_opp_of_add_table(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to get OPP table\n");
+ return ret;
+ }
+
+ priv->profile.polling_ms = 1000;
+ priv->profile.target = imx_ddrc_target;
+ priv->profile.get_dev_status = imx_ddrc_get_dev_status;
+ priv->profile.exit = imx_ddrc_exit;
+ priv->profile.get_cur_freq = imx_ddrc_get_cur_freq;
+ priv->profile.initial_freq = clk_get_rate(priv->dram_core);
+
+ priv->devfreq = devm_devfreq_add_device(dev, &priv->profile,
+ gov, NULL);
+ if (IS_ERR(priv->devfreq)) {
+ ret = PTR_ERR(priv->devfreq);
+ dev_err(dev, "failed to add devfreq device: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ dev_pm_opp_of_remove_table(dev);
+ return ret;
+}
+
+static const struct of_device_id imx_ddrc_of_match[] = {
+ { .compatible = "fsl,imx8m-ddrc", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, imx_ddrc_of_match);
+
+static struct platform_driver imx_ddrc_platdrv = {
+ .probe = imx_ddrc_probe,
+ .driver = {
+ .name = "imx-ddrc-devfreq",
+ .of_match_table = of_match_ptr(imx_ddrc_of_match),
+ },
+};
+module_platform_driver(imx_ddrc_platdrv);
+
+MODULE_DESCRIPTION("i.MX DDR controller frequency driver");
+MODULE_AUTHOR("Leonard Crestez <leonard.crestez@nxp.com>");
+MODULE_LICENSE("GPL v2");
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH 6/7] PM / devfreq: imx-ddrc: Measure bandwidth with perf
From: Leonard Crestez @ 2019-08-12 18:49 UTC (permalink / raw)
To: Stephen Boyd, MyungJoo Ham, Kyungmin Park, Rob Herring
Cc: Dong Aisheng, Artur Świgoń, Abel Vesa, Saravana Kannan,
Anson Huang, linux-arm-kernel, Viresh Kumar, Michael Turquette,
linux-pm, linux-imx, Krzysztof Kozlowski, Chanwoo Choi,
Will Deacon, Alexandre Bailon, kernel, Fabio Estevam,
Mark Rutland, Shawn Guo, Georgi Djakov, devicetree, Jacky Bai
In-Reply-To: <cover.1565633880.git.leonard.crestez@nxp.com>
The imx8m ddrc has a performance monitoring block attached which can
be used to measure bandwidth usage and automatically adjust frequency.
There is already a perf driver for that block so instead of implementing
a devfreq events driver use the in-kernel perf API to implement
get_dev_status directly.
Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
---
drivers/devfreq/imx-ddrc.c | 136 ++++++++++++++++++++++++++++++++++++-
1 file changed, 134 insertions(+), 2 deletions(-)
diff --git a/drivers/devfreq/imx-ddrc.c b/drivers/devfreq/imx-ddrc.c
index e46a1e711bc6..d7f27cdce697 100644
--- a/drivers/devfreq/imx-ddrc.c
+++ b/drivers/devfreq/imx-ddrc.c
@@ -11,10 +11,13 @@
#include <linux/pm_opp.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/arm-smccc.h>
+#include <asm/perf_event.h>
+#include <linux/perf_event.h>
+
#define IMX_SIP_DDR_DVFS 0xc2000004
/* Values starting from 0 switch to specific frequency */
#define IMX_SIP_DDR_FREQ_SET_HIGH 0x00
@@ -81,10 +84,22 @@ struct imx_ddrc {
struct clk *dram_alt;
struct clk *dram_apb;
int freq_count;
struct imx_ddrc_freq freq_table[IMX_DDRC_MAX_FREQ_COUNT];
+
+ /* For measuring load with perf events: */
+ struct platform_device* pmu_pdev;
+ struct pmu *pmu;
+
+ struct perf_event_attr rd_event_attr;
+ struct perf_event_attr wr_event_attr;
+ struct perf_event *rd_event;
+ struct perf_event *wr_event;
+
+ u64 last_rd_val, last_rd_ena, last_rd_run;
+ u64 last_wr_val, last_wr_ena, last_wr_run;
};
static struct imx_ddrc_freq* imx_ddrc_find_freq(struct imx_ddrc* priv,
unsigned long rate)
{
@@ -233,17 +248,117 @@ static int imx_ddrc_get_cur_freq(struct device *dev, unsigned long *freq)
static int imx_ddrc_get_dev_status(struct device *dev,
struct devfreq_dev_status *stat)
{
struct imx_ddrc *priv = dev_get_drvdata(dev);
- stat->busy_time = 0;
- stat->total_time = 0;
stat->current_frequency = clk_get_rate(priv->dram_core);
+ if (priv->rd_event && priv->wr_event) {
+ u64 rd_delta, rd_val, rd_ena, rd_run;
+ u64 wr_delta, wr_val, wr_ena, wr_run;
+
+ rd_val = perf_event_read_value(priv->rd_event, &rd_ena, &rd_run);
+ wr_val = perf_event_read_value(priv->wr_event, &wr_ena, &wr_run);
+
+ rd_delta = (rd_val - priv->last_rd_val) * (rd_ena - priv->last_rd_ena) / (rd_run - priv->last_rd_run);
+ priv->last_rd_val = rd_val;
+ priv->last_rd_ena = rd_ena;
+ priv->last_rd_run = rd_run;
+ wr_delta = (wr_val - priv->last_wr_val) * (wr_ena - priv->last_wr_ena) / (wr_run - priv->last_wr_run);
+ priv->last_wr_val = wr_val;
+ priv->last_wr_ena = wr_ena;
+ priv->last_wr_run = wr_run;
+
+ /* magic numbers, possibly wrong */
+ stat->busy_time = 4 * (rd_delta + wr_delta);
+ stat->total_time = stat->current_frequency;
+ } else {
+ stat->busy_time = 0;
+ stat->total_time = 0;
+ }
+
+ return 0;
+}
+
+static int imx_ddrc_perf_disable(struct imx_ddrc *priv)
+{
+ /* release and set to NULL */
+ if (!IS_ERR_OR_NULL(priv->rd_event))
+ perf_event_release_kernel(priv->rd_event);
+ if (!IS_ERR_OR_NULL(priv->wr_event))
+ perf_event_release_kernel(priv->wr_event);
+ priv->rd_event = NULL;
+ priv->wr_event = NULL;
+
return 0;
}
+static int imx_ddrc_perf_enable(struct imx_ddrc *priv)
+{
+ int ret;
+
+ priv->rd_event_attr.size = sizeof(priv->rd_event_attr);
+ priv->rd_event_attr.type = priv->pmu->type;
+ priv->rd_event_attr.config = 0x2a;
+
+ priv->rd_event = perf_event_create_kernel_counter(
+ &priv->rd_event_attr, 0, NULL, NULL, NULL);
+ if (IS_ERR(priv->rd_event)) {
+ ret = PTR_ERR(priv->rd_event);
+ goto err;
+ }
+
+ priv->wr_event_attr.size = sizeof(priv->wr_event_attr);
+ priv->wr_event_attr.type = priv->pmu->type;
+ priv->wr_event_attr.config = 0x2b;
+
+ priv->wr_event = perf_event_create_kernel_counter(
+ &priv->wr_event_attr, 0, NULL, NULL, NULL);
+ if (IS_ERR(priv->wr_event)) {
+ ret = PTR_ERR(priv->wr_event);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ imx_ddrc_perf_disable(priv);
+ return ret;
+}
+
+static int imx_ddrc_init_events(struct device *dev,
+ struct device_node* events_node)
+{
+ struct imx_ddrc *priv = dev_get_drvdata(dev);
+ struct device_driver *driver;
+
+ /*
+ * We need pmu->type for perf_event_attr but there is no API for
+ * mapping device_node to pmu. Fetch private data for imx-ddr-pmu and
+ * cast that to a struct pmu instead.
+ */
+ priv->pmu_pdev = of_find_device_by_node(events_node);
+ if (!priv->pmu_pdev)
+ return -EPROBE_DEFER;
+ driver = priv->pmu_pdev->dev.driver;
+ if (!driver)
+ return -EPROBE_DEFER;
+ if (strcmp(driver->name, "imx-ddr-pmu")) {
+ dev_warn(dev, "devfreq-events node %pOF has unexpected driver %s\n",
+ events_node, driver->name);
+ return -ENODEV;
+ }
+
+ priv->pmu = platform_get_drvdata(priv->pmu_pdev);
+ if (!priv->pmu)
+ return -EINVAL;
+
+ dev_info(dev, "events from pmu %s\n", priv->pmu->name);
+
+ return imx_ddrc_perf_enable(priv);
+}
+
static int imx_ddrc_init_freq_info(struct device *dev)
{
struct imx_ddrc *priv = dev_get_drvdata(dev);
struct arm_smccc_res res;
int index;
@@ -291,10 +406,13 @@ static int imx_ddrc_init_freq_info(struct device *dev)
static void imx_ddrc_exit(struct device *dev)
{
struct imx_ddrc *priv = dev_get_drvdata(dev);
+ imx_ddrc_perf_disable(priv);
+ platform_device_put(priv->pmu_pdev);
+
return dev_pm_opp_of_remove_table(dev);
}
static int imx_ddrc_probe(struct platform_device *pdev)
{
@@ -342,10 +460,22 @@ static int imx_ddrc_probe(struct platform_device *pdev)
priv->profile.get_dev_status = imx_ddrc_get_dev_status;
priv->profile.exit = imx_ddrc_exit;
priv->profile.get_cur_freq = imx_ddrc_get_cur_freq;
priv->profile.initial_freq = clk_get_rate(priv->dram_core);
+ /* Handle devfreq-events */
+ events_node = of_parse_phandle(dev->of_node, "devfreq-events", 0);
+ if (events_node) {
+ ret = imx_ddrc_init_events(dev, events_node);
+ of_node_put(events_node);
+ if (ret) {
+ dev_warn(dev, "failed to init perf events: %d\n", ret);
+ goto err;
+ }
+ gov = DEVFREQ_GOV_SIMPLE_ONDEMAND;
+ }
+
priv->devfreq = devm_devfreq_add_device(dev, &priv->profile,
gov, NULL);
if (IS_ERR(priv->devfreq)) {
ret = PTR_ERR(priv->devfreq);
dev_err(dev, "failed to add devfreq device: %d\n", ret);
@@ -353,10 +483,12 @@ static int imx_ddrc_probe(struct platform_device *pdev)
}
return 0;
err:
+ imx_ddrc_perf_disable(priv);
+ platform_device_put(priv->pmu_pdev);
dev_pm_opp_of_remove_table(dev);
return ret;
}
static const struct of_device_id imx_ddrc_of_match[] = {
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH 3/7] PM / devfreq: Add generic imx bus driver
From: Leonard Crestez @ 2019-08-12 18:49 UTC (permalink / raw)
To: Stephen Boyd, MyungJoo Ham, Kyungmin Park, Rob Herring
Cc: Dong Aisheng, Artur Świgoń, Abel Vesa, Saravana Kannan,
Anson Huang, linux-arm-kernel, Viresh Kumar, Michael Turquette,
linux-pm, linux-imx, Krzysztof Kozlowski, Chanwoo Choi,
Will Deacon, Alexandre Bailon, kernel, Fabio Estevam,
Mark Rutland, Shawn Guo, Georgi Djakov, devicetree, Jacky Bai
In-Reply-To: <cover.1565633880.git.leonard.crestez@nxp.com>
Add initial support for dynamic frequency switching on pieces of the imx
interconnect fabric.
All this driver actually does is set a clk rate based on an opp table.
No attempt is made to map registers or anything clever.
Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
---
drivers/devfreq/Kconfig | 12 +++
drivers/devfreq/Makefile | 1 +
drivers/devfreq/imx-devfreq.c | 148 ++++++++++++++++++++++++++++++++++
3 files changed, 161 insertions(+)
create mode 100644 drivers/devfreq/imx-devfreq.c
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index defe1d438710..9088a151bafe 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -90,10 +90,22 @@ config ARM_EXYNOS_BUS_DEVFREQ
Each memory bus group could contain many memoby bus block. It reads
PPMU counters of memory controllers by using DEVFREQ-event device
and adjusts the operating frequencies and voltages with OPP support.
This does not yet operate with optimal voltages.
+config ARM_IMX_DEVFREQ
+ tristate "i.MX DEVFREQ Driver"
+ depends on ARCH_MXC || COMPILE_TEST
+ select DEVFREQ_GOV_PASSIVE
+ select DEVFREQ_GOV_SIMPLE_ONDEMAND
+ select DEVFREQ_GOV_USERSPACE
+ select PM_OPP
+ help
+ This adds the DEVFREQ driver for the i.MX family of SoCs.
+ It allows adjusting frequencies for DDRC (DDR Controller) and various
+ NICs and NOCs which form the SOC interconnect fabric
+
config ARM_TEGRA_DEVFREQ
tristate "NVIDIA Tegra30/114/124/210 DEVFREQ Driver"
depends on ARCH_TEGRA_3x_SOC || ARCH_TEGRA_114_SOC || \
ARCH_TEGRA_132_SOC || ARCH_TEGRA_124_SOC || \
ARCH_TEGRA_210_SOC || \
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
index 338ae8440db6..c2463ed4c934 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -7,10 +7,11 @@ obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o
obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o
obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o
# DEVFREQ Drivers
obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o
+obj-$(CONFIG_ARM_IMX_DEVFREQ) += imx-devfreq.o
obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o
obj-$(CONFIG_ARM_TEGRA20_DEVFREQ) += tegra20-devfreq.o
# DEVFREQ Event Drivers
diff --git a/drivers/devfreq/imx-devfreq.c b/drivers/devfreq/imx-devfreq.c
new file mode 100644
index 000000000000..702a4368af7d
--- /dev/null
+++ b/drivers/devfreq/imx-devfreq.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 NXP
+ */
+
+#include <linux/clk.h>
+#include <linux/devfreq.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_opp.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct imx_devfreq {
+ struct devfreq_dev_profile profile;
+ struct devfreq *devfreq;
+ struct clk *clk;
+ struct devfreq_passive_data passive_data;
+};
+
+static int imx_devfreq_target(struct device *dev, unsigned long *freq, u32 flags)
+{
+ struct imx_devfreq *priv = dev_get_drvdata(dev);
+ struct dev_pm_opp *new_opp;
+ unsigned long new_freq;
+ int ret;
+
+ new_opp = devfreq_recommended_opp(dev, freq, flags);
+ if (IS_ERR(new_opp)) {
+ ret = PTR_ERR(new_opp);
+ dev_err(dev, "failed to get recommended opp: %d\n", ret);
+ return ret;
+ }
+ new_freq = dev_pm_opp_get_freq(new_opp);
+ dev_pm_opp_put(new_opp);
+
+ return clk_set_rate(priv->clk, new_freq);
+}
+
+static int imx_devfreq_get_cur_freq(struct device *dev, unsigned long *freq)
+{
+ struct imx_devfreq *priv = dev_get_drvdata(dev);
+
+ *freq = clk_get_rate(priv->clk);
+
+ return 0;
+}
+
+static int imx_devfreq_get_dev_status(struct device *dev,
+ struct devfreq_dev_status *stat)
+{
+ struct imx_devfreq *priv = dev_get_drvdata(dev);
+
+ stat->busy_time = 0;
+ stat->total_time = 0;
+ stat->current_frequency = clk_get_rate(priv->clk);
+
+ return 0;
+}
+
+static void imx_devfreq_exit(struct device *dev)
+{
+ return dev_pm_opp_of_remove_table(dev);
+}
+
+static int imx_devfreq_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imx_devfreq *priv;
+ const char *gov = DEVFREQ_GOV_USERSPACE;
+ void *govdata = NULL;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ dev_err(dev, "failed to fetch clk: %d\n", ret);
+ return ret;
+ }
+ platform_set_drvdata(pdev, priv);
+
+ ret = dev_pm_opp_of_add_table(dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to get OPP table\n");
+ return ret;
+ }
+
+ priv->profile.polling_ms = 1000;
+ priv->profile.target = imx_devfreq_target;
+ priv->profile.get_dev_status = imx_devfreq_get_dev_status;
+ priv->profile.exit = imx_devfreq_exit;
+ priv->profile.get_cur_freq = imx_devfreq_get_cur_freq;
+ priv->profile.initial_freq = clk_get_rate(priv->clk);
+
+ /* Handle passive devfreq parent link */
+ priv->passive_data.parent = devfreq_get_devfreq_by_phandle(dev, 0);
+ if (!IS_ERR(priv->passive_data.parent)) {
+ dev_info(dev, "passive link to %s\n",
+ dev_name(priv->passive_data.parent->dev.parent));
+ gov = DEVFREQ_GOV_PASSIVE;
+ govdata = &priv->passive_data;
+ } else if (priv->passive_data.parent != ERR_PTR(-ENODEV)) {
+ // -ENODEV means no parent: not an error.
+ ret = PTR_ERR(priv->passive_data.parent);
+ if (ret != -EPROBE_DEFER)
+ dev_warn(dev, "failed to get passive parent: %d\n", ret);
+ goto err;
+ }
+
+ priv->devfreq = devm_devfreq_add_device(dev, &priv->profile,
+ gov, govdata);
+ if (IS_ERR(priv->devfreq)) {
+ ret = PTR_ERR(priv->devfreq);
+ dev_err(dev, "failed to add devfreq device: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ dev_pm_opp_of_remove_table(dev);
+ return ret;
+}
+
+static const struct of_device_id imx_devfreq_of_match[] = {
+ { .compatible = "fsl,imx8m-noc", },
+ { .compatible = "fsl,imx8m-nic", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, imx_devfreq_of_match);
+
+static struct platform_driver imx_devfreq_platdrv = {
+ .probe = imx_devfreq_probe,
+ .driver = {
+ .name = "imx-devfreq",
+ .of_match_table = of_match_ptr(imx_devfreq_of_match),
+ },
+};
+module_platform_driver(imx_devfreq_platdrv);
+
+MODULE_DESCRIPTION("Generic i.MX bus frequency driver");
+MODULE_AUTHOR("Leonard Crestez <leonard.crestez@nxp.com>");
+MODULE_LICENSE("GPL v2");
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH 4/7] dt-bindings: devfreq: Add bindings for imx ddr controller
From: Leonard Crestez @ 2019-08-12 18:49 UTC (permalink / raw)
To: Stephen Boyd, MyungJoo Ham, Kyungmin Park, Rob Herring
Cc: Dong Aisheng, Artur Świgoń, Abel Vesa, Saravana Kannan,
Anson Huang, linux-arm-kernel, Viresh Kumar, Michael Turquette,
linux-pm, linux-imx, Krzysztof Kozlowski, Chanwoo Choi,
Will Deacon, Alexandre Bailon, kernel, Fabio Estevam,
Mark Rutland, Shawn Guo, Georgi Djakov, devicetree, Jacky Bai
In-Reply-To: <cover.1565633880.git.leonard.crestez@nxp.com>
Add devicetree bindings for the i.MX DDR Controller on imx8m series
chips. It supports dynamic frequency switching between multiple data
rates and this is exposed to Linux via the devfreq subsystem.
Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
---
.../devicetree/bindings/devfreq/imx-ddrc.yaml | 53 +++++++++++++++++++
1 file changed, 53 insertions(+)
create mode 100644 Documentation/devicetree/bindings/devfreq/imx-ddrc.yaml
diff --git a/Documentation/devicetree/bindings/devfreq/imx-ddrc.yaml b/Documentation/devicetree/bindings/devfreq/imx-ddrc.yaml
new file mode 100644
index 000000000000..fa20280a682f
--- /dev/null
+++ b/Documentation/devicetree/bindings/devfreq/imx-ddrc.yaml
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/devfreq/imx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: i.MX DDR Controller
+
+maintainers:
+ - Leonard Crestez <leonard.crestez@nxp.com>
+
+properties:
+ compatible:
+ contains:
+ enum:
+ - fsl,imx8m-ddrc
+
+ reg:
+ maxItems: 1
+ description: DDR Controller registers
+
+ clocks:
+ minItems: 5
+ maxItems: 5
+
+ clock-names:
+ items:
+ - const: dram_core
+ - const: dram_pll
+ - const: dram_alt_root
+ - const: dram_alt
+ - const: dram_apb
+
+required:
+ - reg
+ - compatible
+ - clocks
+ - clock-names
+
+examples:
+ - |
+ #include <dt-bindings/clock/imx8mm-clock.h>
+ ddrc: dram-controller@3d400000 {
+ compatible = "fsl,imx8mm-ddrc", "fsl,imx8m-ddrc";
+ reg = <0x3d400000 0x400000>;
+ clock-names = "dram_core", "dram_pll", "dram_alt_root", "dram_alt", "dram_apb";
+ clocks = <&clk IMX8MM_CLK_DRAM_CORE>,
+ <&clk IMX8MM_DRAM_PLL>,
+ <&clk IMX8MM_CLK_DRAM_ALT_ROOT>,
+ <&clk IMX8MM_CLK_DRAM_ALT>,
+ <&clk IMX8MM_CLK_DRAM_APB>;
+ operating-points-v2 = <&ddrc_opp_table>;
+ };
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH 2/7] dt-bindings: devfreq: Add bindings for generic imx buses
From: Leonard Crestez @ 2019-08-12 18:49 UTC (permalink / raw)
To: Stephen Boyd, MyungJoo Ham, Kyungmin Park, Rob Herring
Cc: Dong Aisheng, Artur Świgoń, Abel Vesa, Saravana Kannan,
Anson Huang, linux-arm-kernel, Viresh Kumar, Michael Turquette,
linux-pm, linux-imx, Krzysztof Kozlowski, Chanwoo Choi,
Will Deacon, Alexandre Bailon, kernel, Fabio Estevam,
Mark Rutland, Shawn Guo, Georgi Djakov, devicetree, Jacky Bai
In-Reply-To: <cover.1565633880.git.leonard.crestez@nxp.com>
Add initial dt bindings for the interconnects inside i.MX chips.
Multiple external IPs are involved but SOC integration means the
software controllable interfaces are very similar.
This is initially only for imx8mm but add an "fsl,imx8m-nic" fallback
similar to exynos-bus.
Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
---
.../devicetree/bindings/devfreq/imx.yaml | 50 +++++++++++++++++++
1 file changed, 50 insertions(+)
create mode 100644 Documentation/devicetree/bindings/devfreq/imx.yaml
diff --git a/Documentation/devicetree/bindings/devfreq/imx.yaml b/Documentation/devicetree/bindings/devfreq/imx.yaml
new file mode 100644
index 000000000000..0e2ee3a5205e
--- /dev/null
+++ b/Documentation/devicetree/bindings/devfreq/imx.yaml
@@ -0,0 +1,50 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/devfreq/imx.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Generic i.MX bus frequency device
+
+maintainers:
+ - Leonard Crestez <leonard.crestez@nxp.com>
+
+description: |
+ The i.MX SoC family has multiple buses for which clock frequency (and sometimes
+ voltage) can be adjusted.
+
+ Some of those buses expose register areas mentioned in the memory maps as GPV
+ ("Global Programmers View") but not all. Access to this area might be denied for
+ normal world.
+
+ The buses are based on externally licensed IPs such as ARM NIC-301 and Arteris
+ FlexNOC but DT bindings are specific to the integration of these bus
+ interconnect IPs into imx SOCs.
+
+properties:
+ compatible:
+ contains:
+ enum:
+ - fsl,imx8m-noc
+ - fsl,imx8m-nic
+
+ reg:
+ maxItems: 1
+ description: GPV area
+
+ clocks:
+ maxItems: 1
+
+required:
+ - compatible
+ - clocks
+
+examples:
+ - |
+ #include <dt-bindings/clock/imx8mm-clock.h>
+ noc: noc@32700000 {
+ compatible = "fsl,imx8mm-noc", "fsl,imx8m-noc";
+ reg = <0x32700000 0x100000>;
+ clocks = <&clk IMX8MM_CLK_NOC>;
+ operating-points-v2 = <&noc_opp_table>;
+ };
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH 1/7] clk: imx8m: Set CLK_GET_RATE_NOCACHE on dram_alt/apb
From: Leonard Crestez @ 2019-08-12 18:49 UTC (permalink / raw)
To: Stephen Boyd, MyungJoo Ham, Kyungmin Park, Rob Herring
Cc: Dong Aisheng, Artur Świgoń, Abel Vesa, Saravana Kannan,
Anson Huang, linux-arm-kernel, Viresh Kumar, Michael Turquette,
linux-pm, linux-imx, Krzysztof Kozlowski, Chanwoo Choi,
Will Deacon, Alexandre Bailon, kernel, Fabio Estevam,
Mark Rutland, Shawn Guo, Georgi Djakov, devicetree, Jacky Bai
In-Reply-To: <cover.1565633880.git.leonard.crestez@nxp.com>
Dram frequency changes required modifying these clocks outside the
control of clk framework. Mark them as CLK_GET_RATE_NOCACHE so that
rates are always read back from registers.
Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
---
drivers/clk/imx/clk-imx8mm.c | 6 ++++--
drivers/clk/imx/clk-imx8mn.c | 6 ++++--
drivers/clk/imx/clk-imx8mq.c | 7 ++++---
3 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c
index 1a944837f638..0417fa3c8e71 100644
--- a/drivers/clk/imx/clk-imx8mm.c
+++ b/drivers/clk/imx/clk-imx8mm.c
@@ -526,12 +526,14 @@ static int imx8mm_clocks_probe(struct platform_device *pdev)
/* IPG */
clks[IMX8MM_CLK_IPG_ROOT] = imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1);
clks[IMX8MM_CLK_IPG_AUDIO_ROOT] = imx_clk_divider2("ipg_audio_root", "audio_ahb", base + 0x9180, 0, 1);
/* IP */
- clks[IMX8MM_CLK_DRAM_ALT] = imx8m_clk_composite("dram_alt", imx8mm_dram_alt_sels, base + 0xa000);
- clks[IMX8MM_CLK_DRAM_APB] = imx8m_clk_composite_critical("dram_apb", imx8mm_dram_apb_sels, base + 0xa080);
+ clks[IMX8MM_CLK_DRAM_ALT] = __imx8m_clk_composite("dram_alt", imx8mm_dram_alt_sels, base + 0xa000,
+ CLK_GET_RATE_NOCACHE);
+ clks[IMX8MM_CLK_DRAM_APB] = __imx8m_clk_composite("dram_apb", imx8mm_dram_apb_sels, base + 0xa080,
+ CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE);
clks[IMX8MM_CLK_VPU_G1] = imx8m_clk_composite("vpu_g1", imx8mm_vpu_g1_sels, base + 0xa100);
clks[IMX8MM_CLK_VPU_G2] = imx8m_clk_composite("vpu_g2", imx8mm_vpu_g2_sels, base + 0xa180);
clks[IMX8MM_CLK_DISP_DTRC] = imx8m_clk_composite("disp_dtrc", imx8mm_disp_dtrc_sels, base + 0xa200);
clks[IMX8MM_CLK_DISP_DC8000] = imx8m_clk_composite("disp_dc8000", imx8mm_disp_dc8000_sels, base + 0xa280);
clks[IMX8MM_CLK_PCIE1_CTRL] = imx8m_clk_composite("pcie1_ctrl", imx8mm_pcie1_ctrl_sels, base + 0xa300);
diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c
index ecd1062f6847..1dda3e6c3019 100644
--- a/drivers/clk/imx/clk-imx8mn.c
+++ b/drivers/clk/imx/clk-imx8mn.c
@@ -497,12 +497,14 @@ static int imx8mn_clocks_probe(struct platform_device *pdev)
clks[IMX8MN_CLK_AHB] = imx8m_clk_composite_critical("ahb", imx8mn_ahb_sels, base + 0x9000);
clks[IMX8MN_CLK_AUDIO_AHB] = imx8m_clk_composite("audio_ahb", imx8mn_audio_ahb_sels, base + 0x9100);
clks[IMX8MN_CLK_IPG_ROOT] = imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1);
clks[IMX8MN_CLK_IPG_AUDIO_ROOT] = imx_clk_divider2("ipg_audio_root", "audio_ahb", base + 0x9180, 0, 1);
clks[IMX8MN_CLK_DRAM_CORE] = imx_clk_mux2_flags("dram_core_clk", base + 0x9800, 24, 1, imx8mn_dram_core_sels, ARRAY_SIZE(imx8mn_dram_core_sels), CLK_IS_CRITICAL);
- clks[IMX8MN_CLK_DRAM_ALT] = imx8m_clk_composite("dram_alt", imx8mn_dram_alt_sels, base + 0xa000);
- clks[IMX8MN_CLK_DRAM_APB] = imx8m_clk_composite_critical("dram_apb", imx8mn_dram_apb_sels, base + 0xa080);
+ clks[IMX8MN_CLK_DRAM_ALT] = __imx8m_clk_composite("dram_alt", imx8mn_dram_alt_sels, base + 0xa000,
+ CLK_GET_RATE_NOCACHE);
+ clks[IMX8MN_CLK_DRAM_APB] = __imx8m_clk_composite("dram_apb", imx8mn_dram_apb_sels, base + 0xa080,
+ CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE);
clks[IMX8MN_CLK_DISP_PIXEL] = imx8m_clk_composite("disp_pixel", imx8mn_disp_pixel_sels, base + 0xa500);
clks[IMX8MN_CLK_SAI2] = imx8m_clk_composite("sai2", imx8mn_sai2_sels, base + 0xa600);
clks[IMX8MN_CLK_SAI3] = imx8m_clk_composite("sai3", imx8mn_sai3_sels, base + 0xa680);
clks[IMX8MN_CLK_SAI5] = imx8m_clk_composite("sai5", imx8mn_sai5_sels, base + 0xa780);
clks[IMX8MN_CLK_SAI6] = imx8m_clk_composite("sai6", imx8mn_sai6_sels, base + 0xa800);
diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c
index e451e68eaa84..f18206d57020 100644
--- a/drivers/clk/imx/clk-imx8mq.c
+++ b/drivers/clk/imx/clk-imx8mq.c
@@ -436,13 +436,14 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
clks[IMX8MQ_CLK_IPG_ROOT] = imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1);
clks[IMX8MQ_CLK_IPG_AUDIO_ROOT] = imx_clk_divider2("ipg_audio_root", "audio_ahb", base + 0x9180, 0, 1);
/* IP */
clks[IMX8MQ_CLK_DRAM_CORE] = imx_clk_mux2_flags("dram_core_clk", base + 0x9800, 24, 1, imx8mq_dram_core_sels, ARRAY_SIZE(imx8mq_dram_core_sels), CLK_IS_CRITICAL);
-
- clks[IMX8MQ_CLK_DRAM_ALT] = imx8m_clk_composite("dram_alt", imx8mq_dram_alt_sels, base + 0xa000);
- clks[IMX8MQ_CLK_DRAM_APB] = imx8m_clk_composite_critical("dram_apb", imx8mq_dram_apb_sels, base + 0xa080);
+ clks[IMX8MQ_CLK_DRAM_ALT] = __imx8m_clk_composite("dram_alt", imx8mq_dram_alt_sels, base + 0xa000,
+ CLK_GET_RATE_NOCACHE);
+ clks[IMX8MQ_CLK_DRAM_APB] = __imx8m_clk_composite("dram_apb", imx8mq_dram_apb_sels, base + 0xa080,
+ CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE);
clks[IMX8MQ_CLK_VPU_G1] = imx8m_clk_composite("vpu_g1", imx8mq_vpu_g1_sels, base + 0xa100);
clks[IMX8MQ_CLK_VPU_G2] = imx8m_clk_composite("vpu_g2", imx8mq_vpu_g2_sels, base + 0xa180);
clks[IMX8MQ_CLK_DISP_DTRC] = imx8m_clk_composite("disp_dtrc", imx8mq_disp_dtrc_sels, base + 0xa200);
clks[IMX8MQ_CLK_DISP_DC8000] = imx8m_clk_composite("disp_dc8000", imx8mq_disp_dc8000_sels, base + 0xa280);
clks[IMX8MQ_CLK_PCIE1_CTRL] = imx8m_clk_composite("pcie1_ctrl", imx8mq_pcie1_ctrl_sels, base + 0xa300);
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH 0/7] PM / devfreq: Add initial imx support
From: Leonard Crestez @ 2019-08-12 18:49 UTC (permalink / raw)
To: Stephen Boyd, MyungJoo Ham, Kyungmin Park, Rob Herring
Cc: Dong Aisheng, Artur Świgoń, Abel Vesa, Saravana Kannan,
Anson Huang, linux-arm-kernel, Viresh Kumar, Michael Turquette,
linux-pm, linux-imx, Krzysztof Kozlowski, Chanwoo Choi,
Will Deacon, Alexandre Bailon, kernel, Fabio Estevam,
Mark Rutland, Shawn Guo, Georgi Djakov, devicetree, Jacky Bai
This adds devfreq support for imx8mm, covering dynamic scaling of
internal NOC and DDR Controller
Scaling for simple busses (NIC/NOC) is done through the clk framework
while DRAM scaling is performed in firmware with an "imx-ddrc" wrapper
for devfreq.
Changes since RFCv3:
* Implement passive support and set NOC parent to DDRC
* Drop scaling AHB/AXI for now (NOC/DDRC use most power anyway)
* Split into two devreq drivers (and bindings) because the ddrc is
really a distinct piece of hardware.
* Stop relying on clk_min_rate
* Fetch info about dram clk parents from firmware instead of
hardcoding in driver. This can more easily support additional rates.
* Perform DRAM frequency inside devfreq instead of clk, mostly due to
objections to earlier RFCs for "imx8m-dram-clk"
* Link: https://patchwork.kernel.org/cover/11056779/
* Link: https://patchwork.kernel.org/patch/11049429/
Stephen: It would be very helpful if you could comment on the dram
frequency switching code. I moved it outside of clk but now I have to
use provider APIs outside of drivers/clk for parent manipulation. Few
other drivers do that so maybe it's OK?
Scaling buses can cause problems for devices with realtime bandwith
requirements such as display, the intention is to use the interconnect
framework to make DEV_PM_QOS_MIN_FREQUENCY to devfreq. There are
separate patches for that:
* https://patchwork.kernel.org/patch/11084279/
* https://patchwork.kernel.org/cover/11078671/
Leonard Crestez (7):
clk: imx8m: Set CLK_GET_RATE_NOCACHE on dram_alt/apb
dt-bindings: devfreq: Add bindings for generic imx buses
PM / devfreq: Add generic imx bus driver
dt-bindings: devfreq: Add bindings for imx ddr controller
PM / devfreq: Add dynamic scaling for imx ddr controller
PM / devfreq: imx-ddrc: Measure bandwidth with perf
arm64: dts: imx8mm: Add devfreq nodes
.../devicetree/bindings/devfreq/imx-ddrc.yaml | 53 ++
.../devicetree/bindings/devfreq/imx.yaml | 50 ++
arch/arm64/boot/dts/freescale/imx8mm.dtsi | 51 +-
drivers/clk/imx/clk-imx8mm.c | 6 +-
drivers/clk/imx/clk-imx8mn.c | 6 +-
drivers/clk/imx/clk-imx8mq.c | 7 +-
drivers/devfreq/Kconfig | 12 +
drivers/devfreq/Makefile | 1 +
drivers/devfreq/imx-ddrc.c | 511 ++++++++++++++++++
drivers/devfreq/imx-devfreq.c | 148 +++++
10 files changed, 837 insertions(+), 8 deletions(-)
create mode 100644 Documentation/devicetree/bindings/devfreq/imx-ddrc.yaml
create mode 100644 Documentation/devicetree/bindings/devfreq/imx.yaml
create mode 100644 drivers/devfreq/imx-ddrc.c
create mode 100644 drivers/devfreq/imx-devfreq.c
--
2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH] arm64: dts: allwinner: a64: Drop PMU node
From: Harald Geyer @ 2019-08-12 18:22 UTC (permalink / raw)
To: Vasily Khoruzhick
Cc: Mark Rutland, devicetree, Jared D . McNeill, Maxime Ripard,
Chen-Yu Tsai, Rob Herring, Robin Murphy, arm-linux
In-Reply-To: <CA+E=qVchsqOF_hVD-qBuKwi7PTMYtUR-LE2dD_mpptFJcWE_yw@mail.gmail.com>
Vasily Khoruzhick writes:
> OK, so what should we do? 'perf top'/'perf record' work fine if PMU
> node is dropped, but they don't work if PMU node is present (even with
> interrupts dropped).
Really? Even if you tell it to only listen to software events? (Which
is the only thing you get without a PMU anyway, I believe.)
> I'd prefer to have 'perf top' and 'perf record'
> working instead of 'perf stat'
I think, if a broken PMU confuses 'perf top' beyond usability, it
should be fixed.
Harald
--
Nationalratswahl: Ich trete als unabhängiger Experte für Klimaschutz
und freie Software an! Ich will mit Vorzugsstimmen ins Parlament kommen,
weil wenn ich es nicht mache, kümmert sich womöglich niemand darum.
https://haraldgeyer.at
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH] arm64: dts: allwinner: a64: Drop PMU node
From: Vasily Khoruzhick @ 2019-08-12 18:01 UTC (permalink / raw)
To: Maxime Ripard
Cc: Mark Rutland, devicetree, Jared D . McNeill, Chen-Yu Tsai,
Rob Herring, Harald Geyer, Robin Murphy, arm-linux
In-Reply-To: <20190812080420.saelmqb36vkelxn4@flea>
On Mon, Aug 12, 2019 at 1:04 AM Maxime Ripard <maxime.ripard@bootlin.com> wrote:
>
> On Thu, Aug 08, 2019 at 12:59:07PM -0700, Vasily Khoruzhick wrote:
> > On Thu, Aug 8, 2019 at 9:26 AM Maxime Ripard <maxime.ripard@bootlin.com> wrote:
> > >
> > > On Wed, Aug 07, 2019 at 10:36:08AM -0700, Vasily Khoruzhick wrote:
> > > > On Wed, Aug 7, 2019 at 4:56 AM Maxime Ripard <maxime.ripard@bootlin.com> wrote:
> > > > >
> > > > > On Tue, Aug 06, 2019 at 07:39:26PM -0700, Vasily Khoruzhick wrote:
> > > > > > On Tue, Aug 6, 2019 at 2:14 PM Robin Murphy <robin.murphy@arm.com> wrote:
> > > > > > >
> > > > > > > On 2019-08-06 9:52 pm, Vasily Khoruzhick wrote:
> > > > > > > > On Tue, Aug 6, 2019 at 1:19 PM Harald Geyer <harald@ccbib.org> wrote:
> > > > > > > >>
> > > > > > > >> Vasily Khoruzhick writes:
> > > > > > > >>> On Tue, Aug 6, 2019 at 7:35 AM Robin Murphy <robin.murphy@arm.com> wrote:
> > > > > > > >>>>
> > > > > > > >>>> On 06/08/2019 15:01, Vasily Khoruzhick wrote:
> > > > > > > >>>>> Looks like PMU in A64 is broken, it generates no interrupts at all and
> > > > > > > >>>>> as result 'perf top' shows no events.
> > > > > > > >>>>
> > > > > > > >>>> Does something like 'perf stat sleep 1' at least count cycles correctly?
> > > > > > > >>>> It could well just be that the interrupt numbers are wrong...
> > > > > > > >>>
> > > > > > > >>> Looks like it does, at least result looks plausible:
> > > > > > > >>
> > > > > > > >> I'm using perf stat regularly (cache benchmarks) and it works fine.
> > > > > > > >>
> > > > > > > >> Unfortunately I wasn't aware that perf stat is a poor test for
> > > > > > > >> the interrupts part of the node, when I added it. So I'm not too
> > > > > > > >> surprised I got it wrong.
> > > > > > > >>
> > > > > > > >> However, it would be unfortunate if the node got removed completely,
> > > > > > > >> because perf stat would not work anymore. Maybe we can only remove
> > > > > > > >> the interrupts or just fix them even if the HW doesn't work?
> > > > > > > >
> > > > > > > > I'm not familiar with PMU driver. Is it possible to get it working
> > > > > > > > without interrupts?
> > > > > > >
> > > > > > > Yup - you get a grumpy message from the driver, it will refuse sampling
> > > > > > > events (the ones which weren't working anyway), and if you measure
> > > > > > > anything for long enough that a counter overflows you'll get wonky
> > > > > > > results. But for counting hardware events over relatively short periods
> > > > > > > it'll still do the job.
> > > > > >
> > > > > > I tried to drop interrupts completely from the node but 'perf top' is
> > > > > > still broken. Though now in different way: it complains "cycles: PMU
> > > > > > Hardware doesn't support sampling/overflow-interrupts. Try 'perf
> > > > > > stat'"
> > > > >
> > > > > I have no idea if that's the culprit, but what is the state of the
> > > > > 0x09010000 register?
> > > >
> > > > What register is that and how do I check it?
> > >
> > > It's in the CPUX Configuration block, and the bits are labelled as CPU
> > > Debug Reset.
> > >
> > > And if you have busybox, you can use devmem.
> >
> > CPUX configuration block is at 0x01700000 according to A64 user
> > manual, and particular register you're interested in is at 0x01700080,
> > its value is 0x1110110F.
> >
> > Bits 16-19 are not defined in user manual and are not set.
>
> Sorry, I somehow thought this was for the H6...
>
> I don't have any idea then :/
OK, so what should we do? 'perf top'/'perf record' work fine if PMU
node is dropped, but they don't work if PMU node is present (even with
interrupts dropped). I'd prefer to have 'perf top' and 'perf record'
working instead of 'perf stat'
> Maxime
>
> --
> Maxime Ripard, Bootlin
> Embedded Linux and Kernel engineering
> https://bootlin.com
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH 1/1] ARM: dts: meson8b: odroidc1: use the MAC address stored in the eFuse
From: Martin Blumenstingl @ 2019-08-12 17:50 UTC (permalink / raw)
To: linux-amlogic, khilman
Cc: narmstrong, ottuzzi, linux-kernel, Martin Blumenstingl,
linux-arm-kernel
In-Reply-To: <20190812175004.24943-1-martin.blumenstingl@googlemail.com>
Odroid-C1 uses the MAC address stored in eFuse at offset 0x1b4 (which is
defined as a "standard" offset for all Meson8 and Meson8b boards, but
testing shows that MXQ doesn't have the eFuse values programmed and
EC-100 stores it's MAC address in eMMC).
Add the nvmem cell which points to the MAC address and asssign it to the
Ethernet controller as "mac-address".
As result of this the MAC address which is stored in the eFuse is now
assigned to the Ethernet controller and consistent across reboots.
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
arch/arm/boot/dts/meson8b-odroidc1.dts | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm/boot/dts/meson8b-odroidc1.dts b/arch/arm/boot/dts/meson8b-odroidc1.dts
index 90f66dc45115..a24eccc354b9 100644
--- a/arch/arm/boot/dts/meson8b-odroidc1.dts
+++ b/arch/arm/boot/dts/meson8b-odroidc1.dts
@@ -190,6 +190,12 @@
cpu-supply = <&vcck>;
};
+&efuse {
+ ethernet_mac_address: mac@1b4 {
+ reg = <0x1b4 0x6>;
+ };
+};
+
ðmac {
status = "okay";
@@ -200,6 +206,9 @@
phy-handle = <ð_phy>;
amlogic,tx-delay-ns = <4>;
+ nvmem-cells = <ðernet_mac_address>;
+ nvmem-cell-names = "mac-address";
+
mdio {
compatible = "snps,dwmac-mdio";
#address-cells = <1>;
--
2.22.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related
* [PATCH 0/1] ARM: dts: meson8b: persistent MAC address for Odroid-C1
From: Martin Blumenstingl @ 2019-08-12 17:50 UTC (permalink / raw)
To: linux-amlogic, khilman
Cc: narmstrong, ottuzzi, linux-kernel, Martin Blumenstingl,
linux-arm-kernel
This series makes Odroid-C1 use the MAC address which is programmed into
the eFuse.
build-time dependencies:
none
runtime dependencies (without these a random MAC address is assigned,
just like before these patches), both are already part of -next:
- "nvmem: meson-mx-efuse: allow reading data smaller than word_size"
from [1]
- "net: stmmac: manage errors returned by of_get_mac_address()" from [1]
Changes since v1 at [2]:
- only add the nvmem cell to meson8b-odroidc1.dts as suggested by Neil.
It turns out that neither MXQ and EC-100 have the MAC address in eFuse
(which means only 1/3 boards has it at the given eFuse offset, so it's
not worth having it the common .dtsi)
Kevin: you already have v1 of this series in your tree. Feel free to
replace the two patches from v1 with this single one.
[0] https://patchwork.kernel.org/patch/11062659/
[1] https://patchwork.kernel.org/patch/11062657/
[2] https://patchwork.kernel.org/cover/11062663/
Martin Blumenstingl (1):
ARM: dts: meson8b: odroidc1: use the MAC address stored in the eFuse
arch/arm/boot/dts/meson8b-odroidc1.dts | 9 +++++++++
1 file changed, 9 insertions(+)
--
2.22.0
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: next/master boot: 254 boots: 11 failed, 227 passed with 16 offline (v5.3-rc4-5755-g8e72ac275c63)
From: Mark Brown @ 2019-08-12 17:37 UTC (permalink / raw)
To: Bjorn Andersson, Andy Gross
Cc: linux-arm-msm, linux-next, linux-arm-kernel, kernel-build-reports
In-Reply-To: <5d519859.1c69fb81.531c0.088a@mx.google.com>
[-- Attachment #1.1: Type: text/plain, Size: 833 bytes --]
On Mon, Aug 12, 2019 at 09:48:25AM -0700, kernelci.org bot wrote:
Today's -next fails to boot on qcs404-evb-4k:
> arm64:
> defconfig:
> gcc-8:
The boot appears to suddenly drop into a bootloader near the end of
boot:
05:14:44.500307 [ 1.886858] Key type dns_resolver registered
05:14:44.500345 [ 1.892544] registered taskstats version 1
05:14:44.500382 [ 1.895502] Loading compiled-in X.509 certificates
05:14:44.515903 [ 1.916067] hctosys: unable to open rtc device (rtc0)
05:14:51.562124
05:14:51.562251 Format: Log Type - Time(microsec) - Message - Optional Info
05:14:51.562306 Log Type: B - Since Boot(Power On Reset), D - Delta, S - Statistic
05:14:51.562383 S - QC_IMAGE_VERSION_STRING=BOOT.XF.0.1-00080-QCS405LZB-2
05:14:51.562427 S - IMAGE_VARIANT_STRING=Qcs405LA
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
[-- Attachment #2: Type: text/plain, Size: 176 bytes --]
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v7 1/2] arm64: Define Documentation/arm64/tagged-address-abi.rst
From: Catalin Marinas @ 2019-08-12 17:36 UTC (permalink / raw)
To: Dave Hansen
Cc: linux-arch, linux-doc, Szabolcs Nagy, Andrey Konovalov,
Kevin Brodsky, Will Deacon, Vincenzo Frascino, Dave P Martin,
linux-arm-kernel
In-Reply-To: <68354acd-e205-71cb-11c6-74a150178ae0@intel.com>
On Fri, Aug 09, 2019 at 07:10:18AM -0700, Dave Hansen wrote:
> On 8/8/19 10:27 AM, Catalin Marinas wrote:
> > On Wed, Aug 07, 2019 at 01:38:16PM -0700, Dave Hansen wrote:
> >> Also, shouldn't this be converted over to an arch_prctl()?
> >
> > What do you mean by arch_prctl()? We don't have such thing, apart from
> > maybe arch_prctl_spec_ctrl_*(). We achieve the same thing with the
> > {SET,GET}_TAGGED_ADDR_CTRL macros. They could be renamed to
> > arch_prctl_tagged_addr_{set,get} or something but I don't see much
> > point.
>
> Silly me. We have an x86-specific:
>
> SYSCALL_DEFINE2(arch_prctl, int , option, unsigned long , arg2)
>
> I guess there's no ARM equivalent so you're stuck with the generic one.
>
> > What would be better (for a separate patch series) is to clean up
> > sys_prctl() and move the arch-specific options into separate
> > arch_prctl() under arch/*/kernel/. But it's not really for this series.
>
> I think it does make sense for truly arch-specific features to stay out
> of the arch-generic prctl(). Yes, I know I've personally violated this
> in the past. :)
Maybe Dave M could revive his prctl() clean-up patches which moves the
arch specific cases to the corresponding arch/*/ code
> >> What is the scope of these prctl()'s? Are they thread-scoped or
> >> process-scoped? Can two threads in the same process run with different
> >> tagging ABI modes?
> >
> > Good point. They are thread-scoped and this should be made clear in the
> > doc. Two threads can have different modes.
> >
> > The expectation is that this is invoked early during process start (by
> > the dynamic loader or libc init) while in single-thread mode and
> > subsequent threads will inherit the same mode. However, other uses are
> > possible.
>
> If that's the expectation, it would be really nice to codify it.
> Basically, you can't enable the feature if another thread is already
> been forked off.
Well, that's my expectation but I'm not a userspace developer. I don't
think there is any good reason to prevent it. It doesn't cost us
anything to support in the kernel, other than making the documentation
clearer.
> >>> +When a process has successfully enabled the new ABI by invoking
> >>> +prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE), the following
> >>> +behaviours are guaranteed:
> >>> +
> >>> +- Every currently available syscall, except the cases mentioned in section
> >>> + 3, can accept any valid tagged pointer. The same rule is applicable to
> >>> + any syscall introduced in the future.
> >>> +
> >>> +- The syscall behaviour is undefined for non valid tagged pointers.
> >>
> >> Do you really mean "undefined"? I mean, a bad pointer is a bad pointer.
> >> Why should it matter if it's a tagged bad pointer or an untagged bad
> >> pointer?
> >
> > Szabolcs already replied here. We may have tagged pointers that can be
> > dereferenced just fine but being passed to the kernel may not be well
> > defined (e.g. some driver doing a find_vma() that fails unless it
> > explicitly untags the address). It's as undefined as the current
> > behaviour (without these patches) guarantees.
>
> It might just be nicer to point out what this features *changes* about
> invalid pointer handling, which is nothing. :) Maybe:
>
> The syscall behaviour for invalid pointers is the same for both
> tagged and untagged pointers.
Good point.
> >>> +- prctl(PR_SET_MM, ``*``, ...) other than arg2 PR_SET_MM_MAP and
> >>> + PR_SET_MM_MAP_SIZE.
> >>> +
> >>> +- prctl(PR_SET_MM, PR_SET_MM_MAP{,_SIZE}, ...) struct prctl_mm_map fields.
> >>> +
> >>> +Any attempt to use non-zero tagged pointers will lead to undefined
> >>> +behaviour.
> >>
> >> I wonder if you want to generalize this a bit. I think you're saying
> >> that parts of the ABI that modify the *layout* of the address space
> >> never accept tagged pointers.
> >
> > I guess our difficulty in specifying this may have been caused by
> > over-generalising. For example, madvise/mprotect came under the same
> > category but there is a use-case for malloc'ed pointers (and tagged) to
> > the kernel (e.g. MADV_DONTNEED). If we can restrict the meaning to
> > address space *layout* manipulation, we'd have mmap/mremap/munmap,
> > brk/sbrk, prctl(PR_SET_MM). Did I miss anything?. Other related syscalls
> > like mprotect/madvise preserve the layout while only changing permissions,
> > backing store, so the would be allowed to accept tags.
>
> shmat() comes to mind. I also did a quick grep for mmap_sem taken for
> write and didn't see anything else obvious jump out at me.
I'll document shmat() as not supported, together with the prctl().
As I submitted a fixup already, I propose that we allow tagged pointers
on mmap/munmap/mremap/brk. It makes the documentation simpler ;) (and
the user understanding of what is and is not allowed).
> >> It looks like the TAG_SHIFT and tag size are pretty baked into the
> >> aarch64 architecture. But, are you confident that no future
> >> implementations will want different positions or sizes? (obviously
> >> controlled by other TCR_EL1 bits)
> >
> > For the top-byte-ignore (TBI), that's been baked in the architecture
> > since ARMv8.0 and we'll have to keep the backwards compatible mode. As
> > the name implies, it's the top byte of the address and that's what the
> > document above refers to.
> >
> > With MTE, I can't exclude other configurations in the future but I'd
> > expect the kernel to present the option as a new HWCAP and the user to
> > explicitly opt in via a new prctl() flag. I seriously doubt we'd break
> > existing binaries. So, yes TAG_SHIFT may be different but so would the
> > prctl() above.
>
> Basically, what you have is a "turn tagging on" and "turn tagging off"
> call which are binary: all on or all off. How about exposing a mask:
>
> /* Replace hard-coded mask size/position: */
> unsigned long mask = prctl(GET_POSSIBLE_TAGGED_ADDR_BITS);
>
> if (mask == 0)
> // no tagging is supported obviously
>
> prctl(SET_TAGGED_ADDR_BITS, mask);
>
> // now userspace knows via 'mask' where the tag bits are
For the actual hardware memory tagging, maybe we could get the possible
bits but for TBI, as I said above, that's baked into the architecture. I
don't think it's worth the effort of getting a mask as I don't see ARM
changing this without breaking existing software. Even compiler support
like hwasan relies on the 8-bit TBI.
--
Catalin
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v1 1/1] i2c: iproc: Add i2c repeated start capability
From: Ray Jui @ 2019-08-12 17:33 UTC (permalink / raw)
To: Rayagonda Kokatanur, Wolfram Sang, Rob Herring, Mark Rutland
Cc: devicetree, Lori Hikichi, Florian Fainelli, Shivaraj Shetty,
linux-kernel, Icarus Chau, bcm-kernel-feedback-list, linux-i2c,
linux-arm-kernel
In-Reply-To: <1565150941-27297-1-git-send-email-rayagonda.kokatanur@broadcom.com>
Hi Wolfram,
On 8/6/19 9:09 PM, Rayagonda Kokatanur wrote:
> From: Lori Hikichi <lori.hikichi@broadcom.com>
>
> Enable handling of i2c repeated start. The current code
> handles a multi msg i2c transfer as separate i2c bus
> transactions. This change will now handle this case
> using the i2c repeated start protocol. The number of msgs
> in a transfer is limited to two, and must be a write
> followed by a read.
>
> Signed-off-by: Lori Hikichi <lori.hikichi@broadcom.com>
> Signed-off-by: Rayagonda Kokatanur <rayagonda.kokatanur@broadcom.com>
> Signed-off-by: Icarus Chau <icarus.chau@broadcom.com>
> Signed-off-by: Ray Jui <ray.jui@broadcom.com>
> Signed-off-by: Shivaraj Shetty <sshetty1@broadcom.com>
> ---
Note this patch has gone through internal review and testing on various
I2C slave devices. It is introduced to work around limitation of our I2C
controller and allows it to work on certain I2C slave devices that are
sensitive and requires repeated start between transactions instead of a
stop.
Given that my name is also on the Signed-off-by since I helped to
rewrite part of the patch, I'm not going to add my Reviewed-by tag here.
Please help to review.
Thanks,
Ray
> drivers/i2c/busses/i2c-bcm-iproc.c | 70 +++++++++++++++++++++++++++++++-------
> 1 file changed, 57 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c
> index d7fd76b..15fedcf 100644
> --- a/drivers/i2c/busses/i2c-bcm-iproc.c
> +++ b/drivers/i2c/busses/i2c-bcm-iproc.c
> @@ -81,6 +81,7 @@
> #define M_CMD_PROTOCOL_MASK 0xf
> #define M_CMD_PROTOCOL_BLK_WR 0x7
> #define M_CMD_PROTOCOL_BLK_RD 0x8
> +#define M_CMD_PROTOCOL_PROCESS 0xa
> #define M_CMD_PEC_SHIFT 8
> #define M_CMD_RD_CNT_SHIFT 0
> #define M_CMD_RD_CNT_MASK 0xff
> @@ -675,13 +676,20 @@ static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c,
> return 0;
> }
>
> -static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
> - struct i2c_msg *msg)
> +/*
> + * If 'process_call' is true, then this is a multi-msg transfer that requires
> + * a repeated start between the messages.
> + * More specifically, it must be a write (reg) followed by a read (data).
> + * The i2c quirks are set to enforce this rule.
> + */
> +static int bcm_iproc_i2c_xfer_internal(struct bcm_iproc_i2c_dev *iproc_i2c,
> + struct i2c_msg *msgs, bool process_call)
> {
> int i;
> u8 addr;
> u32 val, tmp, val_intr_en;
> unsigned int tx_bytes;
> + struct i2c_msg *msg = &msgs[0];
>
> /* check if bus is busy */
> if (!!(iproc_i2c_rd_reg(iproc_i2c,
> @@ -707,14 +715,29 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
> val = msg->buf[i];
>
> /* mark the last byte */
> - if (i == msg->len - 1)
> - val |= BIT(M_TX_WR_STATUS_SHIFT);
> + if (!process_call && (i == msg->len - 1))
> + val |= 1 << M_TX_WR_STATUS_SHIFT;
>
> iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
> }
> iproc_i2c->tx_bytes = tx_bytes;
> }
>
> + /* Process the read message if this is process call */
> + if (process_call) {
> + msg++;
> + iproc_i2c->msg = msg; /* point to second msg */
> +
> + /*
> + * The last byte to be sent out should be a slave
> + * address with read operation
> + */
> + addr = msg->addr << 1 | 1;
> + /* mark it the last byte out */
> + val = addr | (1 << M_TX_WR_STATUS_SHIFT);
> + iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
> + }
> +
> /* mark as incomplete before starting the transaction */
> if (iproc_i2c->irq)
> reinit_completion(&iproc_i2c->done);
> @@ -733,7 +756,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
> * underrun interrupt, which will be triggerred when the TX FIFO is
> * empty. When that happens we can then pump more data into the FIFO
> */
> - if (!(msg->flags & I2C_M_RD) &&
> + if (!process_call && !(msg->flags & I2C_M_RD) &&
> msg->len > iproc_i2c->tx_bytes)
> val_intr_en |= BIT(IE_M_TX_UNDERRUN_SHIFT);
>
> @@ -743,6 +766,8 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
> */
> val = BIT(M_CMD_START_BUSY_SHIFT);
> if (msg->flags & I2C_M_RD) {
> + u32 protocol;
> +
> iproc_i2c->rx_bytes = 0;
> if (msg->len > M_RX_FIFO_MAX_THLD_VALUE)
> iproc_i2c->thld_bytes = M_RX_FIFO_THLD_VALUE;
> @@ -758,7 +783,10 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
> /* enable the RX threshold interrupt */
> val_intr_en |= BIT(IE_M_RX_THLD_SHIFT);
>
> - val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) |
> + protocol = process_call ?
> + M_CMD_PROTOCOL_PROCESS : M_CMD_PROTOCOL_BLK_RD;
> +
> + val |= (protocol << M_CMD_PROTOCOL_SHIFT) |
> (msg->len << M_CMD_RD_CNT_SHIFT);
> } else {
> val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT);
> @@ -774,17 +802,31 @@ static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter,
> struct i2c_msg msgs[], int num)
> {
> struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter);
> - int ret, i;
> + bool process_call = false;
> + int ret;
>
> - /* go through all messages */
> - for (i = 0; i < num; i++) {
> - ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]);
> - if (ret) {
> - dev_dbg(iproc_i2c->device, "xfer failed\n");
> - return ret;
> + if (num > 2) {
> + dev_err(iproc_i2c->device,
> + "Only support up to 2 messages. Current msg count %d\n",
> + num);
> + return -EOPNOTSUPP;
> + }
> +
> + if (num == 2) {
> + /* Repeated start, use process call */
> + process_call = true;
> + if (msgs[1].flags & I2C_M_NOSTART) {
> + dev_err(iproc_i2c->device, "Invalid repeated start\n");
> + return -EOPNOTSUPP;
> }
> }
>
> + ret = bcm_iproc_i2c_xfer_internal(iproc_i2c, msgs, process_call);
> + if (ret) {
> + dev_dbg(iproc_i2c->device, "xfer failed\n");
> + return ret;
> + }
> +
> return num;
> }
>
> @@ -806,6 +848,8 @@ static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap)
> };
>
> static struct i2c_adapter_quirks bcm_iproc_i2c_quirks = {
> + .flags = I2C_AQ_COMB_WRITE_THEN_READ,
> + .max_comb_1st_msg_len = M_TX_RX_FIFO_SIZE,
> .max_read_len = M_RX_MAX_READ_LEN,
> };
>
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v1 2/2] i2c: iproc: Add full name of devicetree node to adapter name
From: Ray Jui @ 2019-08-12 17:29 UTC (permalink / raw)
To: Rayagonda Kokatanur, Wolfram Sang, Rob Herring, Mark Rutland
Cc: devicetree, Lori Hikichi, Florian Fainelli, linux-kernel,
bcm-kernel-feedback-list, linux-i2c, linux-arm-kernel
In-Reply-To: <1565235473-28461-3-git-send-email-rayagonda.kokatanur@broadcom.com>
On 8/7/19 8:37 PM, Rayagonda Kokatanur wrote:
> From: Lori Hikichi <lori.hikichi@broadcom.com>
>
> Add the full name of the devicetree node to the adapter name.
> Without this change, all adapters have the same name making it difficult
> to distinguish between multiple instances.
> The most obvious way to see this is to use the utility i2c_detect.
> e.g. "i2c-detect -l"
>
> Before
> i2c-1 i2c Broadcom iProc I2C adapter I2C adapter
> i2c-0 i2c Broadcom iProc I2C adapter I2C adapter
>
> After
> i2c-1 i2c Broadcom iProc (i2c@e0000) I2C adapter
> i2c-0 i2c Broadcom iProc (i2c@b0000) I2C adapter
>
> Now it is easy to figure out which adapter maps to a which DT node.
>
> Signed-off-by: Lori Hikichi <lori.hikichi@broadcom.com>
> Signed-off-by: Rayagonda Kokatanur <rayagonda.kokatanur@broadcom.com>
> ---
> drivers/i2c/busses/i2c-bcm-iproc.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c
> index 19ef2b0..183b220 100644
> --- a/drivers/i2c/busses/i2c-bcm-iproc.c
> +++ b/drivers/i2c/busses/i2c-bcm-iproc.c
> @@ -922,7 +922,9 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev)
>
> adap = &iproc_i2c->adapter;
> i2c_set_adapdata(adap, iproc_i2c);
> - strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name));
> + snprintf(adap->name, sizeof(adap->name),
> + "Broadcom iProc (%s)",
> + of_node_full_name(iproc_i2c->device->of_node));
> adap->algo = &bcm_iproc_algo;
> adap->quirks = &bcm_iproc_i2c_quirks;
> adap->dev.parent = &pdev->dev;
>
Looks good, thanks!
Reviewed-by: Ray Jui <ray.jui@broadcom.com>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v1 1/2] i2c: iproc: Stop advertising support of SMBUS quick cmd
From: Ray Jui @ 2019-08-12 17:29 UTC (permalink / raw)
To: Rayagonda Kokatanur, Wolfram Sang, Rob Herring, Mark Rutland
Cc: devicetree, Lori Hikichi, Florian Fainelli, linux-kernel,
bcm-kernel-feedback-list, linux-i2c, linux-arm-kernel
In-Reply-To: <1565235473-28461-2-git-send-email-rayagonda.kokatanur@broadcom.com>
On 8/7/19 8:37 PM, Rayagonda Kokatanur wrote:
> From: Lori Hikichi <lori.hikichi@broadcom.com>
>
> The driver does not support the SMBUS Quick command so remove the
> flag that indicates that level of support.
> By default the i2c_detect tool uses the quick command to try and
> detect devices at some bus addresses. If the quick command is used
> then we will not detect the device, even though it is present.
>
> Fixes: e6e5dd3566e0 (i2c: iproc: Add Broadcom iProc I2C Driver)
>
> Signed-off-by: Lori Hikichi <lori.hikichi@broadcom.com>
> Signed-off-by: Rayagonda Kokatanur <rayagonda.kokatanur@broadcom.com>
> ---
> drivers/i2c/busses/i2c-bcm-iproc.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c
> index d7fd76b..19ef2b0 100644
> --- a/drivers/i2c/busses/i2c-bcm-iproc.c
> +++ b/drivers/i2c/busses/i2c-bcm-iproc.c
> @@ -790,7 +790,10 @@ static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter,
>
> static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap)
> {
> - u32 val = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> + u32 val;
> +
> + /* We do not support the SMBUS Quick command */
> + val = I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
>
> if (adap->algo->reg_slave)
> val |= I2C_FUNC_SLAVE;
>
Change looks good to me. Thanks.
Reviewed-by: Ray Jui <ray.jui@broadcom.com>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: next/master boot: 285 boots: 16 failed, 264 passed with 3 offline, 1 untried/unknown, 1 conflict (next-20190718)
From: Mark Brown @ 2019-08-12 17:24 UTC (permalink / raw)
To: khilman, Heiko Stuebner
Cc: linux-rockchip, linux-next, linux-arm-kernel,
kernel-build-reports
In-Reply-To: <20190718162005.GF5761@sirena.org.uk>
[-- Attachment #1.1: Type: text/plain, Size: 1084 bytes --]
On Thu, Jul 18, 2019 at 05:20:05PM +0100, Mark Brown wrote:
> On Thu, Jul 18, 2019 at 04:28:08AM -0700, kernelci.org bot wrote:
> Today's -next started failing to boot defconfig on rk3399-firefly:
> [ 87.491881] cpufreq: __target_index: Failed to change cpu frequency: -22
> [ 87.495335] ------------[ cut here ]------------
> [ 87.496821] kernel BUG at drivers/cpufreq/cpufreq.c:1438!
> [ 87.498462] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP
> I'm struggling to see anything relevant in the diff from yesterday, the
> unlisted frequency warnings were there in the logs yesterday but no oops
> and I'm not seeing any changes in cpufreq, clk or anything relevant
> looking.
> Full bootlog and other info can be found here:
>
> https://kernelci.org/boot/id/5d302d8359b51498d049e983/
This is still present in -next today, though we don't have the failure
to change frequency any more - it still fails right after cpufreq
though:
https://kernelci.org/boot/id/5d51784259b514a021f12245/
https://kernelci.org/boot/id/5d51781559b514a007f12241/
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
[-- Attachment #2: Type: text/plain, Size: 176 bytes --]
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH v7 1/2] arm64: Define Documentation/arm64/tagged-address-abi.rst
From: Catalin Marinas @ 2019-08-12 17:17 UTC (permalink / raw)
To: Andrew Murray
Cc: linux-arch, linux-doc, Szabolcs Nagy, Andrey Konovalov,
Kevin Brodsky, Will Deacon, Dave Hansen, Vincenzo Frascino,
Will Deacon, linux-arm-kernel
In-Reply-To: <20190812104606.GY56241@e119886-lin.cambridge.arm.com>
On Mon, Aug 12, 2019 at 11:46:06AM +0100, Andrew Murray wrote:
> On Thu, Aug 08, 2019 at 06:04:24PM +0100, Will Deacon wrote:
> > On Wed, Aug 07, 2019 at 04:53:20PM +0100, Catalin Marinas wrote:
> > > +
> > > +- mmap() addr parameter.
> > > +
> > > +- mremap() new_address parameter.
> > > +
> > > +- prctl(PR_SET_MM, ``*``, ...) other than arg2 PR_SET_MM_MAP and
> > > + PR_SET_MM_MAP_SIZE.
> > > +
> > > +- prctl(PR_SET_MM, PR_SET_MM_MAP{,_SIZE}, ...) struct prctl_mm_map fields.
> >
> > How did you generate this list and who will keep it up to date? How do you
> > know you haven't missed anything?
>
> What about shared memory system calls: shmat, shmdt? The latest "arm64: untag
> user pointers passed to the kernel" series doesn't untag these, thus we should
> indicate here that these too are no supported.
Yes. We dropped them from a previous version of the series but they've
never been documented. I'll add something (unless someone has a real
use-case for using tags with shmat/shmdt).
--
Catalin
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH] arm64: smp: disable hotplug on trusted OS resident CPU
From: Sudeep Holla @ 2019-08-12 17:11 UTC (permalink / raw)
To: Will Deacon; +Cc: Mark Rutland, Catalin Marinas, linux-arm-kernel
In-Reply-To: <ae9944d8-0c0a-5f0f-707d-4d9e70a4f163@arm.com>
Hi Will,
(sorry for responding so late, I seem to have lost your reply and
when I saw the patch today in my git, searched and saw this reply)
On Mon, Aug 12, 2019 at 06:00:52PM +0100, Sudeep Holla wrote:
>
> On 13/06/2019 10:14, Will Deacon wrote:
> > Hi Sudeep.
> >
> > On Wed, Jun 12, 2019 at 01:51:37PM +0100, Sudeep Holla wrote:
> >> The trusted OS may reject CPU_OFF calls to its resident CPU, so we must
> >> avoid issuing those. We never migrate a Trusted OS and we already take
> >> care to prevent CPU_OFF PSCI call. However, this is not reflected
> >> explicitly to the userspace. Any user can attempt to hotplug trusted OS
> >> resident CPU. The entire motion of going through the various state
> >> transitions in the CPU hotplug state machine gets executed and the
> >> PSCI layer finally refuses to make CPU_OFF call.
> >>
> >> This results is unnecessary unwinding of CPU hotplug state machine in
> >> the kernel. Instead we can mark the trusted OS resident CPU as not
> >> available for hotplug, so that the user attempt or request to do the
> >> same will get immediately rejected.
> >>
> >> Cc: Mark Rutland <mark.rutland@arm.com>
> >> Cc: Catalin Marinas <catalin.marinas@arm.com>
> >> Cc: Will Deacon <will.deacon@arm.com>
> >> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
> >> ---
> >> arch/arm64/include/asm/cpu_ops.h | 3 +++
> >> arch/arm64/kernel/psci.c | 6 ++++++
> >> arch/arm64/kernel/setup.c | 11 ++++++++++-
> >> 3 files changed, 19 insertions(+), 1 deletion(-)
> >
> > I'm just trying to understand the motivation behind this. It's not a fix as
> > far as I can tell, but more of an optimisation for a failing CPU hotplug
> > case. Why is that important? I feel like I'm missing something.
> >
Yes it's just optimisation and not a fix. The main reasons I came up
with this was to avoid unnecessary CPU hotplug state machine unwinding
as it's generally heavy weight operation. There's no other hidden
reasons :) that you are missing.
IIRC I wrote this when I was debugging some issue with suspend-to-ram
which was broken for a different reason.
--
Regards,
Sudeep
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
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