@@ -8,6 +8,180 @@
#include "core.h"
+/**
+ * macronix_nor_otp_enter() - Send Enter Secured OTP instruction to the chip.
+ * @nor: pointer to 'struct spi_nor'.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int macronix_nor_otp_enter(struct spi_nor *nor)
+{
+ int error;
+
+ error = spi_nor_send_cmd(nor, SPINOR_OP_ENSO);
+
+ if (error)
+ dev_dbg(nor->dev, "error %d on Macronix Enter Secured OTP\n", error);
+
+ return error;
+}
+
+/**
+ * macronix_nor_otp_exit() - Send Exit Secured OTP instruction to the chip.
+ * @nor: pointer to 'struct spi_nor'.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int macronix_nor_otp_exit(struct spi_nor *nor)
+{
+ int error;
+
+ error = spi_nor_send_cmd(nor, SPINOR_OP_EXSO);
+
+ if (error)
+ dev_dbg(nor->dev, "error %d on Macronix Enter Secured OTP\n", error);
+
+ return error;
+}
+
+/**
+ * macronix_nor_otp_read() - read security register
+ * @nor: pointer to 'struct spi_nor'
+ * @addr: offset to read from
+ * @len: number of bytes to read
+ * @buf: pointer to dst buffer
+ *
+ * Return: number of bytes read successfully, -errno otherwise
+ */
+static int macronix_nor_otp_read(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf)
+{
+ int ret, error;
+
+ error = macronix_nor_otp_enter(nor);
+ if (error)
+ return error;
+
+ ret = spi_nor_read_data(nor, addr, len, buf);
+
+ error = macronix_nor_otp_exit(nor);
+
+ if (ret < 0)
+ dev_dbg(nor->dev, "error %d on Macronix read OTP data\n", ret);
+ else if (error)
+ return error;
+
+ return ret;
+}
+
+/**
+ * macronix_nor_otp_write() - write security register
+ * @nor: pointer to 'struct spi_nor'
+ * @addr: offset to write to
+ * @len: number of bytes to write
+ * @buf: pointer to src buffer
+ *
+ * Return: number of bytes written successfully, -errno otherwise
+ */
+static int macronix_nor_otp_write(struct spi_nor *nor, loff_t addr, size_t len, const u8 *buf)
+{
+ int error, ret = 0;
+
+ error = macronix_nor_otp_enter(nor);
+ if (error)
+ return error;
+
+ error = spi_nor_write_enable(nor);
+ if (error)
+ goto otp_write_err;
+
+ ret = spi_nor_write_data(nor, addr, len, buf);
+ if (ret < 0) {
+ dev_dbg(nor->dev, "error %d on Macronix write OTP data\n", ret);
+ goto otp_write_err;
+ }
+
+ error = spi_nor_wait_till_ready(nor);
+ if (error)
+ dev_dbg(nor->dev, "error %d on Macronix waiting write OTP finish\n", error);
+
+otp_write_err:
+
+ error = macronix_nor_otp_exit(nor);
+
+ return ret;
+}
+
+/**
+ * macronix_nor_otp_lock() - lock the OTP region
+ * @nor: pointer to 'struct spi_nor'
+ * @region: OTP region
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int macronix_nor_otp_lock(struct spi_nor *nor, unsigned int region)
+{
+ int error;
+ u8 *rdscur = nor->bouncebuf;
+
+ error = spi_nor_read_reg(nor, SPINOR_OP_RDSCUR, 1);
+ if (error) {
+ dev_dbg(nor->dev, "error %d on read security register\n", error);
+ return error;
+ }
+
+ switch (region) {
+ case 0: /* Lock 1st 4K-bit region */
+ if (rdscur[0] & SEC_REG_LDSO)
+ return 0; /* Already locked */
+ rdscur[0] |= SEC_REG_LDSO;
+ break;
+ case 1: /* Lock 2nd 4K-bit region */
+ if (rdscur[0] & SEC_REG_LDS1)
+ return 0; /* Already locked */
+ rdscur[0] |= SEC_REG_LDS1;
+ break;
+ default:
+ return 0; /* Unknown region */
+ }
+
+ error = spi_nor_write_reg(nor, SPINOR_OP_WRSCUR, 1);
+ if (error)
+ dev_dbg(nor->dev, "error %d on update security register\n", error);
+
+ return error;
+}
+
+/**
+ * macronix_nor_otp_is_locked() - get the OTP region lock status
+ * @nor: pointer to 'struct spi_nor'
+ * @region: OTP region
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int macronix_nor_otp_is_locked(struct spi_nor *nor, unsigned int region)
+{
+ int ret;
+ u8 *rdscur = nor->bouncebuf;
+
+ ret = spi_nor_read_reg(nor, SPINOR_OP_RDSCUR, 1);
+ if (ret) {
+ dev_dbg(nor->dev, "error %d on read security register\n", ret);
+ return ret;
+ }
+
+ switch (region) {
+ case 0: /* 1st 4K-bit region */
+ ret = (rdscur[0] & SEC_REG_LDSO) > 0;
+ break;
+ case 1: /* 2nd 4K-bit region */
+ ret = (rdscur[0] & SEC_REG_LDS1) > 0;
+ break;
+ default: /* Unknown region */
+ break;
+ }
+ return ret;
+}
+
static int
mx25l25635_post_bfpt_fixups(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
@@ -190,8 +364,19 @@ static void macronix_nor_default_init(struct spi_nor *nor)
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
}
+static const struct spi_nor_otp_ops macronix_nor_otp_ops = {
+ .read = macronix_nor_otp_read,
+ .write = macronix_nor_otp_write,
+ /* .erase = Macronix OTP do not support erase, */
+ .lock = macronix_nor_otp_lock,
+ .is_locked = macronix_nor_otp_is_locked,
+};
+
static int macronix_nor_late_init(struct spi_nor *nor)
{
+ if (nor->params->otp.org->n_regions)
+ nor->params->otp.ops = ¯onix_nor_otp_ops;
+
if (!nor->params->set_4byte_addr_mode)
nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_en4b_ex4b;
@@ -81,6 +81,16 @@
#define SPINOR_OP_BP 0x02 /* Byte program */
#define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */
+/* Used by Macronix OTP. */
+#define SPINOR_OP_RDSCUR 0x2b /* read security register */
+#define SPINOR_OP_WRSCUR 0x2f /* write security register */
+#define SPINOR_OP_ENSO 0xb1 /* enter secured OTP */
+#define SPINOR_OP_EXSO 0xc1 /* exit secured OTP */
+
+/* Security register */
+#define SEC_REG_LDSO BIT(1) /* lock-down bit 1st 4K-bit */
+#define SEC_REG_LDS1 BIT(0) /* lock-down bit 2nd 4K-bit */
+
/* Used for Macronix and Winbond flashes. */
#define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */
#define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */