Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 08/12] ARM: dts: socfpga: Add NAND device tree for Arria10
From: Dinh Nguyen @ 2017-01-05 11:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <73a8b5ga1v.fsf@pengutronix.de>



On 01/05/2017 02:55 AM, Steffen Trumtrar wrote:

>> +#include "socfpga_arria10_socdk.dtsi"
>> +
>> +/ {
>> +	soc {
>> +		nand: nand at ffb90000 {
>> +			#address-cells = <1>;
>> +			#size-cells = <1>;
>> +			status = "okay";
>> +
>> +			compatible = "denali,denali-nand-dt", "altr,socfpga-denali-nand";
>> +			reg = <0xffb90000 0x72000>, <0xffb80000 0x10000>;
>> +			reg-names = "nand_data", "denali_reg";
>> +			interrupts = <0 99 4>;
>> +			dma-mask = <0xffffffff>;
>> +			clocks = <&nand_clk>;
> 
> This belongs into the socfpga_arria10.dtsi.
> 

Ah yes, you're right. Thanks for the review.

Dinh

^ permalink raw reply

* [PATCH 4/4] watchdog: tangox: Use watchdog core to install restart handler
From: Måns Rullgård @ 2017-01-05 11:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <df7b5570-a44d-0c25-fedd-03a0891fdff3@sigmadesigns.com>

Marc Gonzalez <marc_gonzalez@sigmadesigns.com> writes:

> [ Adding Mans ]
>
> Guenter, patch c7ef68c32265 states "Fixes: a3e376d26ace".
> Is that true? I mean, they seem quite orthogonal; then again I know
> nothing of this framework.

I don't see the relation of either of those to this patch.

> On 04/01/2017 22:28, Guenter Roeck wrote:
>> Use the infrastructure provided by the watchdog core to install
>> the restart handler.
>> 
>> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
>> ---
>>  drivers/watchdog/tangox_wdt.c | 32 +++++++++++---------------------
>>  1 file changed, 11 insertions(+), 21 deletions(-)
>> 
>> diff --git a/drivers/watchdog/tangox_wdt.c b/drivers/watchdog/tangox_wdt.c
>> index 202c4b9cc921..49e6e805db7c 100644
>> --- a/drivers/watchdog/tangox_wdt.c
>> +++ b/drivers/watchdog/tangox_wdt.c
>> @@ -15,9 +15,7 @@
>>  #include <linux/kernel.h>
>>  #include <linux/module.h>
>>  #include <linux/moduleparam.h>
>> -#include <linux/notifier.h>
>>  #include <linux/platform_device.h>
>> -#include <linux/reboot.h>
>>  #include <linux/watchdog.h>
>>  
>>  #define DEFAULT_TIMEOUT 30
>> @@ -47,7 +45,6 @@ struct tangox_wdt_device {
>>  	void __iomem *base;
>>  	unsigned long clk_rate;
>>  	struct clk *clk;
>> -	struct notifier_block restart;
>>  };
>>  
>>  static int tangox_wdt_set_timeout(struct watchdog_device *wdt,
>> @@ -96,24 +93,24 @@ static const struct watchdog_info tangox_wdt_info = {
>>  	.identity = "tangox watchdog",
>>  };
>>  
>> +static int tangox_wdt_restart(struct watchdog_device *wdt,
>> +			      unsigned long action, void *data)
>> +{
>> +	struct tangox_wdt_device *dev = watchdog_get_drvdata(wdt);
>> +
>> +	writel(1, dev->base + WD_COUNTER);
>> +
>> +	return 0;
>> +}
>> +
>>  static const struct watchdog_ops tangox_wdt_ops = {
>>  	.start		= tangox_wdt_start,
>>  	.stop		= tangox_wdt_stop,
>>  	.set_timeout	= tangox_wdt_set_timeout,
>>  	.get_timeleft	= tangox_wdt_get_timeleft,
>> +	.restart	= tangox_wdt_restart,
>>  };
>>  
>> -static int tangox_wdt_restart(struct notifier_block *nb, unsigned long action,
>> -			      void *data)
>> -{
>> -	struct tangox_wdt_device *dev =
>> -		container_of(nb, struct tangox_wdt_device, restart);
>> -
>> -	writel(1, dev->base + WD_COUNTER);
>> -
>> -	return NOTIFY_DONE;
>> -}
>> -
>>  static int tangox_wdt_probe(struct platform_device *pdev)
>>  {
>>  	struct tangox_wdt_device *dev;
>> @@ -180,12 +177,6 @@ static int tangox_wdt_probe(struct platform_device *pdev)
>>  
>>  	platform_set_drvdata(pdev, dev);
>>  
>> -	dev->restart.notifier_call = tangox_wdt_restart;
>> -	dev->restart.priority = 128;
>> -	err = register_restart_handler(&dev->restart);
>> -	if (err)
>> -		dev_warn(&pdev->dev, "failed to register restart handler\n");
>> -
>>  	dev_info(&pdev->dev, "SMP86xx/SMP87xx watchdog registered\n");
>>  
>>  	return 0;
>> @@ -202,7 +193,6 @@ static int tangox_wdt_remove(struct platform_device *pdev)
>>  	tangox_wdt_stop(&dev->wdt);
>>  	clk_disable_unprepare(dev->clk);
>>  
>> -	unregister_restart_handler(&dev->restart);
>>  	watchdog_unregister_device(&dev->wdt);
>>  
>>  	return 0;
>> 

-- 
M?ns Rullg?rd

^ permalink raw reply

* [RFC PATCH 09/10] drivers/perf: Add support for ARMv8.2 Statistical Profiling Extension
From: Peter Zijlstra @ 2017-01-05 11:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170104191414.GU18193@arm.com>

On Wed, Jan 04, 2017 at 07:14:14PM +0000, Will Deacon wrote:
> Hi Peter,
> 
> On Wed, Jan 04, 2017 at 11:37:13AM +0100, Peter Zijlstra wrote:
> > On Tue, Jan 03, 2017 at 06:10:26PM +0000, Will Deacon wrote:
> > > The ARMv8.2 architecture introduces the Statistical Profiling Extension
> > > (SPE). SPE provides a way to configure and collect profiling samples
> > > from the CPU in the form of a trace buffer, which can be mapped directly
> > > into userspace using the perf AUX buffer infrastructure.
> > > 
> > > This patch adds support for SPE in the form of a new perf driver.
> > > 
> > 
> > Can you give a little high level overview of what exactly SPE is?
> 
> Sure, I can try, although there is no public documentation yet so it's a
> bit fiddly.
> 
> SPE can be used to profile a population of operations in the CPU pipeline
> after instruction decode. These are either architected instructions (i.e.
> a dynamic instruction trace) or CPU-specific uops and the choice is fixed
> statically in the hardware and advertised to userspace via caps/. Sampling
> is controlled using a sampling interval, similar to a regular PMU counter,
> but also with an optional random perturbation to avoid falling into patterns
> where you continuously profile the same instruction in a hot loop.
> 
> After each operation is decoded, the interval counter is decremented. When
> it hits zero, an operation is chosen for profiling and tracked within the
> pipeline until it retires. Along the way, information such as TLB lookups,
> cache misses, time spent to issue etc is captured in the form of a sample.
> The sample is then filtered according to certain criteria (e.g. load
> latency) that can be specified in the event config (described under
> format/) and, if the sample satisfies the filter, it is written out to
> memory as a record, otherwise it is discarded. Only one operation can
> be sampled at a time.
> 
> The in-memory buffer is linear and virtually addressed, raising an
> interrupt when it fills up. The PMU driver handles these interrupts to
> give the appearance of a ring buffer, as expected by the AUX code.
> 
> The in-memory trace-like format is self-describing (though not parseable
> in reverse) and written as a series of records, with each record
> corresponding to a sample and consisting of a sequence of packets. These
> packets are defined by the architecture, although some have CPU-specific
> fields for recording information specific to the microarchitecture.
> 
> As a simple example, a record generated for a branch instruction may
> consist of the following packets:
> 
>   0 (Address) : Virtual PC of the branch instruction
>   1 (Type)    : Conditional direct branch
>   2 (Counter) : Number of cycles taken from Dispatch to Issue
>   3 (Address) : Virtual branch target + condition flags
>   4 (Counter) : Number of cycles taken from Dispatch to Complete
>   5 (Events)  : Mispredicted as not-taken
>   6 (END)     : End of record
> 
> You can also toggle things like timestamp packets in each record.
> 
> Since SPE is an optional extension to the architecture, I'm sure there
> will be big.LITTLE systems where only one of the clusters has SPE support,
> so the driver is slightly complicated by handling that.

Hmm, on first reading that sounds a bit like a combination of AMD-IBS
and Intel-PEBS. PEBS has the memory buffer, but we keep that private to
the implementation, we rewrite the events into 'normal' perf SAMPLE
records on interrupt and context switch. IBS otoh doesn't have the
memory buffer but does similar things like tagging u-ops and providing
various metrics, which are exposed as is through SAMPLE_RAW.

I have no immediate objection to using AUX for this though, its arguably
similar to SAMPLE_RAW and makes sense since you have a memory buffer
already.

We also have an AUX enabled driver for Intel-BTS, which is similar to
PEBS but records branch traces (the precursor to PT in a sense).

^ permalink raw reply

* [PATCH v5 13/17] irqdomain: irq_domain_check_msi_remap
From: Auger Eric @ 2017-01-05 11:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <f6096747-5943-520a-2d16-de5650292ff3@arm.com>

Hi Marc,

On 05/01/2017 12:25, Marc Zyngier wrote:
> On 05/01/17 10:45, Auger Eric wrote:
>> Hi Marc,
>>
>> On 04/01/2017 16:27, Marc Zyngier wrote:
>>> On 04/01/17 14:11, Auger Eric wrote:
>>>> Hi Marc,
>>>>
>>>> On 04/01/2017 14:46, Marc Zyngier wrote:
>>>>> Hi Eric,
>>>>>
>>>>> On 04/01/17 13:32, Eric Auger wrote:
>>>>>> This new function checks whether all platform and PCI
>>>>>> MSI domains implement IRQ remapping. This is useful to
>>>>>> understand whether VFIO passthrough is safe with respect
>>>>>> to interrupts.
>>>>>>
>>>>>> On ARM typically an MSI controller can sit downstream
>>>>>> to the IOMMU without preventing VFIO passthrough.
>>>>>> As such any assigned device can write into the MSI doorbell.
>>>>>> In case the MSI controller implements IRQ remapping, assigned
>>>>>> devices will not be able to trigger interrupts towards the
>>>>>> host. On the contrary, the assignment must be emphasized as
>>>>>> unsafe with respect to interrupts.
>>>>>>
>>>>>> Signed-off-by: Eric Auger <eric.auger@redhat.com>
>>>>>>
>>>>>> ---
>>>>>>
>>>>>> v4 -> v5:
>>>>>> - Handle DOMAIN_BUS_FSL_MC_MSI domains
>>>>>> - Check parents
>>>>>> ---
>>>>>>  include/linux/irqdomain.h |  1 +
>>>>>>  kernel/irq/irqdomain.c    | 41 +++++++++++++++++++++++++++++++++++++++++
>>>>>>  2 files changed, 42 insertions(+)
>>>>>>
>>>>>> diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
>>>>>> index ab017b2..281a40f 100644
>>>>>> --- a/include/linux/irqdomain.h
>>>>>> +++ b/include/linux/irqdomain.h
>>>>>> @@ -219,6 +219,7 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
>>>>>>  					 void *host_data);
>>>>>>  extern struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec,
>>>>>>  						   enum irq_domain_bus_token bus_token);
>>>>>> +extern bool irq_domain_check_msi_remap(void);
>>>>>>  extern void irq_set_default_host(struct irq_domain *host);
>>>>>>  extern int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
>>>>>>  				  irq_hw_number_t hwirq, int node,
>>>>>> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
>>>>>> index 8c0a0ae..700caea 100644
>>>>>> --- a/kernel/irq/irqdomain.c
>>>>>> +++ b/kernel/irq/irqdomain.c
>>>>>> @@ -278,6 +278,47 @@ struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec,
>>>>>>  EXPORT_SYMBOL_GPL(irq_find_matching_fwspec);
>>>>>>  
>>>>>>  /**
>>>>>> + * irq_domain_is_msi_remap - Check if @domain or any parent
>>>>>> + * has MSI remapping support
>>>>>> + * @domain: domain pointer
>>>>>> + */
>>>>>> +static bool irq_domain_is_msi_remap(struct irq_domain *domain)
>>>>>> +{
>>>>>> +	struct irq_domain *h = domain;
>>>>>> +
>>>>>> +	for (; h; h = h->parent) {
>>>>>> +		if (h->flags & IRQ_DOMAIN_FLAG_MSI_REMAP)
>>>>>> +			return true;
>>>>>> +	}
>>>>>> +	return false;
>>>>>> +}
>>>>>> +
>>>>>> +/**
>>>>>> + * irq_domain_check_msi_remap() - Checks whether all MSI
>>>>>> + * irq domains implement IRQ remapping
>>>>>> + */
>>>>>> +bool irq_domain_check_msi_remap(void)
>>>>>> +{
>>>>>> +	struct irq_domain *h;
>>>>>> +	bool ret = true;
>>>>>> +
>>>>>> +	mutex_lock(&irq_domain_mutex);
>>>>>> +	list_for_each_entry(h, &irq_domain_list, link) {
>>>>>> +		if (((h->bus_token & DOMAIN_BUS_PCI_MSI) ||
>>>>>> +		     (h->bus_token & DOMAIN_BUS_PLATFORM_MSI) ||
>>>>>> +		     (h->bus_token & DOMAIN_BUS_FSL_MC_MSI)) &&
>>>>>> +		     !irq_domain_is_msi_remap(h)) {
>>>>>
>>>>> (h->bus_token & DOMAIN_BUS_PCI_MSI) and co looks quite wrong. bus_token
>>>>> is not a bitmap, and DOMAIN_BUS_* not a single bit value (see enum
>>>>> irq_domain_bus_token). Surely this should read
>>>>> (h->bus_token == DOMAIN_BUS_PCI_MSI).
>>>> Oh I did not notice that. Thanks.
>>>>
>>>> Any other comments on the irqdomain side? Do you think the current
>>>> approach consisting in looking at those bus tokens and their parents
>>>> looks good?
>>>
>>> To be completely honest, I don't like it much, as having to enumerate
>>> all the bus types can come up with could become quite a burden in the
>>> long run. I'd rather be able to identify MSI capable domains by
>>> construction. I came up with the following approach (fully untested):
>>>
>>> diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
>>> index 281a40f..7779796 100644
>>> --- a/include/linux/irqdomain.h
>>> +++ b/include/linux/irqdomain.h
>>> @@ -183,8 +183,11 @@ enum {
>>>  	/* Irq domain is an IPI domain with single virq */
>>>  	IRQ_DOMAIN_FLAG_IPI_SINGLE	= (1 << 3),
>>>  
>>> +	/* Irq domain implements MSIs */
>>> +	IRQ_DOMAIN_FLAG_MSI		= (1 << 4),
>>> +
>>>  	/* Irq domain is MSI remapping capable */
>>> -	IRQ_DOMAIN_FLAG_MSI_REMAP	= (1 << 4),
>>> +	IRQ_DOMAIN_FLAG_MSI_REMAP	= (1 << 5),
>>>  
>>>  	/*
>>>  	 * Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved
>>> @@ -450,6 +453,11 @@ static inline bool irq_domain_is_ipi_single(struct irq_domain *domain)
>>>  {
>>>  	return domain->flags & IRQ_DOMAIN_FLAG_IPI_SINGLE;
>>>  }
>>> +
>>> +static inline bool irq_domain_is_msi(struct irq_domain *domain)
>>> +{
>>> +	return domain->flags & IRQ_DOMAIN_FLAG_MSI;
>>> +}
>>>  #else	/* CONFIG_IRQ_DOMAIN_HIERARCHY */
>>>  static inline void irq_domain_activate_irq(struct irq_data *data) { }
>>>  static inline void irq_domain_deactivate_irq(struct irq_data *data) { }
>>> @@ -481,6 +489,11 @@ static inline bool irq_domain_is_ipi_single(struct irq_domain *domain)
>>>  {
>>>  	return false;
>>>  }
>>> +
>>> +static inline bool irq_domain_is_msi(struct irq_domain *domain)
>>> +{
>>> +	return false;
>>> +}
>>>  #endif	/* CONFIG_IRQ_DOMAIN_HIERARCHY */
>>>  
>>>  #else /* CONFIG_IRQ_DOMAIN */
>>> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
>>> index 700caea..33b6921 100644
>>> --- a/kernel/irq/irqdomain.c
>>> +++ b/kernel/irq/irqdomain.c
>>> @@ -304,10 +304,7 @@ bool irq_domain_check_msi_remap(void)
>>>  
>>>  	mutex_lock(&irq_domain_mutex);
>>>  	list_for_each_entry(h, &irq_domain_list, link) {
>>> -		if (((h->bus_token & DOMAIN_BUS_PCI_MSI) ||
>>> -		     (h->bus_token & DOMAIN_BUS_PLATFORM_MSI) ||
>>> -		     (h->bus_token & DOMAIN_BUS_FSL_MC_MSI)) &&
>>> -		     !irq_domain_is_msi_remap(h)) {
>>> +		if (irq_domain_is_msi(h) && !irq_domain_is_msi_remap(h)) {
>>>  			ret = false;
>>>  			goto out;
>>>  		}
>>> diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
>>> index ee23006..b637263 100644
>>> --- a/kernel/irq/msi.c
>>> +++ b/kernel/irq/msi.c
>>> @@ -270,7 +270,7 @@ struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode,
>>>  	if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
>>>  		msi_domain_update_chip_ops(info);
>>>  
>>> -	return irq_domain_create_hierarchy(parent, 0, 0, fwnode,
>>> +	return irq_domain_create_hierarchy(parent, IRQ_DOMAIN_FLAG_MSI, 0, fwnode,
>>>  					   &msi_domain_ops, info);
>>>  }
>>>  
>>>
>>>
>>> Thoughts?
>>
>> Don't we need to set the IRQ_DOMAIN_FLAG_MSI flag in
>> platform_msi_create_device_domain too (drivers/base/platform-msi.c)?
was mentioning platform_msi_create_*device*_domain.
it calls irq_domain_create_hierarchy and looks to be MSI irq domain
related. But I don't have a full understanding of the whole irq domain
hierarchy.

Thanks

Eric
> 
> Well, platform_msi_create_irq_domain does call msi_create_irq_domain,
> doesn't it? That's one of the benefits of the generic MSI
> infrastructure: it is the only function that performs the creation of an
> MSI domain for any bus type.
> 
> Or am I missing something completely obvious (which is perfectly
> possible since I only had a couple of cups of the brown stuff...)?
> 
> Thanks,
> 
> 	M.
> 

^ permalink raw reply

* [PATCH v5 13/17] irqdomain: irq_domain_check_msi_remap
From: Marc Zyngier @ 2017-01-05 11:25 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <92fcb464-3364-c432-29d8-1c2ce4a5f110@redhat.com>

On 05/01/17 10:45, Auger Eric wrote:
> Hi Marc,
> 
> On 04/01/2017 16:27, Marc Zyngier wrote:
>> On 04/01/17 14:11, Auger Eric wrote:
>>> Hi Marc,
>>>
>>> On 04/01/2017 14:46, Marc Zyngier wrote:
>>>> Hi Eric,
>>>>
>>>> On 04/01/17 13:32, Eric Auger wrote:
>>>>> This new function checks whether all platform and PCI
>>>>> MSI domains implement IRQ remapping. This is useful to
>>>>> understand whether VFIO passthrough is safe with respect
>>>>> to interrupts.
>>>>>
>>>>> On ARM typically an MSI controller can sit downstream
>>>>> to the IOMMU without preventing VFIO passthrough.
>>>>> As such any assigned device can write into the MSI doorbell.
>>>>> In case the MSI controller implements IRQ remapping, assigned
>>>>> devices will not be able to trigger interrupts towards the
>>>>> host. On the contrary, the assignment must be emphasized as
>>>>> unsafe with respect to interrupts.
>>>>>
>>>>> Signed-off-by: Eric Auger <eric.auger@redhat.com>
>>>>>
>>>>> ---
>>>>>
>>>>> v4 -> v5:
>>>>> - Handle DOMAIN_BUS_FSL_MC_MSI domains
>>>>> - Check parents
>>>>> ---
>>>>>  include/linux/irqdomain.h |  1 +
>>>>>  kernel/irq/irqdomain.c    | 41 +++++++++++++++++++++++++++++++++++++++++
>>>>>  2 files changed, 42 insertions(+)
>>>>>
>>>>> diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
>>>>> index ab017b2..281a40f 100644
>>>>> --- a/include/linux/irqdomain.h
>>>>> +++ b/include/linux/irqdomain.h
>>>>> @@ -219,6 +219,7 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
>>>>>  					 void *host_data);
>>>>>  extern struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec,
>>>>>  						   enum irq_domain_bus_token bus_token);
>>>>> +extern bool irq_domain_check_msi_remap(void);
>>>>>  extern void irq_set_default_host(struct irq_domain *host);
>>>>>  extern int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
>>>>>  				  irq_hw_number_t hwirq, int node,
>>>>> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
>>>>> index 8c0a0ae..700caea 100644
>>>>> --- a/kernel/irq/irqdomain.c
>>>>> +++ b/kernel/irq/irqdomain.c
>>>>> @@ -278,6 +278,47 @@ struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec,
>>>>>  EXPORT_SYMBOL_GPL(irq_find_matching_fwspec);
>>>>>  
>>>>>  /**
>>>>> + * irq_domain_is_msi_remap - Check if @domain or any parent
>>>>> + * has MSI remapping support
>>>>> + * @domain: domain pointer
>>>>> + */
>>>>> +static bool irq_domain_is_msi_remap(struct irq_domain *domain)
>>>>> +{
>>>>> +	struct irq_domain *h = domain;
>>>>> +
>>>>> +	for (; h; h = h->parent) {
>>>>> +		if (h->flags & IRQ_DOMAIN_FLAG_MSI_REMAP)
>>>>> +			return true;
>>>>> +	}
>>>>> +	return false;
>>>>> +}
>>>>> +
>>>>> +/**
>>>>> + * irq_domain_check_msi_remap() - Checks whether all MSI
>>>>> + * irq domains implement IRQ remapping
>>>>> + */
>>>>> +bool irq_domain_check_msi_remap(void)
>>>>> +{
>>>>> +	struct irq_domain *h;
>>>>> +	bool ret = true;
>>>>> +
>>>>> +	mutex_lock(&irq_domain_mutex);
>>>>> +	list_for_each_entry(h, &irq_domain_list, link) {
>>>>> +		if (((h->bus_token & DOMAIN_BUS_PCI_MSI) ||
>>>>> +		     (h->bus_token & DOMAIN_BUS_PLATFORM_MSI) ||
>>>>> +		     (h->bus_token & DOMAIN_BUS_FSL_MC_MSI)) &&
>>>>> +		     !irq_domain_is_msi_remap(h)) {
>>>>
>>>> (h->bus_token & DOMAIN_BUS_PCI_MSI) and co looks quite wrong. bus_token
>>>> is not a bitmap, and DOMAIN_BUS_* not a single bit value (see enum
>>>> irq_domain_bus_token). Surely this should read
>>>> (h->bus_token == DOMAIN_BUS_PCI_MSI).
>>> Oh I did not notice that. Thanks.
>>>
>>> Any other comments on the irqdomain side? Do you think the current
>>> approach consisting in looking at those bus tokens and their parents
>>> looks good?
>>
>> To be completely honest, I don't like it much, as having to enumerate
>> all the bus types can come up with could become quite a burden in the
>> long run. I'd rather be able to identify MSI capable domains by
>> construction. I came up with the following approach (fully untested):
>>
>> diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
>> index 281a40f..7779796 100644
>> --- a/include/linux/irqdomain.h
>> +++ b/include/linux/irqdomain.h
>> @@ -183,8 +183,11 @@ enum {
>>  	/* Irq domain is an IPI domain with single virq */
>>  	IRQ_DOMAIN_FLAG_IPI_SINGLE	= (1 << 3),
>>  
>> +	/* Irq domain implements MSIs */
>> +	IRQ_DOMAIN_FLAG_MSI		= (1 << 4),
>> +
>>  	/* Irq domain is MSI remapping capable */
>> -	IRQ_DOMAIN_FLAG_MSI_REMAP	= (1 << 4),
>> +	IRQ_DOMAIN_FLAG_MSI_REMAP	= (1 << 5),
>>  
>>  	/*
>>  	 * Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved
>> @@ -450,6 +453,11 @@ static inline bool irq_domain_is_ipi_single(struct irq_domain *domain)
>>  {
>>  	return domain->flags & IRQ_DOMAIN_FLAG_IPI_SINGLE;
>>  }
>> +
>> +static inline bool irq_domain_is_msi(struct irq_domain *domain)
>> +{
>> +	return domain->flags & IRQ_DOMAIN_FLAG_MSI;
>> +}
>>  #else	/* CONFIG_IRQ_DOMAIN_HIERARCHY */
>>  static inline void irq_domain_activate_irq(struct irq_data *data) { }
>>  static inline void irq_domain_deactivate_irq(struct irq_data *data) { }
>> @@ -481,6 +489,11 @@ static inline bool irq_domain_is_ipi_single(struct irq_domain *domain)
>>  {
>>  	return false;
>>  }
>> +
>> +static inline bool irq_domain_is_msi(struct irq_domain *domain)
>> +{
>> +	return false;
>> +}
>>  #endif	/* CONFIG_IRQ_DOMAIN_HIERARCHY */
>>  
>>  #else /* CONFIG_IRQ_DOMAIN */
>> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
>> index 700caea..33b6921 100644
>> --- a/kernel/irq/irqdomain.c
>> +++ b/kernel/irq/irqdomain.c
>> @@ -304,10 +304,7 @@ bool irq_domain_check_msi_remap(void)
>>  
>>  	mutex_lock(&irq_domain_mutex);
>>  	list_for_each_entry(h, &irq_domain_list, link) {
>> -		if (((h->bus_token & DOMAIN_BUS_PCI_MSI) ||
>> -		     (h->bus_token & DOMAIN_BUS_PLATFORM_MSI) ||
>> -		     (h->bus_token & DOMAIN_BUS_FSL_MC_MSI)) &&
>> -		     !irq_domain_is_msi_remap(h)) {
>> +		if (irq_domain_is_msi(h) && !irq_domain_is_msi_remap(h)) {
>>  			ret = false;
>>  			goto out;
>>  		}
>> diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
>> index ee23006..b637263 100644
>> --- a/kernel/irq/msi.c
>> +++ b/kernel/irq/msi.c
>> @@ -270,7 +270,7 @@ struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode,
>>  	if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
>>  		msi_domain_update_chip_ops(info);
>>  
>> -	return irq_domain_create_hierarchy(parent, 0, 0, fwnode,
>> +	return irq_domain_create_hierarchy(parent, IRQ_DOMAIN_FLAG_MSI, 0, fwnode,
>>  					   &msi_domain_ops, info);
>>  }
>>  
>>
>>
>> Thoughts?
> 
> Don't we need to set the IRQ_DOMAIN_FLAG_MSI flag in
> platform_msi_create_device_domain too (drivers/base/platform-msi.c)?

Well, platform_msi_create_irq_domain does call msi_create_irq_domain,
doesn't it? That's one of the benefits of the generic MSI
infrastructure: it is the only function that performs the creation of an
MSI domain for any bus type.

Or am I missing something completely obvious (which is perfectly
possible since I only had a couple of cups of the brown stuff...)?

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply

* [PATCH v2 2/4] linux/const.h: move UL() macro to include/linux/const.h
From: Russell King - ARM Linux @ 2017-01-05 11:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483582810-7046-3-git-send-email-yamada.masahiro@socionext.com>

On Thu, Jan 05, 2017 at 11:20:07AM +0900, Masahiro Yamada wrote:
> ARM, ARM64 and UniCore32 duplicate the definition of UL():
> 
>   #define UL(x) _AC(x, UL)
> 
> This is not actually arch-specific, so it will be useful to move it
> to a common header.  Currently, we only have the uapi variant for
> linux/const.h, so I am creating include/linux/const.h.
> 
> I am also adding _UL(), _ULL() and ULL() because _AC() is mostly
> used in the form either _AC(..., UL) or _AC(..., ULL).  I expect
> they will be replaced in later cleanups.  The underscore-prefixed
> ones should be used for exported headers.
> 
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
> Acked-by: Guan Xuetao <gxt@mprc.pku.edu.cn>
> ---
> 
> Changes in v2: None
> 
>  arch/arm/include/asm/memory.h       | 6 ------
>  arch/arm64/include/asm/memory.h     | 6 ------
>  arch/unicore32/include/asm/memory.h | 6 ------
>  include/linux/const.h               | 9 +++++++++
>  include/uapi/linux/const.h          | 9 ++++++---
>  5 files changed, 15 insertions(+), 21 deletions(-)
>  create mode 100644 include/linux/const.h
> 
> diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
> index 76cbd9c..7558247 100644
> --- a/arch/arm/include/asm/memory.h
> +++ b/arch/arm/include/asm/memory.h
> @@ -22,12 +22,6 @@
>  #include <mach/memory.h>
>  #endif
>  
> -/*
> - * Allow for constants defined here to be used from assembly code
> - * by prepending the UL suffix only with actual C code compilation.
> - */
> -#define UL(x) _AC(x, UL)
> -
>  /* PAGE_OFFSET - the virtual address of the start of the kernel image */
>  #define PAGE_OFFSET		UL(CONFIG_PAGE_OFFSET)
>  

For ARM,

Acked-by: Russell King <rmk+kernel@armlinux.org.uk>

Thanks.

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply

* [PATCH 2/2] arm64: mm: enable CONFIG_HOLES_IN_ZONE for NUMA
From: Robert Richter @ 2017-01-05 11:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170104140223.GF18193@arm.com>

On 04.01.17 14:02:23, Will Deacon wrote:
> Using early_pfn_valid feels like a bodge to me, since having pfn_valid
> return false for something that early_pfn_valid says is valid (and is
> therefore initialised in the memmap) makes the NOMAP semantics even more
> confusing.

The concern I have had with HOLES_IN_ZONE is that it enables
pfn_valid_within() for arm64. This means that each pfn of a section is
checked which is done only once for the section otherwise. With up to
2^18 pages per section we traverse the memblock list by that factor
more often. There could be a performance regression. I haven't numbers
yet, since the fix causes another kernel crash. And, this is the next
problem I have. The crash doesn't happen otherwise. So, either it
uncovers another bug or the fix is incomplete. Though the changes look
like it should work. This needs more investigation.

-Robert

^ permalink raw reply

* [PATCH v2 2/4] linux/const.h: move UL() macro to include/linux/const.h
From: Catalin Marinas @ 2017-01-05 11:18 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483582810-7046-3-git-send-email-yamada.masahiro@socionext.com>

> diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
> index bfe6328..4310bcc 100644
> --- a/arch/arm64/include/asm/memory.h
> +++ b/arch/arm64/include/asm/memory.h
> @@ -28,12 +28,6 @@
>  #include <asm/sizes.h>
>  
>  /*
> - * Allow for constants defined here to be used from assembly code
> - * by prepending the UL suffix only with actual C code compilation.
> - */
> -#define UL(x) _AC(x, UL)
> -
> -/*
>   * Size of the PCI I/O space. This must remain a power of two so that
>   * IO_SPACE_LIMIT acts as a mask for the low bits of I/O addresses.
>   */

For the arm64 bit:

Acked-by: Catalin Marinas <catalin.marinas@arm.com>

^ permalink raw reply

* [PATCH v4 2/2] dt-bindings: Add DT bindings info for FlexRM ring manager
From: Anup Patel @ 2017-01-05 11:07 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483614475-3442-1-git-send-email-anup.patel@broadcom.com>

This patch adds device tree bindings document for the FlexRM
ring manager found on Broadcom iProc SoCs.

Reviewed-by: Ray Jui <ray.jui@broadcom.com>
Reviewed-by: Scott Branden <scott.branden@broadcom.com>
Signed-off-by: Anup Patel <anup.patel@broadcom.com>
---
 .../bindings/mailbox/brcm,iproc-flexrm-mbox.txt    | 59 ++++++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt

diff --git a/Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt b/Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt
new file mode 100644
index 0000000..752ae6b
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt
@@ -0,0 +1,59 @@
+Broadcom FlexRM Ring Manager
+============================
+The Broadcom FlexRM ring manager provides a set of rings which can be
+used to submit work to offload engines. An SoC may have multiple FlexRM
+hardware blocks. There is one device tree entry per FlexRM block. The
+FlexRM driver will create a mailbox-controller instance for given FlexRM
+hardware block where each mailbox channel is a separate FlexRM ring.
+
+Required properties:
+--------------------
+- compatible:	Should be "brcm,iproc-flexrm-mbox"
+- reg:		Specifies base physical address and size of the FlexRM
+		ring registers
+- msi-parent:	Phandles (and potential Device IDs) to MSI controllers
+		The FlexRM engine will send MSIs (instead of wired
+		interrupts) to CPU. There is one MSI for each FlexRM ring.
+		Refer devicetree/bindings/interrupt-controller/msi.txt
+- #mbox-cells:	Specifies the number of cells needed to encode a mailbox
+		channel. This should be 3.
+
+		The 1st cell is the mailbox channel number.
+
+		The 2nd cell contains MSI completion threshold. This is the
+		number of completion messages for which FlexRM will inject
+		one MSI interrupt to CPU.
+
+		The 3nd cell contains MSI timer value representing time for
+		which FlexRM will wait to accumulate N completion messages
+		where N is the value specified by 2nd cell above. If FlexRM
+		does not get required number of completion messages in time
+		specified by this cell then it will inject one MSI interrupt
+		to CPU provided atleast one completion message is available.
+
+Optional properties:
+--------------------
+- dma-coherent:	Present if DMA operations made by the FlexRM engine (such
+		as DMA descriptor access, access to buffers pointed by DMA
+		descriptors and read/write pointer updates to DDR) are
+		cache coherent with the CPU.
+
+Example:
+--------
+crypto_mbox: mbox at 67000000 {
+	compatible = "brcm,iproc-flexrm-mbox";
+	reg = <0x67000000 0x200000>;
+	msi-parent = <&gic_its 0x7f00>;
+	#mbox-cells = <3>;
+};
+
+crypto at 672c0000 {
+	compatible = "brcm,spu2-v2-crypto";
+	reg = <0x672c0000 0x1000>;
+	mboxes = <&crypto_mbox 0 0x1 0xffff>,
+		 <&crypto_mbox 1 0x1 0xffff>,
+		 <&crypto_mbox 16 0x1 0xffff>,
+		 <&crypto_mbox 17 0x1 0xffff>,
+		 <&crypto_mbox 30 0x1 0xffff>,
+		 <&crypto_mbox 31 0x1 0xffff>;
+};
-- 
2.7.4

^ permalink raw reply related

* [PATCH v4 1/2] mailbox: Add driver for Broadcom FlexRM ring manager
From: Anup Patel @ 2017-01-05 11:07 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483614475-3442-1-git-send-email-anup.patel@broadcom.com>

Some of the Broadcom iProc SoCs have FlexRM ring manager
which provides a ring-based programming interface to various
offload engines (e.g. RAID, Crypto, etc).

This patch adds a common mailbox driver for Broadcom FlexRM
ring manager which can be shared by various offload engine
drivers (implemented as mailbox clients).

Reviewed-by: Ray Jui <ray.jui@broadcom.com>
Reviewed-by: Scott Branden <scott.branden@broadcom.com>
Reviewed-by: Pramod KUMAR <pramod.kumar@broadcom.com>
Signed-off-by: Anup Patel <anup.patel@broadcom.com>
---
 drivers/mailbox/Kconfig                      |  11 +
 drivers/mailbox/Makefile                     |   2 +
 drivers/mailbox/mailbox-flexrm/Makefile      |   6 +
 drivers/mailbox/mailbox-flexrm/flexrm-desc.c | 764 ++++++++++++++++++++++++
 drivers/mailbox/mailbox-flexrm/flexrm-desc.h |  47 ++
 drivers/mailbox/mailbox-flexrm/flexrm-main.c | 829 +++++++++++++++++++++++++++
 include/linux/mailbox/brcm-message.h         |  14 +-
 7 files changed, 1669 insertions(+), 4 deletions(-)
 create mode 100644 drivers/mailbox/mailbox-flexrm/Makefile
 create mode 100644 drivers/mailbox/mailbox-flexrm/flexrm-desc.c
 create mode 100644 drivers/mailbox/mailbox-flexrm/flexrm-desc.h
 create mode 100644 drivers/mailbox/mailbox-flexrm/flexrm-main.c

diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index ceff415..305018c 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -152,4 +152,15 @@ config BCM_PDC_MBOX
 	  Mailbox implementation for the Broadcom PDC ring manager,
 	  which provides access to various offload engines on Broadcom
 	  SoCs. Say Y here if you want to use the Broadcom PDC.
+
+config BCM_FLEXRM_MBOX
+	tristate "Broadcom FlexRM Mailbox"
+	depends on ARM64 || COMPILE_TEST
+	depends on HAS_DMA
+	select GENERIC_MSI_IRQ_DOMAIN
+	default ARCH_BCM_IPROC
+	help
+	  Mailbox implementation of the Broadcom FlexRM ring manager,
+	  which provides access to various offload engines on Broadcom
+	  SoCs. Say Y here if you want to use the Broadcom FlexRM.
 endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 7dde4f6..45083c0 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -30,4 +30,6 @@ obj-$(CONFIG_HI6220_MBOX)	+= hi6220-mailbox.o
 
 obj-$(CONFIG_BCM_PDC_MBOX)	+= bcm-pdc-mailbox.o
 
+obj-$(CONFIG_BCM_FLEXRM_MBOX)	+= mailbox-flexrm/
+
 obj-$(CONFIG_TEGRA_HSP_MBOX)	+= tegra-hsp.o
diff --git a/drivers/mailbox/mailbox-flexrm/Makefile b/drivers/mailbox/mailbox-flexrm/Makefile
new file mode 100644
index 0000000..f5bf069
--- /dev/null
+++ b/drivers/mailbox/mailbox-flexrm/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Broadcom FlexRM Mailbox Driver.
+#
+
+flexrm-mbox-objs := flexrm-main.o flexrm-desc.o
+obj-$(CONFIG_BCM_FLEXRM_MBOX) += flexrm-mbox.o
diff --git a/drivers/mailbox/mailbox-flexrm/flexrm-desc.c b/drivers/mailbox/mailbox-flexrm/flexrm-desc.c
new file mode 100644
index 0000000..b0449eb
--- /dev/null
+++ b/drivers/mailbox/mailbox-flexrm/flexrm-desc.c
@@ -0,0 +1,764 @@
+/* Broadcom FlexRM Mailbox Driver
+ *
+ * Copyright (C) 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * FlexRM descriptor library
+ */
+
+#include <asm/barrier.h>
+#include <asm/byteorder.h>
+#include <linux/dma-mapping.h>
+#include <linux/printk.h>
+
+#include "flexrm-desc.h"
+
+/* Completion descriptor format */
+#define CMPL_OPAQUE_SHIFT			0
+#define CMPL_OPAQUE_MASK			0xffff
+#define CMPL_ENGINE_STATUS_SHIFT		16
+#define CMPL_ENGINE_STATUS_MASK			0xffff
+#define CMPL_DME_STATUS_SHIFT			32
+#define CMPL_DME_STATUS_MASK			0xffff
+#define CMPL_RM_STATUS_SHIFT			48
+#define CMPL_RM_STATUS_MASK			0xffff
+
+/* Completion DME status code */
+#define DME_STATUS_MEM_COR_ERR			BIT(0)
+#define DME_STATUS_MEM_UCOR_ERR			BIT(1)
+#define DME_STATUS_FIFO_UNDERFLOW		BIT(2)
+#define DME_STATUS_FIFO_OVERFLOW		BIT(3)
+#define DME_STATUS_RRESP_ERR			BIT(4)
+#define DME_STATUS_BRESP_ERR			BIT(5)
+#define DME_STATUS_ERROR_MASK			(DME_STATUS_MEM_COR_ERR | \
+						 DME_STATUS_MEM_UCOR_ERR | \
+						 DME_STATUS_FIFO_UNDERFLOW | \
+						 DME_STATUS_FIFO_OVERFLOW | \
+						 DME_STATUS_RRESP_ERR | \
+						 DME_STATUS_BRESP_ERR)
+
+/* Completion RM status code */
+#define RM_STATUS_CODE_SHIFT			0
+#define RM_STATUS_CODE_MASK			0x3ff
+#define RM_STATUS_CODE_GOOD			0x0
+#define RM_STATUS_CODE_AE_TIMEOUT		0x3ff
+
+/* General descriptor format */
+#define DESC_TYPE_SHIFT				60
+#define DESC_TYPE_MASK				0xf
+#define DESC_PAYLOAD_SHIFT			0
+#define DESC_PAYLOAD_MASK			0x0fffffffffffffff
+
+/* Null descriptor format  */
+#define NULL_TYPE				0
+#define NULL_TOGGLE_SHIFT			58
+#define NULL_TOGGLE_MASK			0x1
+
+/* Header descriptor format */
+#define HEADER_TYPE				1
+#define HEADER_TOGGLE_SHIFT			58
+#define HEADER_TOGGLE_MASK			0x1
+#define HEADER_ENDPKT_SHIFT			57
+#define HEADER_ENDPKT_MASK			0x1
+#define HEADER_STARTPKT_SHIFT			56
+#define HEADER_STARTPKT_MASK			0x1
+#define HEADER_BDCOUNT_SHIFT			36
+#define HEADER_BDCOUNT_MASK			0x1f
+#define HEADER_BDCOUNT_MAX			HEADER_BDCOUNT_MASK
+#define HEADER_FLAGS_SHIFT			16
+#define HEADER_FLAGS_MASK			0xffff
+#define HEADER_OPAQUE_SHIFT			0
+#define HEADER_OPAQUE_MASK			0xffff
+
+/* Source (SRC) descriptor format */
+#define SRC_TYPE				2
+#define SRC_LENGTH_SHIFT			44
+#define SRC_LENGTH_MASK				0xffff
+#define SRC_ADDR_SHIFT				0
+#define SRC_ADDR_MASK				0x00000fffffffffff
+
+/* Destination (DST) descriptor format */
+#define DST_TYPE				3
+#define DST_LENGTH_SHIFT			44
+#define DST_LENGTH_MASK				0xffff
+#define DST_ADDR_SHIFT				0
+#define DST_ADDR_MASK				0x00000fffffffffff
+
+/* Immediate (IMM) descriptor format */
+#define IMM_TYPE				4
+#define IMM_DATA_SHIFT				0
+#define IMM_DATA_MASK				0x0fffffffffffffff
+
+/* Next pointer (NPTR) descriptor format */
+#define NPTR_TYPE				5
+#define NPTR_TOGGLE_SHIFT			58
+#define NPTR_TOGGLE_MASK			0x1
+#define NPTR_ADDR_SHIFT				0
+#define NPTR_ADDR_MASK				0x00000fffffffffff
+
+/* Mega source (MSRC) descriptor format */
+#define MSRC_TYPE				6
+#define MSRC_LENGTH_SHIFT			44
+#define MSRC_LENGTH_MASK			0xffff
+#define MSRC_ADDR_SHIFT				0
+#define MSRC_ADDR_MASK				0x00000fffffffffff
+
+/* Mega destination (MDST) descriptor format */
+#define MDST_TYPE				7
+#define MDST_LENGTH_SHIFT			44
+#define MDST_LENGTH_MASK			0xffff
+#define MDST_ADDR_SHIFT				0
+#define MDST_ADDR_MASK				0x00000fffffffffff
+
+/* Source with tlast (SRCT) descriptor format */
+#define SRCT_TYPE				8
+#define SRCT_LENGTH_SHIFT			44
+#define SRCT_LENGTH_MASK			0xffff
+#define SRCT_ADDR_SHIFT				0
+#define SRCT_ADDR_MASK				0x00000fffffffffff
+
+/* Destination with tlast (DSTT) descriptor format */
+#define DSTT_TYPE				9
+#define DSTT_LENGTH_SHIFT			44
+#define DSTT_LENGTH_MASK			0xffff
+#define DSTT_ADDR_SHIFT				0
+#define DSTT_ADDR_MASK				0x00000fffffffffff
+
+/* Immediate with tlast (IMMT) descriptor format */
+#define IMMT_TYPE				10
+#define IMMT_DATA_SHIFT				0
+#define IMMT_DATA_MASK				0x0fffffffffffffff
+
+/* Descriptor helper macros */
+#define DESC_DEC(_d, _s, _m)			(((_d) >> (_s)) & (_m))
+#define DESC_ENC(_d, _v, _s, _m)		\
+			do { \
+				(_d) &= ~((u64)(_m) << (_s)); \
+				(_d) |= (((u64)(_v) & (_m)) << (_s)); \
+			} while (0)
+
+u64 flexrm_read_desc(void *desc_ptr)
+{
+	return le64_to_cpu(*((u64 *)desc_ptr));
+}
+
+void flexrm_write_desc(void *desc_ptr, u64 desc)
+{
+	*((u64 *)desc_ptr) = cpu_to_le64(desc);
+}
+
+u32 flexrm_cmpl_desc_to_reqid(u64 cmpl_desc)
+{
+	return (u32)(cmpl_desc & CMPL_OPAQUE_MASK);
+}
+
+int flexrm_cmpl_desc_to_error(u64 cmpl_desc)
+{
+	u32 status;
+
+	status = DESC_DEC(cmpl_desc, CMPL_DME_STATUS_SHIFT,
+			  CMPL_DME_STATUS_MASK);
+	if (status & DME_STATUS_ERROR_MASK)
+		return -EIO;
+
+	status = DESC_DEC(cmpl_desc, CMPL_RM_STATUS_SHIFT,
+			  CMPL_RM_STATUS_MASK);
+	status &= RM_STATUS_CODE_MASK;
+	if (status == RM_STATUS_CODE_AE_TIMEOUT)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+bool flexrm_is_next_table_desc(void *desc_ptr)
+{
+	u64 desc = flexrm_read_desc(desc_ptr);
+	u32 type = DESC_DEC(desc, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+
+	return (type == NPTR_TYPE) ? true : false;
+}
+
+u64 flexrm_next_table_desc(u32 toggle, dma_addr_t next_addr)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, NPTR_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, toggle, NPTR_TOGGLE_SHIFT, NPTR_TOGGLE_MASK);
+	DESC_ENC(desc, next_addr, NPTR_ADDR_SHIFT, NPTR_ADDR_MASK);
+
+	return desc;
+}
+
+u64 flexrm_null_desc(u32 toggle)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, NULL_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, toggle, NULL_TOGGLE_SHIFT, NULL_TOGGLE_MASK);
+
+	return desc;
+}
+
+u32 flexrm_estimate_header_desc_count(u32 nhcnt)
+{
+	u32 hcnt = nhcnt / HEADER_BDCOUNT_MAX;
+
+	if (!(nhcnt % HEADER_BDCOUNT_MAX))
+		hcnt += 1;
+
+	return hcnt;
+}
+
+static void flexrm_flip_header_toogle(void *desc_ptr)
+{
+	u64 desc = flexrm_read_desc(desc_ptr);
+
+	if (desc & ((u64)0x1 << HEADER_TOGGLE_SHIFT))
+		desc &= ~((u64)0x1 << HEADER_TOGGLE_SHIFT);
+	else
+		desc |= ((u64)0x1 << HEADER_TOGGLE_SHIFT);
+
+	flexrm_write_desc(desc_ptr, desc);
+}
+
+static u64 flexrm_header_desc(u32 toggle, u32 startpkt, u32 endpkt,
+			       u32 bdcount, u32 flags, u32 opaque)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, HEADER_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, toggle, HEADER_TOGGLE_SHIFT, HEADER_TOGGLE_MASK);
+	DESC_ENC(desc, startpkt, HEADER_STARTPKT_SHIFT, HEADER_STARTPKT_MASK);
+	DESC_ENC(desc, endpkt, HEADER_ENDPKT_SHIFT, HEADER_ENDPKT_MASK);
+	DESC_ENC(desc, bdcount, HEADER_BDCOUNT_SHIFT, HEADER_BDCOUNT_MASK);
+	DESC_ENC(desc, flags, HEADER_FLAGS_SHIFT, HEADER_FLAGS_MASK);
+	DESC_ENC(desc, opaque, HEADER_OPAQUE_SHIFT, HEADER_OPAQUE_MASK);
+
+	return desc;
+}
+
+static void flexrm_enqueue_desc(u32 nhpos, u32 nhcnt, u32 reqid,
+				 u64 desc, void **desc_ptr, u32 *toggle,
+				 void *start_desc, void *end_desc)
+{
+	u64 d;
+	u32 nhavail, _toggle, _startpkt, _endpkt, _bdcount;
+
+	/* Sanity check */
+	if (nhcnt <= nhpos)
+		return;
+
+	/*
+	 * Each request or packet start with a HEADER descriptor followed
+	 * by one or more non-HEADER descriptors (SRC, SRCT, MSRC, DST,
+	 * DSTT, MDST, IMM, and IMMT). The number of non-HEADER descriptors
+	 * following a HEADER descriptor is represented by BDCOUNT field
+	 * of HEADER descriptor. The max value of BDCOUNT field is 31 which
+	 * means we can only have 31 non-HEADER descriptors following one
+	 * HEADER descriptor.
+	 *
+	 * In general use, number of non-HEADER descriptors can easily go
+	 * beyond 31. To tackle this situation, we have packet (or request)
+	 * extenstion bits (STARTPKT and ENDPKT) in the HEADER descriptor.
+	 *
+	 * To use packet extension, the first HEADER descriptor of request
+	 * (or packet) will have STARTPKT=1 and ENDPKT=0. The intermediate
+	 * HEADER descriptors will have STARTPKT=0 and ENDPKT=0. The last
+	 * HEADER descriptor will have STARTPKT=0 and ENDPKT=1. Also, the
+	 * TOGGLE bit of the first HEADER will be set to invalid state to
+	 * ensure that FlexRM does not start fetching descriptors till all
+	 * descriptors are enqueued. The user of this function will flip
+	 * the TOGGLE bit of first HEADER after all descriptors are
+	 * enqueued.
+	 */
+
+	if ((nhpos % HEADER_BDCOUNT_MAX == 0) && (nhcnt - nhpos)) {
+		/* Prepare the header descriptor */
+		nhavail = (nhcnt - nhpos);
+		_toggle = (nhpos == 0) ? !(*toggle) : (*toggle);
+		_startpkt = (nhpos == 0) ? 0x1 : 0x0;
+		_endpkt = (nhavail <= HEADER_BDCOUNT_MAX) ? 0x1 : 0x0;
+		_bdcount = (nhavail <= HEADER_BDCOUNT_MAX) ?
+				nhavail : HEADER_BDCOUNT_MAX;
+		if (nhavail <= HEADER_BDCOUNT_MAX)
+			_bdcount = nhavail;
+		else
+			_bdcount = HEADER_BDCOUNT_MAX;
+		d = flexrm_header_desc(_toggle, _startpkt, _endpkt,
+					_bdcount, 0x0, reqid);
+
+		/* Write header descriptor */
+		flexrm_write_desc(*desc_ptr, d);
+
+		/* Point to next descriptor */
+		*desc_ptr += sizeof(desc);
+		if (*desc_ptr == end_desc)
+			*desc_ptr = start_desc;
+
+		/* Skip next pointer descriptors */
+		while (flexrm_is_next_table_desc(*desc_ptr)) {
+			*toggle = (*toggle) ? 0 : 1;
+			*desc_ptr += sizeof(desc);
+			if (*desc_ptr == end_desc)
+				*desc_ptr = start_desc;
+		}
+	}
+
+	/* Write desired descriptor */
+	flexrm_write_desc(*desc_ptr, desc);
+
+	/* Point to next descriptor */
+	*desc_ptr += sizeof(desc);
+	if (*desc_ptr == end_desc)
+		*desc_ptr = start_desc;
+
+	/* Skip next pointer descriptors */
+	while (flexrm_is_next_table_desc(*desc_ptr)) {
+		*toggle = (*toggle) ? 0 : 1;
+		*desc_ptr += sizeof(desc);
+		if (*desc_ptr == end_desc)
+			*desc_ptr = start_desc;
+	}
+}
+
+static u64 flexrm_src_desc(dma_addr_t addr, unsigned int length)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, SRC_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, length, SRC_LENGTH_SHIFT, SRC_LENGTH_MASK);
+	DESC_ENC(desc, addr, SRC_ADDR_SHIFT, SRC_ADDR_MASK);
+
+	return desc;
+}
+
+static u64 flexrm_msrc_desc(dma_addr_t addr, unsigned int length_div_16)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, MSRC_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, length_div_16, MSRC_LENGTH_SHIFT, MSRC_LENGTH_MASK);
+	DESC_ENC(desc, addr, MSRC_ADDR_SHIFT, MSRC_ADDR_MASK);
+
+	return desc;
+}
+
+static u64 flexrm_dst_desc(dma_addr_t addr, unsigned int length)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, DST_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, length, DST_LENGTH_SHIFT, DST_LENGTH_MASK);
+	DESC_ENC(desc, addr, DST_ADDR_SHIFT, DST_ADDR_MASK);
+
+	return desc;
+}
+
+static u64 flexrm_mdst_desc(dma_addr_t addr, unsigned int length_div_16)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, MDST_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, length_div_16, MDST_LENGTH_SHIFT, MDST_LENGTH_MASK);
+	DESC_ENC(desc, addr, MDST_ADDR_SHIFT, MDST_ADDR_MASK);
+
+	return desc;
+}
+
+static u64 flexrm_imm_desc(u64 data)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, IMM_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, data, IMM_DATA_SHIFT, IMM_DATA_MASK);
+
+	return desc;
+}
+
+static u64 flexrm_srct_desc(dma_addr_t addr, unsigned int length)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, SRCT_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, length, SRCT_LENGTH_SHIFT, SRCT_LENGTH_MASK);
+	DESC_ENC(desc, addr, SRCT_ADDR_SHIFT, SRCT_ADDR_MASK);
+
+	return desc;
+}
+
+static u64 flexrm_dstt_desc(dma_addr_t addr, unsigned int length)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, DSTT_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, length, DSTT_LENGTH_SHIFT, DSTT_LENGTH_MASK);
+	DESC_ENC(desc, addr, DSTT_ADDR_SHIFT, DSTT_ADDR_MASK);
+
+	return desc;
+}
+
+static u64 flexrm_immt_desc(u64 data)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, IMMT_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, data, IMMT_DATA_SHIFT, IMMT_DATA_MASK);
+
+	return desc;
+}
+
+static bool flexrm_spu_sanity_check(struct brcm_message *msg)
+{
+	struct scatterlist *sg;
+
+	if (!msg->spu.src || !msg->spu.dst)
+		return false;
+	for (sg = msg->spu.src; sg; sg = sg_next(sg)) {
+		if (sg->length & 0xf) {
+			if (sg->length > SRC_LENGTH_MASK)
+				return false;
+		} else {
+			if (sg->length > (MSRC_LENGTH_MASK * 16))
+				return false;
+		}
+	}
+	for (sg = msg->spu.dst; sg; sg = sg_next(sg)) {
+		if (sg->length & 0xf) {
+			if (sg->length > DST_LENGTH_MASK)
+				return false;
+		} else {
+			if (sg->length > (MDST_LENGTH_MASK * 16))
+				return false;
+		}
+	}
+
+	return true;
+}
+
+static u32 flexrm_spu_estimate_nonheader_desc_count(struct brcm_message *msg)
+{
+	u32 cnt = 0;
+	unsigned int dst_target = 0;
+	struct scatterlist *src_sg = msg->spu.src, *dst_sg = msg->spu.dst;
+
+	while (src_sg || dst_sg) {
+		if (src_sg) {
+			cnt++;
+			dst_target = src_sg->length;
+			src_sg = sg_next(src_sg);
+		} else
+			dst_target = UINT_MAX;
+
+		while (dst_target && dst_sg) {
+			cnt++;
+			if (dst_sg->length < dst_target)
+				dst_target -= dst_sg->length;
+			else
+				dst_target = 0;
+			dst_sg = sg_next(dst_sg);
+		}
+	}
+
+	return cnt;
+}
+
+static int flexrm_spu_dma_map(struct device *dev, struct brcm_message *msg)
+{
+	int rc;
+
+	rc = dma_map_sg(dev, msg->spu.src, sg_nents(msg->spu.src),
+			DMA_TO_DEVICE);
+	if (rc < 0)
+		return rc;
+
+	rc = dma_map_sg(dev, msg->spu.dst, sg_nents(msg->spu.dst),
+			DMA_FROM_DEVICE);
+	if (rc < 0) {
+		dma_unmap_sg(dev, msg->spu.src, sg_nents(msg->spu.src),
+			     DMA_TO_DEVICE);
+		return rc;
+	}
+
+	return 0;
+}
+
+static void flexrm_spu_dma_unmap(struct device *dev, struct brcm_message *msg)
+{
+	dma_unmap_sg(dev, msg->spu.dst, sg_nents(msg->spu.dst),
+		     DMA_FROM_DEVICE);
+	dma_unmap_sg(dev, msg->spu.src, sg_nents(msg->spu.src),
+		     DMA_TO_DEVICE);
+}
+
+static void *flexrm_spu_write_descs(struct brcm_message *msg, u32 nhcnt,
+				     u32 reqid, void *desc_ptr, u32 toggle,
+				     void *start_desc, void *end_desc)
+{
+	u64 d;
+	u32 nhpos = 0;
+	void *orig_desc_ptr = desc_ptr;
+	unsigned int dst_target = 0;
+	struct scatterlist *src_sg = msg->spu.src, *dst_sg = msg->spu.dst;
+
+	while (src_sg || dst_sg) {
+		if (src_sg) {
+			if (sg_dma_len(src_sg) & 0xf)
+				d = flexrm_src_desc(sg_dma_address(src_sg),
+						     sg_dma_len(src_sg));
+			else
+				d = flexrm_msrc_desc(sg_dma_address(src_sg),
+						      sg_dma_len(src_sg)/16);
+			flexrm_enqueue_desc(nhpos, nhcnt, reqid,
+					     d, &desc_ptr, &toggle,
+					     start_desc, end_desc);
+			nhpos++;
+			dst_target = sg_dma_len(src_sg);
+			src_sg = sg_next(src_sg);
+		} else
+			dst_target = UINT_MAX;
+
+		while (dst_target && dst_sg) {
+			if (sg_dma_len(dst_sg) & 0xf)
+				d = flexrm_dst_desc(sg_dma_address(dst_sg),
+						     sg_dma_len(dst_sg));
+			else
+				d = flexrm_mdst_desc(sg_dma_address(dst_sg),
+						      sg_dma_len(dst_sg)/16);
+			flexrm_enqueue_desc(nhpos, nhcnt, reqid,
+					     d, &desc_ptr, &toggle,
+					     start_desc, end_desc);
+			nhpos++;
+			if (sg_dma_len(dst_sg) < dst_target)
+				dst_target -= sg_dma_len(dst_sg);
+			else
+				dst_target = 0;
+			dst_sg = sg_next(dst_sg);
+		}
+	}
+
+	/* Null descriptor with invalid toggle bit */
+	flexrm_write_desc(desc_ptr, flexrm_null_desc(!toggle));
+
+	/* Ensure that descriptors have been written to memory */
+	wmb();
+
+	/* Flip toggle bit in header */
+	flexrm_flip_header_toogle(orig_desc_ptr);
+
+	return desc_ptr;
+}
+
+static bool flexrm_sba_sanity_check(struct brcm_message *msg)
+{
+	u32 i;
+
+	if (!msg->sba.cmds || !msg->sba.cmds_count)
+		return false;
+
+	for (i = 0; i < msg->sba.cmds_count; i++) {
+		if (((msg->sba.cmds[i].flags & BRCM_SBA_CMD_TYPE_B) ||
+		     (msg->sba.cmds[i].flags & BRCM_SBA_CMD_TYPE_C)) &&
+		    (msg->sba.cmds[i].flags & BRCM_SBA_CMD_HAS_OUTPUT))
+			return false;
+		if ((msg->sba.cmds[i].flags & BRCM_SBA_CMD_TYPE_B) &&
+		    (msg->sba.cmds[i].data_len > SRCT_LENGTH_MASK))
+			return false;
+		if ((msg->sba.cmds[i].flags & BRCM_SBA_CMD_TYPE_C) &&
+		    (msg->sba.cmds[i].data_len > SRCT_LENGTH_MASK))
+			return false;
+		if ((msg->sba.cmds[i].flags & BRCM_SBA_CMD_HAS_RESP) &&
+		    (msg->sba.cmds[i].resp_len > DSTT_LENGTH_MASK))
+			return false;
+		if ((msg->sba.cmds[i].flags & BRCM_SBA_CMD_HAS_OUTPUT) &&
+		    (msg->sba.cmds[i].data_len > DSTT_LENGTH_MASK))
+			return false;
+	}
+
+	return true;
+}
+
+static u32 flexrm_sba_estimate_nonheader_desc_count(struct brcm_message *msg)
+{
+	u32 i, cnt;
+
+	cnt = 0;
+	for (i = 0; i < msg->sba.cmds_count; i++) {
+		cnt++;
+
+		if ((msg->sba.cmds[i].flags & BRCM_SBA_CMD_TYPE_B) ||
+		    (msg->sba.cmds[i].flags & BRCM_SBA_CMD_TYPE_C))
+			cnt++;
+
+		if (msg->sba.cmds[i].flags & BRCM_SBA_CMD_HAS_RESP)
+			cnt++;
+
+		if (msg->sba.cmds[i].flags & BRCM_SBA_CMD_HAS_OUTPUT)
+			cnt++;
+	}
+
+	return cnt;
+}
+
+static void *flexrm_sba_write_descs(struct brcm_message *msg, u32 nhcnt,
+				     u32 reqid, void *desc_ptr, u32 toggle,
+				     void *start_desc, void *end_desc)
+{
+	u64 d;
+	u32 i, nhpos = 0;
+	struct brcm_sba_command *c;
+	void *orig_desc_ptr = desc_ptr;
+
+	/* Convert SBA commands into descriptors */
+	for (i = 0; i < msg->sba.cmds_count; i++) {
+		c = &msg->sba.cmds[i];
+
+		if ((c->flags & BRCM_SBA_CMD_HAS_RESP) &&
+		    (c->flags & BRCM_SBA_CMD_HAS_OUTPUT)) {
+			/* Destination response descriptor */
+			d = flexrm_dst_desc(c->resp, c->resp_len);
+			flexrm_enqueue_desc(nhpos, nhcnt, reqid,
+					     d, &desc_ptr, &toggle,
+					     start_desc, end_desc);
+			nhpos++;
+		} else if (c->flags & BRCM_SBA_CMD_HAS_RESP) {
+			/* Destination response with tlast descriptor */
+			d = flexrm_dstt_desc(c->resp, c->resp_len);
+			flexrm_enqueue_desc(nhpos, nhcnt, reqid,
+					     d, &desc_ptr, &toggle,
+					     start_desc, end_desc);
+			nhpos++;
+		}
+
+		if (c->flags & BRCM_SBA_CMD_HAS_OUTPUT) {
+			/* Destination with tlast descriptor */
+			d = flexrm_dstt_desc(c->data, c->data_len);
+			flexrm_enqueue_desc(nhpos, nhcnt, reqid,
+					     d, &desc_ptr, &toggle,
+					     start_desc, end_desc);
+			nhpos++;
+		}
+
+		if (c->flags & BRCM_SBA_CMD_TYPE_B) {
+			/* Command as immediate descriptor */
+			d = flexrm_imm_desc(c->cmd);
+			flexrm_enqueue_desc(nhpos, nhcnt, reqid,
+					     d, &desc_ptr, &toggle,
+					     start_desc, end_desc);
+			nhpos++;
+		} else {
+			/* Command as immediate descriptor with tlast */
+			d = flexrm_immt_desc(c->cmd);
+			flexrm_enqueue_desc(nhpos, nhcnt, reqid,
+					     d, &desc_ptr, &toggle,
+					     start_desc, end_desc);
+			nhpos++;
+		}
+
+		if ((c->flags & BRCM_SBA_CMD_TYPE_B) ||
+		    (c->flags & BRCM_SBA_CMD_TYPE_C)) {
+			/* Source with tlast descriptor */
+			d = flexrm_srct_desc(c->data, c->data_len);
+			flexrm_enqueue_desc(nhpos, nhcnt, reqid,
+					     d, &desc_ptr, &toggle,
+					     start_desc, end_desc);
+			nhpos++;
+		}
+	}
+
+	/* Null descriptor with invalid toggle bit */
+	flexrm_write_desc(desc_ptr, flexrm_null_desc(!toggle));
+
+	/* Ensure that descriptors have been written to memory */
+	wmb();
+
+	/* Flip toggle bit in header */
+	flexrm_flip_header_toogle(orig_desc_ptr);
+
+	return desc_ptr;
+}
+
+bool flexrm_sanity_check(struct brcm_message *msg)
+{
+	if (!msg)
+		return false;
+
+	switch (msg->type) {
+	case BRCM_MESSAGE_SPU:
+		return flexrm_spu_sanity_check(msg);
+	case BRCM_MESSAGE_SBA:
+		return flexrm_sba_sanity_check(msg);
+	default:
+		return false;
+	};
+}
+
+u32 flexrm_estimate_nonheader_desc_count(struct brcm_message *msg)
+{
+	if (!msg)
+		return 0;
+
+	switch (msg->type) {
+	case BRCM_MESSAGE_SPU:
+		return flexrm_spu_estimate_nonheader_desc_count(msg);
+	case BRCM_MESSAGE_SBA:
+		return flexrm_sba_estimate_nonheader_desc_count(msg);
+	default:
+		return 0;
+	};
+}
+
+int flexrm_dma_map(struct device *dev, struct brcm_message *msg)
+{
+	if (!dev || !msg)
+		return -EINVAL;
+
+	switch (msg->type) {
+	case BRCM_MESSAGE_SPU:
+		return flexrm_spu_dma_map(dev, msg);
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+void flexrm_dma_unmap(struct device *dev, struct brcm_message *msg)
+{
+	if (!dev || !msg)
+		return;
+
+	switch (msg->type) {
+	case BRCM_MESSAGE_SPU:
+		flexrm_spu_dma_unmap(dev, msg);
+		break;
+	default:
+		break;
+	}
+}
+
+void *flexrm_write_descs(struct brcm_message *msg, u32 nhcnt,
+			  u32 reqid, void *desc_ptr, u32 toggle,
+			  void *start_desc, void *end_desc)
+{
+	if (!msg || !desc_ptr || !start_desc || !end_desc)
+		return ERR_PTR(-ENOTSUPP);
+
+	if ((desc_ptr < start_desc) || (end_desc <= desc_ptr))
+		return ERR_PTR(-ERANGE);
+
+	switch (msg->type) {
+	case BRCM_MESSAGE_SPU:
+		return flexrm_spu_write_descs(msg, nhcnt, reqid,
+					       desc_ptr, toggle,
+					       start_desc, end_desc);
+	case BRCM_MESSAGE_SBA:
+		return flexrm_sba_write_descs(msg, nhcnt, reqid,
+					       desc_ptr, toggle,
+					       start_desc, end_desc);
+	default:
+		return ERR_PTR(-ENOTSUPP);
+	};
+}
diff --git a/drivers/mailbox/mailbox-flexrm/flexrm-desc.h b/drivers/mailbox/mailbox-flexrm/flexrm-desc.h
new file mode 100644
index 0000000..a95cf61
--- /dev/null
+++ b/drivers/mailbox/mailbox-flexrm/flexrm-desc.h
@@ -0,0 +1,47 @@
+/* Broadcom FlexRM Mailbox Driver
+ *
+ * Copyright (C) 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * FlexRM descriptor library
+ */
+
+#ifndef __FLEXRM_DESC_H__
+#define __FLEXRM_DESC_H__
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/mailbox/brcm-message.h>
+
+extern u64 flexrm_read_desc(void *desc_ptr);
+
+extern void flexrm_write_desc(void *desc_ptr, u64 desc);
+
+extern u32 flexrm_cmpl_desc_to_reqid(u64 cmpl_desc);
+
+extern int flexrm_cmpl_desc_to_error(u64 cmpl_desc);
+
+extern bool flexrm_is_next_table_desc(void *desc_ptr);
+
+extern u64 flexrm_next_table_desc(u32 toggle, dma_addr_t next_addr);
+
+extern u64 flexrm_null_desc(u32 toogle);
+
+extern u32 flexrm_estimate_header_desc_count(u32 nhcnt);
+
+extern bool flexrm_sanity_check(struct brcm_message *msg);
+
+extern u32 flexrm_estimate_nonheader_desc_count(struct brcm_message *msg);
+
+extern int flexrm_dma_map(struct device *dev, struct brcm_message *msg);
+
+extern void flexrm_dma_unmap(struct device *dev, struct brcm_message *msg);
+
+extern void *flexrm_write_descs(struct brcm_message *msg, u32 nhcnt,
+				 u32 reqid, void *desc_ptr, u32 toggle,
+				 void *start_desc, void *end_desc);
+
+#endif /* __FLEXRM_DESC_H__ */
diff --git a/drivers/mailbox/mailbox-flexrm/flexrm-main.c b/drivers/mailbox/mailbox-flexrm/flexrm-main.c
new file mode 100644
index 0000000..c8890f1
--- /dev/null
+++ b/drivers/mailbox/mailbox-flexrm/flexrm-main.c
@@ -0,0 +1,829 @@
+/* Broadcom FlexRM Mailbox Driver
+ *
+ * Copyright (C) 2016 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Each Broadcom FlexSparx4 offload engine is implemented as an
+ * extension to Broadcom FlexRM ring manager. The FlexRM ring
+ * manager provides a set of rings which can be used to submit
+ * work to a FlexSparx4 offload engine.
+ *
+ * This driver creates a mailbox controller using a set of FlexRM
+ * rings where each mailbox channel represents a separate FlexRM ring.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/err.h>
+#include <linux/idr.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_controller.h>
+#include <linux/mailbox_client.h>
+#include <linux/mailbox/brcm-message.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#include "flexrm-desc.h"
+
+/* FlexRM configuration */
+#define RING_REGS_SIZE					0x10000
+#define RING_DESC_SIZE					8
+#define RING_DESC_INDEX(offset)				\
+			((offset) / RING_DESC_SIZE)
+#define RING_DESC_OFFSET(index)				\
+			((index) * RING_DESC_SIZE)
+#define RING_MAX_REQ_COUNT				1024
+#define RING_BD_ALIGN_ORDER				12
+#define RING_BD_ALIGN_CHECK(addr)			\
+			(!((addr) & ((0x1 << RING_BD_ALIGN_ORDER) - 1)))
+#define RING_BD_TOGGLE_INVALID(offset)			\
+			(((offset) >> RING_BD_ALIGN_ORDER) & 0x1)
+#define RING_BD_TOGGLE_VALID(offset)			\
+			(!RING_BD_TOGGLE_INVALID(offset))
+#define RING_BD_DESC_PER_REQ				32
+#define RING_BD_DESC_COUNT				\
+			(RING_MAX_REQ_COUNT * RING_BD_DESC_PER_REQ)
+#define RING_BD_SIZE					\
+			(RING_BD_DESC_COUNT * RING_DESC_SIZE)
+#define RING_CMPL_ALIGN_ORDER				13
+#define RING_CMPL_DESC_COUNT				RING_MAX_REQ_COUNT
+#define RING_CMPL_SIZE					\
+			(RING_CMPL_DESC_COUNT * RING_DESC_SIZE)
+#define RING_VER_MAGIC					0x76303031
+
+/* Per-Ring register offsets */
+#define RING_VER					0x000
+#define RING_BD_START_ADDR				0x004
+#define RING_BD_READ_PTR				0x008
+#define RING_BD_WRITE_PTR				0x00c
+#define RING_BD_READ_PTR_DDR_LS				0x010
+#define RING_BD_READ_PTR_DDR_MS				0x014
+#define RING_CMPL_START_ADDR				0x018
+#define RING_CMPL_WRITE_PTR				0x01c
+#define RING_NUM_REQ_RECV_LS				0x020
+#define RING_NUM_REQ_RECV_MS				0x024
+#define RING_NUM_REQ_TRANS_LS				0x028
+#define RING_NUM_REQ_TRANS_MS				0x02c
+#define RING_NUM_REQ_OUTSTAND				0x030
+#define RING_CONTROL					0x034
+#define RING_FLUSH_DONE					0x038
+#define RING_MSI_ADDR_LS				0x03c
+#define RING_MSI_ADDR_MS				0x040
+#define RING_MSI_CONTROL				0x048
+#define RING_BD_READ_PTR_DDR_CONTROL			0x04c
+#define RING_MSI_DATA_VALUE				0x064
+
+/* Register RING_BD_START_ADDR fields */
+#define BD_LAST_UPDATE_HW_SHIFT				28
+#define BD_LAST_UPDATE_HW_MASK				0x1
+#define BD_START_ADDR_VALUE(pa)				\
+	((u32)((((dma_addr_t)(pa)) >> RING_BD_ALIGN_ORDER) & 0x0fffffff))
+#define BD_START_ADDR_DECODE(val)			\
+	((dma_addr_t)((val) & 0x0fffffff) << RING_BD_ALIGN_ORDER)
+
+/* Register RING_CMPL_START_ADDR fields */
+#define CMPL_START_ADDR_VALUE(pa)			\
+	((u32)((((u64)(pa)) >> RING_CMPL_ALIGN_ORDER) & 0x03ffffff))
+
+/* Register RING_CONTROL fields */
+#define CONTROL_MASK_DISABLE_CONTROL			12
+#define CONTROL_FLUSH_SHIFT				5
+#define CONTROL_ACTIVE_SHIFT				4
+#define CONTROL_RATE_ADAPT_MASK				0xf
+#define CONTROL_RATE_DYNAMIC				0x0
+#define CONTROL_RATE_FAST				0x8
+#define CONTROL_RATE_MEDIUM				0x9
+#define CONTROL_RATE_SLOW				0xa
+#define CONTROL_RATE_IDLE				0xb
+
+/* Register RING_FLUSH_DONE fields */
+#define FLUSH_DONE_MASK					0x1
+
+/* Register RING_MSI_CONTROL fields */
+#define MSI_TIMER_VAL_SHIFT				16
+#define MSI_TIMER_VAL_MASK				0xffff
+#define MSI_ENABLE_SHIFT				15
+#define MSI_ENABLE_MASK					0x1
+#define MSI_COUNT_SHIFT					0
+#define MSI_COUNT_MASK					0x3ff
+
+/* Register RING_BD_READ_PTR_DDR_CONTROL fields */
+#define BD_READ_PTR_DDR_TIMER_VAL_SHIFT			16
+#define BD_READ_PTR_DDR_TIMER_VAL_MASK			0xffff
+#define BD_READ_PTR_DDR_ENABLE_SHIFT			15
+#define BD_READ_PTR_DDR_ENABLE_MASK			0x1
+
+struct flexrm_ring {
+	/* Unprotected members */
+	int num;
+	struct flexrm_mbox *mbox;
+	void __iomem *regs;
+	bool irq_requested;
+	unsigned int irq;
+	unsigned int msi_timer_val;
+	unsigned int msi_count_threshold;
+	struct ida requests_ida;
+	struct brcm_message *requests[RING_MAX_REQ_COUNT];
+	void *bd_base;
+	dma_addr_t bd_dma_base;
+	u32 bd_write_offset;
+	void *cmpl_base;
+	dma_addr_t cmpl_dma_base;
+	/* Protected members */
+	spinlock_t lock;
+	struct brcm_message *last_pending_msg;
+	u32 cmpl_read_offset;
+};
+
+struct flexrm_mbox {
+	struct device *dev;
+	void __iomem *regs;
+	u32 num_rings;
+	struct flexrm_ring *rings;
+	u64 dma_mask;
+	struct dma_pool *bd_pool;
+	struct dma_pool *cmpl_pool;
+	struct mbox_controller controller;
+};
+
+static int flexrm_new_request(struct flexrm_ring *ring,
+				struct brcm_message *batch_msg,
+				struct brcm_message *msg)
+{
+	void *next;
+	unsigned long flags;
+	u32 val, count, nhcnt;
+	u32 read_offset, write_offset;
+	bool exit_cleanup = false;
+	int ret = 0, reqid;
+
+	/* Do sanity check on message */
+	if (!flexrm_sanity_check(msg))
+		return -EIO;
+	msg->error = 0;
+
+	/* If no requests possible then save data pointer and goto done. */
+	reqid = ida_simple_get(&ring->requests_ida, 0,
+				RING_MAX_REQ_COUNT, GFP_KERNEL);
+	if (reqid < 0) {
+		spin_lock_irqsave(&ring->lock, flags);
+		if (batch_msg)
+			ring->last_pending_msg = batch_msg;
+		else
+			ring->last_pending_msg = msg;
+		spin_unlock_irqrestore(&ring->lock, flags);
+		return 0;
+	}
+	ring->requests[reqid] = msg;
+
+	/* Do DMA mappings for the message */
+	ret = flexrm_dma_map(ring->mbox->dev, msg);
+	if (ret < 0) {
+		ring->requests[reqid] = NULL;
+		ida_simple_remove(&ring->requests_ida, reqid);
+		return ret;
+	}
+
+	/* If last_pending_msg is already set then goto done with error */
+	spin_lock_irqsave(&ring->lock, flags);
+	if (ring->last_pending_msg)
+		ret = -ENOSPC;
+	spin_unlock_irqrestore(&ring->lock, flags);
+	if (ret < 0) {
+		dev_warn(ring->mbox->dev, "no space in ring %d\n", ring->num);
+		exit_cleanup = true;
+		goto exit;
+	}
+
+	/* Determine current HW BD read offset */
+	read_offset = readl_relaxed(ring->regs + RING_BD_READ_PTR);
+	val = readl_relaxed(ring->regs + RING_BD_START_ADDR);
+	read_offset *= RING_DESC_SIZE;
+	read_offset += (u32)(BD_START_ADDR_DECODE(val) - ring->bd_dma_base);
+
+	/*
+	 * Number required descriptors = number of non-header descriptors +
+	 *				 number of header descriptors +
+	 *				 1x null descriptor
+	 */
+	nhcnt = flexrm_estimate_nonheader_desc_count(msg);
+	count = flexrm_estimate_header_desc_count(nhcnt) + nhcnt + 1;
+
+	/* Check for available descriptor space. */
+	write_offset = ring->bd_write_offset;
+	while (count) {
+		if (!flexrm_is_next_table_desc(ring->bd_base + write_offset))
+			count--;
+		write_offset += RING_DESC_SIZE;
+		if (write_offset == RING_BD_SIZE)
+			write_offset = 0x0;
+		if (write_offset == read_offset)
+			break;
+	}
+	if (count) {
+		spin_lock_irqsave(&ring->lock, flags);
+		if (batch_msg)
+			ring->last_pending_msg = batch_msg;
+		else
+			ring->last_pending_msg = msg;
+		spin_unlock_irqrestore(&ring->lock, flags);
+		ret = 0;
+		exit_cleanup = true;
+		goto exit;
+	}
+
+	/* Write descriptors to ring */
+	next = flexrm_write_descs(msg, nhcnt, reqid,
+			ring->bd_base + ring->bd_write_offset,
+			RING_BD_TOGGLE_VALID(ring->bd_write_offset),
+			ring->bd_base, ring->bd_base + RING_BD_SIZE);
+	if (IS_ERR(next)) {
+		ret = PTR_ERR(next);
+		exit_cleanup = true;
+		goto exit;
+	}
+
+	/* Save ring BD write offset */
+	ring->bd_write_offset = (unsigned long)(next - ring->bd_base);
+
+exit:
+	/* Update error status in message */
+	msg->error = ret;
+
+	/* Cleanup if we failed */
+	if (exit_cleanup) {
+		flexrm_dma_unmap(ring->mbox->dev, msg);
+		ring->requests[reqid] = NULL;
+		ida_simple_remove(&ring->requests_ida, reqid);
+	}
+
+	return ret;
+}
+
+static int flexrm_process_completions(struct flexrm_ring *ring)
+{
+	u64 desc;
+	int err, count = 0;
+	unsigned long flags;
+	struct brcm_message *msg = NULL;
+	u32 reqid, cmpl_read_offset, cmpl_write_offset;
+	struct mbox_chan *chan = &ring->mbox->controller.chans[ring->num];
+
+	spin_lock_irqsave(&ring->lock, flags);
+
+	/* Check last_pending_msg */
+	if (ring->last_pending_msg) {
+		msg = ring->last_pending_msg;
+		ring->last_pending_msg = NULL;
+	}
+
+	/*
+	 * Get current completion read and write offset
+	 *
+	 * Note: We should read completion write pointer atleast once
+	 * after we get a MSI interrupt because HW maintains internal
+	 * MSI status which will allow next MSI interrupt only after
+	 * completion write pointer is read.
+	 */
+	cmpl_write_offset = readl_relaxed(ring->regs + RING_CMPL_WRITE_PTR);
+	cmpl_write_offset *= RING_DESC_SIZE;
+	cmpl_read_offset = ring->cmpl_read_offset;
+	ring->cmpl_read_offset = cmpl_write_offset;
+
+	spin_unlock_irqrestore(&ring->lock, flags);
+
+	/* If last_pending_msg was set then queue it back */
+	if (msg)
+		mbox_send_message(chan, msg);
+
+	/* For each completed request notify mailbox clients */
+	reqid = 0;
+	while (cmpl_read_offset != cmpl_write_offset) {
+		/* Dequeue next completion descriptor */
+		desc = *((u64 *)(ring->cmpl_base + cmpl_read_offset));
+
+		/* Next read offset */
+		cmpl_read_offset += RING_DESC_SIZE;
+		if (cmpl_read_offset == RING_CMPL_SIZE)
+			cmpl_read_offset = 0;
+
+		/* Decode error from completion descriptor */
+		err = flexrm_cmpl_desc_to_error(desc);
+		if (err < 0) {
+			dev_warn(ring->mbox->dev,
+				 "got completion desc=0x%lx with error %d",
+				 (unsigned long)desc, err);
+		}
+
+		/* Determine request id from completion descriptor */
+		reqid = flexrm_cmpl_desc_to_reqid(desc);
+
+		/* Determine message pointer based on reqid */
+		msg = ring->requests[reqid];
+		if (!msg) {
+			dev_warn(ring->mbox->dev,
+				 "null msg pointer for completion desc=0x%lx",
+				 (unsigned long)desc);
+			continue;
+		}
+
+		/* Release reqid for recycling */
+		ring->requests[reqid] = NULL;
+		ida_simple_remove(&ring->requests_ida, reqid);
+
+		/* Unmap DMA mappings */
+		flexrm_dma_unmap(ring->mbox->dev, msg);
+
+		/* Give-back message to mailbox client */
+		msg->error = err;
+		mbox_chan_received_data(chan, msg);
+
+		/* Increment number of completions processed */
+		count++;
+	}
+
+	return count;
+}
+
+static irqreturn_t flexrm_irq_event(int irq, void *dev_id)
+{
+	/* We only have MSI for completions so just wakeup IRQ thread */
+	/* Ring related errors will be informed via completion descriptors */
+
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t flexrm_irq_thread(int irq, void *dev_id)
+{
+	flexrm_process_completions(dev_id);
+
+	return IRQ_HANDLED;
+}
+
+static int flexrm_send_data(struct mbox_chan *chan, void *data)
+{
+	int i, rc;
+	struct flexrm_ring *ring = chan->con_priv;
+	struct brcm_message *msg = data;
+
+	if (msg->type == BRCM_MESSAGE_BATCH) {
+		for (i = msg->batch.msgs_queued;
+		     i < msg->batch.msgs_count; i++) {
+			rc = flexrm_new_request(ring, msg,
+						 &msg->batch.msgs[i]);
+			if (rc) {
+				msg->error = rc;
+				return rc;
+			}
+			msg->batch.msgs_queued++;
+		}
+		return 0;
+	}
+
+	return flexrm_new_request(ring, NULL, data);
+}
+
+static bool flexrm_peek_data(struct mbox_chan *chan)
+{
+	int cnt = flexrm_process_completions(chan->con_priv);
+
+	return (cnt > 0) ? true : false;
+}
+
+static int flexrm_startup(struct mbox_chan *chan)
+{
+	u64 d;
+	u32 val, off;
+	int ret = 0;
+	dma_addr_t next_addr;
+	struct flexrm_ring *ring = chan->con_priv;
+
+	/* Allocate BD memory */
+	ring->bd_base = dma_pool_alloc(ring->mbox->bd_pool,
+				       GFP_KERNEL, &ring->bd_dma_base);
+	if (!ring->bd_base) {
+		dev_err(ring->mbox->dev, "can't allocate BD memory\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	/* Configure next table pointer entries in BD memory */
+	for (off = 0; off < RING_BD_SIZE; off += RING_DESC_SIZE) {
+		next_addr = off + RING_DESC_SIZE;
+		if (next_addr == RING_BD_SIZE)
+			next_addr = 0;
+		next_addr += ring->bd_dma_base;
+		if (RING_BD_ALIGN_CHECK(next_addr))
+			d = flexrm_next_table_desc(RING_BD_TOGGLE_VALID(off),
+						    next_addr);
+		else
+			d = flexrm_null_desc(RING_BD_TOGGLE_INVALID(off));
+		flexrm_write_desc(ring->bd_base + off, d);
+	}
+
+	/* Allocate completion memory */
+	ring->cmpl_base = dma_pool_alloc(ring->mbox->cmpl_pool,
+					 GFP_KERNEL, &ring->cmpl_dma_base);
+	if (!ring->cmpl_base) {
+		dev_err(ring->mbox->dev, "can't allocate completion memory\n");
+		ret = -ENOMEM;
+		goto fail_free_bd_memory;
+	}
+	memset(ring->cmpl_base, 0, RING_CMPL_SIZE);
+
+	/* Request IRQ */
+	if (ring->irq == UINT_MAX) {
+		dev_err(ring->mbox->dev, "ring IRQ not available\n");
+		ret = -ENODEV;
+		goto fail_free_cmpl_memory;
+	}
+	ret = request_threaded_irq(ring->irq,
+				   flexrm_irq_event,
+				   flexrm_irq_thread,
+				   0, dev_name(ring->mbox->dev), ring);
+	if (ret) {
+		dev_err(ring->mbox->dev, "failed to request ring IRQ\n");
+		goto fail_free_cmpl_memory;
+	}
+	ring->irq_requested = true;
+
+	/* Disable/inactivate ring */
+	writel_relaxed(0x0, ring->regs + RING_CONTROL);
+
+	/* Program BD start address */
+	val = BD_START_ADDR_VALUE(ring->bd_dma_base);
+	writel_relaxed(val, ring->regs + RING_BD_START_ADDR);
+
+	/* BD write pointer will be same as HW write pointer */
+	ring->bd_write_offset =
+			readl_relaxed(ring->regs + RING_BD_WRITE_PTR);
+	ring->bd_write_offset *= RING_DESC_SIZE;
+
+	/* Program completion start address */
+	val = CMPL_START_ADDR_VALUE(ring->cmpl_dma_base);
+	writel_relaxed(val, ring->regs + RING_CMPL_START_ADDR);
+
+	/* Ensure last pending message is cleared */
+	ring->last_pending_msg = NULL;
+
+	/* Completion read pointer will be same as HW write pointer */
+	ring->cmpl_read_offset =
+			readl_relaxed(ring->regs + RING_CMPL_WRITE_PTR);
+	ring->cmpl_read_offset *= RING_DESC_SIZE;
+
+	/* Read ring Tx, Rx, and Outstanding counts to clear */
+	readl_relaxed(ring->regs + RING_NUM_REQ_RECV_LS);
+	readl_relaxed(ring->regs + RING_NUM_REQ_RECV_MS);
+	readl_relaxed(ring->regs + RING_NUM_REQ_TRANS_LS);
+	readl_relaxed(ring->regs + RING_NUM_REQ_TRANS_MS);
+	readl_relaxed(ring->regs + RING_NUM_REQ_OUTSTAND);
+
+	/* Configure RING_MSI_CONTROL */
+	val = 0;
+	val |= (ring->msi_timer_val << MSI_TIMER_VAL_SHIFT);
+	val |= BIT(MSI_ENABLE_SHIFT);
+	val |= (ring->msi_count_threshold & MSI_COUNT_MASK) << MSI_COUNT_SHIFT;
+	writel_relaxed(val, ring->regs + RING_MSI_CONTROL);
+
+	/* Enable/activate ring */
+	val = BIT(CONTROL_ACTIVE_SHIFT);
+	writel_relaxed(val, ring->regs + RING_CONTROL);
+
+	return 0;
+
+fail_free_cmpl_memory:
+	dma_pool_free(ring->mbox->cmpl_pool,
+		      ring->cmpl_base, ring->cmpl_dma_base);
+	ring->cmpl_base = NULL;
+fail_free_bd_memory:
+	dma_pool_free(ring->mbox->bd_pool,
+		      ring->bd_base, ring->bd_dma_base);
+	ring->bd_base = NULL;
+fail:
+	return ret;
+}
+
+static void flexrm_shutdown(struct mbox_chan *chan)
+{
+	u32 reqid;
+	unsigned int timeout;
+	struct brcm_message *msg;
+	struct flexrm_ring *ring = chan->con_priv;
+
+	/* Disable/inactivate ring */
+	writel_relaxed(0x0, ring->regs + RING_CONTROL);
+
+	/* Flush ring with timeout of 1s */
+	timeout = 1000;
+	writel_relaxed(BIT(CONTROL_FLUSH_SHIFT),
+			ring->regs + RING_CONTROL);
+	do {
+		if (readl_relaxed(ring->regs + RING_FLUSH_DONE) &
+		    FLUSH_DONE_MASK)
+			break;
+		mdelay(1);
+	} while (timeout--);
+
+	/* Abort all in-flight requests */
+	for (reqid = 0; reqid < RING_MAX_REQ_COUNT; reqid++) {
+		msg = ring->requests[reqid];
+		if (!msg)
+			continue;
+
+		/* Release reqid for recycling */
+		ring->requests[reqid] = NULL;
+		ida_simple_remove(&ring->requests_ida, reqid);
+
+		/* Unmap DMA mappings */
+		flexrm_dma_unmap(ring->mbox->dev, msg);
+
+		/* Give-back message to mailbox client */
+		msg->error = -EIO;
+		mbox_chan_received_data(chan, msg);
+	}
+
+	/* Release IRQ */
+	if (ring->irq_requested) {
+		free_irq(ring->irq, ring);
+		ring->irq_requested = false;
+	}
+
+	/* Free-up completion descriptor ring */
+	if (ring->cmpl_base) {
+		dma_pool_free(ring->mbox->cmpl_pool,
+			      ring->cmpl_base, ring->cmpl_dma_base);
+		ring->cmpl_base = NULL;
+	}
+
+	/* Free-up BD descriptor ring */
+	if (ring->bd_base) {
+		dma_pool_free(ring->mbox->bd_pool,
+			      ring->bd_base, ring->bd_dma_base);
+		ring->bd_base = NULL;
+	}
+}
+
+static bool flexrm_last_tx_done(struct mbox_chan *chan)
+{
+	bool ret;
+	unsigned long flags;
+	struct flexrm_ring *ring = chan->con_priv;
+
+	spin_lock_irqsave(&ring->lock, flags);
+	ret = (ring->last_pending_msg) ? false : true;
+	spin_unlock_irqrestore(&ring->lock, flags);
+
+	return ret;
+}
+
+static const struct mbox_chan_ops flexrm_mbox_chan_ops = {
+	.send_data	= flexrm_send_data,
+	.startup	= flexrm_startup,
+	.shutdown	= flexrm_shutdown,
+	.last_tx_done	= flexrm_last_tx_done,
+	.peek_data	= flexrm_peek_data,
+};
+
+static void flexrm_mbox_msi_write(struct msi_desc *desc, struct msi_msg *msg)
+{
+	struct device *dev = msi_desc_to_dev(desc);
+	struct flexrm_mbox *mbox = dev_get_drvdata(dev);
+	struct flexrm_ring *ring = &mbox->rings[desc->platform.msi_index];
+
+	/* Configure per-Ring MSI registers */
+	writel_relaxed(msg->address_lo, ring->regs + RING_MSI_ADDR_LS);
+	writel_relaxed(msg->address_hi, ring->regs + RING_MSI_ADDR_MS);
+	writel_relaxed(msg->data, ring->regs + RING_MSI_DATA_VALUE);
+}
+
+static struct mbox_chan *flexrm_mbox_of_xlate(struct mbox_controller *cntlr,
+					const struct of_phandle_args *pa)
+{
+	struct mbox_chan *chan;
+	struct flexrm_ring *ring;
+
+	if (pa->args_count < 3)
+		return ERR_PTR(-EINVAL);
+
+	if (pa->args[0] >= cntlr->num_chans)
+		return ERR_PTR(-ENOENT);
+
+	if (pa->args[1] > MSI_COUNT_MASK)
+		return ERR_PTR(-EINVAL);
+
+	if (pa->args[2] > MSI_TIMER_VAL_MASK)
+		return ERR_PTR(-EINVAL);
+
+	chan = &cntlr->chans[pa->args[0]];
+	ring = chan->con_priv;
+	ring->msi_count_threshold = pa->args[1];
+	ring->msi_timer_val = pa->args[2];
+
+	return chan;
+}
+
+static int flexrm_mbox_probe(struct platform_device *pdev)
+{
+	int index, ret = 0;
+	void __iomem *regs;
+	void __iomem *regs_end;
+	struct msi_desc *desc;
+	struct resource *iomem;
+	struct flexrm_ring *ring;
+	struct flexrm_mbox *mbox;
+	struct device *dev = &pdev->dev;
+
+	/* Allocate driver mailbox struct */
+	mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
+	if (!mbox) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+	mbox->dev = dev;
+	platform_set_drvdata(pdev, mbox);
+
+	/* Get resource for registers */
+	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!iomem || (resource_size(iomem) < RING_REGS_SIZE)) {
+		ret = -ENODEV;
+		goto fail;
+	}
+
+	/* Map registers of all rings */
+	mbox->regs = devm_ioremap_resource(&pdev->dev, iomem);
+	if (IS_ERR(mbox->regs)) {
+		ret = PTR_ERR(mbox->regs);
+		dev_err(&pdev->dev, "Failed to remap mailbox regs: %d\n", ret);
+		goto fail;
+	}
+	regs_end = mbox->regs + resource_size(iomem);
+
+	/* Scan and count available rings */
+	mbox->num_rings = 0;
+	for (regs = mbox->regs; regs < regs_end; regs += RING_REGS_SIZE) {
+		if (readl_relaxed(regs + RING_VER) == RING_VER_MAGIC)
+			mbox->num_rings++;
+	}
+	if (!mbox->num_rings) {
+		ret = -ENODEV;
+		goto fail;
+	}
+
+	/* Allocate driver ring structs */
+	ring = devm_kcalloc(dev, mbox->num_rings, sizeof(*ring), GFP_KERNEL);
+	if (!ring) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+	mbox->rings = ring;
+
+	/* Initialize members of driver ring structs */
+	regs = mbox->regs;
+	for (index = 0; index < mbox->num_rings; index++) {
+		ring = &mbox->rings[index];
+		ring->num = index;
+		ring->mbox = mbox;
+		while ((regs < regs_end) &&
+		       (readl_relaxed(regs + RING_VER) != RING_VER_MAGIC))
+			regs += RING_REGS_SIZE;
+		if (regs_end <= regs) {
+			ret = -ENODEV;
+			goto fail;
+		}
+		ring->regs = regs;
+		regs += RING_REGS_SIZE;
+		ring->irq = UINT_MAX;
+		ring->irq_requested = false;
+		ring->msi_timer_val = MSI_TIMER_VAL_MASK;
+		ring->msi_count_threshold = 0x1;
+		ida_init(&ring->requests_ida);
+		memset(ring->requests, 0, sizeof(ring->requests));
+		ring->bd_base = NULL;
+		ring->bd_dma_base = 0;
+		ring->cmpl_base = NULL;
+		ring->cmpl_dma_base = 0;
+		spin_lock_init(&ring->lock);
+		ring->last_pending_msg = NULL;
+		ring->cmpl_read_offset = 0;
+	}
+
+	/* FlexRM is capable of 40-bit physical addresses only */
+	mbox->dma_mask = DMA_BIT_MASK(40);
+	dev->dma_mask = &mbox->dma_mask;
+
+	/* Create DMA pool for ring BD memory */
+	mbox->bd_pool = dma_pool_create("bd", dev, RING_BD_SIZE,
+					1 << RING_BD_ALIGN_ORDER, 0);
+	if (!mbox->bd_pool) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	/* Create DMA pool for ring completion memory */
+	mbox->cmpl_pool = dma_pool_create("cmpl", dev, RING_CMPL_SIZE,
+					  1 << RING_CMPL_ALIGN_ORDER, 0);
+	if (!mbox->cmpl_pool) {
+		ret = -ENOMEM;
+		goto fail_destroy_bd_pool;
+	}
+
+	/* Allocate platform MSIs for each ring */
+	ret = platform_msi_domain_alloc_irqs(dev, mbox->num_rings,
+						flexrm_mbox_msi_write);
+	if (ret)
+		goto fail_destroy_cmpl_pool;
+
+	/* Save alloced IRQ numbers for each ring */
+	for_each_msi_entry(desc, dev) {
+		ring = &mbox->rings[desc->platform.msi_index];
+		ring->irq = desc->irq;
+	}
+
+	/* Initialize mailbox controller */
+	mbox->controller.txdone_irq = false;
+	mbox->controller.txdone_poll = true;
+	mbox->controller.txpoll_period = 1;
+	mbox->controller.ops = &flexrm_mbox_chan_ops;
+	mbox->controller.dev = dev;
+	mbox->controller.num_chans = mbox->num_rings;
+	mbox->controller.of_xlate = flexrm_mbox_of_xlate;
+	mbox->controller.chans = devm_kcalloc(dev, mbox->num_rings,
+				sizeof(*mbox->controller.chans), GFP_KERNEL);
+	if (!mbox->controller.chans) {
+		ret = -ENOMEM;
+		goto fail_free_msis;
+	}
+	for (index = 0; index < mbox->num_rings; index++)
+		mbox->controller.chans[index].con_priv = &mbox->rings[index];
+
+	/* Register mailbox controller */
+	ret = mbox_controller_register(&mbox->controller);
+	if (ret)
+		goto fail_free_msis;
+
+	dev_info(dev, "registered flexrm mailbox with %d channels\n",
+			mbox->controller.num_chans);
+
+	return 0;
+
+fail_free_msis:
+	platform_msi_domain_free_irqs(dev);
+fail_destroy_cmpl_pool:
+	dma_pool_destroy(mbox->cmpl_pool);
+fail_destroy_bd_pool:
+	dma_pool_destroy(mbox->bd_pool);
+fail:
+	return ret;
+}
+
+static int flexrm_mbox_remove(struct platform_device *pdev)
+{
+	int index;
+	struct device *dev = &pdev->dev;
+	struct flexrm_ring *ring;
+	struct flexrm_mbox *mbox = platform_get_drvdata(pdev);
+
+	mbox_controller_unregister(&mbox->controller);
+
+	platform_msi_domain_free_irqs(dev);
+
+	dma_pool_destroy(mbox->cmpl_pool);
+	dma_pool_destroy(mbox->bd_pool);
+
+	for (index = 0; index < mbox->num_rings; index++) {
+		ring = &mbox->rings[index];
+		ida_destroy(&ring->requests_ida);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id flexrm_mbox_of_match[] = {
+	{ .compatible = "brcm,iproc-flexrm-mbox", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, flexrm_mbox_of_match);
+
+static struct platform_driver flexrm_mbox_driver = {
+	.driver = {
+		.name = "brcm-flexrm-mbox",
+		.of_match_table = flexrm_mbox_of_match,
+	},
+	.probe		= flexrm_mbox_probe,
+	.remove		= flexrm_mbox_remove,
+};
+module_platform_driver(flexrm_mbox_driver);
+
+MODULE_AUTHOR("Anup Patel <anup.patel@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom FlexRM mailbox driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mailbox/brcm-message.h b/include/linux/mailbox/brcm-message.h
index 6b55c93..c20b484 100644
--- a/include/linux/mailbox/brcm-message.h
+++ b/include/linux/mailbox/brcm-message.h
@@ -16,6 +16,7 @@
 
 enum brcm_message_type {
 	BRCM_MESSAGE_UNKNOWN = 0,
+	BRCM_MESSAGE_BATCH,
 	BRCM_MESSAGE_SPU,
 	BRCM_MESSAGE_SBA,
 	BRCM_MESSAGE_MAX,
@@ -23,24 +24,29 @@ enum brcm_message_type {
 
 struct brcm_sba_command {
 	u64 cmd;
+	u64 *cmd_dma;
+	dma_addr_t cmd_dma_addr;
 #define BRCM_SBA_CMD_TYPE_A		BIT(0)
 #define BRCM_SBA_CMD_TYPE_B		BIT(1)
 #define BRCM_SBA_CMD_TYPE_C		BIT(2)
 #define BRCM_SBA_CMD_HAS_RESP		BIT(3)
 #define BRCM_SBA_CMD_HAS_OUTPUT		BIT(4)
 	u64 flags;
-	dma_addr_t input;
-	size_t input_len;
 	dma_addr_t resp;
 	size_t resp_len;
-	dma_addr_t output;
-	size_t output_len;
+	dma_addr_t data;
+	size_t data_len;
 };
 
 struct brcm_message {
 	enum brcm_message_type type;
 	union {
 		struct {
+			struct brcm_message *msgs;
+			unsigned int msgs_queued;
+			unsigned int msgs_count;
+		} batch;
+		struct {
 			struct scatterlist *src;
 			struct scatterlist *dst;
 		} spu;
-- 
2.7.4

^ permalink raw reply related

* [PATCH v4 0/2] Broadcom FlexRM ring manager support
From: Anup Patel @ 2017-01-05 11:07 UTC (permalink / raw)
  To: linux-arm-kernel

The Broadcom FlexRM ring manager provides producer-consumer style
ring interface for offload engines on Broadcom iProc SoCs. We can
have one or more instances of Broadcom FlexRM ring manager in a SoC.

This patchset adds a mailbox driver for Broadcom FlexRM ring manager
which can be used by offload engine drivers as mailbox clients.

The Broadcom FlexRM mailbox driver is feature complete for RAID and
Crypto offload engines. We will have incremental patches in-future
for ring-level statistics using debugfs and minor optimizations.

This patchset is based on Linux-4.10-rc2 and it is also available
at flexrm-v4 branch of https://github.com/Broadcom/arm64-linux.git

Changes since v3:
 - Fixed mailbox client example DT node in DT bindings document

Changes since v2:
 - Rebased patches for Linux-4.10-rc2

Changes since v1:
 - Use compatile string as brcm,iproc-flexrm-mbox
 - Rephrase commit message and text in DT bindings patch

Anup Patel (2):
  mailbox: Add driver for Broadcom FlexRM ring manager
  dt-bindings: Add DT bindings info for FlexRM ring manager

 .../bindings/mailbox/brcm,iproc-flexrm-mbox.txt    |  59 ++
 drivers/mailbox/Kconfig                            |  11 +
 drivers/mailbox/Makefile                           |   2 +
 drivers/mailbox/mailbox-flexrm/Makefile            |   6 +
 drivers/mailbox/mailbox-flexrm/flexrm-desc.c       | 764 +++++++++++++++++++
 drivers/mailbox/mailbox-flexrm/flexrm-desc.h       |  47 ++
 drivers/mailbox/mailbox-flexrm/flexrm-main.c       | 829 +++++++++++++++++++++
 include/linux/mailbox/brcm-message.h               |  14 +-
 8 files changed, 1728 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt
 create mode 100644 drivers/mailbox/mailbox-flexrm/Makefile
 create mode 100644 drivers/mailbox/mailbox-flexrm/flexrm-desc.c
 create mode 100644 drivers/mailbox/mailbox-flexrm/flexrm-desc.h
 create mode 100644 drivers/mailbox/mailbox-flexrm/flexrm-main.c

-- 
2.7.4

^ permalink raw reply

* [PATCH v3] arm64: mm: Fix NOMAP page initialization
From: Robert Richter @ 2017-01-05 11:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAKv+Gu_SmTNguC=tSCwYOL2kx-DogLvSYRZc56eGP=JhdrUOsA@mail.gmail.com>

On 04.01.17 13:56:39, Ard Biesheuvel wrote:
> Given that you are touching arch/arm/ as well as arch/arm64, could you
> explain why only arm64 needs this treatment? Is it simply because we
> don't have NUMA support there?

I haven't considered a solution for arch/arm yet. The fixes are
independent. But if that fix would be an excepted solution, it could
be implemented for arm then too. But as you said, since probably only
NUMA is affected, we might not need it there.

-Robert

^ permalink raw reply

* arm64: virt_to_page() does not return right page for a kernel image address
From: Catalin Marinas @ 2017-01-05 10:57 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <322d61f1-0b72-6125-f5cd-153e0baad733@redhat.com>

On Wed, Jan 04, 2017 at 10:39:05AM -0800, Laura Abbott wrote:
> On 01/04/2017 04:13 AM, Catalin Marinas wrote:
> > On Wed, Jan 04, 2017 at 11:19:38AM +0530, Pratyush Anand wrote:
> >> I noticed that on arm64 kmap_atomic() does not return correct address
> >> corresponding to a page located in data section. It causes crash in
> >> kdump kernel with v29 kdump patches. crash happens in a newly
> >> implemented crypto test [1], and the same test fails(even though it
> >> does not crash) in 1st kernel as well.
> >>
> >> Further debugging showed that the physical address returned by
> >> virt_to_phys(kaddr)  and virt_to_phys(kmap_atomic(virt_to_page(kaddr))
> >> + offset_in_page(kaddr)) are not same.
> 
> I think the underlying issue has been resolved but in general, relying
> on virt_to_phys(kmap_atomic(page)) to work at all doesn't seem
> correct. On arm64 and other !CONFIG_HIGHMEM systems this currently
> returns the page_address but if it's actually remapped this isn't
> going to work.

Good point, kmap_atomic() does not always return an address in the
linear map, so virt_to_phys() on such address is not generally expected
to work (unrelated to arm64).

-- 
Catalin

^ permalink raw reply

* [PATCH] i2c: i2c-cadence: Don't register the adapter until it's ready
From: Mike Looijmans @ 2017-01-05 10:47 UTC (permalink / raw)
  To: linux-arm-kernel

The driver calls i2c_add_adapter before writing to config registers,
resulting in dmesg output like this, where devices fail to initialize:

cdns-i2c ff030000.i2c: timeout waiting on completion
pca953x 1-0041: failed reading register
pca953x: probe of 1-0041 failed with error -110
at24 1-0050: 512 byte 24c04 EEPROM, writable, 1 bytes/write
cdns-i2c ff030000.i2c: 100 kHz mmio ff030000 irq 197

The adapter is being used before it completed the "probe". To fix
this, make "i2c_add_adapter" the last thing it calls in probe.
It also makes sense to show the adapter initialization before
the devices on the bus.

Signed-off-by: Mike Looijmans <mike.looijmans@topic.nl>
---
 drivers/i2c/busses/i2c-cadence.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c
index 6869712..7793bb8 100644
--- a/drivers/i2c/busses/i2c-cadence.c
+++ b/drivers/i2c/busses/i2c-cadence.c
@@ -962,10 +962,6 @@ static int cdns_i2c_probe(struct platform_device *pdev)
 		goto err_clk_dis;
 	}
 
-	ret = i2c_add_adapter(&id->adap);
-	if (ret < 0)
-		goto err_clk_dis;
-
 	/*
 	 * Cadence I2C controller has a bug wherein it generates
 	 * invalid read transaction after HW timeout in master receiver mode.
@@ -978,6 +974,10 @@ static int cdns_i2c_probe(struct platform_device *pdev)
 	dev_info(&pdev->dev, "%u kHz mmio %08lx irq %d\n",
 		 id->i2c_clk / 1000, (unsigned long)r_mem->start, id->irq);
 
+	ret = i2c_add_adapter(&id->adap);
+	if (ret < 0)
+		goto err_clk_dis;
+
 	return 0;
 
 err_clk_dis:
-- 
1.9.1

^ permalink raw reply related

* [PATCH v5 13/17] irqdomain: irq_domain_check_msi_remap
From: Auger Eric @ 2017-01-05 10:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <c9c4c159-60ea-8501-5dc2-17bbb24ddfab@arm.com>

Hi Marc,

On 04/01/2017 16:27, Marc Zyngier wrote:
> On 04/01/17 14:11, Auger Eric wrote:
>> Hi Marc,
>>
>> On 04/01/2017 14:46, Marc Zyngier wrote:
>>> Hi Eric,
>>>
>>> On 04/01/17 13:32, Eric Auger wrote:
>>>> This new function checks whether all platform and PCI
>>>> MSI domains implement IRQ remapping. This is useful to
>>>> understand whether VFIO passthrough is safe with respect
>>>> to interrupts.
>>>>
>>>> On ARM typically an MSI controller can sit downstream
>>>> to the IOMMU without preventing VFIO passthrough.
>>>> As such any assigned device can write into the MSI doorbell.
>>>> In case the MSI controller implements IRQ remapping, assigned
>>>> devices will not be able to trigger interrupts towards the
>>>> host. On the contrary, the assignment must be emphasized as
>>>> unsafe with respect to interrupts.
>>>>
>>>> Signed-off-by: Eric Auger <eric.auger@redhat.com>
>>>>
>>>> ---
>>>>
>>>> v4 -> v5:
>>>> - Handle DOMAIN_BUS_FSL_MC_MSI domains
>>>> - Check parents
>>>> ---
>>>>  include/linux/irqdomain.h |  1 +
>>>>  kernel/irq/irqdomain.c    | 41 +++++++++++++++++++++++++++++++++++++++++
>>>>  2 files changed, 42 insertions(+)
>>>>
>>>> diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
>>>> index ab017b2..281a40f 100644
>>>> --- a/include/linux/irqdomain.h
>>>> +++ b/include/linux/irqdomain.h
>>>> @@ -219,6 +219,7 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
>>>>  					 void *host_data);
>>>>  extern struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec,
>>>>  						   enum irq_domain_bus_token bus_token);
>>>> +extern bool irq_domain_check_msi_remap(void);
>>>>  extern void irq_set_default_host(struct irq_domain *host);
>>>>  extern int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
>>>>  				  irq_hw_number_t hwirq, int node,
>>>> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
>>>> index 8c0a0ae..700caea 100644
>>>> --- a/kernel/irq/irqdomain.c
>>>> +++ b/kernel/irq/irqdomain.c
>>>> @@ -278,6 +278,47 @@ struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec,
>>>>  EXPORT_SYMBOL_GPL(irq_find_matching_fwspec);
>>>>  
>>>>  /**
>>>> + * irq_domain_is_msi_remap - Check if @domain or any parent
>>>> + * has MSI remapping support
>>>> + * @domain: domain pointer
>>>> + */
>>>> +static bool irq_domain_is_msi_remap(struct irq_domain *domain)
>>>> +{
>>>> +	struct irq_domain *h = domain;
>>>> +
>>>> +	for (; h; h = h->parent) {
>>>> +		if (h->flags & IRQ_DOMAIN_FLAG_MSI_REMAP)
>>>> +			return true;
>>>> +	}
>>>> +	return false;
>>>> +}
>>>> +
>>>> +/**
>>>> + * irq_domain_check_msi_remap() - Checks whether all MSI
>>>> + * irq domains implement IRQ remapping
>>>> + */
>>>> +bool irq_domain_check_msi_remap(void)
>>>> +{
>>>> +	struct irq_domain *h;
>>>> +	bool ret = true;
>>>> +
>>>> +	mutex_lock(&irq_domain_mutex);
>>>> +	list_for_each_entry(h, &irq_domain_list, link) {
>>>> +		if (((h->bus_token & DOMAIN_BUS_PCI_MSI) ||
>>>> +		     (h->bus_token & DOMAIN_BUS_PLATFORM_MSI) ||
>>>> +		     (h->bus_token & DOMAIN_BUS_FSL_MC_MSI)) &&
>>>> +		     !irq_domain_is_msi_remap(h)) {
>>>
>>> (h->bus_token & DOMAIN_BUS_PCI_MSI) and co looks quite wrong. bus_token
>>> is not a bitmap, and DOMAIN_BUS_* not a single bit value (see enum
>>> irq_domain_bus_token). Surely this should read
>>> (h->bus_token == DOMAIN_BUS_PCI_MSI).
>> Oh I did not notice that. Thanks.
>>
>> Any other comments on the irqdomain side? Do you think the current
>> approach consisting in looking at those bus tokens and their parents
>> looks good?
> 
> To be completely honest, I don't like it much, as having to enumerate
> all the bus types can come up with could become quite a burden in the
> long run. I'd rather be able to identify MSI capable domains by
> construction. I came up with the following approach (fully untested):
> 
> diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
> index 281a40f..7779796 100644
> --- a/include/linux/irqdomain.h
> +++ b/include/linux/irqdomain.h
> @@ -183,8 +183,11 @@ enum {
>  	/* Irq domain is an IPI domain with single virq */
>  	IRQ_DOMAIN_FLAG_IPI_SINGLE	= (1 << 3),
>  
> +	/* Irq domain implements MSIs */
> +	IRQ_DOMAIN_FLAG_MSI		= (1 << 4),
> +
>  	/* Irq domain is MSI remapping capable */
> -	IRQ_DOMAIN_FLAG_MSI_REMAP	= (1 << 4),
> +	IRQ_DOMAIN_FLAG_MSI_REMAP	= (1 << 5),
>  
>  	/*
>  	 * Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved
> @@ -450,6 +453,11 @@ static inline bool irq_domain_is_ipi_single(struct irq_domain *domain)
>  {
>  	return domain->flags & IRQ_DOMAIN_FLAG_IPI_SINGLE;
>  }
> +
> +static inline bool irq_domain_is_msi(struct irq_domain *domain)
> +{
> +	return domain->flags & IRQ_DOMAIN_FLAG_MSI;
> +}
>  #else	/* CONFIG_IRQ_DOMAIN_HIERARCHY */
>  static inline void irq_domain_activate_irq(struct irq_data *data) { }
>  static inline void irq_domain_deactivate_irq(struct irq_data *data) { }
> @@ -481,6 +489,11 @@ static inline bool irq_domain_is_ipi_single(struct irq_domain *domain)
>  {
>  	return false;
>  }
> +
> +static inline bool irq_domain_is_msi(struct irq_domain *domain)
> +{
> +	return false;
> +}
>  #endif	/* CONFIG_IRQ_DOMAIN_HIERARCHY */
>  
>  #else /* CONFIG_IRQ_DOMAIN */
> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
> index 700caea..33b6921 100644
> --- a/kernel/irq/irqdomain.c
> +++ b/kernel/irq/irqdomain.c
> @@ -304,10 +304,7 @@ bool irq_domain_check_msi_remap(void)
>  
>  	mutex_lock(&irq_domain_mutex);
>  	list_for_each_entry(h, &irq_domain_list, link) {
> -		if (((h->bus_token & DOMAIN_BUS_PCI_MSI) ||
> -		     (h->bus_token & DOMAIN_BUS_PLATFORM_MSI) ||
> -		     (h->bus_token & DOMAIN_BUS_FSL_MC_MSI)) &&
> -		     !irq_domain_is_msi_remap(h)) {
> +		if (irq_domain_is_msi(h) && !irq_domain_is_msi_remap(h)) {
>  			ret = false;
>  			goto out;
>  		}
> diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
> index ee23006..b637263 100644
> --- a/kernel/irq/msi.c
> +++ b/kernel/irq/msi.c
> @@ -270,7 +270,7 @@ struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode,
>  	if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
>  		msi_domain_update_chip_ops(info);
>  
> -	return irq_domain_create_hierarchy(parent, 0, 0, fwnode,
> +	return irq_domain_create_hierarchy(parent, IRQ_DOMAIN_FLAG_MSI, 0, fwnode,
>  					   &msi_domain_ops, info);
>  }
>  
> 
> 
> Thoughts?

Don't we need to set the IRQ_DOMAIN_FLAG_MSI flag in
platform_msi_create_device_domain too (drivers/base/platform-msi.c)?

Thanks

Eric
> 
> 	M.
> 

^ permalink raw reply

* [PATCH 1/5] ARM: dts: armada388-clearfog: add phy reset gpio-hog
From: Russell King - ARM Linux @ 2017-01-05 10:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <87zij5dckj.fsf@free-electrons.com>

On Thu, Jan 05, 2017 at 11:29:48AM +0100, Gregory CLEMENT wrote:
> Hi Russell King,
>  
>  On jeu., janv. 05 2017, Russell King - ARM Linux <linux@armlinux.org.uk> wrote:
> 
> > On Wed, Jan 04, 2017 at 05:26:08PM +0100, Gregory CLEMENT wrote:
> >> Hi Russell,
> >>  
> >>  On lun., janv. 02 2017, Russell King <rmk+kernel@armlinux.org.uk> wrote:
> >> 
> >> 
> >> It would be nice to have some word here about this patch. Especially why
> >> we need it now. I guess it is for being less dependent on the
> >> initialization done by the bootloader but maybe you have other reasons.
> >
> > I'm not sure I follow.  This is adding it to the new platform, not the
> > old one.  I guess I should've rolled this into the patch creating the
> > clearfog-base dts file, and this question wouldn't have come up.
> >
> 
> Indeed I missed the fact that it was on the new board as all the other
> patches were related to the common part.
> 
> Do you agree that I squash this patch into the "ARM: dts:
> armada388-clearfog: add base model DTS file" patch?

Yep, thanks.

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply

* [PATCH 1/5] ARM: dts: armada388-clearfog: add phy reset gpio-hog
From: Gregory CLEMENT @ 2017-01-05 10:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20170105101620.GR14217@n2100.armlinux.org.uk>

Hi Russell King,
 
 On jeu., janv. 05 2017, Russell King - ARM Linux <linux@armlinux.org.uk> wrote:

> On Wed, Jan 04, 2017 at 05:26:08PM +0100, Gregory CLEMENT wrote:
>> Hi Russell,
>>  
>>  On lun., janv. 02 2017, Russell King <rmk+kernel@armlinux.org.uk> wrote:
>> 
>> 
>> It would be nice to have some word here about this patch. Especially why
>> we need it now. I guess it is for being less dependent on the
>> initialization done by the bootloader but maybe you have other reasons.
>
> I'm not sure I follow.  This is adding it to the new platform, not the
> old one.  I guess I should've rolled this into the patch creating the
> clearfog-base dts file, and this question wouldn't have come up.
>

Indeed I missed the fact that it was on the new board as all the other
patches were related to the common part.

Do you agree that I squash this patch into the "ARM: dts:
armada388-clearfog: add base model DTS file" patch?

Thanks,

Gregory

>> 
>> > Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
>> > ---
>> >  arch/arm/boot/dts/armada-388-clearfog-base.dts | 15 +++++++++++++++
>> >  1 file changed, 15 insertions(+)
>> >
>> > diff --git a/arch/arm/boot/dts/armada-388-clearfog-base.dts b/arch/arm/boot/dts/armada-388-clearfog-base.dts
>> > index f86e1876fb38..da788ea40717 100644
>> > --- a/arch/arm/boot/dts/armada-388-clearfog-base.dts
>> > +++ b/arch/arm/boot/dts/armada-388-clearfog-base.dts
>> > @@ -74,7 +74,17 @@
>> >  	phy = <&phy1>;
>> >  };
>> >  
>> > +&gpio0 {
>> > +	phy1_reset {
>> > +		gpio-hog;
>> > +		gpios = <19 GPIO_ACTIVE_LOW>;
>> > +		output-low;
>> > +		line-name = "phy1-reset";
>> > +	};
>> > +};
>> > +
>> >  &mdio {
>> > +	pinctrl-0 = <&mdio_pins &microsom_phy_clk_pins &clearfog_phy_pins>;
>> >  	phy1: ethernet-phy at 1 {
>> >  		/*
>> >  		 * Annoyingly, the marvell phy driver configures the LED
>> > @@ -87,6 +97,11 @@
>> >  };
>> >  
>> >  &pinctrl {
>> > +	/* phy1 reset */
>> > +	clearfog_phy_pins: clearfog-phy-pins {
>> > +		marvell,pins = "mpp19";
>> > +		marvell,function = "gpio";
>> > +	};
>> >  	rear_button_pins: rear-button-pins {
>> >  		marvell,pins = "mpp44";
>> >  		marvell,function = "gpio";
>> > -- 
>> > 2.7.4
>> >
>> 
>> -- 
>> Gregory Clement, Free Electrons
>> Kernel, drivers, real-time and embedded Linux
>> development, consulting, training and support.
>> http://free-electrons.com
>
> -- 
> RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
> FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
> according to speedtest.net.
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

-- 
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

^ permalink raw reply

* [PATCH 03/22] iio: adc: add support for X-Powers AXP20X and AXP22X PMICs ADCs
From: Chen-Yu Tsai @ 2017-01-05 10:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <cf4590d1-5381-f1da-7271-e6e7fee0c479@free-electrons.com>

On Thu, Jan 5, 2017 at 5:50 PM, Quentin Schulz
<quentin.schulz@free-electrons.com> wrote:
> On 05/01/2017 09:27, Chen-Yu Tsai wrote:
>> On Thu, Jan 5, 2017 at 4:06 PM, Quentin Schulz
>> <quentin.schulz@free-electrons.com> wrote:
>>> Hi Chen-Yu,
>>>
>>> On 05/01/2017 06:42, Chen-Yu Tsai wrote:
>>>> On Tue, Jan 3, 2017 at 12:37 AM, Quentin Schulz
>>>> <quentin.schulz@free-electrons.com> wrote:
>>> [...]
>>>>> +
>>>>> +#define AXP20X_ADC_RATE_MASK                   (3 << 6)
>>>>> +#define AXP20X_ADC_RATE_25HZ                   (0 << 6)
>>>>> +#define AXP20X_ADC_RATE_50HZ                   BIT(6)
>>>>
>>>> Please be consistent with the format.
>>>>
>>>>> +#define AXP20X_ADC_RATE_100HZ                  (2 << 6)
>>>>> +#define AXP20X_ADC_RATE_200HZ                  (3 << 6)
>>>>> +
>>>>> +#define AXP22X_ADC_RATE_100HZ                  (0 << 6)
>>>>> +#define AXP22X_ADC_RATE_200HZ                  BIT(6)
>>>>> +#define AXP22X_ADC_RATE_400HZ                  (2 << 6)
>>>>> +#define AXP22X_ADC_RATE_800HZ                  (3 << 6)
>>>>
>>>> These are power-of-2 multiples of some base rate. May I suggest
>>>> a formula macro instead. Either way, you seem to be using only
>>>> one value. Will this be made configurable in the future?
>>>>
>>>
>>> Yes, I could use a formula macro instead. No plan to make it
>>> configurable, should I make it configurable?
>>
>> I don't see a use case for that atm.
>>
>>>>> +
>>>>> +#define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg)       \
>>>>> +       {                                                       \
>>>>> +               .type = _type,                                  \
>>>>> +               .indexed = 1,                                   \
>>>>> +               .channel = _channel,                            \
>>>>> +               .address = _reg,                                \
>>>>> +               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
>>>>> +                                     BIT(IIO_CHAN_INFO_SCALE), \
>>>>> +               .datasheet_name = _name,                        \
>>>>> +       }
>>>>> +
>>>>> +#define AXP20X_ADC_CHANNEL_OFFSET(_channel, _name, _type, _reg) \
>>>>> +       {                                                       \
>>>>> +               .type = _type,                                  \
>>>>> +               .indexed = 1,                                   \
>>>>> +               .channel = _channel,                            \
>>>>> +               .address = _reg,                                \
>>>>> +               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
>>>>> +                                     BIT(IIO_CHAN_INFO_SCALE) |\
>>>>> +                                     BIT(IIO_CHAN_INFO_OFFSET),\
>>>>> +               .datasheet_name = _name,                        \
>>>>> +       }
>>>>> +
>>>>> +struct axp20x_adc_iio {
>>>>> +       struct iio_dev          *indio_dev;
>>>>> +       struct regmap           *regmap;
>>>>> +};
>>>>> +
>>>>> +enum axp20x_adc_channel {
>>>>> +       AXP20X_ACIN_V = 0,
>>>>> +       AXP20X_ACIN_I,
>>>>> +       AXP20X_VBUS_V,
>>>>> +       AXP20X_VBUS_I,
>>>>> +       AXP20X_TEMP_ADC,
>>>>
>>>> PMIC_TEMP would be better. And please save a slot for TS input.
>>>>
>>>
>>> ACK.
>>>
>>> Hum.. I'm wondering what should be the IIO type of the TS input channel
>>> then? The TS Pin can be used in two modes: either to monitor the
>>> temperature of the battery or as an external ADC, at least that's what I
>>> understand from the datasheet.
>>
>> AFAIK the battery charge/discharge high/low temperature threshold
>> registers take values in terms of voltage, not actual temperature.
>> And the temperature readout kind of depends on the thermoresistor
>> one is using. So I think "voltage" would be the proper type.
>>
>
> ACK. Should I just add TS_IN in axp20x_adc_channel enum but not add it
> in the exposed IIO channels ("saving" the slot but not using it)?

Sure. Or you could skip the number with

    AXP20X_GPIO0_V = 6,

>>>
>>>>> +       AXP20X_GPIO0_V,
>>>>> +       AXP20X_GPIO1_V,
>>>>
>>>> Please skip a slot for "battery instantaneous power".
>>>>
>>>>> +       AXP20X_BATT_V,
>>>>> +       AXP20X_BATT_CHRG_I,
>>>>> +       AXP20X_BATT_DISCHRG_I,
>>>>> +       AXP20X_IPSOUT_V,
>>>>> +};
>>>>> +
>>>>> +enum axp22x_adc_channel {
>>>>> +       AXP22X_TEMP_ADC = 0,
>>>>
>>>> Same comments as AXP20X_TEMP_ADC.
>>>>
>>>>> +       AXP22X_BATT_V,
>>>>> +       AXP22X_BATT_CHRG_I,
>>>>> +       AXP22X_BATT_DISCHRG_I,
>>>>> +};
>>>>
>>>> Shouldn't these channel numbers be exported as part of the device tree
>>>> bindings? At the very least, they shouldn't be changed.
>>>>
>>>
>>> I don't understand what you mean by that. Do you mean you want a
>>> consistent numbering between the AXP20X and the AXP22X, so that
>>> AXP22X_BATT_V would have the same channel number than AXP20X_BATT_V?
>>>
>>> Could you explain a bit more your thoughts on the channel numbers being
>>> exported as part of the device tree bindings?
>>
>> What I meant was that, since you are referencing the channels in the
>> device tree, the numbering scheme would be part of the device tree
>> binding, and should never be changed. So either these would be macros
>> in include/dt-bindings/, or a big warning should be put before it.
>>
>
> ACK.
>
>> But see my reply on patch 7, about do we actually need to expose this
>> in the device tree.
>>
>
> I don't know what's the best.

Then let's not expose it in the device tree for now. It's easier to
add it later than to remove it.

>
>>>> Also please add a comment saying that the channels are numbered
>>>> in the order of their respective registers, and not the table
>>>> describing the ADCs in the datasheet (9.7 Signal Capture for AXP209
>>>> and 9.5 E-Gauge for AXP221).
>>>>
>>>
>>> Yes I can.
>>>
>>> What about Rob wanting channel numbers to start at zero for each
>>> different IIO type (i.e., today we have AXP22X_BATT_CHRG_I being
>>> exported as in_current1_raw whereas he wants in_current0_raw).
>>
>> Hmm... I missed this. Are you talking about IIO or hwmon? IIRC
>> hwmon numbers things starting at 1.
>>
>
> About IIO.
>
> Today, we have exposed:
> in_voltage0_raw for acin_v
> in_current1_raw for acin_i
> in_voltage2_raw for vbus_v
> in_current3_raw for vbus_i
> in_temp4_raw for adc_temp
> in_voltage5_raw for gpio0_v
> in_voltage6_raw for gpio1_v
> in_voltage7_raw for batt_v
> in_current8_raw for batt_chrg_i
> in_current9_raw for batt_dischrg_i
> in_voltage10_raw for ipsout_v
>
> but I think what Rob wants is:
>
> in_voltage0_raw acin_v
> in_current0_raw for acin_i
> in_voltage1_raw for vbus_v
> in_current1_raw for vbus_i
> in_temp_raw for adc_temp
> in_voltage2_raw for gpio0_v
> in_voltage3_raw for gpio1_v
> in_voltage4_raw for batt_v
> in_current2_raw for batt_chrg_i
> in_current3_raw for batt_dischrg_i
> in_voltage5_raw for ipsout_v

I think that's doable. If I understand IIO code correctly, the channel
number is not used anywhere in the core code for dereferencing. It's
used for sysfs entries (when .indexed is set) and events. However
the twl4030 and twl6030 drivers use our current exposed scheme.
I suppose its best to get some input from the IIO maintainer.

So if we want, we could have the following channel mapping:

  0 -> acin
  1 -> vbus
  2 -> pmic
  3 -> gpio0
  4 -> gpio1
  5 -> ipsout
  6 -> battery

Each channel could have mulitple entries in axp20x_adc_channels[],
one for each type of reading. You might need multiple channels for
the battery to cover charge and discharge currents.

Your callback functions would get a bit messier though.

>
>>> [...]
>>>>> +static int axp22x_adc_read_raw(struct iio_dev *indio_dev,
>>>>> +                              struct iio_chan_spec const *channel, int *val,
>>>>> +                              int *val2)
>>>>> +{
>>>>> +       struct axp20x_adc_iio *info = iio_priv(indio_dev);
>>>>> +       int size = 12, ret;
>>>>> +
>>>>> +       switch (channel->channel) {
>>>>> +       case AXP22X_BATT_DISCHRG_I:
>>>>> +               size = 13;
>>>>> +       case AXP22X_TEMP_ADC:
>>>>> +       case AXP22X_BATT_V:
>>>>> +       case AXP22X_BATT_CHRG_I:
>>>>
>>>> According to the datasheet, AXP22X_BATT_CHRG_I is also 13 bits wide.
>>>>
>>>
>>> Where did you get that?
>>>
>>> Also, the datasheet is inconsistent:
>>>  - 9.5 E-Gauge Fuel Gauge system => the min value is at 0x0 and the max
>>> value at 0xfff for all channels, that's 12 bits.
>>>  - 10.1.4 ADC Data => all channels except battery discharge current are
>>> on 12 bits (8 high, 4 low).
>>
>> My datasheets (AXP221 v1.6, AXP221s v1.2, AXP223 v1.1, all Chinese) say
>> in 10.1.4:
>>
>>   - 7A: battery charge current high 5 bits
>>   - 7B: battery charge current low 8 bits
>>   - 7C: battery discharge current high 5 bits
>>   - 7D: battery discharge current low 8 bits
>>
>
> AXP223 v1.1 in English in 10.1.4[1]:
>     - 7A: battery charge current high 8 bits
>     - 7B: battery charge current low 4 bits
>     - 7C: battery discharge current high 8 bits
>     - 7D: battery discharge current low 5 bits
>
> Note that it's 8 bits for high and 4/5 bits for low while you wrote 4/5
> bits high and low 8 bits (typos or actually what's written in the
> datasheet?).

Typo on my end, sorry. It's high 8bits and low 5/4 bits.

Apart from that, the Chinese and English versions don't match for the
battery charge current. :(

Would it be possible for you to test this? As in, have the module running,
and charging a battery, and have a multimeter in series with the battery
to verify the readings.

Regards
ChenYu

>
> Hum.. from the reg reading function[2] I would say that the correct
> formula is high on 8 bits and low on 4/5 bits.
>
> So hum.. what do we do?
>
> [1] http://dl.linux-sunxi.org/AXP/AXP223-en.pdf
> [2] http://lxr.free-electrons.com/source/include/linux/mfd/axp20x.h#L564
>
>>>
>>> [...]
>>>>> +static int axp22x_read_raw(struct iio_dev *indio_dev,
>>>>> +                          struct iio_chan_spec const *chan, int *val,
>>>>> +                          int *val2, long mask)
>>>>> +{
>>>>> +       switch (mask) {
>>>>> +       case IIO_CHAN_INFO_OFFSET:
>>>>> +               *val = -2667;
>>>>
>>>> Datasheet says -267.7 C, or -2677 here.
>>>>
>>>
>>> The formula in the datasheet is (in milli Celsius):
>>>  processed = raw * 100 - 266700;
>>>
>>> while the IIO framework asks for a scale and an offset which are then
>>> applied as:
>>>  processed = (raw + offset) * scale;
>>>
>>> Thus by factorizing, we get:
>>>  processed = (raw - 2667) * 100;
>>
>> What I meant was that your lower end value is off by one degree,
>> -266.7 in your code vs -267.7 in the datasheet.
>>
>
> Indeed. Thanks.
>
>>>
>>> [...]
>>>>> +static int axp20x_remove(struct platform_device *pdev)
>>>>> +{
>>>>> +       struct axp20x_adc_iio *info;
>>>>> +       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>>>>> +
>>>>> +       info = iio_priv(indio_dev);
>>>>
>>>> Nit: you could just reverse the 2 declarations above and join this
>>>> line after struct axp20x_adc_iio *info;
>>>>
>>>>> +       regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
>>>>> +       regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
>>>>
>>>> The existing VBUS power supply driver enables the VBUS ADC bits itself,
>>>> and does not check them later on. This means if one were to remove this
>>>> axp20x-adc module, the voltage/current readings in the VBUS power supply
>>>> would be invalid. Some sort of workaround would be needed here in this
>>>> driver of the VBUS driver.
>>>>
>>>
>>> That would be one reason to migrate the VBUS driver to use the IIO
>>> channels, wouldn't it?
>>
>> It is, preferably without changing the device tree.
>>
>
> Yes, of course.
>
> Thanks,
> Quentin
>
> --
> Quentin Schulz, Free Electrons
> Embedded Linux and Kernel engineering
> http://free-electrons.com

^ permalink raw reply

* [PATCH 1/5] ARM: dts: armada388-clearfog: add phy reset gpio-hog
From: Russell King - ARM Linux @ 2017-01-05 10:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <871swig5b3.fsf@free-electrons.com>

On Wed, Jan 04, 2017 at 05:26:08PM +0100, Gregory CLEMENT wrote:
> Hi Russell,
>  
>  On lun., janv. 02 2017, Russell King <rmk+kernel@armlinux.org.uk> wrote:
> 
> 
> It would be nice to have some word here about this patch. Especially why
> we need it now. I guess it is for being less dependent on the
> initialization done by the bootloader but maybe you have other reasons.

I'm not sure I follow.  This is adding it to the new platform, not the
old one.  I guess I should've rolled this into the patch creating the
clearfog-base dts file, and this question wouldn't have come up.

> 
> Thanks,
> 
> Gregory
> 
> > Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
> > ---
> >  arch/arm/boot/dts/armada-388-clearfog-base.dts | 15 +++++++++++++++
> >  1 file changed, 15 insertions(+)
> >
> > diff --git a/arch/arm/boot/dts/armada-388-clearfog-base.dts b/arch/arm/boot/dts/armada-388-clearfog-base.dts
> > index f86e1876fb38..da788ea40717 100644
> > --- a/arch/arm/boot/dts/armada-388-clearfog-base.dts
> > +++ b/arch/arm/boot/dts/armada-388-clearfog-base.dts
> > @@ -74,7 +74,17 @@
> >  	phy = <&phy1>;
> >  };
> >  
> > +&gpio0 {
> > +	phy1_reset {
> > +		gpio-hog;
> > +		gpios = <19 GPIO_ACTIVE_LOW>;
> > +		output-low;
> > +		line-name = "phy1-reset";
> > +	};
> > +};
> > +
> >  &mdio {
> > +	pinctrl-0 = <&mdio_pins &microsom_phy_clk_pins &clearfog_phy_pins>;
> >  	phy1: ethernet-phy at 1 {
> >  		/*
> >  		 * Annoyingly, the marvell phy driver configures the LED
> > @@ -87,6 +97,11 @@
> >  };
> >  
> >  &pinctrl {
> > +	/* phy1 reset */
> > +	clearfog_phy_pins: clearfog-phy-pins {
> > +		marvell,pins = "mpp19";
> > +		marvell,function = "gpio";
> > +	};
> >  	rear_button_pins: rear-button-pins {
> >  		marvell,pins = "mpp44";
> >  		marvell,function = "gpio";
> > -- 
> > 2.7.4
> >
> 
> -- 
> Gregory Clement, Free Electrons
> Kernel, drivers, real-time and embedded Linux
> development, consulting, training and support.
> http://free-electrons.com

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply

* [PATCH] cpufreq: Remove CONFIG_CPU_FREQ_STAT_DETAILS config option
From: Chanwoo Choi @ 2017-01-05 10:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <d4299228a500a889c2b4b9e305674f3d1ea9ae06.1483604760.git.viresh.kumar@linaro.org>

Hi Viresh,

On 2017? 01? 05? 17:27, Viresh Kumar wrote:
> This doesn't have any benefit apart from saving a small amount of memory
> when it is disabled. The ifdef hackery in the code makes it dirty
> unnecessarily.
> 
> Clean it up by removing the Kconfig option completely. Few defconfigs
> are also updated and CONFIG_CPU_FREQ_STAT_DETAILS is replaced with
> CONFIG_CPU_FREQ_STAT now in them, as users wanted stats to be enabled.
> 
> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
> ---
>  arch/arm/configs/exynos_defconfig         |  2 +-
>  arch/arm/configs/multi_v5_defconfig       |  2 +-
>  arch/arm/configs/multi_v7_defconfig       |  2 +-
>  arch/arm/configs/mvebu_v5_defconfig       |  2 +-
>  arch/arm/configs/pxa_defconfig            |  2 +-
>  arch/arm/configs/shmobile_defconfig       |  2 +-
>  arch/mips/configs/lemote2f_defconfig      |  1 -
>  arch/powerpc/configs/ppc6xx_defconfig     |  1 -
>  arch/sh/configs/sh7785lcr_32bit_defconfig |  2 +-
>  drivers/cpufreq/Kconfig                   |  8 --------
>  drivers/cpufreq/cpufreq_stats.c           | 14 --------------
>  11 files changed, 7 insertions(+), 31 deletions(-)


I agree. Looks good to me.
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>

[snip]

-- 
Best Regards,
Chanwoo Choi
Samsung Electronics
-------------- next part --------------
A non-text attachment was scrubbed...
Name: cw00_choi.vcf
Type: text/x-vcard
Size: 6 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170105/206bbca6/attachment.vcf>

^ permalink raw reply

* [PATCH 2/2] mmc: sdhci-iproc: Increase max_blk_size for bcm2835
From: Adrian Hunter @ 2017-01-05  9:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483111474-29907-3-git-send-email-stefan.wahren@i2se.com>

On 30/12/16 17:24, Stefan Wahren wrote:
> According to the BCM2835 datasheet the maximum block size for the
> eMMC module is restricted to the internal data FIFO which is 1024 byte.
> But this is still an improvement to the default of 512 byte.
> 
> Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>

Acked-by: Adrian Hunter <adrian.hunter@intel.com>

> ---
>  drivers/mmc/host/sdhci-iproc.c |    4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
> index 30b3fdf..3275d49 100644
> --- a/drivers/mmc/host/sdhci-iproc.c
> +++ b/drivers/mmc/host/sdhci-iproc.c
> @@ -218,7 +218,9 @@ static void sdhci_iproc_writeb(struct sdhci_host *host, u8 val, int reg)
>  
>  static const struct sdhci_iproc_data bcm2835_data = {
>  	.pdata = &sdhci_bcm2835_pltfm_data,
> -	.caps = SDHCI_CAN_VDD_330 |
> +	.caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT)
> +			& SDHCI_MAX_BLOCK_MASK) |
> +		SDHCI_CAN_VDD_330 |
>  		SDHCI_CAN_DO_HISPD,
>  	.caps1 = SDHCI_DRIVER_TYPE_A |
>  		 SDHCI_DRIVER_TYPE_C,
> 

^ permalink raw reply

* [PATCH v6 03/14] ACPI: ARM64: IORT: add missing comment for iort_dev_find_its_id()
From: Lorenzo Pieralisi @ 2017-01-05  9:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4915fe90-ea61-45b5-491d-0628456abed2@linaro.org>

On Thu, Jan 05, 2017 at 02:05:06PM +0800, Hanjun Guo wrote:
> On 2017/1/4 22:34, Lorenzo Pieralisi wrote:
> >On Mon, Jan 02, 2017 at 09:31:34PM +0800, Hanjun Guo wrote:
> >>We are missing req_id's comment for iort_dev_find_its_id(),
> >>add it back.
> >
> >"Add missing req_id parameter to the iort_dev_find_its_id() function
> >kernel-doc comment."
> >
> >>Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
> >>Tested-by: Majun <majun258@huawei.com>
> >>Tested-by: Xinwei Kong <kong.kongxinwei@hisilicon.com>
> >>Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> >>Cc: Tomasz Nowicki <tn@semihalf.com>
> >>---
> >> drivers/acpi/arm64/iort.c | 1 +
> >> 1 file changed, 1 insertion(+)
> >>
> >>diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
> >>index 46e2d82..174e983 100644
> >>--- a/drivers/acpi/arm64/iort.c
> >>+++ b/drivers/acpi/arm64/iort.c
> >>@@ -446,6 +446,7 @@ u32 iort_msi_map_rid(struct device *dev, u32 req_id)
> >> /**
> >>  * iort_dev_find_its_id() - Find the ITS identifier for a device
> >>  * @dev: The device.
> >>+ * @req_id: Device's Requster ID
> >
> >s/Requster/Requester
> >
> >We can send it upstream independently along with some other patches
> >in this series but I will have a look at the whole series first.
> 
> Do you mean go to 4.10-rcx?

Yes, technically it is a fix, not urgent at all though.

Lorenzo

^ permalink raw reply

* [PATCH 1/2] mmc: sdhci-iproc: Apply caps from bcm2835-mmc driver
From: Adrian Hunter @ 2017-01-05  9:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483111474-29907-2-git-send-email-stefan.wahren@i2se.com>

On 30/12/16 17:24, Stefan Wahren wrote:
> Since the mmc module on bcm2835 neither provide a capabilities register nor 
> free documentation we must rely on the downstream implementation [1].
> 
> So enable the following capabilities for bcm2835:
> 
> MMC_CAP_MMC_HIGHSPEED
> MMC_CAP_SD_HIGHSPEED
> MMC_CAP_DRIVER_TYPE_A
> MMC_CAP_DRIVER_TYPE_C
> 
> [1] - https://github.com/raspberrypi/linux/blob/rpi-4.4.y/drivers/mmc/host/bcm2835-mmc.c
> 
> Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>

Acked-by: Adrian Hunter <adrian.hunter@intel.com>

> ---
>  drivers/mmc/host/sdhci-iproc.c |    9 ++++++---
>  1 file changed, 6 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
> index d7046d6..30b3fdf 100644
> --- a/drivers/mmc/host/sdhci-iproc.c
> +++ b/drivers/mmc/host/sdhci-iproc.c
> @@ -211,14 +211,17 @@ static void sdhci_iproc_writeb(struct sdhci_host *host, u8 val, int reg)
>  static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = {
>  	.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
>  		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
> -		  SDHCI_QUIRK_MISSING_CAPS,
> +		  SDHCI_QUIRK_MISSING_CAPS |
> +		  SDHCI_QUIRK_NO_HISPD_BIT,
>  	.ops = &sdhci_iproc_32only_ops,
>  };
>  
>  static const struct sdhci_iproc_data bcm2835_data = {
>  	.pdata = &sdhci_bcm2835_pltfm_data,
> -	.caps = SDHCI_CAN_VDD_330,
> -	.caps1 = 0x00000000,
> +	.caps = SDHCI_CAN_VDD_330 |
> +		SDHCI_CAN_DO_HISPD,
> +	.caps1 = SDHCI_DRIVER_TYPE_A |
> +		 SDHCI_DRIVER_TYPE_C,
>  	.mmc_caps = 0x00000000,
>  };
>  
> 

^ permalink raw reply

* [RFC 1/4] mm: remove unused TASK_SIZE_OF()
From: David Laight @ 2017-01-05  9:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161230155634.8692-2-dsafonov@virtuozzo.com>

From: Dmitry Safonov
> Sent: 30 December 2016 15:57
> All users of TASK_SIZE_OF(tsk) have migrated to mm->task_size or
> TASK_SIZE_MAX since:
> commit d696ca016d57 ("x86/fsgsbase/64: Use TASK_SIZE_MAX for
> FSBASE/GSBASE upper limits"),
> commit a06db751c321 ("pagemap: check permissions and capabilities at
> open time"),
...
> +#define TASK_SIZE	        (current->thread.task_size)

I'm not sure I like he hidden 'current' argument to an
apparent constant.

	David

^ permalink raw reply

* [PATCH 03/22] iio: adc: add support for X-Powers AXP20X and AXP22X PMICs ADCs
From: Quentin Schulz @ 2017-01-05  9:50 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAGb2v66n46KQAzQBUrDwx7sYADkRm3_XyCHf9imN+FiDkHfP1w@mail.gmail.com>

On 05/01/2017 09:27, Chen-Yu Tsai wrote:
> On Thu, Jan 5, 2017 at 4:06 PM, Quentin Schulz
> <quentin.schulz@free-electrons.com> wrote:
>> Hi Chen-Yu,
>>
>> On 05/01/2017 06:42, Chen-Yu Tsai wrote:
>>> On Tue, Jan 3, 2017 at 12:37 AM, Quentin Schulz
>>> <quentin.schulz@free-electrons.com> wrote:
>> [...]
>>>> +
>>>> +#define AXP20X_ADC_RATE_MASK                   (3 << 6)
>>>> +#define AXP20X_ADC_RATE_25HZ                   (0 << 6)
>>>> +#define AXP20X_ADC_RATE_50HZ                   BIT(6)
>>>
>>> Please be consistent with the format.
>>>
>>>> +#define AXP20X_ADC_RATE_100HZ                  (2 << 6)
>>>> +#define AXP20X_ADC_RATE_200HZ                  (3 << 6)
>>>> +
>>>> +#define AXP22X_ADC_RATE_100HZ                  (0 << 6)
>>>> +#define AXP22X_ADC_RATE_200HZ                  BIT(6)
>>>> +#define AXP22X_ADC_RATE_400HZ                  (2 << 6)
>>>> +#define AXP22X_ADC_RATE_800HZ                  (3 << 6)
>>>
>>> These are power-of-2 multiples of some base rate. May I suggest
>>> a formula macro instead. Either way, you seem to be using only
>>> one value. Will this be made configurable in the future?
>>>
>>
>> Yes, I could use a formula macro instead. No plan to make it
>> configurable, should I make it configurable?
> 
> I don't see a use case for that atm.
> 
>>>> +
>>>> +#define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg)       \
>>>> +       {                                                       \
>>>> +               .type = _type,                                  \
>>>> +               .indexed = 1,                                   \
>>>> +               .channel = _channel,                            \
>>>> +               .address = _reg,                                \
>>>> +               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
>>>> +                                     BIT(IIO_CHAN_INFO_SCALE), \
>>>> +               .datasheet_name = _name,                        \
>>>> +       }
>>>> +
>>>> +#define AXP20X_ADC_CHANNEL_OFFSET(_channel, _name, _type, _reg) \
>>>> +       {                                                       \
>>>> +               .type = _type,                                  \
>>>> +               .indexed = 1,                                   \
>>>> +               .channel = _channel,                            \
>>>> +               .address = _reg,                                \
>>>> +               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
>>>> +                                     BIT(IIO_CHAN_INFO_SCALE) |\
>>>> +                                     BIT(IIO_CHAN_INFO_OFFSET),\
>>>> +               .datasheet_name = _name,                        \
>>>> +       }
>>>> +
>>>> +struct axp20x_adc_iio {
>>>> +       struct iio_dev          *indio_dev;
>>>> +       struct regmap           *regmap;
>>>> +};
>>>> +
>>>> +enum axp20x_adc_channel {
>>>> +       AXP20X_ACIN_V = 0,
>>>> +       AXP20X_ACIN_I,
>>>> +       AXP20X_VBUS_V,
>>>> +       AXP20X_VBUS_I,
>>>> +       AXP20X_TEMP_ADC,
>>>
>>> PMIC_TEMP would be better. And please save a slot for TS input.
>>>
>>
>> ACK.
>>
>> Hum.. I'm wondering what should be the IIO type of the TS input channel
>> then? The TS Pin can be used in two modes: either to monitor the
>> temperature of the battery or as an external ADC, at least that's what I
>> understand from the datasheet.
> 
> AFAIK the battery charge/discharge high/low temperature threshold
> registers take values in terms of voltage, not actual temperature.
> And the temperature readout kind of depends on the thermoresistor
> one is using. So I think "voltage" would be the proper type.
> 

ACK. Should I just add TS_IN in axp20x_adc_channel enum but not add it
in the exposed IIO channels ("saving" the slot but not using it)?

>>
>>>> +       AXP20X_GPIO0_V,
>>>> +       AXP20X_GPIO1_V,
>>>
>>> Please skip a slot for "battery instantaneous power".
>>>
>>>> +       AXP20X_BATT_V,
>>>> +       AXP20X_BATT_CHRG_I,
>>>> +       AXP20X_BATT_DISCHRG_I,
>>>> +       AXP20X_IPSOUT_V,
>>>> +};
>>>> +
>>>> +enum axp22x_adc_channel {
>>>> +       AXP22X_TEMP_ADC = 0,
>>>
>>> Same comments as AXP20X_TEMP_ADC.
>>>
>>>> +       AXP22X_BATT_V,
>>>> +       AXP22X_BATT_CHRG_I,
>>>> +       AXP22X_BATT_DISCHRG_I,
>>>> +};
>>>
>>> Shouldn't these channel numbers be exported as part of the device tree
>>> bindings? At the very least, they shouldn't be changed.
>>>
>>
>> I don't understand what you mean by that. Do you mean you want a
>> consistent numbering between the AXP20X and the AXP22X, so that
>> AXP22X_BATT_V would have the same channel number than AXP20X_BATT_V?
>>
>> Could you explain a bit more your thoughts on the channel numbers being
>> exported as part of the device tree bindings?
> 
> What I meant was that, since you are referencing the channels in the
> device tree, the numbering scheme would be part of the device tree
> binding, and should never be changed. So either these would be macros
> in include/dt-bindings/, or a big warning should be put before it.
> 

ACK.

> But see my reply on patch 7, about do we actually need to expose this
> in the device tree.
> 

I don't know what's the best.

>>> Also please add a comment saying that the channels are numbered
>>> in the order of their respective registers, and not the table
>>> describing the ADCs in the datasheet (9.7 Signal Capture for AXP209
>>> and 9.5 E-Gauge for AXP221).
>>>
>>
>> Yes I can.
>>
>> What about Rob wanting channel numbers to start at zero for each
>> different IIO type (i.e., today we have AXP22X_BATT_CHRG_I being
>> exported as in_current1_raw whereas he wants in_current0_raw).
> 
> Hmm... I missed this. Are you talking about IIO or hwmon? IIRC
> hwmon numbers things starting at 1.
> 

About IIO.

Today, we have exposed:
in_voltage0_raw for acin_v
in_current1_raw for acin_i
in_voltage2_raw for vbus_v
in_current3_raw for vbus_i
in_temp4_raw for adc_temp
in_voltage5_raw for gpio0_v
in_voltage6_raw for gpio1_v
in_voltage7_raw for batt_v
in_current8_raw for batt_chrg_i
in_current9_raw for batt_dischrg_i
in_voltage10_raw for ipsout_v

but I think what Rob wants is:

in_voltage0_raw acin_v
in_current0_raw for acin_i
in_voltage1_raw for vbus_v
in_current1_raw for vbus_i
in_temp_raw for adc_temp
in_voltage2_raw for gpio0_v
in_voltage3_raw for gpio1_v
in_voltage4_raw for batt_v
in_current2_raw for batt_chrg_i
in_current3_raw for batt_dischrg_i
in_voltage5_raw for ipsout_v

>> [...]
>>>> +static int axp22x_adc_read_raw(struct iio_dev *indio_dev,
>>>> +                              struct iio_chan_spec const *channel, int *val,
>>>> +                              int *val2)
>>>> +{
>>>> +       struct axp20x_adc_iio *info = iio_priv(indio_dev);
>>>> +       int size = 12, ret;
>>>> +
>>>> +       switch (channel->channel) {
>>>> +       case AXP22X_BATT_DISCHRG_I:
>>>> +               size = 13;
>>>> +       case AXP22X_TEMP_ADC:
>>>> +       case AXP22X_BATT_V:
>>>> +       case AXP22X_BATT_CHRG_I:
>>>
>>> According to the datasheet, AXP22X_BATT_CHRG_I is also 13 bits wide.
>>>
>>
>> Where did you get that?
>>
>> Also, the datasheet is inconsistent:
>>  - 9.5 E-Gauge Fuel Gauge system => the min value is at 0x0 and the max
>> value at 0xfff for all channels, that's 12 bits.
>>  - 10.1.4 ADC Data => all channels except battery discharge current are
>> on 12 bits (8 high, 4 low).
> 
> My datasheets (AXP221 v1.6, AXP221s v1.2, AXP223 v1.1, all Chinese) say
> in 10.1.4:
> 
>   - 7A: battery charge current high 5 bits
>   - 7B: battery charge current low 8 bits
>   - 7C: battery discharge current high 5 bits
>   - 7D: battery discharge current low 8 bits
> 

AXP223 v1.1 in English in 10.1.4[1]:
    - 7A: battery charge current high 8 bits
    - 7B: battery charge current low 4 bits
    - 7C: battery discharge current high 8 bits
    - 7D: battery discharge current low 5 bits

Note that it's 8 bits for high and 4/5 bits for low while you wrote 4/5
bits high and low 8 bits (typos or actually what's written in the
datasheet?).

Hum.. from the reg reading function[2] I would say that the correct
formula is high on 8 bits and low on 4/5 bits.

So hum.. what do we do?

[1] http://dl.linux-sunxi.org/AXP/AXP223-en.pdf
[2] http://lxr.free-electrons.com/source/include/linux/mfd/axp20x.h#L564

>>
>> [...]
>>>> +static int axp22x_read_raw(struct iio_dev *indio_dev,
>>>> +                          struct iio_chan_spec const *chan, int *val,
>>>> +                          int *val2, long mask)
>>>> +{
>>>> +       switch (mask) {
>>>> +       case IIO_CHAN_INFO_OFFSET:
>>>> +               *val = -2667;
>>>
>>> Datasheet says -267.7 C, or -2677 here.
>>>
>>
>> The formula in the datasheet is (in milli Celsius):
>>  processed = raw * 100 - 266700;
>>
>> while the IIO framework asks for a scale and an offset which are then
>> applied as:
>>  processed = (raw + offset) * scale;
>>
>> Thus by factorizing, we get:
>>  processed = (raw - 2667) * 100;
> 
> What I meant was that your lower end value is off by one degree,
> -266.7 in your code vs -267.7 in the datasheet.
> 

Indeed. Thanks.

>>
>> [...]
>>>> +static int axp20x_remove(struct platform_device *pdev)
>>>> +{
>>>> +       struct axp20x_adc_iio *info;
>>>> +       struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>>>> +
>>>> +       info = iio_priv(indio_dev);
>>>
>>> Nit: you could just reverse the 2 declarations above and join this
>>> line after struct axp20x_adc_iio *info;
>>>
>>>> +       regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
>>>> +       regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
>>>
>>> The existing VBUS power supply driver enables the VBUS ADC bits itself,
>>> and does not check them later on. This means if one were to remove this
>>> axp20x-adc module, the voltage/current readings in the VBUS power supply
>>> would be invalid. Some sort of workaround would be needed here in this
>>> driver of the VBUS driver.
>>>
>>
>> That would be one reason to migrate the VBUS driver to use the IIO
>> channels, wouldn't it?
> 
> It is, preferably without changing the device tree.
> 

Yes, of course.

Thanks,
Quentin

-- 
Quentin Schulz, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox