* Re: [PATCH] of: overlay: fix memory leak of ovcs on error exit path
From: Frank Rowand @ 2017-11-30 15:01 UTC (permalink / raw)
To: Colin Ian King, Pantelis Antoniou, Rob Herring, devicetree
Cc: kernel-janitors, linux-kernel
In-Reply-To: <75d7d1c8-8a19-1a4c-0796-7cf69fdebe6d@gmail.com>
On 11/30/17 08:37, Frank Rowand wrote:
> Hi Colin, Rob,
>
> On 11/30/17 07:18, Colin Ian King wrote:
>> On 30/11/17 12:14, Frank Rowand wrote:
>>> On 11/29/17 14:17, Colin King wrote:
>>>> From: Colin Ian King <colin.king@canonical.com>
>>>>
>>>> Currently if the call to of_resolve_phandles fails then then ovcs
>>>> is not kfree'd on the error exit path. Rather than try and make
>>>> the clean up exit path more convoluted, fix this by just kfree'ing
>>>> ovcs at the point of error detection and exit via the same exit
>>>> path.
>>>>
>>>> Detected by CoverityScan, CID#1462296 ("Resource Leak")
>>>>
>>>> Fixes: f948d6d8b792 ("of: overlay: avoid race condition between applying multiple overlays")
>>>> Signed-off-by: Colin Ian King <colin.king@canonical.com>
>>>> ---
>>>> drivers/of/overlay.c | 4 +++-
>>>> 1 file changed, 3 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
>>>> index 53bc9e3f0b98..6c8efe7d8cbb 100644
>>>> --- a/drivers/of/overlay.c
>>>> +++ b/drivers/of/overlay.c
>>>> @@ -708,8 +708,10 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
>>>> of_overlay_mutex_lock();
>>>>
>>>> ret = of_resolve_phandles(tree);
>>>> - if (ret)
>>>> + if (ret) {
>>>> + kfree(ovcs);
>>>> goto err_overlay_unlock;
>>>> + }
>>>>
>>>> mutex_lock(&of_mutex);
>>>>
>>>>
>>>
>>> False coverity warning. ovcs is freed in free_overlay_changeset().
>>>
>>
>> The error exit path is via err_overlay_unlock:
>>
>> err_overlay_unlock:
>> of_overlay_mutex_unlock();
>>
>> out:
>> pr_debug("%s() err=%d\n", __func__, ret);
>>
>> return ret;
>>
>> ..so there is no call to free_overlay_changeset there.
>>
>> Colin
>>
>
> OK, I was looking at 4.15-rc1. You must be looking at a later version where
> "[PATCH 1/2] of: overlay: Fix cleanup order in of_overlay_apply()" has been
> applied. Thanks for providing the extra details about the exit path so I
> could see that.
>
> Rob, I think that the fix for cleanup order was not the best way to fix that
> problem. A better method would have been to move "mutex_lock(&of_mutex);"
> up 5 lines, to just before calling of_reserve_phandles().
It is getting late (midnight my time), so I really should revisit this all
tomorrow. My last comment ("move ... up 5 lines") is probably wrong.
I'll look at this after some sleep.
> The problem
> found by coverity was caused by the "Fix cleanup order" patch.
>
> I can create that alternate fix if you would like, but I am traveling
> right now and don't want to submit a patch without boot testing, so
> there will be a slight delay.
>
> -Frank
^ permalink raw reply
* Re: [RFC 0/2] of: Add whitelist
From: Rob Herring @ 2017-11-30 14:39 UTC (permalink / raw)
To: Frank Rowand
Cc: Alan Tull, Rob Herring, Pantelis Antoniou, Moritz Fischer,
devicetree@vger.kernel.org, Linux Kernel Mailing List, linux-fpga
In-Reply-To: <6a620530-2fac-480f-5c38-6b2b5257c9e9@gmail.com>
On Wed, Nov 29, 2017 at 4:47 PM, Frank Rowand <frowand.list@gmail.com> wrote:
> On 11/29/17 04:20, Frank Rowand wrote:
>> On 11/27/17 15:58, Alan Tull wrote:
>>> Here's a proposal for a whitelist to lock down the dynamic device tree.
>>>
>>> For an overlay to be accepted, all of its targets are required to be
>>> on a target node whitelist.
>>>
>>> Currently the only way I have to get on the whitelist is calling a
>>> function to add a node. That works for fpga regions, but I think
>>> other uses will need a way of having adding specific nodes from the
>>> base device tree, such as by adding a property like 'allow-overlay;'
>>> or 'allow-overlay = "okay";' If that is acceptable, I could use some
>>> advice on where that particular code should go.
>>>
>>> Alan
>>>
>>> Alan Tull (2):
>>> of: overlay: add whitelist
>>> fpga: of region: add of-fpga-region to whitelist
>>>
>>> drivers/fpga/of-fpga-region.c | 9 ++++++
>>> drivers/of/overlay.c | 73 +++++++++++++++++++++++++++++++++++++++++++
>>> include/linux/of.h | 12 +++++++
>>> 3 files changed, 94 insertions(+)
>>>
>>
>> The plan was to use connectors to restrict where an overlay could be applied.
>> I would prefer not to have multiple methods for accomplishing the same thing
>> unless there is a compelling reason to do so.
>
> Going back one level in my thinking, I don't think that having a driver mark
> a node as a location where an overlay fragment can be applied is serving a
> useful purpose. Any driver, including any driver loaded as a module,
> could mark a node as ok. I don't see how this is providing any meaningful
> restriction on where an overlay fragment can be applied.
It serves to separate the setting of which nodes overlays can be
applied to and the mechanism to apply them (checking permissions). The
former can't be centralized and the latter can be. For example,
something in the kernel enables overlays on a node or nodes, then the
overlay is applied with configfs interface and no board specific code
involved.
My concern is not whether any kernel component can enable applying of
overlays, but userspace. If it is a kernel component, then it is
explicit. And an OOT kernel module doesn't count because there's no
ABI guarantee there.
I agree that this patch series alone is not all that useful with only
in kernel users. It is only really interesting when we have a
userspace interface. However, an implementation with a flag bit is so
little code, I'm fine taking it now and not having to update all in
kernel users when adding a userspace interface.
Rob
^ permalink raw reply
* Re: [PATCH] rtc: add mxc driver for i.MX53
From: Russell King - ARM Linux @ 2017-11-30 14:27 UTC (permalink / raw)
To: Patrick Brünn
Cc: Philippe Ombredanne, Greg Kroah-Hartman, linux-kernel-dev,
Alessandro Zummo, Alexandre Belloni, Rob Herring, Mark Rutland,
open list:REAL TIME CLOCK (RTC) SUBSYSTEM,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list, Fabio Estevam, Juergen Borleis, Noel Vellemans,
Shawn Guo, Sascha Hauer, ARM/FREESCALE
In-Reply-To: <3BB206AB2B1BD448954845CE6FF69A8E01CB531330@NT-Mail07.beckhoff.com>
On Thu, Nov 30, 2017 at 01:36:22PM +0000, Patrick Brünn wrote:
> >From: Philippe Ombredanne [mailto:pombredanne@nexb.com]
> >Sent: Donnerstag, 30. November 2017 09:18
> >>
> >>> +/*
> >>> + * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights
> >Reserved.
> >>> + */
> >>> +
> >>> +/*
> >>> + * The code contained herein is licensed under the GNU General Public
> >>> + * License. You may obtain a copy of the GNU General Public License
> >>> + * Version 2 or later at the following locations:
> >>> + *
> >>> + * http://www.opensource.org/licenses/gpl-license.html
> >>> + * http://www.gnu.org/copyleft/gpl.html
> >>> + */
> >
> >Exactly!
> >And while you are it , you could replace the boilerplate license text
> >with the SPDX id.
> >--
>
> How would a perfect header look like?
> Looking at :
> git show b24413180f5600bcb3bb70fbed5cf186b60864bd -- drivers/
> and:
> git grep SPDX -- Documentation/
>
> it could be:
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights
> + */
> or:
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights
> + */
>
> Personally I would prefer:
> +/*
> + * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights
> + *
> + * SPDX-License-Identifier: GPL-2.0
> + */
>
> So, is there any guideline?
There's been lots of discussion about this, but Linus T has set the
requirements.
>From Thomas' patches adding documentation for this:
+1. Placement:
+
+ The SPDX license identifier in kernel files shall be added at the first
+ possible line in a file which can contain a comment. For the majority
+ or files this is the first line, except for scripts which require the
+ '#!PATH_TO_INTERPRETER' in the first line. For those scripts the SPDX
+ identifier goes into the second line.
+
+|
+
+2. Style:
+
+ The SPDX license identifier is added in form of a comment. The comment
+ style depends on the file type:
+
+ ::
+
+ C source: // SPDX-License-Identifier: <SPDX License Expression>
+ C header: /* SPDX-License-Identifier: <SPDX License Expression> */
+ ASM: /* SPDX-License-Identifier: <SPDX License Expression> */
+ scripts: # SPDX-License-Identifier: <SPDX License Expression>
+ .rst: .. SPDX-License-Identifier: <SPDX License Expression>
+
+ If a specific tool cannot handle the standard comment style, then the
+ appropriate comment mechanism which the tool accepts shall be used. This
+ is the reason for having the "/\* \*/" style comment in C header
+ files. There was build breakage observed with generated .lds files where
+ 'ld' failed to parse the C++ comment. This has been fixed by now, but
+ there are still older assembler tools which cannot handle C++ style
+ comments.
There is additional information in Thomas' patch about the SPDX
identifiers, how to deal with multi-licensed files, etc. It's
well worth reading before throwing a SPDX tag on a file to make
sure you get the correct tag(s) and expressions.
It's a fixed format. Tabs and additional spaces that are not in
the format are bad news.
The idea here is (as detailed by Linus) to be able to grep for
the SPDX identifiers using standard Unix tools, sort then and
then eliminate duplicates. If someone decides to use a tab
after the SPDX-Identifier: rather than a space, then it messes
up "uniq"'s ability to get rid of the duplicates.
As I say, the documentation is there to help people get this right,
so it's well worth reading. I don't think it's in mainline yet.
--
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line in suburbia: sync at 8.8Mbps down 630kbps up
According to speedtest.net: 8.21Mbps down 510kbps up
^ permalink raw reply
* RE: [PATCH] rtc: add mxc driver for i.MX53
From: Patrick Brünn @ 2017-11-30 14:24 UTC (permalink / raw)
To: Philippe Ombredanne, Thomas Gleixner
Cc: Greg Kroah-Hartman, linux-kernel-dev, Alessandro Zummo,
Alexandre Belloni, Rob Herring, Mark Rutland,
open list:REAL TIME CLOCK (RTC) SUBSYSTEM,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list, Fabio Estevam, Juergen Borleis, Noel Vellemans,
Shawn Guo, Sascha Hauer, Russell King, ARM/FREESCALE
In-Reply-To: <CAOFm3uHAXKr7KiFeyvYb77jot8M_4rH-cqt2Ze3AbnOiZ_wiwg@mail.gmail.com>
>From: Philippe Ombredanne [mailto:pombredanne@nexb.com]
>Sent: Donnerstag, 30. November 2017 15:00
>On Thu, Nov 30, 2017 at 2:36 PM, Patrick Brünn <P.Bruenn@beckhoff.com>
>wrote:
>
>Thomas:
>I tend to prefer as Linus pointed the // style throughout when there
>is only a license and copyright involved. Here this would mean:
>
>> +// SPDX-License-Identifier: GPL-2.0
>> +// Copyright (c) 2004-2011 Freescale Semiconductor, Inc. All Rights
>Reserved.
>
>What's you take since you wrote the doc for this? And is there still
>some updates needed for this since your last patch set?
>
While looking closer into different files I think it would become multiline again:
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Real Time Clock (RTC) Driver for i.MX53
+ * Copyright (c) 2004-2011 Freescale Semiconductor, Inc.
+ * Copyright (c) 2017 Beckhoff Automation GmbH & Co. KG
+ */
>
>Patrick:
>Side note a trailing "All Rights Reserved." in a copyright statement
>is never needed, even though customarily cargo culted by everyone,
>including me.
>Started in 1910, it become fully obsolete in 2000 thanks to Nicaragua
>[1] signing the Berne convention.
>
>[1] https://en.wikipedia.org/wiki/All_rights_reserved
Interessting. I just kept it because it was in the original file from freescale.
But with this background, I think it's fine to drop it, now.
Thanks,
Patrick
Beckhoff Automation GmbH & Co. KG | Managing Director: Dipl. Phys. Hans Beckhoff
Registered office: Verl, Germany | Register court: Guetersloh HRA 7075
^ permalink raw reply
* Re: [PATCH v3 4/5] pinctrl: mcp23s08: configure irq polarity using irq data
From: Linus Walleij @ 2017-11-30 14:21 UTC (permalink / raw)
To: Sebastian Reichel, Marc Zyngier
Cc: Phil Reid, Rob Herring, Mark Rutland,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS
In-Reply-To: <20171121152142.zg3r57gl2kenjwgi@earth>
On Tue, Nov 21, 2017 at 4:21 PM, Sebastian Reichel <sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> IMHO the explicit line-inverter is a bit over-engineered and
> implicit line-inverter is enough, but I'm fine with both solutions.
> I think the DT binding maintainers should comment on this though,
> since it's pretty much a core decision about interrupt specifiers.
I feel the same.
I am very much back and forth on the subject.
Simplicity of use vs modelling the system as it actually works.
Back and forth.
I honestly have just a very vague idea about this.
I don't know if Marc Z as irqchip maintainer has some idea
on how to model inverters on irq lines or if he's seen some
solutions to it out there.
Yours,
Linus Walleij
--
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 v3 4/5] pinctrl: mcp23s08: configure irq polarity using irq data
From: Linus Walleij @ 2017-11-30 14:17 UTC (permalink / raw)
To: Phil Reid
Cc: Rob Herring, Mark Rutland, Sebastian Reichel, linux-gpio,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS
In-Reply-To: <1511252491-79952-5-git-send-email-preid@electromag.com.au>
On Tue, Nov 21, 2017 at 9:21 AM, Phil Reid <preid@electromag.com.au> wrote:
> The irq polarity is already encoded in the irq config. Use that to
> determine the polarity for the mcp32s08 irq output instead of the
> custom microchip,irq-active-high property.
>
> Signed-off-by: Phil Reid <preid@electromag.com.au>
(...)
> if (mcp->irq && mcp->irq_controller) {
> - irq_active_high =
> - device_property_read_bool(dev,
> - "microchip,irq-active-high");
> + if (device_property_present(dev, "microchip,irq-active-high"))
> + dev_warn(dev,
> + "microchip,irq-active-high is deprecated\n");
> +
> + irq_trig = irqd_get_trigger_type(irq_get_irq_data(mcp->irq));
> + if (irq_trig == IRQF_TRIGGER_HIGH)
> + irq_active_high = true;
This makes the setting from the interrupt line override the
custom property.
Should it not be the other way around to preserve compatibility
with elder device trees?
I.e. the custom property overrides the line setting?
Otherwise it looks good.
Yours,
Linus Walleij
^ permalink raw reply
* Re: [PATCH] rtc: add mxc driver for i.MX53
From: Philippe Ombredanne @ 2017-11-30 14:00 UTC (permalink / raw)
To: Patrick Brünn, Thomas Gleixner
Cc: Greg Kroah-Hartman, linux-kernel-dev, Alessandro Zummo,
Alexandre Belloni, Rob Herring, Mark Rutland,
open list:REAL TIME CLOCK (RTC) SUBSYSTEM,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list, Fabio Estevam, Juergen Borleis, Noel Vellemans,
Shawn Guo, Sascha Hauer, Russell King, ARM/FREESCALE
In-Reply-To: <3BB206AB2B1BD448954845CE6FF69A8E01CB531330-vUij6NSodOcSOXk/eDA+lhuDmiLEvxRJ@public.gmane.org>
On Thu, Nov 30, 2017 at 2:36 PM, Patrick Brünn <P.Bruenn-QonKdJ6Bx35Wk0Htik3J/w@public.gmane.org> wrote:
>>From: Philippe Ombredanne [mailto:pombredanne-od1rfyK75/E@public.gmane.org]
>>Sent: Donnerstag, 30. November 2017 09:18
>>>
>>>> +/*
>>>> + * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights
>>Reserved.
>>>> + */
>>>> +
>>>> +/*
>>>> + * The code contained herein is licensed under the GNU General Public
>>>> + * License. You may obtain a copy of the GNU General Public License
>>>> + * Version 2 or later at the following locations:
>>>> + *
>>>> + * http://www.opensource.org/licenses/gpl-license.html
>>>> + * http://www.gnu.org/copyleft/gpl.html
>>>> + */
>>
>>Exactly!
>>And while you are it , you could replace the boilerplate license text
>>with the SPDX id.
>>--
>
> How would a perfect header look like?
> Looking at :
> git show b24413180f5600bcb3bb70fbed5cf186b60864bd -- drivers/
> and:
> git grep SPDX -- Documentation/
>
> it could be:
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights
> + */
> or:
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights
> + */
>
> Personally I would prefer:
> +/*
> + * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights
> + *
> + * SPDX-License-Identifier: GPL-2.0
> + */
>
> So, is there any guideline?
>
> To be clear: I don't want to waste anyone's time on this. But as SPDX
> was intended for automation, I think it might be good to try having
> some common pattern here.
> I don't want to start a discussion, so in case there is no guideline I will
> just take the /* SPDX */ in firstline version.
Patrick:
There are several threads on the topic explaining why: Linus made it
clear that he wants this, and on the first line:
> +// SPDX-License-Identifier: GPL-2.0
Thomas:
I tend to prefer as Linus pointed the // style throughout when there
is only a license and copyright involved. Here this would mean:
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2004-2011 Freescale Semiconductor, Inc. All Rights Reserved.
What's you take since you wrote the doc for this? And is there still
some updates needed for this since your last patch set?
Patrick:
Side note a trailing "All Rights Reserved." in a copyright statement
is never needed, even though customarily cargo culted by everyone,
including me.
Started in 1910, it become fully obsolete in 2000 thanks to Nicaragua
[1] signing the Berne convention.
[1] https://en.wikipedia.org/wiki/All_rights_reserved
--
Cordially
Philippe Ombredanne
--
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] of: overlay: fix memory leak of ovcs on error exit path
From: Geert Uytterhoeven @ 2017-11-30 13:51 UTC (permalink / raw)
To: Dan Carpenter
Cc: Frank Rowand, Geert Uytterhoeven, Colin King, Pantelis Antoniou,
Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
kernel-janitors-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <20171130125002.wlnnev5pjih6d5bz@mwanda>
Hi Dan,
On Thu, Nov 30, 2017 at 1:50 PM, Dan Carpenter <dan.carpenter-QHcLZuEGTsvQT0dZR+AlfA@public.gmane.org> wrote:
> On Thu, Nov 30, 2017 at 07:14:45AM -0500, Frank Rowand wrote:
>> On 11/29/17 14:17, Colin King wrote:
>> > From: Colin Ian King <colin.king-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
>> >
>> > Currently if the call to of_resolve_phandles fails then then ovcs
>> > is not kfree'd on the error exit path. Rather than try and make
>> > the clean up exit path more convoluted, fix this by just kfree'ing
>> > ovcs at the point of error detection and exit via the same exit
>> > path.
>> >
>> > Detected by CoverityScan, CID#1462296 ("Resource Leak")
>> >
>> > Fixes: f948d6d8b792 ("of: overlay: avoid race condition between applying multiple overlays")
>
> The Fixes tag is wrong. It should be:
>
> Fixes: bd80e2555c5c ("of: overlay: Fix cleanup order in of_overlay_apply()")
Oops, sorry for that.
>> > Signed-off-by: Colin Ian King <colin.king-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
>> > ---
>> > drivers/of/overlay.c | 4 +++-
>> > 1 file changed, 3 insertions(+), 1 deletion(-)
>> >
>> > diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
>> > index 53bc9e3f0b98..6c8efe7d8cbb 100644
>> > --- a/drivers/of/overlay.c
>> > +++ b/drivers/of/overlay.c
>> > @@ -708,8 +708,10 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
>> > of_overlay_mutex_lock();
>> >
>> > ret = of_resolve_phandles(tree);
>> > - if (ret)
>> > + if (ret) {
>> > + kfree(ovcs);
>> > goto err_overlay_unlock;
>> > + }
>> >
>> > mutex_lock(&of_mutex);
>>
>> False coverity warning. ovcs is freed in free_overlay_changeset().
>
> You're looking at an older version of the code and Colin is looking at
> linux-next.
BTW, it's a bit confusing that sometimes ovcs must be freed manually, and
sometimes this is taken care of by free_overlay_changeset().
Especially when building on top of that, and adding more error cases in
between err_free_overlay_changeset and err_overlay_unlock.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
--
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] rtc: add mxc driver for i.MX53
From: Alexandre Belloni @ 2017-11-30 13:42 UTC (permalink / raw)
To: Patrick Brünn
Cc: Philippe Ombredanne, Greg Kroah-Hartman, linux-kernel-dev,
Alessandro Zummo, Rob Herring, Mark Rutland,
open list:REAL TIME CLOCK (RTC) SUBSYSTEM,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list, Fabio Estevam, Juergen Borleis, Noel Vellemans,
Shawn Guo, Sascha Hauer, Russell King, ARM/FREESCALE
In-Reply-To: <3BB206AB2B1BD448954845CE6FF69A8E01CB531330@NT-Mail07.beckhoff.com>
Hi,
On 30/11/2017 at 13:36:22 +0000, Patrick Brünn wrote:
> >From: Philippe Ombredanne [mailto:pombredanne@nexb.com]
> >Sent: Donnerstag, 30. November 2017 09:18
> >>
> >>> +/*
> >>> + * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights
> >Reserved.
> >>> + */
> >>> +
> >>> +/*
> >>> + * The code contained herein is licensed under the GNU General Public
> >>> + * License. You may obtain a copy of the GNU General Public License
> >>> + * Version 2 or later at the following locations:
> >>> + *
> >>> + * http://www.opensource.org/licenses/gpl-license.html
> >>> + * http://www.gnu.org/copyleft/gpl.html
> >>> + */
> >
> >Exactly!
> >And while you are it , you could replace the boilerplate license text
> >with the SPDX id.
> >--
>
> How would a perfect header look like?
> Looking at :
> git show b24413180f5600bcb3bb70fbed5cf186b60864bd -- drivers/
> and:
> git grep SPDX -- Documentation/
>
> it could be:
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights
> + */
> or:
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights
> + */
>
> Personally I would prefer:
> +/*
> + * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights
> + *
> + * SPDX-License-Identifier: GPL-2.0
> + */
>
> So, is there any guideline?
>
I'm quoting LWN here:
"For normal C source files, the string will be a comment using the "//"
syntax; header files, instead, use traditional (/* */) comments for
reasons related to tooling."
> To be clear: I don't want to waste anyone's time on this. But as SPDX
> was intended for automation, I think it might be good to try having
> some common pattern here.
> I don't want to start a discussion, so in case there is no guideline I will
> just take the /* SPDX */ in firstline version.
--
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply
* Re: [RFCv2 PATCH 05/36] iommu/process: Bind and unbind process to and from devices
From: Jean-Philippe Brucker @ 2017-11-30 13:39 UTC (permalink / raw)
To: Yisheng Xie, linux-arm-kernel@lists.infradead.org,
linux-pci@vger.kernel.org, linux-acpi@vger.kernel.org,
devicetree@vger.kernel.org, iommu@lists.linux-foundation.org
Cc: joro@8bytes.org, robh+dt@kernel.org, Mark Rutland,
Catalin Marinas, Will Deacon, Lorenzo Pieralisi,
hanjun.guo@linaro.org, Sudeep Holla, rjw@rjwysocki.net,
lenb@kernel.org, Robin Murphy, bhelgaas@google.com,
alex.williamson@redhat.com, tn@semihalf.com, liubo95@huawei.com,
thunder.leizhen@huawei.com, gabriele.paoloni@huawei.com, nwatters
In-Reply-To: <7ba3d70a-2364-2a92-39a8-ed112807b83c@huawei.com>
On 30/11/17 01:11, Yisheng Xie wrote:
> Hi, Jean,
>
> On 2017/11/29 23:01, Jean-Philippe Brucker wrote:
>> On 29/11/17 06:08, Yisheng Xie wrote:
>>>
>>>
>>> On 2017/10/6 21:31, Jean-Philippe Brucker wrote:
>>>> +int iommu_process_bind_device(struct device *dev, struct task_struct *task,
>>>> + int *pasid, int flags)
>>>> +{
>>> [..]
>>>> + err = iommu_process_attach_locked(context, dev);
>>>> + if (err)
>>>> + iommu_process_put_locked(process);
>>> one ref for a context is enough right? so also need call iommu_process_put_locked()
>>> if attach ok, or will be leak if user call bind twice for the same device and task.
>>
>> I wasn't sure, I think I prefer taking one ref for each bind. If user
>> calls bind twice, it should call unbind twice as well (in case of leak we
>> free the context on process exit).
>>
>> Also with this implementation, user can call bind for two devices in the
>> same domain, which will share the same context structure. So we have to
>> take as many refs as bind() calls.
>
>
> hmm, it has two ref, one for _context_ and the other for *process* (or maybe mm for
> your next version), right? For each bind it will take a ref of context as present
> design. but why also process ref need be taken for each bind? I mean it seems does
> not break _user can call bind for two devices in the same domain_.
>
> And if you really want to take a ref of *process* for echo bind, you should put it when
> unbind, right? I just not find where you put the ref of process when unbind. But just put
> the process ref when free context.
>
> Maybe I just miss something.
No you're right I misunderstood, sorry about that. Each context has a
single ref to a process, so we do need to drop the process ref here as you
pointed out.
I thought I exercised this path though, I'll update my test suite. Also
attach_locked shouldn't take a context ref if attach fails...
Thanks a lot,
Jean
^ permalink raw reply
* Re: [PATCH] gpio: pca953x: fix vendor prefix for PCA9654
From: Linus Walleij @ 2017-11-30 13:38 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: Rob Herring, linux-gpio,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Mark Rutland, Linux-Renesas
In-Reply-To: <20171116201842.006310882@cogentembedded.com>
On Thu, Nov 16, 2017 at 9:18 PM, Sergei Shtylyov
<sergei.shtylyov@cogentembedded.com> wrote:
> Despite commit 55020c8056a8 ("of: Add vendor prefix for ON Semiconductor
> Corp.") was made long ago, the latter commit 9f49f6dd0473 ("gpio: pca953x:
> add onsemi,pca9654 id") made use of another, undocumented vendor prefix.
> Since such prefix doesn't seem to be used in any device trees, I think we
> can just fix the "compatible" string in the driver and the bindings and be
> done with that...
>
> Fixes: 9f49f6dd0473 ("gpio: pca953x: add onsemi,pca9654 id")
> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Patch applied with Rob's ACK.
Thanks for spotting this in time.
Yours,
Linus Walleij
^ permalink raw reply
* Re: [PATCH] of: overlay: fix memory leak of ovcs on error exit path
From: Frank Rowand @ 2017-11-30 13:37 UTC (permalink / raw)
To: Colin Ian King, Pantelis Antoniou, Rob Herring,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: kernel-janitors-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <806a0467-87c8-4100-c7f2-54cfa8732465-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
Hi Colin, Rob,
On 11/30/17 07:18, Colin Ian King wrote:
> On 30/11/17 12:14, Frank Rowand wrote:
>> On 11/29/17 14:17, Colin King wrote:
>>> From: Colin Ian King <colin.king-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
>>>
>>> Currently if the call to of_resolve_phandles fails then then ovcs
>>> is not kfree'd on the error exit path. Rather than try and make
>>> the clean up exit path more convoluted, fix this by just kfree'ing
>>> ovcs at the point of error detection and exit via the same exit
>>> path.
>>>
>>> Detected by CoverityScan, CID#1462296 ("Resource Leak")
>>>
>>> Fixes: f948d6d8b792 ("of: overlay: avoid race condition between applying multiple overlays")
>>> Signed-off-by: Colin Ian King <colin.king-Z7WLFzj8eWMS+FvcfC7Uqw@public.gmane.org>
>>> ---
>>> drivers/of/overlay.c | 4 +++-
>>> 1 file changed, 3 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
>>> index 53bc9e3f0b98..6c8efe7d8cbb 100644
>>> --- a/drivers/of/overlay.c
>>> +++ b/drivers/of/overlay.c
>>> @@ -708,8 +708,10 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
>>> of_overlay_mutex_lock();
>>>
>>> ret = of_resolve_phandles(tree);
>>> - if (ret)
>>> + if (ret) {
>>> + kfree(ovcs);
>>> goto err_overlay_unlock;
>>> + }
>>>
>>> mutex_lock(&of_mutex);
>>>
>>>
>>
>> False coverity warning. ovcs is freed in free_overlay_changeset().
>>
>
> The error exit path is via err_overlay_unlock:
>
> err_overlay_unlock:
> of_overlay_mutex_unlock();
>
> out:
> pr_debug("%s() err=%d\n", __func__, ret);
>
> return ret;
>
> ..so there is no call to free_overlay_changeset there.
>
> Colin
>
OK, I was looking at 4.15-rc1. You must be looking at a later version where
"[PATCH 1/2] of: overlay: Fix cleanup order in of_overlay_apply()" has been
applied. Thanks for providing the extra details about the exit path so I
could see that.
Rob, I think that the fix for cleanup order was not the best way to fix that
problem. A better method would have been to move "mutex_lock(&of_mutex);"
up 5 lines, to just before calling of_reserve_phandles(). The problem
found by coverity was caused by the "Fix cleanup order" patch.
I can create that alternate fix if you would like, but I am traveling
right now and don't want to submit a patch without boot testing, so
there will be a slight delay.
-Frank
--
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] rtc: add mxc driver for i.MX53
From: Patrick Brünn @ 2017-11-30 13:36 UTC (permalink / raw)
To: Philippe Ombredanne, Greg Kroah-Hartman
Cc: linux-kernel-dev, Alessandro Zummo, Alexandre Belloni,
Rob Herring, Mark Rutland,
open list:REAL TIME CLOCK (RTC) SUBSYSTEM,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list, Fabio Estevam, Juergen Borleis, Noel Vellemans,
Shawn Guo, Sascha Hauer, Russell King,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE
In-Reply-To: <CAOFm3uFb8Jo9detD2CziuYt_DH_ohYD9P=uvqHugKDxpN+xvAQ@mail.gmail.com>
>From: Philippe Ombredanne [mailto:pombredanne@nexb.com]
>Sent: Donnerstag, 30. November 2017 09:18
>>
>>> +/*
>>> + * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights
>Reserved.
>>> + */
>>> +
>>> +/*
>>> + * The code contained herein is licensed under the GNU General Public
>>> + * License. You may obtain a copy of the GNU General Public License
>>> + * Version 2 or later at the following locations:
>>> + *
>>> + * http://www.opensource.org/licenses/gpl-license.html
>>> + * http://www.gnu.org/copyleft/gpl.html
>>> + */
>
>Exactly!
>And while you are it , you could replace the boilerplate license text
>with the SPDX id.
>--
How would a perfect header look like?
Looking at :
git show b24413180f5600bcb3bb70fbed5cf186b60864bd -- drivers/
and:
git grep SPDX -- Documentation/
it could be:
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights
+ */
or:
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights
+ */
Personally I would prefer:
+/*
+ * Copyright (C) 2004-2011 Freescale Semiconductor, Inc. All Rights
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
So, is there any guideline?
To be clear: I don't want to waste anyone's time on this. But as SPDX
was intended for automation, I think it might be good to try having
some common pattern here.
I don't want to start a discussion, so in case there is no guideline I will
just take the /* SPDX */ in firstline version.
Regards, Patrick
Beckhoff Automation GmbH & Co. KG | Managing Director: Dipl. Phys. Hans Beckhoff
Registered office: Verl, Germany | Register court: Guetersloh HRA 7075
^ permalink raw reply
* [PATCH v6] mfd: syscon: Add hardware spinlock support
From: Baolin Wang @ 2017-11-30 13:33 UTC (permalink / raw)
To: lee.jones, arnd, robh+dt, mark.rutland
Cc: broonie, linux-kernel, devicetree, baolin.wang
Some system control registers need hardware spinlock to synchronize
between the multiple subsystems, so we should add hardware spinlock
support for syscon.
Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
Acked-by: Rob Herring <robh@kernel.org>
---
Changes since v5:
- Fix the case that hwspinlock is not enabled.
Changes since v4:
- Add one exapmle to show how to add hwlock.
- Fix the coding style issue.
Changes since v3:
- Add error handling for of_hwspin_lock_get_id()
Changes since v2:
- Add acked tag from Rob.
Changes since v1:
- Remove timeout configuration.
- Modify the binding file to add hwlocks.
---
Documentation/devicetree/bindings/mfd/syscon.txt | 8 +++++++
drivers/mfd/syscon.c | 25 ++++++++++++++++++++++
2 files changed, 33 insertions(+)
diff --git a/Documentation/devicetree/bindings/mfd/syscon.txt b/Documentation/devicetree/bindings/mfd/syscon.txt
index 8b92d45..25d9e9c 100644
--- a/Documentation/devicetree/bindings/mfd/syscon.txt
+++ b/Documentation/devicetree/bindings/mfd/syscon.txt
@@ -16,9 +16,17 @@ Required properties:
Optional property:
- reg-io-width: the size (in bytes) of the IO accesses that should be
performed on the device.
+- hwlocks: reference to a phandle of a hardware spinlock provider node.
Examples:
gpr: iomuxc-gpr@20e0000 {
compatible = "fsl,imx6q-iomuxc-gpr", "syscon";
reg = <0x020e0000 0x38>;
+ hwlocks = <&hwlock1 1>;
+};
+
+hwlock1: hwspinlock@40500000 {
+ ...
+ reg = <0x40500000 0x1000>;
+ #hwlock-cells = <1>;
};
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index b93fe4c..a7ae391 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -13,6 +13,7 @@
*/
#include <linux/err.h>
+#include <linux/hwspinlock.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/list.h>
@@ -87,6 +88,30 @@ static struct syscon *of_syscon_register(struct device_node *np)
if (ret)
reg_io_width = 4;
+ ret = of_hwspin_lock_get_id(np, 0);
+ if (ret > 0) {
+ syscon_config.hwlock_id = ret;
+ syscon_config.hwlock_mode = HWLOCK_IRQSTATE;
+ } else {
+ switch (ret) {
+ case -ENOENT:
+ /* Ignore missing hwlock, it's optional. */
+ break;
+ case 0:
+ /* In case of the HWSPINLOCK is not enabled. */
+ if (!IS_ENABLED(CONFIG_HWSPINLOCK))
+ break;
+
+ ret = -EINVAL;
+ /* fall-through */
+ default:
+ pr_err("Failed to retrieve valid hwlock: %d\n", ret);
+ /* fall-through */
+ case -EPROBE_DEFER:
+ goto err_regmap;
+ }
+ }
+
syscon_config.reg_stride = reg_io_width;
syscon_config.val_bits = reg_io_width * 8;
syscon_config.max_register = resource_size(&res) - reg_io_width;
--
1.7.9.5
^ permalink raw reply related
* [PATCH 3/3] dt-bindings: eeprom: at25: Document device-specific compatible values
From: Geert Uytterhoeven @ 2017-11-30 13:29 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Rob Herring, Mark Rutland,
Ivo Sieben
Cc: Chris Wright, Wolfram Sang, devicetree, linux-kernel,
Geert Uytterhoeven
In-Reply-To: <1512048586-17534-1-git-send-email-geert+renesas@glider.be>
Document the recommended presence of a device-specific compatible value,
and list examples that are already in use or soon will be.
This will allow checkpatch to validate compatible values in DTS.
Update the example to match current best practices (generic node name,
specific compatible value first).
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
Documentation/devicetree/bindings/eeprom/at25.txt | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/eeprom/at25.txt b/Documentation/devicetree/bindings/eeprom/at25.txt
index 9b1782809386844d..7129affb2956bb0d 100644
--- a/Documentation/devicetree/bindings/eeprom/at25.txt
+++ b/Documentation/devicetree/bindings/eeprom/at25.txt
@@ -1,7 +1,12 @@
EEPROMs (SPI) compatible with Atmel at25.
Required properties:
-- compatible : "atmel,at25".
+- compatible : Should be "<vendor>,<type>", and generic value "atmel,at25".
+ Example "<vendor>,<type>" values:
+ "microchip,25lc040"
+ "st,m95m02"
+ "st,m95256"
+
- reg : chip select number
- spi-max-frequency : max spi frequency to use
- pagesize : size of the eeprom page
@@ -24,8 +29,8 @@ Obsolete legacy properties can be used in place of "size", "pagesize",
Additional compatible properties are also allowed.
Example:
- at25@0 {
- compatible = "atmel,at25", "st,m95256";
+ eeprom@0 {
+ compatible = "st,m95256", "atmel,at25";
reg = <0>
spi-max-frequency = <5000000>;
spi-cpha;
--
2.7.4
^ permalink raw reply related
* [PATCH 2/3] dt-bindings: eeprom: at25: Grammar s/are can/can/
From: Geert Uytterhoeven @ 2017-11-30 13:29 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Rob Herring, Mark Rutland,
Ivo Sieben
Cc: Chris Wright, Wolfram Sang, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven
In-Reply-To: <1512048586-17534-1-git-send-email-geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
Signed-off-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
---
Documentation/devicetree/bindings/eeprom/at25.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/eeprom/at25.txt b/Documentation/devicetree/bindings/eeprom/at25.txt
index d00779e4ab4377b9..9b1782809386844d 100644
--- a/Documentation/devicetree/bindings/eeprom/at25.txt
+++ b/Documentation/devicetree/bindings/eeprom/at25.txt
@@ -15,7 +15,7 @@ Optional properties:
- spi-cpol : SPI inverse clock polarity, as per spi-bus bindings.
- read-only : this parameter-less property disables writes to the eeprom
-Obsolete legacy properties are can be used in place of "size", "pagesize",
+Obsolete legacy properties can be used in place of "size", "pagesize",
"address-width", and "read-only":
- at25,byte-len : total eeprom size in bytes
- at25,addr-mode : addr-mode flags, as defined in include/linux/spi/eeprom.h
--
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 1/3] eeprom: at25: Add DT support for EEPROMs with odd address bits
From: Geert Uytterhoeven @ 2017-11-30 13:29 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Rob Herring, Mark Rutland,
Ivo Sieben
Cc: Chris Wright, Wolfram Sang, devicetree, linux-kernel,
Geert Uytterhoeven
In-Reply-To: <1512048586-17534-1-git-send-email-geert+renesas@glider.be>
Certain EEPROMS have a size that is larger than the number of address
bytes would allow, and store the MSB of the address in bit 3 of the
instruction byte.
This can be described in platform data using EE_INSTR_BIT3_IS_ADDR, or
in DT using the obsolete legacy "at25,addr-mode" property.
But currently there exists no non-deprecated way to describe this in DT.
Hence extend the existing "address-width" DT property to allow
specifying 9, 17, or 25 address bits, and enable support for that in the
driver.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
EEPROMs using 9 address bits are common (e.g. M95040, 25AA040/25LC040).
Do EEPROMs using 17 or 25 address bits, as mentioned in
include/linux/spi/eeprom.h, really exist?
Or should we just limit it to a single odd value (9 bits)?
---
Documentation/devicetree/bindings/eeprom/at25.txt | 4 +++-
drivers/misc/eeprom/at25.c | 4 ++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/eeprom/at25.txt b/Documentation/devicetree/bindings/eeprom/at25.txt
index 1d3447165c374f67..d00779e4ab4377b9 100644
--- a/Documentation/devicetree/bindings/eeprom/at25.txt
+++ b/Documentation/devicetree/bindings/eeprom/at25.txt
@@ -6,7 +6,9 @@ Required properties:
- spi-max-frequency : max spi frequency to use
- pagesize : size of the eeprom page
- size : total eeprom size in bytes
-- address-width : number of address bits (one of 8, 16, or 24)
+- address-width : number of address bits (one of 8, 9, 16, 17, 24, or 25).
+ For odd values, the MSB of the address is sent as bit 3 of the instruction
+ byte, before the address byte(s).
Optional properties:
- spi-cpha : SPI shifted clock phase, as per spi-bus bindings.
diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c
index 5afe4cd165699060..a50a0f16fa0e1d1d 100644
--- a/drivers/misc/eeprom/at25.c
+++ b/drivers/misc/eeprom/at25.c
@@ -275,6 +275,10 @@ static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip)
"Error: missing \"address-width\" property\n");
return -ENODEV;
}
+ if (val & 1) {
+ chip->flags |= EE_INSTR_BIT3_IS_ADDR;
+ val -= 1;
+ }
switch (val) {
case 8:
chip->flags |= EE_ADDR1;
--
2.7.4
^ permalink raw reply related
* [PATCH 0/3] eeprom: at25: Add DT support for 25lc040
From: Geert Uytterhoeven @ 2017-11-30 13:29 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Rob Herring, Mark Rutland,
Ivo Sieben
Cc: Chris Wright, Wolfram Sang, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven
Hi all,
Some "atmel,at25" compatible SPI EEPROMs (e.g. Microchip 25lc040) use an
odd number of address bits. This patch series adds support for
instantiating such devices from DT.
Do EEPROMs using 17 or 25 address bits, as mentioned in
include/linux/spi/eeprom.h, really exist?
Or should we just limit it to a single odd value (9 bits)?
This has been tested with a bunch of 25lc040 EEPROMs.
Thanks!
Geert Uytterhoeven (3):
eeprom: at25: Add DT support for EEPROMs with odd address bits
dt-bindings: eeprom: at25: Grammar s/are can/can/
dt-bindings: eeprom: at25: Document device-specific compatible values
Documentation/devicetree/bindings/eeprom/at25.txt | 17 ++++++++++++-----
drivers/misc/eeprom/at25.c | 4 ++++
2 files changed, 16 insertions(+), 5 deletions(-)
--
2.7.4
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
--
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 1/2] documentation: Add compatibles for Amlogic Meson AXG pin controllers
From: Linus Walleij @ 2017-11-30 13:27 UTC (permalink / raw)
To: Yixun Lan
Cc: Kevin Hilman, Rob Herring, Mark Rutland, Neil Armstrong,
Jerome Brunet, Carlo Caione, Xingyu Chen,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
linux-gpio-u79uwXL29TY76Z2rM5mHXA, open list:ARM/Amlogic Meson...,
Linux ARM, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <20171120100851.17366-2-yixun.lan-LpR1jeaWuhtBDgjK7y7TUQ@public.gmane.org>
On Mon, Nov 20, 2017 at 11:08 AM, Yixun Lan <yixun.lan-LpR1jeaWuhtBDgjK7y7TUQ@public.gmane.org> wrote:
> From: Xingyu Chen <xingyu.chen-LpR1jeaWuhtBDgjK7y7TUQ@public.gmane.org>
>
> Add compatibles for Amlogic Meson AXG pin controllers
>
> Reviewed-by: Neil Armstrong <narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
> Signed-off-by: Xingyu Chen <xingyu.chen-LpR1jeaWuhtBDgjK7y7TUQ@public.gmane.org>
> Signed-off-by: Yixun Lan <yixun.lan-LpR1jeaWuhtBDgjK7y7TUQ@public.gmane.org>
Patch applied with Rob's ACK.
Yours,
Linus Walleij
--
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 v5 01/16] dt-bindings: mtd: gpmc-onenand: Update properties description
From: Ladislav Michl @ 2017-11-30 13:09 UTC (permalink / raw)
To: Boris Brezillon
Cc: Rob Herring, Aaro Koskinen, Tony Lindgren, Peter Ujfalusi,
Kyungmin Park, linux-mtd, linux-omap, devicetree, Roger Quadros
In-Reply-To: <20171130112544.5509ca1b@bbrezillon>
On Thu, Nov 30, 2017 at 11:25:44AM +0100, Boris Brezillon wrote:
> +Rob and the DT ML
>
> On Wed, 15 Nov 2017 17:24:58 +0100
> Ladislav Michl <ladis@linux-mips.org> wrote:
>
> > Compatible property is required for OMAP2+ mtd driver. Also
> > add INT pin gpio description and delete unused dma-channel
> > property.
> >
> > Signed-off-by: Ladislav Michl <ladis@linux-mips.org>
> > Reviewed-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
> > Acked-by: Roger Quadros <rogerq@ti.com>
> > ---
> >
> > Changes in v5:
> > - renamed R/B pin to INT pin
> >
> > Changes in v4:
> > - new patch
> >
> > Changes in v3: None
> > Changes in v2: None
> >
> > Documentation/devicetree/bindings/mtd/gpmc-onenand.txt | 6 ++++--
> > 1 file changed, 4 insertions(+), 2 deletions(-)
> >
> > diff --git a/Documentation/devicetree/bindings/mtd/gpmc-onenand.txt b/Documentation/devicetree/bindings/mtd/gpmc-onenand.txt
> > index b6e8bfd024f4..e9f01a963a0a 100644
> > --- a/Documentation/devicetree/bindings/mtd/gpmc-onenand.txt
> > +++ b/Documentation/devicetree/bindings/mtd/gpmc-onenand.txt
> > @@ -9,13 +9,14 @@ Documentation/devicetree/bindings/memory-controllers/omap-gpmc.txt
> >
> > Required properties:
> >
> > + - compatible: "ti,omap2-onenand"
>
> Don't you break backward compat by adding this new requirement?
I was told it is okay in this case as OneNAND conversion to DT is unfinished.
There was compat fixup code in earlier versions of patchset and it was dropped
later. If others see it opposite way I can add it back.
> > - reg: The CS line the peripheral is connected to
> > - - gpmc,device-width Width of the ONENAND device connected to the GPMC
> > + - gpmc,device-width: Width of the ONENAND device connected to the GPMC
> > in bytes. Must be 1 or 2.
> >
> > Optional properties:
> >
> > - - dma-channel: DMA Channel index
> > + - int-gpios: GPIO specifier for the INT pin.
> >
> > For inline partition table parsing (optional):
> >
> > @@ -35,6 +36,7 @@ Example for an OMAP3430 board:
> > #size-cells = <1>;
> >
> > onenand@0 {
> > + compatible = "ti,omap2-onenand";
> > reg = <0 0 0>; /* CS0, offset 0 */
> > gpmc,device-width = <2>;
> >
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply
* Re: [PATCH 2/2] can: mcp2517fd: Add Microchip mcp2517fd CAN FD driver
From: Marc Kleine-Budde @ 2017-11-30 13:04 UTC (permalink / raw)
To: kernel-TqfNSX0MhmxHKSADF0wUEw, Wolfgang Grandegger, Rob Herring,
Mark Rutland, linux-can-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20171124183509.12810-3-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
[-- Attachment #1.1: Type: text/plain, Size: 125147 bytes --]
On 11/24/2017 07:35 PM, kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org wrote:
> 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.
I've review about the last 1/3 of the driver. See comments inline.
>
> 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.
> + */
> +
Please use a common prefix, e.g. MCP2517 for all defines.
> +#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)
bool enabel
> +{
> + 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;
Please use if-else and move the | to the end of the line.
> + 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:
Please add comments for fallthrough for all your switch-case.
> + 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;
We have the max() helper function:
bw_sharing_log2bits = max(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");
No need for the dev_err, open_candev() prints in case of an error.
> + 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;
please move this to the common cleanup at the end of this function
> + }
> +
> + /* 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)
Please move the ifdef outside of the function and provide a static
inline void for the else branch.
> + 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
Why does this code depend on 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;
> + }
Why do you keep the clock running after the device has been probed?
Usually we enable the clock during open().
> +
> + 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);
A if else is probably more readable...and you don't need the comment
> +
> + /* check in device tree for overrrides */
> + ret = mcp2517fd_of_parse(priv);
> + if (ret)
> + return ret;
You're keeping the clock running.
> +
> + /* 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;
same here
> + }
> + }
> + 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 */
this obvious :)
> + 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
>
--
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] ARM: dts: imx6sx-sdb: Add DRM panel, power-enable and backlight support
From: Marco Franchi @ 2017-11-30 13:02 UTC (permalink / raw)
To: shawnguo-DgEjT+Ai2ygdnm+yROfE0A
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
festevam-Re5JQEeQqe8AvxtiuMwx3w, marcofrk-Re5JQEeQqe8AvxtiuMwx3w,
Marco Franchi
It is preferred to use the panel compatible string rather than passing
the LCD timming in the device tree.
So pass the "sii,43wvf1g" compatible string, a new driver for the Seiko
Panel, which describes the parallel LCD.
This new driver needs two power supply, so pass 'dvdd-supply' and
'avdd-supply', which makes the kernel to turn on the LCD on is own.
Also, pass the 'backlight' property as described in
Documentation/devicetree/bindings/display/panel/simple-panel.txt
Signed-off-by: Marco Franchi <marco.franchi-3arQi8VN3Tc@public.gmane.org>
---
arch/arm/boot/dts/imx6sx-sdb.dtsi | 49 ++++++++++++++++++++-------------------
1 file changed, 25 insertions(+), 24 deletions(-)
diff --git a/arch/arm/boot/dts/imx6sx-sdb.dtsi b/arch/arm/boot/dts/imx6sx-sdb.dtsi
index da81552..3a597a4 100644
--- a/arch/arm/boot/dts/imx6sx-sdb.dtsi
+++ b/arch/arm/boot/dts/imx6sx-sdb.dtsi
@@ -24,7 +24,7 @@
reg = <0x80000000 0x40000000>;
};
- backlight {
+ backlight_display: backlight-display {
compatible = "pwm-backlight";
pwms = <&pwm3 0 5000000>;
brightness-levels = <0 4 8 16 32 64 128 255>;
@@ -129,6 +129,14 @@
regulator-max-microvolt = <3300000>;
gpios = <&gpio2 6 GPIO_ACTIVE_LOW>;
};
+
+ reg_lcd_5v: regulator@7 {
+ compatible = "regulator-fixed";
+ reg = <7>;
+ regulator-name = "lcd-5v0";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ };
};
sound {
@@ -146,6 +154,19 @@
mux-int-port = <2>;
mux-ext-port = <6>;
};
+
+ panel {
+ compatible = "sii,43wvf1g";
+ backlight = <&backlight_display>;
+ dvdd-supply = <®_lcd_3v3>;
+ avdd-supply = <®_lcd_5v>;
+
+ port {
+ panel_in: endpoint {
+ remote-endpoint = <&display_out>;
+ };
+ };
+ };
};
&audmux {
@@ -215,31 +236,11 @@
&lcdif1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lcd>;
- lcd-supply = <®_lcd_3v3>;
- display = <&display0>;
status = "okay";
- display0: display0 {
- bits-per-pixel = <16>;
- bus-width = <24>;
-
- display-timings {
- native-mode = <&timing0>;
- timing0: timing0 {
- clock-frequency = <33500000>;
- hactive = <800>;
- vactive = <480>;
- hback-porch = <89>;
- hfront-porch = <164>;
- vback-porch = <23>;
- vfront-porch = <10>;
- hsync-len = <10>;
- vsync-len = <10>;
- hsync-active = <0>;
- vsync-active = <0>;
- de-active = <1>;
- pixelclk-active = <0>;
- };
+ port {
+ display_out: endpoint {
+ remote-endpoint = <&panel_in>;
};
};
};
--
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] ARM: dts: imx6sl-evk: Add DRM panel, power-enable and backlight support
From: Marco Franchi @ 2017-11-30 13:00 UTC (permalink / raw)
To: shawnguo-DgEjT+Ai2ygdnm+yROfE0A
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
festevam-Re5JQEeQqe8AvxtiuMwx3w, marcofrk-Re5JQEeQqe8AvxtiuMwx3w,
Marco Frank
From: Marco Frank <marco.franchi-3arQi8VN3Tc@public.gmane.org>
It is preferred to use the panel compatible string rather than passing
the LCD timming in the device tree.
So pass the "sii,43wvf1g" compatible string, a new driver for the Seiko
Panel, which describes the parallel LCD.
This new driver needs two power supply, so pass 'dvdd-supply' and
'avdd-supply', which makes the kernel to turn on the LCD on is own.
Also, pass the 'backlight' property as described in
Documentation/devicetree/bindings/display/panel/simple-panel.txt
Signed-off-by: Marco Franchi <marco.franchi-3arQi8VN3Tc@public.gmane.org>
---
arch/arm/boot/dts/imx6sl-evk.dts | 49 ++++++++++++++++++++--------------------
1 file changed, 25 insertions(+), 24 deletions(-)
diff --git a/arch/arm/boot/dts/imx6sl-evk.dts b/arch/arm/boot/dts/imx6sl-evk.dts
index 0a90eea..d5148f4 100644
--- a/arch/arm/boot/dts/imx6sl-evk.dts
+++ b/arch/arm/boot/dts/imx6sl-evk.dts
@@ -20,7 +20,7 @@
reg = <0x80000000 0x40000000>;
};
- backlight {
+ backlight_display: backlight_display {
compatible = "pwm-backlight";
pwms = <&pwm1 0 5000000>;
brightness-levels = <0 4 8 16 32 64 128 255>;
@@ -91,6 +91,14 @@
gpio = <&gpio4 3 0>;
enable-active-high;
};
+
+ reg_lcd_5v: regulator@5 {
+ compatible = "regulator-fixed";
+ reg = <5>;
+ regulator-name = "lcd-5v0";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ };
};
sound {
@@ -108,6 +116,19 @@
mux-int-port = <2>;
mux-ext-port = <3>;
};
+
+ panel {
+ compatible = "sii,43wvf1g";
+ backlight = <&backlight_display>;
+ dvdd-supply = <®_lcd_3v3>;
+ avdd-supply = <®_lcd_5v>;
+
+ port {
+ panel_in: endpoint {
+ remote-endpoint = <&display_out>;
+ };
+ };
+ };
};
&audmux {
@@ -546,31 +567,11 @@
&lcdif {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lcd>;
- lcd-supply = <®_lcd_3v3>;
- display = <&display0>;
status = "okay";
- display0: display0 {
- bits-per-pixel = <32>;
- bus-width = <24>;
-
- display-timings {
- native-mode = <&timing0>;
- timing0: timing0 {
- clock-frequency = <33500000>;
- hactive = <800>;
- vactive = <480>;
- hback-porch = <89>;
- hfront-porch = <164>;
- vback-porch = <23>;
- vfront-porch = <10>;
- hsync-len = <10>;
- vsync-len = <10>;
- hsync-active = <0>;
- vsync-active = <0>;
- de-active = <1>;
- pixelclk-active = <0>;
- };
+ port {
+ display_out: endpoint {
+ remote-endpoint = <&panel_in>;
};
};
};
--
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 v2 4/4] ARM: dts: alt: Convert to named i2c-gpio bindings
From: Geert Uytterhoeven @ 2017-11-30 12:57 UTC (permalink / raw)
To: Simon Horman, Magnus Damm
Cc: Wolfram Sang, Linus Walleij, linux-renesas-soc, linux-arm-kernel,
linux-i2c, devicetree, Geert Uytterhoeven
In-Reply-To: <1512046646-15253-1-git-send-email-geert+renesas@glider.be>
Commits 7d29f509d2cfd807 ("dt-bindings: i2c: i2c-gpio: Add support for
named gpios") and 05c74778858d7d99 ("i2c: gpio: Add support for named
gpios in DT") introduced named i2c-gpio DT bindings, and deprecated the
more error-prone unnamed variant.
Switch to the new bindings, and add the missing GPIO_OPEN_DRAIN I/O
flags, which were implicitly assumed before.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v2:
- New.
---
arch/arm/boot/dts/r8a7794-alt.dts | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/arch/arm/boot/dts/r8a7794-alt.dts b/arch/arm/boot/dts/r8a7794-alt.dts
index bd98790d964e13a7..60c6515c499660eb 100644
--- a/arch/arm/boot/dts/r8a7794-alt.dts
+++ b/arch/arm/boot/dts/r8a7794-alt.dts
@@ -143,9 +143,8 @@
#size-cells = <0>;
compatible = "i2c-gpio";
status = "disabled";
- gpios = <&gpio4 9 GPIO_ACTIVE_HIGH /* sda */
- &gpio4 8 GPIO_ACTIVE_HIGH /* scl */
- >;
+ sda-gpios = <&gpio4 9 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
+ scl-gpios = <&gpio4 8 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <5>;
};
--
2.7.4
^ permalink raw reply related
* [PATCH v2 3/4] ARM: dts: koelsch: Convert to named i2c-gpio bindings
From: Geert Uytterhoeven @ 2017-11-30 12:57 UTC (permalink / raw)
To: Simon Horman, Magnus Damm
Cc: Wolfram Sang, Linus Walleij, linux-renesas-soc, linux-arm-kernel,
linux-i2c, devicetree, Geert Uytterhoeven
In-Reply-To: <1512046646-15253-1-git-send-email-geert+renesas@glider.be>
Commits 7d29f509d2cfd807 ("dt-bindings: i2c: i2c-gpio: Add support for
named gpios") and 05c74778858d7d99 ("i2c: gpio: Add support for named
gpios in DT") introduced named i2c-gpio DT bindings, and deprecated the
more error-prone unnamed variant.
Switch to the new bindings, and add the missing GPIO_OPEN_DRAIN I/O
flags, which were implicitly assumed before.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v2:
- New.
---
arch/arm/boot/dts/r8a7791-koelsch.dts | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/arch/arm/boot/dts/r8a7791-koelsch.dts b/arch/arm/boot/dts/r8a7791-koelsch.dts
index fac9cdea49650591..a50924d12b6f37d5 100644
--- a/arch/arm/boot/dts/r8a7791-koelsch.dts
+++ b/arch/arm/boot/dts/r8a7791-koelsch.dts
@@ -312,9 +312,8 @@
#size-cells = <0>;
compatible = "i2c-gpio";
status = "disabled";
- gpios = <&gpio7 16 GPIO_ACTIVE_HIGH /* sda */
- &gpio7 15 GPIO_ACTIVE_HIGH /* scl */
- >;
+ sda-gpios = <&gpio7 16 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
+ scl-gpios = <&gpio7 15 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
i2c-gpio,delay-us = <5>;
};
--
2.7.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox