88#include <linux/device.h>
99#include <linux/kernel.h>
1010#include <linux/mtd/spinand.h>
11+ #include <linux/spi/spi-mem.h>
1112
1213/* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */
1314#define SPINAND_MFR_ESMT_C8 0xc8
1415
16+ #define ESMT_F50L1G41LB_CFG_OTP_PROTECT BIT(7)
17+ #define ESMT_F50L1G41LB_CFG_OTP_LOCK \
18+ (CFG_OTP_ENABLE | ESMT_F50L1G41LB_CFG_OTP_PROTECT)
19+
1520static SPINAND_OP_VARIANTS (read_cache_variants ,
1621 SPINAND_PAGE_READ_FROM_CACHE_X4_OP (0 , 1 , NULL, 0 ),
1722 SPINAND_PAGE_READ_FROM_CACHE_X2_OP (0 , 1 , NULL, 0 ),
@@ -102,6 +107,83 @@ static const struct mtd_ooblayout_ops f50l1g41lb_ooblayout = {
102107 .free = f50l1g41lb_ooblayout_free ,
103108};
104109
110+ static int f50l1g41lb_otp_info (struct spinand_device * spinand , size_t len ,
111+ struct otp_info * buf , size_t * retlen , bool user )
112+ {
113+ if (len < sizeof (* buf ))
114+ return - EINVAL ;
115+
116+ buf -> locked = 0 ;
117+ buf -> start = 0 ;
118+ buf -> length = user ? spinand_user_otp_size (spinand ) :
119+ spinand_fact_otp_size (spinand );
120+
121+ * retlen = sizeof (* buf );
122+ return 0 ;
123+ }
124+
125+ static int f50l1g41lb_fact_otp_info (struct spinand_device * spinand , size_t len ,
126+ struct otp_info * buf , size_t * retlen )
127+ {
128+ return f50l1g41lb_otp_info (spinand , len , buf , retlen , false);
129+ }
130+
131+ static int f50l1g41lb_user_otp_info (struct spinand_device * spinand , size_t len ,
132+ struct otp_info * buf , size_t * retlen )
133+ {
134+ return f50l1g41lb_otp_info (spinand , len , buf , retlen , true);
135+ }
136+
137+ static int f50l1g41lb_otp_lock (struct spinand_device * spinand , loff_t from ,
138+ size_t len )
139+ {
140+ struct spi_mem_op write_op = SPINAND_WR_EN_DIS_OP (true);
141+ struct spi_mem_op exec_op = SPINAND_PROG_EXEC_OP (0 );
142+ u8 status ;
143+ int ret ;
144+
145+ ret = spinand_upd_cfg (spinand , ESMT_F50L1G41LB_CFG_OTP_LOCK ,
146+ ESMT_F50L1G41LB_CFG_OTP_LOCK );
147+ if (!ret )
148+ return ret ;
149+
150+ ret = spi_mem_exec_op (spinand -> spimem , & write_op );
151+ if (!ret )
152+ goto out ;
153+
154+ ret = spi_mem_exec_op (spinand -> spimem , & exec_op );
155+ if (!ret )
156+ goto out ;
157+
158+ ret = spinand_wait (spinand ,
159+ SPINAND_WRITE_INITIAL_DELAY_US ,
160+ SPINAND_WRITE_POLL_DELAY_US ,
161+ & status );
162+ if (!ret && (status & STATUS_PROG_FAILED ))
163+ ret = - EIO ;
164+
165+ out :
166+ if (spinand_upd_cfg (spinand , ESMT_F50L1G41LB_CFG_OTP_LOCK , 0 )) {
167+ dev_warn (& spinand_to_mtd (spinand )-> dev ,
168+ "Can not disable OTP mode\n" );
169+ ret = - EIO ;
170+ }
171+
172+ return ret ;
173+ }
174+
175+ static const struct spinand_user_otp_ops f50l1g41lb_user_otp_ops = {
176+ .info = f50l1g41lb_user_otp_info ,
177+ .lock = f50l1g41lb_otp_lock ,
178+ .read = spinand_user_otp_read ,
179+ .write = spinand_user_otp_write ,
180+ };
181+
182+ static const struct spinand_fact_otp_ops f50l1g41lb_fact_otp_ops = {
183+ .info = f50l1g41lb_fact_otp_info ,
184+ .read = spinand_fact_otp_read ,
185+ };
186+
105187static const struct spinand_info esmt_c8_spinand_table [] = {
106188 SPINAND_INFO ("F50L1G41LB" ,
107189 SPINAND_ID (SPINAND_READID_METHOD_OPCODE_ADDR , 0x01 , 0x7f ,
@@ -112,7 +194,9 @@ static const struct spinand_info esmt_c8_spinand_table[] = {
112194 & write_cache_variants ,
113195 & update_cache_variants ),
114196 0 ,
115- SPINAND_ECCINFO (& f50l1g41lb_ooblayout , NULL )),
197+ SPINAND_ECCINFO (& f50l1g41lb_ooblayout , NULL ),
198+ SPINAND_USER_OTP_INFO (28 , 2 , & f50l1g41lb_user_otp_ops ),
199+ SPINAND_FACT_OTP_INFO (2 , 0 , & f50l1g41lb_fact_otp_ops )),
116200 SPINAND_INFO ("F50D1G41LB" ,
117201 SPINAND_ID (SPINAND_READID_METHOD_OPCODE_ADDR , 0x11 , 0x7f ,
118202 0x7f , 0x7f ),
@@ -122,7 +206,9 @@ static const struct spinand_info esmt_c8_spinand_table[] = {
122206 & write_cache_variants ,
123207 & update_cache_variants ),
124208 0 ,
125- SPINAND_ECCINFO (& f50l1g41lb_ooblayout , NULL )),
209+ SPINAND_ECCINFO (& f50l1g41lb_ooblayout , NULL ),
210+ SPINAND_USER_OTP_INFO (28 , 2 , & f50l1g41lb_user_otp_ops ),
211+ SPINAND_FACT_OTP_INFO (2 , 0 , & f50l1g41lb_fact_otp_ops )),
126212 SPINAND_INFO ("F50D2G41KA" ,
127213 SPINAND_ID (SPINAND_READID_METHOD_OPCODE_ADDR , 0x51 , 0x7f ,
128214 0x7f , 0x7f ),
0 commit comments