0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
0014
0015 #include <linux/init.h>
0016 #include <linux/io.h>
0017 #include <linux/module.h>
0018 #include <linux/kernel.h>
0019 #include <linux/mtd/map.h>
0020 #include <linux/mtd/mtd.h>
0021 #include <linux/mtd/partitions.h>
0022 #include <linux/slab.h>
0023 #include <linux/platform_device.h>
0024 #include <linux/ioport.h>
0025 #include <linux/err.h>
0026
0027
0028 #define ERASE_BLOCKSIZE (0x00020000/2)
0029 #define WRITE_BUFFSIZE (0x00000400/2)
0030 #define OW_BASE_ADDRESS 0x00000000
0031 #define BUS_WIDTH 0x00000020
0032
0033
0034 #define PFOW_QUERY_STRING_P (0x0000/2)
0035 #define PFOW_QUERY_STRING_F (0x0002/2)
0036 #define PFOW_QUERY_STRING_O (0x0004/2)
0037 #define PFOW_QUERY_STRING_W (0x0006/2)
0038
0039
0040 #define CMD_CODE_OFS (0x0080/2)
0041 #define CMD_DATA_OFS (0x0084/2)
0042 #define CMD_ADD_L_OFS (0x0088/2)
0043 #define CMD_ADD_H_OFS (0x008A/2)
0044 #define MPR_L_OFS (0x0090/2)
0045 #define MPR_H_OFS (0x0092/2)
0046 #define CMD_EXEC_OFS (0x00C0/2)
0047 #define STATUS_REG_OFS (0x00CC/2)
0048 #define PRG_BUFFER_OFS (0x0010/2)
0049
0050
0051 #define MR_CFGMASK 0x8000
0052 #define SR_OK_DATAMASK 0x0080
0053
0054
0055 #define LPDDR2_NVM_LOCK 0x0061
0056 #define LPDDR2_NVM_UNLOCK 0x0062
0057 #define LPDDR2_NVM_SW_PROGRAM 0x0041
0058 #define LPDDR2_NVM_SW_OVERWRITE 0x0042
0059 #define LPDDR2_NVM_BUF_PROGRAM 0x00E9
0060 #define LPDDR2_NVM_BUF_OVERWRITE 0x00EA
0061 #define LPDDR2_NVM_ERASE 0x0020
0062
0063
0064 #define LPDDR2_MODE_REG_DATA 0x0040
0065 #define LPDDR2_MODE_REG_CFG 0x0050
0066
0067
0068
0069
0070
0071
0072
0073
0074 struct pcm_int_data {
0075 void __iomem *ctl_regs;
0076 int bus_width;
0077 };
0078
0079 static DEFINE_MUTEX(lpdd2_nvm_mutex);
0080
0081
0082
0083
0084 static inline map_word build_map_word(u_long myword)
0085 {
0086 map_word val = { {0} };
0087 val.x[0] = myword;
0088 return val;
0089 }
0090
0091
0092
0093
0094 static inline u_int build_mr_cfgmask(u_int bus_width)
0095 {
0096 u_int val = MR_CFGMASK;
0097
0098 if (bus_width == 0x0004)
0099 val = val << 16;
0100
0101 return val;
0102 }
0103
0104
0105
0106
0107 static inline u_int build_sr_ok_datamask(u_int bus_width)
0108 {
0109 u_int val = SR_OK_DATAMASK;
0110
0111 if (bus_width == 0x0004)
0112 val = (val << 16)+val;
0113
0114 return val;
0115 }
0116
0117
0118
0119
0120 static inline u_long ow_reg_add(struct map_info *map, u_long offset)
0121 {
0122 u_long val = 0;
0123 struct pcm_int_data *pcm_data = map->fldrv_priv;
0124
0125 val = map->pfow_base + offset*pcm_data->bus_width;
0126
0127 return val;
0128 }
0129
0130
0131
0132
0133
0134
0135
0136 static inline void ow_enable(struct map_info *map)
0137 {
0138 struct pcm_int_data *pcm_data = map->fldrv_priv;
0139
0140 writel_relaxed(build_mr_cfgmask(pcm_data->bus_width) | 0x18,
0141 pcm_data->ctl_regs + LPDDR2_MODE_REG_CFG);
0142 writel_relaxed(0x01, pcm_data->ctl_regs + LPDDR2_MODE_REG_DATA);
0143 }
0144
0145
0146
0147
0148
0149
0150
0151 static inline void ow_disable(struct map_info *map)
0152 {
0153 struct pcm_int_data *pcm_data = map->fldrv_priv;
0154
0155 writel_relaxed(build_mr_cfgmask(pcm_data->bus_width) | 0x18,
0156 pcm_data->ctl_regs + LPDDR2_MODE_REG_CFG);
0157 writel_relaxed(0x02, pcm_data->ctl_regs + LPDDR2_MODE_REG_DATA);
0158 }
0159
0160
0161
0162
0163 static int lpddr2_nvm_do_op(struct map_info *map, u_long cmd_code,
0164 u_long cmd_data, u_long cmd_add, u_long cmd_mpr, u_char *buf)
0165 {
0166 map_word add_l = { {0} }, add_h = { {0} }, mpr_l = { {0} },
0167 mpr_h = { {0} }, data_l = { {0} }, cmd = { {0} },
0168 exec_cmd = { {0} }, sr;
0169 map_word data_h = { {0} };
0170 u_long i, status_reg, prg_buff_ofs;
0171 struct pcm_int_data *pcm_data = map->fldrv_priv;
0172 u_int sr_ok_datamask = build_sr_ok_datamask(pcm_data->bus_width);
0173
0174
0175 add_l.x[0] = cmd_add & 0x0000FFFF;
0176 add_h.x[0] = (cmd_add >> 16) & 0x0000FFFF;
0177 mpr_l.x[0] = cmd_mpr & 0x0000FFFF;
0178 mpr_h.x[0] = (cmd_mpr >> 16) & 0x0000FFFF;
0179 cmd.x[0] = cmd_code & 0x0000FFFF;
0180 exec_cmd.x[0] = 0x0001;
0181 data_l.x[0] = cmd_data & 0x0000FFFF;
0182 data_h.x[0] = (cmd_data >> 16) & 0x0000FFFF;
0183
0184
0185 map_write(map, cmd, ow_reg_add(map, CMD_CODE_OFS));
0186 map_write(map, data_l, ow_reg_add(map, CMD_DATA_OFS));
0187 map_write(map, add_l, ow_reg_add(map, CMD_ADD_L_OFS));
0188 map_write(map, add_h, ow_reg_add(map, CMD_ADD_H_OFS));
0189 map_write(map, mpr_l, ow_reg_add(map, MPR_L_OFS));
0190 map_write(map, mpr_h, ow_reg_add(map, MPR_H_OFS));
0191 if (pcm_data->bus_width == 0x0004) {
0192 map_write(map, cmd, ow_reg_add(map, CMD_CODE_OFS) + 2);
0193 map_write(map, data_h, ow_reg_add(map, CMD_DATA_OFS) + 2);
0194 map_write(map, add_l, ow_reg_add(map, CMD_ADD_L_OFS) + 2);
0195 map_write(map, add_h, ow_reg_add(map, CMD_ADD_H_OFS) + 2);
0196 map_write(map, mpr_l, ow_reg_add(map, MPR_L_OFS) + 2);
0197 map_write(map, mpr_h, ow_reg_add(map, MPR_H_OFS) + 2);
0198 }
0199
0200
0201 if ((cmd_code == LPDDR2_NVM_BUF_PROGRAM) ||
0202 (cmd_code == LPDDR2_NVM_BUF_OVERWRITE)) {
0203 prg_buff_ofs = (map_read(map,
0204 ow_reg_add(map, PRG_BUFFER_OFS))).x[0];
0205 for (i = 0; i < cmd_mpr; i++) {
0206 map_write(map, build_map_word(buf[i]), map->pfow_base +
0207 prg_buff_ofs + i);
0208 }
0209 }
0210
0211
0212 map_write(map, exec_cmd, ow_reg_add(map, CMD_EXEC_OFS));
0213 if (pcm_data->bus_width == 0x0004)
0214 map_write(map, exec_cmd, ow_reg_add(map, CMD_EXEC_OFS) + 2);
0215
0216
0217 do {
0218 sr = map_read(map, ow_reg_add(map, STATUS_REG_OFS));
0219 status_reg = sr.x[0];
0220 if (pcm_data->bus_width == 0x0004) {
0221 sr = map_read(map, ow_reg_add(map,
0222 STATUS_REG_OFS) + 2);
0223 status_reg += sr.x[0] << 16;
0224 }
0225 } while ((status_reg & sr_ok_datamask) != sr_ok_datamask);
0226
0227 return (((status_reg & sr_ok_datamask) == sr_ok_datamask) ? 0 : -EIO);
0228 }
0229
0230
0231
0232
0233 static int lpddr2_nvm_do_block_op(struct mtd_info *mtd, loff_t start_add,
0234 uint64_t len, u_char block_op)
0235 {
0236 struct map_info *map = mtd->priv;
0237 u_long add, end_add;
0238 int ret = 0;
0239
0240 mutex_lock(&lpdd2_nvm_mutex);
0241
0242 ow_enable(map);
0243
0244 add = start_add;
0245 end_add = add + len;
0246
0247 do {
0248 ret = lpddr2_nvm_do_op(map, block_op, 0x00, add, add, NULL);
0249 if (ret)
0250 goto out;
0251 add += mtd->erasesize;
0252 } while (add < end_add);
0253
0254 out:
0255 ow_disable(map);
0256 mutex_unlock(&lpdd2_nvm_mutex);
0257 return ret;
0258 }
0259
0260
0261
0262
0263 static int lpddr2_nvm_pfow_present(struct map_info *map)
0264 {
0265 map_word pfow_val[4];
0266 unsigned int found = 1;
0267
0268 mutex_lock(&lpdd2_nvm_mutex);
0269
0270 ow_enable(map);
0271
0272
0273 pfow_val[0] = map_read(map, ow_reg_add(map, PFOW_QUERY_STRING_P));
0274 pfow_val[1] = map_read(map, ow_reg_add(map, PFOW_QUERY_STRING_F));
0275 pfow_val[2] = map_read(map, ow_reg_add(map, PFOW_QUERY_STRING_O));
0276 pfow_val[3] = map_read(map, ow_reg_add(map, PFOW_QUERY_STRING_W));
0277
0278
0279 if (!map_word_equal(map, build_map_word('P'), pfow_val[0]))
0280 found = 0;
0281 if (!map_word_equal(map, build_map_word('F'), pfow_val[1]))
0282 found = 0;
0283 if (!map_word_equal(map, build_map_word('O'), pfow_val[2]))
0284 found = 0;
0285 if (!map_word_equal(map, build_map_word('W'), pfow_val[3]))
0286 found = 0;
0287
0288 ow_disable(map);
0289
0290 mutex_unlock(&lpdd2_nvm_mutex);
0291
0292 return found;
0293 }
0294
0295
0296
0297
0298 static int lpddr2_nvm_read(struct mtd_info *mtd, loff_t start_add,
0299 size_t len, size_t *retlen, u_char *buf)
0300 {
0301 struct map_info *map = mtd->priv;
0302
0303 mutex_lock(&lpdd2_nvm_mutex);
0304
0305 *retlen = len;
0306
0307 map_copy_from(map, buf, start_add, *retlen);
0308
0309 mutex_unlock(&lpdd2_nvm_mutex);
0310 return 0;
0311 }
0312
0313
0314
0315
0316 static int lpddr2_nvm_write(struct mtd_info *mtd, loff_t start_add,
0317 size_t len, size_t *retlen, const u_char *buf)
0318 {
0319 struct map_info *map = mtd->priv;
0320 struct pcm_int_data *pcm_data = map->fldrv_priv;
0321 u_long add, current_len, tot_len, target_len, my_data;
0322 u_char *write_buf = (u_char *)buf;
0323 int ret = 0;
0324
0325 mutex_lock(&lpdd2_nvm_mutex);
0326
0327 ow_enable(map);
0328
0329
0330 add = start_add;
0331 target_len = len;
0332 tot_len = 0;
0333
0334 while (tot_len < target_len) {
0335 if (!(IS_ALIGNED(add, mtd->writesize))) {
0336 my_data = write_buf[tot_len];
0337 my_data += (write_buf[tot_len+1]) << 8;
0338 if (pcm_data->bus_width == 0x0004) {
0339 my_data += (write_buf[tot_len+2]) << 16;
0340 my_data += (write_buf[tot_len+3]) << 24;
0341 }
0342 ret = lpddr2_nvm_do_op(map, LPDDR2_NVM_SW_OVERWRITE,
0343 my_data, add, 0x00, NULL);
0344 if (ret)
0345 goto out;
0346
0347 add += pcm_data->bus_width;
0348 tot_len += pcm_data->bus_width;
0349 } else {
0350 current_len = min(target_len - tot_len,
0351 (u_long) mtd->writesize);
0352 ret = lpddr2_nvm_do_op(map, LPDDR2_NVM_BUF_OVERWRITE,
0353 0x00, add, current_len, write_buf + tot_len);
0354 if (ret)
0355 goto out;
0356
0357 add += current_len;
0358 tot_len += current_len;
0359 }
0360 }
0361
0362 out:
0363 *retlen = tot_len;
0364 ow_disable(map);
0365 mutex_unlock(&lpdd2_nvm_mutex);
0366 return ret;
0367 }
0368
0369
0370
0371
0372 static int lpddr2_nvm_erase(struct mtd_info *mtd, struct erase_info *instr)
0373 {
0374 return lpddr2_nvm_do_block_op(mtd, instr->addr, instr->len,
0375 LPDDR2_NVM_ERASE);
0376 }
0377
0378
0379
0380
0381 static int lpddr2_nvm_unlock(struct mtd_info *mtd, loff_t start_add,
0382 uint64_t len)
0383 {
0384 return lpddr2_nvm_do_block_op(mtd, start_add, len, LPDDR2_NVM_UNLOCK);
0385 }
0386
0387
0388
0389
0390 static int lpddr2_nvm_lock(struct mtd_info *mtd, loff_t start_add,
0391 uint64_t len)
0392 {
0393 return lpddr2_nvm_do_block_op(mtd, start_add, len, LPDDR2_NVM_LOCK);
0394 }
0395
0396 static const struct mtd_info lpddr2_nvm_mtd_info = {
0397 .type = MTD_RAM,
0398 .writesize = 1,
0399 .flags = (MTD_CAP_NVRAM | MTD_POWERUP_LOCK),
0400 ._read = lpddr2_nvm_read,
0401 ._write = lpddr2_nvm_write,
0402 ._erase = lpddr2_nvm_erase,
0403 ._unlock = lpddr2_nvm_unlock,
0404 ._lock = lpddr2_nvm_lock,
0405 };
0406
0407
0408
0409
0410 static int lpddr2_nvm_probe(struct platform_device *pdev)
0411 {
0412 struct map_info *map;
0413 struct mtd_info *mtd;
0414 struct resource *add_range;
0415 struct resource *control_regs;
0416 struct pcm_int_data *pcm_data;
0417
0418
0419 pcm_data = devm_kzalloc(&pdev->dev, sizeof(*pcm_data), GFP_KERNEL);
0420 if (!pcm_data)
0421 return -ENOMEM;
0422
0423 pcm_data->bus_width = BUS_WIDTH;
0424
0425
0426 map = devm_kzalloc(&pdev->dev, sizeof(*map), GFP_KERNEL);
0427 if (!map)
0428 return -ENOMEM;
0429
0430 mtd = devm_kzalloc(&pdev->dev, sizeof(*mtd), GFP_KERNEL);
0431 if (!mtd)
0432 return -ENOMEM;
0433
0434
0435 add_range = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0436
0437
0438 *map = (struct map_info) {
0439 .virt = devm_ioremap_resource(&pdev->dev, add_range),
0440 .name = pdev->dev.init_name,
0441 .phys = add_range->start,
0442 .size = resource_size(add_range),
0443 .bankwidth = pcm_data->bus_width / 2,
0444 .pfow_base = OW_BASE_ADDRESS,
0445 .fldrv_priv = pcm_data,
0446 };
0447
0448 if (IS_ERR(map->virt))
0449 return PTR_ERR(map->virt);
0450
0451 simple_map_init(map);
0452
0453 control_regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
0454 pcm_data->ctl_regs = devm_ioremap_resource(&pdev->dev, control_regs);
0455 if (IS_ERR(pcm_data->ctl_regs))
0456 return PTR_ERR(pcm_data->ctl_regs);
0457
0458
0459 *mtd = lpddr2_nvm_mtd_info;
0460 mtd->dev.parent = &pdev->dev;
0461 mtd->name = pdev->dev.init_name;
0462 mtd->priv = map;
0463 mtd->size = resource_size(add_range);
0464 mtd->erasesize = ERASE_BLOCKSIZE * pcm_data->bus_width;
0465 mtd->writebufsize = WRITE_BUFFSIZE * pcm_data->bus_width;
0466
0467
0468 if (!lpddr2_nvm_pfow_present(map)) {
0469 pr_err("device not recognized\n");
0470 return -EINVAL;
0471 }
0472
0473 return mtd_device_register(mtd, NULL, 0);
0474 }
0475
0476
0477
0478
0479 static int lpddr2_nvm_remove(struct platform_device *pdev)
0480 {
0481 WARN_ON(mtd_device_unregister(dev_get_drvdata(&pdev->dev)));
0482
0483 return 0;
0484 }
0485
0486
0487 static struct platform_driver lpddr2_nvm_drv = {
0488 .driver = {
0489 .name = "lpddr2_nvm",
0490 },
0491 .probe = lpddr2_nvm_probe,
0492 .remove = lpddr2_nvm_remove,
0493 };
0494
0495 module_platform_driver(lpddr2_nvm_drv);
0496 MODULE_LICENSE("GPL");
0497 MODULE_AUTHOR("Vincenzo Aliberti <vincenzo.aliberti@gmail.com>");
0498 MODULE_DESCRIPTION("MTD driver for LPDDR2-NVM PCM memories");