* Re: [PATCH] bpf: Fix test_lru_sanity5() in test_lru_map.c
From: Alexei Starovoitov @ 2017-01-17 18:16 UTC (permalink / raw)
To: Daniel Borkmann, Martin KaFai Lau, netdev; +Cc: Alexei Starovoitov, Kernel Team
In-Reply-To: <587DDED4.5060706@iogearbox.net>
On 1/17/17 1:07 AM, Daniel Borkmann wrote:
> On 01/17/2017 07:17 AM, Martin KaFai Lau wrote:
>> test_lru_sanity5() fails when the number of online cpus
>> is fewer than the number of possible cpus. It can be
>> reproduced with qemu by using cmd args "--smp cpus=2,maxcpus=8".
>>
>> The problem is the loop in test_lru_sanity5() is testing
>> 'i' which is incorrect.
>>
>> This patch:
>> 1. Make sched_next_online() always return -1 if it cannot
>> find a next cpu to schedule the process.
>> 2. In test_lru_sanity5(), the parent process does
>> sched_setaffinity() first (through sched_next_online())
>> and the forked process will inherit it according to
>> the 'man sched_setaffinity'.
>>
>> Fixes: 5db58faf989f ("bpf: Add tests for the LRU bpf_htab")
>> Reported-by: Daniel Borkmann <daniel@iogearbox.net>
>> Signed-off-by: Martin KaFai Lau <kafai@fb.com>
>
> Looks good, thanks for fixing!
>
> Acked-by: Daniel Borkmann <daniel@iogearbox.net>
>
> (Patch is against -net tree.)
Acked-by: Alexei Starovoitov <ast@kernel.org>
^ permalink raw reply
* Re: [PATCH net 1/1] net sched actions: fix refcnt when GETing of action after bind
From: Cong Wang @ 2017-01-17 18:17 UTC (permalink / raw)
To: Jamal Hadi Salim; +Cc: David Miller, Linux Kernel Network Developers
In-Reply-To: <1484493246-10911-1-git-send-email-jhs@emojatatu.com>
On Sun, Jan 15, 2017 at 7:14 AM, Jamal Hadi Salim <jhs@mojatatu.com> wrote:
> diff --git a/net/sched/act_api.c b/net/sched/act_api.c
> index 2095c83..e10456ef6f 100644
> --- a/net/sched/act_api.c
> +++ b/net/sched/act_api.c
> @@ -900,8 +900,6 @@ static int tca_action_flush(struct net *net, struct nlattr *nla,
> goto err;
> }
> act->order = i;
> - if (event == RTM_GETACTION)
> - act->tcfa_refcnt++;
> list_add_tail(&act->list, &actions);
> }
>
> @@ -914,7 +912,8 @@ static int tca_action_flush(struct net *net, struct nlattr *nla,
> return ret;
> }
> err:
> - tcf_action_destroy(&actions, 0);
> + if (event != RTM_GETACTION)
> + tcf_action_destroy(&actions, 0);
Why this check for RTM_GETACTION? It does not make sense
at least for the error case, that is, when tcf_action_get_1() fails
in the middle of the loop, all the previous ones should be destroyed
no matter it's GETACTION or DELACTION...
Also, for the non-error case of GETACTION, they should be
destroyed too after dumping to user-space, otherwise there is no
way to use them since 'actions' is local to that function.
I thought in commit aecc5cefc389 you grab that refcnt on purpose.
^ permalink raw reply
* Re: [net, 2/6] net: korina: define NAPI_WEIGHT
From: Roman Yeryomin @ 2017-01-17 18:30 UTC (permalink / raw)
To: David Miller; +Cc: netdev
In-Reply-To: <20170117.124006.1559010522356313497.davem@davemloft.net>
On 17 January 2017 at 19:40, David Miller <davem@davemloft.net> wrote:
> From: Roman Yeryomin <leroi.lists@gmail.com>
> Date: Tue, 17 Jan 2017 19:32:22 +0200
>
>> @@ -77,6 +77,8 @@
>>
>> #define MII_CLOCK 1250000 /* no more than 2.5MHz */
>>
>> +#define NAPI_WEIGHT 64
>> +
>> /* the following must be powers of two */
>> #define KORINA_NUM_RDS 64 /* number of receive descriptors */
>> #define KORINA_NUM_TDS 64 /* number of transmit descriptors */
>> @@ -1080,7 +1082,7 @@ static int korina_probe(struct platform_device *pdev)
>> dev->netdev_ops = &korina_netdev_ops;
>> dev->ethtool_ops = &netdev_ethtool_ops;
>> dev->watchdog_timeo = TX_TIMEOUT;
>> - netif_napi_add(dev, &lp->napi, korina_poll, 64);
>> + netif_napi_add(dev, &lp->napi, korina_poll, NAPI_WEIGHT);
>
> Please use NAPI_POLL_WEIGHT from linux/netdevice.h
OK, should I resend the whole set or just this one and dependant?
Regards,
Roman
^ permalink raw reply
* Re: [net, 2/6] net: korina: define NAPI_WEIGHT
From: Florian Fainelli @ 2017-01-17 18:32 UTC (permalink / raw)
To: Roman Yeryomin, David Miller; +Cc: netdev
In-Reply-To: <CACiydbLyswWZ2ZX04m22Ga_FBbp-52kxOxBbNcvS+R1d1Kdpcw@mail.gmail.com>
On 01/17/2017 10:30 AM, Roman Yeryomin wrote:
> On 17 January 2017 at 19:40, David Miller <davem@davemloft.net> wrote:
>> From: Roman Yeryomin <leroi.lists@gmail.com>
>> Date: Tue, 17 Jan 2017 19:32:22 +0200
>>
>>> @@ -77,6 +77,8 @@
>>>
>>> #define MII_CLOCK 1250000 /* no more than 2.5MHz */
>>>
>>> +#define NAPI_WEIGHT 64
>>> +
>>> /* the following must be powers of two */
>>> #define KORINA_NUM_RDS 64 /* number of receive descriptors */
>>> #define KORINA_NUM_TDS 64 /* number of transmit descriptors */
>>> @@ -1080,7 +1082,7 @@ static int korina_probe(struct platform_device *pdev)
>>> dev->netdev_ops = &korina_netdev_ops;
>>> dev->ethtool_ops = &netdev_ethtool_ops;
>>> dev->watchdog_timeo = TX_TIMEOUT;
>>> - netif_napi_add(dev, &lp->napi, korina_poll, 64);
>>> + netif_napi_add(dev, &lp->napi, korina_poll, NAPI_WEIGHT);
>>
>> Please use NAPI_POLL_WEIGHT from linux/netdevice.h
>
> OK, should I resend the whole set or just this one and dependant?
You need to resend everything and please also provide a cover letter,
and a proper patch subject (e.g: git format-patch --cover-letter
--subject="PATCH net-next v2" a..b)
Thanks!
--
Florian
^ permalink raw reply
* Re: [net, 3/6] net: korina: increase tx/rx ring sizes
From: Roman Yeryomin @ 2017-01-17 18:27 UTC (permalink / raw)
To: David Miller; +Cc: netdev
In-Reply-To: <20170117.125844.253301507252169868.davem@davemloft.net>
On 17 January 2017 at 19:58, David Miller <davem@davemloft.net> wrote:
> From: Roman Yeryomin <leroi.lists@gmail.com>
> Date: Tue, 17 Jan 2017 19:32:36 +0200
>
>> Having larger ring sizes almost eliminates rx fifo overflow, thus improving performance.
>> This patch reduces rx overflow occurence by approximately 1000 times (from ~25k down to ~25 times per 3M frames)
>
> Those numbers don't mean much without full context.
>
> What kind of system, what kind of traffic, and over what kind of link?
MIPS rb532 board, TCP iperf3 test over 100M link, NATed speed ~55Mbps.
I can do more tests and provide more precise numbers, if needed.
Regards,
Roman
^ permalink raw reply
* Re: [PATCH] virtio: don't set VIRTIO_NET_HDR_F_DATA_VALID on xmit
From: Michael S. Tsirkin @ 2017-01-17 18:27 UTC (permalink / raw)
To: Rolf Neugebauer; +Cc: netdev, davem, jasowang, rppt
In-Reply-To: <20170117181351.98291-1-rolf.neugebauer@docker.com>
On Tue, Jan 17, 2017 at 06:13:51PM +0000, Rolf Neugebauer wrote:
> This patch part reverts fd2a0437dc33 and e858fae2b0b8 which introduced a
> subtle change in how the virtio_net flags are derived from the SKBs
> ip_summed field.
>
> With the above commits, the flags are set to VIRTIO_NET_HDR_F_DATA_VALID
> when ip_summed == CHECKSUM_UNNECESSARY, thus treating it differently to
> ip_summed == CHECKSUM_NONE, which should be the same.
>
> Further, the virtio spec 1.0 / CS04 explicitly says that
> VIRTIO_NET_HDR_F_DATA_VALID must not be set by the driver.
>
> Signed-off-by: Rolf Neugebauer <rolf.neugebauer@docker.com>
Fixes: fd2a0437dc33 ("virtio_net: introduce virtio_net_hdr_{from,to}_skb")
Fixes: e858fae2b0b8 (" virtio_net: use common code for virtio_net_hdr and skb GSO conversion")
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Should be backported into stable as well.
> ---
> include/linux/virtio_net.h | 2 --
> 1 file changed, 2 deletions(-)
>
> diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
> index 66204007d7ac..56436472ccc7 100644
> --- a/include/linux/virtio_net.h
> +++ b/include/linux/virtio_net.h
> @@ -91,8 +91,6 @@ static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb,
> skb_checksum_start_offset(skb));
> hdr->csum_offset = __cpu_to_virtio16(little_endian,
> skb->csum_offset);
> - } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
> - hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID;
> } /* else everything is zero */
>
> return 0;
> --
> 2.11.0
^ permalink raw reply
* [PATCH net-next] tcp: accept RST for rcv_nxt - 1 after receiving a FIN
From: Jason Baron @ 2017-01-17 18:37 UTC (permalink / raw)
To: davem; +Cc: netdev, Eric Dumazet
From: Jason Baron <jbaron@akamai.com>
Using a Mac OSX box as a client connecting to a Linux server, we have found
that when certain applications (such as 'ab'), are abruptly terminated
(via ^C), a FIN is sent followed by a RST packet on tcp connections. The
FIN is accepted by the Linux stack but the RST is sent with the same
sequence number as the FIN, and Linux responds with a challenge ACK per
RFC 5961. The OSX client then sometimes (they are rate-limited) does not
reply with any RST as would be expected on a closed socket.
This results in sockets accumulating on the Linux server left mostly in
the CLOSE_WAIT state, although LAST_ACK and CLOSING are also possible.
This sequence of events can tie up a lot of resources on the Linux server
since there may be a lot of data in write buffers at the time of the RST.
Accepting a RST equal to rcv_nxt - 1, after we have already successfully
processed a FIN, has made a significant difference for us in practice, by
freeing up unneeded resources in a more expedient fashion.
A packetdrill test demonstrating the behavior:
// testing mac osx rst behavior
// Establish a connection
0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
0.000 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
0.000 bind(3, ..., ...) = 0
0.000 listen(3, 1) = 0
0.100 < S 0:0(0) win 32768 <mss 1460,nop,wscale 10>
0.100 > S. 0:0(0) ack 1 <mss 1460,nop,wscale 5>
0.200 < . 1:1(0) ack 1 win 32768
0.200 accept(3, ..., ...) = 4
// Client closes the connection
0.300 < F. 1:1(0) ack 1 win 32768
// now send rst with same sequence
0.300 < R. 1:1(0) ack 1 win 32768
// make sure we are in TCP_CLOSE
0.400 %{
assert tcpi_state == 7
}%
Signed-off-by: Jason Baron <jbaron@akamai.com>
Cc: Eric Dumazet <edumazet@google.com>
---
net/ipv4/tcp_input.c | 28 +++++++++++++++++++++++++---
1 file changed, 25 insertions(+), 3 deletions(-)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 1a34e9278c07..bfa165cc455a 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -5199,6 +5199,23 @@ static int tcp_copy_to_iovec(struct sock *sk, struct sk_buff *skb, int hlen)
return err;
}
+/* Accept RST for rcv_nxt - 1 after a FIN.
+ * When tcp connections are abruptly terminated from Mac OSX (via ^C), a
+ * FIN is sent followed by a RST packet. The RST is sent with the same
+ * sequence number as the FIN, and thus according to RFC 5961 a challenge
+ * ACK should be sent. However, Mac OSX rate limits replies to challenge
+ * ACKs on the closed socket. In addition middleboxes can drop either the
+ * challenge ACK or a subsequent RST.
+ */
+static bool tcp_reset_check(const struct sock *sk, const struct sk_buff *skb)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ return unlikely(TCP_SKB_CB(skb)->seq == (tp->rcv_nxt - 1) &&
+ (1 << sk->sk_state) & (TCPF_CLOSE_WAIT | TCPF_LAST_ACK |
+ TCPF_CLOSING));
+}
+
/* Does PAWS and seqno based validation of an incoming segment, flags will
* play significant role here.
*/
@@ -5237,20 +5254,25 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
LINUX_MIB_TCPACKSKIPPEDSEQ,
&tp->last_oow_ack_time))
tcp_send_dupack(sk, skb);
+ } else if (tcp_reset_check(sk, skb)) {
+ tcp_reset(sk);
}
goto discard;
}
/* Step 2: check RST bit */
if (th->rst) {
- /* RFC 5961 3.2 (extend to match against SACK too if available):
- * If seq num matches RCV.NXT or the right-most SACK block,
+ /* RFC 5961 3.2 (extend to match against (RCV.NXT - 1) after a
+ * FIN and SACK too if available):
+ * If seq num matches RCV.NXT or (RCV.NXT - 1) after a FIN, or
+ * the right-most SACK block,
* then
* RESET the connection
* else
* Send a challenge ACK
*/
- if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt) {
+ if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt ||
+ tcp_reset_check(sk, skb)) {
rst_seq_match = true;
} else if (tcp_is_sack(tp) && tp->rx_opt.num_sacks > 0) {
struct tcp_sack_block *sp = &tp->selective_acks[0];
--
2.6.1
^ permalink raw reply related
* Re: [PATCH net-next v4 1/2] net sched actions: Add support for user cookies
From: Cong Wang @ 2017-01-17 18:40 UTC (permalink / raw)
To: Jamal Hadi Salim
Cc: David Miller, Linux Kernel Network Developers, Jiri Pirko, paulb,
John Fastabend, Simon Horman, Roman Mashak, Hadar Har-Zion,
Or Gerlitz, Roi Dayan, Daniel Borkmann
In-Reply-To: <1484651509-27500-2-git-send-email-jhs@emojatatu.com>
On Tue, Jan 17, 2017 at 3:11 AM, Jamal Hadi Salim <jhs@mojatatu.com> wrote:
> @@ -575,6 +583,23 @@ struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla,
> if (err < 0)
> goto err_mod;
>
> + if (tb[TCA_ACT_COOKIE]) {
> + if (nla_len(tb[TCA_ACT_COOKIE]) > TC_COOKIE_MAX_SIZE) {
> + err = -EINVAL;
> + goto err_mod;
> + }
> +
> + a->act_ck = kzalloc(sizeof(*a->act_ck), GFP_KERNEL);
> + if (unlikely(!a->act_ck)) {
> + err = -ENOMEM;
> + goto err_mod;
> + }
> +
I am afraid you can't just goto err_mod for error case here, b/c ->init()
is already called before this, you probably either have to call ->destroy()
for error path, or move this before ->init().
^ permalink raw reply
* Re: [net PATCH v4 5/6] virtio: add pci_down/pci_up configuration
From: John Fastabend @ 2017-01-17 18:45 UTC (permalink / raw)
To: Jason Wang, mst; +Cc: john.r.fastabend, netdev, alexei.starovoitov, daniel
In-Reply-To: <587C464E.6020407@gmail.com>
On 17-01-15 08:04 PM, John Fastabend wrote:
> On 17-01-15 07:57 PM, Jason Wang wrote:
>>
>>
>> On 2017年01月16日 08:01, John Fastabend wrote:
>>> In virtio_net we need to do a full reset of the device to support
>>> queue reconfiguration and also we can trigger this via ethtool
>>> commands. So instead of open coding this in net driver push this
>>> into generic code in virtio. This also avoid exporting a handful
>>> of internal virtio routines.
>>
>> Looks like this is not a pci specific stuffs. And there's some driver left (e.g
>> scsi and block).
>>
>> In fact, I'm not sure touching other drivers is really needed. Maybe we can just:
>> - move virtio_device_freeze(), virtio_device_restore() and .freeze/.restore in
>> virtio_driver out of CONFIG_PM_SLEEP
>> - move virtnet_freeze() and virtnet_restore() out of CONFIG_PM_SLEEP
>> - introduce virtio_net_reset() and call
>> virtio_device_freeze()/virtio_device_restore() there
>>
>
> Michael, what do you think? I was following your suggestion on writing a
> generic virtio reset here. I don't have a preference either way.
Actually if I roll it into virtio_net per above suggestion I can probably get
the locking correct. As DaveM points out its complete junk at the moment around
the rtnl_try_lock() logic. Ugh.
I'll spin a v5...
>
>> Another possible issue for sleep/hibernation is xdp_prog were not restored, if
>> this is not XDP intended, we'd better fix this.
>
> Yep this bug exists even without this series. I'll add it to my list of things
> to fix. Along with small packet max_mtu.
>
>>
>> Thanks
>>
>> [...]
>
^ permalink raw reply
* Re: [net, 6/6] net: korina: version bump
From: Felix Fietkau @ 2017-01-17 18:55 UTC (permalink / raw)
To: Roman Yeryomin, netdev
In-Reply-To: <1484674393-22515-1-git-send-email-roman@advem.lv>
On 2017-01-17 18:33, Roman Yeryomin wrote:
> Signed-off-by: Roman Yeryomin <roman@advem.lv>
> ---
> drivers/net/ethernet/korina.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/ethernet/korina.c b/drivers/net/ethernet/korina.c
> index 83c994f..c8fed01 100644
> --- a/drivers/net/ethernet/korina.c
> +++ b/drivers/net/ethernet/korina.c
> @@ -66,8 +66,8 @@
> #include <asm/mach-rc32434/dma_v.h>
>
> #define DRV_NAME "korina"
> -#define DRV_VERSION "0.10"
> -#define DRV_RELDATE "04Mar2008"
> +#define DRV_VERSION "0.20"
> +#define DRV_RELDATE "15Jan2017"
I think it would make more sense to remove this version instead of
bumping it. Individual driver versions are rather pointless, the kernel
version is more meaningful anyway.
- Felix
^ permalink raw reply
* Re: [PATCH net-next] tcp: accept RST for rcv_nxt - 1 after receiving a FIN
From: Eric Dumazet @ 2017-01-17 19:02 UTC (permalink / raw)
To: Jason Baron; +Cc: davem, netdev, Eric Dumazet
In-Reply-To: <1484678239-19199-1-git-send-email-jbaron@akamai.com>
On Tue, 2017-01-17 at 13:37 -0500, Jason Baron wrote:
> From: Jason Baron <jbaron@akamai.com>
>
> Using a Mac OSX box as a client connecting to a Linux server, we have found
> that when certain applications (such as 'ab'), are abruptly terminated
> (via ^C), a FIN is sent followed by a RST packet on tcp connections. The
> FIN is accepted by the Linux stack but the RST is sent with the same
> sequence number as the FIN, and Linux responds with a challenge ACK per
> RFC 5961. The OSX client then sometimes (they are rate-limited) does not
> reply with any RST as would be expected on a closed socket.
>
> This results in sockets accumulating on the Linux server left mostly in
> the CLOSE_WAIT state, although LAST_ACK and CLOSING are also possible.
> This sequence of events can tie up a lot of resources on the Linux server
> since there may be a lot of data in write buffers at the time of the RST.
> Accepting a RST equal to rcv_nxt - 1, after we have already successfully
> processed a FIN, has made a significant difference for us in practice, by
> freeing up unneeded resources in a more expedient fashion.
Acked-by: Eric Dumazet <edumazet@google.com>
Thanks Jason !
^ permalink raw reply
* Re: [net PATCH v4 2/6] virtio_net: wrap rtnl_lock in test for calling with lock already held
From: John Fastabend @ 2017-01-17 19:03 UTC (permalink / raw)
To: David Miller
Cc: jasowang, mst, john.r.fastabend, netdev, alexei.starovoitov,
daniel
In-Reply-To: <20170117.115733.428297564258724055.davem@davemloft.net>
On 17-01-17 08:57 AM, David Miller wrote:
> From: John Fastabend <john.fastabend@gmail.com>
> Date: Sun, 15 Jan 2017 15:59:47 -0800
>
>> @@ -2358,7 +2371,10 @@ static void remove_vq_common(struct virtnet_info *vi)
>> /* Free unused buffers in both send and recv, if any. */
>> free_unused_bufs(vi);
>>
>> - free_receive_bufs(vi);
>> + if (rtnl_is_locked())
>> + _free_receive_bufs(vi);
>> + else
>> + free_receive_bufs(vi);
>>
>> free_receive_page_frags(vi);
>>
>
> This doesn't work. rtnl_is_locked() doesn't tell if _you_ own the mutex, it
> just says that someone does.
>
> So if we now execute this code without taking the RTNL lock just because some
> other thread of control holds it, we introduce a race.
>
yeah this bit is junk. dang. Trying to get this locking right without duplicate
code or pushing around lock_me variables is getting tricky.
.John
^ permalink raw reply
* Re: [PATCH net-next] tcp: accept RST for rcv_nxt - 1 after receiving a FIN
From: Rick Jones @ 2017-01-17 19:04 UTC (permalink / raw)
To: Jason Baron, davem; +Cc: netdev, Eric Dumazet
In-Reply-To: <1484678239-19199-1-git-send-email-jbaron@akamai.com>
On 01/17/2017 10:37 AM, Jason Baron wrote:
> From: Jason Baron <jbaron@akamai.com>
>
> Using a Mac OSX box as a client connecting to a Linux server, we have found
> that when certain applications (such as 'ab'), are abruptly terminated
> (via ^C), a FIN is sent followed by a RST packet on tcp connections. The
> FIN is accepted by the Linux stack but the RST is sent with the same
> sequence number as the FIN, and Linux responds with a challenge ACK per
> RFC 5961. The OSX client then sometimes (they are rate-limited) does not
> reply with any RST as would be expected on a closed socket.
>
> This results in sockets accumulating on the Linux server left mostly in
> the CLOSE_WAIT state, although LAST_ACK and CLOSING are also possible.
> This sequence of events can tie up a lot of resources on the Linux server
> since there may be a lot of data in write buffers at the time of the RST.
> Accepting a RST equal to rcv_nxt - 1, after we have already successfully
> processed a FIN, has made a significant difference for us in practice, by
> freeing up unneeded resources in a more expedient fashion.
Drifting a bit, and it doesn't change the value of dealing with it, but
out of curiosity, when you say mostly in CLOSE_WAIT, why aren't the
server-side applications reacting to the read return of zero triggered
by the arrival of the FIN?
happy benchmarking,
rick jones
^ permalink raw reply
* Re: Kernel 4.6.7-rt14 kernel workqueue lockup - rtnl deadlock plus syscall endless loop
From: David Miller @ 2017-01-17 19:05 UTC (permalink / raw)
To: EladN; +Cc: netdev
In-Reply-To: <AM5PR0701MB1873F82D61AF17A09F2B0E26A07C0@AM5PR0701MB1873.eurprd07.prod.outlook.com>
From: Elad Nachman <EladN@gilat.com>
Date: Tue, 17 Jan 2017 18:15:19 +0000
> Any thought about limiting the amount of busy polling? Say if more
> than X polls are done within a jiffy, then at least for preemptable
> kernels you can sleep for a jiffy inside the syscall to yield the
> CPU for a while?
We cannot yield there, because we must return immediately from this
context in order to drop the sysctl locks and references.
^ permalink raw reply
* Re: [PATCH] virtio: don't set VIRTIO_NET_HDR_F_DATA_VALID on xmit
From: David Miller @ 2017-01-17 19:08 UTC (permalink / raw)
To: mst; +Cc: rolf.neugebauer, netdev, jasowang, rppt
In-Reply-To: <20170117202515-mutt-send-email-mst@kernel.org>
From: "Michael S. Tsirkin" <mst@redhat.com>
Date: Tue, 17 Jan 2017 20:27:33 +0200
> On Tue, Jan 17, 2017 at 06:13:51PM +0000, Rolf Neugebauer wrote:
>> This patch part reverts fd2a0437dc33 and e858fae2b0b8 which introduced a
>> subtle change in how the virtio_net flags are derived from the SKBs
>> ip_summed field.
>>
>> With the above commits, the flags are set to VIRTIO_NET_HDR_F_DATA_VALID
>> when ip_summed == CHECKSUM_UNNECESSARY, thus treating it differently to
>> ip_summed == CHECKSUM_NONE, which should be the same.
>>
>> Further, the virtio spec 1.0 / CS04 explicitly says that
>> VIRTIO_NET_HDR_F_DATA_VALID must not be set by the driver.
>>
>> Signed-off-by: Rolf Neugebauer <rolf.neugebauer@docker.com>
>
> Fixes: fd2a0437dc33 ("virtio_net: introduce virtio_net_hdr_{from,to}_skb")
> Fixes: e858fae2b0b8 (" virtio_net: use common code for virtio_net_hdr and skb GSO conversion")
> Acked-by: Michael S. Tsirkin <mst@redhat.com>
>
> Should be backported into stable as well.
Applied and queued up for -stable, thanks everyone.
^ permalink raw reply
* Re: [net, 3/6] net: korina: increase tx/rx ring sizes
From: David Miller @ 2017-01-17 19:08 UTC (permalink / raw)
To: leroi.lists; +Cc: netdev
In-Reply-To: <CACiydbLuVf_jsWor6-OJmwQhxfwd=dzBDnDQnJrp3owf8qCkQA@mail.gmail.com>
From: Roman Yeryomin <leroi.lists@gmail.com>
Date: Tue, 17 Jan 2017 20:27:57 +0200
> On 17 January 2017 at 19:58, David Miller <davem@davemloft.net> wrote:
>> From: Roman Yeryomin <leroi.lists@gmail.com>
>> Date: Tue, 17 Jan 2017 19:32:36 +0200
>>
>>> Having larger ring sizes almost eliminates rx fifo overflow, thus improving performance.
>>> This patch reduces rx overflow occurence by approximately 1000 times (from ~25k down to ~25 times per 3M frames)
>>
>> Those numbers don't mean much without full context.
>>
>> What kind of system, what kind of traffic, and over what kind of link?
>
> MIPS rb532 board, TCP iperf3 test over 100M link, NATed speed ~55Mbps.
> I can do more tests and provide more precise numbers, if needed.
Provide this information in the commit message on your respin of this patch
series after you've addressed all of the other feedback, don't tell me :-)
^ permalink raw reply
* Re: [net, 2/6] net: korina: define NAPI_WEIGHT
From: David Miller @ 2017-01-17 19:08 UTC (permalink / raw)
To: leroi.lists; +Cc: netdev
In-Reply-To: <CACiydbLyswWZ2ZX04m22Ga_FBbp-52kxOxBbNcvS+R1d1Kdpcw@mail.gmail.com>
From: Roman Yeryomin <leroi.lists@gmail.com>
Date: Tue, 17 Jan 2017 20:30:44 +0200
> On 17 January 2017 at 19:40, David Miller <davem@davemloft.net> wrote:
>> From: Roman Yeryomin <leroi.lists@gmail.com>
>> Date: Tue, 17 Jan 2017 19:32:22 +0200
>>
>>> @@ -77,6 +77,8 @@
>>>
>>> #define MII_CLOCK 1250000 /* no more than 2.5MHz */
>>>
>>> +#define NAPI_WEIGHT 64
>>> +
>>> /* the following must be powers of two */
>>> #define KORINA_NUM_RDS 64 /* number of receive descriptors */
>>> #define KORINA_NUM_TDS 64 /* number of transmit descriptors */
>>> @@ -1080,7 +1082,7 @@ static int korina_probe(struct platform_device *pdev)
>>> dev->netdev_ops = &korina_netdev_ops;
>>> dev->ethtool_ops = &netdev_ethtool_ops;
>>> dev->watchdog_timeo = TX_TIMEOUT;
>>> - netif_napi_add(dev, &lp->napi, korina_poll, 64);
>>> + netif_napi_add(dev, &lp->napi, korina_poll, NAPI_WEIGHT);
>>
>> Please use NAPI_POLL_WEIGHT from linux/netdevice.h
>
> OK, should I resend the whole set or just this one and dependant?
You always must do this for any change whatsoever, a full respin of a
series is always required on any modification whatsoever to any patch.
^ permalink raw reply
* Re: [PATCH] stmmac: add missing of_node_put
From: David Miller @ 2017-01-17 19:10 UTC (permalink / raw)
To: Julia.Lawall
Cc: peppe.cavallaro, kernel-janitors, alexandre.torgue, netdev,
linux-kernel
In-Reply-To: <1484652201-20048-1-git-send-email-Julia.Lawall@lip6.fr>
From: Julia Lawall <Julia.Lawall@lip6.fr>
Date: Tue, 17 Jan 2017 12:23:21 +0100
> The function stmmac_dt_phy provides several possibilities for initializing
> plat->mdio_node, all of which have the effect of increasing the reference
> count of the assigned value. This field is not updated elsewhere, so the
> value is live until the end of the lifetime of plat (devm_allocated), just
> after the end of stmmac_remove_config_dt. Thus, add an of_node_put on
> plat->mdio_node in stmmac_remove_config_dt. It is possible that the field
> mdio_node is never initialized, but of_node_put is NULL-safe, so it is also
> safe to call of_node_put in that case.
>
> Signed-off-by: Julia Lawall <Julia.Lawall@lip6.fr>
Applied, thanks Julia.
^ permalink raw reply
* Re: [PATCH 2/2] can: spi: hi311x: Add Holt HI-311x CAN driver
From: Akshay Bhat @ 2017-01-17 19:11 UTC (permalink / raw)
To: Marc Kleine-Budde, wg, robh+dt
Cc: mark.rutland, linux-can, netdev, devicetree, linux-kernel,
Akshay Bhat
In-Reply-To: <7ad30b57-b97f-466e-3792-a433e4a2a2ca@pengutronix.de>
[-- Attachment #1.1: Type: text/plain, Size: 36043 bytes --]
Marc,
On 01/03/2017 10:31 AM, Marc Kleine-Budde wrote:
> On 11/14/2016 06:55 PM, Akshay Bhat wrote:
>> This patch adds support for the Holt HI-311x CAN controller. The HI311x
>> CAN controller is capable of transmitting and receiving standard data
>> frames, extended data frames and remote frames. The HI311x interfaces
>> with the host over SPI.
>
> Don't use uint8_t and similar in the kernel, please use u8 instead.
>
Will fix in V2
>>
>> Datasheet: www.holtic.com/documents/371-hi-3110_v-rev-jpdf.do
>>
>> Signed-off-by: Akshay Bhat <nodeax@gmail.com>
>> ---
>> drivers/net/can/spi/Kconfig | 6 +
>> drivers/net/can/spi/Makefile | 1 +
>> drivers/net/can/spi/hi311x.c | 1071 ++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 1078 insertions(+)
>> create mode 100644 drivers/net/can/spi/hi311x.c
>>
>> diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig
>> index 148cae5..9eb1bb1 100644
>> --- a/drivers/net/can/spi/Kconfig
>> +++ b/drivers/net/can/spi/Kconfig
>> @@ -7,4 +7,10 @@ config CAN_MCP251X
>> ---help---
>> Driver for the Microchip MCP251x SPI CAN controllers.
>>
>> +config CAN_HI311X
>> + tristate "Holt HI311x SPI CAN controllers"
>> + depends on CAN_DEV && SPI && HAS_DMA
>> + ---help---
>> + Driver for the Holt HI311x SPI CAN controllers.
>> +
>> endmenu
>> diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile
>> index 0e86040..eac7c3a 100644
>> --- a/drivers/net/can/spi/Makefile
>> +++ b/drivers/net/can/spi/Makefile
>> @@ -4,3 +4,4 @@
>>
>>
>> obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
>> +obj-$(CONFIG_CAN_HI311X) += hi311x.o
>
> Please keep sorted alphabetically. Same for the Kconfig.
>
Will fix in V2
>> diff --git a/drivers/net/can/spi/hi311x.c b/drivers/net/can/spi/hi311x.c
>> new file mode 100644
>> index 0000000..1020166
>> --- /dev/null
>> +++ b/drivers/net/can/spi/hi311x.c
>> @@ -0,0 +1,1071 @@
>> +/* CAN bus driver for Holt HI3110 CAN Controller with SPI Interface
>> + *
>> + * Based on Microchip 251x CAN Controller (mcp251x) Linux kernel driver
>
> You might want to add the copyright of the mcp authors.
>
Will add in V2
>> + *
>> + * Copyright(C) Timesys Corporation 2016
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <linux/can/core.h>
>> +#include <linux/can/dev.h>
>> +#include <linux/can/led.h>
>> +#include <linux/clk.h>
>> +#include <linux/completion.h>
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/freezer.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/netdevice.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/slab.h>
>> +#include <linux/spi/spi.h>
>> +#include <linux/uaccess.h>
>> +
>
> Please use just a single space after each macro.
>
> VVVVVVVV
Will fix in V2.
>> +#define HI3110_MASTER_RESET 0x56
>> +#define HI3110_READ_CTRL0 0xD2
>> +#define HI3110_READ_CTRL1 0xD4
>> +#define HI3110_READ_STATF 0xE2
>> +#define HI3110_WRITE_CTRL0 0x14
>> +#define HI3110_WRITE_CTRL1 0x16
>> +#define HI3110_WRITE_INTE 0x1C
>> +#define HI3110_WRITE_BTR0 0x18
>> +#define HI3110_WRITE_BTR1 0x1A
>> +#define HI3110_READ_BTR0 0xD6
>> +#define HI3110_READ_BTR1 0xD8
>> +#define HI3110_READ_INTF 0xDE
>> +#define HI3110_READ_ERR 0xDC
>> +#define HI3110_READ_FIFO_WOTIME 0x48
>> +#define HI3110_WRITE_FIFO 0x12
>> +#define HI3110_READ_MESSTAT 0xDA
>> +#define HI3110_READ_TEC 0xEC
>> +
>> +#define HI3110_CTRL0_MODE_MASK (7 << 5)
>> +#define HI3110_CTRL0_NORMAL_MODE (0 << 5)
>> +#define HI3110_CTRL0_LOOPBACK_MODE (1 << 5)
>> +#define HI3110_CTRL0_MONITOR_MODE (2 << 5)
>> +#define HI3110_CTRL0_SLEEP_MODE (3 << 5)
>> +#define HI3110_CTRL0_INIT_MODE (4 << 5)
>> +
>> +#define HI3110_CTRL1_TXEN BIT(7)
>> +
>> +#define HI3110_INT_RXTMP BIT(7)
>> +#define HI3110_INT_RXFIFO BIT(6)
>> +#define HI3110_INT_TXCPLT BIT(5)
>> +#define HI3110_INT_BUSERR BIT(4)
>> +#define HI3110_INT_MCHG BIT(3)
>> +#define HI3110_INT_WAKEUP BIT(2)
>> +#define HI3110_INT_F1MESS BIT(1)
>> +#define HI3110_INT_F0MESS BIT(0)
>> +
>> +#define HI3110_ERR_BUSOFF BIT(7)
>> +#define HI3110_ERR_TXERRP BIT(6)
>> +#define HI3110_ERR_RXERRP BIT(5)
>> +#define HI3110_ERR_BITERR BIT(4)
>> +#define HI3110_ERR_FRMERR BIT(3)
>> +#define HI3110_ERR_CRCERR BIT(2)
>> +#define HI3110_ERR_ACKERR BIT(1)
>> +#define HI3110_ERR_STUFERR BIT(0)
>> +#define HI3110_ERR_PROTOCOL_MASK (0x1F)
>> +
>> +#define HI3110_STAT_RXFMTY BIT(1)
>> +
>> +#define HI3110_BTR0_SJW_SHIFT 6
>> +#define HI3110_BTR0_BRP_SHIFT 0
>> +
>> +#define HI3110_BTR1_SAMP_3PERBIT (1 << 7)
>> +#define HI3110_BTR1_SAMP_1PERBIT (0 << 7)
>> +#define HI3110_BTR1_TSEG2_SHIFT 4
>> +#define HI3110_BTR1_TSEG1_SHIFT 0
>> +
>> +#define HI3110_FIFO_WOTIME_TAG_OFF 0
>> +#define HI3110_FIFO_WOTIME_ID_OFF 1
>> +#define HI3110_FIFO_WOTIME_DLC_OFF 5
>> +#define HI3110_FIFO_WOTIME_DAT_OFF 6
>> +
>> +#define HI3110_FIFO_WOTIME_TAG_IDE BIT(7)
>> +#define HI3110_FIFO_WOTIME_ID_RTR BIT(0)
>> +
>> +#define HI3110_FIFO_TAG_OFF 0
>> +#define HI3110_FIFO_ID_OFF 1
>> +#define HI3110_FIFO_STD_DLC_OFF 3
>> +#define HI3110_FIFO_STD_DATA_OFF 4
>> +#define HI3110_FIFO_EXT_DLC_OFF 5
>> +#define HI3110_FIFO_EXT_DATA_OFF 6
>> +
>
> Please add the already used HI3110_ namespace to these defines, too.
>
Will fix in V2.
>> +#define CAN_FRAME_MAX_DATA_LEN 8
>> +#define RX_BUF_LEN 15
>> +#define TX_STD_BUF_LEN 12
>> +#define TX_EXT_BUF_LEN 14
>> +#define CAN_FRAME_MAX_BITS 128
>> +
>> +#define TX_ECHO_SKB_MAX 1
>> +
>> +#define HI3110_OST_DELAY_MS (10)
>> +
>> +#define DEVICE_NAME "hi3110"
>> +
>> +static int hi3110_enable_dma = 1; /* Enable SPI DMA. Default: 1 (On) */
>> +module_param(hi3110_enable_dma, int, 0444);
>> +MODULE_PARM_DESC(hi3110_enable_dma, "Enable SPI DMA. Default: 1 (On)");
>> +
>> +static const struct can_bittiming_const hi3110_bittiming_const = {
>> + .name = DEVICE_NAME,
>> + .tseg1_min = 2,
>> + .tseg1_max = 16,
>> + .tseg2_min = 2,
>> + .tseg2_max = 8,
>> + .sjw_max = 4,
>> + .brp_min = 1,
>> + .brp_max = 64,
>> + .brp_inc = 1,
>> +};
>> +
>> +enum hi3110_model {
>> + CAN_HI3110_HI3110 = 0x3110,
> ^^^^^^^
> single space here, too
Will fix in V2
>> +};
>> +
>> +struct hi3110_priv {
>> + struct can_priv can;
> ^^^^
> here too
Will fix in V2
>> + struct net_device *net;
>> + struct spi_device *spi;
>> + enum hi3110_model model;
>> +
>> + struct mutex hi3110_lock; /* SPI device lock */
>> +
>> + u8 *spi_tx_buf;
>> + u8 *spi_rx_buf;
>> + dma_addr_t spi_tx_dma;
>> + dma_addr_t spi_rx_dma;
>> +
>> + struct sk_buff *tx_skb;
>> + int tx_len;
>> +
>> + struct workqueue_struct *wq;
>> + struct work_struct tx_work;
>> + struct work_struct restart_work;
>> +
>> + int force_quit;
>> + int after_suspend;
>
> Please add the already used HI3110_ namespace to these defines, too.
>
Will fix in V2
>> +#define AFTER_SUSPEND_UP 1
>> +#define AFTER_SUSPEND_DOWN 2
>> +#define AFTER_SUSPEND_POWER 4
>> +#define AFTER_SUSPEND_RESTART 8
>
>> + int restart_tx;
>> + struct regulator *power;
>> + struct regulator *transceiver;
>> + struct clk *clk;
>> +};
>> +
>> +static void hi3110_clean(struct net_device *net)
>> +{
>> + struct hi3110_priv *priv = netdev_priv(net);
>> +
>> + if (priv->tx_skb || priv->tx_len)
>> + net->stats.tx_errors++;
>> + if (priv->tx_skb)
>> + dev_kfree_skb(priv->tx_skb);
>> + if (priv->tx_len)
>> + can_free_echo_skb(priv->net, 0);
>> + priv->tx_skb = NULL;
>> + priv->tx_len = 0;
>> +}
>> +
>> +/* Note about handling of error return of hi3110_spi_trans: accessing
>> + * registers via SPI is not really different conceptually than using
>> + * normal I/O assembler instructions, although it's much more
>> + * complicated from a practical POV. So it's not advisable to always
>> + * check the return value of this function. Imagine that every
>> + * read{b,l}, write{b,l} and friends would be bracketed in "if ( < 0)
>> + * error();", it would be a great mess (well there are some situation
>> + * when exception handling C++ like could be useful after all). So we
>> + * just check that transfers are OK at the beginning of our
>> + * conversation with the chip and to avoid doing really nasty things
>> + * (like injecting bogus packets in the network stack).
>> + */
>> +static int hi3110_spi_trans(struct spi_device *spi, int len)
>> +{
>> + struct hi3110_priv *priv = spi_get_drvdata(spi);
>> + struct spi_transfer t = {
>> + .tx_buf = priv->spi_tx_buf,
>> + .rx_buf = priv->spi_rx_buf,
>> + .len = len,
>> + .cs_change = 0,
>> + };
>> + struct spi_message m;
>> + int ret;
>> +
>> + spi_message_init(&m);
>> +
>> + if (hi3110_enable_dma) {
>> + t.tx_dma = priv->spi_tx_dma;
>> + t.rx_dma = priv->spi_rx_dma;
>> + m.is_dma_mapped = 1;
>> + }
>> +
>> + spi_message_add_tail(&t, &m);
>> +
>> + ret = spi_sync(spi, &m);
>> +
>> + if (ret)
>> + dev_err(&spi->dev, "spi transfer failed: ret = %d\n", ret);
>> + return ret;
>> +}
>> +
>> +static u8 hi3110_cmd(struct spi_device *spi, uint8_t command)
>> +{
>> + struct hi3110_priv *priv = spi_get_drvdata(spi);
>> +
>> + priv->spi_tx_buf[0] = command;
>> + dev_dbg(&spi->dev, "hi3110_cmd: %02X\n", command);
>> +
>> + return hi3110_spi_trans(spi, 1);
>> +}
>> +
>> +static u8 hi3110_read(struct spi_device *spi, uint8_t command)
>> +{
>> + struct hi3110_priv *priv = spi_get_drvdata(spi);
>> + u8 val = 0;
>> +
>> + priv->spi_tx_buf[0] = command;
>> + hi3110_spi_trans(spi, 2);
>> + val = priv->spi_rx_buf[1];
>> + dev_dbg(&spi->dev, "hi3110_read: %02X, %02X\n", command, val);
>> +
>> + return val;
>> +}
>> +
>> +static void hi3110_write(struct spi_device *spi, u8 reg, uint8_t val)
>> +{
>> + struct hi3110_priv *priv = spi_get_drvdata(spi);
>> +
>> + priv->spi_tx_buf[0] = reg;
>> + priv->spi_tx_buf[1] = val;
>> + dev_dbg(&spi->dev, "hi3110_write: %02X, %02X\n", reg, val);
>> +
>> + hi3110_spi_trans(spi, 2);
>> +}
>> +
>> +static void hi3110_hw_tx_frame(struct spi_device *spi, u8 *buf, int len)
>> +{
>> + struct hi3110_priv *priv = spi_get_drvdata(spi);
>> +
>> + priv->spi_tx_buf[0] = HI3110_WRITE_FIFO;
>> + memcpy(priv->spi_tx_buf + 1, buf, len);
>> + hi3110_spi_trans(spi, len + 1);
>> +}
>> +
>> +static void hi3110_hw_tx(struct spi_device *spi, struct can_frame *frame)
>> +{
>> + u8 buf[TX_EXT_BUF_LEN];
>> +
>> + buf[HI3110_FIFO_TAG_OFF] = 0;
>> +
>> + if (frame->can_id & CAN_EFF_FLAG) {
>> + /* Extended frame */
>> + buf[HI3110_FIFO_ID_OFF] = (frame->can_id & CAN_EFF_MASK) >> 21;
>> + buf[HI3110_FIFO_ID_OFF + 1] =
>> + ((((frame->can_id & CAN_EFF_MASK) >> 18) & 0x07) << 5) |
>
> Why do you first shift down then up?
>
Simplified the logic in V2, shift down then up not needed
>> + 0x18 | /* Recessive SRR and IDE */
>
> Can you add a define for the 0x18?
>
Will add a define in V2
>> + (((frame->can_id & CAN_EFF_MASK) >> 15) & 0x07);
>> + buf[HI3110_FIFO_ID_OFF + 2] =
>> + (frame->can_id & CAN_EFF_MASK) >> 7;
>> + buf[HI3110_FIFO_ID_OFF + 3] =
>> + ((frame->can_id & CAN_EFF_MASK) << 1) |
>> + ((frame->can_id & CAN_RTR_FLAG) ? 1 : 0);
>> +
>> + buf[HI3110_FIFO_EXT_DLC_OFF] = frame->can_dlc;
>> +
>> + memcpy(buf + HI3110_FIFO_EXT_DATA_OFF,
>> + frame->data, frame->can_dlc);
>> +
>> + hi3110_hw_tx_frame(spi, buf, TX_EXT_BUF_LEN -
>> + (CAN_FRAME_MAX_DATA_LEN - frame->can_dlc));
>> + } else {
>> + /* Standard frame */
>> + buf[HI3110_FIFO_ID_OFF] = (frame->can_id & CAN_SFF_MASK) >> 3;
>> + buf[HI3110_FIFO_ID_OFF + 1] =
>> + ((frame->can_id & CAN_SFF_MASK) << 5) |
>> + ((frame->can_id & CAN_RTR_FLAG) ? (1 << 4) : 0);
>> +
>> + buf[HI3110_FIFO_STD_DLC_OFF] = frame->can_dlc;
>> +
>> + memcpy(buf + HI3110_FIFO_STD_DATA_OFF,
>> + frame->data, frame->can_dlc);
>> +
>> + hi3110_hw_tx_frame(spi, buf, TX_STD_BUF_LEN -
>> + (CAN_FRAME_MAX_DATA_LEN - frame->can_dlc));
>> + }
>> +}
>> +
>> +static void hi3110_hw_rx_frame(struct spi_device *spi, u8 *buf)
>> +{
>> + struct hi3110_priv *priv = spi_get_drvdata(spi);
>> +
>> + priv->spi_tx_buf[0] = HI3110_READ_FIFO_WOTIME;
>> + hi3110_spi_trans(spi, RX_BUF_LEN);
>> + memcpy(buf, priv->spi_rx_buf + 1, RX_BUF_LEN - 1);
>> +}
>> +
>> +static void hi3110_hw_rx(struct spi_device *spi)
>> +{
>> + struct hi3110_priv *priv = spi_get_drvdata(spi);
>> + struct sk_buff *skb;
>> + struct can_frame *frame;
>> + u8 buf[RX_BUF_LEN - 1];
>> +
>> + skb = alloc_can_skb(priv->net, &frame);
>> + if (!skb) {
>> + dev_err(&spi->dev, "cannot allocate RX skb\n");
>> + priv->net->stats.rx_dropped++;
>> + return;
>> + }
>> +
>> + hi3110_hw_rx_frame(spi, buf);
>> + if (buf[HI3110_FIFO_WOTIME_TAG_OFF] & HI3110_FIFO_WOTIME_TAG_IDE) {
>> + /* IDE is recessive (1), indicating extended 29-bit frame */
>> + frame->can_id = CAN_EFF_FLAG;
>> + frame->can_id |=
>> + (buf[HI3110_FIFO_WOTIME_ID_OFF] << 21) |
>> + (((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0xE0) >> 5) << 18) |
>> + ((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0x07) << 15) |
>> + (buf[HI3110_FIFO_WOTIME_ID_OFF + 2] << 7) |
>> + (buf[HI3110_FIFO_WOTIME_ID_OFF + 3] >> 1);
>> + } else {
>> + /* IDE is dominant (0), frame indicating standard 11-bit */
>> + frame->can_id =
>> + (buf[HI3110_FIFO_WOTIME_ID_OFF] << 3) |
>> + ((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0xE0) >> 5);
>> + }
>> +
>> + if (buf[HI3110_FIFO_WOTIME_ID_OFF + 3] & HI3110_FIFO_WOTIME_ID_RTR) {
>> + /* RTR is recessive (1), indicating remote request frame */
>> + frame->can_id |= CAN_RTR_FLAG;
>> + }
>> +
>> + /* Data length */
>> + frame->can_dlc = get_can_dlc(buf[HI3110_FIFO_WOTIME_DLC_OFF] & 0x0F);
>> + memcpy(frame->data, buf + HI3110_FIFO_WOTIME_DAT_OFF, frame->can_dlc);
>> +
>> + priv->net->stats.rx_packets++;
>> + priv->net->stats.rx_bytes += frame->can_dlc;
>> +
>> + can_led_event(priv->net, CAN_LED_EVENT_RX);
>> +
>> + netif_rx_ni(skb);
>> +}
>> +
>> +static void hi3110_hw_sleep(struct spi_device *spi)
>> +{
>> + hi3110_write(spi, HI3110_WRITE_CTRL0, HI3110_CTRL0_SLEEP_MODE);
>> +}
>> +
>> +static netdev_tx_t hi3110_hard_start_xmit(struct sk_buff *skb,
>> + struct net_device *net)
>> +{
>> + struct hi3110_priv *priv = netdev_priv(net);
>> + struct spi_device *spi = priv->spi;
>> +
>> + if (priv->tx_skb || priv->tx_len) {
>> + dev_warn(&spi->dev, "hard_xmit called while tx busy\n");
>> + return NETDEV_TX_BUSY;
>> + }
>> +
>> + if (can_dropped_invalid_skb(net, skb))
>> + return NETDEV_TX_OK;
>> +
>> + netif_stop_queue(net);
>> + priv->tx_skb = skb;
>> + queue_work(priv->wq, &priv->tx_work);
>> +
>> + return NETDEV_TX_OK;
>> +}
>> +
>> +static int hi3110_do_set_mode(struct net_device *net, enum can_mode mode)
>> +{
>> + struct hi3110_priv *priv = netdev_priv(net);
>> +
>> + switch (mode) {
>> + case CAN_MODE_START:
>> + hi3110_clean(net);
>> + /* We have to delay work since SPI I/O may sleep */
>> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
>> + priv->restart_tx = 1;
>> + if (priv->can.restart_ms == 0)
>> + priv->after_suspend = AFTER_SUSPEND_RESTART;
>> + queue_work(priv->wq, &priv->restart_work);
>> + break;
>> + default:
>> + return -EOPNOTSUPP;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int hi3110_set_normal_mode(struct spi_device *spi)
>> +{
>> + struct hi3110_priv *priv = spi_get_drvdata(spi);
>> + u8 reg;
>> +
>> + hi3110_write(spi, HI3110_WRITE_INTE, HI3110_INT_BUSERR |
>> + HI3110_INT_RXFIFO | HI3110_INT_TXCPLT);
>> +
>> + /* Enable TX */
>> + hi3110_write(spi, HI3110_WRITE_CTRL1, HI3110_CTRL1_TXEN);
>> +
>> + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
>> + /* Put device into loopback mode */
>> + hi3110_write(spi, HI3110_WRITE_CTRL0,
>> + HI3110_CTRL0_LOOPBACK_MODE);
>> + } else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) {
>> + /* Put device into listen-only mode */
>> + hi3110_write(spi, HI3110_WRITE_CTRL0,
>> + HI3110_CTRL0_MONITOR_MODE);
>> + } else {
>> + /* Put device into normal mode */
>> + hi3110_write(spi, HI3110_WRITE_CTRL0,
>> + HI3110_CTRL0_NORMAL_MODE);
>> +
>> + /* Wait for the device to enter normal mode */
>> + mdelay(HI3110_OST_DELAY_MS);
>> + reg = hi3110_read(spi, HI3110_READ_CTRL0);
>> + if ((reg & HI3110_CTRL0_MODE_MASK) != HI3110_CTRL0_NORMAL_MODE)
>> + return -EBUSY;
>> + }
>> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
>> + return 0;
>> +}
>> +
>> +static int hi3110_do_set_bittiming(struct net_device *net)
>> +{
>> + struct hi3110_priv *priv = netdev_priv(net);
>> + struct can_bittiming *bt = &priv->can.bittiming;
>> + struct spi_device *spi = priv->spi;
>> +
>> + hi3110_write(spi, HI3110_WRITE_BTR0,
>> + ((bt->sjw - 1) << HI3110_BTR0_SJW_SHIFT) |
>> + ((bt->brp - 1) << HI3110_BTR0_BRP_SHIFT));
>> +
>> + hi3110_write(spi, HI3110_WRITE_BTR1,
>> + (priv->can.ctrlmode &
>> + CAN_CTRLMODE_3_SAMPLES ?
>> + HI3110_BTR1_SAMP_3PERBIT : HI3110_BTR1_SAMP_1PERBIT) |
>> + ((bt->phase_seg1 + bt->prop_seg - 1)
>> + << HI3110_BTR1_TSEG1_SHIFT) |
>> + ((bt->phase_seg2 - 1) << HI3110_BTR1_TSEG2_SHIFT));
>> +
>> + dev_dbg(&spi->dev, "BT: 0x%02x 0x%02x\n",
>> + hi3110_read(spi, HI3110_READ_BTR0),
>> + hi3110_read(spi, HI3110_READ_BTR1));
>> +
>> + return 0;
>> +}
>> +
>> +static int hi3110_setup(struct net_device *net, struct hi3110_priv *priv,
>> + struct spi_device *spi)
>
> onlt the first parameter is used.
>
Will remove the unused parameters in V2
>> +{
>> + hi3110_do_set_bittiming(net);
>> + return 0;
>> +}
>> +
>> +static int hi3110_hw_reset(struct spi_device *spi)
>> +{
>> + u8 reg;
>> + int ret;
>> +
>> + /* Wait for oscillator startup timer after power up */
>> + mdelay(HI3110_OST_DELAY_MS);
>> +
>> + ret = hi3110_cmd(spi, HI3110_MASTER_RESET);
>> + if (ret)
>> + return ret;
>> +
>> + /* Wait for oscillator startup timer after reset */
>> + mdelay(HI3110_OST_DELAY_MS);
>> +
>> + reg = hi3110_read(spi, HI3110_READ_CTRL0);
>> + if ((reg & HI3110_CTRL0_MODE_MASK) != HI3110_CTRL0_INIT_MODE)
>> + return -ENODEV;
>> +
>> + /* As per the datasheet it appears the error flags are
>> + * not cleared on reset. Explicitly clear them by performing a read
>> + */
>> + hi3110_read(spi, HI3110_READ_ERR);
>> +
>> + return 0;
>> +}
>> +
>> +static int hi3110_hw_probe(struct spi_device *spi)
>> +{
>> + u8 statf;
>> +
>> + hi3110_hw_reset(spi);
>> +
>> + /* Confirm correct operation by checking against reset values
>> + * in datasheet
>> + */
>> + statf = hi3110_read(spi, HI3110_READ_STATF);
>> +
>> + dev_dbg(&spi->dev, "statf: %02X\n", statf);
>> +
>> + if (statf != 0x82)
>> + return -ENODEV;
>> +
>> + return 0;
>> +}
>> +
>> +static int hi3110_power_enable(struct regulator *reg, int enable)
>> +{
>> + if (IS_ERR_OR_NULL(reg))
>> + return 0;
>> +
>> + if (enable)
>> + return regulator_enable(reg);
>> + else
>> + return regulator_disable(reg);
>> +}
>> +
>> +static void hi3110_open_clean(struct net_device *net)
>> +{
>> + struct hi3110_priv *priv = netdev_priv(net);
>> + struct spi_device *spi = priv->spi;
>> +
>> + free_irq(spi->irq, priv);
>> + hi3110_hw_sleep(spi);
>> + hi3110_power_enable(priv->transceiver, 0);
>> + close_candev(net);
>> +}
>> +
>> +static int hi3110_stop(struct net_device *net)
>> +{
>> + struct hi3110_priv *priv = netdev_priv(net);
>> + struct spi_device *spi = priv->spi;
>> +
>> + close_candev(net);
>> +
>> + priv->force_quit = 1;
>> + free_irq(spi->irq, priv);
>> + destroy_workqueue(priv->wq);
>> + priv->wq = NULL;
>> +
>> + mutex_lock(&priv->hi3110_lock);
>> +
>> + /* Disable transmit, interrupts and clear flags */
>> + hi3110_write(spi, HI3110_WRITE_CTRL1, 0x0);
>> + hi3110_write(spi, HI3110_WRITE_INTE, 0x0);
>> + hi3110_read(spi, HI3110_READ_INTF);
>> +
>> + hi3110_clean(net);
>> +
>> + hi3110_hw_sleep(spi);
>> +
>> + hi3110_power_enable(priv->transceiver, 0);
>> +
>> + priv->can.state = CAN_STATE_STOPPED;
>> +
>> + mutex_unlock(&priv->hi3110_lock);
>> +
>> + can_led_event(net, CAN_LED_EVENT_STOP);
>> +
>> + return 0;
>> +}
>> +
>> +static void hi3110_error_skb(struct net_device *net, int can_id,
>> + int data1, int data2)
>> +{
>> + struct sk_buff *skb;
>> + struct can_frame *frame;
>> +
>> + skb = alloc_can_err_skb(net, &frame);
>> + if (skb) {
>> + frame->can_id |= can_id;
>> + frame->data[1] = data1;
>> + frame->data[2] = data2;
>> + netif_rx_ni(skb);
>> + } else {
>> + netdev_err(net, "cannot allocate error skb\n");
>> + }
>> +}
>> +
>> +static void hi3110_tx_work_handler(struct work_struct *ws)
>> +{
>> + struct hi3110_priv *priv = container_of(ws, struct hi3110_priv,
>> + tx_work);
>> + struct spi_device *spi = priv->spi;
>> + struct net_device *net = priv->net;
>> + struct can_frame *frame;
>> +
>> + mutex_lock(&priv->hi3110_lock);
>> + if (priv->tx_skb) {
>> + if (priv->can.state == CAN_STATE_BUS_OFF) {
>> + hi3110_clean(net);
>> + } else {
>> + frame = (struct can_frame *)priv->tx_skb->data;
>> +
>> + if (frame->can_dlc > CAN_FRAME_MAX_DATA_LEN)
>> + frame->can_dlc = CAN_FRAME_MAX_DATA_LEN;
>
> this has already been checked
>
Will remove the check in V2
>> + hi3110_hw_tx(spi, frame);
>> + priv->tx_len = 1 + frame->can_dlc;
>> + can_put_echo_skb(priv->tx_skb, net, 0);
>> + priv->tx_skb = NULL;
>> + }
>> + }
>> + mutex_unlock(&priv->hi3110_lock);
>> +}
>> +
>> +static void hi3110_restart_work_handler(struct work_struct *ws)
>> +{
>> + struct hi3110_priv *priv = container_of(ws, struct hi3110_priv,
>> + restart_work);
>> + struct spi_device *spi = priv->spi;
>> + struct net_device *net = priv->net;
>> +
>> + mutex_lock(&priv->hi3110_lock);
>> + if (priv->after_suspend) {
>> + hi3110_hw_reset(spi);
>> + hi3110_setup(net, priv, spi);
>> + if (priv->after_suspend & AFTER_SUSPEND_RESTART) {
>> + hi3110_set_normal_mode(spi);
>> + } else if (priv->after_suspend & AFTER_SUSPEND_UP) {
>> + netif_device_attach(net);
>> + hi3110_clean(net);
>> + hi3110_set_normal_mode(spi);
>> + netif_wake_queue(net);
>> + } else {
>> + hi3110_hw_sleep(spi);
>> + }
>> + priv->after_suspend = 0;
>> + priv->force_quit = 0;
>> + }
>> +
>> + if (priv->restart_tx) {
>> + priv->restart_tx = 0;
>> + hi3110_clean(net);
>> + netif_wake_queue(net);
>> + hi3110_error_skb(net, CAN_ERR_RESTARTED, 0, 0);
>> + }
>> + mutex_unlock(&priv->hi3110_lock);
>> +}
>> +
>> +static irqreturn_t hi3110_can_ist(int irq, void *dev_id)
>> +{
>> + struct hi3110_priv *priv = dev_id;
>> + struct spi_device *spi = priv->spi;
>> + struct net_device *net = priv->net;
>> +
>> + mutex_lock(&priv->hi3110_lock);
>> +
>> + while (!priv->force_quit) {
>> + enum can_state new_state;
>> + u8 intf;
>> + u8 eflag;
>> + int can_id = 0, data1 = 0, data2 = 0;
>> +
>> + while (!(HI3110_STAT_RXFMTY &
>> + hi3110_read(spi, HI3110_READ_STATF))) {
>> + hi3110_hw_rx(spi);
>> + };
>> +
>> + intf = hi3110_read(spi, HI3110_READ_INTF);
>> + eflag = hi3110_read(spi, HI3110_READ_ERR);
>
> does the hardware supports multiple reads with a single transfer? If so
> make use of it, for performance reasons.
>
Looking at the datasheet it does not seem possible, would be nice if it
was supported.
>> + /* Update can state */
>> + if (eflag & HI3110_ERR_BUSOFF) {
>> + new_state = CAN_STATE_BUS_OFF;
>> + can_id |= CAN_ERR_BUSOFF;
>> + } else if (eflag & HI3110_ERR_TXERRP) {
>> + new_state = CAN_STATE_ERROR_PASSIVE;
>> + can_id |= CAN_ERR_CRTL;
>> + data1 |= CAN_ERR_CRTL_TX_PASSIVE;
>> + } else if (eflag & HI3110_ERR_RXERRP) {
>> + new_state = CAN_STATE_ERROR_PASSIVE;
>> + can_id |= CAN_ERR_CRTL;
>> + data1 |= CAN_ERR_CRTL_RX_PASSIVE;
>> + } else {
>> + new_state = CAN_STATE_ERROR_ACTIVE;
>> + }
>> +
>> + /* Check for protocol errors */
>> + if (eflag & HI3110_ERR_PROTOCOL_MASK) {
>> + can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
>> + priv->can.can_stats.bus_error++;
>> + priv->net->stats.rx_errors++;
>> + if (eflag & HI3110_ERR_BITERR)
>> + data2 |= CAN_ERR_PROT_BIT;
>> + else if (eflag & HI3110_ERR_FRMERR)
>> + data2 |= CAN_ERR_PROT_FORM;
>> + else if (eflag & HI3110_ERR_STUFERR)
>> + data2 |= CAN_ERR_PROT_STUFF;
>> + else
>> + data2 |= CAN_ERR_PROT_UNSPEC;
>> + }
>> +
>> + /* Update can state statistics */
>> + switch (priv->can.state) {
>> + case CAN_STATE_ERROR_ACTIVE:
>> + if (new_state >= CAN_STATE_ERROR_WARNING &&
>> + new_state <= CAN_STATE_BUS_OFF)
>> + priv->can.can_stats.error_warning++;
>> + /* fallthrough */
>> + case CAN_STATE_ERROR_WARNING:
>> + if (new_state >= CAN_STATE_ERROR_PASSIVE &&
>> + new_state <= CAN_STATE_BUS_OFF)
>> + priv->can.can_stats.error_passive++;
>> + break;
>> + default:
>> + break;
>> + }
>> + priv->can.state = new_state;
>> +
>> + if (intf & HI3110_INT_BUSERR) {
>> + /* Note: HI3110 Does report overflow errors */
>> + hi3110_error_skb(net, can_id, data1, data2);
>> + }
>> +
>> + if (priv->can.state == CAN_STATE_BUS_OFF) {
>> + if (priv->can.restart_ms == 0) {
>> + priv->force_quit = 1;
>> + priv->can.can_stats.bus_off++;
>> + can_bus_off(net);
>> + hi3110_hw_sleep(spi);
>> + break;
>> + }
>> + }
>> +
>> + if (intf == 0)
>> + break;
>> +
>> + if (intf & HI3110_INT_TXCPLT) {
>> + net->stats.tx_packets++;
>> + net->stats.tx_bytes += priv->tx_len - 1;
>> + can_led_event(net, CAN_LED_EVENT_TX);
>> + if (priv->tx_len) {
>> + can_get_echo_skb(net, 0);
>> + priv->tx_len = 0;
>> + }
>> + netif_wake_queue(net);
>> + }
>> + }
>> + mutex_unlock(&priv->hi3110_lock);
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static int hi3110_open(struct net_device *net)
>> +{
>> + struct hi3110_priv *priv = netdev_priv(net);
>> + struct spi_device *spi = priv->spi;
>> + unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_RISING;
>> + int ret;
>> +
>> + ret = open_candev(net);
>> + if (ret) {
>> + dev_err(&spi->dev, "unable to set initial baudrate!\n");
>> + return ret;
>> + }
>> +
>> + mutex_lock(&priv->hi3110_lock);
>> + hi3110_power_enable(priv->transceiver, 1);
>> +
>> + priv->force_quit = 0;
>> + priv->tx_skb = NULL;
>> + priv->tx_len = 0;
>> +
>> + ret = request_threaded_irq(spi->irq, NULL, hi3110_can_ist,
>> + flags, DEVICE_NAME, priv);
>> + if (ret) {
>> + dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq);
>
> add propoer goto targets at the and of this function, for easier error
> handling cleanup. This mean basically get rid of hi3110_open_clean(net).
>
Will fix in V2
>> + hi3110_power_enable(priv->transceiver, 0);
>> + close_candev(net);
>> + goto open_unlock;
>> + }
>> +
>> + priv->wq = alloc_workqueue("hi3110_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM,
>> + 0);
>> + INIT_WORK(&priv->tx_work, hi3110_tx_work_handler);
>> + INIT_WORK(&priv->restart_work, hi3110_restart_work_handler);
>> +
>> + ret = hi3110_hw_reset(spi);
>> + if (ret) {
>> + hi3110_open_clean(net);
>> + goto open_unlock;
>> + }
>> + ret = hi3110_setup(net, priv, spi);
>> + if (ret) {
>> + hi3110_open_clean(net);
>> + goto open_unlock;
>> + }
>> + ret = hi3110_set_normal_mode(spi);
>> + if (ret) {
>> + hi3110_open_clean(net);
>> + goto open_unlock;
>> + }
>> + can_led_event(net, CAN_LED_EVENT_OPEN);
>> + netif_wake_queue(net);
>> +
>> +open_unlock:
>> + mutex_unlock(&priv->hi3110_lock);
>> + return ret;
>> +}
>> +
>> +static const struct net_device_ops hi3110_netdev_ops = {
>> + .ndo_open = hi3110_open,
>> + .ndo_stop = hi3110_stop,
>> + .ndo_start_xmit = hi3110_hard_start_xmit,
>> +};
>> +
>> +static const struct of_device_id hi3110_of_match[] = {
>> + {
>> + .compatible = "holt,hi3110",
>> + .data = (void *)CAN_HI3110_HI3110,
>> + },
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(of, hi3110_of_match);
>> +
>> +static const struct spi_device_id hi3110_id_table[] = {
>> + {
>> + .name = "hi3110",
>> + .driver_data = (kernel_ulong_t)CAN_HI3110_HI3110,
>> + },
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(spi, hi3110_id_table);
>> +
>> +static int hi3110_can_probe(struct spi_device *spi)
>> +{
>> + const struct of_device_id *of_id = of_match_device(hi3110_of_match,
>> + &spi->dev);
>> + struct net_device *net;
>> + struct hi3110_priv *priv;
>> + struct clk *clk;
>> + int freq, ret;
>> +
>> + clk = devm_clk_get(&spi->dev, NULL);
>> + if (IS_ERR(clk)) {
>> + dev_err(&spi->dev, "no CAN clock source defined\n");
>> + return PTR_ERR(clk);
>> + }
>> + freq = clk_get_rate(clk);
>> +
>> + /* Sanity check */
>> + if (freq > 40000000)
>> + return -ERANGE;
>> +
>> + /* Allocate can/net device */
>> + net = alloc_candev(sizeof(struct hi3110_priv), TX_ECHO_SKB_MAX);
>> + if (!net)
>> + return -ENOMEM;
>> +
>> + if (!IS_ERR(clk)) {
>> + ret = clk_prepare_enable(clk);
>> + if (ret)
>> + goto out_free;
>> + }
>> +
>> + net->netdev_ops = &hi3110_netdev_ops;
>> + net->flags |= IFF_ECHO;
>> +
>> + priv = netdev_priv(net);
>> + priv->can.bittiming_const = &hi3110_bittiming_const;
>> + priv->can.do_set_mode = hi3110_do_set_mode;
>> + priv->can.clock.freq = freq / 2;
>> + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
>> + CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY;
>> + if (of_id)
>> + priv->model = (enum hi3110_model)of_id->data;
>> + else
>> + priv->model = spi_get_device_id(spi)->driver_data;
>> + priv->net = net;
>> + priv->clk = clk;
>> +
>> + spi_set_drvdata(spi, priv);
>> +
>> + /* Configure the SPI bus */
>> + spi->bits_per_word = 8;
>> + ret = spi_setup(spi);
>> + if (ret)
>> + goto out_clk;
>> +
>> + priv->power = devm_regulator_get_optional(&spi->dev, "vdd");
>> + priv->transceiver = devm_regulator_get_optional(&spi->dev, "xceiver");
>> + if ((PTR_ERR(priv->power) == -EPROBE_DEFER) ||
>> + (PTR_ERR(priv->transceiver) == -EPROBE_DEFER)) {
>> + ret = -EPROBE_DEFER;
>> + goto out_clk;
>> + }
>> +
>> + ret = hi3110_power_enable(priv->power, 1);
>> + if (ret)
>> + goto out_clk;
>> +
>> + priv->spi = spi;
>> + mutex_init(&priv->hi3110_lock);
>> +
>> + /* If requested, allocate DMA buffers */
>> + if (hi3110_enable_dma) {
>> + spi->dev.coherent_dma_mask = ~0;
>> +
>> + /* Minimum coherent DMA allocation is PAGE_SIZE, so allocate
>> + * that much and share it between Tx and Rx DMA buffers.
>> + */
>> + priv->spi_tx_buf = dmam_alloc_coherent(&spi->dev,
>> + PAGE_SIZE,
>> + &priv->spi_tx_dma,
>> + GFP_DMA);
>> +
>> + if (priv->spi_tx_buf) {
>> + priv->spi_rx_buf = (priv->spi_tx_buf + (PAGE_SIZE / 2));
>> + priv->spi_rx_dma = (dma_addr_t)(priv->spi_tx_dma +
>> + (PAGE_SIZE / 2));
>> + } else {
>> + /* Fall back to non-DMA */
>> + hi3110_enable_dma = 0;
>> + }
>> + }
>> +
>> + /* Allocate non-DMA buffers */
>> + if (!hi3110_enable_dma) {
>> + priv->spi_tx_buf = devm_kzalloc(&spi->dev, RX_BUF_LEN,
>> + GFP_KERNEL);
>> + if (!priv->spi_tx_buf) {
>> + ret = -ENOMEM;
>> + goto error_probe;
>> + }
>> + priv->spi_rx_buf = devm_kzalloc(&spi->dev, RX_BUF_LEN,
>> + GFP_KERNEL);
>> +
>> + if (!priv->spi_rx_buf) {
>> + ret = -ENOMEM;
>> + goto error_probe;
>> + }
>> + }
>> +
>> + SET_NETDEV_DEV(net, &spi->dev);
>> +
>> + ret = hi3110_hw_probe(spi);
>> + if (ret) {
>> + if (ret == -ENODEV)
>> + dev_err(&spi->dev, "Cannot initialize %x. Wrong wiring?\n",
>> + priv->model);
>> + goto error_probe;
>> + }
>> + hi3110_hw_sleep(spi);
>> +
>> + ret = register_candev(net);
>> + if (ret)
>> + goto error_probe;
>> +
>> + devm_can_led_init(net);
>> + netdev_info(net, "%x successfully initialized.\n", priv->model);
>> +
>> + return 0;
>> +
>> +error_probe:
>> + hi3110_power_enable(priv->power, 0);
>> +
>> +out_clk:
>> + if (!IS_ERR(clk))
>> + clk_disable_unprepare(clk);
>> +
>> +out_free:
>> + free_candev(net);
>> +
>> + dev_err(&spi->dev, "Probe failed, err=%d\n", -ret);
>> + return ret;
>> +}
>> +
>> +static int hi3110_can_remove(struct spi_device *spi)
>> +{
>> + struct hi3110_priv *priv = spi_get_drvdata(spi);
>> + struct net_device *net = priv->net;
>> +
>> + unregister_candev(net);
>> +
>> + hi3110_power_enable(priv->power, 0);
>> +
>> + if (!IS_ERR(priv->clk))
>> + clk_disable_unprepare(priv->clk);
>> +
>> + free_candev(net);
>> +
>> + return 0;
>> +}
>> +
>> +static int __maybe_unused hi3110_can_suspend(struct device *dev)
>> +{
>> + struct spi_device *spi = to_spi_device(dev);
>> + struct hi3110_priv *priv = spi_get_drvdata(spi);
>> + struct net_device *net = priv->net;
>> +
>> + priv->force_quit = 1;
>> + disable_irq(spi->irq);
>> +
>> + /* Note: at this point neither IST nor workqueues are running.
>> + * open/stop cannot be called anyway so locking is not needed
>> + */
>> + if (netif_running(net)) {
>> + netif_device_detach(net);
>> +
>> + hi3110_hw_sleep(spi);
>> + hi3110_power_enable(priv->transceiver, 0);
>> + priv->after_suspend = AFTER_SUSPEND_UP;
>> + } else {
>> + priv->after_suspend = AFTER_SUSPEND_DOWN;
>> + }
>> +
>> + if (!IS_ERR_OR_NULL(priv->power)) {
>> + regulator_disable(priv->power);
>> + priv->after_suspend |= AFTER_SUSPEND_POWER;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int __maybe_unused hi3110_can_resume(struct device *dev)
>> +{
>> + struct spi_device *spi = to_spi_device(dev);
>> + struct hi3110_priv *priv = spi_get_drvdata(spi);
>> +
>> + if (priv->after_suspend & AFTER_SUSPEND_POWER)
>> + hi3110_power_enable(priv->power, 1);
>> +
>> + if (priv->after_suspend & AFTER_SUSPEND_UP) {
>> + hi3110_power_enable(priv->transceiver, 1);
>> + queue_work(priv->wq, &priv->restart_work);
>> + } else {
>> + priv->after_suspend = 0;
>> + }
>> +
>> + priv->force_quit = 0;
>> + enable_irq(spi->irq);
>> + return 0;
>> +}
>> +
>> +static SIMPLE_DEV_PM_OPS(hi3110_can_pm_ops, hi3110_can_suspend,
>> + hi3110_can_resume);
>> +
>> +static struct spi_driver hi3110_can_driver = {
>> + .driver = {
>> + .name = DEVICE_NAME,
>> + .of_match_table = hi3110_of_match,
>> + .pm = &hi3110_can_pm_ops,
>> + },
>> + .id_table = hi3110_id_table,
>> + .probe = hi3110_can_probe,
>> + .remove = hi3110_can_remove,
>> +};
>> +
>> +module_spi_driver(hi3110_can_driver);
>> +
>> +MODULE_AUTHOR("Akshay Bhat <akshay.bhat@timesys.com>");
>> +MODULE_AUTHOR("Casey Fitzpatrick <casey.fitzpatrick@timesys.com>");
>> +MODULE_DESCRIPTION("Holt HI-3110 CAN driver");
>> +MODULE_LICENSE("GPL v2");
>>
>
> Marc
>
Thanks for all the feedback.
Akshay
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [net, 6/6] net: korina: version bump
From: Roman Yeryomin @ 2017-01-17 19:19 UTC (permalink / raw)
To: Felix Fietkau; +Cc: netdev
In-Reply-To: <e607e3a1-8227-7768-2234-6a3340f4a368@nbd.name>
On 17 January 2017 at 20:55, Felix Fietkau <nbd@nbd.name> wrote:
> On 2017-01-17 18:33, Roman Yeryomin wrote:
>> Signed-off-by: Roman Yeryomin <roman@advem.lv>
>> ---
>> drivers/net/ethernet/korina.c | 4 ++--
>> 1 file changed, 2 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/korina.c b/drivers/net/ethernet/korina.c
>> index 83c994f..c8fed01 100644
>> --- a/drivers/net/ethernet/korina.c
>> +++ b/drivers/net/ethernet/korina.c
>> @@ -66,8 +66,8 @@
>> #include <asm/mach-rc32434/dma_v.h>
>>
>> #define DRV_NAME "korina"
>> -#define DRV_VERSION "0.10"
>> -#define DRV_RELDATE "04Mar2008"
>> +#define DRV_VERSION "0.20"
>> +#define DRV_RELDATE "15Jan2017"
> I think it would make more sense to remove this version instead of
> bumping it. Individual driver versions are rather pointless, the kernel
> version is more meaningful anyway.
OK, makes sense
Regards,
Roman
^ permalink raw reply
* Re: [PATCH net-next] tcp: accept RST for rcv_nxt - 1 after receiving a FIN
From: Rick Jones @ 2017-01-17 19:19 UTC (permalink / raw)
To: Eric Dumazet; +Cc: Jason Baron, David Miller, netdev
In-Reply-To: <CANn89iJ-tc=m122UcEh9Xmuf1DoeKMwDf4Z42hWFZwxfRYFY_g@mail.gmail.com>
On 01/17/2017 11:13 AM, Eric Dumazet wrote:
> On Tue, Jan 17, 2017 at 11:04 AM, Rick Jones <rick.jones2@hpe.com> wrote:
>> Drifting a bit, and it doesn't change the value of dealing with it, but out
>> of curiosity, when you say mostly in CLOSE_WAIT, why aren't the server-side
>> applications reacting to the read return of zero triggered by the arrival of
>> the FIN?
>
> Even if the application reacts, and calls close(fd), kernel will still
> try to push the data that was queued into socket write queue prior to
> receiving the FIN.
>
> By allowing this RST, we can flush the whole data and react much
> faster, avoiding locking memory in the kernel for very long time.
Understood. I was just wondering if there is also an application bug here.
happy benchmarking,
rick jones
^ permalink raw reply
* Re: [net, 3/6] net: korina: increase tx/rx ring sizes
From: Eric Dumazet @ 2017-01-17 19:20 UTC (permalink / raw)
To: Roman Yeryomin; +Cc: David Miller, netdev
In-Reply-To: <CACiydbLuVf_jsWor6-OJmwQhxfwd=dzBDnDQnJrp3owf8qCkQA@mail.gmail.com>
On Tue, 2017-01-17 at 20:27 +0200, Roman Yeryomin wrote:
> On 17 January 2017 at 19:58, David Miller <davem@davemloft.net> wrote:
> > From: Roman Yeryomin <leroi.lists@gmail.com>
> > Date: Tue, 17 Jan 2017 19:32:36 +0200
> >
> >> Having larger ring sizes almost eliminates rx fifo overflow, thus improving performance.
> >> This patch reduces rx overflow occurence by approximately 1000 times (from ~25k down to ~25 times per 3M frames)
> >
> > Those numbers don't mean much without full context.
> >
> > What kind of system, what kind of traffic, and over what kind of link?
>
> MIPS rb532 board, TCP iperf3 test over 100M link, NATed speed ~55Mbps.
> I can do more tests and provide more precise numbers, if needed.
Note that at 100M, 64 rx descriptors have a 8 ms max latency.
Switching to 256 also multiply by 4 the latency -> 32 ms latency.
Presumably switching to NAPI and GRO would avoid the latency increase
and save a lot of cpu cycles for a MIPS board.
^ permalink raw reply
* [PATCH v2 1/2] can: holt_hi311x: document device tree bindings
From: Akshay Bhat @ 2017-01-17 19:22 UTC (permalink / raw)
To: wg, mkl
Cc: linux-can, netdev, devicetree, linux-kernel, Akshay Bhat,
Akshay Bhat
Document the HOLT HI-311x CAN device tree bindings.
Signed-off-by: Akshay Bhat <nodeax@gmail.com>
Acked-by: Rob Herring <robh@kernel.org>
---
v1 -> v2:
- No changes
.../devicetree/bindings/net/can/holt_hi311x.txt | 24 ++++++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/can/holt_hi311x.txt
diff --git a/Documentation/devicetree/bindings/net/can/holt_hi311x.txt b/Documentation/devicetree/bindings/net/can/holt_hi311x.txt
new file mode 100644
index 0000000..23aa94e
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/can/holt_hi311x.txt
@@ -0,0 +1,24 @@
+* Holt HI-311X stand-alone CAN controller device tree bindings
+
+Required properties:
+ - compatible: Should be one of the following:
+ - "holt,hi3110" for HI-3110
+ - reg: SPI chip select.
+ - clocks: The clock feeding the CAN controller.
+ - interrupt-parent: The parent interrupt controller.
+ - interrupts: Should contain IRQ line for the CAN controller.
+
+Optional properties:
+ - vdd-supply: Regulator that powers the CAN controller.
+ - xceiver-supply: Regulator that powers the CAN transceiver.
+
+Example:
+ can0: can@1 {
+ compatible = "holt,hi3110";
+ reg = <1>;
+ clocks = <&clk32m>;
+ interrupt-parent = <&gpio4>;
+ interrupts = <13 IRQ_TYPE_EDGE_RISING>;
+ vdd-supply = <®5v0>;
+ xceiver-supply = <®5v0>;
+ };
--
2.8.1
^ permalink raw reply related
* [PATCH v2 2/2] can: spi: hi311x: Add Holt HI-311x CAN driver
From: Akshay Bhat @ 2017-01-17 19:22 UTC (permalink / raw)
To: wg, mkl
Cc: linux-can, netdev, devicetree, linux-kernel, Akshay Bhat,
Akshay Bhat
In-Reply-To: <1484680922-25813-1-git-send-email-akshay.bhat@timesys.com>
This patch adds support for the Holt HI-311x CAN controller. The HI311x
CAN controller is capable of transmitting and receiving standard data
frames, extended data frames and remote frames. The HI311x interfaces
with the host over SPI.
Datasheet: www.holtic.com/documents/371-hi-3110_v-rev-jpdf.do
Signed-off-by: Akshay Bhat <nodeax@gmail.com>
---
v1 -> v2:
Address comments from Marc Kleine-Budde:
- use u8 instead of uint8_t
- alphabetically sort Makefile and Kconfig
- copy over copyright information from mcp251x
- use single space after each marco
- add missing HI3110_ namespace in defines
- remove magic number for IDE & SRR bits
- simplify logic to populate extended CAN ID
- remove unused parameters in hi3110_setup function
- remove redundant frame->can_dlc length check
- simplify error handling in hi3110_open function
Address comments from Julia Lawall:
- remove unnecessary semicolon after while loop in hi3110_can_ist
drivers/net/can/spi/Kconfig | 6 +
drivers/net/can/spi/Makefile | 1 +
drivers/net/can/spi/hi311x.c | 1069 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1076 insertions(+)
create mode 100644 drivers/net/can/spi/hi311x.c
diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig
index 148cae5..8f2e0dd 100644
--- a/drivers/net/can/spi/Kconfig
+++ b/drivers/net/can/spi/Kconfig
@@ -1,6 +1,12 @@
menu "CAN SPI interfaces"
depends on SPI
+config CAN_HI311X
+ tristate "Holt HI311x SPI CAN controllers"
+ depends on CAN_DEV && SPI && HAS_DMA
+ ---help---
+ Driver for the Holt HI311x SPI CAN controllers.
+
config CAN_MCP251X
tristate "Microchip MCP251x SPI CAN controllers"
depends on HAS_DMA
diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile
index 0e86040..f59fa37 100644
--- a/drivers/net/can/spi/Makefile
+++ b/drivers/net/can/spi/Makefile
@@ -3,4 +3,5 @@
#
+obj-$(CONFIG_CAN_HI311X) += hi311x.o
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
diff --git a/drivers/net/can/spi/hi311x.c b/drivers/net/can/spi/hi311x.c
new file mode 100644
index 0000000..cccfe2d
--- /dev/null
+++ b/drivers/net/can/spi/hi311x.c
@@ -0,0 +1,1069 @@
+/* CAN bus driver for Holt HI3110 CAN Controller with SPI Interface
+ *
+ * Copyright(C) Timesys Corporation 2016
+ *
+ * Based on Microchip 251x CAN Controller (mcp251x) Linux kernel driver
+ * Copyright 2009 Christian Pellegrin EVOL S.r.l.
+ * Copyright 2007 Raymarine UK, Ltd. All Rights Reserved.
+ * Copyright 2006 Arcom Control Systems Ltd.
+ *
+ * Based on CAN bus driver for the CCAN controller written by
+ * - Sascha Hauer, Marc Kleine-Budde, Pengutronix
+ * - Simon Kallweit, intefo AG
+ * Copyright 2007
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/can/led.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/freezer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/uaccess.h>
+
+#define HI3110_MASTER_RESET 0x56
+#define HI3110_READ_CTRL0 0xD2
+#define HI3110_READ_CTRL1 0xD4
+#define HI3110_READ_STATF 0xE2
+#define HI3110_WRITE_CTRL0 0x14
+#define HI3110_WRITE_CTRL1 0x16
+#define HI3110_WRITE_INTE 0x1C
+#define HI3110_WRITE_BTR0 0x18
+#define HI3110_WRITE_BTR1 0x1A
+#define HI3110_READ_BTR0 0xD6
+#define HI3110_READ_BTR1 0xD8
+#define HI3110_READ_INTF 0xDE
+#define HI3110_READ_ERR 0xDC
+#define HI3110_READ_FIFO_WOTIME 0x48
+#define HI3110_WRITE_FIFO 0x12
+#define HI3110_READ_MESSTAT 0xDA
+#define HI3110_READ_TEC 0xEC
+
+#define HI3110_CTRL0_MODE_MASK (7 << 5)
+#define HI3110_CTRL0_NORMAL_MODE (0 << 5)
+#define HI3110_CTRL0_LOOPBACK_MODE (1 << 5)
+#define HI3110_CTRL0_MONITOR_MODE (2 << 5)
+#define HI3110_CTRL0_SLEEP_MODE (3 << 5)
+#define HI3110_CTRL0_INIT_MODE (4 << 5)
+
+#define HI3110_CTRL1_TXEN BIT(7)
+
+#define HI3110_INT_RXTMP BIT(7)
+#define HI3110_INT_RXFIFO BIT(6)
+#define HI3110_INT_TXCPLT BIT(5)
+#define HI3110_INT_BUSERR BIT(4)
+#define HI3110_INT_MCHG BIT(3)
+#define HI3110_INT_WAKEUP BIT(2)
+#define HI3110_INT_F1MESS BIT(1)
+#define HI3110_INT_F0MESS BIT(0)
+
+#define HI3110_ERR_BUSOFF BIT(7)
+#define HI3110_ERR_TXERRP BIT(6)
+#define HI3110_ERR_RXERRP BIT(5)
+#define HI3110_ERR_BITERR BIT(4)
+#define HI3110_ERR_FRMERR BIT(3)
+#define HI3110_ERR_CRCERR BIT(2)
+#define HI3110_ERR_ACKERR BIT(1)
+#define HI3110_ERR_STUFERR BIT(0)
+#define HI3110_ERR_PROTOCOL_MASK (0x1F)
+
+#define HI3110_STAT_RXFMTY BIT(1)
+
+#define HI3110_BTR0_SJW_SHIFT 6
+#define HI3110_BTR0_BRP_SHIFT 0
+
+#define HI3110_BTR1_SAMP_3PERBIT (1 << 7)
+#define HI3110_BTR1_SAMP_1PERBIT (0 << 7)
+#define HI3110_BTR1_TSEG2_SHIFT 4
+#define HI3110_BTR1_TSEG1_SHIFT 0
+
+#define HI3110_FIFO_WOTIME_TAG_OFF 0
+#define HI3110_FIFO_WOTIME_ID_OFF 1
+#define HI3110_FIFO_WOTIME_DLC_OFF 5
+#define HI3110_FIFO_WOTIME_DAT_OFF 6
+
+#define HI3110_FIFO_WOTIME_TAG_IDE BIT(7)
+#define HI3110_FIFO_WOTIME_ID_RTR BIT(0)
+
+#define HI3110_FIFO_TAG_OFF 0
+#define HI3110_FIFO_ID_OFF 1
+#define HI3110_FIFO_STD_DLC_OFF 3
+#define HI3110_FIFO_STD_DATA_OFF 4
+#define HI3110_FIFO_EXT_DLC_OFF 5
+#define HI3110_FIFO_EXT_DATA_OFF 6
+
+#define HI3110_CAN_FRAME_MAX_DATA_LEN 8
+#define HI3110_RX_BUF_LEN 15
+#define HI3110_TX_STD_BUF_LEN 12
+#define HI3110_TX_EXT_BUF_LEN 14
+#define HI3110_CAN_FRAME_MAX_BITS 128
+#define HI3110_EFF_FLAGS 0x18 /* IDE + SRR */
+
+#define HI3110_TX_ECHO_SKB_MAX 1
+
+#define HI3110_OST_DELAY_MS (10)
+
+#define DEVICE_NAME "hi3110"
+
+static int hi3110_enable_dma = 1; /* Enable SPI DMA. Default: 1 (On) */
+module_param(hi3110_enable_dma, int, 0444);
+MODULE_PARM_DESC(hi3110_enable_dma, "Enable SPI DMA. Default: 1 (On)");
+
+static const struct can_bittiming_const hi3110_bittiming_const = {
+ .name = DEVICE_NAME,
+ .tseg1_min = 2,
+ .tseg1_max = 16,
+ .tseg2_min = 2,
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 64,
+ .brp_inc = 1,
+};
+
+enum hi3110_model {
+ CAN_HI3110_HI3110 = 0x3110,
+};
+
+struct hi3110_priv {
+ struct can_priv can;
+ struct net_device *net;
+ struct spi_device *spi;
+ enum hi3110_model model;
+
+ struct mutex hi3110_lock; /* SPI device lock */
+
+ u8 *spi_tx_buf;
+ u8 *spi_rx_buf;
+ dma_addr_t spi_tx_dma;
+ dma_addr_t spi_rx_dma;
+
+ struct sk_buff *tx_skb;
+ int tx_len;
+
+ struct workqueue_struct *wq;
+ struct work_struct tx_work;
+ struct work_struct restart_work;
+
+ int force_quit;
+ int after_suspend;
+#define HI3110_AFTER_SUSPEND_UP 1
+#define HI3110_AFTER_SUSPEND_DOWN 2
+#define HI3110_AFTER_SUSPEND_POWER 4
+#define HI3110_AFTER_SUSPEND_RESTART 8
+ int restart_tx;
+ struct regulator *power;
+ struct regulator *transceiver;
+ struct clk *clk;
+};
+
+static void hi3110_clean(struct net_device *net)
+{
+ struct hi3110_priv *priv = netdev_priv(net);
+
+ if (priv->tx_skb || priv->tx_len)
+ net->stats.tx_errors++;
+ if (priv->tx_skb)
+ dev_kfree_skb(priv->tx_skb);
+ if (priv->tx_len)
+ can_free_echo_skb(priv->net, 0);
+ priv->tx_skb = NULL;
+ priv->tx_len = 0;
+}
+
+/* Note about handling of error return of hi3110_spi_trans: accessing
+ * registers via SPI is not really different conceptually than using
+ * normal I/O assembler instructions, although it's much more
+ * complicated from a practical POV. So it's not advisable to always
+ * check the return value of this function. Imagine that every
+ * read{b,l}, write{b,l} and friends would be bracketed in "if ( < 0)
+ * error();", it would be a great mess (well there are some situation
+ * when exception handling C++ like could be useful after all). So we
+ * just check that transfers are OK at the beginning of our
+ * conversation with the chip and to avoid doing really nasty things
+ * (like injecting bogus packets in the network stack).
+ */
+static int hi3110_spi_trans(struct spi_device *spi, int len)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+ struct spi_transfer t = {
+ .tx_buf = priv->spi_tx_buf,
+ .rx_buf = priv->spi_rx_buf,
+ .len = len,
+ .cs_change = 0,
+ };
+ struct spi_message m;
+ int ret;
+
+ spi_message_init(&m);
+
+ if (hi3110_enable_dma) {
+ t.tx_dma = priv->spi_tx_dma;
+ t.rx_dma = priv->spi_rx_dma;
+ m.is_dma_mapped = 1;
+ }
+
+ spi_message_add_tail(&t, &m);
+
+ ret = spi_sync(spi, &m);
+
+ if (ret)
+ dev_err(&spi->dev, "spi transfer failed: ret = %d\n", ret);
+ return ret;
+}
+
+static u8 hi3110_cmd(struct spi_device *spi, u8 command)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+
+ priv->spi_tx_buf[0] = command;
+ dev_dbg(&spi->dev, "hi3110_cmd: %02X\n", command);
+
+ return hi3110_spi_trans(spi, 1);
+}
+
+static u8 hi3110_read(struct spi_device *spi, u8 command)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+ u8 val = 0;
+
+ priv->spi_tx_buf[0] = command;
+ hi3110_spi_trans(spi, 2);
+ val = priv->spi_rx_buf[1];
+ dev_dbg(&spi->dev, "hi3110_read: %02X, %02X\n", command, val);
+
+ return val;
+}
+
+static void hi3110_write(struct spi_device *spi, u8 reg, u8 val)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+
+ priv->spi_tx_buf[0] = reg;
+ priv->spi_tx_buf[1] = val;
+ dev_dbg(&spi->dev, "hi3110_write: %02X, %02X\n", reg, val);
+
+ hi3110_spi_trans(spi, 2);
+}
+
+static void hi3110_hw_tx_frame(struct spi_device *spi, u8 *buf, int len)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+
+ priv->spi_tx_buf[0] = HI3110_WRITE_FIFO;
+ memcpy(priv->spi_tx_buf + 1, buf, len);
+ hi3110_spi_trans(spi, len + 1);
+}
+
+static void hi3110_hw_tx(struct spi_device *spi, struct can_frame *frame)
+{
+ u8 buf[HI3110_TX_EXT_BUF_LEN];
+
+ buf[HI3110_FIFO_TAG_OFF] = 0;
+
+ if (frame->can_id & CAN_EFF_FLAG) {
+ /* Extended frame */
+ buf[HI3110_FIFO_ID_OFF] = (frame->can_id & CAN_EFF_MASK) >> 21;
+ buf[HI3110_FIFO_ID_OFF + 1] =
+ (((frame->can_id & CAN_EFF_MASK) >> 13) & 0xe0) |
+ HI3110_EFF_FLAGS |
+ (((frame->can_id & CAN_EFF_MASK) >> 15) & 0x07);
+ buf[HI3110_FIFO_ID_OFF + 2] =
+ (frame->can_id & CAN_EFF_MASK) >> 7;
+ buf[HI3110_FIFO_ID_OFF + 3] =
+ ((frame->can_id & CAN_EFF_MASK) << 1) |
+ ((frame->can_id & CAN_RTR_FLAG) ? 1 : 0);
+
+ buf[HI3110_FIFO_EXT_DLC_OFF] = frame->can_dlc;
+
+ memcpy(buf + HI3110_FIFO_EXT_DATA_OFF,
+ frame->data, frame->can_dlc);
+
+ hi3110_hw_tx_frame(spi, buf, HI3110_TX_EXT_BUF_LEN -
+ (HI3110_CAN_FRAME_MAX_DATA_LEN - frame->can_dlc));
+ } else {
+ /* Standard frame */
+ buf[HI3110_FIFO_ID_OFF] = (frame->can_id & CAN_SFF_MASK) >> 3;
+ buf[HI3110_FIFO_ID_OFF + 1] =
+ ((frame->can_id & CAN_SFF_MASK) << 5) |
+ ((frame->can_id & CAN_RTR_FLAG) ? (1 << 4) : 0);
+
+ buf[HI3110_FIFO_STD_DLC_OFF] = frame->can_dlc;
+
+ memcpy(buf + HI3110_FIFO_STD_DATA_OFF,
+ frame->data, frame->can_dlc);
+
+ hi3110_hw_tx_frame(spi, buf, HI3110_TX_STD_BUF_LEN -
+ (HI3110_CAN_FRAME_MAX_DATA_LEN - frame->can_dlc));
+ }
+}
+
+static void hi3110_hw_rx_frame(struct spi_device *spi, u8 *buf)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+
+ priv->spi_tx_buf[0] = HI3110_READ_FIFO_WOTIME;
+ hi3110_spi_trans(spi, HI3110_RX_BUF_LEN);
+ memcpy(buf, priv->spi_rx_buf + 1, HI3110_RX_BUF_LEN - 1);
+}
+
+static void hi3110_hw_rx(struct spi_device *spi)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+ struct sk_buff *skb;
+ struct can_frame *frame;
+ u8 buf[HI3110_RX_BUF_LEN - 1];
+
+ skb = alloc_can_skb(priv->net, &frame);
+ if (!skb) {
+ dev_err(&spi->dev, "cannot allocate RX skb\n");
+ priv->net->stats.rx_dropped++;
+ return;
+ }
+
+ hi3110_hw_rx_frame(spi, buf);
+ if (buf[HI3110_FIFO_WOTIME_TAG_OFF] & HI3110_FIFO_WOTIME_TAG_IDE) {
+ /* IDE is recessive (1), indicating extended 29-bit frame */
+ frame->can_id = CAN_EFF_FLAG;
+ frame->can_id |=
+ (buf[HI3110_FIFO_WOTIME_ID_OFF] << 21) |
+ (((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0xE0) >> 5) << 18) |
+ ((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0x07) << 15) |
+ (buf[HI3110_FIFO_WOTIME_ID_OFF + 2] << 7) |
+ (buf[HI3110_FIFO_WOTIME_ID_OFF + 3] >> 1);
+ } else {
+ /* IDE is dominant (0), frame indicating standard 11-bit */
+ frame->can_id =
+ (buf[HI3110_FIFO_WOTIME_ID_OFF] << 3) |
+ ((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0xE0) >> 5);
+ }
+
+ if (buf[HI3110_FIFO_WOTIME_ID_OFF + 3] & HI3110_FIFO_WOTIME_ID_RTR) {
+ /* RTR is recessive (1), indicating remote request frame */
+ frame->can_id |= CAN_RTR_FLAG;
+ }
+
+ /* Data length */
+ frame->can_dlc = get_can_dlc(buf[HI3110_FIFO_WOTIME_DLC_OFF] & 0x0F);
+ memcpy(frame->data, buf + HI3110_FIFO_WOTIME_DAT_OFF, frame->can_dlc);
+
+ priv->net->stats.rx_packets++;
+ priv->net->stats.rx_bytes += frame->can_dlc;
+
+ can_led_event(priv->net, CAN_LED_EVENT_RX);
+
+ netif_rx_ni(skb);
+}
+
+static void hi3110_hw_sleep(struct spi_device *spi)
+{
+ hi3110_write(spi, HI3110_WRITE_CTRL0, HI3110_CTRL0_SLEEP_MODE);
+}
+
+static netdev_tx_t hi3110_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *net)
+{
+ struct hi3110_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ if (priv->tx_skb || priv->tx_len) {
+ dev_warn(&spi->dev, "hard_xmit called while tx busy\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ if (can_dropped_invalid_skb(net, skb))
+ return NETDEV_TX_OK;
+
+ netif_stop_queue(net);
+ priv->tx_skb = skb;
+ queue_work(priv->wq, &priv->tx_work);
+
+ return NETDEV_TX_OK;
+}
+
+static int hi3110_do_set_mode(struct net_device *net, enum can_mode mode)
+{
+ struct hi3110_priv *priv = netdev_priv(net);
+
+ switch (mode) {
+ case CAN_MODE_START:
+ hi3110_clean(net);
+ /* We have to delay work since SPI I/O may sleep */
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ priv->restart_tx = 1;
+ if (priv->can.restart_ms == 0)
+ priv->after_suspend = HI3110_AFTER_SUSPEND_RESTART;
+ queue_work(priv->wq, &priv->restart_work);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int hi3110_set_normal_mode(struct spi_device *spi)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+ u8 reg;
+
+ hi3110_write(spi, HI3110_WRITE_INTE, HI3110_INT_BUSERR |
+ HI3110_INT_RXFIFO | HI3110_INT_TXCPLT);
+
+ /* Enable TX */
+ hi3110_write(spi, HI3110_WRITE_CTRL1, HI3110_CTRL1_TXEN);
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
+ /* Put device into loopback mode */
+ hi3110_write(spi, HI3110_WRITE_CTRL0,
+ HI3110_CTRL0_LOOPBACK_MODE);
+ } else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) {
+ /* Put device into listen-only mode */
+ hi3110_write(spi, HI3110_WRITE_CTRL0,
+ HI3110_CTRL0_MONITOR_MODE);
+ } else {
+ /* Put device into normal mode */
+ hi3110_write(spi, HI3110_WRITE_CTRL0,
+ HI3110_CTRL0_NORMAL_MODE);
+
+ /* Wait for the device to enter normal mode */
+ mdelay(HI3110_OST_DELAY_MS);
+ reg = hi3110_read(spi, HI3110_READ_CTRL0);
+ if ((reg & HI3110_CTRL0_MODE_MASK) != HI3110_CTRL0_NORMAL_MODE)
+ return -EBUSY;
+ }
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ return 0;
+}
+
+static int hi3110_do_set_bittiming(struct net_device *net)
+{
+ struct hi3110_priv *priv = netdev_priv(net);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct spi_device *spi = priv->spi;
+
+ hi3110_write(spi, HI3110_WRITE_BTR0,
+ ((bt->sjw - 1) << HI3110_BTR0_SJW_SHIFT) |
+ ((bt->brp - 1) << HI3110_BTR0_BRP_SHIFT));
+
+ hi3110_write(spi, HI3110_WRITE_BTR1,
+ (priv->can.ctrlmode &
+ CAN_CTRLMODE_3_SAMPLES ?
+ HI3110_BTR1_SAMP_3PERBIT : HI3110_BTR1_SAMP_1PERBIT) |
+ ((bt->phase_seg1 + bt->prop_seg - 1)
+ << HI3110_BTR1_TSEG1_SHIFT) |
+ ((bt->phase_seg2 - 1) << HI3110_BTR1_TSEG2_SHIFT));
+
+ dev_dbg(&spi->dev, "BT: 0x%02x 0x%02x\n",
+ hi3110_read(spi, HI3110_READ_BTR0),
+ hi3110_read(spi, HI3110_READ_BTR1));
+
+ return 0;
+}
+
+static int hi3110_setup(struct net_device *net)
+{
+ hi3110_do_set_bittiming(net);
+ return 0;
+}
+
+static int hi3110_hw_reset(struct spi_device *spi)
+{
+ u8 reg;
+ int ret;
+
+ /* Wait for oscillator startup timer after power up */
+ mdelay(HI3110_OST_DELAY_MS);
+
+ ret = hi3110_cmd(spi, HI3110_MASTER_RESET);
+ if (ret)
+ return ret;
+
+ /* Wait for oscillator startup timer after reset */
+ mdelay(HI3110_OST_DELAY_MS);
+
+ reg = hi3110_read(spi, HI3110_READ_CTRL0);
+ if ((reg & HI3110_CTRL0_MODE_MASK) != HI3110_CTRL0_INIT_MODE)
+ return -ENODEV;
+
+ /* As per the datasheet it appears the error flags are
+ * not cleared on reset. Explicitly clear them by performing a read
+ */
+ hi3110_read(spi, HI3110_READ_ERR);
+
+ return 0;
+}
+
+static int hi3110_hw_probe(struct spi_device *spi)
+{
+ u8 statf;
+
+ hi3110_hw_reset(spi);
+
+ /* Confirm correct operation by checking against reset values
+ * in datasheet
+ */
+ statf = hi3110_read(spi, HI3110_READ_STATF);
+
+ dev_dbg(&spi->dev, "statf: %02X\n", statf);
+
+ if (statf != 0x82)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int hi3110_power_enable(struct regulator *reg, int enable)
+{
+ if (IS_ERR_OR_NULL(reg))
+ return 0;
+
+ if (enable)
+ return regulator_enable(reg);
+ else
+ return regulator_disable(reg);
+}
+
+static int hi3110_stop(struct net_device *net)
+{
+ struct hi3110_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ close_candev(net);
+
+ priv->force_quit = 1;
+ free_irq(spi->irq, priv);
+ destroy_workqueue(priv->wq);
+ priv->wq = NULL;
+
+ mutex_lock(&priv->hi3110_lock);
+
+ /* Disable transmit, interrupts and clear flags */
+ hi3110_write(spi, HI3110_WRITE_CTRL1, 0x0);
+ hi3110_write(spi, HI3110_WRITE_INTE, 0x0);
+ hi3110_read(spi, HI3110_READ_INTF);
+
+ hi3110_clean(net);
+
+ hi3110_hw_sleep(spi);
+
+ hi3110_power_enable(priv->transceiver, 0);
+
+ priv->can.state = CAN_STATE_STOPPED;
+
+ mutex_unlock(&priv->hi3110_lock);
+
+ can_led_event(net, CAN_LED_EVENT_STOP);
+
+ return 0;
+}
+
+static void hi3110_error_skb(struct net_device *net, int can_id,
+ int data1, int data2)
+{
+ struct sk_buff *skb;
+ struct can_frame *frame;
+
+ skb = alloc_can_err_skb(net, &frame);
+ if (skb) {
+ frame->can_id |= can_id;
+ frame->data[1] = data1;
+ frame->data[2] = data2;
+ netif_rx_ni(skb);
+ } else {
+ netdev_err(net, "cannot allocate error skb\n");
+ }
+}
+
+static void hi3110_tx_work_handler(struct work_struct *ws)
+{
+ struct hi3110_priv *priv = container_of(ws, struct hi3110_priv,
+ tx_work);
+ struct spi_device *spi = priv->spi;
+ struct net_device *net = priv->net;
+ struct can_frame *frame;
+
+ mutex_lock(&priv->hi3110_lock);
+ if (priv->tx_skb) {
+ if (priv->can.state == CAN_STATE_BUS_OFF) {
+ hi3110_clean(net);
+ } else {
+ frame = (struct can_frame *)priv->tx_skb->data;
+ hi3110_hw_tx(spi, frame);
+ priv->tx_len = 1 + frame->can_dlc;
+ can_put_echo_skb(priv->tx_skb, net, 0);
+ priv->tx_skb = NULL;
+ }
+ }
+ mutex_unlock(&priv->hi3110_lock);
+}
+
+static void hi3110_restart_work_handler(struct work_struct *ws)
+{
+ struct hi3110_priv *priv = container_of(ws, struct hi3110_priv,
+ restart_work);
+ struct spi_device *spi = priv->spi;
+ struct net_device *net = priv->net;
+
+ mutex_lock(&priv->hi3110_lock);
+ if (priv->after_suspend) {
+ hi3110_hw_reset(spi);
+ hi3110_setup(net);
+ if (priv->after_suspend & HI3110_AFTER_SUSPEND_RESTART) {
+ hi3110_set_normal_mode(spi);
+ } else if (priv->after_suspend & HI3110_AFTER_SUSPEND_UP) {
+ netif_device_attach(net);
+ hi3110_clean(net);
+ hi3110_set_normal_mode(spi);
+ netif_wake_queue(net);
+ } else {
+ hi3110_hw_sleep(spi);
+ }
+ priv->after_suspend = 0;
+ priv->force_quit = 0;
+ }
+
+ if (priv->restart_tx) {
+ priv->restart_tx = 0;
+ hi3110_clean(net);
+ netif_wake_queue(net);
+ hi3110_error_skb(net, CAN_ERR_RESTARTED, 0, 0);
+ }
+ mutex_unlock(&priv->hi3110_lock);
+}
+
+static irqreturn_t hi3110_can_ist(int irq, void *dev_id)
+{
+ struct hi3110_priv *priv = dev_id;
+ struct spi_device *spi = priv->spi;
+ struct net_device *net = priv->net;
+
+ mutex_lock(&priv->hi3110_lock);
+
+ while (!priv->force_quit) {
+ enum can_state new_state;
+ u8 intf;
+ u8 eflag;
+ int can_id = 0, data1 = 0, data2 = 0;
+
+ while (!(HI3110_STAT_RXFMTY &
+ hi3110_read(spi, HI3110_READ_STATF))) {
+ hi3110_hw_rx(spi);
+ }
+
+ intf = hi3110_read(spi, HI3110_READ_INTF);
+ eflag = hi3110_read(spi, HI3110_READ_ERR);
+ /* Update can state */
+ if (eflag & HI3110_ERR_BUSOFF) {
+ new_state = CAN_STATE_BUS_OFF;
+ can_id |= CAN_ERR_BUSOFF;
+ } else if (eflag & HI3110_ERR_TXERRP) {
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ can_id |= CAN_ERR_CRTL;
+ data1 |= CAN_ERR_CRTL_TX_PASSIVE;
+ } else if (eflag & HI3110_ERR_RXERRP) {
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ can_id |= CAN_ERR_CRTL;
+ data1 |= CAN_ERR_CRTL_RX_PASSIVE;
+ } else {
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ }
+
+ /* Check for protocol errors */
+ if (eflag & HI3110_ERR_PROTOCOL_MASK) {
+ can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+ priv->can.can_stats.bus_error++;
+ priv->net->stats.rx_errors++;
+ if (eflag & HI3110_ERR_BITERR)
+ data2 |= CAN_ERR_PROT_BIT;
+ else if (eflag & HI3110_ERR_FRMERR)
+ data2 |= CAN_ERR_PROT_FORM;
+ else if (eflag & HI3110_ERR_STUFERR)
+ data2 |= CAN_ERR_PROT_STUFF;
+ else
+ data2 |= CAN_ERR_PROT_UNSPEC;
+ }
+
+ /* Update can state statistics */
+ switch (priv->can.state) {
+ case CAN_STATE_ERROR_ACTIVE:
+ if (new_state >= CAN_STATE_ERROR_WARNING &&
+ new_state <= CAN_STATE_BUS_OFF)
+ priv->can.can_stats.error_warning++;
+ /* fallthrough */
+ case CAN_STATE_ERROR_WARNING:
+ if (new_state >= CAN_STATE_ERROR_PASSIVE &&
+ new_state <= CAN_STATE_BUS_OFF)
+ priv->can.can_stats.error_passive++;
+ break;
+ default:
+ break;
+ }
+ priv->can.state = new_state;
+
+ if (intf & HI3110_INT_BUSERR) {
+ /* Note: HI3110 Does report overflow errors */
+ hi3110_error_skb(net, can_id, data1, data2);
+ }
+
+ if (priv->can.state == CAN_STATE_BUS_OFF) {
+ if (priv->can.restart_ms == 0) {
+ priv->force_quit = 1;
+ priv->can.can_stats.bus_off++;
+ can_bus_off(net);
+ hi3110_hw_sleep(spi);
+ break;
+ }
+ }
+
+ if (intf == 0)
+ break;
+
+ if (intf & HI3110_INT_TXCPLT) {
+ net->stats.tx_packets++;
+ net->stats.tx_bytes += priv->tx_len - 1;
+ can_led_event(net, CAN_LED_EVENT_TX);
+ if (priv->tx_len) {
+ can_get_echo_skb(net, 0);
+ priv->tx_len = 0;
+ }
+ netif_wake_queue(net);
+ }
+ }
+ mutex_unlock(&priv->hi3110_lock);
+ return IRQ_HANDLED;
+}
+
+static int hi3110_open(struct net_device *net)
+{
+ struct hi3110_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_RISING;
+ int ret;
+
+ ret = open_candev(net);
+ if (ret) {
+ dev_err(&spi->dev, "unable to set initial baudrate!\n");
+ return ret;
+ }
+
+ mutex_lock(&priv->hi3110_lock);
+ hi3110_power_enable(priv->transceiver, 1);
+
+ priv->force_quit = 0;
+ priv->tx_skb = NULL;
+ priv->tx_len = 0;
+
+ ret = request_threaded_irq(spi->irq, NULL, hi3110_can_ist,
+ flags, DEVICE_NAME, priv);
+ if (ret) {
+ dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq);
+ goto out_close;
+ }
+
+ priv->wq = alloc_workqueue("hi3110_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM,
+ 0);
+ INIT_WORK(&priv->tx_work, hi3110_tx_work_handler);
+ INIT_WORK(&priv->restart_work, hi3110_restart_work_handler);
+
+ ret = hi3110_hw_reset(spi);
+ if (ret)
+ goto out_free_irq;
+
+ ret = hi3110_setup(net);
+ if (ret)
+ goto out_free_irq;
+
+ ret = hi3110_set_normal_mode(spi);
+ if (ret)
+ goto out_free_irq;
+
+ can_led_event(net, CAN_LED_EVENT_OPEN);
+ netif_wake_queue(net);
+ mutex_unlock(&priv->hi3110_lock);
+
+ return 0;
+
+out_free_irq:
+ free_irq(spi->irq, priv);
+ hi3110_hw_sleep(spi);
+
+out_close:
+ hi3110_power_enable(priv->transceiver, 0);
+ close_candev(net);
+ mutex_unlock(&priv->hi3110_lock);
+ return ret;
+}
+
+static const struct net_device_ops hi3110_netdev_ops = {
+ .ndo_open = hi3110_open,
+ .ndo_stop = hi3110_stop,
+ .ndo_start_xmit = hi3110_hard_start_xmit,
+};
+
+static const struct of_device_id hi3110_of_match[] = {
+ {
+ .compatible = "holt,hi3110",
+ .data = (void *)CAN_HI3110_HI3110,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hi3110_of_match);
+
+static const struct spi_device_id hi3110_id_table[] = {
+ {
+ .name = "hi3110",
+ .driver_data = (kernel_ulong_t)CAN_HI3110_HI3110,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, hi3110_id_table);
+
+static int hi3110_can_probe(struct spi_device *spi)
+{
+ const struct of_device_id *of_id = of_match_device(hi3110_of_match,
+ &spi->dev);
+ struct net_device *net;
+ struct hi3110_priv *priv;
+ struct clk *clk;
+ int freq, ret;
+
+ clk = devm_clk_get(&spi->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&spi->dev, "no CAN clock source defined\n");
+ return PTR_ERR(clk);
+ }
+ freq = clk_get_rate(clk);
+
+ /* Sanity check */
+ if (freq > 40000000)
+ return -ERANGE;
+
+ /* Allocate can/net device */
+ net = alloc_candev(sizeof(struct hi3110_priv), HI3110_TX_ECHO_SKB_MAX);
+ if (!net)
+ return -ENOMEM;
+
+ if (!IS_ERR(clk)) {
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto out_free;
+ }
+
+ net->netdev_ops = &hi3110_netdev_ops;
+ net->flags |= IFF_ECHO;
+
+ priv = netdev_priv(net);
+ priv->can.bittiming_const = &hi3110_bittiming_const;
+ priv->can.do_set_mode = hi3110_do_set_mode;
+ priv->can.clock.freq = freq / 2;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
+ CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY;
+ if (of_id)
+ priv->model = (enum hi3110_model)of_id->data;
+ else
+ priv->model = spi_get_device_id(spi)->driver_data;
+ priv->net = net;
+ priv->clk = clk;
+
+ spi_set_drvdata(spi, priv);
+
+ /* Configure the SPI bus */
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret)
+ goto out_clk;
+
+ priv->power = devm_regulator_get_optional(&spi->dev, "vdd");
+ priv->transceiver = devm_regulator_get_optional(&spi->dev, "xceiver");
+ if ((PTR_ERR(priv->power) == -EPROBE_DEFER) ||
+ (PTR_ERR(priv->transceiver) == -EPROBE_DEFER)) {
+ ret = -EPROBE_DEFER;
+ goto out_clk;
+ }
+
+ ret = hi3110_power_enable(priv->power, 1);
+ if (ret)
+ goto out_clk;
+
+ priv->spi = spi;
+ mutex_init(&priv->hi3110_lock);
+
+ /* If requested, allocate DMA buffers */
+ if (hi3110_enable_dma) {
+ spi->dev.coherent_dma_mask = ~0;
+
+ /* Minimum coherent DMA allocation is PAGE_SIZE, so allocate
+ * that much and share it between Tx and Rx DMA buffers.
+ */
+ priv->spi_tx_buf = dmam_alloc_coherent(&spi->dev,
+ PAGE_SIZE,
+ &priv->spi_tx_dma,
+ GFP_DMA);
+
+ if (priv->spi_tx_buf) {
+ priv->spi_rx_buf = (priv->spi_tx_buf + (PAGE_SIZE / 2));
+ priv->spi_rx_dma = (dma_addr_t)(priv->spi_tx_dma +
+ (PAGE_SIZE / 2));
+ } else {
+ /* Fall back to non-DMA */
+ hi3110_enable_dma = 0;
+ }
+ }
+
+ /* Allocate non-DMA buffers */
+ if (!hi3110_enable_dma) {
+ priv->spi_tx_buf = devm_kzalloc(&spi->dev, HI3110_RX_BUF_LEN,
+ GFP_KERNEL);
+ if (!priv->spi_tx_buf) {
+ ret = -ENOMEM;
+ goto error_probe;
+ }
+ priv->spi_rx_buf = devm_kzalloc(&spi->dev, HI3110_RX_BUF_LEN,
+ GFP_KERNEL);
+
+ if (!priv->spi_rx_buf) {
+ ret = -ENOMEM;
+ goto error_probe;
+ }
+ }
+
+ SET_NETDEV_DEV(net, &spi->dev);
+
+ ret = hi3110_hw_probe(spi);
+ if (ret) {
+ if (ret == -ENODEV)
+ dev_err(&spi->dev, "Cannot initialize %x. Wrong wiring?\n",
+ priv->model);
+ goto error_probe;
+ }
+ hi3110_hw_sleep(spi);
+
+ ret = register_candev(net);
+ if (ret)
+ goto error_probe;
+
+ devm_can_led_init(net);
+ netdev_info(net, "%x successfully initialized.\n", priv->model);
+
+ return 0;
+
+error_probe:
+ hi3110_power_enable(priv->power, 0);
+
+out_clk:
+ if (!IS_ERR(clk))
+ clk_disable_unprepare(clk);
+
+out_free:
+ free_candev(net);
+
+ dev_err(&spi->dev, "Probe failed, err=%d\n", -ret);
+ return ret;
+}
+
+static int hi3110_can_remove(struct spi_device *spi)
+{
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+
+ unregister_candev(net);
+
+ hi3110_power_enable(priv->power, 0);
+
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+
+ free_candev(net);
+
+ return 0;
+}
+
+static int __maybe_unused hi3110_can_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+
+ priv->force_quit = 1;
+ disable_irq(spi->irq);
+
+ /* Note: at this point neither IST nor workqueues are running.
+ * open/stop cannot be called anyway so locking is not needed
+ */
+ if (netif_running(net)) {
+ netif_device_detach(net);
+
+ hi3110_hw_sleep(spi);
+ hi3110_power_enable(priv->transceiver, 0);
+ priv->after_suspend = HI3110_AFTER_SUSPEND_UP;
+ } else {
+ priv->after_suspend = HI3110_AFTER_SUSPEND_DOWN;
+ }
+
+ if (!IS_ERR_OR_NULL(priv->power)) {
+ regulator_disable(priv->power);
+ priv->after_suspend |= HI3110_AFTER_SUSPEND_POWER;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused hi3110_can_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct hi3110_priv *priv = spi_get_drvdata(spi);
+
+ if (priv->after_suspend & HI3110_AFTER_SUSPEND_POWER)
+ hi3110_power_enable(priv->power, 1);
+
+ if (priv->after_suspend & HI3110_AFTER_SUSPEND_UP) {
+ hi3110_power_enable(priv->transceiver, 1);
+ queue_work(priv->wq, &priv->restart_work);
+ } else {
+ priv->after_suspend = 0;
+ }
+
+ priv->force_quit = 0;
+ enable_irq(spi->irq);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(hi3110_can_pm_ops, hi3110_can_suspend,
+ hi3110_can_resume);
+
+static struct spi_driver hi3110_can_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = hi3110_of_match,
+ .pm = &hi3110_can_pm_ops,
+ },
+ .id_table = hi3110_id_table,
+ .probe = hi3110_can_probe,
+ .remove = hi3110_can_remove,
+};
+
+module_spi_driver(hi3110_can_driver);
+
+MODULE_AUTHOR("Akshay Bhat <akshay.bhat@timesys.com>");
+MODULE_AUTHOR("Casey Fitzpatrick <casey.fitzpatrick@timesys.com>");
+MODULE_DESCRIPTION("Holt HI-3110 CAN driver");
+MODULE_LICENSE("GPL v2");
--
2.8.1
^ permalink raw reply related
* Re: [PATCH net-next] tcp: accept RST for rcv_nxt - 1 after receiving a FIN
From: Eric Dumazet @ 2017-01-17 19:13 UTC (permalink / raw)
To: Rick Jones; +Cc: Jason Baron, David Miller, netdev
In-Reply-To: <92daa9c2-7bfc-94ff-bb06-91ef03cd0d66@hpe.com>
On Tue, Jan 17, 2017 at 11:04 AM, Rick Jones <rick.jones2@hpe.com> wrote:
>up unneeded resources in a more expedient fashion.
>
>
> Drifting a bit, and it doesn't change the value of dealing with it, but out
> of curiosity, when you say mostly in CLOSE_WAIT, why aren't the server-side
> applications reacting to the read return of zero triggered by the arrival of
> the FIN?
Even if the application reacts, and calls close(fd), kernel will still
try to push the data that was queued into socket write queue prior to
receiving the FIN.
By allowing this RST, we can flush the whole data and react much
faster, avoiding locking memory in the kernel for very long time.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox