* Re: [PATCH v3 06/12] memory-hotplug: unregister memory section on SPARSEMEM_VMEMMAP
From: Wen Congyang @ 2012-11-21 5:12 UTC (permalink / raw)
To: Jaegeuk Hanse
Cc: linux-s390, linux-ia64, Len Brown, linux-acpi, linux-sh, x86,
linux-kernel, cmetcalf, Jianguo Wu, linux-mm, Yasuaki Ishimatsu,
paulus, Minchan Kim, KOSAKI Motohiro, David Rientjes, sparclinux,
Christoph Lameter, linuxppc-dev, Andrew Morton, Jiang Liu
In-Reply-To: <50AC608F.2020307@gmail.com>
At 11/21/2012 01:03 PM, Jaegeuk Hanse Wrote:
> On 11/21/2012 12:42 PM, Wen Congyang wrote:
>> At 11/21/2012 12:22 PM, Jaegeuk Hanse Wrote:
>>> On 11/21/2012 11:05 AM, Wen Congyang wrote:
>>>> At 11/20/2012 07:16 PM, Jaegeuk Hanse Wrote:
>>>>> On 11/01/2012 05:44 PM, Wen Congyang wrote:
>>>>>> From: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>>>>>>
>>>>>> Currently __remove_section for SPARSEMEM_VMEMMAP does nothing. But
>>>>>> even if
>>>>>> we use SPARSEMEM_VMEMMAP, we can unregister the memory_section.
>>>>>>
>>>>>> So the patch add unregister_memory_section() into __remove_section().
>>>>> Hi Yasuaki,
>>>>>
>>>>> I have a question about these sparse vmemmap memory related
>>>>> patches. Hot
>>>>> add memory need allocated vmemmap pages, but this time is allocated by
>>>>> buddy system. How can gurantee virtual address is continuous to the
>>>>> address allocated before? If not continuous, page_to_pfn and
>>>>> pfn_to_page
>>>>> can't work correctly.
>>>> vmemmap has its virtual address range:
>>>> ffffea0000000000 - ffffeaffffffffff (=40 bits) virtual memory map (1TB)
>>>>
>>>> We allocate memory from buddy system to store struct page, and its
>>>> virtual
>>>> address isn't in this range. So we should update the page table:
>>>>
>>>> kmalloc_section_memmap()
>>>> sparse_mem_map_populate()
>>>> pfn_to_page() // get the virtual address in the vmemmap range
>>>> vmemmap_populate() // we update page table here
>>>>
>>>> When we use vmemmap, page_to_pfn() always returns address in the
>>>> vmemmap
>>>> range, not the address that kmalloc() returns. So the virtual address
>>>> is continuous.
>>> Hi Congyang,
>>>
>>> Another question about memory hotplug. During hot remove memory, it will
>>> also call memblock_remove to remove related memblock.
>> IIRC, we don't touch memblock when hot-add/hot-remove memory. memblock is
>> only used for bootmem allocator. I think it isn't used after booting.
>
> In IBM pseries servers.
>
> pseries_remove_memory()
> pseries_remove_memblock()
> memblock_remove()
>
> Furthermore, memblock is set to record available memory ranges get from
> e820 map(you can check it in memblock_x86_fill()) in x86 case, after
> hot-remove memory, this range of memory can't be available, why not
> remove them as pseries servers' codes do.
Oh, it is powerpc, and I don't read this code. I will check it now.
Thanks for pointing it out.
Wen Congyang
>
>>> memblock_remove()
>>> __memblock_remove()memory-hotplug: unregister memory
>>> section on SPARSEMEM_VMEMMAP
>>>
>>> memblock_isolate_range()
>>> memblock_remove_region()
>>>
>>> But memblock_isolate_range() only record fully contained regions,
>>> regions which are partial overlapped just be splitted instead of record.
>>> So these partial overlapped regions can't be removed. Where I miss?
>> No, memblock_isolate_range() can deal with partial overlapped region.
>> =====================
>> if (rbase < base) {
>> /*
>> * @rgn intersects from below. Split and continue
>> * to process the next region - the new top half.
>> */
>> rgn->base = base;
>> rgn->size -= base - rbase;
>> type->total_size -= base - rbase;
>> memblock_insert_region(type, i, rbase, base - rbase,
>> memblock_get_region_node(rgn));
>> } else if (rend > end) {
>> /*
>> * @rgn intersects from above. Split and redo the
>> * current region - the new bottom half.
>> */
>> rgn->base = end;
>> rgn->size -= end - rbase;
>> type->total_size -= end - rbase;
>> memblock_insert_region(type, i--, rbase, end - rbase,
>> memblock_get_region_node(rgn));
>> =====================
>>
>> If the region is partial overlapped region, we will split the old
>> region into
>> two regions. After doing this, it is full contained region now.
>
> You are right, I misunderstand the codes.
>
>>
>> Thanks
>> Wen Congyang
>>
>>> Regards,
>>> Jaegeuk
>>>
>>>> Thanks
>>>> Wen Congyang
>>>>> Regards,
>>>>> Jaegeuk
>>>>>
>>>>>> CC: David Rientjes <rientjes@google.com>
>>>>>> CC: Jiang Liu <liuj97@gmail.com>
>>>>>> CC: Len Brown <len.brown@intel.com>
>>>>>> CC: Christoph Lameter <cl@linux.com>
>>>>>> Cc: Minchan Kim <minchan.kim@gmail.com>
>>>>>> CC: Andrew Morton <akpm@linux-foundation.org>
>>>>>> CC: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
>>>>>> CC: Wen Congyang <wency@cn.fujitsu.com>
>>>>>> Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>>>>>> ---
>>>>>> mm/memory_hotplug.c | 13 ++++++++-----
>>>>>> 1 file changed, 8 insertions(+), 5 deletions(-)
>>>>>>
>>>>>> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
>>>>>> index ca07433..66a79a7 100644
>>>>>> --- a/mm/memory_hotplug.c
>>>>>> +++ b/mm/memory_hotplug.c
>>>>>> @@ -286,11 +286,14 @@ static int __meminit __add_section(int nid,
>>>>>> struct zone *zone,
>>>>>> #ifdef CONFIG_SPARSEMEM_VMEMMAP
>>>>>> static int __remove_section(struct zone *zone, struct mem_section
>>>>>> *ms)
>>>>>> {
>>>>>> - /*
>>>>>> - * XXX: Freeing memmap with vmemmap is not implement yet.
>>>>>> - * This should be removed later.
>>>>>> - */
>>>>>> - return -EBUSY;
>>>>>> + int ret = -EINVAL;
>>>>>> +
>>>>>> + if (!valid_section(ms))
>>>>>> + return ret;
>>>>>> +
>>>>>> + ret = unregister_memory_section(ms);
>>>>>> +
>>>>>> + return ret;
>>>>>> }
>>>>>> #else
>>>>>> static int __remove_section(struct zone *zone, struct mem_section
>>>>>> *ms)
>>>
>
>
^ permalink raw reply
* Re: [PATCH v3 06/12] memory-hotplug: unregister memory section on SPARSEMEM_VMEMMAP
From: Jaegeuk Hanse @ 2012-11-21 5:03 UTC (permalink / raw)
To: Wen Congyang
Cc: linux-s390, linux-ia64, Len Brown, linux-acpi, linux-sh, x86,
linux-kernel, cmetcalf, Jianguo Wu, linux-mm, Yasuaki Ishimatsu,
paulus, Minchan Kim, KOSAKI Motohiro, David Rientjes, sparclinux,
Christoph Lameter, linuxppc-dev, Andrew Morton, Jiang Liu
In-Reply-To: <50AC5BC1.9050200@cn.fujitsu.com>
On 11/21/2012 12:42 PM, Wen Congyang wrote:
> At 11/21/2012 12:22 PM, Jaegeuk Hanse Wrote:
>> On 11/21/2012 11:05 AM, Wen Congyang wrote:
>>> At 11/20/2012 07:16 PM, Jaegeuk Hanse Wrote:
>>>> On 11/01/2012 05:44 PM, Wen Congyang wrote:
>>>>> From: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>>>>>
>>>>> Currently __remove_section for SPARSEMEM_VMEMMAP does nothing. But
>>>>> even if
>>>>> we use SPARSEMEM_VMEMMAP, we can unregister the memory_section.
>>>>>
>>>>> So the patch add unregister_memory_section() into __remove_section().
>>>> Hi Yasuaki,
>>>>
>>>> I have a question about these sparse vmemmap memory related patches. Hot
>>>> add memory need allocated vmemmap pages, but this time is allocated by
>>>> buddy system. How can gurantee virtual address is continuous to the
>>>> address allocated before? If not continuous, page_to_pfn and pfn_to_page
>>>> can't work correctly.
>>> vmemmap has its virtual address range:
>>> ffffea0000000000 - ffffeaffffffffff (=40 bits) virtual memory map (1TB)
>>>
>>> We allocate memory from buddy system to store struct page, and its
>>> virtual
>>> address isn't in this range. So we should update the page table:
>>>
>>> kmalloc_section_memmap()
>>> sparse_mem_map_populate()
>>> pfn_to_page() // get the virtual address in the vmemmap range
>>> vmemmap_populate() // we update page table here
>>>
>>> When we use vmemmap, page_to_pfn() always returns address in the vmemmap
>>> range, not the address that kmalloc() returns. So the virtual address
>>> is continuous.
>> Hi Congyang,
>>
>> Another question about memory hotplug. During hot remove memory, it will
>> also call memblock_remove to remove related memblock.
> IIRC, we don't touch memblock when hot-add/hot-remove memory. memblock is
> only used for bootmem allocator. I think it isn't used after booting.
In IBM pseries servers.
pseries_remove_memory()
pseries_remove_memblock()
memblock_remove()
Furthermore, memblock is set to record available memory ranges get from
e820 map(you can check it in memblock_x86_fill()) in x86 case, after
hot-remove memory, this range of memory can't be available, why not
remove them as pseries servers' codes do.
>> memblock_remove()
>> __memblock_remove()memory-hotplug: unregister memory section on SPARSEMEM_VMEMMAP
>>
>> memblock_isolate_range()
>> memblock_remove_region()
>>
>> But memblock_isolate_range() only record fully contained regions,
>> regions which are partial overlapped just be splitted instead of record.
>> So these partial overlapped regions can't be removed. Where I miss?
> No, memblock_isolate_range() can deal with partial overlapped region.
> =====================
> if (rbase < base) {
> /*
> * @rgn intersects from below. Split and continue
> * to process the next region - the new top half.
> */
> rgn->base = base;
> rgn->size -= base - rbase;
> type->total_size -= base - rbase;
> memblock_insert_region(type, i, rbase, base - rbase,
> memblock_get_region_node(rgn));
> } else if (rend > end) {
> /*
> * @rgn intersects from above. Split and redo the
> * current region - the new bottom half.
> */
> rgn->base = end;
> rgn->size -= end - rbase;
> type->total_size -= end - rbase;
> memblock_insert_region(type, i--, rbase, end - rbase,
> memblock_get_region_node(rgn));
> =====================
>
> If the region is partial overlapped region, we will split the old region into
> two regions. After doing this, it is full contained region now.
You are right, I misunderstand the codes.
>
> Thanks
> Wen Congyang
>
>> Regards,
>> Jaegeuk
>>
>>> Thanks
>>> Wen Congyang
>>>> Regards,
>>>> Jaegeuk
>>>>
>>>>> CC: David Rientjes <rientjes@google.com>
>>>>> CC: Jiang Liu <liuj97@gmail.com>
>>>>> CC: Len Brown <len.brown@intel.com>
>>>>> CC: Christoph Lameter <cl@linux.com>
>>>>> Cc: Minchan Kim <minchan.kim@gmail.com>
>>>>> CC: Andrew Morton <akpm@linux-foundation.org>
>>>>> CC: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
>>>>> CC: Wen Congyang <wency@cn.fujitsu.com>
>>>>> Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>>>>> ---
>>>>> mm/memory_hotplug.c | 13 ++++++++-----
>>>>> 1 file changed, 8 insertions(+), 5 deletions(-)
>>>>>
>>>>> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
>>>>> index ca07433..66a79a7 100644
>>>>> --- a/mm/memory_hotplug.c
>>>>> +++ b/mm/memory_hotplug.c
>>>>> @@ -286,11 +286,14 @@ static int __meminit __add_section(int nid,
>>>>> struct zone *zone,
>>>>> #ifdef CONFIG_SPARSEMEM_VMEMMAP
>>>>> static int __remove_section(struct zone *zone, struct mem_section
>>>>> *ms)
>>>>> {
>>>>> - /*
>>>>> - * XXX: Freeing memmap with vmemmap is not implement yet.
>>>>> - * This should be removed later.
>>>>> - */
>>>>> - return -EBUSY;
>>>>> + int ret = -EINVAL;
>>>>> +
>>>>> + if (!valid_section(ms))
>>>>> + return ret;
>>>>> +
>>>>> + ret = unregister_memory_section(ms);
>>>>> +
>>>>> + return ret;
>>>>> }
>>>>> #else
>>>>> static int __remove_section(struct zone *zone, struct mem_section
>>>>> *ms)
>>
^ permalink raw reply
* [git pull] Please pull powerpc.git merge branch
From: Benjamin Herrenschmidt @ 2012-11-21 4:37 UTC (permalink / raw)
To: Linus Torvalds; +Cc: linuxppc-dev list, Linux Kernel list
Hi Linus !
Here are small 52xx fixes that Anatolij asked me to pull a while back
and that I completely missed. The stuff is local to that platform code,
and was in next for a while, so it should still go into 3.7.
Thanks,
Ben.
The following changes since commit 8c23f406c6d86808726ace580657186bc3b44587:
Merge git://git.kernel.org/pub/scm/virt/kvm/kvm (2012-11-01 08:27:02 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc.git merge
for you to fetch changes up to d6dc24613c222f9057131ccbd5264a10bcba9f97:
Merge remote-tracking branch 'agust/merge' into merge (2012-11-21 13:24:49 +1100)
----------------------------------------------------------------
Anatolij Gustschin (1):
powerpc/mpc5200: move lpbfifo node and fix its interrupt property
Benjamin Herrenschmidt (1):
Merge remote-tracking branch 'agust/merge' into merge
Eric Millbrandt (1):
powerpc/pcm030: add pcm030-audio-fabric to dts
Wolfram Sang (1):
powerpc: 52xx: nop out unsupported critical IRQs
arch/powerpc/boot/dts/mpc5200b.dtsi | 6 ++++++
arch/powerpc/boot/dts/o2d.dtsi | 6 ------
arch/powerpc/boot/dts/pcm030.dts | 7 ++++++-
arch/powerpc/platforms/52xx/mpc52xx_pic.c | 9 +++++----
4 files changed, 17 insertions(+), 11 deletions(-)
^ permalink raw reply
* Re: [PATCH v3 06/12] memory-hotplug: unregister memory section on SPARSEMEM_VMEMMAP
From: Wen Congyang @ 2012-11-21 4:42 UTC (permalink / raw)
To: Jaegeuk Hanse
Cc: linux-s390, linux-ia64, Len Brown, linux-acpi, linux-sh, x86,
linux-kernel, cmetcalf, Jianguo Wu, linux-mm, Yasuaki Ishimatsu,
paulus, Minchan Kim, KOSAKI Motohiro, David Rientjes, sparclinux,
Christoph Lameter, linuxppc-dev, Andrew Morton, Jiang Liu
In-Reply-To: <50AC571C.4030504@gmail.com>
At 11/21/2012 12:22 PM, Jaegeuk Hanse Wrote:
> On 11/21/2012 11:05 AM, Wen Congyang wrote:
>> At 11/20/2012 07:16 PM, Jaegeuk Hanse Wrote:
>>> On 11/01/2012 05:44 PM, Wen Congyang wrote:
>>>> From: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>>>>
>>>> Currently __remove_section for SPARSEMEM_VMEMMAP does nothing. But
>>>> even if
>>>> we use SPARSEMEM_VMEMMAP, we can unregister the memory_section.
>>>>
>>>> So the patch add unregister_memory_section() into __remove_section().
>>> Hi Yasuaki,
>>>
>>> I have a question about these sparse vmemmap memory related patches. Hot
>>> add memory need allocated vmemmap pages, but this time is allocated by
>>> buddy system. How can gurantee virtual address is continuous to the
>>> address allocated before? If not continuous, page_to_pfn and pfn_to_page
>>> can't work correctly.
>> vmemmap has its virtual address range:
>> ffffea0000000000 - ffffeaffffffffff (=40 bits) virtual memory map (1TB)
>>
>> We allocate memory from buddy system to store struct page, and its
>> virtual
>> address isn't in this range. So we should update the page table:
>>
>> kmalloc_section_memmap()
>> sparse_mem_map_populate()
>> pfn_to_page() // get the virtual address in the vmemmap range
>> vmemmap_populate() // we update page table here
>>
>> When we use vmemmap, page_to_pfn() always returns address in the vmemmap
>> range, not the address that kmalloc() returns. So the virtual address
>> is continuous.
>
> Hi Congyang,
>
> Another question about memory hotplug. During hot remove memory, it will
> also call memblock_remove to remove related memblock.
IIRC, we don't touch memblock when hot-add/hot-remove memory. memblock is
only used for bootmem allocator. I think it isn't used after booting.
> memblock_remove()
> __memblock_remove()
> memblock_isolate_range()
> memblock_remove_region()
>
> But memblock_isolate_range() only record fully contained regions,
> regions which are partial overlapped just be splitted instead of record.
> So these partial overlapped regions can't be removed. Where I miss?
No, memblock_isolate_range() can deal with partial overlapped region.
=====================
if (rbase < base) {
/*
* @rgn intersects from below. Split and continue
* to process the next region - the new top half.
*/
rgn->base = base;
rgn->size -= base - rbase;
type->total_size -= base - rbase;
memblock_insert_region(type, i, rbase, base - rbase,
memblock_get_region_node(rgn));
} else if (rend > end) {
/*
* @rgn intersects from above. Split and redo the
* current region - the new bottom half.
*/
rgn->base = end;
rgn->size -= end - rbase;
type->total_size -= end - rbase;
memblock_insert_region(type, i--, rbase, end - rbase,
memblock_get_region_node(rgn));
=====================
If the region is partial overlapped region, we will split the old region into
two regions. After doing this, it is full contained region now.
Thanks
Wen Congyang
>
> Regards,
> Jaegeuk
>
>> Thanks
>> Wen Congyang
>>> Regards,
>>> Jaegeuk
>>>
>>>> CC: David Rientjes <rientjes@google.com>
>>>> CC: Jiang Liu <liuj97@gmail.com>
>>>> CC: Len Brown <len.brown@intel.com>
>>>> CC: Christoph Lameter <cl@linux.com>
>>>> Cc: Minchan Kim <minchan.kim@gmail.com>
>>>> CC: Andrew Morton <akpm@linux-foundation.org>
>>>> CC: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
>>>> CC: Wen Congyang <wency@cn.fujitsu.com>
>>>> Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>>>> ---
>>>> mm/memory_hotplug.c | 13 ++++++++-----
>>>> 1 file changed, 8 insertions(+), 5 deletions(-)
>>>>
>>>> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
>>>> index ca07433..66a79a7 100644
>>>> --- a/mm/memory_hotplug.c
>>>> +++ b/mm/memory_hotplug.c
>>>> @@ -286,11 +286,14 @@ static int __meminit __add_section(int nid,
>>>> struct zone *zone,
>>>> #ifdef CONFIG_SPARSEMEM_VMEMMAP
>>>> static int __remove_section(struct zone *zone, struct mem_section
>>>> *ms)
>>>> {
>>>> - /*
>>>> - * XXX: Freeing memmap with vmemmap is not implement yet.
>>>> - * This should be removed later.
>>>> - */
>>>> - return -EBUSY;
>>>> + int ret = -EINVAL;
>>>> +
>>>> + if (!valid_section(ms))
>>>> + return ret;
>>>> +
>>>> + ret = unregister_memory_section(ms);
>>>> +
>>>> + return ret;
>>>> }
>>>> #else
>>>> static int __remove_section(struct zone *zone, struct mem_section
>>>> *ms)
>>>
>
>
^ permalink raw reply
* Re: [PATCH v3 06/12] memory-hotplug: unregister memory section on SPARSEMEM_VMEMMAP
From: Jaegeuk Hanse @ 2012-11-21 4:22 UTC (permalink / raw)
To: Wen Congyang
Cc: linux-s390, linux-ia64, Len Brown, linux-acpi, linux-sh, x86,
linux-kernel, cmetcalf, Jianguo Wu, linux-mm, Yasuaki Ishimatsu,
paulus, Minchan Kim, KOSAKI Motohiro, David Rientjes, sparclinux,
Christoph Lameter, linuxppc-dev, Andrew Morton, Jiang Liu
In-Reply-To: <50AC4505.1000007@cn.fujitsu.com>
On 11/21/2012 11:05 AM, Wen Congyang wrote:
> At 11/20/2012 07:16 PM, Jaegeuk Hanse Wrote:
>> On 11/01/2012 05:44 PM, Wen Congyang wrote:
>>> From: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>>>
>>> Currently __remove_section for SPARSEMEM_VMEMMAP does nothing. But
>>> even if
>>> we use SPARSEMEM_VMEMMAP, we can unregister the memory_section.
>>>
>>> So the patch add unregister_memory_section() into __remove_section().
>> Hi Yasuaki,
>>
>> I have a question about these sparse vmemmap memory related patches. Hot
>> add memory need allocated vmemmap pages, but this time is allocated by
>> buddy system. How can gurantee virtual address is continuous to the
>> address allocated before? If not continuous, page_to_pfn and pfn_to_page
>> can't work correctly.
> vmemmap has its virtual address range:
> ffffea0000000000 - ffffeaffffffffff (=40 bits) virtual memory map (1TB)
>
> We allocate memory from buddy system to store struct page, and its virtual
> address isn't in this range. So we should update the page table:
>
> kmalloc_section_memmap()
> sparse_mem_map_populate()
> pfn_to_page() // get the virtual address in the vmemmap range
> vmemmap_populate() // we update page table here
>
> When we use vmemmap, page_to_pfn() always returns address in the vmemmap
> range, not the address that kmalloc() returns. So the virtual address
> is continuous.
Hi Congyang,
Another question about memory hotplug. During hot remove memory, it will
also call memblock_remove to remove related memblock.
memblock_remove()
__memblock_remove()
memblock_isolate_range()
memblock_remove_region()
But memblock_isolate_range() only record fully contained regions,
regions which are partial overlapped just be splitted instead of record.
So these partial overlapped regions can't be removed. Where I miss?
Regards,
Jaegeuk
> Thanks
> Wen Congyang
>> Regards,
>> Jaegeuk
>>
>>> CC: David Rientjes <rientjes@google.com>
>>> CC: Jiang Liu <liuj97@gmail.com>
>>> CC: Len Brown <len.brown@intel.com>
>>> CC: Christoph Lameter <cl@linux.com>
>>> Cc: Minchan Kim <minchan.kim@gmail.com>
>>> CC: Andrew Morton <akpm@linux-foundation.org>
>>> CC: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
>>> CC: Wen Congyang <wency@cn.fujitsu.com>
>>> Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>>> ---
>>> mm/memory_hotplug.c | 13 ++++++++-----
>>> 1 file changed, 8 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
>>> index ca07433..66a79a7 100644
>>> --- a/mm/memory_hotplug.c
>>> +++ b/mm/memory_hotplug.c
>>> @@ -286,11 +286,14 @@ static int __meminit __add_section(int nid,
>>> struct zone *zone,
>>> #ifdef CONFIG_SPARSEMEM_VMEMMAP
>>> static int __remove_section(struct zone *zone, struct mem_section *ms)
>>> {
>>> - /*
>>> - * XXX: Freeing memmap with vmemmap is not implement yet.
>>> - * This should be removed later.
>>> - */
>>> - return -EBUSY;
>>> + int ret = -EINVAL;
>>> +
>>> + if (!valid_section(ms))
>>> + return ret;
>>> +
>>> + ret = unregister_memory_section(ms);
>>> +
>>> + return ret;
>>> }
>>> #else
>>> static int __remove_section(struct zone *zone, struct mem_section *ms)
>>
^ permalink raw reply
* Re: [PATCH v3 06/12] memory-hotplug: unregister memory section on SPARSEMEM_VMEMMAP
From: Wen Congyang @ 2012-11-21 3:05 UTC (permalink / raw)
To: Jaegeuk Hanse
Cc: linux-s390, linux-ia64, Len Brown, linux-acpi, linux-sh, x86,
linux-kernel, cmetcalf, Jianguo Wu, linux-mm, Yasuaki Ishimatsu,
paulus, Minchan Kim, KOSAKI Motohiro, David Rientjes, sparclinux,
Christoph Lameter, linuxppc-dev, Andrew Morton, Jiang Liu
In-Reply-To: <50AB669D.3060007@gmail.com>
At 11/20/2012 07:16 PM, Jaegeuk Hanse Wrote:
> On 11/01/2012 05:44 PM, Wen Congyang wrote:
>> From: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>>
>> Currently __remove_section for SPARSEMEM_VMEMMAP does nothing. But
>> even if
>> we use SPARSEMEM_VMEMMAP, we can unregister the memory_section.
>>
>> So the patch add unregister_memory_section() into __remove_section().
>
> Hi Yasuaki,
>
> I have a question about these sparse vmemmap memory related patches. Hot
> add memory need allocated vmemmap pages, but this time is allocated by
> buddy system. How can gurantee virtual address is continuous to the
> address allocated before? If not continuous, page_to_pfn and pfn_to_page
> can't work correctly.
vmemmap has its virtual address range:
ffffea0000000000 - ffffeaffffffffff (=40 bits) virtual memory map (1TB)
We allocate memory from buddy system to store struct page, and its virtual
address isn't in this range. So we should update the page table:
kmalloc_section_memmap()
sparse_mem_map_populate()
pfn_to_page() // get the virtual address in the vmemmap range
vmemmap_populate() // we update page table here
When we use vmemmap, page_to_pfn() always returns address in the vmemmap
range, not the address that kmalloc() returns. So the virtual address
is continuous.
Thanks
Wen Congyang
>
> Regards,
> Jaegeuk
>
>>
>> CC: David Rientjes <rientjes@google.com>
>> CC: Jiang Liu <liuj97@gmail.com>
>> CC: Len Brown <len.brown@intel.com>
>> CC: Christoph Lameter <cl@linux.com>
>> Cc: Minchan Kim <minchan.kim@gmail.com>
>> CC: Andrew Morton <akpm@linux-foundation.org>
>> CC: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
>> CC: Wen Congyang <wency@cn.fujitsu.com>
>> Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>> ---
>> mm/memory_hotplug.c | 13 ++++++++-----
>> 1 file changed, 8 insertions(+), 5 deletions(-)
>>
>> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
>> index ca07433..66a79a7 100644
>> --- a/mm/memory_hotplug.c
>> +++ b/mm/memory_hotplug.c
>> @@ -286,11 +286,14 @@ static int __meminit __add_section(int nid,
>> struct zone *zone,
>> #ifdef CONFIG_SPARSEMEM_VMEMMAP
>> static int __remove_section(struct zone *zone, struct mem_section *ms)
>> {
>> - /*
>> - * XXX: Freeing memmap with vmemmap is not implement yet.
>> - * This should be removed later.
>> - */
>> - return -EBUSY;
>> + int ret = -EINVAL;
>> +
>> + if (!valid_section(ms))
>> + return ret;
>> +
>> + ret = unregister_memory_section(ms);
>> +
>> + return ret;
>> }
>> #else
>> static int __remove_section(struct zone *zone, struct mem_section *ms)
>
>
^ permalink raw reply
* Re: Please pull 'merge' branch of 5xxx tree
From: Benjamin Herrenschmidt @ 2012-11-21 2:12 UTC (permalink / raw)
To: Anatolij Gustschin; +Cc: linuxppc-dev
In-Reply-To: <20121120092759.5741aeef@wker>
On Tue, 2012-11-20 at 09:27 +0100, Anatolij Gustschin wrote:
> Hi Ben,
>
> On Thu, 25 Oct 2012 22:41:13 +0200
> Anatolij Gustschin <agust@denx.de> wrote:
>
> > Hi Ben,
> >
> > please pull three mpc5200 fixes for 3.7:
>
> Ping.
Sorry, missed that, dunno whether it's too late, I'll try.
Cheers,
Ben.
^ permalink raw reply
* Re: [PATCH] cpuidle: Measure idle state durations with monotonic clock
From: Rafael J. Wysocki @ 2012-11-21 0:17 UTC (permalink / raw)
To: Len Brown, Len Brown
Cc: Kevin Hilman, Deepthi Dharwar, Trinabh Gupta, Lists Linaro-dev,
linux-pm, Daniel Lezcano, linux-kernel, linux-acpi,
Srivatsa S. Bhat, Julius Werner, Andrew Morton, linuxppc-dev,
Sameer Nanda
In-Reply-To: <1352944590-8776-1-git-send-email-jwerner@chromium.org>
On Wednesday, November 14, 2012 05:56:30 PM Julius Werner wrote:
> Many cpuidle drivers measure their time spent in an idle state by
> reading the wallclock time before and after idling and calculating the
> difference. This leads to erroneous results when the wallclock time gets
> updated by another processor in the meantime, adding that clock
> adjustment to the idle state's time counter.
>
> If the clock adjustment was negative, the result is even worse due to an
> erroneous cast from int to unsigned long long of the last_residency
> variable. The negative 32 bit integer will zero-extend and result in a
> forward time jump of roughly four billion milliseconds or 1.3 hours on
> the idle state residency counter.
>
> This patch changes all affected cpuidle drivers to either use the
> monotonic clock for their measurements or make use of the generic time
> measurement wrapper in cpuidle.c, which was already working correctly.
> Some superfluous CLIs/STIs in the ACPI code are removed (interrupts
> should always already be disabled before entering the idle function, and
> not get reenabled until the generic wrapper has performed its second
> measurement). It also removes the erroneous cast, making sure that
> negative residency values are applied correctly even though they should
> not appear anymore.
>
> Signed-off-by: Julius Werner <jwerner@chromium.org>
Len, I need your ACK on intel_idle change in this patch if you agree with it.
Thanks,
Rafael
> ---
> arch/powerpc/platforms/pseries/processor_idle.c | 4 +-
> drivers/acpi/processor_idle.c | 57 +---------------------
> drivers/cpuidle/cpuidle.c | 3 +-
> drivers/idle/intel_idle.c | 14 +-----
> 4 files changed, 7 insertions(+), 71 deletions(-)
>
> diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
> index 45d00e5..4d806b4 100644
> --- a/arch/powerpc/platforms/pseries/processor_idle.c
> +++ b/arch/powerpc/platforms/pseries/processor_idle.c
> @@ -36,7 +36,7 @@ static struct cpuidle_state *cpuidle_state_table;
> static inline void idle_loop_prolog(unsigned long *in_purr, ktime_t *kt_before)
> {
>
> - *kt_before = ktime_get_real();
> + *kt_before = ktime_get();
> *in_purr = mfspr(SPRN_PURR);
> /*
> * Indicate to the HV that we are idle. Now would be
> @@ -50,7 +50,7 @@ static inline s64 idle_loop_epilog(unsigned long in_purr, ktime_t kt_before)
> get_lppaca()->wait_state_cycles += mfspr(SPRN_PURR) - in_purr;
> get_lppaca()->idle = 0;
>
> - return ktime_to_us(ktime_sub(ktime_get_real(), kt_before));
> + return ktime_to_us(ktime_sub(ktime_get(), kt_before));
> }
>
> static int snooze_loop(struct cpuidle_device *dev,
> diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
> index e8086c7..f1a5da4 100644
> --- a/drivers/acpi/processor_idle.c
> +++ b/drivers/acpi/processor_idle.c
> @@ -735,31 +735,18 @@ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx)
> static int acpi_idle_enter_c1(struct cpuidle_device *dev,
> struct cpuidle_driver *drv, int index)
> {
> - ktime_t kt1, kt2;
> - s64 idle_time;
> struct acpi_processor *pr;
> struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
> struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);
>
> pr = __this_cpu_read(processors);
> - dev->last_residency = 0;
>
> if (unlikely(!pr))
> return -EINVAL;
>
> - local_irq_disable();
> -
> -
> lapic_timer_state_broadcast(pr, cx, 1);
> - kt1 = ktime_get_real();
> acpi_idle_do_entry(cx);
> - kt2 = ktime_get_real();
> - idle_time = ktime_to_us(ktime_sub(kt2, kt1));
> -
> - /* Update device last_residency*/
> - dev->last_residency = (int)idle_time;
>
> - local_irq_enable();
> lapic_timer_state_broadcast(pr, cx, 0);
>
> return index;
> @@ -806,19 +793,12 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
> struct acpi_processor *pr;
> struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
> struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);
> - ktime_t kt1, kt2;
> - s64 idle_time_ns;
> - s64 idle_time;
>
> pr = __this_cpu_read(processors);
> - dev->last_residency = 0;
>
> if (unlikely(!pr))
> return -EINVAL;
>
> - local_irq_disable();
> -
> -
> if (cx->entry_method != ACPI_CSTATE_FFH) {
> current_thread_info()->status &= ~TS_POLLING;
> /*
> @@ -829,7 +809,6 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
>
> if (unlikely(need_resched())) {
> current_thread_info()->status |= TS_POLLING;
> - local_irq_enable();
> return -EINVAL;
> }
> }
> @@ -843,22 +822,12 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
> if (cx->type == ACPI_STATE_C3)
> ACPI_FLUSH_CPU_CACHE();
>
> - kt1 = ktime_get_real();
> /* Tell the scheduler that we are going deep-idle: */
> sched_clock_idle_sleep_event();
> acpi_idle_do_entry(cx);
> - kt2 = ktime_get_real();
> - idle_time_ns = ktime_to_ns(ktime_sub(kt2, kt1));
> - idle_time = idle_time_ns;
> - do_div(idle_time, NSEC_PER_USEC);
>
> - /* Update device last_residency*/
> - dev->last_residency = (int)idle_time;
> + sched_clock_idle_wakeup_event(0);
>
> - /* Tell the scheduler how much we idled: */
> - sched_clock_idle_wakeup_event(idle_time_ns);
> -
> - local_irq_enable();
> if (cx->entry_method != ACPI_CSTATE_FFH)
> current_thread_info()->status |= TS_POLLING;
>
> @@ -883,13 +852,8 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
> struct acpi_processor *pr;
> struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
> struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);
> - ktime_t kt1, kt2;
> - s64 idle_time_ns;
> - s64 idle_time;
> -
>
> pr = __this_cpu_read(processors);
> - dev->last_residency = 0;
>
> if (unlikely(!pr))
> return -EINVAL;
> @@ -899,16 +863,11 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
> return drv->states[drv->safe_state_index].enter(dev,
> drv, drv->safe_state_index);
> } else {
> - local_irq_disable();
> acpi_safe_halt();
> - local_irq_enable();
> return -EBUSY;
> }
> }
>
> - local_irq_disable();
> -
> -
> if (cx->entry_method != ACPI_CSTATE_FFH) {
> current_thread_info()->status &= ~TS_POLLING;
> /*
> @@ -919,7 +878,6 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
>
> if (unlikely(need_resched())) {
> current_thread_info()->status |= TS_POLLING;
> - local_irq_enable();
> return -EINVAL;
> }
> }
> @@ -934,7 +892,6 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
> */
> lapic_timer_state_broadcast(pr, cx, 1);
>
> - kt1 = ktime_get_real();
> /*
> * disable bus master
> * bm_check implies we need ARB_DIS
> @@ -965,18 +922,9 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
> c3_cpu_count--;
> raw_spin_unlock(&c3_lock);
> }
> - kt2 = ktime_get_real();
> - idle_time_ns = ktime_to_ns(ktime_sub(kt2, kt1));
> - idle_time = idle_time_ns;
> - do_div(idle_time, NSEC_PER_USEC);
> -
> - /* Update device last_residency*/
> - dev->last_residency = (int)idle_time;
>
> - /* Tell the scheduler how much we idled: */
> - sched_clock_idle_wakeup_event(idle_time_ns);
> + sched_clock_idle_wakeup_event(0);
>
> - local_irq_enable();
> if (cx->entry_method != ACPI_CSTATE_FFH)
> current_thread_info()->status |= TS_POLLING;
>
> @@ -987,6 +935,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
> struct cpuidle_driver acpi_idle_driver = {
> .name = "acpi_idle",
> .owner = THIS_MODULE,
> + .en_core_tk_irqen = 1,
> };
>
> /**
> diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
> index 7f15b85..1536edd 100644
> --- a/drivers/cpuidle/cpuidle.c
> +++ b/drivers/cpuidle/cpuidle.c
> @@ -109,8 +109,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
> /* This can be moved to within driver enter routine
> * but that results in multiple copies of same code.
> */
> - dev->states_usage[entered_state].time +=
> - (unsigned long long)dev->last_residency;
> + dev->states_usage[entered_state].time += dev->last_residency;
> dev->states_usage[entered_state].usage++;
> } else {
> dev->last_residency = 0;
> diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
> index b0f6b4c..c49c04d 100644
> --- a/drivers/idle/intel_idle.c
> +++ b/drivers/idle/intel_idle.c
> @@ -56,7 +56,6 @@
> #include <linux/kernel.h>
> #include <linux/cpuidle.h>
> #include <linux/clockchips.h>
> -#include <linux/hrtimer.h> /* ktime_get_real() */
> #include <trace/events/power.h>
> #include <linux/sched.h>
> #include <linux/notifier.h>
> @@ -72,6 +71,7 @@
> static struct cpuidle_driver intel_idle_driver = {
> .name = "intel_idle",
> .owner = THIS_MODULE,
> + .en_core_tk_irqen = 1,
> };
> /* intel_idle.max_cstate=0 disables driver */
> static int max_cstate = MWAIT_MAX_NUM_CSTATES - 1;
> @@ -281,8 +281,6 @@ static int intel_idle(struct cpuidle_device *dev,
> struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
> unsigned long eax = (unsigned long)cpuidle_get_statedata(state_usage);
> unsigned int cstate;
> - ktime_t kt_before, kt_after;
> - s64 usec_delta;
> int cpu = smp_processor_id();
>
> cstate = (((eax) >> MWAIT_SUBSTATE_SIZE) & MWAIT_CSTATE_MASK) + 1;
> @@ -297,8 +295,6 @@ static int intel_idle(struct cpuidle_device *dev,
> if (!(lapic_timer_reliable_states & (1 << (cstate))))
> clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu);
>
> - kt_before = ktime_get_real();
> -
> stop_critical_timings();
> if (!need_resched()) {
>
> @@ -310,17 +306,9 @@ static int intel_idle(struct cpuidle_device *dev,
>
> start_critical_timings();
>
> - kt_after = ktime_get_real();
> - usec_delta = ktime_to_us(ktime_sub(kt_after, kt_before));
> -
> - local_irq_enable();
> -
> if (!(lapic_timer_reliable_states & (1 << (cstate))))
> clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu);
>
> - /* Update cpuidle counters */
> - dev->last_residency = (int)usec_delta;
> -
> return index;
> }
>
>
--
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.
^ permalink raw reply
* Re: [PATCH 4/4 v5] iommu/fsl: Freescale PAMU driver and IOMMU API implementation.
From: Timur Tabi @ 2012-11-20 22:52 UTC (permalink / raw)
To: Varun Sethi; +Cc: scottwood, joerg.roedel, iommu, linuxppc-dev, linux-kernel
In-Reply-To: <1353419697-31269-5-git-send-email-Varun.Sethi@freescale.com>
Varun Sethi wrote:
> diff --git a/drivers/iommu/fsl_pamu.h b/drivers/iommu/fsl_pamu.h
> new file mode 100644
> index 0000000..6d32fb5
> --- /dev/null
> +++ b/drivers/iommu/fsl_pamu.h
> @@ -0,0 +1,398 @@
> +/*
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + * Copyright (C) 2012 Freescale Semiconductor, Inc.
> + *
> + */
> +
> +#ifndef __FSL_PAMU_H
> +#define __FSL_PAMU_H
> +
> +/* Bit Field macros
> + * v = bit field variable; m = mask, m##_SHIFT = shift, x = value to load
> + */
> +#define set_bf(v, m, x) (v = ((v) & ~(m)) | (((x) << (m##_SHIFT)) & (m)))
> +#define get_bf(v, m) (((v) & (m)) >> (m##_SHIFT))
> +
> +/* PAMU CCSR space */
> +#define PAMU_PGC 0x00000000 /* Allows all peripheral accesses */
> +#define PAMU_PE 0x40000000 /* enable PAMU */
> +
> +/* PAMU_OFFSET to the next pamu space in ccsr */
> +#define PAMU_OFFSET 0x1000
> +
> +#define PAMU_MMAP_REGS_BASE 0
> +
> +struct pamu_mmap_regs {
> + u32 ppbah;
> + u32 ppbal;
> + u32 pplah;
> + u32 pplal;
> + u32 spbah;
> + u32 spbal;
> + u32 splah;
> + u32 splal;
> + u32 obah;
> + u32 obal;
> + u32 olah;
> + u32 olal;
> +};
> +
> +/* PAMU Error Registers */
> +#define PAMU_POES1 0x0040
> +#define PAMU_POES2 0x0044
> +#define PAMU_POEAH 0x0048
> +#define PAMU_POEAL 0x004C
> +#define PAMU_AVS1 0x0050
> +#define PAMU_AVS1_AV 0x1
> +#define PAMU_AVS1_OTV 0x6
> +#define PAMU_AVS1_APV 0x78
> +#define PAMU_AVS1_WAV 0x380
> +#define PAMU_AVS1_LAV 0x1c00
> +#define PAMU_AVS1_GCV 0x2000
> +#define PAMU_AVS1_PDV 0x4000
> +#define PAMU_AV_MASK (PAMU_AVS1_AV | PAMU_AVS1_OTV | PAMU_AVS1_APV | PAMU_AVS1_WAV \
> + | PAMU_AVS1_LAV | PAMU_AVS1_GCV | PAMU_AVS1_PDV)
> +#define PAMU_AVS1_LIODN_SHIFT 16
> +#define PAMU_LAV_LIODN_NOT_IN_PPAACT 0x400
I think you can move most of macros in this .h file into one of the .c files.
> diff --git a/drivers/iommu/fsl_pamu_domain.c b/drivers/iommu/fsl_pamu_domain.c
> new file mode 100644
> index 0000000..d473447
> --- /dev/null
> +++ b/drivers/iommu/fsl_pamu_domain.c
The code in this file is generally hard to read because you
1) have very few comments
2) you have a lot of expressions that span several lines, like this one:
if ((iova >= geom_attr->aperture_start &&
iova < geom_attr->aperture_end - 1 &&
size <= geom_size) &&
!align_check) {
Try adding a few more comments (e.g. each function should be commented)
and maybe try to break up a few of these lines.
> @@ -0,0 +1,978 @@
> +/*
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + * Copyright (C) 2012 Freescale Semiconductor, Inc.
> + * Author: Varun Sethi <varun.sethi@freescale.com>
> + *
> + */
> +
> +#define pr_fmt(fmt) "fsl-pamu-domain: %s: " fmt, __func__
You don't use this macro.
> +
> +#include <linux/init.h>
> +#include <linux/iommu.h>
> +#include <linux/notifier.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/types.h>
> +#include <linux/mm.h>
> +#include <linux/interrupt.h>
> +#include <linux/device.h>
> +#include <linux/of_platform.h>
> +#include <linux/bootmem.h>
> +#include <linux/err.h>
> +#include <asm/io.h>
> +#include <asm/bitops.h>
> +
> +#include "fsl_pamu_domain.h"
> +
> +/* This bitmap advertises the page sizes supported by PAMU hardware
> + * to the IOMMU API.
> + */
> +#define FSL_PAMU_PGSIZES (~0xFFFUL)
> +
> +/* global spinlock that needs to be held while
> + * configuring PAMU.
> + */
> +static DEFINE_SPINLOCK(iommu_lock);
> +
> +static struct kmem_cache *fsl_pamu_domain_cache;
> +static struct kmem_cache *iommu_devinfo_cache;
> +static DEFINE_SPINLOCK(device_domain_lock);
> +
> +int __init iommu_init_mempool(void)
> +{
> +
> + fsl_pamu_domain_cache = kmem_cache_create("fsl_pamu_domain",
> + sizeof(struct fsl_dma_domain),
> + 0,
> + SLAB_HWCACHE_ALIGN,
> +
> + NULL);
> + if (!fsl_pamu_domain_cache) {
> + pr_err("Couldn't create fsl iommu_domain cache\n");
ALL of these pr_xxx functions (in the entire file) need to have a
"fsl-pamu-domain:" prefix. Maybe that's why you created pr_fmt(), but you
forgot to use it.
The above message should look like this:
pr_err("fsl-pamu-domain: could not create iommu domain cache\n");
> + return -ENOMEM;
> + }
> +
> + iommu_devinfo_cache = kmem_cache_create("iommu_devinfo",
> + sizeof(struct device_domain_info),
> + 0,
> + SLAB_HWCACHE_ALIGN,
> + NULL);
> + if (!iommu_devinfo_cache) {
> + pr_err("Couldn't create devinfo cache\n");
> + kmem_cache_destroy(fsl_pamu_domain_cache);
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> +
> +
> +static int reconfig_win(int liodn, struct fsl_dma_domain *domain)
> +{
> + int ret;
> +
> + spin_lock(&iommu_lock);
> + ret = pamu_config_ppaace(liodn, domain->mapped_iova,
> + domain->mapped_size,
> + ~(u32)0,
> + domain->paddr >> PAMU_PAGE_SHIFT,
> + domain->snoop_id, domain->stash_id,
> + 0, domain->prot);
> + spin_unlock(&iommu_lock);
> + if (ret) {
> + pr_err("PAMU PAACE configuration failed for liodn %d\n",
> + liodn);
> + }
> + return ret;
> +}
> +
> +static void update_domain_subwin(struct fsl_dma_domain *dma_domain,
> + unsigned long iova, size_t size,
> + phys_addr_t paddr, int prot, int status)
> +{
> + struct iommu_domain *domain = dma_domain->iommu_domain;
> + u32 subwin_cnt = dma_domain->subwin_cnt;
> + dma_addr_t geom_size = dma_domain->geom_size;
> + u32 subwin_size;
> + u32 mapped_subwin;
> + u32 mapped_subwin_cnt;
> + struct dma_subwindow *sub_win_ptr;
> + int i;
> +
> + subwin_size = geom_size >> ilog2(subwin_cnt);
> + mapped_subwin = (iova - domain->geometry.aperture_start)
> + >> ilog2(subwin_size);
> + sub_win_ptr = &dma_domain->sub_win_arr[mapped_subwin];
> + mapped_subwin_cnt = (size < subwin_size) ? 1 :
> + size >> ilog2(subwin_size);
> + for (i = 0; i < mapped_subwin_cnt; i++) {
> + if (status) {
> + sub_win_ptr[i].paddr = paddr;
> + sub_win_ptr[i].size = (size < subwin_size) ?
> + size : subwin_size;
> + paddr += subwin_size;
> + sub_win_ptr[i].iova = iova;
> + iova += subwin_size;
> + }
> + sub_win_ptr[i].valid = status;
> + sub_win_ptr[i].prot = prot;
> + }
> +
> + dma_domain->mapped_subwin = mapped_subwin;
> + dma_domain->mapped_subwin_cnt = mapped_subwin_cnt;
> +}
> +
> +static int reconfig_subwin(int liodn, struct fsl_dma_domain *dma_domain)
> +{
> + u32 subwin_cnt = dma_domain->subwin_cnt;
> + int ret = 0;
> + u32 mapped_subwin;
> + u32 mapped_subwin_cnt;
> + struct dma_subwindow *sub_win_ptr;
> + unsigned long rpn;
> + int i;
> +
> + mapped_subwin = dma_domain->mapped_subwin;
> + mapped_subwin_cnt = dma_domain->mapped_subwin_cnt;
> + sub_win_ptr = &dma_domain->sub_win_arr[mapped_subwin];
> +
> + for (i = 0; i < mapped_subwin_cnt; i++) {
> + rpn = sub_win_ptr[i].paddr >> PAMU_PAGE_SHIFT,
> +
> + spin_lock(&iommu_lock);
> + ret = pamu_config_spaace(liodn, subwin_cnt, mapped_subwin,
> + sub_win_ptr[i].size,
> + ~(u32)0,
> + rpn, dma_domain->snoop_id,
> + dma_domain->stash_id,
> + (mapped_subwin == 0 &&
> + !dma_domain->enabled) ?
> + 0 : sub_win_ptr[i].valid,
Break out this expression into another variable. This code is illegible.
int enabled = 0;
for (...
if (mapped_subwin || dma_domain->enabled)
enabled = sub_win_ptr[i].valid;
> + sub_win_ptr[i].prot);
> + spin_unlock(&iommu_lock);
> + if (ret) {
> + pr_err("PAMU SPAACE configuration failed for liodn %d\n",liodn);
> + return ret;
> + }
> + mapped_subwin++;
> + }
> +
> + return ret;
> +}
> +
> +static phys_addr_t get_phys_addr(struct fsl_dma_domain *dma_domain, unsigned long iova)
> +{
> + u32 subwin_cnt = dma_domain->subwin_cnt;
> +
> + if (subwin_cnt) {
> + int i;
> + struct dma_subwindow *sub_win_ptr =
> + &dma_domain->sub_win_arr[0];
> +
> + for (i = 0; i < subwin_cnt; i++) {
> + if (sub_win_ptr[i].valid &&
> + iova >= sub_win_ptr[i].iova &&
> + iova < (sub_win_ptr[i].iova +
> + sub_win_ptr[i].size - 1))
> + return (sub_win_ptr[i].paddr + (iova &
> + (sub_win_ptr[i].size - 1)));
> + }
> + } else {
> + return (dma_domain->paddr + (iova & (dma_domain->mapped_size - 1)));
> + }
> +
> + return 0;
> +}
> +
> +static int map_liodn_subwins(int liodn, struct fsl_dma_domain *dma_domain, u32 subwin_cnt)
> +{
> + struct dma_subwindow *sub_win_ptr =
> + &dma_domain->sub_win_arr[0];
> + int i, ret;
> + unsigned long rpn;
> +
> + for (i = 0; i < subwin_cnt; i++) {
> + if (sub_win_ptr[i].valid) {
> + rpn = sub_win_ptr[i].paddr >>
> + PAMU_PAGE_SHIFT,
> + spin_lock(&iommu_lock);
> + ret = pamu_config_spaace(liodn, subwin_cnt, i,
> + sub_win_ptr[i].size,
> + ~(u32)0,
> + rpn,
> + dma_domain->snoop_id,
> + dma_domain->stash_id,
> + (i > 0) ? 1 : 0,
> + sub_win_ptr[i].prot);
> + spin_unlock(&iommu_lock);
> + if (ret) {
> + pr_err("PAMU SPAACE configuration failed for liodn %d\n",
> + liodn);
> + return ret;
> + }
> + }
> + }
> +
> + return ret;
> +}
> +
> +static int map_liodn_win(int liodn, struct fsl_dma_domain *dma_domain)
> +{
> + unsigned long rpn;
> + int ret;
> +
> + rpn = dma_domain->paddr >> PAMU_PAGE_SHIFT;
> + spin_lock(&iommu_lock);
> + ret = pamu_config_ppaace(liodn, dma_domain->mapped_iova,
> + dma_domain->mapped_size,
> + ~(u32)0,
> + rpn,
> + dma_domain->snoop_id, dma_domain->stash_id,
> + 0, dma_domain->prot);
Indentation problem
> + spin_unlock(&iommu_lock);
> + if (ret)
> + pr_err("PAMU PAACE configuration failed for liodn %d\n",
> + liodn);
> +
> + return ret;
> +}
> +
> +static int map_liodn(int liodn, struct fsl_dma_domain *dma_domain)
> +{
> + u32 subwin_cnt = dma_domain->subwin_cnt;
> +
> + if (subwin_cnt)
> + return map_liodn_subwins(liodn, dma_domain, subwin_cnt);
> + else
> + return map_liodn_win(liodn, dma_domain);
> +
> +}
> +
> +static int update_liodn(int liodn, struct fsl_dma_domain *dma_domain)
> +{
> + int ret;
> +
> + if (dma_domain->subwin_cnt) {
> + ret = reconfig_subwin(liodn, dma_domain);
> + if (ret)
> + pr_err("Subwindow reconfiguration failed for liodn %d\n", liodn);
> + } else {
> + ret = reconfig_win(liodn, dma_domain);
> + if (ret)
> + pr_err("Window reconfiguration failed for liodn %d\n", liodn);
> + }
> +
> + return ret;
> +}
> +
> +static int update_liodn_stash(int liodn, struct fsl_dma_domain *dma_domain,
> + u32 val)
> +{
> + int ret = 0, i;
> +
> + spin_lock(&iommu_lock);
> + if (!dma_domain->subwin_cnt) {
> + ret = pamu_update_paace_stash(liodn, 0, val);
> + if (ret) {
> + pr_err("Failed to update PAACE field for liodn %d\n ", liodn);
> + spin_unlock(&iommu_lock);
> + return ret;
> + }
> + } else {
> + for (i = 0; i < dma_domain->subwin_cnt; i++) {
> + ret = pamu_update_paace_stash(liodn, i, val);
> + if (ret) {
> + pr_err("Failed to update SPAACE %d field for liodn %d\n ", i, liodn);
> + spin_unlock(&iommu_lock);
> + return ret;
> + }
> + }
> + }
> + spin_unlock(&iommu_lock);
> +
> + return ret;
> +}
> +
> +static int configure_liodn(int liodn, struct device *dev,
> + struct fsl_dma_domain *dma_domain,
> + struct iommu_domain_geometry *geom_attr,
> + u32 subwin_cnt)
> +{
> + phys_addr_t window_addr, window_size;
> + phys_addr_t subwin_size;
> + int ret = 0, i;
> + u32 omi_index = ~(u32)0;
> +
> + /* Configure the omi_index at the geometry setup time.
> + * This is a static value which depends on the type of
> + * device and would not change thereafter.
> + */
/*
* multi-line comments
* look like this
*/
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply
* Re: [PATCH 159/493] usb: remove use of __devinit
From: Alan Stern @ 2012-11-20 21:44 UTC (permalink / raw)
To: Bill Pemberton
Cc: cbe-oss-dev, Kukjin Kim, Wan ZongShun, Alexander Shishkin, gregkh,
linux-usb, linuxppc-dev, Felipe Balbi, Geoff Levand, linux-omap,
linux-samsung-soc, Ben Dooks, Olav Kongas, Lennert Buytenhek,
linux-arm-kernel
In-Reply-To: <1353349642-3677-159-git-send-email-wfp5p@virginia.edu>
On Mon, 19 Nov 2012, Bill Pemberton wrote:
> CONFIG_HOTPLUG is going away as an option so __devinit is no longer
> needed.
>
> Signed-off-by: Bill Pemberton <wfp5p@virginia.edu>
For all the __devinit* annotations and all the EHCI, OHCI, and UHCI
drivers:
Acked-by: Alan Stern <stern@rowland.harvard.edu>
^ permalink raw reply
* Re: [PATCH] vfio powerpc: enabled and supported on powernv platform
From: Alex Williamson @ 2012-11-20 18:19 UTC (permalink / raw)
To: Alexey Kardashevskiy
Cc: kvm, linux-kernel, Paul Mackerras, linuxppc-dev, David Gibson
In-Reply-To: <1353372530-25205-1-git-send-email-aik@ozlabs.ru>
On Tue, 2012-11-20 at 11:48 +1100, Alexey Kardashevskiy wrote:
> VFIO implements platform independent stuff such as
> a PCI driver, BAR access (via read/write on a file descriptor
> or direct mapping when possible) and IRQ signaling.
> The platform dependent part includes IOMMU initialization
> and handling.
>
> This patch initializes IOMMU groups based on the IOMMU
> configuration discovered during the PCI scan, only POWERNV
> platform is supported at the moment.
>
> Also the patch implements an VFIO-IOMMU driver which
> manages DMA mapping/unmapping requests coming from
> the client (now QEMU). It also returns a DMA window
> information to let the guest initialize the device tree
> for a guest OS properly. Although this driver has been
> tested only on POWERNV, it should work on any platform
> supporting TCE tables.
>
> To enable VFIO on POWER, enable SPAPR_TCE_IOMMU config
> option.
>
> Cc: David Gibson <david@gibson.dropbear.id.au>
> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
> ---
> arch/powerpc/include/asm/iommu.h | 6 +
> arch/powerpc/kernel/iommu.c | 140 +++++++++++++++++++
> arch/powerpc/platforms/powernv/pci.c | 135 +++++++++++++++++++
> drivers/iommu/Kconfig | 8 ++
> drivers/vfio/Kconfig | 6 +
> drivers/vfio/Makefile | 1 +
> drivers/vfio/vfio_iommu_spapr_tce.c | 247 ++++++++++++++++++++++++++++++++++
> include/linux/vfio.h | 20 +++
> 8 files changed, 563 insertions(+)
> create mode 100644 drivers/vfio/vfio_iommu_spapr_tce.c
>
> diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h
> index cbfe678..5ba66cb 100644
> --- a/arch/powerpc/include/asm/iommu.h
> +++ b/arch/powerpc/include/asm/iommu.h
> @@ -64,30 +64,33 @@ struct iommu_pool {
> } ____cacheline_aligned_in_smp;
>
> struct iommu_table {
> unsigned long it_busno; /* Bus number this table belongs to */
> unsigned long it_size; /* Size of iommu table in entries */
> unsigned long it_offset; /* Offset into global table */
> unsigned long it_base; /* mapped address of tce table */
> unsigned long it_index; /* which iommu table this is */
> unsigned long it_type; /* type: PCI or Virtual Bus */
> unsigned long it_blocksize; /* Entries in each block (cacheline) */
> unsigned long poolsize;
> unsigned long nr_pools;
> struct iommu_pool large_pool;
> struct iommu_pool pools[IOMMU_NR_POOLS];
> unsigned long *it_map; /* A simple allocation bitmap for now */
> +#ifdef CONFIG_IOMMU_API
> + struct iommu_group *it_group;
> +#endif
> };
>
> struct scatterlist;
>
> static inline void set_iommu_table_base(struct device *dev, void *base)
> {
> dev->archdata.dma_data.iommu_table_base = base;
> }
>
> static inline void *get_iommu_table_base(struct device *dev)
> {
> return dev->archdata.dma_data.iommu_table_base;
> }
>
> /* Frees table for an individual device node */
> @@ -135,17 +138,20 @@ static inline void pci_iommu_init(void) { }
> extern void alloc_dart_table(void);
> #if defined(CONFIG_PPC64) && defined(CONFIG_PM)
> static inline void iommu_save(void)
> {
> if (ppc_md.iommu_save)
> ppc_md.iommu_save();
> }
>
> static inline void iommu_restore(void)
> {
> if (ppc_md.iommu_restore)
> ppc_md.iommu_restore();
> }
> #endif
>
> +extern long iommu_put_tces(struct iommu_table *tbl, unsigned long entry, uint64_t tce,
> + enum dma_data_direction direction, unsigned long pages);
> +
> #endif /* __KERNEL__ */
> #endif /* _ASM_IOMMU_H */
> diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
> index ff5a6ce..94f614b 100644
> --- a/arch/powerpc/kernel/iommu.c
> +++ b/arch/powerpc/kernel/iommu.c
> @@ -32,30 +32,31 @@
> #include <linux/dma-mapping.h>
> #include <linux/bitmap.h>
> #include <linux/iommu-helper.h>
> #include <linux/crash_dump.h>
> #include <linux/hash.h>
> #include <linux/fault-inject.h>
> #include <linux/pci.h>
> #include <asm/io.h>
> #include <asm/prom.h>
> #include <asm/iommu.h>
> #include <asm/pci-bridge.h>
> #include <asm/machdep.h>
> #include <asm/kdump.h>
> #include <asm/fadump.h>
> #include <asm/vio.h>
> +#include <asm/tce.h>
>
> #define DBG(...)
>
> static int novmerge;
>
> static void __iommu_free(struct iommu_table *, dma_addr_t, unsigned int);
>
> static int __init setup_iommu(char *str)
> {
> if (!strcmp(str, "novmerge"))
> novmerge = 1;
> else if (!strcmp(str, "vmerge"))
> novmerge = 0;
> return 1;
> }
> @@ -844,15 +845,154 @@ void *iommu_alloc_coherent(struct device *dev, struct iommu_table *tbl,
> }
>
> void iommu_free_coherent(struct iommu_table *tbl, size_t size,
> void *vaddr, dma_addr_t dma_handle)
> {
> if (tbl) {
> unsigned int nio_pages;
>
> size = PAGE_ALIGN(size);
> nio_pages = size >> IOMMU_PAGE_SHIFT;
> iommu_free(tbl, dma_handle, nio_pages);
> size = PAGE_ALIGN(size);
> free_pages((unsigned long)vaddr, get_order(size));
> }
> }
> +
> +#ifdef CONFIG_IOMMU_API
> +/*
> + * SPAPR TCE API
> + */
> +static struct page *free_tce(struct iommu_table *tbl, unsigned long entry)
> +{
> + struct page *page = NULL;
NULL initialization doesn't appear to be necessary
> + unsigned long oldtce;
> +
> + oldtce = ppc_md.tce_get(tbl, entry);
> +
> + if (!(oldtce & (TCE_PCI_WRITE | TCE_PCI_READ)))
> + return NULL;
> +
> + page = pfn_to_page(oldtce >> PAGE_SHIFT);
> +
> + WARN_ON(!page);
> + if (page && (oldtce & TCE_PCI_WRITE))
> + SetPageDirty(page);
> + ppc_md.tce_free(tbl, entry, 1);
> +
> + return page;
> +}
> +
> +static int put_tce(struct iommu_table *tbl, unsigned long entry,
> + uint64_t tce, enum dma_data_direction direction)
> +{
> + int ret;
> + struct page *page = NULL;
> + unsigned long kva, offset;
> +
> + /* Map new TCE */
> + offset = (tce & IOMMU_PAGE_MASK) - (tce & PAGE_MASK);
> + ret = get_user_pages_fast(tce & PAGE_MASK, 1,
> + direction != DMA_TO_DEVICE, &page);
> + if (ret < 1) {
> + printk(KERN_ERR "tce_vfio: get_user_pages_fast failed tce=%llx ioba=%lx ret=%d\n",
> + tce, entry << IOMMU_PAGE_SHIFT, ret);
> + if (!ret)
> + ret = -EFAULT;
Missing return ret? Otherwise we've got some bogus uses of page below
and we're setting ret for no reason here.
> + }
> +
> + kva = (unsigned long) page_address(page);
> + kva += offset;
> +
> + /* tce_build receives a virtual address */
> + entry += tbl->it_offset; /* Offset into real TCE table */
> + ret = ppc_md.tce_build(tbl, entry, 1, kva, direction, NULL);
> +
> + /* tce_build() only returns non-zero for transient errors */
> + if (unlikely(ret)) {
> + printk(KERN_ERR "tce_vfio: tce_put failed on tce=%llx ioba=%lx kva=%lx ret=%d\n",
> + tce, entry << IOMMU_PAGE_SHIFT, kva, ret);
> + put_page(page);
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static void tce_flush(struct iommu_table *tbl)
> +{
> + /* Flush/invalidate TLB caches if necessary */
> + if (ppc_md.tce_flush)
> + ppc_md.tce_flush(tbl);
> +
> + /* Make sure updates are seen by hardware */
> + mb();
> +}
> +
> +long iommu_put_tces(struct iommu_table *tbl, unsigned long entry, uint64_t tce,
> + enum dma_data_direction direction, unsigned long pages)
> +{
> + int i, ret = 0, pages_to_put = 0;
> + struct page *page;
> + struct iommu_pool *pool = get_pool(tbl, entry);
> + struct page **oldpages;
> + const int oldpagesnum = PAGE_SIZE/sizeof(*oldpages);
> +
> + BUILD_BUG_ON(PAGE_SIZE < IOMMU_PAGE_SIZE);
> +
> + /* Handle a single page request without allocation
> + of pages-to-release array */
> + if (pages == 1) {
> + spin_lock(&(pool->lock));
> + page = free_tce(tbl, entry);
> +
> + if (direction != DMA_NONE)
> + ret = put_tce(tbl, entry, tce, direction);
> +
> + tce_flush(tbl);
> +
> + if (page)
> + put_page(page);
> +
> + spin_unlock(&(pool->lock));
> + return ret;
> + }
> +
> + /* Releasing multiple pages */
> + /* Allocate an array for pages to be released after TCE table
> + is updated */
> + oldpages = kmalloc(PAGE_SIZE, GFP_KERNEL);
> + if (!oldpages)
> + return -ENOMEM;
> +
> + spin_lock(&(pool->lock));
> +
> + for (i = 0; (i < pages) && !ret; ++i, ++entry, tce += IOMMU_PAGE_SIZE) {
> + page = free_tce(tbl, entry);
> + if (page) {
> + oldpages[pages_to_put] = page;
> + ++pages_to_put;
> + }
> +
> + if (direction != DMA_NONE)
> + ret = put_tce(tbl, entry, tce, direction);
> +
> + /* Release old pages if we reached the end of oldpages[] or
> + it is the last page or we are about to exit the loop */
> + if ((pages_to_put == oldpagesnum) || (i == pages - 1) || ret) {
> + tce_flush(tbl);
Avoiding tce_flush() is the reason for all this extra overhead, right?
I wonder if it'd be cleaner separating map vs unmap, where the map case
can avoid the oldpages array... but that means inserting new mappings on
top of old ones wouldn't put the pages.
> +
> + /* Release pages after removing them from TCE table */
> + while (pages_to_put) {
> + --pages_to_put;
> + put_page(oldpages[pages_to_put]);
> + }
> + }
> + }
> +
> + spin_unlock(&(pool->lock));
> + kfree(oldpages);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(iommu_put_tces);
> +#endif /* CONFIG_IOMMU_API */
> diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
> index 05205cf..676f4d9 100644
> --- a/arch/powerpc/platforms/powernv/pci.c
> +++ b/arch/powerpc/platforms/powernv/pci.c
> @@ -8,30 +8,31 @@
> * This program is free software; you can redistribute it and/or
> * modify it under the terms of the GNU General Public License
> * as published by the Free Software Foundation; either version
> * 2 of the License, or (at your option) any later version.
> */
>
> #include <linux/kernel.h>
> #include <linux/pci.h>
> #include <linux/delay.h>
> #include <linux/string.h>
> #include <linux/init.h>
> #include <linux/bootmem.h>
> #include <linux/irq.h>
> #include <linux/io.h>
> #include <linux/msi.h>
> +#include <linux/iommu.h>
>
> #include <asm/sections.h>
> #include <asm/io.h>
> #include <asm/prom.h>
> #include <asm/pci-bridge.h>
> #include <asm/machdep.h>
> #include <asm/ppc-pci.h>
> #include <asm/opal.h>
> #include <asm/iommu.h>
> #include <asm/tce.h>
> #include <asm/abs_addr.h>
> #include <asm/firmware.h>
>
> #include "powernv.h"
> #include "pci.h"
> @@ -601,15 +602,149 @@ void __init pnv_pci_init(void)
> /* Configure IOMMU DMA hooks */
> ppc_md.pci_dma_dev_setup = pnv_pci_dma_dev_setup;
> ppc_md.tce_build = pnv_tce_build;
> ppc_md.tce_free = pnv_tce_free;
> ppc_md.tce_get = pnv_tce_get;
> ppc_md.pci_probe_mode = pnv_pci_probe_mode;
> set_pci_dma_ops(&dma_iommu_ops);
>
> /* Configure MSIs */
> #ifdef CONFIG_PCI_MSI
> ppc_md.msi_check_device = pnv_msi_check_device;
> ppc_md.setup_msi_irqs = pnv_setup_msi_irqs;
> ppc_md.teardown_msi_irqs = pnv_teardown_msi_irqs;
> #endif
> }
> +
> +#ifdef CONFIG_IOMMU_API
> +/*
> + * IOMMU groups support required by VFIO
> + */
> +static int add_device(struct device *dev)
> +{
> + struct iommu_table *tbl;
> + int ret = 0;
> +
> + if (WARN_ON(dev->iommu_group)) {
> + printk(KERN_WARNING "tce_vfio: device %s is already in iommu group %d, skipping\n",
> + dev->kobj.name,
dev_name(dev)
> + iommu_group_id(dev->iommu_group));
> + return -EBUSY;
> + }
> +
> + tbl = get_iommu_table_base(dev);
> + if (!tbl) {
> + pr_debug("tce_vfio: skipping device %s with no tbl\n",
> + dev->kobj.name);
> + return 0;
> + }
> +
> + pr_debug("tce_vfio: adding %s to iommu group %d\n",
> + dev->kobj.name, iommu_group_id(tbl->it_group));
> +
> + ret = iommu_group_add_device(tbl->it_group, dev);
> + if (ret < 0)
> + printk(KERN_ERR "tce_vfio: %s has not been added, ret=%d\n",
> + dev->kobj.name, ret);
> +
> + return ret;
> +}
> +
> +static void del_device(struct device *dev)
> +{
> + iommu_group_remove_device(dev);
> +}
> +
> +static int iommu_bus_notifier(struct notifier_block *nb,
> + unsigned long action, void *data)
> +{
> + struct device *dev = data;
> +
> + switch (action) {
> + case BUS_NOTIFY_ADD_DEVICE:
> + return add_device(dev);
> + case BUS_NOTIFY_DEL_DEVICE:
> + del_device(dev);
> + return 0;
> + default:
> + return 0;
> + }
> +}
> +
> +static struct notifier_block tce_iommu_bus_nb = {
> + .notifier_call = iommu_bus_notifier,
> +};
> +
> +static void group_release(void *iommu_data)
> +{
> + struct iommu_table *tbl = iommu_data;
> + tbl->it_group = NULL;
> +}
> +
> +static int __init tce_iommu_init(void)
> +{
> + struct pci_dev *pdev = NULL;
> + struct iommu_table *tbl;
> + struct iommu_group *grp;
> +
> + bus_register_notifier(&pci_bus_type, &tce_iommu_bus_nb);
There's already a notifier in the iommu code if you were to register an
iommu_ops with the add/remove_device entries. That would allow you to
remove the notifier block and notifier function below and the second
loop below. Are you avoiding that to avoid the rest of iommu_ops?
Also, shouldn't this notifier only be registered after the first loop
below? Otherwise ADD_DEVICE could race with setting up groups, which we
assume are present in the add_device() above.
> +
> + /* Allocate and initialize IOMMU groups */
> + for_each_pci_dev(pdev) {
> + tbl = get_iommu_table_base(&pdev->dev);
> + if (!tbl)
> + continue;
> +
> + /* Skip already initialized */
> + if (tbl->it_group)
> + continue;
> +
> + grp = iommu_group_alloc();
> + if (IS_ERR(grp)) {
> + printk(KERN_INFO "tce_vfio: cannot create "
> + "new IOMMU group, ret=%ld\n",
> + PTR_ERR(grp));
> + return PTR_ERR(grp);
> + }
> + tbl->it_group = grp;
> + iommu_group_set_iommudata(grp, tbl, group_release);
> + }
> +
> + /* Add PCI devices to VFIO groups */
> + for_each_pci_dev(pdev)
> + add_device(&pdev->dev);
> +
> + return 0;
> +}
> +
> +static void __exit tce_iommu_cleanup(void)
> +{
> + struct pci_dev *pdev = NULL;
> + struct iommu_table *tbl;
> + struct iommu_group *grp = NULL;
> +
> + bus_unregister_notifier(&pci_bus_type, &tce_iommu_bus_nb);
> +
> + /* Delete PCI devices from VFIO groups */
> + for_each_pci_dev(pdev)
> + del_device(&pdev->dev);
> +
> + /* Release VFIO groups */
> + for_each_pci_dev(pdev) {
> + tbl = get_iommu_table_base(&pdev->dev);
> + if (!tbl)
> + continue;
> + grp = tbl->it_group;
> +
> + /* Skip (already) uninitialized */
> + if (!grp)
> + continue;
> +
> + /* Do actual release, group_release() is expected to work */
> + iommu_group_put(grp);
> + BUG_ON(tbl->it_group);
> + }
> +}
> +
> +module_init(tce_iommu_init);
> +module_exit(tce_iommu_cleanup);
> +#endif /* CONFIG_IOMMU_API */
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index 9f69b56..29d11dc 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -175,16 +175,24 @@ config EXYNOS_IOMMU
> processor family. This enables H/W multimedia accellerators to see
> non-linear physical memory chunks as a linear memory in their
> address spaces
>
> If unsure, say N here.
>
> config EXYNOS_IOMMU_DEBUG
> bool "Debugging log for Exynos IOMMU"
> depends on EXYNOS_IOMMU
> help
> Select this to see the detailed log message that shows what
> happens in the IOMMU driver
>
> Say N unless you need kernel log message for IOMMU debugging
>
> +config SPAPR_TCE_IOMMU
> + bool "sPAPR TCE IOMMU Support"
> + depends on PPC_POWERNV
> + select IOMMU_API
> + help
> + Enables bits of IOMMU API required by VFIO. The iommu_ops is
> + still not implemented.
> +
> endif # IOMMU_SUPPORT
How are you planning to split this up among maintainers? A powerpc
patch, an iommu kconfig patch, then the vfio changes below for me?
> diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig
> index 7cd5dec..b464687 100644
> --- a/drivers/vfio/Kconfig
> +++ b/drivers/vfio/Kconfig
> @@ -1,16 +1,22 @@
> config VFIO_IOMMU_TYPE1
> tristate
> depends on VFIO
> default n
>
> +config VFIO_IOMMU_SPAPR_TCE
> + tristate
> + depends on VFIO && SPAPR_TCE_IOMMU
> + default n
> +
> menuconfig VFIO
> tristate "VFIO Non-Privileged userspace driver framework"
> depends on IOMMU_API
> select VFIO_IOMMU_TYPE1 if X86
> + select VFIO_IOMMU_SPAPR_TCE if PPC_POWERNV
> help
> VFIO provides a framework for secure userspace device drivers.
> See Documentation/vfio.txt for more details.
>
> If you don't know what to do here, say N.
>
> source "drivers/vfio/pci/Kconfig"
> diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile
> index 2398d4a..72bfabc 100644
> --- a/drivers/vfio/Makefile
> +++ b/drivers/vfio/Makefile
> @@ -1,3 +1,4 @@
> obj-$(CONFIG_VFIO) += vfio.o
> obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o
> +obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o
> obj-$(CONFIG_VFIO_PCI) += pci/
> diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
> new file mode 100644
> index 0000000..ac72c74d
> --- /dev/null
> +++ b/drivers/vfio/vfio_iommu_spapr_tce.c
> @@ -0,0 +1,247 @@
> +/*
> + * VFIO: IOMMU DMA mapping support for TCE on POWER
> + *
> + * Copyright (C) 2012 IBM Corp. All rights reserved.
> + * Author: Alexey Kardashevskiy <aik@ozlabs.ru>
> + *
> + * 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.
> + *
> + * Derived from original vfio_iommu_type1.c:
> + * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
> + * Author: Alex Williamson <alex.williamson@redhat.com>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <linux/err.h>
> +#include <linux/vfio.h>
> +#include <asm/iommu.h>
> +
> +#define DRIVER_VERSION "0.1"
> +#define DRIVER_AUTHOR "aik@ozlabs.ru"
> +#define DRIVER_DESC "VFIO IOMMU SPAPR TCE"
> +
> +static void tce_iommu_detach_group(void *iommu_data,
> + struct iommu_group *iommu_group);
> +
> +/*
> + * VFIO IOMMU fd for SPAPR_TCE IOMMU implementation
> + */
> +
> +/*
> + * The container descriptor supports only a single group per container.
> + * Required by the API as the container is not supplied with the IOMMU group
> + * at the moment of initialization.
> + */
> +struct tce_container {
> + struct mutex lock;
> + struct iommu_table *tbl;
> +};
> +
> +static void *tce_iommu_open(unsigned long arg)
> +{
> + struct tce_container *container;
> +
> + if (arg != VFIO_SPAPR_TCE_IOMMU) {
> + printk(KERN_ERR "tce_vfio: Wrong IOMMU type\n");
> + return ERR_PTR(-EINVAL);
> + }
> +
> + container = kzalloc(sizeof(*container), GFP_KERNEL);
> + if (!container)
> + return ERR_PTR(-ENOMEM);
> +
> + mutex_init(&container->lock);
> +
> + return container;
> +}
> +
> +static void tce_iommu_release(void *iommu_data)
> +{
> + struct tce_container *container = iommu_data;
> +
> + WARN_ON(container->tbl && !container->tbl->it_group);
> + if (container->tbl && container->tbl->it_group)
> + tce_iommu_detach_group(iommu_data, container->tbl->it_group);
> +
> + mutex_destroy(&container->lock);
> +
> + kfree(container);
> +}
> +
> +static long tce_iommu_ioctl(void *iommu_data,
> + unsigned int cmd, unsigned long arg)
> +{
> + struct tce_container *container = iommu_data;
> + unsigned long minsz;
> +
> + switch (cmd) {
> + case VFIO_CHECK_EXTENSION: {
> + return (arg == VFIO_SPAPR_TCE_IOMMU) ? 1 : 0;
> + }
> + case VFIO_IOMMU_SPAPR_TCE_GET_INFO: {
> + struct vfio_iommu_spapr_tce_info info;
> + struct iommu_table *tbl = container->tbl;
> +
> + if (WARN_ON(!tbl))
> + return -ENXIO;
> +
> + minsz = offsetofend(struct vfio_iommu_spapr_tce_info,
> + dma64_window_size);
> +
> + if (copy_from_user(&info, (void __user *)arg, minsz))
> + return -EFAULT;
> +
> + if (info.argsz < minsz)
> + return -EINVAL;
> +
> + info.dma32_window_start = tbl->it_offset << IOMMU_PAGE_SHIFT;
> + info.dma32_window_size = tbl->it_size << IOMMU_PAGE_SHIFT;
> + info.dma64_window_start = 0;
> + info.dma64_window_size = 0;
> + info.flags = 0;
> +
> + if (copy_to_user((void __user *)arg, &info, minsz))
> + return -EFAULT;
> +
> + return 0;
> + }
> + case VFIO_IOMMU_MAP_DMA: {
> + vfio_iommu_spapr_tce_dma_map par;
What does "par" stand for?
> + struct iommu_table *tbl = container->tbl;
> + enum dma_data_direction direction = DMA_NONE;
> +
> + if (WARN_ON(!tbl))
> + return -ENXIO;
> +
> + minsz = offsetofend(vfio_iommu_spapr_tce_dma_map, size);
> +
> + if (copy_from_user(&par, (void __user *)arg, minsz))
> + return -EFAULT;
> +
> + if (par.argsz < minsz)
> + return -EINVAL;
> +
> + if ((par.flags & VFIO_DMA_MAP_FLAG_READ) &&
> + (par.flags & VFIO_DMA_MAP_FLAG_WRITE)) {
> + direction = DMA_BIDIRECTIONAL;
> + } else if (par.flags & VFIO_DMA_MAP_FLAG_READ) {
> + direction = DMA_TO_DEVICE;
> + } else if (par.flags & VFIO_DMA_MAP_FLAG_WRITE) {
> + direction = DMA_FROM_DEVICE;
> + }
> +
> + par.size += par.iova & ~IOMMU_PAGE_MASK;
> + par.size = _ALIGN_UP(par.size, IOMMU_PAGE_SIZE);
> +
> + return iommu_put_tces(tbl, par.iova >> IOMMU_PAGE_SHIFT,
> + par.vaddr & IOMMU_PAGE_MASK, direction,
> + par.size >> IOMMU_PAGE_SHIFT);
> + }
> + case VFIO_IOMMU_UNMAP_DMA: {
> + vfio_iommu_spapr_tce_dma_unmap par;
> + struct iommu_table *tbl = container->tbl;
> +
> + if (WARN_ON(!tbl))
> + return -ENXIO;
> +
> + minsz = offsetofend(vfio_iommu_spapr_tce_dma_unmap, size);
> +
> + if (copy_from_user(&par, (void __user *)arg, minsz))
> + return -EFAULT;
> +
> + if (par.argsz < minsz)
> + return -EINVAL;
> +
> + par.size += par.iova & ~IOMMU_PAGE_MASK;
> + par.size = _ALIGN_UP(par.size, IOMMU_PAGE_SIZE);
> +
> + return iommu_put_tces(tbl, par.iova >> IOMMU_PAGE_SHIFT,
> + 0, DMA_NONE, par.size >> IOMMU_PAGE_SHIFT);
> + }
> + default:
> + printk(KERN_WARNING "tce_vfio: unexpected cmd %x\n", cmd);
> + }
> +
> + return -ENOTTY;
> +}
> +
> +static int tce_iommu_attach_group(void *iommu_data,
> + struct iommu_group *iommu_group)
> +{
> + struct tce_container *container = iommu_data;
> + struct iommu_table *tbl = iommu_group_get_iommudata(iommu_group);
> +
> + BUG_ON(!tbl);
> + mutex_lock(&container->lock);
> + pr_debug("tce_vfio: Attaching group #%u to iommu %p\n",
> + iommu_group_id(iommu_group), iommu_group);
> + if (container->tbl) {
> + printk(KERN_WARNING "tce_vfio: Only one group per IOMMU container is allowed, existing id=%d, attaching id=%d\n",
> + iommu_group_id(container->tbl->it_group),
> + iommu_group_id(iommu_group));
> + mutex_unlock(&container->lock);
> + return -EBUSY;
> + }
> +
> + container->tbl = tbl;
> + mutex_unlock(&container->lock);
> +
> + return 0;
> +}
> +
> +static void tce_iommu_detach_group(void *iommu_data,
> + struct iommu_group *iommu_group)
> +{
> + struct tce_container *container = iommu_data;
> + struct iommu_table *tbl = iommu_group_get_iommudata(iommu_group);
> +
> + BUG_ON(!tbl);
> + mutex_lock(&container->lock);
> + if (tbl != container->tbl) {
> + printk(KERN_WARNING "tce_vfio: detaching group #%u, expected group is #%u\n",
> + iommu_group_id(iommu_group),
> + iommu_group_id(tbl->it_group));
> + } else {
> +
> + pr_debug("tce_vfio: detaching group #%u from iommu %p\n",
> + iommu_group_id(iommu_group), iommu_group);
> +
> + iommu_put_tces(tbl, tbl->it_offset, 0, DMA_NONE, tbl->it_size);
> + container->tbl = NULL;
> + }
> + mutex_unlock(&container->lock);
> +}
> +
> +const struct vfio_iommu_driver_ops tce_iommu_driver_ops = {
> + .name = "iommu-vfio-powerpc",
> + .owner = THIS_MODULE,
> + .open = tce_iommu_open,
> + .release = tce_iommu_release,
> + .ioctl = tce_iommu_ioctl,
> + .attach_group = tce_iommu_attach_group,
> + .detach_group = tce_iommu_detach_group,
> +};
> +
> +static int __init tce_iommu_init(void)
> +{
> + return vfio_register_iommu_driver(&tce_iommu_driver_ops);
> +}
> +
> +static void __exit tce_iommu_cleanup(void)
> +{
> + vfio_unregister_iommu_driver(&tce_iommu_driver_ops);
> +}
> +
> +module_init(tce_iommu_init);
> +module_exit(tce_iommu_cleanup);
> +
> +MODULE_VERSION(DRIVER_VERSION);
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR(DRIVER_AUTHOR);
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +
> diff --git a/include/linux/vfio.h b/include/linux/vfio.h
> index 0a4f180..3ecd65c 100644
> --- a/include/linux/vfio.h
> +++ b/include/linux/vfio.h
> @@ -87,30 +87,31 @@ extern void vfio_unregister_iommu_driver(
> * Simple helper macro for dealing with variable sized structures passed
> * from user space. This allows us to easily determine if the provided
> * structure is sized to include various fields.
> */
> #define offsetofend(TYPE, MEMBER) ({ \
> TYPE tmp; \
> offsetof(TYPE, MEMBER) + sizeof(tmp.MEMBER); }) \
>
> #endif /* __KERNEL__ */
>
> /* Kernel & User level defines for VFIO IOCTLs. */
>
> /* Extensions */
>
> #define VFIO_TYPE1_IOMMU 1
> +#define VFIO_SPAPR_TCE_IOMMU 2
>
> /*
> * The IOCTL interface is designed for extensibility by embedding the
> * structure length (argsz) and flags into structures passed between
> * kernel and userspace. We therefore use the _IO() macro for these
> * defines to avoid implicitly embedding a size into the ioctl request.
> * As structure fields are added, argsz will increase to match and flag
> * bits will be defined to indicate additional fields with valid data.
> * It's *always* the caller's responsibility to indicate the size of
> * the structure passed by setting argsz appropriately.
> */
>
> #define VFIO_TYPE (';')
> #define VFIO_BASE 100
>
> @@ -430,16 +431,35 @@ struct vfio_iommu_type1_dma_map {
> /**
> * VFIO_IOMMU_UNMAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 14, struct vfio_dma_unmap)
> *
> * Unmap IO virtual addresses using the provided struct vfio_dma_unmap.
> * Caller sets argsz.
> */
> struct vfio_iommu_type1_dma_unmap {
> __u32 argsz;
> __u32 flags;
> __u64 iova; /* IO virtual address */
> __u64 size; /* Size of mapping (bytes) */
> };
>
> #define VFIO_IOMMU_UNMAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 14)
>
> +/* -------- Additional API for SPAPR TCE (Server POWERPC) IOMMU -------- */
> +
> +struct vfio_iommu_spapr_tce_info {
> + __u32 argsz;
> + __u32 flags;
> + __u32 dma32_window_start;
> + __u32 dma32_window_size;
> + __u64 dma64_window_start;
> + __u64 dma64_window_size;
> +};
> +
> +#define VFIO_IOMMU_SPAPR_TCE_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 12)
> +
> +/* Reuse type1 map/unmap structs as they are the same at the moment */
> +typedef struct vfio_iommu_type1_dma_map vfio_iommu_spapr_tce_dma_map;
> +typedef struct vfio_iommu_type1_dma_unmap vfio_iommu_spapr_tce_dma_unmap;
> +
> +/* ***************************************************************** */
> +
> #endif /* VFIO_H */
I'm glad you were able to reuse these, after this gets merged we can
rename the structure to something more common and typedef for both type1
and spapr_tce so we don't forget it's shared. Thanks,
Alex
^ permalink raw reply
* Re: [PATCH 429/493] tty: remove use of __devexit
From: David Brown @ 2012-11-20 17:06 UTC (permalink / raw)
To: Bill Pemberton
Cc: nios2-dev, Jiri Slaby, gregkh, linux-arm-msm, uclinux-dist-devel,
Tony Prisk, Bryan Huntsman, linux-serial, Lucas Tavares,
sparclinux, Daniel Walker, Tobias Klauser, linuxppc-dev,
David S. Miller, linux-arm-kernel, Alan Cox
In-Reply-To: <1353349642-3677-429-git-send-email-wfp5p@virginia.edu>
On Mon, Nov 19, 2012 at 01:26:18PM -0500, Bill Pemberton wrote:
> CONFIG_HOTPLUG is going away as an option so __devexit is no
> longer needed.
>
> Signed-off-by: Bill Pemberton <wfp5p@virginia.edu>
> ---
> drivers/tty/serial/msm_serial.c | 2 +-
> drivers/tty/serial/msm_serial_hs.c | 2 +-
Acked-by: David Brown <davidb@codeaurora.org>
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply
* Re: [PATCH 158/493] video: remove use of __devinit
From: David Brown @ 2012-11-20 16:57 UTC (permalink / raw)
To: Bill Pemberton
Cc: linux-fbdev, virtualization, Michal Januszewski, Paul Mackerras,
linux-nvidia, Daniel Walker, Kukjin Kim, Russell King,
Wan ZongShun, Florian Tobias Schandinat, Jingoo Han,
Tomi Valkeinen, Jaya Kumar, cbe-oss-dev, Antonino Daplas,
linux-arm-msm, Thomas Winischhofer, linux-geode,
linux-samsung-soc, Ben Dooks, linux-omap, linux-arm-kernel,
Maik Broemme, Geoff Levand, gregkh, Ferenc Bakonyi, Tony Prisk,
Bryan Huntsman, Kristoffer Ericson, linuxppc-dev, xen-devel
In-Reply-To: <1353349642-3677-158-git-send-email-wfp5p@virginia.edu>
On Mon, Nov 19, 2012 at 01:21:47PM -0500, Bill Pemberton wrote:
> CONFIG_HOTPLUG is going away as an option so __devinit is no longer
> needed.
>
> Signed-off-by: Bill Pemberton <wfp5p@virginia.edu>
> ---
> drivers/video/msm/mddi.c | 6 +-
Acked-by: David Brown <davidb@codeaurora.org>
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply
* Re: perf: POWER-event translation questions
From: Robert Richter @ 2012-11-20 16:50 UTC (permalink / raw)
To: Stephane Eranian
Cc: Peter Zijlstra, Anton Blanchard, LKML, linuxppc-dev, Ingo Molnar,
Paul Mackerras, Arnaldo Carvalho de Melo, Sukadev Bhattiprolu
In-Reply-To: <CABPqkBQrUVTY9rfeaO+pdaNwZcAgXF45SHnAYJfTvi4HAsPPeQ@mail.gmail.com>
On 09.11.12 11:26:26, Stephane Eranian wrote:
> On Thu, Nov 8, 2012 at 2:10 AM, Sukadev Bhattiprolu
> <sukadev@linux.vnet.ibm.com> wrote:
> > 2. Can we allow hyphens in the {name} token (please see my change to
> > util/parse-events.l below). With this change, I can run:
> >
> The current code does not support this but Andi fixed that in his HSW patch
> and I use it for the PEBS-LL patch series as well.
>
> > perf stat -e cpu/cmplu-stall-bru /tmp/nop
> >
> > without any changes to the user level tool (parse-events.l) I have
> > tested some common cases, not sure if it will break something :-)
But ...
> > diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
> > index c87efc1..1967bb2 100644
> > --- a/tools/perf/util/parse-events.l
> > +++ b/tools/perf/util/parse-events.l
> > @@ -80,7 +80,7 @@ event [^,{}/]+
> > num_dec [0-9]+
> > num_hex 0x[a-fA-F0-9]+
> > num_raw_hex [a-fA-F0-9]+
> > -name [a-zA-Z_*?][a-zA-Z0-9_*?]*
> > +name [-a-zA-Z_*?][-a-zA-Z0-9_*?]*
^
... I wouldn't allow hyphens at the beginning of a name.
And, I am wondering if parsing reserved names like 'cpu-cycles' works
too, e.g. cpu/cpu-cycles-foobar/. There are many reserved words in the
lexer with hyphens in it. This might cause unexpected problems.
-Robert
^ permalink raw reply
* [PATCH 4/4 v5] iommu/fsl: Freescale PAMU driver and IOMMU API implementation.
From: Varun Sethi @ 2012-11-20 13:54 UTC (permalink / raw)
To: joerg.roedel, iommu, linuxppc-dev, linux-kernel, scottwood, timur
Cc: Varun Sethi
In-Reply-To: <1353419697-31269-1-git-send-email-Varun.Sethi@freescale.com>
Following is a brief description of the PAMU hardware:
PAMU determines what action to take and whether to authorize the action on
the basis of the memory address, a Logical IO Device Number (LIODN), and
PAACT table (logically) indexed by LIODN and address. Hardware devices which
need to access memory must provide an LIODN in addition to the memory address.
Peripheral Access Authorization and Control Tables (PAACTs) are the primary
data structures used by PAMU. A PAACT is a table of peripheral access
authorization and control entries (PAACE).Each PAACE defines the range of
I/O bus address space that is accessible by the LIOD and the associated access
capabilities.
There are two types of PAACTs: primary PAACT (PPAACT) and secondary PAACT
(SPAACT).A given physical I/O device may be able to act as one or more
independent logical I/O devices (LIODs). Each such logical I/O device is
assigned an identifier called logical I/O device number (LIODN). A LIODN is
allocated a contiguous portion of the I/O bus address space called the DSA window
for performing DSA operations. The DSA window may optionally be divided into
multiple sub-windows, each of which may be used to map to a region in system
storage space. The first sub-window is referred to as the primary sub-window
and the remaining are called secondary sub-windows.
This patch provides the PAMU driver (fsl_pamu.c) and the corresponding IOMMU
API implementation (fsl_pamu_domain.c). The PAMU hardware driver (fsl_pamu.c)
has been derived from the work done by Ashish Kalra and Timur Tabi
(timur@freescale.com).
Signed-off-by: Timur Tabi <timur@freescale.com>
Signed-off-by: Varun Sethi <Varun.Sethi@freescale.com>
---
changes in v5:
- Addressed comments from Timur.
changes in v4:
- Addressed comments from Timur and Scott.
changes in v3:
- Addressed comments by Kumar Gala
- dynamic fspi allocation
- fixed alignment check in map and unmap
drivers/iommu/Kconfig | 8 +
drivers/iommu/Makefile | 1 +
drivers/iommu/fsl_pamu.c | 1152 +++++++++++++++++++++++++++++++++++++++
drivers/iommu/fsl_pamu.h | 398 ++++++++++++++
drivers/iommu/fsl_pamu_domain.c | 978 +++++++++++++++++++++++++++++++++
drivers/iommu/fsl_pamu_domain.h | 102 ++++
6 files changed, 2639 insertions(+), 0 deletions(-)
create mode 100644 drivers/iommu/fsl_pamu.c
create mode 100644 drivers/iommu/fsl_pamu.h
create mode 100644 drivers/iommu/fsl_pamu_domain.c
create mode 100644 drivers/iommu/fsl_pamu_domain.h
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index e39f9db..f712da2 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -17,6 +17,14 @@ config OF_IOMMU
def_bool y
depends on OF
+config FSL_PAMU
+ bool "Freescale IOMMU support"
+ depends on PPC_E500MC
+ select IOMMU_API
+ select GENERIC_ALLOCATOR
+ help
+ Freescale PAMU support.
+
# MSM IOMMU support
config MSM_IOMMU
bool "MSM IOMMU Support"
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 14a4d5f..a565ebe 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o
obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o
obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
+obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
diff --git a/drivers/iommu/fsl_pamu.c b/drivers/iommu/fsl_pamu.c
new file mode 100644
index 0000000..1613109
--- /dev/null
+++ b/drivers/iommu/fsl_pamu.c
@@ -0,0 +1,1152 @@
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright (C) 2012 Freescale Semiconductor, Inc.
+ *
+ */
+
+#define pr_fmt(fmt) "fsl-pamu: %s: " fmt, __func__
+
+#include <linux/init.h>
+#include <linux/iommu.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/of_platform.h>
+#include <linux/bootmem.h>
+#include <linux/genalloc.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/fsl_guts.h>
+
+#include "fsl_pamu.h"
+
+/* define indexes for each operation mapping scenario */
+#define OMI_QMAN 0x00
+#define OMI_FMAN 0x01
+#define OMI_QMAN_PRIV 0x02
+#define OMI_CAAM 0x03
+
+static struct paace *ppaact;
+static struct paace *spaact;
+static struct ome *omt;
+unsigned int max_subwindow_count;
+
+struct gen_pool *spaace_pool;
+
+/**
+ * pamu_get_ppaace() - Return the primary PACCE
+ * @liodn: liodn PAACT index for desired PAACE
+ *
+ * Returns the ppace pointer upon success else return
+ * null.
+ */
+static struct paace *pamu_get_ppaace(int liodn)
+{
+ if (!ppaact || liodn > PAACE_NUMBER_ENTRIES) {
+ pr_err("PPAACT doesn't exist\n");
+ return NULL;
+ }
+
+ return &ppaact[liodn];
+}
+
+/**
+ * pamu_enable_liodn() - Set valid bit of PACCE
+ * @liodn: liodn PAACT index for desired PAACE
+ *
+ * Returns 0 upon success else error code < 0 returned
+ */
+int pamu_enable_liodn(int liodn)
+{
+ struct paace *ppaace;
+
+ ppaace = pamu_get_ppaace(liodn);
+ if (!ppaace) {
+ pr_err("Invalid primary paace entry\n");
+ return -ENOENT;
+ }
+
+ if (!get_bf(ppaace->addr_bitfields, PPAACE_AF_WSE)) {
+ pr_err("liodn %d not configured\n", liodn);
+ return -EINVAL;
+ }
+
+ /* Ensure that all other stores to the ppaace complete first */
+ mb();
+
+ ppaace->addr_bitfields |= PAACE_V_VALID;
+ mb();
+
+ return 0;
+}
+
+/**
+ * pamu_disable_liodn() - Clears valid bit of PACCE
+ * @liodn: liodn PAACT index for desired PAACE
+ *
+ * Returns 0 upon success else error code < 0 returned
+ */
+int pamu_disable_liodn(int liodn)
+{
+ struct paace *ppaace;
+
+ ppaace = pamu_get_ppaace(liodn);
+ if (!ppaace) {
+ pr_err("Invalid primary paace entry\n");
+ return -ENOENT;
+ }
+
+ set_bf(ppaace->addr_bitfields, PAACE_AF_V, PAACE_V_INVALID);
+ mb();
+
+ return 0;
+}
+
+/* Derive the window size encoding for a particular PAACE entry */
+static unsigned int map_addrspace_size_to_wse(phys_addr_t addrspace_size)
+{
+ /* Bug if not a power of 2 */
+ BUG_ON((addrspace_size & (addrspace_size - 1)));
+
+ /* window size is 2^(WSE+1) bytes */
+ return __ffs(addrspace_size >> PAMU_PAGE_SHIFT) + PAMU_PAGE_SHIFT - 1;
+}
+
+/* Derive the PAACE window count encoding for the subwindow count */
+static unsigned int map_subwindow_cnt_to_wce(u32 subwindow_cnt)
+{
+ /* window count is 2^(WCE+1) bytes */
+ return __ffs(subwindow_cnt) - 1;
+}
+
+/*
+ * Set the PAACE type as primary and set the coherency required domain
+ * attribute
+ */
+static void pamu_setup_default_xfer_to_host_ppaace(struct paace *ppaace)
+{
+ set_bf(ppaace->addr_bitfields, PAACE_AF_PT, PAACE_PT_PRIMARY);
+
+ set_bf(ppaace->domain_attr.to_host.coherency_required, PAACE_DA_HOST_CR,
+ PAACE_M_COHERENCE_REQ);
+}
+
+/*
+ * Set the PAACE type as secondary and set the coherency required domain
+ * attribute.
+ */
+static void pamu_setup_default_xfer_to_host_spaace(struct paace *spaace)
+{
+ set_bf(spaace->addr_bitfields, PAACE_AF_PT, PAACE_PT_SECONDARY);
+ set_bf(spaace->domain_attr.to_host.coherency_required, PAACE_DA_HOST_CR,
+ PAACE_M_COHERENCE_REQ);
+}
+
+/*
+ * Return the spaace (corresponding to the secondary window index)
+ * for a particular ppaace.
+ */
+static struct paace *pamu_get_spaace(struct paace *paace, u32 wnum)
+{
+ u32 subwin_cnt;
+ struct paace *spaace = NULL;
+
+ subwin_cnt = 1UL << (get_bf(paace->impl_attr, PAACE_IA_WCE) + 1);
+
+ if (wnum < subwin_cnt)
+ spaace = &spaact[paace->fspi + wnum];
+ else
+ pr_err("secondary paace out of bounds\n");
+
+ return spaace;
+}
+
+/**
+ * pamu_get_fspi_and_allocate() - Allocates fspi index and reserves subwindows
+ * required for primary PAACE in the secondary
+ * PAACE table.
+ * @subwin_cnt: Number of subwindows to be reserved.
+ *
+ * A PPAACE entry may have a number of associated subwindows. A subwindow
+ * corresponds to a SPAACE entry in the SPAACT table. Each PAACE entry stores
+ * the index (fspi) of the first SPAACE entry in the SPAACT table. This
+ * function returns the index of the first SPAACE entry. The remaining
+ * SPAACE entries are reserved contiguously from that index.
+ *
+ * Returns a valid fspi index in the range of 0 - SPAACE_NUMBER_ENTRIES on success.
+ * If no SPAACE entry is available or the allocator can not reserve the required
+ * number of contiguous entries function returns ULONG_MAX indicating a failure.
+ *
+*/
+static unsigned long pamu_get_fspi_and_allocate(u32 subwin_cnt)
+{
+ unsigned long spaace_addr;
+
+ spaace_addr = gen_pool_alloc(spaace_pool, subwin_cnt * sizeof(struct paace));
+ if (!spaace_addr)
+ return ULONG_MAX;
+
+ return (spaace_addr - (unsigned long)spaact) / (sizeof(struct paace));
+}
+
+/* Release the subwindows reserved for a particular LIODN */
+void pamu_free_subwins(int liodn)
+{
+ struct paace *ppaace;
+ u32 subwin_cnt, size;
+
+ ppaace = pamu_get_ppaace(liodn);
+ if (!ppaace) {
+ pr_err("Invalid liodn entry\n");
+ return;
+ }
+
+ if (get_bf(ppaace->addr_bitfields, PPAACE_AF_MW)) {
+ subwin_cnt = 1UL << (get_bf(ppaace->impl_attr, PAACE_IA_WCE) + 1);
+ size = (subwin_cnt - 1) * sizeof(struct paace);
+ gen_pool_free(spaace_pool, (unsigned long)&spaact[ppaace->fspi], size);
+ set_bf(ppaace->addr_bitfields, PPAACE_AF_MW, 0);
+ }
+}
+
+/*
+ * Function used for updating stash destination for the coressponding
+ * LIODN.
+ */
+int pamu_update_paace_stash(int liodn, u32 subwin, u32 value)
+{
+ struct paace *paace;
+
+ paace = pamu_get_ppaace(liodn);
+ if (!paace) {
+ pr_err("Invalid liodn entry\n");
+ return -ENOENT;
+ }
+ if (subwin) {
+ paace = pamu_get_spaace(paace, subwin - 1);
+ if (!paace) {
+ return -ENOENT;
+ }
+ }
+ set_bf(paace->impl_attr, PAACE_IA_CID, value);
+
+ return 0;
+}
+
+/**
+ * pamu_config_paace() - Sets up PPAACE entry for specified liodn
+ *
+ * @liodn: Logical IO device number
+ * @win_addr: starting address of DSA window
+ * @win-size: size of DSA window
+ * @omi: Operation mapping index -- if ~omi == 0 then omi not defined
+ * @rpn: real (true physical) page number
+ * @stashid: cache stash id for associated cpu -- if ~stashid == 0 then
+ * stashid not defined
+ * @snoopid: snoop id for hardware coherency -- if ~snoopid == 0 then
+ * snoopid not defined
+ * @subwin_cnt: number of sub-windows
+ * @prot: window permissions
+ *
+ * Returns 0 upon success else error code < 0 returned
+ */
+int pamu_config_ppaace(int liodn, phys_addr_t win_addr, phys_addr_t win_size,
+ u32 omi, unsigned long rpn, u32 snoopid, u32 stashid,
+ u32 subwin_cnt, int prot)
+{
+ struct paace *ppaace;
+ unsigned long fspi;
+
+ if ((win_size & (win_size - 1)) || win_size < PAMU_PAGE_SIZE) {
+ pr_err("window size too small or not a power of two %llx\n", win_size);
+ return -EINVAL;
+ }
+
+ if (win_addr & (win_size - 1)) {
+ pr_err("window address is not aligned with window size\n");
+ return -EINVAL;
+ }
+
+ ppaace = pamu_get_ppaace(liodn);
+ if (!ppaace) {
+ return -ENOENT;
+ }
+
+ /* window size is 2^(WSE+1) bytes */
+ set_bf(ppaace->addr_bitfields, PPAACE_AF_WSE,
+ map_addrspace_size_to_wse(win_size));
+
+ pamu_setup_default_xfer_to_host_ppaace(ppaace);
+
+ ppaace->wbah = win_addr >> (PAMU_PAGE_SHIFT + 20);
+ set_bf(ppaace->addr_bitfields, PPAACE_AF_WBAL,
+ (win_addr >> PAMU_PAGE_SHIFT));
+
+ /* set up operation mapping if it's configured */
+ if (omi < OME_NUMBER_ENTRIES) {
+ set_bf(ppaace->impl_attr, PAACE_IA_OTM, PAACE_OTM_INDEXED);
+ ppaace->op_encode.index_ot.omi = omi;
+ } else if (~omi != 0) {
+ pr_err("bad operation mapping index: %d\n", omi);
+ return -EINVAL;
+ }
+
+ /* configure stash id */
+ if (~stashid != 0)
+ set_bf(ppaace->impl_attr, PAACE_IA_CID, stashid);
+
+ /* configure snoop id */
+ if (~snoopid != 0)
+ ppaace->domain_attr.to_host.snpid = snoopid;
+
+ if (subwin_cnt) {
+ /* The first entry is in the primary PAACE instead */
+ fspi = pamu_get_fspi_and_allocate(subwin_cnt - 1);
+ if (fspi == ULONG_MAX) {
+ pr_err("spaace indexes exhausted\n");
+ return -EINVAL;
+ }
+
+ /* window count is 2^(WCE+1) bytes */
+ set_bf(ppaace->impl_attr, PAACE_IA_WCE,
+ map_subwindow_cnt_to_wce(subwin_cnt));
+ set_bf(ppaace->addr_bitfields, PPAACE_AF_MW, 0x1);
+ ppaace->fspi = fspi;
+ } else {
+ set_bf(ppaace->impl_attr, PAACE_IA_ATM, PAACE_ATM_WINDOW_XLATE);
+ ppaace->twbah = rpn >> 20;
+ set_bf(ppaace->win_bitfields, PAACE_WIN_TWBAL, rpn);
+ set_bf(ppaace->addr_bitfields, PAACE_AF_AP, prot);
+ set_bf(ppaace->impl_attr, PAACE_IA_WCE, 0);
+ set_bf(ppaace->addr_bitfields, PPAACE_AF_MW, 0);
+ }
+ mb();
+
+ return 0;
+}
+
+/**
+ * pamu_config_spaace() - Sets up SPAACE entry for specified subwindow
+ *
+ * @liodn: Logical IO device number
+ * @subwin_cnt: number of sub-windows associated with dma-window
+ * @subwin_addr: starting address of subwindow
+ * @subwin_size: size of subwindow
+ * @omi: Operation mapping index
+ * @rpn: real (true physical) page number
+ * @snoopid: snoop id for hardware coherency -- if ~snoopid == 0 then
+ * snoopid not defined
+ * @stashid: cache stash id for associated cpu
+ * @enable: enable/disable subwindow after reconfiguration
+ * @prot: sub window permissions
+ *
+ * Returns 0 upon success else error code < 0 returned
+ */
+int pamu_config_spaace(int liodn, u32 subwin_cnt, u32 subwin_addr,
+ phys_addr_t subwin_size, u32 omi, unsigned long rpn,
+ u32 snoopid, u32 stashid, int enable, int prot)
+{
+ struct paace *paace;
+
+ /* setup sub-windows */
+ if (!subwin_cnt) {
+ pr_err("Invalid subwindow count\n");
+ return -EINVAL;
+ }
+
+ paace = pamu_get_ppaace(liodn);
+ if (subwin_addr > 0 && subwin_addr < subwin_cnt && paace) {
+ paace = pamu_get_spaace(paace, subwin_addr - 1);
+
+ if (paace && !(paace->addr_bitfields & PAACE_V_VALID)) {
+ pamu_setup_default_xfer_to_host_spaace(paace);
+ set_bf(paace->addr_bitfields, SPAACE_AF_LIODN, liodn);
+ }
+ }
+
+ if (!paace) {
+ pr_err("Invalid liodn entry\n");
+ return -ENOENT;
+ }
+
+ if (!enable && prot == PAACE_AP_PERMS_DENIED) {
+ if (subwin_addr > 0)
+ set_bf(paace->addr_bitfields, PAACE_AF_V,
+ PAACE_V_INVALID);
+ else
+ set_bf(paace->addr_bitfields, PAACE_AF_AP,
+ prot);
+ mb();
+ return 0;
+ }
+
+ if (subwin_size & (subwin_size - 1) || subwin_size < PAMU_PAGE_SIZE) {
+ pr_err("subwindow size out of range, or not a power of 2\n");
+ return -EINVAL;
+ }
+
+ if (rpn == ULONG_MAX) {
+ pr_err("real page number out of range\n");
+ return -EINVAL;
+ }
+
+ /* window size is 2^(WSE+1) bytes */
+ set_bf(paace->win_bitfields, PAACE_WIN_SWSE,
+ map_addrspace_size_to_wse(subwin_size));
+
+ set_bf(paace->impl_attr, PAACE_IA_ATM, PAACE_ATM_WINDOW_XLATE);
+ paace->twbah = rpn >> 20;
+ set_bf(paace->win_bitfields, PAACE_WIN_TWBAL, rpn);
+ set_bf(paace->addr_bitfields, PAACE_AF_AP, prot);
+
+ /* configure snoop id */
+ if (~snoopid != 0)
+ paace->domain_attr.to_host.snpid = snoopid;
+
+ /* set up operation mapping if it's configured */
+ if (omi < OME_NUMBER_ENTRIES) {
+ set_bf(paace->impl_attr, PAACE_IA_OTM, PAACE_OTM_INDEXED);
+ paace->op_encode.index_ot.omi = omi;
+ } else if (~omi != 0) {
+ pr_err("bad operation mapping index: %d\n", omi);
+ return -EINVAL;
+ }
+
+ if (~stashid != 0)
+ set_bf(paace->impl_attr, PAACE_IA_CID, stashid);
+
+ smp_wmb();
+
+ if (enable)
+ paace->addr_bitfields |= PAACE_V_VALID;
+
+ mb();
+
+ return 0;
+}
+
+/**
+* get_ome_index() - Returns the index in the operation mapping table
+* for device.
+* @*omi_index: pointer for storing the index value
+*
+*/
+void get_ome_index(u32 *omi_index, struct device *dev)
+{
+ if (of_device_is_compatible(dev->of_node, "fsl,qman-portal"))
+ *omi_index = OMI_QMAN;
+ if (of_device_is_compatible(dev->of_node, "fsl,qman"))
+ *omi_index = OMI_QMAN_PRIV;
+}
+
+/**
+ * get_stash_id - Returns stash destination id corresponding to a
+ * cache type and vcpu.
+ * @stash_dest_hint: L1, L2 or L3
+ * @vcpu: vpcu target for a particular cache type.
+ *
+ * Returs stash on success or ~(u32)0 on failure.
+ *
+ */
+u32 get_stash_id(u32 stash_dest_hint, u32 vcpu)
+{
+ const u32 *prop;
+ struct device_node *node;
+ u32 cache_level;
+ int len;
+
+ /* Fastpath, exit early if L3/CPC cache is target for stashing */
+ if (stash_dest_hint == IOMMU_ATTR_CACHE_L3) {
+ node = of_find_compatible_node(NULL, NULL,
+ "fsl,p4080-l3-cache-controller");
+ if (node) {
+ prop = of_get_property(node, "cache-stash-id", 0);
+ if (!prop) {
+ pr_err("missing cache-stash-id at %s\n", node->full_name);
+ of_node_put(node);
+ return ~(u32)0;
+ }
+ of_node_put(node);
+ return be32_to_cpup(prop);
+ }
+ return ~(u32)0;
+ }
+
+ for_each_node_by_type(node, "cpu") {
+ prop = of_get_property(node, "reg", &len);
+ if (be32_to_cpup(prop) == vcpu)
+ break;
+ }
+
+ /* find the hwnode that represents the cache */
+ for (cache_level = IOMMU_ATTR_CACHE_L1; cache_level < IOMMU_ATTR_CACHE_L3; cache_level++) {
+ if (stash_dest_hint == cache_level) {
+ prop = of_get_property(node, "cache-stash-id", 0);
+ if (!prop) {
+ pr_err("missing cache-stash-id at %s\n", node->full_name);
+ of_node_put(node);
+ return ~(u32)0;
+ }
+ of_node_put(node);
+ return be32_to_cpup(prop);
+ }
+
+ prop = of_get_property(node, "next-level-cache", 0);
+ if (!prop) {
+ pr_err("can't find next-level-cache at %s\n",
+ node->full_name);
+ of_node_put(node);
+ return ~(u32)0; /* can't traverse any further */
+ }
+ of_node_put(node);
+
+ /* advance to next node in cache hierarchy */
+ node = of_find_node_by_phandle(*prop);
+ if (!node) {
+ pr_err("Invalid node for cache hierarchy %s\n",
+ node->full_name);
+ return ~(u32)0;
+ }
+ }
+
+ pr_err("stash dest not found for %d on vcpu %d\n",
+ stash_dest_hint, vcpu);
+ return ~(u32)0;
+}
+
+/* Identify if the PAACT table entry belongs to QMAN, BMAN or QMAN Portal */
+#define QMAN_PAACE 1
+#define QMAN_PORTAL_PAACE 2
+#define BMAN_PAACE 3
+
+/**
+ * Setup operation mapping and stash destinations for QMAN and QMAN portal.
+ * Memory accesses to QMAN and BMAN private memory need not be coherent, so
+ * clear the PAACE entry coherency attribute for them.
+ */
+static void setup_qbman_paace(struct paace *ppaace, int paace_type)
+{
+ switch(paace_type) {
+ case QMAN_PAACE:
+ set_bf(ppaace->impl_attr, PAACE_IA_OTM, PAACE_OTM_INDEXED);
+ ppaace->op_encode.index_ot.omi = OMI_QMAN_PRIV;
+ /* setup QMAN Private data stashing for the L3 cache */
+ set_bf(ppaace->impl_attr, PAACE_IA_CID, get_stash_id(IOMMU_ATTR_CACHE_L3, 0));
+ set_bf(ppaace->domain_attr.to_host.coherency_required, PAACE_DA_HOST_CR,
+ 0);
+ break;
+ case QMAN_PORTAL_PAACE:
+ set_bf(ppaace->impl_attr, PAACE_IA_OTM, PAACE_OTM_INDEXED);
+ ppaace->op_encode.index_ot.omi = OMI_QMAN;
+ /*Set DQRR and Frame stashing for the L3 cache */
+ set_bf(ppaace->impl_attr, PAACE_IA_CID, get_stash_id(IOMMU_ATTR_CACHE_L3, 0));
+ break;
+ case BMAN_PAACE:
+ set_bf(ppaace->domain_attr.to_host.coherency_required, PAACE_DA_HOST_CR,
+ 0);
+ break;
+ }
+}
+
+/**
+ * Setup the operation mapping table for various devices. This is a static
+ * table where each table index corresponds to a particular device. PAMU uses
+ * this table to translate device transaction to appropriate corenet
+ * transaction.
+ */
+static void __init setup_omt(struct ome *omt)
+{
+ struct ome *ome;
+
+ /* Configure OMI_QMAN */
+ ome = &omt[OMI_QMAN];
+
+ ome->moe[IOE_READ_IDX] = EOE_VALID | EOE_READ;
+ ome->moe[IOE_EREAD0_IDX] = EOE_VALID | EOE_RSA;
+ ome->moe[IOE_WRITE_IDX] = EOE_VALID | EOE_WRITE;
+ ome->moe[IOE_EWRITE0_IDX] = EOE_VALID | EOE_WWSAO;
+
+ ome->moe[IOE_DIRECT0_IDX] = EOE_VALID | EOE_LDEC;
+ ome->moe[IOE_DIRECT1_IDX] = EOE_VALID | EOE_LDECPE;
+
+ /* Configure OMI_FMAN */
+ ome = &omt[OMI_FMAN];
+ ome->moe[IOE_READ_IDX] = EOE_VALID | EOE_READI;
+ ome->moe[IOE_WRITE_IDX] = EOE_VALID | EOE_WRITE;
+
+ /* Configure OMI_QMAN private */
+ ome = &omt[OMI_QMAN_PRIV];
+ ome->moe[IOE_READ_IDX] = EOE_VALID | EOE_READ;
+ ome->moe[IOE_WRITE_IDX] = EOE_VALID | EOE_WRITE;
+ ome->moe[IOE_EREAD0_IDX] = EOE_VALID | EOE_RSA;
+ ome->moe[IOE_EWRITE0_IDX] = EOE_VALID | EOE_WWSA;
+
+ /* Configure OMI_CAAM */
+ ome = &omt[OMI_CAAM];
+ ome->moe[IOE_READ_IDX] = EOE_VALID | EOE_READI;
+ ome->moe[IOE_WRITE_IDX] = EOE_VALID | EOE_WRITE;
+}
+
+/* Setup PAMU registers pointing to PAACT, SPAACT and OMT */
+int setup_one_pamu(unsigned long pamu_reg_base, unsigned long pamu_reg_size,
+ phys_addr_t ppaact_phys, phys_addr_t spaact_phys,
+ phys_addr_t omt_phys)
+{
+ u32 *pc;
+ struct pamu_mmap_regs *pamu_regs;
+ u32 pc3_val;
+
+ pc3_val = in_be32((u32 *)(pamu_reg_base + PAMU_PC3));
+ max_subwindow_count = 1 << (1 + PAMU_PC3_MWCE(pc3_val));
+
+ pc = (u32 *) (pamu_reg_base + PAMU_PC);
+ pamu_regs = (struct pamu_mmap_regs *)
+ (pamu_reg_base + PAMU_MMAP_REGS_BASE);
+
+ /* set up pointers to corenet control blocks */
+
+ out_be32(&pamu_regs->ppbah, ((u64)ppaact_phys) >> 32);
+ out_be32(&pamu_regs->ppbal, ppaact_phys);
+ ppaact_phys = ppaact_phys + PAACT_SIZE;
+ out_be32(&pamu_regs->pplah, ((u64)ppaact_phys) >> 32);
+ out_be32(&pamu_regs->pplal, ppaact_phys);
+
+ out_be32(&pamu_regs->spbah, ((u64)spaact_phys) >> 32);
+ out_be32(&pamu_regs->spbal, spaact_phys);
+ spaact_phys = spaact_phys + SPAACT_SIZE;
+ out_be32(&pamu_regs->splah, ((u64)spaact_phys) >> 32);
+ out_be32(&pamu_regs->splal, spaact_phys);
+
+ out_be32(&pamu_regs->obah, ((u64)omt_phys) >> 32);
+ out_be32(&pamu_regs->obal, omt_phys);
+ omt_phys = omt_phys + OMT_SIZE;
+ out_be32(&pamu_regs->olah, ((u64)omt_phys) >> 32);
+ out_be32(&pamu_regs->olal, omt_phys);
+
+ /*
+ * set PAMU enable bit,
+ * allow ppaact & omt to be cached
+ * & enable PAMU access violation interrupts.
+ */
+
+ out_be32((u32 *)(pamu_reg_base + PAMU_PICS),
+ PAMU_ACCESS_VIOLATION_ENABLE);
+ out_be32(pc, PAMU_PC_PE | PAMU_PC_OCE | PAMU_PC_SPCC | PAMU_PC_PPCC);
+ return 0;
+}
+
+/* Enable all device LIODNS */
+static void __init setup_liodns(void)
+{
+ int i, len;
+ struct paace *ppaace;
+ struct device_node *node = NULL;
+ const u32 *prop;
+
+ for_each_node_with_property(node, "fsl,liodn") {
+ prop = of_get_property(node, "fsl,liodn", &len);
+ for (i = 0; i < len / sizeof(u32); i++) {
+ int liodn;
+
+ liodn = be32_to_cpup(&prop[i]);
+ ppaace = pamu_get_ppaace(liodn);
+ pamu_setup_default_xfer_to_host_ppaace(ppaace);
+ /* window size is 2^(WSE+1) bytes */
+ set_bf(ppaace->addr_bitfields, PPAACE_AF_WSE, 35);
+ ppaace->wbah = 0;
+ set_bf(ppaace->addr_bitfields, PPAACE_AF_WBAL, 0);
+ set_bf(ppaace->impl_attr, PAACE_IA_ATM,
+ PAACE_ATM_NO_XLATE);
+ set_bf(ppaace->addr_bitfields, PAACE_AF_AP,
+ PAACE_AP_PERMS_ALL);
+ if (of_device_is_compatible(node, "fsl,qman-portal"))
+ setup_qbman_paace(ppaace, QMAN_PORTAL_PAACE);
+ if (of_device_is_compatible(node, "fsl,qman"))
+ setup_qbman_paace(ppaace, QMAN_PAACE);
+ if (of_device_is_compatible(node, "fsl,bman"))
+ setup_qbman_paace(ppaace, BMAN_PAACE);
+ mb();
+ pamu_enable_liodn(liodn);
+ }
+ }
+}
+
+/* TBD: PAMU access violation interrupt handler */
+irqreturn_t pamu_av_isr(int irq, void *arg)
+{
+ panic("FSL_PAMU: access violation interrupt\n");
+ /* NOTREACHED */
+
+ return IRQ_HANDLED;
+}
+
+#define LAWAR_EN 0x80000000
+#define LAWAR_TARGET_MASK 0x0FF00000
+#define LAWAR_TARGET_SHIFT 20
+#define LAWAR_SIZE_MASK 0x0000003F
+#define LAWAR_CSDID_MASK 0x000FF000
+#define LAWAR_CSDID_SHIFT 12
+
+#define LAW_SIZE_4K 0xb
+
+struct ccsr_law {
+ u32 lawbarh; /* LAWn base address high */
+ u32 lawbarl; /* LAWn base address low */
+ u32 lawar; /* LAWn attributes */
+ u32 reserved;
+};
+
+#define make64(high, low) (((u64)(high) << 32) | (low))
+
+/*
+ * Create a coherence subdomain for a given memory block.
+ */
+static int __init create_csd(phys_addr_t phys, size_t size, u32 csd_port_id)
+{
+ struct device_node *np;
+ const __be32 *iprop;
+ void __iomem *lac = NULL; /* Local Access Control registers */
+ struct ccsr_law __iomem *law;
+ void __iomem *ccm = NULL;
+ u32 __iomem *csdids;
+ unsigned int i, num_laws, num_csds;
+ u32 law_target = 0;
+ u32 csd_id = 0;
+ int ret = 0;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,corenet-law");
+ if (!np)
+ return -ENODEV;
+
+ iprop = of_get_property(np, "fsl,num-laws", NULL);
+ if (!iprop) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ num_laws = be32_to_cpup(iprop);
+ if (!num_laws) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ lac = of_iomap(np, 0);
+ if (!lac) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ /* LAW registers are at offset 0xC00 */
+ law = lac + 0xC00;
+
+ of_node_put(np);
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,corenet-cf");
+ if (!np) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ iprop = of_get_property(np, "fsl,ccf-num-csdids", NULL);
+ if (!iprop) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ num_csds = be32_to_cpup(iprop);
+ if (!num_csds) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ ccm = of_iomap(np, 0);
+ if (!ccm) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /* The undocumented CSDID registers are at offset 0x600 */
+ csdids = ccm + 0x600;
+
+ of_node_put(np);
+ np = NULL;
+
+ /* Find an unused coherence subdomain ID */
+ for (csd_id = 0; csd_id < num_csds; csd_id++) {
+ if (!csdids[csd_id])
+ break;
+ }
+
+ /* Store the Port ID in the (undocumented) proper CIDMRxx register */
+ csdids[csd_id] = csd_port_id;
+
+ /* Find the DDR LAW that maps to our buffer. */
+ for (i = 0; i < num_laws; i++) {
+ if (law[i].lawar & LAWAR_EN) {
+ phys_addr_t law_start, law_end;
+
+ law_start = make64(law[i].lawbarh, law[i].lawbarl);
+ law_end = law_start +
+ (2ULL << (law[i].lawar & LAWAR_SIZE_MASK));
+
+ if (law_start <= phys && phys < law_end) {
+ law_target = law[i].lawar & LAWAR_TARGET_MASK;
+ break;
+ }
+ }
+ }
+
+ if (i == 0 || i == num_laws) {
+ /* This should never happen*/
+ ret = -ENOENT;
+ goto error;
+ }
+
+ /* Find a free LAW entry */
+ while (law[--i].lawar & LAWAR_EN) {
+ if (i == 0) {
+ /* No higher priority LAW slots available */
+ ret = -ENOENT;
+ goto error;
+ }
+ }
+
+ law[i].lawbarh = upper_32_bits(phys);
+ law[i].lawbarl = lower_32_bits(phys);
+ wmb();
+ law[i].lawar = LAWAR_EN | law_target | (csd_id << LAWAR_CSDID_SHIFT) |
+ (LAW_SIZE_4K + get_order(size));
+ wmb();
+
+error:
+ if (ccm)
+ iounmap(ccm);
+
+ if (lac)
+ iounmap(lac);
+
+ if (np)
+ of_node_put(np);
+
+ return ret;
+}
+
+/*
+ * Table of SVRs and the corresponding PORT_ID values.
+ *
+ * All future CoreNet-enabled SOCs will have this erratum fixed, so this table
+ * should never need to be updated. SVRs are guaranteed to be unique, so
+ * there is no worry that a future SOC will inadvertently have one of these
+ * values.
+ */
+static const struct {
+ u32 svr;
+ u32 port_id;
+} port_id_map[] = {
+ {0x82100010, 0xFF000000}, /* P2040 1.0 */
+ {0x82100011, 0xFF000000}, /* P2040 1.1 */
+ {0x82100110, 0xFF000000}, /* P2041 1.0 */
+ {0x82100111, 0xFF000000}, /* P2041 1.1 */
+ {0x82110310, 0xFF000000}, /* P3041 1.0 */
+ {0x82110311, 0xFF000000}, /* P3041 1.1 */
+ {0x82010020, 0xFFF80000}, /* P4040 2.0 */
+ {0x82000020, 0xFFF80000}, /* P4080 2.0 */
+ {0x82210010, 0xFC000000}, /* P5010 1.0 */
+ {0x82210020, 0xFC000000}, /* P5010 2.0 */
+ {0x82200010, 0xFC000000}, /* P5020 1.0 */
+ {0x82050010, 0xFF800000}, /* P5021 1.0 */
+ {0x82040010, 0xFF800000}, /* P5040 1.0 */
+};
+
+#define SVR_SECURITY 0x80000 /* The Security (E) bit */
+
+static int __init fsl_pamu_probe(struct platform_device *pdev)
+{
+ void __iomem *pamu_regs = NULL;
+ struct ccsr_guts __iomem *guts_regs = NULL;
+ u32 pamubypenr, pamu_counter;
+ unsigned long pamu_reg_off;
+ unsigned long pamu_reg_base;
+ struct device_node *guts_node;
+ u64 size;
+ struct page *p;
+ int ret = 0;
+ int irq;
+ phys_addr_t ppaact_phys;
+ phys_addr_t spaact_phys;
+ phys_addr_t omt_phys;
+ size_t mem_size = 0;
+ unsigned int order = 0;
+ u32 csd_port_id = 0;
+ unsigned i;
+ /*
+ * enumerate all PAMUs and allocate and setup PAMU tables
+ * for each of them,
+ * NOTE : All PAMUs share the same LIODN tables.
+ */
+
+ pamu_regs = of_iomap(pdev->dev.of_node, 0);
+ if (!pamu_regs) {
+ dev_err(&pdev->dev, "ioremap of PAMU node failed\n");
+ return -ENOMEM;
+ }
+ of_get_address(pdev->dev.of_node, 0, &size, NULL);
+
+ irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+ if (irq == NO_IRQ) {
+ dev_warn(&pdev->dev, "no interrupts listed in PAMU node\n");
+ goto error;
+ }
+
+ ret = request_irq(irq, pamu_av_isr, IRQF_DISABLED, "pamu", NULL);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "error %i installing ISR for irq %i\n",
+ ret, irq);
+ goto error;
+ }
+
+ guts_node = of_find_compatible_node(NULL, NULL,
+ "fsl,qoriq-device-config-1.0");
+ if (!guts_node) {
+ dev_err(&pdev->dev, "could not find GUTS node %s\n",
+ pdev->dev.of_node->full_name);
+ ret = -ENODEV;
+ goto error;
+ }
+
+ guts_regs = of_iomap(guts_node, 0);
+ of_node_put(guts_node);
+ if (!guts_regs) {
+ dev_err(&pdev->dev, "ioremap of GUTS node failed\n");
+ ret = -ENODEV;
+ goto error;
+ }
+
+ /*
+ * To simplify the allocation of a coherency domain, we allocate the
+ * PAACT and the OMT in the same memory buffer. Unfortunately, this
+ * wastes more memory compared to allocating the buffers separately.
+ */
+
+ /* Determine how much memory we need */
+ mem_size = (PAGE_SIZE << get_order(PAACT_SIZE)) +
+ (PAGE_SIZE << get_order(SPAACT_SIZE)) +
+ (PAGE_SIZE << get_order(OMT_SIZE));
+ order = get_order(mem_size);
+
+ p = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
+ if (!p) {
+ dev_err(&pdev->dev, "unable to allocate PAACT/SPAACT/OMT block\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ ppaact = page_address(p);
+ ppaact_phys = page_to_phys(p);
+
+ /* Make sure the memory is naturally aligned */
+ if (ppaact_phys & ((PAGE_SIZE << order) - 1)) {
+ dev_err(&pdev->dev, "PAACT/OMT block is unaligned\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ spaact = (void *)ppaact + (PAGE_SIZE << get_order(PAACT_SIZE));
+ omt = (void *)spaact + (PAGE_SIZE << get_order(SPAACT_SIZE));
+
+ dev_dbg(&pdev->dev, "ppaact virt=%p phys=0x%llx\n", ppaact,
+ (unsigned long long) ppaact_phys);
+
+ /* Check to see if we need to implement the work-around on this SOC */
+
+ /* Determine the Port ID for our coherence subdomain */
+ for (i = 0; i < ARRAY_SIZE(port_id_map); i++) {
+ if (port_id_map[i].svr == (mfspr(SPRN_SVR) & ~SVR_SECURITY)) {
+ csd_port_id = port_id_map[i].port_id;
+ dev_dbg(&pdev->dev, "found matching SVR %08x\n",
+ port_id_map[i].svr);
+ break;
+ }
+ }
+
+ if (csd_port_id) {
+ dev_dbg(&pdev->dev, "creating coherency subdomain at address "
+ "0x%llx, size %zu, port id 0x%08x", ppaact_phys,
+ mem_size, csd_port_id);
+
+ ret = create_csd(ppaact_phys, mem_size, csd_port_id);
+ if (ret) {
+ dev_err(&pdev->dev, "could not create coherence "
+ "subdomain\n");
+ return ret;
+ }
+ }
+
+ spaact_phys = virt_to_phys(spaact);
+ omt_phys = virt_to_phys(omt);
+
+ spaace_pool = gen_pool_create(ilog2(sizeof(struct paace)), -1);
+ if (!spaace_pool) {
+ ret = -ENOMEM;
+ dev_err(&pdev->dev, "PAMU : failed to allocate spaace gen pool\n");
+ goto error;
+ }
+
+ ret = gen_pool_add(spaace_pool, (unsigned long)spaact, SPAACT_SIZE, -1);
+ if (ret)
+ goto error_genpool;
+
+ pamubypenr = in_be32(&guts_regs->pamubypenr);
+
+ for (pamu_reg_off = 0, pamu_counter = 0x80000000; pamu_reg_off < size;
+ pamu_reg_off += PAMU_OFFSET, pamu_counter >>= 1) {
+
+ pamu_reg_base = (unsigned long) pamu_regs + pamu_reg_off;
+ setup_one_pamu(pamu_reg_base, pamu_reg_off, ppaact_phys,
+ spaact_phys, omt_phys);
+ /* Disable PAMU bypass for this PAMU */
+ pamubypenr &= ~pamu_counter;
+ }
+
+ setup_omt(omt);
+
+ /* Enable all relevant PAMU(s) */
+ out_be32(&guts_regs->pamubypenr, pamubypenr);
+
+ iounmap(pamu_regs);
+ iounmap(guts_regs);
+
+ /* Enable DMA for the LIODNs in the device tree*/
+
+ setup_liodns();
+
+ return 0;
+
+error_genpool:
+ gen_pool_destroy(spaace_pool);
+
+error:
+ if (irq != NO_IRQ)
+ free_irq(irq, 0);
+
+ if (pamu_regs)
+ iounmap(pamu_regs);
+
+ if (guts_regs)
+ iounmap(guts_regs);
+
+ if (ppaact)
+ free_pages((unsigned long)ppaact, order);
+
+ ppaact = NULL;
+
+ return ret;
+}
+
+static const struct of_device_id fsl_of_pamu_ids[] = {
+ {
+ .compatible = "fsl,p4080-pamu",
+ },
+ {
+ .compatible = "fsl,pamu",
+ },
+ {},
+};
+
+static struct platform_driver fsl_of_pamu_driver = {
+ .driver = {
+ .name = "fsl-of-pamu",
+ .owner = THIS_MODULE,
+ },
+ .probe = fsl_pamu_probe,
+};
+
+static __init int fsl_pamu_init(void)
+{
+ struct platform_device *pdev = NULL;
+ struct device_node *np;
+ int ret;
+
+ /*
+ * The normal OF process calls the probe function at some
+ * indeterminate later time, after most drivers have loaded. This is
+ * too late for us, because PAMU clients (like the Qman driver)
+ * depend on PAMU being initialized early.
+ *
+ * So instead, we "manually" call our probe function by creating the
+ * platform devices ourselves.
+ */
+
+ /*
+ * We assume that there is only one PAMU node in the device tree. A
+ * single PAMU node represents all of the PAMU devices in the SOC
+ * already. Everything else already makes that assumption, and the
+ * binding for the PAMU nodes doesn't allow for any parent-child
+ * relationships anyway. In other words, support for more than one
+ * PAMU node would require significant changes to a lot of code.
+ */
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,pamu");
+ if (!np) {
+ pr_err("fsl-pamu: could not find a PAMU node\n");
+ return -ENODEV;
+ }
+
+ ret = platform_driver_register(&fsl_of_pamu_driver);
+ if (ret) {
+ pr_err("fsl-pamu: could not register driver (err=%i)\n", ret);
+ goto error_driver_register;
+ }
+
+ pdev = platform_device_alloc("fsl-of-pamu", 0);
+ if (!pdev) {
+ pr_err("fsl-pamu: could not allocate device %s\n",
+ np->full_name);
+ ret = -ENOMEM;
+ goto error_device_alloc;
+ }
+ pdev->dev.of_node = of_node_get(np);
+
+ ret = pamu_domain_init();
+ if (ret)
+ goto error_device_add;
+
+ ret = platform_device_add(pdev);
+ if (ret) {
+ pr_err("fsl-pamu: could not add device %s (err=%i)\n",
+ np->full_name, ret);
+ goto error_device_add;
+ }
+
+ return 0;
+
+error_device_add:
+ of_node_put(pdev->dev.of_node);
+ pdev->dev.of_node = NULL;
+
+ platform_device_put(pdev);
+
+error_device_alloc:
+ platform_driver_unregister(&fsl_of_pamu_driver);
+
+error_driver_register:
+ of_node_put(np);
+
+ return ret;
+}
+subsys_initcall(fsl_pamu_init);
diff --git a/drivers/iommu/fsl_pamu.h b/drivers/iommu/fsl_pamu.h
new file mode 100644
index 0000000..6d32fb5
--- /dev/null
+++ b/drivers/iommu/fsl_pamu.h
@@ -0,0 +1,398 @@
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright (C) 2012 Freescale Semiconductor, Inc.
+ *
+ */
+
+#ifndef __FSL_PAMU_H
+#define __FSL_PAMU_H
+
+/* Bit Field macros
+ * v = bit field variable; m = mask, m##_SHIFT = shift, x = value to load
+ */
+#define set_bf(v, m, x) (v = ((v) & ~(m)) | (((x) << (m##_SHIFT)) & (m)))
+#define get_bf(v, m) (((v) & (m)) >> (m##_SHIFT))
+
+/* PAMU CCSR space */
+#define PAMU_PGC 0x00000000 /* Allows all peripheral accesses */
+#define PAMU_PE 0x40000000 /* enable PAMU */
+
+/* PAMU_OFFSET to the next pamu space in ccsr */
+#define PAMU_OFFSET 0x1000
+
+#define PAMU_MMAP_REGS_BASE 0
+
+struct pamu_mmap_regs {
+ u32 ppbah;
+ u32 ppbal;
+ u32 pplah;
+ u32 pplal;
+ u32 spbah;
+ u32 spbal;
+ u32 splah;
+ u32 splal;
+ u32 obah;
+ u32 obal;
+ u32 olah;
+ u32 olal;
+};
+
+/* PAMU Error Registers */
+#define PAMU_POES1 0x0040
+#define PAMU_POES2 0x0044
+#define PAMU_POEAH 0x0048
+#define PAMU_POEAL 0x004C
+#define PAMU_AVS1 0x0050
+#define PAMU_AVS1_AV 0x1
+#define PAMU_AVS1_OTV 0x6
+#define PAMU_AVS1_APV 0x78
+#define PAMU_AVS1_WAV 0x380
+#define PAMU_AVS1_LAV 0x1c00
+#define PAMU_AVS1_GCV 0x2000
+#define PAMU_AVS1_PDV 0x4000
+#define PAMU_AV_MASK (PAMU_AVS1_AV | PAMU_AVS1_OTV | PAMU_AVS1_APV | PAMU_AVS1_WAV \
+ | PAMU_AVS1_LAV | PAMU_AVS1_GCV | PAMU_AVS1_PDV)
+#define PAMU_AVS1_LIODN_SHIFT 16
+#define PAMU_LAV_LIODN_NOT_IN_PPAACT 0x400
+
+#define PAMU_AVS2 0x0054
+#define PAMU_AVAH 0x0058
+#define PAMU_AVAL 0x005C
+#define PAMU_EECTL 0x0060
+#define PAMU_EEDIS 0x0064
+#define PAMU_EEINTEN 0x0068
+#define PAMU_EEDET 0x006C
+#define PAMU_EEATTR 0x0070
+#define PAMU_EEAHI 0x0074
+#define PAMU_EEALO 0x0078
+#define PAMU_EEDHI 0X007C
+#define PAMU_EEDLO 0x0080
+#define PAMU_EECC 0x0084
+
+/* PAMU Revision Registers */
+#define PAMU_PR1 0x0BF8
+#define PAMU_PR2 0x0BFC
+
+/* PAMU Capabilities Registers */
+#define PAMU_PC1 0x0C00
+#define PAMU_PC2 0x0C04
+#define PAMU_PC3 0x0C08
+#define PAMU_PC4 0x0C0C
+
+/* PAMU Control Register */
+#define PAMU_PC 0x0C10
+
+/* PAMU control defs */
+#define PAMU_CONTROL 0x0C10
+#define PAMU_PC_PGC 0x80000000 /* PAMU gate closed bit */
+#define PAMU_PC_PE 0x40000000 /* PAMU enable bit */
+#define PAMU_PC_SPCC 0x00000010 /* sPAACE cache enable */
+#define PAMU_PC_PPCC 0x00000001 /* pPAACE cache enable */
+#define PAMU_PC_OCE 0x00001000 /* OMT cache enable */
+
+#define PAMU_PFA1 0x0C14
+#define PAMU_PFA2 0x0C18
+
+#define PAMU_PC3_MWCE(X) (((X) >> 21) & 0xf)
+
+/* PAMU Interrupt control and Status Register */
+#define PAMU_PICS 0x0C1C
+#define PAMU_ACCESS_VIOLATION_STAT 0x8
+#define PAMU_ACCESS_VIOLATION_ENABLE 0x4
+
+/* PAMU Debug Registers */
+#define PAMU_PD1 0x0F00
+#define PAMU_PD2 0x0F04
+#define PAMU_PD3 0x0F08
+#define PAMU_PD4 0x0F0C
+
+#define PAACE_AP_PERMS_DENIED 0x0
+#define PAACE_AP_PERMS_QUERY 0x1
+#define PAACE_AP_PERMS_UPDATE 0x2
+#define PAACE_AP_PERMS_ALL 0x3
+
+#define PAACE_DD_TO_HOST 0x0
+#define PAACE_DD_TO_IO 0x1
+#define PAACE_PT_PRIMARY 0x0
+#define PAACE_PT_SECONDARY 0x1
+#define PAACE_V_INVALID 0x0
+#define PAACE_V_VALID 0x1
+#define PAACE_MW_SUBWINDOWS 0x1
+
+#define PAACE_WSE_4K 0xB
+#define PAACE_WSE_8K 0xC
+#define PAACE_WSE_16K 0xD
+#define PAACE_WSE_32K 0xE
+#define PAACE_WSE_64K 0xF
+#define PAACE_WSE_128K 0x10
+#define PAACE_WSE_256K 0x11
+#define PAACE_WSE_512K 0x12
+#define PAACE_WSE_1M 0x13
+#define PAACE_WSE_2M 0x14
+#define PAACE_WSE_4M 0x15
+#define PAACE_WSE_8M 0x16
+#define PAACE_WSE_16M 0x17
+#define PAACE_WSE_32M 0x18
+#define PAACE_WSE_64M 0x19
+#define PAACE_WSE_128M 0x1A
+#define PAACE_WSE_256M 0x1B
+#define PAACE_WSE_512M 0x1C
+#define PAACE_WSE_1G 0x1D
+#define PAACE_WSE_2G 0x1E
+#define PAACE_WSE_4G 0x1F
+
+#define PAACE_DID_PCI_EXPRESS_1 0x00
+#define PAACE_DID_PCI_EXPRESS_2 0x01
+#define PAACE_DID_PCI_EXPRESS_3 0x02
+#define PAACE_DID_PCI_EXPRESS_4 0x03
+#define PAACE_DID_LOCAL_BUS 0x04
+#define PAACE_DID_SRIO 0x0C
+#define PAACE_DID_MEM_1 0x10
+#define PAACE_DID_MEM_2 0x11
+#define PAACE_DID_MEM_3 0x12
+#define PAACE_DID_MEM_4 0x13
+#define PAACE_DID_MEM_1_2 0x14
+#define PAACE_DID_MEM_3_4 0x15
+#define PAACE_DID_MEM_1_4 0x16
+#define PAACE_DID_BM_SW_PORTAL 0x18
+#define PAACE_DID_PAMU 0x1C
+#define PAACE_DID_CAAM 0x21
+#define PAACE_DID_QM_SW_PORTAL 0x3C
+#define PAACE_DID_CORE0_INST 0x80
+#define PAACE_DID_CORE0_DATA 0x81
+#define PAACE_DID_CORE1_INST 0x82
+#define PAACE_DID_CORE1_DATA 0x83
+#define PAACE_DID_CORE2_INST 0x84
+#define PAACE_DID_CORE2_DATA 0x85
+#define PAACE_DID_CORE3_INST 0x86
+#define PAACE_DID_CORE3_DATA 0x87
+#define PAACE_DID_CORE4_INST 0x88
+#define PAACE_DID_CORE4_DATA 0x89
+#define PAACE_DID_CORE5_INST 0x8A
+#define PAACE_DID_CORE5_DATA 0x8B
+#define PAACE_DID_CORE6_INST 0x8C
+#define PAACE_DID_CORE6_DATA 0x8D
+#define PAACE_DID_CORE7_INST 0x8E
+#define PAACE_DID_CORE7_DATA 0x8F
+#define PAACE_DID_BROADCAST 0xFF
+
+#define PAACE_ATM_NO_XLATE 0x00
+#define PAACE_ATM_WINDOW_XLATE 0x01
+#define PAACE_ATM_PAGE_XLATE 0x02
+#define PAACE_ATM_WIN_PG_XLATE \
+ ( PAACE_ATM_WINDOW_XLATE | PAACE_ATM_PAGE_XLATE )
+#define PAACE_OTM_NO_XLATE 0x00
+#define PAACE_OTM_IMMEDIATE 0x01
+#define PAACE_OTM_INDEXED 0x02
+#define PAACE_OTM_RESERVED 0x03
+
+#define PAACE_M_COHERENCE_REQ 0x01
+
+#define PAACE_PID_0 0x0
+#define PAACE_PID_1 0x1
+#define PAACE_PID_2 0x2
+#define PAACE_PID_3 0x3
+#define PAACE_PID_4 0x4
+#define PAACE_PID_5 0x5
+#define PAACE_PID_6 0x6
+#define PAACE_PID_7 0x7
+
+#define PAACE_TCEF_FORMAT0_8B 0x00
+#define PAACE_TCEF_FORMAT1_RSVD 0x01
+
+#define PAACE_NUMBER_ENTRIES 0xFF
+
+#define OME_NUMBER_ENTRIES 16
+
+#define SPAACE_NUMBER_ENTRIES 0x8000
+
+/* PAACE Bit Field Defines */
+#define PPAACE_AF_WBAL 0xfffff000
+#define PPAACE_AF_WBAL_SHIFT 12
+#define PPAACE_AF_WSE 0x00000fc0
+#define PPAACE_AF_WSE_SHIFT 6
+#define PPAACE_AF_MW 0x00000020
+#define PPAACE_AF_MW_SHIFT 5
+
+#define SPAACE_AF_LIODN 0xffff0000
+#define SPAACE_AF_LIODN_SHIFT 16
+
+#define PAACE_AF_AP 0x00000018
+#define PAACE_AF_AP_SHIFT 3
+#define PAACE_AF_DD 0x00000004
+#define PAACE_AF_DD_SHIFT 2
+#define PAACE_AF_PT 0x00000002
+#define PAACE_AF_PT_SHIFT 1
+#define PAACE_AF_V 0x00000001
+#define PAACE_AF_V_SHIFT 0
+
+#define PAACE_DA_HOST_CR 0x80
+#define PAACE_DA_HOST_CR_SHIFT 7
+
+#define PAACE_IA_CID 0x00FF0000
+#define PAACE_IA_CID_SHIFT 16
+#define PAACE_IA_WCE 0x000000F0
+#define PAACE_IA_WCE_SHIFT 4
+#define PAACE_IA_ATM 0x0000000C
+#define PAACE_IA_ATM_SHIFT 2
+#define PAACE_IA_OTM 0x00000003
+#define PAACE_IA_OTM_SHIFT 0
+
+#define PAACE_WIN_TWBAL 0xfffff000
+#define PAACE_WIN_TWBAL_SHIFT 12
+#define PAACE_WIN_SWSE 0x00000fc0
+#define PAACE_WIN_SWSE_SHIFT 6
+
+/* PAMU Data Structures */
+/* primary / secondary paact structure */
+struct paace {
+ /* PAACE Offset 0x00 */
+ u32 wbah; /* only valid for Primary PAACE */
+ u32 addr_bitfields; /* See P/S PAACE_AF_* */
+
+ /* PAACE Offset 0x08 */
+ /* Interpretation of first 32 bits dependent on DD above */
+ union {
+ struct {
+ /* Destination ID, see PAACE_DID_* defines */
+ u8 did;
+ /* Partition ID */
+ u8 pid;
+ /* Snoop ID */
+ u8 snpid;
+ /* coherency_required : 1 reserved : 7 */
+ u8 coherency_required; /* See PAACE_DA_* */
+ } to_host;
+ struct {
+ /* Destination ID, see PAACE_DID_* defines */
+ u8 did;
+ u8 reserved1;
+ u16 reserved2;
+ } to_io;
+ } domain_attr;
+
+ /* Implementation attributes + window count + address & operation translation modes */
+ u32 impl_attr; /* See PAACE_IA_* */
+
+ /* PAACE Offset 0x10 */
+ /* Translated window base address */
+ u32 twbah;
+ u32 win_bitfields; /* See PAACE_WIN_* */
+
+ /* PAACE Offset 0x18 */
+ /* first secondary paace entry */
+ u32 fspi; /* only valid for Primary PAACE */
+ union {
+ struct {
+ u8 ioea;
+ u8 moea;
+ u8 ioeb;
+ u8 moeb;
+ } immed_ot;
+ struct {
+ u16 reserved;
+ u16 omi;
+ } index_ot;
+ } op_encode;
+
+ /* PAACE Offsets 0x20-0x38 */
+ u32 reserved[8]; /* not currently implemented */
+};
+
+/* OME : Operation mapping entry
+ * MOE : Mapped Operation Encodings
+ * The operation mapping table is table containing operation mapping entries (OME).
+ * The index of a particular OME is programmed in the PAACE entry for translation
+ * in bound I/O operations corresponding to an LIODN. The OMT is used for translation
+ * specifically in case of the indexed translation mode. Each OME contains a 128
+ * byte mapped operation encoding (MOE), where each byte represents an MOE.
+ */
+#define NUM_MOE 128
+struct ome {
+ u8 moe[NUM_MOE];
+} __attribute__((packed));
+
+#define PAACT_SIZE (sizeof(struct paace) * PAACE_NUMBER_ENTRIES)
+#define OMT_SIZE (sizeof(struct ome) * OME_NUMBER_ENTRIES)
+#define SPAACT_SIZE (sizeof(struct paace) * SPAACE_NUMBER_ENTRIES)
+
+#define PAMU_PAGE_SHIFT 12
+#define PAMU_PAGE_SIZE 4096ULL
+
+#define IOE_READ 0x00
+#define IOE_READ_IDX 0x00
+#define IOE_WRITE 0x81
+#define IOE_WRITE_IDX 0x01
+#define IOE_EREAD0 0x82 /* Enhanced read type 0 */
+#define IOE_EREAD0_IDX 0x02 /* Enhanced read type 0 */
+#define IOE_EWRITE0 0x83 /* Enhanced write type 0 */
+#define IOE_EWRITE0_IDX 0x03 /* Enhanced write type 0 */
+#define IOE_DIRECT0 0x84 /* Directive type 0 */
+#define IOE_DIRECT0_IDX 0x04 /* Directive type 0 */
+#define IOE_EREAD1 0x85 /* Enhanced read type 1 */
+#define IOE_EREAD1_IDX 0x05 /* Enhanced read type 1 */
+#define IOE_EWRITE1 0x86 /* Enhanced write type 1 */
+#define IOE_EWRITE1_IDX 0x06 /* Enhanced write type 1 */
+#define IOE_DIRECT1 0x87 /* Directive type 1 */
+#define IOE_DIRECT1_IDX 0x07 /* Directive type 1 */
+#define IOE_RAC 0x8c /* Read with Atomic clear */
+#define IOE_RAC_IDX 0x0c /* Read with Atomic clear */
+#define IOE_RAS 0x8d /* Read with Atomic set */
+#define IOE_RAS_IDX 0x0d /* Read with Atomic set */
+#define IOE_RAD 0x8e /* Read with Atomic decrement */
+#define IOE_RAD_IDX 0x0e /* Read with Atomic decrement */
+#define IOE_RAI 0x8f /* Read with Atomic increment */
+#define IOE_RAI_IDX 0x0f /* Read with Atomic increment */
+
+#define EOE_READ 0x00
+#define EOE_WRITE 0x01
+#define EOE_RAC 0x0c /* Read with Atomic clear */
+#define EOE_RAS 0x0d /* Read with Atomic set */
+#define EOE_RAD 0x0e /* Read with Atomic decrement */
+#define EOE_RAI 0x0f /* Read with Atomic increment */
+#define EOE_LDEC 0x10 /* Load external cache */
+#define EOE_LDECL 0x11 /* Load external cache with stash lock */
+#define EOE_LDECPE 0x12 /* Load external cache with preferred exclusive */
+#define EOE_LDECPEL 0x13 /* Load external cache with preferred exclusive and lock */
+#define EOE_LDECFE 0x14 /* Load external cache with forced exclusive */
+#define EOE_LDECFEL 0x15 /* Load external cache with forced exclusive and lock */
+#define EOE_RSA 0x16 /* Read with stash allocate */
+#define EOE_RSAU 0x17 /* Read with stash allocate and unlock */
+#define EOE_READI 0x18 /* Read with invalidate */
+#define EOE_RWNITC 0x19 /* Read with no intention to cache */
+#define EOE_WCI 0x1a /* Write cache inhibited */
+#define EOE_WWSA 0x1b /* Write with stash allocate */
+#define EOE_WWSAL 0x1c /* Write with stash allocate and lock */
+#define EOE_WWSAO 0x1d /* Write with stash allocate only */
+#define EOE_WWSAOL 0x1e /* Write with stash allocate only and lock */
+#define EOE_VALID 0x80
+
+/* Function prototypes */
+int pamu_domain_init(void);
+int pamu_enable_liodn(int liodn);
+int pamu_disable_liodn(int liodn);
+void pamu_free_subwins(int liodn);
+int pamu_config_ppaace(int liodn, phys_addr_t win_addr, phys_addr_t win_size,
+ u32 omi, unsigned long rpn, u32 snoopid, uint32_t stashid,
+ u32 subwin_cnt, int prot);
+int pamu_config_spaace(int liodn, u32 subwin_cnt, u32 subwin_addr,
+ phys_addr_t subwin_size, u32 omi, unsigned long rpn,
+ uint32_t snoopid, u32 stashid, int enable, int prot);
+
+u32 get_stash_id(u32 stash_dest_hint, u32 vcpu);
+void get_ome_index(u32 *omi_index, struct device *dev);
+int pamu_update_paace_stash(int liodn, u32 subwin, u32 value);
+
+#endif /* __FSL_PAMU_H */
diff --git a/drivers/iommu/fsl_pamu_domain.c b/drivers/iommu/fsl_pamu_domain.c
new file mode 100644
index 0000000..d473447
--- /dev/null
+++ b/drivers/iommu/fsl_pamu_domain.c
@@ -0,0 +1,978 @@
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright (C) 2012 Freescale Semiconductor, Inc.
+ * Author: Varun Sethi <varun.sethi@freescale.com>
+ *
+ */
+
+#define pr_fmt(fmt) "fsl-pamu-domain: %s: " fmt, __func__
+
+#include <linux/init.h>
+#include <linux/iommu.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/of_platform.h>
+#include <linux/bootmem.h>
+#include <linux/err.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+
+#include "fsl_pamu_domain.h"
+
+/* This bitmap advertises the page sizes supported by PAMU hardware
+ * to the IOMMU API.
+ */
+#define FSL_PAMU_PGSIZES (~0xFFFUL)
+
+/* global spinlock that needs to be held while
+ * configuring PAMU.
+ */
+static DEFINE_SPINLOCK(iommu_lock);
+
+static struct kmem_cache *fsl_pamu_domain_cache;
+static struct kmem_cache *iommu_devinfo_cache;
+static DEFINE_SPINLOCK(device_domain_lock);
+
+int __init iommu_init_mempool(void)
+{
+
+ fsl_pamu_domain_cache = kmem_cache_create("fsl_pamu_domain",
+ sizeof(struct fsl_dma_domain),
+ 0,
+ SLAB_HWCACHE_ALIGN,
+
+ NULL);
+ if (!fsl_pamu_domain_cache) {
+ pr_err("Couldn't create fsl iommu_domain cache\n");
+ return -ENOMEM;
+ }
+
+ iommu_devinfo_cache = kmem_cache_create("iommu_devinfo",
+ sizeof(struct device_domain_info),
+ 0,
+ SLAB_HWCACHE_ALIGN,
+ NULL);
+ if (!iommu_devinfo_cache) {
+ pr_err("Couldn't create devinfo cache\n");
+ kmem_cache_destroy(fsl_pamu_domain_cache);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+
+static int reconfig_win(int liodn, struct fsl_dma_domain *domain)
+{
+ int ret;
+
+ spin_lock(&iommu_lock);
+ ret = pamu_config_ppaace(liodn, domain->mapped_iova,
+ domain->mapped_size,
+ ~(u32)0,
+ domain->paddr >> PAMU_PAGE_SHIFT,
+ domain->snoop_id, domain->stash_id,
+ 0, domain->prot);
+ spin_unlock(&iommu_lock);
+ if (ret) {
+ pr_err("PAMU PAACE configuration failed for liodn %d\n",
+ liodn);
+ }
+ return ret;
+}
+
+static void update_domain_subwin(struct fsl_dma_domain *dma_domain,
+ unsigned long iova, size_t size,
+ phys_addr_t paddr, int prot, int status)
+{
+ struct iommu_domain *domain = dma_domain->iommu_domain;
+ u32 subwin_cnt = dma_domain->subwin_cnt;
+ dma_addr_t geom_size = dma_domain->geom_size;
+ u32 subwin_size;
+ u32 mapped_subwin;
+ u32 mapped_subwin_cnt;
+ struct dma_subwindow *sub_win_ptr;
+ int i;
+
+ subwin_size = geom_size >> ilog2(subwin_cnt);
+ mapped_subwin = (iova - domain->geometry.aperture_start)
+ >> ilog2(subwin_size);
+ sub_win_ptr = &dma_domain->sub_win_arr[mapped_subwin];
+ mapped_subwin_cnt = (size < subwin_size) ? 1 :
+ size >> ilog2(subwin_size);
+ for (i = 0; i < mapped_subwin_cnt; i++) {
+ if (status) {
+ sub_win_ptr[i].paddr = paddr;
+ sub_win_ptr[i].size = (size < subwin_size) ?
+ size : subwin_size;
+ paddr += subwin_size;
+ sub_win_ptr[i].iova = iova;
+ iova += subwin_size;
+ }
+ sub_win_ptr[i].valid = status;
+ sub_win_ptr[i].prot = prot;
+ }
+
+ dma_domain->mapped_subwin = mapped_subwin;
+ dma_domain->mapped_subwin_cnt = mapped_subwin_cnt;
+}
+
+static int reconfig_subwin(int liodn, struct fsl_dma_domain *dma_domain)
+{
+ u32 subwin_cnt = dma_domain->subwin_cnt;
+ int ret = 0;
+ u32 mapped_subwin;
+ u32 mapped_subwin_cnt;
+ struct dma_subwindow *sub_win_ptr;
+ unsigned long rpn;
+ int i;
+
+ mapped_subwin = dma_domain->mapped_subwin;
+ mapped_subwin_cnt = dma_domain->mapped_subwin_cnt;
+ sub_win_ptr = &dma_domain->sub_win_arr[mapped_subwin];
+
+ for (i = 0; i < mapped_subwin_cnt; i++) {
+ rpn = sub_win_ptr[i].paddr >> PAMU_PAGE_SHIFT,
+
+ spin_lock(&iommu_lock);
+ ret = pamu_config_spaace(liodn, subwin_cnt, mapped_subwin,
+ sub_win_ptr[i].size,
+ ~(u32)0,
+ rpn, dma_domain->snoop_id,
+ dma_domain->stash_id,
+ (mapped_subwin == 0 &&
+ !dma_domain->enabled) ?
+ 0 : sub_win_ptr[i].valid,
+ sub_win_ptr[i].prot);
+ spin_unlock(&iommu_lock);
+ if (ret) {
+ pr_err("PAMU SPAACE configuration failed for liodn %d\n",liodn);
+ return ret;
+ }
+ mapped_subwin++;
+ }
+
+ return ret;
+}
+
+static phys_addr_t get_phys_addr(struct fsl_dma_domain *dma_domain, unsigned long iova)
+{
+ u32 subwin_cnt = dma_domain->subwin_cnt;
+
+ if (subwin_cnt) {
+ int i;
+ struct dma_subwindow *sub_win_ptr =
+ &dma_domain->sub_win_arr[0];
+
+ for (i = 0; i < subwin_cnt; i++) {
+ if (sub_win_ptr[i].valid &&
+ iova >= sub_win_ptr[i].iova &&
+ iova < (sub_win_ptr[i].iova +
+ sub_win_ptr[i].size - 1))
+ return (sub_win_ptr[i].paddr + (iova &
+ (sub_win_ptr[i].size - 1)));
+ }
+ } else {
+ return (dma_domain->paddr + (iova & (dma_domain->mapped_size - 1)));
+ }
+
+ return 0;
+}
+
+static int map_liodn_subwins(int liodn, struct fsl_dma_domain *dma_domain, u32 subwin_cnt)
+{
+ struct dma_subwindow *sub_win_ptr =
+ &dma_domain->sub_win_arr[0];
+ int i, ret;
+ unsigned long rpn;
+
+ for (i = 0; i < subwin_cnt; i++) {
+ if (sub_win_ptr[i].valid) {
+ rpn = sub_win_ptr[i].paddr >>
+ PAMU_PAGE_SHIFT,
+ spin_lock(&iommu_lock);
+ ret = pamu_config_spaace(liodn, subwin_cnt, i,
+ sub_win_ptr[i].size,
+ ~(u32)0,
+ rpn,
+ dma_domain->snoop_id,
+ dma_domain->stash_id,
+ (i > 0) ? 1 : 0,
+ sub_win_ptr[i].prot);
+ spin_unlock(&iommu_lock);
+ if (ret) {
+ pr_err("PAMU SPAACE configuration failed for liodn %d\n",
+ liodn);
+ return ret;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int map_liodn_win(int liodn, struct fsl_dma_domain *dma_domain)
+{
+ unsigned long rpn;
+ int ret;
+
+ rpn = dma_domain->paddr >> PAMU_PAGE_SHIFT;
+ spin_lock(&iommu_lock);
+ ret = pamu_config_ppaace(liodn, dma_domain->mapped_iova,
+ dma_domain->mapped_size,
+ ~(u32)0,
+ rpn,
+ dma_domain->snoop_id, dma_domain->stash_id,
+ 0, dma_domain->prot);
+ spin_unlock(&iommu_lock);
+ if (ret)
+ pr_err("PAMU PAACE configuration failed for liodn %d\n",
+ liodn);
+
+ return ret;
+}
+
+static int map_liodn(int liodn, struct fsl_dma_domain *dma_domain)
+{
+ u32 subwin_cnt = dma_domain->subwin_cnt;
+
+ if (subwin_cnt)
+ return map_liodn_subwins(liodn, dma_domain, subwin_cnt);
+ else
+ return map_liodn_win(liodn, dma_domain);
+
+}
+
+static int update_liodn(int liodn, struct fsl_dma_domain *dma_domain)
+{
+ int ret;
+
+ if (dma_domain->subwin_cnt) {
+ ret = reconfig_subwin(liodn, dma_domain);
+ if (ret)
+ pr_err("Subwindow reconfiguration failed for liodn %d\n", liodn);
+ } else {
+ ret = reconfig_win(liodn, dma_domain);
+ if (ret)
+ pr_err("Window reconfiguration failed for liodn %d\n", liodn);
+ }
+
+ return ret;
+}
+
+static int update_liodn_stash(int liodn, struct fsl_dma_domain *dma_domain,
+ u32 val)
+{
+ int ret = 0, i;
+
+ spin_lock(&iommu_lock);
+ if (!dma_domain->subwin_cnt) {
+ ret = pamu_update_paace_stash(liodn, 0, val);
+ if (ret) {
+ pr_err("Failed to update PAACE field for liodn %d\n ", liodn);
+ spin_unlock(&iommu_lock);
+ return ret;
+ }
+ } else {
+ for (i = 0; i < dma_domain->subwin_cnt; i++) {
+ ret = pamu_update_paace_stash(liodn, i, val);
+ if (ret) {
+ pr_err("Failed to update SPAACE %d field for liodn %d\n ", i, liodn);
+ spin_unlock(&iommu_lock);
+ return ret;
+ }
+ }
+ }
+ spin_unlock(&iommu_lock);
+
+ return ret;
+}
+
+static int configure_liodn(int liodn, struct device *dev,
+ struct fsl_dma_domain *dma_domain,
+ struct iommu_domain_geometry *geom_attr,
+ u32 subwin_cnt)
+{
+ phys_addr_t window_addr, window_size;
+ phys_addr_t subwin_size;
+ int ret = 0, i;
+ u32 omi_index = ~(u32)0;
+
+ /* Configure the omi_index at the geometry setup time.
+ * This is a static value which depends on the type of
+ * device and would not change thereafter.
+ */
+ get_ome_index(&omi_index, dev);
+
+ window_addr = geom_attr->aperture_start;
+ window_size = geom_attr->aperture_end - geom_attr->aperture_start;
+
+ spin_lock(&iommu_lock);
+ ret = pamu_disable_liodn(liodn);
+ if (!ret)
+ ret = pamu_config_ppaace(liodn, window_addr, window_size, omi_index,
+ 0, dma_domain->snoop_id,
+ dma_domain->stash_id, subwin_cnt, 0);
+ spin_unlock(&iommu_lock);
+ if (ret) {
+ pr_err("PAMU PAACE configuration failed for liodn %d\n", liodn);
+ return ret;
+ }
+
+ if (subwin_cnt) {
+ subwin_size = window_size >> ilog2(subwin_cnt);
+ for (i = 0; i < subwin_cnt; i++) {
+ spin_lock(&iommu_lock);
+ ret = pamu_config_spaace(liodn, subwin_cnt, i, subwin_size,
+ omi_index, 0,
+ dma_domain->snoop_id,
+ dma_domain->stash_id, 0, 0);
+ spin_unlock(&iommu_lock);
+ if (ret) {
+ pr_err("PAMU SPAACE configuration failed for liodn %d\n", liodn);
+ return ret;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int check_size(u64 size, unsigned long iova)
+{
+ if ((size & (size - 1)) || size < PAMU_PAGE_SIZE) {
+ pr_err("%s: size too small or not a power of two\n", __func__);
+ return -EINVAL;
+ }
+
+ if (iova & (size - 1)) {
+ pr_err("%s: address is not aligned with window size\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static inline int check_size_align(u64 size, u64 subwin_size)
+{
+ return ((size < subwin_size) ? ((size & (size - 1)) ||
+ size < PAMU_PAGE_SIZE) :
+ (size & (subwin_size -1)));
+}
+
+
+static struct fsl_dma_domain *iommu_alloc_dma_domain(void)
+{
+ struct fsl_dma_domain *domain;
+
+ domain = kmem_cache_zalloc(fsl_pamu_domain_cache, GFP_KERNEL);
+ if (!domain)
+ return NULL;
+
+ domain->stash_id = ~(u32)0;
+ domain->snoop_id = ~(u32)0;
+
+ INIT_LIST_HEAD(&domain->devices);
+
+ spin_lock_init(&domain->domain_lock);
+
+ return domain;
+}
+
+static inline struct device_domain_info *find_domain(struct device *dev)
+{
+ return dev->archdata.iommu_domain;
+}
+
+static void remove_domain_ref(struct device_domain_info *info, u32 subwin_cnt)
+{
+ list_del(&info->link);
+ spin_lock(&iommu_lock);
+ if (subwin_cnt)
+ pamu_free_subwins(info->liodn);
+ pamu_disable_liodn(info->liodn);
+ spin_unlock(&iommu_lock);
+ spin_lock(&device_domain_lock);
+ info->dev->archdata.iommu_domain = NULL;
+ kmem_cache_free(iommu_devinfo_cache, info);
+ spin_unlock(&device_domain_lock);
+}
+
+static void destroy_domain(struct fsl_dma_domain *dma_domain)
+{
+ struct device_domain_info *info;
+
+ while (!list_empty(&dma_domain->devices)) {
+ info = list_entry(dma_domain->devices.next,
+ struct device_domain_info, link);
+ remove_domain_ref(info, dma_domain->subwin_cnt);
+ }
+}
+
+static void detach_domain(struct device *dev, struct fsl_dma_domain *dma_domain)
+{
+ struct device_domain_info *info;
+ struct list_head *entry, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dma_domain->domain_lock, flags);
+ if (!list_empty(&dma_domain->devices)) {
+ list_for_each_safe(entry, tmp, &dma_domain->devices) {
+ info = list_entry(entry, struct device_domain_info, link);
+ if (info->dev == dev)
+ remove_domain_ref(info, dma_domain->subwin_cnt);
+ }
+ }
+ spin_unlock_irqrestore(&dma_domain->domain_lock, flags);
+}
+
+static void attach_domain(struct fsl_dma_domain *dma_domain, int liodn, struct device *dev)
+{
+ struct device_domain_info *info, *old_domain_info;
+
+ spin_lock(&device_domain_lock);
+ /*
+ * Check here if the device is already attached to domain or not.
+ * If the device is already attached to a domain detach it.
+ */
+ old_domain_info = find_domain(dev);
+ if (old_domain_info && old_domain_info->domain != dma_domain) {
+ spin_unlock(&device_domain_lock);
+ detach_domain(dev, old_domain_info->domain);
+ spin_lock(&device_domain_lock);
+ }
+
+ info = kmem_cache_zalloc(iommu_devinfo_cache, GFP_KERNEL);
+
+ info->dev = dev;
+ info->liodn = liodn;
+ info->domain = dma_domain;
+
+ list_add(&info->link, &dma_domain->devices);
+ /* In case of devices with multiple LIODNs just store
+ * the info for the first LIODN as all
+ * LIODNs share the same domain
+ */
+ if (!old_domain_info)
+ dev->archdata.iommu_domain = info;
+ spin_unlock(&device_domain_lock);
+
+}
+
+static phys_addr_t fsl_pamu_iova_to_phys(struct iommu_domain *domain,
+ unsigned long iova)
+{
+ struct fsl_dma_domain *dma_domain = domain->priv;
+
+ if ((iova < domain->geometry.aperture_start) ||
+ iova > (domain->geometry.aperture_end))
+ return 0;
+
+ return get_phys_addr(dma_domain, iova);
+}
+
+static int fsl_pamu_domain_has_cap(struct iommu_domain *domain,
+ unsigned long cap)
+{
+ return cap == IOMMU_CAP_CACHE_COHERENCY;
+}
+
+static void fsl_pamu_domain_destroy(struct iommu_domain *domain)
+{
+ struct fsl_dma_domain *dma_domain = domain->priv;
+
+ domain->priv = NULL;
+
+ destroy_domain(dma_domain);
+
+ dma_domain->enabled = 0;
+ dma_domain->valid = 0;
+ dma_domain->mapped = 0;
+
+ kmem_cache_free(fsl_pamu_domain_cache, dma_domain);
+}
+
+static int fsl_pamu_domain_init(struct iommu_domain *domain)
+{
+ struct fsl_dma_domain *dma_domain;
+
+ dma_domain = iommu_alloc_dma_domain();
+ if (!dma_domain) {
+ pr_err("dma_domain allocation failed\n");
+ return -ENOMEM;
+ }
+ domain->priv = dma_domain;
+ dma_domain->iommu_domain = domain;
+ /* defaul geometry = 1MB */
+ domain->geometry.aperture_start = 0;
+ domain->geometry.aperture_end = 0x100000;
+ domain->geometry.subwindows = 0;
+ domain->geometry.force_aperture = true;
+
+ return 0;
+}
+
+static int configure_domain(struct fsl_dma_domain *dma_domain,
+ struct iommu_domain_geometry *geom_attr,
+ u32 subwin_cnt)
+{
+ struct device_domain_info *info;
+ int ret = 0;
+
+ list_for_each_entry(info, &dma_domain->devices, link) {
+ ret = configure_liodn(info->liodn, info->dev, dma_domain,
+ geom_attr, subwin_cnt);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int update_domain_stash(struct fsl_dma_domain *dma_domain, u32 val)
+{
+ struct device_domain_info *info;
+ int ret = 0;
+
+ list_for_each_entry(info, &dma_domain->devices, link) {
+ ret = update_liodn_stash(info->liodn, dma_domain, val);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int update_domain_mapping(struct fsl_dma_domain *domain)
+{
+ struct device_domain_info *info;
+ int ret = 0;
+
+ list_for_each_entry(info, &domain->devices, link) {
+ ret = update_liodn(info->liodn, domain);
+ if (ret)
+ break;
+ }
+ return ret;
+}
+
+static int fsl_pamu_map(struct iommu_domain *domain,
+ unsigned long iova, phys_addr_t paddr,
+ size_t size, int iommu_prot)
+{
+ struct fsl_dma_domain *dma_domain = domain->priv;
+ struct iommu_domain_geometry *geom_attr = &domain->geometry;
+ int prot = 0;
+ unsigned long flags;
+ int ret = 0;
+
+
+ if (iommu_prot & IOMMU_READ)
+ prot |= PAACE_AP_PERMS_QUERY;
+ if (iommu_prot & IOMMU_WRITE)
+ prot |= PAACE_AP_PERMS_UPDATE;
+
+ spin_lock_irqsave(&dma_domain->domain_lock, flags);
+ if (dma_domain->valid) {
+ if (dma_domain->subwin_cnt) {
+ u32 align_check, subwin_size;
+ dma_addr_t geom_size = dma_domain->geom_size;
+
+ subwin_size = geom_size >> ilog2(dma_domain->subwin_cnt);
+ align_check = check_size(subwin_size, iova) ||
+ check_size_align(size, subwin_size);
+ if ((iova >= geom_attr->aperture_start &&
+ iova < geom_attr->aperture_end - 1 &&
+ size <= geom_size) &&
+ !align_check) {
+ update_domain_subwin(dma_domain, iova, size, paddr, prot, 1);
+ } else {
+ pr_err("Mismatch between geometry and mapping\n");
+ ret = -EINVAL;
+ }
+ } else {
+ ret = check_size(size, iova);
+ if (!ret && !dma_domain->enabled) {
+ dma_domain->mapped_iova = iova;
+ dma_domain->mapped_size = size;
+ dma_domain->paddr = paddr;
+ dma_domain->prot = prot;
+ } else {
+ pr_err("Can't create mapping, %s\n",
+ (ret) ? "Invalid size" : "DMA enabled");
+ ret = ret ? ret : -EBUSY;
+ }
+ }
+
+ if (!ret) {
+ ret = update_domain_mapping(dma_domain);
+ if (!ret)
+ dma_domain->mapped = 1;
+ }
+ } else {
+ pr_err("Set domain geometry before creating the mapping\n");
+ ret = -ENODEV;
+ }
+ spin_unlock_irqrestore(&dma_domain->domain_lock, flags);
+
+ return ret;
+}
+
+static size_t fsl_pamu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
+{
+ struct fsl_dma_domain *dma_domain = domain->priv;
+ struct iommu_domain_geometry *geom_attr = &domain->geometry;
+ size_t ret = size;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dma_domain->domain_lock, flags);
+ if (dma_domain->valid && dma_domain->mapped) {
+ if (dma_domain->subwin_cnt) {
+ u32 align_check, subwin_size;
+ dma_addr_t geom_size = dma_domain->geom_size;
+
+ subwin_size = geom_size >> ilog2(dma_domain->subwin_cnt);
+ align_check = check_size(subwin_size, iova) ||
+ check_size_align(size, subwin_size);
+ if ((iova >= geom_attr->aperture_start &&
+ iova < geom_attr->aperture_end - 1 &&
+ size <= geom_size) &&
+ !align_check) {
+ update_domain_subwin(dma_domain, iova, size, 0,
+ PAACE_AP_PERMS_DENIED, 0);
+ } else {
+ pr_err("Invalid address/size alignment\n");
+ ret = -EINVAL;
+ }
+ } else {
+ if (!dma_domain->enabled) {
+ u64 max_addr, unmap_range;
+ size_t domain_size;
+ unsigned long domain_iova;
+
+ unmap_range = iova + size;
+ domain_iova = dma_domain->mapped_iova;
+ domain_size = dma_domain->mapped_size;
+ max_addr = domain_iova + domain_size;
+
+ if ((domain_iova != iova &&
+ (max_addr < unmap_range ||
+ max_addr > unmap_range)) ||
+ size > domain_size ||
+ iova < domain_iova) {
+ pr_err("Invalid size/address parameters for unmap\n");
+ ret = -EINVAL;
+ } else {
+ domain_size -= size;
+ if (iova == domain_iova)
+ domain_iova += size;
+ ret = check_size(domain_size, domain_iova);
+ if (!ret) {
+ dma_domain->mapped_iova = domain_iova;
+ dma_domain->mapped_size = domain_size;
+ if (!domain_size)
+ dma_domain->mapped = 0;
+ }
+ }
+ } else {
+ pr_err("Can't update mapping with DMA enabled\n");
+ ret = -EBUSY;
+ }
+ }
+ if (ret == size)
+ update_domain_mapping(dma_domain);
+ } else {
+ pr_err("Can't unmap an invalid domain\n");
+ ret = -ENODEV;
+ }
+ spin_unlock_irqrestore(&dma_domain->domain_lock, flags);
+
+ return ret;
+}
+
+static int handle_attach_device(struct fsl_dma_domain *dma_domain,
+ struct device *dev, const u32 *liodn,
+ int num)
+{
+ unsigned long flags;
+ struct iommu_domain *domain = dma_domain->iommu_domain;
+ int ret = 0;
+ int i;
+
+ spin_lock_irqsave(&dma_domain->domain_lock, flags);
+ for (i = 0; i < num; i++) {
+ attach_domain(dma_domain, liodn[i], dev);
+ if (dma_domain->valid) {
+ ret = configure_liodn(liodn[i], dev, dma_domain,
+ &domain->geometry,
+ dma_domain->subwin_cnt);
+ if (ret)
+ break;
+ if (dma_domain->mapped) {
+ ret = map_liodn(liodn[i], dma_domain);
+ if (ret)
+ break;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&dma_domain->domain_lock, flags);
+
+ return ret;
+}
+
+static int fsl_pamu_attach_device(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct fsl_dma_domain *dma_domain = domain->priv;
+ const u32 *prop;
+ u32 prop_cnt;
+ int len, ret = 0;
+
+ prop = of_get_property(dev->of_node, "fsl,liodn", &len);
+ if (prop) {
+ prop_cnt = len / sizeof(u32);
+ ret = handle_attach_device(dma_domain, dev,
+ prop, prop_cnt);
+ } else {
+ pr_err("missing fsl,liodn property at %s\n",
+ dev->of_node->full_name);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void fsl_pamu_detach_device(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct fsl_dma_domain *dma_domain = domain->priv;
+ const u32 *prop;
+ int len;
+
+ prop = of_get_property(dev->of_node, "fsl,liodn", &len);
+ if (prop)
+ detach_domain(dev, dma_domain);
+ else
+ pr_err("missing fsl,liodn property at %s\n",
+ dev->of_node->full_name);
+}
+
+static int get_subwin_cnt(dma_addr_t geom_size, u32 subwin, u32 *subwin_cnt)
+{
+ switch (subwin) {
+ case 0:
+ /* We can't support geometry size > 1MB*/
+ if (geom_size != PAMU_DEFAULT_GEOMETRY)
+ return 0;
+ *subwin_cnt = 256;
+ break;
+ case 1:
+ /* No subwindows only a single PAMU window */
+ *subwin_cnt = 0;
+ break;
+ default:
+ if (subwin > max_subwindow_count ||
+ (subwin & (subwin - 1)))
+ return 0;
+ *subwin_cnt = subwin;
+ }
+ return 1;
+}
+
+static int configure_domain_geometry(struct iommu_domain *domain, void *data)
+{
+ int ret;
+ struct iommu_domain_geometry *geom_attr = data;
+ struct fsl_dma_domain *dma_domain = domain->priv;
+ dma_addr_t geom_size;
+ u32 subwin_cnt;
+ unsigned long flags;
+
+ geom_size = geom_attr->aperture_end - geom_attr->aperture_start;
+
+ if (check_size(geom_size, geom_attr->aperture_start) ||
+ !geom_attr->force_aperture ||
+ !get_subwin_cnt(geom_size, geom_attr->subwindows,
+ &subwin_cnt)) {
+ pr_err("Invalid PAMU geometry attributes\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&dma_domain->domain_lock, flags);
+ if (dma_domain->enabled) {
+ pr_err("Can't set geometry attributes as domain is active\n");
+ spin_unlock_irqrestore(&dma_domain->domain_lock, flags);
+ return -EBUSY;
+ }
+ ret = configure_domain(dma_domain, geom_attr, subwin_cnt);
+ if (!ret) {
+ if (subwin_cnt) {
+ if (dma_domain->sub_win_arr)
+ kfree(dma_domain->sub_win_arr);
+ dma_domain->sub_win_arr = kmalloc(sizeof(struct dma_subwindow) *
+ subwin_cnt, GFP_KERNEL);
+ if (!dma_domain->sub_win_arr) {
+ spin_unlock_irqrestore(&dma_domain->domain_lock, flags);
+ return -ENOMEM;
+ }
+ }
+ memcpy(&domain->geometry, geom_attr,
+ sizeof(struct iommu_domain_geometry));
+ dma_domain->geom_size = geom_size;
+ dma_domain->subwin_cnt = subwin_cnt;
+ dma_domain->valid = 1;
+ }
+ spin_unlock_irqrestore(&dma_domain->domain_lock, flags);
+
+ return ret;
+}
+
+static int configure_domain_stash(struct fsl_dma_domain *dma_domain, void *data)
+{
+ struct iommu_stash_attribute *stash_attr = data;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&dma_domain->domain_lock, flags);
+
+ memcpy(&dma_domain->dma_stash, stash_attr,
+ sizeof(struct iommu_stash_attribute));
+
+ dma_domain->stash_id = get_stash_id(stash_attr->cache,
+ stash_attr->cpu);
+ if (dma_domain->stash_id == ~(u32)0) {
+ pr_err("Invalid stash attributes\n");
+ spin_unlock_irqrestore(&dma_domain->domain_lock, flags);
+ return -EINVAL;
+ }
+
+ ret = update_domain_stash(dma_domain, dma_domain->stash_id);
+
+ spin_unlock_irqrestore(&dma_domain->domain_lock, flags);
+
+ return ret;
+}
+
+static int configure_domain_dma_state(struct fsl_dma_domain *dma_domain, bool enable)
+{
+ struct device_domain_info *info;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&dma_domain->domain_lock, flags);
+
+ if (enable && !dma_domain->mapped) {
+ pr_err("Can't enable DMA domain without valid mapping\n");
+ spin_unlock_irqrestore(&dma_domain->domain_lock, flags);
+ return -ENODEV;
+ }
+
+ dma_domain->enabled = enable;
+ if (!list_empty(&dma_domain->devices)) {
+ list_for_each_entry(info, &dma_domain->devices,
+ link) {
+ ret = (enable) ? pamu_enable_liodn(info->liodn):
+ pamu_disable_liodn(info->liodn);
+ if (ret)
+ pr_err("Unable to set dma state for liodn %d",
+ info->liodn);
+ }
+ }
+ spin_unlock_irqrestore(&dma_domain->domain_lock, flags);
+
+ return 0;
+}
+
+int fsl_pamu_set_domain_attr(struct iommu_domain *domain,
+ enum iommu_attr attr_type, void *data)
+{
+ struct fsl_dma_domain *dma_domain = domain->priv;
+ int ret = 0;
+
+
+ switch(attr_type) {
+ case DOMAIN_ATTR_GEOMETRY:
+ ret = configure_domain_geometry(domain, data);
+ break;
+ case DOMAIN_ATTR_STASH:
+ ret = configure_domain_stash(dma_domain, data);
+ break;
+ case DOMAIN_ATTR_ENABLE:
+ ret = configure_domain_dma_state(dma_domain, *(int *)data);
+ break;
+ default:
+ pr_err("Unsupported attribute type\n");
+ ret = -EINVAL;
+ break;
+ };
+
+ return ret;
+}
+
+int fsl_pamu_get_domain_attr(struct iommu_domain *domain,
+ enum iommu_attr attr_type, void *data)
+{
+ struct fsl_dma_domain *dma_domain = domain->priv;
+ int ret = 0;
+
+
+ switch(attr_type) {
+ case DOMAIN_ATTR_STASH:
+ memcpy((struct iommu_stash_attribute *) data, &dma_domain->dma_stash,
+ sizeof(struct iommu_stash_attribute));
+ break;
+ case DOMAIN_ATTR_ENABLE:
+ *(int *)data = dma_domain->enabled;
+ break;
+ default:
+ pr_err("Unsupported attribute type\n");
+ ret = -EINVAL;
+ break;
+ };
+
+ return ret;
+}
+
+static struct iommu_ops fsl_pamu_ops = {
+ .domain_init = fsl_pamu_domain_init,
+ .domain_destroy = fsl_pamu_domain_destroy,
+ .attach_dev = fsl_pamu_attach_device,
+ .detach_dev = fsl_pamu_detach_device,
+ .map = fsl_pamu_map,
+ .unmap = fsl_pamu_unmap,
+ .iova_to_phys = fsl_pamu_iova_to_phys,
+ .domain_has_cap = fsl_pamu_domain_has_cap,
+ .domain_set_attr = fsl_pamu_set_domain_attr,
+ .domain_get_attr = fsl_pamu_get_domain_attr,
+ .pgsize_bitmap = FSL_PAMU_PGSIZES,
+};
+
+int pamu_domain_init()
+{
+ int ret = 0;
+
+ ret = iommu_init_mempool();
+ if (ret)
+ return ret;
+
+ bus_set_iommu(&platform_bus_type, &fsl_pamu_ops);
+
+ return ret;
+}
diff --git a/drivers/iommu/fsl_pamu_domain.h b/drivers/iommu/fsl_pamu_domain.h
new file mode 100644
index 0000000..a976000
--- /dev/null
+++ b/drivers/iommu/fsl_pamu_domain.h
@@ -0,0 +1,102 @@
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright (C) 2012 Freescale Semiconductor, Inc.
+ *
+ */
+
+#ifndef __FSL_PAMU_DOMAIN_H
+#define __FSL_PAMU_DOMAIN_H
+
+#include "fsl_pamu.h"
+
+/**
+ * This corresponds to a geometry size set to 1MB, where number
+ * of subwindows is set to 256 and size of each subwindow is 4K.
+ */
+#define PAMU_DEFAULT_GEOMETRY (256 * PAMU_PAGE_SIZE)
+
+struct dma_subwindow {
+ unsigned long iova;
+ phys_addr_t paddr;
+ size_t size;
+ int valid;
+ int prot;
+};
+
+struct fsl_dma_domain {
+ /* mapped_iova and mapped_size are used in case there are
+ * no subwindows associated with the domain. These are
+ * updated on each iommu_map/iommu_unmap call. Based
+ * on these values the corresponding PPAACE entry is
+ * updated.
+ */
+ unsigned long mapped_iova;
+ size_t mapped_size;
+ /* physical address mapping */
+ u64 paddr;
+ /* mapped_subwin/mapped_subwin_cnt are only valid if
+ * the domain geometry has subwindows. These fields
+ * are updated on each iommu_map/iommu_unmap call.
+ * Based on these values the corresponding SPAACE
+ * entries are updated.
+ */
+ u32 mapped_subwin;
+ u32 mapped_subwin_cnt;
+ /* Access permission associated with the domain */
+ int prot;
+ /* number of subwindows assocaited with this domain */
+ u32 subwin_cnt;
+ /* sub_win_arr contains information of the configured
+ * subwindows for a domain.
+ */
+ struct dma_subwindow *sub_win_arr;
+ /* list of devices associated with the domain */
+ struct list_head devices;
+ /* dma_domain states:
+ * valid - Geometry attribute has been configured.
+ * mapped - A particular mapping has been created
+ * within the configured geometry. Domain has to
+ * be in the valid state before any DMA mapping
+ * can be created in it.
+ * enabled - DMA has been enabled for the given
+ * domain. This translates to setting of the
+ * valid bit for the primary PAACE in the PAMU
+ * PAACT table. Domain should be valid and have
+ * a valid mapping before DMA can be enabled for it.
+ *
+ */
+ int valid;
+ int mapped;
+ int enabled;
+ /* stash_id obtained from the stash attribute details */
+ u32 stash_id;
+ struct iommu_stash_attribute dma_stash;
+ u32 snoop_id;
+ dma_addr_t geom_size;
+ struct iommu_domain *iommu_domain;
+ spinlock_t domain_lock;
+};
+
+/* domain-device relationship */
+struct device_domain_info {
+ struct list_head link; /* link to domain siblings */
+ struct device *dev;
+ u32 liodn;
+ struct fsl_dma_domain *domain; /* pointer to domain */
+};
+
+extern unsigned int max_subwindow_count;
+
+#endif /* __FSL_PAMU_DOMAIN_H */
--
1.7.4.1
^ permalink raw reply related
* Re: [PATCH 119/493] usb: remove use of __devexit_p
From: Peter Korsgaard @ 2012-11-20 14:02 UTC (permalink / raw)
To: Nicolas Ferre
Cc: Kukjin Kim, Wan ZongShun, Alexander Shishkin, gregkh, linux-usb,
Felipe Balbi, linux-samsung-soc, Bill Pemberton, Alan Stern,
Ben Dooks, linux-omap, linuxppc-dev, linux-arm-kernel
In-Reply-To: <50AB4D90.4080907@atmel.com>
>>>>> "Nicolas" == Nicolas Ferre <nicolas.ferre@atmel.com> writes:
Nicolas> On 11/19/2012 07:21 PM, Bill Pemberton :
>> CONFIG_HOTPLUG is going away as an option so __devexit_p is no longer
>> needed.
>>
>> Signed-off-by: Bill Pemberton <wfp5p@virginia.edu>
>> Cc: Peter Korsgaard <jacmet@sunsite.dk>
>> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
>> Cc: Felipe Balbi <balbi@ti.com>
>> Cc: Li Yang <leoli@freescale.com>
>> Cc: Alan Stern <stern@rowland.harvard.edu>
>> Cc: Wan ZongShun <mcuos.com@gmail.com>
>> Cc: Ben Dooks <ben-linux@fluff.org>
>> Cc: Kukjin Kim <kgene.kim@samsung.com>
>> Cc: linux-usb@vger.kernel.org
>> Cc: linux-omap@vger.kernel.org
>> Cc: linuxppc-dev@lists.ozlabs.org
>> Cc: linux-arm-kernel@lists.infradead.org
>> Cc: linux-samsung-soc@vger.kernel.org
Nicolas> [..]
>> drivers/usb/host/ehci-atmel.c | 2 +-
>> drivers/usb/host/ohci-at91.c | 2 +-
Nicolas> For Atmel:
Nicolas> Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
For c67x00 and g_hid:
Acked-by: Peter Korsgaard <jacmet@sunsite.dk>
--
Bye, Peter Korsgaard
^ permalink raw reply
* [PATCH 3/4 v5] iommu/fsl: Add iommu domain attributes required by fsl PAMU driver.
From: Varun Sethi @ 2012-11-20 13:54 UTC (permalink / raw)
To: joerg.roedel, iommu, linuxppc-dev, linux-kernel, scottwood, timur
Cc: Varun Sethi
In-Reply-To: <1353419697-31269-1-git-send-email-Varun.Sethi@freescale.com>
Added the following domain attributes required by FSL PAMU driver:
1. Subwindows field added to the iommu domain geometry attribute.
2. Added new iommu stash attribute, which allows setting of the
LIODN specific stash id parameter through IOMMU API.
3. Added an attribute for enabling/disabling DMA to a particular
memory window.
Signed-off-by: Varun Sethi <Varun.Sethi@freescale.com>
---
changes in v5:
- Updated description of the subwindows field.
changes in v4:
- Updated comment explaining subwindows(as mentioned by Scott).
change in v3:
-renamed the stash attribute targets
include/linux/iommu.h | 43 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 43 insertions(+), 0 deletions(-)
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index f3b99e1..7ca1cda 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -44,6 +44,41 @@ struct iommu_domain_geometry {
dma_addr_t aperture_start; /* First address that can be mapped */
dma_addr_t aperture_end; /* Last address that can be mapped */
bool force_aperture; /* DMA only allowed in mappable range? */
+
+ /*
+ * A geometry mapping can be created in one of the following ways
+ * for an IOMMU:
+ * 1. A single contiguous window
+ * 2. Through arbritary paging throughout the aperture.
+ * 3. Using multiple subwindows
+ *
+ * In absence of arbritary paging, subwindows allow for supporting
+ * physically discontiguous mappings.
+ *
+ * This attribute indicates number of DMA subwindows supported by
+ * the geometry. If there is a single window that maps the entire
+ * geometry, attribute must be set to "1". A value of "0" implies
+ * that this mechanism is not used at all(normal paging is used).
+ * Value other than* "0" or "1" indicates the actual number of
+ * subwindows.
+ */
+ u32 subwindows;
+};
+
+/* cache stash targets */
+#define IOMMU_ATTR_CACHE_L1 1
+#define IOMMU_ATTR_CACHE_L2 2
+#define IOMMU_ATTR_CACHE_L3 3
+
+/* This attribute corresponds to IOMMUs capable of generating
+ * a stash transaction. A stash transaction is typically a
+ * hardware initiated prefetch of data from memory to cache.
+ * This attribute allows configuring stashig specific parameters
+ * in the IOMMU hardware.
+ */
+struct iommu_stash_attribute {
+ u32 cpu; /* cpu number */
+ u32 cache; /* cache to stash to: L1,L2,L3 */
};
struct iommu_domain {
@@ -60,6 +95,14 @@ struct iommu_domain {
enum iommu_attr {
DOMAIN_ATTR_MAX,
DOMAIN_ATTR_GEOMETRY,
+ /* Set the IOMMU hardware stashing
+ * parameters.
+ */
+ DOMAIN_ATTR_STASH,
+ /* Explicity enable/disable DMA for a
+ * particular memory window.
+ */
+ DOMAIN_ATTR_ENABLE,
};
#ifdef CONFIG_IOMMU_API
--
1.7.4.1
^ permalink raw reply related
* [PATCH 2/4] iommu/fsl: Add PAMU bypass enable register to ccsr_guts structure.
From: Varun Sethi @ 2012-11-20 13:54 UTC (permalink / raw)
To: joerg.roedel, iommu, linuxppc-dev, linux-kernel, scottwood, timur
Cc: Varun Sethi
In-Reply-To: <1353419697-31269-1-git-send-email-Varun.Sethi@freescale.com>
PAMU bypass enable register added to the ccsr_guts structure.
Signed-off-by: Timur Tabi <timur@freescale.com>
Signed-off-by: Varun Sethi <Varun.Sethi@freescale.com>
---
arch/powerpc/include/asm/fsl_guts.h | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/include/asm/fsl_guts.h b/arch/powerpc/include/asm/fsl_guts.h
index aa4c488..bb208eb 100644
--- a/arch/powerpc/include/asm/fsl_guts.h
+++ b/arch/powerpc/include/asm/fsl_guts.h
@@ -69,7 +69,9 @@ struct ccsr_guts {
u8 res0c4[0x224 - 0xc4];
__be32 iodelay1; /* 0x.0224 - IO delay control register 1 */
__be32 iodelay2; /* 0x.0228 - IO delay control register 2 */
- u8 res22c[0x800 - 0x22c];
+ u8 res22c[0x604 - 0x22c];
+ __be32 pamubypenr; /* 0x.604 - PAMU bypass enable register */
+ u8 res608[0x800 - 0x608];
__be32 clkdvdr; /* 0x.0800 - Clock Divide Register */
u8 res804[0x900 - 0x804];
__be32 ircr; /* 0x.0900 - Infrared Control Register */
--
1.7.4.1
^ permalink raw reply related
* [PATCH 1/4 v2] iommu/fsl: Store iommu domain information pointer in archdata.
From: Varun Sethi @ 2012-11-20 13:54 UTC (permalink / raw)
To: joerg.roedel, iommu, linuxppc-dev, linux-kernel, scottwood, timur
Cc: Varun Sethi
In-Reply-To: <1353419697-31269-1-git-send-email-Varun.Sethi@freescale.com>
Add a new field in the device (powerpc) archdata structure for storing iommu domain
information pointer. This pointer is stored when the device is attached to a particular
domain.
Signed-off-by: Varun Sethi <Varun.Sethi@freescale.com>
---
arch/powerpc/include/asm/device.h | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/include/asm/device.h b/arch/powerpc/include/asm/device.h
index 77e97dd..6dc79fe 100644
--- a/arch/powerpc/include/asm/device.h
+++ b/arch/powerpc/include/asm/device.h
@@ -28,6 +28,10 @@ struct dev_archdata {
void *iommu_table_base;
} dma_data;
+ /* IOMMU domain information pointer. This would be set
+ * when this device is attached to an iommu_domain.
+ */
+ void *iommu_domain;
#ifdef CONFIG_SWIOTLB
dma_addr_t max_direct_dma_addr;
#endif
--
1.7.4.1
^ permalink raw reply related
* [PATCH 0/4] iommu/fsl: Freescale PAMU driver and IOMMU API implementation.
From: Varun Sethi @ 2012-11-20 13:54 UTC (permalink / raw)
To: joerg.roedel, iommu, linuxppc-dev, linux-kernel, scottwood, timur
Cc: Varun Sethi
This patchset provides the Freescale PAMU (Peripheral Access Management Unit) driver
and the corresponding IOMMU API implementation. PAMU is the IOMMU present on Freescale
QorIQ platforms. PAMU can authorize memory access, remap the memory address, and remap
the I/O transaction type.
This set consists of the following patches:
1. Addition of new field in the device (powerpc) archdata structure for storing iommu domain information
pointer. This pointer is stored when the device is attached to a particular iommu domain.
2. Add PAMU bypass enable register to the ccsr_guts structure.
3. Addition of domain attributes required by the PAMU driver IOMMU API.
4. PAMU driver and IOMMU API implementation.
This patch set is based on the next branch of the iommu git tree maintained by Joerg.
Varun Sethi (4):
store iommu domain pointer in device archdata structure.
Add PAMU bypass enable register to ccsr_guts structure.
Add attributes for fsl PAMU driver.
FSL PAMU driver.
arch/powerpc/include/asm/device.h | 4 +
arch/powerpc/include/asm/fsl_guts.h | 4 +-
drivers/iommu/Kconfig | 8 +
drivers/iommu/Makefile | 1 +
drivers/iommu/fsl_pamu.c | 1152 +++++++++++++++++++++++++++++++++++++++
drivers/iommu/fsl_pamu.h | 398 ++++++++++++++
drivers/iommu/fsl_pamu_domain.c | 978 +++++++++++++++++++++++++++++++++
drivers/iommu/fsl_pamu_domain.h | 102 ++++
include/linux/iommu.h | 43 +++++++++++++++++++++++++++++++++++++++++++
9 files changed, 2680 insertions(+), 1 deletions(-)
create mode 100644 drivers/iommu/fsl_pamu.c
create mode 100644 drivers/iommu/fsl_pamu.h
create mode 100644 drivers/iommu/fsl_pamu_domain.c
create mode 100644 drivers/iommu/fsl_pamu_domain.h
--
1.7.4.1
^ permalink raw reply
* Re: [PATCH 207/493] i2c: remove use of __devinit
From: Russell King - ARM Linux @ 2012-11-20 13:23 UTC (permalink / raw)
To: Jean Delvare
Cc: Rudolf Marek, Tony Lindgren, gregkh, Guan Xuetao, Wolfram Sang,
Mark M. Hoffman, Bill Pemberton, linux-i2c, Ben Dooks, Barry Song,
Olof Johansson, linux-omap, linuxppc-dev, linux-arm-kernel
In-Reply-To: <20121120092046.24496415@endymion.delvare>
On Tue, Nov 20, 2012 at 09:20:46AM +0100, Jean Delvare wrote:
> Hi Bill,
>
> On Mon, 19 Nov 2012 13:22:36 -0500, Bill Pemberton wrote:
> > CONFIG_HOTPLUG is going away as an option so __devinit is no longer
> > needed.
>
> Can you please point me/us to the discussion explaining the rationale
> behind this move, and the explanation of what will be done exactly?
> While I can easily understand that we want to drop CONFIG_HOTPLUG and
> always enable hot-plug support, I don't see where we are going with
> removing __devinit annotations and the like.
It's actually very simple to understand.
1. CONFIG_HOTPLUG is going away; it's already defined to always be 'Y'.
2. This means that the the devinit sections will not be discarded anymore.
3. As a result, there's no point the devinit sections existing anymore.
4. As there's no devinit sections, the __devinit marker is entirely
redundant and useless.
The reason this is being done is because the benefit to cost ratio of this
is far too high; it's well proven that people constantly get these markings
wrong, and with most kernels having had hotplug enabled anyway, it's not
providing much in the way of space saving benefit over the number of section
conflicts it causes. So, it's been decided a few years ago that this is
going to happen, with that justification, and it's been accepted by 300
odd kernel developers in at least one kernel summit when it was talked
about... and it's been mentioned on mailing lists several times.
^ permalink raw reply
* Re: [PATCH 065/493] i2c: remove use of __devexit_p
From: Jean Delvare @ 2012-11-20 13:46 UTC (permalink / raw)
To: Bill Pemberton
Cc: Rudolf Marek, Tony Lindgren, gregkh, Wolfram Sang,
Mark M. Hoffman, linux-omap, linux-i2c, Ben Dooks, Barry Song,
Olof Johansson, Guan Xuetao, linuxppc-dev, linux-arm-kernel
In-Reply-To: <1353349642-3677-65-git-send-email-wfp5p@virginia.edu>
On Mon, 19 Nov 2012 13:20:14 -0500, Bill Pemberton wrote:
> CONFIG_HOTPLUG is going away as an option so __devexit_p is no longer
> needed.
As mentioned on the lm-sensors list for hwmon patches already, I think
it would be much clearer to not split __devexit, __devexit_p, __devinit
etc. removal into separate patches. One patch per subsystem would be
easier to review and apply. If patches grow too large then you'd rather
split in a different direction, for example drivers/i2c/muxes vs.
drivers/i2c/busses or even grouped by related bus drivers (see entries
"I2C OVER PARALLEL PORT" and "I2C/SMBUS CONTROLLER DRIVERS FOR PC" in
MAINTAINERS for examples of meaningful groups.)
--
Jean Delvare
^ permalink raw reply
* Re: [PATCH 207/493] i2c: remove use of __devinit
From: Jean Delvare @ 2012-11-20 13:37 UTC (permalink / raw)
To: Russell King - ARM Linux
Cc: Rudolf Marek, Tony Lindgren, gregkh, Guan Xuetao, Wolfram Sang,
Mark M. Hoffman, Bill Pemberton, linux-i2c, Ben Dooks, Barry Song,
Olof Johansson, linux-omap, linuxppc-dev, linux-arm-kernel
In-Reply-To: <20121120132342.GP3332@n2100.arm.linux.org.uk>
On Tue, 20 Nov 2012 13:23:42 +0000, Russell King - ARM Linux wrote:
> On Tue, Nov 20, 2012 at 09:20:46AM +0100, Jean Delvare wrote:
> > Hi Bill,
> >
> > On Mon, 19 Nov 2012 13:22:36 -0500, Bill Pemberton wrote:
> > > CONFIG_HOTPLUG is going away as an option so __devinit is no longer
> > > needed.
> >
> > Can you please point me/us to the discussion explaining the rationale
> > behind this move, and the explanation of what will be done exactly?
> > While I can easily understand that we want to drop CONFIG_HOTPLUG and
> > always enable hot-plug support, I don't see where we are going with
> > removing __devinit annotations and the like.
>
> It's actually very simple to understand.
>
> 1. CONFIG_HOTPLUG is going away; it's already defined to always be 'Y'.
> 2. This means that the the devinit sections will not be discarded anymore.
> 3. As a result, there's no point the devinit sections existing anymore.
> 4. As there's no devinit sections, the __devinit marker is entirely
> redundant and useless.
Ah, yes, very simple indeed. Not sure how I managed to not understand
it earlier today. Thanks for explaining.
> The reason this is being done is because the benefit to cost ratio of this
> is far too high; it's well proven that people constantly get these markings
> wrong, and with most kernels having had hotplug enabled anyway, it's not
> providing much in the way of space saving benefit over the number of section
> conflicts it causes. So, it's been decided a few years ago that this is
Yes, I completely agree.
> going to happen, with that justification, and it's been accepted by 300
> odd kernel developers in at least one kernel summit when it was talked
Probably that was one I didn't attend to, as I can't remember this
discussion.
> about... and it's been mentioned on mailing lists several times.
Maybe LKML, which I don't read. So I wasn't aware of the plan before
seeing Bill's patches.
--
Jean Delvare
^ permalink raw reply
* Re: [PATCH] PCI: MSI: Restore read_msi_msg_desc(); add get_cached_msi_msg_desc()
From: Ben Hutchings @ 2012-11-20 12:36 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: Stephen Rothwell, linux-pci, LKML, Jesse Barnes, Bjorn Helgaas,
ppc-dev
In-Reply-To: <1353396032.17856.7.camel@pasglop>
On Tue, 2012-11-20 at 18:20 +1100, Benjamin Herrenschmidt wrote:
> On Fri, 2010-07-23 at 14:56 +0100, Ben Hutchings wrote:
> > commit 2ca1af9aa3285c6a5f103ed31ad09f7399fc65d7 "PCI: MSI: Remove
> > unsafe and unnecessary hardware access" changed read_msi_msg_desc() to
> > return the last MSI message written instead of reading it from the
> > device, since it may be called while the device is in a reduced
> > power state.
>
> Looks reasonable... Jesse ?
[...]
So reasonable that it was committed a couple of years ago!
Where did you dredge this from?
Ben.
--
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply
* Re: [PATCH v3 06/12] memory-hotplug: unregister memory section on SPARSEMEM_VMEMMAP
From: Jaegeuk Hanse @ 2012-11-20 11:16 UTC (permalink / raw)
To: Yasuaki Ishimatsu, Wen Congyang
Cc: linux-s390, linux-ia64, Len Brown, linux-acpi, linux-sh, x86,
linux-kernel, cmetcalf, Jianguo Wu, linux-mm, Yasuaki Ishimatsu,
paulus, Minchan Kim, KOSAKI Motohiro, David Rientjes, sparclinux,
Christoph Lameter, linuxppc-dev, Andrew Morton, Jiang Liu
In-Reply-To: <1351763083-7905-7-git-send-email-wency@cn.fujitsu.com>
On 11/01/2012 05:44 PM, Wen Congyang wrote:
> From: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
>
> Currently __remove_section for SPARSEMEM_VMEMMAP does nothing. But even if
> we use SPARSEMEM_VMEMMAP, we can unregister the memory_section.
>
> So the patch add unregister_memory_section() into __remove_section().
Hi Yasuaki,
I have a question about these sparse vmemmap memory related patches. Hot
add memory need allocated vmemmap pages, but this time is allocated by
buddy system. How can gurantee virtual address is continuous to the
address allocated before? If not continuous, page_to_pfn and pfn_to_page
can't work correctly.
Regards,
Jaegeuk
>
> CC: David Rientjes <rientjes@google.com>
> CC: Jiang Liu <liuj97@gmail.com>
> CC: Len Brown <len.brown@intel.com>
> CC: Christoph Lameter <cl@linux.com>
> Cc: Minchan Kim <minchan.kim@gmail.com>
> CC: Andrew Morton <akpm@linux-foundation.org>
> CC: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
> CC: Wen Congyang <wency@cn.fujitsu.com>
> Signed-off-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
> ---
> mm/memory_hotplug.c | 13 ++++++++-----
> 1 file changed, 8 insertions(+), 5 deletions(-)
>
> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
> index ca07433..66a79a7 100644
> --- a/mm/memory_hotplug.c
> +++ b/mm/memory_hotplug.c
> @@ -286,11 +286,14 @@ static int __meminit __add_section(int nid, struct zone *zone,
> #ifdef CONFIG_SPARSEMEM_VMEMMAP
> static int __remove_section(struct zone *zone, struct mem_section *ms)
> {
> - /*
> - * XXX: Freeing memmap with vmemmap is not implement yet.
> - * This should be removed later.
> - */
> - return -EBUSY;
> + int ret = -EINVAL;
> +
> + if (!valid_section(ms))
> + return ret;
> +
> + ret = unregister_memory_section(ms);
> +
> + return ret;
> }
> #else
> static int __remove_section(struct zone *zone, struct mem_section *ms)
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox