diff mbox

[v3,4/7] of: configure the platform device dma parameters

Message ID 1398353407-2345-5-git-send-email-santosh.shilimkar@ti.com (mailing list archive)
State New, archived
Headers show

Commit Message

Santosh Shilimkar April 24, 2014, 3:30 p.m. UTC
Retrieve DMA configuration from DT and setup platform device's DMA
parameters. The DMA configuration in DT has to be specified using
"dma-ranges" and "dma-coherent" properties if supported.

We setup dma_pfn_offset using "dma-ranges" and dma_coherent_ops
using "dma-coherent" device tree properties.

The set_arch_dma_coherent_ops macro has to be defined by arch if
it supports coherent dma_ops. Otherwise, set_arch_dma_coherent_ops() is
declared as nop.

Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Olof Johansson <olof@lixom.net>
Cc: Grant Likely <grant.likely@linaro.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
---
 drivers/of/platform.c       |   48 ++++++++++++++++++++++++++++++++++++++++---
 include/linux/dma-mapping.h |    7 +++++++
 2 files changed, 52 insertions(+), 3 deletions(-)

Comments

Grant Likely April 29, 2014, 2:41 p.m. UTC | #1
On Thu, 24 Apr 2014 11:30:04 -0400, Santosh Shilimkar <santosh.shilimkar@ti.com> wrote:
> Retrieve DMA configuration from DT and setup platform device's DMA
> parameters. The DMA configuration in DT has to be specified using
> "dma-ranges" and "dma-coherent" properties if supported.
> 
> We setup dma_pfn_offset using "dma-ranges" and dma_coherent_ops
> using "dma-coherent" device tree properties.
> 
> The set_arch_dma_coherent_ops macro has to be defined by arch if
> it supports coherent dma_ops. Otherwise, set_arch_dma_coherent_ops() is
> declared as nop.
> 
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: Russell King <linux@arm.linux.org.uk>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Olof Johansson <olof@lixom.net>
> Cc: Grant Likely <grant.likely@linaro.org>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Linus Walleij <linus.walleij@linaro.org>
> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
> ---
>  drivers/of/platform.c       |   48 ++++++++++++++++++++++++++++++++++++++++---
>  include/linux/dma-mapping.h |    7 +++++++
>  2 files changed, 52 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/of/platform.c b/drivers/of/platform.c
> index 48de98f..270c0b9 100644
> --- a/drivers/of/platform.c
> +++ b/drivers/of/platform.c
> @@ -187,6 +187,50 @@ struct platform_device *of_device_alloc(struct device_node *np,
>  EXPORT_SYMBOL(of_device_alloc);
>  
>  /**
> + * of_dma_configure - Setup DMA configuration
> + * @dev:	Device to apply DMA configuration
> + *
> + * Try to get devices's DMA configuration from DT and update it
> + * accordingly.
> + *
> + * In case if platform code need to use own special DMA configuration,it
> + * can use Platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE event
> + * to fix up DMA configuration.
> + */
> +static void of_dma_configure(struct device *dev)
> +{
> +	u64 dma_addr, paddr, size;
> +	int ret;
> +
> +	dev->coherent_dma_mask = DMA_BIT_MASK(32);
> +	if (!dev->dma_mask)
> +		dev->dma_mask = &dev->coherent_dma_mask;
> +
> +	/*
> +	 * if dma-coherent property exist, call arch hook to setup
> +	 * dma coherent operations.
> +	 */
> +	if (of_dma_is_coherent(dev->of_node)) {
> +		set_arch_dma_coherent_ops(dev);
> +		dev_dbg(dev, "device is dma coherent\n");
> +	}
> +
> +	/*
> +	 * if dma-ranges property doesn't exist - just return else
> +	 * setup the dma offset
> +	 */
> +	ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size);
> +	if ((ret == -ENODEV) || (ret < 0)) {
> +		dev_dbg(dev, "no dma range information to setup\n");
> +		return;
> +	}
> +
> +	/* DMA ranges found. Calculate and set dma_pfn_offset */
> +	dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr);
> +	dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);

I've got two concerns here. of_dma_get_range() retrieves only the first
tuple from the dma-ranges property, but it is perfectly valid for
dma-ranges to contain multiple tuples. How should we handle it if a
device has multiple ranges it can DMA from?

Second, while the pfn offset is being determined, I don't see anything
making use of either the base address or size. How is the device
constrained to only getting DMA buffers from within that range? Is the
driver expected to manage that directly?

g.

> +}
> +
> +/**
>   * of_platform_device_create_pdata - Alloc, initialize and register an of_device
>   * @np: pointer to node to create device for
>   * @bus_id: name to assign device
> @@ -214,9 +258,7 @@ static struct platform_device *of_platform_device_create_pdata(
>  #if defined(CONFIG_MICROBLAZE)
>  	dev->archdata.dma_mask = 0xffffffffUL;
>  #endif
> -	dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
> -	if (!dev->dev.dma_mask)
> -		dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
> +	of_dma_configure(&dev->dev);
>  	dev->dev.bus = &platform_bus_type;
>  	dev->dev.platform_data = platform_data;
>  
> diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
> index fd4aee2..c7d9b1b 100644
> --- a/include/linux/dma-mapping.h
> +++ b/include/linux/dma-mapping.h
> @@ -123,6 +123,13 @@ static inline int dma_coerce_mask_and_coherent(struct device *dev, u64 mask)
>  
>  extern u64 dma_get_required_mask(struct device *dev);
>  
> +#ifndef set_arch_dma_coherent_ops
> +static inline int set_arch_dma_coherent_ops(struct device *dev)
> +{
> +	return 0;
> +}
> +#endif
> +
>  static inline unsigned int dma_get_max_seg_size(struct device *dev)
>  {
>  	return dev->dma_parms ? dev->dma_parms->max_segment_size : 65536;
> -- 
> 1.7.9.5
>
Santosh Shilimkar April 30, 2014, 2:19 p.m. UTC | #2
Hi Grant,

On Tuesday 29 April 2014 10:41 AM, Grant Likely wrote:
> On Thu, 24 Apr 2014 11:30:04 -0400, Santosh Shilimkar <santosh.shilimkar@ti.com> wrote:
>> Retrieve DMA configuration from DT and setup platform device's DMA
>> parameters. The DMA configuration in DT has to be specified using
>> "dma-ranges" and "dma-coherent" properties if supported.
>>
>> We setup dma_pfn_offset using "dma-ranges" and dma_coherent_ops
>> using "dma-coherent" device tree properties.
>>
>> The set_arch_dma_coherent_ops macro has to be defined by arch if
>> it supports coherent dma_ops. Otherwise, set_arch_dma_coherent_ops() is
>> declared as nop.
>>
>> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
>> Cc: Russell King <linux@arm.linux.org.uk>
>> Cc: Arnd Bergmann <arnd@arndb.de>
>> Cc: Olof Johansson <olof@lixom.net>
>> Cc: Grant Likely <grant.likely@linaro.org>
>> Cc: Rob Herring <robh+dt@kernel.org>
>> Cc: Catalin Marinas <catalin.marinas@arm.com>
>> Cc: Linus Walleij <linus.walleij@linaro.org>
>> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
>> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
>> ---
>>  drivers/of/platform.c       |   48 ++++++++++++++++++++++++++++++++++++++++---
>>  include/linux/dma-mapping.h |    7 +++++++
>>  2 files changed, 52 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/of/platform.c b/drivers/of/platform.c
>> index 48de98f..270c0b9 100644
>> --- a/drivers/of/platform.c
>> +++ b/drivers/of/platform.c
>> @@ -187,6 +187,50 @@ struct platform_device *of_device_alloc(struct device_node *np,
>>  EXPORT_SYMBOL(of_device_alloc);
>>  
>>  /**
>> + * of_dma_configure - Setup DMA configuration
>> + * @dev:	Device to apply DMA configuration
>> + *
>> + * Try to get devices's DMA configuration from DT and update it
>> + * accordingly.
>> + *
>> + * In case if platform code need to use own special DMA configuration,it
>> + * can use Platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE event
>> + * to fix up DMA configuration.
>> + */
>> +static void of_dma_configure(struct device *dev)
>> +{
>> +	u64 dma_addr, paddr, size;
>> +	int ret;
>> +
>> +	dev->coherent_dma_mask = DMA_BIT_MASK(32);
>> +	if (!dev->dma_mask)
>> +		dev->dma_mask = &dev->coherent_dma_mask;
>> +
>> +	/*
>> +	 * if dma-coherent property exist, call arch hook to setup
>> +	 * dma coherent operations.
>> +	 */
>> +	if (of_dma_is_coherent(dev->of_node)) {
>> +		set_arch_dma_coherent_ops(dev);
>> +		dev_dbg(dev, "device is dma coherent\n");
>> +	}
>> +
>> +	/*
>> +	 * if dma-ranges property doesn't exist - just return else
>> +	 * setup the dma offset
>> +	 */
>> +	ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size);
>> +	if ((ret == -ENODEV) || (ret < 0)) {
>> +		dev_dbg(dev, "no dma range information to setup\n");
>> +		return;
>> +	}
>> +
>> +	/* DMA ranges found. Calculate and set dma_pfn_offset */
>> +	dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr);
>> +	dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
> 
> I've got two concerns here. of_dma_get_range() retrieves only the first
> tuple from the dma-ranges property, but it is perfectly valid for
> dma-ranges to contain multiple tuples. How should we handle it if a
> device has multiple ranges it can DMA from?
> 

We've not found any cases in current Linux where more than one dma-ranges
would be used. Moreover, The MM (definitely for ARM) isn't supported such
cases at all (if i understand everything right).
- there are only one arm_dma_pfn_limit
- there is only one MM zone is used for ARM
- some arches like x86,mips can support 2 zones (per arch - not per device or bus)
  DMA & DMA32, but they configured once and forever per arch.

Example:
static void *loongson_dma_alloc_coherent(struct device *dev, size_t size,
		dma_addr_t *dma_handle, gfp_t gfp, struct dma_attrs *attrs)
{
...

	/* ignore region specifiers */
	gfp &= ~(__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM);

#ifdef CONFIG_ISA
	if (dev == NULL)
		gfp |= __GFP_DMA;
	else
#endif
#ifdef CONFIG_ZONE_DMA
	if (dev->coherent_dma_mask < DMA_BIT_MASK(32))
		gfp |= __GFP_DMA;
	else
#endif
#ifdef CONFIG_ZONE_DMA32
	if (dev->coherent_dma_mask < DMA_BIT_MASK(40))
		gfp |= __GFP_DMA32;
	else
#endif
...
}

Any ways, it can be added later if we have an usecase for
that.

> Second, while the pfn offset is being determined, I don't see anything
> making use of either the base address or size. How is the device
> constrained to only getting DMA buffers from within that range? Is the
> driver expected to manage that directly?
> 
Drivers don't have to do anything special apart from setting
the correct mask. The pfn_offset case, we use DMA_ZONE which takes
care of masks already. Size is suppose to be used for dma_mask
setup which we discussed in previous threads.

Regards,
Santosh
Grant Likely May 1, 2014, 1:12 p.m. UTC | #3
On Wed, 30 Apr 2014 10:19:15 -0400, Santosh Shilimkar <santosh.shilimkar@ti.com> wrote:
> Hi Grant,
> 
> On Tuesday 29 April 2014 10:41 AM, Grant Likely wrote:
> > On Thu, 24 Apr 2014 11:30:04 -0400, Santosh Shilimkar <santosh.shilimkar@ti.com> wrote:
> >> Retrieve DMA configuration from DT and setup platform device's DMA
> >> parameters. The DMA configuration in DT has to be specified using
> >> "dma-ranges" and "dma-coherent" properties if supported.
> >>
> >> We setup dma_pfn_offset using "dma-ranges" and dma_coherent_ops
> >> using "dma-coherent" device tree properties.
> >>
> >> The set_arch_dma_coherent_ops macro has to be defined by arch if
> >> it supports coherent dma_ops. Otherwise, set_arch_dma_coherent_ops() is
> >> declared as nop.
> >>
> >> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> >> Cc: Russell King <linux@arm.linux.org.uk>
> >> Cc: Arnd Bergmann <arnd@arndb.de>
> >> Cc: Olof Johansson <olof@lixom.net>
> >> Cc: Grant Likely <grant.likely@linaro.org>
> >> Cc: Rob Herring <robh+dt@kernel.org>
> >> Cc: Catalin Marinas <catalin.marinas@arm.com>
> >> Cc: Linus Walleij <linus.walleij@linaro.org>
> >> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
> >> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
> >> ---
> >>  drivers/of/platform.c       |   48 ++++++++++++++++++++++++++++++++++++++++---
> >>  include/linux/dma-mapping.h |    7 +++++++
> >>  2 files changed, 52 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/drivers/of/platform.c b/drivers/of/platform.c
> >> index 48de98f..270c0b9 100644
> >> --- a/drivers/of/platform.c
> >> +++ b/drivers/of/platform.c
> >> @@ -187,6 +187,50 @@ struct platform_device *of_device_alloc(struct device_node *np,
> >>  EXPORT_SYMBOL(of_device_alloc);
> >>  
> >>  /**
> >> + * of_dma_configure - Setup DMA configuration
> >> + * @dev:	Device to apply DMA configuration
> >> + *
> >> + * Try to get devices's DMA configuration from DT and update it
> >> + * accordingly.
> >> + *
> >> + * In case if platform code need to use own special DMA configuration,it
> >> + * can use Platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE event
> >> + * to fix up DMA configuration.
> >> + */
> >> +static void of_dma_configure(struct device *dev)
> >> +{
> >> +	u64 dma_addr, paddr, size;
> >> +	int ret;
> >> +
> >> +	dev->coherent_dma_mask = DMA_BIT_MASK(32);
> >> +	if (!dev->dma_mask)
> >> +		dev->dma_mask = &dev->coherent_dma_mask;
> >> +
> >> +	/*
> >> +	 * if dma-coherent property exist, call arch hook to setup
> >> +	 * dma coherent operations.
> >> +	 */
> >> +	if (of_dma_is_coherent(dev->of_node)) {
> >> +		set_arch_dma_coherent_ops(dev);
> >> +		dev_dbg(dev, "device is dma coherent\n");
> >> +	}
> >> +
> >> +	/*
> >> +	 * if dma-ranges property doesn't exist - just return else
> >> +	 * setup the dma offset
> >> +	 */
> >> +	ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size);
> >> +	if ((ret == -ENODEV) || (ret < 0)) {
> >> +		dev_dbg(dev, "no dma range information to setup\n");
> >> +		return;
> >> +	}
> >> +
> >> +	/* DMA ranges found. Calculate and set dma_pfn_offset */
> >> +	dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr);
> >> +	dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
> > 
> > I've got two concerns here. of_dma_get_range() retrieves only the first
> > tuple from the dma-ranges property, but it is perfectly valid for
> > dma-ranges to contain multiple tuples. How should we handle it if a
> > device has multiple ranges it can DMA from?
> > 
> 
> We've not found any cases in current Linux where more than one dma-ranges
> would be used. Moreover, The MM (definitely for ARM) isn't supported such
> cases at all (if i understand everything right).
> - there are only one arm_dma_pfn_limit
> - there is only one MM zone is used for ARM
> - some arches like x86,mips can support 2 zones (per arch - not per device or bus)
>   DMA & DMA32, but they configured once and forever per arch.

Okay. If anyone ever does implement multiple ranges then this code will
need to be revisited.

> > Second, while the pfn offset is being determined, I don't see anything
> > making use of either the base address or size. How is the device
> > constrained to only getting DMA buffers from within that range? Is the
> > driver expected to manage that directly?
> > 
> Drivers don't have to do anything special apart from setting
> the correct mask. The pfn_offset case, we use DMA_ZONE which takes
> care of masks already. Size is suppose to be used for dma_mask
> setup which we discussed in previous threads.

okay.

g.
Santosh Shilimkar May 1, 2014, 1:16 p.m. UTC | #4
On Thursday 01 May 2014 09:12 AM, Grant Likely wrote:
> On Wed, 30 Apr 2014 10:19:15 -0400, Santosh Shilimkar <santosh.shilimkar@ti.com> wrote:
>> Hi Grant,
>>
>> On Tuesday 29 April 2014 10:41 AM, Grant Likely wrote:
>>> On Thu, 24 Apr 2014 11:30:04 -0400, Santosh Shilimkar <santosh.shilimkar@ti.com> wrote:
>>>> Retrieve DMA configuration from DT and setup platform device's DMA
>>>> parameters. The DMA configuration in DT has to be specified using
>>>> "dma-ranges" and "dma-coherent" properties if supported.
>>>>
>>>> We setup dma_pfn_offset using "dma-ranges" and dma_coherent_ops
>>>> using "dma-coherent" device tree properties.
>>>>
>>>> The set_arch_dma_coherent_ops macro has to be defined by arch if
>>>> it supports coherent dma_ops. Otherwise, set_arch_dma_coherent_ops() is
>>>> declared as nop.
>>>>
>>>> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
>>>> Cc: Russell King <linux@arm.linux.org.uk>
>>>> Cc: Arnd Bergmann <arnd@arndb.de>
>>>> Cc: Olof Johansson <olof@lixom.net>
>>>> Cc: Grant Likely <grant.likely@linaro.org>
>>>> Cc: Rob Herring <robh+dt@kernel.org>
>>>> Cc: Catalin Marinas <catalin.marinas@arm.com>
>>>> Cc: Linus Walleij <linus.walleij@linaro.org>
>>>> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
>>>> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
>>>> ---
>>>>  drivers/of/platform.c       |   48 ++++++++++++++++++++++++++++++++++++++++---
>>>>  include/linux/dma-mapping.h |    7 +++++++
>>>>  2 files changed, 52 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git a/drivers/of/platform.c b/drivers/of/platform.c
>>>> index 48de98f..270c0b9 100644
>>>> --- a/drivers/of/platform.c
>>>> +++ b/drivers/of/platform.c
>>>> @@ -187,6 +187,50 @@ struct platform_device *of_device_alloc(struct device_node *np,
>>>>  EXPORT_SYMBOL(of_device_alloc);
>>>>  
>>>>  /**
>>>> + * of_dma_configure - Setup DMA configuration
>>>> + * @dev:	Device to apply DMA configuration
>>>> + *
>>>> + * Try to get devices's DMA configuration from DT and update it
>>>> + * accordingly.
>>>> + *
>>>> + * In case if platform code need to use own special DMA configuration,it
>>>> + * can use Platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE event
>>>> + * to fix up DMA configuration.
>>>> + */
>>>> +static void of_dma_configure(struct device *dev)
>>>> +{
>>>> +	u64 dma_addr, paddr, size;
>>>> +	int ret;
>>>> +
>>>> +	dev->coherent_dma_mask = DMA_BIT_MASK(32);
>>>> +	if (!dev->dma_mask)
>>>> +		dev->dma_mask = &dev->coherent_dma_mask;
>>>> +
>>>> +	/*
>>>> +	 * if dma-coherent property exist, call arch hook to setup
>>>> +	 * dma coherent operations.
>>>> +	 */
>>>> +	if (of_dma_is_coherent(dev->of_node)) {
>>>> +		set_arch_dma_coherent_ops(dev);
>>>> +		dev_dbg(dev, "device is dma coherent\n");
>>>> +	}
>>>> +
>>>> +	/*
>>>> +	 * if dma-ranges property doesn't exist - just return else
>>>> +	 * setup the dma offset
>>>> +	 */
>>>> +	ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size);
>>>> +	if ((ret == -ENODEV) || (ret < 0)) {
>>>> +		dev_dbg(dev, "no dma range information to setup\n");
>>>> +		return;
>>>> +	}
>>>> +
>>>> +	/* DMA ranges found. Calculate and set dma_pfn_offset */
>>>> +	dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr);
>>>> +	dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
>>>
>>> I've got two concerns here. of_dma_get_range() retrieves only the first
>>> tuple from the dma-ranges property, but it is perfectly valid for
>>> dma-ranges to contain multiple tuples. How should we handle it if a
>>> device has multiple ranges it can DMA from?
>>>
>>
>> We've not found any cases in current Linux where more than one dma-ranges
>> would be used. Moreover, The MM (definitely for ARM) isn't supported such
>> cases at all (if i understand everything right).
>> - there are only one arm_dma_pfn_limit
>> - there is only one MM zone is used for ARM
>> - some arches like x86,mips can support 2 zones (per arch - not per device or bus)
>>   DMA & DMA32, but they configured once and forever per arch.
> 
> Okay. If anyone ever does implement multiple ranges then this code will
> need to be revisited.
>
Sure. Thanks for the review !!
 
Regards,
Santosh
Rob Herring May 2, 2014, 12:49 a.m. UTC | #5
On Thu, Apr 24, 2014 at 10:30 AM, Santosh Shilimkar
<santosh.shilimkar@ti.com> wrote:
> Retrieve DMA configuration from DT and setup platform device's DMA
> parameters. The DMA configuration in DT has to be specified using
> "dma-ranges" and "dma-coherent" properties if supported.
>
> We setup dma_pfn_offset using "dma-ranges" and dma_coherent_ops
> using "dma-coherent" device tree properties.
>
> The set_arch_dma_coherent_ops macro has to be defined by arch if
> it supports coherent dma_ops. Otherwise, set_arch_dma_coherent_ops() is
> declared as nop.
>

[...]

> +       /*
> +        * if dma-ranges property doesn't exist - just return else
> +        * setup the dma offset
> +        */
> +       ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size);
> +       if ((ret == -ENODEV) || (ret < 0)) {

The 1st condition is redundant.

> +               dev_dbg(dev, "no dma range information to setup\n");
> +               return;
> +       }
> +
> +       /* DMA ranges found. Calculate and set dma_pfn_offset */
> +       dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr);
> +       dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
> +}
> +
> +/**
>   * of_platform_device_create_pdata - Alloc, initialize and register an of_device
>   * @np: pointer to node to create device for
>   * @bus_id: name to assign device
> @@ -214,9 +258,7 @@ static struct platform_device *of_platform_device_create_pdata(
>  #if defined(CONFIG_MICROBLAZE)
>         dev->archdata.dma_mask = 0xffffffffUL;
>  #endif

This is also dma related setup. Please move *all* dma setup into the function.

> -       dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
> -       if (!dev->dev.dma_mask)
> -               dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
> +       of_dma_configure(&dev->dev);
>         dev->dev.bus = &platform_bus_type;
>         dev->dev.platform_data = platform_data;
>
> diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
> index fd4aee2..c7d9b1b 100644
> --- a/include/linux/dma-mapping.h
> +++ b/include/linux/dma-mapping.h
> @@ -123,6 +123,13 @@ static inline int dma_coerce_mask_and_coherent(struct device *dev, u64 mask)
>
>  extern u64 dma_get_required_mask(struct device *dev);
>
> +#ifndef set_arch_dma_coherent_ops
> +static inline int set_arch_dma_coherent_ops(struct device *dev)
> +{
> +       return 0;
> +}
> +#endif
> +
>  static inline unsigned int dma_get_max_seg_size(struct device *dev)
>  {
>         return dev->dma_parms ? dev->dma_parms->max_segment_size : 65536;
> --
> 1.7.9.5
>
Arnd Bergmann May 2, 2014, 9:58 a.m. UTC | #6
On Thursday 01 May 2014 14:12:10 Grant Likely wrote:
> > > I've got two concerns here. of_dma_get_range() retrieves only the first
> > > tuple from the dma-ranges property, but it is perfectly valid for
> > > dma-ranges to contain multiple tuples. How should we handle it if a
> > > device has multiple ranges it can DMA from?
> > > 
> > 
> > We've not found any cases in current Linux where more than one dma-ranges
> > would be used. Moreover, The MM (definitely for ARM) isn't supported such
> > cases at all (if i understand everything right).
> > - there are only one arm_dma_pfn_limit
> > - there is only one MM zone is used for ARM
> > - some arches like x86,mips can support 2 zones (per arch - not per device or bus)
> >   DMA & DMA32, but they configured once and forever per arch.
> 
> Okay. If anyone ever does implement multiple ranges then this code will
> need to be revisited.

I wonder if it's needed for platforms implementing the standard "ARM memory map" [1].
The document only talks about addresses as seen from the CPU, and I can see
two logical interpretations how the RAM is supposed to be visible from a device:
either all RAM would be visible contiguously at DMA address zero, or everything
would be visible at the same physical address as the CPU sees it.

If anyone picks the first interpretation, we will have to implement that
in Linux. We can of course hope that all hardware designs follow the second
interpretation, which would be more convenient for us here.

	Arnd

[1] http://infocenter.arm.com/help/topic/com.arm.doc.den0001c/DEN0001C_principles_of_arm_memory_maps.pdf
Santosh Shilimkar May 2, 2014, 1:13 p.m. UTC | #7
On Friday 02 May 2014 05:58 AM, Arnd Bergmann wrote:
> On Thursday 01 May 2014 14:12:10 Grant Likely wrote:
>>>> I've got two concerns here. of_dma_get_range() retrieves only the first
>>>> tuple from the dma-ranges property, but it is perfectly valid for
>>>> dma-ranges to contain multiple tuples. How should we handle it if a
>>>> device has multiple ranges it can DMA from?
>>>>
>>>
>>> We've not found any cases in current Linux where more than one dma-ranges
>>> would be used. Moreover, The MM (definitely for ARM) isn't supported such
>>> cases at all (if i understand everything right).
>>> - there are only one arm_dma_pfn_limit
>>> - there is only one MM zone is used for ARM
>>> - some arches like x86,mips can support 2 zones (per arch - not per device or bus)
>>>   DMA & DMA32, but they configured once and forever per arch.
>>
>> Okay. If anyone ever does implement multiple ranges then this code will
>> need to be revisited.
> 
> I wonder if it's needed for platforms implementing the standard "ARM memory map" [1].
> The document only talks about addresses as seen from the CPU, and I can see
> two logical interpretations how the RAM is supposed to be visible from a device:
> either all RAM would be visible contiguously at DMA address zero, or everything
> would be visible at the same physical address as the CPU sees it.
> 
> If anyone picks the first interpretation, we will have to implement that
> in Linux. We can of course hope that all hardware designs follow the second
> interpretation, which would be more convenient for us here.
>
not sure if I got your point correctly but DMA address 0 isn't used as DRAM start in
any of the ARM SOC today, mainly because of the boot architecture where address 0 is
typically used by ROM code. RAM start will be at some offset always and hence I
believe ARM SOCs will follow second interpretation. This was one of the main reason
we ended up fixing the max*pfn stuff. 
26ba47b {ARM: 7805/1: mm: change max*pfn to include the physical offset of memory}

> 
> [1] http://infocenter.arm.com/help/topic/com.arm.doc.den0001c/DEN0001C_principles_of_arm_memory_maps.pdf
> 
 
Regards,
Santosh
Arnd Bergmann May 2, 2014, 3:13 p.m. UTC | #8
On Friday 02 May 2014 09:13:48 Santosh Shilimkar wrote:
> On Friday 02 May 2014 05:58 AM, Arnd Bergmann wrote:
> > On Thursday 01 May 2014 14:12:10 Grant Likely wrote:
> >>>> I've got two concerns here. of_dma_get_range() retrieves only the first
> >>>> tuple from the dma-ranges property, but it is perfectly valid for
> >>>> dma-ranges to contain multiple tuples. How should we handle it if a
> >>>> device has multiple ranges it can DMA from?
> >>>>
> >>>
> >>> We've not found any cases in current Linux where more than one dma-ranges
> >>> would be used. Moreover, The MM (definitely for ARM) isn't supported such
> >>> cases at all (if i understand everything right).
> >>> - there are only one arm_dma_pfn_limit
> >>> - there is only one MM zone is used for ARM
> >>> - some arches like x86,mips can support 2 zones (per arch - not per device or bus)
> >>>   DMA & DMA32, but they configured once and forever per arch.
> >>
> >> Okay. If anyone ever does implement multiple ranges then this code will
> >> need to be revisited.
> > 
> > I wonder if it's needed for platforms implementing the standard "ARM memory map" [1].
> > The document only talks about addresses as seen from the CPU, and I can see
> > two logical interpretations how the RAM is supposed to be visible from a device:
> > either all RAM would be visible contiguously at DMA address zero, or everything
> > would be visible at the same physical address as the CPU sees it.
> > 
> > If anyone picks the first interpretation, we will have to implement that
> > in Linux. We can of course hope that all hardware designs follow the second
> > interpretation, which would be more convenient for us here.
> >
> not sure if I got your point correctly but DMA address 0 isn't used as DRAM start in
> any of the ARM SOC today, mainly because of the boot architecture where address 0 is
> typically used by ROM code. RAM start will be at some offset always and hence I
> believe ARM SOCs will follow second interpretation. This was one of the main reason
> we ended up fixing the max*pfn stuff. 
> 26ba47b {ARM: 7805/1: mm: change max*pfn to include the physical offset of memory}

Marvell normally has memory starting at physical address zero.

Even if RAM starts elsewhere, I don't think that is a reason to have
the DMA address do the same. The memory controller internally obviously
starts at zero, and it wouldn't be unreasonable to have the DMA space
match what the memory controller sees rather than have it match what
the CPU sees.

If you look at the table 3.1.4, you have both addresses listed:

	  Physical Addresses in SoC	Offset	 		Internal DRAM address

2 GBytes	0x00 8000 0000 –	-0x00 8000 0000		0x00 0000 0000 –
		0x00 FFFF FFFF			 		0x00 7FFF FFFF

30 GBytes	0x08 8000 0000 – 	-0x08 0000 0000		0x00 8000 0000 –
		0x0F FFFF FFFF					0x07 FFFF FFFF

32 GBytes	0x88 0000 0000 -	-0x80 0000 0000	 	0x08 0000 0000 -
		0x8F FFFF FFFF					0x0F FFFF FFFF

The wording "Physical Addresses in SoC" would indeed suggest that the
same address is used for DMA, but I would trust everybody to do that.

	Arnd
Bjorn Helgaas May 2, 2014, 4:54 p.m. UTC | #9
On Thu, Apr 24, 2014 at 11:30:04AM -0400, Santosh Shilimkar wrote:
> Retrieve DMA configuration from DT and setup platform device's DMA
> parameters. The DMA configuration in DT has to be specified using
> "dma-ranges" and "dma-coherent" properties if supported.
> 
> We setup dma_pfn_offset using "dma-ranges" and dma_coherent_ops
> using "dma-coherent" device tree properties.
> 
> The set_arch_dma_coherent_ops macro has to be defined by arch if
> it supports coherent dma_ops. Otherwise, set_arch_dma_coherent_ops() is
> declared as nop.
> 
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: Russell King <linux@arm.linux.org.uk>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Olof Johansson <olof@lixom.net>
> Cc: Grant Likely <grant.likely@linaro.org>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Catalin Marinas <catalin.marinas@arm.com>
> Cc: Linus Walleij <linus.walleij@linaro.org>
> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
> ---
>  drivers/of/platform.c       |   48 ++++++++++++++++++++++++++++++++++++++++---
>  include/linux/dma-mapping.h |    7 +++++++
>  2 files changed, 52 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/of/platform.c b/drivers/of/platform.c
> index 48de98f..270c0b9 100644
> --- a/drivers/of/platform.c
> +++ b/drivers/of/platform.c
> @@ -187,6 +187,50 @@ struct platform_device *of_device_alloc(struct device_node *np,
>  EXPORT_SYMBOL(of_device_alloc);
>  
>  /**
> + * of_dma_configure - Setup DMA configuration
> + * @dev:	Device to apply DMA configuration
> + *
> + * Try to get devices's DMA configuration from DT and update it
> + * accordingly.
> + *
> + * In case if platform code need to use own special DMA configuration,it
> + * can use Platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE event
> + * to fix up DMA configuration.
> + */
> +static void of_dma_configure(struct device *dev)
> +{
> +	u64 dma_addr, paddr, size;
> +	int ret;
> +
> +	dev->coherent_dma_mask = DMA_BIT_MASK(32);
> +	if (!dev->dma_mask)
> +		dev->dma_mask = &dev->coherent_dma_mask;
> +
> +	/*
> +	 * if dma-coherent property exist, call arch hook to setup
> +	 * dma coherent operations.
> +	 */
> +	if (of_dma_is_coherent(dev->of_node)) {
> +		set_arch_dma_coherent_ops(dev);
> +		dev_dbg(dev, "device is dma coherent\n");
> +	}
> +
> +	/*
> +	 * if dma-ranges property doesn't exist - just return else
> +	 * setup the dma offset
> +	 */
> +	ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size);
> +	if ((ret == -ENODEV) || (ret < 0)) {
> +		dev_dbg(dev, "no dma range information to setup\n");
> +		return;
> +	}
> +
> +	/* DMA ranges found. Calculate and set dma_pfn_offset */
> +	dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr);
> +	dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);

Is this effectively the same as an IOMMU that applies a constant offset
to the bus address?  Could or should this be done by adding a simple IOMMU
driver instead of adding dma_pfn_offset to struct device?

If we had both dma-ranges (and we set dma_pfn_offset as you do here) and an
IOMMU, how would the combination work?  If the IOMMU driver managed
dma_pfn_offset internally, it seems like we'd have two entities dealing
with it.  If the IOMMU driver doesn't use dma_pfn_offset, it seems like
it would be exposing a weird intermediate address space that's not usable
by either CPU or device.
Arnd Bergmann May 2, 2014, 6:59 p.m. UTC | #10
On Friday 02 May 2014 10:54:59 Bjorn Helgaas wrote:
> > +static void of_dma_configure(struct device *dev)
> > +{
> > +     u64 dma_addr, paddr, size;
> > +     int ret;
> > +
> > +     dev->coherent_dma_mask = DMA_BIT_MASK(32);
> > +     if (!dev->dma_mask)
> > +             dev->dma_mask = &dev->coherent_dma_mask;
> > +
> > +     /*
> > +      * if dma-coherent property exist, call arch hook to setup
> > +      * dma coherent operations.
> > +      */
> > +     if (of_dma_is_coherent(dev->of_node)) {
> > +             set_arch_dma_coherent_ops(dev);
> > +             dev_dbg(dev, "device is dma coherent\n");
> > +     }
> > +
> > +     /*
> > +      * if dma-ranges property doesn't exist - just return else
> > +      * setup the dma offset
> > +      */
> > +     ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size);
> > +     if ((ret == -ENODEV) || (ret < 0)) {
> > +             dev_dbg(dev, "no dma range information to setup\n");
> > +             return;
> > +     }
> > +
> > +     /* DMA ranges found. Calculate and set dma_pfn_offset */
> > +     dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr);
> > +     dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
> 
> Is this effectively the same as an IOMMU that applies a constant offset
> to the bus address?  Could or should this be done by adding a simple IOMMU
> driver instead of adding dma_pfn_offset to struct device?

We currently have two dma_map_ops variants on ARM (plus another set for
coherent/noncoherent differences, but we can ignore that for the sake
of this discussion): one that handles linear mappings and one that
handles IOMMUs by calling into the linux/iommu.h APIs.

I guess what you mean by 'a simple IOMMU driver' would be another
dma_map_ops implementation that is separate from real IOMMUs, right?

That could certainly be done, but in effect it is almost the same as
the linear mapping we already have.

> If we had both dma-ranges (and we set dma_pfn_offset as you do here) and an
> IOMMU, how would the combination work?  If the IOMMU driver managed
> dma_pfn_offset internally, it seems like we'd have two entities dealing
> with it.  If the IOMMU driver doesn't use dma_pfn_offset, it seems like
> it would be exposing a weird intermediate address space that's not usable
> by either CPU or device.

The iommu dma_map_ops implementation does not need to worry about the
dma_pfn_offset. We are still debating how to represent IOMMUs in DT
at the moment, so it's not clear yet if we would consider a device node
with both a dma-ranges property and an iommu reference as valid.

What we will probably need is a way to represent the valid bus addresses
that can be used for transfers from the DMA master through the IOMMU.
This could be done through dma-ranges, or some other property, we will
have to decide that soon.

	Arnd
Bjorn Helgaas May 5, 2014, 8:45 p.m. UTC | #11
[+cc Ben, Chris]

On Fri, May 2, 2014 at 12:59 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Friday 02 May 2014 10:54:59 Bjorn Helgaas wrote:
>> > +static void of_dma_configure(struct device *dev)
>> > +{
>> > +     u64 dma_addr, paddr, size;
>> > +     int ret;
>> > +
>> > +     dev->coherent_dma_mask = DMA_BIT_MASK(32);
>> > +     if (!dev->dma_mask)
>> > +             dev->dma_mask = &dev->coherent_dma_mask;
>> > +
>> > +     /*
>> > +      * if dma-coherent property exist, call arch hook to setup
>> > +      * dma coherent operations.
>> > +      */
>> > +     if (of_dma_is_coherent(dev->of_node)) {
>> > +             set_arch_dma_coherent_ops(dev);
>> > +             dev_dbg(dev, "device is dma coherent\n");
>> > +     }
>> > +
>> > +     /*
>> > +      * if dma-ranges property doesn't exist - just return else
>> > +      * setup the dma offset
>> > +      */
>> > +     ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size);
>> > +     if ((ret == -ENODEV) || (ret < 0)) {
>> > +             dev_dbg(dev, "no dma range information to setup\n");
>> > +             return;
>> > +     }
>> > +
>> > +     /* DMA ranges found. Calculate and set dma_pfn_offset */
>> > +     dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr);
>> > +     dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
>>
>> Is this effectively the same as an IOMMU that applies a constant offset
>> to the bus address?  Could or should this be done by adding a simple IOMMU
>> driver instead of adding dma_pfn_offset to struct device?
>
> We currently have two dma_map_ops variants on ARM (plus another set for
> coherent/noncoherent differences, but we can ignore that for the sake
> of this discussion): one that handles linear mappings and one that
> handles IOMMUs by calling into the linux/iommu.h APIs.
>
> I guess what you mean by 'a simple IOMMU driver' would be another
> dma_map_ops implementation that is separate from real IOMMUs, right?

I suppose so; it seems like the offset could be managed inside
arm_dma_ops.  My idea of an IOMMU is something that maps bus addresses
to physical memory addresses.  That's what we're doing here; it's just
that the mapping function is very simple.  So why add something new in
struct device for it?

I think powerpc and tile do something similar in dma_direct_map_page()
and tile_pci_dma_map_page() (they store the offset in struct
dev_archdata).

Bjorn
Arnd Bergmann May 5, 2014, 8:55 p.m. UTC | #12
On Monday 05 May 2014 14:45:21 Bjorn Helgaas wrote:
> [+cc Ben, Chris]
> 
> On Fri, May 2, 2014 at 12:59 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> > On Friday 02 May 2014 10:54:59 Bjorn Helgaas wrote:
> >> > +static void of_dma_configure(struct device *dev)
> >> > +{
> >> > +     u64 dma_addr, paddr, size;
> >> > +     int ret;
> >> > +
> >> > +     dev->coherent_dma_mask = DMA_BIT_MASK(32);
> >> > +     if (!dev->dma_mask)
> >> > +             dev->dma_mask = &dev->coherent_dma_mask;
> >> > +
> >> > +     /*
> >> > +      * if dma-coherent property exist, call arch hook to setup
> >> > +      * dma coherent operations.
> >> > +      */
> >> > +     if (of_dma_is_coherent(dev->of_node)) {
> >> > +             set_arch_dma_coherent_ops(dev);
> >> > +             dev_dbg(dev, "device is dma coherent\n");
> >> > +     }
> >> > +
> >> > +     /*
> >> > +      * if dma-ranges property doesn't exist - just return else
> >> > +      * setup the dma offset
> >> > +      */
> >> > +     ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size);
> >> > +     if ((ret == -ENODEV) || (ret < 0)) {
> >> > +             dev_dbg(dev, "no dma range information to setup\n");
> >> > +             return;
> >> > +     }
> >> > +
> >> > +     /* DMA ranges found. Calculate and set dma_pfn_offset */
> >> > +     dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr);
> >> > +     dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
> >>
> >> Is this effectively the same as an IOMMU that applies a constant offset
> >> to the bus address?  Could or should this be done by adding a simple IOMMU
> >> driver instead of adding dma_pfn_offset to struct device?
> >
> > We currently have two dma_map_ops variants on ARM (plus another set for
> > coherent/noncoherent differences, but we can ignore that for the sake
> > of this discussion): one that handles linear mappings and one that
> > handles IOMMUs by calling into the linux/iommu.h APIs.
> >
> > I guess what you mean by 'a simple IOMMU driver' would be another
> > dma_map_ops implementation that is separate from real IOMMUs, right?
> 
> I suppose so; it seems like the offset could be managed inside
> arm_dma_ops.  My idea of an IOMMU is something that maps bus addresses
> to physical memory addresses.  That's what we're doing here; it's just
> that the mapping function is very simple.  So why add something new in
> struct device for it?
> 
> I think powerpc and tile do something similar in dma_direct_map_page()
> and tile_pci_dma_map_page() (they store the offset in struct
> dev_archdata).

Ah, so the only question is whether to store it in dev_archdata or
in device. I think the argument for using struct device directly
was that it lets us access this field from architecture independent
code. It's not important to the overall design though: we could easily
put it in dev_archdata and call an architecture specific function to
set it, if there are good reasons for keeping it out of the generic
device structure.

	Arnd
Bjorn Helgaas May 5, 2014, 10:28 p.m. UTC | #13
On Mon, May 5, 2014 at 2:55 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Monday 05 May 2014 14:45:21 Bjorn Helgaas wrote:
>> [+cc Ben, Chris]
>>
>> On Fri, May 2, 2014 at 12:59 PM, Arnd Bergmann <arnd@arndb.de> wrote:
>> > On Friday 02 May 2014 10:54:59 Bjorn Helgaas wrote:
>> >> > +static void of_dma_configure(struct device *dev)
>> >> > +{
>> >> > +     u64 dma_addr, paddr, size;
>> >> > +     int ret;
>> >> > +
>> >> > +     dev->coherent_dma_mask = DMA_BIT_MASK(32);
>> >> > +     if (!dev->dma_mask)
>> >> > +             dev->dma_mask = &dev->coherent_dma_mask;
>> >> > +
>> >> > +     /*
>> >> > +      * if dma-coherent property exist, call arch hook to setup
>> >> > +      * dma coherent operations.
>> >> > +      */
>> >> > +     if (of_dma_is_coherent(dev->of_node)) {
>> >> > +             set_arch_dma_coherent_ops(dev);
>> >> > +             dev_dbg(dev, "device is dma coherent\n");
>> >> > +     }
>> >> > +
>> >> > +     /*
>> >> > +      * if dma-ranges property doesn't exist - just return else
>> >> > +      * setup the dma offset
>> >> > +      */
>> >> > +     ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size);
>> >> > +     if ((ret == -ENODEV) || (ret < 0)) {
>> >> > +             dev_dbg(dev, "no dma range information to setup\n");
>> >> > +             return;
>> >> > +     }
>> >> > +
>> >> > +     /* DMA ranges found. Calculate and set dma_pfn_offset */
>> >> > +     dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr);
>> >> > +     dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
>> >>
>> >> Is this effectively the same as an IOMMU that applies a constant offset
>> >> to the bus address?  Could or should this be done by adding a simple IOMMU
>> >> driver instead of adding dma_pfn_offset to struct device?
>> >
>> > We currently have two dma_map_ops variants on ARM (plus another set for
>> > coherent/noncoherent differences, but we can ignore that for the sake
>> > of this discussion): one that handles linear mappings and one that
>> > handles IOMMUs by calling into the linux/iommu.h APIs.
>> >
>> > I guess what you mean by 'a simple IOMMU driver' would be another
>> > dma_map_ops implementation that is separate from real IOMMUs, right?
>>
>> I suppose so; it seems like the offset could be managed inside
>> arm_dma_ops.  My idea of an IOMMU is something that maps bus addresses
>> to physical memory addresses.  That's what we're doing here; it's just
>> that the mapping function is very simple.  So why add something new in
>> struct device for it?
>>
>> I think powerpc and tile do something similar in dma_direct_map_page()
>> and tile_pci_dma_map_page() (they store the offset in struct
>> dev_archdata).
>
> Ah, so the only question is whether to store it in dev_archdata or
> in device. I think the argument for using struct device directly
> was that it lets us access this field from architecture independent
> code. It's not important to the overall design though: we could easily
> put it in dev_archdata and call an architecture specific function to
> set it, if there are good reasons for keeping it out of the generic
> device structure.

Well, it wasn't my *only* question, since I didn't know about the
powerpc and tile usage originally :).  Putting it in dev_archdata does
seem better because it's a similar solution to a similar problem, and
it's less public.

I still wonder whether arm, powerpc, and tile (and I just noticed
microblaze also has a similar dma_direct_map_page()) could all be
handled by attaching devices to a generic trivial IOMMU driver
parameterized with the appropriate constant offset.

What arch-independent code would access this data?  I expect that
drivers will use dma_map_page(), etc., so they should already have
both the bus and the physical address and wouldn't need it.  Shouldn't
generic code just rely on the DMA API, which might use an IOMMU or
this dma_pfn_offset internally?

Bjorn
Benjamin Herrenschmidt May 6, 2014, 3:44 a.m. UTC | #14
On Mon, 2014-05-05 at 16:28 -0600, Bjorn Helgaas wrote:
> I still wonder whether arm, powerpc, and tile (and I just noticed
> microblaze also has a similar dma_direct_map_page()) could all be
> handled by attaching devices to a generic trivial IOMMU driver
> parameterized with the appropriate constant offset.

On powerpc, the offset is not constant, it can be per-device

Cheers,
Ben.
Arnd Bergmann May 6, 2014, 9:54 a.m. UTC | #15
On Tuesday 06 May 2014 13:44:38 Benjamin Herrenschmidt wrote:
> On Mon, 2014-05-05 at 16:28 -0600, Bjorn Helgaas wrote:
> > I still wonder whether arm, powerpc, and tile (and I just noticed
> > microblaze also has a similar dma_direct_map_page()) could all be
> > handled by attaching devices to a generic trivial IOMMU driver
> > parameterized with the appropriate constant offset.

It gets complex at that point, but it can be done. We are going to
need support for IOMMU and swiotlb in the same place, as well
as coherent and noncoherent ops, so the code here will be something
like

	/* default is noncoherent, non-offset, no iommu */
	if (offset) {
		if (coherent)
			set_arch_dma_coherent_offset_ops(dev);
		else
			set_arch_dma_offset_ops(dev);
	} else if (coherent)
		set_arch_dma_coherent_ops(dev);

	if (iommu) {
		if (coherent)
			set_arch_dma_coherent_iommu_ops(dev);
		else
			set_arch_dma_iommu_ops(dev);
	}

Doing it in the default ops would reduce the number of cases
from 5 to 3.

It may be easier to replace set_arch_dma_coherent_ops() with
a generic function that handles all cases:

int set_arch_dma_ops(struct device *dev, bool coherent,
		phys_addr_t offset, struct device_node *iommu);

and let the architecture handle the cases it needs.

> On powerpc, the offset is not constant, it can be per-device.

I think that's the case on all of them. The code under review here
is what parses the dma-ranges property in order to put the correct
value into the per-device structure.

	Arnd
Santosh Shilimkar May 6, 2014, 1:32 p.m. UTC | #16
On Tuesday 06 May 2014 05:54 AM, Arnd Bergmann wrote:
> On Tuesday 06 May 2014 13:44:38 Benjamin Herrenschmidt wrote:
>> On Mon, 2014-05-05 at 16:28 -0600, Bjorn Helgaas wrote:
>>> I still wonder whether arm, powerpc, and tile (and I just noticed
>>> microblaze also has a similar dma_direct_map_page()) could all be
>>> handled by attaching devices to a generic trivial IOMMU driver
>>> parameterized with the appropriate constant offset.
> 

[..]

> It may be easier to replace set_arch_dma_coherent_ops() with
> a generic function that handles all cases:
> 
> int set_arch_dma_ops(struct device *dev, bool coherent,
> 		phys_addr_t offset, struct device_node *iommu);
> 
> and let the architecture handle the cases it needs.
> 
>> On powerpc, the offset is not constant, it can be per-device.
> 
> I think that's the case on all of them. The code under review here
> is what parses the dma-ranges property in order to put the correct
> value into the per-device structure.
> 
Yep. The per-device property need is one of the reason, we added the
information to struct device. The constant need not be same though
on keystone it is same for all devices.

Regards,
Santosh
Grant Likely May 27, 2014, 12:56 p.m. UTC | #17
On Fri, 02 May 2014 11:58:30 +0200, Arnd Bergmann <arnd@arndb.de> wrote:
> On Thursday 01 May 2014 14:12:10 Grant Likely wrote:
> > > > I've got two concerns here. of_dma_get_range() retrieves only the first
> > > > tuple from the dma-ranges property, but it is perfectly valid for
> > > > dma-ranges to contain multiple tuples. How should we handle it if a
> > > > device has multiple ranges it can DMA from?
> > > > 
> > > 
> > > We've not found any cases in current Linux where more than one dma-ranges
> > > would be used. Moreover, The MM (definitely for ARM) isn't supported such
> > > cases at all (if i understand everything right).
> > > - there are only one arm_dma_pfn_limit
> > > - there is only one MM zone is used for ARM
> > > - some arches like x86,mips can support 2 zones (per arch - not per device or bus)
> > >   DMA & DMA32, but they configured once and forever per arch.
> > 
> > Okay. If anyone ever does implement multiple ranges then this code will
> > need to be revisited.
> 
> I wonder if it's needed for platforms implementing the standard "ARM memory map" [1].
> The document only talks about addresses as seen from the CPU, and I can see
> two logical interpretations how the RAM is supposed to be visible from a device:
> either all RAM would be visible contiguously at DMA address zero, or everything
> would be visible at the same physical address as the CPU sees it.
> 
> If anyone picks the first interpretation, we will have to implement that
> in Linux. We can of course hope that all hardware designs follow the second
> interpretation, which would be more convenient for us here.

Indeed. Hope though we might, I would not be surprised to see a platform
that does the first. In that case we could probably handle it with a
ranges property that is DMA-controller facing instead of device facing.
That would be able to handle the translation between CPU addressing and
DMA addressing.

Come to think of it, doesn't PCI DMA have to deal with that situation if
the PCI window is not 1:1 mapped into the CPU address space?

g.
Arnd Bergmann May 27, 2014, 1:30 p.m. UTC | #18
On Tuesday 27 May 2014 13:56:55 Grant Likely wrote:
> On Fri, 02 May 2014 11:58:30 +0200, Arnd Bergmann <arnd@arndb.de> wrote:
> > On Thursday 01 May 2014 14:12:10 Grant Likely wrote:
> > > > > I've got two concerns here. of_dma_get_range() retrieves only the first
> > > > > tuple from the dma-ranges property, but it is perfectly valid for
> > > > > dma-ranges to contain multiple tuples. How should we handle it if a
> > > > > device has multiple ranges it can DMA from?
> > > > > 
> > > > 
> > > > We've not found any cases in current Linux where more than one dma-ranges
> > > > would be used. Moreover, The MM (definitely for ARM) isn't supported such
> > > > cases at all (if i understand everything right).
> > > > - there are only one arm_dma_pfn_limit
> > > > - there is only one MM zone is used for ARM
> > > > - some arches like x86,mips can support 2 zones (per arch - not per device or bus)
> > > >   DMA & DMA32, but they configured once and forever per arch.
> > > 
> > > Okay. If anyone ever does implement multiple ranges then this code will
> > > need to be revisited.
> > 
> > I wonder if it's needed for platforms implementing the standard "ARM memory map" [1].
> > The document only talks about addresses as seen from the CPU, and I can see
> > two logical interpretations how the RAM is supposed to be visible from a device:
> > either all RAM would be visible contiguously at DMA address zero, or everything
> > would be visible at the same physical address as the CPU sees it.
> > 
> > If anyone picks the first interpretation, we will have to implement that
> > in Linux. We can of course hope that all hardware designs follow the second
> > interpretation, which would be more convenient for us here.
> 
> Indeed. Hope though we might, I would not be surprised to see a platform
> that does the first. In that case we could probably handle it with a
> ranges property that is DMA-controller facing instead of device facing.
> That would be able to handle the translation between CPU addressing and
> DMA addressing.
> 
> Come to think of it, doesn't PCI DMA have to deal with that situation if
> the PCI window is not 1:1 mapped into the CPU address space?

I think all PCI buses we support so far only need a single entry in the
dma-ranges property.

	Arnd
diff mbox

Patch

diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 48de98f..270c0b9 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -187,6 +187,50 @@  struct platform_device *of_device_alloc(struct device_node *np,
 EXPORT_SYMBOL(of_device_alloc);
 
 /**
+ * of_dma_configure - Setup DMA configuration
+ * @dev:	Device to apply DMA configuration
+ *
+ * Try to get devices's DMA configuration from DT and update it
+ * accordingly.
+ *
+ * In case if platform code need to use own special DMA configuration,it
+ * can use Platform bus notifier and handle BUS_NOTIFY_ADD_DEVICE event
+ * to fix up DMA configuration.
+ */
+static void of_dma_configure(struct device *dev)
+{
+	u64 dma_addr, paddr, size;
+	int ret;
+
+	dev->coherent_dma_mask = DMA_BIT_MASK(32);
+	if (!dev->dma_mask)
+		dev->dma_mask = &dev->coherent_dma_mask;
+
+	/*
+	 * if dma-coherent property exist, call arch hook to setup
+	 * dma coherent operations.
+	 */
+	if (of_dma_is_coherent(dev->of_node)) {
+		set_arch_dma_coherent_ops(dev);
+		dev_dbg(dev, "device is dma coherent\n");
+	}
+
+	/*
+	 * if dma-ranges property doesn't exist - just return else
+	 * setup the dma offset
+	 */
+	ret = of_dma_get_range(dev->of_node, &dma_addr, &paddr, &size);
+	if ((ret == -ENODEV) || (ret < 0)) {
+		dev_dbg(dev, "no dma range information to setup\n");
+		return;
+	}
+
+	/* DMA ranges found. Calculate and set dma_pfn_offset */
+	dev->dma_pfn_offset = PFN_DOWN(paddr - dma_addr);
+	dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
+}
+
+/**
  * of_platform_device_create_pdata - Alloc, initialize and register an of_device
  * @np: pointer to node to create device for
  * @bus_id: name to assign device
@@ -214,9 +258,7 @@  static struct platform_device *of_platform_device_create_pdata(
 #if defined(CONFIG_MICROBLAZE)
 	dev->archdata.dma_mask = 0xffffffffUL;
 #endif
-	dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
-	if (!dev->dev.dma_mask)
-		dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
+	of_dma_configure(&dev->dev);
 	dev->dev.bus = &platform_bus_type;
 	dev->dev.platform_data = platform_data;
 
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index fd4aee2..c7d9b1b 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -123,6 +123,13 @@  static inline int dma_coerce_mask_and_coherent(struct device *dev, u64 mask)
 
 extern u64 dma_get_required_mask(struct device *dev);
 
+#ifndef set_arch_dma_coherent_ops
+static inline int set_arch_dma_coherent_ops(struct device *dev)
+{
+	return 0;
+}
+#endif
+
 static inline unsigned int dma_get_max_seg_size(struct device *dev)
 {
 	return dev->dma_parms ? dev->dma_parms->max_segment_size : 65536;