99#include <linux/device.h>
1010#include <linux/kernel.h>
1111#include <linux/mtd/spinand.h>
12+ #include <linux/spi/spi-mem.h>
13+ #include <linux/string.h>
1214
1315#define SPINAND_MFR_MICRON 0x2c
1416
2830
2931#define MICRON_SELECT_DIE (x ) ((x) << 6)
3032
33+ #define MICRON_MT29F2G01ABAGD_CFG_OTP_STATE BIT(7)
34+ #define MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK \
35+ (CFG_OTP_ENABLE | MICRON_MT29F2G01ABAGD_CFG_OTP_STATE)
36+
3137static SPINAND_OP_VARIANTS (quadio_read_cache_variants ,
3238 SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP (0 , 2 , NULL, 0 ),
3339 SPINAND_PAGE_READ_FROM_CACHE_X4_OP (0 , 1 , NULL, 0 ),
@@ -168,6 +174,131 @@ static int micron_8_ecc_get_status(struct spinand_device *spinand,
168174 return - EINVAL ;
169175}
170176
177+ static int mt29f2g01abagd_otp_is_locked (struct spinand_device * spinand )
178+ {
179+ size_t bufsize = spinand_otp_page_size (spinand );
180+ size_t retlen ;
181+ u8 * buf ;
182+ int ret ;
183+
184+ buf = kmalloc (bufsize , GFP_KERNEL );
185+ if (!buf )
186+ return - ENOMEM ;
187+
188+ ret = spinand_upd_cfg (spinand ,
189+ MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK ,
190+ MICRON_MT29F2G01ABAGD_CFG_OTP_STATE );
191+ if (ret )
192+ goto free_buf ;
193+
194+ ret = spinand_user_otp_read (spinand , 0 , bufsize , & retlen , buf );
195+
196+ if (spinand_upd_cfg (spinand , MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK ,
197+ 0 )) {
198+ dev_warn (& spinand_to_mtd (spinand )-> dev ,
199+ "Can not disable OTP mode\n" );
200+ ret = - EIO ;
201+ }
202+
203+ if (ret )
204+ goto free_buf ;
205+
206+ /* If all zeros, then the OTP area is locked. */
207+ if (mem_is_zero (buf , bufsize ))
208+ ret = 1 ;
209+
210+ free_buf :
211+ kfree (buf );
212+ return ret ;
213+ }
214+
215+ static int mt29f2g01abagd_otp_info (struct spinand_device * spinand , size_t len ,
216+ struct otp_info * buf , size_t * retlen ,
217+ bool user )
218+ {
219+ int locked ;
220+
221+ if (len < sizeof (* buf ))
222+ return - EINVAL ;
223+
224+ locked = mt29f2g01abagd_otp_is_locked (spinand );
225+ if (locked < 0 )
226+ return locked ;
227+
228+ buf -> locked = locked ;
229+ buf -> start = 0 ;
230+ buf -> length = user ? spinand_user_otp_size (spinand ) :
231+ spinand_fact_otp_size (spinand );
232+
233+ * retlen = sizeof (* buf );
234+ return 0 ;
235+ }
236+
237+ static int mt29f2g01abagd_fact_otp_info (struct spinand_device * spinand ,
238+ size_t len , struct otp_info * buf ,
239+ size_t * retlen )
240+ {
241+ return mt29f2g01abagd_otp_info (spinand , len , buf , retlen , false);
242+ }
243+
244+ static int mt29f2g01abagd_user_otp_info (struct spinand_device * spinand ,
245+ size_t len , struct otp_info * buf ,
246+ size_t * retlen )
247+ {
248+ return mt29f2g01abagd_otp_info (spinand , len , buf , retlen , true);
249+ }
250+
251+ static int mt29f2g01abagd_otp_lock (struct spinand_device * spinand , loff_t from ,
252+ size_t len )
253+ {
254+ struct spi_mem_op write_op = SPINAND_WR_EN_DIS_OP (true);
255+ struct spi_mem_op exec_op = SPINAND_PROG_EXEC_OP (0 );
256+ u8 status ;
257+ int ret ;
258+
259+ ret = spinand_upd_cfg (spinand ,
260+ MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK ,
261+ MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK );
262+ if (!ret )
263+ return ret ;
264+
265+ ret = spi_mem_exec_op (spinand -> spimem , & write_op );
266+ if (!ret )
267+ goto out ;
268+
269+ ret = spi_mem_exec_op (spinand -> spimem , & exec_op );
270+ if (!ret )
271+ goto out ;
272+
273+ ret = spinand_wait (spinand ,
274+ SPINAND_WRITE_INITIAL_DELAY_US ,
275+ SPINAND_WRITE_POLL_DELAY_US ,
276+ & status );
277+ if (!ret && (status & STATUS_PROG_FAILED ))
278+ ret = - EIO ;
279+
280+ out :
281+ if (spinand_upd_cfg (spinand , MICRON_MT29F2G01ABAGD_CFG_OTP_LOCK , 0 )) {
282+ dev_warn (& spinand_to_mtd (spinand )-> dev ,
283+ "Can not disable OTP mode\n" );
284+ ret = - EIO ;
285+ }
286+
287+ return ret ;
288+ }
289+
290+ static const struct spinand_user_otp_ops mt29f2g01abagd_user_otp_ops = {
291+ .info = mt29f2g01abagd_user_otp_info ,
292+ .lock = mt29f2g01abagd_otp_lock ,
293+ .read = spinand_user_otp_read ,
294+ .write = spinand_user_otp_write ,
295+ };
296+
297+ static const struct spinand_fact_otp_ops mt29f2g01abagd_fact_otp_ops = {
298+ .info = mt29f2g01abagd_fact_otp_info ,
299+ .read = spinand_fact_otp_read ,
300+ };
301+
171302static const struct spinand_info micron_spinand_table [] = {
172303 /* M79A 2Gb 3.3V */
173304 SPINAND_INFO ("MT29F2G01ABAGD" ,
@@ -179,7 +310,9 @@ static const struct spinand_info micron_spinand_table[] = {
179310 & x4_update_cache_variants ),
180311 0 ,
181312 SPINAND_ECCINFO (& micron_8_ooblayout ,
182- micron_8_ecc_get_status )),
313+ micron_8_ecc_get_status ),
314+ SPINAND_USER_OTP_INFO (12 , 2 , & mt29f2g01abagd_user_otp_ops ),
315+ SPINAND_FACT_OTP_INFO (2 , 0 , & mt29f2g01abagd_fact_otp_ops )),
183316 /* M79A 2Gb 1.8V */
184317 SPINAND_INFO ("MT29F2G01ABBGD" ,
185318 SPINAND_ID (SPINAND_READID_METHOD_OPCODE_DUMMY , 0x25 ),
0 commit comments