* [PATCH v2 1/2] mtd: spi-nor: allow the platform to supply write protection state
2026-08-31 12:05 [PATCH v2 0/2] Report platform enforced SPI flash write protection Tobias Jakobsen via B4 Relay
@ 2026-08-31 12:05 ` Tobias Jakobsen via B4 Relay
2026-08-31 12:36 ` Michael Walle
2026-08-31 12:05 ` [PATCH v2 2/2] spi: spi-intel: report controller enforced write protection Tobias Jakobsen via B4 Relay
2026-08-31 12:37 ` [PATCH v2 0/2] Report platform enforced SPI flash " Michael Walle
2 siblings, 1 reply; 11+ messages in thread
From: Tobias Jakobsen via B4 Relay @ 2026-08-31 12:05 UTC (permalink / raw)
To: Pratyush Yadav, Michael Walle, Takahiro Kuwano, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Mark Brown
Cc: Mika Westerberg, linux-mtd, linux-kernel, linux-spi,
Tobias Jakobsen
From: Tobias Jakobsen <tjakobsen84@protonmail.com>
Some flashes are write protected by the platform they are attached to
rather than by their own block protection bits. An Intel PCH SPI
controller programmed with protected range registers is one example: it
refuses writes to a range regardless of what the chip's status register
says, while the chip's block protection bits are typically left clear.
MEMISLOCKED therefore either fails with -EOPNOTSUPP, or, once the chip
gains SPI_NOR_HAS_LOCK, reports a range as unlocked while writes to it
are in fact being refused.
Let the platform supply an optional is_locked() callback in struct
flash_platform_data, alongside the partitions it can already supply, and
prefer it over the chip's own block protection bits. This is independent
of SPI_NOR_HAS_LOCK, so an answer is also given for chips that have no
block protection support of their own.
lock() and unlock() return -EOPNOTSUPP, as platform enforced protection
is not expected to be changed at runtime.
Platforms that do not supply the callback are unaffected.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=221927
Assisted-by: LLM
Signed-off-by: Tobias Jakobsen <tjakobsen84@protonmail.com>
---
drivers/mtd/spi-nor/core.c | 52 ++++++++++++++++++++++++++++++++++++++++++++--
include/linux/spi/flash.h | 12 +++++++++++
2 files changed, 62 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index ccf4396cd..a5c37eea4 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -3001,6 +3001,50 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor)
nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE;
}
+static int spi_nor_platform_lock(struct spi_nor *nor, loff_t ofs, u64 len)
+{
+ return -EOPNOTSUPP;
+}
+
+static int spi_nor_platform_unlock(struct spi_nor *nor, loff_t ofs, u64 len)
+{
+ return -EOPNOTSUPP;
+}
+
+static int spi_nor_platform_is_locked(struct spi_nor *nor, loff_t ofs, u64 len)
+{
+ struct flash_platform_data *data = dev_get_platdata(nor->dev);
+
+ return data->is_locked(nor->spimem->spi, ofs, len);
+}
+
+static const struct spi_nor_locking_ops spi_nor_platform_locking_ops = {
+ .lock = spi_nor_platform_lock,
+ .unlock = spi_nor_platform_unlock,
+ .is_locked = spi_nor_platform_is_locked,
+};
+
+/**
+ * spi_nor_init_platform_locking_ops() - Use the platform supplied write
+ * protection query, if there is one.
+ * @nor: pointer to a 'struct spi_nor'
+ *
+ * Some flashes are write protected by the platform they are attached to rather
+ * than by their own block protection bits, for example by an Intel PCH SPI
+ * controller programmed with protected range registers. In that case the chip's
+ * block protection bits are typically left clear and say nothing about what is
+ * actually enforced, so prefer the platform supplied query when available.
+ */
+static void spi_nor_init_platform_locking_ops(struct spi_nor *nor)
+{
+ struct flash_platform_data *data = dev_get_platdata(nor->dev);
+
+ if (!data || !data->is_locked || !nor->spimem)
+ return;
+
+ nor->params->locking_ops = &spi_nor_platform_locking_ops;
+}
+
/**
* spi_nor_late_init_params() - Late initialization of default flash parameters.
* @nor: pointer to a 'struct spi_nor'
@@ -3040,9 +3084,13 @@ static int spi_nor_late_init_params(struct spi_nor *nor)
spi_nor_init_fixup_flags(nor);
/*
- * NOR protection support. When locking_ops are not provided, we pick
- * the default ones.
+ * NOR protection support. Platform enforced protection is preferred
+ * over the chip's own, as the chip is not necessarily aware of it.
+ * When locking_ops are not provided, we pick the default ones.
*/
+ if (!nor->params->locking_ops)
+ spi_nor_init_platform_locking_ops(nor);
+
if (nor->flags & SNOR_F_HAS_LOCK && !nor->params->locking_ops)
spi_nor_init_default_locking_ops(nor);
diff --git a/include/linux/spi/flash.h b/include/linux/spi/flash.h
index 2401a0887..f415e2c0b 100644
--- a/include/linux/spi/flash.h
+++ b/include/linux/spi/flash.h
@@ -2,7 +2,10 @@
#ifndef LINUX_SPI_FLASH_H
#define LINUX_SPI_FLASH_H
+#include <linux/types.h>
+
struct mtd_partition;
+struct spi_device;
/**
* struct flash_platform_data: board-specific flash data
@@ -11,6 +14,13 @@ struct mtd_partition;
* @nr_parts: number of mtd_partitions for static partitioning
* @type: optional flash device type (e.g. m25p80 vs m25p64), for use
* with chips that can't be queried for JEDEC or other IDs
+ * @is_locked: optional callback to query write protection enforced by the
+ * platform rather than by the flash chip itself, for example a SPI
+ * controller that gates writes to a range of the flash. Returns 1 if
+ * the whole range is protected, 0 if it is not, or a negative errno.
+ * When supplied it takes precedence over the chip's own block
+ * protection bits, which do not necessarily reflect what is actually
+ * being enforced.
*
* Board init code (in arch/.../mach-xxx/board-yyy.c files) can
* provide information about SPI flash parts (such as DataFlash) to
@@ -26,6 +36,8 @@ struct flash_platform_data {
char *type;
+ int (*is_locked)(struct spi_device *spi, loff_t ofs, u64 len);
+
/* we'll likely add more ... use JEDEC IDs, etc */
};
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v2 1/2] mtd: spi-nor: allow the platform to supply write protection state
2026-08-31 12:05 ` [PATCH v2 1/2] mtd: spi-nor: allow the platform to supply write protection state Tobias Jakobsen via B4 Relay
@ 2026-08-31 12:36 ` Michael Walle
2026-09-01 8:32 ` Tobias Jakobsen
0 siblings, 1 reply; 11+ messages in thread
From: Michael Walle @ 2026-08-31 12:36 UTC (permalink / raw)
To: tjakobsen84, Pratyush Yadav, Takahiro Kuwano, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Mark Brown
Cc: Mika Westerberg, linux-mtd, linux-kernel, linux-spi
[-- Attachment #1: Type: text/plain, Size: 5969 bytes --]
Hi,
On Mon Aug 31, 2026 at 2:05 PM CEST, Tobias Jakobsen via B4 Relay wrote:
> From: Tobias Jakobsen <tjakobsen84@protonmail.com>
>
> Some flashes are write protected by the platform they are attached to
> rather than by their own block protection bits. An Intel PCH SPI
> controller programmed with protected range registers is one example: it
> refuses writes to a range regardless of what the chip's status register
> says, while the chip's block protection bits are typically left clear.
>
> MEMISLOCKED therefore either fails with -EOPNOTSUPP, or, once the chip
> gains SPI_NOR_HAS_LOCK, reports a range as unlocked while writes to it
> are in fact being refused.
>
> Let the platform supply an optional is_locked() callback in struct
> flash_platform_data, alongside the partitions it can already supply, and
> prefer it over the chip's own block protection bits. This is independent
> of SPI_NOR_HAS_LOCK, so an answer is also given for chips that have no
> block protection support of their own.
>
> lock() and unlock() return -EOPNOTSUPP, as platform enforced protection
> is not expected to be changed at runtime.
>
> Platforms that do not supply the callback are unaffected.
>
> Link: https://bugzilla.kernel.org/show_bug.cgi?id=221927
> Assisted-by: LLM
> Signed-off-by: Tobias Jakobsen <tjakobsen84@protonmail.com>
> ---
> drivers/mtd/spi-nor/core.c | 52 ++++++++++++++++++++++++++++++++++++++++++++--
> include/linux/spi/flash.h | 12 +++++++++++
> 2 files changed, 62 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index ccf4396cd..a5c37eea4 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -3001,6 +3001,50 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor)
> nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE;
> }
>
> +static int spi_nor_platform_lock(struct spi_nor *nor, loff_t ofs, u64 len)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +static int spi_nor_platform_unlock(struct spi_nor *nor, loff_t ofs, u64 len)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +static int spi_nor_platform_is_locked(struct spi_nor *nor, loff_t ofs, u64 len)
> +{
> + struct flash_platform_data *data = dev_get_platdata(nor->dev);
> +
> + return data->is_locked(nor->spimem->spi, ofs, len);
> +}
> +
> +static const struct spi_nor_locking_ops spi_nor_platform_locking_ops = {
> + .lock = spi_nor_platform_lock,
> + .unlock = spi_nor_platform_unlock,
> + .is_locked = spi_nor_platform_is_locked,
> +};
If we can't unlock the flash, what's it's use then? Can't we just
clear the HAS_LOCK if there is an intel-spi driver?
> +
> +/**
> + * spi_nor_init_platform_locking_ops() - Use the platform supplied write
> + * protection query, if there is one.
> + * @nor: pointer to a 'struct spi_nor'
> + *
> + * Some flashes are write protected by the platform they are attached to rather
> + * than by their own block protection bits, for example by an Intel PCH SPI
> + * controller programmed with protected range registers. In that case the chip's
> + * block protection bits are typically left clear and say nothing about what is
> + * actually enforced, so prefer the platform supplied query when available.
> + */
> +static void spi_nor_init_platform_locking_ops(struct spi_nor *nor)
> +{
> + struct flash_platform_data *data = dev_get_platdata(nor->dev);
> +
> + if (!data || !data->is_locked || !nor->spimem)
> + return;
> +
> + nor->params->locking_ops = &spi_nor_platform_locking_ops;
> +}
> +
> /**
> * spi_nor_late_init_params() - Late initialization of default flash parameters.
> * @nor: pointer to a 'struct spi_nor'
> @@ -3040,9 +3084,13 @@ static int spi_nor_late_init_params(struct spi_nor *nor)
> spi_nor_init_fixup_flags(nor);
>
> /*
> - * NOR protection support. When locking_ops are not provided, we pick
> - * the default ones.
> + * NOR protection support. Platform enforced protection is preferred
> + * over the chip's own, as the chip is not necessarily aware of it.
> + * When locking_ops are not provided, we pick the default ones.
> */
This doesn't work, does it? What if a flash already provide locking
ops?
-michael
> + if (!nor->params->locking_ops)
> + spi_nor_init_platform_locking_ops(nor);
> +
> if (nor->flags & SNOR_F_HAS_LOCK && !nor->params->locking_ops)
> spi_nor_init_default_locking_ops(nor);
>
> diff --git a/include/linux/spi/flash.h b/include/linux/spi/flash.h
> index 2401a0887..f415e2c0b 100644
> --- a/include/linux/spi/flash.h
> +++ b/include/linux/spi/flash.h
> @@ -2,7 +2,10 @@
> #ifndef LINUX_SPI_FLASH_H
> #define LINUX_SPI_FLASH_H
>
> +#include <linux/types.h>
> +
> struct mtd_partition;
> +struct spi_device;
>
> /**
> * struct flash_platform_data: board-specific flash data
> @@ -11,6 +14,13 @@ struct mtd_partition;
> * @nr_parts: number of mtd_partitions for static partitioning
> * @type: optional flash device type (e.g. m25p80 vs m25p64), for use
> * with chips that can't be queried for JEDEC or other IDs
> + * @is_locked: optional callback to query write protection enforced by the
> + * platform rather than by the flash chip itself, for example a SPI
> + * controller that gates writes to a range of the flash. Returns 1 if
> + * the whole range is protected, 0 if it is not, or a negative errno.
> + * When supplied it takes precedence over the chip's own block
> + * protection bits, which do not necessarily reflect what is actually
> + * being enforced.
> *
> * Board init code (in arch/.../mach-xxx/board-yyy.c files) can
> * provide information about SPI flash parts (such as DataFlash) to
> @@ -26,6 +36,8 @@ struct flash_platform_data {
>
> char *type;
>
> + int (*is_locked)(struct spi_device *spi, loff_t ofs, u64 len);
> +
> /* we'll likely add more ... use JEDEC IDs, etc */
> };
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 297 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v2 1/2] mtd: spi-nor: allow the platform to supply write protection state
2026-08-31 12:36 ` Michael Walle
@ 2026-09-01 8:32 ` Tobias Jakobsen
2026-09-01 8:43 ` Michael Walle
0 siblings, 1 reply; 11+ messages in thread
From: Tobias Jakobsen @ 2026-09-01 8:32 UTC (permalink / raw)
To: Michael Walle
Cc: Pratyush Yadav, Takahiro Kuwano, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Mark Brown,
Mika Westerberg, linux-mtd, linux-kernel, linux-spi
Hello!
On Monday, 31 August 2026 at 14:37, Michael Walle <mwalle@kernel.org> wrote:
> > +static int spi_nor_platform_is_locked(struct spi_nor *nor, loff_t ofs, u64 len)
> > +{
> > + struct flash_platform_data *data = dev_get_platdata(nor->dev);
> > +
> > + return data->is_locked(nor->spimem->spi, ofs, len);
> > +}
> > +
> > +static const struct spi_nor_locking_ops spi_nor_platform_locking_ops = {
> > + .lock = spi_nor_platform_lock,
> > + .unlock = spi_nor_platform_unlock,
> > + .is_locked = spi_nor_platform_is_locked,
> > +};
>
> If we can't unlock the flash, what's it's use then? Can't we just
> clear the HAS_LOCK if there is an intel-spi driver?
Clearing HAS_LOCK would replace a wrong answer with no answer
(-EOPNOTSUPP). That is an improvement, but it discards information the
kernel already has, since spi-intel reads the protected range registers
at probe anyway for the MTD_WRITEABLE masking in
intel_spi_fill_partition(). Userspace is then left parsing the Intel
specific sysfs attributes to find out, which is the platform specific
special casing this series is trying to remove the need for.
If I understood correctly MEMISLOCKED is a query rather than a control.
"check if chip is locked" with no qualifier restricting it to the
chip's own block protection bits. On PCH protected machines the answer it
gives is wrong: the region is protected and it reports otherwise.
spi-nor cannot tell which controller it sits behind,
so suppressing HAS_LOCK needs the same channel through
flash_platform_data; only the payload changes.
The reason it is a callback rather than a flag is that the answer is per
range. On the machine I tested, PR0 covers 0x860000-0xffffff of a 16M
chip:
query 0x860000 + 0x7a0000 -> locked
query 0x0 + 0x1000000 -> not locked
query 0x0 + 0x10000 -> not locked
A boolean would have to claim the whole device is locked, which is wrong
for everything below 0x860000.
That said, if you would rather have the simpler suppression, I am happy
to do that instead.
> > /*
> > - * NOR protection support. When locking_ops are not provided, we pick
> > - * the default ones.
> > + * NOR protection support. Platform enforced protection is preferred
> > + * over the chip's own, as the chip is not necessarily aware of it.
> > + * When locking_ops are not provided, we pick the default ones.
> > */
>
> This doesn't work, does it? What if a flash already provide locking
> ops?
You are right, it does not. The vendor late_init hooks run before this
(core.c 3063 and 3072), so atmel.c and sst.c have already set
params->locking_ops by then and the platform query is skipped. A chip
with its own locking ops behind an Intel PCH would still report the
chip's answer.
I will fix that in v3 by having the platform ops take precedence
unconditionally rather than only filling in when nothing else has.
Let me know how you'd like to proceed regarding HAS_LOCK clearing.
Regards,
Tobias.
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v2 1/2] mtd: spi-nor: allow the platform to supply write protection state
2026-09-01 8:32 ` Tobias Jakobsen
@ 2026-09-01 8:43 ` Michael Walle
2026-09-01 8:52 ` Tobias Jakobsen
0 siblings, 1 reply; 11+ messages in thread
From: Michael Walle @ 2026-09-01 8:43 UTC (permalink / raw)
To: Tobias Jakobsen
Cc: Pratyush Yadav, Takahiro Kuwano, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Mark Brown,
Mika Westerberg, linux-mtd, linux-kernel, linux-spi
On Tue Sep 1, 2026 at 10:32 AM CEST, Tobias Jakobsen wrote:
>
> Hello!
>
> On Monday, 31 August 2026 at 14:37, Michael Walle <mwalle@kernel.org> wrote:
>> > +static int spi_nor_platform_is_locked(struct spi_nor *nor, loff_t ofs, u64 len)
>> > +{
>> > + struct flash_platform_data *data = dev_get_platdata(nor->dev);
>> > +
>> > + return data->is_locked(nor->spimem->spi, ofs, len);
>> > +}
>> > +
>> > +static const struct spi_nor_locking_ops spi_nor_platform_locking_ops = {
>> > + .lock = spi_nor_platform_lock,
>> > + .unlock = spi_nor_platform_unlock,
>> > + .is_locked = spi_nor_platform_is_locked,
>> > +};
>>
>> If we can't unlock the flash, what's it's use then? Can't we just
>> clear the HAS_LOCK if there is an intel-spi driver?
>
> Clearing HAS_LOCK would replace a wrong answer with no answer
> (-EOPNOTSUPP). That is an improvement, but it discards information the
> kernel already has, since spi-intel reads the protected range registers
> at probe anyway for the MTD_WRITEABLE masking in
> intel_spi_fill_partition(). Userspace is then left parsing the Intel
> specific sysfs attributes to find out, which is the platform specific
> special casing this series is trying to remove the need for.
>
> If I understood correctly MEMISLOCKED is a query rather than a control.
> "check if chip is locked" with no qualifier restricting it to the
> chip's own block protection bits. On PCH protected machines the answer it
> gives is wrong: the region is protected and it reports otherwise.
This feels like I'm talking with an AI agent. Honestly, this is
rather discouraging.
So my short answer: I don't want to clutter the code just for some
weird behavior and my point stands: whats the use, if it's not
possible to unprotect that region. The intel-spi controller is
rather restrictive anyway.
-michael
> spi-nor cannot tell which controller it sits behind,
> so suppressing HAS_LOCK needs the same channel through
> flash_platform_data; only the payload changes.
>
> The reason it is a callback rather than a flag is that the answer is per
> range. On the machine I tested, PR0 covers 0x860000-0xffffff of a 16M
> chip:
>
> query 0x860000 + 0x7a0000 -> locked
> query 0x0 + 0x1000000 -> not locked
> query 0x0 + 0x10000 -> not locked
>
> A boolean would have to claim the whole device is locked, which is wrong
> for everything below 0x860000.
>
> That said, if you would rather have the simpler suppression, I am happy
> to do that instead.
>
>> > /*
>> > - * NOR protection support. When locking_ops are not provided, we pick
>> > - * the default ones.
>> > + * NOR protection support. Platform enforced protection is preferred
>> > + * over the chip's own, as the chip is not necessarily aware of it.
>> > + * When locking_ops are not provided, we pick the default ones.
>> > */
>>
>> This doesn't work, does it? What if a flash already provide locking
>> ops?
>
> You are right, it does not. The vendor late_init hooks run before this
> (core.c 3063 and 3072), so atmel.c and sst.c have already set
> params->locking_ops by then and the platform query is skipped. A chip
> with its own locking ops behind an Intel PCH would still report the
> chip's answer.
>
> I will fix that in v3 by having the platform ops take precedence
> unconditionally rather than only filling in when nothing else has.
> Let me know how you'd like to proceed regarding HAS_LOCK clearing.
>
> Regards,
> Tobias.
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v2 1/2] mtd: spi-nor: allow the platform to supply write protection state
2026-09-01 8:43 ` Michael Walle
@ 2026-09-01 8:52 ` Tobias Jakobsen
0 siblings, 0 replies; 11+ messages in thread
From: Tobias Jakobsen @ 2026-09-01 8:52 UTC (permalink / raw)
To: Michael Walle
Cc: Pratyush Yadav, Takahiro Kuwano, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Mark Brown,
Mika Westerberg, linux-mtd, linux-kernel, linux-spi
On Tuesday, 1 September 2026 at 10:43, Michael Walle <mwalle@kernel.org> wrote:
> This feels like I'm talking with an AI agent. Honestly, this is
> rather discouraging.
>
> So my short answer: I don't want to clutter the code just for some
> weird behavior and my point stands: whats the use, if it's not
> possible to unprotect that region. The intel-spi controller is
> rather restrictive anyway.
Already disclosed LLM was used in translating and research/execution. But point taken. I will bow out.
I never intended to write a patch but was asked.
My intention was to merely report a bug.
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 2/2] spi: spi-intel: report controller enforced write protection
2026-08-31 12:05 [PATCH v2 0/2] Report platform enforced SPI flash write protection Tobias Jakobsen via B4 Relay
2026-08-31 12:05 ` [PATCH v2 1/2] mtd: spi-nor: allow the platform to supply write protection state Tobias Jakobsen via B4 Relay
@ 2026-08-31 12:05 ` Tobias Jakobsen via B4 Relay
2026-08-31 12:37 ` [PATCH v2 0/2] Report platform enforced SPI flash " Michael Walle
2 siblings, 0 replies; 11+ messages in thread
From: Tobias Jakobsen via B4 Relay @ 2026-08-31 12:05 UTC (permalink / raw)
To: Pratyush Yadav, Michael Walle, Takahiro Kuwano, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Mark Brown
Cc: Mika Westerberg, linux-mtd, linux-kernel, linux-spi,
Tobias Jakobsen
From: Tobias Jakobsen <tjakobsen84@protonmail.com>
On Intel PCH platforms write protection is enforced by the controller's
protected range registers, not by the flash chip's block protection
bits, which are typically left clear. The SPI MEM conversion left
spi-intel without visibility of the MTD device, so it cannot supply MTD
locking operations directly.
Pass a write protection query through struct flash_platform_data, which
spi-intel already uses to hand the partition layout to spi-nor.
Note this asks the opposite question to intel_spi_is_protected(): rather
than whether a flash region contains a protected range, it asks whether
the queried range is itself entirely covered by one, which is what the
MTD layer means by locked.
Tested on a Coffee Lake i5 with PR0 covering 0x860000-0xffffff and
FLOCKDN set, querying MEMISLOCKED over four ranges:
PR0 range whole chip in PR0 below PR0
unpatched -95 -95 -95 -95
chip lock flags only 0 0 0 0
patched, no chip lock flags 1 0 1 0
patched + chip lock flags 1 0 1 0
The whole chip and below-PR0 columns stay unlocked because only
0x860000-0xffffff is covered by a protected range.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=221927
Assisted-by: LLM
Signed-off-by: Tobias Jakobsen <tjakobsen84@protonmail.com>
---
drivers/spi/spi-intel.c | 68 ++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 62 insertions(+), 6 deletions(-)
diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c
index 7494b921a..63639f742 100644
--- a/drivers/spi/spi-intel.c
+++ b/drivers/spi/spi-intel.c
@@ -1201,21 +1201,36 @@ static int intel_spi_init(struct intel_spi *ispi)
return 0;
}
+/*
+ * Read protected range register @idx and decode it, provided any of the
+ * protection bits in @mask are set.
+ */
+static bool intel_spi_pr_range(const struct intel_spi *ispi, int idx, u32 mask,
+ unsigned int *base, unsigned int *limit)
+{
+ u32 pr_value = readl(ispi->pregs + PR(idx));
+
+ if (!(pr_value & mask))
+ return false;
+
+ *limit = (pr_value & PR_LIMIT_MASK) >> PR_LIMIT_SHIFT;
+ *base = pr_value & PR_BASE_MASK;
+
+ return true;
+}
+
static bool intel_spi_is_protected(const struct intel_spi *ispi,
unsigned int base, unsigned int limit)
{
int i;
for (i = 0; i < ispi->pr_num; i++) {
- u32 pr_base, pr_limit, pr_value;
+ unsigned int pr_base, pr_limit;
- pr_value = readl(ispi->pregs + PR(i));
- if (!(pr_value & (PR_WPE | PR_RPE)))
+ if (!intel_spi_pr_range(ispi, i, PR_WPE | PR_RPE, &pr_base,
+ &pr_limit))
continue;
- pr_limit = (pr_value & PR_LIMIT_MASK) >> PR_LIMIT_SHIFT;
- pr_base = pr_value & PR_BASE_MASK;
-
if (pr_base >= base && pr_limit <= limit)
return true;
}
@@ -1223,6 +1238,41 @@ static bool intel_spi_is_protected(const struct intel_spi *ispi,
return false;
}
+/*
+ * Unlike intel_spi_is_protected(), which asks whether a flash region contains
+ * any protected range, this asks the opposite: whether the given range is
+ * itself entirely covered by a write protected range. That is what the MTD
+ * layer means by "locked".
+ */
+static bool intel_spi_is_range_protected(const struct intel_spi *ispi,
+ unsigned int base, unsigned int limit)
+{
+ int i;
+
+ for (i = 0; i < ispi->pr_num; i++) {
+ unsigned int pr_base, pr_limit;
+
+ if (!intel_spi_pr_range(ispi, i, PR_WPE, &pr_base, &pr_limit))
+ continue;
+
+ if (base >= pr_base && limit <= pr_limit)
+ return true;
+ }
+
+ return false;
+}
+
+static int intel_spi_is_locked(struct spi_device *spi, loff_t ofs, u64 len)
+{
+ struct intel_spi *ispi = spi_controller_get_devdata(spi->controller);
+
+ if (!len)
+ return 0;
+
+ return intel_spi_is_range_protected(ispi, ofs >> 12,
+ (ofs + len - 1) >> 12);
+}
+
/*
* There will be a single partition holding all enabled flash regions. We
* call this "BIOS".
@@ -1395,6 +1445,12 @@ static int intel_spi_populate_chip(struct intel_spi *ispi)
intel_spi_fill_partition(ispi, pdata->parts);
+ /*
+ * The protected range registers address the first chip, so only it can
+ * be queried this way.
+ */
+ pdata->is_locked = intel_spi_is_locked;
+
memset(&chip, 0, sizeof(chip));
snprintf(chip.modalias, 8, "spi-nor");
chip.platform_data = pdata;
--
2.53.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v2 0/2] Report platform enforced SPI flash write protection
2026-08-31 12:05 [PATCH v2 0/2] Report platform enforced SPI flash write protection Tobias Jakobsen via B4 Relay
2026-08-31 12:05 ` [PATCH v2 1/2] mtd: spi-nor: allow the platform to supply write protection state Tobias Jakobsen via B4 Relay
2026-08-31 12:05 ` [PATCH v2 2/2] spi: spi-intel: report controller enforced write protection Tobias Jakobsen via B4 Relay
@ 2026-08-31 12:37 ` Michael Walle
2026-08-31 12:55 ` Mika Westerberg
2 siblings, 1 reply; 11+ messages in thread
From: Michael Walle @ 2026-08-31 12:37 UTC (permalink / raw)
To: tjakobsen84, Pratyush Yadav, Takahiro Kuwano, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Mark Brown
Cc: Mika Westerberg, linux-mtd, linux-kernel, linux-spi
[-- Attachment #1: Type: text/plain, Size: 331 bytes --]
Hi,
On Mon Aug 31, 2026 at 2:05 PM CEST, Tobias Jakobsen via B4 Relay wrote:
> Patch 2 adds the only user, spi-intel, which already computes protected
> range state at probe but has had no way to expose it since the SPI MEM
> conversion removed its visibility of the MTD device.
Where was it removed, though?
-michael
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 297 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 0/2] Report platform enforced SPI flash write protection
2026-08-31 12:37 ` [PATCH v2 0/2] Report platform enforced SPI flash " Michael Walle
@ 2026-08-31 12:55 ` Mika Westerberg
2026-08-31 13:00 ` Michael Walle
0 siblings, 1 reply; 11+ messages in thread
From: Mika Westerberg @ 2026-08-31 12:55 UTC (permalink / raw)
To: Michael Walle
Cc: tjakobsen84, Pratyush Yadav, Takahiro Kuwano, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Mark Brown, linux-mtd,
linux-kernel, linux-spi
Hi,
On Mon, Aug 31, 2026 at 02:37:59PM +0200, Michael Walle wrote:
> Hi,
>
> On Mon Aug 31, 2026 at 2:05 PM CEST, Tobias Jakobsen via B4 Relay wrote:
> > Patch 2 adds the only user, spi-intel, which already computes protected
> > range state at probe but has had no way to expose it since the SPI MEM
> > conversion removed its visibility of the MTD device.
>
> Where was it removed, though?
After the SPI MEM conversion the driver is a regular SPI driver so it can
only use spi_new_device() to add and configure the SPI-NOR chip (via
platform data, struct flash_platform_data). Prior this it was creating the
MTD device itself.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 0/2] Report platform enforced SPI flash write protection
2026-08-31 12:55 ` Mika Westerberg
@ 2026-08-31 13:00 ` Michael Walle
2026-08-31 13:07 ` Mika Westerberg
0 siblings, 1 reply; 11+ messages in thread
From: Michael Walle @ 2026-08-31 13:00 UTC (permalink / raw)
To: Mika Westerberg
Cc: tjakobsen84, Pratyush Yadav, Takahiro Kuwano, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Mark Brown, linux-mtd,
linux-kernel, linux-spi
[-- Attachment #1: Type: text/plain, Size: 809 bytes --]
Hi,
On Mon Aug 31, 2026 at 2:55 PM CEST, Mika Westerberg wrote:
> Hi,
>
> On Mon, Aug 31, 2026 at 02:37:59PM +0200, Michael Walle wrote:
>> Hi,
>>
>> On Mon Aug 31, 2026 at 2:05 PM CEST, Tobias Jakobsen via B4 Relay wrote:
>> > Patch 2 adds the only user, spi-intel, which already computes protected
>> > range state at probe but has had no way to expose it since the SPI MEM
>> > conversion removed its visibility of the MTD device.
>>
>> Where was it removed, though?
>
> After the SPI MEM conversion the driver is a regular SPI driver so it can
> only use spi_new_device() to add and configure the SPI-NOR chip (via
> platform data, struct flash_platform_data). Prior this it was creating the
> MTD device itself.
Yeah, but it never exposed the _is_locked() op, right?
-michael
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 297 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 0/2] Report platform enforced SPI flash write protection
2026-08-31 13:00 ` Michael Walle
@ 2026-08-31 13:07 ` Mika Westerberg
0 siblings, 0 replies; 11+ messages in thread
From: Mika Westerberg @ 2026-08-31 13:07 UTC (permalink / raw)
To: Michael Walle
Cc: tjakobsen84, Pratyush Yadav, Takahiro Kuwano, Miquel Raynal,
Richard Weinberger, Vignesh Raghavendra, Mark Brown, linux-mtd,
linux-kernel, linux-spi
On Mon, Aug 31, 2026 at 03:00:36PM +0200, Michael Walle wrote:
> Hi,
>
> On Mon Aug 31, 2026 at 2:55 PM CEST, Mika Westerberg wrote:
> > Hi,
> >
> > On Mon, Aug 31, 2026 at 02:37:59PM +0200, Michael Walle wrote:
> >> Hi,
> >>
> >> On Mon Aug 31, 2026 at 2:05 PM CEST, Tobias Jakobsen via B4 Relay wrote:
> >> > Patch 2 adds the only user, spi-intel, which already computes protected
> >> > range state at probe but has had no way to expose it since the SPI MEM
> >> > conversion removed its visibility of the MTD device.
> >>
> >> Where was it removed, though?
> >
> > After the SPI MEM conversion the driver is a regular SPI driver so it can
> > only use spi_new_device() to add and configure the SPI-NOR chip (via
> > platform data, struct flash_platform_data). Prior this it was creating the
> > MTD device itself.
>
> Yeah, but it never exposed the _is_locked() op, right?
Ah, yeah that's right :)
^ permalink raw reply [flat|nested] 11+ messages in thread