* [PATCH v2] Documentation: dt: reset: Revise typos in TI syscon reset example
From: Philipp Zabel @ 2017-01-12 10:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170112012217.18115-1-s-anna@ti.com>
Am Mittwoch, den 11.01.2017, 19:22 -0600 schrieb Suman Anna:
> Fix couple of typos in the example given in the TI syscon reset
> binding. The ti,reset-bits used for DSP0 are corrected to match
> the values that will be used in the actual DT node.
>
> Signed-off-by: Suman Anna <s-anna@ti.com>
> ---
> v2: Address Rob Herring's comment to change the reset node name
> from "psc-reset" to "reset-controller"
I've applied the patch, thank you.
regards
Philipp
^ permalink raw reply
* kvm: deadlock in kvm_vgic_map_resources
From: Andre Przywara @ 2017-01-12 10:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170112104253.GB13547@cbox>
Hi,
On 12/01/17 10:42, Christoffer Dall wrote:
> On Thu, Jan 12, 2017 at 10:30:39AM +0000, Marc Zyngier wrote:
>> On 12/01/17 09:55, Andre Przywara wrote:
>>> Hi,
>>>
>>> On 12/01/17 09:32, Marc Zyngier wrote:
>>>> Hi Dmitry,
>>>>
>>>> On 11/01/17 19:01, Dmitry Vyukov wrote:
>>>>> Hello,
>>>>>
>>>>> While running syzkaller fuzzer I've got the following deadlock.
>>>>> On commit 9c763584b7c8911106bb77af7e648bef09af9d80.
>>>>>
>>>>>
>>>>> =============================================
>>>>> [ INFO: possible recursive locking detected ]
>>>>> 4.9.0-rc6-xc2-00056-g08372dd4b91d-dirty #50 Not tainted
>>>>> ---------------------------------------------
>>>>> syz-executor/20805 is trying to acquire lock:
>>>>> (
>>>>> &kvm->lock
>>>>> ){+.+.+.}
>>>>> , at:
>>>>> [< inline >] kvm_vgic_dist_destroy
>>>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:271
>>>>> [<ffff2000080ea4bc>] kvm_vgic_destroy+0x34/0x250
>>>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:294
>>>>> but task is already holding lock:
>>>>> (&kvm->lock){+.+.+.}, at:
>>>>> [<ffff2000080ea7e4>] kvm_vgic_map_resources+0x2c/0x108
>>>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:343
>>>>> other info that might help us debug this:
>>>>> Possible unsafe locking scenario:
>>>>> CPU0
>>>>> ----
>>>>> lock(&kvm->lock);
>>>>> lock(&kvm->lock);
>>>>> *** DEADLOCK ***
>>>>> May be due to missing lock nesting notation
>>>>> 2 locks held by syz-executor/20805:
>>>>> #0:(&vcpu->mutex){+.+.+.}, at:
>>>>> [<ffff2000080bcc30>] vcpu_load+0x28/0x1d0
>>>>> arch/arm64/kvm/../../../virt/kvm/kvm_main.c:143
>>>>> #1:(&kvm->lock){+.+.+.}, at:
>>>>> [<ffff2000080ea7e4>] kvm_vgic_map_resources+0x2c/0x108
>>>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:343
>>>>> stack backtrace:
>>>>> CPU: 2 PID: 20805 Comm: syz-executor Not tainted
>>>>> 4.9.0-rc6-xc2-00056-g08372dd4b91d-dirty #50
>>>>> Hardware name: Hardkernel ODROID-C2 (DT)
>>>>> Call trace:
>>>>> [<ffff200008090560>] dump_backtrace+0x0/0x3c8 arch/arm64/kernel/traps.c:69
>>>>> [<ffff200008090948>] show_stack+0x20/0x30 arch/arm64/kernel/traps.c:219
>>>>> [< inline >] __dump_stack lib/dump_stack.c:15
>>>>> [<ffff200008895840>] dump_stack+0x100/0x150 lib/dump_stack.c:51
>>>>> [< inline >] print_deadlock_bug kernel/locking/lockdep.c:1728
>>>>> [< inline >] check_deadlock kernel/locking/lockdep.c:1772
>>>>> [< inline >] validate_chain kernel/locking/lockdep.c:2250
>>>>> [<ffff2000081c8718>] __lock_acquire+0x1938/0x3440 kernel/locking/lockdep.c:3335
>>>>> [<ffff2000081caa84>] lock_acquire+0xdc/0x1d8 kernel/locking/lockdep.c:3746
>>>>> [< inline >] __mutex_lock_common kernel/locking/mutex.c:521
>>>>> [<ffff200009700004>] mutex_lock_nested+0xdc/0x7b8 kernel/locking/mutex.c:621
>>>>> [< inline >] kvm_vgic_dist_destroy
>>>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:271
>>>>> [<ffff2000080ea4bc>] kvm_vgic_destroy+0x34/0x250
>>>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:294
>>>>> [<ffff2000080ec290>] vgic_v2_map_resources+0x218/0x430
>>>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-v2.c:295
>>>>> [<ffff2000080ea884>] kvm_vgic_map_resources+0xcc/0x108
>>>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:348
>>>>> [< inline >] kvm_vcpu_first_run_init
>>>>> arch/arm64/kvm/../../../arch/arm/kvm/arm.c:505
>>>>> [<ffff2000080d2768>] kvm_arch_vcpu_ioctl_run+0xab8/0xce0
>>>>> arch/arm64/kvm/../../../arch/arm/kvm/arm.c:591
>>>>> [<ffff2000080c1fec>] kvm_vcpu_ioctl+0x434/0xc08
>>>>> arch/arm64/kvm/../../../virt/kvm/kvm_main.c:2557
>>>>> [< inline >] vfs_ioctl fs/ioctl.c:43
>>>>> [<ffff200008450c38>] do_vfs_ioctl+0x128/0xfc0 fs/ioctl.c:679
>>>>> [< inline >] SYSC_ioctl fs/ioctl.c:694
>>>>> [<ffff200008451b78>] SyS_ioctl+0xa8/0xb8 fs/ioctl.c:685
>>>>> [<ffff200008083ef0>] el0_svc_naked+0x24/0x28 arch/arm64/kernel/entry.S:755
>>>>
>>>> Nice catch, and many thanks for reporting this.
>>>>
>>>> The bug is fairly obvious. Christoffer, what do you think? I don't think
>>>> we need to hold the kvm->lock all the way, but I'd like another pair of
>>>> eyes (the coffee machine is out of order again, and tea doesn't cut it).
>>>>
>>>> Thanks,
>>>>
>>>> M.
>>>>
>>>> From 93f80b20fb9351a49ee8b74eed3fc59c84651371 Mon Sep 17 00:00:00 2001
>>>> From: Marc Zyngier <marc.zyngier@arm.com>
>>>> Date: Thu, 12 Jan 2017 09:21:56 +0000
>>>> Subject: [PATCH] KVM: arm/arm64: vgic: Fix deadlock on error handling
>>>>
>>>> Dmitry Vyukov reported that the syzkaller fuzzer triggered a
>>>> deadlock in the vgic setup code when an error was detected, as
>>>> the cleanup code tries to take a lock that is already held by
>>>> the setup code.
>>>>
>>>> The fix is pretty obvious: move the cleaup call after having
>>>> dropped the lock, since not much can happen at that point.
>>> ^^^^^^^^
>>> Is that really true? If for instance the calls to
>>> vgic_register_dist_iodev() or kvm_phys_addr_ioremap() in
>>> vgic_v2_map_resources() fail, we leave the function with a half
>>> initialized VGIC (because vgic_init() succeeded).
>>
>> But we only set dist->ready to true when everything went OK. How is
>> that an issue?
>>
>>> Dropping the lock at
>>> this point without having the GIC cleaned up before sounds a bit
>>> suspicious (I may be wrong on this, though).
>>
>> Thinking of it, that may open a race with vgic init call, leading to
>> leaking distributor memory.
>>
>>>
>>> Can't we just document that kvm_vgic_destroy() needs to be called with
>>> the kvm->lock held and take the lock around the only other caller
>>> (kvm_arch_destroy_vm() in arch/arm/kvm/arm.c)?
>>> We can then keep holding the lock in the map_resources calls.
>>> Though we might still move the calls to kvm_vgic_destroy() into the
>>> wrapper function as a cleanup (as shown below), just before dropping the
>>> lock.
>>
>> I'd rather keep the changes limited to the vgic code, and save myself
>> having to document more locking (we already have our fair share here).
>> How about this (untested):
>>
>> From 24dc3f5750da20d89e0ce9b7855d125d0100bee8 Mon Sep 17 00:00:00 2001
>> From: Marc Zyngier <marc.zyngier@arm.com>
>> Date: Thu, 12 Jan 2017 09:21:56 +0000
>> Subject: [PATCH] KVM: arm/arm64: vgic: Fix deadlock on error handling
>>
>> Dmitry Vyukov reported that the syzkaller fuzzer triggered a
>> deadlock in the vgic setup code when an error was detected, as
>> the cleanup code tries to take a lock that is already held by
>> the setup code.
>>
>> The fix is to avoid retaking the lock when cleaning up, by
>> telling the cleanup function that we already hold it.
>>
>> Cc: stable at vger.kernel.org
>> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
>> ---
>> virt/kvm/arm/vgic/vgic-init.c | 21 ++++++++++++++++-----
>> virt/kvm/arm/vgic/vgic-v2.c | 2 --
>> virt/kvm/arm/vgic/vgic-v3.c | 2 --
>> 3 files changed, 16 insertions(+), 9 deletions(-)
>>
>> diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
>> index 5114391..30d74e2 100644
>> --- a/virt/kvm/arm/vgic/vgic-init.c
>> +++ b/virt/kvm/arm/vgic/vgic-init.c
>> @@ -264,11 +264,12 @@ int vgic_init(struct kvm *kvm)
>> return ret;
>> }
>>
>> -static void kvm_vgic_dist_destroy(struct kvm *kvm)
>> +static void kvm_vgic_dist_destroy(struct kvm *kvm, bool locked)
>> {
>> struct vgic_dist *dist = &kvm->arch.vgic;
>>
>> - mutex_lock(&kvm->lock);
>> + if (!locked)
>> + mutex_lock(&kvm->lock);
>
> Hmm, not a fan of passing this variable around. How about this instead
> then (untested):
Yes, I like that version more.
And if we now move the calls to __kvm_vgic_destroy() into vgic-init.c
(as in Marc's first patch, but just before dropping the lock), we don't
even need to export __kvm_vgic_destroy(), right?
Cheers,
Andre.
> diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
> index 5114391..a25806b 100644
> --- a/virt/kvm/arm/vgic/vgic-init.c
> +++ b/virt/kvm/arm/vgic/vgic-init.c
> @@ -264,19 +264,16 @@ int vgic_init(struct kvm *kvm)
> return ret;
> }
>
> +/* Must be called with the kvm->lock held */
> static void kvm_vgic_dist_destroy(struct kvm *kvm)
> {
> struct vgic_dist *dist = &kvm->arch.vgic;
>
> - mutex_lock(&kvm->lock);
> -
> dist->ready = false;
> dist->initialized = false;
>
> kfree(dist->spis);
> dist->nr_spis = 0;
> -
> - mutex_unlock(&kvm->lock);
> }
>
> void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)
> @@ -286,7 +283,7 @@ void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)
> INIT_LIST_HEAD(&vgic_cpu->ap_list_head);
> }
>
> -void kvm_vgic_destroy(struct kvm *kvm)
> +void __kvm_vgic_destroy(struct kvm *kvm)
> {
> struct kvm_vcpu *vcpu;
> int i;
> @@ -297,6 +294,13 @@ void kvm_vgic_destroy(struct kvm *kvm)
> kvm_vgic_vcpu_destroy(vcpu);
> }
>
> +void kvm_vgic_destroy(struct kvm *kvm)
> +{
> + mutex_lock(&kvm->lock);
> + __kvm_vgic_destroy(kvm);
> + mutex_unlock(&kvm->lock);
> +}
> +
> /**
> * vgic_lazy_init: Lazy init is only allowed if the GIC exposed to the guest
> * is a GICv2. A GICv3 must be explicitly initialized by the guest using the
> diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
> index 9bab867..c6f7ec7 100644
> --- a/virt/kvm/arm/vgic/vgic-v2.c
> +++ b/virt/kvm/arm/vgic/vgic-v2.c
> @@ -294,7 +294,7 @@ int vgic_v2_map_resources(struct kvm *kvm)
>
> out:
> if (ret)
> - kvm_vgic_destroy(kvm);
> + __kvm_vgic_destroy(kvm);
> return ret;
> }
>
> diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
> index 5c9f974..f1c7819 100644
> --- a/virt/kvm/arm/vgic/vgic-v3.c
> +++ b/virt/kvm/arm/vgic/vgic-v3.c
> @@ -303,7 +303,7 @@ int vgic_v3_map_resources(struct kvm *kvm)
>
> out:
> if (ret)
> - kvm_vgic_destroy(kvm);
> + __kvm_vgic_destroy(kvm);
> return ret;
> }
>
> diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
> index 859f65c..74a0bbb 100644
> --- a/virt/kvm/arm/vgic/vgic.h
> +++ b/virt/kvm/arm/vgic/vgic.h
> @@ -37,6 +37,8 @@ struct vgic_vmcr {
> u32 pmr;
> };
>
> +void __kvm_vgic_destroy(struct kvm *kvm);
> +
> struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
> u32 intid);
> void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq);
>
>
> Thanks,
> -Christoffer
>
^ permalink raw reply
* kvm: deadlock in kvm_vgic_map_resources
From: Marc Zyngier @ 2017-01-12 10:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170112104253.GB13547@cbox>
On 12/01/17 10:42, Christoffer Dall wrote:
> On Thu, Jan 12, 2017 at 10:30:39AM +0000, Marc Zyngier wrote:
>> On 12/01/17 09:55, Andre Przywara wrote:
>>> Hi,
>>>
>>> On 12/01/17 09:32, Marc Zyngier wrote:
>>>> Hi Dmitry,
>>>>
>>>> On 11/01/17 19:01, Dmitry Vyukov wrote:
>>>>> Hello,
>>>>>
>>>>> While running syzkaller fuzzer I've got the following deadlock.
>>>>> On commit 9c763584b7c8911106bb77af7e648bef09af9d80.
>>>>>
>>>>>
>>>>> =============================================
>>>>> [ INFO: possible recursive locking detected ]
>>>>> 4.9.0-rc6-xc2-00056-g08372dd4b91d-dirty #50 Not tainted
>>>>> ---------------------------------------------
>>>>> syz-executor/20805 is trying to acquire lock:
>>>>> (
>>>>> &kvm->lock
>>>>> ){+.+.+.}
>>>>> , at:
>>>>> [< inline >] kvm_vgic_dist_destroy
>>>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:271
>>>>> [<ffff2000080ea4bc>] kvm_vgic_destroy+0x34/0x250
>>>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:294
>>>>> but task is already holding lock:
>>>>> (&kvm->lock){+.+.+.}, at:
>>>>> [<ffff2000080ea7e4>] kvm_vgic_map_resources+0x2c/0x108
>>>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:343
>>>>> other info that might help us debug this:
>>>>> Possible unsafe locking scenario:
>>>>> CPU0
>>>>> ----
>>>>> lock(&kvm->lock);
>>>>> lock(&kvm->lock);
>>>>> *** DEADLOCK ***
>>>>> May be due to missing lock nesting notation
>>>>> 2 locks held by syz-executor/20805:
>>>>> #0:(&vcpu->mutex){+.+.+.}, at:
>>>>> [<ffff2000080bcc30>] vcpu_load+0x28/0x1d0
>>>>> arch/arm64/kvm/../../../virt/kvm/kvm_main.c:143
>>>>> #1:(&kvm->lock){+.+.+.}, at:
>>>>> [<ffff2000080ea7e4>] kvm_vgic_map_resources+0x2c/0x108
>>>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:343
>>>>> stack backtrace:
>>>>> CPU: 2 PID: 20805 Comm: syz-executor Not tainted
>>>>> 4.9.0-rc6-xc2-00056-g08372dd4b91d-dirty #50
>>>>> Hardware name: Hardkernel ODROID-C2 (DT)
>>>>> Call trace:
>>>>> [<ffff200008090560>] dump_backtrace+0x0/0x3c8 arch/arm64/kernel/traps.c:69
>>>>> [<ffff200008090948>] show_stack+0x20/0x30 arch/arm64/kernel/traps.c:219
>>>>> [< inline >] __dump_stack lib/dump_stack.c:15
>>>>> [<ffff200008895840>] dump_stack+0x100/0x150 lib/dump_stack.c:51
>>>>> [< inline >] print_deadlock_bug kernel/locking/lockdep.c:1728
>>>>> [< inline >] check_deadlock kernel/locking/lockdep.c:1772
>>>>> [< inline >] validate_chain kernel/locking/lockdep.c:2250
>>>>> [<ffff2000081c8718>] __lock_acquire+0x1938/0x3440 kernel/locking/lockdep.c:3335
>>>>> [<ffff2000081caa84>] lock_acquire+0xdc/0x1d8 kernel/locking/lockdep.c:3746
>>>>> [< inline >] __mutex_lock_common kernel/locking/mutex.c:521
>>>>> [<ffff200009700004>] mutex_lock_nested+0xdc/0x7b8 kernel/locking/mutex.c:621
>>>>> [< inline >] kvm_vgic_dist_destroy
>>>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:271
>>>>> [<ffff2000080ea4bc>] kvm_vgic_destroy+0x34/0x250
>>>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:294
>>>>> [<ffff2000080ec290>] vgic_v2_map_resources+0x218/0x430
>>>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-v2.c:295
>>>>> [<ffff2000080ea884>] kvm_vgic_map_resources+0xcc/0x108
>>>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:348
>>>>> [< inline >] kvm_vcpu_first_run_init
>>>>> arch/arm64/kvm/../../../arch/arm/kvm/arm.c:505
>>>>> [<ffff2000080d2768>] kvm_arch_vcpu_ioctl_run+0xab8/0xce0
>>>>> arch/arm64/kvm/../../../arch/arm/kvm/arm.c:591
>>>>> [<ffff2000080c1fec>] kvm_vcpu_ioctl+0x434/0xc08
>>>>> arch/arm64/kvm/../../../virt/kvm/kvm_main.c:2557
>>>>> [< inline >] vfs_ioctl fs/ioctl.c:43
>>>>> [<ffff200008450c38>] do_vfs_ioctl+0x128/0xfc0 fs/ioctl.c:679
>>>>> [< inline >] SYSC_ioctl fs/ioctl.c:694
>>>>> [<ffff200008451b78>] SyS_ioctl+0xa8/0xb8 fs/ioctl.c:685
>>>>> [<ffff200008083ef0>] el0_svc_naked+0x24/0x28 arch/arm64/kernel/entry.S:755
>>>>
>>>> Nice catch, and many thanks for reporting this.
>>>>
>>>> The bug is fairly obvious. Christoffer, what do you think? I don't think
>>>> we need to hold the kvm->lock all the way, but I'd like another pair of
>>>> eyes (the coffee machine is out of order again, and tea doesn't cut it).
>>>>
>>>> Thanks,
>>>>
>>>> M.
>>>>
>>>> From 93f80b20fb9351a49ee8b74eed3fc59c84651371 Mon Sep 17 00:00:00 2001
>>>> From: Marc Zyngier <marc.zyngier@arm.com>
>>>> Date: Thu, 12 Jan 2017 09:21:56 +0000
>>>> Subject: [PATCH] KVM: arm/arm64: vgic: Fix deadlock on error handling
>>>>
>>>> Dmitry Vyukov reported that the syzkaller fuzzer triggered a
>>>> deadlock in the vgic setup code when an error was detected, as
>>>> the cleanup code tries to take a lock that is already held by
>>>> the setup code.
>>>>
>>>> The fix is pretty obvious: move the cleaup call after having
>>>> dropped the lock, since not much can happen at that point.
>>> ^^^^^^^^
>>> Is that really true? If for instance the calls to
>>> vgic_register_dist_iodev() or kvm_phys_addr_ioremap() in
>>> vgic_v2_map_resources() fail, we leave the function with a half
>>> initialized VGIC (because vgic_init() succeeded).
>>
>> But we only set dist->ready to true when everything went OK. How is
>> that an issue?
>>
>>> Dropping the lock at
>>> this point without having the GIC cleaned up before sounds a bit
>>> suspicious (I may be wrong on this, though).
>>
>> Thinking of it, that may open a race with vgic init call, leading to
>> leaking distributor memory.
>>
>>>
>>> Can't we just document that kvm_vgic_destroy() needs to be called with
>>> the kvm->lock held and take the lock around the only other caller
>>> (kvm_arch_destroy_vm() in arch/arm/kvm/arm.c)?
>>> We can then keep holding the lock in the map_resources calls.
>>> Though we might still move the calls to kvm_vgic_destroy() into the
>>> wrapper function as a cleanup (as shown below), just before dropping the
>>> lock.
>>
>> I'd rather keep the changes limited to the vgic code, and save myself
>> having to document more locking (we already have our fair share here).
>> How about this (untested):
>>
>> From 24dc3f5750da20d89e0ce9b7855d125d0100bee8 Mon Sep 17 00:00:00 2001
>> From: Marc Zyngier <marc.zyngier@arm.com>
>> Date: Thu, 12 Jan 2017 09:21:56 +0000
>> Subject: [PATCH] KVM: arm/arm64: vgic: Fix deadlock on error handling
>>
>> Dmitry Vyukov reported that the syzkaller fuzzer triggered a
>> deadlock in the vgic setup code when an error was detected, as
>> the cleanup code tries to take a lock that is already held by
>> the setup code.
>>
>> The fix is to avoid retaking the lock when cleaning up, by
>> telling the cleanup function that we already hold it.
>>
>> Cc: stable at vger.kernel.org
>> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
>> ---
>> virt/kvm/arm/vgic/vgic-init.c | 21 ++++++++++++++++-----
>> virt/kvm/arm/vgic/vgic-v2.c | 2 --
>> virt/kvm/arm/vgic/vgic-v3.c | 2 --
>> 3 files changed, 16 insertions(+), 9 deletions(-)
>>
>> diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
>> index 5114391..30d74e2 100644
>> --- a/virt/kvm/arm/vgic/vgic-init.c
>> +++ b/virt/kvm/arm/vgic/vgic-init.c
>> @@ -264,11 +264,12 @@ int vgic_init(struct kvm *kvm)
>> return ret;
>> }
>>
>> -static void kvm_vgic_dist_destroy(struct kvm *kvm)
>> +static void kvm_vgic_dist_destroy(struct kvm *kvm, bool locked)
>> {
>> struct vgic_dist *dist = &kvm->arch.vgic;
>>
>> - mutex_lock(&kvm->lock);
>> + if (!locked)
>> + mutex_lock(&kvm->lock);
>
> Hmm, not a fan of passing this variable around. How about this instead
> then (untested):
>
> diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
> index 5114391..a25806b 100644
> --- a/virt/kvm/arm/vgic/vgic-init.c
> +++ b/virt/kvm/arm/vgic/vgic-init.c
> @@ -264,19 +264,16 @@ int vgic_init(struct kvm *kvm)
> return ret;
> }
>
> +/* Must be called with the kvm->lock held */
> static void kvm_vgic_dist_destroy(struct kvm *kvm)
> {
> struct vgic_dist *dist = &kvm->arch.vgic;
>
> - mutex_lock(&kvm->lock);
> -
> dist->ready = false;
> dist->initialized = false;
>
> kfree(dist->spis);
> dist->nr_spis = 0;
> -
> - mutex_unlock(&kvm->lock);
> }
>
> void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)
> @@ -286,7 +283,7 @@ void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)
> INIT_LIST_HEAD(&vgic_cpu->ap_list_head);
> }
>
> -void kvm_vgic_destroy(struct kvm *kvm)
> +void __kvm_vgic_destroy(struct kvm *kvm)
> {
> struct kvm_vcpu *vcpu;
> int i;
> @@ -297,6 +294,13 @@ void kvm_vgic_destroy(struct kvm *kvm)
> kvm_vgic_vcpu_destroy(vcpu);
> }
>
> +void kvm_vgic_destroy(struct kvm *kvm)
> +{
> + mutex_lock(&kvm->lock);
> + __kvm_vgic_destroy(kvm);
> + mutex_unlock(&kvm->lock);
> +}
> +
I initially wrote that exactly, but ended up deciding against as it
changes the locking more than strictly necessary. On the other hand, I
think this looks better, so if everyone agrees I'll take that.
> /**
> * vgic_lazy_init: Lazy init is only allowed if the GIC exposed to the guest
> * is a GICv2. A GICv3 must be explicitly initialized by the guest using the
> diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
> index 9bab867..c6f7ec7 100644
> --- a/virt/kvm/arm/vgic/vgic-v2.c
> +++ b/virt/kvm/arm/vgic/vgic-v2.c
> @@ -294,7 +294,7 @@ int vgic_v2_map_resources(struct kvm *kvm)
>
> out:
> if (ret)
> - kvm_vgic_destroy(kvm);
> + __kvm_vgic_destroy(kvm);
> return ret;
> }
>
> diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
> index 5c9f974..f1c7819 100644
> --- a/virt/kvm/arm/vgic/vgic-v3.c
> +++ b/virt/kvm/arm/vgic/vgic-v3.c
> @@ -303,7 +303,7 @@ int vgic_v3_map_resources(struct kvm *kvm)
>
> out:
> if (ret)
> - kvm_vgic_destroy(kvm);
> + __kvm_vgic_destroy(kvm);
I'm still keen on factoring the destroy calls in the calling function.
Is there any reason why we wouldn't do it?
Thanks,
M.
--
Jazz is not dead. It just smells funny...
^ permalink raw reply
* kvm: deadlock in kvm_vgic_map_resources
From: Christoffer Dall @ 2017-01-12 10:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <0994d5a9-d9f4-7b80-4b16-1797c093edb5@arm.com>
On Thu, Jan 12, 2017 at 10:30:39AM +0000, Marc Zyngier wrote:
> On 12/01/17 09:55, Andre Przywara wrote:
> > Hi,
> >
> > On 12/01/17 09:32, Marc Zyngier wrote:
> >> Hi Dmitry,
> >>
> >> On 11/01/17 19:01, Dmitry Vyukov wrote:
> >>> Hello,
> >>>
> >>> While running syzkaller fuzzer I've got the following deadlock.
> >>> On commit 9c763584b7c8911106bb77af7e648bef09af9d80.
> >>>
> >>>
> >>> =============================================
> >>> [ INFO: possible recursive locking detected ]
> >>> 4.9.0-rc6-xc2-00056-g08372dd4b91d-dirty #50 Not tainted
> >>> ---------------------------------------------
> >>> syz-executor/20805 is trying to acquire lock:
> >>> (
> >>> &kvm->lock
> >>> ){+.+.+.}
> >>> , at:
> >>> [< inline >] kvm_vgic_dist_destroy
> >>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:271
> >>> [<ffff2000080ea4bc>] kvm_vgic_destroy+0x34/0x250
> >>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:294
> >>> but task is already holding lock:
> >>> (&kvm->lock){+.+.+.}, at:
> >>> [<ffff2000080ea7e4>] kvm_vgic_map_resources+0x2c/0x108
> >>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:343
> >>> other info that might help us debug this:
> >>> Possible unsafe locking scenario:
> >>> CPU0
> >>> ----
> >>> lock(&kvm->lock);
> >>> lock(&kvm->lock);
> >>> *** DEADLOCK ***
> >>> May be due to missing lock nesting notation
> >>> 2 locks held by syz-executor/20805:
> >>> #0:(&vcpu->mutex){+.+.+.}, at:
> >>> [<ffff2000080bcc30>] vcpu_load+0x28/0x1d0
> >>> arch/arm64/kvm/../../../virt/kvm/kvm_main.c:143
> >>> #1:(&kvm->lock){+.+.+.}, at:
> >>> [<ffff2000080ea7e4>] kvm_vgic_map_resources+0x2c/0x108
> >>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:343
> >>> stack backtrace:
> >>> CPU: 2 PID: 20805 Comm: syz-executor Not tainted
> >>> 4.9.0-rc6-xc2-00056-g08372dd4b91d-dirty #50
> >>> Hardware name: Hardkernel ODROID-C2 (DT)
> >>> Call trace:
> >>> [<ffff200008090560>] dump_backtrace+0x0/0x3c8 arch/arm64/kernel/traps.c:69
> >>> [<ffff200008090948>] show_stack+0x20/0x30 arch/arm64/kernel/traps.c:219
> >>> [< inline >] __dump_stack lib/dump_stack.c:15
> >>> [<ffff200008895840>] dump_stack+0x100/0x150 lib/dump_stack.c:51
> >>> [< inline >] print_deadlock_bug kernel/locking/lockdep.c:1728
> >>> [< inline >] check_deadlock kernel/locking/lockdep.c:1772
> >>> [< inline >] validate_chain kernel/locking/lockdep.c:2250
> >>> [<ffff2000081c8718>] __lock_acquire+0x1938/0x3440 kernel/locking/lockdep.c:3335
> >>> [<ffff2000081caa84>] lock_acquire+0xdc/0x1d8 kernel/locking/lockdep.c:3746
> >>> [< inline >] __mutex_lock_common kernel/locking/mutex.c:521
> >>> [<ffff200009700004>] mutex_lock_nested+0xdc/0x7b8 kernel/locking/mutex.c:621
> >>> [< inline >] kvm_vgic_dist_destroy
> >>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:271
> >>> [<ffff2000080ea4bc>] kvm_vgic_destroy+0x34/0x250
> >>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:294
> >>> [<ffff2000080ec290>] vgic_v2_map_resources+0x218/0x430
> >>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-v2.c:295
> >>> [<ffff2000080ea884>] kvm_vgic_map_resources+0xcc/0x108
> >>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:348
> >>> [< inline >] kvm_vcpu_first_run_init
> >>> arch/arm64/kvm/../../../arch/arm/kvm/arm.c:505
> >>> [<ffff2000080d2768>] kvm_arch_vcpu_ioctl_run+0xab8/0xce0
> >>> arch/arm64/kvm/../../../arch/arm/kvm/arm.c:591
> >>> [<ffff2000080c1fec>] kvm_vcpu_ioctl+0x434/0xc08
> >>> arch/arm64/kvm/../../../virt/kvm/kvm_main.c:2557
> >>> [< inline >] vfs_ioctl fs/ioctl.c:43
> >>> [<ffff200008450c38>] do_vfs_ioctl+0x128/0xfc0 fs/ioctl.c:679
> >>> [< inline >] SYSC_ioctl fs/ioctl.c:694
> >>> [<ffff200008451b78>] SyS_ioctl+0xa8/0xb8 fs/ioctl.c:685
> >>> [<ffff200008083ef0>] el0_svc_naked+0x24/0x28 arch/arm64/kernel/entry.S:755
> >>
> >> Nice catch, and many thanks for reporting this.
> >>
> >> The bug is fairly obvious. Christoffer, what do you think? I don't think
> >> we need to hold the kvm->lock all the way, but I'd like another pair of
> >> eyes (the coffee machine is out of order again, and tea doesn't cut it).
> >>
> >> Thanks,
> >>
> >> M.
> >>
> >> From 93f80b20fb9351a49ee8b74eed3fc59c84651371 Mon Sep 17 00:00:00 2001
> >> From: Marc Zyngier <marc.zyngier@arm.com>
> >> Date: Thu, 12 Jan 2017 09:21:56 +0000
> >> Subject: [PATCH] KVM: arm/arm64: vgic: Fix deadlock on error handling
> >>
> >> Dmitry Vyukov reported that the syzkaller fuzzer triggered a
> >> deadlock in the vgic setup code when an error was detected, as
> >> the cleanup code tries to take a lock that is already held by
> >> the setup code.
> >>
> >> The fix is pretty obvious: move the cleaup call after having
> >> dropped the lock, since not much can happen at that point.
> > ^^^^^^^^
> > Is that really true? If for instance the calls to
> > vgic_register_dist_iodev() or kvm_phys_addr_ioremap() in
> > vgic_v2_map_resources() fail, we leave the function with a half
> > initialized VGIC (because vgic_init() succeeded).
>
> But we only set dist->ready to true when everything went OK. How is
> that an issue?
>
> > Dropping the lock at
> > this point without having the GIC cleaned up before sounds a bit
> > suspicious (I may be wrong on this, though).
>
> Thinking of it, that may open a race with vgic init call, leading to
> leaking distributor memory.
>
> >
> > Can't we just document that kvm_vgic_destroy() needs to be called with
> > the kvm->lock held and take the lock around the only other caller
> > (kvm_arch_destroy_vm() in arch/arm/kvm/arm.c)?
> > We can then keep holding the lock in the map_resources calls.
> > Though we might still move the calls to kvm_vgic_destroy() into the
> > wrapper function as a cleanup (as shown below), just before dropping the
> > lock.
>
> I'd rather keep the changes limited to the vgic code, and save myself
> having to document more locking (we already have our fair share here).
> How about this (untested):
>
> From 24dc3f5750da20d89e0ce9b7855d125d0100bee8 Mon Sep 17 00:00:00 2001
> From: Marc Zyngier <marc.zyngier@arm.com>
> Date: Thu, 12 Jan 2017 09:21:56 +0000
> Subject: [PATCH] KVM: arm/arm64: vgic: Fix deadlock on error handling
>
> Dmitry Vyukov reported that the syzkaller fuzzer triggered a
> deadlock in the vgic setup code when an error was detected, as
> the cleanup code tries to take a lock that is already held by
> the setup code.
>
> The fix is to avoid retaking the lock when cleaning up, by
> telling the cleanup function that we already hold it.
>
> Cc: stable at vger.kernel.org
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
> ---
> virt/kvm/arm/vgic/vgic-init.c | 21 ++++++++++++++++-----
> virt/kvm/arm/vgic/vgic-v2.c | 2 --
> virt/kvm/arm/vgic/vgic-v3.c | 2 --
> 3 files changed, 16 insertions(+), 9 deletions(-)
>
> diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
> index 5114391..30d74e2 100644
> --- a/virt/kvm/arm/vgic/vgic-init.c
> +++ b/virt/kvm/arm/vgic/vgic-init.c
> @@ -264,11 +264,12 @@ int vgic_init(struct kvm *kvm)
> return ret;
> }
>
> -static void kvm_vgic_dist_destroy(struct kvm *kvm)
> +static void kvm_vgic_dist_destroy(struct kvm *kvm, bool locked)
> {
> struct vgic_dist *dist = &kvm->arch.vgic;
>
> - mutex_lock(&kvm->lock);
> + if (!locked)
> + mutex_lock(&kvm->lock);
Hmm, not a fan of passing this variable around. How about this instead
then (untested):
diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
index 5114391..a25806b 100644
--- a/virt/kvm/arm/vgic/vgic-init.c
+++ b/virt/kvm/arm/vgic/vgic-init.c
@@ -264,19 +264,16 @@ int vgic_init(struct kvm *kvm)
return ret;
}
+/* Must be called with the kvm->lock held */
static void kvm_vgic_dist_destroy(struct kvm *kvm)
{
struct vgic_dist *dist = &kvm->arch.vgic;
- mutex_lock(&kvm->lock);
-
dist->ready = false;
dist->initialized = false;
kfree(dist->spis);
dist->nr_spis = 0;
-
- mutex_unlock(&kvm->lock);
}
void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)
@@ -286,7 +283,7 @@ void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)
INIT_LIST_HEAD(&vgic_cpu->ap_list_head);
}
-void kvm_vgic_destroy(struct kvm *kvm)
+void __kvm_vgic_destroy(struct kvm *kvm)
{
struct kvm_vcpu *vcpu;
int i;
@@ -297,6 +294,13 @@ void kvm_vgic_destroy(struct kvm *kvm)
kvm_vgic_vcpu_destroy(vcpu);
}
+void kvm_vgic_destroy(struct kvm *kvm)
+{
+ mutex_lock(&kvm->lock);
+ __kvm_vgic_destroy(kvm);
+ mutex_unlock(&kvm->lock);
+}
+
/**
* vgic_lazy_init: Lazy init is only allowed if the GIC exposed to the guest
* is a GICv2. A GICv3 must be explicitly initialized by the guest using the
diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
index 9bab867..c6f7ec7 100644
--- a/virt/kvm/arm/vgic/vgic-v2.c
+++ b/virt/kvm/arm/vgic/vgic-v2.c
@@ -294,7 +294,7 @@ int vgic_v2_map_resources(struct kvm *kvm)
out:
if (ret)
- kvm_vgic_destroy(kvm);
+ __kvm_vgic_destroy(kvm);
return ret;
}
diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
index 5c9f974..f1c7819 100644
--- a/virt/kvm/arm/vgic/vgic-v3.c
+++ b/virt/kvm/arm/vgic/vgic-v3.c
@@ -303,7 +303,7 @@ int vgic_v3_map_resources(struct kvm *kvm)
out:
if (ret)
- kvm_vgic_destroy(kvm);
+ __kvm_vgic_destroy(kvm);
return ret;
}
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index 859f65c..74a0bbb 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -37,6 +37,8 @@ struct vgic_vmcr {
u32 pmr;
};
+void __kvm_vgic_destroy(struct kvm *kvm);
+
struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
u32 intid);
void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq);
Thanks,
-Christoffer
^ permalink raw reply related
* [PATCH 1/2] mmc: mediatek: Use data tune for CMD line tune
From: Ulf Hansson @ 2017-01-12 10:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484215490-7494-2-git-send-email-yong.mao@mediatek.com>
On 12 January 2017 at 11:04, Yong Mao <yong.mao@mediatek.com> wrote:
> From: yong mao <yong.mao@mediatek.com>
>
> CMD response CRC error may cause cannot boot up
> Change to use data tune for CMD line
> Separate cmd internal delay for HS200/HS400 mode
Please try to work a little bit on improving the change log. Moreover
as this is a fix for a regression (it seems like so?), please try to
make that clear.
>
> Signed-off-by: Yong Mao <yong.mao@mediatek.com>
> Signed-off-by: Chaotian Jing <chaotian.jing@mediatek.com>
> ---
> arch/arm64/boot/dts/mediatek/mt8173-evb.dts | 3 +
Changes to the DTS files should be a separate change. Please split it
into its own patch.
> drivers/mmc/host/mtk-sd.c | 169 +++++++++++++++++++++++----
> 2 files changed, 149 insertions(+), 23 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
> index 0ecaad4..29c3100 100644
> --- a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
> +++ b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
> @@ -134,6 +134,9 @@
> bus-width = <8>;
> max-frequency = <50000000>;
> cap-mmc-highspeed;
> + hs200-cmd-int-delay = <26>;
> + hs400-cmd-int-delay = <14>;
> + cmd-resp-sel = <0>; /* 0: rising, 1: falling */
> vmmc-supply = <&mt6397_vemc_3v3_reg>;
> vqmmc-supply = <&mt6397_vio18_reg>;
> non-removable;
> diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
> index 80ba034..93eb395 100644
> --- a/drivers/mmc/host/mtk-sd.c
> +++ b/drivers/mmc/host/mtk-sd.c
> @@ -75,6 +75,7 @@
> #define MSDC_PATCH_BIT1 0xb4
> #define MSDC_PAD_TUNE 0xec
> #define PAD_DS_TUNE 0x188
> +#define PAD_CMD_TUNE 0x18c
> #define EMMC50_CFG0 0x208
>
> /*--------------------------------------------------------------------------*/
> @@ -210,12 +211,17 @@
> #define MSDC_PATCH_BIT_SPCPUSH (0x1 << 29) /* RW */
> #define MSDC_PATCH_BIT_DECRCTMO (0x1 << 30) /* RW */
>
> -#define MSDC_PAD_TUNE_DATRRDLY (0x1f << 8) /* RW */
> -#define MSDC_PAD_TUNE_CMDRDLY (0x1f << 16) /* RW */
> +#define MSDC_PAD_TUNE_DATWRDLY (0x1f << 0) /* RW */
> +#define MSDC_PAD_TUNE_DATRRDLY (0x1f << 8) /* RW */
> +#define MSDC_PAD_TUNE_CMDRDLY (0x1f << 16) /* RW */
> +#define MSDC_PAD_TUNE_CMDRRDLY (0x1f << 22) /* RW */
Is there a white space change somewhere here? I don't see any changes
to MSDC_PAD_TUNE_DATRRDLY and MSDC_PAD_TUNE_CMDRDLY.
> +#define MSDC_PAD_TUNE_CLKTDLY (0x1f << 27) /* RW */
>
> -#define PAD_DS_TUNE_DLY1 (0x1f << 2) /* RW */
> -#define PAD_DS_TUNE_DLY2 (0x1f << 7) /* RW */
> -#define PAD_DS_TUNE_DLY3 (0x1f << 12) /* RW */
> +#define PAD_DS_TUNE_DLY1 (0x1f << 2) /* RW */
> +#define PAD_DS_TUNE_DLY2 (0x1f << 7) /* RW */
> +#define PAD_DS_TUNE_DLY3 (0x1f << 12) /* RW */
> +
Ditto.
> +#define PAD_CMD_TUNE_RX_DLY3 (0x1f << 1) /* RW */
>
> #define EMMC50_CFG_PADCMD_LATCHCK (0x1 << 0) /* RW */
> #define EMMC50_CFG_CRCSTS_EDGE (0x1 << 3) /* RW */
> @@ -236,7 +242,9 @@
> #define CMD_TIMEOUT (HZ/10 * 5) /* 100ms x5 */
> #define DAT_TIMEOUT (HZ * 5) /* 1000ms x5 */
>
> -#define PAD_DELAY_MAX 32 /* PAD delay cells */
> +#define PAD_DELAY_MAX 32 /* PAD delay cells */
Ditto.
> +#define ENOUGH_MARGIN_MIN 12 /* Enough Margin */
> +#define PREFER_START_POS_MAX 4 /* Prefer start position */
> /*--------------------------------------------------------------------------*/
> /* Descriptor Structure */
> /*--------------------------------------------------------------------------*/
> @@ -284,12 +292,14 @@ struct msdc_save_para {
> u32 patch_bit0;
> u32 patch_bit1;
> u32 pad_ds_tune;
> + u32 pad_cmd_tune;
> u32 emmc50_cfg0;
> };
>
> struct msdc_tune_para {
> u32 iocon;
> u32 pad_tune;
> + u32 pad_cmd_tune;
> };
>
> struct msdc_delay_phase {
> @@ -331,6 +341,9 @@ struct msdc_host {
> unsigned char timing;
> bool vqmmc_enabled;
> u32 hs400_ds_delay;
> + u32 hs200_cmd_int_delay; /* cmd internal delay for HS200/SDR104 */
> + u32 hs400_cmd_int_delay; /* cmd internal delay for HS400 */
> + u32 hs200_cmd_resp_sel; /* cmd response sample selection */
> bool hs400_mode; /* current eMMC will run at hs400 mode */
> struct msdc_save_para save_para; /* used when gate HCLK */
> struct msdc_tune_para def_tune_para; /* default tune setting */
> @@ -596,12 +609,21 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
> */
> if (host->sclk <= 52000000) {
> writel(host->def_tune_para.iocon, host->base + MSDC_IOCON);
> - writel(host->def_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
> + writel(host->def_tune_para.pad_tune,
> + host->base + MSDC_PAD_TUNE);
Please don't change code just because you feel like doing it. This is
a completely unessarry change and it makes it harder for me to review.
Can you go thorugh the complete patch and make sure to undo all
similar changes, there are more of them.
> } else {
> - writel(host->saved_tune_para.iocon, host->base + MSDC_IOCON);
> - writel(host->saved_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
> + writel(host->saved_tune_para.iocon,
> + host->base + MSDC_IOCON);
> + writel(host->saved_tune_para.pad_tune,
> + host->base + MSDC_PAD_TUNE);
> + writel(host->saved_tune_para.pad_cmd_tune,
> + host->base + PAD_CMD_TUNE);
> }
>
> + if (timing == MMC_TIMING_MMC_HS400)
> + sdr_set_field(host->base + PAD_CMD_TUNE,
> + MSDC_PAD_TUNE_CMDRRDLY,
> + host->hs400_cmd_int_delay);
> dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->sclk, timing);
> }
>
> @@ -1302,7 +1324,8 @@ static struct msdc_delay_phase get_best_delay(struct msdc_host *host, u32 delay)
> len_final = len;
> }
> start += len ? len : 1;
> - if (len >= 8 && start_final < 4)
> + if (len >= ENOUGH_MARGIN_MIN &&
> + start_final < PREFER_START_POS_MAX)
> break;
> }
>
> @@ -1325,48 +1348,128 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
> struct msdc_host *host = mmc_priv(mmc);
> u32 rise_delay = 0, fall_delay = 0;
> struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,};
> + struct msdc_delay_phase internal_delay_phase;
> u8 final_delay, final_maxlen;
> + u32 internal_delay = 0;
> int cmd_err;
> - int i;
> + int i, j;
>
> + if (mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
> + mmc->ios.timing == MMC_TIMING_UHS_SDR104)
> + sdr_set_field(host->base + MSDC_PAD_TUNE,
> + MSDC_PAD_TUNE_CMDRRDLY,
> + host->hs200_cmd_int_delay);
> sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
> for (i = 0 ; i < PAD_DELAY_MAX; i++) {
> sdr_set_field(host->base + MSDC_PAD_TUNE,
> MSDC_PAD_TUNE_CMDRDLY, i);
> - mmc_send_tuning(mmc, opcode, &cmd_err);
> - if (!cmd_err)
> - rise_delay |= (1 << i);
> + for (j = 0; j < 3; j++) {
Any reason to why looping three times makes sense? Maybe add a comment?
> + mmc_send_tuning(mmc, opcode, &cmd_err);
> + if (!cmd_err) {
> + rise_delay |= (1 << i);
> + } else {
> + rise_delay &= ~(1 << i);
> + break;
> + }
> + }
> }
> final_rise_delay = get_best_delay(host, rise_delay);
> /* if rising edge has enough margin, then do not scan falling edge */
> - if (final_rise_delay.maxlen >= 10 ||
> - (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
> + if (final_rise_delay.maxlen >= ENOUGH_MARGIN_MIN &&
> + final_rise_delay.start < PREFER_START_POS_MAX)
This looks like clean-ups, as you are converting from magic numbers to
defines. Please make this kind of changes separately.
> goto skip_fall;
>
> sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
> for (i = 0; i < PAD_DELAY_MAX; i++) {
> sdr_set_field(host->base + MSDC_PAD_TUNE,
> MSDC_PAD_TUNE_CMDRDLY, i);
> - mmc_send_tuning(mmc, opcode, &cmd_err);
> - if (!cmd_err)
> - fall_delay |= (1 << i);
> + for (j = 0; j < 3; j++) {
3?
> + mmc_send_tuning(mmc, opcode, &cmd_err);
> + if (!cmd_err) {
> + fall_delay |= (1 << i);
> + } else {
> + fall_delay &= ~(1 << i);
> + break;
> + };
> + }
> }
> final_fall_delay = get_best_delay(host, fall_delay);
>
> skip_fall:
> final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
> + if (final_fall_delay.maxlen >= ENOUGH_MARGIN_MIN &&
> + final_fall_delay.start < PREFER_START_POS_MAX)
> + final_maxlen = final_fall_delay.maxlen;
> if (final_maxlen == final_rise_delay.maxlen) {
> sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
> - sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
> + sdr_set_field(host->base + MSDC_PAD_TUNE,
> + MSDC_PAD_TUNE_CMDRDLY,
> final_rise_delay.final_phase);
> final_delay = final_rise_delay.final_phase;
> } else {
> sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
> - sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
> + sdr_set_field(host->base + MSDC_PAD_TUNE,
> + MSDC_PAD_TUNE_CMDRDLY,
> final_fall_delay.final_phase);
> final_delay = final_fall_delay.final_phase;
> }
> + if (host->hs200_cmd_int_delay)
> + goto skip_internal;
>
> + for (i = 0; i < PAD_DELAY_MAX; i++) {
> + sdr_set_field(host->base + MSDC_PAD_TUNE,
> + MSDC_PAD_TUNE_CMDRRDLY, i);
> + mmc_send_tuning(mmc, opcode, &cmd_err);
> + if (!cmd_err)
> + internal_delay |= (1 << i);
> + }
> + dev_info(host->dev, "Final internal delay: 0x%x\n", internal_delay);
I don't think dev_info() is what you want, right? Perhaps dev_dbg(),
anything at all.
> + internal_delay_phase = get_best_delay(host, internal_delay);
> + sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY,
> + internal_delay_phase.final_phase);
> +skip_internal:
> + dev_info(host->dev, "Final cmd pad delay: %x\n", final_delay);
I don't think dev_info() is what you want, right? Perhaps dev_dbg(),
anything at all.
> + return final_delay == 0xff ? -EIO : 0;
> +}
> +
> +static int hs400_tune_response(struct mmc_host *mmc, u32 opcode)
> +{
> + struct msdc_host *host = mmc_priv(mmc);
> + u32 cmd_delay = 0;
> + struct msdc_delay_phase final_cmd_delay = { 0,};
> + u8 final_delay;
> + int cmd_err;
> + int i, j;
> +
> + /* select EMMC50 PAD CMD tune */
> + sdr_set_bits(host->base + PAD_CMD_TUNE, BIT(0));
> +
> + if (mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
> + mmc->ios.timing == MMC_TIMING_UHS_SDR104)
> + sdr_set_field(host->base + MSDC_PAD_TUNE,
> + MSDC_PAD_TUNE_CMDRRDLY,
> + host->hs200_cmd_int_delay);
> + sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL,
> + host->hs200_cmd_resp_sel);
> + for (i = 0 ; i < PAD_DELAY_MAX; i++) {
> + sdr_set_field(host->base + PAD_CMD_TUNE,
> + PAD_CMD_TUNE_RX_DLY3, i);
> + for (j = 0; j < 3; j++) {
3?
> + mmc_send_tuning(mmc, opcode, &cmd_err);
> + if (!cmd_err) {
> + cmd_delay |= (1 << i);
> + } else {
> + cmd_delay &= ~(1 << i);
> + break;
> + }
> + }
> + }
> + final_cmd_delay = get_best_delay(host, cmd_delay);
> + sdr_set_field(host->base + PAD_CMD_TUNE, PAD_CMD_TUNE_RX_DLY3,
> + final_cmd_delay.final_phase);
> + final_delay = final_cmd_delay.final_phase;
> +
> + dev_info(host->dev, "Final cmd pad delay: %x\n", final_delay);
dev_info() -> dev_dbg() or remove it.
> return final_delay == 0xff ? -EIO : 0;
> }
>
> @@ -1389,7 +1492,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
> }
> final_rise_delay = get_best_delay(host, rise_delay);
> /* if rising edge has enough margin, then do not scan falling edge */
> - if (final_rise_delay.maxlen >= 10 ||
> + if (final_rise_delay.maxlen >= ENOUGH_MARGIN_MIN ||
Clean up. Move to separate change.
> (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
> goto skip_fall;
>
> @@ -1422,6 +1525,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
> final_delay = final_fall_delay.final_phase;
> }
>
> + dev_info(host->dev, "Final data pad delay: %x\n", final_delay);
dev_info() -> dev_dbg() or remove it.
> return final_delay == 0xff ? -EIO : 0;
> }
>
> @@ -1430,10 +1534,13 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
> struct msdc_host *host = mmc_priv(mmc);
> int ret;
>
> + if (host->hs400_mode)
> + ret = hs400_tune_response(mmc, opcode);
> + else
> ret = msdc_tune_response(mmc, opcode);
Because of the new else clause, seems like above needs an intendation.
> if (ret == -EIO) {
> dev_err(host->dev, "Tune response fail!\n");
> - return ret;
> + goto out;
Not needed, remove the label and this change.
> }
> if (host->hs400_mode == false) {
> ret = msdc_tune_data(mmc, opcode);
> @@ -1443,6 +1550,8 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
>
> host->saved_tune_para.iocon = readl(host->base + MSDC_IOCON);
> host->saved_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
> + host->saved_tune_para.pad_cmd_tune = readl(host->base + PAD_CMD_TUNE);
> +out:
> return ret;
> }
>
> @@ -1553,6 +1662,18 @@ static int msdc_drv_probe(struct platform_device *pdev)
> dev_dbg(&pdev->dev, "hs400-ds-delay: %x\n",
> host->hs400_ds_delay);
>
> + if (!of_property_read_u32(pdev->dev.of_node, "hs200-cmd-int-delay",
> + &host->hs200_cmd_int_delay))
> + dev_dbg(&pdev->dev, "host->hs200-cmd-int-delay: %x\n",
> + host->hs200_cmd_int_delay);
> + if (!of_property_read_u32(pdev->dev.of_node, "hs400-cmd-int-delay",
> + &host->hs400_cmd_int_delay))
> + dev_dbg(&pdev->dev, "host->hs400-cmd-int-delay: %x\n",
> + host->hs400_cmd_int_delay);
> + if (!of_property_read_u32(pdev->dev.of_node, "cmd-resp-sel",
> + &host->hs200_cmd_resp_sel))
> + dev_dbg(&pdev->dev, "host->hs200_cmd-resp-sel: %x\n",
> + host->hs200_cmd_resp_sel);
I suggest you take the oppotunity to move the MTK DTS parsing into its
own function, include the existing parsing of the "hs400-ds-delay".
This improve the readablitity of the code.
> host->dev = &pdev->dev;
> host->mmc = mmc;
> host->src_clk_freq = clk_get_rate(host->src_clk);
> @@ -1663,6 +1784,7 @@ static void msdc_save_reg(struct msdc_host *host)
> host->save_para.patch_bit0 = readl(host->base + MSDC_PATCH_BIT);
> host->save_para.patch_bit1 = readl(host->base + MSDC_PATCH_BIT1);
> host->save_para.pad_ds_tune = readl(host->base + PAD_DS_TUNE);
> + host->save_para.pad_cmd_tune = readl(host->base + PAD_CMD_TUNE);
> host->save_para.emmc50_cfg0 = readl(host->base + EMMC50_CFG0);
> }
>
> @@ -1675,6 +1797,7 @@ static void msdc_restore_reg(struct msdc_host *host)
> writel(host->save_para.patch_bit0, host->base + MSDC_PATCH_BIT);
> writel(host->save_para.patch_bit1, host->base + MSDC_PATCH_BIT1);
> writel(host->save_para.pad_ds_tune, host->base + PAD_DS_TUNE);
> + writel(host->save_para.pad_cmd_tune, host->base + PAD_CMD_TUNE);
> writel(host->save_para.emmc50_cfg0, host->base + EMMC50_CFG0);
> }
>
> --
> 1.7.9.5
>
Kind regards
Uffe
^ permalink raw reply
* [RFC PATCH v4 0/5] ARM: Fix dma_alloc_coherent() and friends for NOMMU
From: Benjamin Gaignard @ 2017-01-12 10:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <f4d17374-eb46-79f7-e394-cb2739f71ef4@arm.com>
2017-01-11 15:34 GMT+01:00 Vladimir Murzin <vladimir.murzin@arm.com>:
> On 11/01/17 13:17, Benjamin Gaignard wrote:
>> 2017-01-10 15:18 GMT+01:00 Vladimir Murzin <vladimir.murzin@arm.com>:
>>> Hi,
>>>
>>> It seem that addition of cache support for M-class cpus uncovered
>>> latent bug in DMA usage. NOMMU memory model has been treated as being
>>> always consistent; however, for R/M classes of cpu memory can be
>>> covered by MPU which in turn might configure RAM as Normal
>>> i.e. bufferable and cacheable. It breaks dma_alloc_coherent() and
>>> friends, since data can stuck in caches now or be buffered.
>>>
>>> This patch set is trying to address the issue by providing region of
>>> memory suitable for consistent DMA operations. It is supposed that
>>> such region is marked by MPU as non-cacheable. Robin suggested to
>>> advertise such memory as reserved shared-dma-pool, rather then using
>>> homebrew command line option, and extend dma-coherent to provide
>>> default DMA area in the similar way as it is done for CMA (PATCH
>>> 2/5). It allows us to offload all bookkeeping on generic coherent DMA
>>> framework, and it is seems that it might be reused by other
>>> architectures like c6x and blackfin.
>>>
>>> Dedicated DMA region is required for cases other than:
>>> - MMU/MPU is off
>>> - cpu is v7m w/o cache support
>>> - device is coherent
>>>
>>> In case one of the above conditions is true dma operations are forced
>>> to be coherent and wired with dma_noop_ops.
>>>
>>> To make life easier NOMMU dma operations are kept in separate
>>> compilation unit.
>>>
>>> Since the issue was reported in the same time as Benjamin sent his
>>> patch [1] to allow mmap for NOMMU, his case is also addressed in this
>>> series (PATCH 1/5 and PATCH 3/5).
>>>
>>> Thanks!
>>
>> I have tested this v4 on my setup (stm32f4, no cache, no MPU) and unfortunately
>> it doesn't work with my drm/kms driver.
>
> I guess the same is for fbmem, but would be better to have confirmation since
> amba-clcd I use has not been ported to drm/kms (yet), so I can't test.
>
>> I haven't any errors but nothing is displayed unlike what I have when
>> using current dma-mapping
>> code.
>> I guess the issue is coming from dma-noop where __get_free_pages() is
>> used instead of alloc_pages()
>> in dma-mapping.
>
> Unless I've missed something bellow is a call stack for both
>
> #1
> __alloc_simple_buffer
> __dma_alloc_buffer
> alloc_pages
> split_page
> __dma_clear_buffer
> memset
> page_address
>
> #2
> __get_free_pages
> alloc_pages
> page_address
>
> So the difference is that nommu case in dma-mapping.c memzeros memory, handles
> DMA_ATTR_NO_KERNEL_MAPPING and does optimisation of memory usage.
>
> Is something from above critical for your driver?
I have removed all the diff (split_page, __dma_clear_buffer, memset)
from #1 and it is still working.
DMA_ATTR_NO_KERNEL_MAPPING flag is not set when allocating the buffer.
I have investigated more and found that dma-noop doesn't take care of
"dma-ranges" property which is set in DT.
I believed that is the root cause of my problem with your patches.
Benjamin
>
>>
>> Since my hardware doesn't have cache or MPU (and so use dma-noop) I
>> haven't reserved specific memory region.
>> Buffer addresses and vma parameters look correct... What could I have
>> miss here ?
>
> No ideas, sorry...
>
> Cheers
> Vladimir
>
>>
>> Benjamin
>>
>>>
>>> [1] http://www.armlinux.org.uk/developer/patches/viewpatch.php?id=8633/1
>>>
>>> Vladimir Murzin (5):
>>> dma: Add simple dma_noop_mmap
>>> drivers: dma-coherent: Introduce default DMA pool
>>> ARM: NOMMU: Introduce dma operations for noMMU
>>> ARM: NOMMU: Set ARM_DMA_MEM_BUFFERABLE for M-class cpus
>>> ARM: dma-mapping: Remove traces of NOMMU code
>>>
>>> .../bindings/reserved-memory/reserved-memory.txt | 3 +
>>> arch/arm/include/asm/dma-mapping.h | 3 +-
>>> arch/arm/mm/Kconfig | 2 +-
>>> arch/arm/mm/Makefile | 5 +-
>>> arch/arm/mm/dma-mapping-nommu.c | 252 +++++++++++++++++++++
>>> arch/arm/mm/dma-mapping.c | 26 +--
>>> drivers/base/dma-coherent.c | 59 ++++-
>>> lib/dma-noop.c | 21 ++
>>> 8 files changed, 335 insertions(+), 36 deletions(-)
>>> create mode 100644 arch/arm/mm/dma-mapping-nommu.c
>>>
>>> --
>>> 2.0.0
>>>
>>
>>
>>
>
--
Benjamin Gaignard
Graphic Study Group
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* kvm: deadlock in kvm_vgic_map_resources
From: Marc Zyngier @ 2017-01-12 10:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <286255e8-918e-66b5-012f-f347a2ae71cd@arm.com>
On 12/01/17 09:55, Andre Przywara wrote:
> Hi,
>
> On 12/01/17 09:32, Marc Zyngier wrote:
>> Hi Dmitry,
>>
>> On 11/01/17 19:01, Dmitry Vyukov wrote:
>>> Hello,
>>>
>>> While running syzkaller fuzzer I've got the following deadlock.
>>> On commit 9c763584b7c8911106bb77af7e648bef09af9d80.
>>>
>>>
>>> =============================================
>>> [ INFO: possible recursive locking detected ]
>>> 4.9.0-rc6-xc2-00056-g08372dd4b91d-dirty #50 Not tainted
>>> ---------------------------------------------
>>> syz-executor/20805 is trying to acquire lock:
>>> (
>>> &kvm->lock
>>> ){+.+.+.}
>>> , at:
>>> [< inline >] kvm_vgic_dist_destroy
>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:271
>>> [<ffff2000080ea4bc>] kvm_vgic_destroy+0x34/0x250
>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:294
>>> but task is already holding lock:
>>> (&kvm->lock){+.+.+.}, at:
>>> [<ffff2000080ea7e4>] kvm_vgic_map_resources+0x2c/0x108
>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:343
>>> other info that might help us debug this:
>>> Possible unsafe locking scenario:
>>> CPU0
>>> ----
>>> lock(&kvm->lock);
>>> lock(&kvm->lock);
>>> *** DEADLOCK ***
>>> May be due to missing lock nesting notation
>>> 2 locks held by syz-executor/20805:
>>> #0:(&vcpu->mutex){+.+.+.}, at:
>>> [<ffff2000080bcc30>] vcpu_load+0x28/0x1d0
>>> arch/arm64/kvm/../../../virt/kvm/kvm_main.c:143
>>> #1:(&kvm->lock){+.+.+.}, at:
>>> [<ffff2000080ea7e4>] kvm_vgic_map_resources+0x2c/0x108
>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:343
>>> stack backtrace:
>>> CPU: 2 PID: 20805 Comm: syz-executor Not tainted
>>> 4.9.0-rc6-xc2-00056-g08372dd4b91d-dirty #50
>>> Hardware name: Hardkernel ODROID-C2 (DT)
>>> Call trace:
>>> [<ffff200008090560>] dump_backtrace+0x0/0x3c8 arch/arm64/kernel/traps.c:69
>>> [<ffff200008090948>] show_stack+0x20/0x30 arch/arm64/kernel/traps.c:219
>>> [< inline >] __dump_stack lib/dump_stack.c:15
>>> [<ffff200008895840>] dump_stack+0x100/0x150 lib/dump_stack.c:51
>>> [< inline >] print_deadlock_bug kernel/locking/lockdep.c:1728
>>> [< inline >] check_deadlock kernel/locking/lockdep.c:1772
>>> [< inline >] validate_chain kernel/locking/lockdep.c:2250
>>> [<ffff2000081c8718>] __lock_acquire+0x1938/0x3440 kernel/locking/lockdep.c:3335
>>> [<ffff2000081caa84>] lock_acquire+0xdc/0x1d8 kernel/locking/lockdep.c:3746
>>> [< inline >] __mutex_lock_common kernel/locking/mutex.c:521
>>> [<ffff200009700004>] mutex_lock_nested+0xdc/0x7b8 kernel/locking/mutex.c:621
>>> [< inline >] kvm_vgic_dist_destroy
>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:271
>>> [<ffff2000080ea4bc>] kvm_vgic_destroy+0x34/0x250
>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:294
>>> [<ffff2000080ec290>] vgic_v2_map_resources+0x218/0x430
>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-v2.c:295
>>> [<ffff2000080ea884>] kvm_vgic_map_resources+0xcc/0x108
>>> arch/arm64/kvm/../../../virt/kvm/arm/vgic/vgic-init.c:348
>>> [< inline >] kvm_vcpu_first_run_init
>>> arch/arm64/kvm/../../../arch/arm/kvm/arm.c:505
>>> [<ffff2000080d2768>] kvm_arch_vcpu_ioctl_run+0xab8/0xce0
>>> arch/arm64/kvm/../../../arch/arm/kvm/arm.c:591
>>> [<ffff2000080c1fec>] kvm_vcpu_ioctl+0x434/0xc08
>>> arch/arm64/kvm/../../../virt/kvm/kvm_main.c:2557
>>> [< inline >] vfs_ioctl fs/ioctl.c:43
>>> [<ffff200008450c38>] do_vfs_ioctl+0x128/0xfc0 fs/ioctl.c:679
>>> [< inline >] SYSC_ioctl fs/ioctl.c:694
>>> [<ffff200008451b78>] SyS_ioctl+0xa8/0xb8 fs/ioctl.c:685
>>> [<ffff200008083ef0>] el0_svc_naked+0x24/0x28 arch/arm64/kernel/entry.S:755
>>
>> Nice catch, and many thanks for reporting this.
>>
>> The bug is fairly obvious. Christoffer, what do you think? I don't think
>> we need to hold the kvm->lock all the way, but I'd like another pair of
>> eyes (the coffee machine is out of order again, and tea doesn't cut it).
>>
>> Thanks,
>>
>> M.
>>
>> From 93f80b20fb9351a49ee8b74eed3fc59c84651371 Mon Sep 17 00:00:00 2001
>> From: Marc Zyngier <marc.zyngier@arm.com>
>> Date: Thu, 12 Jan 2017 09:21:56 +0000
>> Subject: [PATCH] KVM: arm/arm64: vgic: Fix deadlock on error handling
>>
>> Dmitry Vyukov reported that the syzkaller fuzzer triggered a
>> deadlock in the vgic setup code when an error was detected, as
>> the cleanup code tries to take a lock that is already held by
>> the setup code.
>>
>> The fix is pretty obvious: move the cleaup call after having
>> dropped the lock, since not much can happen at that point.
> ^^^^^^^^
> Is that really true? If for instance the calls to
> vgic_register_dist_iodev() or kvm_phys_addr_ioremap() in
> vgic_v2_map_resources() fail, we leave the function with a half
> initialized VGIC (because vgic_init() succeeded).
But we only set dist->ready to true when everything went OK. How is
that an issue?
> Dropping the lock at
> this point without having the GIC cleaned up before sounds a bit
> suspicious (I may be wrong on this, though).
Thinking of it, that may open a race with vgic init call, leading to
leaking distributor memory.
>
> Can't we just document that kvm_vgic_destroy() needs to be called with
> the kvm->lock held and take the lock around the only other caller
> (kvm_arch_destroy_vm() in arch/arm/kvm/arm.c)?
> We can then keep holding the lock in the map_resources calls.
> Though we might still move the calls to kvm_vgic_destroy() into the
> wrapper function as a cleanup (as shown below), just before dropping the
> lock.
I'd rather keep the changes limited to the vgic code, and save myself
having to document more locking (we already have our fair share here).
How about this (untested):
>From 24dc3f5750da20d89e0ce9b7855d125d0100bee8 Mon Sep 17 00:00:00 2001
From: Marc Zyngier <marc.zyngier@arm.com>
Date: Thu, 12 Jan 2017 09:21:56 +0000
Subject: [PATCH] KVM: arm/arm64: vgic: Fix deadlock on error handling
Dmitry Vyukov reported that the syzkaller fuzzer triggered a
deadlock in the vgic setup code when an error was detected, as
the cleanup code tries to take a lock that is already held by
the setup code.
The fix is to avoid retaking the lock when cleaning up, by
telling the cleanup function that we already hold it.
Cc: stable at vger.kernel.org
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
virt/kvm/arm/vgic/vgic-init.c | 21 ++++++++++++++++-----
virt/kvm/arm/vgic/vgic-v2.c | 2 --
virt/kvm/arm/vgic/vgic-v3.c | 2 --
3 files changed, 16 insertions(+), 9 deletions(-)
diff --git a/virt/kvm/arm/vgic/vgic-init.c b/virt/kvm/arm/vgic/vgic-init.c
index 5114391..30d74e2 100644
--- a/virt/kvm/arm/vgic/vgic-init.c
+++ b/virt/kvm/arm/vgic/vgic-init.c
@@ -264,11 +264,12 @@ int vgic_init(struct kvm *kvm)
return ret;
}
-static void kvm_vgic_dist_destroy(struct kvm *kvm)
+static void kvm_vgic_dist_destroy(struct kvm *kvm, bool locked)
{
struct vgic_dist *dist = &kvm->arch.vgic;
- mutex_lock(&kvm->lock);
+ if (!locked)
+ mutex_lock(&kvm->lock);
dist->ready = false;
dist->initialized = false;
@@ -276,7 +277,8 @@ static void kvm_vgic_dist_destroy(struct kvm *kvm)
kfree(dist->spis);
dist->nr_spis = 0;
- mutex_unlock(&kvm->lock);
+ if (!locked)
+ mutex_unlock(&kvm->lock);
}
void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)
@@ -286,17 +288,22 @@ void kvm_vgic_vcpu_destroy(struct kvm_vcpu *vcpu)
INIT_LIST_HEAD(&vgic_cpu->ap_list_head);
}
-void kvm_vgic_destroy(struct kvm *kvm)
+static void kvm_vgic_destroy_locked(struct kvm *kvm, bool locked)
{
struct kvm_vcpu *vcpu;
int i;
- kvm_vgic_dist_destroy(kvm);
+ kvm_vgic_dist_destroy(kvm, locked);
kvm_for_each_vcpu(i, vcpu, kvm)
kvm_vgic_vcpu_destroy(vcpu);
}
+void kvm_vgic_destroy(struct kvm *kvm)
+{
+ kvm_vgic_destroy_locked(kvm, false);
+}
+
/**
* vgic_lazy_init: Lazy init is only allowed if the GIC exposed to the guest
* is a GICv2. A GICv3 must be explicitly initialized by the guest using the
@@ -348,6 +355,10 @@ int kvm_vgic_map_resources(struct kvm *kvm)
ret = vgic_v2_map_resources(kvm);
else
ret = vgic_v3_map_resources(kvm);
+
+ if (ret)
+ kvm_vgic_destroy_locked(kvm, true);
+
out:
mutex_unlock(&kvm->lock);
return ret;
diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
index 9bab867..834137e 100644
--- a/virt/kvm/arm/vgic/vgic-v2.c
+++ b/virt/kvm/arm/vgic/vgic-v2.c
@@ -293,8 +293,6 @@ int vgic_v2_map_resources(struct kvm *kvm)
dist->ready = true;
out:
- if (ret)
- kvm_vgic_destroy(kvm);
return ret;
}
diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
index 7df1b90..a4c7fff 100644
--- a/virt/kvm/arm/vgic/vgic-v3.c
+++ b/virt/kvm/arm/vgic/vgic-v3.c
@@ -308,8 +308,6 @@ int vgic_v3_map_resources(struct kvm *kvm)
dist->ready = true;
out:
- if (ret)
- kvm_vgic_destroy(kvm);
return ret;
}
--
2.1.4
--
Jazz is not dead. It just smells funny...
^ permalink raw reply related
* [PATCH 37/37] ARM: dts: DRA7: Add pcie1 dt node for EP mode
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
Add pcie1 dt node in order for the controller to operate in
endpoint mode. However since none of the dra7 based boards have
slots configured to operate in endpoint mode, keep EP mode
disabled.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
arch/arm/boot/dts/am572x-idk.dts | 7 ++++++-
arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi | 7 ++++++-
arch/arm/boot/dts/dra7-evm.dts | 4 ++++
arch/arm/boot/dts/dra7.dtsi | 22 +++++++++++++++++++++-
arch/arm/boot/dts/dra72-evm-common.dtsi | 4 ++++
5 files changed, 41 insertions(+), 3 deletions(-)
diff --git a/arch/arm/boot/dts/am572x-idk.dts b/arch/arm/boot/dts/am572x-idk.dts
index 1540f7a..2ca2839 100644
--- a/arch/arm/boot/dts/am572x-idk.dts
+++ b/arch/arm/boot/dts/am572x-idk.dts
@@ -88,6 +88,11 @@
load-gpios = <&gpio3 19 GPIO_ACTIVE_LOW>;
};
-&pcie1 {
+&pcie1_rc {
+ status = "okay";
+ gpios = <&gpio3 23 GPIO_ACTIVE_HIGH>;
+};
+
+&pcie1_ep {
gpios = <&gpio3 23 GPIO_ACTIVE_HIGH>;
};
diff --git a/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi b/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi
index 78bee26..079a7e1 100644
--- a/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi
+++ b/arch/arm/boot/dts/am57xx-beagle-x15-common.dtsi
@@ -556,7 +556,12 @@
};
};
-&pcie1 {
+&pcie1_rc {
+ status = "ok";
+ gpios = <&gpio2 8 GPIO_ACTIVE_LOW>;
+};
+
+&pcie1_ep {
gpios = <&gpio2 8 GPIO_ACTIVE_LOW>;
};
diff --git a/arch/arm/boot/dts/dra7-evm.dts b/arch/arm/boot/dts/dra7-evm.dts
index 132f2be..fd0aa3a 100644
--- a/arch/arm/boot/dts/dra7-evm.dts
+++ b/arch/arm/boot/dts/dra7-evm.dts
@@ -937,3 +937,7 @@
status = "okay";
};
};
+
+&pcie1_rc {
+ status = "okay";
+};
diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi
index addb753..bf9c668 100644
--- a/arch/arm/boot/dts/dra7.dtsi
+++ b/arch/arm/boot/dts/dra7.dtsi
@@ -272,7 +272,11 @@
#address-cells = <1>;
ranges = <0x51000000 0x51000000 0x3000
0x0 0x20000000 0x10000000>;
- pcie1: pcie at 51000000 {
+ /**
+ * To enable PCI endpoint mode, disable the pcie1_rc
+ * node and enable pcie1_ep mode.
+ */
+ pcie1_rc: pcie at 51000000 {
compatible = "ti,dra7-pcie";
reg = <0x51000000 0x2000>, <0x51002000 0x14c>, <0x1000 0x2000>;
reg-names = "rc_dbics", "ti_conf", "config";
@@ -293,12 +297,28 @@
<0 0 0 2 &pcie1_intc 2>,
<0 0 0 3 &pcie1_intc 3>,
<0 0 0 4 &pcie1_intc 4>;
+ status = "disabled";
pcie1_intc: interrupt-controller {
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <1>;
};
};
+
+ pcie1_ep: pcie_ep at 51000000 {
+ compatible = "ti,dra7-pcie-ep";
+ reg = <0x51000000 0x28>, <0x51002000 0x14c>, <0x51001000 0x28>, <0x1000 0x10000000>;
+ reg-names = "ep_dbics", "ti_conf", "ep_dbics2", "addr_space";
+ interrupts = <0 232 0x4>;
+ num-lanes = <1>;
+ num-ib-windows = <4>;
+ num-ob-windows = <16>;
+ ti,hwmods = "pcie1";
+ phys = <&pcie1_phy>;
+ phy-names = "pcie-phy0";
+ syscon-legacy-mode = <&scm_conf1 0x14 2>;
+ status = "disabled";
+ };
};
axi at 1 {
diff --git a/arch/arm/boot/dts/dra72-evm-common.dtsi b/arch/arm/boot/dts/dra72-evm-common.dtsi
index e50fbee..5d9762c 100644
--- a/arch/arm/boot/dts/dra72-evm-common.dtsi
+++ b/arch/arm/boot/dts/dra72-evm-common.dtsi
@@ -545,3 +545,7 @@
status = "okay";
};
};
+
+&pcie1_rc {
+ status = "okay";
+};
--
1.7.9.5
^ permalink raw reply related
* [PATCH 36/37] ARM: DRA7: clockdomain: Change the CLKTRCTRL of CM_PCIE_CLKSTCTRL to SW_WKUP
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
The PCIe programming sequence in TRM suggests CLKSTCTRL of PCIe should
be set to SW_WKUP. There are no issues when CLKSTCTRL is set to HW_AUTO
in RC mode. However in EP mode, the host system is not able to access the
MEMSPACE and setting the CLKSTCTRL to SW_WKUP fixes it.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
arch/arm/mach-omap2/clockdomains7xx_data.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/mach-omap2/clockdomains7xx_data.c b/arch/arm/mach-omap2/clockdomains7xx_data.c
index 6c67965..67ebff8 100644
--- a/arch/arm/mach-omap2/clockdomains7xx_data.c
+++ b/arch/arm/mach-omap2/clockdomains7xx_data.c
@@ -524,7 +524,7 @@
.dep_bit = DRA7XX_PCIE_STATDEP_SHIFT,
.wkdep_srcs = pcie_wkup_sleep_deps,
.sleepdep_srcs = pcie_wkup_sleep_deps,
- .flags = CLKDM_CAN_HWSUP_SWSUP,
+ .flags = CLKDM_CAN_SWSUP,
};
static struct clockdomain atl_7xx_clkdm = {
--
1.7.9.5
^ permalink raw reply related
* [PATCH 35/37] MAINTAINERS: add PCI EP maintainer
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
Add maintainer for the newly introduced PCI EP framework.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
MAINTAINERS | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 8672f18..021f676 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9407,6 +9407,15 @@ F: include/linux/pci*
F: arch/x86/pci/
F: arch/x86/kernel/quirks.c
+PCI EP SUBSYSTEM
+M: Kishon Vijay Abraham I <kishon@ti.com>
+L: linux-pci at vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git
+S: Supported
+F: drivers/pci/endpoint/
+F: drivers/misc/pci_endpoint_test.c
+F: tools/pci/
+
PCI DRIVER FOR ALTERA PCIE IP
M: Ley Foon Tan <lftan@altera.com>
L: rfi at lists.rocketboards.org (moderated for non-subscribers)
--
1.7.9.5
^ permalink raw reply related
* [PATCH 34/37] tools: PCI: Add sample test script to invoke pcitest
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
Add a simple test script that invokes the pcitest userspace tool
to perform all the PCI endpoint tests (BAR tests, interrupt tests,
read tests, write tests and copy tests).
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
tools/pci/pcitest.sh | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
create mode 100644 tools/pci/pcitest.sh
diff --git a/tools/pci/pcitest.sh b/tools/pci/pcitest.sh
new file mode 100644
index 0000000..5442bbe
--- /dev/null
+++ b/tools/pci/pcitest.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+echo "BAR tests"
+echo
+
+bar=0
+
+while [ $bar -lt 6 ]
+do
+ pcitest -b $bar
+ bar=`expr $bar + 1`
+done
+echo
+
+echo "Interrupt tests"
+echo
+
+pcitest -l
+msi=1
+
+while [ $msi -lt 33 ]
+do
+ pcitest -m $msi
+ msi=`expr $msi + 1`
+done
+echo
+
+echo "Read Tests"
+echo
+
+pcitest -r -s 1
+pcitest -r -s 1024
+pcitest -r -s 1025
+pcitest -r -s 1024000
+pcitest -r -s 1024001
+echo
+
+echo "Write Tests"
+echo
+
+pcitest -w -s 1
+pcitest -w -s 1024
+pcitest -w -s 1025
+pcitest -w -s 1024000
+pcitest -w -s 1024001
+echo
+
+echo "Copy Tests"
+echo
+
+pcitest -c -s 1
+pcitest -c -s 1024
+pcitest -c -s 1025
+pcitest -c -s 1024000
+pcitest -c -s 1024001
+echo
--
1.7.9.5
^ permalink raw reply related
* [PATCH 33/37] tools: PCI: Add a userspace tool to test PCI endpoint
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
Add a userspace tool to invoke the ioctls exposed by the
PCI endpoint test driver to perform various PCI tests.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
tools/pci/pcitest.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 186 insertions(+)
create mode 100644 tools/pci/pcitest.c
diff --git a/tools/pci/pcitest.c b/tools/pci/pcitest.c
new file mode 100644
index 0000000..39b5b0b
--- /dev/null
+++ b/tools/pci/pcitest.c
@@ -0,0 +1,186 @@
+/**
+ * Userspace PCI Endpoint Test Module
+ *
+ * Copyright (C) 2017 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <linux/pcitest.h>
+
+#define BILLION 1E9
+
+static char *result[] = { "NOT OKAY", "OKAY" };
+
+struct pci_test {
+ char *device;
+ char barnum;
+ bool legacyirq;
+ unsigned int msinum;
+ bool read;
+ bool write;
+ bool copy;
+ unsigned long size;
+};
+
+static int run_test(struct pci_test *test)
+{
+ long ret;
+ int fd;
+ struct timespec start, end;
+ double time;
+
+ fd = open(test->device, O_RDWR);
+ if (fd < 0) {
+ perror("can't open PCI Endpoint Test device");
+ return fd;
+ }
+
+ if (test->barnum >= 0 && test->barnum <= 5) {
+ ret = ioctl(fd, PCITEST_BAR, test->barnum);
+ fprintf(stdout, "BAR%d:\t\t", test->barnum);
+ if (ret < 0)
+ fprintf(stdout, "TEST FAILED\n");
+ else
+ fprintf(stdout, "%s\n", result[ret]);
+ }
+
+ if (test->legacyirq) {
+ ret = ioctl(fd, PCITEST_LEGACY_IRQ, 0);
+ fprintf(stdout, "LEGACY IRQ:\t");
+ if (ret < 0)
+ fprintf(stdout, "TEST FAILED\n");
+ else
+ fprintf(stdout, "%s\n", result[ret]);
+ }
+
+ if (test->msinum > 0 && test->msinum <= 32) {
+ ret = ioctl(fd, PCITEST_MSI, test->msinum);
+ fprintf(stdout, "MSI%d:\t\t", test->msinum);
+ if (ret < 0)
+ fprintf(stdout, "TEST FAILED\n");
+ else
+ fprintf(stdout, "%s\n", result[ret]);
+ }
+
+ if (test->write) {
+ ret = ioctl(fd, PCITEST_WRITE, test->size);
+ fprintf(stdout, "WRITE (%7ld bytes):\t\t", test->size);
+ if (ret < 0)
+ fprintf(stdout, "TEST FAILED\n");
+ else
+ fprintf(stdout, "%s\n", result[ret]);
+ }
+
+ if (test->read) {
+ ret = ioctl(fd, PCITEST_READ, test->size);
+ fprintf(stdout, "READ (%7ld bytes):\t\t", test->size);
+ if (ret < 0)
+ fprintf(stdout, "TEST FAILED\n");
+ else
+ fprintf(stdout, "%s\n", result[ret]);
+ }
+
+ if (test->copy) {
+ ret = ioctl(fd, PCITEST_COPY, test->size);
+ fprintf(stdout, "COPY (%7ld bytes):\t\t", test->size);
+ if (ret < 0)
+ fprintf(stdout, "TEST FAILED\n");
+ else
+ fprintf(stdout, "%s\n", result[ret]);
+ }
+
+ fflush(stdout);
+}
+
+int main(int argc, char **argv)
+{
+ int c;
+ struct pci_test *test;
+
+ test = calloc(1, sizeof(*test));
+ if (!test) {
+ perror("Fail to allocate memory for pci_test\n");
+ return -ENOMEM;
+ }
+
+ /* since '0' is a valid BAR number, initialize it to -1 */
+ test->barnum = -1;
+
+ /* set default size as 100KB */
+ test->size = 0x19000;
+
+ /* set default endpoint device */
+ test->device = "/dev/pci-endpoint-test.0";
+
+ while ((c = getopt(argc, argv, "D:b:m:lrwcs:")) != EOF)
+ switch (c) {
+ case 'D':
+ test->device = optarg;
+ continue;
+ case 'b':
+ test->barnum = atoi(optarg);
+ if (test->barnum < 0 || test->barnum > 5)
+ goto usage;
+ continue;
+ case 'l':
+ test->legacyirq = true;
+ continue;
+ case 'm':
+ test->msinum = atoi(optarg);
+ if (test->msinum < 1 || test->msinum > 32)
+ goto usage;
+ continue;
+ case 'r':
+ test->read = true;
+ continue;
+ case 'w':
+ test->write = true;
+ continue;
+ case 'c':
+ test->copy = true;
+ continue;
+ case 's':
+ test->size = strtoul(optarg, NULL, 0);
+ continue;
+ case '?':
+ case 'h':
+ default:
+usage:
+ fprintf(stderr,
+ "usage: %s [options]\n"
+ "Options:\n"
+ "\t-D <dev> pci endpoint test device {default: /dev/pci-endpoint-test.0}\n"
+ "\t-b <bar num> BAR test (bar number between 0..5)\n"
+ "\t-m <msi num> MSI test (msi number between 1..32)\n"
+ "\t-r Read buffer test\n"
+ "\t-w Write buffer test\n"
+ "\t-c Copy buffer test\n"
+ "\t-s <size> Size of buffer {default: 100KB}\n",
+ argv[0]);
+ return -EINVAL;
+ }
+
+ run_test(test);
+ return 0;
+}
--
1.7.9.5
^ permalink raw reply related
* [PATCH 32/37] Documentation: misc-devices: Add Documentation for pci-endpoint-test driver
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
Add Documentation for pci-endpoint-test driver.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
Documentation/misc-devices/pci-endpoint-test.txt | 35 ++++++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 Documentation/misc-devices/pci-endpoint-test.txt
diff --git a/Documentation/misc-devices/pci-endpoint-test.txt b/Documentation/misc-devices/pci-endpoint-test.txt
new file mode 100644
index 0000000..4385718
--- /dev/null
+++ b/Documentation/misc-devices/pci-endpoint-test.txt
@@ -0,0 +1,35 @@
+Driver for PCI Endpoint Test Function
+
+This driver should be used as a host side driver if the root complex is
+connected to a configurable pci endpoint running *pci_epf_test* function
+driver configured according to [1].
+
+The "pci_endpoint_test" driver can be used to perform the following tests.
+
+The PCI driver for the test device performs the following tests
+ *) verifying addresses programmed in BAR
+ *) raise legacy IRQ
+ *) raise MSI IRQ
+ *) read data
+ *) write data
+ *) copy data
+
+This misc driver creates /dev/pci-endpoint-test.<num> for every
+*pci_epf_test* function connected to the root complex and "ioctls"
+should be used to perform the above tests.
+
+ioctl
+-----
+ PCITEST_BAR: Tests the BAR. The number of the BAR that has to be tested
+ should be passed as argument.
+ PCITEST_LEGACY_IRQ: Tests legacy IRQ
+ PCITEST_MSI: Tests message signalled interrupts. The MSI number that has
+ to be tested should be passed as argument.
+ PCITEST_WRITE: Perform write tests. The size of the buffer should be passed
+ as argument.
+ PCITEST_READ: Perform read tests. The size of the buffer should be passed
+ as argument.
+ PCITEST_COPY: Perform read tests. The size of the buffer should be passed
+ as argument.
+
+[1] -> Documentation/PCI/endpoint/function/binding/pci-test.txt
--
1.7.9.5
^ permalink raw reply related
* [PATCH 31/37] misc: Add host side pci driver for pci test function device
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
Add PCI endpoint test driver that can verify base address
register, legacy interrupt/MSI interrupt and read/write/copy
buffers between host and device. The corresponding pci-epf-test
function driver should be used on the EP side.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
drivers/misc/Kconfig | 7 +
drivers/misc/Makefile | 1 +
drivers/misc/pci_endpoint_test.c | 533 ++++++++++++++++++++++++++++++++++++++
include/uapi/linux/Kbuild | 1 +
include/uapi/linux/pcitest.h | 19 ++
5 files changed, 561 insertions(+)
create mode 100644 drivers/misc/pci_endpoint_test.c
create mode 100644 include/uapi/linux/pcitest.h
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 64971ba..14a95a6 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -766,6 +766,13 @@ config PANEL_BOOT_MESSAGE
An empty message will only clear the display at driver init time. Any other
printf()-formatted message is valid with newline and escape codes.
+config PCI_ENDPOINT_TEST
+ depends on PCI || COMPILE_TEST
+ tristate "PCI Endpoint Test driver"
+ ---help---
+ Enable this configuration option to enable the host side test driver
+ for PCI Endpoint.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 3198336..64a532ac2 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
obj-$(CONFIG_CXL_BASE) += cxl/
obj-$(CONFIG_PANEL) += panel.o
+obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o
diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c
new file mode 100644
index 0000000..920b14c
--- /dev/null
+++ b/drivers/misc/pci_endpoint_test.c
@@ -0,0 +1,533 @@
+/**
+ * Host side test driver to test endpoint functionality
+ *
+ * Copyright (C) 2017 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+
+#include <linux/pci_regs.h>
+
+#include <uapi/linux/pcitest.h>
+
+#define DRV_MODULE_NAME "pci-endpoint-test"
+
+#define PCI_ENDPOINT_TEST_MAGIC 0x0
+
+#define PCI_ENDPOINT_TEST_COMMAND 0x4
+#define COMMAND_RAISE_LEGACY_IRQ BIT(0)
+#define COMMAND_RAISE_MSI_IRQ BIT(1)
+#define MSI_NUMBER_SHIFT 2
+/* 6 bits for MSI number */
+#define COMMAND_READ BIT(8)
+#define COMMAND_WRITE BIT(9)
+#define COMMAND_COPY BIT(10)
+
+#define PCI_ENDPOINT_TEST_STATUS 0x8
+#define STATUS_READ_SUCCESS BIT(0)
+#define STATUS_READ_FAIL BIT(1)
+#define STATUS_WRITE_SUCCESS BIT(2)
+#define STATUS_WRITE_FAIL BIT(3)
+#define STATUS_COPY_SUCCESS BIT(4)
+#define STATUS_COPY_FAIL BIT(5)
+#define STATUS_IRQ_RAISED BIT(6)
+#define STATUS_SRC_ADDR_INVALID BIT(7)
+#define STATUS_DST_ADDR_INVALID BIT(8)
+
+#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR 0xc
+#define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR 0x10
+
+#define PCI_ENDPOINT_TEST_LOWER_DST_ADDR 0x14
+#define PCI_ENDPOINT_TEST_UPPER_DST_ADDR 0x18
+
+#define PCI_ENDPOINT_TEST_SIZE 0x1c
+#define PCI_ENDPOINT_TEST_CHECKSUM 0x20
+
+static DEFINE_IDA(pci_endpoint_test_ida);
+
+#define to_endpoint_test(priv) container_of((priv), struct pci_endpoint_test, \
+ miscdev)
+enum pci_barno {
+ BAR_0,
+ BAR_1,
+ BAR_2,
+ BAR_3,
+ BAR_4,
+ BAR_5,
+};
+
+struct pci_endpoint_test {
+ struct pci_dev *pdev;
+ void __iomem *base;
+ void __iomem *bar[6];
+ struct completion irq_raised;
+ int last_irq;
+ /* mutex to protect the ioctls */
+ struct mutex mutex;
+ struct miscdevice miscdev;
+};
+
+static int bar_size[] = { 4, 512, 1024, 16384, 131072, 1048576 };
+
+static inline u32 pci_endpoint_test_readl(struct pci_endpoint_test *test,
+ u32 offset)
+{
+ return readl(test->base + offset);
+}
+
+static inline void pci_endpoint_test_writel(struct pci_endpoint_test *test,
+ u32 offset, u32 value)
+{
+ writel(value, test->base + offset);
+}
+
+static inline u32 pci_endpoint_test_bar_readl(struct pci_endpoint_test *test,
+ int bar, int offset)
+{
+ return readl(test->bar[bar] + offset);
+}
+
+static inline void pci_endpoint_test_bar_writel(struct pci_endpoint_test *test,
+ int bar, u32 offset, u32 value)
+{
+ writel(value, test->bar[bar] + offset);
+}
+
+static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id)
+{
+ struct pci_endpoint_test *test = dev_id;
+ u32 reg;
+
+ reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
+ if (reg & STATUS_IRQ_RAISED) {
+ test->last_irq = irq;
+ complete(&test->irq_raised);
+ reg &= ~STATUS_IRQ_RAISED;
+ }
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS,
+ reg);
+
+ return IRQ_HANDLED;
+}
+
+static bool pci_endpoint_test_bar(struct pci_endpoint_test *test,
+ enum pci_barno barno)
+{
+ int j;
+ u32 val;
+ int size;
+
+ if (!test->bar[barno])
+ return false;
+
+ size = bar_size[barno];
+
+ for (j = 0; j < size; j += 4)
+ pci_endpoint_test_bar_writel(test, barno, j, 0xA0A0A0A0);
+
+ for (j = 0; j < size; j += 4) {
+ val = pci_endpoint_test_bar_readl(test, barno, j);
+ if (val != 0xA0A0A0A0)
+ return false;
+ }
+
+ return true;
+}
+
+static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test)
+{
+ u32 val;
+
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+ COMMAND_RAISE_LEGACY_IRQ);
+ val = wait_for_completion_timeout(&test->irq_raised,
+ msecs_to_jiffies(1000));
+ if (!val)
+ return false;
+
+ return true;
+}
+
+static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test,
+ u8 msi_num)
+{
+ u32 val;
+ struct pci_dev *pdev = test->pdev;
+
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+ msi_num << MSI_NUMBER_SHIFT |
+ COMMAND_RAISE_MSI_IRQ);
+ val = wait_for_completion_timeout(&test->irq_raised,
+ msecs_to_jiffies(1000));
+ if (!val)
+ return false;
+
+ if (test->last_irq - pdev->irq == msi_num - 1)
+ return true;
+
+ return false;
+}
+
+static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size)
+{
+ bool ret = false;
+ void *src_addr;
+ void *dst_addr;
+ dma_addr_t src_phys_addr;
+ dma_addr_t dst_phys_addr;
+ struct pci_dev *pdev = test->pdev;
+ struct device *dev = &pdev->dev;
+ u32 src_crc32;
+ u32 dst_crc32;
+
+ src_addr = dma_alloc_coherent(dev, size, &src_phys_addr, GFP_KERNEL);
+ if (!src_addr) {
+ dev_err(dev, "failed to allocate source buffer\n");
+ ret = false;
+ goto err;
+ }
+
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_SRC_ADDR,
+ lower_32_bits(src_phys_addr));
+
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_SRC_ADDR,
+ upper_32_bits(src_phys_addr));
+
+ get_random_bytes(src_addr, size);
+ src_crc32 = crc32_le(~0, src_addr, size);
+
+ dst_addr = dma_alloc_coherent(dev, size, &dst_phys_addr, GFP_KERNEL);
+ if (!dst_addr) {
+ dev_err(dev, "failed to allocate destination address\n");
+ ret = false;
+ goto err_src_addr;
+ }
+
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_DST_ADDR,
+ lower_32_bits(dst_phys_addr));
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_DST_ADDR,
+ upper_32_bits(dst_phys_addr));
+
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE,
+ size);
+
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+ 1 << MSI_NUMBER_SHIFT | COMMAND_COPY);
+
+ wait_for_completion(&test->irq_raised);
+
+ dst_crc32 = crc32_le(~0, dst_addr, size);
+ if (dst_crc32 == src_crc32)
+ ret = true;
+
+ dma_free_coherent(dev, size, dst_addr, dst_phys_addr);
+
+err_src_addr:
+ dma_free_coherent(dev, size, src_addr, src_phys_addr);
+
+err:
+ return ret;
+}
+
+static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size)
+{
+ bool ret = false;
+ u32 reg;
+ void *addr;
+ dma_addr_t phys_addr;
+ struct pci_dev *pdev = test->pdev;
+ struct device *dev = &pdev->dev;
+ u32 crc32;
+
+ addr = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL);
+ if (!addr) {
+ dev_err(dev, "failed to allocate address\n");
+ ret = false;
+ goto err;
+ }
+
+ get_random_bytes(addr, size);
+
+ crc32 = crc32_le(~0, addr, size);
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_CHECKSUM,
+ crc32);
+
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_SRC_ADDR,
+ lower_32_bits(phys_addr));
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_SRC_ADDR,
+ upper_32_bits(phys_addr));
+
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
+
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+ 1 << MSI_NUMBER_SHIFT | COMMAND_READ);
+
+ wait_for_completion(&test->irq_raised);
+
+ reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
+ if (reg & STATUS_READ_SUCCESS)
+ ret = true;
+
+ dma_free_coherent(dev, size, addr, phys_addr);
+
+err:
+ return ret;
+}
+
+static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size)
+{
+ bool ret = false;
+ void *addr;
+ dma_addr_t phys_addr;
+ struct pci_dev *pdev = test->pdev;
+ struct device *dev = &pdev->dev;
+ u32 crc32;
+
+ addr = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL);
+ if (!addr) {
+ dev_err(dev, "failed to allocate destination address\n");
+ ret = false;
+ goto err;
+ }
+
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_DST_ADDR,
+ lower_32_bits(phys_addr));
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_DST_ADDR,
+ upper_32_bits(phys_addr));
+
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
+
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+ 1 << MSI_NUMBER_SHIFT | COMMAND_WRITE);
+
+ wait_for_completion(&test->irq_raised);
+
+ crc32 = crc32_le(~0, addr, size);
+ if (crc32 == pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CHECKSUM))
+ ret = true;
+
+ dma_free_coherent(dev, size, addr, phys_addr);
+err:
+ return ret;
+}
+
+static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -EINVAL;
+ enum pci_barno bar;
+ struct pci_endpoint_test *test = to_endpoint_test(file->private_data);
+
+ mutex_lock(&test->mutex);
+ switch (cmd) {
+ case PCITEST_BAR:
+ bar = arg;
+ if (bar < 0 || bar > 5)
+ goto ret;
+ ret = pci_endpoint_test_bar(test, bar);
+ break;
+ case PCITEST_LEGACY_IRQ:
+ ret = pci_endpoint_test_legacy_irq(test);
+ break;
+ case PCITEST_MSI:
+ ret = pci_endpoint_test_msi_irq(test, arg);
+ break;
+ case PCITEST_WRITE:
+ ret = pci_endpoint_test_write(test, arg);
+ break;
+ case PCITEST_READ:
+ ret = pci_endpoint_test_read(test, arg);
+ break;
+ case PCITEST_COPY:
+ ret = pci_endpoint_test_copy(test, arg);
+ break;
+ }
+
+ret:
+ mutex_unlock(&test->mutex);
+ return ret;
+}
+
+static const struct file_operations pci_endpoint_test_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = pci_endpoint_test_ioctl,
+};
+
+static int pci_endpoint_test_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int i;
+ int err;
+ int irq;
+ int id;
+ char name[20];
+ enum pci_barno bar;
+ void __iomem *base;
+ struct device *dev = &pdev->dev;
+ struct pci_endpoint_test *test;
+ struct miscdevice *misc_device;
+
+ if (pci_is_bridge(pdev))
+ return -ENODEV;
+
+ test = devm_kzalloc(dev, sizeof(*test), GFP_KERNEL);
+ if (!test)
+ return -ENOMEM;
+
+ test->pdev = pdev;
+ init_completion(&test->irq_raised);
+ mutex_init(&test->mutex);
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ dev_err(dev, "Cannot enable PCI device\n");
+ return err;
+ }
+
+ err = pci_request_regions(pdev, DRV_MODULE_NAME);
+ if (err) {
+ dev_err(dev, "Cannot obtain PCI resources\n");
+ goto err_disable_pdev;
+ }
+
+ pci_set_master(pdev);
+
+ irq = pci_enable_msi_range(pdev, 1, 32);
+ if (irq < 0)
+ dev_err(dev, "failed to get MSI interrupts\n");
+
+ err = devm_request_irq(dev, pdev->irq, pci_endpoint_test_irqhandler,
+ IRQF_SHARED, DRV_MODULE_NAME, test);
+ if (err) {
+ dev_err(dev, "failed to request irq\n");
+ goto err_disable_msi;
+ }
+
+ for (i = 1; i < irq; i++) {
+ err = devm_request_irq(dev, pdev->irq + i,
+ pci_endpoint_test_irqhandler,
+ IRQF_SHARED, DRV_MODULE_NAME, test);
+ if (err)
+ dev_err(dev, "failed to request irq for MSI %d\n",
+ i + 1);
+ }
+
+ for (bar = BAR_0; bar <= BAR_5; bar++) {
+ base = pci_ioremap_bar(pdev, bar);
+ if (!base) {
+ dev_err(dev, "failed to read BAR%d\n", bar);
+ WARN_ON(bar == BAR_0);
+ }
+ test->bar[bar] = base;
+ }
+
+ test->base = test->bar[0];
+ if (!test->base) {
+ dev_err(dev, "Cannot perform PCI test without BAR0\n");
+ goto err_iounmap;
+ }
+
+ pci_set_drvdata(pdev, test);
+
+ id = ida_simple_get(&pci_endpoint_test_ida, 0, 0, GFP_KERNEL);
+ if (id < 0) {
+ dev_err(dev, "unable to get id\n");
+ goto err_iounmap;
+ }
+
+ snprintf(name, sizeof(name), DRV_MODULE_NAME ".%d", id);
+ misc_device = &test->miscdev;
+ misc_device->minor = MISC_DYNAMIC_MINOR;
+ misc_device->name = name;
+ misc_device->fops = &pci_endpoint_test_fops,
+
+ err = misc_register(misc_device);
+ if (err) {
+ dev_err(dev, "failed to register device\n");
+ goto err_ida_remove;
+ }
+
+ return 0;
+
+err_ida_remove:
+ ida_simple_remove(&pci_endpoint_test_ida, id);
+
+err_iounmap:
+ for (bar = BAR_0; bar <= BAR_5; bar++) {
+ if (test->bar[bar])
+ pci_iounmap(pdev, test->bar[bar]);
+ }
+
+err_disable_msi:
+ pci_disable_msi(pdev);
+ pci_release_regions(pdev);
+
+err_disable_pdev:
+ pci_disable_device(pdev);
+
+ return err;
+}
+
+static void pci_endpoint_test_remove(struct pci_dev *pdev)
+{
+ int id;
+ enum pci_barno bar;
+ struct pci_endpoint_test *test = pci_get_drvdata(pdev);
+ struct miscdevice *misc_device = &test->miscdev;
+
+ if (sscanf(misc_device->name, DRV_MODULE_NAME ".%d", &id) != 1)
+ return;
+
+ misc_deregister(&test->miscdev);
+ ida_simple_remove(&pci_endpoint_test_ida, id);
+ for (bar = BAR_0; bar <= BAR_5; bar++) {
+ if (test->bar[bar])
+ pci_iounmap(pdev, test->bar[bar]);
+ }
+ pci_disable_msi(pdev);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+}
+
+static const struct pci_device_id pci_endpoint_test_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_ANY_ID) },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl);
+
+static struct pci_driver pci_endpoint_test_driver = {
+ .name = DRV_MODULE_NAME,
+ .id_table = pci_endpoint_test_tbl,
+ .probe = pci_endpoint_test_probe,
+ .remove = pci_endpoint_test_remove,
+};
+module_pci_driver(pci_endpoint_test_driver);
+
+MODULE_DESCRIPTION("PCI ENDPOINT TEST DRIVER");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index a8b93e6..f3c2769 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -329,6 +329,7 @@ header-y += parport.h
header-y += patchkey.h
header-y += pci.h
header-y += pci_regs.h
+header-y += pcitest.h
header-y += perf_event.h
header-y += personality.h
header-y += pfkeyv2.h
diff --git a/include/uapi/linux/pcitest.h b/include/uapi/linux/pcitest.h
new file mode 100644
index 0000000..a6aa10c
--- /dev/null
+++ b/include/uapi/linux/pcitest.h
@@ -0,0 +1,19 @@
+/**
+ * pcitest.h - PCI test uapi defines
+ *
+ * Copyright (C) 2017 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ */
+
+#ifndef __UAPI_LINUX_PCITEST_H
+#define __UAPI_LINUX_PCITEST_H
+
+#define PCITEST_BAR _IO('P', 0x1)
+#define PCITEST_LEGACY_IRQ _IO('P', 0x2)
+#define PCITEST_MSI _IOW('P', 0x3, int)
+#define PCITEST_WRITE _IOW('P', 0x4, unsigned long)
+#define PCITEST_READ _IOW('P', 0x5, unsigned long)
+#define PCITEST_COPY _IOW('P', 0x6, unsigned long)
+
+#endif /* __UAPI_LINUX_PCITEST_H */
--
1.7.9.5
^ permalink raw reply related
* [PATCH 30/37] dt-bindings: PCI: dra7xx: Add dt bindings to enable legacy mode
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
Update device tree binding documentation of TI's dra7xx PCI
controller to include property for enabling legacy mode.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
Documentation/devicetree/bindings/pci/ti-pci.txt | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/pci/ti-pci.txt b/Documentation/devicetree/bindings/pci/ti-pci.txt
index 62f5f59..ed85e8e 100644
--- a/Documentation/devicetree/bindings/pci/ti-pci.txt
+++ b/Documentation/devicetree/bindings/pci/ti-pci.txt
@@ -39,6 +39,10 @@ DEVICE MODE
- interrupts : one interrupt entries must be specified for main interrupt.
- num-ib-windows : number of inbound address translation windows
- num-ob-windows : number of outbound address translation windows
+ - syscon-legacy-mode: phandle to the syscon dt node. The 1st argument should
+ contain the register offset within syscon and the 2nd
+ argument should contain the bit field for setting the
+ legacy mode
Optional Property:
- gpios : Should be added if a gpio line is required to drive PERST# line
--
1.7.9.5
^ permalink raw reply related
* [PATCH 29/37] PCI: dwc: dra7xx: Workaround for errata id i870
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
According to errata i870, access to the PCIe slave port
that are not 32-bit aligned will result in incorrect mapping
to TLP Address and Byte enable fields.
Accessing non 32-bit aligned data causes incorrect data in the target
buffer if memcpy is used. Implement the workaround for this
errata here.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
drivers/pci/dwc/pci-dra7xx.c | 50 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/drivers/pci/dwc/pci-dra7xx.c b/drivers/pci/dwc/pci-dra7xx.c
index 333aa56..7666e3e 100644
--- a/drivers/pci/dwc/pci-dra7xx.c
+++ b/drivers/pci/dwc/pci-dra7xx.c
@@ -26,6 +26,8 @@
#include <linux/pm_runtime.h>
#include <linux/resource.h>
#include <linux/types.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
#include "pcie-designware.h"
@@ -531,6 +533,48 @@ static int dra7xx_pcie_enable_phy(struct dra7xx_pcie *dra7xx)
{},
};
+/*
+ * dra7xx_pcie_ep_legacy_mode: workaround for AM572x/AM571x Errata i870
+ * @dra7xx: the dra7xx device where the workaround should be applied
+ *
+ * Access to the PCIe slave port that are not 32-bit aligned will result
+ * in incorrect mapping to TLP Address and Byte enable fields. Therefore,
+ * byte and half-word accesses are not possible to byte offset 0x1, 0x2, or
+ * 0x3.
+ *
+ * To avoid this issue set PCIE_SS1_AXI2OCP_LEGACY_MODE_ENABLE to 1.
+ */
+static int dra7xx_pcie_ep_legacy_mode(struct device *dev)
+{
+ int ret;
+ struct device_node *np = dev->of_node;
+ struct regmap *regmap;
+ unsigned int reg;
+ unsigned int field;
+
+ regmap = syscon_regmap_lookup_by_phandle(np, "syscon-legacy-mode");
+ if (IS_ERR(regmap)) {
+ dev_dbg(dev, "can't get syscon-legacy-mode\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32_index(np, "syscon-legacy-mode", 1, ®)) {
+ dev_err(dev, "couldn't get legacy mode register offset\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32_index(np, "syscon-legacy-mode", 2, &field)) {
+ dev_err(dev, "can't get bit field for setting legacy mode\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(regmap, reg, field, field);
+ if (ret)
+ dev_err(dev, "failed to set legacy mode\n");
+
+ return ret;
+}
+
static int __init dra7xx_pcie_probe(struct platform_device *pdev)
{
u32 reg;
@@ -643,6 +687,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
case DW_PCIE_RC_TYPE:
dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE,
DEVICE_TYPE_RC);
+
ret = dra7xx_add_pcie_port(dra7xx, pdev);
if (ret < 0)
goto err_gpio;
@@ -650,6 +695,11 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
case DW_PCIE_EP_TYPE:
dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE,
DEVICE_TYPE_EP);
+
+ ret = dra7xx_pcie_ep_legacy_mode(dev);
+ if (ret)
+ goto err_gpio;
+
ret = dra7xx_add_pcie_ep(dra7xx, pdev);
if (ret < 0)
goto err_gpio;
--
1.7.9.5
^ permalink raw reply related
* [PATCH 28/37] dt-bindings: PCI: dra7xx: Add dt bindings for pci dra7xx EP mode
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
Add device tree binding documentation for pci dra7xx EP mode.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
Documentation/devicetree/bindings/pci/ti-pci.txt | 37 ++++++++++++++++++----
1 file changed, 30 insertions(+), 7 deletions(-)
diff --git a/Documentation/devicetree/bindings/pci/ti-pci.txt b/Documentation/devicetree/bindings/pci/ti-pci.txt
index 60e2516..62f5f59 100644
--- a/Documentation/devicetree/bindings/pci/ti-pci.txt
+++ b/Documentation/devicetree/bindings/pci/ti-pci.txt
@@ -1,17 +1,22 @@
TI PCI Controllers
PCIe Designware Controller
- - compatible: Should be "ti,dra7-pcie""
- - reg : Two register ranges as listed in the reg-names property
- - reg-names : The first entry must be "ti-conf" for the TI specific registers
- The second entry must be "rc-dbics" for the designware pcie
- registers
- The third entry must be "config" for the PCIe configuration space
+ - compatible: Should be "ti,dra7-pcie" for RC
+ Should be "ti,dra7-pcie-ep" for EP
- phys : list of PHY specifiers (used by generic PHY framework)
- phy-names : must be "pcie-phy0", "pcie-phy1", "pcie-phyN".. based on the
number of PHYs as specified in *phys* property.
- ti,hwmods : Name of the hwmod associated to the pcie, "pcie<X>",
where <X> is the instance number of the pcie from the HW spec.
+ - num-lanes as specified in ../designware-pcie.txt
+
+HOST MODE
+=========
+ - reg : Two register ranges as listed in the reg-names property
+ - reg-names : The first entry must be "ti-conf" for the TI specific registers
+ The second entry must be "rc-dbics" for the designware pcie
+ registers
+ The third entry must be "config" for the PCIe configuration space
- interrupts : Two interrupt entries must be specified. The first one is for
main interrupt line and the second for MSI interrupt line.
- #address-cells,
@@ -19,13 +24,31 @@ PCIe Designware Controller
#interrupt-cells,
device_type,
ranges,
- num-lanes,
interrupt-map-mask,
interrupt-map : as specified in ../designware-pcie.txt
+DEVICE MODE
+===========
+ - reg : Four register ranges as listed in the reg-names property
+ - reg-names : "ti-conf" for the TI specific registers
+ "ep_dbics" for the standard configuration registers as
+ they are locally accessed within the DIF CS space
+ "ep_dbics2" for the standard configuration registers as
+ they are locally accessed within the DIF CS2 space
+ "addr_space" used to map remote RC address space
+ - interrupts : one interrupt entries must be specified for main interrupt.
+ - num-ib-windows : number of inbound address translation windows
+ - num-ob-windows : number of outbound address translation windows
+
Optional Property:
- gpios : Should be added if a gpio line is required to drive PERST# line
+NOTE: Two dt nodes should be added for each PCI controller; one for host
+mode and another for device mode. So in order for PCI to
+work in host mode, EP mode dt node should be disabled and in order to PCI to
+work in EP mode, host mode dt node should be disabled. And host mode and EP
+mode are mutually exclusive.
+
Example:
axi {
compatible = "simple-bus";
--
1.7.9.5
^ permalink raw reply related
* [PATCH 27/37] PCI: dwc: dra7xx: Add EP mode support
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
The PCIe controller integrated in dra7xx SoCs is capable of operating
in endpoint mode. Add endpoint mode support to dra7xx driver.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
drivers/pci/dwc/Kconfig | 31 +++++-
drivers/pci/dwc/Makefile | 4 +-
drivers/pci/dwc/pci-dra7xx.c | 197 ++++++++++++++++++++++++++++++++++---
drivers/pci/dwc/pcie-designware.h | 7 ++
4 files changed, 221 insertions(+), 18 deletions(-)
diff --git a/drivers/pci/dwc/Kconfig b/drivers/pci/dwc/Kconfig
index 4cb1ba0..7932be6 100644
--- a/drivers/pci/dwc/Kconfig
+++ b/drivers/pci/dwc/Kconfig
@@ -16,14 +16,37 @@ config PCIE_DW_EP
config PCI_DRA7XX
bool "TI DRA7xx PCIe controller"
- depends on PCI
+ depends on (PCI && PCI_MSI_IRQ_DOMAIN) || PCI_ENDPOINT
depends on OF && HAS_IOMEM && TI_PIPE3
+ help
+ Enables support for the PCIe controller in the DRA7xx SoC. There
+ are two instances of PCIe controller in DRA7xx. This controller can
+ work either as EP or RC. In order to enable host specific features
+ PCI_DRA7XX_HOST must be selected and in order to enable device
+ specific features PCI_DRA7XX_EP must be selected. This uses
+ the Designware core.
+
+if PCI_DRA7XX
+
+config PCI_DRA7XX_HOST
+ bool "PCI DRA7xx Host Mode"
+ depends on PCI
depends on PCI_MSI_IRQ_DOMAIN
select PCIE_DW_HOST
+ default y
help
- Enables support for the PCIe controller in the DRA7xx SoC. There
- are two instances of PCIe controller in DRA7xx. This controller can
- act both as EP and RC. This reuses the Designware core.
+ Enables support for the PCIe controller in the DRA7xx SoC to work in
+ host mode.
+
+config PCI_DRA7XX_EP
+ bool "PCI DRA7xx Endpoint Mode"
+ depends on PCI_ENDPOINT
+ select PCIE_DW_EP
+ help
+ Enables support for the PCIe controller in the DRA7xx SoC to work in
+ endpoint mode.
+
+endif
config PCIE_DW_PLAT
bool "Platform bus based DesignWare PCIe Controller"
diff --git a/drivers/pci/dwc/Makefile b/drivers/pci/dwc/Makefile
index b38425d..f31a859 100644
--- a/drivers/pci/dwc/Makefile
+++ b/drivers/pci/dwc/Makefile
@@ -2,7 +2,9 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o
obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o
obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o
obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o
-obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
+ifneq ($(filter y,$(CONFIG_PCI_DRA7XX_HOST) $(CONFIG_PCI_DRA7XX_EP)),)
+ obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
+endif
obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
diff --git a/drivers/pci/dwc/pci-dra7xx.c b/drivers/pci/dwc/pci-dra7xx.c
index eb3a9c6..333aa56 100644
--- a/drivers/pci/dwc/pci-dra7xx.c
+++ b/drivers/pci/dwc/pci-dra7xx.c
@@ -10,12 +10,14 @@
* published by the Free Software Foundation.
*/
+#include <linux/delay.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/of_pci.h>
#include <linux/pci.h>
@@ -57,6 +59,11 @@
#define MSI BIT(4)
#define LEG_EP_INTERRUPTS (INTA | INTB | INTC | INTD)
+#define PCIECTRL_TI_CONF_DEVICE_TYPE 0x0100
+#define DEVICE_TYPE_EP 0x0
+#define DEVICE_TYPE_LEG_EP 0x1
+#define DEVICE_TYPE_RC 0x4
+
#define PCIECTRL_DRA7XX_CONF_DEVICE_CMD 0x0104
#define LTSSM_EN 0x1
@@ -66,6 +73,13 @@
#define EXP_CAP_ID_OFFSET 0x70
+#define PCIECTRL_TI_CONF_INTX_ASSERT 0x0124
+#define PCIECTRL_TI_CONF_INTX_DEASSERT 0x0128
+
+#define PCIECTRL_TI_CONF_MSI_XMT 0x012c
+#define MSI_REQ_GRANT BIT(0)
+#define MSI_VECTOR_SHIFT 7
+
struct dra7xx_pcie {
struct dw_pcie *pci;
void __iomem *base; /* DT ti_conf */
@@ -73,6 +87,11 @@ struct dra7xx_pcie {
struct phy **phy;
int link_gen;
struct irq_domain *irq_domain;
+ enum dw_pcie_device_mode mode;
+};
+
+struct dra7xx_pcie_of_data {
+ enum dw_pcie_device_mode mode;
};
#define to_dra7xx_pcie(x) dev_get_drvdata((x)->dev)
@@ -101,9 +120,19 @@ static int dra7xx_pcie_link_up(struct dw_pcie *pci)
return !!(reg & LINK_UP);
}
-static int dra7xx_pcie_establish_link(struct dra7xx_pcie *dra7xx)
+static void dra7xx_pcie_stop_link(struct dw_pcie *pci)
{
- struct dw_pcie *pci = dra7xx->pci;
+ struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
+ u32 reg;
+
+ reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD);
+ reg &= ~LTSSM_EN;
+ dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
+}
+
+static int dra7xx_pcie_establish_link(struct dw_pcie *pci)
+{
+ struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
struct device *dev = pci->dev;
u32 reg;
u32 exp_cap_off = EXP_CAP_ID_OFFSET;
@@ -137,7 +166,7 @@ static int dra7xx_pcie_establish_link(struct dra7xx_pcie *dra7xx)
reg |= LTSSM_EN;
dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
- return dw_pcie_wait_for_link(pci);
+ return 0;
}
static void dra7xx_pcie_enable_msi_interrupts(struct dra7xx_pcie *dra7xx)
@@ -171,7 +200,8 @@ static void dra7xx_pcie_host_init(struct pcie_port *pp)
dw_pcie_setup_rc(pp);
- dra7xx_pcie_establish_link(dra7xx);
+ dra7xx_pcie_establish_link(pci);
+ dw_pcie_wait_for_link(pci);
dw_pcie_msi_init(pp);
dra7xx_pcie_enable_interrupts(dra7xx);
}
@@ -249,6 +279,7 @@ static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg)
struct dra7xx_pcie *dra7xx = arg;
struct dw_pcie *pci = dra7xx->pci;
struct device *dev = pci->dev;
+ struct dw_pcie_ep *ep = &pci->ep;
u32 reg;
reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN);
@@ -285,8 +316,11 @@ static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg)
if (reg & LINK_REQ_RST)
dev_dbg(dev, "Link Request Reset\n");
- if (reg & LINK_UP_EVT)
+ if (reg & LINK_UP_EVT) {
+ if (dra7xx->mode == DW_PCIE_EP_TYPE)
+ dw_pcie_ep_linkup(ep);
dev_dbg(dev, "Link-up state change\n");
+ }
if (reg & CFG_BME_EVT)
dev_dbg(dev, "CFG 'Bus Master Enable' change\n");
@@ -299,6 +333,94 @@ static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg)
return IRQ_HANDLED;
}
+static void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
+
+ dra7xx_pcie_enable_wrapper_interrupts(dra7xx);
+}
+
+static void dra7xx_pcie_raise_legacy_irq(struct dra7xx_pcie *dra7xx)
+{
+ dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_INTX_ASSERT, 0x1);
+ mdelay(1);
+ dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_INTX_DEASSERT, 0x1);
+}
+
+static void dra7xx_pcie_raise_msi_irq(struct dra7xx_pcie *dra7xx,
+ u8 interrupt_num)
+{
+ u32 reg;
+
+ reg = (interrupt_num - 1) << MSI_VECTOR_SHIFT;
+ reg |= MSI_REQ_GRANT;
+ dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_MSI_XMT, reg);
+}
+
+static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep,
+ enum pci_epc_irq_type type, u8 interrupt_num)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
+
+ switch (type) {
+ case PCI_EPC_IRQ_LEGACY:
+ dra7xx_pcie_raise_legacy_irq(dra7xx);
+ break;
+ case PCI_EPC_IRQ_MSI:
+ dra7xx_pcie_raise_msi_irq(dra7xx, interrupt_num);
+ break;
+ default:
+ dev_err(pci->dev, "UNKNOWN IRQ type\n");
+ }
+
+ return 0;
+}
+
+static struct dw_pcie_ep_ops pcie_ep_ops = {
+ .ep_init = dra7xx_pcie_ep_init,
+ .raise_irq = dra7xx_pcie_raise_irq,
+};
+
+static int __init dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx,
+ struct platform_device *pdev)
+{
+ int ret;
+ struct dw_pcie_ep *ep;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ struct dw_pcie *pci = dra7xx->pci;
+
+ ep = &pci->ep;
+ ep->ops = &pcie_ep_ops;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ep_dbics");
+ pci->dbi_base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!pci->dbi_base)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ep_dbics2");
+ pci->dbi_base2 = devm_ioremap(dev, res->start, resource_size(res));
+ if (!pci->dbi_base2)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
+ if (!res)
+ return -EINVAL;
+
+ ep->phys_base = res->start;
+ ep->addr_size = resource_size(res);
+
+ ret = dw_pcie_ep_init(ep);
+ if (ret) {
+ dev_err(dev, "failed to initialize endpoint\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
struct platform_device *pdev)
{
@@ -345,6 +467,8 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
static const struct dw_pcie_ops dw_pcie_ops = {
.cpu_addr_fixup = dra7xx_pcie_cpu_addr_fixup,
+ .start_link = dra7xx_pcie_establish_link,
+ .stop_link = dra7xx_pcie_stop_link,
.link_up = dra7xx_pcie_link_up,
};
@@ -387,6 +511,26 @@ static int dra7xx_pcie_enable_phy(struct dra7xx_pcie *dra7xx)
return ret;
}
+static const struct dra7xx_pcie_of_data dra7xx_pcie_rc_of_data = {
+ .mode = DW_PCIE_RC_TYPE,
+};
+
+static const struct dra7xx_pcie_of_data dra7xx_pcie_ep_of_data = {
+ .mode = DW_PCIE_EP_TYPE,
+};
+
+static const struct of_device_id of_dra7xx_pcie_match[] = {
+ {
+ .compatible = "ti,dra7-pcie",
+ .data = &dra7xx_pcie_rc_of_data,
+ },
+ {
+ .compatible = "ti,dra7-pcie-ep",
+ .data = &dra7xx_pcie_ep_of_data,
+ },
+ {},
+};
+
static int __init dra7xx_pcie_probe(struct platform_device *pdev)
{
u32 reg;
@@ -403,6 +547,16 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
char name[10];
struct gpio_desc *reset;
+ const struct of_device_id *match;
+ const struct dra7xx_pcie_of_data *data;
+ enum dw_pcie_device_mode mode;
+
+ match = of_match_device(of_match_ptr(of_dra7xx_pcie_match), dev);
+ if (!match)
+ return -EINVAL;
+
+ data = (struct dra7xx_pcie_of_data *)match->data;
+ mode = (enum dw_pcie_device_mode)data->mode;
dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL);
if (!dra7xx)
@@ -485,9 +639,25 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
if (dra7xx->link_gen < 0 || dra7xx->link_gen > 2)
dra7xx->link_gen = 2;
- ret = dra7xx_add_pcie_port(dra7xx, pdev);
- if (ret < 0)
- goto err_gpio;
+ switch (mode) {
+ case DW_PCIE_RC_TYPE:
+ dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE,
+ DEVICE_TYPE_RC);
+ ret = dra7xx_add_pcie_port(dra7xx, pdev);
+ if (ret < 0)
+ goto err_gpio;
+ break;
+ case DW_PCIE_EP_TYPE:
+ dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE,
+ DEVICE_TYPE_EP);
+ ret = dra7xx_add_pcie_ep(dra7xx, pdev);
+ if (ret < 0)
+ goto err_gpio;
+ break;
+ default:
+ dev_err(dev, "INVALID device type %d\n", mode);
+ }
+ dra7xx->mode = mode;
return 0;
@@ -509,6 +679,9 @@ static int dra7xx_pcie_suspend(struct device *dev)
void __iomem *base = pci->dbi_base;
u32 val;
+ if (dra7xx->mode != DW_PCIE_RC_TYPE)
+ return 0;
+
/* clear MSE */
val = dw_pcie_read_dbi(pci, base, PCI_COMMAND, 0x4);
val &= ~PCI_COMMAND_MEMORY;
@@ -524,6 +697,9 @@ static int dra7xx_pcie_resume(struct device *dev)
void __iomem *base = pci->dbi_base;
u32 val;
+ if (dra7xx->mode != DW_PCIE_RC_TYPE)
+ return 0;
+
/* set MSE */
val = dw_pcie_read_dbi(pci, base, PCI_COMMAND, 0x4);
val |= PCI_COMMAND_MEMORY;
@@ -562,11 +738,6 @@ static int dra7xx_pcie_resume_noirq(struct device *dev)
dra7xx_pcie_resume_noirq)
};
-static const struct of_device_id of_dra7xx_pcie_match[] = {
- { .compatible = "ti,dra7-pcie", },
- {},
-};
-
static struct platform_driver dra7xx_pcie_driver = {
.driver = {
.name = "dra7-pcie",
diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h
index 7476234..5679aa3 100644
--- a/drivers/pci/dwc/pcie-designware.h
+++ b/drivers/pci/dwc/pcie-designware.h
@@ -117,6 +117,13 @@ enum dw_pcie_region_type {
DW_PCIE_REGION_OUTBOUND,
};
+enum dw_pcie_device_mode {
+ DW_PCIE_UNKNOWN_TYPE,
+ DW_PCIE_EP_TYPE,
+ DW_PCIE_LEG_EP_TYPE,
+ DW_PCIE_RC_TYPE,
+};
+
struct dw_pcie_host_ops {
int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val);
int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val);
--
1.7.9.5
^ permalink raw reply related
* [PATCH 26/37] PCI: dwc: dra7xx: Facilitate wrapper and msi interrupts to be enabled independently
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
No functional change. Split dra7xx_pcie_enable_interrupts into
dra7xx_pcie_enable_wrapper_interrupts and dra7xx_pcie_enable_msi_interrupts
so that wrapper interrupts and msi interrupts can be enabled independently.
This is in preparation for adding EP mode support to dra7xx driver since
EP mode doesn't have to enable msi_interrupts.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
drivers/pci/dwc/pci-dra7xx.c | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/drivers/pci/dwc/pci-dra7xx.c b/drivers/pci/dwc/pci-dra7xx.c
index 8a1fccd..eb3a9c6 100644
--- a/drivers/pci/dwc/pci-dra7xx.c
+++ b/drivers/pci/dwc/pci-dra7xx.c
@@ -140,18 +140,30 @@ static int dra7xx_pcie_establish_link(struct dra7xx_pcie *dra7xx)
return dw_pcie_wait_for_link(pci);
}
-static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx)
+static void dra7xx_pcie_enable_msi_interrupts(struct dra7xx_pcie *dra7xx)
{
- dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN,
- ~INTERRUPTS);
- dra7xx_pcie_writel(dra7xx,
- PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN, INTERRUPTS);
dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI,
~LEG_EP_INTERRUPTS & ~MSI);
- dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI,
+
+ dra7xx_pcie_writel(dra7xx,
+ PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI,
MSI | LEG_EP_INTERRUPTS);
}
+static void dra7xx_pcie_enable_wrapper_interrupts(struct dra7xx_pcie *dra7xx)
+{
+ dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN,
+ ~INTERRUPTS);
+ dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN,
+ INTERRUPTS);
+}
+
+static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx)
+{
+ dra7xx_pcie_enable_wrapper_interrupts(dra7xx);
+ dra7xx_pcie_enable_msi_interrupts(dra7xx);
+}
+
static void dra7xx_pcie_host_init(struct pcie_port *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
--
1.7.9.5
^ permalink raw reply related
* [PATCH 25/37] dt-bindings: PCI: Add dt bindings for pci designware EP mode
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
Add device tree binding documentation for pci designware EP mode.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
.../devicetree/bindings/pci/designware-pcie.txt | 26 ++++++++++++++------
1 file changed, 18 insertions(+), 8 deletions(-)
diff --git a/Documentation/devicetree/bindings/pci/designware-pcie.txt b/Documentation/devicetree/bindings/pci/designware-pcie.txt
index 1392c70..b2480dd 100644
--- a/Documentation/devicetree/bindings/pci/designware-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/designware-pcie.txt
@@ -6,30 +6,40 @@ Required properties:
- reg-names: Must be "config" for the PCIe configuration space.
(The old way of getting the configuration address space from "ranges"
is deprecated and should be avoided.)
+- num-lanes: number of lanes to use
+RC mode:
- #address-cells: set to <3>
- #size-cells: set to <2>
- device_type: set to "pci"
- ranges: ranges for the PCI memory and I/O regions
- #interrupt-cells: set to <1>
-- interrupt-map-mask and interrupt-map: standard PCI properties
- to define the mapping of the PCIe interface to interrupt
+- interrupt-map-mask and interrupt-map: standard PCI
+ properties to define the mapping of the PCIe interface to interrupt
numbers.
-- num-lanes: number of lanes to use
+EP mode:
+- num-ib-windows: number of inbound address translation
+ windows
+- num-ob-windows: number of outbound address translation
+ windows
Optional properties:
-- num-viewport: number of view ports configured in hardware. If a platform
- does not specify it, the driver assumes 2.
- num-lanes: number of lanes to use (this property should be specified unless
the link is brought already up in BIOS)
- reset-gpio: gpio pin number of power good signal
-- bus-range: PCI bus numbers covered (it is recommended for new devicetrees to
- specify this property, to keep backwards compatibility a range of 0x00-0xff
- is assumed if not present)
- clocks: Must contain an entry for each entry in clock-names.
See ../clocks/clock-bindings.txt for details.
- clock-names: Must include the following entries:
- "pcie"
- "pcie_bus"
+RC mode:
+- num-viewport: number of view ports configured in
+ hardware. If a platform does not specify it, the driver assumes 2.
+- bus-range: PCI bus numbers covered (it is recommended
+ for new devicetrees to specify this property, to keep backwards
+ compatibility a range of 0x00-0xff is assumed if not present)
+EP mode:
+- max-functions: maximum number of functions that can be
+ configured
Example configuration:
--
1.7.9.5
^ permalink raw reply related
* [PATCH 24/37] PCI: dwc: designware: Add EP mode support
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
Add endpoint mode support to designware driver. This uses the
EP Core layer introduced recently to add endpoint mode support.
*Any* function driver can now use this designware device
in order to achieve the EP functionality.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
drivers/pci/dwc/Kconfig | 5 +
drivers/pci/dwc/Makefile | 1 +
drivers/pci/dwc/pcie-designware-ep.c | 342 ++++++++++++++++++++++++++++++++++
drivers/pci/dwc/pcie-designware.c | 51 +++++
drivers/pci/dwc/pcie-designware.h | 70 +++++++
5 files changed, 469 insertions(+)
create mode 100644 drivers/pci/dwc/pcie-designware-ep.c
diff --git a/drivers/pci/dwc/Kconfig b/drivers/pci/dwc/Kconfig
index bee8b52..4cb1ba0 100644
--- a/drivers/pci/dwc/Kconfig
+++ b/drivers/pci/dwc/Kconfig
@@ -9,6 +9,11 @@ config PCIE_DW_HOST
depends on PCI_MSI_IRQ_DOMAIN
select PCIE_DW
+config PCIE_DW_EP
+ bool
+ depends on PCI_ENDPOINT
+ select PCIE_DW
+
config PCI_DRA7XX
bool "TI DRA7xx PCIe controller"
depends on PCI
diff --git a/drivers/pci/dwc/Makefile b/drivers/pci/dwc/Makefile
index a2df13c..b38425d 100644
--- a/drivers/pci/dwc/Makefile
+++ b/drivers/pci/dwc/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_PCIE_DW) += pcie-designware.o
obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o
+obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o
obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o
obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c
new file mode 100644
index 0000000..e465c5e
--- /dev/null
+++ b/drivers/pci/dwc/pcie-designware-ep.c
@@ -0,0 +1,342 @@
+/**
+ * Synopsys Designware PCIe Endpoint controller driver
+ *
+ * Copyright (C) 2017 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/of.h>
+
+#include "pcie-designware.h"
+#include <linux/pci-epc.h>
+#include <linux/pci-epf.h>
+
+void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
+{
+ struct pci_epc *epc = ep->epc;
+ struct pci_epf *epf;
+
+ list_for_each_entry(epf, &epc->pci_epf, list)
+ pci_epf_linkup(epf);
+}
+
+static void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
+{
+ u32 reg;
+
+ reg = PCI_BASE_ADDRESS_0 + (4 * bar);
+ dw_pcie_write_dbi(pci, pci->dbi_base2, reg, 0x4, 0x0);
+ dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x4, 0x0);
+}
+
+static int dw_pcie_ep_write_header(struct pci_epc *epc,
+ struct pci_epf_header *hdr)
+{
+ struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ void __iomem *base = pci->dbi_base;
+
+ dw_pcie_write_dbi(pci, base, PCI_VENDOR_ID, 0x2, hdr->vendorid);
+ dw_pcie_write_dbi(pci, base, PCI_DEVICE_ID, 0x2, hdr->deviceid);
+ dw_pcie_write_dbi(pci, base, PCI_REVISION_ID, 0x1, hdr->revid);
+ dw_pcie_write_dbi(pci, base, PCI_CLASS_PROG, 0x1, hdr->progif_code);
+ dw_pcie_write_dbi(pci, base, PCI_CLASS_DEVICE, 0x2,
+ hdr->subclass_code | hdr->baseclass_code << 8);
+ dw_pcie_write_dbi(pci, base, PCI_CACHE_LINE_SIZE, 0x1,
+ hdr->cache_line_size);
+ dw_pcie_write_dbi(pci, base, PCI_SUBSYSTEM_VENDOR_ID, 0x2,
+ hdr->subsys_vendor_id);
+ dw_pcie_write_dbi(pci, base, PCI_SUBSYSTEM_ID, 0x2, hdr->subsys_id);
+ dw_pcie_write_dbi(pci, base, PCI_INTERRUPT_PIN, 0x1,
+ hdr->interrupt_pin);
+
+ return 0;
+}
+
+static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, enum pci_barno bar,
+ dma_addr_t cpu_addr,
+ enum dw_pcie_as_type as_type)
+{
+ int ret;
+ u32 free_win;
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+ free_win = find_first_zero_bit(&ep->ib_window_map,
+ sizeof(ep->ib_window_map));
+ if (free_win >= ep->num_ib_windows) {
+ dev_err(pci->dev, "no free inbound window\n");
+ return -EINVAL;
+ }
+
+ ret = dw_pcie_prog_inbound_atu(pci, free_win, bar, cpu_addr,
+ as_type);
+ if (ret < 0) {
+ dev_err(pci->dev, "Failed to program IB window\n");
+ return ret;
+ }
+
+ ep->bar_to_atu[bar] = free_win;
+ set_bit(free_win, &ep->ib_window_map);
+
+ return 0;
+}
+
+static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr,
+ u64 pci_addr, size_t size)
+{
+ u32 free_win;
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+ free_win = find_first_zero_bit(&ep->ob_window_map,
+ sizeof(ep->ob_window_map));
+ if (free_win >= ep->num_ob_windows) {
+ dev_err(pci->dev, "no free outbound window\n");
+ return -EINVAL;
+ }
+
+ dw_pcie_prog_outbound_atu(pci, free_win, PCIE_ATU_TYPE_MEM,
+ phys_addr, pci_addr, size);
+
+ set_bit(free_win, &ep->ob_window_map);
+ ep->outbound_addr[free_win] = phys_addr;
+
+ return 0;
+}
+
+static void dw_pcie_ep_clear_bar(struct pci_epc *epc, enum pci_barno bar)
+{
+ struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ u32 atu_index = ep->bar_to_atu[bar];
+
+ dw_pcie_ep_reset_bar(pci, bar);
+
+ dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND);
+ clear_bit(atu_index, &ep->ib_window_map);
+}
+
+static int dw_pcie_ep_set_bar(struct pci_epc *epc, enum pci_barno bar,
+ dma_addr_t bar_phys, size_t size, int flags)
+{
+ int ret;
+ struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ enum dw_pcie_as_type as_type;
+ u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar);
+
+ if (!(flags & PCI_BASE_ADDRESS_SPACE))
+ as_type = DW_PCIE_AS_MEM;
+ else
+ as_type = DW_PCIE_AS_IO;
+
+ ret = dw_pcie_ep_inbound_atu(ep, bar, bar_phys, as_type);
+ if (ret)
+ return ret;
+
+ dw_pcie_write_dbi(pci, pci->dbi_base2, reg, 0x4, size - 1);
+ dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x4, flags);
+
+ return 0;
+}
+
+static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr,
+ u32 *atu_index)
+{
+ u32 index;
+
+ for (index = 0; index < ep->num_ob_windows; index++) {
+ if (ep->outbound_addr[index] != addr)
+ continue;
+ *atu_index = index;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, phys_addr_t addr)
+{
+ int ret;
+ u32 atu_index;
+ struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+ ret = dw_pcie_find_index(ep, addr, &atu_index);
+ if (ret < 0)
+ return;
+
+ dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_OUTBOUND);
+ clear_bit(atu_index, &ep->ob_window_map);
+}
+
+static int dw_pcie_ep_map_addr(struct pci_epc *epc, phys_addr_t addr,
+ u64 pci_addr, size_t size)
+{
+ int ret;
+ struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+ ret = dw_pcie_ep_outbound_atu(ep, addr, pci_addr, size);
+ if (ret) {
+ dev_err(pci->dev, "failed to enable address\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dw_pcie_ep_get_msi(struct pci_epc *epc)
+{
+ int val;
+ u32 lower_addr;
+ u32 upper_addr;
+ struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+ val = dw_pcie_read_dbi(pci, pci->dbi_base, MSI_MESSAGE_CONTROL, 0x2);
+ val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT;
+
+ lower_addr = dw_pcie_read_dbi(pci, pci->dbi_base, MSI_MESSAGE_ADDR_L32,
+ 0x4);
+ upper_addr = dw_pcie_read_dbi(pci, pci->dbi_base, MSI_MESSAGE_ADDR_U32,
+ 0x4);
+
+ if (!(lower_addr || upper_addr))
+ return -EINVAL;
+
+ return val;
+}
+
+static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 encode_int)
+{
+ int val;
+ struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+ val = (encode_int << MSI_CAP_MMC_SHIFT);
+ dw_pcie_write_dbi(pci, pci->dbi_base, MSI_MESSAGE_CONTROL, 0x2, val);
+
+ return 0;
+}
+
+static int dw_pcie_ep_raise_irq(struct pci_epc *epc,
+ enum pci_epc_irq_type type, u8 interrupt_num)
+{
+ struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+
+ if (!ep->ops->raise_irq)
+ return -EINVAL;
+
+ return ep->ops->raise_irq(ep, type, interrupt_num);
+}
+
+static void dw_pcie_ep_stop(struct pci_epc *epc)
+{
+ struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+ if (!pci->ops->stop_link)
+ return;
+
+ pci->ops->stop_link(pci);
+}
+
+static int dw_pcie_ep_start(struct pci_epc *epc)
+{
+ struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+ if (!pci->ops->start_link)
+ return -EINVAL;
+
+ return pci->ops->start_link(pci);
+}
+
+static const struct pci_epc_ops epc_ops = {
+ .write_header = dw_pcie_ep_write_header,
+ .set_bar = dw_pcie_ep_set_bar,
+ .clear_bar = dw_pcie_ep_clear_bar,
+ .map_addr = dw_pcie_ep_map_addr,
+ .unmap_addr = dw_pcie_ep_unmap_addr,
+ .set_msi = dw_pcie_ep_set_msi,
+ .get_msi = dw_pcie_ep_get_msi,
+ .raise_irq = dw_pcie_ep_raise_irq,
+ .start = dw_pcie_ep_start,
+ .stop = dw_pcie_ep_stop,
+};
+
+void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
+{
+ struct pci_epc *epc = ep->epc;
+
+ pci_epc_mem_exit(epc);
+}
+
+int dw_pcie_ep_init(struct dw_pcie_ep *ep)
+{
+ int ret;
+ void *addr;
+ enum pci_barno bar;
+ struct pci_epc *epc;
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ struct device *dev = pci->dev;
+ struct device_node *np = dev->of_node;
+
+ ret = of_property_read_u32(np, "num-ib-windows", &ep->num_ib_windows);
+ if (ret < 0) {
+ dev_err(dev, "unable to read *num-ib-windows* property\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "num-ob-windows", &ep->num_ob_windows);
+ if (ret < 0) {
+ dev_err(dev, "unable to read *num-ob-windows* property\n");
+ return ret;
+ }
+
+ addr = devm_kzalloc(dev, sizeof(phys_addr_t) * ep->num_ob_windows,
+ GFP_KERNEL);
+ if (!addr)
+ return -ENOMEM;
+ ep->outbound_addr = addr;
+
+ for (bar = BAR_0; bar <= BAR_5; bar++)
+ dw_pcie_ep_reset_bar(pci, bar);
+
+ if (ep->ops->ep_init)
+ ep->ops->ep_init(ep);
+
+ epc = devm_pci_epc_create(dev, &epc_ops);
+ if (IS_ERR(epc)) {
+ dev_err(dev, "failed to create epc device\n");
+ return PTR_ERR(epc);
+ }
+
+ ret = of_property_read_u8(np, "max-functions", &epc->max_functions);
+ if (ret < 0)
+ epc->max_functions = 1;
+
+ ret = pci_epc_mem_init(epc, ep->phys_base, ep->addr_size);
+ if (ret < 0) {
+ dev_err(dev, "Failed to initialize address space\n");
+ return ret;
+ }
+
+ ep->epc = epc;
+ epc_set_drvdata(epc, ep);
+ dw_pcie_setup(pci);
+
+ return 0;
+}
diff --git a/drivers/pci/dwc/pcie-designware.c b/drivers/pci/dwc/pcie-designware.c
index ef545e7..ae96ccb 100644
--- a/drivers/pci/dwc/pcie-designware.c
+++ b/drivers/pci/dwc/pcie-designware.c
@@ -172,6 +172,57 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
dev_err(pci->dev, "iATU is not being enabled\n");
}
+int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int bar,
+ u64 cpu_addr, enum dw_pcie_as_type as_type)
+{
+ int type;
+ void __iomem *base = pci->dbi_base;
+
+ dw_pcie_write_dbi(pci, base, PCIE_ATU_VIEWPORT, 0x4,
+ PCIE_ATU_REGION_INBOUND | index);
+ dw_pcie_write_dbi(pci, base, PCIE_ATU_LOWER_TARGET, 0x4,
+ lower_32_bits(cpu_addr));
+ dw_pcie_write_dbi(pci, base, PCIE_ATU_UPPER_TARGET, 0x4,
+ upper_32_bits(cpu_addr));
+
+ switch (as_type) {
+ case DW_PCIE_AS_MEM:
+ type = PCIE_ATU_TYPE_MEM;
+ break;
+ case DW_PCIE_AS_IO:
+ type = PCIE_ATU_TYPE_IO;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dw_pcie_write_dbi(pci, base, PCIE_ATU_CR1, 0x4, type);
+ dw_pcie_write_dbi(pci, base, PCIE_ATU_CR2, 0x4, PCIE_ATU_ENABLE |
+ PCIE_ATU_BAR_MODE_ENABLE | (bar << 8));
+ return 0;
+}
+
+void dw_pcie_disable_atu(struct dw_pcie *pci, int index,
+ enum dw_pcie_region_type type)
+{
+ int region;
+ void __iomem *base = pci->dbi_base;
+
+ switch (type) {
+ case DW_PCIE_REGION_INBOUND:
+ region = PCIE_ATU_REGION_INBOUND;
+ break;
+ case DW_PCIE_REGION_OUTBOUND:
+ region = PCIE_ATU_REGION_OUTBOUND;
+ break;
+ default:
+ return;
+ }
+
+ dw_pcie_write_dbi(pci, base, PCIE_ATU_VIEWPORT, 0x4, region | index);
+ dw_pcie_write_dbi(pci, base, PCIE_ATU_CR2, 0x4, ~PCIE_ATU_ENABLE);
+}
+
int dw_pcie_wait_for_link(struct dw_pcie *pci)
{
int retries;
diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h
index 25b3b8b..7476234 100644
--- a/drivers/pci/dwc/pcie-designware.h
+++ b/drivers/pci/dwc/pcie-designware.h
@@ -18,6 +18,9 @@
#include <linux/msi.h>
#include <linux/pci.h>
+#include <linux/pci-epc.h>
+#include <linux/pci-epf.h>
+
/* Parameters for the waiting for link up routine */
#define LINK_WAIT_MAX_RETRIES 10
#define LINK_WAIT_USLEEP_MIN 90000
@@ -89,6 +92,13 @@
#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) \
((0x3 << 20) | ((region) << 9))
+#define MSI_MESSAGE_CONTROL 0x52
+#define MSI_CAP_MMC_SHIFT 1
+#define MSI_CAP_MME_SHIFT 4
+#define MSI_CAP_MME_MASK (7 << MSI_CAP_MME_SHIFT)
+#define MSI_MESSAGE_ADDR_L32 0x54
+#define MSI_MESSAGE_ADDR_U32 0x58
+
/*
* Maximum number of MSI IRQs can be 256 per controller. But keep
* it 32 as of now. Probably we will never need more than 32. If needed,
@@ -99,6 +109,13 @@
struct pcie_port;
struct dw_pcie;
+struct dw_pcie_ep;
+
+enum dw_pcie_region_type {
+ DW_PCIE_REGION_UNKNOWN,
+ DW_PCIE_REGION_INBOUND,
+ DW_PCIE_REGION_OUTBOUND,
+};
struct dw_pcie_host_ops {
int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val);
@@ -142,6 +159,31 @@ struct pcie_port {
DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
};
+enum dw_pcie_as_type {
+ DW_PCIE_AS_UNKNOWN,
+ DW_PCIE_AS_MEM,
+ DW_PCIE_AS_IO,
+};
+
+struct dw_pcie_ep_ops {
+ void (*ep_init)(struct dw_pcie_ep *ep);
+ int (*raise_irq)(struct dw_pcie_ep *ep, enum pci_epc_irq_type type,
+ u8 interrupt_num);
+};
+
+struct dw_pcie_ep {
+ struct pci_epc *epc;
+ struct dw_pcie_ep_ops *ops;
+ phys_addr_t phys_base;
+ size_t addr_size;
+ u8 bar_to_atu[6];
+ phys_addr_t *outbound_addr;
+ unsigned long ib_window_map;
+ unsigned long ob_window_map;
+ u32 num_ib_windows;
+ u32 num_ob_windows;
+};
+
struct dw_pcie_ops {
u64 (*cpu_addr_fixup)(u64 cpu_addr);
u32 (*read_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg,
@@ -156,14 +198,19 @@ struct dw_pcie_ops {
struct dw_pcie {
struct device *dev;
void __iomem *dbi_base;
+ void __iomem *dbi_base2;
u32 num_viewport;
u8 iatu_unroll_enabled;
struct pcie_port pp;
+ struct dw_pcie_ep ep;
const struct dw_pcie_ops *ops;
};
#define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp)
+#define to_dw_pcie_from_ep(endpoint) \
+ container_of((endpoint), struct dw_pcie, ep)
+
int dw_pcie_read(void __iomem *addr, int size, u32 *val);
int dw_pcie_write(void __iomem *addr, int size, u32 val);
@@ -176,6 +223,10 @@ void dw_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg,
void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index,
int type, u64 cpu_addr, u64 pci_addr,
u32 size);
+int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int bar,
+ u64 cpu_addr, enum dw_pcie_as_type as_type);
+void dw_pcie_disable_atu(struct dw_pcie *pci, int index,
+ enum dw_pcie_region_type type);
void dw_pcie_setup(struct dw_pcie *pci);
#ifdef CONFIG_PCIE_DW_HOST
@@ -202,4 +253,23 @@ static inline int dw_pcie_host_init(struct pcie_port *pp)
return 0;
}
#endif
+
+#ifdef CONFIG_PCIE_DW_EP
+void dw_pcie_ep_linkup(struct dw_pcie_ep *ep);
+int dw_pcie_ep_init(struct dw_pcie_ep *ep);
+void dw_pcie_ep_exit(struct dw_pcie_ep *ep);
+#else
+static inline void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
+{
+}
+
+static inline int dw_pcie_ep_init(struct dw_pcie_ep *ep)
+{
+ return 0;
+}
+
+static inline void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
+{
+}
+#endif
#endif /* _PCIE_DESIGNWARE_H */
--
1.7.9.5
^ permalink raw reply related
* [PATCH 23/37] PCI: dwc: Add *ops* to start and stop pcie link
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
Add start_link and stop_link ops in dw_pcie_ops to start or stop
the link. This will be used by endpoint functions to start the
link once the setup has been done.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
drivers/pci/dwc/pcie-designware.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h
index 0ef6ae7..25b3b8b 100644
--- a/drivers/pci/dwc/pcie-designware.h
+++ b/drivers/pci/dwc/pcie-designware.h
@@ -149,6 +149,8 @@ struct dw_pcie_ops {
void (*write_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg,
int size, u32 val);
int (*link_up)(struct dw_pcie *pcie);
+ int (*start_link)(struct dw_pcie *pcie);
+ void (*stop_link)(struct dw_pcie *pcie);
};
struct dw_pcie {
--
1.7.9.5
^ permalink raw reply related
* [PATCH 22/37] PCI: dwc: Modify dbi accessors to access data of 4/2/1 bytes
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
Previously dbi accessors can be used to access data of size 4
bytes. But there might be situations (like accessing
MSI_MESSAGE_CONTROL in order to set/get the number of required
MSI interrupts in EP mode) where dbi accessors must
be used to access data of size 2. This is in preparation for
adding endpoint mode support to designware driver.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
drivers/pci/dwc/pci-dra7xx.c | 8 ++--
drivers/pci/dwc/pci-exynos.c | 16 +++----
drivers/pci/dwc/pci-imx6.c | 58 +++++++++++------------
drivers/pci/dwc/pci-keystone-dw.c | 13 +++---
drivers/pci/dwc/pcie-armada8k.c | 38 +++++++--------
drivers/pci/dwc/pcie-artpec6.c | 6 +--
drivers/pci/dwc/pcie-designware-host.c | 16 +++----
drivers/pci/dwc/pcie-designware.c | 79 +++++++++++++++++++-------------
drivers/pci/dwc/pcie-designware.h | 14 +++---
drivers/pci/dwc/pcie-hisi.c | 14 +++---
10 files changed, 140 insertions(+), 122 deletions(-)
diff --git a/drivers/pci/dwc/pci-dra7xx.c b/drivers/pci/dwc/pci-dra7xx.c
index 76d0b40..8a1fccd 100644
--- a/drivers/pci/dwc/pci-dra7xx.c
+++ b/drivers/pci/dwc/pci-dra7xx.c
@@ -498,9 +498,9 @@ static int dra7xx_pcie_suspend(struct device *dev)
u32 val;
/* clear MSE */
- val = dw_pcie_readl_dbi(pci, base, PCI_COMMAND);
+ val = dw_pcie_read_dbi(pci, base, PCI_COMMAND, 0x4);
val &= ~PCI_COMMAND_MEMORY;
- dw_pcie_writel_dbi(pci, base, PCI_COMMAND, val);
+ dw_pcie_write_dbi(pci, base, PCI_COMMAND, 0x4, val);
return 0;
}
@@ -513,9 +513,9 @@ static int dra7xx_pcie_resume(struct device *dev)
u32 val;
/* set MSE */
- val = dw_pcie_readl_dbi(pci, base, PCI_COMMAND);
+ val = dw_pcie_read_dbi(pci, base, PCI_COMMAND, 0x4);
val |= PCI_COMMAND_MEMORY;
- dw_pcie_writel_dbi(pci, base, PCI_COMMAND, val);
+ dw_pcie_write_dbi(pci, base, PCI_COMMAND, 0x4, val);
return 0;
}
diff --git a/drivers/pci/dwc/pci-exynos.c b/drivers/pci/dwc/pci-exynos.c
index a109cf0..f6beb05 100644
--- a/drivers/pci/dwc/pci-exynos.c
+++ b/drivers/pci/dwc/pci-exynos.c
@@ -405,25 +405,25 @@ static void exynos_pcie_enable_interrupts(struct exynos_pcie *exynos_pcie)
exynos_pcie_msi_init(exynos_pcie);
}
-static u32 exynos_pcie_readl_dbi(struct dw_pcie *pci, void __iomem *base,
- u32 reg)
+static u32 exynos_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base,
+ u32 reg, int size)
{
struct exynos_pcie *exynos_pcie = to_exynos_pcie(pci);
u32 val;
exynos_pcie_sideband_dbi_r_mode(exynos_pcie, true);
- val = readl(base + reg);
+ dw_pcie_read(base + reg, size, &val);
exynos_pcie_sideband_dbi_r_mode(exynos_pcie, false);
return val;
}
-static void exynos_pcie_writel_dbi(struct dw_pcie *pci, void __iomem *base,
- u32 reg, u32 val)
+static void exynos_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base,
+ u32 reg, int size, u32 val)
{
struct exynos_pcie *exynos_pcie = to_exynos_pcie(pci);
exynos_pcie_sideband_dbi_w_mode(exynos_pcie, true);
- writel(val, base + reg);
+ dw_pcie_write(base + reg, size, val);
exynos_pcie_sideband_dbi_w_mode(exynos_pcie, false);
}
@@ -530,8 +530,8 @@ static int __init exynos_add_pcie_port(struct exynos_pcie *exynos_pcie,
}
static const struct dw_pcie_ops dw_pcie_ops = {
- .readl_dbi = exynos_pcie_readl_dbi,
- .writel_dbi = exynos_pcie_writel_dbi,
+ .read_dbi = exynos_pcie_read_dbi,
+ .write_dbi = exynos_pcie_write_dbi,
.link_up = exynos_pcie_link_up,
};
diff --git a/drivers/pci/dwc/pci-imx6.c b/drivers/pci/dwc/pci-imx6.c
index ecc8690..08ebe62 100644
--- a/drivers/pci/dwc/pci-imx6.c
+++ b/drivers/pci/dwc/pci-imx6.c
@@ -104,7 +104,7 @@ static int pcie_phy_poll_ack(struct imx6_pcie *imx6_pcie, int exp_val)
u32 wait_counter = 0;
do {
- val = dw_pcie_readl_dbi(pci, base, PCIE_PHY_STAT);
+ val = dw_pcie_read_dbi(pci, base, PCIE_PHY_STAT, 0x4);
val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1;
wait_counter++;
@@ -125,17 +125,17 @@ static int pcie_phy_wait_ack(struct imx6_pcie *imx6_pcie, int addr)
int ret;
val = addr << PCIE_PHY_CTRL_DATA_LOC;
- dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, val);
+ dw_pcie_write_dbi(pci, base, PCIE_PHY_CTRL, 0x4, val);
val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC);
- dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, val);
+ dw_pcie_write_dbi(pci, base, PCIE_PHY_CTRL, 0x4, val);
ret = pcie_phy_poll_ack(imx6_pcie, 1);
if (ret)
return ret;
val = addr << PCIE_PHY_CTRL_DATA_LOC;
- dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, val);
+ dw_pcie_write_dbi(pci, base, PCIE_PHY_CTRL, 0x4, val);
return pcie_phy_poll_ack(imx6_pcie, 0);
}
@@ -154,17 +154,17 @@ static int pcie_phy_read(struct imx6_pcie *imx6_pcie, int addr, int *data)
/* assert Read signal */
phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC;
- dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, phy_ctl);
+ dw_pcie_write_dbi(pci, base, PCIE_PHY_CTRL, 0x4, phy_ctl);
ret = pcie_phy_poll_ack(imx6_pcie, 1);
if (ret)
return ret;
- val = dw_pcie_readl_dbi(pci, base, PCIE_PHY_STAT);
+ val = dw_pcie_read_dbi(pci, base, PCIE_PHY_STAT, 0x4);
*data = val & 0xffff;
/* deassert Read signal */
- dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, 0x00);
+ dw_pcie_write_dbi(pci, base, PCIE_PHY_CTRL, 0x4, 0x00);
return pcie_phy_poll_ack(imx6_pcie, 0);
}
@@ -183,11 +183,11 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
return ret;
var = data << PCIE_PHY_CTRL_DATA_LOC;
- dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, var);
+ dw_pcie_write_dbi(pci, base, PCIE_PHY_CTRL, 0x4, var);
/* capture data */
var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC);
- dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, var);
+ dw_pcie_write_dbi(pci, base, PCIE_PHY_CTRL, 0x4, var);
ret = pcie_phy_poll_ack(imx6_pcie, 1);
if (ret)
@@ -195,7 +195,7 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
/* deassert cap data */
var = data << PCIE_PHY_CTRL_DATA_LOC;
- dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, var);
+ dw_pcie_write_dbi(pci, base, PCIE_PHY_CTRL, 0x4, var);
/* wait for ack de-assertion */
ret = pcie_phy_poll_ack(imx6_pcie, 0);
@@ -204,7 +204,7 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
/* assert wr signal */
var = 0x1 << PCIE_PHY_CTRL_WR_LOC;
- dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, var);
+ dw_pcie_write_dbi(pci, base, PCIE_PHY_CTRL, 0x4, var);
/* wait for ack */
ret = pcie_phy_poll_ack(imx6_pcie, 1);
@@ -213,14 +213,14 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
/* deassert wr signal */
var = data << PCIE_PHY_CTRL_DATA_LOC;
- dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, var);
+ dw_pcie_write_dbi(pci, base, PCIE_PHY_CTRL, 0x4, var);
/* wait for ack de-assertion */
ret = pcie_phy_poll_ack(imx6_pcie, 0);
if (ret)
return ret;
- dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, 0x0);
+ dw_pcie_write_dbi(pci, base, PCIE_PHY_CTRL, 0x4, 0x0);
return 0;
}
@@ -289,10 +289,10 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
if ((gpr1 & IMX6Q_GPR1_PCIE_REF_CLK_EN) &&
(gpr12 & IMX6Q_GPR12_PCIE_CTL_2)) {
- val = dw_pcie_readl_dbi(pci, base, PCIE_PL_PFLR);
+ val = dw_pcie_read_dbi(pci, base, PCIE_PL_PFLR, 0x4);
val &= ~PCIE_PL_PFLR_LINK_STATE_MASK;
val |= PCIE_PL_PFLR_FORCE_LINK;
- dw_pcie_writel_dbi(pci, base, PCIE_PL_PFLR, val);
+ dw_pcie_write_dbi(pci, base, PCIE_PL_PFLR, 0x4, val);
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
@@ -454,8 +454,8 @@ static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie)
return 0;
dev_dbg(dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
- dw_pcie_readl_dbi(pci, base, PCIE_PHY_DEBUG_R0),
- dw_pcie_readl_dbi(pci, base, PCIE_PHY_DEBUG_R1));
+ dw_pcie_read_dbi(pci, base, PCIE_PHY_DEBUG_R0, 0x4),
+ dw_pcie_read_dbi(pci, base, PCIE_PHY_DEBUG_R1, 0x4));
return -ETIMEDOUT;
}
@@ -468,8 +468,8 @@ static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie)
unsigned int retries;
for (retries = 0; retries < 200; retries++) {
- tmp = dw_pcie_readl_dbi(pci, base,
- PCIE_LINK_WIDTH_SPEED_CONTROL);
+ tmp = dw_pcie_read_dbi(pci, base,
+ PCIE_LINK_WIDTH_SPEED_CONTROL, 0x4);
/* Test if the speed change finished. */
if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
return 0;
@@ -502,10 +502,10 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
* started in Gen2 mode, there is a possibility the devices on the
* bus will not be detected@all. This happens with PCIe switches.
*/
- tmp = dw_pcie_readl_dbi(pci, base, PCIE_RC_LCR);
+ tmp = dw_pcie_read_dbi(pci, base, PCIE_RC_LCR, 0x4);
tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1;
- dw_pcie_writel_dbi(pci, base, PCIE_RC_LCR, tmp);
+ dw_pcie_write_dbi(pci, base, PCIE_RC_LCR, 0x4, tmp);
/* Start LTSSM. */
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
@@ -519,10 +519,10 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
if (imx6_pcie->link_gen == 2) {
/* Allow Gen2 mode after the link is up. */
- tmp = dw_pcie_readl_dbi(pci, base, PCIE_RC_LCR);
+ tmp = dw_pcie_read_dbi(pci, base, PCIE_RC_LCR, 0x4);
tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2;
- dw_pcie_writel_dbi(pci, base, PCIE_RC_LCR, tmp);
+ dw_pcie_write_dbi(pci, base, PCIE_RC_LCR, 0x4, tmp);
} else {
dev_info(dev, "Link: Gen2 disabled\n");
}
@@ -531,9 +531,9 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
* Start Directed Speed Change so the best possible speed both link
* partners support can be negotiated.
*/
- tmp = dw_pcie_readl_dbi(pci, base, PCIE_LINK_WIDTH_SPEED_CONTROL);
+ tmp = dw_pcie_read_dbi(pci, base, PCIE_LINK_WIDTH_SPEED_CONTROL, 0x4);
tmp |= PORT_LOGIC_SPEED_CHANGE;
- dw_pcie_writel_dbi(pci, base, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
+ dw_pcie_write_dbi(pci, base, PCIE_LINK_WIDTH_SPEED_CONTROL, 0x4, tmp);
ret = imx6_pcie_wait_for_speed_change(imx6_pcie);
if (ret) {
@@ -548,14 +548,14 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
goto err_reset_phy;
}
- tmp = dw_pcie_readl_dbi(pci, base, PCIE_RC_LCSR);
+ tmp = dw_pcie_read_dbi(pci, base, PCIE_RC_LCSR, 0x4);
dev_info(dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf);
return 0;
err_reset_phy:
dev_dbg(dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n",
- dw_pcie_readl_dbi(pci, base, PCIE_PHY_DEBUG_R0),
- dw_pcie_readl_dbi(pci, base, PCIE_PHY_DEBUG_R1));
+ dw_pcie_read_dbi(pci, base, PCIE_PHY_DEBUG_R0, 0x4),
+ dw_pcie_read_dbi(pci, base, PCIE_PHY_DEBUG_R1, 0x4));
imx6_pcie_reset_phy(imx6_pcie);
return ret;
}
@@ -579,7 +579,7 @@ static int imx6_pcie_link_up(struct dw_pcie *pci)
{
void __iomem *base = pci->dbi_base;
- return dw_pcie_readl_dbi(pci, base, PCIE_PHY_DEBUG_R1) &
+ return dw_pcie_read_dbi(pci, base, PCIE_PHY_DEBUG_R1, 0x4) &
PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
}
diff --git a/drivers/pci/dwc/pci-keystone-dw.c b/drivers/pci/dwc/pci-keystone-dw.c
index 7220c04..8318efe 100644
--- a/drivers/pci/dwc/pci-keystone-dw.c
+++ b/drivers/pci/dwc/pci-keystone-dw.c
@@ -386,8 +386,8 @@ void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie)
/* Disable BARs for inbound access */
ks_dw_pcie_set_dbi_mode(ks_pcie);
- dw_pcie_writel_dbi(pci, base, PCI_BASE_ADDRESS_0, 0);
- dw_pcie_writel_dbi(pci, base, PCI_BASE_ADDRESS_1, 0);
+ dw_pcie_write_dbi(pci, base, PCI_BASE_ADDRESS_0, 0x4, 0);
+ dw_pcie_write_dbi(pci, base, PCI_BASE_ADDRESS_1, 0x4, 0);
ks_dw_pcie_clear_dbi_mode(ks_pcie);
/* Set outbound translation size per window division */
@@ -490,8 +490,8 @@ void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp)
ks_dw_pcie_set_dbi_mode(ks_pcie);
/* Enable BAR0 */
- dw_pcie_writel_dbi(pci, base, PCI_BASE_ADDRESS_0, 1);
- dw_pcie_writel_dbi(pci, base, PCI_BASE_ADDRESS_0, SZ_4K - 1);
+ dw_pcie_write_dbi(pci, base, PCI_BASE_ADDRESS_0, 0x4, 1);
+ dw_pcie_write_dbi(pci, base, PCI_BASE_ADDRESS_0, 0x4, SZ_4K - 1);
ks_dw_pcie_clear_dbi_mode(ks_pcie);
@@ -499,7 +499,8 @@ void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp)
* For BAR0, just setting bus address for inbound writes (MSI) should
* be sufficient. Use physical address to avoid any conflicts.
*/
- dw_pcie_writel_dbi(pci, base, PCI_BASE_ADDRESS_0, ks_pcie->app.start);
+ dw_pcie_write_dbi(pci, base, PCI_BASE_ADDRESS_0, 0x4,
+ ks_pcie->app.start);
}
/**
@@ -510,7 +511,7 @@ int ks_dw_pcie_link_up(struct dw_pcie *pci)
u32 val;
void __iomem *base = pci->dbi_base;
- val = dw_pcie_readl_dbi(pci, base, DEBUG0);
+ val = dw_pcie_read_dbi(pci, base, DEBUG0, 0x4);
return (val & LTSSM_STATE_MASK) == LTSSM_STATE_L0;
}
diff --git a/drivers/pci/dwc/pcie-armada8k.c b/drivers/pci/dwc/pcie-armada8k.c
index 2656f86..6067749 100644
--- a/drivers/pci/dwc/pcie-armada8k.c
+++ b/drivers/pci/dwc/pcie-armada8k.c
@@ -75,7 +75,7 @@ static int armada8k_pcie_link_up(struct dw_pcie *pci)
u32 mask = PCIE_GLB_STS_RDLH_LINK_UP | PCIE_GLB_STS_PHY_LINK_UP;
void __iomem *base = pci->dbi_base;
- reg = dw_pcie_readl_dbi(pci, base, PCIE_GLOBAL_STATUS_REG);
+ reg = dw_pcie_read_dbi(pci, base, PCIE_GLOBAL_STATUS_REG, 0x4);
if ((reg & mask) == mask)
return 1;
@@ -92,45 +92,45 @@ static void armada8k_pcie_establish_link(struct armada8k_pcie *pcie)
if (!dw_pcie_link_up(pci)) {
/* Disable LTSSM state machine to enable configuration */
- reg = dw_pcie_readl_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG);
+ reg = dw_pcie_read_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG, 0x4);
reg &= ~(PCIE_APP_LTSSM_EN);
- dw_pcie_writel_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG, reg);
+ dw_pcie_write_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG, 0x4, reg);
}
/* Set the device to root complex mode */
- reg = dw_pcie_readl_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG);
+ reg = dw_pcie_read_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG, 0x4);
reg &= ~(PCIE_DEVICE_TYPE_MASK << PCIE_DEVICE_TYPE_SHIFT);
reg |= PCIE_DEVICE_TYPE_RC << PCIE_DEVICE_TYPE_SHIFT;
- dw_pcie_writel_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG, reg);
+ dw_pcie_write_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG, 0x4, reg);
/* Set the PCIe master AxCache attributes */
- dw_pcie_writel_dbi(pci, base, PCIE_ARCACHE_TRC_REG,
- ARCACHE_DEFAULT_VALUE);
- dw_pcie_writel_dbi(pci, base, PCIE_AWCACHE_TRC_REG,
- AWCACHE_DEFAULT_VALUE);
+ dw_pcie_write_dbi(pci, base, PCIE_ARCACHE_TRC_REG, 0x4,
+ ARCACHE_DEFAULT_VALUE);
+ dw_pcie_write_dbi(pci, base, PCIE_AWCACHE_TRC_REG, 0x4,
+ AWCACHE_DEFAULT_VALUE);
/* Set the PCIe master AxDomain attributes */
- reg = dw_pcie_readl_dbi(pci, base, PCIE_ARUSER_REG);
+ reg = dw_pcie_read_dbi(pci, base, PCIE_ARUSER_REG, 0x4);
reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT);
reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT;
- dw_pcie_writel_dbi(pci, base, PCIE_ARUSER_REG, reg);
+ dw_pcie_write_dbi(pci, base, PCIE_ARUSER_REG, 0x4, reg);
- reg = dw_pcie_readl_dbi(pci, base, PCIE_AWUSER_REG);
+ reg = dw_pcie_read_dbi(pci, base, PCIE_AWUSER_REG, 0x4);
reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT);
reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT;
- dw_pcie_writel_dbi(pci, base, PCIE_AWUSER_REG, reg);
+ dw_pcie_write_dbi(pci, base, PCIE_AWUSER_REG, 0x4, reg);
/* Enable INT A-D interrupts */
- reg = dw_pcie_readl_dbi(pci, base, PCIE_GLOBAL_INT_MASK1_REG);
+ reg = dw_pcie_read_dbi(pci, base, PCIE_GLOBAL_INT_MASK1_REG, 0x4);
reg |= PCIE_INT_A_ASSERT_MASK | PCIE_INT_B_ASSERT_MASK |
PCIE_INT_C_ASSERT_MASK | PCIE_INT_D_ASSERT_MASK;
- dw_pcie_writel_dbi(pci, base, PCIE_GLOBAL_INT_MASK1_REG, reg);
+ dw_pcie_write_dbi(pci, base, PCIE_GLOBAL_INT_MASK1_REG, 0x4, reg);
if (!dw_pcie_link_up(pci)) {
/* Configuration done. Start LTSSM */
- reg = dw_pcie_readl_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG);
+ reg = dw_pcie_read_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG, 0x4);
reg |= PCIE_APP_LTSSM_EN;
- dw_pcie_writel_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG, reg);
+ dw_pcie_write_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG, 0x4, reg);
}
/* Wait until the link becomes active again */
@@ -159,8 +159,8 @@ static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg)
* PCI device. However, they are also latched into the PCIe
* controller, so we simply discard them.
*/
- val = dw_pcie_readl_dbi(pci, base, PCIE_GLOBAL_INT_CAUSE1_REG);
- dw_pcie_writel_dbi(pci, base, PCIE_GLOBAL_INT_CAUSE1_REG, val);
+ val = dw_pcie_read_dbi(pci, base, PCIE_GLOBAL_INT_CAUSE1_REG, 0x4);
+ dw_pcie_write_dbi(pci, base, PCIE_GLOBAL_INT_CAUSE1_REG, 0x4, val);
return IRQ_HANDLED;
}
diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c
index 4c7820a..a00b703 100644
--- a/drivers/pci/dwc/pcie-artpec6.c
+++ b/drivers/pci/dwc/pcie-artpec6.c
@@ -141,7 +141,7 @@ static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie)
* Enable writing to config regs. This is required as the Synopsys
* driver changes the class code. That register needs DBI write enable.
*/
- dw_pcie_writel_dbi(pci, base, MISC_CONTROL_1_OFF, DBI_RO_WR_EN);
+ dw_pcie_write_dbi(pci, base, MISC_CONTROL_1_OFF, 0x4, DBI_RO_WR_EN);
pp->io_base &= ARTPEC6_CPU_TO_BUS_ADDR;
pp->mem_base &= ARTPEC6_CPU_TO_BUS_ADDR;
@@ -161,8 +161,8 @@ static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie)
return 0;
dev_dbg(pci->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
- dw_pcie_readl_dbi(pci, base, PCIE_PHY_DEBUG_R0),
- dw_pcie_readl_dbi(pci, base, PCIE_PHY_DEBUG_R1));
+ dw_pcie_read_dbi(pci, base, PCIE_PHY_DEBUG_R0, 0x4),
+ dw_pcie_read_dbi(pci, base, PCIE_PHY_DEBUG_R1, 0x4));
return -ETIMEDOUT;
}
diff --git a/drivers/pci/dwc/pcie-designware-host.c b/drivers/pci/dwc/pcie-designware-host.c
index e3a5104..044fcda 100644
--- a/drivers/pci/dwc/pcie-designware-host.c
+++ b/drivers/pci/dwc/pcie-designware-host.c
@@ -572,27 +572,27 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
dw_pcie_setup(pci);
/* setup RC BARs */
- dw_pcie_writel_dbi(pci, base, PCI_BASE_ADDRESS_0, 0x00000004);
- dw_pcie_writel_dbi(pci, base, PCI_BASE_ADDRESS_1, 0x00000000);
+ dw_pcie_write_dbi(pci, base, PCI_BASE_ADDRESS_0, 0x4, 0x00000004);
+ dw_pcie_write_dbi(pci, base, PCI_BASE_ADDRESS_1, 0x4, 0x00000000);
/* setup interrupt pins */
- val = dw_pcie_readl_dbi(pci, base, PCI_INTERRUPT_LINE);
+ val = dw_pcie_read_dbi(pci, base, PCI_INTERRUPT_LINE, 0x4);
val &= 0xffff00ff;
val |= 0x00000100;
- dw_pcie_writel_dbi(pci, base, PCI_INTERRUPT_LINE, val);
+ dw_pcie_write_dbi(pci, base, PCI_INTERRUPT_LINE, 0x4, val);
/* setup bus numbers */
- val = dw_pcie_readl_dbi(pci, base, PCI_PRIMARY_BUS);
+ val = dw_pcie_read_dbi(pci, base, PCI_PRIMARY_BUS, 0x4);
val &= 0xff000000;
val |= 0x00010100;
- dw_pcie_writel_dbi(pci, base, PCI_PRIMARY_BUS, val);
+ dw_pcie_write_dbi(pci, base, PCI_PRIMARY_BUS, 0x4, val);
/* setup command register */
- val = dw_pcie_readl_dbi(pci, base, PCI_COMMAND);
+ val = dw_pcie_read_dbi(pci, base, PCI_COMMAND, 0x4);
val &= 0xffff0000;
val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
- dw_pcie_writel_dbi(pci, base, PCI_COMMAND, val);
+ dw_pcie_write_dbi(pci, base, PCI_COMMAND, 0x4, val);
/*
* If the platform provides ->rd_other_conf, it means the platform
diff --git a/drivers/pci/dwc/pcie-designware.c b/drivers/pci/dwc/pcie-designware.c
index db39f13..ef545e7 100644
--- a/drivers/pci/dwc/pcie-designware.c
+++ b/drivers/pci/dwc/pcie-designware.c
@@ -61,21 +61,35 @@ int dw_pcie_write(void __iomem *addr, int size, u32 val)
return PCIBIOS_SUCCESSFUL;
}
-u32 dw_pcie_readl_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg)
+u32 dw_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg,
+ int size)
{
- if (pci->ops->readl_dbi)
- return pci->ops->readl_dbi(pci, base, reg);
+ int ret;
+ u32 val;
+
+ if (pci->ops->read_dbi)
+ return pci->ops->read_dbi(pci, base, reg, size);
- return readl(base + reg);
+ ret = dw_pcie_read(base + reg, size, &val);
+ if (ret)
+ dev_err(pci->dev, "read DBI address failed\n");
+
+ return val;
}
-void dw_pcie_writel_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg,
- u32 val)
+void dw_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg,
+ int size, u32 val)
{
- if (pci->ops->writel_dbi)
- pci->ops->writel_dbi(pci, base, reg, val);
- else
- writel(val, base + reg);
+ int ret;
+
+ if (pci->ops->write_dbi) {
+ pci->ops->write_dbi(pci, base, reg, size, val);
+ return;
+ }
+
+ ret = dw_pcie_write(base + reg, size, val);
+ if (ret)
+ dev_err(pci->dev, "write DBI address failed\n");
}
static u32 dw_pcie_readl_unroll(struct dw_pcie *pci, void __iomem *base,
@@ -83,7 +97,7 @@ static u32 dw_pcie_readl_unroll(struct dw_pcie *pci, void __iomem *base,
{
u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
- return dw_pcie_readl_dbi(pci, base, offset + reg);
+ return dw_pcie_read_dbi(pci, base, offset + reg, 0x4);
}
static void dw_pcie_writel_unroll(struct dw_pcie *pci, void __iomem *base,
@@ -91,7 +105,7 @@ static void dw_pcie_writel_unroll(struct dw_pcie *pci, void __iomem *base,
{
u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
- dw_pcie_writel_dbi(pci, base, offset + reg, val);
+ dw_pcie_write_dbi(pci, base, offset + reg, 0x4, val);
}
void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
@@ -122,20 +136,21 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
PCIE_ATU_UNR_REGION_CTRL2,
PCIE_ATU_ENABLE);
} else {
- dw_pcie_writel_dbi(pci, base, PCIE_ATU_VIEWPORT,
- PCIE_ATU_REGION_OUTBOUND | index);
- dw_pcie_writel_dbi(pci, base, PCIE_ATU_LOWER_BASE,
- lower_32_bits(cpu_addr));
- dw_pcie_writel_dbi(pci, base, PCIE_ATU_UPPER_BASE,
- upper_32_bits(cpu_addr));
- dw_pcie_writel_dbi(pci, base, PCIE_ATU_LIMIT,
- lower_32_bits(cpu_addr + size - 1));
- dw_pcie_writel_dbi(pci, base, PCIE_ATU_LOWER_TARGET,
- lower_32_bits(pci_addr));
- dw_pcie_writel_dbi(pci, base, PCIE_ATU_UPPER_TARGET,
- upper_32_bits(pci_addr));
- dw_pcie_writel_dbi(pci, base, PCIE_ATU_CR1, type);
- dw_pcie_writel_dbi(pci, base, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
+ dw_pcie_write_dbi(pci, base, PCIE_ATU_VIEWPORT, 0x4,
+ PCIE_ATU_REGION_OUTBOUND | index);
+ dw_pcie_write_dbi(pci, base, PCIE_ATU_LOWER_BASE, 0x4,
+ lower_32_bits(cpu_addr));
+ dw_pcie_write_dbi(pci, base, PCIE_ATU_UPPER_BASE, 0x4,
+ upper_32_bits(cpu_addr));
+ dw_pcie_write_dbi(pci, base, PCIE_ATU_LIMIT, 0x4,
+ lower_32_bits(cpu_addr + size - 1));
+ dw_pcie_write_dbi(pci, base, PCIE_ATU_LOWER_TARGET, 0x4,
+ lower_32_bits(pci_addr));
+ dw_pcie_write_dbi(pci, base, PCIE_ATU_UPPER_TARGET, 0x4,
+ upper_32_bits(pci_addr));
+ dw_pcie_write_dbi(pci, base, PCIE_ATU_CR1, 0x4, type);
+ dw_pcie_write_dbi(pci, base, PCIE_ATU_CR2, 0x4,
+ PCIE_ATU_ENABLE);
}
/*
@@ -147,7 +162,7 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
val = dw_pcie_readl_unroll(pci, base, index,
PCIE_ATU_UNR_REGION_CTRL2);
else
- val = dw_pcie_readl_dbi(pci, base, PCIE_ATU_CR2);
+ val = dw_pcie_read_dbi(pci, base, PCIE_ATU_CR2, 0x4);
if (val == PCIE_ATU_ENABLE)
return;
@@ -192,7 +207,7 @@ static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci)
u32 val;
void __iomem *base = pci->dbi_base;
- val = dw_pcie_readl_dbi(pci, base, PCIE_ATU_VIEWPORT);
+ val = dw_pcie_read_dbi(pci, base, PCIE_ATU_VIEWPORT, 0x4);
if (val == 0xffffffff)
return 1;
@@ -218,7 +233,7 @@ void dw_pcie_setup(struct dw_pcie *pci)
lanes = 0;
/* set the number of lanes */
- val = dw_pcie_readl_dbi(pci, base, PCIE_PORT_LINK_CONTROL);
+ val = dw_pcie_read_dbi(pci, base, PCIE_PORT_LINK_CONTROL, 0x4);
val &= ~PORT_LINK_MODE_MASK;
switch (lanes) {
case 1:
@@ -237,10 +252,10 @@ void dw_pcie_setup(struct dw_pcie *pci)
dev_err(pci->dev, "num-lanes %u: invalid value\n", lanes);
return;
}
- dw_pcie_writel_dbi(pci, base, PCIE_PORT_LINK_CONTROL, val);
+ dw_pcie_write_dbi(pci, base, PCIE_PORT_LINK_CONTROL, 0x4, val);
/* set link width speed control register */
- val = dw_pcie_readl_dbi(pci, base, PCIE_LINK_WIDTH_SPEED_CONTROL);
+ val = dw_pcie_read_dbi(pci, base, PCIE_LINK_WIDTH_SPEED_CONTROL, 0x4);
val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
switch (lanes) {
case 1:
@@ -256,5 +271,5 @@ void dw_pcie_setup(struct dw_pcie *pci)
val |= PORT_LOGIC_LINK_WIDTH_8_LANES;
break;
}
- dw_pcie_writel_dbi(pci, base, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
+ dw_pcie_write_dbi(pci, base, PCIE_LINK_WIDTH_SPEED_CONTROL, 0x4, val);
}
diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h
index fe93f7f..0ef6ae7 100644
--- a/drivers/pci/dwc/pcie-designware.h
+++ b/drivers/pci/dwc/pcie-designware.h
@@ -144,9 +144,10 @@ struct pcie_port {
struct dw_pcie_ops {
u64 (*cpu_addr_fixup)(u64 cpu_addr);
- u32 (*readl_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg);
- void (*writel_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg,
- u32 val);
+ u32 (*read_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg,
+ int size);
+ void (*write_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg,
+ int size, u32 val);
int (*link_up)(struct dw_pcie *pcie);
};
@@ -164,9 +165,10 @@ struct dw_pcie {
int dw_pcie_read(void __iomem *addr, int size, u32 *val);
int dw_pcie_write(void __iomem *addr, int size, u32 val);
-u32 dw_pcie_readl_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg);
-void dw_pcie_writel_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg,
- u32 val);
+u32 dw_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg,
+ int size);
+void dw_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg,
+ int size, u32 val);
int dw_pcie_link_up(struct dw_pcie *pci);
int dw_pcie_wait_for_link(struct dw_pcie *pci);
void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index,
diff --git a/drivers/pci/dwc/pcie-hisi.c b/drivers/pci/dwc/pcie-hisi.c
index 560f6a4..f8f7d43 100644
--- a/drivers/pci/dwc/pcie-hisi.c
+++ b/drivers/pci/dwc/pcie-hisi.c
@@ -154,7 +154,7 @@ static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size,
walker += (where & 0x3);
reg = where & ~0x3;
- reg_val = dw_pcie_readl_dbi(pci, base, reg);
+ reg_val = dw_pcie_read_dbi(pci, base, reg, 0x4);
if (size == 1)
*val = *(u8 __force *) walker;
@@ -181,15 +181,15 @@ static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int size,
walker += (where & 0x3);
reg = where & ~0x3;
if (size == 4)
- dw_pcie_writel_dbi(pci, base, reg, val);
+ dw_pcie_write_dbi(pci, base, reg, 0x4, val);
else if (size == 2) {
- reg_val = dw_pcie_readl_dbi(pci, base, reg);
+ reg_val = dw_pcie_read_dbi(pci, base, reg, 0x4);
*(u16 __force *) walker = val;
- dw_pcie_writel_dbi(pci, base, reg, reg_val);
+ dw_pcie_write_dbi(pci, base, reg, 0x4, reg_val);
} else if (size == 1) {
- reg_val = dw_pcie_readl_dbi(pci, base, reg);
+ reg_val = dw_pcie_read_dbi(pci, base, reg, 0x4);
*(u8 __force *) walker = val;
- dw_pcie_writel_dbi(pci, base, reg, reg_val);
+ dw_pcie_write_dbi(pci, base, reg, 0x4, reg_val);
} else
return PCIBIOS_BAD_REGISTER_NUMBER;
@@ -212,7 +212,7 @@ static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie)
void __iomem *base = pci->dbi_base;
u32 val;
- val = dw_pcie_readl_dbi(pci, base, PCIE_SYS_STATE4);
+ val = dw_pcie_read_dbi(pci, base, PCIE_SYS_STATE4, 0x4);
return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
}
--
1.7.9.5
^ permalink raw reply related
* [PATCH 21/37] PCI: dwc: Modify dbi accessors to take dbi_base as argument
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
dwc has 2 dbi address space labelled dbics and dbics2. The existing
helper to access dbi address space can access only dbics. However
dbics2 has to be accessed for programming the BAR registers in the
case of EP mode. This is in preparation for adding EP mode support
to dwc driver.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
drivers/pci/dwc/pci-dra7xx.c | 10 +++--
drivers/pci/dwc/pci-exynos.c | 10 +++--
drivers/pci/dwc/pci-imx6.c | 67 ++++++++++++++++------------
drivers/pci/dwc/pci-keystone-dw.c | 15 ++++---
drivers/pci/dwc/pcie-armada8k.c | 39 +++++++++-------
drivers/pci/dwc/pcie-artpec6.c | 7 +--
drivers/pci/dwc/pcie-designware-host.c | 17 +++----
drivers/pci/dwc/pcie-designware.c | 76 ++++++++++++++++++--------------
drivers/pci/dwc/pcie-designware.h | 10 +++--
drivers/pci/dwc/pcie-hisi.c | 17 ++++---
10 files changed, 153 insertions(+), 115 deletions(-)
diff --git a/drivers/pci/dwc/pci-dra7xx.c b/drivers/pci/dwc/pci-dra7xx.c
index 3c525b0..76d0b40 100644
--- a/drivers/pci/dwc/pci-dra7xx.c
+++ b/drivers/pci/dwc/pci-dra7xx.c
@@ -494,12 +494,13 @@ static int dra7xx_pcie_suspend(struct device *dev)
{
struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev);
struct dw_pcie *pci = dra7xx->pci;
+ void __iomem *base = pci->dbi_base;
u32 val;
/* clear MSE */
- val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
+ val = dw_pcie_readl_dbi(pci, base, PCI_COMMAND);
val &= ~PCI_COMMAND_MEMORY;
- dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
+ dw_pcie_writel_dbi(pci, base, PCI_COMMAND, val);
return 0;
}
@@ -508,12 +509,13 @@ static int dra7xx_pcie_resume(struct device *dev)
{
struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev);
struct dw_pcie *pci = dra7xx->pci;
+ void __iomem *base = pci->dbi_base;
u32 val;
/* set MSE */
- val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
+ val = dw_pcie_readl_dbi(pci, base, PCI_COMMAND);
val |= PCI_COMMAND_MEMORY;
- dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
+ dw_pcie_writel_dbi(pci, base, PCI_COMMAND, val);
return 0;
}
diff --git a/drivers/pci/dwc/pci-exynos.c b/drivers/pci/dwc/pci-exynos.c
index 0295ec9..a109cf0 100644
--- a/drivers/pci/dwc/pci-exynos.c
+++ b/drivers/pci/dwc/pci-exynos.c
@@ -405,23 +405,25 @@ static void exynos_pcie_enable_interrupts(struct exynos_pcie *exynos_pcie)
exynos_pcie_msi_init(exynos_pcie);
}
-static u32 exynos_pcie_readl_dbi(struct dw_pcie *pci, u32 reg)
+static u32 exynos_pcie_readl_dbi(struct dw_pcie *pci, void __iomem *base,
+ u32 reg)
{
struct exynos_pcie *exynos_pcie = to_exynos_pcie(pci);
u32 val;
exynos_pcie_sideband_dbi_r_mode(exynos_pcie, true);
- val = readl(pci->dbi_base + reg);
+ val = readl(base + reg);
exynos_pcie_sideband_dbi_r_mode(exynos_pcie, false);
return val;
}
-static void exynos_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val)
+static void exynos_pcie_writel_dbi(struct dw_pcie *pci, void __iomem *base,
+ u32 reg, u32 val)
{
struct exynos_pcie *exynos_pcie = to_exynos_pcie(pci);
exynos_pcie_sideband_dbi_w_mode(exynos_pcie, true);
- writel(val, pci->dbi_base + reg);
+ writel(val, base + reg);
exynos_pcie_sideband_dbi_w_mode(exynos_pcie, false);
}
diff --git a/drivers/pci/dwc/pci-imx6.c b/drivers/pci/dwc/pci-imx6.c
index 70fa380..ecc8690 100644
--- a/drivers/pci/dwc/pci-imx6.c
+++ b/drivers/pci/dwc/pci-imx6.c
@@ -98,12 +98,13 @@ struct imx6_pcie {
static int pcie_phy_poll_ack(struct imx6_pcie *imx6_pcie, int exp_val)
{
struct dw_pcie *pci = imx6_pcie->pci;
+ void __iomem *base = pci->dbi_base;
u32 val;
u32 max_iterations = 10;
u32 wait_counter = 0;
do {
- val = dw_pcie_readl_dbi(pci, PCIE_PHY_STAT);
+ val = dw_pcie_readl_dbi(pci, base, PCIE_PHY_STAT);
val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1;
wait_counter++;
@@ -119,21 +120,22 @@ static int pcie_phy_poll_ack(struct imx6_pcie *imx6_pcie, int exp_val)
static int pcie_phy_wait_ack(struct imx6_pcie *imx6_pcie, int addr)
{
struct dw_pcie *pci = imx6_pcie->pci;
+ void __iomem *base = pci->dbi_base;
u32 val;
int ret;
val = addr << PCIE_PHY_CTRL_DATA_LOC;
- dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val);
+ dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, val);
val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC);
- dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val);
+ dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, val);
ret = pcie_phy_poll_ack(imx6_pcie, 1);
if (ret)
return ret;
val = addr << PCIE_PHY_CTRL_DATA_LOC;
- dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, val);
+ dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, val);
return pcie_phy_poll_ack(imx6_pcie, 0);
}
@@ -142,6 +144,7 @@ static int pcie_phy_wait_ack(struct imx6_pcie *imx6_pcie, int addr)
static int pcie_phy_read(struct imx6_pcie *imx6_pcie, int addr, int *data)
{
struct dw_pcie *pci = imx6_pcie->pci;
+ void __iomem *base = pci->dbi_base;
u32 val, phy_ctl;
int ret;
@@ -151,17 +154,17 @@ static int pcie_phy_read(struct imx6_pcie *imx6_pcie, int addr, int *data)
/* assert Read signal */
phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC;
- dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, phy_ctl);
+ dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, phy_ctl);
ret = pcie_phy_poll_ack(imx6_pcie, 1);
if (ret)
return ret;
- val = dw_pcie_readl_dbi(pci, PCIE_PHY_STAT);
+ val = dw_pcie_readl_dbi(pci, base, PCIE_PHY_STAT);
*data = val & 0xffff;
/* deassert Read signal */
- dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, 0x00);
+ dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, 0x00);
return pcie_phy_poll_ack(imx6_pcie, 0);
}
@@ -169,6 +172,7 @@ static int pcie_phy_read(struct imx6_pcie *imx6_pcie, int addr, int *data)
static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
{
struct dw_pcie *pci = imx6_pcie->pci;
+ void __iomem *base = pci->dbi_base;
u32 var;
int ret;
@@ -179,11 +183,11 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
return ret;
var = data << PCIE_PHY_CTRL_DATA_LOC;
- dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var);
+ dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, var);
/* capture data */
var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC);
- dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var);
+ dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, var);
ret = pcie_phy_poll_ack(imx6_pcie, 1);
if (ret)
@@ -191,7 +195,7 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
/* deassert cap data */
var = data << PCIE_PHY_CTRL_DATA_LOC;
- dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var);
+ dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, var);
/* wait for ack de-assertion */
ret = pcie_phy_poll_ack(imx6_pcie, 0);
@@ -200,7 +204,7 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
/* assert wr signal */
var = 0x1 << PCIE_PHY_CTRL_WR_LOC;
- dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var);
+ dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, var);
/* wait for ack */
ret = pcie_phy_poll_ack(imx6_pcie, 1);
@@ -209,14 +213,14 @@ static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
/* deassert wr signal */
var = data << PCIE_PHY_CTRL_DATA_LOC;
- dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, var);
+ dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, var);
/* wait for ack de-assertion */
ret = pcie_phy_poll_ack(imx6_pcie, 0);
if (ret)
return ret;
- dw_pcie_writel_dbi(pci, PCIE_PHY_CTRL, 0x0);
+ dw_pcie_writel_dbi(pci, base, PCIE_PHY_CTRL, 0x0);
return 0;
}
@@ -248,6 +252,7 @@ static int imx6q_pcie_abort_handler(unsigned long addr,
static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
{
struct dw_pcie *pci = imx6_pcie->pci;
+ void __iomem *base = pci->dbi_base;
u32 val, gpr1, gpr12;
switch (imx6_pcie->variant) {
@@ -284,10 +289,10 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
if ((gpr1 & IMX6Q_GPR1_PCIE_REF_CLK_EN) &&
(gpr12 & IMX6Q_GPR12_PCIE_CTL_2)) {
- val = dw_pcie_readl_dbi(pci, PCIE_PL_PFLR);
+ val = dw_pcie_readl_dbi(pci, base, PCIE_PL_PFLR);
val &= ~PCIE_PL_PFLR_LINK_STATE_MASK;
val |= PCIE_PL_PFLR_FORCE_LINK;
- dw_pcie_writel_dbi(pci, PCIE_PL_PFLR, val);
+ dw_pcie_writel_dbi(pci, base, PCIE_PL_PFLR, val);
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
@@ -441,6 +446,7 @@ static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie)
{
struct dw_pcie *pci = imx6_pcie->pci;
+ void __iomem *base = pci->dbi_base;
struct device *dev = pci->dev;
/* check if the link is up or not */
@@ -448,20 +454,22 @@ static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie)
return 0;
dev_dbg(dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
- dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0),
- dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1));
+ dw_pcie_readl_dbi(pci, base, PCIE_PHY_DEBUG_R0),
+ dw_pcie_readl_dbi(pci, base, PCIE_PHY_DEBUG_R1));
return -ETIMEDOUT;
}
static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie)
{
struct dw_pcie *pci = imx6_pcie->pci;
+ void __iomem *base = pci->dbi_base;
struct device *dev = pci->dev;
u32 tmp;
unsigned int retries;
for (retries = 0; retries < 200; retries++) {
- tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+ tmp = dw_pcie_readl_dbi(pci, base,
+ PCIE_LINK_WIDTH_SPEED_CONTROL);
/* Test if the speed change finished. */
if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
return 0;
@@ -484,6 +492,7 @@ static irqreturn_t imx6_pcie_msi_handler(int irq, void *arg)
static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
{
struct dw_pcie *pci = imx6_pcie->pci;
+ void __iomem *base = pci->dbi_base;
struct device *dev = pci->dev;
u32 tmp;
int ret;
@@ -493,10 +502,10 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
* started in Gen2 mode, there is a possibility the devices on the
* bus will not be detected at all. This happens with PCIe switches.
*/
- tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCR);
+ tmp = dw_pcie_readl_dbi(pci, base, PCIE_RC_LCR);
tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1;
- dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp);
+ dw_pcie_writel_dbi(pci, base, PCIE_RC_LCR, tmp);
/* Start LTSSM. */
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
@@ -510,10 +519,10 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
if (imx6_pcie->link_gen == 2) {
/* Allow Gen2 mode after the link is up. */
- tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCR);
+ tmp = dw_pcie_readl_dbi(pci, base, PCIE_RC_LCR);
tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2;
- dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp);
+ dw_pcie_writel_dbi(pci, base, PCIE_RC_LCR, tmp);
} else {
dev_info(dev, "Link: Gen2 disabled\n");
}
@@ -522,9 +531,9 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
* Start Directed Speed Change so the best possible speed both link
* partners support can be negotiated.
*/
- tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+ tmp = dw_pcie_readl_dbi(pci, base, PCIE_LINK_WIDTH_SPEED_CONTROL);
tmp |= PORT_LOGIC_SPEED_CHANGE;
- dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
+ dw_pcie_writel_dbi(pci, base, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
ret = imx6_pcie_wait_for_speed_change(imx6_pcie);
if (ret) {
@@ -539,14 +548,14 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
goto err_reset_phy;
}
- tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCSR);
+ tmp = dw_pcie_readl_dbi(pci, base, PCIE_RC_LCSR);
dev_info(dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf);
return 0;
err_reset_phy:
dev_dbg(dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n",
- dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0),
- dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1));
+ dw_pcie_readl_dbi(pci, base, PCIE_PHY_DEBUG_R0),
+ dw_pcie_readl_dbi(pci, base, PCIE_PHY_DEBUG_R1));
imx6_pcie_reset_phy(imx6_pcie);
return ret;
}
@@ -568,7 +577,9 @@ static void imx6_pcie_host_init(struct pcie_port *pp)
static int imx6_pcie_link_up(struct dw_pcie *pci)
{
- return dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1) &
+ void __iomem *base = pci->dbi_base;
+
+ return dw_pcie_readl_dbi(pci, base, PCIE_PHY_DEBUG_R1) &
PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
}
diff --git a/drivers/pci/dwc/pci-keystone-dw.c b/drivers/pci/dwc/pci-keystone-dw.c
index 6b396f6..7220c04 100644
--- a/drivers/pci/dwc/pci-keystone-dw.c
+++ b/drivers/pci/dwc/pci-keystone-dw.c
@@ -378,6 +378,7 @@ static void ks_dw_pcie_clear_dbi_mode(struct keystone_pcie *ks_pcie)
void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie)
{
struct dw_pcie *pci = ks_pcie->pci;
+ void __iomem *base = pci->dbi_base;
struct pcie_port *pp = &pci->pp;
u32 start = pp->mem->start, end = pp->mem->end;
int i, tr_size;
@@ -385,8 +386,8 @@ void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie)
/* Disable BARs for inbound access */
ks_dw_pcie_set_dbi_mode(ks_pcie);
- dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0);
- dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0);
+ dw_pcie_writel_dbi(pci, base, PCI_BASE_ADDRESS_0, 0);
+ dw_pcie_writel_dbi(pci, base, PCI_BASE_ADDRESS_1, 0);
ks_dw_pcie_clear_dbi_mode(ks_pcie);
/* Set outbound translation size per window division */
@@ -482,14 +483,15 @@ int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ void __iomem *base = pci->dbi_base;
struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);
/* Configure and set up BAR0 */
ks_dw_pcie_set_dbi_mode(ks_pcie);
/* Enable BAR0 */
- dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 1);
- dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, SZ_4K - 1);
+ dw_pcie_writel_dbi(pci, base, PCI_BASE_ADDRESS_0, 1);
+ dw_pcie_writel_dbi(pci, base, PCI_BASE_ADDRESS_0, SZ_4K - 1);
ks_dw_pcie_clear_dbi_mode(ks_pcie);
@@ -497,7 +499,7 @@ void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp)
* For BAR0, just setting bus address for inbound writes (MSI) should
* be sufficient. Use physical address to avoid any conflicts.
*/
- dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, ks_pcie->app.start);
+ dw_pcie_writel_dbi(pci, base, PCI_BASE_ADDRESS_0, ks_pcie->app.start);
}
/**
@@ -506,8 +508,9 @@ void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp)
int ks_dw_pcie_link_up(struct dw_pcie *pci)
{
u32 val;
+ void __iomem *base = pci->dbi_base;
- val = dw_pcie_readl_dbi(pci, DEBUG0);
+ val = dw_pcie_readl_dbi(pci, base, DEBUG0);
return (val & LTSSM_STATE_MASK) == LTSSM_STATE_L0;
}
diff --git a/drivers/pci/dwc/pcie-armada8k.c b/drivers/pci/dwc/pcie-armada8k.c
index 66bac6f..2656f86 100644
--- a/drivers/pci/dwc/pcie-armada8k.c
+++ b/drivers/pci/dwc/pcie-armada8k.c
@@ -73,8 +73,9 @@ static int armada8k_pcie_link_up(struct dw_pcie *pci)
{
u32 reg;
u32 mask = PCIE_GLB_STS_RDLH_LINK_UP | PCIE_GLB_STS_PHY_LINK_UP;
+ void __iomem *base = pci->dbi_base;
- reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_STATUS_REG);
+ reg = dw_pcie_readl_dbi(pci, base, PCIE_GLOBAL_STATUS_REG);
if ((reg & mask) == mask)
return 1;
@@ -86,47 +87,50 @@ static int armada8k_pcie_link_up(struct dw_pcie *pci)
static void armada8k_pcie_establish_link(struct armada8k_pcie *pcie)
{
struct dw_pcie *pci = pcie->pci;
+ void __iomem *base = pci->dbi_base;
u32 reg;
if (!dw_pcie_link_up(pci)) {
/* Disable LTSSM state machine to enable configuration */
- reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG);
+ reg = dw_pcie_readl_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG);
reg &= ~(PCIE_APP_LTSSM_EN);
- dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg);
+ dw_pcie_writel_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG, reg);
}
/* Set the device to root complex mode */
- reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG);
+ reg = dw_pcie_readl_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG);
reg &= ~(PCIE_DEVICE_TYPE_MASK << PCIE_DEVICE_TYPE_SHIFT);
reg |= PCIE_DEVICE_TYPE_RC << PCIE_DEVICE_TYPE_SHIFT;
- dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg);
+ dw_pcie_writel_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG, reg);
/* Set the PCIe master AxCache attributes */
- dw_pcie_writel_dbi(pci, PCIE_ARCACHE_TRC_REG, ARCACHE_DEFAULT_VALUE);
- dw_pcie_writel_dbi(pci, PCIE_AWCACHE_TRC_REG, AWCACHE_DEFAULT_VALUE);
+ dw_pcie_writel_dbi(pci, base, PCIE_ARCACHE_TRC_REG,
+ ARCACHE_DEFAULT_VALUE);
+ dw_pcie_writel_dbi(pci, base, PCIE_AWCACHE_TRC_REG,
+ AWCACHE_DEFAULT_VALUE);
/* Set the PCIe master AxDomain attributes */
- reg = dw_pcie_readl_dbi(pci, PCIE_ARUSER_REG);
+ reg = dw_pcie_readl_dbi(pci, base, PCIE_ARUSER_REG);
reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT);
reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT;
- dw_pcie_writel_dbi(pci, PCIE_ARUSER_REG, reg);
+ dw_pcie_writel_dbi(pci, base, PCIE_ARUSER_REG, reg);
- reg = dw_pcie_readl_dbi(pci, PCIE_AWUSER_REG);
+ reg = dw_pcie_readl_dbi(pci, base, PCIE_AWUSER_REG);
reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT);
reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT;
- dw_pcie_writel_dbi(pci, PCIE_AWUSER_REG, reg);
+ dw_pcie_writel_dbi(pci, base, PCIE_AWUSER_REG, reg);
/* Enable INT A-D interrupts */
- reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG);
+ reg = dw_pcie_readl_dbi(pci, base, PCIE_GLOBAL_INT_MASK1_REG);
reg |= PCIE_INT_A_ASSERT_MASK | PCIE_INT_B_ASSERT_MASK |
PCIE_INT_C_ASSERT_MASK | PCIE_INT_D_ASSERT_MASK;
- dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_MASK1_REG, reg);
+ dw_pcie_writel_dbi(pci, base, PCIE_GLOBAL_INT_MASK1_REG, reg);
if (!dw_pcie_link_up(pci)) {
/* Configuration done. Start LTSSM */
- reg = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_CONTROL_REG);
+ reg = dw_pcie_readl_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG);
reg |= PCIE_APP_LTSSM_EN;
- dw_pcie_writel_dbi(pci, PCIE_GLOBAL_CONTROL_REG, reg);
+ dw_pcie_writel_dbi(pci, base, PCIE_GLOBAL_CONTROL_REG, reg);
}
/* Wait until the link becomes active again */
@@ -147,6 +151,7 @@ static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg)
{
struct armada8k_pcie *pcie = arg;
struct dw_pcie *pci = pcie->pci;
+ void __iomem *base = pci->dbi_base;
u32 val;
/*
@@ -154,8 +159,8 @@ static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg)
* PCI device. However, they are also latched into the PCIe
* controller, so we simply discard them.
*/
- val = dw_pcie_readl_dbi(pci, PCIE_GLOBAL_INT_CAUSE1_REG);
- dw_pcie_writel_dbi(pci, PCIE_GLOBAL_INT_CAUSE1_REG, val);
+ val = dw_pcie_readl_dbi(pci, base, PCIE_GLOBAL_INT_CAUSE1_REG);
+ dw_pcie_writel_dbi(pci, base, PCIE_GLOBAL_INT_CAUSE1_REG, val);
return IRQ_HANDLED;
}
diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c
index 59ecc9e..4c7820a 100644
--- a/drivers/pci/dwc/pcie-artpec6.c
+++ b/drivers/pci/dwc/pcie-artpec6.c
@@ -81,6 +81,7 @@ static void artpec6_pcie_writel(struct artpec6_pcie *artpec6_pcie, u32 offset, u
static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie)
{
struct dw_pcie *pci = artpec6_pcie->pci;
+ void __iomem *base = pci->dbi_base;
struct pcie_port *pp = &pci->pp;
u32 val;
unsigned int retries;
@@ -140,7 +141,7 @@ static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie)
* Enable writing to config regs. This is required as the Synopsys
* driver changes the class code. That register needs DBI write enable.
*/
- dw_pcie_writel_dbi(pci, MISC_CONTROL_1_OFF, DBI_RO_WR_EN);
+ dw_pcie_writel_dbi(pci, base, MISC_CONTROL_1_OFF, DBI_RO_WR_EN);
pp->io_base &= ARTPEC6_CPU_TO_BUS_ADDR;
pp->mem_base &= ARTPEC6_CPU_TO_BUS_ADDR;
@@ -160,8 +161,8 @@ static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie)
return 0;
dev_dbg(pci->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
- dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R0),
- dw_pcie_readl_dbi(pci, PCIE_PHY_DEBUG_R1));
+ dw_pcie_readl_dbi(pci, base, PCIE_PHY_DEBUG_R0),
+ dw_pcie_readl_dbi(pci, base, PCIE_PHY_DEBUG_R1));
return -ETIMEDOUT;
}
diff --git a/drivers/pci/dwc/pcie-designware-host.c b/drivers/pci/dwc/pcie-designware-host.c
index e7eb653..e3a5104 100644
--- a/drivers/pci/dwc/pcie-designware-host.c
+++ b/drivers/pci/dwc/pcie-designware-host.c
@@ -567,31 +567,32 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
{
u32 val;
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ void __iomem *base = pci->dbi_base;
dw_pcie_setup(pci);
/* setup RC BARs */
- dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004);
- dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000);
+ dw_pcie_writel_dbi(pci, base, PCI_BASE_ADDRESS_0, 0x00000004);
+ dw_pcie_writel_dbi(pci, base, PCI_BASE_ADDRESS_1, 0x00000000);
/* setup interrupt pins */
- val = dw_pcie_readl_dbi(pci, PCI_INTERRUPT_LINE);
+ val = dw_pcie_readl_dbi(pci, base, PCI_INTERRUPT_LINE);
val &= 0xffff00ff;
val |= 0x00000100;
- dw_pcie_writel_dbi(pci, PCI_INTERRUPT_LINE, val);
+ dw_pcie_writel_dbi(pci, base, PCI_INTERRUPT_LINE, val);
/* setup bus numbers */
- val = dw_pcie_readl_dbi(pci, PCI_PRIMARY_BUS);
+ val = dw_pcie_readl_dbi(pci, base, PCI_PRIMARY_BUS);
val &= 0xff000000;
val |= 0x00010100;
- dw_pcie_writel_dbi(pci, PCI_PRIMARY_BUS, val);
+ dw_pcie_writel_dbi(pci, base, PCI_PRIMARY_BUS, val);
/* setup command register */
- val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
+ val = dw_pcie_readl_dbi(pci, base, PCI_COMMAND);
val &= 0xffff0000;
val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
- dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
+ dw_pcie_writel_dbi(pci, base, PCI_COMMAND, val);
/*
* If the platform provides ->rd_other_conf, it means the platform
diff --git a/drivers/pci/dwc/pcie-designware.c b/drivers/pci/dwc/pcie-designware.c
index fb020ee..db39f13 100644
--- a/drivers/pci/dwc/pcie-designware.c
+++ b/drivers/pci/dwc/pcie-designware.c
@@ -61,75 +61,81 @@ int dw_pcie_write(void __iomem *addr, int size, u32 val)
return PCIBIOS_SUCCESSFUL;
}
-u32 dw_pcie_readl_dbi(struct dw_pcie *pci, u32 reg)
+u32 dw_pcie_readl_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg)
{
if (pci->ops->readl_dbi)
- return pci->ops->readl_dbi(pci, reg);
+ return pci->ops->readl_dbi(pci, base, reg);
- return readl(pci->dbi_base + reg);
+ return readl(base + reg);
}
-void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val)
+void dw_pcie_writel_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg,
+ u32 val)
{
if (pci->ops->writel_dbi)
- pci->ops->writel_dbi(pci, reg, val);
+ pci->ops->writel_dbi(pci, base, reg, val);
else
- writel(val, pci->dbi_base + reg);
+ writel(val, base + reg);
}
-static u32 dw_pcie_readl_unroll(struct dw_pcie *pci, u32 index, u32 reg)
+static u32 dw_pcie_readl_unroll(struct dw_pcie *pci, void __iomem *base,
+ u32 index, u32 reg)
{
u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
- return dw_pcie_readl_dbi(pci, offset + reg);
+ return dw_pcie_readl_dbi(pci, base, offset + reg);
}
-static void dw_pcie_writel_unroll(struct dw_pcie *pci, u32 index, u32 reg,
- u32 val)
+static void dw_pcie_writel_unroll(struct dw_pcie *pci, void __iomem *base,
+ u32 index, u32 reg, u32 val)
{
u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
- dw_pcie_writel_dbi(pci, offset + reg, val);
+ dw_pcie_writel_dbi(pci, base, offset + reg, val);
}
void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
u64 cpu_addr, u64 pci_addr, u32 size)
{
u32 retries, val;
+ void __iomem *base = pci->dbi_base;
if (pci->ops->cpu_addr_fixup)
cpu_addr = pci->ops->cpu_addr_fixup(cpu_addr);
if (pci->iatu_unroll_enabled) {
- dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE,
+ dw_pcie_writel_unroll(pci, base, index, PCIE_ATU_UNR_LOWER_BASE,
lower_32_bits(cpu_addr));
- dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE,
+ dw_pcie_writel_unroll(pci, base, index, PCIE_ATU_UNR_UPPER_BASE,
upper_32_bits(cpu_addr));
- dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_LIMIT,
+ dw_pcie_writel_unroll(pci, base, index, PCIE_ATU_UNR_LIMIT,
lower_32_bits(cpu_addr + size - 1));
- dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET,
+ dw_pcie_writel_unroll(pci, base, index,
+ PCIE_ATU_UNR_LOWER_TARGET,
lower_32_bits(pci_addr));
- dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET,
+ dw_pcie_writel_unroll(pci, base, index,
+ PCIE_ATU_UNR_UPPER_TARGET,
upper_32_bits(pci_addr));
- dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1,
- type);
- dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
+ dw_pcie_writel_unroll(pci, base, index,
+ PCIE_ATU_UNR_REGION_CTRL1, type);
+ dw_pcie_writel_unroll(pci, base, index,
+ PCIE_ATU_UNR_REGION_CTRL2,
PCIE_ATU_ENABLE);
} else {
- dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT,
+ dw_pcie_writel_dbi(pci, base, PCIE_ATU_VIEWPORT,
PCIE_ATU_REGION_OUTBOUND | index);
- dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE,
+ dw_pcie_writel_dbi(pci, base, PCIE_ATU_LOWER_BASE,
lower_32_bits(cpu_addr));
- dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE,
+ dw_pcie_writel_dbi(pci, base, PCIE_ATU_UPPER_BASE,
upper_32_bits(cpu_addr));
- dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT,
+ dw_pcie_writel_dbi(pci, base, PCIE_ATU_LIMIT,
lower_32_bits(cpu_addr + size - 1));
- dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET,
+ dw_pcie_writel_dbi(pci, base, PCIE_ATU_LOWER_TARGET,
lower_32_bits(pci_addr));
- dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET,
+ dw_pcie_writel_dbi(pci, base, PCIE_ATU_UPPER_TARGET,
upper_32_bits(pci_addr));
- dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type);
- dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
+ dw_pcie_writel_dbi(pci, base, PCIE_ATU_CR1, type);
+ dw_pcie_writel_dbi(pci, base, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
}
/*
@@ -138,10 +144,10 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
*/
for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
if (pci->iatu_unroll_enabled)
- val = dw_pcie_readl_unroll(pci, index,
+ val = dw_pcie_readl_unroll(pci, base, index,
PCIE_ATU_UNR_REGION_CTRL2);
else
- val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2);
+ val = dw_pcie_readl_dbi(pci, base, PCIE_ATU_CR2);
if (val == PCIE_ATU_ENABLE)
return;
@@ -184,8 +190,9 @@ int dw_pcie_link_up(struct dw_pcie *pci)
static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci)
{
u32 val;
+ void __iomem *base = pci->dbi_base;
- val = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT);
+ val = dw_pcie_readl_dbi(pci, base, PCIE_ATU_VIEWPORT);
if (val == 0xffffffff)
return 1;
@@ -199,6 +206,7 @@ void dw_pcie_setup(struct dw_pcie *pci)
u32 lanes;
struct device *dev = pci->dev;
struct device_node *np = dev->of_node;
+ void __iomem *base = pci->dbi_base;
/* get iATU unroll support */
pci->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pci);
@@ -210,7 +218,7 @@ void dw_pcie_setup(struct dw_pcie *pci)
lanes = 0;
/* set the number of lanes */
- val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL);
+ val = dw_pcie_readl_dbi(pci, base, PCIE_PORT_LINK_CONTROL);
val &= ~PORT_LINK_MODE_MASK;
switch (lanes) {
case 1:
@@ -229,10 +237,10 @@ void dw_pcie_setup(struct dw_pcie *pci)
dev_err(pci->dev, "num-lanes %u: invalid value\n", lanes);
return;
}
- dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val);
+ dw_pcie_writel_dbi(pci, base, PCIE_PORT_LINK_CONTROL, val);
/* set link width speed control register */
- val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+ val = dw_pcie_readl_dbi(pci, base, PCIE_LINK_WIDTH_SPEED_CONTROL);
val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
switch (lanes) {
case 1:
@@ -248,5 +256,5 @@ void dw_pcie_setup(struct dw_pcie *pci)
val |= PORT_LOGIC_LINK_WIDTH_8_LANES;
break;
}
- dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
+ dw_pcie_writel_dbi(pci, base, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
}
diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h
index 8f3dcb2..fe93f7f 100644
--- a/drivers/pci/dwc/pcie-designware.h
+++ b/drivers/pci/dwc/pcie-designware.h
@@ -144,8 +144,9 @@ struct pcie_port {
struct dw_pcie_ops {
u64 (*cpu_addr_fixup)(u64 cpu_addr);
- u32 (*readl_dbi)(struct dw_pcie *pcie, u32 reg);
- void (*writel_dbi)(struct dw_pcie *pcie, u32 reg, u32 val);
+ u32 (*readl_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg);
+ void (*writel_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg,
+ u32 val);
int (*link_up)(struct dw_pcie *pcie);
};
@@ -163,8 +164,9 @@ struct dw_pcie {
int dw_pcie_read(void __iomem *addr, int size, u32 *val);
int dw_pcie_write(void __iomem *addr, int size, u32 val);
-u32 dw_pcie_readl_dbi(struct dw_pcie *pci, u32 reg);
-void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val);
+u32 dw_pcie_readl_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg);
+void dw_pcie_writel_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg,
+ u32 val);
int dw_pcie_link_up(struct dw_pcie *pci);
int dw_pcie_wait_for_link(struct dw_pcie *pci);
void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index,
diff --git a/drivers/pci/dwc/pcie-hisi.c b/drivers/pci/dwc/pcie-hisi.c
index 386467a..560f6a4 100644
--- a/drivers/pci/dwc/pcie-hisi.c
+++ b/drivers/pci/dwc/pcie-hisi.c
@@ -150,10 +150,11 @@ static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size,
u32 reg_val;
void *walker = ®_val;
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ void __iomem *base = pci->dbi_base;
walker += (where & 0x3);
reg = where & ~0x3;
- reg_val = dw_pcie_readl_dbi(pci, reg);
+ reg_val = dw_pcie_readl_dbi(pci, base, reg);
if (size == 1)
*val = *(u8 __force *) walker;
@@ -175,19 +176,20 @@ static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int size,
u32 reg;
void *walker = ®_val;
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ void __iomem *base = pci->dbi_base;
walker += (where & 0x3);
reg = where & ~0x3;
if (size == 4)
- dw_pcie_writel_dbi(pci, reg, val);
+ dw_pcie_writel_dbi(pci, base, reg, val);
else if (size == 2) {
- reg_val = dw_pcie_readl_dbi(pci, reg);
+ reg_val = dw_pcie_readl_dbi(pci, base, reg);
*(u16 __force *) walker = val;
- dw_pcie_writel_dbi(pci, reg, reg_val);
+ dw_pcie_writel_dbi(pci, base, reg, reg_val);
} else if (size == 1) {
- reg_val = dw_pcie_readl_dbi(pci, reg);
+ reg_val = dw_pcie_readl_dbi(pci, base, reg);
*(u8 __force *) walker = val;
- dw_pcie_writel_dbi(pci, reg, reg_val);
+ dw_pcie_writel_dbi(pci, base, reg, reg_val);
} else
return PCIBIOS_BAD_REGISTER_NUMBER;
@@ -207,9 +209,10 @@ static int hisi_pcie_link_up_hip05(struct hisi_pcie *hisi_pcie)
static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie)
{
struct dw_pcie *pci = hisi_pcie->pci;
+ void __iomem *base = pci->dbi_base;
u32 val;
- val = dw_pcie_readl_dbi(pci, PCIE_SYS_STATE4);
+ val = dw_pcie_readl_dbi(pci, base, PCIE_SYS_STATE4);
return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
}
--
1.7.9.5
^ permalink raw reply related
* [PATCH 20/37] Documentation: PCI: Add binding documentation for pci-test endpoint function
From: Kishon Vijay Abraham I @ 2017-01-12 10:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1484216786-17292-1-git-send-email-kishon@ti.com>
Add binding documentation for pci-test endpoint function that helps in
adding and configuring pci-test endpoint function.
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
Documentation/PCI/00-INDEX | 2 ++
.../PCI/endpoint/function/binding/pci-test.txt | 17 +++++++++++++++++
2 files changed, 19 insertions(+)
create mode 100644 Documentation/PCI/endpoint/function/binding/pci-test.txt
diff --git a/Documentation/PCI/00-INDEX b/Documentation/PCI/00-INDEX
index 4e5a283..53717b7 100644
--- a/Documentation/PCI/00-INDEX
+++ b/Documentation/PCI/00-INDEX
@@ -18,3 +18,5 @@ endpoint/pci-endpoint-cfs.txt
- guide to use configfs to configure the pci endpoint function.
endpoint/pci-test-function.txt
- specification of *pci test* function device.
+endpoint/function/binding/
+ - binding documentation for pci endpoint function
diff --git a/Documentation/PCI/endpoint/function/binding/pci-test.txt b/Documentation/PCI/endpoint/function/binding/pci-test.txt
new file mode 100644
index 0000000..7358240
--- /dev/null
+++ b/Documentation/PCI/endpoint/function/binding/pci-test.txt
@@ -0,0 +1,17 @@
+PCI TEST ENDPOINT FUNCTION
+
+name: Should be "pci_epf_test" to bind to the pci_epf_test driver.
+
+Configurable Fields:
+vendorid : should be 0x104c
+deviceid : should be 0xffff
+revid : dont't care
+progif_code : don't care
+subclass_code : don't care
+baseclass_code : should be 0xff
+cache_line_size : don't care
+subsys_vendor_id : don't care
+subsys_id : don't care
+interrupt_pin : Should be 1 - INTA, 2 - INTB, 3 - INTC, 4 -INTD
+msi_interrupts : Should be 1 to 32 depending on the number of msi interrupts
+ to test
--
1.7.9.5
^ permalink raw reply related
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