Coverage Report

Created: 2026-03-11 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/u-boot/drivers/mtd/nand/raw/nand_macronix.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0+
2
/*
3
 * Copyright (C) 2017 Free Electrons
4
 * Copyright (C) 2017 NextThing Co
5
 *
6
 * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
7
 *
8
 * This program is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 2 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 */
18
19
#include <dm/device_compat.h>
20
#include <linux/mtd/rawnand.h>
21
22
0
#define ONFI_FEATURE_ADDR_30LFXG18AC_OTP  0x90
23
0
#define MACRONIX_30LFXG18AC_OTP_START_PAGE  2
24
0
#define MACRONIX_30LFXG18AC_OTP_PAGES   30
25
0
#define MACRONIX_30LFXG18AC_OTP_PAGE_SIZE 2112
26
#define MACRONIX_30LFXG18AC_OTP_SIZE_BYTES  \
27
0
  (MACRONIX_30LFXG18AC_OTP_PAGES *  \
28
0
   MACRONIX_30LFXG18AC_OTP_PAGE_SIZE)
29
30
0
#define MACRONIX_30LFXG18AC_OTP_EN    BIT(0)
31
32
static int macronix_30lfxg18ac_get_otp_info(struct mtd_info *mtd, size_t len,
33
              size_t *retlen,
34
              struct otp_info *buf)
35
0
{
36
0
  if (len < sizeof(*buf))
37
0
    return -EINVAL;
38
39
  /* Always report that OTP is unlocked. Reason is that this
40
   * type of flash chip doesn't provide way to check that OTP
41
   * is locked or not: subfeature parameter is implemented as
42
   * volatile register. Technically OTP region could be locked
43
   * and become readonly, but as there is no way to check it,
44
   * don't allow to lock it ('_lock_user_prot_reg' callback
45
   * always returns -EOPNOTSUPP) and thus we report that OTP
46
   * is unlocked.
47
   */
48
0
  buf->locked = 0;
49
0
  buf->start = 0;
50
0
  buf->length = MACRONIX_30LFXG18AC_OTP_SIZE_BYTES;
51
52
0
  *retlen = sizeof(*buf);
53
54
0
  return 0;
55
0
}
56
57
static int macronix_30lfxg18ac_otp_enable(struct nand_chip *nand)
58
0
{
59
0
  u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 };
60
0
  struct mtd_info *mtd;
61
62
0
  mtd = nand_to_mtd(nand);
63
0
  feature_buf[0] = MACRONIX_30LFXG18AC_OTP_EN;
64
65
0
  return nand->onfi_set_features(mtd, nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, feature_buf);
66
0
}
67
68
static int macronix_30lfxg18ac_otp_disable(struct nand_chip *nand)
69
0
{
70
0
  u8 feature_buf[ONFI_SUBFEATURE_PARAM_LEN] = { 0 };
71
0
  struct mtd_info *mtd;
72
73
0
  mtd = nand_to_mtd(nand);
74
0
  return nand->onfi_set_features(mtd, nand, ONFI_FEATURE_ADDR_30LFXG18AC_OTP, feature_buf);
75
0
}
76
77
static int __macronix_30lfxg18ac_rw_otp(struct mtd_info *mtd,
78
          loff_t offs_in_flash,
79
          size_t len, size_t *retlen,
80
          u_char *buf, bool write)
81
0
{
82
0
  struct nand_chip *nand;
83
0
  size_t bytes_handled;
84
0
  off_t offs_in_page;
85
0
  u64 page;
86
0
  int ret;
87
88
0
  nand = mtd_to_nand(mtd);
89
0
  nand->select_chip(mtd, 0);
90
91
0
  ret = macronix_30lfxg18ac_otp_enable(nand);
92
0
  if (ret)
93
0
    goto out_otp;
94
95
0
  page = offs_in_flash;
96
  /* 'page' will be result of division. */
97
0
  offs_in_page = do_div(page, MACRONIX_30LFXG18AC_OTP_PAGE_SIZE);
98
0
  bytes_handled = 0;
99
100
0
  while (bytes_handled < len &&
101
0
         page < MACRONIX_30LFXG18AC_OTP_PAGES) {
102
0
    size_t bytes_to_handle;
103
0
    u64 phys_page = page + MACRONIX_30LFXG18AC_OTP_START_PAGE;
104
105
0
    bytes_to_handle = min_t(size_t, len - bytes_handled,
106
0
          MACRONIX_30LFXG18AC_OTP_PAGE_SIZE -
107
0
          offs_in_page);
108
109
0
    if (write)
110
0
      ret = nand_prog_page_op(nand, phys_page, offs_in_page,
111
0
            &buf[bytes_handled], bytes_to_handle);
112
0
    else
113
0
      ret = nand_read_page_op(nand, phys_page, offs_in_page,
114
0
            &buf[bytes_handled], bytes_to_handle);
115
0
    if (ret)
116
0
      goto out_otp;
117
118
0
    bytes_handled += bytes_to_handle;
119
0
    offs_in_page = 0;
120
0
    page++;
121
0
  }
122
123
0
  *retlen = bytes_handled;
124
125
0
out_otp:
126
0
  if (ret)
127
0
    dev_err(mtd->dev, "failed to perform OTP IO: %i\n", ret);
128
129
0
  ret = macronix_30lfxg18ac_otp_disable(nand);
130
0
  if (ret)
131
0
    dev_err(mtd->dev, "failed to leave OTP mode after %s\n",
132
0
      write ? "write" : "read");
133
134
0
  nand->select_chip(mtd, -1);
135
136
0
  return ret;
137
0
}
138
139
static int macronix_30lfxg18ac_write_otp(struct mtd_info *mtd, loff_t to,
140
           size_t len, size_t *rlen,
141
           u_char *buf)
142
0
{
143
0
  return __macronix_30lfxg18ac_rw_otp(mtd, to, len, rlen, (u_char *)buf,
144
0
              true);
145
0
}
146
147
static int macronix_30lfxg18ac_read_otp(struct mtd_info *mtd, loff_t from,
148
          size_t len, size_t *rlen,
149
          u_char *buf)
150
0
{
151
0
  return __macronix_30lfxg18ac_rw_otp(mtd, from, len, rlen, buf, false);
152
0
}
153
154
static int macronix_30lfxg18ac_lock_otp(struct mtd_info *mtd, loff_t from,
155
          size_t len)
156
0
{
157
  /* See comment in 'macronix_30lfxg18ac_get_otp_info()'. */
158
0
  return -EOPNOTSUPP;
159
0
}
160
161
static void macronix_nand_setup_otp(struct nand_chip *chip)
162
0
{
163
0
  static const char * const supported_otp_models[] = {
164
0
    "MX30LF1G18AC",
165
0
    "MX30LF2G18AC",
166
0
    "MX30LF4G18AC",
167
0
  };
168
0
  int i;
169
170
0
  if (!chip->onfi_version ||
171
0
      !(le16_to_cpu(chip->onfi_params.opt_cmd)
172
0
        & ONFI_OPT_CMD_SET_GET_FEATURES))
173
0
    return;
174
175
0
  for (i = 0; i < ARRAY_SIZE(supported_otp_models); i++) {
176
0
    if (!strcmp(chip->onfi_params.model, supported_otp_models[i])) {
177
0
      struct mtd_info *mtd;
178
179
0
      mtd = nand_to_mtd(chip);
180
0
      mtd->_get_user_prot_info = macronix_30lfxg18ac_get_otp_info;
181
0
      mtd->_read_user_prot_reg = macronix_30lfxg18ac_read_otp;
182
0
      mtd->_write_user_prot_reg = macronix_30lfxg18ac_write_otp;
183
0
      mtd->_lock_user_prot_reg = macronix_30lfxg18ac_lock_otp;
184
0
      return;
185
0
    }
186
0
  }
187
0
}
188
189
static int macronix_nand_init(struct nand_chip *chip)
190
0
{
191
0
  if (nand_is_slc(chip))
192
0
    chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
193
194
0
  macronix_nand_setup_otp(chip);
195
196
0
  return 0;
197
0
}
198
199
const struct nand_manufacturer_ops macronix_nand_manuf_ops = {
200
  .init = macronix_nand_init,
201
};