1b98ac5e2SFrieder Schrempf // SPDX-License-Identifier: GPL-2.0 2b98ac5e2SFrieder Schrempf /* 3b98ac5e2SFrieder Schrempf * Copyright (c) 2017 exceet electronics GmbH 4b98ac5e2SFrieder Schrempf * 5b98ac5e2SFrieder Schrempf * Authors: 6b98ac5e2SFrieder Schrempf * Frieder Schrempf <frieder.schrempf@exceet.de> 7b98ac5e2SFrieder Schrempf * Boris Brezillon <boris.brezillon@bootlin.com> 8b98ac5e2SFrieder Schrempf */ 9b98ac5e2SFrieder Schrempf 10b98ac5e2SFrieder Schrempf #ifndef __UBOOT__ 11b98ac5e2SFrieder Schrempf #include <linux/device.h> 12b98ac5e2SFrieder Schrempf #include <linux/kernel.h> 13b98ac5e2SFrieder Schrempf #endif 14b98ac5e2SFrieder Schrempf #include <linux/mtd/spinand.h> 15b98ac5e2SFrieder Schrempf 16b98ac5e2SFrieder Schrempf #define SPINAND_MFR_WINBOND 0xEF 17b98ac5e2SFrieder Schrempf 18b98ac5e2SFrieder Schrempf #define WINBOND_CFG_BUF_READ BIT(3) 19b98ac5e2SFrieder Schrempf 20b98ac5e2SFrieder Schrempf static SPINAND_OP_VARIANTS(read_cache_variants, 21b98ac5e2SFrieder Schrempf SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), 22b98ac5e2SFrieder Schrempf SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), 23b98ac5e2SFrieder Schrempf SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), 24b98ac5e2SFrieder Schrempf SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), 25b98ac5e2SFrieder Schrempf SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), 26b98ac5e2SFrieder Schrempf SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); 27b98ac5e2SFrieder Schrempf 28b98ac5e2SFrieder Schrempf static SPINAND_OP_VARIANTS(write_cache_variants, 29b98ac5e2SFrieder Schrempf SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), 30b98ac5e2SFrieder Schrempf SPINAND_PROG_LOAD(true, 0, NULL, 0)); 31b98ac5e2SFrieder Schrempf 32b98ac5e2SFrieder Schrempf static SPINAND_OP_VARIANTS(update_cache_variants, 33b98ac5e2SFrieder Schrempf SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), 34b98ac5e2SFrieder Schrempf SPINAND_PROG_LOAD(false, 0, NULL, 0)); 35b98ac5e2SFrieder Schrempf 36b98ac5e2SFrieder Schrempf static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section, 37b98ac5e2SFrieder Schrempf struct mtd_oob_region *region) 38b98ac5e2SFrieder Schrempf { 39b98ac5e2SFrieder Schrempf if (section > 3) 40b98ac5e2SFrieder Schrempf return -ERANGE; 41b98ac5e2SFrieder Schrempf 42b98ac5e2SFrieder Schrempf region->offset = (16 * section) + 8; 43b98ac5e2SFrieder Schrempf region->length = 8; 44b98ac5e2SFrieder Schrempf 45b98ac5e2SFrieder Schrempf return 0; 46b98ac5e2SFrieder Schrempf } 47b98ac5e2SFrieder Schrempf 48b98ac5e2SFrieder Schrempf static int w25m02gv_ooblayout_free(struct mtd_info *mtd, int section, 49b98ac5e2SFrieder Schrempf struct mtd_oob_region *region) 50b98ac5e2SFrieder Schrempf { 51b98ac5e2SFrieder Schrempf if (section > 3) 52b98ac5e2SFrieder Schrempf return -ERANGE; 53b98ac5e2SFrieder Schrempf 54b98ac5e2SFrieder Schrempf region->offset = (16 * section) + 2; 55b98ac5e2SFrieder Schrempf region->length = 6; 56b98ac5e2SFrieder Schrempf 57b98ac5e2SFrieder Schrempf return 0; 58b98ac5e2SFrieder Schrempf } 59b98ac5e2SFrieder Schrempf 60b98ac5e2SFrieder Schrempf static const struct mtd_ooblayout_ops w25m02gv_ooblayout = { 61b98ac5e2SFrieder Schrempf .ecc = w25m02gv_ooblayout_ecc, 62b98ac5e2SFrieder Schrempf .free = w25m02gv_ooblayout_free, 63b98ac5e2SFrieder Schrempf }; 64b98ac5e2SFrieder Schrempf 65b98ac5e2SFrieder Schrempf static int w25m02gv_select_target(struct spinand_device *spinand, 66b98ac5e2SFrieder Schrempf unsigned int target) 67b98ac5e2SFrieder Schrempf { 68b98ac5e2SFrieder Schrempf struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0xc2, 1), 69b98ac5e2SFrieder Schrempf SPI_MEM_OP_NO_ADDR, 70b98ac5e2SFrieder Schrempf SPI_MEM_OP_NO_DUMMY, 71b98ac5e2SFrieder Schrempf SPI_MEM_OP_DATA_OUT(1, 72b98ac5e2SFrieder Schrempf spinand->scratchbuf, 73b98ac5e2SFrieder Schrempf 1)); 74b98ac5e2SFrieder Schrempf 75b98ac5e2SFrieder Schrempf *spinand->scratchbuf = target; 76b98ac5e2SFrieder Schrempf return spi_mem_exec_op(spinand->slave, &op); 77b98ac5e2SFrieder Schrempf } 78b98ac5e2SFrieder Schrempf 79b98ac5e2SFrieder Schrempf static const struct spinand_info winbond_spinand_table[] = { 80b98ac5e2SFrieder Schrempf SPINAND_INFO("W25M02GV", 0xAB, 81b98ac5e2SFrieder Schrempf NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 2), 82b98ac5e2SFrieder Schrempf NAND_ECCREQ(1, 512), 83b98ac5e2SFrieder Schrempf SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 84b98ac5e2SFrieder Schrempf &write_cache_variants, 85b98ac5e2SFrieder Schrempf &update_cache_variants), 86b98ac5e2SFrieder Schrempf 0, 87b98ac5e2SFrieder Schrempf SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), 88b98ac5e2SFrieder Schrempf SPINAND_SELECT_TARGET(w25m02gv_select_target)), 892ec05489SJon Lin SPINAND_INFO("W25N01GV", 0xAA, 902ec05489SJon Lin NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), 912ec05489SJon Lin NAND_ECCREQ(1, 512), 922ec05489SJon Lin SPINAND_INFO_OP_VARIANTS(&read_cache_variants, 932ec05489SJon Lin &write_cache_variants, 942ec05489SJon Lin &update_cache_variants), 952ec05489SJon Lin 0, 96*b2ee20d3SJason Zhu SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), 97*b2ee20d3SJason Zhu SPINAND_SELECT_TARGET(w25m02gv_select_target)), 98b98ac5e2SFrieder Schrempf }; 99b98ac5e2SFrieder Schrempf 100b98ac5e2SFrieder Schrempf /** 101b98ac5e2SFrieder Schrempf * winbond_spinand_detect - initialize device related part in spinand_device 102b98ac5e2SFrieder Schrempf * struct if it is a Winbond device. 103b98ac5e2SFrieder Schrempf * @spinand: SPI NAND device structure 104b98ac5e2SFrieder Schrempf */ 105b98ac5e2SFrieder Schrempf static int winbond_spinand_detect(struct spinand_device *spinand) 106b98ac5e2SFrieder Schrempf { 107b98ac5e2SFrieder Schrempf u8 *id = spinand->id.data; 108b98ac5e2SFrieder Schrempf int ret; 109b98ac5e2SFrieder Schrempf 110b98ac5e2SFrieder Schrempf /* 111b98ac5e2SFrieder Schrempf * Winbond SPI NAND read ID need a dummy byte, 112b98ac5e2SFrieder Schrempf * so the first byte in raw_id is dummy. 113b98ac5e2SFrieder Schrempf */ 114b98ac5e2SFrieder Schrempf if (id[1] != SPINAND_MFR_WINBOND) 115b98ac5e2SFrieder Schrempf return 0; 116b98ac5e2SFrieder Schrempf 117b98ac5e2SFrieder Schrempf ret = spinand_match_and_init(spinand, winbond_spinand_table, 118b98ac5e2SFrieder Schrempf ARRAY_SIZE(winbond_spinand_table), id[2]); 119b98ac5e2SFrieder Schrempf if (ret) 120b98ac5e2SFrieder Schrempf return ret; 121b98ac5e2SFrieder Schrempf 122b98ac5e2SFrieder Schrempf return 1; 123b98ac5e2SFrieder Schrempf } 124b98ac5e2SFrieder Schrempf 125b98ac5e2SFrieder Schrempf static int winbond_spinand_init(struct spinand_device *spinand) 126b98ac5e2SFrieder Schrempf { 127b98ac5e2SFrieder Schrempf struct nand_device *nand = spinand_to_nand(spinand); 128b98ac5e2SFrieder Schrempf unsigned int i; 129b98ac5e2SFrieder Schrempf 130b98ac5e2SFrieder Schrempf /* 131b98ac5e2SFrieder Schrempf * Make sure all dies are in buffer read mode and not continuous read 132b98ac5e2SFrieder Schrempf * mode. 133b98ac5e2SFrieder Schrempf */ 134b98ac5e2SFrieder Schrempf for (i = 0; i < nand->memorg.ntargets; i++) { 135b98ac5e2SFrieder Schrempf spinand_select_target(spinand, i); 136b98ac5e2SFrieder Schrempf spinand_upd_cfg(spinand, WINBOND_CFG_BUF_READ, 137b98ac5e2SFrieder Schrempf WINBOND_CFG_BUF_READ); 138b98ac5e2SFrieder Schrempf } 139b98ac5e2SFrieder Schrempf 140b98ac5e2SFrieder Schrempf return 0; 141b98ac5e2SFrieder Schrempf } 142b98ac5e2SFrieder Schrempf 143b98ac5e2SFrieder Schrempf static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { 144b98ac5e2SFrieder Schrempf .detect = winbond_spinand_detect, 145b98ac5e2SFrieder Schrempf .init = winbond_spinand_init, 146b98ac5e2SFrieder Schrempf }; 147b98ac5e2SFrieder Schrempf 148b98ac5e2SFrieder Schrempf const struct spinand_manufacturer winbond_spinand_manufacturer = { 149b98ac5e2SFrieder Schrempf .id = SPINAND_MFR_WINBOND, 150b98ac5e2SFrieder Schrempf .name = "Winbond", 151b98ac5e2SFrieder Schrempf .ops = &winbond_spinand_manuf_ops, 152b98ac5e2SFrieder Schrempf }; 153