* Re: [PATCH bpf] xsk: relax UMEM headroom alignment
From: Daniel Borkmann @ 2019-09-19 12:33 UTC (permalink / raw)
To: Björn Töpel
Cc: ast, netdev, Björn Töpel, magnus.karlsson,
magnus.karlsson, bpf, jonathan.lemon
In-Reply-To: <20190918075739.19451-1-bjorn.topel@gmail.com>
On Wed, Sep 18, 2019 at 09:57:39AM +0200, Björn Töpel wrote:
> From: Björn Töpel <bjorn.topel@intel.com>
>
> This patch removes the 64B alignment of the UMEM headroom. There is
> really no reason for it, and having a headroom less than 64B should be
> valid.
>
> Fixes: c0c77d8fb787 ("xsk: add user memory registration support sockopt")
> Signed-off-by: Björn Töpel <bjorn.topel@intel.com>
Applied, thanks!
^ permalink raw reply
* Re: [PATCH net-next] tcp: force a PSH flag on TSO packets
From: David Miller @ 2019-09-19 12:32 UTC (permalink / raw)
To: gerlitz.or
Cc: edumazet, netdev, eric.dumazet, soheil, ncardwell, ycheng, daniel,
tariqt
In-Reply-To: <CAJ3xEMhh=Ow-fZqnPtxUyZsCN89dRmy=NcaO+iK+iZZYBdZbqA@mail.gmail.com>
From: Or Gerlitz <gerlitz.or@gmail.com>
Date: Thu, 19 Sep 2019 15:17:54 +0300
> On Wed, Sep 11, 2019 at 12:54 AM Eric Dumazet <edumazet@google.com> wrote:
>> When tcp sends a TSO packet, adding a PSH flag on it
>> reduces the sojourn time of GRO packet in GRO receivers.
>>
>> This is particularly the case under pressure, since RX queues
>> receive packets for many concurrent flows.
>>
>> A sender can give a hint to GRO engines when it is
>> appropriate to flush a super-packet, especially when pacing
>
> Hi Eric,
>
> Is this correct that we add here the push flag for the tcp header template
> from which all the tcp headers for SW GSO packets will be generated?
>
> Wouldn't that cause a too early flush on GRO engines at the receiver side?
I thought segmentation offload mechanism are not supposed to propagate
the PSH to all of the packets.
^ permalink raw reply
* Re: KASAN: use-after-free Read in atusb_disconnect
From: syzbot @ 2019-09-19 12:32 UTC (permalink / raw)
To: alex.aring, andreyknvl, davem, johan, linux-kernel, linux-wpan,
netdev, stable, stefan, syzkaller-bugs
In-Reply-To: <20190919121234.30620-1-johan@kernel.org>
Hello,
syzbot has tested the proposed patch and the reproducer did not trigger
crash:
Reported-and-tested-by:
syzbot+f4509a9138a1472e7e80@syzkaller.appspotmail.com
Tested on:
commit: f0df5c1b usb-fuzzer: main usb gadget fuzzer driver
git tree: https://github.com/google/kasan.git
kernel config: https://syzkaller.appspot.com/x/.config?x=5c6633fa4ed00be5
dashboard link: https://syzkaller.appspot.com/bug?extid=f4509a9138a1472e7e80
compiler: gcc (GCC) 9.0.0 20181231 (experimental)
patch: https://syzkaller.appspot.com/x/patch.diff?x=10f3ebb5600000
Note: testing is done by a robot and is best-effort only.
^ permalink raw reply
* Re: [PATCH bpf v2] libbpf: Remove getsockopt() check for XDP_OPTIONS
From: Daniel Borkmann @ 2019-09-19 12:31 UTC (permalink / raw)
To: Toke Høiland-Jørgensen
Cc: Alexei Starovoitov, netdev, bpf, Björn Töpel,
Yonghong Song
In-Reply-To: <20190916123342.49928-1-toke@redhat.com>
On Mon, Sep 16, 2019 at 02:33:42PM +0200, Toke Høiland-Jørgensen wrote:
> The xsk_socket__create() function fails and returns an error if it cannot
> get the XDP_OPTIONS through getsockopt(). However, support for XDP_OPTIONS
> was not added until kernel 5.3, so this means that creating XSK sockets
> always fails on older kernels.
>
> Since the option is just used to set the zero-copy flag in the xsk struct,
> and that flag is not really used for anything yet, just remove the
> getsockopt() call until a proper use for it is introduced.
>
> Suggested-by: Yonghong Song <yhs@fb.com>
> Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
Applied, thanks!
^ permalink raw reply
* Re: [PATCH net-next] tcp: force a PSH flag on TSO packets
From: Or Gerlitz @ 2019-09-19 12:17 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, netdev, Eric Dumazet, Soheil Hassas Yeganeh,
Neal Cardwell, Yuchung Cheng, Daniel Borkmann, Tariq Toukan
In-Reply-To: <20190910214928.220727-1-edumazet@google.com>
On Wed, Sep 11, 2019 at 12:54 AM Eric Dumazet <edumazet@google.com> wrote:
> When tcp sends a TSO packet, adding a PSH flag on it
> reduces the sojourn time of GRO packet in GRO receivers.
>
> This is particularly the case under pressure, since RX queues
> receive packets for many concurrent flows.
>
> A sender can give a hint to GRO engines when it is
> appropriate to flush a super-packet, especially when pacing
Hi Eric,
Is this correct that we add here the push flag for the tcp header template
from which all the tcp headers for SW GSO packets will be generated?
Wouldn't that cause a too early flush on GRO engines at the receiver side?
Or.
^ permalink raw reply
* Re: [RFC PATCH v3 4/6] psci: Add hvc call service for ptp_kvm.
From: Paolo Bonzini @ 2019-09-19 12:13 UTC (permalink / raw)
To: Marc Zyngier, Jianyong Wu (Arm Technology China),
netdev@vger.kernel.org, yangbo.lu@nxp.com, john.stultz@linaro.org,
tglx@linutronix.de, sean.j.christopherson@intel.com,
richardcochran@gmail.com, Mark Rutland, Will Deacon,
Suzuki Poulose
Cc: linux-kernel@vger.kernel.org, kvm@vger.kernel.org, Steve Capper,
Kaly Xin (Arm Technology China), Justin He (Arm Technology China),
nd, linux-arm-kernel@lists.infradead.org
In-Reply-To: <a1b554b8-4417-5305-3419-fe71a8c50842@kernel.org>
On 19/09/19 13:39, Marc Zyngier wrote:
>> I don't think it's ugly but more important, using tk->tkr_mono.clock is
>> incorrect. See how the x86 code hardcodes &kvm_clock, it's the same for
>> ARM.
> Not really. The guest kernel is free to use any clocksource it wishes.
Understood, in fact it's the same on x86.
However, for PTP to work, the cycles value returned by the clocksource
must match the one returned by the hypercall. So for ARM
get_device_system_crosststamp must receive the arch timer clocksource,
so that it will return -ENODEV if the active clocksource is anything else.
Paolo
> In some cases, it is actually desirable (like these broken systems that
> cannot use an in-kernel irqchip...). Maybe it is that on x86 the guest
> only uses the kvm_clock, but that's a much harder sell on ARM. The fact
> that ptp_kvm assumes that the clocksource is fixed doesn't seem correct
> in that case.
^ permalink raw reply
* [PATCH] ieee802154: atusb: fix use-after-free at disconnect
From: Johan Hovold @ 2019-09-19 12:12 UTC (permalink / raw)
To: Stefan Schmidt
Cc: Alexander Aring, David S. Miller, linux-wpan, netdev,
linux-kernel, andreyknvl, syzkaller-bugs, Johan Hovold, stable,
syzbot+f4509a9138a1472e7e80
The disconnect callback was accessing the hardware-descriptor private
data after having having freed it.
Fixes: 7490b008d123 ("ieee802154: add support for atusb transceiver")
Cc: stable <stable@vger.kernel.org> # 4.2
Cc: Alexander Aring <alex.aring@gmail.com>
Reported-by: syzbot+f4509a9138a1472e7e80@syzkaller.appspotmail.com
Signed-off-by: Johan Hovold <johan@kernel.org>
---
#syz test: https://github.com/google/kasan.git f0df5c1b
drivers/net/ieee802154/atusb.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c
index ceddb424f887..0dd0ba915ab9 100644
--- a/drivers/net/ieee802154/atusb.c
+++ b/drivers/net/ieee802154/atusb.c
@@ -1137,10 +1137,11 @@ static void atusb_disconnect(struct usb_interface *interface)
ieee802154_unregister_hw(atusb->hw);
+ usb_put_dev(atusb->usb_dev);
+
ieee802154_free_hw(atusb->hw);
usb_set_intfdata(interface, NULL);
- usb_put_dev(atusb->usb_dev);
pr_debug("%s done\n", __func__);
}
--
2.23.0
^ permalink raw reply related
* Re: [PATCH] iwlwifi: fix building without CONFIG_THERMAL
From: Luca Coelho @ 2019-09-19 11:58 UTC (permalink / raw)
To: Arnd Bergmann, Johannes Berg, Emmanuel Grumbach,
Intel Linux Wireless, Kalle Valo
Cc: linux-wireless, netdev, linux-kernel
In-Reply-To: <20190919115612.1924937-1-arnd@arndb.de>
On Thu, 2019-09-19 at 13:55 +0200, Arnd Bergmann wrote:
> The iwl_mvm_send_temp_report_ths_cmd() function is now called without
> CONFIG_THERMAL, but not defined:
>
> ERROR: "iwl_mvm_send_temp_report_ths_cmd" [drivers/net/wireless/intel/iwlwifi/mvm/iwlmvm.ko] undefined!
>
> Move that function out of the #ifdef as well and change it so
> that empty data gets sent even if no thermal device was
> registered.
>
> Fixes: 242d9c8b9a93 ("iwlwifi: mvm: use FW thermal monitoring regardless of CONFIG_THERMAL")
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
> No idea if this does what was intended in the commit that introduced
> the link failure, please see for youself.
Thanks for the fix, Arnd! We already sent a fix for this though[1] and
Kalle has already queued it for v5.3.
[1] https://patchwork.kernel.org/patch/11150431/
--
Cheers,
Luca.
^ permalink raw reply
* [PATCH] iwlwifi: fix building without CONFIG_THERMAL
From: Arnd Bergmann @ 2019-09-19 11:55 UTC (permalink / raw)
To: Johannes Berg, Emmanuel Grumbach, Luca Coelho,
Intel Linux Wireless, Kalle Valo
Cc: Arnd Bergmann, linux-wireless, netdev, linux-kernel
The iwl_mvm_send_temp_report_ths_cmd() function is now called without
CONFIG_THERMAL, but not defined:
ERROR: "iwl_mvm_send_temp_report_ths_cmd" [drivers/net/wireless/intel/iwlwifi/mvm/iwlmvm.ko] undefined!
Move that function out of the #ifdef as well and change it so
that empty data gets sent even if no thermal device was
registered.
Fixes: 242d9c8b9a93 ("iwlwifi: mvm: use FW thermal monitoring regardless of CONFIG_THERMAL")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
No idea if this does what was intended in the commit that introduced
the link failure, please see for youself.
---
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 4 ++--
drivers/net/wireless/intel/iwlwifi/mvm/tt.c | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index 843d00bf2bd5..1b4139372e57 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -542,7 +542,6 @@ struct iwl_mvm_tt_mgmt {
bool throttle;
};
-#ifdef CONFIG_THERMAL
/**
*struct iwl_mvm_thermal_device - thermal zone related data
* @temp_trips: temperature thresholds for report
@@ -555,6 +554,7 @@ struct iwl_mvm_thermal_device {
struct thermal_zone_device *tzone;
};
+#ifdef CONFIG_THERMAL
/*
* struct iwl_mvm_cooling_device
* @cur_state: current state
@@ -1034,8 +1034,8 @@ struct iwl_mvm {
/* Thermal Throttling and CTkill */
struct iwl_mvm_tt_mgmt thermal_throttle;
-#ifdef CONFIG_THERMAL
struct iwl_mvm_thermal_device tz_device;
+#ifdef CONFIG_THERMAL
struct iwl_mvm_cooling_device cooling_dev;
#endif
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
index 32a708301cfc..6d717bb65ab7 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c
@@ -549,7 +549,6 @@ int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state)
return 0;
}
-#ifdef CONFIG_THERMAL
static int compare_temps(const void *a, const void *b)
{
return ((s16)le16_to_cpu(*(__le16 *)a) -
@@ -564,7 +563,7 @@ int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
lockdep_assert_held(&mvm->mutex);
if (!mvm->tz_device.tzone)
- return -EINVAL;
+ goto send;
/* The driver holds array of temperature trips that are unsorted
* and uncompressed, the FW should get it compressed and sorted
@@ -607,6 +606,7 @@ int iwl_mvm_send_temp_report_ths_cmd(struct iwl_mvm *mvm)
return ret;
}
+#ifdef CONFIG_THERMAL
static int iwl_mvm_tzone_get_temp(struct thermal_zone_device *device,
int *temperature)
{
--
2.20.0
^ permalink raw reply related
* Re: [PATCH 00/20] Add support for Silicon Labs WiFi chip WF200 and further
From: Arend Van Spriel @ 2019-09-19 11:39 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jerome Pouiller
Cc: devel@driverdev.osuosl.org, linux-wireless@vger.kernel.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Kalle Valo,
David S . Miller, David Le Goff
In-Reply-To: <20190919112508.GA3037175@kroah.com>
On 9/19/2019 1:25 PM, Greg Kroah-Hartman wrote:
>> - I also kept compatibility code for earlier Linux kernel version. I
>> may drop it in future. Maybe I will maintain compatibility with
>> older kernels in a external set of patches.
> That has to be dropped for the in-kernel version.
There is no need to maintain such compatibility. You basically get it
for free with the linux-backports project [1].
Regards,
Arend
[1] https://backports.wiki.kernel.org/index.php/Main_Page
^ permalink raw reply
* Re: [RFC PATCH v3 4/6] psci: Add hvc call service for ptp_kvm.
From: Marc Zyngier @ 2019-09-19 11:39 UTC (permalink / raw)
To: Paolo Bonzini, Jianyong Wu (Arm Technology China),
netdev@vger.kernel.org, yangbo.lu@nxp.com, john.stultz@linaro.org,
tglx@linutronix.de, sean.j.christopherson@intel.com,
richardcochran@gmail.com, Mark Rutland, Will Deacon,
Suzuki Poulose
Cc: linux-kernel@vger.kernel.org, kvm@vger.kernel.org, Steve Capper,
Kaly Xin (Arm Technology China), Justin He (Arm Technology China),
nd, linux-arm-kernel@lists.infradead.org
In-Reply-To: <ef6ab8bd-41ad-88f8-9cfd-dc749ca65310@redhat.com>
On 19/09/2019 12:07, Paolo Bonzini wrote:
> On 19/09/19 11:46, Jianyong Wu (Arm Technology China) wrote:
>>> On 18/09/19 11:57, Jianyong Wu (Arm Technology China) wrote:
>>>> Paolo Bonzini wrote:
>>>>> This is not Y2038-safe. Please use ktime_get_real_ts64 instead, and
>>>>> split the 64-bit seconds value between val[0] and val[1].
>>
>> Val[] should be long not u32 I think, so in arm64 I can avoid that Y2038_safe, but
>> also need rewrite for arm32.
>
> I don't think there's anything inherently wrong with u32 val[], and as
> you notice it lets you reuse code between arm and arm64. It's up to you
> and Marc to decide.
>
>>>>> However, it seems to me that the new function is not needed and you
>>>>> can just use ktime_get_snapshot. You'll get the time in
>>>>> systime_snapshot->real and the cycles value in systime_snapshot->cycles.
>>>>
>>>> See patch 5/6, I need both counter cycle and clocksource,
>>> ktime_get_snapshot seems only offer cycles.
>>>
>>> No, patch 5/6 only needs the current clock (ptp_sc.cycles is never accessed).
>>> So you could just use READ_ONCE(tk->tkr_mono.clock).
>>>
>> Yeah, patch 5/6 just need clocksource, but I think tk->tkr_mono.clock can't read in external like module,
>> So I need an API to expose clocksource.
>>
>>> However, even then I don't think it is correct to use ptp_sc.cs blindly in patch
>>> 5. I think there is a misunderstanding on the meaning of
>>> system_counterval.cs as passed to get_device_system_crosststamp.
>>> system_counterval.cs is not the active clocksource; it's the clocksource on
>>> which system_counterval.cycles is based.
>>>
>>
>> I think we can use system_counterval_t as pass current clocksource to system_counterval_t.cs and its
>> corresponding cycles to system_counterval_t.cycles. is it a big problem?
>
> Yes, it is. Because...
>
>>> Hypothetically, the clocksource could be one for which ptp_sc.cycles is _not_
>>> a cycle value. If you set system_counterval.cs to the system clocksource,
>>> get_device_system_crosststamp will return a bogus value.
>>
>> Yeah, but in patch 3/6, we have a corresponding pair of clock source and cycle value. So I think there will be no
>> that problem in this patch set.
>> In the implementation of get_device_system_crosststamp:
>> "
>> ...
>> if (tk->tkr_mono.clock != system_counterval.cs)
>> return -ENODEV;
>> ...
>> "
>> We need tk->tkr_mono.clock passed to get_device_system_crosststamp, just like patch 3/6 do, otherwise will return error.
>
> ... if the hypercall returns an architectural timer value, you must not
> pass tk->tkr.mono.clock to get_device_system_crosststamp: you must pass
> &clocksource_counter. This way, PTP is disabled when using any other
> clocksource.
>
>>> So system_counterval.cs should be set to something like
>>> &clocksource_counter (from drivers/clocksource/arm_arch_timer.c).
>>> Perhaps the right place to define kvm_arch_ptp_get_clock_fn is in that file?
>>>
>> I have checked that ptp_sc.cs is arch_sys_counter.
>> Also move the module API to arm_arch_timer.c will looks a little
>> ugly and it's not easy to be accept by arm side I think.
>
> I don't think it's ugly but more important, using tk->tkr_mono.clock is
> incorrect. See how the x86 code hardcodes &kvm_clock, it's the same for
> ARM.
Not really. The guest kernel is free to use any clocksource it wishes.
In some cases, it is actually desirable (like these broken systems that
cannot use an in-kernel irqchip...). Maybe it is that on x86 the guest
only uses the kvm_clock, but that's a much harder sell on ARM. The fact
that ptp_kvm assumes that the clocksource is fixed doesn't seem correct
in that case.
M.
--
Jazz is not dead, it just smells funny...
^ permalink raw reply
* Re: [PATCH RFC v3 0/5] Support fraglist GRO/GSO
From: Steffen Klassert @ 2019-09-19 11:36 UTC (permalink / raw)
To: David Miller; +Cc: marcelo.leitner, willemdebruijn.kernel, netdev, pabeni
In-Reply-To: <20190919.131816.1861650130627229336.davem@davemloft.net>
On Thu, Sep 19, 2019 at 01:18:16PM +0200, David Miller wrote:
> From: Steffen Klassert <steffen.klassert@secunet.com>
> Date: Thu, 19 Sep 2019 13:01:25 +0200
>
> > If the packet data of all the fraglist GRO skbs are backed by a
> > page fragment then we could just do the same by iterating with
> > skb_walk_frags(). I'm not a driver expert and might be misstaken,
> > but it looks like that could be done with existing hardware that
> > supports segmentation offload.
>
> Having to add frag list as well as page frag iterating in a driver is
> quite a bit of logic, and added complexity.
>
> If the frag list SKBs are indeed backed by a page, you could just as
> easily coalesce everything into the page frag array of the first SKB.
That is true indeed. Fraglist GRO is more optimized to the case
where we still need the skb arround the packet data. I.e. if it
can't be offloaded.
^ permalink raw reply
* Re: [PATCH v8 1/7] nfc: pn533: i2c: "pn532" as dt compatible string
From: David Miller @ 2019-09-19 11:32 UTC (permalink / raw)
To: poeschel
Cc: swinslow, tglx, kstewart, allison, opensource, netdev,
linux-kernel, johan, horms
In-Reply-To: <20190919091645.16439-1-poeschel@lemonage.de>
As we are in the merge window, the net-next tree is closed, as shown
also at:
http://vger.kernel.org/~davem/net-next.html
Please resubmit this after the merge window when the net-next tree opens
back up.
Please also provide an appropriate "[PATCH 0/N]" header posting
explaining what the patch series is doing, how it is doing it, and
why it is doing it that way.
Thank you.
^ permalink raw reply
* Re: [PATCH] usbnet: sanity checking of packet sizes and device mtu
From: David Miller @ 2019-09-19 11:27 UTC (permalink / raw)
To: oneukum; +Cc: netdev
In-Reply-To: <20190919082309.23365-1-oneukum@suse.com>
From: Oliver Neukum <oneukum@suse.com>
Date: Thu, 19 Sep 2019 10:23:08 +0200
> After a reset packet sizes and device mtu can change and need
> to be reevaluated to calculate queue sizes.
> Malicious devices can set this to zero and we divide by it.
> Introduce sanity checking.
>
> Reported-and-tested-by: syzbot+6102c120be558c885f04@syzkaller.appspotmail.com
> Signed-off-by: Oliver Neukum <oneukum@suse.com>
Applied and queued up for -stable.
^ permalink raw reply
* Re: [PATCH 00/20] Add support for Silicon Labs WiFi chip WF200 and further
From: Greg Kroah-Hartman @ 2019-09-19 11:25 UTC (permalink / raw)
To: Jerome Pouiller
Cc: devel@driverdev.osuosl.org, linux-wireless@vger.kernel.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Kalle Valo,
David S . Miller, David Le Goff
In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com>
On Thu, Sep 19, 2019 at 10:52:34AM +0000, Jerome Pouiller wrote:
> From: Jérôme Pouiller <jerome.pouiller@silabs.com>
>
> Hello all,
>
> This series add support for Silicon Labs WiFi chip WF200 and further:
>
> https://www.silabs.com/documents/public/data-sheets/wf200-datasheet.pdf
>
> This driver is an export from:
>
> https://github.com/SiliconLabs/wfx-linux-driver/
>
> I squashed all commits from github (it definitely does not make sense to
> import history). Then I split it in comprehensible (at least try to be)
> commits. I hope it will help readers to understand driver architecture.
> IMHO, firsts commits are clean enough to be reviewed. Things get more
> difficult when I introduce mac8011 API. I tried to extract important
> parts like Rx/Tx process but, big and complex patches seem unavoidable
> in this part.
>
> Architecture itself is described in commit messages.
>
> The series below is aligned on version 2.3.1 on github. If compare this
> series with github, you will find traditional differences between
> external and a in-tree driver: Documentation, build infrastructure,
> etc... In add, I dropped all code in CONFIG_WFX_SECURE_LINK. Indeed,
> "Secure Link" feature depends on mbedtls and I don't think to pull
> mbedtls in kernel is an option (see "To be done" below).
>
>
> What need to be done in this driver to leave staging area?
>
> - I kept wfx_version.h in order to ensure synchronization with github
> waiting for development goes entirely in kernel
That should be removed soon.
> - I also kept compatibility code for earlier Linux kernel version. I
> may drop it in future. Maybe I will maintain compatibility with
> older kernels in a external set of patches.
That has to be dropped for the in-kernel version.
The rest of these are fine, can you add this list in a TODO file for
this directory like the other staging drivers have?
thanks,
greg k-h
^ permalink raw reply
* Re: [GIT] Networking
From: David Miller @ 2019-09-19 11:22 UTC (permalink / raw)
To: torvalds; +Cc: akpm, netdev, linux-kernel
In-Reply-To: <CAHk-=whSTx4ofABCgWVe_2Kfo3CV6kSkBSRQBR-o5=DefgXnUQ@mail.gmail.com>
From: Linus Torvalds <torvalds@linux-foundation.org>
Date: Wed, 18 Sep 2019 13:37:57 -0700
> Hmm. This adds that NET_TC_SKB_EXT config thing, and makes it "default y".
>
> Why?
>
> It's also done in a crazy way:
>
> + depends on NET_CLS_ACT
> + default y if NET_CLS_ACT
I agree.
I've asked Paul Blakey, who added this, to make it depend upon OpenVSWwtch
or whatever else uses it.
^ permalink raw reply
* CONFIG_NET_TC_SKB_EXT
From: David Miller @ 2019-09-19 11:21 UTC (permalink / raw)
To: paulb; +Cc: netdev
As Linus pointed out, the Kconfig logic for CONFIG_NET_TC_SKB_EXT
is really not acceptable.
It should not be enabled by default at all.
Instead the actual users should turn it on or depend upon it, which in
this case seems to be OVS.
Please fix this, thank you.
^ permalink raw reply
* Re: [PATCH RFC v3 0/5] Support fraglist GRO/GSO
From: David Miller @ 2019-09-19 11:18 UTC (permalink / raw)
To: steffen.klassert; +Cc: marcelo.leitner, willemdebruijn.kernel, netdev, pabeni
In-Reply-To: <20190919110125.GN2879@gauss3.secunet.de>
From: Steffen Klassert <steffen.klassert@secunet.com>
Date: Thu, 19 Sep 2019 13:01:25 +0200
> If the packet data of all the fraglist GRO skbs are backed by a
> page fragment then we could just do the same by iterating with
> skb_walk_frags(). I'm not a driver expert and might be misstaken,
> but it looks like that could be done with existing hardware that
> supports segmentation offload.
Having to add frag list as well as page frag iterating in a driver is
quite a bit of logic, and added complexity.
If the frag list SKBs are indeed backed by a page, you could just as
easily coalesce everything into the page frag array of the first SKB.
^ permalink raw reply
* Re: [RFC PATCH v3 4/6] psci: Add hvc call service for ptp_kvm.
From: Paolo Bonzini @ 2019-09-19 11:07 UTC (permalink / raw)
To: Jianyong Wu (Arm Technology China), netdev@vger.kernel.org,
yangbo.lu@nxp.com, john.stultz@linaro.org, tglx@linutronix.de,
sean.j.christopherson@intel.com, maz@kernel.org,
richardcochran@gmail.com, Mark Rutland, Will Deacon,
Suzuki Poulose
Cc: linux-kernel@vger.kernel.org, kvm@vger.kernel.org, Steve Capper,
Kaly Xin (Arm Technology China), Justin He (Arm Technology China),
nd, linux-arm-kernel@lists.infradead.org
In-Reply-To: <HE1PR0801MB167639E2F025998058A77F86F4890@HE1PR0801MB1676.eurprd08.prod.outlook.com>
On 19/09/19 11:46, Jianyong Wu (Arm Technology China) wrote:
>> On 18/09/19 11:57, Jianyong Wu (Arm Technology China) wrote:
>>> Paolo Bonzini wrote:
>>>> This is not Y2038-safe. Please use ktime_get_real_ts64 instead, and
>>>> split the 64-bit seconds value between val[0] and val[1].
>
> Val[] should be long not u32 I think, so in arm64 I can avoid that Y2038_safe, but
> also need rewrite for arm32.
I don't think there's anything inherently wrong with u32 val[], and as
you notice it lets you reuse code between arm and arm64. It's up to you
and Marc to decide.
>>>> However, it seems to me that the new function is not needed and you
>>>> can just use ktime_get_snapshot. You'll get the time in
>>>> systime_snapshot->real and the cycles value in systime_snapshot->cycles.
>>>
>>> See patch 5/6, I need both counter cycle and clocksource,
>> ktime_get_snapshot seems only offer cycles.
>>
>> No, patch 5/6 only needs the current clock (ptp_sc.cycles is never accessed).
>> So you could just use READ_ONCE(tk->tkr_mono.clock).
>>
> Yeah, patch 5/6 just need clocksource, but I think tk->tkr_mono.clock can't read in external like module,
> So I need an API to expose clocksource.
>
>> However, even then I don't think it is correct to use ptp_sc.cs blindly in patch
>> 5. I think there is a misunderstanding on the meaning of
>> system_counterval.cs as passed to get_device_system_crosststamp.
>> system_counterval.cs is not the active clocksource; it's the clocksource on
>> which system_counterval.cycles is based.
>>
>
> I think we can use system_counterval_t as pass current clocksource to system_counterval_t.cs and its
> corresponding cycles to system_counterval_t.cycles. is it a big problem?
Yes, it is. Because...
>> Hypothetically, the clocksource could be one for which ptp_sc.cycles is _not_
>> a cycle value. If you set system_counterval.cs to the system clocksource,
>> get_device_system_crosststamp will return a bogus value.
>
> Yeah, but in patch 3/6, we have a corresponding pair of clock source and cycle value. So I think there will be no
> that problem in this patch set.
> In the implementation of get_device_system_crosststamp:
> "
> ...
> if (tk->tkr_mono.clock != system_counterval.cs)
> return -ENODEV;
> ...
> "
> We need tk->tkr_mono.clock passed to get_device_system_crosststamp, just like patch 3/6 do, otherwise will return error.
... if the hypercall returns an architectural timer value, you must not
pass tk->tkr.mono.clock to get_device_system_crosststamp: you must pass
&clocksource_counter. This way, PTP is disabled when using any other
clocksource.
>> So system_counterval.cs should be set to something like
>> &clocksource_counter (from drivers/clocksource/arm_arch_timer.c).
>> Perhaps the right place to define kvm_arch_ptp_get_clock_fn is in that file?
>>
> I have checked that ptp_sc.cs is arch_sys_counter.
> Also move the module API to arm_arch_timer.c will looks a little
> ugly and it's not easy to be accept by arm side I think.
I don't think it's ugly but more important, using tk->tkr_mono.clock is
incorrect. See how the x86 code hardcodes &kvm_clock, it's the same for
ARM.
>> You also have to check here that the clocksource is based on the ARM
>> architectural timer. Again, maybe you could place the implementation in
>> drivers/clocksource/arm_arch_timer.c, and make it return -ENODEV if the
>> active clocksource is not clocksource_counter. Then KVM can look for errors
>> and return SMCCC_RET_NOT_SUPPORTED in that case.
>
> I have checked it. The clock source is arch_sys_counter which is arm arch timer.
> I can try to do that but I'm not sure arm side will be happy for that change.
Just try. For my taste, it's nice to include both sides of the
hypercall in drivers/clocksource/arm_arch_timer.c, possibly
conditionalizing them on #ifdef CONFIG_KVM and #ifdef
CONFIG_PTP_1588_CLOCK_KVM. But there is an alternative which is simply
to export the clocksource struct. Both choices are easy to implement so
you can just ask the ARM people what they prefer and they can judge from
the code.
Paolo
^ permalink raw reply
* Re: [PATCH RFC v3 0/5] Support fraglist GRO/GSO
From: Steffen Klassert @ 2019-09-19 11:01 UTC (permalink / raw)
To: Marcelo Ricardo Leitner
Cc: Willem de Bruijn, Network Development, Paolo Abeni
In-Reply-To: <20190918165817.GA3431@localhost.localdomain>
On Wed, Sep 18, 2019 at 01:58:17PM -0300, Marcelo Ricardo Leitner wrote:
> On Wed, Sep 18, 2019 at 12:17:08PM -0400, Willem de Bruijn wrote:
> >
> > More specifically, whether we can remove that in favor of using your
> > new skb_segment_list. That would actually be a big first step in
> > simplifying skb_segment back to something manageable.
>
> The main issue (that I know) on obsoleting GSO_BY_FRAGS is that
> dealing with frags instead of frag_list was considered easier to be
> offloaded, if ever attempted. So this would be a step back on that
> aspect. Other than this, it should be doable.
I wonder why offloading the frag_list should be harder.
I looked at the i40e driver, it just iterates over the page
fragments found in skb_shinfo(skb)->frags in i40e_tx_map().
If the packet data of all the fraglist GRO skbs are backed by a
page fragment then we could just do the same by iterating with
skb_walk_frags(). I'm not a driver expert and might be misstaken,
but it looks like that could be done with existing hardware that
supports segmentation offload.
^ permalink raw reply
* [PATCH 14/20] staging: wfx: setup initial chip configuration
From: Jerome Pouiller @ 2019-09-19 10:52 UTC (permalink / raw)
To: devel@driverdev.osuosl.org, linux-wireless@vger.kernel.org
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
Greg Kroah-Hartman, Kalle Valo, David S . Miller, David Le Goff,
Jerome Pouiller
In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com>
From: Jérôme Pouiller <jerome.pouiller@silabs.com>
A few tasks remain to be done in order to finish chip initial
configuration:
- configure chip to use multi-tx confirmation (speed up data
transfer)
- configure chip to use wake-up feature (save power consumption
during runtime)
- set hardware configuration (clocks, RF, pinout, etc...) using a
Platform Data Set (PDS) file
On release, driver completely shutdown the chip to save power
consumption.
Documentation about PDS and PDS data for sample boards are available
here[1]. One day, PDS data may find a place in device tree but,
currently, PDS is too much linked with firmware to allowing that.
This patch also add "send_pds" file in debugfs to be able to dynamically
change PDS (only for debug, of course).
[1]: https://github.com/SiliconLabs/wfx-firmware/tree/master/PDS
Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
---
drivers/staging/wfx/bus_sdio.c | 1 +
drivers/staging/wfx/bus_spi.c | 1 +
drivers/staging/wfx/debug.c | 29 +++++++++++
drivers/staging/wfx/hif_rx.c | 11 ++++
drivers/staging/wfx/main.c | 94 ++++++++++++++++++++++++++++++++++
drivers/staging/wfx/main.h | 2 +
6 files changed, 138 insertions(+)
diff --git a/drivers/staging/wfx/bus_sdio.c b/drivers/staging/wfx/bus_sdio.c
index c0c063c3cfc9..05f02c278782 100644
--- a/drivers/staging/wfx/bus_sdio.c
+++ b/drivers/staging/wfx/bus_sdio.c
@@ -19,6 +19,7 @@
static const struct wfx_platform_data wfx_sdio_pdata = {
.file_fw = "wfm_wf200",
+ .file_pds = "wf200.pds",
};
struct wfx_sdio_priv {
diff --git a/drivers/staging/wfx/bus_spi.c b/drivers/staging/wfx/bus_spi.c
index 8a9aab3e7384..163342b66a5e 100644
--- a/drivers/staging/wfx/bus_spi.c
+++ b/drivers/staging/wfx/bus_spi.c
@@ -32,6 +32,7 @@ MODULE_PARM_DESC(gpio_reset, "gpio number for reset. -1 for none.");
static const struct wfx_platform_data wfx_spi_pdata = {
.file_fw = "wfm_wf200",
+ .file_pds = "wf200.pds",
.use_rising_clk = true,
};
diff --git a/drivers/staging/wfx/debug.c b/drivers/staging/wfx/debug.c
index f79693a4be7f..0619c7d1cf79 100644
--- a/drivers/staging/wfx/debug.c
+++ b/drivers/staging/wfx/debug.c
@@ -10,6 +10,7 @@
#include "debug.h"
#include "wfx.h"
+#include "main.h"
#define CREATE_TRACE_POINTS
#include "traces.h"
@@ -54,6 +55,33 @@ const char *get_reg_name(unsigned long id)
return get_symbol(id, wfx_reg_print_map);
}
+static ssize_t wfx_send_pds_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wfx_dev *wdev = file->private_data;
+ char *buf;
+ int ret;
+
+ if (*ppos != 0) {
+ dev_dbg(wdev->dev, "PDS data must be written in one transaction");
+ return -EBUSY;
+ }
+ buf = memdup_user(user_buf, count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+ *ppos = *ppos + count;
+ ret = wfx_send_pds(wdev, buf, count);
+ kfree(buf);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+static const struct file_operations wfx_send_pds_fops = {
+ .open = simple_open,
+ .write = wfx_send_pds_write,
+};
+
static ssize_t wfx_burn_slk_key_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
@@ -162,6 +190,7 @@ int wfx_debug_init(struct wfx_dev *wdev)
struct dentry *d;
d = debugfs_create_dir("wfx", wdev->hw->wiphy->debugfsdir);
+ debugfs_create_file("send_pds", 0200, d, wdev, &wfx_send_pds_fops);
debugfs_create_file("burn_slk_key", 0200, d, wdev, &wfx_burn_slk_key_fops);
debugfs_create_file("send_hif_msg", 0600, d, wdev, &wfx_send_hif_msg_fops);
diff --git a/drivers/staging/wfx/hif_rx.c b/drivers/staging/wfx/hif_rx.c
index dd5f1dea4e85..6b9683d69a3f 100644
--- a/drivers/staging/wfx/hif_rx.c
+++ b/drivers/staging/wfx/hif_rx.c
@@ -71,6 +71,16 @@ static int hif_startup_indication(struct wfx_dev *wdev, struct hif_msg *hif, voi
return 0;
}
+static int hif_wakeup_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
+{
+ if (!wdev->pdata.gpio_wakeup
+ || !gpiod_get_value(wdev->pdata.gpio_wakeup)) {
+ dev_warn(wdev->dev, "unexpected wake-up indication\n");
+ return -EIO;
+ }
+ return 0;
+}
+
static int hif_keys_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
{
struct hif_ind_sl_exchange_pub_keys *body = buf;
@@ -89,6 +99,7 @@ static const struct {
int (*handler)(struct wfx_dev *wdev, struct hif_msg *hif, void *buf);
} hif_handlers[] = {
{ HIF_IND_ID_STARTUP, hif_startup_indication },
+ { HIF_IND_ID_WAKEUP, hif_wakeup_indication },
{ HIF_IND_ID_SL_EXCHANGE_PUB_KEYS, hif_keys_indication },
};
diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c
index 0cfd6b2ec8d1..5b04ea5f4353 100644
--- a/drivers/staging/wfx/main.c
+++ b/drivers/staging/wfx/main.c
@@ -18,6 +18,7 @@
#include <linux/mmc/sdio_func.h>
#include <linux/spi/spi.h>
#include <linux/etherdevice.h>
+#include <linux/firmware.h>
#include "main.h"
#include "wfx.h"
@@ -28,9 +29,12 @@
#include "sta.h"
#include "debug.h"
#include "secure_link.h"
+#include "hif_tx_mib.h"
#include "hif_api_cmd.h"
#include "wfx_version.h"
+#define WFX_PDS_MAX_SIZE 1500
+
MODULE_DESCRIPTION("Silicon Labs 802.11 Wireless LAN driver for WFx");
MODULE_AUTHOR("Jérôme Pouiller <jerome.pouiller@silabs.com>");
MODULE_LICENSE("GPL");
@@ -112,6 +116,69 @@ static void wfx_fill_sl_key(struct device *dev, struct wfx_platform_data *pdata)
dev_err(dev, "secure link is not supported by this driver, ignoring provided key\n");
}
+/* NOTE: wfx_send_pds() destroy buf */
+int wfx_send_pds(struct wfx_dev *wdev, unsigned char *buf, size_t len)
+{
+ int ret;
+ int start, brace_level, i;
+
+ start = 0;
+ brace_level = 0;
+ if (buf[0] != '{') {
+ dev_err(wdev->dev, "valid PDS start with '{'. Did you forget to compress it?\n");
+ return -EINVAL;
+ }
+ for (i = 1; i < len - 1; i++) {
+ if (buf[i] == '{')
+ brace_level++;
+ if (buf[i] == '}')
+ brace_level--;
+ if (buf[i] == '}' && !brace_level) {
+ i++;
+ if (i - start + 1 > WFX_PDS_MAX_SIZE)
+ return -EFBIG;
+ buf[start] = '{';
+ buf[i] = 0;
+ dev_dbg(wdev->dev, "send PDS '%s}'\n", buf + start);
+ buf[i] = '}';
+ ret = hif_configuration(wdev, buf + start, i - start + 1);
+ if (ret == HIF_STATUS_FAILURE) {
+ dev_err(wdev->dev, "PDS bytes %d to %d: invalid data (unsupported options?)\n", start, i);
+ return -EINVAL;
+ }
+ if (ret == -ETIMEDOUT) {
+ dev_err(wdev->dev, "PDS bytes %d to %d: chip didn't reply (corrupted file?)\n", start, i);
+ return ret;
+ }
+ if (ret) {
+ dev_err(wdev->dev, "PDS bytes %d to %d: chip returned an unknown error\n", start, i);
+ return -EIO;
+ }
+ buf[i] = ',';
+ start = i;
+ }
+ }
+ return 0;
+}
+
+static int wfx_send_pdata_pds(struct wfx_dev *wdev)
+{
+ int ret = 0;
+ const struct firmware *pds;
+ unsigned char *tmp_buf;
+
+ ret = request_firmware(&pds, wdev->pdata.file_pds, wdev->dev);
+ if (ret) {
+ dev_err(wdev->dev, "can't load PDS file %s\n", wdev->pdata.file_pds);
+ return ret;
+ }
+ tmp_buf = kmemdup(pds->data, pds->size, GFP_KERNEL);
+ ret = wfx_send_pds(wdev, tmp_buf, pds->size);
+ kfree(tmp_buf);
+ release_firmware(pds);
+ return ret;
+}
+
struct wfx_dev *wfx_init_common(struct device *dev,
const struct wfx_platform_data *pdata,
const struct hwbus_ops *hwbus_ops,
@@ -141,6 +208,8 @@ struct wfx_dev *wfx_init_common(struct device *dev,
wdev->hwbus_ops = hwbus_ops;
wdev->hwbus_priv = hwbus_priv;
memcpy(&wdev->pdata, pdata, sizeof(*pdata));
+ of_property_read_string(dev->of_node, "config-file", &wdev->pdata.file_pds);
+ wdev->pdata.gpio_wakeup = wfx_get_gpio(dev, gpio_wakeup, "wakeup");
wfx_fill_sl_key(dev, &wdev->pdata);
init_completion(&wdev->firmware_ready);
@@ -159,6 +228,12 @@ int wfx_probe(struct wfx_dev *wdev)
int i;
int err;
const void *macaddr;
+ struct gpio_desc *gpio_saved;
+
+ // During first part of boot, gpio_wakeup cannot yet been used. So
+ // prevent bh() to touch it.
+ gpio_saved = wdev->pdata.gpio_wakeup;
+ wdev->pdata.gpio_wakeup = NULL;
wfx_bh_register(wdev);
@@ -202,6 +277,24 @@ int wfx_probe(struct wfx_dev *wdev)
goto err1;
}
+ dev_dbg(wdev->dev, "sending configuration file %s\n", wdev->pdata.file_pds);
+ err = wfx_send_pdata_pds(wdev);
+ if (err < 0)
+ goto err1;
+
+ wdev->pdata.gpio_wakeup = gpio_saved;
+ if (wdev->pdata.gpio_wakeup) {
+ dev_dbg(wdev->dev, "enable 'quiescent' power mode with gpio %d and PDS file %s\n",
+ desc_to_gpio(wdev->pdata.gpio_wakeup), wdev->pdata.file_pds);
+ gpiod_set_value(wdev->pdata.gpio_wakeup, 1);
+ control_reg_write(wdev, 0);
+ hif_set_operational_mode(wdev, HIF_OP_POWER_MODE_QUIESCENT);
+ } else {
+ hif_set_operational_mode(wdev, HIF_OP_POWER_MODE_DOZE);
+ }
+
+ hif_use_multi_tx_conf(wdev, true);
+
for (i = 0; i < ARRAY_SIZE(wdev->addresses); i++) {
eth_zero_addr(wdev->addresses[i].addr);
macaddr = of_get_mac_address(wdev->dev->of_node);
@@ -232,6 +325,7 @@ int wfx_probe(struct wfx_dev *wdev)
void wfx_release(struct wfx_dev *wdev)
{
+ hif_shutdown(wdev);
wfx_bh_unregister(wdev);
wfx_sl_deinit(wdev);
}
diff --git a/drivers/staging/wfx/main.h b/drivers/staging/wfx/main.h
index 2c9c215455ce..f2b07ed1627c 100644
--- a/drivers/staging/wfx/main.h
+++ b/drivers/staging/wfx/main.h
@@ -21,6 +21,7 @@ struct wfx_dev;
struct wfx_platform_data {
/* Keyset and ".sec" extention will appended to this string */
const char *file_fw;
+ const char *file_pds;
unsigned char slk_key[API_KEY_VALUE_SIZE];
struct gpio_desc *gpio_wakeup;
/*
@@ -42,5 +43,6 @@ void wfx_release(struct wfx_dev *wdev);
struct gpio_desc *wfx_get_gpio(struct device *dev, int override,
const char *label);
bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor);
+int wfx_send_pds(struct wfx_dev *wdev, unsigned char *buf, size_t len);
#endif
--
2.20.1
^ permalink raw reply related
* [PATCH 03/20] staging: wfx: add I/O API
From: Jerome Pouiller @ 2019-09-19 10:52 UTC (permalink / raw)
To: devel@driverdev.osuosl.org, linux-wireless@vger.kernel.org
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
Greg Kroah-Hartman, Kalle Valo, David S . Miller, David Le Goff,
Jerome Pouiller
In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com>
From: Jérôme Pouiller <jerome.pouiller@silabs.com>
hwio.c provides an abstraction to access different types of register of
the chip.
Note that only data register (aka FRAME_OUT) and control register are
used normal communication. Other registers are only used during chip
start up.
Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
---
drivers/staging/wfx/Makefile | 1 +
drivers/staging/wfx/hwio.c | 327 +++++++++++++++++++++++++++++++++++
drivers/staging/wfx/hwio.h | 27 +++
3 files changed, 355 insertions(+)
create mode 100644 drivers/staging/wfx/hwio.c
diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile
index 74939a5a0a1c..e860845186cf 100644
--- a/drivers/staging/wfx/Makefile
+++ b/drivers/staging/wfx/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
wfx-y := \
+ hwio.o \
main.o
wfx-$(CONFIG_SPI) += bus_spi.o
wfx-$(subst m,y,$(CONFIG_MMC)) += bus_sdio.o
diff --git a/drivers/staging/wfx/hwio.c b/drivers/staging/wfx/hwio.c
new file mode 100644
index 000000000000..fa626a49dd8a
--- /dev/null
+++ b/drivers/staging/wfx/hwio.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Low-level I/O functions.
+ *
+ * Copyright (c) 2017-2019, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "hwio.h"
+#include "wfx.h"
+#include "bus.h"
+
+/*
+ * Internal helpers.
+ *
+ * About CONFIG_VMAP_STACK:
+ * When CONFIG_VMAP_STACK is enabled, it is not possible to run DMA on stack
+ * allocated data. Functions below that work with registers (aka functions
+ * ending with "32") automatically reallocate buffers with kmalloc. However,
+ * functions that work with arbitrary length buffers let's caller to handle
+ * memory location. In doubt, enable CONFIG_DEBUG_SG to detect badly located
+ * buffer.
+ */
+
+static int read32(struct wfx_dev *wdev, int reg, u32 *val)
+{
+ int ret;
+ __le32 *tmp = kmalloc(sizeof(u32), GFP_KERNEL);
+
+ *val = ~0; // Never return undefined value
+ if (!tmp)
+ return -ENOMEM;
+ ret = wdev->hwbus_ops->copy_from_io(wdev->hwbus_priv, reg, tmp, sizeof(u32));
+ if (ret >= 0)
+ *val = le32_to_cpu(*tmp);
+ kfree(tmp);
+ if (ret)
+ dev_err(wdev->dev, "%s: bus communication error: %d\n", __func__, ret);
+ return ret;
+}
+
+static int write32(struct wfx_dev *wdev, int reg, u32 val)
+{
+ int ret;
+ __le32 *tmp = kmalloc(sizeof(u32), GFP_KERNEL);
+
+ if (!tmp)
+ return -ENOMEM;
+ *tmp = cpu_to_le32(val);
+ ret = wdev->hwbus_ops->copy_to_io(wdev->hwbus_priv, reg, tmp, sizeof(u32));
+ kfree(tmp);
+ if (ret)
+ dev_err(wdev->dev, "%s: bus communication error: %d\n", __func__, ret);
+ return ret;
+}
+
+static int read32_locked(struct wfx_dev *wdev, int reg, u32 *val)
+{
+ int ret;
+
+ wdev->hwbus_ops->lock(wdev->hwbus_priv);
+ ret = read32(wdev, reg, val);
+ wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+ return ret;
+}
+
+static int write32_locked(struct wfx_dev *wdev, int reg, u32 val)
+{
+ int ret;
+
+ wdev->hwbus_ops->lock(wdev->hwbus_priv);
+ ret = write32(wdev, reg, val);
+ wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+ return ret;
+}
+
+static int write32_bits_locked(struct wfx_dev *wdev, int reg, u32 mask, u32 val)
+{
+ int ret;
+ u32 val_r, val_w;
+
+ WARN_ON(~mask & val);
+ val &= mask;
+ wdev->hwbus_ops->lock(wdev->hwbus_priv);
+ ret = read32(wdev, reg, &val_r);
+ if (ret < 0)
+ goto err;
+ val_w = (val_r & ~mask) | val;
+ if (val_w != val_r) {
+ ret = write32(wdev, reg, val_w);
+ }
+err:
+ wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+ return ret;
+}
+
+static int indirect_read(struct wfx_dev *wdev, int reg, u32 addr, void *buf, size_t len)
+{
+ int ret;
+ int i;
+ u32 cfg;
+ u32 prefetch;
+
+ WARN_ON(len >= 0x2000);
+ WARN_ON(reg != WFX_REG_AHB_DPORT && reg != WFX_REG_SRAM_DPORT);
+
+ if (reg == WFX_REG_AHB_DPORT)
+ prefetch = CFG_PREFETCH_AHB;
+ else if (reg == WFX_REG_SRAM_DPORT)
+ prefetch = CFG_PREFETCH_SRAM;
+ else
+ return -ENODEV;
+
+ ret = write32(wdev, WFX_REG_BASE_ADDR, addr);
+ if (ret < 0)
+ goto err;
+
+ ret = read32(wdev, WFX_REG_CONFIG, &cfg);
+ if (ret < 0)
+ goto err;
+
+ ret = write32(wdev, WFX_REG_CONFIG, cfg | prefetch);
+ if (ret < 0)
+ goto err;
+
+ for (i = 0; i < 20; i++) {
+ ret = read32(wdev, WFX_REG_CONFIG, &cfg);
+ if (ret < 0)
+ goto err;
+ if (!(cfg & prefetch))
+ break;
+ udelay(200);
+ }
+ if (i == 20) {
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ ret = wdev->hwbus_ops->copy_from_io(wdev->hwbus_priv, reg, buf, len);
+
+err:
+ if (ret < 0)
+ memset(buf, 0xFF, len); // Never return undefined value
+ return ret;
+}
+
+static int indirect_write(struct wfx_dev *wdev, int reg, u32 addr, const void *buf, size_t len)
+{
+ int ret;
+
+ WARN_ON(len >= 0x2000);
+ WARN_ON(reg != WFX_REG_AHB_DPORT && reg != WFX_REG_SRAM_DPORT);
+ ret = write32(wdev, WFX_REG_BASE_ADDR, addr);
+ if (ret < 0)
+ return ret;
+
+ return wdev->hwbus_ops->copy_to_io(wdev->hwbus_priv, reg, buf, len);
+}
+
+static int indirect_read_locked(struct wfx_dev *wdev, int reg, u32 addr, void *buf, size_t len)
+{
+ int ret;
+
+ wdev->hwbus_ops->lock(wdev->hwbus_priv);
+ ret = indirect_read(wdev, reg, addr, buf, len);
+ wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+ return ret;
+}
+
+static int indirect_write_locked(struct wfx_dev *wdev, int reg, u32 addr, const void *buf, size_t len)
+{
+ int ret;
+
+ wdev->hwbus_ops->lock(wdev->hwbus_priv);
+ ret = indirect_write(wdev, reg, addr, buf, len);
+ wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+ return ret;
+}
+
+static int indirect_read32_locked(struct wfx_dev *wdev, int reg, u32 addr, u32 *val)
+{
+ int ret;
+ __le32 *tmp = kmalloc(sizeof(u32), GFP_KERNEL);
+
+ if (!tmp)
+ return -ENOMEM;
+ wdev->hwbus_ops->lock(wdev->hwbus_priv);
+ ret = indirect_read(wdev, reg, addr, tmp, sizeof(u32));
+ *val = cpu_to_le32(*tmp);
+ wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+ kfree(tmp);
+ return ret;
+}
+
+static int indirect_write32_locked(struct wfx_dev *wdev, int reg, u32 addr, u32 val)
+{
+ int ret;
+ __le32 *tmp = kmalloc(sizeof(u32), GFP_KERNEL);
+
+ if (!tmp)
+ return -ENOMEM;
+ *tmp = cpu_to_le32(val);
+ wdev->hwbus_ops->lock(wdev->hwbus_priv);
+ ret = indirect_write(wdev, reg, addr, tmp, sizeof(u32));
+ wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+ kfree(tmp);
+ return ret;
+}
+
+int wfx_data_read(struct wfx_dev *wdev, void *buf, size_t len)
+{
+ int ret;
+
+ WARN((long) buf & 3, "%s: unaligned buffer", __func__);
+ wdev->hwbus_ops->lock(wdev->hwbus_priv);
+ ret = wdev->hwbus_ops->copy_from_io(wdev->hwbus_priv, WFX_REG_IN_OUT_QUEUE, buf, len);
+ wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+ if (ret)
+ dev_err(wdev->dev, "%s: bus communication error: %d\n", __func__, ret);
+ return ret;
+}
+
+int wfx_data_write(struct wfx_dev *wdev, const void *buf, size_t len)
+{
+ int ret;
+
+ WARN((long) buf & 3, "%s: unaligned buffer", __func__);
+ wdev->hwbus_ops->lock(wdev->hwbus_priv);
+ ret = wdev->hwbus_ops->copy_to_io(wdev->hwbus_priv, WFX_REG_IN_OUT_QUEUE, buf, len);
+ wdev->hwbus_ops->unlock(wdev->hwbus_priv);
+ if (ret)
+ dev_err(wdev->dev, "%s: bus communication error: %d\n", __func__, ret);
+ return ret;
+}
+
+int sram_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len)
+{
+ return indirect_read_locked(wdev, WFX_REG_SRAM_DPORT, addr, buf, len);
+}
+
+int ahb_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len)
+{
+ return indirect_read_locked(wdev, WFX_REG_AHB_DPORT, addr, buf, len);
+}
+
+int sram_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t len)
+{
+ return indirect_write_locked(wdev, WFX_REG_SRAM_DPORT, addr, buf, len);
+}
+
+int ahb_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t len)
+{
+ return indirect_write_locked(wdev, WFX_REG_AHB_DPORT, addr, buf, len);
+}
+
+int sram_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val)
+{
+ return indirect_read32_locked(wdev, WFX_REG_SRAM_DPORT, addr, val);
+}
+
+int ahb_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val)
+{
+ return indirect_read32_locked(wdev, WFX_REG_AHB_DPORT, addr, val);
+}
+
+int sram_reg_write(struct wfx_dev *wdev, u32 addr, u32 val)
+{
+ return indirect_write32_locked(wdev, WFX_REG_SRAM_DPORT, addr, val);
+}
+
+int ahb_reg_write(struct wfx_dev *wdev, u32 addr, u32 val)
+{
+ return indirect_write32_locked(wdev, WFX_REG_AHB_DPORT, addr, val);
+}
+
+int config_reg_read(struct wfx_dev *wdev, u32 *val)
+{
+ return read32_locked(wdev, WFX_REG_CONFIG, val);
+}
+
+int config_reg_write(struct wfx_dev *wdev, u32 val)
+{
+ return write32_locked(wdev, WFX_REG_CONFIG, val);
+}
+
+int config_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val)
+{
+ return write32_bits_locked(wdev, WFX_REG_CONFIG, mask, val);
+}
+
+int control_reg_read(struct wfx_dev *wdev, u32 *val)
+{
+ return read32_locked(wdev, WFX_REG_CONTROL, val);
+}
+
+int control_reg_write(struct wfx_dev *wdev, u32 val)
+{
+ return write32_locked(wdev, WFX_REG_CONTROL, val);
+}
+
+int control_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val)
+{
+ return write32_bits_locked(wdev, WFX_REG_CONTROL, mask, val);
+}
+
+int igpr_reg_read(struct wfx_dev *wdev, int index, u32 *val)
+{
+ int ret;
+
+ *val = ~0; // Never return undefined value
+ ret = write32_locked(wdev, WFX_REG_SET_GEN_R_W, IGPR_RW | index << 24);
+ if (ret)
+ return ret;
+ ret = read32_locked(wdev, WFX_REG_SET_GEN_R_W, val);
+ if (ret)
+ return ret;
+ *val &= IGPR_VALUE;
+ return ret;
+}
+
+int igpr_reg_write(struct wfx_dev *wdev, int index, u32 val)
+{
+ return write32_locked(wdev, WFX_REG_SET_GEN_R_W, index << 24 | val);
+}
diff --git a/drivers/staging/wfx/hwio.h b/drivers/staging/wfx/hwio.h
index c490014c1df8..906524f71fd1 100644
--- a/drivers/staging/wfx/hwio.h
+++ b/drivers/staging/wfx/hwio.h
@@ -8,6 +8,25 @@
#ifndef WFX_HWIO_H
#define WFX_HWIO_H
+#include <linux/types.h>
+
+struct wfx_dev;
+
+int wfx_data_read(struct wfx_dev *wdev, void *buf, size_t buf_len);
+int wfx_data_write(struct wfx_dev *wdev, const void *buf, size_t buf_len);
+
+int sram_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len);
+int sram_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t len);
+
+int ahb_buf_read(struct wfx_dev *wdev, u32 addr, void *buf, size_t len);
+int ahb_buf_write(struct wfx_dev *wdev, u32 addr, const void *buf, size_t len);
+
+int sram_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val);
+int sram_reg_write(struct wfx_dev *wdev, u32 addr, u32 val);
+
+int ahb_reg_read(struct wfx_dev *wdev, u32 addr, u32 *val);
+int ahb_reg_write(struct wfx_dev *wdev, u32 addr, u32 val);
+
#define CFG_ERR_SPI_FRAME 0x00000001 // only with SPI
#define CFG_ERR_SDIO_BUF_MISMATCH 0x00000001 // only with SDIO
#define CFG_ERR_BUF_UNDERRUN 0x00000002
@@ -36,13 +55,21 @@
#define CFG_DEVICE_ID_MAJOR 0x07000000
#define CFG_DEVICE_ID_RESERVED 0x78000000
#define CFG_DEVICE_ID_TYPE 0x80000000
+int config_reg_read(struct wfx_dev *wdev, u32 *val);
+int config_reg_write(struct wfx_dev *wdev, u32 val);
+int config_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val);
#define CTRL_NEXT_LEN_MASK 0x00000FFF
#define CTRL_WLAN_WAKEUP 0x00001000
#define CTRL_WLAN_READY 0x00002000
+int control_reg_read(struct wfx_dev *wdev, u32 *val);
+int control_reg_write(struct wfx_dev *wdev, u32 val);
+int control_reg_write_bits(struct wfx_dev *wdev, u32 mask, u32 val);
#define IGPR_RW 0x80000000
#define IGPR_INDEX 0x7F000000
#define IGPR_VALUE 0x00FFFFFF
+int igpr_reg_read(struct wfx_dev *wdev, int index, u32 *val);
+int igpr_reg_write(struct wfx_dev *wdev, int index, u32 val);
#endif /* WFX_HWIO_H */
--
2.20.1
^ permalink raw reply related
* [PATCH 02/20] staging: wfx: add support for I/O access
From: Jerome Pouiller @ 2019-09-19 10:52 UTC (permalink / raw)
To: devel@driverdev.osuosl.org, linux-wireless@vger.kernel.org
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
Greg Kroah-Hartman, Kalle Valo, David S . Miller, David Le Goff,
Jerome Pouiller
In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com>
From: Jérôme Pouiller <jerome.pouiller@silabs.com>
Introduce bus level communication layer. At this level, 7 registers can
be addressed.
Notice that SPI driver is able to manage chip reset. SDIO mode relies
on an external driver (`mmc-pwrseq`) to reset chip.
Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
---
drivers/staging/wfx/bus.h | 17 +++
drivers/staging/wfx/bus_sdio.c | 189 ++++++++++++++++++++++-
drivers/staging/wfx/bus_spi.c | 271 ++++++++++++++++++++++++++++++++-
drivers/staging/wfx/hwio.h | 48 ++++++
drivers/staging/wfx/main.c | 53 +++++++
drivers/staging/wfx/main.h | 32 ++++
drivers/staging/wfx/wfx.h | 24 +++
7 files changed, 632 insertions(+), 2 deletions(-)
create mode 100644 drivers/staging/wfx/hwio.h
create mode 100644 drivers/staging/wfx/main.h
create mode 100644 drivers/staging/wfx/wfx.h
diff --git a/drivers/staging/wfx/bus.h b/drivers/staging/wfx/bus.h
index 8ce871a8a9ff..eb77abc09ec2 100644
--- a/drivers/staging/wfx/bus.h
+++ b/drivers/staging/wfx/bus.h
@@ -11,6 +11,23 @@
#include <linux/mmc/sdio_func.h>
#include <linux/spi/spi.h>
+#define WFX_REG_CONFIG 0x0
+#define WFX_REG_CONTROL 0x1
+#define WFX_REG_IN_OUT_QUEUE 0x2
+#define WFX_REG_AHB_DPORT 0x3
+#define WFX_REG_BASE_ADDR 0x4
+#define WFX_REG_SRAM_DPORT 0x5
+#define WFX_REG_SET_GEN_R_W 0x6
+#define WFX_REG_FRAME_OUT 0x7
+
+struct hwbus_ops {
+ int (*copy_from_io)(void *bus_priv, unsigned int addr, void *dst, size_t count);
+ int (*copy_to_io)(void *bus_priv, unsigned int addr, const void *src, size_t count);
+ void (*lock)(void *bus_priv);
+ void (*unlock)(void *bus_priv);
+ size_t (*align_size)(void *bus_priv, size_t size);
+};
+
extern struct sdio_driver wfx_sdio_driver;
extern struct spi_driver wfx_spi_driver;
diff --git a/drivers/staging/wfx/bus_sdio.c b/drivers/staging/wfx/bus_sdio.c
index 4b26c994f43c..35bcca7ec5dc 100644
--- a/drivers/staging/wfx/bus_sdio.c
+++ b/drivers/staging/wfx/bus_sdio.c
@@ -8,36 +8,223 @@
#include <linux/module.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/card.h>
+#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include "bus.h"
+#include "wfx.h"
+#include "hwio.h"
+#include "main.h"
+
+static const struct wfx_platform_data wfx_sdio_pdata = {
+};
+
+struct wfx_sdio_priv {
+ struct sdio_func *func;
+ struct wfx_dev *core;
+ u8 buf_id_tx;
+ u8 buf_id_rx;
+ int of_irq;
+};
+
+static int wfx_sdio_copy_from_io(void *priv, unsigned int reg_id,
+ void *dst, size_t count)
+{
+ struct wfx_sdio_priv *bus = priv;
+ unsigned int sdio_addr = reg_id << 2;
+ int ret;
+
+ BUG_ON(reg_id > 7);
+ WARN(((uintptr_t) dst) & 3, "unaligned buffer size");
+ WARN(count & 3, "unaligned buffer address");
+
+ /* Use queue mode buffers */
+ if (reg_id == WFX_REG_IN_OUT_QUEUE)
+ sdio_addr |= (bus->buf_id_rx + 1) << 7;
+ ret = sdio_memcpy_fromio(bus->func, dst, sdio_addr, count);
+ if (!ret && reg_id == WFX_REG_IN_OUT_QUEUE)
+ bus->buf_id_rx = (bus->buf_id_rx + 1) % 4;
+
+ return ret;
+}
+
+static int wfx_sdio_copy_to_io(void *priv, unsigned int reg_id,
+ const void *src, size_t count)
+{
+ struct wfx_sdio_priv *bus = priv;
+ unsigned int sdio_addr = reg_id << 2;
+ int ret;
+
+ BUG_ON(reg_id > 7);
+ WARN(((uintptr_t) src) & 3, "unaligned buffer size");
+ WARN(count & 3, "unaligned buffer address");
+
+ /* Use queue mode buffers */
+ if (reg_id == WFX_REG_IN_OUT_QUEUE)
+ sdio_addr |= bus->buf_id_tx << 7;
+ // FIXME: discards 'const' qualifier for src
+ ret = sdio_memcpy_toio(bus->func, sdio_addr, (void *) src, count);
+ if (!ret && reg_id == WFX_REG_IN_OUT_QUEUE)
+ bus->buf_id_tx = (bus->buf_id_tx + 1) % 32;
+
+ return ret;
+}
+
+static void wfx_sdio_lock(void *priv)
+{
+ struct wfx_sdio_priv *bus = priv;
+
+ sdio_claim_host(bus->func);
+}
+
+static void wfx_sdio_unlock(void *priv)
+{
+ struct wfx_sdio_priv *bus = priv;
+
+ sdio_release_host(bus->func);
+}
+
+static void wfx_sdio_irq_handler(struct sdio_func *func)
+{
+ struct wfx_sdio_priv *bus = sdio_get_drvdata(func);
+
+ if (bus->core)
+ /* empty */;
+ else
+ WARN(!bus->core, "race condition in driver init/deinit");
+}
+
+static irqreturn_t wfx_sdio_irq_handler_ext(int irq, void *priv)
+{
+ struct wfx_sdio_priv *bus = priv;
+
+ if (!bus->core) {
+ WARN(!bus->core, "race condition in driver init/deinit");
+ return IRQ_NONE;
+ }
+ sdio_claim_host(bus->func);
+ sdio_release_host(bus->func);
+ return IRQ_HANDLED;
+}
+
+static int wfx_sdio_irq_subscribe(struct wfx_sdio_priv *bus)
+{
+ int ret;
+
+ if (bus->of_irq) {
+ ret = request_irq(bus->of_irq, wfx_sdio_irq_handler_ext,
+ IRQF_TRIGGER_RISING, "wfx", bus);
+ } else {
+ sdio_claim_host(bus->func);
+ ret = sdio_claim_irq(bus->func, wfx_sdio_irq_handler);
+ sdio_release_host(bus->func);
+ }
+ return ret;
+}
+
+static int wfx_sdio_irq_unsubscribe(struct wfx_sdio_priv *bus)
+{
+ int ret;
+
+ if (bus->of_irq) {
+ free_irq(bus->of_irq, bus);
+ ret = 0;
+ } else {
+ sdio_claim_host(bus->func);
+ ret = sdio_release_irq(bus->func);
+ sdio_release_host(bus->func);
+ }
+ return ret;
+}
+
+static size_t wfx_sdio_align_size(void *priv, size_t size)
+{
+ struct wfx_sdio_priv *bus = priv;
+
+ return sdio_align_size(bus->func, size);
+}
+
+static const struct hwbus_ops wfx_sdio_hwbus_ops = {
+ .copy_from_io = wfx_sdio_copy_from_io,
+ .copy_to_io = wfx_sdio_copy_to_io,
+ .lock = wfx_sdio_lock,
+ .unlock = wfx_sdio_unlock,
+ .align_size = wfx_sdio_align_size,
+};
static const struct of_device_id wfx_sdio_of_match[];
static int wfx_sdio_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
struct device_node *np = func->dev.of_node;
+ struct wfx_sdio_priv *bus;
+ int ret;
if (func->num != 1) {
dev_err(&func->dev, "SDIO function number is %d while it should always be 1 (unsupported chip?)\n", func->num);
return -ENODEV;
}
+ bus = devm_kzalloc(&func->dev, sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return -ENOMEM;
+
if (np) {
if (!of_match_node(wfx_sdio_of_match, np)) {
dev_warn(&func->dev, "no compatible device found in DT\n");
return -ENODEV;
}
+ bus->of_irq = irq_of_parse_and_map(np, 0);
} else {
dev_warn(&func->dev, "device is not declared in DT, features will be limited\n");
// FIXME: ignore VID/PID and only rely on device tree
// return -ENODEV;
}
- return -EIO; // FIXME: not yet supported
+
+ bus->func = func;
+ sdio_set_drvdata(func, bus);
+ func->card->quirks |= MMC_QUIRK_LENIENT_FN0 | MMC_QUIRK_BLKSZ_FOR_BYTE_MODE | MMC_QUIRK_BROKEN_BYTE_MODE_512;
+
+ sdio_claim_host(func);
+ ret = sdio_enable_func(func);
+ // Block of 64 bytes is more efficient than 512B for frame sizes < 4k
+ sdio_set_block_size(func, 64);
+ sdio_release_host(func);
+ if (ret)
+ goto err0;
+
+ ret = wfx_sdio_irq_subscribe(bus);
+ if (ret)
+ goto err1;
+
+ bus->core = wfx_init_common(&func->dev, &wfx_sdio_pdata,
+ &wfx_sdio_hwbus_ops, bus);
+ if (!bus->core) {
+ ret = -EIO;
+ goto err2;
+ }
+
+ return 0;
+
+err2:
+ wfx_sdio_irq_unsubscribe(bus);
+err1:
+ sdio_claim_host(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+err0:
+ return ret;
}
static void wfx_sdio_remove(struct sdio_func *func)
{
+ struct wfx_sdio_priv *bus = sdio_get_drvdata(func);
+
+ wfx_free_common(bus->core);
+ wfx_sdio_irq_unsubscribe(bus);
+ sdio_claim_host(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
}
#define SDIO_VENDOR_ID_SILABS 0x0000
diff --git a/drivers/staging/wfx/bus_spi.c b/drivers/staging/wfx/bus_spi.c
index 574b60f513e9..b311ff72cf80 100644
--- a/drivers/staging/wfx/bus_spi.c
+++ b/drivers/staging/wfx/bus_spi.c
@@ -7,19 +7,288 @@
* Copyright (c) 2010, ST-Ericsson
*/
#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/spi/spi.h>
+#include <linux/interrupt.h>
#include <linux/of.h>
#include "bus.h"
+#include "wfx.h"
+#include "hwio.h"
+#include "main.h"
+
+#define DETECT_INVALID_CTRL_ACCESS
+
+static int gpio_reset = -2;
+module_param(gpio_reset, int, 0644);
+MODULE_PARM_DESC(gpio_reset, "gpio number for reset. -1 for none.");
+
+#define SET_WRITE 0x7FFF /* usage: and operation */
+#define SET_READ 0x8000 /* usage: or operation */
+
+static const struct wfx_platform_data wfx_spi_pdata = {
+};
+
+struct wfx_spi_priv {
+ struct spi_device *func;
+ struct wfx_dev *core;
+ struct gpio_desc *gpio_reset;
+ struct work_struct request_rx;
+ bool need_swab;
+};
+
+/*
+ * Read of control register need a particular attention because it should be
+ * done only after an IRQ raise. We can detect if this event happens by reading
+ * control register twice (it is safe to read twice since we can garantee that
+ * no data acess was done since IRQ raising). In add, this function optimize it
+ * by doing only one SPI request.
+ */
+#if (KERNEL_VERSION(4, 19, 14) > LINUX_VERSION_CODE)
+static int wfx_spi_read_ctrl_reg(struct wfx_spi_priv *bus, u16 *dst)
+{
+ int i, ret = 0;
+ u16 tx_buf[4] = { };
+ u16 rx_buf[4] = { };
+ u16 tmp[2] = { };
+ struct spi_message m;
+ struct spi_transfer t = {
+ .rx_buf = rx_buf,
+ .tx_buf = tx_buf,
+ .len = sizeof(tx_buf),
+ };
+ u16 regaddr = (WFX_REG_CONTROL << 12) | (sizeof(u16) / 2) | SET_READ;
+
+ cpu_to_le16s(regaddr);
+ if (bus->need_swab)
+ swab16s(®addr);
+
+ tx_buf[0] = tx_buf[2] = regaddr;
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ for (i = 0, tmp[0] = tmp[1] + 1; tmp[0] != tmp[1] && i < 3; i++) {
+ ret = spi_sync(bus->func, &m);
+ // Changes of gpio-wakeup can occur during control register
+ // access. In this case, CTRL_WLAN_READY may differs.
+ tmp[0] = rx_buf[1] & cpu_to_le16(~CTRL_WLAN_READY);
+ tmp[1] = rx_buf[3] & cpu_to_le16(~CTRL_WLAN_READY);
+ }
+ if (tmp[0] != tmp[1])
+ ret = -ETIMEDOUT;
+ else if (i > 1)
+ dev_info(bus->core->dev, "success read after %d failures\n", i - 1);
+
+ *dst = rx_buf[1];
+ return ret;
+}
+#endif
+
+/*
+ * WFx chip read data 16bits at time and place them directly into (little
+ * endian) CPU register. So, chip expect byte order like "B1 B0 B3 B2" (while
+ * LE is "B0 B1 B2 B3" and BE is "B3 B2 B1 B0")
+ *
+ * A little endian host with bits_per_word == 16 should do the right job
+ * natively. The code below to support big endian host and commonly used SPI
+ * 8bits.
+ */
+static int wfx_spi_copy_from_io(void *priv, unsigned int addr,
+ void *dst, size_t count)
+{
+ struct wfx_spi_priv *bus = priv;
+ u16 regaddr = (addr << 12) | (count / 2) | SET_READ;
+ struct spi_message m;
+ struct spi_transfer t_addr = {
+ .tx_buf = ®addr,
+ .len = sizeof(regaddr),
+ };
+ struct spi_transfer t_msg = {
+ .rx_buf = dst,
+ .len = count,
+ };
+ u16 *dst16 = dst;
+#if (KERNEL_VERSION(4, 19, 14) > LINUX_VERSION_CODE)
+ u8 *dst8 = dst;
+#endif
+ int ret, i;
+
+ WARN(count % 2, "buffer size must be a multiple of 2");
+
+#if (KERNEL_VERSION(4, 19, 14) > LINUX_VERSION_CODE)
+ /* Some SPI driver (and especially Raspberry one) have race conditions
+ * during SPI transfers. It impact last byte of transfer. Work around
+ * bellow try to detect and solve them.
+ * See https://github.com/raspberrypi/linux/issues/2200
+ */
+ if (addr == WFX_REG_IN_OUT_QUEUE)
+ dst8[count - 1] = 0xFF;
+#endif
+
+ cpu_to_le16s(®addr);
+ if (bus->need_swab)
+ swab16s(®addr);
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t_addr, &m);
+ spi_message_add_tail(&t_msg, &m);
+ ret = spi_sync(bus->func, &m);
+
+#if (KERNEL_VERSION(4, 19, 14) > LINUX_VERSION_CODE)
+ /* If last byte has not been overwritten, read ctrl_reg manually
+ */
+ if (addr == WFX_REG_IN_OUT_QUEUE && !ret && dst8[count - 1] == 0xFF) {
+ dev_warn(bus->core->dev, "SPI DMA error detected (and resolved)\n");
+ ret = wfx_spi_read_ctrl_reg(bus, (u16 *) (dst8 + count - 2));
+ }
+#endif
+
+ if (bus->need_swab && addr == WFX_REG_CONFIG)
+ for (i = 0; i < count / 2; i++)
+ swab16s(&dst16[i]);
+ return ret;
+}
+
+static int wfx_spi_copy_to_io(void *priv, unsigned int addr,
+ const void *src, size_t count)
+{
+ struct wfx_spi_priv *bus = priv;
+ u16 regaddr = (addr << 12) | (count / 2);
+ // FIXME: use a bounce buffer
+ u16 *src16 = (void *) src;
+ int ret, i;
+ struct spi_message m;
+ struct spi_transfer t_addr = {
+ .tx_buf = ®addr,
+ .len = sizeof(regaddr),
+ };
+ struct spi_transfer t_msg = {
+ .tx_buf = src,
+ .len = count,
+ };
+
+ WARN(count % 2, "buffer size must be a multiple of 2");
+ WARN(regaddr & SET_READ, "bad addr or size overflow");
+
+ cpu_to_le16s(®addr);
+
+ if (bus->need_swab)
+ swab16s(®addr);
+ if (bus->need_swab && addr == WFX_REG_CONFIG)
+ for (i = 0; i < count / 2; i++)
+ swab16s(&src16[i]);
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t_addr, &m);
+ spi_message_add_tail(&t_msg, &m);
+ ret = spi_sync(bus->func, &m);
+
+ if (bus->need_swab && addr == WFX_REG_CONFIG)
+ for (i = 0; i < count / 2; i++)
+ swab16s(&src16[i]);
+ return ret;
+}
+
+static void wfx_spi_lock(void *priv)
+{
+}
+
+static void wfx_spi_unlock(void *priv)
+{
+}
+
+static irqreturn_t wfx_spi_irq_handler(int irq, void *priv)
+{
+ struct wfx_spi_priv *bus = priv;
+
+ if (!bus->core) {
+ WARN(!bus->core, "race condition in driver init/deinit");
+ return IRQ_NONE;
+ }
+ queue_work(system_highpri_wq, &bus->request_rx);
+ return IRQ_HANDLED;
+}
+
+static void wfx_spi_request_rx(struct work_struct *work)
+{
+}
+
+static size_t wfx_spi_align_size(void *priv, size_t size)
+{
+ // Most of SPI controllers avoid DMA if buffer size is not 32bit aligned
+ return ALIGN(size, 4);
+}
+
+static const struct hwbus_ops wfx_spi_hwbus_ops = {
+ .copy_from_io = wfx_spi_copy_from_io,
+ .copy_to_io = wfx_spi_copy_to_io,
+ .lock = wfx_spi_lock,
+ .unlock = wfx_spi_unlock,
+ .align_size = wfx_spi_align_size,
+};
static int wfx_spi_probe(struct spi_device *func)
{
- return -EIO;
+ struct wfx_spi_priv *bus;
+ int ret;
+
+ if (!func->bits_per_word)
+ func->bits_per_word = 16;
+ ret = spi_setup(func);
+ if (ret)
+ return ret;
+ // Trace below is also displayed by spi_setup() if compiled with DEBUG
+ dev_dbg(&func->dev, "SPI params: CS=%d, mode=%d bits/word=%d speed=%d\n",
+ func->chip_select, func->mode, func->bits_per_word, func->max_speed_hz);
+ if (func->bits_per_word != 16 && func->bits_per_word != 8)
+ dev_warn(&func->dev, "unusual bits/word value: %d\n", func->bits_per_word);
+ if (func->max_speed_hz > 49000000)
+ dev_warn(&func->dev, "%dHz is a very high speed\n", func->max_speed_hz);
+
+ bus = devm_kzalloc(&func->dev, sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return -ENOMEM;
+ bus->func = func;
+ if (func->bits_per_word == 8 || IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
+ bus->need_swab = true;
+ spi_set_drvdata(func, bus);
+
+ bus->gpio_reset = wfx_get_gpio(&func->dev, gpio_reset, "reset");
+ if (!bus->gpio_reset) {
+ dev_warn(&func->dev, "try to load firmware anyway\n");
+ } else {
+ gpiod_set_value(bus->gpio_reset, 0);
+ udelay(100);
+ gpiod_set_value(bus->gpio_reset, 1);
+ udelay(2000);
+ }
+
+ ret = devm_request_irq(&func->dev, func->irq, wfx_spi_irq_handler,
+ IRQF_TRIGGER_RISING, "wfx", bus);
+ if (ret)
+ return ret;
+
+ INIT_WORK(&bus->request_rx, wfx_spi_request_rx);
+ bus->core = wfx_init_common(&func->dev, &wfx_spi_pdata,
+ &wfx_spi_hwbus_ops, bus);
+ if (!bus->core)
+ return -EIO;
+
+ return ret;
}
/* Disconnect Function to be called by SPI stack when device is disconnected */
static int wfx_spi_disconnect(struct spi_device *func)
{
+ struct wfx_spi_priv *bus = spi_get_drvdata(func);
+
+ wfx_free_common(bus->core);
+ // A few IRQ will be sent during device release. Hopefully, no IRQ
+ // should happen after wdev/wvif are released.
+ devm_free_irq(&func->dev, func->irq, bus);
+ flush_work(&bus->request_rx);
return 0;
}
diff --git a/drivers/staging/wfx/hwio.h b/drivers/staging/wfx/hwio.h
new file mode 100644
index 000000000000..c490014c1df8
--- /dev/null
+++ b/drivers/staging/wfx/hwio.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Low-level API.
+ *
+ * Copyright (c) 2017-2018, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#ifndef WFX_HWIO_H
+#define WFX_HWIO_H
+
+#define CFG_ERR_SPI_FRAME 0x00000001 // only with SPI
+#define CFG_ERR_SDIO_BUF_MISMATCH 0x00000001 // only with SDIO
+#define CFG_ERR_BUF_UNDERRUN 0x00000002
+#define CFG_ERR_DATA_IN_TOO_LARGE 0x00000004
+#define CFG_ERR_HOST_NO_OUT_QUEUE 0x00000008
+#define CFG_ERR_BUF_OVERRUN 0x00000010
+#define CFG_ERR_DATA_OUT_TOO_LARGE 0x00000020
+#define CFG_ERR_HOST_NO_IN_QUEUE 0x00000040
+#define CFG_ERR_HOST_CRC_MISS 0x00000080 // only with SDIO
+#define CFG_SPI_IGNORE_CS 0x00000080 // only with SPI
+#define CFG_WORD_MODE_MASK 0x00000300 // Bytes ordering (only writable in SPI):
+#define CFG_WORD_MODE0 0x00000000 // B1,B0,B3,B2 (In SPI, register address and CONFIG data always use this mode)
+#define CFG_WORD_MODE1 0x00000100 // B3,B2,B1,B0
+#define CFG_WORD_MODE2 0x00000200 // B0,B1,B2,B3 (SDIO)
+#define CFG_DIRECT_ACCESS_MODE 0x00000400 // Direct or queue access mode
+#define CFG_PREFETCH_AHB 0x00000800
+#define CFG_DISABLE_CPU_CLK 0x00001000
+#define CFG_PREFETCH_SRAM 0x00002000
+#define CFG_CPU_RESET 0x00004000
+#define CFG_SDIO_DISABLE_IRQ 0x00008000 // only with SDIO
+#define CFG_IRQ_ENABLE_DATA 0x00010000
+#define CFG_IRQ_ENABLE_WRDY 0x00020000
+#define CFG_CLK_RISE_EDGE 0x00040000
+#define CFG_SDIO_DISABLE_CRC_CHK 0x00080000 // only with SDIO
+#define CFG_RESERVED 0x00F00000
+#define CFG_DEVICE_ID_MAJOR 0x07000000
+#define CFG_DEVICE_ID_RESERVED 0x78000000
+#define CFG_DEVICE_ID_TYPE 0x80000000
+
+#define CTRL_NEXT_LEN_MASK 0x00000FFF
+#define CTRL_WLAN_WAKEUP 0x00001000
+#define CTRL_WLAN_READY 0x00002000
+
+#define IGPR_RW 0x80000000
+#define IGPR_INDEX 0x7F000000
+#define IGPR_VALUE 0x00FFFFFF
+
+#endif /* WFX_HWIO_H */
diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c
index cd69f955f531..744445ef597c 100644
--- a/drivers/staging/wfx/main.c
+++ b/drivers/staging/wfx/main.c
@@ -11,10 +11,15 @@
* Copyright (c) 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
*/
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/mmc/sdio_func.h>
#include <linux/spi/spi.h>
#include <linux/etherdevice.h>
+#include "main.h"
+#include "wfx.h"
#include "bus.h"
#include "wfx_version.h"
@@ -23,6 +28,54 @@ MODULE_AUTHOR("Jérôme Pouiller <jerome.pouiller@silabs.com>");
MODULE_LICENSE("GPL");
MODULE_VERSION(WFX_LABEL);
+struct gpio_desc *wfx_get_gpio(struct device *dev, int override, const char *label)
+{
+ struct gpio_desc *ret;
+ char label_buf[256];
+
+ if (override >= 0) {
+ snprintf(label_buf, sizeof(label_buf), "wfx_%s", label);
+ ret = ERR_PTR(devm_gpio_request_one(dev, override, GPIOF_OUT_INIT_LOW, label_buf));
+ if (!ret)
+ ret = gpio_to_desc(override);
+ } else if (override == -1) {
+ ret = NULL;
+ } else {
+ ret = devm_gpiod_get(dev, label, GPIOD_OUT_LOW);
+ }
+ if (IS_ERR(ret) || !ret) {
+ if (!ret || PTR_ERR(ret) == -ENOENT)
+ dev_warn(dev, "gpio %s is not defined\n", label);
+ else
+ dev_warn(dev, "error while requesting gpio %s\n", label);
+ ret = NULL;
+ } else {
+ dev_dbg(dev, "using gpio %d for %s\n", desc_to_gpio(ret), label);
+ }
+ return ret;
+}
+
+struct wfx_dev *wfx_init_common(struct device *dev,
+ const struct wfx_platform_data *pdata,
+ const struct hwbus_ops *hwbus_ops,
+ void *hwbus_priv)
+{
+ struct wfx_dev *wdev;
+
+ wdev = devm_kmalloc(dev, sizeof(*wdev), GFP_KERNEL);
+ if (!wdev)
+ return NULL;
+ wdev->dev = dev;
+ wdev->hwbus_ops = hwbus_ops;
+ wdev->hwbus_priv = hwbus_priv;
+ memcpy(&wdev->pdata, pdata, sizeof(*pdata));
+ return wdev;
+}
+
+void wfx_free_common(struct wfx_dev *wdev)
+{
+}
+
static int __init wfx_core_init(void)
{
int ret = 0;
diff --git a/drivers/staging/wfx/main.h b/drivers/staging/wfx/main.h
new file mode 100644
index 000000000000..82222edf998b
--- /dev/null
+++ b/drivers/staging/wfx/main.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Device probe and register.
+ *
+ * Copyright (c) 2017-2019, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ */
+#ifndef WFX_MAIN_H
+#define WFX_MAIN_H
+
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+
+#include "bus.h"
+
+struct wfx_dev;
+
+struct wfx_platform_data {
+};
+
+struct wfx_dev *wfx_init_common(struct device *dev,
+ const struct wfx_platform_data *pdata,
+ const struct hwbus_ops *hwbus_ops,
+ void *hwbus_priv);
+void wfx_free_common(struct wfx_dev *wdev);
+
+struct gpio_desc *wfx_get_gpio(struct device *dev, int override,
+ const char *label);
+
+#endif
diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h
new file mode 100644
index 000000000000..9716acc981df
--- /dev/null
+++ b/drivers/staging/wfx/wfx.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Common private data for Silicon Labs WFx chips.
+ *
+ * Copyright (c) 2017-2019, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ */
+#ifndef WFX_H
+#define WFX_H
+
+#include "main.h"
+
+struct hwbus_ops;
+
+struct wfx_dev {
+ struct wfx_platform_data pdata;
+ struct device *dev;
+ const struct hwbus_ops *hwbus_ops;
+ void *hwbus_priv;
+};
+
+#endif /* WFX_H */
--
2.20.1
^ permalink raw reply related
* [PATCH 07/20] staging: wfx: add IRQ handling
From: Jerome Pouiller @ 2019-09-19 10:52 UTC (permalink / raw)
To: devel@driverdev.osuosl.org, linux-wireless@vger.kernel.org
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
Greg Kroah-Hartman, Kalle Valo, David S . Miller, David Le Goff,
Jerome Pouiller
In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com>
From: Jérôme Pouiller <jerome.pouiller@silabs.com>
bh_work() is in charge to schedule all HIF message from/to chip.
On normal operation, when an IRQ is received, driver can get size of
next message in control register. In order to save control register
access, when chip send a message, it also appends a copy of control
register after the message (this register is not accounted in message
length declared in message header, but must accounted in bus request).
This copy of control register is called "piggyback".
It also handles a power saving mechanism specific to WFxxx series. This
mechanism is based on a GPIO called "wakeup" GPIO. Obviously, this gpio
is not part of SPI/SDIO standard buses and must be declared
independently (this is the main reason for why SDIO mode try to get
parameters from DT).
When wakeup is enabled, host can communicate with chip only if it is
awake. To wake up chip, there are two cases:
- host receive an IRQ from chip (chip initiate communication): host
just have to set wakeup GPIO before reading data
- host want to send data to chip: host set wakeup GPIO, then wait
for an IRQ (in fact, wait for an empty message) and finally send data
bh_work() is also in charge to track usage of chip buffers. Normally
each request expect a confirmation. However, you can notice that special
"multi tx" confirmation can acknowledge multiple requests at time.
Finally, note that wfx_bh_request_rx() is not atomic (because of
control_reg_read()). So, in SPI mode, hard-irq handler only postpone all
processing to wfx_spi_request_rx().
Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
---
drivers/staging/wfx/Makefile | 1 +
drivers/staging/wfx/bh.c | 277 +++++++++++++++++++++++++++++++++
drivers/staging/wfx/bh.h | 32 ++++
drivers/staging/wfx/bus_sdio.c | 4 +-
drivers/staging/wfx/bus_spi.c | 5 +
drivers/staging/wfx/main.c | 21 +++
drivers/staging/wfx/main.h | 2 +
drivers/staging/wfx/wfx.h | 4 +
8 files changed, 345 insertions(+), 1 deletion(-)
create mode 100644 drivers/staging/wfx/bh.c
create mode 100644 drivers/staging/wfx/bh.h
diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile
index e568d7a6fb06..1abd3115f11d 100644
--- a/drivers/staging/wfx/Makefile
+++ b/drivers/staging/wfx/Makefile
@@ -4,6 +4,7 @@
CFLAGS_debug.o = -I$(src)
wfx-y := \
+ bh.o \
hwio.o \
fwio.o \
main.o \
diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c
new file mode 100644
index 000000000000..02a42e5c1e10
--- /dev/null
+++ b/drivers/staging/wfx/bh.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Interrupt bottom half (BH).
+ *
+ * Copyright (c) 2017-2019, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#include <linux/gpio/consumer.h>
+#include <net/mac80211.h>
+
+#include "bh.h"
+#include "wfx.h"
+#include "hwio.h"
+#include "hif_api_cmd.h"
+
+static void device_wakeup(struct wfx_dev *wdev)
+{
+ if (!wdev->pdata.gpio_wakeup)
+ return;
+ if (gpiod_get_value(wdev->pdata.gpio_wakeup))
+ return;
+
+ gpiod_set_value(wdev->pdata.gpio_wakeup, 1);
+ if (wfx_api_older_than(wdev, 1, 4)) {
+ if (!completion_done(&wdev->hif.ctrl_ready))
+ udelay(2000);
+ } else {
+ // completion.h does not provide any function to wait
+ // completion without consume it (a kind of
+ // wait_for_completion_done_timeout()). So we have to emulate
+ // it.
+ if (wait_for_completion_timeout(&wdev->hif.ctrl_ready, msecs_to_jiffies(2) + 1))
+ complete(&wdev->hif.ctrl_ready);
+ else
+ dev_err(wdev->dev, "timeout while wake up chip\n");
+ }
+}
+
+static void device_release(struct wfx_dev *wdev)
+{
+ if (!wdev->pdata.gpio_wakeup)
+ return;
+
+ gpiod_set_value(wdev->pdata.gpio_wakeup, 0);
+}
+
+static int rx_helper(struct wfx_dev *wdev, size_t read_len, int *is_cnf)
+{
+ struct sk_buff *skb;
+ struct hif_msg *hif;
+ size_t alloc_len;
+ size_t computed_len;
+ int release_count;
+ int piggyback = 0;
+
+ WARN_ON(read_len < 4);
+ WARN(read_len > round_down(0xFFF, 2) * sizeof(u16),
+ "%s: request exceed WFx capability", __func__);
+
+ // Add 2 to take into account piggyback size
+ alloc_len = wdev->hwbus_ops->align_size(wdev->hwbus_priv, read_len + 2);
+ skb = dev_alloc_skb(alloc_len);
+ if (!skb)
+ return -ENOMEM;
+
+ if (wfx_data_read(wdev, skb->data, alloc_len))
+ goto err;
+
+ piggyback = le16_to_cpup((u16 *) (skb->data + alloc_len - 2));
+
+ hif = (struct hif_msg *) skb->data;
+ WARN(hif->encrypted & 0x1, "unsupported encryption type");
+ if (hif->encrypted == 0x2) {
+ BUG(); // Not yet implemented
+ } else {
+ le16_to_cpus(hif->len);
+ computed_len = round_up(hif->len, 2);
+ }
+ if (computed_len != read_len) {
+ dev_err(wdev->dev, "inconsistent message length: %zu != %zu\n",
+ computed_len, read_len);
+ print_hex_dump(KERN_INFO, "hif: ", DUMP_PREFIX_OFFSET, 16, 1,
+ hif, read_len, true);
+ goto err;
+ }
+
+ if (!(hif->id & HIF_ID_IS_INDICATION)) {
+ (*is_cnf)++;
+ if (hif->id == HIF_CNF_ID_MULTI_TRANSMIT)
+ release_count = le32_to_cpu(((struct hif_cnf_multi_transmit *) hif->body)->num_tx_confs);
+ else
+ release_count = 1;
+ WARN(wdev->hif.tx_buffers_used < release_count, "corrupted buffer counter");
+ wdev->hif.tx_buffers_used -= release_count;
+ if (!wdev->hif.tx_buffers_used)
+ wake_up(&wdev->hif.tx_buffers_empty);
+ }
+
+ if (hif->id != HIF_IND_ID_EXCEPTION && hif->id != HIF_IND_ID_ERROR) {
+ if (hif->seqnum != wdev->hif.rx_seqnum)
+ dev_warn(wdev->dev, "wrong message sequence: %d != %d\n",
+ hif->seqnum, wdev->hif.rx_seqnum);
+ wdev->hif.rx_seqnum = (hif->seqnum + 1) % (HIF_COUNTER_MAX + 1);
+ }
+
+ skb_put(skb, hif->len);
+ dev_kfree_skb(skb); /* FIXME: handle received data */
+
+ return piggyback;
+
+err:
+ if (skb)
+ dev_kfree_skb(skb);
+ return -EIO;
+}
+
+static int bh_work_rx(struct wfx_dev *wdev, int max_msg, int *num_cnf)
+{
+ size_t len;
+ int i;
+ int ctrl_reg, piggyback;
+
+ piggyback = 0;
+ for (i = 0; i < max_msg; i++) {
+ if (piggyback & CTRL_NEXT_LEN_MASK)
+ ctrl_reg = piggyback;
+ else if (try_wait_for_completion(&wdev->hif.ctrl_ready))
+ ctrl_reg = atomic_xchg(&wdev->hif.ctrl_reg, 0);
+ else
+ ctrl_reg = 0;
+ if (!(ctrl_reg & CTRL_NEXT_LEN_MASK))
+ return i;
+ // ctrl_reg units are 16bits words
+ len = (ctrl_reg & CTRL_NEXT_LEN_MASK) * 2;
+ piggyback = rx_helper(wdev, len, num_cnf);
+ if (piggyback < 0)
+ return i;
+ if (!(piggyback & CTRL_WLAN_READY))
+ dev_err(wdev->dev, "unexpected piggyback value: ready bit not set: %04x\n",
+ piggyback);
+ }
+ if (piggyback & CTRL_NEXT_LEN_MASK) {
+ ctrl_reg = atomic_xchg(&wdev->hif.ctrl_reg, piggyback);
+ complete(&wdev->hif.ctrl_ready);
+ if (ctrl_reg)
+ dev_err(wdev->dev, "unexpected IRQ happened: %04x/%04x\n",
+ ctrl_reg, piggyback);
+ }
+ return i;
+}
+
+static void tx_helper(struct wfx_dev *wdev, struct hif_msg *hif)
+{
+ int ret;
+ void *data;
+ bool is_encrypted = false;
+ size_t len = le16_to_cpu(hif->len);
+
+ BUG_ON(len < sizeof(*hif));
+
+ hif->seqnum = wdev->hif.tx_seqnum;
+ wdev->hif.tx_seqnum = (wdev->hif.tx_seqnum + 1) % (HIF_COUNTER_MAX + 1);
+
+ data = hif;
+ WARN(len > wdev->hw_caps.size_inp_ch_buf,
+ "%s: request exceed WFx capability: %zu > %d\n", __func__,
+ len, wdev->hw_caps.size_inp_ch_buf);
+ len = wdev->hwbus_ops->align_size(wdev->hwbus_priv, len);
+ ret = wfx_data_write(wdev, data, len);
+ if (ret)
+ goto end;
+
+ wdev->hif.tx_buffers_used++;
+end:
+ if (is_encrypted)
+ kfree(data);
+}
+
+static int bh_work_tx(struct wfx_dev *wdev, int max_msg)
+{
+ struct hif_msg *hif;
+ int i;
+
+ for (i = 0; i < max_msg; i++) {
+ hif = NULL;
+ if (wdev->hif.tx_buffers_used < wdev->hw_caps.num_inp_ch_bufs) {
+ /* FIXME: get queued data */
+ }
+ if (!hif)
+ return i;
+ tx_helper(wdev, hif);
+ }
+ return i;
+}
+
+/* In SDIO mode, it is necessary to make an access to a register to acknowledge
+ * last received message. It could be possible to restrict this acknowledge to
+ * SDIO mode and only if last operation was rx.
+ */
+static void ack_sdio_data(struct wfx_dev *wdev)
+{
+ uint32_t cfg_reg;
+
+ config_reg_read(wdev, &cfg_reg);
+ if (cfg_reg & 0xFF) {
+ dev_warn(wdev->dev, "chip reports errors: %02x\n", cfg_reg & 0xFF);
+ config_reg_write_bits(wdev, 0xFF, 0x00);
+ }
+}
+
+static void bh_work(struct work_struct *work)
+{
+ struct wfx_dev *wdev = container_of(work, struct wfx_dev, hif.bh);
+ int stats_req = 0, stats_cnf = 0, stats_ind = 0;
+ bool release_chip = false, last_op_is_rx = false;
+ int num_tx, num_rx;
+
+ device_wakeup(wdev);
+ do {
+ num_tx = bh_work_tx(wdev, 32);
+ stats_req += num_tx;
+ if (num_tx)
+ last_op_is_rx = false;
+ num_rx = bh_work_rx(wdev, 32, &stats_cnf);
+ stats_ind += num_rx;
+ if (num_rx)
+ last_op_is_rx = true;
+ } while (num_rx || num_tx);
+ stats_ind -= stats_cnf;
+
+ if (last_op_is_rx)
+ ack_sdio_data(wdev);
+ if (!wdev->hif.tx_buffers_used && !work_pending(work)) {
+ device_release(wdev);
+ release_chip = true;
+ }
+}
+
+/*
+ * An IRQ from chip did occur
+ */
+void wfx_bh_request_rx(struct wfx_dev *wdev)
+{
+ u32 cur, prev;
+
+ control_reg_read(wdev, &cur);
+ prev = atomic_xchg(&wdev->hif.ctrl_reg, cur);
+ complete(&wdev->hif.ctrl_ready);
+ queue_work(system_highpri_wq, &wdev->hif.bh);
+
+ if (!(cur & CTRL_NEXT_LEN_MASK))
+ dev_err(wdev->dev, "unexpected control register value: length field is 0: %04x\n",
+ cur);
+ if (prev != 0)
+ dev_err(wdev->dev, "received IRQ but previous data was not (yet) read: %04x/%04x\n",
+ prev, cur);
+}
+
+/*
+ * Driver want to send data
+ */
+void wfx_bh_request_tx(struct wfx_dev *wdev)
+{
+ queue_work(system_highpri_wq, &wdev->hif.bh);
+}
+
+void wfx_bh_register(struct wfx_dev *wdev)
+{
+ INIT_WORK(&wdev->hif.bh, bh_work);
+ init_completion(&wdev->hif.ctrl_ready);
+ init_waitqueue_head(&wdev->hif.tx_buffers_empty);
+}
+
+void wfx_bh_unregister(struct wfx_dev *wdev)
+{
+ flush_work(&wdev->hif.bh);
+}
diff --git a/drivers/staging/wfx/bh.h b/drivers/staging/wfx/bh.h
new file mode 100644
index 000000000000..93ca98424e0b
--- /dev/null
+++ b/drivers/staging/wfx/bh.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Interrupt bottom half.
+ *
+ * Copyright (c) 2017-2019, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+#ifndef WFX_BH_H
+#define WFX_BH_H
+
+#include <linux/atomic.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+struct wfx_dev;
+
+struct wfx_hif {
+ struct work_struct bh;
+ struct completion ctrl_ready;
+ wait_queue_head_t tx_buffers_empty;
+ atomic_t ctrl_reg;
+ int rx_seqnum;
+ int tx_seqnum;
+ int tx_buffers_used;
+};
+
+void wfx_bh_register(struct wfx_dev *wdev);
+void wfx_bh_unregister(struct wfx_dev *wdev);
+void wfx_bh_request_rx(struct wfx_dev *wdev);
+void wfx_bh_request_tx(struct wfx_dev *wdev);
+
+#endif /* WFX_BH_H */
diff --git a/drivers/staging/wfx/bus_sdio.c b/drivers/staging/wfx/bus_sdio.c
index 25c587fe2141..c0c063c3cfc9 100644
--- a/drivers/staging/wfx/bus_sdio.c
+++ b/drivers/staging/wfx/bus_sdio.c
@@ -15,6 +15,7 @@
#include "wfx.h"
#include "hwio.h"
#include "main.h"
+#include "bh.h"
static const struct wfx_platform_data wfx_sdio_pdata = {
.file_fw = "wfm_wf200",
@@ -90,7 +91,7 @@ static void wfx_sdio_irq_handler(struct sdio_func *func)
struct wfx_sdio_priv *bus = sdio_get_drvdata(func);
if (bus->core)
- /* empty */;
+ wfx_bh_request_rx(bus->core);
else
WARN(!bus->core, "race condition in driver init/deinit");
}
@@ -104,6 +105,7 @@ static irqreturn_t wfx_sdio_irq_handler_ext(int irq, void *priv)
return IRQ_NONE;
}
sdio_claim_host(bus->func);
+ wfx_bh_request_rx(bus->core);
sdio_release_host(bus->func);
return IRQ_HANDLED;
}
diff --git a/drivers/staging/wfx/bus_spi.c b/drivers/staging/wfx/bus_spi.c
index c474949a32dd..8a9aab3e7384 100644
--- a/drivers/staging/wfx/bus_spi.c
+++ b/drivers/staging/wfx/bus_spi.c
@@ -19,6 +19,7 @@
#include "wfx.h"
#include "hwio.h"
#include "main.h"
+#include "bh.h"
#define DETECT_INVALID_CTRL_ACCESS
@@ -215,6 +216,10 @@ static irqreturn_t wfx_spi_irq_handler(int irq, void *priv)
static void wfx_spi_request_rx(struct work_struct *work)
{
+ struct wfx_spi_priv *bus =
+ container_of(work, struct wfx_spi_priv, request_rx);
+
+ wfx_bh_request_rx(bus->core);
}
static size_t wfx_spi_align_size(void *priv, size_t size)
diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c
index a8ef29174232..f0bea053a0d9 100644
--- a/drivers/staging/wfx/main.c
+++ b/drivers/staging/wfx/main.c
@@ -23,6 +23,7 @@
#include "fwio.h"
#include "hwio.h"
#include "bus.h"
+#include "bh.h"
#include "wfx_version.h"
MODULE_DESCRIPTION("Silicon Labs 802.11 Wireless LAN driver for WFx");
@@ -30,6 +31,21 @@ MODULE_AUTHOR("Jérôme Pouiller <jerome.pouiller@silabs.com>");
MODULE_LICENSE("GPL");
MODULE_VERSION(WFX_LABEL);
+static int gpio_wakeup = -2;
+module_param(gpio_wakeup, int, 0644);
+MODULE_PARM_DESC(gpio_wakeup, "gpio number for wakeup. -1 for none.");
+
+bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor)
+{
+ if (wdev->hw_caps.api_version_major < major)
+ return true;
+ if (wdev->hw_caps.api_version_major > major)
+ return false;
+ if (wdev->hw_caps.api_version_minor < minor)
+ return true;
+ return false;
+}
+
struct gpio_desc *wfx_get_gpio(struct device *dev, int override, const char *label)
{
struct gpio_desc *ret;
@@ -82,18 +98,23 @@ int wfx_probe(struct wfx_dev *wdev)
{
int err;
+ wfx_bh_register(wdev);
+
err = wfx_init_device(wdev);
if (err)
goto err1;
+
return 0;
err1:
+ wfx_bh_unregister(wdev);
return err;
}
void wfx_release(struct wfx_dev *wdev)
{
+ wfx_bh_unregister(wdev);
}
static int __init wfx_core_init(void)
diff --git a/drivers/staging/wfx/main.h b/drivers/staging/wfx/main.h
index 8b2526d81984..f7c65999a493 100644
--- a/drivers/staging/wfx/main.h
+++ b/drivers/staging/wfx/main.h
@@ -20,6 +20,7 @@ struct wfx_dev;
struct wfx_platform_data {
/* Keyset and ".sec" extention will appended to this string */
const char *file_fw;
+ struct gpio_desc *gpio_wakeup;
/*
* if true HIF D_out is sampled on the rising edge of the clock
* (intended to be used in 50Mhz SDIO)
@@ -38,5 +39,6 @@ void wfx_release(struct wfx_dev *wdev);
struct gpio_desc *wfx_get_gpio(struct device *dev, int override,
const char *label);
+bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor);
#endif
diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h
index 56aed33291ae..4f28938fa3a6 100644
--- a/drivers/staging/wfx/wfx.h
+++ b/drivers/staging/wfx/wfx.h
@@ -10,7 +10,9 @@
#ifndef WFX_H
#define WFX_H
+#include "bh.h"
#include "main.h"
+#include "hif_api_general.h"
struct hwbus_ops;
@@ -21,6 +23,8 @@ struct wfx_dev {
void *hwbus_priv;
u8 keyset;
+ struct hif_ind_startup hw_caps;
+ struct wfx_hif hif;
};
#endif /* WFX_H */
--
2.20.1
^ permalink raw reply related
* [PATCH 04/20] staging: wfx: add tracepoints for I/O access
From: Jerome Pouiller @ 2019-09-19 10:52 UTC (permalink / raw)
To: devel@driverdev.osuosl.org, linux-wireless@vger.kernel.org
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
Greg Kroah-Hartman, Kalle Valo, David S . Miller, David Le Goff,
Jerome Pouiller
In-Reply-To: <20190919105153.15285-1-Jerome.Pouiller@silabs.com>
From: Jérôme Pouiller <jerome.pouiller@silabs.com>
Some tracepoints are useful for debugging.
Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
---
drivers/staging/wfx/Makefile | 6 +-
drivers/staging/wfx/debug.c | 10 +++
drivers/staging/wfx/hwio.c | 11 +++
drivers/staging/wfx/traces.h | 154 +++++++++++++++++++++++++++++++++++
4 files changed, 180 insertions(+), 1 deletion(-)
create mode 100644 drivers/staging/wfx/debug.c
create mode 100644 drivers/staging/wfx/traces.h
diff --git a/drivers/staging/wfx/Makefile b/drivers/staging/wfx/Makefile
index e860845186cf..330b7288ebb5 100644
--- a/drivers/staging/wfx/Makefile
+++ b/drivers/staging/wfx/Makefile
@@ -1,8 +1,12 @@
# SPDX-License-Identifier: GPL-2.0
+# Necessary for CREATE_TRACE_POINTS
+CFLAGS_debug.o = -I$(src)
+
wfx-y := \
hwio.o \
- main.o
+ main.o \
+ debug.o
wfx-$(CONFIG_SPI) += bus_spi.o
wfx-$(subst m,y,$(CONFIG_MMC)) += bus_sdio.o
diff --git a/drivers/staging/wfx/debug.c b/drivers/staging/wfx/debug.c
new file mode 100644
index 000000000000..bf44c944640d
--- /dev/null
+++ b/drivers/staging/wfx/debug.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Debugfs interface.
+ *
+ * Copyright (c) 2017-2019, Silicon Laboratories, Inc.
+ * Copyright (c) 2010, ST-Ericsson
+ */
+
+#define CREATE_TRACE_POINTS
+#include "traces.h"
diff --git a/drivers/staging/wfx/hwio.c b/drivers/staging/wfx/hwio.c
index fa626a49dd8a..0cf52aee10e7 100644
--- a/drivers/staging/wfx/hwio.c
+++ b/drivers/staging/wfx/hwio.c
@@ -12,6 +12,7 @@
#include "hwio.h"
#include "wfx.h"
#include "bus.h"
+#include "traces.h"
/*
* Internal helpers.
@@ -63,6 +64,7 @@ static int read32_locked(struct wfx_dev *wdev, int reg, u32 *val)
wdev->hwbus_ops->lock(wdev->hwbus_priv);
ret = read32(wdev, reg, val);
+ _trace_io_read32(reg, *val);
wdev->hwbus_ops->unlock(wdev->hwbus_priv);
return ret;
}
@@ -73,6 +75,7 @@ static int write32_locked(struct wfx_dev *wdev, int reg, u32 val)
wdev->hwbus_ops->lock(wdev->hwbus_priv);
ret = write32(wdev, reg, val);
+ _trace_io_write32(reg, val);
wdev->hwbus_ops->unlock(wdev->hwbus_priv);
return ret;
}
@@ -86,11 +89,13 @@ static int write32_bits_locked(struct wfx_dev *wdev, int reg, u32 mask, u32 val)
val &= mask;
wdev->hwbus_ops->lock(wdev->hwbus_priv);
ret = read32(wdev, reg, &val_r);
+ _trace_io_read32(reg, val_r);
if (ret < 0)
goto err;
val_w = (val_r & ~mask) | val;
if (val_w != val_r) {
ret = write32(wdev, reg, val_w);
+ _trace_io_write32(reg, val_w);
}
err:
wdev->hwbus_ops->unlock(wdev->hwbus_priv);
@@ -166,6 +171,7 @@ static int indirect_read_locked(struct wfx_dev *wdev, int reg, u32 addr, void *b
wdev->hwbus_ops->lock(wdev->hwbus_priv);
ret = indirect_read(wdev, reg, addr, buf, len);
+ _trace_io_ind_read(reg, addr, buf, len);
wdev->hwbus_ops->unlock(wdev->hwbus_priv);
return ret;
}
@@ -176,6 +182,7 @@ static int indirect_write_locked(struct wfx_dev *wdev, int reg, u32 addr, const
wdev->hwbus_ops->lock(wdev->hwbus_priv);
ret = indirect_write(wdev, reg, addr, buf, len);
+ _trace_io_ind_write(reg, addr, buf, len);
wdev->hwbus_ops->unlock(wdev->hwbus_priv);
return ret;
}
@@ -190,6 +197,7 @@ static int indirect_read32_locked(struct wfx_dev *wdev, int reg, u32 addr, u32 *
wdev->hwbus_ops->lock(wdev->hwbus_priv);
ret = indirect_read(wdev, reg, addr, tmp, sizeof(u32));
*val = cpu_to_le32(*tmp);
+ _trace_io_ind_read32(reg, addr, *val);
wdev->hwbus_ops->unlock(wdev->hwbus_priv);
kfree(tmp);
return ret;
@@ -205,6 +213,7 @@ static int indirect_write32_locked(struct wfx_dev *wdev, int reg, u32 addr, u32
*tmp = cpu_to_le32(val);
wdev->hwbus_ops->lock(wdev->hwbus_priv);
ret = indirect_write(wdev, reg, addr, tmp, sizeof(u32));
+ _trace_io_ind_write32(reg, addr, val);
wdev->hwbus_ops->unlock(wdev->hwbus_priv);
kfree(tmp);
return ret;
@@ -217,6 +226,7 @@ int wfx_data_read(struct wfx_dev *wdev, void *buf, size_t len)
WARN((long) buf & 3, "%s: unaligned buffer", __func__);
wdev->hwbus_ops->lock(wdev->hwbus_priv);
ret = wdev->hwbus_ops->copy_from_io(wdev->hwbus_priv, WFX_REG_IN_OUT_QUEUE, buf, len);
+ _trace_io_read(WFX_REG_IN_OUT_QUEUE, buf, len);
wdev->hwbus_ops->unlock(wdev->hwbus_priv);
if (ret)
dev_err(wdev->dev, "%s: bus communication error: %d\n", __func__, ret);
@@ -230,6 +240,7 @@ int wfx_data_write(struct wfx_dev *wdev, const void *buf, size_t len)
WARN((long) buf & 3, "%s: unaligned buffer", __func__);
wdev->hwbus_ops->lock(wdev->hwbus_priv);
ret = wdev->hwbus_ops->copy_to_io(wdev->hwbus_priv, WFX_REG_IN_OUT_QUEUE, buf, len);
+ _trace_io_write(WFX_REG_IN_OUT_QUEUE, buf, len);
wdev->hwbus_ops->unlock(wdev->hwbus_priv);
if (ret)
dev_err(wdev->dev, "%s: bus communication error: %d\n", __func__, ret);
diff --git a/drivers/staging/wfx/traces.h b/drivers/staging/wfx/traces.h
new file mode 100644
index 000000000000..ba97df821f1b
--- /dev/null
+++ b/drivers/staging/wfx/traces.h
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Tracepoints definitions.
+ *
+ * Copyright (c) 2018-2019, Silicon Laboratories, Inc.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM wfx
+
+#if !defined(_WFX_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _WFX_TRACE_H
+
+#include <linux/tracepoint.h>
+#include <linux/version.h>
+
+#include "bus.h"
+
+#if (KERNEL_VERSION(4, 1, 0) > LINUX_VERSION_CODE)
+#define TRACE_DEFINE_ENUM(a)
+#endif
+
+/* The hell below need some explanations. For each symbolic number, we need to
+ * define it with TRACE_DEFINE_ENUM() and in a list for __print_symbolic.
+ *
+ * 1. Define a new macro that call TRACE_DEFINE_ENUM():
+ *
+ * #define xxx_name(sym) TRACE_DEFINE_ENUM(sym);
+ *
+ * 2. Define list of all symbols:
+ *
+ * #define list_names \
+ * ... \
+ * xxx_name(XXX) \
+ * ...
+ *
+ * 3. Instanciate that list_names:
+ *
+ * list_names
+ *
+ * 4. Redefine xxx_name() as a entry of array for __print_symbolic()
+ *
+ * #undef xxx_name
+ * #define xxx_name(msg) { msg, #msg },
+ *
+ * 5. list_name can now nearlu be used with __print_symbolic() but,
+ * __print_symbolic() dislike last comma of list. So we define a new list
+ * with a dummy element:
+ *
+ * #define list_for_print_symbolic list_names { -1, NULL }
+ */
+
+#define wfx_reg_list_enum \
+ wfx_reg_name(WFX_REG_CONFIG, "CONFIG") \
+ wfx_reg_name(WFX_REG_CONTROL, "CONTROL") \
+ wfx_reg_name(WFX_REG_IN_OUT_QUEUE, "QUEUE") \
+ wfx_reg_name(WFX_REG_AHB_DPORT, "AHB") \
+ wfx_reg_name(WFX_REG_BASE_ADDR, "BASE_ADDR") \
+ wfx_reg_name(WFX_REG_SRAM_DPORT, "SRAM") \
+ wfx_reg_name(WFX_REG_SET_GEN_R_W, "SET_GEN_R_W") \
+ wfx_reg_name(WFX_REG_FRAME_OUT, "FRAME_OUT")
+
+#undef wfx_reg_name
+#define wfx_reg_name(sym, name) TRACE_DEFINE_ENUM(sym);
+wfx_reg_list_enum
+#undef wfx_reg_name
+#define wfx_reg_name(sym, name) { sym, name },
+#define wfx_reg_list wfx_reg_list_enum { -1, NULL }
+
+DECLARE_EVENT_CLASS(io_data,
+ TP_PROTO(int reg, int addr, const void *io_buf, size_t len),
+ TP_ARGS(reg, addr, io_buf, len),
+ TP_STRUCT__entry(
+ __field(int, reg)
+ __field(int, addr)
+ __field(int, msg_len)
+ __field(int, buf_len)
+ __array(u8, buf, 32)
+ __array(u8, addr_str, 10)
+ ),
+ TP_fast_assign(
+ __entry->reg = reg;
+ __entry->addr = addr;
+ __entry->msg_len = len;
+ __entry->buf_len = min_t(int, sizeof(__entry->buf), __entry->msg_len);
+ memcpy(__entry->buf, io_buf, __entry->buf_len);
+ if (addr >= 0)
+ snprintf(__entry->addr_str, 10, "/%08x", addr);
+ else
+ __entry->addr_str[0] = 0;
+ ),
+ TP_printk("%s%s: %s%s (%d bytes)",
+ __print_symbolic(__entry->reg, wfx_reg_list),
+ __entry->addr_str,
+ __print_hex(__entry->buf, __entry->buf_len),
+ __entry->msg_len > sizeof(__entry->buf) ? " ..." : "",
+ __entry->msg_len
+ )
+);
+DEFINE_EVENT(io_data, io_write,
+ TP_PROTO(int reg, int addr, const void *io_buf, size_t len),
+ TP_ARGS(reg, addr, io_buf, len));
+#define _trace_io_ind_write(reg, addr, io_buf, len) trace_io_write(reg, addr, io_buf, len)
+#define _trace_io_write(reg, io_buf, len) trace_io_write(reg, -1, io_buf, len)
+DEFINE_EVENT(io_data, io_read,
+ TP_PROTO(int reg, int addr, const void *io_buf, size_t len),
+ TP_ARGS(reg, addr, io_buf, len));
+#define _trace_io_ind_read(reg, addr, io_buf, len) trace_io_read(reg, addr, io_buf, len)
+#define _trace_io_read(reg, io_buf, len) trace_io_read(reg, -1, io_buf, len)
+
+DECLARE_EVENT_CLASS(io_data32,
+ TP_PROTO(int reg, int addr, u32 val),
+ TP_ARGS(reg, addr, val),
+ TP_STRUCT__entry(
+ __field(int, reg)
+ __field(int, addr)
+ __field(int, val)
+ __array(u8, addr_str, 10)
+ ),
+ TP_fast_assign(
+ __entry->reg = reg;
+ __entry->addr = addr;
+ __entry->val = val;
+ if (addr >= 0)
+ snprintf(__entry->addr_str, 10, "/%08x", addr);
+ else
+ __entry->addr_str[0] = 0;
+ ),
+ TP_printk("%s%s: %08x",
+ __print_symbolic(__entry->reg, wfx_reg_list),
+ __entry->addr_str,
+ __entry->val
+ )
+);
+DEFINE_EVENT(io_data32, io_write32,
+ TP_PROTO(int reg, int addr, u32 val),
+ TP_ARGS(reg, addr, val));
+#define _trace_io_ind_write32(reg, addr, val) trace_io_write32(reg, addr, val)
+#define _trace_io_write32(reg, val) trace_io_write32(reg, -1, val)
+DEFINE_EVENT(io_data32, io_read32,
+ TP_PROTO(int reg, int addr, u32 val),
+ TP_ARGS(reg, addr, val));
+#define _trace_io_ind_read32(reg, addr, val) trace_io_read32(reg, addr, val)
+#define _trace_io_read32(reg, val) trace_io_read32(reg, -1, val)
+
+#endif
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE traces
+
+#include <trace/define_trace.h>
--
2.20.1
^ 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