linux-spi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] intel-spi: Split hardware and software sequencing
@ 2022-10-20 16:45 Mauro Lima
  2022-10-20 16:45 ` [PATCH 1/2] spi: intel-spi: Move software sequencing logic outside the core Mauro Lima
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Mauro Lima @ 2022-10-20 16:45 UTC (permalink / raw)
  To: broonie; +Cc: mika.westerberg, linux-spi, linux-kernel, Mauro Lima

Right now the only driver for Intel's spi has a DANGEROUS tag for
a bug in the past on certain Lenovo platforms. It was cleared out
that the bug was caused for the spi software sequencing mechanism
and if we only use the driver with the hardware sequencing
capabilities will be much safer[1].

This changes will remove all the software sequencing bits from
the driver and left only the hardware sequencing functionality.
If the software sequencing capabilities are needed, the old driver
can be build using the DANGEROUS option from the menu.

[1] https://lkml.org/lkml/2021/11/11/468

Mauro Lima (2):
  spi: intel-spi: Move software sequencing logic outside the core
  spi: intel-spi: build the driver with hardware sequencing by default

 drivers/spi/Kconfig            |  15 +-
 drivers/spi/Makefile           |   2 +-
 drivers/spi/spi-intel-common.h | 171 +++++++++++++++++
 drivers/spi/spi-intel-swseq.c  | 231 +++++++++++++++++++++++
 drivers/spi/spi-intel-swseq.h  |  23 +++
 drivers/spi/spi-intel.c        | 326 +++------------------------------
 6 files changed, 464 insertions(+), 304 deletions(-)
 create mode 100644 drivers/spi/spi-intel-common.h
 create mode 100644 drivers/spi/spi-intel-swseq.c
 create mode 100644 drivers/spi/spi-intel-swseq.h

-- 
2.34.3


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] spi: intel-spi: Move software sequencing logic outside the core
  2022-10-20 16:45 [PATCH 0/2] intel-spi: Split hardware and software sequencing Mauro Lima
@ 2022-10-20 16:45 ` Mauro Lima
  2022-10-23  5:45   ` Mika Westerberg
  2022-10-20 16:45 ` [PATCH 2/2] spi: intel-spi: build the driver with hardware sequencing by default Mauro Lima
  2022-10-25  6:09 ` [PATCH 0/2] intel-spi: Split hardware and software sequencing Mika Westerberg
  2 siblings, 1 reply; 5+ messages in thread
From: Mauro Lima @ 2022-10-20 16:45 UTC (permalink / raw)
  To: broonie; +Cc: mika.westerberg, linux-spi, linux-kernel, Mauro Lima

Move out the software sequencing logic from the core. Keep the
functionality without changes.

Signed-off-by: Mauro Lima <mauro.lima@eclypsium.com>
---
 drivers/spi/Makefile           |   2 +-
 drivers/spi/spi-intel-common.h | 171 +++++++++++++++++
 drivers/spi/spi-intel-swseq.c  | 181 ++++++++++++++++++
 drivers/spi/spi-intel-swseq.h  |  23 +++
 drivers/spi/spi-intel.c        | 326 +++------------------------------
 5 files changed, 401 insertions(+), 302 deletions(-)
 create mode 100644 drivers/spi/spi-intel-common.h
 create mode 100644 drivers/spi/spi-intel-swseq.c
 create mode 100644 drivers/spi/spi-intel-swseq.h

diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 4b34e855c841..47ebb6ec084e 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -63,7 +63,7 @@ obj-$(CONFIG_SPI_HISI_SFC_V3XX)		+= spi-hisi-sfc-v3xx.o
 obj-$(CONFIG_SPI_IMG_SPFI)		+= spi-img-spfi.o
 obj-$(CONFIG_SPI_IMX)			+= spi-imx.o
 obj-$(CONFIG_SPI_INGENIC)		+= spi-ingenic.o
-obj-$(CONFIG_SPI_INTEL)			+= spi-intel.o
+obj-$(CONFIG_SPI_INTEL)			+= spi-intel.o spi-intel-swseq.o
 obj-$(CONFIG_SPI_INTEL_PCI)		+= spi-intel-pci.o
 obj-$(CONFIG_SPI_INTEL_PLATFORM)	+= spi-intel-platform.o
 obj-$(CONFIG_SPI_LANTIQ_SSC)		+= spi-lantiq-ssc.o
diff --git a/drivers/spi/spi-intel-common.h b/drivers/spi/spi-intel-common.h
new file mode 100644
index 000000000000..32dd75a1ea30
--- /dev/null
+++ b/drivers/spi/spi-intel-common.h
@@ -0,0 +1,171 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Intel PCH/PCU SPI flash driver.
+ *
+ * Copyright (C) 2016 - 2022, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ */
+
+#ifndef SPI_INTEL_COMMON_H
+#define SPI_INTEL_COMMON_H
+
+#include <linux/spi/spi-mem.h>
+#include <linux/mtd/spi-nor.h>
+
+/* Offsets are from @ispi->base */
+#define BFPREG				0x00
+
+#define HSFSTS_CTL			0x04
+#define HSFSTS_CTL_FSMIE		BIT(31)
+#define HSFSTS_CTL_FDBC_SHIFT		24
+#define HSFSTS_CTL_FDBC_MASK		(0x3f << HSFSTS_CTL_FDBC_SHIFT)
+
+#define HSFSTS_CTL_FCYCLE_SHIFT		17
+#define HSFSTS_CTL_FCYCLE_MASK		(0x0f << HSFSTS_CTL_FCYCLE_SHIFT)
+/* HW sequencer opcodes */
+#define HSFSTS_CTL_FCYCLE_READ		(0x00 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_WRITE		(0x02 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_ERASE		(0x03 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_ERASE_64K	(0x04 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_RDID		(0x06 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_WRSR		(0x07 << HSFSTS_CTL_FCYCLE_SHIFT)
+#define HSFSTS_CTL_FCYCLE_RDSR		(0x08 << HSFSTS_CTL_FCYCLE_SHIFT)
+
+#define HSFSTS_CTL_FGO			BIT(16)
+#define HSFSTS_CTL_FLOCKDN		BIT(15)
+#define HSFSTS_CTL_FDV			BIT(14)
+#define HSFSTS_CTL_SCIP			BIT(5)
+#define HSFSTS_CTL_AEL			BIT(2)
+#define HSFSTS_CTL_FCERR		BIT(1)
+#define HSFSTS_CTL_FDONE		BIT(0)
+
+#define FADDR				0x08
+#define DLOCK				0x0c
+#define FDATA(n)			(0x10 + ((n) * 4))
+
+#define FRACC				0x50
+
+#define FREG(n)				(0x54 + ((n) * 4))
+#define FREG_BASE_MASK			0x3fff
+#define FREG_LIMIT_SHIFT		16
+#define FREG_LIMIT_MASK			(0x03fff << FREG_LIMIT_SHIFT)
+
+/* Offset is from @ispi->pregs */
+#define PR(n)				((n) * 4)
+#define PR_WPE				BIT(31)
+#define PR_LIMIT_SHIFT			16
+#define PR_LIMIT_MASK			(0x3fff << PR_LIMIT_SHIFT)
+#define PR_RPE				BIT(15)
+#define PR_BASE_MASK			0x3fff
+
+/* Offsets are from @ispi->sregs */
+#define SSFSTS_CTL			0x00
+#define SSFSTS_CTL_FSMIE		BIT(23)
+#define SSFSTS_CTL_DS			BIT(22)
+#define SSFSTS_CTL_DBC_SHIFT		16
+#define SSFSTS_CTL_SPOP			BIT(11)
+#define SSFSTS_CTL_ACS			BIT(10)
+#define SSFSTS_CTL_SCGO			BIT(9)
+#define SSFSTS_CTL_COP_SHIFT		12
+#define SSFSTS_CTL_FRS			BIT(7)
+#define SSFSTS_CTL_DOFRS		BIT(6)
+#define SSFSTS_CTL_AEL			BIT(4)
+#define SSFSTS_CTL_FCERR		BIT(3)
+#define SSFSTS_CTL_FDONE		BIT(2)
+#define SSFSTS_CTL_SCIP			BIT(0)
+
+#define PREOP_OPTYPE			0x04
+#define OPMENU0				0x08
+#define OPMENU1				0x0c
+
+#define OPTYPE_READ_NO_ADDR		0
+#define OPTYPE_WRITE_NO_ADDR		1
+#define OPTYPE_READ_WITH_ADDR		2
+#define OPTYPE_WRITE_WITH_ADDR		3
+
+/* CPU specifics */
+#define BYT_PR				0x74
+#define BYT_SSFSTS_CTL			0x90
+#define BYT_FREG_NUM			5
+#define BYT_PR_NUM			5
+
+#define LPT_PR				0x74
+#define LPT_SSFSTS_CTL			0x90
+#define LPT_FREG_NUM			5
+#define LPT_PR_NUM			5
+
+#define BXT_PR				0x84
+#define BXT_SSFSTS_CTL			0xa0
+#define BXT_FREG_NUM			12
+#define BXT_PR_NUM			6
+
+#define CNL_PR				0x84
+#define CNL_FREG_NUM			6
+#define CNL_PR_NUM			5
+
+#define LVSCC				0xc4
+#define UVSCC				0xc8
+#define ERASE_OPCODE_SHIFT		8
+#define ERASE_OPCODE_MASK		(0xff << ERASE_OPCODE_SHIFT)
+#define ERASE_64K_OPCODE_SHIFT		16
+#define ERASE_64K_OPCODE_MASK		(0xff << ERASE_OPCODE_SHIFT)
+
+/* Flash descriptor fields */
+#define FLVALSIG_MAGIC			0x0ff0a55a
+#define FLMAP0_NC_MASK			GENMASK(9, 8)
+#define FLMAP0_NC_SHIFT			8
+#define FLMAP0_FCBA_MASK		GENMASK(7, 0)
+
+#define FLCOMP_C0DEN_MASK		GENMASK(3, 0)
+#define FLCOMP_C0DEN_512K		0x00
+#define FLCOMP_C0DEN_1M			0x01
+#define FLCOMP_C0DEN_2M			0x02
+#define FLCOMP_C0DEN_4M			0x03
+#define FLCOMP_C0DEN_8M			0x04
+#define FLCOMP_C0DEN_16M		0x05
+#define FLCOMP_C0DEN_32M		0x06
+#define FLCOMP_C0DEN_64M		0x07
+
+#define INTEL_SPI_TIMEOUT		5000 /* ms */
+#define INTEL_SPI_FIFO_SZ		64
+
+/**
+ * struct intel_spi - Driver private data
+ * @dev: Device pointer
+ * @info: Pointer to board specific info
+ * @base: Beginning of MMIO space
+ * @pregs: Start of protection registers
+ * @sregs: Start of software sequencer registers
+ * @master: Pointer to the SPI controller structure
+ * @nregions: Maximum number of regions
+ * @pr_num: Maximum number of protected range registers
+ * @chip0_size: Size of the first flash chip in bytes
+ * @locked: Is SPI setting locked
+ * @swseq_reg: Use SW sequencer in register reads/writes
+ * @swseq_erase: Use SW sequencer in erase operation
+ * @swseq_enabled: SW sequencer enabled to use
+ * @atomic_preopcode: Holds preopcode when atomic sequence is requested
+ * @opcodes: Opcodes which are supported. This are programmed by BIOS
+ *           before it locks down the controller.
+ * @mem_ops: Pointer to SPI MEM ops supported by the controller
+ */
+struct intel_spi {
+	struct device *dev;
+	const struct intel_spi_boardinfo *info;
+	void __iomem *base;
+	void __iomem *pregs;
+	void __iomem *sregs;
+	struct spi_controller *master;
+	size_t nregions;
+	size_t pr_num;
+	size_t chip0_size;
+	bool locked;
+	bool swseq_reg;
+	bool swseq_erase;
+	bool swseq_enabled;
+	u8 atomic_preopcode;
+	u8 opcodes[8];
+	const struct intel_spi_mem_op *mem_ops;
+};
+
+#endif /* SPI_INTEL_COMMON_H */
diff --git a/drivers/spi/spi-intel-swseq.c b/drivers/spi/spi-intel-swseq.c
new file mode 100644
index 000000000000..2597aa06a160
--- /dev/null
+++ b/drivers/spi/spi-intel-swseq.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Intel PCH/PCU SPI flash driver.
+ *
+ * Copyright (C) 2016 - 2022, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ */
+
+#include <linux/iopoll.h>
+
+#include "spi-intel.h"
+#include "spi-intel-common.h"
+#include "spi-intel-swseq.h"
+
+bool mem_op_supported_on_spi_locked(const struct intel_spi *ispi,
+				    const struct spi_mem_op *op)
+{
+	int i;
+
+	/* Check if it is in the locked opcodes list */
+	for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++) {
+		if (ispi->opcodes[i] == op->cmd.opcode)
+			return true;
+	}
+
+	dev_dbg(ispi->dev, "%#x not supported\n", op->cmd.opcode);
+	return false;
+}
+EXPORT_SYMBOL(mem_op_supported_on_spi_locked);
+
+inline bool is_swseq_enabled(void)
+{
+	return true;
+}
+EXPORT_SYMBOL(is_swseq_enabled);
+
+int handle_swseq_wren(struct intel_spi *ispi)
+{
+	u16 preop;
+	const u8 opcode = SPINOR_OP_WREN;
+
+	if (!ispi->swseq_reg)
+		return 0;
+
+	preop = readw(ispi->sregs + PREOP_OPTYPE);
+	if ((preop & 0xff) != SPINOR_OP_WREN && (preop >> 8) != SPINOR_OP_WREN) {
+		if (ispi->locked)
+			return -EINVAL;
+		writel(opcode, ispi->sregs + PREOP_OPTYPE);
+	}
+
+	/*
+	 * This enables atomic sequence on next SW sycle. Will
+	 * be cleared after next operation.
+	*/
+	ispi->atomic_preopcode = opcode;
+	return 0;
+}
+EXPORT_SYMBOL(handle_swseq_wren);
+
+static int intel_spi_wait_sw_busy(const struct intel_spi *ispi)
+{
+	u32 val;
+
+	return readl_poll_timeout(ispi->sregs + SSFSTS_CTL, val,
+				  !(val & SSFSTS_CTL_SCIP), 0,
+				  INTEL_SPI_TIMEOUT * 1000);
+}
+
+static int intel_spi_opcode_index(const struct intel_spi *ispi, u8 opcode, int optype)
+{
+	int i;
+	int preop;
+
+	if (ispi->locked) {
+		for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++)
+			if (ispi->opcodes[i] == opcode)
+				return i;
+
+		return -EINVAL;
+	}
+
+	/* The lock is off, so just use index 0 */
+	writel(opcode, ispi->sregs + OPMENU0);
+	preop = readw(ispi->sregs + PREOP_OPTYPE);
+	writel(optype << 16 | preop, ispi->sregs + PREOP_OPTYPE);
+
+	return 0;
+}
+
+int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, size_t len,
+		       int optype)
+{
+	u32 val = 0, status;
+	u8 atomic_preopcode;
+	int ret;
+
+	ret = intel_spi_opcode_index(ispi, opcode, optype);
+	if (ret < 0)
+		return ret;
+
+	if (len > INTEL_SPI_FIFO_SZ)
+		return -EINVAL;
+
+	/*
+	 * Always clear it after each SW sequencer operation regardless
+	 * of whether it is successful or not.
+	 */
+	atomic_preopcode = ispi->atomic_preopcode;
+	ispi->atomic_preopcode = 0;
+
+	/* Only mark 'Data Cycle' bit when there is data to be transferred */
+	if (len > 0)
+		val = ((len - 1) << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS;
+	val |= ret << SSFSTS_CTL_COP_SHIFT;
+	val |= SSFSTS_CTL_FCERR | SSFSTS_CTL_FDONE;
+	val |= SSFSTS_CTL_SCGO;
+	if (atomic_preopcode) {
+		u16 preop;
+
+		switch (optype) {
+		case OPTYPE_WRITE_NO_ADDR:
+		case OPTYPE_WRITE_WITH_ADDR:
+			/* Pick matching preopcode for the atomic sequence */
+			preop = readw(ispi->sregs + PREOP_OPTYPE);
+			if ((preop & 0xff) == atomic_preopcode)
+				; /* Do nothing */
+			else if ((preop >> 8) == atomic_preopcode)
+				val |= SSFSTS_CTL_SPOP;
+			else
+				return -EINVAL;
+
+			/* Enable atomic sequence */
+			val |= SSFSTS_CTL_ACS;
+			break;
+
+		default:
+			return -EINVAL;
+		}
+	}
+	writel(val, ispi->sregs + SSFSTS_CTL);
+
+	ret = intel_spi_wait_sw_busy(ispi);
+	if (ret)
+		return ret;
+
+	status = readl(ispi->sregs + SSFSTS_CTL);
+	if (status & SSFSTS_CTL_FCERR)
+		return -EIO;
+	else if (status & SSFSTS_CTL_AEL)
+		return -EACCES;
+
+	return 0;
+}
+EXPORT_SYMBOL(intel_spi_sw_cycle);
+
+void disable_smi_generation(const struct intel_spi *ispi)
+{
+	u32 val;
+	val = readl(ispi->sregs + SSFSTS_CTL);
+	val &= ~SSFSTS_CTL_FSMIE;
+	writel(val, ispi->sregs + SSFSTS_CTL);
+}
+EXPORT_SYMBOL(disable_smi_generation);
+
+void populate_opmenus(struct intel_spi *ispi, u32 *opmenu0, u32 *opmenu1)
+{
+	unsigned int i;
+	*opmenu0 = readl(ispi->sregs + OPMENU0);
+	*opmenu1 = readl(ispi->sregs + OPMENU1);
+
+	if (*opmenu0 && *opmenu1) {
+		for (i = 0; i < ARRAY_SIZE(ispi->opcodes) / 2; i++) {
+			ispi->opcodes[i] = *opmenu0 >> i * 8;
+			ispi->opcodes[i + 4] = *opmenu1 >> i * 8;
+		}
+	}
+}
+EXPORT_SYMBOL(populate_opmenus);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/spi/spi-intel-swseq.h b/drivers/spi/spi-intel-swseq.h
new file mode 100644
index 000000000000..457fb64814e2
--- /dev/null
+++ b/drivers/spi/spi-intel-swseq.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Intel PCH/PCU SPI flash driver.
+ *
+ * Copyright (C) 2016 - 2022, Intel Corporation
+ * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
+ */
+
+#ifndef SPI_INTEL_SWSEQ_H
+#define SPI_INTEL_SWSEQ_H
+
+#include <linux/types.h>
+
+int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, size_t len,
+		       int optype);
+inline bool is_swseq_enabled(void);
+int handle_swseq_wren(struct intel_spi *ispi);
+bool mem_op_supported_on_spi_locked(const struct intel_spi *ispi,
+				    const struct spi_mem_op *op);
+void disable_smi_generation(const struct intel_spi *ispi);
+void populate_opmenus(struct intel_spi *ispi, u32 *opmenu0, u32 *opmenu1);
+
+#endif /* SPI_INTEL_SWSEQ_H */
diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c
index 55f4ee2db002..8cdea5551e36 100644
--- a/drivers/spi/spi-intel.c
+++ b/drivers/spi/spi-intel.c
@@ -17,160 +17,8 @@
 #include <linux/spi/spi-mem.h>
 
 #include "spi-intel.h"
-
-/* Offsets are from @ispi->base */
-#define BFPREG				0x00
-
-#define HSFSTS_CTL			0x04
-#define HSFSTS_CTL_FSMIE		BIT(31)
-#define HSFSTS_CTL_FDBC_SHIFT		24
-#define HSFSTS_CTL_FDBC_MASK		(0x3f << HSFSTS_CTL_FDBC_SHIFT)
-
-#define HSFSTS_CTL_FCYCLE_SHIFT		17
-#define HSFSTS_CTL_FCYCLE_MASK		(0x0f << HSFSTS_CTL_FCYCLE_SHIFT)
-/* HW sequencer opcodes */
-#define HSFSTS_CTL_FCYCLE_READ		(0x00 << HSFSTS_CTL_FCYCLE_SHIFT)
-#define HSFSTS_CTL_FCYCLE_WRITE		(0x02 << HSFSTS_CTL_FCYCLE_SHIFT)
-#define HSFSTS_CTL_FCYCLE_ERASE		(0x03 << HSFSTS_CTL_FCYCLE_SHIFT)
-#define HSFSTS_CTL_FCYCLE_ERASE_64K	(0x04 << HSFSTS_CTL_FCYCLE_SHIFT)
-#define HSFSTS_CTL_FCYCLE_RDID		(0x06 << HSFSTS_CTL_FCYCLE_SHIFT)
-#define HSFSTS_CTL_FCYCLE_WRSR		(0x07 << HSFSTS_CTL_FCYCLE_SHIFT)
-#define HSFSTS_CTL_FCYCLE_RDSR		(0x08 << HSFSTS_CTL_FCYCLE_SHIFT)
-
-#define HSFSTS_CTL_FGO			BIT(16)
-#define HSFSTS_CTL_FLOCKDN		BIT(15)
-#define HSFSTS_CTL_FDV			BIT(14)
-#define HSFSTS_CTL_SCIP			BIT(5)
-#define HSFSTS_CTL_AEL			BIT(2)
-#define HSFSTS_CTL_FCERR		BIT(1)
-#define HSFSTS_CTL_FDONE		BIT(0)
-
-#define FADDR				0x08
-#define DLOCK				0x0c
-#define FDATA(n)			(0x10 + ((n) * 4))
-
-#define FRACC				0x50
-
-#define FREG(n)				(0x54 + ((n) * 4))
-#define FREG_BASE_MASK			0x3fff
-#define FREG_LIMIT_SHIFT		16
-#define FREG_LIMIT_MASK			(0x03fff << FREG_LIMIT_SHIFT)
-
-/* Offset is from @ispi->pregs */
-#define PR(n)				((n) * 4)
-#define PR_WPE				BIT(31)
-#define PR_LIMIT_SHIFT			16
-#define PR_LIMIT_MASK			(0x3fff << PR_LIMIT_SHIFT)
-#define PR_RPE				BIT(15)
-#define PR_BASE_MASK			0x3fff
-
-/* Offsets are from @ispi->sregs */
-#define SSFSTS_CTL			0x00
-#define SSFSTS_CTL_FSMIE		BIT(23)
-#define SSFSTS_CTL_DS			BIT(22)
-#define SSFSTS_CTL_DBC_SHIFT		16
-#define SSFSTS_CTL_SPOP			BIT(11)
-#define SSFSTS_CTL_ACS			BIT(10)
-#define SSFSTS_CTL_SCGO			BIT(9)
-#define SSFSTS_CTL_COP_SHIFT		12
-#define SSFSTS_CTL_FRS			BIT(7)
-#define SSFSTS_CTL_DOFRS		BIT(6)
-#define SSFSTS_CTL_AEL			BIT(4)
-#define SSFSTS_CTL_FCERR		BIT(3)
-#define SSFSTS_CTL_FDONE		BIT(2)
-#define SSFSTS_CTL_SCIP			BIT(0)
-
-#define PREOP_OPTYPE			0x04
-#define OPMENU0				0x08
-#define OPMENU1				0x0c
-
-#define OPTYPE_READ_NO_ADDR		0
-#define OPTYPE_WRITE_NO_ADDR		1
-#define OPTYPE_READ_WITH_ADDR		2
-#define OPTYPE_WRITE_WITH_ADDR		3
-
-/* CPU specifics */
-#define BYT_PR				0x74
-#define BYT_SSFSTS_CTL			0x90
-#define BYT_FREG_NUM			5
-#define BYT_PR_NUM			5
-
-#define LPT_PR				0x74
-#define LPT_SSFSTS_CTL			0x90
-#define LPT_FREG_NUM			5
-#define LPT_PR_NUM			5
-
-#define BXT_PR				0x84
-#define BXT_SSFSTS_CTL			0xa0
-#define BXT_FREG_NUM			12
-#define BXT_PR_NUM			6
-
-#define CNL_PR				0x84
-#define CNL_FREG_NUM			6
-#define CNL_PR_NUM			5
-
-#define LVSCC				0xc4
-#define UVSCC				0xc8
-#define ERASE_OPCODE_SHIFT		8
-#define ERASE_OPCODE_MASK		(0xff << ERASE_OPCODE_SHIFT)
-#define ERASE_64K_OPCODE_SHIFT		16
-#define ERASE_64K_OPCODE_MASK		(0xff << ERASE_OPCODE_SHIFT)
-
-/* Flash descriptor fields */
-#define FLVALSIG_MAGIC			0x0ff0a55a
-#define FLMAP0_NC_MASK			GENMASK(9, 8)
-#define FLMAP0_NC_SHIFT			8
-#define FLMAP0_FCBA_MASK		GENMASK(7, 0)
-
-#define FLCOMP_C0DEN_MASK		GENMASK(3, 0)
-#define FLCOMP_C0DEN_512K		0x00
-#define FLCOMP_C0DEN_1M			0x01
-#define FLCOMP_C0DEN_2M			0x02
-#define FLCOMP_C0DEN_4M			0x03
-#define FLCOMP_C0DEN_8M			0x04
-#define FLCOMP_C0DEN_16M		0x05
-#define FLCOMP_C0DEN_32M		0x06
-#define FLCOMP_C0DEN_64M		0x07
-
-#define INTEL_SPI_TIMEOUT		5000 /* ms */
-#define INTEL_SPI_FIFO_SZ		64
-
-/**
- * struct intel_spi - Driver private data
- * @dev: Device pointer
- * @info: Pointer to board specific info
- * @base: Beginning of MMIO space
- * @pregs: Start of protection registers
- * @sregs: Start of software sequencer registers
- * @master: Pointer to the SPI controller structure
- * @nregions: Maximum number of regions
- * @pr_num: Maximum number of protected range registers
- * @chip0_size: Size of the first flash chip in bytes
- * @locked: Is SPI setting locked
- * @swseq_reg: Use SW sequencer in register reads/writes
- * @swseq_erase: Use SW sequencer in erase operation
- * @atomic_preopcode: Holds preopcode when atomic sequence is requested
- * @opcodes: Opcodes which are supported. This are programmed by BIOS
- *           before it locks down the controller.
- * @mem_ops: Pointer to SPI MEM ops supported by the controller
- */
-struct intel_spi {
-	struct device *dev;
-	const struct intel_spi_boardinfo *info;
-	void __iomem *base;
-	void __iomem *pregs;
-	void __iomem *sregs;
-	struct spi_controller *master;
-	size_t nregions;
-	size_t pr_num;
-	size_t chip0_size;
-	bool locked;
-	bool swseq_reg;
-	bool swseq_erase;
-	u8 atomic_preopcode;
-	u8 opcodes[8];
-	const struct intel_spi_mem_op *mem_ops;
-};
+#include "spi-intel-common.h"
+#include "spi-intel-swseq.h"
 
 struct intel_spi_mem_op {
 	struct spi_mem_op mem_op;
@@ -259,9 +107,12 @@ static void intel_spi_dump_regs(struct intel_spi *ispi)
 	}
 
 	dev_dbg(ispi->dev, "Using %cW sequencer for register access\n",
-		ispi->swseq_reg ? 'S' : 'H');
+		ispi->swseq_reg && ispi->swseq_enabled ? 'S' : 'H');
 	dev_dbg(ispi->dev, "Using %cW sequencer for erase operation\n",
-		ispi->swseq_erase ? 'S' : 'H');
+		ispi->swseq_erase && ispi->swseq_enabled ? 'S' : 'H');
+
+	if (!ispi->swseq_enabled)
+		dev_dbg(ispi->dev, "SW sequencer is disabled for all operations\n");
 }
 
 /* Reads max INTEL_SPI_FIFO_SZ bytes from the device fifo */
@@ -314,15 +165,6 @@ static int intel_spi_wait_hw_busy(struct intel_spi *ispi)
 				  INTEL_SPI_TIMEOUT * 1000);
 }
 
-static int intel_spi_wait_sw_busy(struct intel_spi *ispi)
-{
-	u32 val;
-
-	return readl_poll_timeout(ispi->sregs + SSFSTS_CTL, val,
-				  !(val & SSFSTS_CTL_SCIP), 0,
-				  INTEL_SPI_TIMEOUT * 1000);
-}
-
 static bool intel_spi_set_writeable(struct intel_spi *ispi)
 {
 	if (!ispi->info->set_writeable)
@@ -331,27 +173,6 @@ static bool intel_spi_set_writeable(struct intel_spi *ispi)
 	return ispi->info->set_writeable(ispi->base, ispi->info->data);
 }
 
-static int intel_spi_opcode_index(struct intel_spi *ispi, u8 opcode, int optype)
-{
-	int i;
-	int preop;
-
-	if (ispi->locked) {
-		for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++)
-			if (ispi->opcodes[i] == opcode)
-				return i;
-
-		return -EINVAL;
-	}
-
-	/* The lock is off, so just use index 0 */
-	writel(opcode, ispi->sregs + OPMENU0);
-	preop = readw(ispi->sregs + PREOP_OPTYPE);
-	writel(optype << 16 | preop, ispi->sregs + PREOP_OPTYPE);
-
-	return 0;
-}
-
 static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, size_t len)
 {
 	u32 val, status;
@@ -395,71 +216,6 @@ static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, size_t len)
 	return 0;
 }
 
-static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, size_t len,
-			      int optype)
-{
-	u32 val = 0, status;
-	u8 atomic_preopcode;
-	int ret;
-
-	ret = intel_spi_opcode_index(ispi, opcode, optype);
-	if (ret < 0)
-		return ret;
-
-	if (len > INTEL_SPI_FIFO_SZ)
-		return -EINVAL;
-
-	/*
-	 * Always clear it after each SW sequencer operation regardless
-	 * of whether it is successful or not.
-	 */
-	atomic_preopcode = ispi->atomic_preopcode;
-	ispi->atomic_preopcode = 0;
-
-	/* Only mark 'Data Cycle' bit when there is data to be transferred */
-	if (len > 0)
-		val = ((len - 1) << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS;
-	val |= ret << SSFSTS_CTL_COP_SHIFT;
-	val |= SSFSTS_CTL_FCERR | SSFSTS_CTL_FDONE;
-	val |= SSFSTS_CTL_SCGO;
-	if (atomic_preopcode) {
-		u16 preop;
-
-		switch (optype) {
-		case OPTYPE_WRITE_NO_ADDR:
-		case OPTYPE_WRITE_WITH_ADDR:
-			/* Pick matching preopcode for the atomic sequence */
-			preop = readw(ispi->sregs + PREOP_OPTYPE);
-			if ((preop & 0xff) == atomic_preopcode)
-				; /* Do nothing */
-			else if ((preop >> 8) == atomic_preopcode)
-				val |= SSFSTS_CTL_SPOP;
-			else
-				return -EINVAL;
-
-			/* Enable atomic sequence */
-			val |= SSFSTS_CTL_ACS;
-			break;
-
-		default:
-			return -EINVAL;
-		}
-	}
-	writel(val, ispi->sregs + SSFSTS_CTL);
-
-	ret = intel_spi_wait_sw_busy(ispi);
-	if (ret)
-		return ret;
-
-	status = readl(ispi->sregs + SSFSTS_CTL);
-	if (status & SSFSTS_CTL_FCERR)
-		return -EIO;
-	else if (status & SSFSTS_CTL_AEL)
-		return -EACCES;
-
-	return 0;
-}
-
 static u32 intel_spi_chip_addr(const struct intel_spi *ispi,
 			       const struct spi_mem *mem)
 {
@@ -479,7 +235,7 @@ static int intel_spi_read_reg(struct intel_spi *ispi, const struct spi_mem *mem,
 
 	writel(intel_spi_chip_addr(ispi, mem), ispi->base + FADDR);
 
-	if (ispi->swseq_reg)
+	if (ispi->swseq_reg && ispi->swseq_enabled)
 		ret = intel_spi_sw_cycle(ispi, opcode, nbytes,
 					 OPTYPE_READ_NO_ADDR);
 	else
@@ -508,26 +264,8 @@ static int intel_spi_write_reg(struct intel_spi *ispi, const struct spi_mem *mem
 	 * When hardware sequencer is used there is no need to program
 	 * any opcodes (it handles them automatically as part of a command).
 	 */
-	if (opcode == SPINOR_OP_WREN) {
-		u16 preop;
-
-		if (!ispi->swseq_reg)
-			return 0;
-
-		preop = readw(ispi->sregs + PREOP_OPTYPE);
-		if ((preop & 0xff) != opcode && (preop >> 8) != opcode) {
-			if (ispi->locked)
-				return -EINVAL;
-			writel(opcode, ispi->sregs + PREOP_OPTYPE);
-		}
-
-		/*
-		 * This enables atomic sequence on next SW sycle. Will
-		 * be cleared after next operation.
-		 */
-		ispi->atomic_preopcode = opcode;
-		return 0;
-	}
+	if (opcode == SPINOR_OP_WREN)
+		return handle_swseq_wren(ispi);
 
 	/*
 	 * We hope that HW sequencer will do the right thing automatically and
@@ -545,7 +283,7 @@ static int intel_spi_write_reg(struct intel_spi *ispi, const struct spi_mem *mem
 	if (ret)
 		return ret;
 
-	if (ispi->swseq_reg)
+	if (ispi->swseq_reg && ispi->swseq_enabled)
 		return intel_spi_sw_cycle(ispi, opcode, nbytes,
 					  OPTYPE_WRITE_NO_ADDR);
 	return intel_spi_hw_cycle(ispi, opcode, nbytes);
@@ -686,7 +424,11 @@ static int intel_spi_erase(struct intel_spi *ispi, const struct spi_mem *mem,
 
 	writel(addr, ispi->base + FADDR);
 
-	if (ispi->swseq_erase)
+	/*
+	 * If swseq_erase is true, it means that we cannot erase using
+	 * HW sequencer.
+	 */
+	if (ispi->swseq_erase && ispi->swseq_enabled)
 		return intel_spi_sw_cycle(ispi, opcode, 0,
 					  OPTYPE_WRITE_WITH_ADDR);
 
@@ -767,18 +509,8 @@ static bool intel_spi_supports_mem_op(struct spi_mem *mem,
 	 * For software sequencer check that the opcode is actually
 	 * present in the opmenu if it is locked.
 	 */
-	if (ispi->swseq_reg && ispi->locked) {
-		int i;
-
-		/* Check if it is in the locked opcodes list */
-		for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++) {
-			if (ispi->opcodes[i] == op->cmd.opcode)
-				return true;
-		}
-
-		dev_dbg(ispi->dev, "%#x not supported\n", op->cmd.opcode);
-		return false;
-	}
+	if (ispi->swseq_reg && ispi->locked && ispi->swseq_enabled)
+		return mem_op_supported_on_spi_locked(ispi, op);
 
 	return true;
 }
@@ -1068,7 +800,8 @@ static int intel_spi_init(struct intel_spi *ispi)
 {
 	u32 opmenu0, opmenu1, lvscc, uvscc, val;
 	bool erase_64k = false;
-	int i;
+
+	ispi->swseq_enabled = is_swseq_enabled();
 
 	switch (ispi->info->type) {
 	case INTEL_SPI_BYT:
@@ -1141,37 +874,28 @@ static int intel_spi_init(struct intel_spi *ispi)
 		return -EINVAL;
 	}
 
+
 	/*
 	 * Some controllers can only do basic operations using hardware
 	 * sequencer. All other operations are supposed to be carried out
 	 * using software sequencer.
 	 */
-	if (ispi->swseq_reg) {
+	if (ispi->swseq_reg && ispi->swseq_enabled) {
 		/* Disable #SMI generation from SW sequencer */
-		val = readl(ispi->sregs + SSFSTS_CTL);
-		val &= ~SSFSTS_CTL_FSMIE;
-		writel(val, ispi->sregs + SSFSTS_CTL);
+		disable_smi_generation(ispi);
 	}
 
 	/* Check controller's lock status */
 	val = readl(ispi->base + HSFSTS_CTL);
 	ispi->locked = !!(val & HSFSTS_CTL_FLOCKDN);
 
-	if (ispi->locked && ispi->sregs) {
+	if (ispi->locked && ispi->sregs && ispi->swseq_enabled) {
 		/*
 		 * BIOS programs allowed opcodes and then locks down the
 		 * register. So read back what opcodes it decided to support.
 		 * That's the set we are going to support as well.
 		 */
-		opmenu0 = readl(ispi->sregs + OPMENU0);
-		opmenu1 = readl(ispi->sregs + OPMENU1);
-
-		if (opmenu0 && opmenu1) {
-			for (i = 0; i < ARRAY_SIZE(ispi->opcodes) / 2; i++) {
-				ispi->opcodes[i] = opmenu0 >> i * 8;
-				ispi->opcodes[i + 4] = opmenu1 >> i * 8;
-			}
-		}
+		populate_opmenus(ispi, &opmenu0, &opmenu1);
 	}
 
 	if (erase_64k) {
-- 
2.34.3


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] spi: intel-spi: build the driver with hardware sequencing by default
  2022-10-20 16:45 [PATCH 0/2] intel-spi: Split hardware and software sequencing Mauro Lima
  2022-10-20 16:45 ` [PATCH 1/2] spi: intel-spi: Move software sequencing logic outside the core Mauro Lima
@ 2022-10-20 16:45 ` Mauro Lima
  2022-10-25  6:09 ` [PATCH 0/2] intel-spi: Split hardware and software sequencing Mika Westerberg
  2 siblings, 0 replies; 5+ messages in thread
From: Mauro Lima @ 2022-10-20 16:45 UTC (permalink / raw)
  To: broonie; +Cc: mika.westerberg, linux-spi, linux-kernel, Mauro Lima

Add menuconfig option to build the driver with hardware sequencing by
default and another to specify software sequencing support if needed.
For the software sequencing functionality preserve the *DANGEROUS* tag.

Signed-off-by: Mauro Lima <mauro.lima@eclypsium.com>
---
 drivers/spi/Kconfig           | 15 +++++++++--
 drivers/spi/spi-intel-swseq.c | 50 +++++++++++++++++++++++++++++++++++
 2 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index d1bb62f7368b..aec095988ab7 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -448,7 +448,7 @@ config SPI_INTEL
 	tristate
 
 config SPI_INTEL_PCI
-	tristate "Intel PCH/PCU SPI flash PCI driver (DANGEROUS)"
+	tristate "Intel PCH/PCU SPI flash PCI driver"
 	depends on PCI
 	depends on X86 || COMPILE_TEST
 	depends on SPI_MEM
@@ -458,6 +458,8 @@ config SPI_INTEL_PCI
 	  master mode. This controller is present in modern Intel hardware
 	  and is used to hold BIOS and other persistent settings. Using
 	  this driver it is possible to upgrade BIOS directly from Linux.
+	  The driver will use hardware sequencing capabilities from the chip
+	  by default.
 
 	  Say N here unless you know what you are doing. Overwriting the
 	  SPI flash may render the system unbootable.
@@ -466,7 +468,7 @@ config SPI_INTEL_PCI
 	  will be called spi-intel-pci.
 
 config SPI_INTEL_PLATFORM
-	tristate "Intel PCH/PCU SPI flash platform driver (DANGEROUS)"
+	tristate "Intel PCH/PCU SPI flash platform driver"
 	depends on X86 || COMPILE_TEST
 	depends on SPI_MEM
 	select SPI_INTEL
@@ -476,6 +478,8 @@ config SPI_INTEL_PLATFORM
 	  Intel hardware and is used to hold BIOS and other persistent
 	  settings. Using this driver it is possible to upgrade BIOS
 	  directly from Linux.
+	  The driver will use hardware sequencing capabilities from the chip
+	  by default.
 
 	  Say N here unless you know what you are doing. Overwriting the
 	  SPI flash may render the system unbootable.
@@ -483,6 +487,13 @@ config SPI_INTEL_PLATFORM
 	  To compile this driver as a module, choose M here: the module
 	  will be called spi-intel-platform.
 
+config SPI_INTEL_SWSEQ
+	tristate "Intel SPI controller software sequencing support (DANGEROUS)"
+	depends on X86 || COMPILE_TEST
+	depends on SPI_MEM
+	help
+	  This enables software sequencing functionality to the SPI controller
+
 config SPI_JCORE
 	tristate "J-Core SPI Master"
 	depends on OF && (SUPERH || COMPILE_TEST)
diff --git a/drivers/spi/spi-intel-swseq.c b/drivers/spi/spi-intel-swseq.c
index 2597aa06a160..d7e4834be6db 100644
--- a/drivers/spi/spi-intel-swseq.c
+++ b/drivers/spi/spi-intel-swseq.c
@@ -12,6 +12,7 @@
 #include "spi-intel-common.h"
 #include "spi-intel-swseq.h"
 
+#if defined(CONFIG_SPI_INTEL_SWSEQ)
 bool mem_op_supported_on_spi_locked(const struct intel_spi *ispi,
 				    const struct spi_mem_op *op)
 {
@@ -178,4 +179,53 @@ void populate_opmenus(struct intel_spi *ispi, u32 *opmenu0, u32 *opmenu1)
 }
 EXPORT_SYMBOL(populate_opmenus);
 
+#else
+static inline void log_error_swseq_not_supported(const struct intel_spi *ispi)
+{
+	dev_err(ispi->dev, "SW sequencing is not enabled");
+}
+
+int handle_swseq_wren(struct intel_spi *ispi)
+{
+	log_error_swseq_not_supported(ispi);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(handle_swseq_wren);
+
+bool mem_op_supported_on_spi_locked(const struct intel_spi *ispi,
+				    const struct spi_mem_op *op)
+{
+	log_error_swseq_not_supported(ispi);
+	return false;
+}
+EXPORT_SYMBOL(mem_op_supported_on_spi_locked);
+
+int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, size_t len,
+		       int optype)
+{
+	log_error_swseq_not_supported(ispi);
+	return -ENOTSUPP;
+}
+EXPORT_SYMBOL(intel_spi_sw_cycle);
+
+inline bool is_swseq_enabled(void)
+{
+	return false;
+}
+EXPORT_SYMBOL(is_swseq_enabled);
+
+void disable_smi_generation(const struct intel_spi *ispi)
+{
+	log_error_swseq_not_supported(ispi);
+}
+EXPORT_SYMBOL(disable_smi_generation);
+
+void populate_opmenus(struct intel_spi *ispi, u32 *opmenu0, u32 *opmenu1)
+{
+	log_error_swseq_not_supported(ispi);
+}
+EXPORT_SYMBOL(populate_opmenus);
+
+#endif
+
 MODULE_LICENSE("GPL v2");
-- 
2.34.3


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] spi: intel-spi: Move software sequencing logic outside the core
  2022-10-20 16:45 ` [PATCH 1/2] spi: intel-spi: Move software sequencing logic outside the core Mauro Lima
@ 2022-10-23  5:45   ` Mika Westerberg
  0 siblings, 0 replies; 5+ messages in thread
From: Mika Westerberg @ 2022-10-23  5:45 UTC (permalink / raw)
  To: Mauro Lima; +Cc: broonie, linux-spi, linux-kernel

Hi,

On Thu, Oct 20, 2022 at 01:45:07PM -0300, Mauro Lima wrote:
> +#include "spi-intel.h"
> +#include "spi-intel-common.h"
> +#include "spi-intel-swseq.h"

Do we really need all these headers? Why not just "spi-intel.h"?

> +
> +bool mem_op_supported_on_spi_locked(const struct intel_spi *ispi,
> +				    const struct spi_mem_op *op)
> +{
> +	int i;
> +
> +	/* Check if it is in the locked opcodes list */
> +	for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++) {
> +		if (ispi->opcodes[i] == op->cmd.opcode)
> +			return true;
> +	}
> +
> +	dev_dbg(ispi->dev, "%#x not supported\n", op->cmd.opcode);
> +	return false;
> +}
> +EXPORT_SYMBOL(mem_op_supported_on_spi_locked);

You need to namespace all these symbols with intel_spi_ or so (and use
_GPL version of EXPORT_SYMBOL()).

However, I don't think we even need all these to be exported in the
first place. It has been quite a while we discussed about this so I
might be forgetting something but IIRC I did not suggest the split this
way? ;-)

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 0/2] intel-spi: Split hardware and software sequencing
  2022-10-20 16:45 [PATCH 0/2] intel-spi: Split hardware and software sequencing Mauro Lima
  2022-10-20 16:45 ` [PATCH 1/2] spi: intel-spi: Move software sequencing logic outside the core Mauro Lima
  2022-10-20 16:45 ` [PATCH 2/2] spi: intel-spi: build the driver with hardware sequencing by default Mauro Lima
@ 2022-10-25  6:09 ` Mika Westerberg
  2 siblings, 0 replies; 5+ messages in thread
From: Mika Westerberg @ 2022-10-25  6:09 UTC (permalink / raw)
  To: Mauro Lima; +Cc: broonie, linux-spi, linux-kernel

Hi,

On Thu, Oct 20, 2022 at 01:45:06PM -0300, Mauro Lima wrote:
> Right now the only driver for Intel's spi has a DANGEROUS tag for
> a bug in the past on certain Lenovo platforms. It was cleared out
> that the bug was caused for the spi software sequencing mechanism
> and if we only use the driver with the hardware sequencing
> capabilities will be much safer[1].
> 
> This changes will remove all the software sequencing bits from
> the driver and left only the hardware sequencing functionality.
> If the software sequencing capabilities are needed, the old driver
> can be build using the DANGEROUS option from the menu.
> 
> [1] https://lkml.org/lkml/2021/11/11/468
> 
> Mauro Lima (2):
>   spi: intel-spi: Move software sequencing logic outside the core
>   spi: intel-spi: build the driver with hardware sequencing by default

I'be been thinking about this and I believe we can do something simpler
instead.

All the modern "Core" CPUs expose this as PCI device and that only
supports hardware sequencer which should be safe so I think we can do
something like this:

1. Make spi-intel-pci.c to set the type to INTEL_SPI_CNL for all the
   controllers it supports (and double check that this is the case for
   all these controllers).

As a side effect the ispi->sregs will be set to NULL so the core driver
does not even try to use the software seguencer.

2. Update Kconfig of SPI_INTEL_PCI to remove "DANGEROUS" and mention in
   the help text that this only supports the hardware sequencer and only
   the modern core hardware.

3. Update Kconfig of SPI_INTEL_PLATFORM help text to mention that most
   of these are using software sequencer, leave "DANGEROUS" there.

Does this make sense? Let me know what you think. I can do this myself
as well (might take some while though since busy with other things
usual).

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-10-25  6:09 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-20 16:45 [PATCH 0/2] intel-spi: Split hardware and software sequencing Mauro Lima
2022-10-20 16:45 ` [PATCH 1/2] spi: intel-spi: Move software sequencing logic outside the core Mauro Lima
2022-10-23  5:45   ` Mika Westerberg
2022-10-20 16:45 ` [PATCH 2/2] spi: intel-spi: build the driver with hardware sequencing by default Mauro Lima
2022-10-25  6:09 ` [PATCH 0/2] intel-spi: Split hardware and software sequencing Mika Westerberg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).