11075492bSFrieder Schrempf // SPDX-License-Identifier: GPL-2.0 21075492bSFrieder Schrempf /* 31075492bSFrieder Schrempf * Copyright (c) 2017 exceet electronics GmbH 41075492bSFrieder Schrempf * 51075492bSFrieder Schrempf * Authors: 61075492bSFrieder Schrempf * Frieder Schrempf <frieder.schrempf@exceet.de> 71075492bSFrieder Schrempf * Boris Brezillon <boris.brezillon@bootlin.com> 81075492bSFrieder Schrempf */ 91075492bSFrieder Schrempf 101075492bSFrieder Schrempf #include <linux/device.h> 111075492bSFrieder Schrempf #include <linux/kernel.h> 121075492bSFrieder Schrempf #include <linux/mtd/spinand.h> 131075492bSFrieder Schrempf 141075492bSFrieder Schrempf #define SPINAND_MFR_WINBOND 0xEF 151075492bSFrieder Schrempf 161075492bSFrieder Schrempf #define WINBOND_CFG_BUF_READ BIT(3) 171075492bSFrieder Schrempf 181075492bSFrieder Schrempf static SPINAND_OP_VARIANTS(read_cache_variants, 191075492bSFrieder Schrempf SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), 201075492bSFrieder Schrempf SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), 211075492bSFrieder Schrempf SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), 221075492bSFrieder Schrempf SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), 231075492bSFrieder Schrempf SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), 241075492bSFrieder Schrempf SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); 251075492bSFrieder Schrempf 261075492bSFrieder Schrempf static SPINAND_OP_VARIANTS(write_cache_variants, 271075492bSFrieder Schrempf SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), 281075492bSFrieder Schrempf SPINAND_PROG_LOAD(true, 0, NULL, 0)); 291075492bSFrieder Schrempf 301075492bSFrieder Schrempf static SPINAND_OP_VARIANTS(update_cache_variants, 311075492bSFrieder Schrempf SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), 321075492bSFrieder Schrempf SPINAND_PROG_LOAD(false, 0, NULL, 0)); 331075492bSFrieder Schrempf 341075492bSFrieder Schrempf static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section, 351075492bSFrieder Schrempf struct mtd_oob_region *region) 361075492bSFrieder Schrempf { 371075492bSFrieder Schrempf if (section > 3) 381075492bSFrieder Schrempf return -ERANGE; 391075492bSFrieder Schrempf 401075492bSFrieder Schrempf region->offset = (16 * section) + 8; 411075492bSFrieder Schrempf region->length = 8; 421075492bSFrieder Schrempf 431075492bSFrieder Schrempf return 0; 441075492bSFrieder Schrempf } 451075492bSFrieder Schrempf 461075492bSFrieder Schrempf static int w25m02gv_ooblayout_free(struct mtd_info *mtd, int section, 471075492bSFrieder Schrempf struct mtd_oob_region *region) 481075492bSFrieder Schrempf { 491075492bSFrieder Schrempf if (section > 3) 501075492bSFrieder Schrempf return -ERANGE; 511075492bSFrieder Schrempf 521075492bSFrieder Schrempf region->offset = (16 * section) + 2; 531075492bSFrieder Schrempf region->length = 6; 541075492bSFrieder Schrempf 551075492bSFrieder Schrempf return 0; 561075492bSFrieder Schrempf } 571075492bSFrieder Schrempf 581075492bSFrieder Schrempf static const struct mtd_ooblayout_ops w25m02gv_ooblayout = { 591075492bSFrieder Schrempf .ecc = w25m02gv_ooblayout_ecc, 601075492bSFrieder Schrempf .free = w25m02gv_ooblayout_free, 611075492bSFrieder Schrempf }; 621075492bSFrieder Schrempf 631075492bSFrieder Schrempf static int w25m02gv_select_target(struct spinand_device *spinand, 641075492bSFrieder Schrempf unsigned int target) 651075492bSFrieder Schrempf { 661075492bSFrieder Schrempf struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1), 671075492bSFrieder Schrempf SPI_MEM_OP_NO_ADDR, 681075492bSFrieder Schrempf SPI_MEM_OP_NO_DUMMY, 691075492bSFrieder Schrempf SPI_MEM_OP_DATA_OUT(1, 701075492bSFrieder Schrempf spinand->scratchbuf, 711075492bSFrieder Schrempf 1)); 721075492bSFrieder Schrempf 731075492bSFrieder Schrempf *spinand->scratchbuf = target; 741075492bSFrieder Schrempf return spi_mem_exec_op(spinand->spimem, &op); 751075492bSFrieder Schrempf } 761075492bSFrieder Schrempf 771075492bSFrieder Schrempf static const struct spinand_info winbond_spinand_table[] = { 78*f1541773SChuanhong Guo SPINAND_INFO("W25M02GV", 79*f1541773SChuanhong Guo SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab), 80377e517bSBoris Brezillon NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2), 811075492bSFrieder Schrempf NAND_ECCREQ(1, 512), 821075492bSFrieder Schrempf SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 831075492bSFrieder Schrempf &write_cache_variants, 841075492bSFrieder Schrempf &update_cache_variants), 851075492bSFrieder Schrempf 0, 861075492bSFrieder Schrempf SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), 871075492bSFrieder Schrempf SPINAND_SELECT_TARGET(w25m02gv_select_target)), 88*f1541773SChuanhong Guo SPINAND_INFO("W25N01GV", 89*f1541773SChuanhong Guo SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa), 90377e517bSBoris Brezillon NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), 919a4d8307SRobert Marko NAND_ECCREQ(1, 512), 929a4d8307SRobert Marko SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 939a4d8307SRobert Marko &write_cache_variants, 949a4d8307SRobert Marko &update_cache_variants), 959a4d8307SRobert Marko 0, 969a4d8307SRobert Marko SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), 971075492bSFrieder Schrempf }; 981075492bSFrieder Schrempf 991075492bSFrieder Schrempf static int winbond_spinand_init(struct spinand_device *spinand) 1001075492bSFrieder Schrempf { 1011075492bSFrieder Schrempf struct nand_device *nand = spinand_to_nand(spinand); 1021075492bSFrieder Schrempf unsigned int i; 1031075492bSFrieder Schrempf 1041075492bSFrieder Schrempf /* 1051075492bSFrieder Schrempf * Make sure all dies are in buffer read mode and not continuous read 1061075492bSFrieder Schrempf * mode. 1071075492bSFrieder Schrempf */ 1081075492bSFrieder Schrempf for (i = 0; i < nand->memorg.ntargets; i++) { 1091075492bSFrieder Schrempf spinand_select_target(spinand, i); 1101075492bSFrieder Schrempf spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ, 1111075492bSFrieder Schrempf WINBOND_CFG_BUF_READ); 1121075492bSFrieder Schrempf } 1131075492bSFrieder Schrempf 1141075492bSFrieder Schrempf return 0; 1151075492bSFrieder Schrempf } 1161075492bSFrieder Schrempf 1171075492bSFrieder Schrempf static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { 1181075492bSFrieder Schrempf .init = winbond_spinand_init, 1191075492bSFrieder Schrempf }; 1201075492bSFrieder Schrempf 1211075492bSFrieder Schrempf const struct spinand_manufacturer winbond_spinand_manufacturer = { 1221075492bSFrieder Schrempf .id = SPINAND_MFR_WINBOND, 1231075492bSFrieder Schrempf .name = "Winbond", 124*f1541773SChuanhong Guo .chips = winbond_spinand_table, 125*f1541773SChuanhong Guo .nchips = ARRAY_SIZE(winbond_spinand_table), 1261075492bSFrieder Schrempf .ops = &winbond_spinand_manuf_ops, 1271075492bSFrieder Schrempf }; 128