* Re: [PATCH 1/2] dt-binding: can: mcp2517fd: document device tree bindings
From: Rob Herring @ 2017-11-26 22:28 UTC (permalink / raw)
To: kernel-TqfNSX0MhmxHKSADF0wUEw
Cc: Wolfgang Grandegger, Marc Kleine-Budde, Mark Rutland,
linux-can-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20171124183509.12810-2-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
On Fri, Nov 24, 2017 at 06:35:08PM +0000, kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org wrote:
> From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
>
> Add device-tree bindings for Microcip CanFD Controller mcp2517fd
s/Microcip/Microchip/
>
> Signed-off-by: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
> ---
> .../bindings/net/can/microchip,mcp2517fd.txt | 47 ++++++++++++++++++++++
> 1 file changed, 47 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/net/can/microchip,mcp2517fd.txt
>
> diff --git a/Documentation/devicetree/bindings/net/can/microchip,mcp2517fd.txt b/Documentation/devicetree/bindings/net/can/microchip,mcp2517fd.txt
> new file mode 100644
> index 000000000000..96cbf0c96895
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/can/microchip,mcp2517fd.txt
> @@ -0,0 +1,47 @@
> +* Microchip MCP2517 stand-alone CAN controller device tree bindings
> +
> +Required properties:
> + - compatible: Should be one of the following:
> + - "microchip,mcp2517fd" for MCP2517fd.
> + - 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.
> + - microchip,clock_out_div = <0|1|2|4|10>: Clock output pin divider
s/_/-/
And on the rest. Don't use underscores...
> + 0 = Start of Frame output
> + default: 10
> + - microchip,clock_div = <1|2>: internal clock divider - default 1
> + - microchip,gpio_opendrain: gpio (int0,1) in open drain mode
> + instead of default push/pull
> + - microchip,int_opendrain: int pin in open drain mode
> + instead of default push/pull
IIRC, we have a standard property for this.
> + - microchip,txcan_opendrain: txcan pin in open drain mode
> + instead of default push/pull
> + - microchip,gpio0_mode : gpio mode functionality
> + 0 = input
> + 1 = TX interrupt output - default
> + 2 = output default low
> + 3 = output default high
> + 4 = (tx) transceiver standby
> + - microchip,gpio1_mode : gpio mode functionality
> + 0 = input - default
> + 1 = RX interrupt output - default
> + 2 = output default low
> + 3 = output default high
> +
> +Example:
> + can0: can@1 {
> + compatible = "microchip,mcp2515";
> + reg = <1>;
> + clocks = <&clk24m>;
> + interrupt-parent = <&gpio4>;
> + interrupts = <13 0x8>;
> + vdd-supply = <®5v0>;
> + xceiver-supply = <®5v0>;
> + microchip,gpio0_mode = <4>;
> + microchip,gpio0_mode = <1>;
> + };
> --
> 2.11.0
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v4 3/6] Documentation : can : flexcan : Add big-endian property to device tree
From: Rob Herring @ 2017-11-26 22:18 UTC (permalink / raw)
To: Pankaj Bansal
Cc: wg-5Yr1BZd7O62+XT7JhA+gdA, mkl-bIcnvbaLZ9MEGnE8C9+IrQ,
linux-can-u79uwXL29TY76Z2rM5mHXA, mark.rutland-5wv7dgnIgG8,
devicetree-u79uwXL29TY76Z2rM5mHXA, V.Sethi-3arQi8VN3Tc,
poonam.aggrwal-3arQi8VN3Tc
In-Reply-To: <1511529733-27942-4-git-send-email-pankaj.bansal-3arQi8VN3Tc@public.gmane.org>
On Fri, Nov 24, 2017 at 06:52:10PM +0530, Pankaj Bansal wrote:
> The FlexCAN controller can be modelled as little or big endian depending
> on SOC design. This device tree property identifies the controller
> endianness and the driver reads/writes controller registers based on
> that.
>
> This is optional property. i.e. if this property is not present in
> device tree node then controller is assumed to be little endian. if this
> property is present then controller is assumed to be big endian.
>
> Signed-off-by: Pankaj Bansal <pankaj.bansal-3arQi8VN3Tc@public.gmane.org>
> Reviewed-by: Poonam Aggrwal <poonam.aggrwal-3arQi8VN3Tc@public.gmane.org>
> ---
> Changes in v4:
> - document the default behaviour in the bindings if the big-endian property
> is missing.
> - Merged device tree changes and driver changes in one series
> Changes in v3:
> - No change.
> Changes in v2:
> - No change.
> - Added one more patch in series.
>
> Documentation/devicetree/bindings/net/can/fsl-flexcan.txt | 6 ++++++
> 1 file changed, 6 insertions(+)
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 2/2] can: flexcan: adding platform specific details for LS1021A
From: Mirza Krak @ 2017-11-26 21:11 UTC (permalink / raw)
To: Wolfgang Grandegger
Cc: Marc Kleine-Budde, ZHU Yi (ST-FIR/ENG1-Zhu), Pankaj Bansal,
linux-can@vger.kernel.org, Varun Sethi, Poonam Aggrwal,
Stefan Agner
In-Reply-To: <d339bdc6-49c4-3184-6fa4-12521f491f20@grandegger.com>
2017-11-24 20:19 GMT+01:00 Wolfgang Grandegger <wg@grandegger.com>:
> Hello Mirza,
>
< snip >
>>>
>>> You can test such state changes as show below. First setup the interface
>>> and
>>> start a session calling "candump" to report the state changes:
>>>
>>> # ip link set can0 up type can bitrate 500000 restart-ms 100
>>> # candump -td -e any,0:0,#FFFFFFFF
>>>
>>> Then
>>>
>>> 1. disconnect the cable and send messages with "cangen"
>>>
>>> # cangen -i can0
>>>
>>> "candump" should then report the error state changes "error-active" ->
>>> "warning" -> "error-passive". If you don't see "error-passive", the
>>> hardware
>>> needs the FLEXCAN_QUIRK_BROKEN_PERR_STATE as well. Then reconnect the
>>> cable
>>> and send messages till the "error active" state is reached (again with
>>> "cangen").
>>
>>
>> This was the result of this test:
>>
>> root@colibri-vf:~# ./candump -td -e can0,0:0,#FFFFFFFF
>> (000.000000) can0 0C8 [8] E7 D5 75 20 1C F8 AA 6D
>> (000.200201) can0 7CD [8] 58 5F B1 1E D7 AB 3C 15
>> (000.200123) can0 7FD [8] EC 43 EF 11 BB 76 50 4F
>> (000.200119) can0 20D [8] 64 E0 A6 74 A2 A4 22 42
>> (000.200115) can0 226 [7] CB 4D 12 46 F3 37 F1
>> (000.200000) can0 42F [0]
>> (000.200245) can0 0F9 [8] AD 61 16 77 0A 64 50 5C
>> (000.199983) can0 76E [0]
>> (000.200206) can0 3CB [6] 53 28 03 34 98 43
>> (000.200152) can0 230 [8] 70 EF 66 38 2D BA 5D 4D
>> (000.200106) can0 568 [8] E8 30 AE 1C 75 77 4E 5E
>> (000.200274) can0 20000004 [8] 00 04 00 00 00 00 00 00 ERRORFRAME
>> controller-problem{rx-error-warning}
>>
>> and the statistics
>>
>> root@colibri-vf:~# ip -d -s link show can0
>> 2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state
>> UNKNOWN mode DEFAULT group default qlen 10
>> link/can promiscuity 0
>> can state ERROR-WARNING (berr-counter tx 0 rx 128) restart-ms 100
>> bitrate 496240 sample-point 0.857
>> tq 143 prop-seg 5 phase-seg1 6 phase-seg2 2 sjw 1
>> flexcan: tseg1 4..16 tseg2 2..8 sjw 1..4 brp 1..256 brp-inc 1
>> clock 83368421
>> re-started bus-errors arbit-lost error-warn error-pass bus-off
>> 0 0 0 1 0 0
>> numtxqueues 1
>> RX: bytes packets errors dropped overrun mcast
>> 8 1 0 0 0 0
>> TX: bytes packets errors dropped carrier collsns
>> 69 11 0 0 0 0
>>
>> I ran out of time as wanted to test a bit more as I was confused that
>> it is showing RX errors on the first test? I was not able to get it
>> back to ERROR-ACTIVE from ERROR-WARNING either. But posted result
>> anyway
>
>
> The received error messages are counted as well.
>
>>> 2. The second test is quite similar. Instead of disconnecting the cable,
>>> short-circuit the CAN low and high lines. The device should then go to
>>> "bus-off".
>>
>>
>> And this one:
>>
>> root@colibri-vf:~# ./candump -td -e any,0:0,#FFFFFFFF
>> (000.000000) can0 20000004 [8] 00 20 00 00 00 00 00 00 ERRORFRAME
>> controller-problem{tx-error-passive}
>> (000.000809) can0 20000040 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
>> bus-off
>> (000.109797) can0 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
>> restarted-after-bus-off
>> (000.100551) can0 20000004 [8] 00 20 00 00 00 00 00 00 ERRORFRAME
>> controller-problem{tx-error-passive}
>> (000.000823) can0 20000040 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
>> bus-off
>> (000.108644) can0 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
>> restarted-after-bus-off
>> (000.090692) can0 20000004 [8] 00 08 00 00 00 00 00 00 ERRORFRAME
>> controller-problem{tx-error-warning}
>> (000.000797) can0 20000040 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
>> bus-off
>>
>> Will try again next week when I am back at the office.
>>
>> I ran my tests on 4.14 kernel.
>
>
> This Flexcan core needs the FLEXCAN_QUIRK_BROKEN_PERR_STATE as well, like
> all other cores. Adding that quirk for the vf610 will cure the problems.
>
I will probably get some time during the coming week to test adding
FLEXCAN_QUIRK_BROKEN_PERR_STATE and I can send a patch if it all looks
good.
--
Med Vänliga Hälsningar / Best Regards
Mirza Krak
^ permalink raw reply
* Re: [PATCH 0/2] Microchip mcp2517fd can controller driver
From: kernel @ 2017-11-26 19:53 UTC (permalink / raw)
To: Wolfgang Grandegger
Cc: Oliver Hartkopp, Marc Kleine-Budde, Rob Herring, Mark Rutland,
linux-can, devicetree
In-Reply-To: <3b1c4778-b155-7aaf-e5b4-14061e687161@grandegger.com>
Hi Wolfgang!
> On 26.11.2017, at 20:05, Wolfgang Grandegger <wg@grandegger.com> wrote:
>> Test messages sent and received: 32
>> (I can not go to longer tests because I may hit a bug I am just
>> now trying to fix in the current code level).
>
> This test does not load the bus a lot but uses burst of message to trigger out-of-order issues. Please run the test without "-v" and much longer.
I agree, but as mentioned: I am debugging something else, which makes the
system slightly unstable, so I refrained from running longer tests...
>> If you are interested in any more specific tests that I should
>> be running, then please list those and I can report them
>> when submitting V2 of the patch set.
>
> Other useful test are about bus error reporting and error state changes.
>
> Can bus errors been disabled via interrupt? I do not see that CAN_CTRLMODE_BERR_REPORTING is handled. This means that bus error reporting is always on which may put heavy load on the system. e.g if no cable is connected and a message sent (at 1MB/s).
>
> Concerning the error state changes, have a look to [1].
Yes - it comes with the 9 (32-bit) register read on every interrupt.
This is actually an optimization to avoid kernel latencies that would
occur when reading 3 sets of registers separately.
Also longer reads means a higher likleyhood that DMA will be used
by the spi-controller/driver.
>
>> Just to put everything into perspective:
>> The equipment available to me is:
>> * beagle bone black with c_can
>> * raspberry pi 3 with mcp2515
>> * raspberry pi CM3 with mcp2517fd
>> * raspberry pi 2 with mcp2517fd
>> * saleae logic analyzer
>
> [1] https://marc.info/?l=linux-can&m=151147114323582&w=2
>
I will try to run those and add them to the cover page for V2.
> The driver has more than 3000 lines... a review will take some time.
Please look mostly at the device-tree and the main comments about the
rationale and performance optimizations at first.
Feedback on these I could include with V2 of the patch that will
fix also the above mentioned errors and MAB avoidance code.
I should be able to post it later this week.
Thanks,
Martin
^ permalink raw reply
* Re: [PATCH 0/2] Microchip mcp2517fd can controller driver
From: Wolfgang Grandegger @ 2017-11-26 19:05 UTC (permalink / raw)
To: kernel
Cc: Oliver Hartkopp, Marc Kleine-Budde, Rob Herring, Mark Rutland,
linux-can, devicetree
In-Reply-To: <CEE8AB99-2B26-44C0-A8D1-DD339DC9FEF7@martin.sperl.org>
Am 26.11.2017 um 19:29 schrieb kernel@martin.sperl.org:
>
>> On 26.11.2017, at 17:18, Wolfgang Grandegger <wg@grandegger.com> wrote:
>>
>> Hello Martin,
>>
>> Am 25.11.2017 um 15:47 schrieb kernel@martin.sperl.org:
>>
>> ...snip...
>>> I see the following counters:
>>> 7: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10
>>> link/can promiscuity 0
>>> can <ONE-SHOT,BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>>
>> Why do you use "ONE_SHOT" in all your test cases?
> I did test oneshot and non-oneshot - I just did not document them
> here, I am just mentioning the issue of dropped frames when the driver
> is submitting them correctly.
>
> Also - for what it is worse - the RX case would not be impacted by one-shot
> enabled or not.
>
>
>> Did you also check for out-of-order issues, e.g. by using "canfdtest”?
>
> As for out of order: yes I have taken extreme care that we do not
> get out of order packets for all possible cases testing with cangen - there
> may be one error case where there could be an out of order case in the
> tx path (unless we want to drop that frame - it would be a MAB case)
>
> But until now I did not know about canfdtest, but for all practical
> purposes it can not test for out-of-order delivery on a 100% saturated
> bus the way it is working - latencies are too big for that!
>
> Still for completeness a quick test from beagleboneblack to a
> RPI CM3 with a mcp2517fd gives:
> root@beaglebone:~/can-utils# ./canfdtest -g -vvv can0 -l 32
> interface = can0, family = 29, type = 3, proto = 1
> 0078: [8] 01 02 03 04 05 06 07 08
> 0078: [8] 02 03 04 05 06 07 08 09
> 0078: [8] 03 04 05 06 07 08 09 0a
> 0078: [8] 04 05 06 07 08 09 0a 0b
> 0078: [8] 05 06 07 08 09 0a 0b 0c
> 0078: [8] 06 07 08 09 0a 0b 0c 0d
> 0078: [8] 07 08 09 0a 0b 0c 0d 0e
> 0078: [8] 08 09 0a 0b 0c 0d 0e 0f
> 0078: [8] 09 0a 0b 0c 0d 0e 0f 10
> 0078: [8] 0a 0b 0c 0d 0e 0f 10 11
> 0078: [8] 0b 0c 0d 0e 0f 10 11 12
> 0078: [8] 0c 0d 0e 0f 10 11 12 13
> 0078: [8] 0d 0e 0f 10 11 12 13 14
> 0078: [8] 0e 0f 10 11 12 13 14 15
> 0078: [8] 0f 10 11 12 13 14 15 16
> 0078: [8] 10 11 12 13 14 15 16 17
> 0078: [8] 11 12 13 14 15 16 17 18
> 0078: [8] 12 13 14 15 16 17 18 19
> 0078: [8] 13 14 15 16 17 18 19 1a
> 0078: [8] 14 15 16 17 18 19 1a 1b
> 0078: [8] 15 16 17 18 19 1a 1b 1c
> 0078: [8] 16 17 18 19 1a 1b 1c 1d
> 0078: [8] 17 18 19 1a 1b 1c 1d 1e
> 0078: [8] 18 19 1a 1b 1c 1d 1e 1f
> 0078: [8] 19 1a 1b 1c 1d 1e 1f 20
> 0078: [8] 1a 1b 1c 1d 1e 1f 20 21
> 0078: [8] 1b 1c 1d 1e 1f 20 21 22
> 0078: [8] 1c 1d 1e 1f 20 21 22 23
> 0078: [8] 1d 1e 1f 20 21 22 23 24
> 0078: [8] 1e 1f 20 21 22 23 24 25
> 0078: [8] 1f 20 21 22 23 24 25 26
> 0078: [8] 20 21 22 23 24 25 26 27
>
> Test messages sent and received: 32
>
> (I can not go to longer tests because I may hit a bug I am just
> now trying to fix in the current code level).
This test does not load the bus a lot but uses burst of message to
trigger out-of-order issues. Please run the test without "-v" and much
longer.
> If you are interested in any more specific tests that I should
> be running, then please list those and I can report them
> when submitting V2 of the patch set.
Other useful test are about bus error reporting and error state changes.
Can bus errors been disabled via interrupt? I do not see that
CAN_CTRLMODE_BERR_REPORTING is handled. This means that bus error
reporting is always on which may put heavy load on the system. e.g if no
cable is connected and a message sent (at 1MB/s).
Concerning the error state changes, have a look to [1].
> Just to put everything into perspective:
> The equipment available to me is:
> * beagle bone black with c_can
> * raspberry pi 3 with mcp2515
> * raspberry pi CM3 with mcp2517fd
> * raspberry pi 2 with mcp2517fd
> * saleae logic analyzer
[1] https://marc.info/?l=linux-can&m=151147114323582&w=2
The driver has more than 3000 lines... a review will take some time.
Thanks for your contribution.
Wolfgang.
>
> Thanks,
> Martin--
> To unsubscribe from this list: send the line "unsubscribe linux-can" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
>
^ permalink raw reply
* Re: [PATCH 0/2] Microchip mcp2517fd can controller driver
From: kernel @ 2017-11-26 18:29 UTC (permalink / raw)
To: Wolfgang Grandegger
Cc: Oliver Hartkopp, Marc Kleine-Budde, Rob Herring, Mark Rutland,
linux-can, devicetree
In-Reply-To: <0c2f4a35-3b86-7ae6-ccc6-2751fa92f497@grandegger.com>
> On 26.11.2017, at 17:18, Wolfgang Grandegger <wg@grandegger.com> wrote:
>
> Hello Martin,
>
> Am 25.11.2017 um 15:47 schrieb kernel@martin.sperl.org:
>
> ...snip...
>> I see the following counters:
>> 7: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10
>> link/can promiscuity 0
>> can <ONE-SHOT,BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>
> Why do you use "ONE_SHOT" in all your test cases?
I did test oneshot and non-oneshot - I just did not document them
here, I am just mentioning the issue of dropped frames when the driver
is submitting them correctly.
Also - for what it is worse - the RX case would not be impacted by one-shot
enabled or not.
> Did you also check for out-of-order issues, e.g. by using "canfdtest”?
As for out of order: yes I have taken extreme care that we do not
get out of order packets for all possible cases testing with cangen - there
may be one error case where there could be an out of order case in the
tx path (unless we want to drop that frame - it would be a MAB case)
But until now I did not know about canfdtest, but for all practical
purposes it can not test for out-of-order delivery on a 100% saturated
bus the way it is working - latencies are too big for that!
Still for completeness a quick test from beagleboneblack to a
RPI CM3 with a mcp2517fd gives:
root@beaglebone:~/can-utils# ./canfdtest -g -vvv can0 -l 32
interface = can0, family = 29, type = 3, proto = 1
0078: [8] 01 02 03 04 05 06 07 08
0078: [8] 02 03 04 05 06 07 08 09
0078: [8] 03 04 05 06 07 08 09 0a
0078: [8] 04 05 06 07 08 09 0a 0b
0078: [8] 05 06 07 08 09 0a 0b 0c
0078: [8] 06 07 08 09 0a 0b 0c 0d
0078: [8] 07 08 09 0a 0b 0c 0d 0e
0078: [8] 08 09 0a 0b 0c 0d 0e 0f
0078: [8] 09 0a 0b 0c 0d 0e 0f 10
0078: [8] 0a 0b 0c 0d 0e 0f 10 11
0078: [8] 0b 0c 0d 0e 0f 10 11 12
0078: [8] 0c 0d 0e 0f 10 11 12 13
0078: [8] 0d 0e 0f 10 11 12 13 14
0078: [8] 0e 0f 10 11 12 13 14 15
0078: [8] 0f 10 11 12 13 14 15 16
0078: [8] 10 11 12 13 14 15 16 17
0078: [8] 11 12 13 14 15 16 17 18
0078: [8] 12 13 14 15 16 17 18 19
0078: [8] 13 14 15 16 17 18 19 1a
0078: [8] 14 15 16 17 18 19 1a 1b
0078: [8] 15 16 17 18 19 1a 1b 1c
0078: [8] 16 17 18 19 1a 1b 1c 1d
0078: [8] 17 18 19 1a 1b 1c 1d 1e
0078: [8] 18 19 1a 1b 1c 1d 1e 1f
0078: [8] 19 1a 1b 1c 1d 1e 1f 20
0078: [8] 1a 1b 1c 1d 1e 1f 20 21
0078: [8] 1b 1c 1d 1e 1f 20 21 22
0078: [8] 1c 1d 1e 1f 20 21 22 23
0078: [8] 1d 1e 1f 20 21 22 23 24
0078: [8] 1e 1f 20 21 22 23 24 25
0078: [8] 1f 20 21 22 23 24 25 26
0078: [8] 20 21 22 23 24 25 26 27
Test messages sent and received: 32
(I can not go to longer tests because I may hit a bug I am just
now trying to fix in the current code level).
If you are interested in any more specific tests that I should
be running, then please list those and I can report them
when submitting V2 of the patch set.
Just to put everything into perspective:
The equipment available to me is:
* beagle bone black with c_can
* raspberry pi 3 with mcp2515
* raspberry pi CM3 with mcp2517fd
* raspberry pi 2 with mcp2517fd
* saleae logic analyzer
Thanks,
Martin
^ permalink raw reply
* Re: [PATCH 0/2] Microchip mcp2517fd can controller driver
From: Wolfgang Grandegger @ 2017-11-26 16:18 UTC (permalink / raw)
To: kernel, Oliver Hartkopp
Cc: Marc Kleine-Budde, Rob Herring, Mark Rutland, linux-can,
devicetree
In-Reply-To: <263BAF25-3F9A-4BE8-8E19-757B3C22A037@martin.sperl.org>
Hello Martin,
Am 25.11.2017 um 15:47 schrieb kernel@martin.sperl.org:
...snip...
> I see the following counters:
> 7: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10
> link/can promiscuity 0
> can <ONE-SHOT,BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
Why do you use "ONE_SHOT" in all your test cases?
Did you also check for out-of-order issues, e.g. by using "canfdtest"?
Wolfgang.
^ permalink raw reply
* Re: [PATCH 0/2] Microchip mcp2517fd can controller driver
From: kernel @ 2017-11-26 15:43 UTC (permalink / raw)
To: Oliver Hartkopp; +Cc: Wolfgang Grandegger, Marc Kleine-Budde, linux-can
In-Reply-To: <0a300896-3980-3b4e-ce5a-a3a97de3df55@hartkopp.net>
> On 26.11.2017, at 13:38, Oliver Hartkopp <socketcan@hartkopp.net> wrote:
>
> -devicetree
> -Mark Rutland
> -Rob Herring
>
> On 11/25/2017 03:47 PM, kernel@martin.sperl.org wrote:
>>> Unfortunately [PATCH 2/2] only hit the devicetree mailing list but not the linux-can mailing list for the review.
>> I have no idea why you have not received it, but I have sent all
>> of them to the same Recipient list!
>> I even got a relayed confirmation for all 3 of them:
>> 19:35:28.689 2 DEQUEUER [7520159] SMTP(vger.kernel.org)linux-can@vger.kernel.org relayed: relayed via vger.kernel.org
>> 19:35:29.823 2 DEQUEUER [7520154] SMTP(vger.kernel.org)linux-can@vger.kernel.org relayed: relayed via vger.kernel.org
>> 19:35:31.902 2 DEQUEUER [7520160] SMTP(vger.kernel.org)linux-can@vger.kernel.org relayed: relayed via vger.kernel.org
>> So they should all have arrived!
>
> In fact patch 2/2 didn't show up on the mailing list archive and in my inbox ...
>
> https://marc.info/?l=linux-can&r=1&b=201711&w=2
>
> Hm ... obviously no sender issue then :-)
I shall send a V2 anyway - a few more issues/performance improvements came up...
>
>>> Seems you improved the SPI handling here. Would it make sense to separate the SPI-related part of the code to a separate C-file so that the existing mcp251x driver can benefit from these improvements too?
>> The biggest problem is that the Register-design of the mcp2517fd is totally
>> different compared to mcp251x, so most of those “optimizations” for the
>> mcp2517 do not apply to the mcp251x (SPI-API wise IMO the mcp2515 is much
>> more efficient - better still when having dedicated gpios for the RX lines
>> to avoid having to query the status first via spi (reduces latencies
>> and thus reduces packet loss/overflow on a saturated CAN bus).
>> I have a spi_async only version of the mcp251x as well (which has been dormant
>> for a long time, as the mcp251x stopped showing interrupt stopped issues...).
>
> Interesting. I was not aware that the mcp251x is working reliable under heavy loads. I've just seen people providing bcm2835 related hacks/fixes to make it run with the Raspi.
For all practical purposes it is related to edge vs. level interrupts
and the last I have seen seems to indicate that - on a RPI3 - it can
handle the 1MHz Can-bus utilized 100% with 0 length 11 bit can frames
(this is what I call worsted case and target for in all my tests)
>
>>>> (still dropps are observed in the can/network stack).
>>>
>>> Are you sure drops are taking place in the network layer? Can you give me some more details about this statement?
>> Well - when I increase net->stat.rx_dropped there is always a
>> dev_warn_ratelimited that comes along. Also rx_errors is increased at the
>> same time. And I see the drops counters increase while there are no messages
>> in dmesg.
>
> Ok - at least this is not network layer related (the code in linux/net/can) but network driver stuff (linux/drivers/net/can/).
>
>> Also note that the driver is only incrementing rx_packets and rx_bytes only
>> when netif_rx_ni(skb) is called.
>> So the setup is this:
>> * raspberry pi 2 with the mcp2517fd
>> * beagle bone black
>> * 1MHz CAN2.0 bus
>> Beagle bone black:
>> root@beaglebone:~# cangen can0 -g0.0 -L0 -n 1000000 -p10 -Ii
>> This saturates the CAN BUS to 100%
>> on the RPI2 I have no consumer (candump or similar) and the module has
>> just been reloaded (counters are reset).
>> I see the following counters:
>> 7: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10
>> link/can promiscuity 0
>> can <ONE-SHOT,BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>> bitrate 1000000 sample-point 0.750
>> tq 25 prop-seg 14 phase-seg1 15 phase-seg2 10 sjw 1
>> mcp2517fd: tseg1 2..256 tseg2 1..128 sjw 1..128 brp 1..256 brp-inc 1
>> mcp2517fd: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..256 dbrp-inc 1
>> clock 40000000
>> re-started bus-errors arbit-lost error-warn error-pass bus-off
>> 0 0 0 0 0 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
>> RX: bytes packets errors dropped overrun mcast
>> 0 998774 703 4588 0 0
>> TX: bytes packets errors dropped carrier collsns
>> 0 0 0 0 0 0
>> So you see that packets + dropped > transmitted (1000000).
>> In a different setup with 4 bytes of data payload:
>> root@beaglebone:~# cangen can0 -g0.0 -L0 -n 10000 -L4 -p10 -Ii
>> root@beaglebone:~# cangen can0 -g0.0 -L0 -n 100000 -L4 -p10 -Ii
>> I get the following statistics:
>> root@rasp2a:~# ip -details -statistics link show can0
>> 8: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10
>> link/can promiscuity 0
>> can <ONE-SHOT,BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>> bitrate 1000000 sample-point 0.750
>> tq 25 prop-seg 14 phase-seg1 15 phase-seg2 10 sjw 1
>> mcp2517fd: tseg1 2..256 tseg2 1..128 sjw 1..128 brp 1..256 brp-inc 1
>> mcp2517fd: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..256 dbrp-inc 1
>> clock 40000000
>> re-started bus-errors arbit-lost error-warn error-pass bus-off
>> 0 0 0 0 0 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
>> RX: bytes packets errors dropped overrun mcast
>> 440000 110000 0 4343 0 0
>> TX: bytes packets errors dropped carrier collsns
>> 0 0 0 0 0 0
>> Here 1 get 100% reception rates (bytes and packets are exactly what they
>> are supposed to be) but still 0.1% dropped.
>> When CAN FD is configured on the mcp2517 then things look different
>> even on a RPI2:
>> root@beaglebone:~# cangen can0 -g0.0 -L0 -n 1000000 -L0 -p10 -Ii
>> root@rasp2a:~# ip -details -statistics link show can0
>> 13: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 72 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10
>> link/can promiscuity 0
>> can <ONE-SHOT,BERR-REPORTING,FD> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>> bitrate 1000000 sample-point 0.750
>> tq 25 prop-seg 14 phase-seg1 15 phase-seg2 10 sjw 1
>> mcp2517fd: tseg1 2..256 tseg2 1..128 sjw 1..128 brp 1..256 brp-inc 1
>> dbitrate 2000000 dsample-point 0.750
>> dtq 25 dprop-seg 7 dphase-seg1 7 dphase-seg2 5 dsjw 1
>> mcp2517fd: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..256 dbrp-inc 1
>> clock 40000000
>> re-started bus-errors arbit-lost error-warn error-pass bus-off
>> 0 0 0 0 0 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
>> RX: bytes packets errors dropped overrun mcast
>> 0 999840 3 752028 0 0
>> TX: bytes packets errors dropped carrier collsns
>> 0 0 0 0 0 0
>> In this case the number of packets actually lost on the controller is 0.01%
>> But the number of dropped packets on the stack is 75%.
>
> Looking into https://marc.info/?l=devicetree&m=151154853410879&w=2 :
>
> +static int mcp2517fd_can_transform_rx_fd(struct spi_device *spi,
> + struct mcp2517fd_obj_rx *rx)
> +{
> + struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
> + struct canfd_frame *frame;
> + struct sk_buff *skb;
> + u32 flags = rx->header.flags;
> +
> + /* allocate the skb buffer */
> + skb = alloc_canfd_skb(priv->net, &frame);
> + if (!skb) {
> + dev_err(&spi->dev, "cannot allocate RX skb\n");
> + priv->net->stats.rx_dropped++;
> + return -ENOMEM;
>
> Here the packet is counted as dropped - and the function is finished.
>
but it would have been logged to dmesg and would not have
been accounted for as Packets and bytes. (as you found out below)
> + }
> +
> + mcp2517fd_mcpid_to_canid(rx->header.id, flags, &frame->can_id);
> + frame->flags |= (flags & CAN_OBJ_FLAGS_BRS) ? CANFD_BRS : 0;
> + frame->flags |= (flags & CAN_OBJ_FLAGS_ESI) ? CANFD_ESI : 0;
> +
> + frame->len = can_dlc2len((flags & CAN_OBJ_FLAGS_DLC_MASK)
> + >> CAN_OBJ_FLAGS_DLC_SHIFT);
> +
> + memcpy(frame->data, rx->data, frame->len);
> +
> + priv->net->stats.rx_packets++;
>
> Here the packet is counted.
>
> + priv->net->stats.rx_bytes += frame->len;
> +
> + can_led_event(priv->net, CAN_LED_EVENT_RX);
> +
> + netif_rx_ni(skb);
> +
> + return 0;
> +}
>
> I wonder how you can send 1.000.000 frames and then count
> 999.840 + 752.028 = 1.751.868 on the reception side ?!?
well - the driver is submitting the packets to the network
stack and if it can not handle it (say because of memory,…)
then it would still drop it. But as this (probably) happens
in a workqueue this is asynchronous and work may “be left”.
So essentially “internal” drops of the network stack.
>
>> The shorter the interval the lower the dropped count.
>> P.s: For the last test-case here the fifo utilization statistics:
>> root@rasp2a:~# cat /sys/kernel/debug/mcp2517fd-can0/fifo_usage/* | awk '{ C=C+$1; printf "%8i %s\n",C,$0}’
>> Total Count/fifo
>> 384401 384401
>> 674306 289905
>> 960133 285827
>> 990117 29984
>> 994351 4234
>> 996745 2394
>> 997668 923
>> 998217 549
>> 998593 376
>> 998849 256
>> 999059 210
>> 999240 181
>> 999385 145
>> 999508 123
>> 999604 96
>> 999680 76
>> 999745 65
>> 999797 52
>> 999840 43
>> root@rasp2a:~# cat /sys/kernel/debug/mcp2517fd-can0/rx/fifo_count
>> 19
>> root@rasp2a:~# dmesg
>> [77843.229423] mcp2517fd spi0.0: RX MAB overflow
>> [77856.329824] mcp2517fd spi0.0: RX MAB overflow
>> [77883.299221] mcp2517fd spi0.0: RX MAB overflow
>> These dmesg lines correspond with the errors (and would also increase “dropped” by 3)
>
> But doesn't this FIFO behavior just show that the SPI connection is too slow to be able to cope with the CAN FD traffic?
I believe I have understood the source of those MAB errors.
that is what will get fixed in V2.
(It has to do with CS getting deasserted sometimes very late
(> 20us after the last spi clock beat) and that introduces
some locking issues between SPI reads and Controller writes
to the same SRAM word.
So the order of the fifos had to be rearranged, so that
this can not happen…
Martin
^ permalink raw reply
* Re: [PATCH 0/2] Microchip mcp2517fd can controller driver
From: Oliver Hartkopp @ 2017-11-26 12:38 UTC (permalink / raw)
To: kernel; +Cc: Wolfgang Grandegger, Marc Kleine-Budde, linux-can
In-Reply-To: <263BAF25-3F9A-4BE8-8E19-757B3C22A037@martin.sperl.org>
-devicetree
-Mark Rutland
-Rob Herring
On 11/25/2017 03:47 PM, kernel@martin.sperl.org wrote:
>> Unfortunately [PATCH 2/2] only hit the devicetree mailing list but not the linux-can mailing list for the review.
>
> I have no idea why you have not received it, but I have sent all
> of them to the same Recipient list!
>
> I even got a relayed confirmation for all 3 of them:
> 19:35:28.689 2 DEQUEUER [7520159] SMTP(vger.kernel.org)linux-can@vger.kernel.org relayed: relayed via vger.kernel.org
> 19:35:29.823 2 DEQUEUER [7520154] SMTP(vger.kernel.org)linux-can@vger.kernel.org relayed: relayed via vger.kernel.org
> 19:35:31.902 2 DEQUEUER [7520160] SMTP(vger.kernel.org)linux-can@vger.kernel.org relayed: relayed via vger.kernel.org
>
> So they should all have arrived!
In fact patch 2/2 didn't show up on the mailing list archive and in my
inbox ...
https://marc.info/?l=linux-can&r=1&b=201711&w=2
Hm ... obviously no sender issue then :-)
>> Seems you improved the SPI handling here. Would it make sense to separate the SPI-related part of the code to a separate C-file so that the existing mcp251x driver can benefit from these improvements too?
>
> The biggest problem is that the Register-design of the mcp2517fd is totally
> different compared to mcp251x, so most of those “optimizations” for the
> mcp2517 do not apply to the mcp251x (SPI-API wise IMO the mcp2515 is much
> more efficient - better still when having dedicated gpios for the RX lines
> to avoid having to query the status first via spi (reduces latencies
> and thus reduces packet loss/overflow on a saturated CAN bus).
>
> I have a spi_async only version of the mcp251x as well (which has been dormant
> for a long time, as the mcp251x stopped showing interrupt stopped issues...).
Interesting. I was not aware that the mcp251x is working reliable under
heavy loads. I've just seen people providing bcm2835 related hacks/fixes
to make it run with the Raspi.
>>> (still dropps are observed in the can/network stack).
>>
>> Are you sure drops are taking place in the network layer? Can you give me some more details about this statement?
> Well - when I increase net->stat.rx_dropped there is always a
> dev_warn_ratelimited that comes along. Also rx_errors is increased at the
> same time. And I see the drops counters increase while there are no messages
> in dmesg.
Ok - at least this is not network layer related (the code in
linux/net/can) but network driver stuff (linux/drivers/net/can/).
> Also note that the driver is only incrementing rx_packets and rx_bytes only
> when netif_rx_ni(skb) is called.
>
> So the setup is this:
> * raspberry pi 2 with the mcp2517fd
> * beagle bone black
> * 1MHz CAN2.0 bus
>
> Beagle bone black:
> root@beaglebone:~# cangen can0 -g0.0 -L0 -n 1000000 -p10 -Ii
>
> This saturates the CAN BUS to 100%
>
> on the RPI2 I have no consumer (candump or similar) and the module has
> just been reloaded (counters are reset).
>
> I see the following counters:
> 7: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10
> link/can promiscuity 0
> can <ONE-SHOT,BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
> bitrate 1000000 sample-point 0.750
> tq 25 prop-seg 14 phase-seg1 15 phase-seg2 10 sjw 1
> mcp2517fd: tseg1 2..256 tseg2 1..128 sjw 1..128 brp 1..256 brp-inc 1
> mcp2517fd: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..256 dbrp-inc 1
> clock 40000000
> re-started bus-errors arbit-lost error-warn error-pass bus-off
> 0 0 0 0 0 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
> RX: bytes packets errors dropped overrun mcast
> 0 998774 703 4588 0 0
> TX: bytes packets errors dropped carrier collsns
> 0 0 0 0 0 0
>
> So you see that packets + dropped > transmitted (1000000).
>
> In a different setup with 4 bytes of data payload:
> root@beaglebone:~# cangen can0 -g0.0 -L0 -n 10000 -L4 -p10 -Ii
> root@beaglebone:~# cangen can0 -g0.0 -L0 -n 100000 -L4 -p10 -Ii
>
> I get the following statistics:
> root@rasp2a:~# ip -details -statistics link show can0
> 8: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10
> link/can promiscuity 0
> can <ONE-SHOT,BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
> bitrate 1000000 sample-point 0.750
> tq 25 prop-seg 14 phase-seg1 15 phase-seg2 10 sjw 1
> mcp2517fd: tseg1 2..256 tseg2 1..128 sjw 1..128 brp 1..256 brp-inc 1
> mcp2517fd: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..256 dbrp-inc 1
> clock 40000000
> re-started bus-errors arbit-lost error-warn error-pass bus-off
> 0 0 0 0 0 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
> RX: bytes packets errors dropped overrun mcast
> 440000 110000 0 4343 0 0
> TX: bytes packets errors dropped carrier collsns
> 0 0 0 0 0 0
> Here 1 get 100% reception rates (bytes and packets are exactly what they
> are supposed to be) but still 0.1% dropped.
>
> When CAN FD is configured on the mcp2517 then things look different
> even on a RPI2:
> root@beaglebone:~# cangen can0 -g0.0 -L0 -n 1000000 -L0 -p10 -Ii
> root@rasp2a:~# ip -details -statistics link show can0
> 13: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 72 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10
> link/can promiscuity 0
> can <ONE-SHOT,BERR-REPORTING,FD> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
> bitrate 1000000 sample-point 0.750
> tq 25 prop-seg 14 phase-seg1 15 phase-seg2 10 sjw 1
> mcp2517fd: tseg1 2..256 tseg2 1..128 sjw 1..128 brp 1..256 brp-inc 1
> dbitrate 2000000 dsample-point 0.750
> dtq 25 dprop-seg 7 dphase-seg1 7 dphase-seg2 5 dsjw 1
> mcp2517fd: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..256 dbrp-inc 1
> clock 40000000
> re-started bus-errors arbit-lost error-warn error-pass bus-off
> 0 0 0 0 0 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
> RX: bytes packets errors dropped overrun mcast
> 0 999840 3 752028 0 0
> TX: bytes packets errors dropped carrier collsns
> 0 0 0 0 0 0
>
> In this case the number of packets actually lost on the controller is 0.01%
> But the number of dropped packets on the stack is 75%.
Looking into https://marc.info/?l=devicetree&m=151154853410879&w=2 :
+static int mcp2517fd_can_transform_rx_fd(struct spi_device *spi,
+ struct mcp2517fd_obj_rx *rx)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct canfd_frame *frame;
+ struct sk_buff *skb;
+ u32 flags = rx->header.flags;
+
+ /* allocate the skb buffer */
+ skb = alloc_canfd_skb(priv->net, &frame);
+ if (!skb) {
+ dev_err(&spi->dev, "cannot allocate RX skb\n");
+ priv->net->stats.rx_dropped++;
+ return -ENOMEM;
Here the packet is counted as dropped - and the function is finished.
+ }
+
+ mcp2517fd_mcpid_to_canid(rx->header.id, flags, &frame->can_id);
+ frame->flags |= (flags & CAN_OBJ_FLAGS_BRS) ? CANFD_BRS : 0;
+ frame->flags |= (flags & CAN_OBJ_FLAGS_ESI) ? CANFD_ESI : 0;
+
+ frame->len = can_dlc2len((flags & CAN_OBJ_FLAGS_DLC_MASK)
+ >> CAN_OBJ_FLAGS_DLC_SHIFT);
+
+ memcpy(frame->data, rx->data, frame->len);
+
+ priv->net->stats.rx_packets++;
Here the packet is counted.
+ priv->net->stats.rx_bytes += frame->len;
+
+ can_led_event(priv->net, CAN_LED_EVENT_RX);
+
+ netif_rx_ni(skb);
+
+ return 0;
+}
I wonder how you can send 1.000.000 frames and then count
999.840 + 752.028 = 1.751.868 on the reception side ?!?
> The shorter the interval the lower the dropped count.
>
> P.s: For the last test-case here the fifo utilization statistics:
> root@rasp2a:~# cat /sys/kernel/debug/mcp2517fd-can0/fifo_usage/* | awk '{ C=C+$1; printf "%8i %s\n",C,$0}’
> Total Count/fifo
> 384401 384401
> 674306 289905
> 960133 285827
> 990117 29984
> 994351 4234
> 996745 2394
> 997668 923
> 998217 549
> 998593 376
> 998849 256
> 999059 210
> 999240 181
> 999385 145
> 999508 123
> 999604 96
> 999680 76
> 999745 65
> 999797 52
> 999840 43
> root@rasp2a:~# cat /sys/kernel/debug/mcp2517fd-can0/rx/fifo_count
> 19
> root@rasp2a:~# dmesg
> [77843.229423] mcp2517fd spi0.0: RX MAB overflow
> [77856.329824] mcp2517fd spi0.0: RX MAB overflow
> [77883.299221] mcp2517fd spi0.0: RX MAB overflow
>
> These dmesg lines correspond with the errors (and would also increase “dropped” by 3)
>
But doesn't this FIFO behavior just show that the SPI connection is too
slow to be able to cope with the CAN FD traffic?
Best regards,
Oliver
^ permalink raw reply
* RE: [PATCH v4 1/6] can: flexcan: Remodel FlexCAN register r/w APIs for big endian FlexCAN controllers.
From: Pankaj Bansal @ 2017-11-26 2:20 UTC (permalink / raw)
To: Marc Kleine-Budde, wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org,
linux-can-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
mark.rutland-5wv7dgnIgG8@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Varun Sethi, Poonam Aggrwal, Bhupesh Sharma, Sakar Arora
In-Reply-To: <44f37aef-7f45-a7c7-7320-01a62e23fbde-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> -----Original Message-----
> From: Marc Kleine-Budde [mailto:mkl@pengutronix.de]
> Sent: Friday, November 24, 2017 8:33 PM
> To: Pankaj Bansal <pankaj.bansal@nxp.com>; wg@grandegger.com; linux-
> can@vger.kernel.org; robh+dt@kernel.org; mark.rutland@arm.com;
> devicetree@vger.kernel.org
> Cc: Varun Sethi <V.Sethi@nxp.com>; Poonam Aggrwal
> <poonam.aggrwal@nxp.com>; Bhupesh Sharma
> <bhupesh.sharma@freescale.com>; Sakar Arora
> <Sakar.Arora@freescale.com>
> Subject: Re: [PATCH v4 1/6] can: flexcan: Remodel FlexCAN register r/w APIs
> for big endian FlexCAN controllers.
>
> On 11/24/2017 02:22 PM, Pankaj Bansal wrote:
> > The FlexCAN driver assumed that FlexCAN controller is big endian for
> > powerpc architecture and little endian for other architectures.
> >
> > But this may not be the case. FlexCAN controller can be little or big
> > endian on any architecture. For e.g. NXP LS1021A ARM based SOC has big
> > endian FlexCAN controller.
> >
> > Therefore, the driver has been modified to add a provision for both
> > types of controllers using an additional device tree property. On a
> > "fsl,p1010-flexcan" device BE is default, on all other devices LE is.
> >
> > Big Endian controllers should have "big-endian" set in the device tree.
> > check "Documentation/devicetree/bindings/net/can/fsl-flexcan.txt" for
> > usage.
> >
> > This is the standard practice followed in linux. for more info check:
> > Documentation/devicetree/bindings/common-properties.txt
> >
> > Signed-off-by: Pankaj Bansal <pankaj.bansal@nxp.com>
> > Signed-off-by: Bhupesh Sharma <bhupesh.sharma@freescale.com>
> > Signed-off-by: Sakar Arora <Sakar.Arora@freescale.com>
> > Reviewed-by: Zhengxiong Jin <Jason.Jin@freescale.com>
> > Reviewed-by: Poonam Aggrwal <poonam.aggrwal@nxp.com>
> > ---
> > Changes in v4:
> > - Merged device tree changes and driver changes in one series
> > Changes in v3:
> > - Added fsl,imx25-flexcan, fsl,imx35-flexcan and fsl,imx53-flexcan
> > support to the driver.
> > - Modified patch deciption to state default endianness followed and
> > to include fsl-flexcan.txt referance.
> > Changes in v2:
> > - Modified patch deciption to include common-properties.txt reference.
> > - Reorder the LE/BE read/write APIs for better readability of code
> > - Added an exception to force BE API selection, for powerpc based
> platform
> > P1010. This ensures that new linux kernel works with old P1010
> > device-tree, while future powerpc platforms that use big endian
> > FlexCAN controller need to specify big-endian in device tree in
> > FlexCAN node.
> > - Tested on P1010 after backporting to freescale sdk 1.4 linux, without
> > any change in device-tree.
> > - Tested on NXP LS1021A arm based platform.
> >
> > drivers/net/can/flexcan.c | 233 ++++++++++++++++++++----------------
> > 1 file changed, 131 insertions(+), 102 deletions(-)
> >
> > diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
> > index a13a489..4c873fb 100644
> > --- a/drivers/net/can/flexcan.c
> > +++ b/drivers/net/can/flexcan.c
>
> [...]
> I think this will select LE for non DT devices, right?
>
Yes. As per below code snippet:
static struct property *__of_find_property(const struct device_node *np,
const char *name, int *lenp)
{
struct property *pp;
if (!np)
return NULL;
....
}
If no device node is present null is returned.
So we select le as default.
> > + if (of_property_read_bool(pdev->dev.of_node, "big-endian")) {
> > + priv->read = flexcan_read_be;
> > + priv->write = flexcan_write_be;
> > + } else {
> > + if (of_device_is_compatible(pdev->dev.of_node,
> > + "fsl,p1010-flexcan")) {
> > + priv->read = flexcan_read_be;
> > + priv->write = flexcan_write_be;
> > + } else {
> > + priv->read = flexcan_read_le;
> > + priv->write = flexcan_write_le;
> > + }
> > + }
> > +
> > priv->can.clock.freq = clock_freq;
> > priv->can.bittiming_const = &flexcan_bittiming_const;
> > priv->can.do_set_mode = flexcan_set_mode;
> >
>
> Marc
>
> --
> Pengutronix e.K. | Marc Kleine-Budde |
> Industrial Linux Solutions | Phone: +49-231-2826-924 |
> Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
> Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
^ permalink raw reply
* Re: [PATCH 0/2] Microchip mcp2517fd can controller driver
From: kernel @ 2017-11-25 14:47 UTC (permalink / raw)
To: Oliver Hartkopp
Cc: Wolfgang Grandegger, Marc Kleine-Budde, Rob Herring, Mark Rutland,
linux-can, devicetree
In-Reply-To: <3d355c51-09a3-40b8-46bf-8437e26a95f3@hartkopp.net>
Hi Oliver!
> On 25.11.2017, at 13:03, Oliver Hartkopp <socketcan@hartkopp.net> wrote:
>
> Hello Martin,
>
> thanks for the contribution!
>
> Unfortunately [PATCH 2/2] only hit the devicetree mailing list but not the linux-can mailing list for the review.
I have no idea why you have not received it, but I have sent all
of them to the same Recipient list!
I even got a relayed confirmation for all 3 of them:
19:35:28.689 2 DEQUEUER [7520159] SMTP(vger.kernel.org)linux-can@vger.kernel.org relayed: relayed via vger.kernel.org
19:35:29.823 2 DEQUEUER [7520154] SMTP(vger.kernel.org)linux-can@vger.kernel.org relayed: relayed via vger.kernel.org
19:35:31.902 2 DEQUEUER [7520160] SMTP(vger.kernel.org)linux-can@vger.kernel.org relayed: relayed via vger.kernel.org
So they should all have arrived!
>
> Btw. I already have two questions from the description:
>
> On 11/24/2017 07:35 PM, kernel@martin.sperl.org wrote:
>
> (..)
>
>> The driver has been heavily optimized so that it can handle
>> a 100% utilized 1MHz Can-bus (with 11 bit can frames with DLC=0)
>> even on less powerfull SOCs like the raspberry pi 1 without
>> dropping frames due to driver/spi latencies
>
> (..)
>
> > The driver implements a lock-less design for transmissions
> > making use instead of prepared spi messages submitted via spi_async
> > for transmission in the start_xmit_start code without requireing
> > an extra workqueue and the corresponding latencies.
>
> Seems you improved the SPI handling here. Would it make sense to separate the SPI-related part of the code to a separate C-file so that the existing mcp251x driver can benefit from these improvements too?
The biggest problem is that the Register-design of the mcp2517fd is totally
different compared to mcp251x, so most of those “optimizations” for the
mcp2517 do not apply to the mcp251x (SPI-API wise IMO the mcp2515 is much
more efficient - better still when having dedicated gpios for the RX lines
to avoid having to query the status first via spi (reduces latencies
and thus reduces packet loss/overflow on a saturated CAN bus).
I have a spi_async only version of the mcp251x as well (which has been dormant
for a long time, as the mcp251x stopped showing interrupt stopped issues...).
>
>> (still dropps are observed in the can/network stack).
>
> Are you sure drops are taking place in the network layer? Can you give me some more details about this statement?
Well - when I increase net->stat.rx_dropped there is always a
dev_warn_ratelimited that comes along. Also rx_errors is increased at the
same time. And I see the drops counters increase while there are no messages
in dmesg.
Also note that the driver is only incrementing rx_packets and rx_bytes only
when netif_rx_ni(skb) is called.
So the setup is this:
* raspberry pi 2 with the mcp2517fd
* beagle bone black
* 1MHz CAN2.0 bus
Beagle bone black:
root@beaglebone:~# cangen can0 -g0.0 -L0 -n 1000000 -p10 -Ii
This saturates the CAN BUS to 100%
on the RPI2 I have no consumer (candump or similar) and the module has
just been reloaded (counters are reset).
I see the following counters:
7: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10
link/can promiscuity 0
can <ONE-SHOT,BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
bitrate 1000000 sample-point 0.750
tq 25 prop-seg 14 phase-seg1 15 phase-seg2 10 sjw 1
mcp2517fd: tseg1 2..256 tseg2 1..128 sjw 1..128 brp 1..256 brp-inc 1
mcp2517fd: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..256 dbrp-inc 1
clock 40000000
re-started bus-errors arbit-lost error-warn error-pass bus-off
0 0 0 0 0 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
RX: bytes packets errors dropped overrun mcast
0 998774 703 4588 0 0
TX: bytes packets errors dropped carrier collsns
0 0 0 0 0 0
So you see that packets + dropped > transmitted (1000000).
In a different setup with 4 bytes of data payload:
root@beaglebone:~# cangen can0 -g0.0 -L0 -n 10000 -L4 -p10 -Ii
root@beaglebone:~# cangen can0 -g0.0 -L0 -n 100000 -L4 -p10 -Ii
I get the following statistics:
root@rasp2a:~# ip -details -statistics link show can0
8: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10
link/can promiscuity 0
can <ONE-SHOT,BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
bitrate 1000000 sample-point 0.750
tq 25 prop-seg 14 phase-seg1 15 phase-seg2 10 sjw 1
mcp2517fd: tseg1 2..256 tseg2 1..128 sjw 1..128 brp 1..256 brp-inc 1
mcp2517fd: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..256 dbrp-inc 1
clock 40000000
re-started bus-errors arbit-lost error-warn error-pass bus-off
0 0 0 0 0 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
RX: bytes packets errors dropped overrun mcast
440000 110000 0 4343 0 0
TX: bytes packets errors dropped carrier collsns
0 0 0 0 0 0
Here 1 get 100% reception rates (bytes and packets are exactly what they
are supposed to be) but still 0.1% dropped.
When CAN FD is configured on the mcp2517 then things look different
even on a RPI2:
root@beaglebone:~# cangen can0 -g0.0 -L0 -n 1000000 -L0 -p10 -Ii
root@rasp2a:~# ip -details -statistics link show can0
13: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 72 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 10
link/can promiscuity 0
can <ONE-SHOT,BERR-REPORTING,FD> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
bitrate 1000000 sample-point 0.750
tq 25 prop-seg 14 phase-seg1 15 phase-seg2 10 sjw 1
mcp2517fd: tseg1 2..256 tseg2 1..128 sjw 1..128 brp 1..256 brp-inc 1
dbitrate 2000000 dsample-point 0.750
dtq 25 dprop-seg 7 dphase-seg1 7 dphase-seg2 5 dsjw 1
mcp2517fd: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..256 dbrp-inc 1
clock 40000000
re-started bus-errors arbit-lost error-warn error-pass bus-off
0 0 0 0 0 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
RX: bytes packets errors dropped overrun mcast
0 999840 3 752028 0 0
TX: bytes packets errors dropped carrier collsns
0 0 0 0 0 0
In this case the number of packets actually lost on the controller is 0.01%
But the number of dropped packets on the stack is 75%.
The shorter the interval the lower the dropped count.
Ciao,
Martin
P.s: For the last test-case here the fifo utilization statistics:
root@rasp2a:~# cat /sys/kernel/debug/mcp2517fd-can0/fifo_usage/* | awk '{ C=C+$1; printf "%8i %s\n",C,$0}’
Total Count/fifo
384401 384401
674306 289905
960133 285827
990117 29984
994351 4234
996745 2394
997668 923
998217 549
998593 376
998849 256
999059 210
999240 181
999385 145
999508 123
999604 96
999680 76
999745 65
999797 52
999840 43
root@rasp2a:~# cat /sys/kernel/debug/mcp2517fd-can0/rx/fifo_count
19
root@rasp2a:~# dmesg
[77843.229423] mcp2517fd spi0.0: RX MAB overflow
[77856.329824] mcp2517fd spi0.0: RX MAB overflow
[77883.299221] mcp2517fd spi0.0: RX MAB overflow
These dmesg lines correspond with the errors (and would also increase “dropped” by 3)
^ permalink raw reply
* Re: [PATCH 0/2] Microchip mcp2517fd can controller driver
From: Oliver Hartkopp @ 2017-11-25 12:03 UTC (permalink / raw)
To: kernel
Cc: Wolfgang Grandegger, Marc Kleine-Budde, Rob Herring, Mark Rutland,
linux-can, devicetree
In-Reply-To: <20171124183509.12810-1-kernel@martin.sperl.org>
Hello Martin,
thanks for the contribution!
Unfortunately [PATCH 2/2] only hit the devicetree mailing list but not
the linux-can mailing list for the review.
Btw. I already have two questions from the description:
On 11/24/2017 07:35 PM, kernel@martin.sperl.org wrote:
(..)
> The driver has been heavily optimized so that it can handle
> a 100% utilized 1MHz Can-bus (with 11 bit can frames with DLC=0)
> even on less powerfull SOCs like the raspberry pi 1 without
> dropping frames due to driver/spi latencies
(..)
> The driver implements a lock-less design for transmissions
> making use instead of prepared spi messages submitted via spi_async
> for transmission in the start_xmit_start code without requireing
> an extra workqueue and the corresponding latencies.
Seems you improved the SPI handling here. Would it make sense to
separate the SPI-related part of the code to a separate C-file so that
the existing mcp251x driver can benefit from these improvements too?
> (still dropps are observed in the can/network stack).
Are you sure drops are taking place in the network layer? Can you give
me some more details about this statement?
Best regards,
Oliver
^ permalink raw reply
* Re: [PATCH v2 2/2] can: flexcan: adding platform specific details for LS1021A
From: Wolfgang Grandegger @ 2017-11-24 19:19 UTC (permalink / raw)
To: Mirza Krak
Cc: Marc Kleine-Budde, ZHU Yi (ST-FIR/ENG1-Zhu), Pankaj Bansal,
linux-can@vger.kernel.org, Varun Sethi, Poonam Aggrwal,
Stefan Agner
In-Reply-To: <CALw8SCWFDt58x6zrbnoXuX=DkG0+OZtXnCO5CdK+MQb2JyubuA@mail.gmail.com>
Hello Mirza,
Am 24.11.2017 um 17:02 schrieb Mirza Krak:
> 2017-11-23 22:05 GMT+01:00 Wolfgang Grandegger <wg@grandegger.com>:
>> Hello Mirza,
>>
>> Am 23.11.2017 um 21:17 schrieb Mirza Krak:
>>>
>>> 2017-11-23 21:12 GMT+01:00 Mirza Krak <mirza.krak@gmail.com>:
>>>>
>>>> On Nov 22, 2017 13:00, "Marc Kleine-Budde" <mkl@pengutronix.de> wrote:
>>>>
>>>
>>> < snip >
>>>
>>>>
>>>> Cc'ed Stefan Agner - maybe he has access to this SoC.
>>>>
>>>>> @Marc, @Wolfgang,
>>>>> What do you think? Or shall we fix first and then wait?
>>>>
>>>>
>>>> Let's wait if Stefan can test, otherwise just fix.
>>>>
>>>>
>>>> I have access to an vf610 and can test CAN stuff.
>>>>
>>>> What is it more specifically we want to test? I read the thread but not
>>>> completely clear to me.
>>
>>
>> We want to know how the reporting of CAN error state changes "error-active"
>> -> "warning" -> "error-passive" -> "bus-off" and back is working on that
>> hardware.
>>
>> You can test such state changes as show below. First setup the interface and
>> start a session calling "candump" to report the state changes:
>>
>> # ip link set can0 up type can bitrate 500000 restart-ms 100
>> # candump -td -e any,0:0,#FFFFFFFF
>>
>> Then
>>
>> 1. disconnect the cable and send messages with "cangen"
>>
>> # cangen -i can0
>>
>> "candump" should then report the error state changes "error-active" ->
>> "warning" -> "error-passive". If you don't see "error-passive", the hardware
>> needs the FLEXCAN_QUIRK_BROKEN_PERR_STATE as well. Then reconnect the cable
>> and send messages till the "error active" state is reached (again with
>> "cangen").
>
> This was the result of this test:
>
> root@colibri-vf:~# ./candump -td -e can0,0:0,#FFFFFFFF
> (000.000000) can0 0C8 [8] E7 D5 75 20 1C F8 AA 6D
> (000.200201) can0 7CD [8] 58 5F B1 1E D7 AB 3C 15
> (000.200123) can0 7FD [8] EC 43 EF 11 BB 76 50 4F
> (000.200119) can0 20D [8] 64 E0 A6 74 A2 A4 22 42
> (000.200115) can0 226 [7] CB 4D 12 46 F3 37 F1
> (000.200000) can0 42F [0]
> (000.200245) can0 0F9 [8] AD 61 16 77 0A 64 50 5C
> (000.199983) can0 76E [0]
> (000.200206) can0 3CB [6] 53 28 03 34 98 43
> (000.200152) can0 230 [8] 70 EF 66 38 2D BA 5D 4D
> (000.200106) can0 568 [8] E8 30 AE 1C 75 77 4E 5E
> (000.200274) can0 20000004 [8] 00 04 00 00 00 00 00 00 ERRORFRAME
> controller-problem{rx-error-warning}
>
> and the statistics
>
> root@colibri-vf:~# ip -d -s link show can0
> 2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state
> UNKNOWN mode DEFAULT group default qlen 10
> link/can promiscuity 0
> can state ERROR-WARNING (berr-counter tx 0 rx 128) restart-ms 100
> bitrate 496240 sample-point 0.857
> tq 143 prop-seg 5 phase-seg1 6 phase-seg2 2 sjw 1
> flexcan: tseg1 4..16 tseg2 2..8 sjw 1..4 brp 1..256 brp-inc 1
> clock 83368421
> re-started bus-errors arbit-lost error-warn error-pass bus-off
> 0 0 0 1 0 0 numtxqueues 1
> RX: bytes packets errors dropped overrun mcast
> 8 1 0 0 0 0
> TX: bytes packets errors dropped carrier collsns
> 69 11 0 0 0 0
>
> I ran out of time as wanted to test a bit more as I was confused that
> it is showing RX errors on the first test? I was not able to get it
> back to ERROR-ACTIVE from ERROR-WARNING either. But posted result
> anyway
The received error messages are counted as well.
>> 2. The second test is quite similar. Instead of disconnecting the cable,
>> short-circuit the CAN low and high lines. The device should then go to
>> "bus-off".
>
> And this one:
>
> root@colibri-vf:~# ./candump -td -e any,0:0,#FFFFFFFF
> (000.000000) can0 20000004 [8] 00 20 00 00 00 00 00 00 ERRORFRAME
> controller-problem{tx-error-passive}
> (000.000809) can0 20000040 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
> bus-off
> (000.109797) can0 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
> restarted-after-bus-off
> (000.100551) can0 20000004 [8] 00 20 00 00 00 00 00 00 ERRORFRAME
> controller-problem{tx-error-passive}
> (000.000823) can0 20000040 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
> bus-off
> (000.108644) can0 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
> restarted-after-bus-off
> (000.090692) can0 20000004 [8] 00 08 00 00 00 00 00 00 ERRORFRAME
> controller-problem{tx-error-warning}
> (000.000797) can0 20000040 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
> bus-off
>
> Will try again next week when I am back at the office.
>
> I ran my tests on 4.14 kernel.
This Flexcan core needs the FLEXCAN_QUIRK_BROKEN_PERR_STATE as well,
like all other cores. Adding that quirk for the vf610 will cure the
problems.
Thanks for testing.
Wolfgang.
^ permalink raw reply
* [PATCH 2/2] can: mcp2517fd: Add Microchip mcp2517fd CAN FD driver
From: kernel-TqfNSX0MhmxHKSADF0wUEw @ 2017-11-24 18:35 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde, Rob Herring, Mark Rutland,
linux-can-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Martin Sperl
In-Reply-To: <20171124183509.12810-1-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
This patch adds support for the Microchip mcp2517fd CAN-FD controller.
The mcp2517fd is capable of transmitting and receiving standard data
frames, extended data frames, remote frames and Can-FD frames.
The mcp2517fd interfaces with the host over SPI.
Datasheet:
* http://ww1.microchip.com/downloads/en/DeviceDoc/20005688A.pdf
Reference manual:
* http://ww1.microchip.com/downloads/en/DeviceDoc/20005678A.pdf
Signed-off-by: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
---
drivers/net/can/spi/Kconfig | 6 +
drivers/net/can/spi/Makefile | 1 +
drivers/net/can/spi/mcp2517fd.c | 3733 +++++++++++++++++++++++++++++++++++++++
3 files changed, 3740 insertions(+)
create mode 100644 drivers/net/can/spi/mcp2517fd.c
diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig
index 8f2e0dd7b756..03c0176ccfbf 100644
--- a/drivers/net/can/spi/Kconfig
+++ b/drivers/net/can/spi/Kconfig
@@ -13,4 +13,10 @@ config CAN_MCP251X
---help---
Driver for the Microchip MCP251x SPI CAN controllers.
+config CAN_MCP2517FD
+ tristate "Microchip MCP2517FD SPI CAN controllers"
+ depends on HAS_DMA
+ ---help---
+ Driver for the Microchip MCP2517FD SPI CAN controllers.
+
endmenu
diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile
index f59fa3731073..7a4ed1998661 100644
--- a/drivers/net/can/spi/Makefile
+++ b/drivers/net/can/spi/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_CAN_HI311X) += hi311x.o
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
+obj-$(CONFIG_CAN_MCP2517FD) += mcp2517fd.o
diff --git a/drivers/net/can/spi/mcp2517fd.c b/drivers/net/can/spi/mcp2517fd.c
new file mode 100644
index 000000000000..11a3f7e7daa8
--- /dev/null
+++ b/drivers/net/can/spi/mcp2517fd.c
@@ -0,0 +1,3733 @@
+/*
+ * CAN bus driver for Microchip 2517FD CAN Controller with SPI Interface
+ *
+ * Copyright 2017 Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ *
+ */
+
+#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/debugfs.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/jiffies.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/slab.h>
+#include <linux/sort.h>
+#include <linux/spi/spi.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/consumer.h>
+
+#define DEVICE_NAME "mcp2517fd"
+
+/* device description and rational:
+ *
+ * the mcp2517fd is a CanFD controller that also supports can2.0 only
+ * modes.
+ * It is connected via spi to the host and requires at minimum a single
+ * irq line in addition to the SPI lines - it is not mentioned explicitly
+ * in the documentation but in principle SPI 3-wire should be possible.
+ *
+ * The clock connected is typically 4MHz, 20MHz or 40MHz.
+ * For the 4MHz clock the controller contains 10x PLL circuitry.
+ *
+ * The controller itself has 2KB or ECC-SRAM for data.
+ * It also has 32 FIFOs (of up to 32 CAN-frames).
+ * There are 4 Fifo types which can get configured:
+ * * TEF - Transmission Event Fifo - which consumes FIFO 0
+ * even if it is not configured
+ * * Tansmission Queue - for up to 32 Frames.
+ * this queue reorders CAN frames to get transmitted following the
+ * typical CAN dominant/recessive rules on the can bus itself.
+ * This FIFO is optional.
+ * * TX FIFO: generic TX fifos that can contain arbitrary data
+ * and which come with a configurable priority for transmission
+ * It is also possible to have the Controller automatically trigger
+ * a transfer when a Filter Rule for a RTR frame matches.
+ * Each of these fifos in principle can get configured for distinct
+ * dlc sizes (8 thru 64 bytes)
+ * * RX FIFO: generic RX fifo which is filled via filter-rules.
+ * Each of these fifos in principle can get configured for distinct
+ * dlc sizes (8 thru 64 bytes)
+ * Unfortunately there is no filter rule that would allow triggering
+ * on different frame sizes, so for all practical purposes the
+ * RX fifos have to be of the same size (unless one wants to experience
+ * lost data).
+ * When a Can Frame is transmitted fromthe TX Queue or an individual
+ * TX FIFO then a small TEF Frame can get added to the TEF FIFO queue
+ * to log the Transmission of the frame - this includes ID, Flags
+ * (including a custom identifier/index) .
+ *
+ * The controller provides an optional free running counter with a divider
+ * for timestamping of RX frames as well as for TEF entries.
+ *
+ * Driver Implementation details and rational:
+ * * The whole driver has been designed to give best performance
+ * and as little packet loss as possible with 1MHZ Can frames with DLC=0
+ * on small/slow devices like the Raspberry Pi 1
+ * * This means that some optimizations for full duplex communication
+ * have been implemented to avoid CPU introduced latencies
+ * (especially for spi_write_then_read cases) - this only applies to
+ * 4 wire SPI busses.
+ * * Due to the fact that the TXQ does reorder Can-Frames we do not make
+ * use of it to avoid unexpected behaviour (say when running a
+ * firmware upgrade via Can)
+ * * this means we use individual TX-fifos with a given priority and
+ * we have to wait until all the TX fifos have been transmitted before
+ * we can restart the networking queue to avoid reordering the frames on
+ * the Can bus itself.
+ * Still we can transmit a transmit only Duty-cycle of 66% to 90% on the
+ * Can bus (at 1MHz).
+ * The scaling factors here are:
+ * * Can bus speed - lower Speeds increase Duty-cycle
+ * * SPI Clock Rate - higher speeds increase duty-cycle
+ * * CPU speed + SPI implementation - reduces latencies between transfers
+ * * There is a module parameter that allows the modification of the
+ * number of tx_fifos, which is by default 7.
+ * * The driver offers some module parameters that allow to control the use
+ * of some optimizations (prefer reading more data than necessary instead
+ * of multiple SPI transfers - the idea here is that this way we may
+ * allow the SPI-controller to use DMA instead of programmed IO to
+ * limit latencies/number of interrupts)
+ * When we have to read multiple RX frames in CanFD mode:
+ * * we allow reading all 64 bytes of payload even if DLC <=8
+ * this mode is used in Can2.0 only mode by default and can not get
+ * disabled (SRAM Reads have to be a multiple of 4 bytes anyway)
+ * * Releasing/freeing the RX queue requires writing of 1 byte per fifo.
+ * unfortunately these 32-bit registers are not ajacent to each other,
+ * so that for 2 consecutive RX Frames instead of writing 1 byte per
+ * fifo (with protocol overhead of 2 bytes - so a total of 6 bytes in
+ * 2 transfers) we transmit 13 bytes (with a protocol overhead of 2 -
+ * so a total of 15 bytes)
+ * This optimization is only enabled by a module parameter.
+ * * we use TEF + time stamping to record the transmitted frames
+ * including their timestamp - we use this to order TX and RX frames
+ * when submitting them to the network stack.
+ * * due to the inability to "filter" based on DLC sizes we have to use
+ * a common FIFO size. This is 8 bytes for Can2.0 and 64 bytes for CanFD.
+ * * the driver tries to detect the Controller only by reading registers,
+ * but there are circumstances (e.g. after a crashed driver) where we
+ * have to "blindly" configure the clock rate to get the controller to
+ * respond correctly.
+ * * There is one situation where the controller will require a full POR
+ * (total power off) to recover from a bad Clock configuration.
+ * This happens when the wrong clock is configured in the device tree
+ * (say 4MHz are configured, while 20 or 40MHz are used)
+ * in such a situation the driver tries to enable the PLL, which will
+ * never synchronize and the controller becomes unresponsive to further
+ * spi requests until a POR.
+ */
+
+#define MCP2517FD_OST_DELAY_MS 3
+#define MCP2517FD_MIN_CLOCK_FREQUENCY 1000000
+#define MCP2517FD_MAX_CLOCK_FREQUENCY 40000000
+#define MCP2517FD_PLL_MULTIPLIER 10
+#define MCP2517FD_AUTO_PLL_MAX_CLOCK_FREQUENCY \
+ (MCP2517FD_MAX_CLOCK_FREQUENCY / MCP2517FD_PLL_MULTIPLIER)
+#define MCP2517FD_SCLK_DIVIDER 2
+
+#define MCP2517FD_OSC_POLLING_JIFFIES (HZ / 2)
+
+#define TX_ECHO_SKB_MAX 32
+
+#define INSTRUCTION_RESET 0x0000
+#define INSTRUCTION_READ 0x3000
+#define INSTRUCTION_WRITE 0x2000
+#define INSTRUCTION_READ_CRC 0xB000
+#define INSTRUCTION_WRITE_CRC 0xA000
+#define INSTRUCTION_WRITE_SAVE 0xC000
+
+#define ADDRESS_MASK 0x0fff
+
+#define MCP2517FD_SFR_BASE(x) (0xE00 + (x))
+#define MCP2517FD_OSC MCP2517FD_SFR_BASE(0x00)
+# define MCP2517FD_OSC_PLLEN BIT(0)
+# define MCP2517FD_OSC_OSCDIS BIT(2)
+# define MCP2517FD_OSC_SCLKDIV BIT(4)
+# define MCP2517FD_OSC_CLKODIV_BITS 2
+# define MCP2517FD_OSC_CLKODIV_SHIFT 5
+# define MCP2517FD_OSC_CLKODIV_MASK \
+ GENMASK(MCP2517FD_OSC_CLKODIV_SHIFT \
+ + MCP2517FD_OSC_CLKODIV_BITS - 1, \
+ MCP2517FD_OSC_CLKODIV_SHIFT)
+# define MCP2517FD_OSC_CLKODIV_10 3
+# define MCP2517FD_OSC_CLKODIV_4 2
+# define MCP2517FD_OSC_CLKODIV_2 1
+# define MCP2517FD_OSC_CLKODIV_1 0
+# define MCP2517FD_OSC_PLLRDY BIT(8)
+# define MCP2517FD_OSC_OSCRDY BIT(10)
+# define MCP2517FD_OSC_SCLKRDY BIT(12)
+#define MCP2517FD_IOCON MCP2517FD_SFR_BASE(0x04)
+# define MCP2517FD_IOCON_TRIS0 BIT(0)
+# define MCP2517FD_IOCON_TRIS1 BIT(1)
+# define MCP2517FD_IOCON_XSTBYEN BIT(6)
+# define MCP2517FD_IOCON_LAT0 BIT(8)
+# define MCP2517FD_IOCON_LAT1 BIT(9)
+# define MCP2517FD_IOCON_GPIO0 BIT(16)
+# define MCP2517FD_IOCON_GPIO1 BIT(17)
+# define MCP2517FD_IOCON_PM0 BIT(24)
+# define MCP2517FD_IOCON_PM1 BIT(25)
+# define MCP2517FD_IOCON_TXCANOD BIT(28)
+# define MCP2517FD_IOCON_SOF BIT(29)
+# define MCP2517FD_IOCON_INTOD BIT(29)
+#define MCP2517FD_CRC MCP2517FD_SFR_BASE(0x08)
+# define MCP2517FD_CRC_MASK GENMASK(15, 0)
+# define MCP2517FD_CRC_CRCERRIE BIT(16)
+# define MCP2517FD_CRC_FERRIE BIT(17)
+# define MCP2517FD_CRC_CRCERRIF BIT(24)
+# define MCP2517FD_CRC_FERRIF BIT(25)
+#define MCP2517FD_ECCCON MCP2517FD_SFR_BASE(0x0C)
+# define MCP2517FD_ECCCON_ECCEN BIT(0)
+# define MCP2517FD_ECCCON_SECIE BIT(1)
+# define MCP2517FD_ECCCON_DEDIE BIT(2)
+# define MCP2517FD_ECCCON_PARITY_BITS 6
+# define MCP2517FD_ECCCON_PARITY_SHIFT 8
+# define MCP2517FD_ECCCON_PARITY_MASK \
+ GENMASK(MCP2517FD_ECCCON_PARITY_SHIFT \
+ + MCP2517FD_ECCCON_PARITY_BITS - 1, \
+ MCP2517FD_ECCCON_PARITY_SHIFT)
+#define MCP2517FD_ECCSTAT MCP2517FD_SFR_BASE(0x10)
+# define MCP2517FD_ECCSTAT_SECIF BIT(1)
+# define MCP2517FD_ECCSTAT_DEDIF BIT(2)
+# define MCP2517FD_ECCSTAT_ERRADDR_SHIFT 8
+# define MCP2517FD_ECCSTAT_ERRADDR_MASK \
+ GENMASK(MCP2517FD_ECCSTAT_ERRADDR_SHIFT + 11, \
+ MCP2517FD_ECCSTAT_ERRADDR_SHIFT)
+
+#define CAN_SFR_BASE(x) (0x000 + (x))
+#define CAN_CON CAN_SFR_BASE(0x00)
+# define CAN_CON_DNCNT_BITS 5
+# define CAN_CON_DNCNT_SHIFT 0
+# define CAN_CON_DNCNT_MASK \
+ GENMASK(CAN_CON_DNCNT_SHIFT + CAN_CON_DNCNT_BITS - 1, \
+ CAN_CON_DNCNT_SHIFT)
+# define CAN_CON_ISOCRCEN BIT(5)
+# define CAN_CON_PXEDIS BIT(6)
+# define CAN_CON_WAKFIL BIT(8)
+# define CAN_CON_WFT_BITS 2
+# define CAN_CON_WFT_SHIFT 9
+# define CAN_CON_WFT_MASK \
+ GENMASK(CAN_CON_WFT_SHIFT + CAN_CON_WFT_BITS - 1, \
+ CAN_CON_WFT_SHIFT)
+# define CAN_CON_BUSY BIT(11)
+# define CAN_CON_BRSDIS BIT(12)
+# define CAN_CON_RTXAT BIT(16)
+# define CAN_CON_ESIGM BIT(17)
+# define CAN_CON_SERR2LOM BIT(18)
+# define CAN_CON_STEF BIT(19)
+# define CAN_CON_TXQEN BIT(20)
+# define CAN_CON_OPMODE_BITS 3
+# define CAN_CON_OPMOD_SHIFT 21
+# define CAN_CON_OPMOD_MASK \
+ GENMASK(CAN_CON_OPMOD_SHIFT + CAN_CON_OPMODE_BITS - 1, \
+ CAN_CON_OPMOD_SHIFT)
+# define CAN_CON_REQOP_BITS 3
+# define CAN_CON_REQOP_SHIFT 24
+# define CAN_CON_REQOP_MASK \
+ GENMASK(CAN_CON_REQOP_SHIFT + CAN_CON_REQOP_BITS - 1, \
+ CAN_CON_REQOP_SHIFT)
+# define CAN_CON_MODE_MIXED 0
+# define CAN_CON_MODE_SLEEP 1
+# define CAN_CON_MODE_INTERNAL_LOOPBACK 2
+# define CAN_CON_MODE_LISTENONLY 3
+# define CAN_CON_MODE_CONFIG 4
+# define CAN_CON_MODE_EXTERNAL_LOOPBACK 5
+# define CAN_CON_MODE_CAN2_0 6
+# define CAN_CON_MODE_RESTRICTED 7
+# define CAN_CON_ABAT BIT(27)
+# define CAN_CON_TXBWS_BITS 3
+# define CAN_CON_TXBWS_SHIFT 28
+# define CAN_CON_TXBWS_MASK \
+ GENMASK(CAN_CON_TXBWS_SHIFT + CAN_CON_TXBWS_BITS - 1, \
+ CAN_CON_TXBWS_SHIFT)
+# define CAN_CON_DEFAULT \
+ (CAN_CON_ISOCRCEN | \
+ CAN_CON_PXEDIS | \
+ CAN_CON_WAKFIL | \
+ (3 << CAN_CON_WFT_SHIFT) | \
+ CAN_CON_STEF | \
+ CAN_CON_TXQEN | \
+ (CAN_CON_MODE_CONFIG << CAN_CON_OPMOD_SHIFT) | \
+ (CAN_CON_MODE_CONFIG << CAN_CON_REQOP_SHIFT))
+# define CAN_CON_DEFAULT_MASK \
+ (CAN_CON_DNCNT_MASK | \
+ CAN_CON_ISOCRCEN | \
+ CAN_CON_PXEDIS | \
+ CAN_CON_WAKFIL | \
+ CAN_CON_WFT_MASK | \
+ CAN_CON_BRSDIS | \
+ CAN_CON_RTXAT | \
+ CAN_CON_ESIGM | \
+ CAN_CON_SERR2LOM | \
+ CAN_CON_STEF | \
+ CAN_CON_TXQEN | \
+ CAN_CON_OPMOD_MASK | \
+ CAN_CON_REQOP_MASK | \
+ CAN_CON_ABAT | \
+ CAN_CON_TXBWS_MASK)
+#define CAN_NBTCFG CAN_SFR_BASE(0x04)
+# define CAN_NBTCFG_SJW_BITS 7
+# define CAN_NBTCFG_SJW_SHIFT 0
+# define CAN_NBTCFG_SJW_MASK \
+ GENMASK(CAN_NBTCFG_SJW_SHIFT + CAN_NBTCFG_SJW_BITS - 1, \
+ CAN_NBTCFG_SJW_SHIFT)
+# define CAN_NBTCFG_TSEG2_BITS 7
+# define CAN_NBTCFG_TSEG2_SHIFT 8
+# define CAN_NBTCFG_TSEG2_MASK \
+ GENMASK(CAN_NBTCFG_TSEG2_SHIFT + CAN_NBTCFG_TSEG2_BITS - 1, \
+ CAN_NBTCFG_TSEG2_SHIFT)
+# define CAN_NBTCFG_TSEG1_BITS 8
+# define CAN_NBTCFG_TSEG1_SHIFT 16
+# define CAN_NBTCFG_TSEG1_MASK \
+ GENMASK(CAN_NBTCFG_TSEG1_SHIFT + CAN_NBTCFG_TSEG1_BITS - 1, \
+ CAN_NBTCFG_TSEG1_SHIFT)
+# define CAN_NBTCFG_BRP_BITS 8
+# define CAN_NBTCFG_BRP_SHIFT 24
+# define CAN_NBTCFG_BRP_MASK \
+ GENMASK(CAN_NBTCFG_BRP_SHIFT + CAN_NBTCFG_BRP_BITS - 1, \
+ CAN_NBTCFG_BRP_SHIFT)
+#define CAN_DBTCFG CAN_SFR_BASE(0x08)
+# define CAN_DBTCFG_SJW_BITS 4
+# define CAN_DBTCFG_SJW_SHIFT 0
+# define CAN_DBTCFG_SJW_MASK \
+ GENMASK(CAN_DBTCFG_SJW_SHIFT + CAN_DBTCFG_SJW_BITS - 1, \
+ CAN_DBTCFG_SJW_SHIFT)
+# define CAN_DBTCFG_TSEG2_BITS 4
+# define CAN_DBTCFG_TSEG2_SHIFT 8
+# define CAN_DBTCFG_TSEG2_MASK \
+ GENMASK(CAN_DBTCFG_TSEG2_SHIFT + CAN_DBTCFG_TSEG2_BITS - 1, \
+ CAN_DBTCFG_TSEG2_SHIFT)
+# define CAN_DBTCFG_TSEG1_BITS 5
+# define CAN_DBTCFG_TSEG1_SHIFT 16
+# define CAN_DBTCFG_TSEG1_MASK \
+ GENMASK(CAN_DBTCFG_TSEG1_SHIFT + CAN_DBTCFG_TSEG1_BITS - 1, \
+ CAN_DBTCFG_TSEG1_SHIFT)
+# define CAN_DBTCFG_BRP_BITS 8
+# define CAN_DBTCFG_BRP_SHIFT 24
+# define CAN_DBTCFG_BRP_MASK \
+ GENMASK(CAN_DBTCFG_BRP_SHIFT + CAN_DBTCFG_BRP_BITS - 1, \
+ CAN_DBTCFG_BRP_SHIFT)
+#define CAN_TDC CAN_SFR_BASE(0x0C)
+# define CAN_TDC_TDCV_BITS 5
+# define CAN_TDC_TDCV_SHIFT 0
+# define CAN_TDC_TDCV_MASK \
+ GENMASK(CAN_TDC_TDCV_SHIFT + CAN_TDC_TDCV_BITS - 1, \
+ CAN_TDC_TDCV_SHIFT)
+# define CAN_TDC_TDCO_BITS 5
+# define CAN_TDC_TDCO_SHIFT 8
+# define CAN_TDC_TDCO_MASK \
+ GENMASK(CAN_TDC_TDCO_SHIFT + CAN_TDC_TDCO_BITS - 1, \
+ CAN_TDC_TDCO_SHIFT)
+# define CAN_TDC_TDCMOD_BITS 2
+# define CAN_TDC_TDCMOD_SHIFT 16
+# define CAN_TDC_TDCMOD_MASK \
+ GENMASK(CAN_TDC_TDCMOD_SHIFT + CAN_TDC_TDCMOD_BITS - 1, \
+ CAN_TDC_TDCMOD_SHIFT)
+# define CAN_TDC_SID11EN BIT(24)
+# define CAN_TDC_EDGFLTEN BIT(25)
+#define CAN_TBC CAN_SFR_BASE(0x10)
+#define CAN_TSCON CAN_SFR_BASE(0x14)
+# define CAN_TSCON_TBCPRE_BITS 10
+# define CAN_TSCON_TBCPRE_SHIFT 0
+# define CAN_TSCON_TBCPRE_MASK \
+ GENMASK(CAN_TSCON_TBCPRE_SHIFT + CAN_TSCON_TBCPRE_BITS - 1, \
+ CAN_TSCON_TBCPRE_SHIFT)
+# define CAN_TSCON_TBCEN BIT(16)
+# define CAN_TSCON_TSEOF BIT(17)
+# define CAN_TSCON_TSRES BIT(18)
+#define CAN_VEC CAN_SFR_BASE(0x18)
+# define CAN_VEC_ICODE_BITS 7
+# define CAN_VEC_ICODE_SHIFT 0
+# define CAN_VEC_ICODE_MASK \
+ GENMASK(CAN_VEC_ICODE_SHIFT + CAN_VEC_ICODE_BITS - 1, \
+ CAN_VEC_ICODE_SHIFT)
+# define CAN_VEC_FILHIT_BITS 5
+# define CAN_VEC_FILHIT_SHIFT 8
+# define CAN_VEC_FILHIT_MASK \
+ GENMASK(CAN_VEC_FILHIT_SHIFT + CAN_VEC_FILHIT_BITS - 1, \
+ CAN_VEC_FILHIT_SHIFT)
+# define CAN_VEC_TXCODE_BITS 7
+# define CAN_VEC_TXCODE_SHIFT 16
+# define CAN_VEC_TXCODE_MASK \
+ GENMASK(CAN_VEC_TXCODE_SHIFT + CAN_VEC_TXCODE_BITS - 1, \
+ CAN_VEC_TXCODE_SHIFT)
+# define CAN_VEC_RXCODE_BITS 7
+# define CAN_VEC_RXCODE_SHIFT 24
+# define CAN_VEC_RXCODE_MASK \
+ GENMASK(CAN_VEC_RXCODE_SHIFT + CAN_VEC_RXCODE_BITS - 1, \
+ CAN_VEC_RXCODE_SHIFT)
+#define CAN_INT CAN_SFR_BASE(0x1C)
+# define CAN_INT_IF_SHIFT 0
+# define CAN_INT_TXIF BIT(0)
+# define CAN_INT_RXIF BIT(1)
+# define CAN_INT_TBCIF BIT(2)
+# define CAN_INT_MODIF BIT(3)
+# define CAN_INT_TEFIF BIT(4)
+# define CAN_INT_ECCIF BIT(8)
+# define CAN_INT_SPICRCIF BIT(9)
+# define CAN_INT_TXATIF BIT(10)
+# define CAN_INT_RXOVIF BIT(11)
+# define CAN_INT_SERRIF BIT(12)
+# define CAN_INT_CERRIF BIT(13)
+# define CAN_INT_WAKIF BIT(14)
+# define CAN_INT_IVMIF BIT(15)
+# define CAN_INT_IF_MASK \
+ (CAN_INT_TXIF | \
+ CAN_INT_RXIF | \
+ CAN_INT_TBCIF | \
+ CAN_INT_MODIF | \
+ CAN_INT_TEFIF | \
+ CAN_INT_ECCIF | \
+ CAN_INT_SPICRCIF | \
+ CAN_INT_TXATIF | \
+ CAN_INT_RXOVIF | \
+ CAN_INT_CERRIF | \
+ CAN_INT_SERRIF | \
+ CAN_INT_WAKEIF | \
+ CAN_INT_IVMIF)
+# define CAN_INT_IE_SHIFT 16
+# define CAN_INT_TXIE (CAN_INT_TXIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_RXIE (CAN_INT_RXIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_TBCIE (CAN_INT_TBCIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_MODIE (CAN_INT_MODIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_TEFIE (CAN_INT_TEFIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_ECCIE (CAN_INT_ECCIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_SPICRCIE \
+ (CAN_INT_SPICRCIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_TXATIE (CAN_INT_TXATIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_RXOVIE (CAN_INT_RXOVIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_CERRIE (CAN_INT_CERRIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_SERRIE (CAN_INT_SERRIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_WAKIE (CAN_INT_WAKIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_IVMIE (CAN_INT_IVMIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_IE_MASK \
+ (CAN_INT_TXIE | \
+ CAN_INT_RXIE | \
+ CAN_INT_TBCIE | \
+ CAN_INT_MODIE | \
+ CAN_INT_TEFIE | \
+ CAN_INT_ECCIE | \
+ CAN_INT_SPICRCIE | \
+ CAN_INT_TXATIE | \
+ CAN_INT_RXOVIE | \
+ CAN_INT_CERRIE | \
+ CAN_INT_SERRIE | \
+ CAN_INT_WAKEIE | \
+ CAN_INT_IVMIE)
+#define CAN_RXIF CAN_SFR_BASE(0x20)
+#define CAN_TXIF CAN_SFR_BASE(0x24)
+#define CAN_RXOVIF CAN_SFR_BASE(0x28)
+#define CAN_TXATIF CAN_SFR_BASE(0x2C)
+#define CAN_TXREQ CAN_SFR_BASE(0x30)
+#define CAN_TREC CAN_SFR_BASE(0x34)
+# define CAN_TREC_REC_BITS 8
+# define CAN_TREC_REC_SHIFT 0
+# define CAN_TREC_REC_MASK \
+ GENMASK(CAN_TREC_REC_SHIFT + CAN_TREC_REC_BITS - 1, \
+ CAN_TREC_REC_SHIFT)
+# define CAN_TREC_TEC_BITS 8
+# define CAN_TREC_TEC_SHIFT 8
+# define CAN_TREC_TEC_MASK \
+ GENMASK(CAN_TREC_TEC_SHIFT + CAN_TREC_TEC_BITS - 1, \
+ CAN_TREC_TEC_SHIFT)
+# define CAN_TREC_EWARN BIT(16)
+# define CAN_TREC_RXWARN BIT(17)
+# define CAN_TREC_TXWARN BIT(18)
+# define CAN_TREC_RXBP BIT(19)
+# define CAN_TREC_TXBP BIT(20)
+# define CAN_TREC_TXBO BIT(21)
+#define CAN_BDIAG0 CAN_SFR_BASE(0x38)
+# define CAN_BDIAG0_NRERRCNT_BITS 8
+# define CAN_BDIAG0_NRERRCNT_SHIFT 0
+# define CAN_BDIAG0_NRERRCNT_MASK \
+ GENMASK(CAN_BDIAG0_NRERRCNT_SHIFT + CAN_BDIAG0_NRERRCNT_BITS - 1, \
+ CAN_BDIAG0_NRERRCNT_SHIFT)
+# define CAN_BDIAG0_NTERRCNT_BITS 8
+# define CAN_BDIAG0_NTERRCNT_SHIFT 8
+# define CAN_BDIAG0_NTERRCNT_MASK \
+ GENMASK(CAN_BDIAG0_NTERRCNT_SHIFT + CAN_BDIAG0_NTERRCNT_BITS - 1, \
+ CAN_BDIAG0_NTERRCNT_SHIFT)
+# define CAN_BDIAG0_DRERRCNT_BITS 8
+# define CAN_BDIAG0_DRERRCNT_SHIFT 16
+# define CAN_BDIAG0_DRERRCNT_MASK \
+ GENMASK(CAN_BDIAG0_DRERRCNT_SHIFT + CAN_BDIAG0_DRERRCNT_BITS - 1, \
+ CAN_BDIAG0_DRERRCNT_SHIFT)
+# define CAN_BDIAG0_DTERRCNT_BITS 8
+# define CAN_BDIAG0_DTERRCNT_SHIFT 24
+# define CAN_BDIAG0_DTERRCNT_MASK \
+ GENMASK(CAN_BDIAG0_DTERRCNT_SHIFT + CAN_BDIAG0_DTERRCNT_BITS - 1, \
+ CAN_BDIAG0_DTERRCNT_SHIFT)
+#define CAN_BDIAG1 CAN_SFR_BASE(0x3C)
+# define CAN_BDIAG1_EFMSGCNT_BITS 16
+# define CAN_BDIAG1_EFMSGCNT_SHIFT 0
+# define CAN_BDIAG1_EFMSGCNT_MASK \
+ GENMASK(CAN_BDIAG1_EFMSGCNT_SHIFT + CAN_BDIAG1_EFMSGCNT_BITS - 1, \
+ CAN_BDIAG1_EFMSGCNT_SHIFT)
+# define CAN_BDIAG1_NBIT0ERR BIT(16)
+# define CAN_BDIAG1_NBIT1ERR BIT(17)
+# define CAN_BDIAG1_NACKERR BIT(18)
+# define CAN_BDIAG1_NSTUFERR BIT(19)
+# define CAN_BDIAG1_NFORMERR BIT(20)
+# define CAN_BDIAG1_NCRCERR BIT(21)
+# define CAN_BDIAG1_TXBOERR BIT(23)
+# define CAN_BDIAG1_DBIT0ERR BIT(24)
+# define CAN_BDIAG1_DBIT1ERR BIT(25)
+# define CAN_BDIAG1_DFORMERR BIT(27)
+# define CAN_BDIAG1_DSTUFERR BIT(28)
+# define CAN_BDIAG1_DCRCERR BIT(29)
+# define CAN_BDIAG1_ESI BIT(30)
+# define CAN_BDIAG1_DLCMM BIT(31)
+#define CAN_TEFCON CAN_SFR_BASE(0x40)
+# define CAN_TEFCON_TEFNEIE BIT(0)
+# define CAN_TEFCON_TEFHIE BIT(1)
+# define CAN_TEFCON_TEFFIE BIT(2)
+# define CAN_TEFCON_TEFOVIE BIT(3)
+# define CAN_TEFCON_TEFTSEN BIT(5)
+# define CAN_TEFCON_UINC BIT(8)
+# define CAN_TEFCON_FRESET BIT(10)
+# define CAN_TEFCON_FSIZE_BITS 5
+# define CAN_TEFCON_FSIZE_SHIFT 24
+# define CAN_TEFCON_FSIZE_MASK \
+ GENMASK(CAN_TEFCON_FSIZE_SHIFT + CAN_TEFCON_FSIZE_BITS - 1, \
+ CAN_TEFCON_FSIZE_SHIFT)
+#define CAN_TEFSTA CAN_SFR_BASE(0x44)
+# define CAN_TEFSTA_TEFNEIF BIT(0)
+# define CAN_TEFSTA_TEFHIF BIT(1)
+# define CAN_TEFSTA_TEFFIF BIT(2)
+# define CAN_TEFSTA_TEVOVIF BIT(3)
+#define CAN_TEFUA CAN_SFR_BASE(0x48)
+#define CAN_RESERVED CAN_SFR_BASE(0x4C)
+#define CAN_TXQCON CAN_SFR_BASE(0x50)
+# define CAN_TXQCON_TXQNIE BIT(0)
+# define CAN_TXQCON_TXQEIE BIT(2)
+# define CAN_TXQCON_TXATIE BIT(4)
+# define CAN_TXQCON_TXEN BIT(7)
+# define CAN_TXQCON_UINC BIT(8)
+# define CAN_TXQCON_TXREQ BIT(9)
+# define CAN_TXQCON_FRESET BIT(10)
+# define CAN_TXQCON_TXPRI_BITS 5
+# define CAN_TXQCON_TXPRI_SHIFT 16
+# define CAN_TXQCON_TXPRI_MASK \
+ GENMASK(CAN_TXQCON_TXPRI_SHIFT + CAN_TXQCON_TXPRI_BITS - 1, \
+ CAN_TXQCON_TXPRI_SHIFT)
+# define CAN_TXQCON_TXAT_BITS 2
+# define CAN_TXQCON_TXAT_SHIFT 21
+# define CAN_TXQCON_TXAT_MASK \
+ GENMASK(CAN_TXQCON_TXAT_SHIFT + CAN_TXQCON_TXAT_BITS - 1, \
+ CAN_TXQCON_TXAT_SHIFT)
+# define CAN_TXQCON_FSIZE_BITS 5
+# define CAN_TXQCON_FSIZE_SHIFT 24
+# define CAN_TXQCON_FSIZE_MASK \
+ GENMASK(CAN_TXQCON_FSIZE_SHIFT + CAN_TXQCON_FSIZE_BITS - 1, \
+ CAN_TXQCON_FSIZE_SHIFT)
+# define CAN_TXQCON_PLSIZE_BITS 3
+# define CAN_TXQCON_PLSIZE_SHIFT 29
+# define CAN_TXQCON_PLSIZE_MASK \
+ GENMASK(CAN_TXQCON_PLSIZE_SHIFT + CAN_TXQCON_PLSIZE_BITS - 1, \
+ CAN_TXQCON_PLSIZE_SHIFT)
+# define CAN_TXQCON_PLSIZE_8 0
+# define CAN_TXQCON_PLSIZE_12 1
+# define CAN_TXQCON_PLSIZE_16 2
+# define CAN_TXQCON_PLSIZE_20 3
+# define CAN_TXQCON_PLSIZE_24 4
+# define CAN_TXQCON_PLSIZE_32 5
+# define CAN_TXQCON_PLSIZE_48 6
+# define CAN_TXQCON_PLSIZE_64 7
+
+#define CAN_TXQSTA CAN_SFR_BASE(0x54)
+# define CAN_TXQSTA_TXQNIF BIT(0)
+# define CAN_TXQSTA_TXQEIF BIT(2)
+# define CAN_TXQSTA_TXATIF BIT(4)
+# define CAN_TXQSTA_TXERR BIT(5)
+# define CAN_TXQSTA_TXLARB BIT(6)
+# define CAN_TXQSTA_TXABT BIT(7)
+# define CAN_TXQSTA_TXQCI_BITS 5
+# define CAN_TXQSTA_TXQCI_SHIFT 8
+# define CAN_TXQSTA_TXQCI_MASK \
+ GENMASK(CAN_TXQSTA_TXQCI_SHIFT + CAN_TXQSTA_TXQCI_BITS - 1, \
+ CAN_TXQSTA_TXQCI_SHIFT)
+
+#define CAN_TXQUA CAN_SFR_BASE(0x58)
+#define CAN_FIFOCON(x) CAN_SFR_BASE(0x5C + 12 * (x - 1))
+#define CAN_FIFOCON_TFNRFNIE BIT(0)
+#define CAN_FIFOCON_TFHRFHIE BIT(1)
+#define CAN_FIFOCON_TFERFFIE BIT(2)
+#define CAN_FIFOCON_RXOVIE BIT(3)
+#define CAN_FIFOCON_TXATIE BIT(4)
+#define CAN_FIFOCON_RXTSEN BIT(5)
+#define CAN_FIFOCON_RTREN BIT(6)
+#define CAN_FIFOCON_TXEN BIT(7)
+#define CAN_FIFOCON_UINC BIT(8)
+#define CAN_FIFOCON_TXREQ BIT(9)
+#define CAN_FIFOCON_FRESET BIT(10)
+# define CAN_FIFOCON_TXPRI_BITS 5
+# define CAN_FIFOCON_TXPRI_SHIFT 16
+# define CAN_FIFOCON_TXPRI_MASK \
+ GENMASK(CAN_FIFOCON_TXPRI_SHIFT + CAN_FIFOCON_TXPRI_BITS - 1, \
+ CAN_FIFOCON_TXPRI_SHIFT)
+# define CAN_FIFOCON_TXAT_BITS 2
+# define CAN_FIFOCON_TXAT_SHIFT 21
+# define CAN_FIFOCON_TXAT_MASK \
+ GENMASK(CAN_FIFOCON_TXAT_SHIFT + CAN_FIFOCON_TXAT_BITS - 1, \
+ CAN_FIFOCON_TXAT_SHIFT)
+# define CAN_FIFOCON_TXAT_ONE_SHOT 0
+# define CAN_FIFOCON_TXAT_THREE_SHOT 1
+# define CAN_FIFOCON_TXAT_UNLIMITED 2
+# define CAN_FIFOCON_FSIZE_BITS 5
+# define CAN_FIFOCON_FSIZE_SHIFT 24
+# define CAN_FIFOCON_FSIZE_MASK \
+ GENMASK(CAN_FIFOCON_FSIZE_SHIFT + CAN_FIFOCON_FSIZE_BITS - 1, \
+ CAN_FIFOCON_FSIZE_SHIFT)
+# define CAN_FIFOCON_PLSIZE_BITS 3
+# define CAN_FIFOCON_PLSIZE_SHIFT 29
+# define CAN_FIFOCON_PLSIZE_MASK \
+ GENMASK(CAN_FIFOCON_PLSIZE_SHIFT + CAN_FIFOCON_PLSIZE_BITS - 1, \
+ CAN_FIFOCON_PLSIZE_SHIFT)
+#define CAN_FIFOSTA(x) CAN_SFR_BASE(0x60 + 12 * (x - 1))
+# define CAN_FIFOSTA_TFNRFNIF BIT(0)
+# define CAN_FIFOSTA_TFHRFHIF BIT(1)
+# define CAN_FIFOSTA_TFERFFIF BIT(2)
+# define CAN_FIFOSTA_RXOVIF BIT(3)
+# define CAN_FIFOSTA_TXATIF BIT(4)
+# define CAN_FIFOSTA_RXTSEN BIT(5)
+# define CAN_FIFOSTA_RTREN BIT(6)
+# define CAN_FIFOSTA_TXEN BIT(7)
+# define CAN_FIFOSTA_FIFOCI_BITS 5
+# define CAN_FIFOSTA_FIFOCI_SHIFT 8
+# define CAN_FIFOSTA_FIFOCI_MASK \
+ GENMASK(CAN_FIFOSTA_FIFOCI_SHIFT + CAN_FIFOSTA_FIFOCI_BITS - 1, \
+ CAN_FIFOSTA_FIFOCI_SHIFT)
+#define CAN_FIFOUA(x) CAN_SFR_BASE(0x64 + 12 * (x - 1))
+#define CAN_FLTCON(x) CAN_SFR_BASE(0x1D0 + (x & 0x1c))
+# define CAN_FILCON_SHIFT(x) ((x & 3) * 8)
+# define CAN_FILCON_BITS(x) 4
+# define CAN_FILCON_MASK(x) \
+ GENMASK(CAN_FILCON_SHIFT(x) + CAN_FILCON_BITS(x) - 1, \
+ CAN_FILCON_SHIFT(x))
+# define CAN_FIFOCON_FLTEN(x) BIT(7 + CAN_FILCON_SHIFT(x))
+#define CAN_FLTOBJ(x) CAN_SFR_BASE(0x1F0 + 8 * x)
+# define CAN_FILOBJ_SID_BITS 11
+# define CAN_FILOBJ_SID_SHIFT 0
+# define CAN_FILOBJ_SID_MASK \
+ GENMASK(CAN_FILOBJ_SID_SHIFT + CAN_FILOBJ_SID_BITS - 1, \
+ CAN_FILOBJ_SID_SHIFT)
+# define CAN_FILOBJ_EID_BITS 18
+# define CAN_FILOBJ_EID_SHIFT 12
+# define CAN_FILOBJ_EID_MASK \
+ GENMASK(CAN_FILOBJ_EID_SHIFT + CAN_FILOBJ_EID_BITS - 1, \
+ CAN_FILOBJ_EID_SHIFT)
+# define CAN_FILOBJ_SID11 BIT(29)
+# define CAN_FILOBJ_EXIDE BIT(30)
+#define CAN_FLTMASK(x) CAN_SFR_BASE(0x1F4 + 8 * x)
+# define CAN_FILMASK_MSID_BITS 11
+# define CAN_FILMASK_MSID_SHIFT 0
+# define CAN_FILMASK_MSID_MASK \
+ GENMASK(CAN_FILMASK_MSID_SHIFT + CAN_FILMASK_MSID_BITS - 1, \
+ CAN_FILMASK_MSID_SHIFT)
+# define CAN_FILMASK_MEID_BITS 18
+# define CAN_FILMASK_MEID_SHIFT 12
+# define CAN_FILMASK_MEID_MASK \
+ GENMASK(CAN_FILMASK_MEID_SHIFT + CAN_FILMASK_MEID_BITS - 1, \
+ CAN_FILMASK_MEID_SHIFT)
+# define CAN_FILMASK_MSID11 BIT(29)
+# define CAN_FILMASK_MIDE BIT(30)
+
+#define CAN_OBJ_ID_SID_BITS 11
+#define CAN_OBJ_ID_SID_SHIFT 0
+#define CAN_OBJ_ID_SID_MASK \
+ GENMASK(CAN_OBJ_ID_SID_SHIFT + CAN_OBJ_ID_SID_BITS - 1, \
+ CAN_OBJ_ID_SID_SHIFT)
+#define CAN_OBJ_ID_EID_BITS 18
+#define CAN_OBJ_ID_EID_SHIFT 11
+#define CAN_OBJ_ID_EID_MASK \
+ GENMASK(CAN_OBJ_ID_EID_SHIFT + CAN_OBJ_ID_EID_BITS - 1, \
+ CAN_OBJ_ID_EID_SHIFT)
+#define CAN_OBJ_ID_SID_BIT11 BIT(29)
+
+#define CAN_OBJ_FLAGS_DLC_BITS 4
+#define CAN_OBJ_FLAGS_DLC_SHIFT 0
+#define CAN_OBJ_FLAGS_DLC_MASK \
+ GENMASK(CAN_OBJ_FLAGS_DLC_SHIFT + CAN_OBJ_FLAGS_DLC_BITS - 1, \
+ CAN_OBJ_FLAGS_DLC_SHIFT)
+#define CAN_OBJ_FLAGS_IDE BIT(4)
+#define CAN_OBJ_FLAGS_RTR BIT(5)
+#define CAN_OBJ_FLAGS_BRS BIT(6)
+#define CAN_OBJ_FLAGS_FDF BIT(7)
+#define CAN_OBJ_FLAGS_ESI BIT(8)
+#define CAN_OBJ_FLAGS_SEQ_BITS 7
+#define CAN_OBJ_FLAGS_SEQ_SHIFT 9
+#define CAN_OBJ_FLAGS_SEQ_MASK \
+ GENMASK(CAN_OBJ_FLAGS_SEQ_SHIFT + CAN_OBJ_FLAGS_SEQ_BITS - 1, \
+ CAN_OBJ_FLAGS_SEQ_SHIFT)
+#define CAN_OBJ_FLAGS_FILHIT_BITS 11
+#define CAN_OBJ_FLAGS_FILHIT_SHIFT 5
+#define CAN_OBJ_FLAGS_FILHIT_MASK \
+ GENMASK(CAN_FLAGS_FILHIT_SHIFT + CAN_FLAGS_FILHIT_BITS - 1, \
+ CAN_FLAGS_FILHIT_SHIFT)
+
+#define CAN_OBJ_FLAGS_CUSTOM_ISTEF BIT(31)
+
+#define MCP2517FD_BUFFER_TXRX_SIZE 2048
+
+static const char * const mcp2517fd_mode_names[] = {
+ [CAN_CON_MODE_MIXED] = "can2.0+canfd",
+ [CAN_CON_MODE_SLEEP] = "sleep",
+ [CAN_CON_MODE_INTERNAL_LOOPBACK] = "internal loopback",
+ [CAN_CON_MODE_LISTENONLY] = "listen only",
+ [CAN_CON_MODE_CONFIG] = "config",
+ [CAN_CON_MODE_EXTERNAL_LOOPBACK] = "external loopback",
+ [CAN_CON_MODE_CAN2_0] = "can2.0",
+ [CAN_CON_MODE_RESTRICTED] = "restricted"
+};
+
+struct mcp2517fd_obj {
+ u32 id;
+ u32 flags;
+};
+
+struct mcp2517fd_obj_tx {
+ struct mcp2517fd_obj header;
+ u32 data[];
+};
+
+static void mcp2517fd_obj_to_le(struct mcp2517fd_obj *obj)
+{
+ obj->id = cpu_to_le32(obj->id);
+ obj->flags = cpu_to_le32(obj->flags);
+}
+
+struct mcp2517fd_obj_ts {
+ u32 id;
+ u32 flags;
+ u32 ts;
+};
+
+struct mcp2517fd_obj_tef {
+ struct mcp2517fd_obj_ts header;
+};
+
+struct mcp2517fd_obj_rx {
+ struct mcp2517fd_obj_ts header;
+ u8 data[];
+};
+
+static void mcp2517fd_obj_ts_from_le(struct mcp2517fd_obj_ts *obj)
+{
+ obj->id = le32_to_cpu(obj->id);
+ obj->flags = le32_to_cpu(obj->flags);
+ obj->ts = le32_to_cpu(obj->ts);
+}
+
+#define FIFO_DATA(x) (0x400 + (x))
+#define FIFO_DATA_SIZE 0x800
+
+static const struct can_bittiming_const mcp2517fd_nominal_bittiming_const = {
+ .name = DEVICE_NAME,
+ .tseg1_min = 2,
+ .tseg1_max = BIT(CAN_NBTCFG_TSEG1_BITS),
+ .tseg2_min = 1,
+ .tseg2_max = BIT(CAN_NBTCFG_TSEG2_BITS),
+ .sjw_max = BIT(CAN_NBTCFG_SJW_BITS),
+ .brp_min = 1,
+ .brp_max = BIT(CAN_NBTCFG_BRP_BITS),
+ .brp_inc = 1,
+};
+
+static const struct can_bittiming_const mcp2517fd_data_bittiming_const = {
+ .name = DEVICE_NAME,
+ .tseg1_min = 1,
+ .tseg1_max = BIT(CAN_DBTCFG_TSEG1_BITS),
+ .tseg2_min = 1,
+ .tseg2_max = BIT(CAN_DBTCFG_TSEG2_BITS),
+ .sjw_max = BIT(CAN_DBTCFG_SJW_BITS),
+ .brp_min = 1,
+ .brp_max = BIT(CAN_DBTCFG_BRP_BITS),
+ .brp_inc = 1,
+};
+
+enum mcp2517fd_model {
+ CAN_MCP2517FD = 0x2517,
+};
+
+enum mcp2517fd_gpio_mode {
+ gpio_mode_int = 0,
+ gpio_mode_standby = MCP2517FD_IOCON_XSTBYEN,
+ gpio_mode_out_low = MCP2517FD_IOCON_PM0,
+ gpio_mode_out_high = MCP2517FD_IOCON_PM0 | MCP2517FD_IOCON_LAT0,
+ gpio_mode_in = MCP2517FD_IOCON_PM0 | MCP2517FD_IOCON_TRIS0
+};
+
+struct mcp2517fd_trigger_tx_message {
+ struct spi_message msg;
+ struct spi_transfer fill_xfer;
+ struct spi_transfer trigger_xfer;
+ int fifo;
+ char fill_cmd[2];
+ char fill_obj[sizeof(struct mcp2517fd_obj_tx)];
+ char fill_data[64];
+ char trigger_cmd[2];
+ char trigger_data;
+};
+
+struct mcp2517fd_read_fifo_info {
+ struct mcp2517fd_obj_ts *rxb[32];
+ int rx_count;
+ u32 tsmin;
+ u32 tsmax;
+};
+
+struct mcp2517fd_priv {
+ struct can_priv can;
+ struct net_device *net;
+ struct spi_device *spi;
+ struct regulator *power;
+ struct regulator *transceiver;
+ struct clk *clk;
+
+ struct dentry *debugfs_dir;
+
+ /* the actual model of the mcp2517fd */
+ enum mcp2517fd_model model;
+
+ struct {
+ /* clock configuration */
+ bool clock_pll;
+ bool clock_div2;
+ int clock_odiv;
+
+ /* GPIO configuration */
+ enum mcp2517fd_gpio_mode gpio0_mode;
+ enum mcp2517fd_gpio_mode gpio1_mode;
+ bool gpio_opendrain;
+ bool txcan_opendrain;
+ bool int_opendrain;
+ } config;
+
+ /* the distinct spi_speeds to use for spi communication */
+ u32 spi_setup_speed_hz;
+ u32 spi_speed_hz;
+
+ /* fifo info */
+ struct {
+ /* define payload size and mode */
+ int payload_size;
+ u32 payload_mode;
+
+ /* TEF addresses - start, end and current */
+ u32 tef_address_start;
+ u32 tef_address_end;
+ u32 tef_address;
+
+ /* address in mcp2517fd-Fifo RAM of each fifo */
+ u32 fifo_address[32];
+
+ /* infos on tx-fifos */
+ u32 tx_fifos;
+ u32 tx_fifo_start;
+ u32 tx_fifo_mask; /* bitmask of which fifo is a tx fifo */
+ u32 tx_submitted_mask;
+ u32 tx_pending_mask;
+ u32 tx_processed_mask;
+
+ /* info on rx_fifos */
+ u32 rx_fifos;
+ u32 rx_fifo_depth;
+ u32 rx_fifo_start;
+ u32 rx_fifo_mask; /* bitmask of which fifo is a rx fifo */
+
+ /* memory image of FIFO RAM on mcp2517fd */
+ u8 fifo_data[MCP2517FD_BUFFER_TXRX_SIZE];
+
+ } fifos;
+
+ /* structure with active fifos that need to get fed to the system */
+ struct mcp2517fd_read_fifo_info queued_fifos;
+
+ /* statistics */
+ struct {
+ /* number of calls to the irq handler */
+ u64 irq_calls;
+ /* number of loops inside the irq handler */
+ u64 irq_loops;
+
+ /* interrupt handler state and statistics */
+ u32 irq_state;
+#define IRQ_STATE_NEVER_RUN 0
+#define IRQ_STATE_RUNNING 1
+#define IRQ_STATE_HANDLED 2
+ /* stats on number of rx overflows */
+ u64 rx_overflow;
+ /* statistics of FIFO usage */
+ u64 fifo_usage[32];
+ } stats;
+
+ /* the current status of the mcp2517fd */
+ struct {
+ u32 intf;
+ /* ASSERT(CAN_INT + 4 == CAN_RXIF) */
+ u32 rxif;
+ /* ASSERT(CAN_RXIF + 4 == CAN_TXIF) */
+ u32 txif;
+ /* ASSERT(CAN_TXIF + 4 == CAN_RXOVIF) */
+ u32 rxovif;
+ /* ASSERT(CAN_RXOVIF + 4 == CAN_TXATIF) */
+ u32 txatif;
+ /* ASSERT(CAN_TXATIF + 4 == CAN_TXREQ) */
+ u32 txreq;
+ /* ASSERT(CAN_TXREQ + 4 == CAN_TREC) */
+ u32 trec;
+ /* ASSERT(CAN_TREC + 4 == CAN_BDIAG0) */
+ u32 bdiag0;
+ /* ASSERT(CAN_BDIAG0 + 4 == CAN_BDIAG1) */
+ u32 bdiag1;
+ } status;
+
+ /* configuration registers */
+ struct {
+ u32 osc;
+ u32 ecccon;
+ u32 con;
+ u32 iocon;
+ u32 tdc;
+ u32 tscon;
+ u32 tefcon;
+ u32 nbtcfg;
+ u32 dbtcfg;
+ } regs;
+
+ /* interrupt handler signaling */
+ int force_quit;
+ int after_suspend;
+#define AFTER_SUSPEND_UP 1
+#define AFTER_SUSPEND_DOWN 2
+#define AFTER_SUSPEND_POWER 4
+#define AFTER_SUSPEND_RESTART 8
+ int restart_tx;
+
+ /* interrupt flags during irq handling */
+ u32 int_clear_mask;
+ u32 int_clear_value;
+ u32 bdiag1_clear_mask;
+ u32 bdiag1_clear_value;
+
+ /* composit error id and dataduring irq handling */
+ u32 can_err_id;
+ u32 can_err_data[8];
+
+ /* the current mode */
+ u32 active_can_mode;
+ u32 new_state;
+
+ /* status of the tx_queue enabled/disabled */
+ u32 tx_queue_status;
+
+ /* spi-tx/rx buffers for efficient transfers
+ * used during setup and irq
+ */
+ u8 spi_tx[MCP2517FD_BUFFER_TXRX_SIZE];
+ u8 spi_rx[MCP2517FD_BUFFER_TXRX_SIZE];
+
+ /* structure for transmit fifo spi_messages */
+ struct mcp2517fd_trigger_tx_message *spi_transmit_fifos;
+};
+
+/* module parameters */
+bool use_bulk_release_fifos;
+module_param(use_bulk_release_fifos, bool, 0664);
+MODULE_PARM_DESC(use_bulk_release_fifos,
+ "Use code that favours longer spi transfers over multiple transfers");
+bool use_complete_fdfifo_read;
+module_param(use_complete_fdfifo_read, bool, 0664);
+MODULE_PARM_DESC(use_complete_fdfifo_read,
+ "Use code that favours longer spi transfers over multiple transfers for fd can");
+unsigned int tx_fifos;
+module_param(tx_fifos, uint, 0664);
+MODULE_PARM_DESC(tx_fifos,
+ "Number of tx-fifos to configure\n");
+unsigned int bw_sharing_log2bits;
+module_param(bw_sharing_log2bits, uint, 0664);
+MODULE_PARM_DESC(bw_sharing_log2bits,
+ "Delay between 2 transmissions in number of arbitration bit times\n");
+
+/* spi sync helper */
+
+/* wrapper arround spi_sync, that sets speed_hz */
+static int mcp2517fd_sync_transfer(struct spi_device *spi,
+ struct spi_transfer *xfer,
+ unsigned int xfers,
+ int speed_hz)
+{
+ int i;
+
+ for (i = 0; i < xfers; i++)
+ xfer[i].speed_hz = speed_hz;
+
+ return spi_sync_transfer(spi, xfer, xfers);
+}
+
+/* an optimization of spi_write_then_read that merges the transfers */
+static int mcp2517fd_write_then_read(struct spi_device *spi,
+ const void *tx_buf,
+ unsigned int tx_len,
+ void *rx_buf,
+ unsigned int rx_len,
+ int speed_hz)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct spi_transfer xfer[2];
+ int ret;
+
+ memset(xfer, 0, sizeof(xfer));
+
+ /* when using a halfduplex controller or to big for buffer */
+ if ((spi->master->flags & SPI_MASTER_HALF_DUPLEX) ||
+ (tx_len + rx_len > sizeof(priv->spi_tx))) {
+ xfer[0].tx_buf = tx_buf;
+ xfer[0].len = tx_len;
+
+ xfer[1].rx_buf = rx_buf;
+ xfer[1].len = rx_len;
+
+ return mcp2517fd_sync_transfer(spi, xfer, 2, speed_hz);
+ }
+
+ /* full duplex optimization */
+ xfer[0].tx_buf = priv->spi_tx;
+ xfer[0].rx_buf = priv->spi_rx;
+ xfer[0].len = tx_len + rx_len;
+
+ /* copy and clean */
+ memcpy(priv->spi_tx, tx_buf, tx_len);
+ memset(priv->spi_tx + tx_len, 0, rx_len);
+
+ ret = mcp2517fd_sync_transfer(spi, xfer, 1, speed_hz);
+ if (ret)
+ return ret;
+
+ memcpy(rx_buf, xfer[0].rx_buf + tx_len, rx_len);
+
+ return 0;
+}
+
+/* simple spi_write wrapper with speed_hz */
+static int mcp2517fd_write(struct spi_device *spi,
+ const void *tx_buf,
+ unsigned int tx_len,
+ int speed_hz)
+{
+ struct spi_transfer xfer;
+
+ memset(&xfer, 0, sizeof(xfer));
+ xfer.tx_buf = tx_buf;
+ xfer.len = tx_len;
+
+ return mcp2517fd_sync_transfer(spi, &xfer, 1, speed_hz);
+}
+
+/* spi_sync wrapper similar to spi_write_then_read that optimizes transfers */
+static int mcp2517fd_write_then_write(struct spi_device *spi,
+ const void *tx_buf,
+ unsigned int tx_len,
+ const void *tx2_buf,
+ unsigned int tx2_len,
+ int speed_hz)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct spi_transfer xfer;
+
+ if (tx_len + tx2_len > MCP2517FD_BUFFER_TXRX_SIZE)
+ return -EINVAL;
+
+ memset(&xfer, 0, sizeof(xfer));
+ xfer.len = tx_len + tx2_len;
+ xfer.tx_buf = priv->spi_tx;
+
+ memcpy(priv->spi_tx, tx_buf, tx_len);
+ memcpy(priv->spi_tx + tx_len, tx2_buf, tx2_len);
+
+ return mcp2517fd_sync_transfer(spi, &xfer, 1, speed_hz);
+}
+
+/* mcp2517fd spi command/protocol helper */
+
+static void mcp2517fd_calc_cmd_addr(u16 cmd, u16 addr, u8 *data)
+{
+ cmd = cmd | (addr & ADDRESS_MASK);
+
+ data[0] = (cmd >> 8) & 0xff;
+ data[1] = (cmd >> 0) & 0xff;
+}
+
+static int mcp2517fd_cmd_reset(struct spi_device *spi, u32 speed_hz)
+{
+ u8 cmd[2];
+
+ mcp2517fd_calc_cmd_addr(INSTRUCTION_RESET, 0, cmd);
+
+ /* write the reset command */
+ return mcp2517fd_write(spi, cmd, 2, speed_hz);
+}
+
+/* read multiple bytes, transform some registers */
+static int mcp2517fd_cmd_readn(struct spi_device *spi, u32 reg,
+ void *data, int n, u32 speed_hz)
+{
+ u8 cmd[2];
+ int ret;
+
+ mcp2517fd_calc_cmd_addr(INSTRUCTION_READ, reg, cmd);
+
+ ret = mcp2517fd_write_then_read(spi, &cmd, 2, data, n, speed_hz);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mcp2517fd_convert_to_cpu(u32 *data, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ data[i] = le32_to_cpu(data[i]);
+
+ return 0;
+}
+
+/* read a register, but we are only interrested in a few bytes */
+static int mcp2517fd_cmd_read_mask(struct spi_device *spi, u32 reg,
+ u32 *data, u32 mask, u32 speed_hz)
+{
+ int first_byte, last_byte, len_byte;
+ int ret;
+
+ /* check that at least one bit is set */
+ if (!mask)
+ return -EINVAL;
+
+ /* calculate first and last byte used */
+ first_byte = (ffs(mask) - 1) >> 3;
+ last_byte = (fls(mask) - 1) >> 3;
+ len_byte = last_byte - first_byte + 1;
+
+ /* do a partial read */
+ *data = 0;
+ ret = mcp2517fd_cmd_readn(spi, reg,
+ ((void *)data + first_byte), len_byte,
+ speed_hz);
+ if (ret)
+ return ret;
+
+ return mcp2517fd_convert_to_cpu(data, 1);
+}
+
+static int mcp2517fd_cmd_read(struct spi_device *spi, u32 reg, u32 *data,
+ u32 speed_hz)
+{
+ return mcp2517fd_cmd_read_mask(spi, reg, data, -1, speed_hz);
+}
+
+/* read a register, but we are only interrested in a few bytes */
+static int mcp2517fd_cmd_write_mask(struct spi_device *spi, u32 reg,
+ u32 data, u32 mask, u32 speed_hz)
+{
+ int first_byte, last_byte, len_byte;
+ u8 cmd[2];
+
+ /* check that at least one bit is set */
+ if (!mask)
+ return -EINVAL;
+
+ /* calculate first and last byte used */
+ first_byte = (ffs(mask) - 1) >> 3;
+ last_byte = (fls(mask) - 1) >> 3;
+ len_byte = last_byte - first_byte + 1;
+
+ /* prepare buffer */
+ mcp2517fd_calc_cmd_addr(INSTRUCTION_WRITE, reg + first_byte, cmd);
+ data = cpu_to_le32(data);
+
+ return mcp2517fd_write_then_write(spi,
+ cmd, sizeof(cmd),
+ ((void *)&data + first_byte),
+ len_byte,
+ speed_hz);
+}
+
+static int mcp2517fd_cmd_write(struct spi_device *spi, u32 reg, u32 data,
+ u32 speed_hz)
+{
+ return mcp2517fd_cmd_write_mask(spi, reg, data, -1, speed_hz);
+}
+
+static int mcp2517fd_cmd_writen(struct spi_device *spi, u32 reg,
+ void *data, int n, u32 speed_hz)
+{
+ u8 cmd[2];
+ int ret;
+
+ mcp2517fd_calc_cmd_addr(INSTRUCTION_WRITE, reg, cmd);
+
+ ret = mcp2517fd_write_then_write(spi, &cmd, 2, data, n, speed_hz);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* ideally these would be defined in uapi/linux/can.h */
+#define CAN_EFF_SID_SHIFT (CAN_EFF_ID_BITS - CAN_SFF_ID_BITS)
+#define CAN_EFF_SID_BITS CAN_SFF_ID_BITS
+#define CAN_EFF_SID_MASK \
+ GENMASK(CAN_EFF_SID_SHIFT + CAN_EFF_SID_BITS - 1, \
+ CAN_EFF_SID_SHIFT)
+#define CAN_EFF_EID_SHIFT 0
+#define CAN_EFF_EID_BITS CAN_EFF_SID_SHIFT
+#define CAN_EFF_EID_MASK \
+ GENMASK(CAN_EFF_EID_SHIFT + CAN_EFF_EID_BITS - 1, \
+ CAN_EFF_EID_SHIFT)
+
+static void mcp2517fd_canid_to_mcpid(u32 can_id, u32 *id, u32 *flags)
+{
+ if (can_id & CAN_EFF_FLAG) {
+ int sid = (can_id & CAN_EFF_SID_MASK) >> CAN_EFF_SID_SHIFT;
+ int eid = (can_id & CAN_EFF_EID_MASK) >> CAN_EFF_EID_SHIFT;
+ *id = (eid << CAN_OBJ_ID_EID_SHIFT) |
+ (sid << CAN_OBJ_ID_SID_SHIFT);
+ *flags = CAN_OBJ_FLAGS_IDE;
+ } else {
+ *id = can_id & CAN_SFF_MASK;
+ *flags = 0;
+ }
+
+ *flags |= (can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
+}
+
+static void mcp2517fd_mcpid_to_canid(u32 mcpid, u32 mcpflags, u32 *id)
+{
+ u32 sid = (mcpid & CAN_OBJ_ID_SID_MASK) >> CAN_OBJ_ID_SID_SHIFT;
+ u32 eid = (mcpid & CAN_OBJ_ID_EID_MASK) >> CAN_OBJ_ID_EID_SHIFT;
+
+ if (mcpflags & CAN_OBJ_FLAGS_IDE) {
+ *id = (eid << CAN_EFF_EID_SHIFT) |
+ (sid << CAN_EFF_SID_SHIFT) |
+ CAN_EFF_FLAG;
+ } else {
+ *id = sid;
+ }
+
+ *id |= (mcpflags & CAN_OBJ_FLAGS_RTR) ? CAN_RTR_FLAG : 0;
+}
+
+/* CAN transmit related*/
+
+static void mcp2517fd_mark_tx_pending(void *context)
+{
+ struct mcp2517fd_trigger_tx_message *txm = context;
+ struct spi_device *spi = txm->msg.spi;
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+
+ /* only here or in the irq handler this value is changed,
+ * so there is no race condition and it does not require locking
+ * serialization happens via spi_pump_message
+ */
+ priv->fifos.tx_pending_mask |= BIT(txm->fifo);
+}
+
+static int mcp2517fd_fill_spi_transmit_fifos(struct mcp2517fd_priv *priv)
+{
+ struct mcp2517fd_trigger_tx_message *txm;
+ int i, fifo;
+ const u32 trigger = CAN_FIFOCON_TXREQ | CAN_FIFOCON_UINC;
+ const int first_byte = (ffs(trigger) - 1) >> 3;
+
+ priv->spi_transmit_fifos = kcalloc(
+ priv->fifos.tx_fifos,
+ sizeof(*priv->spi_transmit_fifos),
+ GFP_KERNEL | GFP_DMA);
+ if (!priv->spi_transmit_fifos)
+ return -ENOMEM;
+
+ for (i = 0; i < priv->fifos.tx_fifos; i++) {
+ fifo = priv->fifos.tx_fifo_start + i;
+ txm = &priv->spi_transmit_fifos[i];
+ /* prepare the message */
+ spi_message_init(&txm->msg);
+ txm->msg.complete = mcp2517fd_mark_tx_pending;
+ txm->msg.context = txm;
+ txm->fifo = fifo;
+ /* the payload itself */
+ txm->fill_xfer.speed_hz = priv->spi_speed_hz;
+ txm->fill_xfer.tx_buf = txm->fill_cmd;
+ txm->fill_xfer.len = 2;
+ txm->fill_xfer.cs_change = true;
+ mcp2517fd_calc_cmd_addr(
+ INSTRUCTION_WRITE,
+ FIFO_DATA(priv->fifos.fifo_address[fifo]),
+ txm->fill_cmd);
+ spi_message_add_tail(&txm->fill_xfer, &txm->msg);
+ /* the trigger command */
+ txm->trigger_xfer.speed_hz = priv->spi_speed_hz;
+ txm->trigger_xfer.tx_buf = txm->trigger_cmd;
+ txm->trigger_xfer.len = 3;
+ mcp2517fd_calc_cmd_addr(INSTRUCTION_WRITE,
+ CAN_FIFOCON(fifo) + first_byte,
+ txm->trigger_cmd);
+ txm->trigger_data = trigger >> (8 * first_byte);
+ spi_message_add_tail(&txm->trigger_xfer, &txm->msg);
+ }
+
+ return 0;
+}
+
+static int mcp2517fd_transmit_message_common(
+ struct spi_device *spi, int fifo,
+ struct mcp2517fd_obj_tx *obj, int len, u8 *data)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct mcp2517fd_trigger_tx_message *txm =
+ &priv->spi_transmit_fifos[fifo - priv->fifos.tx_fifo_start];
+ int ret;
+
+ /* add fifo as seq */
+ obj->header.flags |= fifo << CAN_OBJ_FLAGS_SEQ_SHIFT;
+
+ /* transform to le32 */
+ mcp2517fd_obj_to_le(&obj->header);
+
+ /* fill in details */
+ memcpy(txm->fill_obj, obj, sizeof(struct mcp2517fd_obj_tx));
+ memset(txm->fill_data, 0, priv->fifos.payload_size);
+ memcpy(txm->fill_data, data, len);
+
+ /* transfers to FIFO RAM has to be multiple of 4 */
+ txm->fill_xfer.len =
+ 2 + sizeof(struct mcp2517fd_obj_tx) + ALIGN(len, 4);
+
+ /* and transmit asyncroniously */
+ ret = spi_async(spi, &txm->msg);
+ if (ret)
+ return NETDEV_TX_BUSY;
+
+ return NETDEV_TX_OK;
+}
+
+static int mcp2517fd_transmit_fdmessage(struct spi_device *spi, int fifo,
+ struct canfd_frame *frame)
+{
+ struct mcp2517fd_obj_tx obj;
+ int dlc = can_len2dlc(frame->len);
+ u32 flags;
+
+ frame->len = can_dlc2len(dlc);
+
+ mcp2517fd_canid_to_mcpid(frame->can_id, &obj.header.id, &flags);
+
+ flags |= dlc << CAN_OBJ_FLAGS_DLC_SHIFT;
+ flags |= (frame->can_id & CAN_EFF_FLAG) ? CAN_OBJ_FLAGS_IDE : 0;
+ flags |= (frame->can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
+ flags |= (frame->flags & CANFD_BRS) ? CAN_OBJ_FLAGS_BRS : 0;
+ flags |= (frame->flags & CANFD_ESI) ? CAN_OBJ_FLAGS_ESI : 0;
+ flags |= CAN_OBJ_FLAGS_FDF;
+
+ obj.header.flags = flags;
+
+ return mcp2517fd_transmit_message_common(
+ spi, fifo, &obj, frame->len, frame->data);
+}
+
+static int mcp2517fd_transmit_message(struct spi_device *spi, int fifo,
+ struct can_frame *frame)
+{
+ struct mcp2517fd_obj_tx obj;
+ u32 flags;
+
+ if (frame->can_dlc > 8)
+ frame->can_dlc = 8;
+
+ mcp2517fd_canid_to_mcpid(frame->can_id, &obj.header.id, &flags);
+
+ flags |= frame->can_dlc << CAN_OBJ_FLAGS_DLC_SHIFT;
+ flags |= (frame->can_id & CAN_EFF_FLAG) ? CAN_OBJ_FLAGS_IDE : 0;
+ flags |= (frame->can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
+
+ obj.header.flags = flags;
+
+ return mcp2517fd_transmit_message_common(
+ spi, fifo, &obj, frame->can_dlc, frame->data);
+}
+
+static netdev_tx_t mcp2517fd_start_xmit(struct sk_buff *skb,
+ struct net_device *net)
+{
+ struct mcp2517fd_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ u32 pending_mask;
+ int fifo;
+ int ret;
+
+ if (can_dropped_invalid_skb(net, skb))
+ return NETDEV_TX_OK;
+
+ if (priv->can.state == CAN_STATE_BUS_OFF) {
+ priv->tx_queue_status = 0;
+ netif_stop_queue(priv->net);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* get effective mask */
+ pending_mask = priv->fifos.tx_pending_mask |
+ priv->fifos.tx_submitted_mask;
+
+ /* decide on fifo to assign */
+ if (pending_mask)
+ fifo = ffs(pending_mask) - 2;
+ else
+ fifo = priv->fifos.tx_fifo_start + priv->fifos.tx_fifos - 1;
+
+ /* handle error - this should not happen... */
+ if (fifo < priv->fifos.tx_fifo_start) {
+ dev_err(&spi->dev,
+ "reached tx-fifo %i, which is not valid\n",
+ fifo);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* if we are the last one, then stop the queue */
+ if (fifo == priv->fifos.tx_fifo_start) {
+ priv->tx_queue_status = 0;
+ netif_stop_queue(priv->net);
+ }
+
+ /* mark as submitted */
+ priv->fifos.tx_submitted_mask |= BIT(fifo);
+ priv->stats.fifo_usage[fifo]++;
+
+ /* now process it for real */
+ if (can_is_canfd_skb(skb))
+ ret = mcp2517fd_transmit_fdmessage(
+ spi, fifo, (struct canfd_frame *)skb->data);
+ else
+ ret = mcp2517fd_transmit_message(
+ spi, fifo, (struct can_frame *)skb->data);
+
+ /* keep it for reference until the message really got transmitted */
+ if (ret == NETDEV_TX_OK)
+ can_put_echo_skb(skb, priv->net, fifo);
+
+ return ret;
+}
+
+/* CAN RX Related */
+
+static int mcp2517fd_can_transform_rx_fd(struct spi_device *spi,
+ struct mcp2517fd_obj_rx *rx)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct canfd_frame *frame;
+ struct sk_buff *skb;
+ u32 flags = rx->header.flags;
+
+ /* allocate the skb buffer */
+ skb = alloc_canfd_skb(priv->net, &frame);
+ if (!skb) {
+ dev_err(&spi->dev, "cannot allocate RX skb\n");
+ priv->net->stats.rx_dropped++;
+ return -ENOMEM;
+ }
+
+ mcp2517fd_mcpid_to_canid(rx->header.id, flags, &frame->can_id);
+ frame->flags |= (flags & CAN_OBJ_FLAGS_BRS) ? CANFD_BRS : 0;
+ frame->flags |= (flags & CAN_OBJ_FLAGS_ESI) ? CANFD_ESI : 0;
+
+ frame->len = can_dlc2len((flags & CAN_OBJ_FLAGS_DLC_MASK)
+ >> CAN_OBJ_FLAGS_DLC_SHIFT);
+
+ memcpy(frame->data, rx->data, frame->len);
+
+ priv->net->stats.rx_packets++;
+ priv->net->stats.rx_bytes += frame->len;
+
+ can_led_event(priv->net, CAN_LED_EVENT_RX);
+
+ netif_rx_ni(skb);
+
+ return 0;
+}
+
+static int mcp2517fd_can_transform_rx_normal(struct spi_device *spi,
+ struct mcp2517fd_obj_rx *rx)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct sk_buff *skb;
+ struct can_frame *frame;
+ u32 flags = rx->header.flags;
+ int len;
+
+ /* allocate the skb buffer */
+ skb = alloc_can_skb(priv->net, &frame);
+ if (!skb) {
+ dev_err(&spi->dev, "cannot allocate RX skb\n");
+ priv->net->stats.rx_dropped++;
+ return -ENOMEM;
+ }
+
+ mcp2517fd_mcpid_to_canid(rx->header.id, flags, &frame->can_id);
+
+ frame->can_dlc = (flags & CAN_OBJ_FLAGS_DLC_MASK)
+ >> CAN_OBJ_FLAGS_DLC_SHIFT;
+
+ len = can_dlc2len(frame->can_dlc);
+
+ memcpy(frame->data, rx->data, len);
+
+ priv->net->stats.rx_packets++;
+ priv->net->stats.rx_bytes += len;
+
+ can_led_event(priv->net, CAN_LED_EVENT_RX);
+
+ netif_rx_ni(skb);
+
+ return 0;
+}
+
+static int mcp2517fd_process_queued_rx(struct spi_device *spi,
+ struct mcp2517fd_obj_ts *obj)
+{
+ struct mcp2517fd_obj_rx *rx = container_of(
+ obj, struct mcp2517fd_obj_rx, header);
+
+ if (obj->flags & CAN_OBJ_FLAGS_FDF)
+ return mcp2517fd_can_transform_rx_fd(
+ spi, rx);
+ else
+ return mcp2517fd_can_transform_rx_normal(
+ spi, rx);
+}
+
+static int mcp2517fd_normal_release_fifos(struct spi_device *spi,
+ int start, int end)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int ret;
+
+ /* release each fifo in a separate transfer */
+ for (; start < end ; start++) {
+ ret = mcp2517fd_cmd_write_mask(
+ spi, CAN_FIFOCON(start),
+ CAN_FIFOCON_UINC,
+ CAN_FIFOCON_UINC,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* unfortunately the CAN_FIFOCON are not directly consecutive
+ * so the optimization of "clearing all in one spi_transfer"
+ * would produce an overhead of 11 unnecessary bytes/fifo
+ * - transferring 14 (2 cmd + 12 data) bytes
+ * instead of just 3 (2 + 1).
+ * On some slower systems this may still be beneficial,
+ * but it is not good enough for the generic case.
+ * On a Raspberry Pi CM the timings for clearing 3 fifos
+ * (at 12.5MHz SPI clock speed) are:
+ * * normal:
+ * * 3 spi transfers
+ * * 9 bytes total
+ * * 36.74us from first CS low to last CS high
+ * * individual CS: 9.14us, 5.74us and 5.16us
+ * * 77.02us from CS up of fifo transfer to last release CS up
+ * * bulk:
+ * * 1 spi transfer
+ * * 27 bytes total
+ * * 29.06us CS Low
+ * * 78.28us from CS up of fifo transfer to last release CS up
+ * this obviously varies with SPI_clock speed
+ * - the slower the clock the less efficient the optimization.
+ * similarly the faster the CPU (and bigger the code cache) the
+ * less effcient the optimization - the above case is border line.
+ */
+
+#define FIFOCON_SPACING (CAN_FIFOCON(1) - CAN_FIFOCON(0))
+#define FIFOCON_SPACINGW (FIFOCON_SPACING / sizeof(u32))
+
+static int mcp2517fd_bulk_release_fifos(struct spi_device *spi,
+ int start, int end)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int i;
+ int ret;
+
+ /* calculate start address and length */
+ int fifos = end - start;
+ int first_byte = (ffs(CAN_FIFOCON_UINC) - 1) >> 3;
+ int addr = CAN_FIFOCON(start);
+ int len = 1 + (fifos - 1) * FIFOCON_SPACING;
+
+ /* the worsted case buffer */
+ u32 buf[32 * FIFOCON_SPACINGW], base;
+
+ base = (priv->fifos.payload_mode << CAN_FIFOCON_PLSIZE_SHIFT) |
+ ((priv->fifos.rx_fifo_depth - 1) << CAN_FIFOCON_FSIZE_SHIFT) |
+ CAN_FIFOCON_RXTSEN | /* RX timestamps */
+ CAN_FIFOCON_UINC |
+ CAN_FIFOCON_TFERFFIE | /* FIFO Full */
+ CAN_FIFOCON_TFHRFHIE | /* FIFO Half Full*/
+ CAN_FIFOCON_TFNRFNIE; /* FIFO not empty */
+
+ memset(buf, 0, sizeof(buf));
+ for (i = 0; i < end - start ; i++) {
+ if (i == priv->fifos.rx_fifos - 1)
+ base |= CAN_FIFOCON_RXOVIE;
+ buf[FIFOCON_SPACINGW * i] = cpu_to_le32(base);
+ }
+
+ ret = mcp2517fd_cmd_writen(spi, addr + first_byte,
+ (u8 *)buf + first_byte,
+ len,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* queued FIFO handling for release to system */
+
+static void mcp2517fd_clear_queued_fifos(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+
+ /* prepare rfi - mostly used for sorting */
+ priv->queued_fifos.tsmin = -1;
+ priv->queued_fifos.tsmax = 0;
+ priv->queued_fifos.rx_count = 0;
+}
+
+static void mcp2517fd_addto_queued_fifos(struct spi_device *spi,
+ struct mcp2517fd_obj_ts *obj)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct mcp2517fd_read_fifo_info *rfi = &priv->queued_fifos;
+
+ /* add pointer to queued array-list */
+ rfi->rxb[rfi->rx_count] = obj;
+ rfi->rx_count++;
+
+ /* and get tsmin/tsmax */
+ if (rfi->tsmin > obj->ts)
+ rfi->tsmin = obj->ts;
+ if (rfi->tsmax > obj->ts)
+ rfi->tsmax = obj->ts;
+}
+
+static int mcp2517fd_process_queued_tef(struct spi_device *spi,
+ struct mcp2517fd_obj_ts *obj)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct mcp2517fd_obj_tef *tef = container_of(
+ obj, struct mcp2517fd_obj_tef, header);
+ int dlc = (obj->flags & CAN_OBJ_FLAGS_DLC_MASK)
+ >> CAN_OBJ_FLAGS_DLC_SHIFT;
+ int fifo = (tef->header.flags & CAN_OBJ_FLAGS_SEQ_MASK) >>
+ CAN_OBJ_FLAGS_SEQ_SHIFT;
+
+ /* update counters */
+ priv->net->stats.tx_packets++;
+ priv->net->stats.tx_bytes += can_dlc2len(dlc);
+
+ /* release it */
+ can_get_echo_skb(priv->net, fifo);
+
+ can_led_event(priv->net, CAN_LED_EVENT_TX);
+
+ return 0;
+}
+
+static int mcp2517fd_compare_obj_ts(const void *a, const void *b)
+{
+ const struct mcp2517fd_obj_ts * const *rxa = a;
+ const struct mcp2517fd_obj_ts * const *rxb = b;
+ /* using signed here to handle rollover correctly */
+ s32 ats = (*rxa)->ts;
+ s32 bts = (*rxb)->ts;
+
+ if (ats < bts)
+ return -1;
+ if (ats > bts)
+ return 1;
+ return 0;
+}
+
+static int mcp2517fd_process_queued_fifos(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct mcp2517fd_read_fifo_info *rfi = &priv->queued_fifos;
+ int i;
+ int ret;
+
+ /* sort the fifos (rx and TEF) by receive timestamp */
+ sort(rfi->rxb, rfi->rx_count, sizeof(struct mcp2517fd_obj_ts *),
+ mcp2517fd_compare_obj_ts, NULL);
+
+ /* process the recived fifos */
+ for (i = 0; i < rfi->rx_count ; i++) {
+ if (rfi->rxb[i]->flags & CAN_OBJ_FLAGS_CUSTOM_ISTEF) {
+ ret = mcp2517fd_process_queued_tef(
+ spi, rfi->rxb[i]);
+ } else {
+ ret = mcp2517fd_process_queued_rx(
+ spi, rfi->rxb[i]);
+ }
+ if (ret)
+ return ret;
+ }
+
+ /* clear queued fifos */
+ mcp2517fd_clear_queued_fifos(spi);
+
+ return 0;
+}
+
+static int mcp2517fd_transform_rx(struct spi_device *spi,
+ struct mcp2517fd_obj_rx *rx)
+{
+ int dlc;
+
+ /* transform the data to system byte order */
+ mcp2517fd_obj_ts_from_le(&rx->header);
+
+ /* add the object to the list */
+ mcp2517fd_addto_queued_fifos(spi, &rx->header);
+
+ /* calc length and return it */
+ dlc = (rx->header.flags & CAN_OBJ_FLAGS_DLC_MASK)
+ >> CAN_OBJ_FLAGS_DLC_SHIFT;
+ return can_dlc2len(dlc);
+}
+
+/* read_fifo implementations
+ *
+ * read_fifos is a simple implementation, that:
+ * * loops all fifos
+ * * read header + some data-bytes (8)
+ * * read rest of data-bytes bytes
+ * * release fifo
+ * for 3 can frames dlc<=8 to read here we have:
+ * * 6 spi transfers
+ * * 75 bytes (= 3 * (2 + 12 + 8) bytes + 3 * 3 bytes)
+ * for 3 canfd frames dlc>8 to read here we have:
+ * * 9 spi transfers
+ * * 81 (= 3 * (2 + 12 + 8 + 2) bytes + 3 * 3 bytes) + 3 * extra payload
+ * this only transfers the required size of bytes on the spi bus.
+ *
+ * bulk_read_fifos is an optimization that is most practical for
+ * Can2.0 busses, but may also be practical for CanFD busses that
+ * have a high average payload data size.
+ *
+ * It will read all of the fifo data in a single spi_transfer:
+ * * read all fifos in one go (as long as these are ajacent to each other)
+ * * loop all fifos
+ * * release fifo
+ * for 3 can2.0 frames to read here we have:
+ * * 4 spi transfers
+ * * 71 bytes (= 2 + 3 * (12 + 8) bytes + 3 * 3 bytes)
+ * for 3 canfd frames to read here we have:
+ * * 4 spi transfers
+ * * 230 bytes (= 2 + 3 * (12 + 64) bytes)
+ * obviously this reads way too many bytes for framesizes <=32 bytes,
+ * but it avoids the overhead on the CPU side and may even trigger
+ * DMA transfers due to the high byte count, which release CPU cycles.
+ *
+ * This optimization will also be efficient for cases where a high
+ * percentage of canFD frames has a dlc-size > 8.
+ * This mode is used for Can2.0 configured busses.
+ *
+ * For now this option can get forced for CanFD via a module parameter.
+ * In the future there may be some heuristics that could trigger a usage
+ * of this mode as well in some circumstances.
+ *
+ * Note: there is a second optimization for release fifo as well,
+ * but it is not as efficient as this optimization for the
+ * non-CanFD case - see mcp2517fd_bulk_release_fifos
+ */
+
+static int mcp2517fd_read_fifos(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int fifo_header_size = sizeof(struct mcp2517fd_obj_rx);
+ int fifo_min_payload_size = 8;
+ int fifo_min_size = fifo_header_size + fifo_min_payload_size;
+ int fifo_max_payload_size =
+ ((priv->can.ctrlmode & CAN_CTRLMODE_FD) ? 64 : 8);
+ u32 mask = priv->status.rxif;
+ struct mcp2517fd_obj_rx *rx;
+ int i, len;
+ int ret;
+
+ /* read all the "open" segments in big chunks */
+ for (i = priv->fifos.rx_fifo_start;
+ i < priv->fifos.rx_fifo_start + priv->fifos.rx_fifos;
+ i++) {
+ if (!(mask & BIT(i)))
+ continue;
+ /* the fifo to fill */
+ rx = (struct mcp2517fd_obj_rx *)
+ (priv->fifos.fifo_data + priv->fifos.fifo_address[i]);
+ /* read the minimal payload */
+ ret = mcp2517fd_cmd_readn(
+ spi, FIFO_DATA(priv->fifos.fifo_address[i]),
+ rx,
+ fifo_min_size,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ /* process fifo stats and get length */
+ len = min_t(int, mcp2517fd_transform_rx(spi, rx),
+ fifo_max_payload_size);
+
+ /* read extra payload if needed */
+ if (len > fifo_min_payload_size) {
+ ret = mcp2517fd_cmd_readn(
+ spi,
+ FIFO_DATA(priv->fifos.fifo_address[i] +
+ fifo_min_size),
+ &rx->data[fifo_min_payload_size],
+ len - fifo_min_payload_size,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ }
+ /* release fifo */
+ ret = mcp2517fd_normal_release_fifos(spi, i, i + 1);
+ if (ret)
+ return ret;
+ /* increment fifo_usage */
+ priv->stats.fifo_usage[i]++;
+ }
+
+ return 0;
+}
+
+static int mcp2517fd_bulk_read_fifos(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int fifo_header_size = sizeof(struct mcp2517fd_obj_rx);
+ int fifo_max_payload_size = priv->fifos.payload_size;
+ int fifo_max_size = fifo_header_size + fifo_max_payload_size;
+ u32 mask = priv->status.rxif;
+ u32 rx_fifo_end = priv->fifos.rx_fifo_start +
+ priv->fifos.rx_fifos;
+ struct mcp2517fd_obj_rx *rx;
+ int i, j;
+ int ret;
+
+ /* read all the "open" segments in big chunks */
+ for (i = priv->fifos.rx_fifo_start; i < rx_fifo_end; i++) {
+ if (mask & BIT(i)) {
+ /* find the last set bit in sequence */
+ for (j = i;
+ (j < rx_fifo_end) && (mask & BIT(j));
+ j++) {
+ /* clear the mask */
+ mask &= ~BIT(j);
+ }
+
+ /* now we got start and end, so read the range */
+ ret = mcp2517fd_cmd_readn(
+ spi,
+ FIFO_DATA(priv->fifos.fifo_address[i]),
+ priv->fifos.fifo_data +
+ priv->fifos.fifo_address[i],
+ (j - i) * fifo_max_size,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ /* clear all the fifos in range */
+ if (use_bulk_release_fifos)
+ ret = mcp2517fd_bulk_release_fifos(spi,
+ i, j);
+ else
+ ret = mcp2517fd_normal_release_fifos(spi,
+ i, j);
+ if (ret)
+ return ret;
+
+ /* preprocess data */
+ for (; i < j ; i++) {
+ /* store the fifo to process */
+ rx = (struct mcp2517fd_obj_rx *)(
+ priv->fifos.fifo_data +
+ priv->fifos.fifo_address[i]);
+ /* process fifo stats */
+ mcp2517fd_transform_rx(spi, rx);
+ /* increment usage */
+ priv->stats.fifo_usage[i]++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int mcp2517fd_can_ist_handle_rxif(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ u32 mask = priv->status.rxif;
+ int ret;
+
+ if (!mask)
+ return 0;
+
+ /* read all the fifos - for non-fd case use bulk read optimization */
+ if (((priv->can.ctrlmode & CAN_CTRLMODE_FD) == 0) ||
+ use_complete_fdfifo_read)
+ ret = mcp2517fd_bulk_read_fifos(spi);
+ else
+ ret = mcp2517fd_read_fifos(spi);
+
+ return 0;
+}
+
+static int mcp2517fd_can_ist_handle_tefif(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct mcp2517fd_obj_tef *tef;
+ u32 pending = priv->fifos.tx_pending_mask &
+ (~priv->fifos.tx_processed_mask);
+ int i, count, fifo;
+ int ret;
+
+ /* calculate the number of fifos that have been processed */
+ count = hweight_long(pending);
+ count -= hweight_long(priv->status.txreq);
+ if (count <= 0) {
+ dev_err(&spi->dev,
+ "handle_tefif: unexpected count = %i\n",
+ count);
+ return -EINVAL;
+ }
+
+ /* now clear TEF for each */
+ /* TODO: optimize for BULK reads, as we (hopefully) know COUNT */
+ for (i = 0; i < count; i++) {
+ /* calc address in address space */
+ tef = (struct mcp2517fd_obj_tef *)(priv->fifos.fifo_data +
+ priv->fifos.tef_address);
+ /* read all the object data */
+ ret = mcp2517fd_cmd_readn(spi,
+ FIFO_DATA(priv->fifos.tef_address),
+ tef, sizeof(*tef),
+ priv->spi_speed_hz);
+
+ /* increment the counter to read next */
+ ret = mcp2517fd_cmd_write_mask(spi,
+ CAN_TEFCON,
+ CAN_TEFCON_UINC,
+ CAN_TEFCON_UINC,
+ priv->spi_speed_hz);
+
+ /* transform the data to system byte order */
+ mcp2517fd_obj_ts_from_le(&tef->header);
+
+ fifo = (tef->header.flags & CAN_OBJ_FLAGS_SEQ_MASK) >>
+ CAN_OBJ_FLAGS_SEQ_SHIFT;
+
+ /* submit to queue */
+ tef->header.flags |= CAN_OBJ_FLAGS_CUSTOM_ISTEF;
+ mcp2517fd_addto_queued_fifos(spi, &tef->header);
+
+ /* increment tef */
+ priv->fifos.tef_address += sizeof(*tef);
+ if (priv->fifos.tef_address > priv->fifos.tef_address_end)
+ priv->fifos.tef_address = priv->fifos.tef_address_start;
+
+ /* and set mask */
+ priv->fifos.tx_processed_mask |= BIT(fifo);
+
+ if (fifo == priv->fifos.tx_fifo_start)
+ priv->tx_queue_status = 2;
+ }
+
+ return 0;
+}
+
+static void mcp2517fd_error_skb(struct net_device *net)
+{
+ struct mcp2517fd_priv *priv = netdev_priv(net);
+ struct sk_buff *skb;
+ struct can_frame *frame;
+
+ skb = alloc_can_err_skb(net, &frame);
+ if (skb) {
+ frame->can_id = priv->can_err_id;
+ memcpy(frame->data, priv->can_err_data, 8);
+ netif_rx_ni(skb);
+ } else {
+ netdev_err(net, "cannot allocate error skb\n");
+ }
+}
+
+static int mcp2517fd_can_ist_handle_rxovif(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ u32 mask = priv->status.rxovif;
+ int i;
+ int ret;
+
+ /* clear all fifos that have an overflow bit set */
+ for (i = 0; i < 32; i++) {
+ if (mask & BIT(i)) {
+ ret = mcp2517fd_cmd_write_mask(spi,
+ CAN_FIFOSTA(i),
+ 0,
+ CAN_FIFOSTA_RXOVIF,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ /* update statistics */
+ priv->net->stats.rx_over_errors++;
+ priv->net->stats.rx_errors++;
+ priv->stats.rx_overflow++;
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+ }
+ }
+
+ return 0;
+}
+
+static int mcp2517fd_can_ist_handle_modif(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int mode;
+ int ret;
+
+ /* mask interrupt for clearing */
+ priv->int_clear_mask |= CAN_INT_MODIF;
+
+ /* read the mode bit */
+ ret = mcp2517fd_cmd_read_mask(spi,
+ CAN_CON,
+ &priv->regs.con,
+ CAN_CON_OPMOD_MASK,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ mode = (priv->regs.con & CAN_CON_OPMOD_MASK) >>
+ CAN_CON_OPMOD_SHIFT;
+
+ /* this mostly happens on initialization */
+ if (mode == priv->active_can_mode) {
+ dev_err(&spi->dev,
+ "Controller switched to already active mode: %s(%u)\n",
+ mcp2517fd_mode_names[mode], mode);
+ return 0;
+ }
+
+ /* these we need to handle correctly */
+ dev_err(&spi->dev,
+ "Controller switched from mode %s(%u) to %s(%u)\n",
+ mcp2517fd_mode_names[priv->active_can_mode],
+ priv->active_can_mode,
+ mcp2517fd_mode_names[mode], mode);
+
+ /* finally assign the mode as currently active */
+ priv->active_can_mode = mode;
+ return 0;
+}
+
+static int mcp2517fd_can_ist_handle_cerrif(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+
+ /* in principle we could also delay reading bdiag registers
+ * until we get here - it would add some extra delay in the
+ * error case, but be slightly faster in the "normal" case.
+ * slightly faster would be saving 8 bytes of spi transfer.
+ */
+
+ dev_err_ratelimited(&spi->dev, "CAN Bus error\n");
+ priv->can_err_id |= CAN_ERR_BUSERROR;
+ priv->int_clear_mask |= CAN_INT_CERRIF;
+
+ if (priv->status.bdiag1 &
+ (CAN_BDIAG1_DBIT0ERR | CAN_BDIAG1_NBIT0ERR)) {
+ priv->can_err_id |= CAN_ERR_BUSERROR;
+ priv->can_err_data[2] |= CAN_ERR_PROT_BIT0;
+ priv->bdiag1_clear_mask |= CAN_BDIAG1_DBIT0ERR |
+ CAN_BDIAG1_NBIT0ERR;
+ }
+ if (priv->status.bdiag1 &
+ (CAN_BDIAG1_DBIT1ERR | CAN_BDIAG1_NBIT1ERR)) {
+ priv->can_err_id |= CAN_ERR_BUSERROR;
+ priv->can_err_data[2] |= CAN_ERR_PROT_BIT1;
+ priv->bdiag1_clear_mask |= CAN_BDIAG1_DBIT1ERR |
+ CAN_BDIAG1_NBIT1ERR;
+ }
+ if (priv->status.bdiag1 &
+ (CAN_BDIAG1_DSTUFERR | CAN_BDIAG1_NSTUFERR)) {
+ priv->can_err_id |= CAN_ERR_BUSERROR;
+ priv->can_err_data[2] |= CAN_ERR_PROT_STUFF;
+ priv->bdiag1_clear_mask |= CAN_BDIAG1_DSTUFERR |
+ CAN_BDIAG1_NSTUFERR;
+ }
+ if (priv->status.bdiag1 &
+ (CAN_BDIAG1_DFORMERR | CAN_BDIAG1_NFORMERR)) {
+ priv->can_err_id |= CAN_ERR_BUSERROR;
+ priv->can_err_data[2] |= CAN_ERR_PROT_FORM;
+ priv->bdiag1_clear_mask |= CAN_BDIAG1_DFORMERR |
+ CAN_BDIAG1_NFORMERR;
+ }
+ if (priv->status.bdiag1 & CAN_BDIAG1_NACKERR) {
+ priv->can_err_id |= CAN_ERR_ACK;
+ priv->bdiag1_clear_mask |= CAN_BDIAG1_NACKERR;
+ }
+
+ return 0;
+}
+
+static int mcp2517fd_can_ist_handle_eccif(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int ret;
+ u32 val;
+ u32 addr;
+
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_UNSPEC;
+ priv->int_clear_mask |= CAN_INT_ECCIF;
+
+ /* read ECC status register */
+ ret = mcp2517fd_cmd_read(spi, MCP2517FD_ECCSTAT, &val,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ addr = (val & MCP2517FD_ECCSTAT_ERRADDR_MASK) >>
+ MCP2517FD_ECCSTAT_ERRADDR_SHIFT;
+
+ dev_err_ratelimited(&spi->dev,
+ "ECC %s bit error at %03x\n",
+ (val & MCP2517FD_ECCSTAT_DEDIF) ?
+ "double" : "single",
+ addr);
+
+ return mcp2517fd_cmd_write(spi, MCP2517FD_ECCSTAT, 0,
+ priv->spi_speed_hz);
+}
+
+static int mcp2517fd_can_ist_handle_serrif(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+
+ /* Errors here are:
+ * * Bus Bandwidth Error: when a RX Message Assembly Buffer
+ * is still full when the next message has already arrived
+ * the recived message shall be ignored
+ * * TX MAB Underflow: when a TX Message is invalid
+ * due to ECC errors or TXMAB underflow
+ * in this situatioon the system will transition to
+ * Restricted or Listen Only mode
+ */
+
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_UNSPEC;
+ priv->int_clear_mask |= CAN_INT_SERRIF;
+
+ /* a mode change or ecc error would indicate TX MAB Undeflow */
+ if (priv->status.intf & (CAN_INT_MODIF | CAN_INT_ECCIF)) {
+ dev_warn_ratelimited(&spi->dev, "TX MAB underflow\n");
+ priv->net->stats.tx_fifo_errors++;
+ priv->net->stats.tx_errors++;
+ } else {
+ dev_warn_ratelimited(&spi->dev, "RX MAB overflow\n");
+ priv->net->stats.rx_dropped++;
+ priv->net->stats.rx_errors++;
+ }
+ return 0;
+}
+
+static int mcp2517fd_disable_interrupts(struct spi_device *spi,
+ u32 speed_hz)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+
+ priv->status.intf = 0;
+ return mcp2517fd_cmd_write(spi, CAN_INT, 0, speed_hz);
+}
+
+static int mcp2517fd_enable_interrupts(struct spi_device *spi,
+ u32 speed_hz)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+
+ priv->status.intf = CAN_INT_TEFIE |
+ CAN_INT_RXIE |
+ CAN_INT_MODIE |
+ CAN_INT_SERRIE |
+ CAN_INT_IVMIE |
+ CAN_INT_CERRIE |
+ CAN_INT_ECCIE;
+ return mcp2517fd_cmd_write(spi, CAN_INT,
+ priv->status.intf,
+ speed_hz);
+}
+
+static int mcp2517fd_hw_wake(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ u32 waitfor = MCP2517FD_OSC_OSCRDY;
+ u32 mask = waitfor | MCP2517FD_OSC_OSCDIS;
+ unsigned long timeout;
+ int ret;
+
+ if (priv->active_can_mode != CAN_CON_MODE_SLEEP)
+ return 0;
+
+ /* write clock */
+ ret = mcp2517fd_cmd_write(
+ spi, MCP2517FD_OSC, priv->regs.osc,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* wait for synced pll/osc/sclk */
+ timeout = jiffies + MCP2517FD_OSC_POLLING_JIFFIES;
+ while (time_before_eq(jiffies, timeout)) {
+ ret = mcp2517fd_cmd_read(spi, MCP2517FD_OSC,
+ &priv->regs.osc,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ if ((priv->regs.osc & mask) == waitfor) {
+ priv->active_can_mode = CAN_CON_MODE_CONFIG;
+ return 0;
+ }
+ }
+
+ dev_err(&spi->dev,
+ "Clock did not enable within the timeout period\n");
+ return -ETIMEDOUT;
+}
+
+static void mcp2517fd_hw_sleep(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+
+ /* disable interrupts */
+ mcp2517fd_disable_interrupts(spi, priv->spi_setup_speed_hz);
+
+ priv->active_can_mode = CAN_CON_MODE_SLEEP;
+ priv->regs.con = (priv->regs.con & ~CAN_CON_REQOP_MASK) |
+ (priv->active_can_mode << CAN_CON_REQOP_SHIFT);
+ mcp2517fd_cmd_write(spi, CAN_CON,
+ priv->regs.con,
+ priv->spi_setup_speed_hz);
+}
+
+static int mcp2517fd_can_ist_handle_status(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int ret;
+
+ /* interrupt clearing info */
+ priv->int_clear_value = 0;
+ priv->int_clear_mask = 0;
+ priv->bdiag1_clear_value = 0;
+ priv->bdiag1_clear_mask = 0;
+ priv->can_err_id = 0;
+ memset(priv->can_err_data, 0, 8);
+
+ /* state changes */
+ priv->new_state = priv->can.state;
+
+ /* clear queued fifos */
+ mcp2517fd_clear_queued_fifos(spi);
+
+ /* handle the rx */
+ if (priv->status.intf & CAN_INT_RXIF) {
+ ret = mcp2517fd_can_ist_handle_rxif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* handle the tef */
+ if (priv->status.intf & CAN_INT_TEFIF) {
+ ret = mcp2517fd_can_ist_handle_tefif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* process the queued fifos */
+ ret = mcp2517fd_process_queued_fifos(spi);
+
+ /* restart the tx queue if needed */
+ if (priv->tx_queue_status == 2) {
+ /* nothing should be left pending /in flight now... */
+ priv->fifos.tx_pending_mask = 0;
+ priv->fifos.tx_submitted_mask = 0;
+ priv->fifos.tx_processed_mask = 0;
+ priv->tx_queue_status = 1;
+ /* wake queue now */
+ netif_wake_queue(priv->net);
+ }
+
+ /* handle error interrupt flags */
+ if (priv->status.rxovif) {
+ ret = mcp2517fd_can_ist_handle_rxovif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* mode change erros */
+ if (priv->status.intf & CAN_INT_MODIF) {
+ ret = mcp2517fd_can_ist_handle_modif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* sram ECC error interrupt */
+ if (priv->status.intf & CAN_INT_ECCIF) {
+ ret = mcp2517fd_can_ist_handle_eccif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* system error interrupt*/
+ if (priv->status.intf & CAN_INT_SERRIF) {
+ ret = mcp2517fd_can_ist_handle_serrif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* message format interrupt */
+ if (priv->status.intf & CAN_INT_IVMIF) {
+ priv->can_err_id |= CAN_ERR_PROT;
+ priv->can_err_data[2] |= CAN_ERR_PROT_FORM;
+ priv->int_clear_mask |= CAN_INT_IVMIF;
+ priv->net->stats.rx_frame_errors++;
+ priv->net->stats.rx_errors++;
+ }
+
+ /* handle bus errors in more detail */
+ if (priv->status.intf & CAN_INT_CERRIF) {
+ ret = mcp2517fd_can_ist_handle_cerrif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* Error counter handling */
+ if (priv->status.trec & CAN_TREC_TXWARN) {
+ priv->new_state = CAN_STATE_ERROR_WARNING;
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_TX_WARNING;
+ }
+ if (priv->status.trec & CAN_TREC_RXWARN) {
+ priv->new_state = CAN_STATE_ERROR_WARNING;
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_RX_WARNING;
+ }
+ if (priv->status.trec & CAN_TREC_TXBP) {
+ priv->new_state = CAN_STATE_ERROR_PASSIVE;
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+ }
+ if (priv->status.trec & CAN_TREC_RXBP) {
+ priv->new_state = CAN_STATE_ERROR_PASSIVE;
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+ }
+ if (priv->status.trec & CAN_TREC_TXBO) {
+ priv->new_state = CAN_STATE_BUS_OFF;
+ priv->can_err_id |= CAN_ERR_BUSOFF;
+ }
+
+ /* based on the last state state check the new state */
+ switch (priv->can.state) {
+ case CAN_STATE_ERROR_ACTIVE:
+ if (priv->new_state >= CAN_STATE_ERROR_WARNING &&
+ priv->new_state <= CAN_STATE_BUS_OFF)
+ priv->can.can_stats.error_warning++;
+ /* fallthrough */
+ case CAN_STATE_ERROR_WARNING:
+ if (priv->new_state >= CAN_STATE_ERROR_PASSIVE &&
+ priv->new_state <= CAN_STATE_BUS_OFF)
+ priv->can.can_stats.error_passive++;
+ break;
+ default:
+ break;
+ }
+ priv->can.state = priv->new_state;
+
+ /* and send error packet */
+ if (priv->can_err_id)
+ mcp2517fd_error_skb(priv->net);
+
+ /* handle BUS OFF */
+ if (priv->can.state == CAN_STATE_BUS_OFF) {
+ if (priv->can.restart_ms == 0) {
+ netif_stop_queue(priv->net);
+ priv->force_quit = 1;
+ priv->can.can_stats.bus_off++;
+ can_bus_off(priv->net);
+ mcp2517fd_hw_sleep(spi);
+ }
+ }
+
+ /* clear int flags */
+ if (priv->int_clear_mask) {
+ ret = mcp2517fd_cmd_write_mask(spi,
+ CAN_INT,
+ priv->int_clear_value,
+ priv->int_clear_mask,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ }
+ if (priv->bdiag1_clear_mask) {
+ ret = mcp2517fd_cmd_write_mask(spi,
+ CAN_BDIAG1,
+ priv->bdiag1_clear_value,
+ priv->bdiag1_clear_mask,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static irqreturn_t mcp2517fd_can_ist(int irq, void *dev_id)
+{
+ struct mcp2517fd_priv *priv = dev_id;
+ struct spi_device *spi = priv->spi;
+ int ret;
+
+ priv->stats.irq_calls++;
+ priv->stats.irq_state = IRQ_STATE_RUNNING;
+
+ while (!priv->force_quit) {
+ /* count irq loops */
+ priv->stats.irq_loops++;
+
+ /* read interrupt status flags */
+ ret = mcp2517fd_cmd_readn(spi, CAN_INT,
+ &priv->status,
+ sizeof(priv->status),
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ /* only act if the mask is applied */
+ if ((priv->status.intf &
+ (priv->status.intf >> CAN_INT_IE_SHIFT)) == 0)
+ break;
+
+ /* handle the status */
+ ret = mcp2517fd_can_ist_handle_status(spi);
+ if (ret)
+ return ret;
+ }
+
+ priv->stats.irq_state = IRQ_STATE_HANDLED;
+
+ return IRQ_HANDLED;
+}
+
+static int mcp2517fd_get_berr_counter(const struct net_device *net,
+ struct can_berr_counter *bec)
+{
+ struct mcp2517fd_priv *priv = netdev_priv(net);
+
+ bec->txerr = (priv->status.trec & CAN_TREC_TEC_MASK) >>
+ CAN_TREC_TEC_SHIFT;
+ bec->rxerr = (priv->status.trec & CAN_TREC_REC_MASK) >>
+ CAN_TREC_REC_SHIFT;
+
+ return 0;
+}
+
+static int mcp2517fd_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 mcp2517fd_do_set_mode(struct net_device *net, enum can_mode mode)
+{
+ switch (mode) {
+ case CAN_MODE_START:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int mcp2517fd_do_set_nominal_bittiming(struct net_device *net)
+{
+ struct mcp2517fd_priv *priv = netdev_priv(net);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct spi_device *spi = priv->spi;
+
+ int sjw = bt->sjw;
+ int pseg2 = bt->phase_seg2;
+ int pseg1 = bt->phase_seg1;
+ int propseg = bt->prop_seg;
+ int brp = bt->brp;
+
+ int tseg1 = propseg + pseg1;
+ int tseg2 = pseg2;
+
+ /* calculate nominal bit timing */
+ priv->regs.nbtcfg = ((sjw - 1) << CAN_NBTCFG_SJW_SHIFT) |
+ ((tseg2 - 1) << CAN_NBTCFG_TSEG2_SHIFT) |
+ ((tseg1 - 1) << CAN_NBTCFG_TSEG1_SHIFT) |
+ ((brp - 1) << CAN_NBTCFG_BRP_SHIFT);
+
+ return mcp2517fd_cmd_write(spi, CAN_NBTCFG,
+ priv->regs.nbtcfg,
+ priv->spi_setup_speed_hz);
+}
+
+static int mcp2517fd_do_set_data_bittiming(struct net_device *net)
+{
+ struct mcp2517fd_priv *priv = netdev_priv(net);
+ struct can_bittiming *bt = &priv->can.data_bittiming;
+ struct spi_device *spi = priv->spi;
+
+ int sjw = bt->sjw;
+ int pseg2 = bt->phase_seg2;
+ int pseg1 = bt->phase_seg1;
+ int propseg = bt->prop_seg;
+ int brp = bt->brp;
+
+ int tseg1 = propseg + pseg1;
+ int tseg2 = pseg2;
+
+ /* calculate nominal bit timing */
+ priv->regs.dbtcfg = ((sjw - 1) << CAN_DBTCFG_SJW_SHIFT) |
+ ((tseg2 - 1) << CAN_DBTCFG_TSEG2_SHIFT) |
+ ((tseg1 - 1) << CAN_DBTCFG_TSEG1_SHIFT) |
+ ((brp - 1) << CAN_DBTCFG_BRP_SHIFT);
+
+ return mcp2517fd_cmd_write(spi, CAN_DBTCFG,
+ priv->regs.dbtcfg,
+ priv->spi_setup_speed_hz);
+}
+
+static int mcp2517fd_hw_probe(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ u32 val;
+ int ret;
+
+ /* Wait for oscillator startup timer after power up */
+ mdelay(MCP2517FD_OST_DELAY_MS);
+
+ /* send a "blind" reset, hoping we are in Config mode */
+ mcp2517fd_cmd_reset(spi, priv->spi_setup_speed_hz);
+
+ /* Wait for oscillator startup again */
+ mdelay(MCP2517FD_OST_DELAY_MS);
+
+ /* check clock register that the clock is ready or disabled */
+ ret = mcp2517fd_cmd_read(spi, MCP2517FD_OSC, &val,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* there can only be one... */
+ switch (val & (MCP2517FD_OSC_OSCRDY | MCP2517FD_OSC_OSCDIS)) {
+ case MCP2517FD_OSC_OSCRDY: /* either the clock is ready */
+ break;
+ case MCP2517FD_OSC_OSCDIS: /* or the clock is disabled */
+ /* wakeup sleeping system */
+ ret = mcp2517fd_hw_wake(spi);
+ if (ret)
+ return ret;
+ /* send a reset, hoping we are now in Config mode */
+ mcp2517fd_cmd_reset(spi, priv->spi_setup_speed_hz);
+
+ /* Wait for oscillator startup again */
+ mdelay(MCP2517FD_OST_DELAY_MS);
+ break;
+ default:
+ /* otherwise there is no valid device (or in strange state)
+ *
+ * if PLL is enabled but not ready, then there may be
+ * something "fishy"
+ * this happened during driver development
+ * (enabling pll, when when on wrong clock), so best warn
+ * about such a possibility
+ */
+ if ((val & (MCP2517FD_OSC_PLLEN | MCP2517FD_OSC_PLLRDY))
+ == MCP2517FD_OSC_PLLEN)
+ dev_err(&spi->dev,
+ "mcp2517fd may be in a strange state - a power disconnect may be required\n");
+
+ return -ENODEV;
+ }
+
+ /* check if we are in config mode already*/
+
+ /* read CON register and match */
+ ret = mcp2517fd_cmd_read(spi, CAN_CON, &val,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* apply mask and check */
+ if ((val & CAN_CON_DEFAULT_MASK) == CAN_CON_DEFAULT)
+ return 0;
+
+ /* as per datasheet a reset only works in Config Mode
+ * so as we have in principle no knowledge of the current
+ * mode that the controller is in we have no safe way
+ * to detect the device correctly
+ * hence we need to "blindly" put the controller into
+ * config mode.
+ * on the "save" side, the OSC reg has to be valid already,
+ * so there is a chance we got the controller...
+ */
+
+ /* blindly force it into config mode */
+ ret = mcp2517fd_cmd_write(spi, CAN_CON, CAN_CON_DEFAULT,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* delay some time */
+ mdelay(MCP2517FD_OST_DELAY_MS);
+
+ /* reset can controller */
+ mcp2517fd_cmd_reset(spi, priv->spi_setup_speed_hz);
+
+ /* delay some time */
+ mdelay(MCP2517FD_OST_DELAY_MS);
+
+ /* read CON register and match a final time */
+ ret = mcp2517fd_cmd_read(spi, CAN_CON, &val,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* just allow dumping the register if we ever get here */
+ dev_dbg(&spi->dev, "read CAN_CON = 0x%08x\n", val);
+
+ /* apply mask and check */
+ if ((val & CAN_CON_DEFAULT_MASK) != CAN_CON_DEFAULT)
+ return -ENODEV;
+
+ /* just in case: disable interrupts on controller */
+ return mcp2517fd_disable_interrupts(spi,
+ priv->spi_setup_speed_hz);
+}
+
+static int mcp2517fd_set_normal_mode(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int ret;
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
+ priv->active_can_mode = CAN_CON_MODE_EXTERNAL_LOOPBACK;
+ else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+ priv->active_can_mode = CAN_CON_MODE_LISTENONLY;
+ else if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
+ priv->active_can_mode = CAN_CON_MODE_MIXED;
+ else
+ priv->active_can_mode = CAN_CON_MODE_CAN2_0;
+
+ /* set mode to normal */
+ priv->regs.con = (priv->regs.con & ~CAN_CON_REQOP_MASK) |
+ (priv->active_can_mode << CAN_CON_REQOP_SHIFT);
+
+ ret = mcp2517fd_cmd_write(spi, CAN_CON,
+ priv->regs.con,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ return 0;
+}
+
+static int mcp2517fd_setup_osc(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int val = ((priv->config.clock_pll) ? MCP2517FD_OSC_PLLEN : 0)
+ | ((priv->config.clock_div2) ? MCP2517FD_OSC_SCLKDIV : 0);
+ int waitfor = ((priv->config.clock_pll) ? MCP2517FD_OSC_PLLRDY : 0)
+ | ((priv->config.clock_div2) ? MCP2517FD_OSC_SCLKRDY : 0)
+ | MCP2517FD_OSC_OSCRDY;
+ int ret;
+ unsigned long timeout;
+
+ /* manage clock_out divider */
+ switch (priv->config.clock_odiv) {
+ case 10:
+ val |= (MCP2517FD_OSC_CLKODIV_10)
+ << MCP2517FD_OSC_CLKODIV_SHIFT;
+ break;
+ case 4:
+ val |= (MCP2517FD_OSC_CLKODIV_4)
+ << MCP2517FD_OSC_CLKODIV_SHIFT;
+ break;
+ case 2:
+ val |= (MCP2517FD_OSC_CLKODIV_2)
+ << MCP2517FD_OSC_CLKODIV_SHIFT;
+ break;
+ case 1:
+ val |= (MCP2517FD_OSC_CLKODIV_1)
+ << MCP2517FD_OSC_CLKODIV_SHIFT;
+ break;
+ case 0:
+ /* this means implicitly SOF output */
+ val |= (MCP2517FD_OSC_CLKODIV_10)
+ << MCP2517FD_OSC_CLKODIV_SHIFT;
+ break;
+ default:
+ dev_err(&spi->dev,
+ "Unsupported output clock divider %i\n",
+ priv->config.clock_odiv);
+ return -EINVAL;
+ }
+
+ /* write clock */
+ ret = mcp2517fd_cmd_write(spi, MCP2517FD_OSC, val,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* wait for synced pll/osc/sclk */
+ timeout = jiffies + MCP2517FD_OSC_POLLING_JIFFIES;
+ while (time_before_eq(jiffies, timeout)) {
+ ret = mcp2517fd_cmd_read(spi, MCP2517FD_OSC,
+ &priv->regs.osc,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ if ((priv->regs.osc & waitfor) == waitfor)
+ return 0;
+ }
+
+ dev_err(&spi->dev,
+ "Clock did not lock within the timeout period\n");
+
+ /* we timed out */
+ return -ENODEV;
+}
+
+static int mcp2517fd_setup_fifo(struct net_device *net,
+ struct mcp2517fd_priv *priv,
+ struct spi_device *spi)
+{
+ u32 con_val = priv->regs.con & (~CAN_CON_REQOP_MASK);
+ u32 val, available_memory, tx_memory_used;
+ int ret;
+ int i, fifo;
+
+ /* clear all filter */
+ for (i = 0; i < 32; i++) {
+ ret = mcp2517fd_cmd_write(spi, CAN_FLTOBJ(i), 0,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ ret = mcp2517fd_cmd_write(spi, CAN_FLTMASK(i), 0,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ ret = mcp2517fd_cmd_write_mask(
+ spi, CAN_FLTCON(i), 0,
+ CAN_FILCON_MASK(i),
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ }
+
+ /* decide on TEF, tx and rx FIFOS */
+ switch (net->mtu) {
+ case CAN_MTU:
+ /* note: if we have INT1 connected to a GPIO
+ * then we could handle this differently and more
+ * efficiently
+ */
+
+ /* mtu is 8 */
+ priv->fifos.payload_size = 8;
+ priv->fifos.payload_mode = CAN_TXQCON_PLSIZE_8;
+
+ /* 7 tx fifos starting at fifo 1 */
+ priv->fifos.tx_fifos = 7;
+
+ /* 24 rx fifos with 1 buffers/fifo */
+ priv->fifos.rx_fifo_depth = 1;
+
+ break;
+ case CANFD_MTU:
+ /* wish there was a way to have hw filters
+ * that can separate based on length ...
+ */
+ /* MTU is 64 */
+ priv->fifos.payload_size = 64;
+ priv->fifos.payload_mode = CAN_TXQCON_PLSIZE_64;
+
+ /* 7 tx fifos */
+ priv->fifos.tx_fifos = 7;
+
+ /* 19 rx fifos with 1 buffer/fifo */
+ priv->fifos.rx_fifo_depth = 1;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* if defined as a module modify the number of tx_fifos */
+ if (tx_fifos) {
+ dev_info(&spi->dev,
+ "Using %i tx-fifos as per module parameter\n",
+ tx_fifos);
+ priv->fifos.tx_fifos = tx_fifos;
+ }
+
+ /* check range - we need 1 RX-fifo and one tef-fifo, hence 30 */
+ if (priv->fifos.tx_fifos > 30) {
+ dev_err(&spi->dev,
+ "There is an absolute maximum of 30 tx-fifos\n");
+ return -EINVAL;
+ }
+
+ tx_memory_used = priv->fifos.tx_fifos * (
+ sizeof(struct mcp2517fd_obj_tef) +
+ sizeof(struct mcp2517fd_obj_tx) +
+ priv->fifos.payload_size
+ );
+ /* check that we are not exceeding memory limits with 1 RX buffer */
+ if (tx_memory_used + (sizeof(struct mcp2517fd_obj_rx) +
+ priv->fifos.payload_size) > MCP2517FD_BUFFER_TXRX_SIZE) {
+ dev_err(&spi->dev,
+ "Configured %i tx-fifos exceeds available memory already\n",
+ priv->fifos.tx_fifos);
+ return -EINVAL;
+ }
+
+ /* calculate possible amount of RX fifos */
+ available_memory = MCP2517FD_BUFFER_TXRX_SIZE - tx_memory_used;
+
+ priv->fifos.rx_fifos = available_memory /
+ (sizeof(struct mcp2517fd_obj_rx) +
+ priv->fifos.payload_size) /
+ priv->fifos.rx_fifo_depth;
+
+ /* we only support 31 FIFOS in total (TEF = FIFO0),
+ * so modify rx accordingly
+ */
+ if (priv->fifos.tx_fifos + priv->fifos.rx_fifos > 31)
+ priv->fifos.rx_fifos = 31 - priv->fifos.tx_fifos;
+
+ /* calculate rx/tx fifo start */
+ priv->fifos.rx_fifo_start = 1;
+ priv->fifos.tx_fifo_start =
+ priv->fifos.rx_fifo_start + priv->fifos.rx_fifos;
+
+ /* set up TEF SIZE to the number of tx_fifos and IRQ */
+ priv->regs.tefcon = CAN_TEFCON_FRESET |
+ CAN_TEFCON_TEFNEIE |
+ CAN_TEFCON_TEFTSEN |
+ ((priv->fifos.tx_fifos - 1) << CAN_TEFCON_FSIZE_SHIFT),
+
+ ret = mcp2517fd_cmd_write(
+ spi, CAN_TEFCON,
+ priv->regs.tefcon,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* set up tx fifos */
+ val = CAN_FIFOCON_TXEN |
+ CAN_FIFOCON_FRESET | /* reset FIFO */
+ (priv->fifos.payload_mode << CAN_FIFOCON_PLSIZE_SHIFT) |
+ (0 << CAN_FIFOCON_FSIZE_SHIFT); /* 1 FIFO only */
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+ val |= CAN_FIFOCON_TXAT_ONE_SHOT <<
+ CAN_FIFOCON_TXAT_SHIFT;
+ else
+ val |= CAN_FIFOCON_TXAT_UNLIMITED <<
+ CAN_FIFOCON_TXAT_SHIFT;
+
+ for (i = 0; i < priv->fifos.tx_fifos; i++) {
+ fifo = priv->fifos.tx_fifo_start + i;
+ ret = mcp2517fd_cmd_write(
+ spi, CAN_FIFOCON(fifo),
+ val | (fifo << CAN_FIFOCON_TXPRI_SHIFT),
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ priv->fifos.tx_fifo_mask |= BIT(fifo);
+ }
+
+ /* now set up RX FIFO */
+ for (i = 0; i < priv->fifos.rx_fifos; i++) {
+ fifo = priv->fifos.rx_fifo_start + i;
+ /* prepare the fifo itself */
+ ret = mcp2517fd_cmd_write(
+ spi, CAN_FIFOCON(fifo),
+ (priv->fifos.payload_mode <<
+ CAN_FIFOCON_PLSIZE_SHIFT) |
+ ((priv->fifos.rx_fifo_depth - 1) <<
+ CAN_FIFOCON_FSIZE_SHIFT) |
+ CAN_FIFOCON_RXTSEN | /* RX timestamps */
+ CAN_FIFOCON_FRESET | /* reset FIFO */
+ CAN_FIFOCON_TFERFFIE | /* FIFO Full */
+ CAN_FIFOCON_TFHRFHIE | /* FIFO Half Full*/
+ CAN_FIFOCON_TFNRFNIE | /* FIFO not empty */
+ /* on the last fifo add overflow flag */
+ ((i == priv->fifos.rx_fifos - 1) ?
+ CAN_FIFOCON_RXOVIE : 0),
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ /* prepare the rx filter config: filter i directs to fifo
+ * FLTMSK and FLTOBJ are 0 already, so they match everything
+ */
+ ret = mcp2517fd_cmd_write_mask(
+ spi, CAN_FLTCON(i),
+ CAN_FIFOCON_FLTEN(i) | (fifo << CAN_FILCON_SHIFT(i)),
+ CAN_FIFOCON_FLTEN(i) | CAN_FILCON_MASK(i),
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ priv->fifos.rx_fifo_mask |= BIT(fifo);
+ }
+
+ /* we need to move out of CONFIG mode shortly to get the addresses */
+ ret = mcp2517fd_cmd_write(
+ spi, CAN_CON, con_val |
+ (CAN_CON_MODE_INTERNAL_LOOPBACK << CAN_CON_REQOP_SHIFT),
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* for the TEF fifo */
+ ret = mcp2517fd_cmd_read(spi, CAN_TEFUA, &val,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ priv->fifos.tef_address = val;
+ priv->fifos.tef_address_start = val;
+ priv->fifos.tef_address_end = priv->fifos.tef_address_start +
+ (priv->fifos.tx_fifos) * sizeof(struct mcp2517fd_obj_tef) -
+ 1;
+
+ /* get all the relevant addresses for the transmit fifos */
+ for (i = 0; i < priv->fifos.tx_fifos; i++) {
+ fifo = priv->fifos.tx_fifo_start + i;
+ ret = mcp2517fd_cmd_read(spi, CAN_FIFOUA(fifo),
+ &val, priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ priv->fifos.fifo_address[fifo] = val;
+ }
+
+ /* and prepare the spi_messages */
+ ret = mcp2517fd_fill_spi_transmit_fifos(priv);
+ if (ret)
+ return ret;
+
+ /* get all the relevant addresses for the rx fifos */
+ for (i = 0; i < priv->fifos.rx_fifos; i++) {
+ fifo = priv->fifos.rx_fifo_start + i;
+ ret = mcp2517fd_cmd_read(spi, CAN_FIFOUA(fifo),
+ &val, priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ priv->fifos.fifo_address[fifo] = val;
+ }
+
+ /* now get back into config mode */
+ ret = mcp2517fd_cmd_write(
+ spi, CAN_CON, con_val |
+ (CAN_CON_MODE_CONFIG << CAN_CON_REQOP_SHIFT),
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mcp2517fd_setup(struct net_device *net,
+ struct mcp2517fd_priv *priv,
+ struct spi_device *spi)
+{
+ u32 val;
+ int ret;
+
+ /* set up pll/clock if required */
+ ret = mcp2517fd_setup_osc(spi);
+ if (ret)
+ return ret;
+
+ /* set up RAM ECC (but for now without interrupts) */
+ priv->regs.ecccon = MCP2517FD_ECCCON_ECCEN;
+ ret = mcp2517fd_cmd_write(spi, MCP2517FD_ECCCON,
+ priv->regs.ecccon,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* GPIO handling - could expose this as gpios*/
+ val = 0; /* PUSHPULL INT , TXCAN PUSH/PULL, no Standby */
+
+ /* SOF/CLOCKOUT pin 3 */
+ if (priv->config.clock_odiv < 0)
+ val |= MCP2517FD_IOCON_SOF;
+ /* GPIO0 - pin 9 */
+ switch (priv->config.gpio0_mode) {
+ case gpio_mode_standby:
+ case gpio_mode_int: /* asserted low on TXIF */
+ case gpio_mode_out_low:
+ case gpio_mode_out_high:
+ case gpio_mode_in:
+ val |= priv->config.gpio0_mode;
+ break;
+ default: /* GPIO IN */
+ dev_err(&spi->dev,
+ "GPIO1 does not support mode %08x\n",
+ priv->config.gpio0_mode);
+ return -EINVAL;
+ }
+ /* GPIO1 - pin 8 */
+ switch (priv->config.gpio1_mode) {
+ case gpio_mode_standby:
+ dev_err(&spi->dev,
+ "GPIO1 does not support transceiver standby\n");
+ return -EINVAL;
+ case gpio_mode_int: /* asserted low on RXIF */
+ case gpio_mode_out_low:
+ case gpio_mode_out_high:
+ case gpio_mode_in:
+ val |= priv->config.gpio1_mode << 1;
+ break;
+ default:
+ dev_err(&spi->dev,
+ "GPIO1 does not support mode %08x\n",
+ priv->config.gpio0_mode);
+ return -EINVAL;
+ }
+ /* INT/GPIO pins as open drain */
+ if (priv->config.gpio_opendrain)
+ val |= MCP2517FD_IOCON_INTOD;
+ if (priv->config.txcan_opendrain)
+ val |= MCP2517FD_IOCON_TXCANOD; /* OpenDrain TXCAN */
+ if (priv->config.int_opendrain)
+ val |= MCP2517FD_IOCON_INTOD; /* OpenDrain INT pins */
+
+ priv->regs.iocon = val;
+ ret = mcp2517fd_cmd_write(spi, MCP2517FD_IOCON, val,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* set up Transmitter Delay compensation */
+ priv->regs.tdc = CAN_TDC_EDGFLTEN;
+ ret = mcp2517fd_cmd_write(spi, CAN_TDC, priv->regs.tdc,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* time stamp control register - 1ns resolution, but disabled */
+ ret = mcp2517fd_cmd_write(spi, CAN_TBC, 0,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ priv->regs.tscon = CAN_TSCON_TBCEN |
+ ((priv->can.clock.freq / 1000000)
+ << CAN_TSCON_TBCPRE_SHIFT);
+ ret = mcp2517fd_cmd_write(spi, CAN_TSCON,
+ priv->regs.tscon,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* setup value of con_register */
+ priv->regs.con = CAN_CON_STEF /* enable TEF */;
+ /* transmissin bandwidth sharing bits */
+ if (bw_sharing_log2bits < 12)
+ bw_sharing_log2bits = 12;
+ priv->regs.con |= bw_sharing_log2bits << CAN_CON_TXBWS_SHIFT;
+ /* non iso FD mode */
+ if (!(priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO))
+ priv->regs.con |= CAN_CON_ISOCRCEN;
+ /* one shot */
+ if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+ priv->regs.con |= CAN_CON_RTXAT;
+
+ /* setup fifos - this also puts the system into sleep mode */
+ return mcp2517fd_setup_fifo(net, priv, spi);
+}
+
+static int mcp2517fd_open(struct net_device *net)
+{
+ struct mcp2517fd_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ int ret;
+
+ ret = open_candev(net);
+ if (ret) {
+ dev_err(&spi->dev, "unable to set initial baudrate!\n");
+ return ret;
+ }
+
+ mcp2517fd_power_enable(priv->transceiver, 1);
+
+ priv->force_quit = 0;
+
+ priv->stats.irq_state = 0;
+ priv->stats.irq_calls = 0;
+ priv->stats.irq_loops = 0;
+
+ priv->force_quit = 0;
+ ret = request_threaded_irq(spi->irq, NULL,
+ mcp2517fd_can_ist,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ DEVICE_NAME, priv);
+ if (ret) {
+ dev_err(&spi->dev, "failed to acquire irq %d - %i\n",
+ spi->irq, ret);
+ mcp2517fd_power_enable(priv->transceiver, 0);
+ close_candev(net);
+ return ret;
+ }
+
+ /* wake from sleep if necessary */
+ ret = mcp2517fd_hw_wake(spi);
+ if (ret)
+ goto open_clean;
+
+ ret = mcp2517fd_hw_probe(spi);
+ if (ret) {
+ dev_err(&spi->dev,
+ "HW Probe failed, but was working earlier!\n");
+ goto open_clean;
+ }
+
+ ret = mcp2517fd_setup(net, priv, spi);
+ if (ret)
+ goto open_clean;
+
+ mcp2517fd_do_set_nominal_bittiming(net);
+ mcp2517fd_do_set_data_bittiming(net);
+
+ ret = mcp2517fd_set_normal_mode(spi);
+ if (ret)
+ goto open_clean;
+
+ /* only now enable the interrupt on the controller */
+ ret = mcp2517fd_enable_interrupts(spi,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ goto open_clean;
+
+ can_led_event(net, CAN_LED_EVENT_OPEN);
+
+ priv->tx_queue_status = 1;
+ netif_wake_queue(net);
+
+ return 0;
+
+open_clean:
+ mcp2517fd_disable_interrupts(spi, priv->spi_setup_speed_hz);
+ free_irq(spi->irq, priv);
+ mcp2517fd_hw_sleep(spi);
+ mcp2517fd_power_enable(priv->transceiver, 0);
+ close_candev(net);
+
+ return ret;
+}
+
+static void mcp2517fd_clean(struct net_device *net)
+{
+ struct mcp2517fd_priv *priv = netdev_priv(net);
+ int i;
+
+ for (i = 0; i < priv->fifos.tx_fifos; i++) {
+ if (priv->fifos.tx_pending_mask & BIT(i)) {
+ can_free_echo_skb(priv->net, 0);
+ priv->net->stats.tx_errors++;
+ }
+ }
+
+ priv->fifos.tx_pending_mask = 0;
+}
+
+static int mcp2517fd_stop(struct net_device *net)
+{
+ struct mcp2517fd_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ close_candev(net);
+
+ kfree(priv->spi_transmit_fifos);
+ priv->spi_transmit_fifos = NULL;
+
+ priv->force_quit = 1;
+ free_irq(spi->irq, priv);
+
+ /* Disable and clear pending interrupts */
+ mcp2517fd_disable_interrupts(spi, priv->spi_setup_speed_hz);
+
+ mcp2517fd_clean(net);
+
+ mcp2517fd_hw_sleep(spi);
+
+ mcp2517fd_power_enable(priv->transceiver, 0);
+
+ priv->can.state = CAN_STATE_STOPPED;
+
+ can_led_event(net, CAN_LED_EVENT_STOP);
+
+ return 0;
+}
+
+static const struct net_device_ops mcp2517fd_netdev_ops = {
+ .ndo_open = mcp2517fd_open,
+ .ndo_stop = mcp2517fd_stop,
+ .ndo_start_xmit = mcp2517fd_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
+};
+
+static const struct of_device_id mcp2517fd_of_match[] = {
+ {
+ .compatible = "microchip,mcp2517fd",
+ .data = (void *)CAN_MCP2517FD,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mcp2517fd_of_match);
+
+static const struct spi_device_id mcp2517fd_id_table[] = {
+ {
+ .name = "mcp2517fd",
+ .driver_data = (kernel_ulong_t)CAN_MCP2517FD,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, mcp2517fd_id_table);
+
+static void mcp2517fd_debugfs_add(struct mcp2517fd_priv *priv)
+{
+#if defined(CONFIG_DEBUG_FS)
+ struct dentry *root, *fifousage, *fifoaddr, *rx, *tx, *status, *regs;
+ char name[32];
+ int i;
+
+ /* create the net device name */
+ snprintf(name, sizeof(name), DEVICE_NAME "-%s", priv->net->name);
+ priv->debugfs_dir = debugfs_create_dir(name, NULL);
+ root = priv->debugfs_dir;
+
+ rx = debugfs_create_dir("rx", root);
+ tx = debugfs_create_dir("tx", root);
+ fifousage = debugfs_create_dir("fifo_usage", root);
+ fifoaddr = debugfs_create_dir("fifo_address", root);
+ status = debugfs_create_dir("status", root);
+ regs = debugfs_create_dir("regs", root);
+
+ /* add spi speed info */
+ debugfs_create_u32("spi_setup_speed_hz", 0444, root,
+ &priv->spi_setup_speed_hz);
+ debugfs_create_u32("spi_speed_hz", 0444, root,
+ &priv->spi_speed_hz);
+
+ /* add irq state info */
+ debugfs_create_u64("irq_calls", 0444, root, &priv->stats.irq_calls);
+ debugfs_create_u64("irq_loops", 0444, root, &priv->stats.irq_loops);
+ debugfs_create_u32("irq_state", 0444, root, &priv->stats.irq_state);
+
+ /* export the status structure */
+ debugfs_create_x32("intf", 0444, status, &priv->status.intf);
+ debugfs_create_x32("rx_if", 0444, status, &priv->status.rxif);
+ debugfs_create_x32("tx_if", 0444, status, &priv->status.txif);
+ debugfs_create_x32("rx_ovif", 0444, status, &priv->status.rxovif);
+ debugfs_create_x32("tx_atif", 0444, status, &priv->status.txatif);
+ debugfs_create_x32("tx_req", 0444, status, &priv->status.txreq);
+ debugfs_create_x32("trec", 0444, status, &priv->status.trec);
+ debugfs_create_x32("bdiag0", 0444, status, &priv->status.bdiag0);
+ debugfs_create_x32("bdiag1", 0444, status, &priv->status.bdiag1);
+
+ /* some configuration registers */
+ debugfs_create_x32("con", 0444, regs, &priv->regs.con);
+ debugfs_create_x32("ecccon", 0444, regs, &priv->regs.ecccon);
+ debugfs_create_x32("osc", 0444, regs, &priv->regs.osc);
+ debugfs_create_x32("iocon", 0444, regs, &priv->regs.iocon);
+ debugfs_create_x32("tdc", 0444, regs, &priv->regs.tdc);
+ debugfs_create_x32("tscon", 0444, regs, &priv->regs.tscon);
+ debugfs_create_x32("nbtcfg", 0444, regs, &priv->regs.nbtcfg);
+ debugfs_create_x32("dbtcfg", 0444, regs, &priv->regs.dbtcfg);
+
+ /* information on fifos */
+ debugfs_create_u32("fifo_start", 0444, rx,
+ &priv->fifos.rx_fifo_start);
+ debugfs_create_u32("fifo_count", 0444, rx,
+ &priv->fifos.rx_fifos);
+ debugfs_create_x32("fifo_mask", 0444, rx,
+ &priv->fifos.rx_fifo_mask);
+ debugfs_create_u64("rx_overflow", 0444, rx,
+ &priv->stats.rx_overflow);
+
+ debugfs_create_u32("fifo_start", 0444, tx,
+ &priv->fifos.tx_fifo_start);
+ debugfs_create_u32("fifo_count", 0444, tx,
+ &priv->fifos.tx_fifos);
+ debugfs_create_x32("fifo_mask", 0444, tx,
+ &priv->fifos.tx_fifo_mask);
+ debugfs_create_x32("fifo_pending", 0444, tx,
+ &priv->fifos.tx_pending_mask);
+ debugfs_create_x32("fifo_submitted", 0444, tx,
+ &priv->fifos.tx_submitted_mask);
+ debugfs_create_x32("fifo_processed", 0444, tx,
+ &priv->fifos.tx_processed_mask);
+ debugfs_create_u32("queue_status", 0444, tx,
+ &priv->tx_queue_status);
+
+ debugfs_create_u32("fifo_max_payload_size", 0444, root,
+ &priv->fifos.payload_size);
+
+ /* statistics on fifo buffer usage */
+ for (i = 1; i < 32; i++) {
+ snprintf(name, sizeof(name), "%02i", i);
+ debugfs_create_u64(name, 0444, fifousage,
+ &priv->stats.fifo_usage[i]);
+ debugfs_create_u32(name, 0444, fifoaddr,
+ &priv->fifos.fifo_address[i]);
+ }
+
+#endif
+}
+
+static void mcp2517fd_debugfs_remove(struct mcp2517fd_priv *priv)
+{
+#if defined(CONFIG_DEBUG_FS)
+ debugfs_remove_recursive(priv->debugfs_dir);
+#endif
+}
+
+int mcp2517fd_of_parse(struct mcp2517fd_priv *priv)
+{
+#ifdef CONFIG_OF_DYNAMIC
+ struct spi_device *spi = priv->spi;
+ const struct device_node *np = spi->dev.of_node;
+ u32 val;
+ int ret;
+
+ ret = of_property_read_u32_index(np, "microchip,clock_div",
+ 0, &val);
+ if (!ret) {
+ switch (val) {
+ case 1:
+ priv->config.clock_div2 = false;
+ break;
+ case 2:
+ priv->config.clock_div2 = true;
+ break;
+ default:
+ dev_err(&spi->dev,
+ "Invalid value in device tree for microchip,clock_div: %u - valid_values: 1, 2\n",
+ val);
+ return -EINVAL;
+ }
+ }
+
+ ret = of_property_read_u32_index(np, "microchip,clock_out_div",
+ 0, &val);
+ if (!ret) {
+ switch (val) {
+ case 0:
+ case 1:
+ case 2:
+ case 4:
+ case 10:
+ priv->config.clock_odiv = val;
+ break;
+ default:
+ dev_err(&spi->dev,
+ "Invalid value in device tree for microchip,clock_out_div: %u - valid values: 0, 1, 2, 4, 10\n",
+ val);
+ return -EINVAL;
+ }
+ }
+
+ ret = of_property_read_u32_index(np, "microchip,gpio0_mode",
+ 0, &val);
+ if (!ret) {
+ switch (val) {
+ case 0:
+ priv->config.gpio0_mode = gpio_mode_in;
+ break;
+ case 1:
+ priv->config.gpio0_mode = gpio_mode_int;
+ break;
+ case 2:
+ priv->config.gpio0_mode = gpio_mode_out_low;
+ break;
+ case 3:
+ priv->config.gpio0_mode = gpio_mode_out_high;
+ break;
+ case 4:
+ priv->config.gpio0_mode = gpio_mode_standby;
+ break;
+ default:
+ dev_err(&spi->dev,
+ "Invalid value in device tree for microchip,gpio0_mode: %u - valid values: 0, 1, 2, 3, 4\n",
+ val);
+ return -EINVAL;
+ }
+ } else {
+ priv->config.gpio0_mode = gpio_mode_in;
+ }
+
+ ret = of_property_read_u32_index(np, "microchip,gpio1_mode",
+ 0, &val);
+ if (!ret) {
+ switch (val) {
+ case 0:
+ priv->config.gpio1_mode = gpio_mode_in;
+ break;
+ case 1:
+ priv->config.gpio1_mode = gpio_mode_int;
+ break;
+ case 2:
+ priv->config.gpio1_mode = gpio_mode_out_low;
+ break;
+ case 3:
+ priv->config.gpio1_mode = gpio_mode_out_high;
+ break;
+ default:
+ dev_err(&spi->dev,
+ "Invalid value in device tree for microchip,gpio1_mode: %u - valif_values: 0, 1, 2, 3, 4\n",
+ val);
+ return -EINVAL;
+ }
+ } else {
+ priv->config.gpio1_mode = gpio_mode_in;
+ }
+
+ priv->config.gpio_opendrain = of_property_read_bool(
+ np, "microchip,gpio_opendrain");
+
+ priv->config.txcan_opendrain = of_property_read_bool(
+ np, "microchip,txcan_opendrain");
+
+ priv->config.int_opendrain = of_property_read_bool(
+ np, "microchip,int_opendrain");
+#endif
+ return 0;
+}
+
+static int mcp2517fd_can_probe(struct spi_device *spi)
+{
+ const struct of_device_id *of_id =
+ of_match_device(mcp2517fd_of_match, &spi->dev);
+ struct net_device *net;
+ struct mcp2517fd_priv *priv;
+ struct clk *clk;
+ int ret, freq;
+
+ /* as irq_create_fwspec_mapping() can return 0, check for it */
+ if (spi->irq <= 0) {
+ dev_err(&spi->dev, "no valid irq line defined: irq = %i\n",
+ spi->irq);
+ return -EINVAL;
+ }
+
+ clk = devm_clk_get(&spi->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+ freq = clk_get_rate(clk);
+
+ if (freq < MCP2517FD_MIN_CLOCK_FREQUENCY ||
+ freq > MCP2517FD_MAX_CLOCK_FREQUENCY) {
+ dev_err(&spi->dev,
+ "Clock frequency %i is not in range\n", freq);
+ return -ERANGE;
+ }
+
+ /* Allocate can/net device */
+ net = alloc_candev(sizeof(*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 = &mcp2517fd_netdev_ops;
+ net->flags |= IFF_ECHO;
+
+ priv = netdev_priv(net);
+ priv->can.bittiming_const = &mcp2517fd_nominal_bittiming_const;
+ priv->can.do_set_bittiming = &mcp2517fd_do_set_nominal_bittiming;
+ priv->can.data_bittiming_const = &mcp2517fd_data_bittiming_const;
+ priv->can.do_set_data_bittiming = &mcp2517fd_do_set_data_bittiming;
+ priv->can.do_set_mode = mcp2517fd_do_set_mode;
+ priv->can.do_get_berr_counter = mcp2517fd_get_berr_counter;
+
+ priv->can.ctrlmode_supported =
+ CAN_CTRLMODE_FD |
+ CAN_CTRLMODE_LOOPBACK |
+ CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_BERR_REPORTING |
+ CAN_CTRLMODE_FD_NON_ISO |
+ CAN_CTRLMODE_ONE_SHOT;
+
+ if (of_id)
+ priv->model = (enum mcp2517fd_model)of_id->data;
+ else
+ priv->model = spi_get_device_id(spi)->driver_data;
+
+ priv->spi = spi;
+ priv->net = net;
+ priv->clk = clk;
+
+ spi_set_drvdata(spi, priv);
+
+ /* set up gpio modes as GPIO INT */
+ priv->config.gpio0_mode = gpio_mode_int;
+ priv->config.gpio1_mode = gpio_mode_int;
+ /* all by default as push/pull */
+ priv->config.gpio_opendrain = false;
+ priv->config.txcan_opendrain = false;
+ priv->config.int_opendrain = false;
+ /* do not use the SCK clock divider of 2 */
+ priv->config.clock_div2 = false;
+ /* clock output is divided by 10 */
+ priv->config.clock_odiv = 10;
+
+ /* as a first guess we assume we are in CAN_CON_MODE_SLEEP
+ * this is how we leave the controller when removing ourselves
+ */
+ priv->active_can_mode = CAN_CON_MODE_SLEEP;
+
+ /* if we have a clock that is smaller then 4MHz, then enable the pll */
+ priv->config.clock_pll =
+ (freq <= MCP2517FD_AUTO_PLL_MAX_CLOCK_FREQUENCY);
+
+ /* check in device tree for overrrides */
+ ret = mcp2517fd_of_parse(priv);
+ if (ret)
+ return ret;
+
+ /* decide on real can clock rate */
+ priv->can.clock.freq = freq;
+ if (priv->config.clock_pll) {
+ priv->can.clock.freq *= MCP2517FD_PLL_MULTIPLIER;
+ if (priv->can.clock.freq > MCP2517FD_MAX_CLOCK_FREQUENCY) {
+ dev_err(&spi->dev,
+ "PLL clock frequency %i would exceed limit\n",
+ priv->can.clock.freq
+ );
+ return -EINVAL;
+ }
+ }
+ if (priv->config.clock_div2)
+ priv->can.clock.freq /= MCP2517FD_SCLK_DIVIDER;
+
+ /* calclculate the clock frequencies to use */
+ priv->spi_setup_speed_hz = freq / 2;
+ priv->spi_speed_hz = priv->can.clock.freq / 2;
+ if (priv->config.clock_div2) {
+ priv->spi_setup_speed_hz /= MCP2517FD_SCLK_DIVIDER;
+ priv->spi_speed_hz /= MCP2517FD_SCLK_DIVIDER;
+ }
+
+ if (spi->max_speed_hz) {
+ priv->spi_setup_speed_hz = min_t(int,
+ priv->spi_setup_speed_hz,
+ spi->max_speed_hz);
+ priv->spi_speed_hz = min_t(int,
+ priv->spi_speed_hz,
+ spi->max_speed_hz);
+ }
+
+ /* 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 = mcp2517fd_power_enable(priv->power, 1);
+ if (ret)
+ goto out_clk;
+
+ SET_NETDEV_DEV(net, &spi->dev);
+
+ ret = mcp2517fd_hw_probe(spi);
+ /* on error retry a second time */
+ if (ret == -ENODEV) {
+ ret = mcp2517fd_hw_probe(spi);
+ if (!ret)
+ dev_info(&spi->dev,
+ "found device only during retry\n");
+ }
+ if (ret) {
+ if (ret == -ENODEV)
+ dev_err(&spi->dev,
+ "Cannot initialize MCP%x. Wrong wiring?\n",
+ priv->model);
+ goto error_probe;
+ }
+
+ mcp2517fd_hw_sleep(spi);
+
+ ret = register_candev(net);
+ if (ret)
+ goto error_probe;
+
+ /* register debugfs */
+ mcp2517fd_debugfs_add(priv);
+
+ devm_can_led_init(net);
+
+ netdev_info(net, "MCP%x successfully initialized.\n", priv->model);
+ return 0;
+
+error_probe:
+ mcp2517fd_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 mcp2517fd_can_remove(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+
+ mcp2517fd_debugfs_remove(priv);
+
+ unregister_candev(net);
+
+ mcp2517fd_power_enable(priv->power, 0);
+
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+
+ free_candev(net);
+
+ return 0;
+}
+
+static int __maybe_unused mcp2517fd_can_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+
+ priv->force_quit = 1;
+ disable_irq(spi->irq);
+
+ if (netif_running(net)) {
+ netif_device_detach(net);
+
+ mcp2517fd_hw_sleep(spi);
+ mcp2517fd_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 mcp2517fd_can_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+
+ if (priv->after_suspend & AFTER_SUSPEND_POWER)
+ mcp2517fd_power_enable(priv->power, 1);
+
+ if (priv->after_suspend & AFTER_SUSPEND_UP)
+ mcp2517fd_power_enable(priv->transceiver, 1);
+ else
+ priv->after_suspend = 0;
+
+ priv->force_quit = 0;
+
+ enable_irq(spi->irq);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mcp2517fd_can_pm_ops, mcp2517fd_can_suspend,
+ mcp2517fd_can_resume);
+
+static struct spi_driver mcp2517fd_can_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = mcp2517fd_of_match,
+ .pm = &mcp2517fd_can_pm_ops,
+ },
+ .id_table = mcp2517fd_id_table,
+ .probe = mcp2517fd_can_probe,
+ .remove = mcp2517fd_can_remove,
+};
+module_spi_driver(mcp2517fd_can_driver);
+
+MODULE_AUTHOR("Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>");
+MODULE_DESCRIPTION("Microchip 2517FD CAN driver");
+MODULE_LICENSE("GPL v2");
--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 1/2] dt-binding: can: mcp2517fd: document device tree bindings
From: kernel-TqfNSX0MhmxHKSADF0wUEw @ 2017-11-24 18:35 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde, Rob Herring, Mark Rutland,
linux-can-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Martin Sperl
In-Reply-To: <20171124183509.12810-1-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
Add device-tree bindings for Microcip CanFD Controller mcp2517fd
Signed-off-by: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
---
.../bindings/net/can/microchip,mcp2517fd.txt | 47 ++++++++++++++++++++++
1 file changed, 47 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/can/microchip,mcp2517fd.txt
diff --git a/Documentation/devicetree/bindings/net/can/microchip,mcp2517fd.txt b/Documentation/devicetree/bindings/net/can/microchip,mcp2517fd.txt
new file mode 100644
index 000000000000..96cbf0c96895
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/can/microchip,mcp2517fd.txt
@@ -0,0 +1,47 @@
+* Microchip MCP2517 stand-alone CAN controller device tree bindings
+
+Required properties:
+ - compatible: Should be one of the following:
+ - "microchip,mcp2517fd" for MCP2517fd.
+ - 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.
+ - microchip,clock_out_div = <0|1|2|4|10>: Clock output pin divider
+ 0 = Start of Frame output
+ default: 10
+ - microchip,clock_div = <1|2>: internal clock divider - default 1
+ - microchip,gpio_opendrain: gpio (int0,1) in open drain mode
+ instead of default push/pull
+ - microchip,int_opendrain: int pin in open drain mode
+ instead of default push/pull
+ - microchip,txcan_opendrain: txcan pin in open drain mode
+ instead of default push/pull
+ - microchip,gpio0_mode : gpio mode functionality
+ 0 = input
+ 1 = TX interrupt output - default
+ 2 = output default low
+ 3 = output default high
+ 4 = (tx) transceiver standby
+ - microchip,gpio1_mode : gpio mode functionality
+ 0 = input - default
+ 1 = RX interrupt output - default
+ 2 = output default low
+ 3 = output default high
+
+Example:
+ can0: can@1 {
+ compatible = "microchip,mcp2515";
+ reg = <1>;
+ clocks = <&clk24m>;
+ interrupt-parent = <&gpio4>;
+ interrupts = <13 0x8>;
+ vdd-supply = <®5v0>;
+ xceiver-supply = <®5v0>;
+ microchip,gpio0_mode = <4>;
+ microchip,gpio0_mode = <1>;
+ };
--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 0/2] Microchip mcp2517fd can controller driver
From: kernel-TqfNSX0MhmxHKSADF0wUEw @ 2017-11-24 18:35 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde, Rob Herring, Mark Rutland,
linux-can-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Martin Sperl
From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
This patchset adds a driver for the mcp2517fd CanFD controller.
Most of the features of the controller are supported by this driver release.
The controller includes a few features that the current core Can(FD)
implementation does not support:
* Transmit Bandwidth Sharing bits
- this waits for a configurable number of syncronization bits before
atempting to transmit the next frame - for the moment controllable
via a module parameter
* SID11 with CanFD frames
- at this moment not supported by driver
* 3 transmit attempts in addition to ONE_SHOT
* transmitter delay compensation configurations
* micro second exact transmission and reception timestamps
- used internally by driver
* variable number of tx-fifos
- exposed via module parameter at this moment
The driver has been heavily optimized so that it can handle
a 100% utilized 1MHz Can-bus (with 11 bit can frames with DLC=0)
even on less powerfull SOCs like the raspberry pi 1 without
dropping frames due to driver/spi latencies (still dropps
are observed in the can/network stack).
To achive this the driver includes several code optimization
Two of which prefers continuous long spi transfers over multiple
small individual spi transfers of variable length.
This is an optimization that would favour spi dma transfers instead
of more expensive PIO in typical SPI controllers.
Both of them can get explicitly enabled by the use of a module parameter.
One of those is automatically used in the case of Can2.0 only bus
configurations.
Finer details of the implementation and rational is included with
the driver code it self for future reference.
The driver has also been tested for basic CanFD transfers with
BRS up to 5.7Mhz data bitrates - the available transceiver did
not allow for faster rates...
The driver itself exposes lots of internal data/statistics via
debugfs.
The driver implements a lock-less design for transmissions
making use instead of prepared spi messages submitted via spi_async
for transmission in the start_xmit_start code without requireing
an extra workqueue and the corresponding latencies.
Martin Sperl (2):
dt-binding: can: mcp2517fd: document device tree bindings
can: mcp2517fd: Add Microchip mcp2517fd CAN FD driver
.../bindings/net/can/microchip,mcp2517fd.txt | 47 +
drivers/net/can/spi/Kconfig | 6 +
drivers/net/can/spi/Makefile | 1 +
drivers/net/can/spi/mcp2517fd.c | 3733 ++++++++++++++++++++
4 files changed, 3787 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/can/microchip,mcp2517fd.txt
create mode 100644 drivers/net/can/spi/mcp2517fd.c
--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 2/2] can: flexcan: adding platform specific details for LS1021A
From: Mirza Krak @ 2017-11-24 16:02 UTC (permalink / raw)
To: Wolfgang Grandegger
Cc: Marc Kleine-Budde, ZHU Yi (ST-FIR/ENG1-Zhu), Pankaj Bansal,
linux-can@vger.kernel.org, Varun Sethi, Poonam Aggrwal,
Stefan Agner
In-Reply-To: <e318ca19-bb08-f537-ed7a-4f8008b9a341@grandegger.com>
2017-11-23 22:05 GMT+01:00 Wolfgang Grandegger <wg@grandegger.com>:
> Hello Mirza,
>
> Am 23.11.2017 um 21:17 schrieb Mirza Krak:
>>
>> 2017-11-23 21:12 GMT+01:00 Mirza Krak <mirza.krak@gmail.com>:
>>>
>>> On Nov 22, 2017 13:00, "Marc Kleine-Budde" <mkl@pengutronix.de> wrote:
>>>
>>
>> < snip >
>>
>>>
>>> Cc'ed Stefan Agner - maybe he has access to this SoC.
>>>
>>>> @Marc, @Wolfgang,
>>>> What do you think? Or shall we fix first and then wait?
>>>
>>>
>>> Let's wait if Stefan can test, otherwise just fix.
>>>
>>>
>>> I have access to an vf610 and can test CAN stuff.
>>>
>>> What is it more specifically we want to test? I read the thread but not
>>> completely clear to me.
>
>
> We want to know how the reporting of CAN error state changes "error-active"
> -> "warning" -> "error-passive" -> "bus-off" and back is working on that
> hardware.
>
> You can test such state changes as show below. First setup the interface and
> start a session calling "candump" to report the state changes:
>
> # ip link set can0 up type can bitrate 500000 restart-ms 100
> # candump -td -e any,0:0,#FFFFFFFF
>
> Then
>
> 1. disconnect the cable and send messages with "cangen"
>
> # cangen -i can0
>
> "candump" should then report the error state changes "error-active" ->
> "warning" -> "error-passive". If you don't see "error-passive", the hardware
> needs the FLEXCAN_QUIRK_BROKEN_PERR_STATE as well. Then reconnect the cable
> and send messages till the "error active" state is reached (again with
> "cangen").
This was the result of this test:
root@colibri-vf:~# ./candump -td -e can0,0:0,#FFFFFFFF
(000.000000) can0 0C8 [8] E7 D5 75 20 1C F8 AA 6D
(000.200201) can0 7CD [8] 58 5F B1 1E D7 AB 3C 15
(000.200123) can0 7FD [8] EC 43 EF 11 BB 76 50 4F
(000.200119) can0 20D [8] 64 E0 A6 74 A2 A4 22 42
(000.200115) can0 226 [7] CB 4D 12 46 F3 37 F1
(000.200000) can0 42F [0]
(000.200245) can0 0F9 [8] AD 61 16 77 0A 64 50 5C
(000.199983) can0 76E [0]
(000.200206) can0 3CB [6] 53 28 03 34 98 43
(000.200152) can0 230 [8] 70 EF 66 38 2D BA 5D 4D
(000.200106) can0 568 [8] E8 30 AE 1C 75 77 4E 5E
(000.200274) can0 20000004 [8] 00 04 00 00 00 00 00 00 ERRORFRAME
controller-problem{rx-error-warning}
and the statistics
root@colibri-vf:~# ip -d -s link show can0
2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state
UNKNOWN mode DEFAULT group default qlen 10
link/can promiscuity 0
can state ERROR-WARNING (berr-counter tx 0 rx 128) restart-ms 100
bitrate 496240 sample-point 0.857
tq 143 prop-seg 5 phase-seg1 6 phase-seg2 2 sjw 1
flexcan: tseg1 4..16 tseg2 2..8 sjw 1..4 brp 1..256 brp-inc 1
clock 83368421
re-started bus-errors arbit-lost error-warn error-pass bus-off
0 0 0 1 0 0 numtxqueues 1
RX: bytes packets errors dropped overrun mcast
8 1 0 0 0 0
TX: bytes packets errors dropped carrier collsns
69 11 0 0 0 0
I ran out of time as wanted to test a bit more as I was confused that
it is showing RX errors on the first test? I was not able to get it
back to ERROR-ACTIVE from ERROR-WARNING either. But posted result
anyway
> 2. The second test is quite similar. Instead of disconnecting the cable,
> short-circuit the CAN low and high lines. The device should then go to
> "bus-off".
And this one:
root@colibri-vf:~# ./candump -td -e any,0:0,#FFFFFFFF
(000.000000) can0 20000004 [8] 00 20 00 00 00 00 00 00 ERRORFRAME
controller-problem{tx-error-passive}
(000.000809) can0 20000040 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
bus-off
(000.109797) can0 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
restarted-after-bus-off
(000.100551) can0 20000004 [8] 00 20 00 00 00 00 00 00 ERRORFRAME
controller-problem{tx-error-passive}
(000.000823) can0 20000040 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
bus-off
(000.108644) can0 20000100 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
restarted-after-bus-off
(000.090692) can0 20000004 [8] 00 08 00 00 00 00 00 00 ERRORFRAME
controller-problem{tx-error-warning}
(000.000797) can0 20000040 [8] 00 00 00 00 00 00 00 00 ERRORFRAME
bus-off
Will try again next week when I am back at the office.
I ran my tests on 4.14 kernel.
Best Regards
Mirza
^ permalink raw reply
* Re: [PATCH v4 1/6] can: flexcan: Remodel FlexCAN register r/w APIs for big endian FlexCAN controllers.
From: Marc Kleine-Budde @ 2017-11-24 15:02 UTC (permalink / raw)
To: Pankaj Bansal, wg-5Yr1BZd7O62+XT7JhA+gdA,
linux-can-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: V.Sethi-3arQi8VN3Tc, poonam.aggrwal-3arQi8VN3Tc, Bhupesh Sharma,
Sakar Arora
In-Reply-To: <1511529733-27942-2-git-send-email-pankaj.bansal-3arQi8VN3Tc@public.gmane.org>
[-- Attachment #1.1: Type: text/plain, Size: 3511 bytes --]
On 11/24/2017 02:22 PM, Pankaj Bansal wrote:
> The FlexCAN driver assumed that FlexCAN controller is big endian for
> powerpc architecture and little endian for other architectures.
>
> But this may not be the case. FlexCAN controller can be little or big
> endian on any architecture. For e.g. NXP LS1021A ARM based SOC has big
> endian FlexCAN controller.
>
> Therefore, the driver has been modified to add a provision for both
> types of controllers using an additional device tree property. On a
> "fsl,p1010-flexcan" device BE is default, on all other devices LE is.
>
> Big Endian controllers should have "big-endian" set in the device tree.
> check "Documentation/devicetree/bindings/net/can/fsl-flexcan.txt" for
> usage.
>
> This is the standard practice followed in linux. for more info check:
> Documentation/devicetree/bindings/common-properties.txt
>
> Signed-off-by: Pankaj Bansal <pankaj.bansal-3arQi8VN3Tc@public.gmane.org>
> Signed-off-by: Bhupesh Sharma <bhupesh.sharma-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
> Signed-off-by: Sakar Arora <Sakar.Arora-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
> Reviewed-by: Zhengxiong Jin <Jason.Jin-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
> Reviewed-by: Poonam Aggrwal <poonam.aggrwal-3arQi8VN3Tc@public.gmane.org>
> ---
> Changes in v4:
> - Merged device tree changes and driver changes in one series
> Changes in v3:
> - Added fsl,imx25-flexcan, fsl,imx35-flexcan and fsl,imx53-flexcan
> support to the driver.
> - Modified patch deciption to state default endianness followed and
> to include fsl-flexcan.txt referance.
> Changes in v2:
> - Modified patch deciption to include common-properties.txt reference.
> - Reorder the LE/BE read/write APIs for better readability of code
> - Added an exception to force BE API selection, for powerpc based platform
> P1010. This ensures that new linux kernel works with old P1010
> device-tree, while future powerpc platforms that use big endian
> FlexCAN controller need to specify big-endian in device tree in
> FlexCAN node.
> - Tested on P1010 after backporting to freescale sdk 1.4 linux, without
> any change in device-tree.
> - Tested on NXP LS1021A arm based platform.
>
> drivers/net/can/flexcan.c | 233 ++++++++++++++++++++----------------
> 1 file changed, 131 insertions(+), 102 deletions(-)
>
> diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
> index a13a489..4c873fb 100644
> --- a/drivers/net/can/flexcan.c
> +++ b/drivers/net/can/flexcan.c
[...]
I think this will select LE for non DT devices, right?
> + if (of_property_read_bool(pdev->dev.of_node, "big-endian")) {
> + priv->read = flexcan_read_be;
> + priv->write = flexcan_write_be;
> + } else {
> + if (of_device_is_compatible(pdev->dev.of_node,
> + "fsl,p1010-flexcan")) {
> + priv->read = flexcan_read_be;
> + priv->write = flexcan_write_be;
> + } else {
> + priv->read = flexcan_read_le;
> + priv->write = flexcan_write_le;
> + }
> + }
> +
> priv->can.clock.freq = clock_freq;
> priv->can.bittiming_const = &flexcan_bittiming_const;
> priv->can.do_set_mode = flexcan_set_mode;
>
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Industrial Linux Solutions | Phone: +49-231-2826-924 |
Vertretung West/Dortmund | Fax: +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686 | http://www.pengutronix.de |
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* [PATCH v4 6/6] arm/dts: Add nodes for flexcan devices present on LS1021A-Rev2 SoC
From: Pankaj Bansal @ 2017-11-24 13:22 UTC (permalink / raw)
To: wg-5Yr1BZd7O62+XT7JhA+gdA, mkl-bIcnvbaLZ9MEGnE8C9+IrQ,
linux-can-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: V.Sethi-3arQi8VN3Tc, poonam.aggrwal-3arQi8VN3Tc, Pankaj Bansal,
Bhupesh Sharma, Sakar Arora
In-Reply-To: <1511529733-27942-1-git-send-email-pankaj.bansal-3arQi8VN3Tc@public.gmane.org>
This patch adds the device nodes for flexcan controller(s) present on
LS1021A-Rev2 SoC.
Signed-off-by: Pankaj Bansal <pankaj.bansal-3arQi8VN3Tc@public.gmane.org>
Signed-off-by: Bhupesh Sharma <bhupesh.sharma-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Signed-off-by: Sakar Arora <Sakar.Arora-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Reviewed-by: Zhengxiong Jin <Jason.Jin-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Reviewed-by: Poonam Aggrwal <poonam.aggrwal-3arQi8VN3Tc@public.gmane.org>
---
Changes in v4:
- Merged device tree changes and driver changes in one series
Changes in v3:
- No change.
Changes in v2:
- No change.
- Added one more patch in series.
arch/arm/boot/dts/ls1021a-qds.dts | 16 +++++++++++++
arch/arm/boot/dts/ls1021a-twr.dts | 16 +++++++++++++
arch/arm/boot/dts/ls1021a.dtsi | 36 +++++++++++++++++++++++++++++
3 files changed, 68 insertions(+)
diff --git a/arch/arm/boot/dts/ls1021a-qds.dts b/arch/arm/boot/dts/ls1021a-qds.dts
index 9408753..4f211e3 100644
--- a/arch/arm/boot/dts/ls1021a-qds.dts
+++ b/arch/arm/boot/dts/ls1021a-qds.dts
@@ -331,3 +331,19 @@
&uart1 {
status = "okay";
};
+
+&can0 {
+ status = "okay";
+};
+
+&can1 {
+ status = "okay";
+};
+
+&can2 {
+ status = "disabled";
+};
+
+&can3 {
+ status = "disabled";
+};
diff --git a/arch/arm/boot/dts/ls1021a-twr.dts b/arch/arm/boot/dts/ls1021a-twr.dts
index a8b148a..7202d9c 100644
--- a/arch/arm/boot/dts/ls1021a-twr.dts
+++ b/arch/arm/boot/dts/ls1021a-twr.dts
@@ -243,3 +243,19 @@
&uart1 {
status = "okay";
};
+
+&can0 {
+ status = "okay";
+};
+
+&can1 {
+ status = "okay";
+};
+
+&can2 {
+ status = "disabled";
+};
+
+&can3 {
+ status = "disabled";
+};
diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi
index 9319e1f..7789031 100644
--- a/arch/arm/boot/dts/ls1021a.dtsi
+++ b/arch/arm/boot/dts/ls1021a.dtsi
@@ -730,5 +730,41 @@
<0000 0 0 3 &gic GIC_SPI 191 IRQ_TYPE_LEVEL_HIGH>,
<0000 0 0 4 &gic GIC_SPI 193 IRQ_TYPE_LEVEL_HIGH>;
};
+
+ can0: can@2a70000 {
+ compatible = "fsl,ls1021ar2-flexcan";
+ reg = <0x0 0x2a70000 0x0 0x1000>;
+ interrupts = <GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clockgen 4 1>, <&clockgen 4 1>;
+ clock-names = "ipg", "per";
+ big-endian;
+ };
+
+ can1: can@2a80000 {
+ compatible = "fsl,ls1021ar2-flexcan";
+ reg = <0x0 0x2a80000 0x0 0x1000>;
+ interrupts = <GIC_SPI 127 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clockgen 4 1>, <&clockgen 4 1>;
+ clock-names = "ipg", "per";
+ big-endian;
+ };
+
+ can2: can@2a90000 {
+ compatible = "fsl,ls1021ar2-flexcan";
+ reg = <0x0 0x2a90000 0x0 0x1000>;
+ interrupts = <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clockgen 4 1>, <&clockgen 4 1>;
+ clock-names = "ipg", "per";
+ big-endian;
+ };
+
+ can3: can@2aa0000 {
+ compatible = "fsl,ls1021ar2-flexcan";
+ reg = <0x0 0x2aa0000 0x0 0x1000>;
+ interrupts = <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clockgen 4 1>, <&clockgen 4 1>;
+ clock-names = "ipg", "per";
+ big-endian;
+ };
};
};
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v4 3/6] Documentation : can : flexcan : Add big-endian property to device tree
From: Pankaj Bansal @ 2017-11-24 13:22 UTC (permalink / raw)
To: wg-5Yr1BZd7O62+XT7JhA+gdA, mkl-bIcnvbaLZ9MEGnE8C9+IrQ,
linux-can-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: V.Sethi-3arQi8VN3Tc, poonam.aggrwal-3arQi8VN3Tc, Pankaj Bansal
In-Reply-To: <1511529733-27942-1-git-send-email-pankaj.bansal-3arQi8VN3Tc@public.gmane.org>
The FlexCAN controller can be modelled as little or big endian depending
on SOC design. This device tree property identifies the controller
endianness and the driver reads/writes controller registers based on
that.
This is optional property. i.e. if this property is not present in
device tree node then controller is assumed to be little endian. if this
property is present then controller is assumed to be big endian.
Signed-off-by: Pankaj Bansal <pankaj.bansal-3arQi8VN3Tc@public.gmane.org>
Reviewed-by: Poonam Aggrwal <poonam.aggrwal-3arQi8VN3Tc@public.gmane.org>
---
Changes in v4:
- document the default behaviour in the bindings if the big-endian property
is missing.
- Merged device tree changes and driver changes in one series
Changes in v3:
- No change.
Changes in v2:
- No change.
- Added one more patch in series.
Documentation/devicetree/bindings/net/can/fsl-flexcan.txt | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/can/fsl-flexcan.txt b/Documentation/devicetree/bindings/net/can/fsl-flexcan.txt
index 56d6cc3..bfc0c43 100644
--- a/Documentation/devicetree/bindings/net/can/fsl-flexcan.txt
+++ b/Documentation/devicetree/bindings/net/can/fsl-flexcan.txt
@@ -18,6 +18,12 @@ Optional properties:
- xceiver-supply: Regulator that powers the CAN transceiver
+- big-endian: This means the registers of FlexCAN controller are big endian.
+ This is optional property.i.e. if this property is not present in
+ device tree node then controller is assumed to be little endian.
+ if this property is present then controller is assumed to be big
+ endian.
+
Example:
can@1c000 {
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v4 2/6] can: flexcan: adding platform specific details for LS1021A
From: Pankaj Bansal @ 2017-11-24 13:22 UTC (permalink / raw)
To: wg-5Yr1BZd7O62+XT7JhA+gdA, mkl-bIcnvbaLZ9MEGnE8C9+IrQ,
linux-can-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: V.Sethi-3arQi8VN3Tc, poonam.aggrwal-3arQi8VN3Tc, Pankaj Bansal,
Bhupesh Sharma
In-Reply-To: <1511529733-27942-1-git-send-email-pankaj.bansal-3arQi8VN3Tc@public.gmane.org>
This patch adds platform specific details for NXP SOC LS1021A to the
flexcan driver code.
Signed-off-by: Pankaj Bansal <pankaj.bansal-3arQi8VN3Tc@public.gmane.org>
Signed-off-by: Bhupesh Sharma <bhupesh.sharma-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Reviewed-by: Zhengxiong Jin <Jason.Jin-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Reviewed-by: Poonam Aggrwal <poonam.aggrwal-3arQi8VN3Tc@public.gmane.org>
---
Changes in v4:
- Merged device tree changes and driver changes in one series
- Remove obsolete comment about rev2 functional RX FIFO.
Changes in v3:
- Add LS1021 in FLEXCAN hardware feature flags table
- Kept LS1021 compatible in one line, even if it's more than 80
chars.
- Add PERR_STATE quirk and USE_OFF_TIMESTAMP quirk and
ENABLE_EACEN_RRS quirk for LS1021A.
- Tested on LS1021A using "cangen can0 -D 11223344DEADBEEF -L 8 -R"
and "cangen can0 -D 11223344DEADBEEF -L 8"
Changes in v2:
- No change.
drivers/net/can/flexcan.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 4c873fb..3a370d8 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -190,6 +190,7 @@
* MX53 FlexCAN2 03.00.00.00 yes no no no no
* MX6s FlexCAN3 10.00.12.00 yes yes no no yes
* VF610 FlexCAN3 ? no yes ? yes yes?
+ * LS1021A FlexCAN2 03.00.04.00 no yes no no yes
*
* Some SOCs do not have the RX_WARN & TX_WARN interrupt line connected.
*/
@@ -304,6 +305,12 @@ static const struct flexcan_devtype_data fsl_vf610_devtype_data = {
FLEXCAN_QUIRK_DISABLE_MECR | FLEXCAN_QUIRK_USE_OFF_TIMESTAMP,
};
+static const struct flexcan_devtype_data fsl_ls1021a_r2_devtype_data = {
+ .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
+ FLEXCAN_QUIRK_DISABLE_MECR | FLEXCAN_QUIRK_BROKEN_PERR_STATE |
+ FLEXCAN_QUIRK_USE_OFF_TIMESTAMP,
+};
+
static const struct can_bittiming_const flexcan_bittiming_const = {
.name = DRV_NAME,
.tseg1_min = 4,
@@ -1248,6 +1255,7 @@ static const struct of_device_id flexcan_of_match[] = {
{ .compatible = "fsl,imx25-flexcan", .data = &fsl_p1010_devtype_data, },
{ .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, },
{ .compatible = "fsl,vf610-flexcan", .data = &fsl_vf610_devtype_data, },
+ { .compatible = "fsl,ls1021ar2-flexcan", .data = &fsl_ls1021a_r2_devtype_data, },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, flexcan_of_match);
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v4 1/6] can: flexcan: Remodel FlexCAN register r/w APIs for big endian FlexCAN controllers.
From: Pankaj Bansal @ 2017-11-24 13:22 UTC (permalink / raw)
To: wg-5Yr1BZd7O62+XT7JhA+gdA, mkl-bIcnvbaLZ9MEGnE8C9+IrQ,
linux-can-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: V.Sethi-3arQi8VN3Tc, poonam.aggrwal-3arQi8VN3Tc, Pankaj Bansal,
Bhupesh Sharma, Sakar Arora
In-Reply-To: <1511529733-27942-1-git-send-email-pankaj.bansal-3arQi8VN3Tc@public.gmane.org>
The FlexCAN driver assumed that FlexCAN controller is big endian for
powerpc architecture and little endian for other architectures.
But this may not be the case. FlexCAN controller can be little or big
endian on any architecture. For e.g. NXP LS1021A ARM based SOC has big
endian FlexCAN controller.
Therefore, the driver has been modified to add a provision for both
types of controllers using an additional device tree property. On a
"fsl,p1010-flexcan" device BE is default, on all other devices LE is.
Big Endian controllers should have "big-endian" set in the device tree.
check "Documentation/devicetree/bindings/net/can/fsl-flexcan.txt" for
usage.
This is the standard practice followed in linux. for more info check:
Documentation/devicetree/bindings/common-properties.txt
Signed-off-by: Pankaj Bansal <pankaj.bansal-3arQi8VN3Tc@public.gmane.org>
Signed-off-by: Bhupesh Sharma <bhupesh.sharma-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Signed-off-by: Sakar Arora <Sakar.Arora-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Reviewed-by: Zhengxiong Jin <Jason.Jin-KZfg59tc24xl57MIdRCFDg@public.gmane.org>
Reviewed-by: Poonam Aggrwal <poonam.aggrwal-3arQi8VN3Tc@public.gmane.org>
---
Changes in v4:
- Merged device tree changes and driver changes in one series
Changes in v3:
- Added fsl,imx25-flexcan, fsl,imx35-flexcan and fsl,imx53-flexcan
support to the driver.
- Modified patch deciption to state default endianness followed and
to include fsl-flexcan.txt referance.
Changes in v2:
- Modified patch deciption to include common-properties.txt reference.
- Reorder the LE/BE read/write APIs for better readability of code
- Added an exception to force BE API selection, for powerpc based platform
P1010. This ensures that new linux kernel works with old P1010
device-tree, while future powerpc platforms that use big endian
FlexCAN controller need to specify big-endian in device tree in
FlexCAN node.
- Tested on P1010 after backporting to freescale sdk 1.4 linux, without
any change in device-tree.
- Tested on NXP LS1021A arm based platform.
drivers/net/can/flexcan.c | 233 ++++++++++++++++++++----------------
1 file changed, 131 insertions(+), 102 deletions(-)
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index a13a489..4c873fb 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -279,6 +279,10 @@ struct flexcan_priv {
struct clk *clk_per;
const struct flexcan_devtype_data *devtype_data;
struct regulator *reg_xceiver;
+
+ /* Read and Write APIs */
+ u32 (*read)(void __iomem *addr);
+ void (*write)(u32 val, void __iomem *addr);
};
static const struct flexcan_devtype_data fsl_p1010_devtype_data = {
@@ -312,39 +316,45 @@ static const struct can_bittiming_const flexcan_bittiming_const = {
.brp_inc = 1,
};
-/* Abstract off the read/write for arm versus ppc. This
- * assumes that PPC uses big-endian registers and everything
- * else uses little-endian registers, independent of CPU
- * endianness.
+/* FlexCAN module is essentially modelled as a little-endian IP in most
+ * SoCs, i.e the registers as well as the message buffer areas are
+ * implemented in a little-endian fashion.
+ *
+ * However there are some SoCs (e.g. LS1021A) which implement the FlexCAN
+ * module in a big-endian fashion (i.e the registers as well as the
+ * message buffer areas are implemented in a big-endian way).
+ *
+ * In addition, the FlexCAN module can be found on SoCs having ARM or
+ * PPC cores. So, we need to abstract off the register read/write
+ * functions, ensuring that these cater to all the combinations of module
+ * endianness and underlying CPU endianness.
*/
-#if defined(CONFIG_PPC)
-static inline u32 flexcan_read(void __iomem *addr)
+static inline u32 flexcan_read_be(void __iomem *addr)
{
- return in_be32(addr);
+ return ioread32be(addr);
}
-static inline void flexcan_write(u32 val, void __iomem *addr)
+static inline void flexcan_write_be(u32 val, void __iomem *addr)
{
- out_be32(addr, val);
+ iowrite32be(val, addr);
}
-#else
-static inline u32 flexcan_read(void __iomem *addr)
+
+static inline u32 flexcan_read_le(void __iomem *addr)
{
- return readl(addr);
+ return ioread32(addr);
}
-static inline void flexcan_write(u32 val, void __iomem *addr)
+static inline void flexcan_write_le(u32 val, void __iomem *addr)
{
- writel(val, addr);
+ iowrite32(val, addr);
}
-#endif
static inline void flexcan_error_irq_enable(const struct flexcan_priv *priv)
{
struct flexcan_regs __iomem *regs = priv->regs;
u32 reg_ctrl = (priv->reg_ctrl_default | FLEXCAN_CTRL_ERR_MSK);
- flexcan_write(reg_ctrl, ®s->ctrl);
+ priv->write(reg_ctrl, ®s->ctrl);
}
static inline void flexcan_error_irq_disable(const struct flexcan_priv *priv)
@@ -352,7 +362,7 @@ static inline void flexcan_error_irq_disable(const struct flexcan_priv *priv)
struct flexcan_regs __iomem *regs = priv->regs;
u32 reg_ctrl = (priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_MSK);
- flexcan_write(reg_ctrl, ®s->ctrl);
+ priv->write(reg_ctrl, ®s->ctrl);
}
static inline int flexcan_transceiver_enable(const struct flexcan_priv *priv)
@@ -377,14 +387,14 @@ static int flexcan_chip_enable(struct flexcan_priv *priv)
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
u32 reg;
- reg = flexcan_read(®s->mcr);
+ reg = priv->read(®s->mcr);
reg &= ~FLEXCAN_MCR_MDIS;
- flexcan_write(reg, ®s->mcr);
+ priv->write(reg, ®s->mcr);
- while (timeout-- && (flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK))
+ while (timeout-- && (priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK))
udelay(10);
- if (flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)
+ if (priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK)
return -ETIMEDOUT;
return 0;
@@ -396,14 +406,14 @@ static int flexcan_chip_disable(struct flexcan_priv *priv)
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
u32 reg;
- reg = flexcan_read(®s->mcr);
+ reg = priv->read(®s->mcr);
reg |= FLEXCAN_MCR_MDIS;
- flexcan_write(reg, ®s->mcr);
+ priv->write(reg, ®s->mcr);
- while (timeout-- && !(flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK))
+ while (timeout-- && !(priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK))
udelay(10);
- if (!(flexcan_read(®s->mcr) & FLEXCAN_MCR_LPM_ACK))
+ if (!(priv->read(®s->mcr) & FLEXCAN_MCR_LPM_ACK))
return -ETIMEDOUT;
return 0;
@@ -415,14 +425,14 @@ static int flexcan_chip_freeze(struct flexcan_priv *priv)
unsigned int timeout = 1000 * 1000 * 10 / priv->can.bittiming.bitrate;
u32 reg;
- reg = flexcan_read(®s->mcr);
+ reg = priv->read(®s->mcr);
reg |= FLEXCAN_MCR_HALT;
- flexcan_write(reg, ®s->mcr);
+ priv->write(reg, ®s->mcr);
- while (timeout-- && !(flexcan_read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK))
+ while (timeout-- && !(priv->read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK))
udelay(100);
- if (!(flexcan_read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK))
+ if (!(priv->read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK))
return -ETIMEDOUT;
return 0;
@@ -434,14 +444,14 @@ static int flexcan_chip_unfreeze(struct flexcan_priv *priv)
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
u32 reg;
- reg = flexcan_read(®s->mcr);
+ reg = priv->read(®s->mcr);
reg &= ~FLEXCAN_MCR_HALT;
- flexcan_write(reg, ®s->mcr);
+ priv->write(reg, ®s->mcr);
- while (timeout-- && (flexcan_read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK))
+ while (timeout-- && (priv->read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK))
udelay(10);
- if (flexcan_read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK)
+ if (priv->read(®s->mcr) & FLEXCAN_MCR_FRZ_ACK)
return -ETIMEDOUT;
return 0;
@@ -452,11 +462,11 @@ static int flexcan_chip_softreset(struct flexcan_priv *priv)
struct flexcan_regs __iomem *regs = priv->regs;
unsigned int timeout = FLEXCAN_TIMEOUT_US / 10;
- flexcan_write(FLEXCAN_MCR_SOFTRST, ®s->mcr);
- while (timeout-- && (flexcan_read(®s->mcr) & FLEXCAN_MCR_SOFTRST))
+ priv->write(FLEXCAN_MCR_SOFTRST, ®s->mcr);
+ while (timeout-- && (priv->read(®s->mcr) & FLEXCAN_MCR_SOFTRST))
udelay(10);
- if (flexcan_read(®s->mcr) & FLEXCAN_MCR_SOFTRST)
+ if (priv->read(®s->mcr) & FLEXCAN_MCR_SOFTRST)
return -ETIMEDOUT;
return 0;
@@ -467,7 +477,7 @@ static int __flexcan_get_berr_counter(const struct net_device *dev,
{
const struct flexcan_priv *priv = netdev_priv(dev);
struct flexcan_regs __iomem *regs = priv->regs;
- u32 reg = flexcan_read(®s->ecr);
+ u32 reg = priv->read(®s->ecr);
bec->txerr = (reg >> 0) & 0xff;
bec->rxerr = (reg >> 8) & 0xff;
@@ -523,24 +533,24 @@ static int flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (cf->can_dlc > 0) {
data = be32_to_cpup((__be32 *)&cf->data[0]);
- flexcan_write(data, &priv->tx_mb->data[0]);
+ priv->write(data, &priv->tx_mb->data[0]);
}
if (cf->can_dlc > 3) {
data = be32_to_cpup((__be32 *)&cf->data[4]);
- flexcan_write(data, &priv->tx_mb->data[1]);
+ priv->write(data, &priv->tx_mb->data[1]);
}
can_put_echo_skb(skb, dev, 0);
- flexcan_write(can_id, &priv->tx_mb->can_id);
- flexcan_write(ctrl, &priv->tx_mb->can_ctrl);
+ priv->write(can_id, &priv->tx_mb->can_id);
+ priv->write(ctrl, &priv->tx_mb->can_ctrl);
/* Errata ERR005829 step8:
* Write twice INACTIVE(0x8) code to first MB.
*/
- flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
+ priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
&priv->tx_mb_reserved->can_ctrl);
- flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
+ priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
&priv->tx_mb_reserved->can_ctrl);
return NETDEV_TX_OK;
@@ -659,7 +669,7 @@ static unsigned int flexcan_mailbox_read(struct can_rx_offload *offload,
u32 code;
do {
- reg_ctrl = flexcan_read(&mb->can_ctrl);
+ reg_ctrl = priv->read(&mb->can_ctrl);
} while (reg_ctrl & FLEXCAN_MB_CODE_RX_BUSY_BIT);
/* is this MB empty? */
@@ -674,17 +684,17 @@ static unsigned int flexcan_mailbox_read(struct can_rx_offload *offload,
offload->dev->stats.rx_errors++;
}
} else {
- reg_iflag1 = flexcan_read(®s->iflag1);
+ reg_iflag1 = priv->read(®s->iflag1);
if (!(reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE))
return 0;
- reg_ctrl = flexcan_read(&mb->can_ctrl);
+ reg_ctrl = priv->read(&mb->can_ctrl);
}
/* increase timstamp to full 32 bit */
*timestamp = reg_ctrl << 16;
- reg_id = flexcan_read(&mb->can_id);
+ reg_id = priv->read(&mb->can_id);
if (reg_ctrl & FLEXCAN_MB_CNT_IDE)
cf->can_id = ((reg_id >> 0) & CAN_EFF_MASK) | CAN_EFF_FLAG;
else
@@ -694,19 +704,19 @@ static unsigned int flexcan_mailbox_read(struct can_rx_offload *offload,
cf->can_id |= CAN_RTR_FLAG;
cf->can_dlc = get_can_dlc((reg_ctrl >> 16) & 0xf);
- *(__be32 *)(cf->data + 0) = cpu_to_be32(flexcan_read(&mb->data[0]));
- *(__be32 *)(cf->data + 4) = cpu_to_be32(flexcan_read(&mb->data[1]));
+ *(__be32 *)(cf->data + 0) = cpu_to_be32(priv->read(&mb->data[0]));
+ *(__be32 *)(cf->data + 4) = cpu_to_be32(priv->read(&mb->data[1]));
/* mark as read */
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
/* Clear IRQ */
if (n < 32)
- flexcan_write(BIT(n), ®s->iflag1);
+ priv->write(BIT(n), ®s->iflag1);
else
- flexcan_write(BIT(n - 32), ®s->iflag2);
+ priv->write(BIT(n - 32), ®s->iflag2);
} else {
- flexcan_write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->iflag1);
- flexcan_read(®s->timer);
+ priv->write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->iflag1);
+ priv->read(®s->timer);
}
return 1;
@@ -718,8 +728,8 @@ static inline u64 flexcan_read_reg_iflag_rx(struct flexcan_priv *priv)
struct flexcan_regs __iomem *regs = priv->regs;
u32 iflag1, iflag2;
- iflag2 = flexcan_read(®s->iflag2) & priv->reg_imask2_default;
- iflag1 = flexcan_read(®s->iflag1) & priv->reg_imask1_default &
+ iflag2 = priv->read(®s->iflag2) & priv->reg_imask2_default;
+ iflag1 = priv->read(®s->iflag1) & priv->reg_imask1_default &
~FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
return (u64)iflag2 << 32 | iflag1;
@@ -735,7 +745,7 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
u32 reg_iflag1, reg_esr;
enum can_state last_state = priv->can.state;
- reg_iflag1 = flexcan_read(®s->iflag1);
+ reg_iflag1 = priv->read(®s->iflag1);
/* reception interrupt */
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
@@ -758,7 +768,8 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
/* FIFO overflow interrupt */
if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) {
handled = IRQ_HANDLED;
- flexcan_write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, ®s->iflag1);
+ priv->write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW,
+ ®s->iflag1);
dev->stats.rx_over_errors++;
dev->stats.rx_errors++;
}
@@ -772,18 +783,18 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
can_led_event(dev, CAN_LED_EVENT_TX);
/* after sending a RTR frame MB is in RX mode */
- flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
- &priv->tx_mb->can_ctrl);
- flexcan_write(FLEXCAN_IFLAG_MB(priv->tx_mb_idx), ®s->iflag1);
+ priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
+ &priv->tx_mb->can_ctrl);
+ priv->write(FLEXCAN_IFLAG_MB(priv->tx_mb_idx), ®s->iflag1);
netif_wake_queue(dev);
}
- reg_esr = flexcan_read(®s->esr);
+ reg_esr = priv->read(®s->esr);
/* ACK all bus error and state change IRQ sources */
if (reg_esr & FLEXCAN_ESR_ALL_INT) {
handled = IRQ_HANDLED;
- flexcan_write(reg_esr & FLEXCAN_ESR_ALL_INT, ®s->esr);
+ priv->write(reg_esr & FLEXCAN_ESR_ALL_INT, ®s->esr);
}
/* state change interrupt or broken error state quirk fix is enabled */
@@ -845,7 +856,7 @@ static void flexcan_set_bittiming(struct net_device *dev)
struct flexcan_regs __iomem *regs = priv->regs;
u32 reg;
- reg = flexcan_read(®s->ctrl);
+ reg = priv->read(®s->ctrl);
reg &= ~(FLEXCAN_CTRL_PRESDIV(0xff) |
FLEXCAN_CTRL_RJW(0x3) |
FLEXCAN_CTRL_PSEG1(0x7) |
@@ -869,11 +880,11 @@ static void flexcan_set_bittiming(struct net_device *dev)
reg |= FLEXCAN_CTRL_SMP;
netdev_dbg(dev, "writing ctrl=0x%08x\n", reg);
- flexcan_write(reg, ®s->ctrl);
+ priv->write(reg, ®s->ctrl);
/* print chip status */
netdev_dbg(dev, "%s: mcr=0x%08x ctrl=0x%08x\n", __func__,
- flexcan_read(®s->mcr), flexcan_read(®s->ctrl));
+ priv->read(®s->mcr), priv->read(®s->ctrl));
}
/* flexcan_chip_start
@@ -912,7 +923,7 @@ static int flexcan_chip_start(struct net_device *dev)
* choose format C
* set max mailbox number
*/
- reg_mcr = flexcan_read(®s->mcr);
+ reg_mcr = priv->read(®s->mcr);
reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff);
reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | FLEXCAN_MCR_SUPV |
FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_IRMQ |
@@ -926,7 +937,7 @@ static int flexcan_chip_start(struct net_device *dev)
FLEXCAN_MCR_MAXMB(priv->tx_mb_idx);
}
netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr);
- flexcan_write(reg_mcr, ®s->mcr);
+ priv->write(reg_mcr, ®s->mcr);
/* CTRL
*
@@ -939,7 +950,7 @@ static int flexcan_chip_start(struct net_device *dev)
* enable bus off interrupt
* (== FLEXCAN_CTRL_ERR_STATE)
*/
- reg_ctrl = flexcan_read(®s->ctrl);
+ reg_ctrl = priv->read(®s->ctrl);
reg_ctrl &= ~FLEXCAN_CTRL_TSYN;
reg_ctrl |= FLEXCAN_CTRL_BOFF_REC | FLEXCAN_CTRL_LBUF |
FLEXCAN_CTRL_ERR_STATE;
@@ -959,45 +970,45 @@ static int flexcan_chip_start(struct net_device *dev)
/* leave interrupts disabled for now */
reg_ctrl &= ~FLEXCAN_CTRL_ERR_ALL;
netdev_dbg(dev, "%s: writing ctrl=0x%08x", __func__, reg_ctrl);
- flexcan_write(reg_ctrl, ®s->ctrl);
+ priv->write(reg_ctrl, ®s->ctrl);
if ((priv->devtype_data->quirks & FLEXCAN_QUIRK_ENABLE_EACEN_RRS)) {
- reg_ctrl2 = flexcan_read(®s->ctrl2);
+ reg_ctrl2 = priv->read(®s->ctrl2);
reg_ctrl2 |= FLEXCAN_CTRL2_EACEN | FLEXCAN_CTRL2_RRS;
- flexcan_write(reg_ctrl2, ®s->ctrl2);
+ priv->write(reg_ctrl2, ®s->ctrl2);
}
/* clear and invalidate all mailboxes first */
for (i = priv->tx_mb_idx; i < ARRAY_SIZE(regs->mb); i++) {
- flexcan_write(FLEXCAN_MB_CODE_RX_INACTIVE,
- ®s->mb[i].can_ctrl);
+ priv->write(FLEXCAN_MB_CODE_RX_INACTIVE,
+ ®s->mb[i].can_ctrl);
}
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
for (i = priv->offload.mb_first; i <= priv->offload.mb_last; i++)
- flexcan_write(FLEXCAN_MB_CODE_RX_EMPTY,
- ®s->mb[i].can_ctrl);
+ priv->write(FLEXCAN_MB_CODE_RX_EMPTY,
+ ®s->mb[i].can_ctrl);
}
/* Errata ERR005829: mark first TX mailbox as INACTIVE */
- flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
- &priv->tx_mb_reserved->can_ctrl);
+ priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
+ &priv->tx_mb_reserved->can_ctrl);
/* mark TX mailbox as INACTIVE */
- flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
- &priv->tx_mb->can_ctrl);
+ priv->write(FLEXCAN_MB_CODE_TX_INACTIVE,
+ &priv->tx_mb->can_ctrl);
/* acceptance mask/acceptance code (accept everything) */
- flexcan_write(0x0, ®s->rxgmask);
- flexcan_write(0x0, ®s->rx14mask);
- flexcan_write(0x0, ®s->rx15mask);
+ priv->write(0x0, ®s->rxgmask);
+ priv->write(0x0, ®s->rx14mask);
+ priv->write(0x0, ®s->rx15mask);
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_DISABLE_RXFG)
- flexcan_write(0x0, ®s->rxfgmask);
+ priv->write(0x0, ®s->rxfgmask);
/* clear acceptance filters */
for (i = 0; i < ARRAY_SIZE(regs->mb); i++)
- flexcan_write(0, ®s->rximr[i]);
+ priv->write(0, ®s->rximr[i]);
/* On Vybrid, disable memory error detection interrupts
* and freeze mode.
@@ -1010,16 +1021,16 @@ static int flexcan_chip_start(struct net_device *dev)
* and Correction of Memory Errors" to write to
* MECR register
*/
- reg_ctrl2 = flexcan_read(®s->ctrl2);
+ reg_ctrl2 = priv->read(®s->ctrl2);
reg_ctrl2 |= FLEXCAN_CTRL2_ECRWRE;
- flexcan_write(reg_ctrl2, ®s->ctrl2);
+ priv->write(reg_ctrl2, ®s->ctrl2);
- reg_mecr = flexcan_read(®s->mecr);
+ reg_mecr = priv->read(®s->mecr);
reg_mecr &= ~FLEXCAN_MECR_ECRWRDIS;
- flexcan_write(reg_mecr, ®s->mecr);
+ priv->write(reg_mecr, ®s->mecr);
reg_mecr &= ~(FLEXCAN_MECR_NCEFAFRZ | FLEXCAN_MECR_HANCEI_MSK |
FLEXCAN_MECR_FANCEI_MSK);
- flexcan_write(reg_mecr, ®s->mecr);
+ priv->write(reg_mecr, ®s->mecr);
}
err = flexcan_transceiver_enable(priv);
@@ -1035,14 +1046,14 @@ static int flexcan_chip_start(struct net_device *dev)
/* enable interrupts atomically */
disable_irq(dev->irq);
- flexcan_write(priv->reg_ctrl_default, ®s->ctrl);
- flexcan_write(priv->reg_imask1_default, ®s->imask1);
- flexcan_write(priv->reg_imask2_default, ®s->imask2);
+ priv->write(priv->reg_ctrl_default, ®s->ctrl);
+ priv->write(priv->reg_imask1_default, ®s->imask1);
+ priv->write(priv->reg_imask2_default, ®s->imask2);
enable_irq(dev->irq);
/* print chip status */
netdev_dbg(dev, "%s: reading mcr=0x%08x ctrl=0x%08x\n", __func__,
- flexcan_read(®s->mcr), flexcan_read(®s->ctrl));
+ priv->read(®s->mcr), priv->read(®s->ctrl));
return 0;
@@ -1067,10 +1078,10 @@ static void flexcan_chip_stop(struct net_device *dev)
flexcan_chip_disable(priv);
/* Disable all interrupts */
- flexcan_write(0, ®s->imask2);
- flexcan_write(0, ®s->imask1);
- flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
- ®s->ctrl);
+ priv->write(0, ®s->imask2);
+ priv->write(0, ®s->imask1);
+ priv->write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
+ ®s->ctrl);
flexcan_transceiver_disable(priv);
priv->can.state = CAN_STATE_STOPPED;
@@ -1185,26 +1196,26 @@ static int register_flexcandev(struct net_device *dev)
err = flexcan_chip_disable(priv);
if (err)
goto out_disable_per;
- reg = flexcan_read(®s->ctrl);
+ reg = priv->read(®s->ctrl);
reg |= FLEXCAN_CTRL_CLK_SRC;
- flexcan_write(reg, ®s->ctrl);
+ priv->write(reg, ®s->ctrl);
err = flexcan_chip_enable(priv);
if (err)
goto out_chip_disable;
/* set freeze, halt and activate FIFO, restrict register access */
- reg = flexcan_read(®s->mcr);
+ reg = priv->read(®s->mcr);
reg |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT |
FLEXCAN_MCR_FEN | FLEXCAN_MCR_SUPV;
- flexcan_write(reg, ®s->mcr);
+ priv->write(reg, ®s->mcr);
/* Currently we only support newer versions of this core
* featuring a RX hardware FIFO (although this driver doesn't
* make use of it on some cores). Older cores, found on some
* Coldfire derivates are not tested.
*/
- reg = flexcan_read(®s->mcr);
+ reg = priv->read(®s->mcr);
if (!(reg & FLEXCAN_MCR_FEN)) {
netdev_err(dev, "Could not enable RX FIFO, unsupported core\n");
err = -ENODEV;
@@ -1232,6 +1243,9 @@ static void unregister_flexcandev(struct net_device *dev)
static const struct of_device_id flexcan_of_match[] = {
{ .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, },
{ .compatible = "fsl,imx28-flexcan", .data = &fsl_imx28_devtype_data, },
+ { .compatible = "fsl,imx53-flexcan", .data = &fsl_p1010_devtype_data, },
+ { .compatible = "fsl,imx35-flexcan", .data = &fsl_p1010_devtype_data, },
+ { .compatible = "fsl,imx25-flexcan", .data = &fsl_p1010_devtype_data, },
{ .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, },
{ .compatible = "fsl,vf610-flexcan", .data = &fsl_vf610_devtype_data, },
{ /* sentinel */ },
@@ -1313,6 +1327,21 @@ static int flexcan_probe(struct platform_device *pdev)
dev->flags |= IFF_ECHO;
priv = netdev_priv(dev);
+
+ if (of_property_read_bool(pdev->dev.of_node, "big-endian")) {
+ priv->read = flexcan_read_be;
+ priv->write = flexcan_write_be;
+ } else {
+ if (of_device_is_compatible(pdev->dev.of_node,
+ "fsl,p1010-flexcan")) {
+ priv->read = flexcan_read_be;
+ priv->write = flexcan_write_be;
+ } else {
+ priv->read = flexcan_read_le;
+ priv->write = flexcan_write_le;
+ }
+ }
+
priv->can.clock.freq = clock_freq;
priv->can.bittiming_const = &flexcan_bittiming_const;
priv->can.do_set_mode = flexcan_set_mode;
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v4 5/6] arm: dts: Remove p1010-flexcan compatible from imx series dts
From: Pankaj Bansal @ 2017-11-24 13:22 UTC (permalink / raw)
To: wg, mkl, linux-can, robh+dt, mark.rutland, devicetree
Cc: V.Sethi, poonam.aggrwal, Pankaj Bansal
In-Reply-To: <1511529733-27942-1-git-send-email-pankaj.bansal@nxp.com>
The flexcan driver has been modified to check for big-endian dts
property for be read/write to flexcan registers/mb.
An exception to this rule is powerpc P1010RDB, which is always
big-endian, even if big-endian is not present in dts. This is
checked using p1010-flexcan compatible in dts.
Therefore, remove p1010-flexcan compatible from imx series dts,
as their flexcan core is little endian.
Signed-off-by: Pankaj Bansal <pankaj.bansal@nxp.com>
---
Changes in v4:
- Merged device tree changes and driver changes in one series
Changes in v3:
- No change.
Changes in v2:
- Add this patch in series.
arch/arm/boot/dts/imx25.dtsi | 4 ++--
arch/arm/boot/dts/imx28.dtsi | 4 ++--
arch/arm/boot/dts/imx35.dtsi | 4 ++--
arch/arm/boot/dts/imx53.dtsi | 4 ++--
4 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/arch/arm/boot/dts/imx25.dtsi b/arch/arm/boot/dts/imx25.dtsi
index 09ce8b8..fcaff1c 100644
--- a/arch/arm/boot/dts/imx25.dtsi
+++ b/arch/arm/boot/dts/imx25.dtsi
@@ -122,7 +122,7 @@
};
can1: can@43f88000 {
- compatible = "fsl,imx25-flexcan", "fsl,p1010-flexcan";
+ compatible = "fsl,imx25-flexcan";
reg = <0x43f88000 0x4000>;
interrupts = <43>;
clocks = <&clks 75>, <&clks 75>;
@@ -131,7 +131,7 @@
};
can2: can@43f8c000 {
- compatible = "fsl,imx25-flexcan", "fsl,p1010-flexcan";
+ compatible = "fsl,imx25-flexcan";
reg = <0x43f8c000 0x4000>;
interrupts = <44>;
clocks = <&clks 76>, <&clks 76>;
diff --git a/arch/arm/boot/dts/imx28.dtsi b/arch/arm/boot/dts/imx28.dtsi
index 2f4ebe0..e52e05c 100644
--- a/arch/arm/boot/dts/imx28.dtsi
+++ b/arch/arm/boot/dts/imx28.dtsi
@@ -1038,7 +1038,7 @@
};
can0: can@80032000 {
- compatible = "fsl,imx28-flexcan", "fsl,p1010-flexcan";
+ compatible = "fsl,imx28-flexcan";
reg = <0x80032000 0x2000>;
interrupts = <8>;
clocks = <&clks 58>, <&clks 58>;
@@ -1047,7 +1047,7 @@
};
can1: can@80034000 {
- compatible = "fsl,imx28-flexcan", "fsl,p1010-flexcan";
+ compatible = "fsl,imx28-flexcan";
reg = <0x80034000 0x2000>;
interrupts = <9>;
clocks = <&clks 59>, <&clks 59>;
diff --git a/arch/arm/boot/dts/imx35.dtsi b/arch/arm/boot/dts/imx35.dtsi
index 6d5e6a6..1f0e220 100644
--- a/arch/arm/boot/dts/imx35.dtsi
+++ b/arch/arm/boot/dts/imx35.dtsi
@@ -303,7 +303,7 @@
};
can1: can@53fe4000 {
- compatible = "fsl,imx35-flexcan", "fsl,p1010-flexcan";
+ compatible = "fsl,imx35-flexcan";
reg = <0x53fe4000 0x1000>;
clocks = <&clks 33>, <&clks 33>;
clock-names = "ipg", "per";
@@ -312,7 +312,7 @@
};
can2: can@53fe8000 {
- compatible = "fsl,imx35-flexcan", "fsl,p1010-flexcan";
+ compatible = "fsl,imx35-flexcan";
reg = <0x53fe8000 0x1000>;
clocks = <&clks 34>, <&clks 34>;
clock-names = "ipg", "per";
diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi
index 8bf0d89..85573cf 100644
--- a/arch/arm/boot/dts/imx53.dtsi
+++ b/arch/arm/boot/dts/imx53.dtsi
@@ -545,7 +545,7 @@
};
can1: can@53fc8000 {
- compatible = "fsl,imx53-flexcan", "fsl,p1010-flexcan";
+ compatible = "fsl,imx53-flexcan";
reg = <0x53fc8000 0x4000>;
interrupts = <82>;
clocks = <&clks IMX5_CLK_CAN1_IPG_GATE>,
@@ -555,7 +555,7 @@
};
can2: can@53fcc000 {
- compatible = "fsl,imx53-flexcan", "fsl,p1010-flexcan";
+ compatible = "fsl,imx53-flexcan";
reg = <0x53fcc000 0x4000>;
interrupts = <83>;
clocks = <&clks IMX5_CLK_CAN2_IPG_GATE>,
--
2.7.4
^ permalink raw reply related
* [PATCH v4 4/6] powerpc: dts: P1010: Add endianness property to flexcan node
From: Pankaj Bansal @ 2017-11-24 13:22 UTC (permalink / raw)
To: wg, mkl, linux-can, robh+dt, mark.rutland, devicetree
Cc: V.Sethi, poonam.aggrwal, Pankaj Bansal
In-Reply-To: <1511529733-27942-1-git-send-email-pankaj.bansal@nxp.com>
The flexcan driver assumed that flexcan controller is big endian for
powerpc architecture and little endian for other architectures.
But this is not universally true. flexcan controller can be little or
big endian on any architecture.
Therefore the flexcan driver has been modified to check for "big-endian"
device tree property for controllers that are big endian.
consequently add the property to freescale P1010 SOC device tree.
Signed-off-by: Pankaj Bansal <pankaj.bansal@nxp.com>
Reviewed-by: Poonam Aggrwal <poonam.aggrwal@nxp.com>
---
Changes in v4:
- Merged device tree changes and driver changes in one series
Changes in v3:
- No change.
Changes in v2:
- No change.
- Added one more patch in series.
arch/powerpc/boot/dts/fsl/p1010si-post.dtsi | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi b/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi
index af12ead..1b4aafc 100644
--- a/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi
@@ -137,12 +137,14 @@
compatible = "fsl,p1010-flexcan";
reg = <0x1c000 0x1000>;
interrupts = <48 0x2 0 0>;
+ big-endian;
};
can1: can@1d000 {
compatible = "fsl,p1010-flexcan";
reg = <0x1d000 0x1000>;
interrupts = <61 0x2 0 0>;
+ big-endian;
};
L2: l2-cache-controller@20000 {
--
2.7.4
^ permalink raw reply related
* [PATCH v4 0/6] Remodel FlexCAN register r/w APIs for big endian
From: Pankaj Bansal @ 2017-11-24 13:22 UTC (permalink / raw)
To: wg, mkl, linux-can, robh+dt, mark.rutland, devicetree
Cc: V.Sethi, poonam.aggrwal, Pankaj Bansal
This patch series remodels the FlexCAN register r/w APIs for big endian.
The endianness is checked based on optional big-endian property in
device tree. if this property is not present in device tree node then
controller is assumed to be little endian. if this property is present then
controller is assumed to be big endian.
An exception to this rule is powerpc P1010RDB, which is always
big-endian, even if big-endian is not present in dts. This is
checked using p1010-flexcan compatible in dts.
Therefore, remove p1010-flexcan compatible from imx series dts,
as their flexcan core is little endian.
Finally this series adds support for NXP LS1021A SOC in flexcan,
which is arm based SOC having big-endian FlexCAN controller.
Pankaj Bansal (6):
can: flexcan: Remodel FlexCAN register r/w APIs for big endian FlexCAN
controllers.
can: flexcan: adding platform specific details for LS1021A
Documentation : can : flexcan : Add big-endian property to device tree
powerpc: dts: P1010: Add endianness property to flexcan node
arm: dts: Remove p1010-flexcan compatible from imx series dts
arm/dts: Add nodes for flexcan devices present on LS1021A-Rev2 SoC
.../bindings/net/can/fsl-flexcan.txt | 6 +
arch/arm/boot/dts/imx25.dtsi | 4 +-
arch/arm/boot/dts/imx28.dtsi | 4 +-
arch/arm/boot/dts/imx35.dtsi | 4 +-
arch/arm/boot/dts/imx53.dtsi | 4 +-
arch/arm/boot/dts/ls1021a-qds.dts | 16 ++
arch/arm/boot/dts/ls1021a-twr.dts | 16 ++
arch/arm/boot/dts/ls1021a.dtsi | 36 +++
arch/powerpc/boot/dts/fsl/p1010si-post.dtsi | 2 +
drivers/net/can/flexcan.c | 241 ++++++++++-------
10 files changed, 223 insertions(+), 110 deletions(-)
--
2.7.4
^ 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