kernel/0054-feat-bootspi-flash-support-write-proection.patch

283 lines
9.3 KiB
Diff
Raw Normal View History

2024-12-15 18:29:23 +00:00
From 6b8cc0e69850c544290f4209f74b32366986ca31 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9Chuangyifeng=E2=80=9D?=
<huangyifeng@eswincomputing.com>
Date: Mon, 17 Jun 2024 09:53:37 +0800
Subject: [PATCH 054/219] feat:bootspi flash support write proection
Changelogs:
bootspi flash support write proection,
echo 0/1 > /sys/devices/platform/soc/51800000.spi/wp_enable to
disable/enable write proection
---
arch/riscv/boot/dts/eswin/eic7700-evb-a2.dts | 1 +
arch/riscv/boot/dts/eswin/eic7700-evb.dts | 1 +
.../boot/dts/eswin/hifive-premier-550.dts | 3 +-
drivers/spi/spi-eswin-bootspi.c | 163 +++++++++++++++++-
4 files changed, 163 insertions(+), 5 deletions(-)
diff --git a/arch/riscv/boot/dts/eswin/eic7700-evb-a2.dts b/arch/riscv/boot/dts/eswin/eic7700-evb-a2.dts
index 3b90f64fcdb2..71c26495502d 100644
--- a/arch/riscv/boot/dts/eswin/eic7700-evb-a2.dts
+++ b/arch/riscv/boot/dts/eswin/eic7700-evb-a2.dts
@@ -636,6 +636,7 @@ &bootspi {
status = "okay";
num-cs = <1>;
cs-gpios = <&portd 0 GPIO_ACTIVE_LOW>;
+ wp-gpios = <&portd 4 GPIO_ACTIVE_LOW>;
spi-flash@0 {
compatible = "winbond,w25q128jw",
"jedec,spi-nor";
diff --git a/arch/riscv/boot/dts/eswin/eic7700-evb.dts b/arch/riscv/boot/dts/eswin/eic7700-evb.dts
index fd94c6a3aeea..896895768139 100644
--- a/arch/riscv/boot/dts/eswin/eic7700-evb.dts
+++ b/arch/riscv/boot/dts/eswin/eic7700-evb.dts
@@ -603,6 +603,7 @@ &bootspi {
status = "okay";
num-cs = <1>;
cs-gpios = <&portd 0 GPIO_ACTIVE_LOW>;
+ wp-gpios = <&portd 4 GPIO_ACTIVE_LOW>;
spi-flash@0 {
compatible = "winbond,w25q128jw",
"jedec,spi-nor";
diff --git a/arch/riscv/boot/dts/eswin/hifive-premier-550.dts b/arch/riscv/boot/dts/eswin/hifive-premier-550.dts
index bfb59b7d6eb5..7644991dc251 100644
--- a/arch/riscv/boot/dts/eswin/hifive-premier-550.dts
+++ b/arch/riscv/boot/dts/eswin/hifive-premier-550.dts
@@ -512,6 +512,7 @@ &bootspi {
status = "okay";
num-cs = <1>;
cs-gpios = <&portd 0 GPIO_ACTIVE_LOW>;
+ wp-gpios = <&portd 4 GPIO_ACTIVE_LOW>;
spi-flash@0 {
compatible = "winbond,w25q128jw",
"jedec,spi-nor";
@@ -823,7 +824,7 @@ &pinctrl_gpio7_default &pinctrl_gpio8_default &pinctrl_gpio9_default &pinctrl
&pinctrl_gpio35_default &pinctrl_gpio36_default &pinctrl_gpio37_default &pinctrl_gpio38_default &pinctrl_gpio39_default &pinctrl_gpio40_default
&pinctrl_gpio46_default &pinctrl_gpio47_default
&pinctrl_gpio92_default &pinctrl_gpio93_default>;
-
+
/* pin header default function for GPIO
SPI1 (CS0,SCLK,MOSI,MISO,D2,D3): GPIO 35,36,37,38,39,40
I2C1 (SCL,SDA): GPIO 46,47
diff --git a/drivers/spi/spi-eswin-bootspi.c b/drivers/spi/spi-eswin-bootspi.c
index c8fbc1b3fc91..977bc6487e99 100644
--- a/drivers/spi/spi-eswin-bootspi.c
+++ b/drivers/spi/spi-eswin-bootspi.c
@@ -32,6 +32,8 @@
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
#include <linux/mtd/spi-nor.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
/* Register offsets */
#define ES_SPI_CSR_00 0x00 /*WRITE_STATUS_REG_TIME*/
@@ -119,6 +121,7 @@ struct es_spi_priv {
struct clk *clk;
struct reset_control *rstc;
struct gpio_desc *cs_gpio; /* External chip-select gpio */
+ struct gpio_desc *wp_gpio; /* External chip-write protection gpio */
void __iomem *regs;
void __iomem *sys_regs;
@@ -136,7 +139,7 @@ struct es_spi_priv {
int bits_per_word;
int len;
- u8 cs; /* chip select pin */
+ bool wp_enabled;
u8 tmode; /* TR/TO/RO/EEPROM */
u8 type; /* SPI/SSP/MicroWire */
struct spi_controller *master;
@@ -373,6 +376,126 @@ static bool eswin_bootspi_supports_op(struct spi_mem *mem,
return spi_mem_default_supports_op(mem, op);
}
+uint8_t eswin_bootspi_read_flash_status_register(struct es_spi_priv *priv,
+ uint8_t *register_data, int flash_cmd)
+{
+ u32 command;
+ struct device *dev = priv->dev;
+
+ memset(register_data, 0, sizeof(uint8_t));
+ //Flash status register-2 is 1byte
+ eswin_bootspi_read_write_cfg(priv, 1, 0);
+
+ //Set SPI_FLASH_COMMAND
+ command = eswin_bootspi_read(priv, ES_SPI_CSR_06);
+ command &= ~((0xFF << 6) | (0x1 << 5) | (0xF << 1) | 0x1);
+ command |= ((flash_cmd << SPI_COMMAND_CODE_FIELD_POSITION) |
+ (SPI_COMMAND_MOVE_VALUE << SPI_COMMAND_MOVE_FIELD_POSITION) |
+ (SPIC_CMD_TYPE_READ_STATUS_REGISTER << SPI_COMMAND_TYPE_FIELD_POSITION) | SPI_COMMAND_VALID);
+ eswin_bootspi_write(priv, ES_SPI_CSR_06, command);
+
+ //Wait command finish
+ eswin_bootspi_wait_over(priv);
+
+ //Read back data
+ eswin_bootspi_recv_data(priv, register_data, 1);
+ dev_dbg(dev, "[%s %d]: command 0x%x, status register_data 0x%x\n",__func__,__LINE__,
+ command, *register_data);
+ return 0;
+}
+
+uint8_t eswin_bootspi_write_flash_status_register(struct es_spi_priv *priv,
+ uint8_t register_data, int flash_cmd)
+{
+ u32 command;
+ struct device *dev = priv->dev;
+
+ //Flash status register-2 is 1byte
+ eswin_bootspi_read_write_cfg(priv, 1, 0);
+ eswin_bootspi_send_data(priv, &register_data, 1);
+
+ command = eswin_bootspi_read(priv, ES_SPI_CSR_06);
+ command &= ~((0xFF << 6) | (0x1 << 5) | (0xF << 1) | 0x1);
+ command |= ((flash_cmd << SPI_COMMAND_CODE_FIELD_POSITION) |
+ (SPI_COMMAND_MOVE_VALUE << SPI_COMMAND_MOVE_FIELD_POSITION) |
+ (SPIC_CMD_TYPE_WRITE_STATUS_REGISTER << SPI_COMMAND_TYPE_FIELD_POSITION) | SPI_COMMAND_VALID);
+ eswin_bootspi_write(priv, ES_SPI_CSR_06, command);
+
+ //Wait command finish
+ eswin_bootspi_wait_over(priv);
+ dev_dbg(dev,"[%s %d]: command 0x%x, status register_data 0x%x\n",__func__,__LINE__,
+ command, register_data);
+ return 0;
+}
+
+int eswin_bootspi_flash_write_protection_cfg(struct es_spi_priv *priv, int enable)
+{
+ uint8_t register_data;
+
+ external_cs_manage(priv, false);
+
+ //Update status register1
+ eswin_bootspi_read_flash_status_register(priv, &register_data, SPINOR_OP_RDSR);
+ /*
+ SRP SEC TB BP2 BP1 BP0 WEL BUSY
+ */
+ if (enable) {
+ register_data |= ((1 << 2) | (1 << 3) | (1 << 4) | (1 << 7));
+ } else {
+ register_data &= ~((1 << 2) | (1 << 3) | (1 << 4) | (1 << 7));
+ }
+ eswin_bootspi_write_flash_status_register(priv, register_data, SPINOR_OP_WRSR);
+
+ //eswin_bootspi_read_flash_status_register(priv, &register_data, SPINOR_OP_RDSR);
+
+ external_cs_manage(priv, true);
+ return 0;
+}
+
+/*
+ 0: disable write_protection
+ 1: enable write_protection
+*/
+void eswin_bootspi_wp_cfg(struct es_spi_priv *priv, int enable)
+{
+ struct device *dev = priv->dev;
+
+ dev_info(dev, "Boot spi flash write protection %s\n", enable ? "enable" : "disable");
+ if (enable) {
+ eswin_bootspi_flash_write_protection_cfg(priv, enable);
+ gpiod_set_value(priv->wp_gpio, enable); //gpio output low, enable protection
+ } else {
+ gpiod_set_value(priv->wp_gpio, enable); //gpio output high, disable protection
+ eswin_bootspi_flash_write_protection_cfg(priv, enable);
+ }
+}
+
+static ssize_t wp_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct es_spi_priv *priv = dev_get_drvdata(dev);
+ return sprintf(buf, "%s\n", priv->wp_enabled ? "enabled" : "disabled");
+}
+
+static ssize_t wp_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct es_spi_priv *priv = dev_get_drvdata(dev);
+ unsigned long enable;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &enable);
+ if (ret)
+ return ret;
+
+ eswin_bootspi_wp_cfg(priv, enable);
+ priv->wp_enabled = enable;
+ return count;
+}
+
+static DEVICE_ATTR_RW(wp_enable);
+
static int eswin_bootspi_exec_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
@@ -455,6 +578,24 @@ static int eswin_bootspi_exec_op(struct spi_mem *mem,
dev_dbg(dev, "[%s %d]: data direction=%d, opcode = 0x%x, cmd_type 0x%x\n",
__func__,__LINE__, op->data.dir, priv->opcode, priv->cmd_type);
+
+ if (priv->wp_enabled) {
+ switch(priv->opcode) {
+ case SPINOR_OP_BE_4K:
+ case SPINOR_OP_BE_4K_PMC:
+ case SPINOR_OP_BE_32K:
+ case SPINOR_OP_SE:
+ case SPINOR_OP_CHIP_ERASE:
+ case SPINOR_OP_PP:
+ case SPINOR_OP_PP_1_1_4:
+ case SPINOR_OP_PP_1_4_4:
+ case SPINOR_OP_PP_1_1_8:
+ case SPINOR_OP_PP_1_8_8:
+ dev_warn_ratelimited(dev, "Write protection is enabled, do not have permission to "
+ "perform this operation(%d)!\n", priv->opcode);
+ return -EACCES;
+ }
+ }
external_cs_manage(priv, false);
if (read) {
@@ -595,9 +736,15 @@ static int eswin_bootspi_probe(struct platform_device *pdev)
return PTR_ERR(priv->cs_gpio);
}
- priv->max_xfer = 32;
- dev_info(dev, "ssi_max_xfer_size=%u\n", priv->max_xfer);
+ priv->wp_gpio = devm_gpiod_get(dev, "wp", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->cs_gpio)) {
+ dev_err(dev, "%s %d: couldn't request gpio! (error %ld)\n", __func__,__LINE__,
+ PTR_ERR(priv->cs_gpio));
+ return PTR_ERR(priv->cs_gpio);
+ }
+ priv->wp_enabled = 1;
+ priv->max_xfer = 32;
/* Currently only bits_per_word == 8 supported */
priv->bits_per_word = 8;
priv->tmode = 0; /* Tx & Rx */
@@ -609,7 +756,15 @@ static int eswin_bootspi_probe(struct platform_device *pdev)
if (ret)
goto err_put_master;
- dev_info(&pdev->dev, "fifo_len %d, %s mode.\n", priv->fifo_len, priv->irq ? "irq" : "polling");
+ // Create sysfs node
+ ret = device_create_file(&pdev->dev, &dev_attr_wp_enable);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to create wp_enable attribute\n");
+ goto err_put_master;
+ }
+
+ dev_info(&pdev->dev, "ssi_max_xfer_size %d, fifo_len %d, %s mode.\n",
+ priv->max_xfer, priv->fifo_len, priv->irq ? "irq" : "polling");
return 0;
err_put_master:
--
2.47.0