diff mbox

[RFC,v1,1/2] of/pci: add of_pci_dma_configure() update dma configuration

Message ID 1418940425-2017-2-git-send-email-m-karicheri2@ti.com (mailing list archive)
State New, archived
Delegated to: Bjorn Helgaas
Headers show

Commit Message

Murali Karicheri Dec. 18, 2014, 10:07 p.m. UTC
Add of_pci_dma_configure() to allow updating the dma configuration
of the pci device using the configuration from the parent of
the root bridge device.

Signed-off-by: Murali Karicheri <m-karicheri2@ti.com>
---
 drivers/of/of_pci.c    |   71 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/of_pci.h |   12 ++++++++
 2 files changed, 83 insertions(+)

Comments

Arnd Bergmann Dec. 18, 2014, 10:29 p.m. UTC | #1
On Thursday 18 December 2014 17:07:04 Murali Karicheri wrote:
> Add of_pci_dma_configure() to allow updating the dma configuration
> of the pci device using the configuration from the parent of
> the root bridge device.
> 
> Signed-off-by: Murali Karicheri <m-karicheri2@ti.com>

Much better!

There is one detail that we should get right from the start here,
which is currently wrong in the platform device code, but I have so
far not dared change it:

> +	/*
> +	 * Set default dma-mask to 32 bit. Drivers are expected to setup
> +	 * the correct supported dma_mask.
> +	 */
> +	dev->coherent_dma_mask = DMA_BIT_MASK(32);

The 32-bit mask is indeed correct as the default for almost all cases,
and we must not set it larger than DMA_BIT_MASK(32). There is one
corner case though, which happens on some shmobile machines that have
a 31-bit mask on the PCI host, and I think we should use that as the
default.

> +	/*
> +	 * Set it to coherent_dma_mask by default if the architecture
> +	 * code has not set it.
> +	 */
> +	if (!dev->dma_mask)
> +		dev->dma_mask = &dev->coherent_dma_mask;
> +
> +	ret = of_dma_get_range(parent_np, &dma_addr, &paddr, &size);
> +	if (ret < 0) {
> +		dma_addr = offset = 0;
> +		size = dev->coherent_dma_mask;

Can you check one thing here? I believe the size argument as returned
from of_dma_get_range is inclusive (e.g. 0x100000000), while the coherent
mask by definition is exlusive (e.g. 0xffffffff), so the size needs to
be adapted here. I haven't checked all the code here though, so I may
be wrong.

> +	} else {
> +		offset = PFN_DOWN(paddr - dma_addr);
> +		dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
> +	}
> +	dev->dma_pfn_offset = offset;
> +
> +	coherent = of_dma_is_coherent(parent_np);
> +	dev_dbg(dev, "device is%sdma coherent\n",
> +		coherent ? " " : " not ");
> +
> +	arch_setup_dma_ops(dev, dma_addr, size, NULL, coherent);

Basically, I would use limit the size argument we pass into
arch_setup_dma_ops to the minimum of 'size' and 'dma_mask' here,
after converting into the same format. We should make sure we do the
same thing for platform_device as well, so it might be better to do
it inside of arch_setup_dma_ops instead.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Murali Karicheri Dec. 22, 2014, 5:46 p.m. UTC | #2
On 12/18/2014 05:29 PM, Arnd Bergmann wrote:
> On Thursday 18 December 2014 17:07:04 Murali Karicheri wrote:
>> Add of_pci_dma_configure() to allow updating the dma configuration
>> of the pci device using the configuration from the parent of
>> the root bridge device.
>>
>> Signed-off-by: Murali Karicheri<m-karicheri2@ti.com>
>
> Much better!
>
> There is one detail that we should get right from the start here,
> which is currently wrong in the platform device code, but I have so
> far not dared change it:
>
>> +	/*
>> +	 * Set default dma-mask to 32 bit. Drivers are expected to setup
>> +	 * the correct supported dma_mask.
>> +	 */
>> +	dev->coherent_dma_mask = DMA_BIT_MASK(32);
>
> The 32-bit mask is indeed correct as the default for almost all cases,
> and we must not set it larger than DMA_BIT_MASK(32). There is one
> corner case though, which happens on some shmobile machines that have
> a 31-bit mask on the PCI host, and I think we should use that as the
> default.
>
>> +	/*
>> +	 * Set it to coherent_dma_mask by default if the architecture
>> +	 * code has not set it.
>> +	 */
>> +	if (!dev->dma_mask)
>> +		dev->dma_mask =&dev->coherent_dma_mask;
>> +
>> +	ret = of_dma_get_range(parent_np,&dma_addr,&paddr,&size);
>> +	if (ret<  0) {
>> +		dma_addr = offset = 0;
>> +		size = dev->coherent_dma_mask;
>
> Can you check one thing here? I believe the size argument as returned
> from of_dma_get_range is inclusive (e.g. 0x100000000), while the coherent
> mask by definition is exlusive (e.g. 0xffffffff), so the size needs to
> be adapted here. I haven't checked all the code here though, so I may
> be wrong.

size returned by of_dma_get_range() is inclusive as you indicated. Fromt 
the grep of dma-ranges in arch/arm/boot/dts, I see

arch/arm/boot/dts/keystone.dtsi:		dma-ranges = <0x80000000 0x8 
0x00000000 0x80000000>;
arch/arm/boot/dts/keystone.dtsi:			dma-ranges;
arch/arm/boot/dts/r8a7790.dtsi:		dma-ranges = <0x42000000 0 0x40000000 0 
0x40000000 0 0x80000000
arch/arm/boot/dts/integratorap.dts:	dma-ranges = <0x80000000 0x0 
0x80000000>;
arch/arm/boot/dts/r8a7791.dtsi:		dma-ranges = <0x42000000 0 0x40000000 0 
0x40000000 0 0x80000000

So I guess I need to change the code to

 >> +	ret = of_dma_get_range(parent_np,&dma_addr,&paddr,&size);
 >> +	if (ret<  0) {
 >> +		dma_addr = offset = 0;
 >> +		size = dev->coherent_dma_mask + 1;

>
>> +	} else {
>> +		offset = PFN_DOWN(paddr - dma_addr);
>> +		dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
>> +	}
>> +	dev->dma_pfn_offset = offset;
>> +
>> +	coherent = of_dma_is_coherent(parent_np);
>> +	dev_dbg(dev, "device is%sdma coherent\n",
>> +		coherent ? " " : " not ");
>> +
>> +	arch_setup_dma_ops(dev, dma_addr, size, NULL, coherent);
>
> Basically, I would use limit the size argument we pass into
> arch_setup_dma_ops to the minimum of 'size' and 'dma_mask' here,
> after converting into the same format. We should make sure we do the
> same thing for platform_device as well, so it might be better to do
> it inside of arch_setup_dma_ops instead.

Do you think following changes will work?

+++ b/arch/arm/mm/dma-mapping.c
@@ -2052,9 +2052,10 @@ void arch_setup_dma_ops(struct device *dev, u64 
dma_base, u64 size,
                         struct iommu_ops *iommu, bool coherent)
  {
         struct dma_map_ops *dma_ops;
+       u64 temp_size = min((*(dev->dma_mask) + 1), size);

         dev->archdata.dma_coherent = coherent;
-       if (arm_setup_iommu_dma_ops(dev, dma_base, size, iommu))
+       if (arm_setup_iommu_dma_ops(dev, dma_base, temp_size, iommu))

If you agree, I will post v1 of the patch with these updates. Let me 
know. I did some basic tests on Keystone with these changes and it works 
fine.

Murali

>
> 	Arnd
Arnd Bergmann Dec. 22, 2014, 7:43 p.m. UTC | #3
On Monday 22 December 2014 12:46:12 Murali Karicheri wrote:
> On 12/18/2014 05:29 PM, Arnd Bergmann wrote:
> > On Thursday 18 December 2014 17:07:04 Murali Karicheri wrote:
> >> Add of_pci_dma_configure() to allow updating the dma configuration
> >> of the pci device using the configuration from the parent of
> >> the root bridge device.
> >> +	/*
> >> +	 * Set it to coherent_dma_mask by default if the architecture
> >> +	 * code has not set it.
> >> +	 */
> >> +	if (!dev->dma_mask)
> >> +		dev->dma_mask =&dev->coherent_dma_mask;
> >> +
> >> +	ret = of_dma_get_range(parent_np,&dma_addr,&paddr,&size);
> >> +	if (ret<  0) {
> >> +		dma_addr = offset = 0;
> >> +		size = dev->coherent_dma_mask;
> >
> > Can you check one thing here? I believe the size argument as returned
> > from of_dma_get_range is inclusive (e.g. 0x100000000), while the coherent
> > mask by definition is exlusive (e.g. 0xffffffff), so the size needs to
> > be adapted here. I haven't checked all the code here though, so I may
> > be wrong.
> 
> size returned by of_dma_get_range() is inclusive as you indicated. Fromt 
> the grep of dma-ranges in arch/arm/boot/dts, I see
> 
> arch/arm/boot/dts/keystone.dtsi:		dma-ranges = <0x80000000 0x8 
> 0x00000000 0x80000000>;
> arch/arm/boot/dts/keystone.dtsi:			dma-ranges;
> arch/arm/boot/dts/r8a7790.dtsi:		dma-ranges = <0x42000000 0 0x40000000 0 
> 0x40000000 0 0x80000000
> arch/arm/boot/dts/integratorap.dts:	dma-ranges = <0x80000000 0x0 
> 0x80000000>;
> arch/arm/boot/dts/r8a7791.dtsi:		dma-ranges = <0x42000000 0 0x40000000 0 
> 0x40000000 0 0x80000000
> 
> So I guess I need to change the code to
> 
>  >> +	ret = of_dma_get_range(parent_np,&dma_addr,&paddr,&size);
>  >> +	if (ret<  0) {
>  >> +		dma_addr = offset = 0;
>  >> +		size = dev->coherent_dma_mask + 1;

Right.

> >> +	} else {
> >> +		offset = PFN_DOWN(paddr - dma_addr);
> >> +		dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
> >> +	}
> >> +	dev->dma_pfn_offset = offset;
> >> +
> >> +	coherent = of_dma_is_coherent(parent_np);
> >> +	dev_dbg(dev, "device is%sdma coherent\n",
> >> +		coherent ? " " : " not ");
> >> +
> >> +	arch_setup_dma_ops(dev, dma_addr, size, NULL, coherent);
> >
> > Basically, I would use limit the size argument we pass into
> > arch_setup_dma_ops to the minimum of 'size' and 'dma_mask' here,
> > after converting into the same format. We should make sure we do the
> > same thing for platform_device as well, so it might be better to do
> > it inside of arch_setup_dma_ops instead.
> 
> Do you think following changes will work?
> 
> +++ b/arch/arm/mm/dma-mapping.c
> @@ -2052,9 +2052,10 @@ void arch_setup_dma_ops(struct device *dev, u64 
> dma_base, u64 size,
>                          struct iommu_ops *iommu, bool coherent)
>   {
>          struct dma_map_ops *dma_ops;
> +       u64 temp_size = min((*(dev->dma_mask) + 1), size);
> 
>          dev->archdata.dma_coherent = coherent;
> -       if (arm_setup_iommu_dma_ops(dev, dma_base, size, iommu))
> +       if (arm_setup_iommu_dma_ops(dev, dma_base, temp_size, iommu))
> 
> If you agree, I will post v1 of the patch with these updates. Let me 
> know. I did some basic tests on Keystone with these changes and it works 
> fine.

It's not exactly what I meant. My main point was that we need to limit
dev->dma_mask to (size-1) here, but you are not touching that. We can
either change the two functions in which we first assign the dma_mask
(platform and pci bus), or set it again in arch_setup_dma_ops (on each
architecture implementing it), either way would work. Someone else
might have a stronger opinion on that matter.

For arm_setup_iommu_dma_ops, passing the original size is probably
best.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Murali Karicheri Dec. 22, 2014, 9:40 p.m. UTC | #4
On 12/22/2014 02:43 PM, Arnd Bergmann wrote:
> On Monday 22 December 2014 12:46:12 Murali Karicheri wrote:
>> On 12/18/2014 05:29 PM, Arnd Bergmann wrote:
>>> On Thursday 18 December 2014 17:07:04 Murali Karicheri wrote:
>>>> Add of_pci_dma_configure() to allow updating the dma configuration
>>>> of the pci device using the configuration from the parent of
>>>> the root bridge device.
>>>> +	/*
>>>> +	 * Set it to coherent_dma_mask by default if the architecture
>>>> +	 * code has not set it.
>>>> +	 */
>>>> +	if (!dev->dma_mask)
>>>> +		dev->dma_mask =&dev->coherent_dma_mask;
>>>> +
>>>> +	ret = of_dma_get_range(parent_np,&dma_addr,&paddr,&size);
>>>> +	if (ret<   0) {
>>>> +		dma_addr = offset = 0;
>>>> +		size = dev->coherent_dma_mask;
>>>
>>> Can you check one thing here? I believe the size argument as returned
>>> from of_dma_get_range is inclusive (e.g. 0x100000000), while the coherent
>>> mask by definition is exlusive (e.g. 0xffffffff), so the size needs to
>>> be adapted here. I haven't checked all the code here though, so I may
>>> be wrong.
>>
>> size returned by of_dma_get_range() is inclusive as you indicated. Fromt
>> the grep of dma-ranges in arch/arm/boot/dts, I see
>>
>> arch/arm/boot/dts/keystone.dtsi:		dma-ranges =<0x80000000 0x8
>> 0x00000000 0x80000000>;
>> arch/arm/boot/dts/keystone.dtsi:			dma-ranges;
>> arch/arm/boot/dts/r8a7790.dtsi:		dma-ranges =<0x42000000 0 0x40000000 0
>> 0x40000000 0 0x80000000
>> arch/arm/boot/dts/integratorap.dts:	dma-ranges =<0x80000000 0x0
>> 0x80000000>;
>> arch/arm/boot/dts/r8a7791.dtsi:		dma-ranges =<0x42000000 0 0x40000000 0
>> 0x40000000 0 0x80000000
>>
>> So I guess I need to change the code to
>>
>>   >>  +	ret = of_dma_get_range(parent_np,&dma_addr,&paddr,&size);
>>   >>  +	if (ret<   0) {
>>   >>  +		dma_addr = offset = 0;
>>   >>  +		size = dev->coherent_dma_mask + 1;
>
> Right.
>
>>>> +	} else {
>>>> +		offset = PFN_DOWN(paddr - dma_addr);
>>>> +		dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
>>>> +	}
>>>> +	dev->dma_pfn_offset = offset;
>>>> +
>>>> +	coherent = of_dma_is_coherent(parent_np);
>>>> +	dev_dbg(dev, "device is%sdma coherent\n",
>>>> +		coherent ? " " : " not ");
>>>> +
>>>> +	arch_setup_dma_ops(dev, dma_addr, size, NULL, coherent);
>>>
>>> Basically, I would use limit the size argument we pass into
>>> arch_setup_dma_ops to the minimum of 'size' and 'dma_mask' here,
>>> after converting into the same format. We should make sure we do the
>>> same thing for platform_device as well, so it might be better to do
>>> it inside of arch_setup_dma_ops instead.
>>
>> Do you think following changes will work?
>>
>> +++ b/arch/arm/mm/dma-mapping.c
>> @@ -2052,9 +2052,10 @@ void arch_setup_dma_ops(struct device *dev, u64
>> dma_base, u64 size,
>>                           struct iommu_ops *iommu, bool coherent)
>>    {
>>           struct dma_map_ops *dma_ops;
>> +       u64 temp_size = min((*(dev->dma_mask) + 1), size);
>>
>>           dev->archdata.dma_coherent = coherent;
>> -       if (arm_setup_iommu_dma_ops(dev, dma_base, size, iommu))
>> +       if (arm_setup_iommu_dma_ops(dev, dma_base, temp_size, iommu))
>>
>> If you agree, I will post v1 of the patch with these updates. Let me
>> know. I did some basic tests on Keystone with these changes and it works
>> fine.
>
> It's not exactly what I meant. My main point was that we need to limit
> dev->dma_mask to (size-1) here, but you are not touching that.

if you mean overriding the dev->dma_mask to min((*dev->dma_mask), 
size-1), then I am getting the error "Coherent DMA mask 0x7fffffff (pfn 
0x780000-0x800000) covers a smaller range of system memory than the DMA 
zone pfn 0x0-0x880000) when the devices on Keystone tries to set the dma 
mask. Something wrong and I need to look into the code.

> either change the two functions in which we first assign the dma_mask
> (platform and pci bus), or set it again in arch_setup_dma_ops (on each
> architecture implementing it), either way would work. Someone else
> might have a stronger opinion on that matter.
>
> For arm_setup_iommu_dma_ops, passing the original size is probably
> best.
>
> 	Arnd
Arnd Bergmann Dec. 22, 2014, 10:24 p.m. UTC | #5
On Monday 22 December 2014 16:40:43 Murali Karicheri wrote:
> >> +++ b/arch/arm/mm/dma-mapping.c
> >> @@ -2052,9 +2052,10 @@ void arch_setup_dma_ops(struct device *dev, u64
> >> dma_base, u64 size,
> >>                           struct iommu_ops *iommu, bool coherent)
> >>    {
> >>           struct dma_map_ops *dma_ops;
> >> +       u64 temp_size = min((*(dev->dma_mask) + 1), size);
> >>
> >>           dev->archdata.dma_coherent = coherent;
> >> -       if (arm_setup_iommu_dma_ops(dev, dma_base, size, iommu))
> >> +       if (arm_setup_iommu_dma_ops(dev, dma_base, temp_size, iommu))
> >>
> >> If you agree, I will post v1 of the patch with these updates. Let me
> >> know. I did some basic tests on Keystone with these changes and it works
> >> fine.
> >
> > It's not exactly what I meant. My main point was that we need to limit
> > dev->dma_mask to (size-1) here, but you are not touching that.
> 
> if you mean overriding the dev->dma_mask to min((*dev->dma_mask), 
> size-1), then I am getting the error "Coherent DMA mask 0x7fffffff (pfn 
> 0x780000-0x800000) covers a smaller range of system memory than the DMA 
> zone pfn 0x0-0x880000) when the devices on Keystone tries to set the dma 
> mask. Something wrong and I need to look into the code.

Right, it sounds like the offset was applied incorrectly at some point.

What are the DMA zone size and the phys-offset?

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Murali Karicheri Dec. 22, 2014, 10:40 p.m. UTC | #6
On 12/22/2014 05:24 PM, Arnd Bergmann wrote:
> On Monday 22 December 2014 16:40:43 Murali Karicheri wrote:
>>>> +++ b/arch/arm/mm/dma-mapping.c
>>>> @@ -2052,9 +2052,10 @@ void arch_setup_dma_ops(struct device *dev, u64
>>>> dma_base, u64 size,
>>>>                            struct iommu_ops *iommu, bool coherent)
>>>>     {
>>>>            struct dma_map_ops *dma_ops;
>>>> +       u64 temp_size = min((*(dev->dma_mask) + 1), size);
>>>>
>>>>            dev->archdata.dma_coherent = coherent;
>>>> -       if (arm_setup_iommu_dma_ops(dev, dma_base, size, iommu))
>>>> +       if (arm_setup_iommu_dma_ops(dev, dma_base, temp_size, iommu))
>>>>
>>>> If you agree, I will post v1 of the patch with these updates. Let me
>>>> know. I did some basic tests on Keystone with these changes and it works
>>>> fine.
>>>
>>> It's not exactly what I meant. My main point was that we need to limit
>>> dev->dma_mask to (size-1) here, but you are not touching that.
>>
>> if you mean overriding the dev->dma_mask to min((*dev->dma_mask),
>> size-1), then I am getting the error "Coherent DMA mask 0x7fffffff (pfn
>> 0x780000-0x800000) covers a smaller range of system memory than the DMA
>> zone pfn 0x0-0x880000) when the devices on Keystone tries to set the dma
>> mask. Something wrong and I need to look into the code.
>
> Right, it sounds like the offset was applied incorrectly at some point.
>
> What are the DMA zone size and the phys-offset?
2G and 0x8_0000_0000. This limit the usable DMA size to 2G on Keystone, 
I believe you shouldn't be limiting the dma mask to size-1 in this case, 
right? The DT setup the dma-range to have a size of 2G (0x80000000).

Murali
>
> 	Arnd
Arnd Bergmann Dec. 22, 2014, 10:44 p.m. UTC | #7
On Monday 22 December 2014 17:40:30 Murali Karicheri wrote:
> On 12/22/2014 05:24 PM, Arnd Bergmann wrote:
> > On Monday 22 December 2014 16:40:43 Murali Karicheri wrote:
> >>>> +++ b/arch/arm/mm/dma-mapping.c
> >>>> @@ -2052,9 +2052,10 @@ void arch_setup_dma_ops(struct device *dev, u64
> >>>> dma_base, u64 size,
> >>>>                            struct iommu_ops *iommu, bool coherent)
> >>>>     {
> >>>>            struct dma_map_ops *dma_ops;
> >>>> +       u64 temp_size = min((*(dev->dma_mask) + 1), size);
> >>>>
> >>>>            dev->archdata.dma_coherent = coherent;
> >>>> -       if (arm_setup_iommu_dma_ops(dev, dma_base, size, iommu))
> >>>> +       if (arm_setup_iommu_dma_ops(dev, dma_base, temp_size, iommu))
> >>>>
> >>>> If you agree, I will post v1 of the patch with these updates. Let me
> >>>> know. I did some basic tests on Keystone with these changes and it works
> >>>> fine.
> >>>
> >>> It's not exactly what I meant. My main point was that we need to limit
> >>> dev->dma_mask to (size-1) here, but you are not touching that.
> >>
> >> if you mean overriding the dev->dma_mask to min((*dev->dma_mask),
> >> size-1), then I am getting the error "Coherent DMA mask 0x7fffffff (pfn
> >> 0x780000-0x800000) covers a smaller range of system memory than the DMA
> >> zone pfn 0x0-0x880000) when the devices on Keystone tries to set the dma
> >> mask. Something wrong and I need to look into the code.
> >
> > Right, it sounds like the offset was applied incorrectly at some point.
> >
> > What are the DMA zone size and the phys-offset?
> 2G and 0x8_0000_0000. This limit the usable DMA size to 2G on Keystone, 
> I believe you shouldn't be limiting the dma mask to size-1 in this case, 
> right? The DT setup the dma-range to have a size of 2G (0x80000000).

No, the problem is something else: the pfn range that we calculate for the
coherent DMA mask should have been 0x800000-0x880000, not 0x780000-0x800000,
so we would exactly match the ZONE_DMA.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arnd Bergmann Dec. 22, 2014, 11:07 p.m. UTC | #8
On Monday 22 December 2014 23:44:52 Arnd Bergmann wrote:
> On Monday 22 December 2014 17:40:30 Murali Karicheri wrote:
> > On 12/22/2014 05:24 PM, Arnd Bergmann wrote:
> > > On Monday 22 December 2014 16:40:43 Murali Karicheri wrote:
> > >>>> +++ b/arch/arm/mm/dma-mapping.c
> > >>>> @@ -2052,9 +2052,10 @@ void arch_setup_dma_ops(struct device *dev, u64
> > >>>> dma_base, u64 size,
> > >>>>                            struct iommu_ops *iommu, bool coherent)
> > >>>>     {
> > >>>>            struct dma_map_ops *dma_ops;
> > >>>> +       u64 temp_size = min((*(dev->dma_mask) + 1), size);
> > >>>>
> > >>>>            dev->archdata.dma_coherent = coherent;
> > >>>> -       if (arm_setup_iommu_dma_ops(dev, dma_base, size, iommu))
> > >>>> +       if (arm_setup_iommu_dma_ops(dev, dma_base, temp_size, iommu))
> > >>>>
> > >>>> If you agree, I will post v1 of the patch with these updates. Let me
> > >>>> know. I did some basic tests on Keystone with these changes and it works
> > >>>> fine.
> > >>>
> > >>> It's not exactly what I meant. My main point was that we need to limit
> > >>> dev->dma_mask to (size-1) here, but you are not touching that.
> > >>
> > >> if you mean overriding the dev->dma_mask to min((*dev->dma_mask),
> > >> size-1), then I am getting the error "Coherent DMA mask 0x7fffffff (pfn
> > >> 0x780000-0x800000) covers a smaller range of system memory than the DMA
> > >> zone pfn 0x0-0x880000) when the devices on Keystone tries to set the dma
> > >> mask. Something wrong and I need to look into the code.
> > >
> > > Right, it sounds like the offset was applied incorrectly at some point.
> > >
> > > What are the DMA zone size and the phys-offset?
> > 2G and 0x8_0000_0000. This limit the usable DMA size to 2G on Keystone, 
> > I believe you shouldn't be limiting the dma mask to size-1 in this case, 
> > right? The DT setup the dma-range to have a size of 2G (0x80000000).
> 
> No, the problem is something else: the pfn range that we calculate for the
> coherent DMA mask should have been 0x800000-0x880000, not 0x780000-0x800000,
> so we would exactly match the ZONE_DMA.

I gave it some more thought, and concluded that the size that gets passed
down is not really the right value to be compared to a mask if the dma-capable
area starts at a nonzero bus address.

In your case, bus addresses 0x80000000-0xffffffff are valid for DMA, so
the mask must be 0xffffffff to forbid any DMA to addresses larger than
0x100000000, not 0x7fffffff which would not be enough to cover any RAM.

I guess the dma_mask should be 'min((*dev->dma_mask), dma_base + size - 1)'
here.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c
index 88471d3..966b418 100644
--- a/drivers/of/of_pci.c
+++ b/drivers/of/of_pci.c
@@ -229,6 +229,77 @@  parse_failed:
 	return err;
 }
 EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources);
+
+/**
+ * of_get_pci_root_bridge_parent - Helper function to get the OF node of
+ *		the root bridge's parent
+ * @dev: ptr to pci_dev struct of the pci device
+ *
+ * This function will traverse the bus up to the root bus starting with
+ * the child and return the of node ptr to bridge device's parent device.
+ */
+struct device_node *of_get_pci_root_bridge_parent(struct pci_dev *dev)
+{
+	struct pci_bus *bus = dev->bus;
+	struct device *bridge;
+
+	while (!pci_is_root_bus(bus))
+		bus = bus->parent;
+	bridge = bus->bridge;
+
+	return bridge->parent->of_node;
+}
+EXPORT_SYMBOL_GPL(of_get_pci_root_bridge_parent);
+
+/**
+ * of_pci_dma_configure - Setup DMA configuration
+ * @dev: ptr to pci_dev struct of the pci device
+ *
+ * Try to get PCI devices's DMA configuration from DT and update it
+ * accordingly. This is similar to of_dma_configure() in of/platform.c
+ */
+void of_pci_dma_configure(struct pci_dev *pci_dev)
+{
+	struct device *dev = &pci_dev->dev;
+	u64 dma_addr, paddr, size;
+	struct device_node *parent_np;
+	unsigned long offset;
+	bool coherent;
+	int ret;
+
+	parent_np = of_get_pci_root_bridge_parent(pci_dev);
+
+	/*
+	 * Set default dma-mask to 32 bit. Drivers are expected to setup
+	 * the correct supported dma_mask.
+	 */
+	dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	/*
+	 * Set it to coherent_dma_mask by default if the architecture
+	 * code has not set it.
+	 */
+	if (!dev->dma_mask)
+		dev->dma_mask = &dev->coherent_dma_mask;
+
+	ret = of_dma_get_range(parent_np, &dma_addr, &paddr, &size);
+	if (ret < 0) {
+		dma_addr = offset = 0;
+		size = dev->coherent_dma_mask;
+	} else {
+		offset = PFN_DOWN(paddr - dma_addr);
+		dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset);
+	}
+	dev->dma_pfn_offset = offset;
+
+	coherent = of_dma_is_coherent(parent_np);
+	dev_dbg(dev, "device is%sdma coherent\n",
+		coherent ? " " : " not ");
+
+	arch_setup_dma_ops(dev, dma_addr, size, NULL, coherent);
+}
+EXPORT_SYMBOL_GPL(of_pci_dma_configure);
+
 #endif /* CONFIG_OF_ADDRESS */
 
 #ifdef CONFIG_PCI_MSI
diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h
index ce0e5ab..0465a2a 100644
--- a/include/linux/of_pci.h
+++ b/include/linux/of_pci.h
@@ -16,6 +16,8 @@  int of_pci_get_devfn(struct device_node *np);
 int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin);
 int of_pci_parse_bus_range(struct device_node *node, struct resource *res);
 int of_get_pci_domain_nr(struct device_node *node);
+struct device_node *of_get_pci_root_bridge_parent(struct pci_dev *dev);
+void of_pci_dma_configure(struct pci_dev *pci_dev);
 #else
 static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq)
 {
@@ -50,6 +52,16 @@  of_get_pci_domain_nr(struct device_node *node)
 {
 	return -1;
 }
+
+static inline struct device_node
+*of_get_pci_root_bridge_parent(struct pci_dev *dev)
+{
+	return NULL;
+}
+
+static inline void of_pci_dma_configure(struct pci_dev *pci_dev)
+{
+}
 #endif
 
 #if defined(CONFIG_OF_ADDRESS)