/* Copyright (c) 2015, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include #include #include #include #include #include "giga_spinand.h" /* OOB area specification layout for W25N01GV */ /* see Datasheet W25N01GV_RevO_092619.pdf */ /* ECC parity code stored automatically by onchip ecc-engine */ static struct nand_ecclayout winbond_oob_64 = { .eccbytes = 0, .eccpos = { }, .oobavail = 16, .oobfree = { {.offset = 4, .length = 4}, {.offset = 20, .length = 4}, {.offset = 36, .length = 4}, {.offset = 52, .length = 4}, } }; /* Only ecc un-protected fields in the spare area included */ static struct nand_ecclayout ath79_spinand_oob_128_gd = { .eccbytes = 64, .eccpos = { 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}, .oobfree = { {.offset = 16, .length = 3}, {.offset = 32, .length = 3}, {.offset = 48, .length = 3}, }, }; /* Only ecc un-protected fields in the spare area included */ /* ECC parity code stored in the additional hidden spare area */ static struct nand_ecclayout fidelix_oob_64 = { .eccbytes = 0, .eccpos = {}, .oobfree = { {.offset = 0, .length = 4}, {.offset = 8, .length = 12}, {.offset = 24, .length = 12}, {.offset = 40, .length = 12}, {.offset = 56, .length = 8}, } }; /* Only ecc un-protected fields in the spare area included */ /* ECC parity code stored in the additional hidden spare area */ static struct nand_ecclayout macronix_oob_64 = { .eccbytes = 0, .eccpos = {}, .oobfree = { {.offset = 2, .length = 2}, {.offset = 18, .length = 2}, {.offset = 34, .length = 2}, {.offset = 50, .length = 2}, } }; void gigadevice_set_defaults_128mb(struct spi_device *spi_nand) { struct mtd_info *mtd = (struct mtd_info *)dev_get_drvdata (&spi_nand->dev); struct nand_chip *chip = (struct nand_chip *)mtd->priv; chip->ecc.size = 0x800; chip->ecc.bytes = 0x0; chip->ecc.steps = 0x0; chip->ecc.strength = 1; chip->ecc.total = 0; chip->ecc.layout = &ath79_spinand_oob_128_gd; } void gigadevice_set_defaults(struct spi_device *spi_nand) { struct mtd_info *mtd = (struct mtd_info *)dev_get_drvdata (&spi_nand->dev); struct nand_chip *chip = (struct nand_chip *)mtd->priv; chip->ecc.size = 0x800; chip->ecc.bytes = 0x0; chip->ecc.steps = 0x0; chip->ecc.strength = 1; chip->ecc.total = 0; chip->ecc.layout = NULL; } void gigadevice_set_defaults_512mb(struct spi_device *spi_nand) { struct mtd_info *mtd = (struct mtd_info *)dev_get_drvdata (&spi_nand->dev); struct nand_chip *chip = (struct nand_chip *)mtd->priv; chip->ecc.size = 0x1000; chip->ecc.bytes = 0x0; chip->ecc.steps = 0x0; chip->ecc.strength = 1; chip->ecc.total = 0; chip->ecc.layout = NULL; } void winbond_set_defaults(struct spi_device *spi_nand) { struct mtd_info *mtd = dev_get_drvdata(&spi_nand->dev); struct nand_chip *chip = (struct nand_chip *)mtd->priv; chip->ecc.size = 0x800; chip->ecc.bytes = 0x0; chip->ecc.steps = 0x0; chip->ecc.strength = 1; chip->ecc.total = 0; chip->ecc.layout = &winbond_oob_64; } void macronix_set_defaults(struct spi_device *spi_nand) { struct mtd_info *mtd = dev_get_drvdata(&spi_nand->dev); struct nand_chip *chip = (struct nand_chip *)mtd->priv; chip->ecc.size = 0x800; chip->ecc.bytes = 0x0; chip->ecc.steps = 0x0; chip->ecc.strength = 1; chip->ecc.total = 0; chip->ecc.layout = ¯onix_oob_64; } void fidelix_set_defaults(struct spi_device *spi_nand) { struct mtd_info *mtd = dev_get_drvdata(&spi_nand->dev); struct nand_chip *chip = (struct nand_chip *)mtd->priv; chip->ecc.size = 0x800; chip->ecc.bytes = 0x0; chip->ecc.steps = 0x0; chip->ecc.strength = 1; chip->ecc.total = 0; chip->ecc.layout = &fidelix_oob_64; } void gigadevice_read_cmd(struct spinand_cmd *cmd, u32 page_id) { cmd->addr[0] = (u8)(page_id >> 16); cmd->addr[1] = (u8)(page_id >> 8); cmd->addr[2] = (u8)(page_id); } void toshiba_read_cmd(struct spinand_cmd *cmd, u32 page_id) { cmd->addr[0] = ((u8)(page_id >> 16) % 2); cmd->addr[1] = (u8)(page_id >> 8); cmd->addr[2] = (u8)(page_id); } void fidelix_read_cmd(struct spinand_cmd *cmd, u32 page_id) { u32 plane_id = GET_PLANE_ID(page_id); if (plane_id) page_id = page_id - FIDELIX_PAGES_PER_PLANE; page_id = INSERT_PLANE_CMD_BIT(page_id, plane_id); cmd->addr[0] = (u8)(page_id >> 16); cmd->addr[1] = (u8)(page_id >> 8); cmd->addr[2] = (u8)(page_id); } void gigadevice_read_data(struct spinand_cmd *cmd, u16 column, u32 page_id) { cmd->addr[0] = 0xff; /*dummy byte*/ cmd->addr[1] = (u8)(column >> 8); cmd->addr[2] = (u8)(column); } void gigadevice_read_data_v2(struct spinand_cmd *cmd, u16 column, u32 page_id) { cmd->addr[0] = (u8)(column >> 8); cmd->addr[1] = (u8)(column); cmd->addr[2] = 0xff; /*dummy byte*/ } void macronix_read_data(struct spinand_cmd *cmd, u16 column, u32 page_id) { cmd->addr[0] = ((u8)(column >> 8) & MACRONIX_NORM_RW_MASK); cmd->addr[1] = (u8)(column); } void winbond_read_data(struct spinand_cmd *cmd, u16 column, u32 page_id) { cmd->addr[0] = (u8)(column >> 8); cmd->addr[1] = (u8)(column); } void toshiba_read_data(struct spinand_cmd *cmd, u16 column, u32 page_id) { cmd->addr[0] = ((u8)(column >> 8) & TOSHIBA_NORM_RW_MASK); cmd->addr[1] = (u8)(column); } void fidelix_read_data(struct spinand_cmd *cmd, u16 column, u32 page_id) { cmd->addr[0] = (((u8)(column >> 8) & FIDELIX_NORM_RW_MASK) | SET_CMD_PLANE_BIT(GET_PLANE_ID(page_id))); cmd->addr[1] = (u8)(column); } void fidelix_write_cmd(struct spinand_cmd *cmd, u32 page_id) { u32 plane_id = GET_PLANE_ID(page_id); if (plane_id) page_id = page_id - FIDELIX_PAGES_PER_PLANE; page_id = INSERT_PLANE_CMD_BIT(page_id, plane_id); cmd->addr[0] = (u8)(page_id >> 16); cmd->addr[1] = (u8)(page_id >> 8); cmd->addr[2] = (u8)(page_id); } void gigadevice_write_cmd(struct spinand_cmd *cmd, u32 page_id) { cmd->addr[0] = (u8)(page_id >> 16); cmd->addr[1] = (u8)(page_id >> 8); cmd->addr[2] = (u8)(page_id); } void toshiba_write_cmd(struct spinand_cmd *cmd, u32 page_id) { cmd->addr[0] = ((u8)(page_id >> 16) % 2); cmd->addr[1] = (u8)(page_id >> 8); cmd->addr[2] = (u8)(page_id); } void gigadevice_write_data(struct spinand_cmd *cmd, u16 column, u32 page_id) { cmd->addr[0] = (u8)(column >> 8); cmd->addr[1] = (u8)(column); } void macronix_write_data(struct spinand_cmd *cmd, u16 column, u32 page_id) { cmd->addr[0] = ((u8)(column >> 8) & MACRONIX_NORM_RW_MASK); cmd->addr[1] = (u8)(column); } void winbond_write_data(struct spinand_cmd *cmd, u16 column, u32 page_id) { cmd->addr[0] = (u8)(column >> 8); cmd->addr[1] = (u8)(column); } void toshiba_write_data(struct spinand_cmd *cmd, u16 column, u32 page_id) { cmd->addr[0] = ((u8)(column >> 8) & TOSHIBA_NORM_RW_MASK); cmd->addr[1] = (u8)(column); } void fidelix_write_data(struct spinand_cmd *cmd, u16 column, u32 page_id) { cmd->addr[0] = (((u8)(column >> 8) & FIDELIX_NORM_RW_MASK) | SET_CMD_PLANE_BIT(GET_PLANE_ID(page_id))); cmd->addr[1] = (u8)(column); } void fidelix_erase_blk(struct spinand_cmd *cmd, u32 page_id) { u32 plane_id = GET_PLANE_ID(page_id); if (plane_id) page_id = page_id - FIDELIX_PAGES_PER_PLANE; page_id = INSERT_PLANE_CMD_BIT(page_id, plane_id); cmd->addr[0] = (u8)(page_id >> 16); cmd->addr[1] = (u8)(page_id >> 8); cmd->addr[2] = (u8)(page_id); } void gigadevice_erase_blk(struct spinand_cmd *cmd, u32 page_id) { cmd->addr[0] = (u8)(page_id >> 16); cmd->addr[1] = (u8)(page_id >> 8); cmd->addr[2] = (u8)(page_id); } void toshiba_erase_blk(struct spinand_cmd *cmd, u32 page_id) { cmd->addr[0] = (u8)((page_id >> 16) % 2); cmd->addr[1] = (u8)(page_id >> 8); cmd->addr[2] = (u8)(page_id); } int gigadevice_verify_ecc(u8 status) { int ecc_status = (status & STATUS_ECC_MASK_GIGA); if (ecc_status == STATUS_ECC_ERROR_GIGA) return SPINAND_ECC_ERROR; else if (ecc_status >= STATUS_ECC_BF_THRESHOLD_GIGA) return SPINAND_ECC_CORRECTED; else return 0; } int macronix_verify_ecc(u8 status) { int ecc_status = (status & STATUS_ECC_MASK_MACRONIX); if ((ecc_status == STATUS_ECC_ERROR_MACRONIX) || (ecc_status == STATUS_ECC_MASK_MACRONIX)) return SPINAND_ECC_ERROR; else if (ecc_status) return SPINAND_ECC_CORRECTED; else return 0; } int toshiba_verify_ecc(u8 status) { int ecc_status = (status & STATUS_ECC_MASK_TOSHIBA); if (ecc_status == STATUS_ECC_ERROR_TOSHIBA) return SPINAND_ECC_ERROR; else if (ecc_status == STATUS_ECC_BF_THRESHOLD_TOSHIBA) return SPINAND_ECC_CORRECTED; else return 0; } int dummy_verify_ecc(u8 status) { return 0; } int gigadevice_parse_id(struct spi_device *spi_nand, struct spinand_ops *ops, u8 *nand_id, u8 *id) { if (nand_id[0] != NAND_MFR_GIGA && nand_id[0] != NAND_MFR_ATO) return -EINVAL; if (!(nand_id[0] == NAND_MFR_GIGA && nand_id[1] == ops->dev_id)) return -EINVAL; id[0] = nand_id[0]; id[1] = nand_id[1]; return 0; } int gigadevice_parse_id_v2(struct spi_device *spi_nand, struct spinand_ops *ops, u8 *nand_id, u8 *id) { if (nand_id[1] != NAND_MFR_GIGA && nand_id[1] != NAND_MFR_ATO) return -EINVAL; if (!(nand_id[1] == NAND_MFR_GIGA && nand_id[2] == ops->dev_id)) return -EINVAL; id[0] = nand_id[1]; id[1] = nand_id[2]; return 0; } int gigadevice_parse_id_v3(struct spi_device *spi_nand, struct spinand_ops *ops, u8 *nand_id, u8 *id) { if (!(nand_id[2] == NAND_MFR_GIGA && nand_id[3] == ops->dev_id)) return -EINVAL; id[0] = nand_id[2]; id[1] = nand_id[3]; return 0; } int macronix_parse_id(struct spi_device *spi_nand, struct spinand_ops *ops, u8 *nand_id, u8 *id) { if (nand_id[1] != NAND_MFR_MACRONIX) return -EINVAL; return 0; } int fidelix_parse_id(struct spi_device *spi_nand, struct spinand_ops *ops, u8 *nand_id, u8 *id) { if (nand_id[1] != NAND_MFR_FIDELIX) return -EINVAL; return 0; } int winbond_parse_id(struct spi_device *spi_nand, struct spinand_ops *ops, u8 *nand_id, u8 *id) { if (!(nand_id[1] == NAND_MFR_WINBOND && nand_id[2] == ops->dev_id)) return -EINVAL; return 0; } int toshiba_parse_id(struct spi_device *spi_nand, struct spinand_ops *ops, u8 *nand_id, u8 *id) { if (!(nand_id[1] == NAND_MFR_TOSHIBA && nand_id[2] == ops->dev_id)) return -EINVAL; return 0; } MODULE_DESCRIPTION("SPI NAND driver for Gigadevice and Macronix");