* Re: [PATCH 05/11] ARM: dts: Reflect change of FSL QSPI driver and remove unused properties
From: Frieder Schrempf @ 2018-06-01 9:27 UTC (permalink / raw)
To: Boris Brezillon
Cc: Mark Rutland, devicetree, yogeshnarayan.gaur, Rob Herring,
richard, Sascha Hauer, prabhakar.kushwaha, linux-kernel,
Shawn Guo, linux-spi, marek.vasut, han.xu, broonie, linux-mtd,
Pengutronix Kernel Team, miquel.raynal, fabio.estevam,
david.wolfe, computersforpeace, dwmw2, linux-arm-kernel
In-Reply-To: <20180530171057.39f1a2be@bbrezillon>
Hi Boris,
On 30.05.2018 17:10, Boris Brezillon wrote:
> On Wed, 30 May 2018 15:14:34 +0200
> Frieder Schrempf <frieder.schrempf@exceet.de> wrote:
>
>> The FSL QSPI driver was moved to the SPI framework and it now
>> acts as a SPI controller. Therefore the subnodes need to set
>> spi-[rx/tx]-bus-width = <4>, so quad mode is used just as before.
>
> We should try to keep the current behavior even when
> spi-[rx/tx]-bus-width are not defined. How about considering
> spi-[rx/tx]-bus-width as board constraints and then let the core pick
> the best mode based on these constraints plus the SPI NOR chip
> limitations.
Ok, I'll try to adjust this, so we can leave spi-[rx/tx]-bus-width
undefined and still get quad mode as default if possible.
>
>>
>> Also the properties 'bus-num', 'fsl,spi-num-chipselects' and
>> 'fsl,spi-flash-chipselects' were never read by the driver and
>> can be removed.
>>
>> The 'reg' properties are adjusted to reflect the what bus and
>> chipselect the flash is connected to, as the new driver needs
>> this information.
>>
>> The property 'fsl,qspi-has-second-chip' is not needed anymore
>> and will be removed after the old driver was disabled to avoid
>> breaking ls1021a-moxa-uc-8410a.dts.
>>
>> Signed-off-by: Frieder Schrempf <frieder.schrempf@exceet.de>
>> ---
>> arch/arm/boot/dts/imx6sx-sdb-reva.dts | 8 ++++++--
>> arch/arm/boot/dts/imx6sx-sdb.dts | 8 ++++++--
>> arch/arm/boot/dts/imx6ul-14x14-evk.dtsi | 2 ++
>> arch/arm/boot/dts/ls1021a-moxa-uc-8410a.dts | 5 ++---
>> 4 files changed, 16 insertions(+), 7 deletions(-)
>>
>> diff --git a/arch/arm/boot/dts/imx6sx-sdb-reva.dts b/arch/arm/boot/dts/imx6sx-sdb-reva.dts
>> index e3533e7..1a6f680 100644
>> --- a/arch/arm/boot/dts/imx6sx-sdb-reva.dts
>> +++ b/arch/arm/boot/dts/imx6sx-sdb-reva.dts
>> @@ -131,13 +131,17 @@
>> #size-cells = <1>;
>> compatible = "spansion,s25fl128s", "jedec,spi-nor";
>> spi-max-frequency = <66000000>;
>> + spi-rx-bus-width = <4>;
>> + spi-tx-bus-width = <4>;
>> };
>>
>> - flash1: s25fl128s@1 {
>> - reg = <1>;
>> + flash1: s25fl128s@2 {
>> + reg = <2>;
>
> Hm, you're breaking backward compat here. Can we try to re-use the
> old numbering scheme instead of patching all DTs?
Unfortunately in the current setup, the definitions for the reg property
are already broken.
For example imx6sx-sdb.dts seems to have one chip connected on bus A,
CS0 and one on bus B, CS0. It has reg set to 0 for the first and 1 for
the second chip.
While fsl-ls208xa-qds.dtsi uses the same hw setup, but has reg set to 0
and 2.
So either way we need to change the reg property at some place.
So the best approach in my opinion is to fix the definitions to use a
single scheme and while at it also remove the fsl,qspi-has-second-chip
property, that is not needed if a single consistent scheme for the reg
properties is used.
Regards,
Frieder
^ permalink raw reply
* Re: [PATCH v3 0/8] gnss: add new GNSS subsystem
From: Pavel Machek @ 2018-06-01 9:33 UTC (permalink / raw)
To: Johan Hovold
Cc: Greg Kroah-Hartman, Rob Herring, Mark Rutland, Andreas Kemnade,
Arnd Bergmann, H . Nikolaus Schaller, Marcel Holtmann,
Sebastian Reichel, Tony Lindgren, linux-kernel, devicetree
In-Reply-To: <20180601082259.17563-1-johan@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 1862 bytes --]
Hi!
> This series adds a new subsystem for GNSS receivers (e.g. GPS
> receivers).
>
> While GNSS receivers are typically accessed using a UART interface they
> often also support other I/O interfaces such as I2C, SPI and USB, while
> yet other devices use iomem or even some form of remote-processor
> messaging (rpmsg).
>
> The new GNSS subsystem abstracts the underlying interface and provides a
> new "gnss" class type, which exposes a character-device interface (e.g.
> /dev/gnss0) to user space. This allows GNSS receivers to have a
> representation in the Linux device model, something which is important
> not least for power management purposes and which also allows for easy
> detection and identification of GNSS devices.
>
> Note that the character-device interface provides raw access to whatever
> protocol the receiver is (currently) using, such as NMEA 0183, UBX or
> SiRF Binary. These protocols are expected to be continued to be handled
> by user space for the time being, even if some hybrid solutions are also
> conceivable (e.g. to have kernel drivers issue management commands).
So.. while you did good work on serial power management, this is
grossly misnamed. There's nothing GNSS specific in the code, and you
are not presenting consistent interface to the userland.
This is _not_ GNSS subsystem. This is serial power management
subsystem, or something like that. GPS/GNSS subsystem will need to be
built on top of this.
This will never handle devices like Nokia N900, where GPS is connected
over netlink.
> Another possible extension is to add generic 1PPS support.
And there's nothing GNSS specific in the patches.
Best regards,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]
^ permalink raw reply
* Re: [RESEND PATCH V2 2/2] media: ak7375: Add ak7375 lens voice coil driver
From: Sakari Ailus @ 2018-06-01 9:34 UTC (permalink / raw)
To: bingbu.cao; +Cc: linux-media, devicetree, tian.shu.qiu, rajmohan.mani, tfiga
In-Reply-To: <1527242135-22866-2-git-send-email-bingbu.cao@intel.com>
Hi Bingbu,
A few comments below.
On Fri, May 25, 2018 at 05:55:35PM +0800, bingbu.cao@intel.com wrote:
> From: Bingbu Cao <bingbu.cao@intel.com>
>
> Add a V4L2 sub-device driver for the ak7375 lens voice coil.
> This is a voice coil module using the I2C bus to control the
> focus position.
>
> Signed-off-by: Tianshu Qiu <tian.shu.qiu@intel.com>
> Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
> ---
> MAINTAINERS | 8 ++
> drivers/media/i2c/Kconfig | 10 ++
> drivers/media/i2c/Makefile | 1 +
> drivers/media/i2c/ak7375.c | 278 +++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 297 insertions(+)
> create mode 100644 drivers/media/i2c/ak7375.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ea362219c4aa..20379a7baca0 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -625,6 +625,14 @@ T: git git://linuxtv.org/anttip/media_tree.git
> S: Maintained
> F: drivers/media/usb/airspy/
>
> +AKM AK7375 LENS VOICE COIL DRIVER
> +M: Tianshu Qiu <tian.shu.qiu@intel.com>
> +L: linux-media@vger.kernel.org
> +T: git git://linuxtv.org/media_tree.git
> +S: Maintained
> +F: drivers/media/i2c/ak7375.c
> +F: Documentation/devicetree/bindings/media/i2c/akm,ak7375.txt
The name of the file also needs to match. Currently it doesn't. How about
"asahi-kasei,ak7375.txt"?
Could you also move the MAINTAINERS entry to the patch adding the DT
bindings?
> +
> ALACRITECH GIGABIT ETHERNET DRIVER
> M: Lino Sanfilippo <LinoSanfilippo@gmx.de>
> S: Maintained
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 341452fe98df..ff3cb5afb0e1 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -326,6 +326,16 @@ config VIDEO_AD5820
> This is a driver for the AD5820 camera lens voice coil.
> It is used for example in Nokia N900 (RX-51).
>
> +config VIDEO_AK7375
> + tristate "AK7375 lens voice coil support"
> + depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
> + depends on VIDEO_V4L2_SUBDEV_API
> + help
> + This is a driver for the AK7375 camera lens voice coil.
> + AK7375 is a 12 bit DAC with 120mA output current sink
> + capability. This is designed for linear control of
> + voice coil motors, controlled via I2C serial interface.
> +
> config VIDEO_DW9714
> tristate "DW9714 lens voice coil support"
> depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index d679d57cd3b3..05b97e319ea9 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -23,6 +23,7 @@ obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
> obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o
> obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o
> obj-$(CONFIG_VIDEO_AD5820) += ad5820.o
> +obj-$(CONFIG_VIDEO_AK7375) += ak7375.o
> obj-$(CONFIG_VIDEO_DW9714) += dw9714.o
> obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
> obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
> diff --git a/drivers/media/i2c/ak7375.c b/drivers/media/i2c/ak7375.c
> new file mode 100644
> index 000000000000..012e673e9ced
> --- /dev/null
> +++ b/drivers/media/i2c/ak7375.c
...
> +static const struct of_device_id ak7375_of_table[] = {
> + { .compatible = "akm,ak7375" },
"asahi-kasei,ak7375"
--
Sakari Ailus
sakari.ailus@linux.intel.com
^ permalink raw reply
* Re: [RESEND PATCH V2 2/2] media: ak7375: Add ak7375 lens voice coil driver
From: Sakari Ailus @ 2018-06-01 9:42 UTC (permalink / raw)
To: bingbu.cao; +Cc: linux-media, devicetree, tian.shu.qiu, rajmohan.mani, tfiga
In-Reply-To: <1527242135-22866-2-git-send-email-bingbu.cao@intel.com>
On Fri, May 25, 2018 at 05:55:35PM +0800, bingbu.cao@intel.com wrote:
> +static int ak7375_i2c_write(struct ak7375_device *ak7375,
> + u8 addr, u16 data, int size)
> +{
> + struct i2c_client *client = v4l2_get_subdevdata(&ak7375->sd);
> + int ret;
> + u8 buf[3];
> +
> + if (size != 1 && size != 2)
> + return -EINVAL;
> + buf[0] = addr;
> + buf[2] = data & 0xff;
> + if (size == 2)
> + buf[1] = data >> 8;
> + ret = i2c_master_send(client, (const char *)buf, size + 1);
I don't have a data datasheet for this thing, but it looks like buf[1] will
be undefined for writes the size of which is 1. And this what appears to be
written to the device as well...
> + if (ret < 0)
> + return ret;
> + if (ret != size + 1)
> + return -EIO;
> + return 0;
> +}
--
Sakari Ailus
sakari.ailus@linux.intel.com
^ permalink raw reply
* Re: [PATCH v3 0/8] gnss: add new GNSS subsystem
From: Johan Hovold @ 2018-06-01 9:49 UTC (permalink / raw)
To: Pavel Machek
Cc: Johan Hovold, Greg Kroah-Hartman, Rob Herring, Mark Rutland,
Andreas Kemnade, Arnd Bergmann, H . Nikolaus Schaller,
Marcel Holtmann, Sebastian Reichel, Tony Lindgren, linux-kernel,
devicetree
In-Reply-To: <20180601093311.GA31639@amd>
On Fri, Jun 01, 2018 at 11:33:11AM +0200, Pavel Machek wrote:
> Hi!
>
> > This series adds a new subsystem for GNSS receivers (e.g. GPS
> > receivers).
> >
> > While GNSS receivers are typically accessed using a UART interface they
> > often also support other I/O interfaces such as I2C, SPI and USB, while
> > yet other devices use iomem or even some form of remote-processor
> > messaging (rpmsg).
> >
> > The new GNSS subsystem abstracts the underlying interface and provides a
> > new "gnss" class type, which exposes a character-device interface (e.g.
> > /dev/gnss0) to user space. This allows GNSS receivers to have a
> > representation in the Linux device model, something which is important
> > not least for power management purposes and which also allows for easy
> > detection and identification of GNSS devices.
> >
> > Note that the character-device interface provides raw access to whatever
> > protocol the receiver is (currently) using, such as NMEA 0183, UBX or
> > SiRF Binary. These protocols are expected to be continued to be handled
> > by user space for the time being, even if some hybrid solutions are also
> > conceivable (e.g. to have kernel drivers issue management commands).
>
> So.. while you did good work on serial power management, this is
> grossly misnamed. There's nothing GNSS specific in the code, and you
> are not presenting consistent interface to the userland.
>
> This is _not_ GNSS subsystem. This is serial power management
> subsystem, or something like that. GPS/GNSS subsystem will need to be
> built on top of this.
I thought we had this discussion already.
This series adds an abstraction for GNSS receivers so that they can be
detected by userspace without resorting to probing hacks. That is GNSS
specific.
Furthermore, (since v2) we export a GNSS receiver type, which allows
further protocol detection hacks to be dropped, something which is also
GNSS specific.
The two drivers and library added are for GNSS devices and their
specific power management needs, while a random serial-connected device
may need to manage power differently. Also GNSS specific.
Finally, the GNSS subsystem is clearly not serial (as in UART) specific
and works just as fine with I2C, SPI, USB, iomem, rproc or whatever
interface the GNSS uses.
> This will never handle devices like Nokia N900, where GPS is connected
> over netlink.
Marcel already suggested adding a ugnss interface, which would allow
feeding GNSS data through the generic GNSS interface from user space for
use cases where it's not (yet) feasible to implement a kernel driver.
> > Another possible extension is to add generic 1PPS support.
>
> And there's nothing GNSS specific in the patches.
I disagree.
Johan
^ permalink raw reply
* Re: [RESEND PATCH V2 2/2] media: ak7375: Add ak7375 lens voice coil driver
From: Bing Bu Cao @ 2018-06-01 9:54 UTC (permalink / raw)
To: Sakari Ailus, bingbu.cao
Cc: linux-media, devicetree, tian.shu.qiu, rajmohan.mani, tfiga
In-Reply-To: <20180601093416.i5mnos5titb5ggiz@paasikivi.fi.intel.com>
On 2018年06月01日 17:34, Sakari Ailus wrote:
> Hi Bingbu,
>
> A few comments below.
>
> On Fri, May 25, 2018 at 05:55:35PM +0800, bingbu.cao@intel.com wrote:
>> From: Bingbu Cao <bingbu.cao@intel.com>
>>
>> Add a V4L2 sub-device driver for the ak7375 lens voice coil.
>> This is a voice coil module using the I2C bus to control the
>> focus position.
>>
>> Signed-off-by: Tianshu Qiu <tian.shu.qiu@intel.com>
>> Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
>> ---
>> MAINTAINERS | 8 ++
>> drivers/media/i2c/Kconfig | 10 ++
>> drivers/media/i2c/Makefile | 1 +
>> drivers/media/i2c/ak7375.c | 278 +++++++++++++++++++++++++++++++++++++++++++++
>> 4 files changed, 297 insertions(+)
>> create mode 100644 drivers/media/i2c/ak7375.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index ea362219c4aa..20379a7baca0 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -625,6 +625,14 @@ T: git git://linuxtv.org/anttip/media_tree.git
>> S: Maintained
>> F: drivers/media/usb/airspy/
>>
>> +AKM AK7375 LENS VOICE COIL DRIVER
>> +M: Tianshu Qiu <tian.shu.qiu@intel.com>
>> +L: linux-media@vger.kernel.org
>> +T: git git://linuxtv.org/media_tree.git
>> +S: Maintained
>> +F: drivers/media/i2c/ak7375.c
>> +F: Documentation/devicetree/bindings/media/i2c/akm,ak7375.txt
> The name of the file also needs to match. Currently it doesn't. How about
> "asahi-kasei,ak7375.txt"?
Ack.
Does it make sense if just change the compatible string to
"asahi-kasei,ak7375" and keep the file name unchanged?
I have no clear idea about the DT binding rules.
>
> Could you also move the MAINTAINERS entry to the patch adding the DT
> bindings?
Ack.
>
>> +
>> ALACRITECH GIGABIT ETHERNET DRIVER
>> M: Lino Sanfilippo <LinoSanfilippo@gmx.de>
>> S: Maintained
>> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
>> index 341452fe98df..ff3cb5afb0e1 100644
>> --- a/drivers/media/i2c/Kconfig
>> +++ b/drivers/media/i2c/Kconfig
>> @@ -326,6 +326,16 @@ config VIDEO_AD5820
>> This is a driver for the AD5820 camera lens voice coil.
>> It is used for example in Nokia N900 (RX-51).
>>
>> +config VIDEO_AK7375
>> + tristate "AK7375 lens voice coil support"
>> + depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
>> + depends on VIDEO_V4L2_SUBDEV_API
>> + help
>> + This is a driver for the AK7375 camera lens voice coil.
>> + AK7375 is a 12 bit DAC with 120mA output current sink
>> + capability. This is designed for linear control of
>> + voice coil motors, controlled via I2C serial interface.
>> +
>> config VIDEO_DW9714
>> tristate "DW9714 lens voice coil support"
>> depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
>> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
>> index d679d57cd3b3..05b97e319ea9 100644
>> --- a/drivers/media/i2c/Makefile
>> +++ b/drivers/media/i2c/Makefile
>> @@ -23,6 +23,7 @@ obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
>> obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o
>> obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o
>> obj-$(CONFIG_VIDEO_AD5820) += ad5820.o
>> +obj-$(CONFIG_VIDEO_AK7375) += ak7375.o
>> obj-$(CONFIG_VIDEO_DW9714) += dw9714.o
>> obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
>> obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
>> diff --git a/drivers/media/i2c/ak7375.c b/drivers/media/i2c/ak7375.c
>> new file mode 100644
>> index 000000000000..012e673e9ced
>> --- /dev/null
>> +++ b/drivers/media/i2c/ak7375.c
> ...
>
>> +static const struct of_device_id ak7375_of_table[] = {
>> + { .compatible = "akm,ak7375" },
> "asahi-kasei,ak7375"
>
^ permalink raw reply
* Re: [RESEND PATCH V2 2/2] media: ak7375: Add ak7375 lens voice coil driver
From: Sakari Ailus @ 2018-06-01 9:56 UTC (permalink / raw)
To: Bing Bu Cao
Cc: bingbu.cao, linux-media, devicetree, tian.shu.qiu, rajmohan.mani,
tfiga
In-Reply-To: <a0f63669-db91-7174-1f88-761bff6af0d3@linux.intel.com>
On Fri, Jun 01, 2018 at 05:54:30PM +0800, Bing Bu Cao wrote:
> > > +AKM AK7375 LENS VOICE COIL DRIVER
> > > +M: Tianshu Qiu <tian.shu.qiu@intel.com>
> > > +L: linux-media@vger.kernel.org
> > > +T: git git://linuxtv.org/media_tree.git
> > > +S: Maintained
> > > +F: drivers/media/i2c/ak7375.c
> > > +F: Documentation/devicetree/bindings/media/i2c/akm,ak7375.txt
> > The name of the file also needs to match. Currently it doesn't. How about
> > "asahi-kasei,ak7375.txt"?
> Ack.
> Does it make sense if just change the compatible string to
> "asahi-kasei,ak7375" and keep the file name unchanged?
Most binding files in the directory seem to use the chip name rather than
the full compatible string (including the vendor). I guess both are fine.
--
Sakari Ailus
sakari.ailus@linux.intel.com
^ permalink raw reply
* Re: [PATCH v3] PCI: mediatek: Add system pm support for MT2712
From: Andy Shevchenko @ 2018-06-01 10:17 UTC (permalink / raw)
To: honghui.zhang, Arnd Bergmann
Cc: Lorenzo Pieralisi, Marc Zyngier, Bjorn Helgaas, Matthias Brugger,
linux-arm Mailing List, moderated list:ARM/Mediatek SoC support,
linux-pci, Linux Kernel Mailing List, devicetree, yingjoe.chen,
Eddie Huang, ryder.lee, hongkun.cao, youlin.pei, yong.wu, yt.shen,
sean.wang, xinping.qian
In-Reply-To: <1527822270-30280-1-git-send-email-honghui.zhang@mediatek.com>
On Fri, Jun 1, 2018 at 6:04 AM, <honghui.zhang@mediatek.com> wrote:
> From: Honghui Zhang <honghui.zhang@mediatek.com>
> +#ifdef CONFIG_PM_SLEEP
> +static int mtk_pcie_suspend_noirq(struct device *dev)
__maybe_unused
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH v3 0/8] gnss: add new GNSS subsystem
From: Pavel Machek @ 2018-06-01 10:26 UTC (permalink / raw)
To: Johan Hovold
Cc: Greg Kroah-Hartman, Rob Herring, Mark Rutland, Andreas Kemnade,
Arnd Bergmann, H . Nikolaus Schaller, Marcel Holtmann,
Sebastian Reichel, Tony Lindgren, linux-kernel, devicetree
In-Reply-To: <20180601094959.GA13775@localhost>
[-- Attachment #1: Type: text/plain, Size: 4663 bytes --]
On Fri 2018-06-01 11:49:59, Johan Hovold wrote:
> On Fri, Jun 01, 2018 at 11:33:11AM +0200, Pavel Machek wrote:
> > Hi!
> >
> > > This series adds a new subsystem for GNSS receivers (e.g. GPS
> > > receivers).
> > >
> > > While GNSS receivers are typically accessed using a UART interface they
> > > often also support other I/O interfaces such as I2C, SPI and USB, while
> > > yet other devices use iomem or even some form of remote-processor
> > > messaging (rpmsg).
> > >
> > > The new GNSS subsystem abstracts the underlying interface and provides a
> > > new "gnss" class type, which exposes a character-device interface (e.g.
> > > /dev/gnss0) to user space. This allows GNSS receivers to have a
> > > representation in the Linux device model, something which is important
> > > not least for power management purposes and which also allows for easy
> > > detection and identification of GNSS devices.
> > >
> > > Note that the character-device interface provides raw access to whatever
> > > protocol the receiver is (currently) using, such as NMEA 0183, UBX or
> > > SiRF Binary. These protocols are expected to be continued to be handled
> > > by user space for the time being, even if some hybrid solutions are also
> > > conceivable (e.g. to have kernel drivers issue management commands).
> >
> > So.. while you did good work on serial power management, this is
> > grossly misnamed. There's nothing GNSS specific in the code, and you
> > are not presenting consistent interface to the userland.
> >
> > This is _not_ GNSS subsystem. This is serial power management
> > subsystem, or something like that. GPS/GNSS subsystem will need to be
> > built on top of this.
>
> I thought we had this discussion already.
I don't remember useful discussion.
> This series adds an abstraction for GNSS receivers so that they can be
> detected by userspace without resorting to probing hacks. That is GNSS
> specific.
>
> Furthermore, (since v2) we export a GNSS receiver type, which allows
> further protocol detection hacks to be dropped, something which is also
> GNSS specific.
So you have serial line + pm + protocol type. Nothing GNSS specific
really, it would be useful to (for example) say this is modem with AT
commands. Or this is QMI modem.
> The two drivers and library added are for GNSS devices and their
> specific power management needs, while a random serial-connected device
> may need to manage power differently. Also GNSS specific.
Or maybe it will need to manager power in the same way. How would the
AT modem be different?
> Finally, the GNSS subsystem is clearly not serial (as in UART) specific
> and works just as fine with I2C, SPI, USB, iomem, rproc or whatever
> interface the GNSS uses.
Ok, true. It is "we pass tty-like data across". Again, it would be
useful for AT commands, too, and yes, they go over serials and usbs
and more, too.
> > This will never handle devices like Nokia N900, where GPS is connected
> > over netlink.
>
> Marcel already suggested adding a ugnss interface, which would allow
> feeding GNSS data through the generic GNSS interface from user space for
> use cases where it's not (yet) feasible to implement a kernel
> driver.
Yes, and ugnss would be at wrong layer for N900. First, lets take a
look at N900 gps driver:
https://github.com/postmarketOS/gps-nokia-n900
Got it? I'll eventually like to see it in the kernel, but your "GNSS"
subsystem is unsuitable for it, as it does not talk well-known
protocol.
I'd like to see "datapipe" devices (what you currently call GNSS) and
"gps" devices, which would have common interface to the userland.
N900 would not have any datapipe devices, but would have /dev/gps0
exposing common GPS interface.
Droid4 would have /dev/datapipe0 (usb, AT protocol), /dev/datapipe1
(usb, QMI protocol), /dev/datapipe2 (gsmtty over serial, custom AT
protocol), /dev/datapipe3 (gsmtty over serial, NMEA wrapped in AT
protocol) (and more, it is complex hardware). And if we really wanted,
we'd have /dev/gps0, too, exposing common GPS interface.
Your devices would have /dev/datapipe0 with sirf or ublox or nmea
protocol. In we really wanted, we'd have /dev/gps0, too, again,
exposing common GPS interface.
I believe we really want to use your code for AT commands, too. And we
really should keep GNSS/GPS names for future layer that actually
provides single interface for userland.
Best regards,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]
^ permalink raw reply
* Re: [PATCH v3 2/8] dt-bindings: media: Document data-enable-active property
From: Sakari Ailus @ 2018-06-01 10:29 UTC (permalink / raw)
To: Jacopo Mondi
Cc: devicetree, robh+dt, linux-renesas-soc, horms, hans.verkuil,
laurent.pinchart, niklas.soderlund, geert, mchehab,
linux-arm-kernel, linux-media
In-Reply-To: <1527606359-19261-3-git-send-email-jacopo+renesas@jmondi.org>
Hi Jacopo,
Thanks for the patch.
On Tue, May 29, 2018 at 05:05:53PM +0200, Jacopo Mondi wrote:
> Add 'data-enable-active' property to endpoint node properties list.
>
> The property allows to specify the polarity of the data-enable signal, which
> when in active state determinates when data lanes have to sampled for valid
> pixel data.
Lanes or lines? I understand this is forthe parallel interface.
>
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> ---
> v3:
> - new patch
> ---
> Documentation/devicetree/bindings/media/video-interfaces.txt | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt b/Documentation/devicetree/bindings/media/video-interfaces.txt
> index 258b8df..9839d26 100644
> --- a/Documentation/devicetree/bindings/media/video-interfaces.txt
> +++ b/Documentation/devicetree/bindings/media/video-interfaces.txt
> @@ -109,6 +109,8 @@ Optional endpoint properties
> Note, that if HSYNC and VSYNC polarities are not specified, embedded
> synchronization may be required, where supported.
> - data-active: similar to HSYNC and VSYNC, specifies data line polarity.
> +- data-enable-active: similar to HSYNC and VSYNC, specifies the data enable
> + signal polarity.
> - field-even-active: field signal level during the even field data transmission.
> - pclk-sample: sample data on rising (1) or falling (0) edge of the pixel clock
> signal.
--
Sakari Ailus
sakari.ailus@linux.intel.com
^ permalink raw reply
* Re: [PATCH v2 21/40] iommu/arm-smmu-v3: Add support for Substream IDs
From: Jean-Philippe Brucker @ 2018-06-01 10:46 UTC (permalink / raw)
To: Bharat Kumar Gogada,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
linux-pci-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-acpi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org,
kvm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-mm-Bw31MaZKKs3YtjvyW6yDsg@public.gmane.org
Cc: xuzaibo-hv44wF8Li93QT0dZR+AlfA@public.gmane.org, Will Deacon,
okaya-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org,
ashok.raj-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org,
rfranz-YGCgFSpz5w/QT0dZR+AlfA@public.gmane.org,
Ravikiran Gummaluri,
ilias.apalodimas-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org,
dwmw2-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org,
christian.koenig-5C7GfCeVMHo@public.gmane.org
In-Reply-To: <BLUPR0201MB1505AA55707BE2E13392FFAFA5630-hRBPhS1iNj/g9tdZWAsUFxrHTHEw16jenBOFsp37pqbUKgpGm//BTAC/G2K4zDHf@public.gmane.org>
On 31/05/18 12:01, Bharat Kumar Gogada wrote:
>> static void arm_smmu_sync_cd(void *cookie, int ssid, bool leaf) {
>> + struct arm_smmu_cmdq_ent cmd = {
>> + .opcode = CMDQ_OP_CFGI_CD_ALL,
>
> Hi Jean, here CMDQ_OP_CFGI_CD opcode 0x5.
Woops, nice catch!
I pushed fixes for all comments so far to branch sva/current
Thanks,
Jean
^ permalink raw reply
* Re: [PATCH v3] PCI: mediatek: Add system pm support for MT2712
From: Honghui Zhang @ 2018-06-01 10:49 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Arnd Bergmann, Lorenzo Pieralisi, Marc Zyngier, Bjorn Helgaas,
Matthias Brugger, linux-arm Mailing List,
moderated list:ARM/Mediatek SoC support, linux-pci,
Linux Kernel Mailing List, devicetree, yingjoe.chen, Eddie Huang,
ryder.lee, hongkun.cao, youlin.pei, yong.wu, yt.shen, sean.wang,
xinping.qian
In-Reply-To: <CAHp75VeBTmC=3nJ-t+OO9bp0mzJuTYsGBwi8fokT16bu2nE9RA@mail.gmail.com>
On Fri, 2018-06-01 at 13:17 +0300, Andy Shevchenko wrote:
> On Fri, Jun 1, 2018 at 6:04 AM, <honghui.zhang@mediatek.com> wrote:
> > From: Honghui Zhang <honghui.zhang@mediatek.com>
>
> > +#ifdef CONFIG_PM_SLEEP
> > +static int mtk_pcie_suspend_noirq(struct device *dev)
>
> __maybe_unused
>
Hi, Andy, thanks for your review.
Bjorn had point this out that at:
https://www.spinics.net/lists/arm-kernel/msg656774.html
So __maybe_unused is not really needed.
thanks
^ permalink raw reply
* Re: [PATCH v4 2/6] mfd: bd71837: Devicetree bindings for ROHM BD71837 PMIC
From: Matti Vaittinen @ 2018-06-01 10:51 UTC (permalink / raw)
To: Stephen Boyd
Cc: Matti Vaittinen, Rob Herring, Matti Vaittinen, Michael Turquette,
Mark Rutland, Lee Jones, Liam Girdwood, Mark Brown, linux-clk,
devicetree, linux-kernel@vger.kernel.org, mikko.mutanen,
heikki.haikola
In-Reply-To: <152777867392.144038.18188452389972834689@swboyd.mtv.corp.google.com>
On Thu, May 31, 2018 at 07:57:53AM -0700, Stephen Boyd wrote:
> Quoting Rob Herring (2018-05-31 07:07:24)
> > On Thu, May 31, 2018 at 5:23 AM, Matti Vaittinen
> > <mazziesaccount@gmail.com> wrote:
> > > On Thu, May 31, 2018 at 10:17:17AM +0300, Matti Vaittinen wrote:
> > >> Hello Rob,
> > >>
> > >> Thanks for the review!
> > >>
> > >> On Wed, May 30, 2018 at 10:01:29PM -0500, Rob Herring wrote:
> > >> > On Wed, May 30, 2018 at 11:42:03AM +0300, Matti Vaittinen wrote:
> > >> > > Document devicetree bindings for ROHM BD71837 PMIC MFD.
> > >> > > + - interrupts : The interrupt line the device is connected to.
> > >> > > + - interrupt-controller : Marks the device node as an interrupt controller.
> > >> >
> > >> > What sub blocks have interrupts?
> > >>
> > >> The PMIC can generate interrupts from events which cause it to reset.
> > >> Eg, irq from watchdog line change, power button pushes, reset request
> > >> via register interface etc. I don't know any generic handling for these
> > >> interrupts. In "normal" use-case this PMIC is powering the processor
> > >> where driver is running and I do not see reasonable handling because
> > >> power-reset is going to follow the irq.
> > >>
> > >
> > > Oh, but when reading this I understand that the interrupt-controller
> > > property should at least be optional.
> >
> > I don't think it should. The h/w either has an interrupt controller or
> > it doesn't. My concern is you added it but nothing uses it which tells
> > me your binding is incomplete. I'd rather see complete bindings even
> > if you don't have drivers. For example, as-is, there's not really any
> > need for the clocks child node. You can just make the parent a clock
> > provider. But we need a complete picture of the h/w to make that
> > determination.
> >
>
> I don't see a reason to have the clk subnode either.
After some pondering - do you mean I could:
1. remove clk binfing document and clk node.
2. add clock-output-names etc to pmic node (and describe them in pmic
node binding document)
3. use parent DT node in clk driver and do something like:
if (parent->of_node)
ret = of_clk_add_hw_provider(parent->of_node, of_clk_hw_simple_get,
hw);
4. remove the clkdev
I will cook new set of patches with all suggested changes but it may be I don't
get it ready for posting untill next week.
Br,
Matti Vaittinen
^ permalink raw reply
* Re: [PATCH v3] PCI: mediatek: Add system pm support for MT2712
From: Andy Shevchenko @ 2018-06-01 10:52 UTC (permalink / raw)
To: Honghui Zhang
Cc: Arnd Bergmann, Lorenzo Pieralisi, Marc Zyngier, Bjorn Helgaas,
Matthias Brugger, linux-arm Mailing List,
moderated list:ARM/Mediatek SoC support, linux-pci,
Linux Kernel Mailing List, devicetree, yingjoe.chen, Eddie Huang,
ryder.lee, hongkun.cao, youlin.pei, yong.wu, yt.shen, sean.wang,
xinping.qian
In-Reply-To: <1527850145.28160.6.camel@mhfsdcap03>
On Fri, Jun 1, 2018 at 1:49 PM, Honghui Zhang
<honghui.zhang@mediatek.com> wrote:
> On Fri, 2018-06-01 at 13:17 +0300, Andy Shevchenko wrote:
>> On Fri, Jun 1, 2018 at 6:04 AM, <honghui.zhang@mediatek.com> wrote:
>> > From: Honghui Zhang <honghui.zhang@mediatek.com>
>>
>> > +#ifdef CONFIG_PM_SLEEP
>> > +static int mtk_pcie_suspend_noirq(struct device *dev)
>>
>> __maybe_unused
>>
>
> Hi, Andy, thanks for your review.
> Bjorn had point this out that at:
> https://www.spinics.net/lists/arm-kernel/msg656774.html
Nice, one more maintainer with strong opinion here.
Arnd, that's what I mentioned as a split in opinions earlier.
Any new developer or even existing contributor would be now really
confusing since maintainers asked for two different approaches on the
same matter.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Applied "ASoC: simple-card: set cpu dai clk in hw_params" to the asoc tree
From: Mark Brown @ 2018-06-01 11:05 UTC (permalink / raw)
To: Daniel Mack
Cc: devicetree, alsa-devel, kuninori.morimoto.gx, lgirdwood, broonie,
robert.jarzmik
In-Reply-To: <20180530194556.407-1-daniel@zonque.org>
The patch
ASoC: simple-card: set cpu dai clk in hw_params
has been applied to the asoc tree at
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
>From e9be4ffd4f40fcb18209dc5120233f2d11a24b6a Mon Sep 17 00:00:00 2001
From: Daniel Mack <daniel@zonque.org>
Date: Wed, 30 May 2018 21:45:56 +0200
Subject: [PATCH] ASoC: simple-card: set cpu dai clk in hw_params
The simple-card driver currently accepts a clock node in the cpu dai
sub-node and only uses it as an alternative to the
'system-clock-frequency' property to get the current frequency.
This patch adds another use of the passed clock node. If mclk-fs is
specified, the clocks in cpu and codec dai sub-nodes will be set to
the calculated rate (stream rate * mclk_fs) in hw_params.
This allows platforms to pass tuneable clocks as phandle that will
automatically be set to the right rates.
Signed-off-by: Daniel Mack <daniel@zonque.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
.../devicetree/bindings/sound/simple-card.txt | 5 +++++
sound/soc/generic/simple-card.c | 21 +++++++++++++++++++
2 files changed, 26 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/simple-card.txt b/Documentation/devicetree/bindings/sound/simple-card.txt
index 17c13e74667d..a4c72d09cd45 100644
--- a/Documentation/devicetree/bindings/sound/simple-card.txt
+++ b/Documentation/devicetree/bindings/sound/simple-card.txt
@@ -86,6 +86,11 @@ Optional CPU/CODEC subnodes properties:
in dai startup() and disabled with
clk_disable_unprepare() in dai
shutdown().
+ If a clock is specified and a
+ multiplication factor is given with
+ mclk-fs, the clock will be set to the
+ calculated mclk frequency when the
+ stream starts.
- system-clock-direction-out : specifies clock direction as 'out' on
initialization. It is useful for some aCPUs with
fixed clocks.
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 6959a74a6f49..4a516c428b3d 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -135,6 +135,18 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
asoc_simple_card_clk_disable(&dai_props->codec_dai);
}
+static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai,
+ unsigned long rate)
+{
+ if (!simple_dai->clk)
+ return 0;
+
+ if (clk_get_rate(simple_dai->clk) == rate)
+ return 0;
+
+ return clk_set_rate(simple_dai->clk, rate);
+}
+
static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -154,6 +166,15 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
if (mclk_fs) {
mclk = params_rate(params) * mclk_fs;
+
+ ret = asoc_simple_set_clk_rate(&dai_props->codec_dai, mclk);
+ if (ret < 0)
+ return ret;
+
+ ret = asoc_simple_set_clk_rate(&dai_props->cpu_dai, mclk);
+ if (ret < 0)
+ return ret;
+
ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
SND_SOC_CLOCK_IN);
if (ret && ret != -ENOTSUPP)
--
2.17.0
^ permalink raw reply related
* Re: [PATCH v3 0/8] gnss: add new GNSS subsystem
From: Johan Hovold @ 2018-06-01 12:22 UTC (permalink / raw)
To: Pavel Machek
Cc: Johan Hovold, Greg Kroah-Hartman, Rob Herring, Mark Rutland,
Andreas Kemnade, Arnd Bergmann, H . Nikolaus Schaller,
Marcel Holtmann, Sebastian Reichel, Tony Lindgren, linux-kernel,
devicetree
In-Reply-To: <20180601102612.GC31639@amd>
On Fri, Jun 01, 2018 at 12:26:12PM +0200, Pavel Machek wrote:
> On Fri 2018-06-01 11:49:59, Johan Hovold wrote:
> > On Fri, Jun 01, 2018 at 11:33:11AM +0200, Pavel Machek wrote:
> > This series adds an abstraction for GNSS receivers so that they can be
> > detected by userspace without resorting to probing hacks. That is GNSS
> > specific.
> >
> > Furthermore, (since v2) we export a GNSS receiver type, which allows
> > further protocol detection hacks to be dropped, something which is also
> > GNSS specific.
>
> So you have serial line + pm + protocol type. Nothing GNSS specific
> really, it would be useful to (for example) say this is modem with AT
> commands. Or this is QMI modem.
It's a matter of finding the right abstraction level. A user space
location service will now have easy access to the class of devices it
cares about, without having to go through a list of other random devices
which happen to use a similar underlying interface (e.g. a modem or
whatever).
> > The two drivers and library added are for GNSS devices and their
> > specific power management needs, while a random serial-connected device
> > may need to manage power differently. Also GNSS specific.
>
> Or maybe it will need to manager power in the same way. How would the
> AT modem be different?
Point is, you can't write a subsystem for everything. If it later turns
out some part of the code can be shared internally, fine.
> > Finally, the GNSS subsystem is clearly not serial (as in UART) specific
> > and works just as fine with I2C, SPI, USB, iomem, rproc or whatever
> > interface the GNSS uses.
>
> Ok, true. It is "we pass tty-like data across". Again, it would be
> useful for AT commands, too, and yes, they go over serials and usbs
> and more, too.
Modems and GNSS devices would have different characteristics for buffers
and throughput for starters.
The GNSS interface uses a synchronous writes for commands to the
receiver, something which may not be appropriate for an outgoing data
channel, for example.
As mentioned in the cover letter, we may eventually want to add support
for some kinds of configuration from within the kernel (e.g. protocol
and line speed changes).
> > > This will never handle devices like Nokia N900, where GPS is connected
> > > over netlink.
> >
> > Marcel already suggested adding a ugnss interface, which would allow
> > feeding GNSS data through the generic GNSS interface from user space for
> > use cases where it's not (yet) feasible to implement a kernel
> > driver.
>
> Yes, and ugnss would be at wrong layer for N900. First, lets take a
> look at N900 gps driver:
> https://github.com/postmarketOS/gps-nokia-n900
>
> Got it? I'll eventually like to see it in the kernel, but your "GNSS"
> subsystem is unsuitable for it, as it does not talk well-known
> protocol.
Seriously? If you can't be arsed to summarise your use case, why would I
bother reading your random user space code?
> I'd like to see "datapipe" devices (what you currently call GNSS) and
> "gps" devices, which would have common interface to the userland.
You keep talking about this "gps" interface, which based on your
earlier comments I assume you mean a NMEA 0183 interface? Again,
converting a vendor-specific binary protocol may not be feasible. Please
try to be more be specific.
> N900 would not have any datapipe devices, but would have /dev/gps0
> exposing common GPS interface.
>
> Droid4 would have /dev/datapipe0 (usb, AT protocol), /dev/datapipe1
> (usb, QMI protocol), /dev/datapipe2 (gsmtty over serial, custom AT
> protocol), /dev/datapipe3 (gsmtty over serial, NMEA wrapped in AT
> protocol) (and more, it is complex hardware). And if we really wanted,
> we'd have /dev/gps0, too, exposing common GPS interface.
>
> Your devices would have /dev/datapipe0 with sirf or ublox or nmea
> protocol. In we really wanted, we'd have /dev/gps0, too, again,
> exposing common GPS interface.
This does not seem like the right abstraction level and doesn't appear
to provide much more than what we currently have with ttys.
> I believe we really want to use your code for AT commands, too. And we
> really should keep GNSS/GPS names for future layer that actually
> provides single interface for userland.
Specifics, please. What would such an interface look like? I'm pretty
sure, we do not want to move every GNSS middleware protocol handler into
the kernel.
Johan
^ permalink raw reply
* [PATCH v8 0/4] mtd: Add a SPI NAND driver
From: Boris Brezillon @ 2018-06-01 13:13 UTC (permalink / raw)
To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
Richard Weinberger, linux-mtd, Miquel Raynal, Mark Brown,
linux-spi, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
Kumar Gala, devicetree
Hello,
Not much has changed in this v8, I mainly fixed bugs/issues reported
by Miquel and Frider.
This is hopefully the last version, and if everything goes well I
should merge this stuff just after 4.18-rc1 is out.
Mark, Rob, just one thing I'm not sure about: in patch 2 I use
spi-{tx,rx}-bus-width to encode board limitations on the SPI bus, but
I'm not sure these properties were created to express that. Should I
create a new prop (spi-max-bus-width?) or is it fine to reuse those
spi-{tx,rx}-bus-width props?
Comments/reviews are welcome.
Thanks,
Boris
v8 changes:
- dropped patch 1 which has been applied
- fix various bugs in the core (see changelog in patch 1)
- add a commit message to patch 4
v7 changes:
- Use the spi-mem interface
- Add support for on-die ECC
- Add support for Winbond W25M02GV chip
v6 changes:
- includes generic NAND framework patches in series
- rebase on nand/next (commit 6076fd1e9d879521f7082a5e22185b71e480b777)
- remove on-die ECC support
- remove devm_free() since everything allocated by devm_kmalloc() will be
automatically freed when device is released
- add comment header for structs in spinand.h
- remove spinand_register()/unregister(), call spinand_detect() in
spinand_init() and only expose spinand_init()/cleanup()
- add nand_release_bbt() in bbt.c and use it in nand_cleanup() and
spinand_cleanup()
- use BIT(n) instead (1 << n) in macro of spinand.h
- rename spinand_alloc() to devm_spinand_alloc()
- name lables in better way
- fix some typos
- add empty lines between code blocks
v5 changes:
- rebase patch on nand/next with Boris's generic NAND framework patches[3]
- replace pr_xxx() with dev_xxx()
- replace kzalloc()i/kfree() with devm_kzalloc()/devm_kfree()
- rename spinand_op_init() to spinand_init_op() for consistency
- remove command opcode in function comments
- use BIT(n) instead (1 << n) in macro
- remove manufactures.c and put spinand_manufacturers table in core.c
- change spinand_write_reg() u8 *buf argument to u8 value,
since the length is always 1
- remove spinand_manufacture->detect() check, since it is always != NULL
- alloc spinand_ecc_engine struct in vendor.c when using on-die ECC
(for hardware ECC, it should be in controllers/*.c)
- add comment header for struct spinand_op
- fix timeout bug in spinand_wait(), thanks for Arnaud's debug
- make spinand_manufacturers const
- add ecc_engine_ops pointer in struct micron_spinand_info
- make controller->cap assignment right with SPI_TX/RX_QUAD/DUAL flag
v4 changes:
- initialize struct mtd_oob_ops to 0 in bbt.c
- rename new added helper in nand.h to nand_check_xxxx()
- add struct mtd_oob_ops consistency check in nand_check_oob_ops()
- add dataleft in struct nand_page_iter instead of offs
- remove spinand_manufacturers->ops->detect() check since it is mandatory
- remove spinand_set_manufacturer_ops() and do the job in
spinand_manufacturer_detect()
- move .priv out of struct spinand_controller
- add spinand_alloc/free/register/unregister() and make
spinand_detect/init() static
- make BBT be configured by device tree
- chip->id.data stores raw ID directly
- refine device info print message after detect success
- add struct mtd_layout_ops pointer in struct micron_spinand_info
- remove micron_spinand_init() and do its job in micron_spinand_detect()
- fix BBT block cannot be erased bug
v3 changes:
- rebase patch on 4.11-rc1[2]
- change read ID method. read 4 bytes ID out then let ->detect() of each
manufacutre driver to decode ID and detect the device.
- make SPI NAND id table private to each manufacutre driver
- fix coding style to make checkpatch.pl happy
- update the MAINTAINERS file for spi nand code
- add nand_size() helper in nand.h
- use nand_for_each_page() helper in spinand_do_read/write_ops()
- create helper to check boundaries in generic NAND code and use it
in SPI NAND core
- rename spinand_base.c to core.c
- manufactures' drivers expose spinand_manufacturer struct instead of
spinand_manufacturer_ops struct to keep Manufacture ID macro in
manufactures' drivers and rename spinand_ids.c to manufacture.c
- rename spinand_micron.c to micron.c
- rename chips/ directory to controllers/
- rename generic_spi.c to generic-spi.c
- replace ->build_column_addr() and ->get_dummy() hooks with ->prepare_op() in
spinand_manufacturer_ops struct
- rename __spinand_erase() to spinand_erase()
- rename spinand_erase() to spinand_erase_skip_bbt()
- rename spinand_scan_ident() to spinand_detect()
- rename spinand_scan_tail() to spinand_init()
- move non detect related code from spinand_detect() to spinand_init()
- remove spinand_fill_nandd, assign nand->ops in spinand_detect()
- merge v2 patch 3(bad block support) and patch 4(BBT support)
- drop getchip parameter, remove spinand_get/remove_device(), take the lock
by caller directly
- fix function comment headers
- use nand_bbt_is_initialized() helper
- replace spinand_ecc_engine and spinand_controller object in spinand_device
struct with pointer
- replace struct spinand_manufacturer_ops pointer in spinand_device struct
with spinand_manufacturer struct
v2 changes:
- replace "spi_nand" with "spinand".
- rename spi nand related structs for better understanding.
- introduce spi nand controller, manufacturer and ecc_engine struct.
- add spi nand manufacturer initialization function refer to Boris's
manuf-init branch.
- remove NAND_SKIP_BBTSCAN from series. Add it later when enabling HW ECC.
- reorganize series according to Boris's suggestion.
*** BLURB HERE ***
Boris Brezillon (1):
dt-bindings: Add bindings for SPI NAND devices
Frieder Schrempf (1):
mtd: spinand: Add initial support for Winbond W25M02GV
Peter Pan (2):
mtd: nand: Add core infrastructure to support SPI NANDs
mtd: spinand: Add initial support for Micron MT29F2G01ABAGD
Documentation/devicetree/bindings/mtd/spi-nand.txt | 27 +
drivers/mtd/nand/Kconfig | 1 +
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/spi/Kconfig | 7 +
drivers/mtd/nand/spi/Makefile | 2 +
drivers/mtd/nand/spi/core.c | 1129 ++++++++++++++++++++
drivers/mtd/nand/spi/micron.c | 133 +++
drivers/mtd/nand/spi/winbond.c | 141 +++
include/linux/mtd/spinand.h | 419 ++++++++
include/linux/spi/spi-mem.h | 4 +-
10 files changed, 1863 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/mtd/spi-nand.txt
create mode 100644 drivers/mtd/nand/spi/Kconfig
create mode 100644 drivers/mtd/nand/spi/Makefile
create mode 100644 drivers/mtd/nand/spi/core.c
create mode 100644 drivers/mtd/nand/spi/micron.c
create mode 100644 drivers/mtd/nand/spi/winbond.c
create mode 100644 include/linux/mtd/spinand.h
--
2.14.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply
* [PATCH v8 1/4] mtd: nand: Add core infrastructure to support SPI NANDs
From: Boris Brezillon @ 2018-06-01 13:13 UTC (permalink / raw)
To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
Richard Weinberger, linux-mtd, Miquel Raynal, Mark Brown,
linux-spi, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
Kumar Gala, devicetree
Cc: Peter Pan
In-Reply-To: <20180601131400.17634-1-boris.brezillon@bootlin.com>
From: Peter Pan <peterpandong@micron.com>
Add a SPI NAND framework based on the generic NAND framework and the
spi-mem infrastructure.
In its current state, this framework supports the following features:
- single/dual/quad IO modes
- on-die ECC
Signed-off-by: Peter Pan <peterpandong@micron.com>
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
---
Changes in v8:
- Use spinand_upd_cfg() instead of spinand_get_cfg()+spinand_set_cfg()
in spinand_init_quad_enable() and spinand_ecc_enable()
- Make sure spinand_init_quad_enable() worked
- Do not check return code of spinand_read_page() in spinand_isbad()
- Fix the spinand_init() error path
---
drivers/mtd/nand/Kconfig | 1 +
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/spi/Kconfig | 7 +
drivers/mtd/nand/spi/Makefile | 2 +
drivers/mtd/nand/spi/core.c | 1111 +++++++++++++++++++++++++++++++++++++++++
include/linux/mtd/spinand.h | 415 +++++++++++++++
include/linux/spi/spi-mem.h | 4 +-
7 files changed, 1540 insertions(+), 1 deletion(-)
create mode 100644 drivers/mtd/nand/spi/Kconfig
create mode 100644 drivers/mtd/nand/spi/Makefile
create mode 100644 drivers/mtd/nand/spi/core.c
create mode 100644 include/linux/mtd/spinand.h
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 88c7d3b4ff8b..9033215e62ea 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -4,3 +4,4 @@ config MTD_NAND_CORE
source "drivers/mtd/nand/onenand/Kconfig"
source "drivers/mtd/nand/raw/Kconfig"
+source "drivers/mtd/nand/spi/Kconfig"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 3f0cb87f1a57..7ecd80c0a66e 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
obj-y += onenand/
obj-y += raw/
+obj-y += spi/
diff --git a/drivers/mtd/nand/spi/Kconfig b/drivers/mtd/nand/spi/Kconfig
new file mode 100644
index 000000000000..7c37d2929b68
--- /dev/null
+++ b/drivers/mtd/nand/spi/Kconfig
@@ -0,0 +1,7 @@
+menuconfig MTD_SPI_NAND
+ tristate "SPI NAND device Support"
+ select MTD_NAND_CORE
+ depends on SPI_MASTER
+ select SPI_MEM
+ help
+ This is the framework for the SPI NAND device drivers.
diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile
new file mode 100644
index 000000000000..feb79a1d1b46
--- /dev/null
+++ b/drivers/mtd/nand/spi/Makefile
@@ -0,0 +1,2 @@
+spinand-objs := core.o
+obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
new file mode 100644
index 000000000000..a99ea352eb85
--- /dev/null
+++ b/drivers/mtd/nand/spi/core.c
@@ -0,0 +1,1111 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2016-2017 Micron Technology, Inc.
+ *
+ * Authors:
+ * Peter Pan <peterpandong@micron.com>
+ * Boris Brezillon <boris.brezillon@bootlin.com>
+ */
+
+#define pr_fmt(fmt) "spi-nand: " fmt
+
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mtd/spinand.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi-mem.h>
+
+static void spinand_cache_op_adjust_colum(struct spinand_device *spinand,
+ const struct nand_page_io_req *req,
+ u16 *column)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+ unsigned int shift;
+
+ if (nand->memorg.planes_per_lun < 2)
+ return;
+
+ /* The plane number is passed in MSB just above the column address */
+ shift = fls(nand->memorg.pagesize);
+ *column |= req->pos.plane << shift;
+}
+
+static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
+{
+ struct spi_mem_op op = SPINAND_GET_FEATURE_OP(reg,
+ spinand->scratchbuf);
+ int ret;
+
+ ret = spi_mem_exec_op(spinand->spimem, &op);
+ if (ret)
+ return ret;
+
+ *val = *spinand->scratchbuf;
+ return 0;
+}
+
+static int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val)
+{
+ struct spi_mem_op op = SPINAND_SET_FEATURE_OP(reg,
+ spinand->scratchbuf);
+
+ *spinand->scratchbuf = val;
+ return spi_mem_exec_op(spinand->spimem, &op);
+}
+
+static int spinand_read_status(struct spinand_device *spinand, u8 *status)
+{
+ return spinand_read_reg_op(spinand, REG_STATUS, status);
+}
+
+static int spinand_get_cfg(struct spinand_device *spinand, u8 *cfg)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+
+ if (WARN_ON(spinand->cur_target < 0 ||
+ spinand->cur_target >= nand->memorg.ntargets))
+ return -EINVAL;
+
+ *cfg = spinand->cfg_cache[spinand->cur_target];
+ return 0;
+}
+
+static int spinand_set_cfg(struct spinand_device *spinand, u8 cfg)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+ int ret;
+
+ if (WARN_ON(spinand->cur_target < 0 ||
+ spinand->cur_target >= nand->memorg.ntargets))
+ return -EINVAL;
+
+ if (spinand->cfg_cache[spinand->cur_target] == cfg)
+ return 0;
+
+ ret = spinand_write_reg_op(spinand, REG_CFG, cfg);
+ if (ret)
+ return ret;
+
+ spinand->cfg_cache[spinand->cur_target] = cfg;
+ return 0;
+}
+
+/**
+ * spinand_upd_cfg() - Update the configuration register
+ * @spinand: the spinand device
+ * @mask: the mask encoding the bits to update in the config reg
+ * @val: the new value to apply
+ *
+ * Update the configuration register.
+ *
+ * Return: 0 on success, a negative error code otherwise.
+ */
+int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val)
+{
+ int ret;
+ u8 cfg;
+
+ ret = spinand_get_cfg(spinand, &cfg);
+ if (ret)
+ return ret;
+
+ cfg &= ~mask;
+ cfg |= val;
+
+ return spinand_set_cfg(spinand, cfg);
+}
+
+/**
+ * spinand_select_target() - Select a specific NAND target/die
+ * @spinand: the spinand device
+ * @target: the target/die to select
+ *
+ * Select a new target/die. If chip only has one die, this function is a NOOP.
+ *
+ * Return: 0 on success, a negative error code otherwise.
+ */
+int spinand_select_target(struct spinand_device *spinand, unsigned int target)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+ int ret;
+
+ if (WARN_ON(target >= nand->memorg.ntargets))
+ return -EINVAL;
+
+ if (spinand->cur_target == target)
+ return 0;
+
+ if (nand->memorg.ntargets == 1) {
+ spinand->cur_target = target;
+ return 0;
+ }
+
+ ret = spinand->select_target(spinand, target);
+ if (ret)
+ return ret;
+
+ spinand->cur_target = target;
+ return 0;
+}
+
+static int spinand_init_cfg_cache(struct spinand_device *spinand)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+ struct device *dev = &spinand->spimem->spi->dev;
+ unsigned int target;
+ int ret;
+
+ spinand->cfg_cache = devm_kzalloc(dev,
+ sizeof(*spinand->cfg_cache) *
+ nand->memorg.ntargets,
+ GFP_KERNEL);
+ if (!spinand->cfg_cache)
+ return -ENOMEM;
+
+ for (target = 0; target < nand->memorg.ntargets; target++) {
+ ret = spinand_select_target(spinand, target);
+ if (ret)
+ return ret;
+
+ /*
+ * We use spinand_read_reg_op() instead of spinand_get_cfg()
+ * here to bypass the config cache.
+ */
+ ret = spinand_read_reg_op(spinand, REG_CFG,
+ &spinand->cfg_cache[target]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int spinand_init_quad_enable(struct spinand_device *spinand)
+{
+ bool enable = false;
+
+ if (!(spinand->flags & SPINAND_HAS_QE_BIT))
+ return 0;
+
+ if (spinand->op_templates.read_cache->data.buswidth == 4 ||
+ spinand->op_templates.write_cache->data.buswidth == 4 ||
+ spinand->op_templates.update_cache->data.buswidth == 4)
+ enable = true;
+
+ return spinand_upd_cfg(spinand, CFG_QUAD_ENABLE,
+ enable ? CFG_QUAD_ENABLE : 0);
+}
+
+static void spinand_ecc_enable(struct spinand_device *spinand,
+ bool enable)
+{
+ WARN_ON(spinand_upd_cfg(spinand, CFG_ECC_ENABLE,
+ enable ? CFG_ECC_ENABLE : 0));
+}
+
+static int spinand_write_enable_op(struct spinand_device *spinand)
+{
+ struct spi_mem_op op = SPINAND_WR_EN_DIS_OP(true);
+
+ return spi_mem_exec_op(spinand->spimem, &op);
+}
+
+static int spinand_load_page_op(struct spinand_device *spinand,
+ const struct nand_page_io_req *req)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+ unsigned int row = nanddev_pos_to_row(nand, &req->pos);
+ struct spi_mem_op op = SPINAND_PAGE_READ_OP(row);
+
+ return spi_mem_exec_op(spinand->spimem, &op);
+}
+
+static int spinand_read_from_cache_op(struct spinand_device *spinand,
+ const struct nand_page_io_req *req)
+{
+ struct spi_mem_op op = *spinand->op_templates.read_cache;
+ struct nand_device *nand = spinand_to_nand(spinand);
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ struct nand_page_io_req adjreq = *req;
+ unsigned int nbytes = 0;
+ void *buf = NULL;
+ u16 column = 0;
+ int ret;
+
+ if (req->datalen) {
+ adjreq.datalen = nanddev_page_size(nand);
+ adjreq.dataoffs = 0;
+ adjreq.databuf.in = spinand->databuf;
+ buf = spinand->databuf;
+ nbytes = adjreq.datalen;
+ }
+
+ if (req->ooblen) {
+ adjreq.ooblen = nanddev_per_page_oobsize(nand);
+ adjreq.ooboffs = 0;
+ adjreq.oobbuf.in = spinand->oobbuf;
+ nbytes += nanddev_per_page_oobsize(nand);
+ if (!buf) {
+ buf = spinand->oobbuf;
+ column = nanddev_page_size(nand);
+ }
+ }
+
+ spinand_cache_op_adjust_colum(spinand, &adjreq, &column);
+ op.addr.val = column;
+
+ /*
+ * Some controllers are limited in term of max RX data size. In this
+ * case, just repeat the READ_CACHE operation after updating the
+ * column.
+ */
+ while (nbytes) {
+ op.data.buf.in = buf;
+ op.data.nbytes = nbytes;
+ ret = spi_mem_adjust_op_size(spinand->spimem, &op);
+ if (ret)
+ return ret;
+
+ ret = spi_mem_exec_op(spinand->spimem, &op);
+ if (ret)
+ return ret;
+
+ buf += op.data.nbytes;
+ nbytes -= op.data.nbytes;
+ op.addr.val += op.data.nbytes;
+ }
+
+ if (req->datalen)
+ memcpy(req->databuf.in, spinand->databuf + req->dataoffs,
+ req->datalen);
+
+ if (req->ooblen) {
+ if (req->mode == MTD_OPS_AUTO_OOB)
+ mtd_ooblayout_get_databytes(mtd, req->oobbuf.in,
+ spinand->oobbuf,
+ req->ooboffs,
+ req->ooblen);
+ else
+ memcpy(req->oobbuf.in, spinand->oobbuf + req->ooboffs,
+ req->ooblen);
+ }
+
+ return 0;
+}
+
+static int spinand_write_to_cache_op(struct spinand_device *spinand,
+ const struct nand_page_io_req *req)
+{
+ struct spi_mem_op op = *spinand->op_templates.write_cache;
+ struct nand_device *nand = spinand_to_nand(spinand);
+ struct mtd_info *mtd = nanddev_to_mtd(nand);
+ struct nand_page_io_req adjreq = *req;
+ unsigned int nbytes = 0;
+ void *buf = NULL;
+ u16 column = 0;
+ int ret;
+
+ memset(spinand->databuf, 0xff,
+ nanddev_page_size(nand) +
+ nanddev_per_page_oobsize(nand));
+
+ if (req->datalen) {
+ memcpy(spinand->databuf + req->dataoffs, req->databuf.out,
+ req->datalen);
+ adjreq.dataoffs = 0;
+ adjreq.datalen = nanddev_page_size(nand);
+ adjreq.databuf.out = spinand->databuf;
+ nbytes = adjreq.datalen;
+ buf = spinand->databuf;
+ }
+
+ if (req->ooblen) {
+ if (req->mode == MTD_OPS_AUTO_OOB)
+ mtd_ooblayout_set_databytes(mtd, req->oobbuf.out,
+ spinand->oobbuf,
+ req->ooboffs,
+ req->ooblen);
+ else
+ memcpy(spinand->oobbuf + req->ooboffs, req->oobbuf.out,
+ req->ooblen);
+
+ adjreq.ooblen = nanddev_per_page_oobsize(nand);
+ adjreq.ooboffs = 0;
+ nbytes += nanddev_per_page_oobsize(nand);
+ if (!buf) {
+ buf = spinand->oobbuf;
+ column = nanddev_page_size(nand);
+ }
+ }
+
+ spinand_cache_op_adjust_colum(spinand, &adjreq, &column);
+
+ op = *spinand->op_templates.write_cache;
+ op.addr.val = column;
+
+ /*
+ * Some controllers are limited in term of max TX data size. In this
+ * case, split the operation into one LOAD CACHE and one or more
+ * LOAD RANDOM CACHE.
+ */
+ while (nbytes) {
+ op.data.buf.out = buf;
+ op.data.nbytes = nbytes;
+
+ ret = spi_mem_adjust_op_size(spinand->spimem, &op);
+ if (ret)
+ return ret;
+
+ ret = spi_mem_exec_op(spinand->spimem, &op);
+ if (ret)
+ return ret;
+
+ buf += op.data.nbytes;
+ nbytes -= op.data.nbytes;
+ op.addr.val += op.data.nbytes;
+
+ /*
+ * We need to use the RANDOM LOAD CACHE operation if there's
+ * more than one iteration, because the LOAD operation resets
+ * the cache to 0xff.
+ */
+ if (nbytes) {
+ column = op.addr.val;
+ op = *spinand->op_templates.update_cache;
+ op.addr.val = column;
+ }
+ }
+
+ return 0;
+}
+
+static int spinand_program_op(struct spinand_device *spinand,
+ const struct nand_page_io_req *req)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+ unsigned int row = nanddev_pos_to_row(nand, &req->pos);
+ struct spi_mem_op op = SPINAND_PROG_EXEC_OP(row);
+
+ return spi_mem_exec_op(spinand->spimem, &op);
+}
+
+static int spinand_erase_op(struct spinand_device *spinand,
+ const struct nand_pos *pos)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+ unsigned int row = nanddev_pos_to_row(nand, pos);
+ struct spi_mem_op op = SPINAND_BLK_ERASE_OP(row);
+
+ return spi_mem_exec_op(spinand->spimem, &op);
+}
+
+static int spinand_wait(struct spinand_device *spinand, u8 *s)
+{
+ unsigned long timeo = jiffies + msecs_to_jiffies(400);
+ u8 status;
+
+ do {
+ spinand_read_status(spinand, &status);
+ if (!(status & STATUS_BUSY))
+ goto out;
+ } while (time_before(jiffies, timeo));
+
+ /*
+ * Extra read, just in case the STATUS_READY bit has changed
+ * since our last check
+ */
+ spinand_read_status(spinand, &status);
+
+out:
+ if (s)
+ *s = status;
+
+ return status & STATUS_BUSY ? -ETIMEDOUT : 0;
+}
+
+static int spinand_read_id_op(struct spinand_device *spinand, u8 *buf)
+{
+ struct spi_mem_op op = SPINAND_READID_OP(0, spinand->scratchbuf,
+ SPINAND_MAX_ID_LEN);
+ int ret;
+
+ ret = spi_mem_exec_op(spinand->spimem, &op);
+ if (!ret)
+ memcpy(buf, spinand->scratchbuf, SPINAND_MAX_ID_LEN);
+
+ return 0;
+}
+
+static int spinand_reset_op(struct spinand_device *spinand)
+{
+ struct spi_mem_op op = SPINAND_RESET_OP;
+ int ret;
+
+ ret = spi_mem_exec_op(spinand->spimem, &op);
+ if (ret)
+ return ret;
+
+ return spinand_wait(spinand, NULL);
+}
+
+static int spinand_lock_block(struct spinand_device *spinand, u8 lock)
+{
+ return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, lock);
+}
+
+static int spinand_check_ecc_status(struct spinand_device *spinand, u8 status)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+
+ if (spinand->eccinfo.get_status)
+ return spinand->eccinfo.get_status(spinand, status);
+
+ switch (status & STATUS_ECC_MASK) {
+ case STATUS_ECC_NO_BITFLIPS:
+ return 0;
+
+ case STATUS_ECC_HAS_BITFLIPS:
+ /*
+ * We have no way to know exactly how many bitflips have been
+ * fixed, so let's return the maximum possible value so that
+ * wear-leveling layers move the data immediately.
+ */
+ return nand->eccreq.strength;
+
+ case STATUS_ECC_UNCOR_ERROR:
+ return -EBADMSG;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int spinand_read_page(struct spinand_device *spinand,
+ const struct nand_page_io_req *req,
+ bool ecc_enabled)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+ struct device *dev = &spinand->spimem->spi->dev;
+ u8 status;
+ int ret;
+
+ spinand_load_page_op(spinand, req);
+
+ ret = spinand_wait(spinand, &status);
+ if (ret < 0) {
+ dev_err(dev, "failed to load page @%llx (err = %d)\n",
+ nanddev_pos_to_offs(nand, &req->pos), ret);
+ return ret;
+ }
+
+ spinand_read_from_cache_op(spinand, req);
+
+ if (!ecc_enabled)
+ return 0;
+
+ return spinand_check_ecc_status(spinand, status);
+}
+
+static int spinand_write_page(struct spinand_device *spinand,
+ const struct nand_page_io_req *req)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+ struct device *dev = &spinand->spimem->spi->dev;
+ u8 status;
+ int ret = 0;
+
+ spinand_write_enable_op(spinand);
+ spinand_write_to_cache_op(spinand, req);
+ spinand_program_op(spinand, req);
+
+ ret = spinand_wait(spinand, &status);
+ if (!ret && (status & STATUS_PROG_FAILED))
+ ret = -EIO;
+
+ if (ret < 0)
+ dev_err(dev, "failed to program page @%llx (err = %d)\n",
+ nanddev_pos_to_offs(nand, &req->pos), ret);
+
+ return ret;
+}
+
+static int spinand_mtd_read(struct mtd_info *mtd, loff_t from,
+ struct mtd_oob_ops *ops)
+{
+ struct spinand_device *spinand = mtd_to_spinand(mtd);
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ unsigned int max_bitflips = 0;
+ struct nand_io_iter iter;
+ bool enable_ecc = false;
+ bool ecc_failed = false;
+ int ret = 0;
+
+ if (ops->mode != MTD_OPS_RAW && spinand->eccinfo.ooblayout)
+ enable_ecc = true;
+
+ mutex_lock(&spinand->lock);
+
+ nanddev_io_for_each_page(nand, from, ops, &iter) {
+ spinand_select_target(spinand, iter.req.pos.target);
+ spinand_ecc_enable(spinand, enable_ecc);
+ ret = spinand_read_page(spinand, &iter.req, enable_ecc);
+ if (ret < 0 && ret != -EBADMSG)
+ break;
+
+ if (ret == -EBADMSG) {
+ ecc_failed = true;
+ mtd->ecc_stats.failed++;
+ ret = 0;
+ } else {
+ mtd->ecc_stats.corrected += ret;
+ max_bitflips = max_t(unsigned int, max_bitflips, ret);
+ }
+
+ ops->retlen += iter.req.datalen;
+ ops->oobretlen += iter.req.ooblen;
+ }
+
+ mutex_unlock(&spinand->lock);
+
+ if (ecc_failed && !ret)
+ ret = -EBADMSG;
+
+ return ret ? ret : max_bitflips;
+}
+
+static int spinand_mtd_write(struct mtd_info *mtd, loff_t to,
+ struct mtd_oob_ops *ops)
+{
+ struct spinand_device *spinand = mtd_to_spinand(mtd);
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ struct nand_io_iter iter;
+ bool enable_ecc = false;
+ int ret = 0;
+
+ if (ops->mode != MTD_OPS_RAW && mtd->ooblayout)
+ enable_ecc = true;
+
+ mutex_lock(&spinand->lock);
+
+ nanddev_io_for_each_page(nand, to, ops, &iter) {
+ spinand_select_target(spinand, iter.req.pos.target);
+ spinand_ecc_enable(spinand, enable_ecc);
+
+ ret = spinand_write_page(spinand, &iter.req);
+ if (ret)
+ break;
+
+ ops->retlen += iter.req.datalen;
+ ops->oobretlen += iter.req.ooblen;
+ }
+
+ mutex_unlock(&spinand->lock);
+
+ return ret;
+}
+
+static bool spinand_isbad(struct nand_device *nand, const struct nand_pos *pos)
+{
+ struct spinand_device *spinand = nand_to_spinand(nand);
+ struct nand_page_io_req req = {
+ .pos = *pos,
+ .ooblen = 2,
+ .ooboffs = 0,
+ .oobbuf.in = spinand->oobbuf,
+ .mode = MTD_OPS_RAW,
+ };
+
+ memset(spinand->oobbuf, 0, 2);
+ spinand_select_target(spinand, pos->target);
+ spinand_read_page(spinand, &req, false);
+ if (spinand->oobbuf[0] != 0xff || spinand->oobbuf[1] != 0xff)
+ return true;
+
+ return false;
+}
+
+static int spinand_mtd_block_isbad(struct mtd_info *mtd, loff_t offs)
+{
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ struct spinand_device *spinand = nand_to_spinand(nand);
+ struct nand_pos pos;
+ int ret;
+
+ nanddev_offs_to_pos(nand, offs, &pos);
+ mutex_lock(&spinand->lock);
+ ret = nanddev_isbad(nand, &pos);
+ mutex_unlock(&spinand->lock);
+
+ return ret;
+}
+
+static int spinand_markbad(struct nand_device *nand, const struct nand_pos *pos)
+{
+ struct spinand_device *spinand = nand_to_spinand(nand);
+ struct nand_page_io_req req = {
+ .pos = *pos,
+ .ooboffs = 0,
+ .ooblen = 2,
+ .oobbuf.out = spinand->oobbuf,
+ };
+
+ /* Erase block before marking it bad. */
+ spinand_select_target(spinand, pos->target);
+ spinand_write_enable_op(spinand);
+ spinand_erase_op(spinand, pos);
+
+ memset(spinand->oobbuf, 0, 2);
+ return spinand_write_page(spinand, &req);
+}
+
+static int spinand_mtd_block_markbad(struct mtd_info *mtd, loff_t offs)
+{
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ struct spinand_device *spinand = nand_to_spinand(nand);
+ struct nand_pos pos;
+ int ret;
+
+ nanddev_offs_to_pos(nand, offs, &pos);
+ mutex_lock(&spinand->lock);
+ ret = nanddev_markbad(nand, &pos);
+ mutex_unlock(&spinand->lock);
+
+ return ret;
+}
+
+static int spinand_erase(struct nand_device *nand, const struct nand_pos *pos)
+{
+ struct spinand_device *spinand = nand_to_spinand(nand);
+ struct device *dev = &spinand->spimem->spi->dev;
+ u8 status;
+ int ret;
+
+ spinand_select_target(spinand, pos->target);
+ spinand_write_enable_op(spinand);
+ spinand_erase_op(spinand, pos);
+
+ ret = spinand_wait(spinand, &status);
+
+ if (!ret && (status & STATUS_ERASE_FAILED))
+ ret = -EIO;
+
+ if (ret)
+ dev_err(dev, "failed to erase block %d (err = %d)\n",
+ pos->eraseblock, ret);
+
+ return ret;
+}
+
+static int spinand_mtd_erase(struct mtd_info *mtd,
+ struct erase_info *einfo)
+{
+ struct spinand_device *spinand = mtd_to_spinand(mtd);
+ int ret;
+
+ mutex_lock(&spinand->lock);
+ ret = nanddev_mtd_erase(mtd, einfo);
+ mutex_unlock(&spinand->lock);
+
+ return ret;
+}
+
+static int spinand_mtd_block_isreserved(struct mtd_info *mtd, loff_t offs)
+{
+ struct spinand_device *spinand = mtd_to_spinand(mtd);
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ struct nand_pos pos;
+ int ret;
+
+ nanddev_offs_to_pos(nand, offs, &pos);
+ mutex_lock(&spinand->lock);
+ ret = nanddev_isreserved(nand, &pos);
+ mutex_unlock(&spinand->lock);
+
+ return ret;
+}
+
+const struct spi_mem_op *
+spinand_find_supported_op(struct spinand_device *spinand,
+ const struct spi_mem_op *ops,
+ unsigned int nops)
+{
+ unsigned int i;
+
+ for (i = 0; i < nops; i++) {
+ if (spi_mem_supports_op(spinand->spimem, &ops[i]))
+ return &ops[i];
+ }
+
+ return NULL;
+}
+
+static const struct nand_ops spinand_ops = {
+ .erase = spinand_erase,
+ .markbad = spinand_markbad,
+ .isbad = spinand_isbad,
+};
+
+static int spinand_manufacturer_detect(struct spinand_device *spinand)
+{
+ return -ENOTSUPP;
+}
+
+static int spinand_manufacturer_init(struct spinand_device *spinand)
+{
+ if (spinand->manufacturer->ops->init)
+ return spinand->manufacturer->ops->init(spinand);
+
+ return 0;
+}
+
+static void spinand_manufacturer_cleanup(struct spinand_device *spinand)
+{
+ /* Release manufacturer private data */
+ if (spinand->manufacturer->ops->cleanup)
+ return spinand->manufacturer->ops->cleanup(spinand);
+}
+
+static const struct spi_mem_op *
+spinand_select_op_variant(struct spinand_device *spinand,
+ const struct spinand_op_variants *variants)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+ unsigned int i;
+
+ for (i = 0; i < variants->nops; i++) {
+ struct spi_mem_op op = variants->ops[i];
+ unsigned int nbytes;
+ int ret;
+
+ nbytes = nanddev_per_page_oobsize(nand) +
+ nanddev_page_size(nand);
+
+ while (nbytes) {
+ op.data.nbytes = nbytes;
+ ret = spi_mem_adjust_op_size(spinand->spimem, &op);
+ if (ret)
+ break;
+
+ if (!spi_mem_supports_op(spinand->spimem, &op))
+ break;
+
+ nbytes -= op.data.nbytes;
+ }
+
+ if (!nbytes)
+ return &variants->ops[i];
+ }
+
+ return NULL;
+}
+
+/**
+ * spinand_match_and_init() - Try to find a match between a device ID and an
+ * entry in a spinand_info table
+ * @spinand: SPI NAND object
+ * @table: SPI NAND device description table
+ * @table_size: size of the device description table
+ *
+ * Should be used by SPI NAND manufacturer drivers when they want to find a
+ * match between a device ID retrieved through the READ_ID command and an
+ * entry in the SPI NAND description table. If a match is found, the spinand
+ * object will be initialized with information provided by the matching
+ * spinand_info entry.
+ *
+ * Return: 0 on success, a negative error code otherwise.
+ */
+int spinand_match_and_init(struct spinand_device *spinand,
+ const struct spinand_info *table,
+ unsigned int table_size, u8 devid)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+ unsigned int i;
+
+ for (i = 0; i < table_size; i++) {
+ const struct spinand_info *info = &table[i];
+ const struct spi_mem_op *op;
+
+ if (devid != info->devid)
+ continue;
+
+ nand->memorg = table[i].memorg;
+ nand->eccreq = table[i].eccreq;
+ spinand->eccinfo = table[i].eccinfo;
+ spinand->flags = table[i].flags;
+ spinand->select_target = table[i].select_target;
+
+ op = spinand_select_op_variant(spinand,
+ info->op_variants.read_cache);
+ if (!op)
+ return -ENOTSUPP;
+
+ spinand->op_templates.read_cache = op;
+
+ op = spinand_select_op_variant(spinand,
+ info->op_variants.write_cache);
+ if (!op)
+ return -ENOTSUPP;
+
+ spinand->op_templates.write_cache = op;
+
+ op = spinand_select_op_variant(spinand,
+ info->op_variants.update_cache);
+ spinand->op_templates.update_cache = op;
+
+ return 0;
+ }
+
+ return -ENOTSUPP;
+}
+
+static int spinand_detect(struct spinand_device *spinand)
+{
+ struct device *dev = &spinand->spimem->spi->dev;
+ struct nand_device *nand = spinand_to_nand(spinand);
+ int ret;
+
+ ret = spinand_reset_op(spinand);
+ if (ret)
+ return ret;
+
+ ret = spinand_read_id_op(spinand, spinand->id.data);
+ if (ret)
+ return ret;
+
+ spinand->id.len = SPINAND_MAX_ID_LEN;
+
+ ret = spinand_manufacturer_detect(spinand);
+ if (ret) {
+ dev_err(dev, "unknown raw ID %*phN\n", SPINAND_MAX_ID_LEN,
+ spinand->id.data);
+ return ret;
+ }
+
+ if (nand->memorg.ntargets > 1 && !spinand->select_target) {
+ dev_err(dev,
+ "SPI NANDs with more than one die must implement ->select_target()\n");
+ return -EINVAL;
+ }
+
+ dev_info(&spinand->spimem->spi->dev,
+ "%s SPI NAND was found.\n", spinand->manufacturer->name);
+ dev_info(&spinand->spimem->spi->dev,
+ "%llu MiB, block size: %zu KiB, page size: %zu, OOB size: %u\n",
+ nanddev_size(nand) >> 20, nanddev_eraseblock_size(nand) >> 10,
+ nanddev_page_size(nand), nanddev_per_page_oobsize(nand));
+
+ return 0;
+}
+
+static int spinand_noecc_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ return -ERANGE;
+}
+
+static int spinand_noecc_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ if (section)
+ return -ERANGE;
+
+ /* Reserve 2 bytes for the BBM. */
+ region->offset = 2;
+ region->length = 62;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops spinand_noecc_ooblayout = {
+ .ecc = spinand_noecc_ooblayout_ecc,
+ .free = spinand_noecc_ooblayout_free,
+};
+
+static int spinand_init(struct spinand_device *spinand)
+{
+ struct device *dev = &spinand->spimem->spi->dev;
+ struct mtd_info *mtd = spinand_to_mtd(spinand);
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ int ret, i;
+
+ /*
+ * We need a scratch buffer because the spi_mem interface requires that
+ * buf passed in spi_mem_op->data.buf be DMA-able.
+ */
+ spinand->scratchbuf = kzalloc(SPINAND_MAX_ID_LEN, GFP_KERNEL);
+ if (!spinand->scratchbuf)
+ return -ENOMEM;
+
+ ret = spinand_detect(spinand);
+ if (ret)
+ goto err_free_bufs;
+
+ /*
+ * Use kzalloc() instead of devm_kzalloc() here, because some drivers
+ * may use this buffer for DMA access.
+ * Memory allocated by devm_ does not guarantee DMA-safe alignment.
+ */
+ spinand->databuf = kzalloc(nanddev_page_size(nand) +
+ nanddev_per_page_oobsize(nand),
+ GFP_KERNEL);
+ if (!spinand->databuf)
+ goto err_free_bufs;
+
+ spinand->oobbuf = spinand->databuf + nanddev_page_size(nand);
+
+ ret = spinand_init_cfg_cache(spinand);
+ if (ret)
+ goto err_free_bufs;
+
+ ret = spinand_init_quad_enable(spinand);
+ if (ret)
+ goto err_free_bufs;
+
+ ret = spinand_manufacturer_init(spinand);
+ if (ret) {
+ dev_err(dev,
+ "Failed to initialize the SPI NAND chip (err = %d)\n",
+ ret);
+ goto err_free_bufs;
+ }
+
+ /* After power up, all blocks are locked, so unlock them here. */
+ for (i = 0; i < nand->memorg.ntargets; i++) {
+ spinand_select_target(spinand, i);
+ spinand_lock_block(spinand, BL_ALL_UNLOCKED);
+ }
+
+ ret = nanddev_init(nand, &spinand_ops, THIS_MODULE);
+ if (ret)
+ goto err_manuf_cleanup;
+
+ /*
+ * Right now, we don't support ECC, so let the whole oob
+ * area is available for user.
+ */
+ mtd->_read_oob = spinand_mtd_read;
+ mtd->_write_oob = spinand_mtd_write;
+ mtd->_block_isbad = spinand_mtd_block_isbad;
+ mtd->_block_markbad = spinand_mtd_block_markbad;
+ mtd->_block_isreserved = spinand_mtd_block_isreserved;
+ mtd->_erase = spinand_mtd_erase;
+
+ if (spinand->eccinfo.ooblayout)
+ mtd_set_ooblayout(mtd, spinand->eccinfo.ooblayout);
+ else
+ mtd_set_ooblayout(mtd, &spinand_noecc_ooblayout);
+
+ ret = mtd_ooblayout_count_freebytes(mtd);
+ if (ret < 0)
+ goto err_cleanup_nanddev;
+
+ return 0;
+
+err_cleanup_nanddev:
+ nanddev_cleanup(nand);
+
+err_manuf_cleanup:
+ spinand_manufacturer_cleanup(spinand);
+
+err_free_bufs:
+ kfree(spinand->databuf);
+ kfree(spinand->scratchbuf);
+ return ret;
+}
+
+static void spinand_cleanup(struct spinand_device *spinand)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+
+ nanddev_cleanup(nand);
+ spinand_manufacturer_cleanup(spinand);
+ kfree(spinand->databuf);
+ kfree(spinand->scratchbuf);
+}
+
+static int spinand_probe(struct spi_mem *mem)
+{
+ struct spinand_device *spinand;
+ struct mtd_info *mtd;
+ int ret;
+
+ spinand = devm_kzalloc(&mem->spi->dev, sizeof(*spinand),
+ GFP_KERNEL);
+ if (!spinand)
+ return -ENOMEM;
+
+ spinand->spimem = mem;
+ spi_mem_set_drvdata(mem, spinand);
+ spinand_set_of_node(spinand, mem->spi->dev.of_node);
+ mutex_init(&spinand->lock);
+ mtd = spinand_to_mtd(spinand);
+ mtd->dev.parent = &mem->spi->dev;
+
+ ret = spinand_init(spinand);
+ if (ret)
+ return ret;
+
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret)
+ goto err_spinand_cleanup;
+
+ return 0;
+
+err_spinand_cleanup:
+ spinand_cleanup(spinand);
+
+ return ret;
+}
+
+static int spinand_remove(struct spi_mem *mem)
+{
+ struct spinand_device *spinand;
+ struct mtd_info *mtd;
+ int ret;
+
+ spinand = spi_mem_get_drvdata(mem);
+ mtd = spinand_to_mtd(spinand);
+
+ ret = mtd_device_unregister(mtd);
+ if (ret)
+ return ret;
+
+ spinand_cleanup(spinand);
+
+ return 0;
+}
+
+static const struct spi_device_id spinand_ids[] = {
+ { .name = "spi-nand" },
+ { /* sentinel */ },
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id spinand_of_ids[] = {
+ { .compatible = "spi-nand" },
+ { /* sentinel */ },
+};
+#endif
+
+static struct spi_mem_driver spinand_drv = {
+ .spidrv = {
+ .id_table = spinand_ids,
+ .driver = {
+ .name = "spi-nand",
+ .of_match_table = of_match_ptr(spinand_of_ids),
+ },
+ },
+ .probe = spinand_probe,
+ .remove = spinand_remove,
+};
+module_spi_mem_driver(spinand_drv);
+
+MODULE_DESCRIPTION("SPI NAND framework");
+MODULE_AUTHOR("Peter Pan<peterpandong@micron.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
new file mode 100644
index 000000000000..17a4584c0017
--- /dev/null
+++ b/include/linux/mtd/spinand.h
@@ -0,0 +1,415 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2016-2017 Micron Technology, Inc.
+ *
+ * Authors:
+ * Peter Pan <peterpandong@micron.com>
+ */
+#ifndef __LINUX_MTD_SPINAND_H
+#define __LINUX_MTD_SPINAND_H
+
+#include <linux/mutex.h>
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi-mem.h>
+
+/**
+ * Standard SPI NAND flash operations
+ */
+
+#define SPINAND_RESET_OP \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xff, 1), \
+ SPI_MEM_OP_NO_ADDR, \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_WR_EN_DIS_OP(enable) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD((enable) ? 0x06 : 0x04, 1), \
+ SPI_MEM_OP_NO_ADDR, \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_READID_OP(ndummy, buf, len) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x9f, 1), \
+ SPI_MEM_OP_NO_ADDR, \
+ SPI_MEM_OP_DUMMY(ndummy, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 1))
+
+#define SPINAND_SET_FEATURE_OP(reg, valptr) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x1f, 1), \
+ SPI_MEM_OP_ADDR(1, reg, 1), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_DATA_OUT(1, valptr, 1))
+
+#define SPINAND_GET_FEATURE_OP(reg, valptr) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x0f, 1), \
+ SPI_MEM_OP_ADDR(1, reg, 1), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_DATA_IN(1, valptr, 1))
+
+#define SPINAND_BLK_ERASE_OP(addr) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xd8, 1), \
+ SPI_MEM_OP_ADDR(3, addr, 1), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_PAGE_READ_OP(addr) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x13, 1), \
+ SPI_MEM_OP_ADDR(3, addr, 1), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_PAGE_READ_FROM_CACHE_OP(fast, addr, ndummy, buf, len) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1), \
+ SPI_MEM_OP_ADDR(2, addr, 1), \
+ SPI_MEM_OP_DUMMY(ndummy, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 1))
+
+#define SPINAND_PAGE_READ_FROM_CACHE_X2_OP(addr, ndummy, buf, len) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \
+ SPI_MEM_OP_ADDR(2, addr, 1), \
+ SPI_MEM_OP_DUMMY(ndummy, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 2))
+
+#define SPINAND_PAGE_READ_FROM_CACHE_X4_OP(addr, ndummy, buf, len) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \
+ SPI_MEM_OP_ADDR(2, addr, 1), \
+ SPI_MEM_OP_DUMMY(ndummy, 1), \
+ SPI_MEM_OP_DATA_IN(len, buf, 4))
+
+#define SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(addr, ndummy, buf, len) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1), \
+ SPI_MEM_OP_ADDR(2, addr, 2), \
+ SPI_MEM_OP_DUMMY(ndummy, 2), \
+ SPI_MEM_OP_DATA_IN(len, buf, 2))
+
+#define SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(addr, ndummy, buf, len) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1), \
+ SPI_MEM_OP_ADDR(2, addr, 4), \
+ SPI_MEM_OP_DUMMY(ndummy, 4), \
+ SPI_MEM_OP_DATA_IN(len, buf, 4))
+
+#define SPINAND_PROG_EXEC_OP(addr) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(0x10, 1), \
+ SPI_MEM_OP_ADDR(3, addr, 1), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_NO_DATA)
+
+#define SPINAND_PROG_LOAD(reset, addr, buf, len) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x02 : 0x84, 1), \
+ SPI_MEM_OP_ADDR(2, addr, 1), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_DATA_OUT(len, buf, 1))
+
+#define SPINAND_PROG_LOAD_X4(reset, addr, buf, len) \
+ SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x32 : 0x34, 1), \
+ SPI_MEM_OP_ADDR(2, addr, 1), \
+ SPI_MEM_OP_NO_DUMMY, \
+ SPI_MEM_OP_DATA_OUT(len, buf, 4))
+
+/**
+ * Standard SPI NAND flash commands
+ */
+#define SPINAND_CMD_PROG_LOAD_X4 0x32
+#define SPINAND_CMD_PROG_LOAD_RDM_DATA_X4 0x34
+
+/* feature register */
+#define REG_BLOCK_LOCK 0xa0
+#define BL_ALL_UNLOCKED 0x00
+
+/* configuration register */
+#define REG_CFG 0xb0
+#define CFG_ECC_ENABLE BIT(4)
+#define CFG_QUAD_ENABLE BIT(0)
+
+/* status register */
+#define REG_STATUS 0xc0
+#define STATUS_BUSY BIT(0)
+#define STATUS_ERASE_FAILED BIT(2)
+#define STATUS_PROG_FAILED BIT(3)
+#define STATUS_ECC_MASK GENMASK(5, 4)
+#define STATUS_ECC_NO_BITFLIPS (0 << 4)
+#define STATUS_ECC_HAS_BITFLIPS (1 << 4)
+#define STATUS_ECC_UNCOR_ERROR (2 << 4)
+
+struct spinand_op;
+struct spinand_device;
+
+#define SPINAND_MAX_ID_LEN 4
+
+/**
+ * struct spinand_id - SPI NAND id structure
+ * @data: buffer containing the id bytes. Currently 4 bytes large, but can
+ * be extended if required
+ * @len: ID length
+ *
+ * struct_spinand_id->data contains all bytes returned after a READ_ID command,
+ * including dummy bytes if the chip does not emit ID bytes right after the
+ * READ_ID command. The responsibility to extract real ID bytes is left to
+ * struct_manufacurer_ops->detect().
+ */
+struct spinand_id {
+ u8 data[SPINAND_MAX_ID_LEN];
+ int len;
+};
+
+/**
+ * struct manufacurer_ops - SPI NAND manufacturer specific operations
+ * @detect: detect a SPI NAND device. Every time a SPI NAND device is probed
+ * the core calls the struct_manufacurer_ops->detect() hook of each
+ * registered manufacturer until one of them return 1. Note that
+ * the first thing to check in this hook is that the manufacturer ID
+ * in struct_spinand_device->id matches the manufacturer whose
+ * ->detect() hook has been called. Should return 1 if there's a
+ * match, 0 if the manufacturer ID does not match and a negative
+ * error code otherwise. When true is returned, the core assumes
+ * that properties of the NAND chip (spinand->base.memorg and
+ * spinand->base.eccreq) have been filled
+ * @init: initialize a SPI NAND device
+ * @cleanup: cleanup a SPI NAND device
+ *
+ * Each SPI NAND manufacturer driver should implement this interface so that
+ * NAND chips coming from this vendor can be detected and initialized properly.
+ */
+struct spinand_manufacturer_ops {
+ int (*detect)(struct spinand_device *spinand);
+ int (*init)(struct spinand_device *spinand);
+ void (*cleanup)(struct spinand_device *spinand);
+};
+
+/**
+ * struct spinand_manufacturer - SPI NAND manufacturer instance
+ * @id: manufacturer ID
+ * @name: manufacturer name
+ * @ops: manufacturer operations
+ */
+struct spinand_manufacturer {
+ u8 id;
+ char *name;
+ const struct spinand_manufacturer_ops *ops;
+};
+
+/**
+ * struct spinand_op_variants - SPI NAND operation variants
+ * @ops: the list of variants for a given operation
+ * @nops: the number of variants
+ *
+ * Some operations like read-from-cache/write-to-cache have several variants
+ * depending on the number of IO lines you use to transfer data or address
+ * cycles. This structure is a way to describe the different variants supported
+ * by a chip and let the core pick the best one based on the SPI mem controller
+ * capabilities.
+ */
+struct spinand_op_variants {
+ const struct spi_mem_op *ops;
+ unsigned int nops;
+};
+
+#define SPINAND_OP_VARIANTS(name, ...) \
+ const struct spinand_op_variants name = { \
+ .ops = (struct spi_mem_op[]) { __VA_ARGS__ }, \
+ .nops = sizeof((struct spi_mem_op[]){ __VA_ARGS__ }) / \
+ sizeof(struct spi_mem_op), \
+ }
+
+/**
+ * spinand_ecc_info - description of the on-die ECC implemented by a SPI NAND
+ * chip
+ * @get_status: get the ECC status. Should return a positive number encoding
+ * the number of corrected bitflips if correction was possible or
+ * -EBADMSG if there are uncorrectable errors. I can also return
+ * other negative error codes if the error is not caused by
+ * uncorrectable bitflips
+ * @ooblayout: the OOB layout used by the on-die ECC implementation
+ */
+struct spinand_ecc_info {
+ int (*get_status)(struct spinand_device *spinand, u8 status);
+ const struct mtd_ooblayout_ops *ooblayout;
+};
+
+#define SPINAND_HAS_QE_BIT BIT(0)
+
+/**
+ * struct spinand_info - Structure used to describe SPI NAND chips
+ * @model: model name
+ * @devid: device ID
+ * @flags: OR-ing of the SPINAND_XXX flags
+ * @memorg: memory organization
+ * @eccreq: ECC requirements
+ * @eccinfo: on-die ECC info
+ * @op_variants: operations variants
+ * @op_variants.read_cache: variants of the read-cache operation
+ * @op_variants.write_cache: variants of the write-cache operation
+ * @op_variants.update_cache: variants of the update-cache operation
+ * @select_target: function used to select a target/die. Required only for
+ * multi-die chips
+ *
+ * Each SPI NAND manufacturer driver should have a spinand_info table
+ * describing all the chips supported by the driver.
+ */
+struct spinand_info {
+ const char *model;
+ u8 devid;
+ u32 flags;
+ struct nand_memory_organization memorg;
+ struct nand_ecc_req eccreq;
+ struct spinand_ecc_info eccinfo;
+ struct {
+ const struct spinand_op_variants *read_cache;
+ const struct spinand_op_variants *write_cache;
+ const struct spinand_op_variants *update_cache;
+ } op_variants;
+ int (*select_target)(struct spinand_device *spinand,
+ unsigned int target);
+};
+
+#define SPINAND_INFO_OP_VARIANTS(__read, __write, __update) \
+ { \
+ .read_cache = __read, \
+ .write_cache = __write, \
+ .update_cache = __update, \
+ }
+
+#define SPINAND_ECCINFO(__ooblayout, __get_status) \
+ .eccinfo = { \
+ .ooblayout = __ooblayout, \
+ .get_status = __get_status, \
+ }
+
+#define SPINAND_SELECT_TARGET(__func) \
+ .select_target = __func,
+
+#define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants, \
+ __flags, ...) \
+ { \
+ .model = __model, \
+ .devid = __id, \
+ .memorg = __memorg, \
+ .eccreq = __eccreq, \
+ .op_variants = __op_variants, \
+ .flags = __flags, \
+ __VA_ARGS__ \
+ }
+
+/**
+ * struct spinand_device - SPI NAND device instance
+ * @base: NAND device instance
+ * @spimem: pointer to the SPI mem object
+ * @lock: lock used to serialize accesses to the NAND
+ * @id: NAND ID as returned by READ_ID
+ * @flags: NAND flags
+ * @op_templates: various SPI mem op templates
+ * @op_templates.read_cache: read cache op template
+ * @op_templates.write_cache: write cache op template
+ * @op_templates.update_cache: update cache op template
+ * @select_target: select a specific target/die. Usually called before sending
+ * a command addressing a page or an eraseblock embedded in
+ * this die. Only required if your chip exposes several dies
+ * @cur_target: currently selected target/die
+ * @eccinfo: on-die ECC information
+ * @cfg_cache: config register cache. One entry per die
+ * @databuf: bounce buffer for data
+ * @oobbuf: bounce buffer for OOB data
+ * @scratchbuf: buffer used for everything but page accesses. This is needed
+ * because the spi-mem interface explicitly requests that buffers
+ * passed in spi_mem_op be DMA-able, so we can't based the bufs on
+ * the stack
+ * @manufacturer: SPI NAND manufacturer information
+ * @priv: manufacturer private data
+ */
+struct spinand_device {
+ struct nand_device base;
+ struct spi_mem *spimem;
+ struct mutex lock;
+ struct spinand_id id;
+ u32 flags;
+
+ struct {
+ const struct spi_mem_op *read_cache;
+ const struct spi_mem_op *write_cache;
+ const struct spi_mem_op *update_cache;
+ } op_templates;
+
+ int (*select_target)(struct spinand_device *spinand,
+ unsigned int target);
+ unsigned int cur_target;
+
+ struct spinand_ecc_info eccinfo;
+
+ u8 *cfg_cache;
+ u8 *databuf;
+ u8 *oobbuf;
+ u8 *scratchbuf;
+ const struct spinand_manufacturer *manufacturer;
+ void *priv;
+};
+
+/**
+ * mtd_to_spinand() - Get the SPI NAND device attached to an MTD instance
+ * @mtd: MTD instance
+ *
+ * Return: the SPI NAND device attached to @mtd.
+ */
+static inline struct spinand_device *mtd_to_spinand(struct mtd_info *mtd)
+{
+ return container_of(mtd_to_nanddev(mtd), struct spinand_device, base);
+}
+
+/**
+ * spinand_to_mtd() - Get the MTD device embedded in a SPI NAND device
+ * @spinand: SPI NAND device
+ *
+ * Return: the MTD device embedded in @spinand.
+ */
+static inline struct mtd_info *spinand_to_mtd(struct spinand_device *spinand)
+{
+ return nanddev_to_mtd(&spinand->base);
+}
+
+/**
+ * nand_to_spinand() - Get the SPI NAND device embedding an NAND object
+ * @nand: NAND object
+ *
+ * Return: the SPI NAND device embedding @nand.
+ */
+static inline struct spinand_device *nand_to_spinand(struct nand_device *nand)
+{
+ return container_of(nand, struct spinand_device, base);
+}
+
+/**
+ * spinand_to_nand() - Get the NAND device embedded in a SPI NAND object
+ * @spinand: SPI NAND device
+ *
+ * Return: the NAND device embedded in @spinand.
+ */
+static inline struct nand_device *
+spinand_to_nand(struct spinand_device *spinand)
+{
+ return &spinand->base;
+}
+
+/**
+ * spinand_set_of_node - Attach a DT node to a SPI NAND device
+ * @spinand: SPI NAND device
+ * @np: DT node
+ *
+ * Attach a DT node to a SPI NAND device.
+ */
+static inline void spinand_set_of_node(struct spinand_device *spinand,
+ struct device_node *np)
+{
+ nanddev_set_of_node(&spinand->base, np);
+}
+
+int spinand_match_and_init(struct spinand_device *dev,
+ const struct spinand_info *table,
+ unsigned int table_size, u8 devid);
+
+int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val);
+int spinand_select_target(struct spinand_device *spinand, unsigned int target);
+
+#endif /* __LINUX_MTD_SPINAND_H */
diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
index bb4bd15ae1f6..4fa34a227a0f 100644
--- a/include/linux/spi/spi-mem.h
+++ b/include/linux/spi/spi-mem.h
@@ -3,7 +3,9 @@
* Copyright (C) 2018 Exceet Electronics GmbH
* Copyright (C) 2018 Bootlin
*
- * Author: Boris Brezillon <boris.brezillon@bootlin.com>
+ * Author:
+ * Peter Pan <peterpandong@micron.com>
+ * Boris Brezillon <boris.brezillon@bootlin.com>
*/
#ifndef __LINUX_SPI_MEM_H
--
2.14.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related
* [PATCH v8 2/4] dt-bindings: Add bindings for SPI NAND devices
From: Boris Brezillon @ 2018-06-01 13:13 UTC (permalink / raw)
To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
Richard Weinberger, linux-mtd, Miquel Raynal, Mark Brown,
linux-spi, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
Kumar Gala, devicetree
In-Reply-To: <20180601131400.17634-1-boris.brezillon@bootlin.com>
Add bindings for SPI NAND chips.
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
---
Changes in v8:
- Fixed a typo in the commit message
---
Documentation/devicetree/bindings/mtd/spi-nand.txt | 27 ++++++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/spi-nand.txt
diff --git a/Documentation/devicetree/bindings/mtd/spi-nand.txt b/Documentation/devicetree/bindings/mtd/spi-nand.txt
new file mode 100644
index 000000000000..d55f80196c63
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/spi-nand.txt
@@ -0,0 +1,27 @@
+SPI NAND flash
+
+Required properties:
+- compatible: should be "spi-nand"
+- reg: should encode the chip-select line used to access the NAND chip
+
+Optional properties
+- spi-max-frequency: maximum frequency of the SPI bus the chip can operate at.
+ This should encode board limitations (i.e. max freq can't
+ be achieved due to crosstalk on IO lines).
+ When unspecified, the driver assumes the chip can run at
+ the max frequency defined in the spec (information
+ extracted chip detection time).
+- spi-tx-bus-width: The bus width (number of data wires) that is used for MOSI.
+ Only encodes the board constraints (i.e. when not all IO
+ signals are routed on the board). Device constraints are
+ extracted when detecting the chip, and controller
+ constraints are exposed by the SPI mem controller. If this
+ property is missing that means no constraint at the board
+ level.
+- spi-rx-bus-width: The bus width (number of data wires) that is used for MISO.
+ Only encodes the board constraints (i.e. when not all IO
+ signals are routed on the board). Device constraints are
+ extracted when detecting the chip, and controller
+ constraints are exposed by the SPI mem controller. If this
+ property is missing that means no constraint at the board
+ level.
--
2.14.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related
* [PATCH v8 3/4] mtd: spinand: Add initial support for Micron MT29F2G01ABAGD
From: Boris Brezillon @ 2018-06-01 13:13 UTC (permalink / raw)
To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
Richard Weinberger, linux-mtd, Miquel Raynal, Mark Brown,
linux-spi, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
Kumar Gala, devicetree
Cc: Peter Pan
In-Reply-To: <20180601131400.17634-1-boris.brezillon@bootlin.com>
From: Peter Pan <peterpandong@micron.com>
Add a basic driver for Micron SPI NANDs. Only one device is supported
right now, but the driver will be extended to support more devices
afterwards.
Signed-off-by: Peter Pan <peterpandong@micron.com>
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
---
Changes in v8:
- Changed the subject prefix
---
drivers/mtd/nand/spi/Makefile | 2 +-
drivers/mtd/nand/spi/core.c | 17 ++++++
drivers/mtd/nand/spi/micron.c | 133 ++++++++++++++++++++++++++++++++++++++++++
include/linux/mtd/spinand.h | 3 +
4 files changed, 154 insertions(+), 1 deletion(-)
create mode 100644 drivers/mtd/nand/spi/micron.c
diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile
index feb79a1d1b46..79a1f1e79221 100644
--- a/drivers/mtd/nand/spi/Makefile
+++ b/drivers/mtd/nand/spi/Makefile
@@ -1,2 +1,2 @@
-spinand-objs := core.o
+spinand-objs := core.o micron.o
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index a99ea352eb85..2194a7f9b550 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -751,8 +751,25 @@ static const struct nand_ops spinand_ops = {
.isbad = spinand_isbad,
};
+static const struct spinand_manufacturer *spinand_manufacturers[] = {
+ µn_spinand_manufacturer,
+};
+
static int spinand_manufacturer_detect(struct spinand_device *spinand)
{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(spinand_manufacturers); i++) {
+ ret = spinand_manufacturers[i]->ops->detect(spinand);
+ if (ret > 0) {
+ spinand->manufacturer = spinand_manufacturers[i];
+ return 0;
+ } else if (ret < 0) {
+ return ret;
+ }
+ }
+
return -ENOTSUPP;
}
diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c
new file mode 100644
index 000000000000..9c4381d6847b
--- /dev/null
+++ b/drivers/mtd/nand/spi/micron.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016-2017 Micron Technology, Inc.
+ *
+ * Authors:
+ * Peter Pan <peterpandong@micron.com>
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mtd/spinand.h>
+
+#define SPINAND_MFR_MICRON 0x2c
+
+#define MICRON_STATUS_ECC_MASK GENMASK(7, 4)
+#define MICRON_STATUS_ECC_NO_BITFLIPS (0 << 4)
+#define MICRON_STATUS_ECC_1TO3_BITFLIPS (1 << 4)
+#define MICRON_STATUS_ECC_4TO6_BITFLIPS (3 << 4)
+#define MICRON_STATUS_ECC_7TO8_BITFLIPS (5 << 4)
+
+static SPINAND_OP_VARIANTS(read_cache_variants,
+ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+
+static SPINAND_OP_VARIANTS(write_cache_variants,
+ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
+ SPINAND_PROG_LOAD(true, 0, NULL, 0));
+
+static SPINAND_OP_VARIANTS(update_cache_variants,
+ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
+ SPINAND_PROG_LOAD(false, 0, NULL, 0));
+
+static int mt29f2g01abagd_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ if (section)
+ return -ERANGE;
+
+ region->offset = 64;
+ region->length = 64;
+
+ return 0;
+}
+
+static int mt29f2g01abagd_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ if (section)
+ return -ERANGE;
+
+ /* Reserve 2 bytes for the BBM. */
+ region->offset = 2;
+ region->length = 62;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops mt29f2g01abagd_ooblayout = {
+ .ecc = mt29f2g01abagd_ooblayout_ecc,
+ .free = mt29f2g01abagd_ooblayout_free,
+};
+
+static int mt29f2g01abagd_ecc_get_status(struct spinand_device *spinand,
+ u8 status)
+{
+ switch (status & MICRON_STATUS_ECC_MASK) {
+ case STATUS_ECC_NO_BITFLIPS:
+ return 0;
+
+ case STATUS_ECC_UNCOR_ERROR:
+ return -EBADMSG;
+
+ case MICRON_STATUS_ECC_1TO3_BITFLIPS:
+ return 3;
+
+ case MICRON_STATUS_ECC_4TO6_BITFLIPS:
+ return 6;
+
+ case MICRON_STATUS_ECC_7TO8_BITFLIPS:
+ return 8;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct spinand_info micron_spinand_table[] = {
+ SPINAND_INFO("MT29F2G01ABAGD", 0x24,
+ NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants),
+ 0,
+ SPINAND_ECCINFO(&mt29f2g01abagd_ooblayout,
+ mt29f2g01abagd_ecc_get_status)),
+};
+
+static int micron_spinand_detect(struct spinand_device *spinand)
+{
+ u8 *id = spinand->id.data;
+ int ret;
+
+ /*
+ * Micron SPI NAND read ID need a dummy byte,
+ * so the first byte in raw_id is dummy.
+ */
+ if (id[1] != SPINAND_MFR_MICRON)
+ return 0;
+
+ ret = spinand_match_and_init(spinand, micron_spinand_table,
+ ARRAY_SIZE(micron_spinand_table), id[2]);
+ if (ret)
+ return ret;
+
+ return 1;
+}
+
+static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = {
+ .detect = micron_spinand_detect,
+};
+
+const struct spinand_manufacturer micron_spinand_manufacturer = {
+ .id = SPINAND_MFR_MICRON,
+ .name = "Micron",
+ .ops = µn_spinand_manuf_ops,
+};
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 17a4584c0017..11a18c1993e8 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -192,6 +192,9 @@ struct spinand_manufacturer {
const struct spinand_manufacturer_ops *ops;
};
+/* SPI NAND manufacturers */
+extern const struct spinand_manufacturer micron_spinand_manufacturer;
+
/**
* struct spinand_op_variants - SPI NAND operation variants
* @ops: the list of variants for a given operation
--
2.14.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related
* [PATCH v8 4/4] mtd: spinand: Add initial support for Winbond W25M02GV
From: Boris Brezillon @ 2018-06-01 13:14 UTC (permalink / raw)
To: David Woodhouse, Brian Norris, Boris Brezillon, Marek Vasut,
Richard Weinberger, linux-mtd, Miquel Raynal, Mark Brown,
linux-spi, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
Kumar Gala, devicetree
Cc: Frieder Schrempf
In-Reply-To: <20180601131400.17634-1-boris.brezillon@bootlin.com>
From: Frieder Schrempf <frieder.schrempf@exceet.de>
Add support for the W25M02GV chip.
Signed-off-by: Frieder Schrempf <frieder.schrempf@exceet.de>
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
---
Changes in v8:
- Add a commit message
- Changed the subject prefix
---
drivers/mtd/nand/spi/Makefile | 2 +-
drivers/mtd/nand/spi/core.c | 1 +
drivers/mtd/nand/spi/winbond.c | 141 +++++++++++++++++++++++++++++++++++++++++
include/linux/mtd/spinand.h | 1 +
4 files changed, 144 insertions(+), 1 deletion(-)
create mode 100644 drivers/mtd/nand/spi/winbond.c
diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile
index 79a1f1e79221..983a94f7f205 100644
--- a/drivers/mtd/nand/spi/Makefile
+++ b/drivers/mtd/nand/spi/Makefile
@@ -1,2 +1,2 @@
-spinand-objs := core.o micron.o
+spinand-objs := core.o micron.o winbond.o
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 2194a7f9b550..83fd039fe4d8 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -753,6 +753,7 @@ static const struct nand_ops spinand_ops = {
static const struct spinand_manufacturer *spinand_manufacturers[] = {
µn_spinand_manufacturer,
+ &winbond_spinand_manufacturer,
};
static int spinand_manufacturer_detect(struct spinand_device *spinand)
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
new file mode 100644
index 000000000000..67baa1b32c00
--- /dev/null
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2017 exceet electronics GmbH
+ *
+ * Authors:
+ * Frieder Schrempf <frieder.schrempf@exceet.de>
+ * Boris Brezillon <boris.brezillon@bootlin.com>
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mtd/spinand.h>
+
+#define SPINAND_MFR_WINBOND 0xEF
+
+#define WINBOND_CFG_BUF_READ BIT(3)
+
+static SPINAND_OP_VARIANTS(read_cache_variants,
+ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+
+static SPINAND_OP_VARIANTS(write_cache_variants,
+ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
+ SPINAND_PROG_LOAD(true, 0, NULL, 0));
+
+static SPINAND_OP_VARIANTS(update_cache_variants,
+ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
+ SPINAND_PROG_LOAD(false, 0, NULL, 0));
+
+static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ if (section > 3)
+ return -ERANGE;
+
+ region->offset = (16 * section) + 8;
+ region->length = 8;
+
+ return 0;
+}
+
+static int w25m02gv_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ if (section > 3)
+ return -ERANGE;
+
+ region->offset = (16 * section) + 2;
+ region->length = 6;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops w25m02gv_ooblayout = {
+ .ecc = w25m02gv_ooblayout_ecc,
+ .free = w25m02gv_ooblayout_free,
+};
+
+static int w25m02gv_select_target(struct spinand_device *spinand,
+ unsigned int target)
+{
+ struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_DATA_OUT(1,
+ spinand->scratchbuf,
+ 1));
+
+ *spinand->scratchbuf = target;
+ return spi_mem_exec_op(spinand->spimem, &op);
+}
+
+static const struct spinand_info winbond_spinand_table[] = {
+ SPINAND_INFO("W25M02GV", 0xAB,
+ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 2),
+ NAND_ECCREQ(1, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants),
+ 0,
+ SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
+ SPINAND_SELECT_TARGET(w25m02gv_select_target)),
+};
+
+/**
+ * winbond_spinand_detect - initialize device related part in spinand_device
+ * struct if it is a Winbond device.
+ * @spinand: SPI NAND device structure
+ */
+static int winbond_spinand_detect(struct spinand_device *spinand)
+{
+ u8 *id = spinand->id.data;
+ int ret;
+
+ /*
+ * Winbond SPI NAND read ID need a dummy byte,
+ * so the first byte in raw_id is dummy.
+ */
+ if (id[1] != SPINAND_MFR_WINBOND)
+ return 0;
+
+ ret = spinand_match_and_init(spinand, winbond_spinand_table,
+ ARRAY_SIZE(winbond_spinand_table), id[2]);
+ if (ret)
+ return ret;
+
+ return 1;
+}
+
+static int winbond_spinand_init(struct spinand_device *spinand)
+{
+ struct nand_device *nand = spinand_to_nand(spinand);
+ unsigned int i;
+
+ /*
+ * Make sure all dies are in buffer read mode and not continuous read
+ * mode.
+ */
+ for (i = 0; i < nand->memorg.ntargets; i++) {
+ spinand_select_target(spinand, i);
+ spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ,
+ WINBOND_CFG_BUF_READ);
+ }
+
+ return 0;
+}
+
+static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = {
+ .detect = winbond_spinand_detect,
+ .init = winbond_spinand_init,
+};
+
+const struct spinand_manufacturer winbond_spinand_manufacturer = {
+ .id = SPINAND_MFR_WINBOND,
+ .name = "Winbond",
+ .ops = &winbond_spinand_manuf_ops,
+};
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 11a18c1993e8..31d993628a0d 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -194,6 +194,7 @@ struct spinand_manufacturer {
/* SPI NAND manufacturers */
extern const struct spinand_manufacturer micron_spinand_manufacturer;
+extern const struct spinand_manufacturer winbond_spinand_manufacturer;
/**
* struct spinand_op_variants - SPI NAND operation variants
--
2.14.1
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related
* [RFC PATCH 0/8] coresight: Update device tree bindings
From: Suzuki K Poulose @ 2018-06-01 13:15 UTC (permalink / raw)
To: linux-arm-kernel
Cc: mathieu.poirier, sudeep.holla, robh, mark.rutland, frowand.list,
matt.sealey, charles.garcia-tobin, john.horley, mike.leach,
coresight, linux-kernel, devicetree, Suzuki K Poulose
Coresight uses DT graph bindings to describe the connections of the
components. However we have some undocumented usage of the bindings
to describe some of the properties of the connections.
The coresight driver needs to know the hardware ports invovled
in the connection and the direction of data flow to effectively
manage the trace sessions. So far we have relied on the "port"
address (as described by the generic graph bindings) to represent
the hardware port of the component for a connection.
The hardware uses separate numbering scheme for input and output
ports, which implies, we could have two different (input and output)
ports with the same port number. This could create problems in the
graph bindings where the label of the port wouldn't match the address.
e.g, with the existing bindings we get :
port@0{ // Output port 0
reg = <0>;
...
};
port@1{
reg = <0>; // Input port 0
endpoint {
slave-mode;
...
};
};
With the new enforcement in the DT rules, mismatches in label and address
are not allowed (as see in the case for port@1). So, we need a new mechanism
to describe the hardware port number reliably.
Also, we relied on an undocumented "slave-mode" property (see the above
example) to indicate if the port is an input port. Let us formalise and
switch to a new property to describe the direction of data flow.
There were three options considered for the hardware port number scheme:
1) Use natural ordering in the DT to infer the hardware port number.
i.e, Mandate that the all ports are listed in the DT and in the ascending
order for each class (input and output respectively).
Pros :
- We don't need new properties and if the existing DTS list them in
order (which most of them do), they work out of the box.
Cons :
- We must list all the ports even if the system cannot/shouldn't use
it.
- It is prone to human errors (if the order is not kept).
2) Use an explicit property to list both the direction and the hw port
number and direction. Define "coresight,hwid" as 2 member array of u32,
where the members are port number and the direction respectively.
e.g
port@0{
reg = <0>;
endpoint {
coresight,hwid = <0 1>; // Port # 0, Output
}
};
port@1{
reg = <1>;
endpoint {
coresight,hwid = <0 0>; // Port # 0, Input
};
};
Pros:
- The bindings are formal but not so reader friendly and could potentially
lead to human errors.
Cons:
- Backward compatiblity is lost.
3) Use explicit properties (implemented in the series) for the hardware
port id and direction. We define a new property "coresight,hwid" for
each endpoint in coresight devices to specify the hardware port number
explicitly. Also use a separate property "direction" to specify the
direction of the data flow.
e.g,
port@0{
reg = <0>;
endpoint {
direction = <1>; // Output
coresight,hwid = <0>; // Port # 0
}
};
port@1{
reg = <1>;
endpoint {
direction = <0>; // Input
coresight,hwid = <0>; // Port # 0
};
};
Pros:
- The bindings are formal and reader friendly, and less prone to errors.
Cons:
- Backward compatibility is lost.
This series achieves implements Option (3) listed above while still retaining
the backward compatibility. The driver now issues a warning (once) when it
encounters the old bindings.
It also cleans up the platform parsing code to reduce the memory usage by
reusing the platform description. The series also includes the
changes for Juno platform as an example. If there are no objections
to the approach, I could post the series, converting all the
in-kernel DTS to the new binding.
Suzuki K Poulose (8):
dts: binding: coresight: Document graph bindings
coresight: Fix remote endpoint parsing
coresight: Cleanup platform description data
coresight: platform: Cleanup coresight connection handling
coresight: Handle errors in finding input/output ports
dts: coresight: Clean up the device tree graph bindings
dts: coresight: Define new bindings for direction of data flow
dts: juno: Update coresight bindings for hw port
.../devicetree/bindings/arm/coresight.txt | 52 ++++++++--
arch/arm64/boot/dts/arm/juno-base.dtsi | 82 +++++++++++----
arch/arm64/boot/dts/arm/juno.dts | 5 +-
drivers/hwtracing/coresight/coresight.c | 28 ++----
drivers/hwtracing/coresight/of_coresight.c | 111 ++++++++++++---------
include/linux/coresight.h | 11 +-
6 files changed, 181 insertions(+), 108 deletions(-)
--
2.7.4
^ permalink raw reply
* [RFC PATCH 1/8] dts: binding: coresight: Document graph bindings
From: Suzuki K Poulose @ 2018-06-01 13:16 UTC (permalink / raw)
To: linux-arm-kernel
Cc: mathieu.poirier, sudeep.holla, robh, mark.rutland, frowand.list,
matt.sealey, charles.garcia-tobin, john.horley, mike.leach,
coresight, linux-kernel, devicetree, Suzuki K Poulose
In-Reply-To: <1527858967-16047-1-git-send-email-suzuki.poulose@arm.com>
Before we updat the bindings, document the current graph bindings
and usage of additional properties.
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
.../devicetree/bindings/arm/coresight.txt | 28 ++++++++++++++++++----
1 file changed, 24 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt
index 15ac8e8..bd36e40 100644
--- a/Documentation/devicetree/bindings/arm/coresight.txt
+++ b/Documentation/devicetree/bindings/arm/coresight.txt
@@ -52,9 +52,7 @@ its hardware characteristcs.
clocks the core of that coresight component. The latter clock
is optional.
- * port or ports: The representation of the component's port
- layout using the generic DT graph presentation found in
- "bindings/graph.txt".
+ * port or ports: see "Graph bindings for Coresight" below
* Additional required properties for System Trace Macrocells (STM):
* reg: along with the physical base address and length of the register
@@ -71,7 +69,7 @@ its hardware characteristcs.
AMBA markee):
- "arm,coresight-replicator"
- * port or ports: same as above.
+ * port or ports: see "Graph bindings for Coresight" below.
* Optional properties for ETM/PTMs:
@@ -86,6 +84,28 @@ its hardware characteristcs.
* arm,buffer-size: size of contiguous buffer space for TMC ETR
(embedded trace router)
+Graph bindings for Coresight
+-------------------------------
+
+Coresight components are interconnected to create a data path for the flow of
+trace data generated from the "sources" to their collection points "sink". Each
+coresight component must describe the "input" and "output" connections.
+The connections must be described via generic DT graph bindings as described
+by the "bindings/graph.txt", where each "port" along with an "endpoint" component
+represents a hardware port and the connection.
+
+Since it is possible to have multiple connections for any coresight component with
+a specific direction of data flow, each connection must define the following
+properties to uniquely identify the connection details.
+
+ * Direction of the data flow w.r.t the component :
+ Each input port must have the following property defined at the "endpoint"
+ for the port.
+ "slave-mode"
+
+ * Hardware Port number at the component:
+ - The hardware port number is assumed to be the address of the "port" component.
+
Example:
--
2.7.4
^ permalink raw reply related
* [RFC PATCH 2/8] coresight: Fix remote endpoint parsing
From: Suzuki K Poulose @ 2018-06-01 13:16 UTC (permalink / raw)
To: linux-arm-kernel
Cc: mathieu.poirier, sudeep.holla, robh, mark.rutland, frowand.list,
matt.sealey, charles.garcia-tobin, john.horley, mike.leach,
coresight, linux-kernel, devicetree, Suzuki K Poulose
In-Reply-To: <1527858967-16047-1-git-send-email-suzuki.poulose@arm.com>
When parsing the remote endpoint of an output port, we do :
rport = of_graph_get_remote_port(ep);
rparent = of_graph_get_remote_port_parent(ep);
and then parse the "remote_port" as if it was the remote endpoint,
which is wrong. The code worked fine because we used endpoint number
as the port number. Let us fix it and optimise a bit as:
remote_ep = of_graph_get_remote_endpoint(ep);
if (remote_ep)
remote_parent = of_graph_get_port_parent(remote_ep);
and then, parse the remote_ep for the port/endpoint details.
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
drivers/hwtracing/coresight/of_coresight.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c
index 7c37544..e0deab0 100644
--- a/drivers/hwtracing/coresight/of_coresight.c
+++ b/drivers/hwtracing/coresight/of_coresight.c
@@ -128,7 +128,7 @@ of_get_coresight_platform_data(struct device *dev,
struct device *rdev;
struct device_node *ep = NULL;
struct device_node *rparent = NULL;
- struct device_node *rport = NULL;
+ struct device_node *rep = NULL;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@@ -169,16 +169,17 @@ of_get_coresight_platform_data(struct device *dev,
pdata->outports[i] = endpoint.port;
/*
- * Get a handle on the remote port and parent
- * attached to it.
+ * Get a handle on the remote endpoint and the device
+ * it is attached to.
*/
- rparent = of_graph_get_remote_port_parent(ep);
- rport = of_graph_get_remote_port(ep);
-
- if (!rparent || !rport)
+ rep = of_graph_get_remote_endpoint(ep);
+ if (!rep)
+ continue;
+ rparent = of_graph_get_port_parent(rep);
+ if (!rparent)
continue;
- if (of_graph_parse_endpoint(rport, &rendpoint))
+ if (of_graph_parse_endpoint(rep, &rendpoint))
continue;
rdev = of_coresight_get_endpoint_device(rparent);
@@ -186,7 +187,7 @@ of_get_coresight_platform_data(struct device *dev,
return ERR_PTR(-EPROBE_DEFER);
pdata->child_names[i] = dev_name(rdev);
- pdata->child_ports[i] = rendpoint.id;
+ pdata->child_ports[i] = rendpoint.port;
i++;
} while (ep);
--
2.7.4
^ permalink raw reply related
* [RFC PATCH 3/8] coresight: Cleanup platform description data
From: Suzuki K Poulose @ 2018-06-01 13:16 UTC (permalink / raw)
To: linux-arm-kernel
Cc: mathieu.poirier, sudeep.holla, robh, mark.rutland, frowand.list,
matt.sealey, charles.garcia-tobin, john.horley, mike.leach,
coresight, linux-kernel, devicetree, Suzuki K Poulose
In-Reply-To: <1527858967-16047-1-git-send-email-suzuki.poulose@arm.com>
Nobody uses the "clk" field in struct coresight_platform_data.
Remove it.
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
include/linux/coresight.h | 2 --
1 file changed, 2 deletions(-)
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index d950dad..32aaa1c 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -94,7 +94,6 @@ struct coresight_dev_subtype {
* @child_ports:child component port number the current component is
connected to.
* @nr_outport: number of output ports for this component.
- * @clk: The clock this component is associated to.
*/
struct coresight_platform_data {
int cpu;
@@ -104,7 +103,6 @@ struct coresight_platform_data {
const char **child_names;
int *child_ports;
int nr_outport;
- struct clk *clk;
};
/**
--
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