| /* |
| * NAND Flash Controller Device Driver |
| * Copyright © 2015-2016, Cadence and its suppliers. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms and conditions of the GNU General Public License, |
| * version 2, as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * for more details. |
| * |
| */ |
| |
| #include <linux/clk.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/interrupt.h> |
| #include <linux/module.h> |
| #include <linux/mtd/mtd.h> |
| #include <linux/mtd/rawnand.h> |
| #include <linux/mutex.h> |
| #include <linux/platform_device.h> |
| #include <linux/reset.h> |
| |
| #include "nand_randomizer.h" |
| |
| #define RANDOM_DATA_LENGTH 4096 |
| /***************************************************/ |
| /* Register definition */ |
| /***************************************************/ |
| |
| /* Command register 0. |
| * Writing data to this register will initiate a new transaction |
| * of the NF controller. */ |
| #define HPNFC_CMD_REG0 0x0000 |
| /* command type field shift */ |
| #define HPNFC_CMD_REG0_CT_SHIFT 30 |
| /* command type field mask */ |
| #define HPNFC_CMD_REG0_CT_MASK (3uL << 30) |
| /* command type CDMA */ |
| #define HPNFC_CMD_REG0_CT_CDMA (0uL) |
| /* command type PIO */ |
| #define HPNFC_CMD_REG0_CT_PIO (1uL) |
| /* command type reset */ |
| #define HPNFC_CMD_REG0_CT_RST (2uL) |
| /* command type generic */ |
| #define HPNFC_CMD_REG0_CT_GEN (3uL) |
| /* command thread number field shift */ |
| #define HPNFC_CMD_REG0_TN_SHIFT 24 |
| /* command thread number field mask */ |
| #define HPNFC_CMD_REG0_TN_MASK (3uL << 24) |
| /* command code field shift */ |
| #define HPNFC_CMD_REG0_PIO_CC_SHIFT 0 |
| /* command code field mask */ |
| #define HPNFC_CMD_REG0_PIO_CC_MASK (0xFFFFuL << 0) |
| /* command code - read page */ |
| #define HPNFC_CMD_REG0_PIO_CC_RD (0x2200uL) |
| /* command code - write page */ |
| #define HPNFC_CMD_REG0_PIO_CC_WR (0x2100uL) |
| /* command code - copy back */ |
| #define HPNFC_CMD_REG0_PIO_CC_CPB (0x1200uL) |
| /* command code - reset */ |
| #define HPNFC_CMD_REG0_PIO_CC_RST (0x1100uL) |
| /* command code - set feature */ |
| #define HPNFC_CMD_REG0_PIO_CC_SF (0x0100uL) |
| /* command interrupt shift */ |
| #define HPNFC_CMD_REG0_INT_SHIFT 20 |
| /* command interrupt mask */ |
| #define HPNFC_CMD_REG0_INT_MASK (1uL << 20) |
| |
| /* PIO command - volume ID - shift */ |
| #define HPNFC_CMD_REG0_VOL_ID_SHIFT 16 |
| /* PIO command - volume ID - mask */ |
| #define HPNFC_CMD_REG0_VOL_ID_MASK (0xFuL << 16) |
| |
| /* Command register 1. */ |
| #define HPNFC_CMD_REG1 0x0004 |
| /* PIO command - bank number - shift */ |
| #define HPNFC_CMD_REG1_BANK_SHIFT 24 |
| /* PIO command - bank number - mask */ |
| #define HPNFC_CMD_REG1_BANK_MASK (0x3uL << 24) |
| /* PIO command - set feature - feature address - shift */ |
| #define HPNFC_CMD_REG1_FADDR_SHIFT 0 |
| /* PIO command - set feature - feature address - mask */ |
| #define HPNFC_CMD_REG1_FADDR_MASK (0xFFuL << 0) |
| |
| /* Command register 2 */ |
| #define HPNFC_CMD_REG2 0x0008 |
| /* Command register 3 */ |
| #define HPNFC_CMD_REG3 0x000C |
| /* Pointer register to select which thread status will be selected. */ |
| #define HPNFC_CMD_STATUS_PTR 0x0010 |
| /* Command status register for selected thread */ |
| #define HPNFC_CMD_STATUS 0x0014 |
| |
| /* interrupt status register */ |
| #define HPNFC_INTR_STATUS 0x0110 |
| #define HPNFC_INTR_STATUS_SDMA_ERR_MASK (1uL << 22) |
| #define HPNFC_INTR_STATUS_SDMA_TRIGG_MASK (1uL << 21) |
| #define HPNFC_INTR_STATUS_UNSUPP_CMD_MASK (1uL << 19) |
| #define HPNFC_INTR_STATUS_DDMA_TERR_MASK (1uL << 18) |
| #define HPNFC_INTR_STATUS_CDMA_TERR_MASK (1uL << 17) |
| #define HPNFC_INTR_STATUS_CDMA_IDL_MASK (1uL << 16) |
| |
| /* interrupt enable register */ |
| #define HPNFC_INTR_ENABLE 0x0114 |
| #define HPNFC_INTR_ENABLE_INTR_EN_MASK (1uL << 31) |
| #define HPNFC_INTR_ENABLE_SDMA_ERR_EN_MASK (1uL << 22) |
| #define HPNFC_INTR_ENABLE_SDMA_TRIGG_EN_MASK (1uL << 21) |
| #define HPNFC_INTR_ENABLE_UNSUPP_CMD_EN_MASK (1uL << 19) |
| #define HPNFC_INTR_ENABLE_DDMA_TERR_EN_MASK (1uL << 18) |
| #define HPNFC_INTR_ENABLE_CDMA_TERR_EN_MASK (1uL << 17) |
| #define HPNFC_INTR_ENABLE_CDMA_IDLE_EN_MASK (1uL << 16) |
| |
| /* Controller internal state */ |
| #define HPNFC_CTRL_STATUS 0x0118 |
| #define HPNFC_CTRL_STATUS_INIT_COMP_MASK (1uL << 9) |
| #define HPNFC_CTRL_STATUS_CTRL_BUSY_MASK (1uL << 8) |
| |
| /* Command Engine threads state */ |
| #define HPNFC_TRD_STATUS 0x0120 |
| |
| /* Command Engine interrupt thread error status */ |
| #define HPNFC_TRD_ERR_INT_STATUS 0x0128 |
| /* Command Engine interrupt thread error enable */ |
| #define HPNFC_TRD_ERR_INT_STATUS_EN 0x0130 |
| /* Command Engine interrupt thread complete status*/ |
| #define HPNFC_TRD_COMP_INT_STATUS 0x0138 |
| |
| /* Transfer config 0 register. |
| * Configures data transfer parameters. */ |
| #define HPNFC_TRAN_CFG_0 0x0400 |
| /* Offset value from the beginning of the page - shift */ |
| #define HPNFC_TRAN_CFG_0_OFFSET_SHIFT 16 |
| /* Offset value from the beginning of the page - mask */ |
| #define HPNFC_TRAN_CFG_0_OFFSET_MASK (0xFFFFuL << 16) |
| /* Numbers of sectors to transfer within single NF device's page */ |
| #define HPNFC_TRAN_CFG_0_SEC_CNT_SHIFT 0 |
| #define HPNFC_TRAN_CFG_0_SEC_CNT_MASK (0xFFuL << 0) |
| |
| /* Transfer config 1 register. |
| * Configures data transfer parameters. */ |
| #define HPNFC_TRAN_CFG_1 0x0404 |
| /* Size of last data sector. - shift */ |
| #define HPNFC_TRAN_CFG_1_LAST_SEC_SIZE_SHIFT 16 |
| /* Size of last data sector. - mask */ |
| #define HPNFC_TRAN_CFG_1_LAST_SEC_SIZE_MASK (0xFFFFuL << 16) |
| /* Size of not-last data sector. - shift*/ |
| #define HPNFC_TRAN_CFG_1_SECTOR_SIZE_SHIFT 0 |
| /* Size of not-last data sector. - last*/ |
| #define HPNFC_TRAN_CFG_1_SECTOR_SIZE_MASK (0xFFFFuL << 0) |
| |
| /* NF device layout. */ |
| #define HPNFC_NF_DEV_LAYOUT 0x0424 |
| /* Bit in ROW address used for selecting of the LUN - shift */ |
| #define HPNFC_NF_DEV_LAYOUT_ROWAC_SHIFT 24 |
| /* Bit in ROW address used for selecting of the LUN - mask */ |
| #define HPNFC_NF_DEV_LAYOUT_ROWAC_MASK (0xFuL << 24) |
| /* The number of LUN presents in the device. - shift */ |
| #define HPNFC_NF_DEV_LAYOUT_LN_SHIFT 20 |
| /* The number of LUN presents in the device. - mask */ |
| #define HPNFC_NF_DEV_LAYOUT_LN_MASK (0xF << 20) |
| /* Enables Multi LUN operations - mask */ |
| #define HPNFC_NF_DEV_LAYOUT_LUN_EN_MASK (1uL << 16) |
| /* Pages Per Block - number of pages in a block - shift */ |
| #define HPNFC_NF_DEV_LAYOUT_PPB_SHIFT 0 |
| /* Pages Per Block - number of pages in a block - mask */ |
| #define HPNFC_NF_DEV_LAYOUT_PPB_MASK (0xFFFFuL << 0) |
| |
| /* ECC engine configuration register 0. */ |
| #define HPNFC_ECC_CONFIG_0 0x0428 |
| /* Correction strength - shift */ |
| #define HPNFC_ECC_CONFIG_0_CORR_STR_SHIFT 8 |
| /* Correction strength - mask */ |
| #define HPNFC_ECC_CONFIG_0_CORR_STR_MASK (3uL << 8) |
| /* Enables scrambler logic in the controller - mask */ |
| #define HPNFC_ECC_CONFIG_0_SCRAMBLER_EN_MASK (1uL << 2) |
| /* Enable erased pages detection mechanism - mask */ |
| #define HPNFC_ECC_CONFIG_0_ERASE_DET_EN_MASK (1uL << 1) |
| /* Enable controller ECC check bits generation and correction - mask */ |
| #define HPNFC_ECC_CONFIG_0_ECC_EN_MASK (1uL << 0) |
| /* ECC engine configuration register 1. */ |
| #define HPNFC_ECC_CONFIG_1 0x042C |
| |
| /* Multiplane settings register */ |
| #define HPNFC_MULTIPLANE_CFG 0x0434 |
| /* Cache operation settings. */ |
| #define HPNFC_CACHE_CFG 0x0438 |
| |
| /* DMA settings register */ |
| #define HPNFC_DMA_SETTINGS 0x043C |
| /* Enable SDMA error report on access unprepared slave DMA interface */ |
| #define HPNFC_DMA_SETTINGS_SDMA_ERR_RSP_MASK (1uL << 17) |
| /* Outstanding transaction enable - mask */ |
| #define HPNFC_DMA_SETTINGS_OTE_MASK (1uL << 16) |
| /* DMA burst selection - shift */ |
| #define HPNFC_DMA_SETTINGS_BURST_SEL_SHIFT 0 |
| /* DMA burst selection - mask */ |
| #define HPNFC_DMA_SETTINGS_BURST_SEL_MASK (0xFFuL << 0) |
| |
| /* Transferred data block size for the slave DMA module */ |
| #define HPNFC_SDMA_SIZE 0x0440 |
| |
| /* Thread number associated with transferred data block |
| * for the slave DMA module */ |
| #define HPNFC_SDMA_TRD_NUM 0x0444 |
| /* Thread number mask */ |
| #define HPNFC_SDMA_TRD_NUM_SDMA_TRD_MASK (0x3 << 0) |
| /* Thread number shift */ |
| #define HPNFC_SDMA_TRD_NUM_SDMA_TRD_SHIFT (0) |
| |
| /* available hardware features of the controller */ |
| #define HPNFC_CTRL_FEATURES 0x804 |
| /* Support for NV-DDR2/3 work mode - mask */ |
| #define HPNFC_CTRL_FEATURES_NVDDR_2_3_MASK (1 << 28) |
| /* Support for NV-DDR2/3 work mode - shift */ |
| #define HPNFC_CTRL_FEATURES_NVDDR_2_3_SHIFT 28 |
| /* Support for NV-DDR work mode - mask */ |
| #define HPNFC_CTRL_FEATURES_NVDDR_MASK (1 << 27) |
| /* Support for NV-DDR work mode - shift */ |
| #define HPNFC_CTRL_FEATURES_NVDDR_SHIFT 27 |
| /* Support for asynchronous work mode - mask */ |
| #define HPNFC_CTRL_FEATURES_ASYNC_MASK (1 << 26) |
| /* Support for asynchronous work mode - shift */ |
| #define HPNFC_CTRL_FEATURES_ASYNC_SHIFT 26 |
| /* Support for number of banks supported by HW - mask */ |
| #define HPNFC_CTRL_FEATURES_N_BANKS_MASK (3 << 24) |
| /* Support for number of banks supported by HW - shift */ |
| #define HPNFC_CTRL_FEATURES_N_BANKS_SHIFT 24 |
| /* Slave and Master DMA data width - mask */ |
| #define HPNFC_CTRL_FEATURES_DMA_DWITH_64_MASK (1 << 21) |
| /* Slave and Master DMA data width - shift */ |
| #define HPNFC_CTRL_FEATURES_DMA_DWITH_64_SHIFT 21 |
| /* number of threads available in the controller - mask */ |
| #define HPNFC_CTRL_FEATURES_N_THREADS_MASK (0x7 << 0) |
| /* number of threads available in the controller - shift */ |
| #define HPNFC_CTRL_FEATURES_N_THREADS_SHIFT 0x0 |
| |
| /* NAND Flash memory device ID information */ |
| #define HPNFC_MANUFACTURER_ID 0x0808 |
| /* Device ID - mask */ |
| #define HPNFC_MANUFACTURER_ID_DID_MASK (0xFFuL << 16) |
| /* Device ID - shift */ |
| #define HPNFC_MANUFACTURER_ID_DID_SHIFT 16 |
| /* Manufacturer ID - mask */ |
| #define HPNFC_MANUFACTURER_ID_MID_MASK (0xFFuL << 0) |
| /* Manufacturer ID - shift */ |
| #define HPNFC_MANUFACTURER_ID_MID_SHIFT 0 |
| |
| /* Device areas settings. */ |
| #define HPNFC_NF_DEV_AREAS 0x080c |
| /* Spare area size in bytes for the NF device page - shift */ |
| #define HPNFC_NF_DEV_AREAS_SPARE_SIZE_SHIFT 16 |
| /* Spare area size in bytes for the NF device page - mask */ |
| #define HPNFC_NF_DEV_AREAS_SPARE_SIZE_MASK (0xFFFFuL << 16) |
| /* Main area size in bytes for the NF device page - shift*/ |
| #define HPNFC_NF_DEV_AREAS_MAIN_SIZE_SHIFT 0 |
| /* Main area size in bytes for the NF device page - mask*/ |
| #define HPNFC_NF_DEV_AREAS_MAIN_SIZE_MASK (0xFFFFuL << 0) |
| |
| /* device parameters 1 register contains device signature */ |
| #define HPNFC_DEV_PARAMS_1 0x0814 |
| #define HPNFC_DEV_PARAMS_1_READID_6_SHIFT 24 |
| #define HPNFC_DEV_PARAMS_1_READID_6_MASK (0xFFuL << 24) |
| #define HPNFC_DEV_PARAMS_1_READID_5_SHIFT 16 |
| #define HPNFC_DEV_PARAMS_1_READID_5_MASK (0xFFuL << 16) |
| #define HPNFC_DEV_PARAMS_1_READID_4_SHIFT 8 |
| #define HPNFC_DEV_PARAMS_1_READID_4_MASK (0xFFuL << 8) |
| #define HPNFC_DEV_PARAMS_1_READID_3_SHIFT 0 |
| #define HPNFC_DEV_PARAMS_1_READID_3_MASK (0xFFuL << 0) |
| |
| /* device parameters 0 register */ |
| #define HPNFC_DEV_PARAMS_0 0x0810 |
| /* device type shift */ |
| #define HPNFC_DEV_PARAMS_0_DEV_TYPE_SHIFT 30 |
| /* device type mask */ |
| #define HPNFC_DEV_PARAMS_0_DEV_TYPE_MASK (3uL << 30) |
| /* device type - ONFI */ |
| #define HPNFC_DEV_PARAMS_0_DEV_TYPE_ONFI 1 |
| /* device type - JEDEC */ |
| #define HPNFC_DEV_PARAMS_0_DEV_TYPE_JEDEC 2 |
| /* device type - unknown */ |
| #define HPNFC_DEV_PARAMS_0_DEV_TYPE_UNKNOWN 3 |
| /* Number of bits used to addressing planes - shift*/ |
| #define HPNFC_DEV_PARAMS_0_PLANE_ADDR_SHIFT 8 |
| /* Number of bits used to addressing planes - mask*/ |
| #define HPNFC_DEV_PARAMS_0_PLANE_ADDR_MASK (0xFFuL << 8) |
| /* Indicates the number of LUNS present - mask*/ |
| #define HPNFC_DEV_PARAMS_0_NO_OF_LUNS_SHIFT 0 |
| /* Indicates the number of LUNS present - mask*/ |
| #define HPNFC_DEV_PARAMS_0_NO_OF_LUNS_MASK (0xFFuL << 0) |
| |
| /* Features and optional commands supported |
| * by the connected device */ |
| #define HPNFC_DEV_FEATURES 0x0818 |
| |
| /* Number of blocks per LUN present in the NF device. */ |
| #define HPNFC_DEV_BLOCKS_PER_LUN 0x081c |
| |
| /* Device revision version */ |
| #define HPNFC_DEV_REVISION 0x0820 |
| |
| /* Device Timing modes 0*/ |
| #define HPNFC_ONFI_TIME_MOD_0 0x0824 |
| /* SDR timing modes support - shift */ |
| #define HPNFC_ONFI_TIME_MOD_0_SDR_SHIFT 0 |
| /* SDR timing modes support - mask */ |
| #define HPNFC_ONFI_TIME_MOD_0_SDR_MASK (0xFFFFuL << 0) |
| /* DDR timing modes support - shift */ |
| #define HPNFC_ONFI_TIME_MOD_0_DDR_SHIFT 16 |
| /* DDR timing modes support - mask */ |
| #define HPNFC_ONFI_TIME_MOD_0_DDR_MASK (0xFFFFuL << 16) |
| |
| /* Device Timing modes 1*/ |
| #define HPNFC_ONFI_TIME_MOD_1 0x0828 |
| /* DDR2 timing modes support - shift */ |
| #define HPNFC_ONFI_TIME_MOD_1_DDR2_SHIFT 0 |
| /* DDR2 timing modes support - mask */ |
| #define HPNFC_ONFI_TIME_MOD_1_DDR2_MASK (0xFFFFuL << 0) |
| /* DDR3 timing modes support - shift */ |
| #define HPNFC_ONFI_TIME_MOD_1_DDR3_SHIFT 16 |
| /* DDR3 timing modes support - mask */ |
| #define HPNFC_ONFI_TIME_MOD_1_DDR3_MASK (0xFFFFuL << 16) |
| |
| /* BCH Engine identification register 0 - correction strengths. */ |
| #define HPNFC_BCH_CFG_0 0x838 |
| #define HPNFC_BCH_CFG_0_CORR_CAP_0_SHIFT 0 |
| #define HPNFC_BCH_CFG_0_CORR_CAP_0_MASK (0XFFul << 0) |
| #define HPNFC_BCH_CFG_0_CORR_CAP_1_SHIFT 8 |
| #define HPNFC_BCH_CFG_0_CORR_CAP_1_MASK (0XFFul << 8) |
| #define HPNFC_BCH_CFG_0_CORR_CAP_2_SHIFT 16 |
| #define HPNFC_BCH_CFG_0_CORR_CAP_2_MASK (0XFFul << 16) |
| #define HPNFC_BCH_CFG_0_CORR_CAP_3_SHIFT 24 |
| #define HPNFC_BCH_CFG_0_CORR_CAP_3_MASK (0XFFul << 24) |
| |
| |
| /* BCH Engine identification register 1 - correction strengths. */ |
| #define HPNFC_BCH_CFG_1 0x83C |
| #define HPNFC_BCH_CFG_1_CORR_CAP_4_SHIFT 0 |
| #define HPNFC_BCH_CFG_1_CORR_CAP_4_MASK (0XFFul << 0) |
| #define HPNFC_BCH_CFG_1_CORR_CAP_5_SHIFT 8 |
| #define HPNFC_BCH_CFG_1_CORR_CAP_5_MASK (0XFFul << 8) |
| #define HPNFC_BCH_CFG_1_CORR_CAP_6_SHIFT 16 |
| #define HPNFC_BCH_CFG_1_CORR_CAP_6_MASK (0XFFul << 16) |
| #define HPNFC_BCH_CFG_1_CORR_CAP_7_SHIFT 24 |
| #define HPNFC_BCH_CFG_1_CORR_CAP_7_MASK (0XFFul << 24) |
| |
| /* BCH Engine identification register 2 - sector sizes. */ |
| #define HPNFC_BCH_CFG_2 0x840 |
| #define HPNFC_BCH_CFG_2_SECT_0_SHIFT 0 |
| #define HPNFC_BCH_CFG_2_SECT_0_MASK (0xFFFFuL << 0) |
| #define HPNFC_BCH_CFG_2_SECT_1_SHIFT 16 |
| #define HPNFC_BCH_CFG_2_SECT_1_MASK (0xFFFFuL << 16) |
| |
| /* BCH Engine identification register 3 */ |
| #define HPNFC_BCH_CFG_3 0x844 |
| |
| /* Ready/Busy# line status */ |
| #define HPNFC_RBN_SETTINGS 0x1004 |
| |
| /* Common settings */ |
| #define HPNFC_COMMON_SETT 0x1008 |
| /* write warm-up cycles. shift */ |
| #define HPNFC_COMMON_SETT_WR_WARMUP_SHIFT 20 |
| /* read warm-up cycles. shift */ |
| #define HPNFC_COMMON_SETT_RD_WARMUP_SHIFT 16 |
| /* 16 bit device connected to the NAND Flash interface - shift */ |
| #define HPNFC_COMMON_SETT_DEVICE_16BIT_SHIFT 8 |
| /* Operation work mode - mask */ |
| #define HPNFC_COMMON_SETT_OPR_MODE_MASK 0x3 |
| /* Operation work mode - shift */ |
| #define HPNFC_COMMON_SETT_OPR_MODE_SHIFT 0 |
| /* Operation work mode - SDR mode */ |
| #define HPNFC_COMMON_SETT_OPR_MODE_SDR 0 |
| /* Operation work mode - NV_DDR mode */ |
| #define HPNFC_COMMON_SETT_OPR_MODE_NV_DDR 1 |
| /* Operation work mode - ToggleMode/NV-DDR2/NV-DDR3 mode */ |
| #define HPNFC_COMMON_SETT_OPR_MODE_TOGGLE 2 |
| |
| /* ToggleMode/NV-DDR2/NV-DDR3 and SDR timings configuration. */ |
| #define HPNFC_ASYNC_TOGGLE_TIMINGS 0x101c |
| /* The number of clock cycles (nf_clk) the Minicontroller |
| * needs to de-assert RE# to meet the tREH - shift*/ |
| #define HPNFC_ASYNC_TOGGLE_TIMINGS_TRH_SHIFT 24 |
| /* The number of clock cycles (nf_clk) the Minicontroller |
| * needs to de-assert RE# to meet the tREH - mask*/ |
| #define HPNFC_ASYNC_TOGGLE_TIMINGS_TRH_MASK (0x1FuL << 24) |
| /* The number of clock cycles (nf_clk) the Minicontroller |
| * needs to assert RE# to meet the tRP - shift*/ |
| #define HPNFC_ASYNC_TOGGLE_TIMINGS_TRP_SHIFT 16 |
| /* The number of clock cycles (nf_clk) the Minicontroller |
| * needs to assert RE# to meet the tRP - mask*/ |
| #define HPNFC_ASYNC_TOGGLE_TIMINGS_TRP_MASK (0x1FuL << 16) |
| /* The number of clock cycles (nf_clk) the Minicontroller |
| * needs to assert WE# to meet the tWH - shift*/ |
| #define HPNFC_ASYNC_TOGGLE_TIMINGS_TWH_SHIFT 8 |
| /* The number of clock cycles (nf_clk) the Minicontroller |
| * needs to assert WE# to meet the tWH - mask*/ |
| #define HPNFC_ASYNC_TOGGLE_TIMINGS_TWH_MASK (0x1FuL << 8) |
| /* The number of clock cycles (nf_clk) the Minicontroller |
| * needs to assert WE# to meet the tWP - shift */ |
| #define HPNFC_ASYNC_TOGGLE_TIMINGS_TWP_SHIFT 0 |
| /* The number of clock cycles (nf_clk) the Minicontroller |
| * needs to assert WE# to meet the tWP - mask */ |
| #define HPNFC_ASYNC_TOGGLE_TIMINGS_TWP_MASK (0x1FuL << 0) |
| |
| /* Configuration of the resynchronization of slave DLL of PHY */ |
| #define HPNFC_DLL_PHY_CTRL 0x1034 |
| /* Acknowledge signal to resynchronize the DLLs and read and |
| * write FIFO pointers - mask */ |
| #define HPNFC_DLL_PHY_CTRL_DFI_CTRLUPD_ACK_MASK (1uL << 26) |
| /* Signal to resynchronize the DLLs and read and write FIFO pointers */ |
| #define HPNFC_DLL_PHY_CTRL_DFI_CTRLUPD_REQ_MASK (1uL << 25) |
| /* Signal to reset the DLLs of the PHY and start |
| * searching for lock again - mask */ |
| #define HPNFC_DLL_PHY_CTRL_DLL_RST_N_MASK (1uL << 24) |
| /* Information to controller and PHY that the WE# is in extended mode */ |
| #define HPNFC_DLL_PHY_CTRL_EXTENDED_WR_MODE_MASK (1uL << 17) |
| /* Information to controller and PHY that the RE# is in extended mode */ |
| #define HPNFC_DLL_PHY_CTRL_EXTENDED_RD_MODE_MASK (1uL << 16) |
| /* This field defines the number of Minicontroller clock cycles |
| * (nf_clk) for which the DLL update request (dfi_ctrlupd_req) |
| * has to be asserted - mask */ |
| #define HPNFC_DLL_PHY_CTRL_RS_HIGH_WAIT_CNT_MASK (0xFuL << 8) |
| /* This field defines the wait time(in terms of Minicontroller |
| * clock cycles (nf_clk)) between the deassertion of the DLL |
| * update request (dfi_ctrlupd_req) and resuming traffic to the PHY */ |
| #define HPNFC_DLL_PHY_CTRL_RS_IDLE_CNT_MASK (0xFFuL << 0) |
| |
| /* register controlling DQ related timing */ |
| #define HPNFC_PHY_DQ_TIMING_REG 0x2000 |
| /* register controlling DSQ related timing */ |
| #define HPNFC_PHY_DQS_TIMING_REG 0x2004 |
| /* register controlling the gate and loopback control related timing. */ |
| #define HPNFC_PHY_GATE_LPBK_CTRL_REG 0x2008 |
| /* register holds the control for the master DLL logic */ |
| #define HPNFC_PHY_DLL_MASTER_CTRL_REG 0x200C |
| /* register holds the control for the slave DLL logic */ |
| #define HPNFC_PHY_DLL_SLAVE_CTRL_REG 0x2010 |
| |
| /* This register handles the global control settings for the PHY */ |
| #define HPNFC_PHY_CTRL_REG 0x2080 |
| /*The value that should be driven on the DQS pin while SDR |
| * operations are in progress. - shift*/ |
| #define HPNFC_PHY_CTRL_REG_SDR_DQS_SHIFT 14 |
| /*The value that should be driven on the DQS pin while SDR |
| * operations are in progress. - mask*/ |
| #define HPNFC_PHY_CTRL_REG_SDR_DQS_MASK (1uL << 14) |
| /* The timing of assertion of phony DQS to the data slices - shift */ |
| #define HPNFC_PHY_CTRL_REG_PHONY_DQS_SHIFT 4 |
| /* The timing of assertion of phony DQS to the data slices - mask */ |
| #define HPNFC_PHY_CTRL_REG_PHONY_DQS_MASK 4 |
| /* This register handles the global control settings |
| * for the termination selects for reads */ |
| #define HPNFC_PHY_TSEL_REG 0x2084 |
| /***************************************************/ |
| |
| #ifdef CONFIG_MTD_NAND_CADENCE_AS390REMAP |
| #undef HPNFC_RBN_SETTINGS |
| #define HPNFC_RBN_SETTINGS 0x0904 |
| #undef HPNFC_COMMON_SETT |
| #define HPNFC_COMMON_SETT 0x0908 |
| #undef HPNFC_ASYNC_TOGGLE_TIMINGS |
| #define HPNFC_ASYNC_TOGGLE_TIMINGS 0x091c |
| #undef HPNFC_DLL_PHY_CTRL |
| #define HPNFC_DLL_PHY_CTRL 0x0934 |
| #undef HPNFC_PHY_DQ_TIMING_REG |
| #define HPNFC_PHY_DQ_TIMING_REG 0x0a00 |
| #undef HPNFC_PHY_DQS_TIMING_REG |
| #define HPNFC_PHY_DQS_TIMING_REG 0x0a04 |
| #undef HPNFC_PHY_GATE_LPBK_CTRL_REG |
| #define HPNFC_PHY_GATE_LPBK_CTRL_REG 0x0a08 |
| #undef HPNFC_PHY_DLL_MASTER_CTRL_REG |
| #define HPNFC_PHY_DLL_MASTER_CTRL_REG 0x0a0C |
| #undef HPNFC_PHY_DLL_SLAVE_CTRL_REG |
| #define HPNFC_PHY_DLL_SLAVE_CTRL_REG 0x0a10 |
| #undef HPNFC_PHY_CTRL_REG |
| #define HPNFC_PHY_CTRL_REG 0x0a80 |
| #undef HPNFC_PHY_TSEL_REG |
| #define HPNFC_PHY_TSEL_REG 0x0a84 |
| #endif |
| |
| /***************************************************/ |
| /* Register field accessing macros */ |
| /***************************************************/ |
| /* write field of 32 bit register */ |
| #define WRITE_FIELD(reg, field_name, field_val) \ |
| reg = (reg & ~field_name ## _MASK) \ |
| | ((uint32_t)field_val) << field_name ## _SHIFT |
| |
| /* write field of 64 bit register */ |
| #define WRITE_FIELD64(reg, field_name, field_val) \ |
| reg = (reg & ~field_name ## _MASK) \ |
| | ((uint64_t)field_val) << field_name ## _SHIFT |
| |
| /* read field of 32 bit register */ |
| #define READ_FIELD(reg, field_name) \ |
| ((reg & field_name ## _MASK) >> field_name ## _SHIFT) |
| /***************************************************/ |
| |
| |
| /***************************************************/ |
| /* Register write/read macros */ |
| /***************************************************/ |
| #define IOWR_32(reg, val) iowrite32((val), (reg)); |
| #define IORD_32(reg) ioread32(reg) |
| /***************************************************/ |
| |
| |
| /***************************************************/ |
| /* Generic command definition */ |
| /***************************************************/ |
| typedef struct generic_data_t { |
| /* use interrupts */ |
| uint8_t use_intr : 1; |
| /* transfer direction */ |
| uint8_t direction : 1; |
| /* enable ECC */ |
| uint8_t ecc_en : 1; |
| /* enable scrambler (if ECC is enabled) */ |
| uint8_t scr_en : 1; |
| /* enable erase page detection */ |
| uint8_t erpg_en : 1; |
| /* number of bytes to transfer of the n-1 sectors |
| * except the last onesector */ |
| uint16_t sec_size; |
| /* defines the number of sectors to transfer within a single |
| * sequence */ |
| uint8_t sec_cnt; |
| /* number of bytes to transfer in the last sector */ |
| uint16_t last_sec_size; |
| /* ECC correction capability (if ECC is enabled) */ |
| uint8_t corr_cap; |
| } generic_data_t; |
| |
| |
| /* generic command layout*/ |
| /* chip select - shift */ |
| #define HPNFC_GCMD_LAY_CS_SHIFT 8 |
| /* chip select - mask */ |
| #define HPNFC_GCMD_LAY_CS_MASK (0xF << 8) |
| /* commands complaint with Jedec spec*/ |
| #define HPNFC_GCMD_LAY_JEDEC_MASK (1 << 7) |
| /* This bit informs the minicotroller if it has to wait for tWB |
| * after sending the last CMD/ADDR/DATA in the sequence. */ |
| #define HPNFC_GCMD_LAY_TWB_MASK (1 << 6) |
| /* type of instruction - shift */ |
| #define HPNFC_GCMD_LAY_INSTR_SHIFT 0 |
| /* type of instruction - mask */ |
| #define HPNFC_GCMD_LAY_INSTR_MASK (0x3F << 0) |
| /* type of instruction - data transfer */ |
| #define HPNFC_GCMD_LAY_INSTR_DATA 2 |
| /* type of instruction - read parameter page (0xEF) */ |
| #define HPNFC_GCMD_LAY_INSTR_RDPP 28 |
| /* type of instruction - read memory ID (0x90) */ |
| #define HPNFC_GCMD_LAY_INSTR_RDID 27 |
| /* type of instruction - reset command (0xFF) */ |
| #define HPNFC_GCMD_LAY_INSTR_RDST 7 |
| /* type of instruction - change read column command */ |
| #define HPNFC_GCMD_LAY_INSTR_CHRC 12 |
| |
| /* input part of generic command type of input is address 0 - shift */ |
| #define HPNFC_GCMD_LAY_INPUT_ADDR0_SHIFT 16 |
| /* input part of generic command type of input is address 0 - mask */ |
| #define HPNFC_GCMD_LAY_INPUT_ADDR0_MASK (0xFFFFFFFFFFuLL << 16) |
| |
| /* generic command data sequence - transfer direction - shift */ |
| #define HPNFC_GCMD_DIR_SHIFT 11 |
| /* generic command data sequence - transfer direction - mask */ |
| #define HPNFC_GCMD_DIR_MASK (1uL << 11) |
| /* generic command data sequence - transfer direction - read */ |
| #define HPNFC_GCMD_DIR_READ 0 |
| /* generic command data sequence - transfer direction - write */ |
| #define HPNFC_GCMD_DIR_WRITE 1 |
| |
| /* generic command data sequence - ecc enabled - mask */ |
| #define HPNFC_GCMD_ECC_EN_MASK (1uLL << 12) |
| /* generic command data sequence - scrambler enabled - mask */ |
| #define HPNFC_GCMD_SCR_EN_MASK (1uLL << 13) |
| /* generic command data sequence - erase page detection enabled - mask */ |
| #define HPNFC_GCMD_ERPG_EN_MASK (1uLL << 14) |
| /* generic command data sequence - sctor size - shift */ |
| #define HPNFC_GCMD_SECT_SIZE_SHIFT 16 |
| /* generic command data sequence - sector size - mask */ |
| #define HPNFC_GCMD_SECT_SIZE_MASK (0xFFFFuL << 16) |
| /* generic command data sequence - sector count - shift */ |
| #define HPNFC_GCMD_SECT_CNT_SHIFT 32 |
| /* generic command data sequence - sector count - mask */ |
| #define HPNFC_GCMD_SECT_CNT_MASK (0xFFuLL << 32) |
| /* generic command data sequence - last sector size - shift */ |
| #define HPNFC_GCMD_LAST_SIZE_SHIFT 40 |
| /* generic command data sequence - last sector size - mask */ |
| #define HPNFC_GCMD_LAST_SIZE_MASK (0xFFFFuLL << 40) |
| /* generic command data sequence - correction capability - shift */ |
| #define HPNFC_GCMD_CORR_CAP_SHIFT 56 |
| /* generic command data sequence - correction capability - mask */ |
| #define HPNFC_GCMD_CORR_CAP_MASK (3uLL << 56) |
| |
| |
| |
| /***************************************************/ |
| /* CDMA descriptor fields */ |
| /***************************************************/ |
| |
| /** command DMA descriptor type - erase command */ |
| #define HPNFC_CDMA_CT_ERASE 0x1000 |
| /** command DMA descriptor type - reset command */ |
| #define HPNFC_CDMA_CT_RST 0x1100 |
| /** command DMA descriptor type - copy back command */ |
| #define HPNFC_CDMA_CT_CPYB 0x1200 |
| /** command DMA descriptor type - write page command */ |
| #define HPNFC_CDMA_CT_WR 0x2100 |
| /** command DMA descriptor type - read page command */ |
| #define HPNFC_CDMA_CT_RD 0x2200 |
| /** command DMA descriptor type - nop command */ |
| #define HPNFC_CDMA_CT_NOP 0xFFFF |
| |
| /** flash pointer memory - shift */ |
| #define HPNFC_CDMA_CFPTR_MEM_SHIFT 24 |
| /** flash pointer memory - mask */ |
| #define HPNFC_CDMA_CFPTR_MEM_MASK (7uL << 24) |
| |
| /** command DMA descriptor flags - issue interrupt after |
| * the completion of descriptor processing */ |
| #define HPNFC_CDMA_CF_INT (1uL << 8) |
| /** command DMA descriptor flags - the next descriptor |
| * address field is valid and descriptor processing should continue */ |
| #define HPNFC_CDMA_CF_CONT (1uL << 9) |
| /* command DMA descriptor flags - selects DMA slave */ |
| #define HPNFC_CDMA_CF_DMA_SLAVE (0uL << 10) |
| /* command DMA descriptor flags - selects DMA master */ |
| #define HPNFC_CDMA_CF_DMA_MASTER (1uL << 10) |
| |
| |
| /* command descriptor status - the operation number |
| * where the first error was detected - shift*/ |
| #define HPNFC_CDMA_CS_ERR_IDX_SHIFT 24 |
| /* command descriptor status - the operation number |
| * where the first error was detected - mask*/ |
| #define HPNFC_CDMA_CS_ERR_IDX_MASK (0xFFuL << 24) |
| /* command descriptor status - operation complete - mask*/ |
| #define HPNFC_CDMA_CS_COMP_MASK (1uL << 15) |
| /* command descriptor status - operation fail - mask*/ |
| #define HPNFC_CDMA_CS_FAIL_MASK (1uL << 14) |
| /* command descriptor status - page erased - mask*/ |
| #define HPNFC_CDMA_CS_ERP_MASK (1uL << 11) |
| /* command descriptor status - timeout occurred - mask*/ |
| #define HPNFC_CDMA_CS_TOUT_MASK (1uL << 10) |
| /* command descriptor status - maximum amount of correction |
| * applied to one ECC sector - shift */ |
| #define HPNFC_CDMA_CS_MAXERR_SHIFT 2 |
| /* command descriptor status - maximum amount of correction |
| * applied to one ECC sector - mask */ |
| #define HPNFC_CDMA_CS_MAXERR_MASK (0xFFuL << 2) |
| /* command descriptor status - uncorrectable ECC error - mask*/ |
| #define HPNFC_CDMA_CS_UNCE_MASK (1uL << 1) |
| /* command descriptor status - descriptor error - mask*/ |
| #define HPNFC_CDMA_CS_ERR_MASK (1uL << 0) |
| |
| /***************************************************/ |
| |
| |
| /***************************************************/ |
| /* internal used status*/ |
| /***************************************************/ |
| /* status of operation - OK */ |
| #define HPNFC_STAT_OK 0 |
| /* status of operation - FAIL */ |
| #define HPNFC_STAT_FAIL 2 |
| /* status of operation - uncorrectable ECC error */ |
| #define HPNFC_STAT_ECC_UNCORR 3 |
| /* status of operation - page erased */ |
| #define HPNFC_STAT_ERASED 5 |
| /* status of operation - correctable ECC error */ |
| #define HPNFC_STAT_ECC_CORR 6 |
| /* status of operation - operation is not completed yet */ |
| #define HPNFC_STAT_BUSY 0xFF |
| /***************************************************/ |
| |
| /***************************************************/ |
| /* work modes */ |
| /***************************************************/ |
| #define HPNFC_WORK_MODE_ASYNC 0x00 |
| #define HPNFC_WORK_MODE_NV_DDR 0x10 |
| #define HPNFC_WORK_MODE_NV_DDR2 0x20 |
| #define HPNFC_WORK_MODE_NV_DDR3 0x30 |
| #define HPNFC_WORK_MODE_TOGG 0x40 |
| /***************************************************/ |
| |
| /***************************************************/ |
| /* ECC correction capabilities */ |
| /***************************************************/ |
| #define HPNFC_ECC_CORR_CAP_2 2 |
| #define HPNFC_ECC_CORR_CAP_4 4 |
| #define HPNFC_ECC_CORR_CAP_8 8 |
| #define HPNFC_ECC_CORR_CAP_12 12 |
| #define HPNFC_ECC_CORR_CAP_16 16 |
| #define HPNFC_ECC_CORR_CAP_24 24 |
| #define HPNFC_ECC_CORR_CAP_40 40 |
| /***************************************************/ |
| |
| struct nand_buf { |
| uint8_t *buf; |
| int tail; |
| int head; |
| dma_addr_t dma_buf; |
| }; |
| |
| /* Command DMA descriptor */ |
| typedef struct hpnfc_cdma_desc_t { |
| /* next descriptor address */ |
| uint64_t next_pointer; |
| /* glash address is a 32-bit address comprising of BANK and ROW ADDR. */ |
| uint32_t flash_pointer; |
| uint32_t rsvd0; |
| /* operation the controller needs to perform */ |
| uint16_t command_type; |
| uint16_t rsvd1; |
| /* flags for operation of this command */ |
| uint16_t command_flags; |
| uint16_t rsvd2; |
| /* system/host memory address required for data DMA commands. */ |
| uint64_t memory_pointer; |
| /* status of operation */ |
| uint32_t status; |
| uint32_t rsvd3; |
| /* address pointer to sync buffer location */ |
| uint64_t sync_flag_pointer; |
| /* Controls the buffer sync mechanism. */ |
| uint32_t sync_arguments; |
| uint32_t rsvd4; |
| } hpnfc_cdma_desc_t; |
| |
| /* interrupt status */ |
| typedef struct hpnfc_irq_status_t { |
| /* Thread operation complete status */ |
| uint32_t trd_status; |
| /* Thread operation error */ |
| uint32_t trd_error; |
| /* Controller status */ |
| uint32_t status; |
| } hpnfc_irq_status_t; |
| |
| /** BCH HW configuration info */ |
| typedef struct hpnfc_bch_config_info_t { |
| /** Supported BCH correction capabilities. */ |
| uint8_t corr_caps[8]; |
| /** Supported BCH sector sizes */ |
| uint32_t sector_sizes[2]; |
| } hpnfc_bch_config_info_t; |
| |
| typedef struct hpnfc_state_t { |
| hpnfc_cdma_desc_t *cdma_desc; |
| dma_addr_t dma_cdma_desc; |
| struct nand_buf buf; |
| /* actual selected chip */ |
| uint8_t chip_nr; |
| int offset; |
| uint8_t *random_data; |
| struct nand_randomizer randomizer; |
| |
| struct nand_chip nand; |
| void __iomem *reg; |
| void __iomem *slave_dma; |
| struct device *dev; |
| |
| int irq; |
| hpnfc_irq_status_t irq_status; |
| hpnfc_irq_status_t irq_mask; |
| struct completion complete; |
| spinlock_t irq_lock; |
| |
| /* part of spare area of NANF flash memory page. |
| * This part is available for user to read or write. Rest of spare area |
| * is used by controller for ECC correction */ |
| uint32_t usnused_spare_size; |
| /* spare area size of NANF flash memory page */ |
| uint32_t spare_size; |
| /* main area size of NANF flash memory page */ |
| uint32_t main_size; |
| /* sector size few sectors are located on main area of NF memory page */ |
| uint32_t sector_size; |
| /* last sector size (containing spare area) */ |
| uint32_t last_sector_size; |
| uint32_t sector_count; |
| uint32_t curr_trans_type; |
| uint8_t corr_cap; |
| uint8_t lun_count; |
| uint32_t blocks_per_lun; |
| |
| uint32_t devnum; |
| uint32_t bbtskipbytes; |
| uint8_t max_banks; |
| uint8_t dev_type; |
| uint8_t ecc_enabled; |
| hpnfc_bch_config_info_t bch_cfg; |
| uint8_t bytesPerSdmaAccess; |
| int zos_page_end; |
| } hpnfc_state_t; |
| |
| #define CADENCE_NAND_NAME "cdns-hpnfc" |
| #define mtd_to_hpnfc(m) container_of(mtd_to_nand(m), struct hpnfc_state_t, nand) |
| |
| #define HPNFC_MINIMUM_SPARE_SIZE 4 |
| #define HPNFC_MAX_SPARE_SIZE_PER_SECTOR 32 |
| #define HPNFC_BCH_MAX_NUM_CORR_CAPS 8 |
| #define HPNFC_BCH_MAX_NUM_SECTOR_SIZES 2 |
| |
| static int maxchips = 0; |
| static int disable_ddr = 0; |
| |
| module_param(maxchips, int, S_IRUGO); |
| module_param(disable_ddr, int, S_IRUGO); |
| |
| /* PHY configurations for nf_clk = 100MHz |
| * phy ctrl, phy tsel, DQ timing, DQS timing, LPBK, dll master, dll slave*/ |
| static uint32_t phy_timings_ddr[] = { |
| 0x0000, 0x00, 0x02, 0x00000004, 0x00200002, 0x01140004, 0x1f1f |
| }; |
| static uint32_t phy_timings_ddr2[] = { |
| 0x4000, 0x00, 0x02, 0x00000005, 0x00380000, 0x01140004, 0x1f1f |
| }; |
| static uint32_t phy_timings_toggle[] = { |
| 0x4000, 0x00, 0x02, 0x00000004, 0x00280001, 0x01140004, 0x1f1f |
| }; |
| static uint32_t phy_timings_async[] = { |
| 0x4040, 0x00, 0x02, 0x00100004, 0x1b << 19, 0x00800000, 0x0000 |
| }; |
| |
| static int wait_for_thread(hpnfc_state_t *hpnfc, int8_t thread); |
| static int hpnfc_wait_for_idle(hpnfc_state_t *hpnfc); |
| |
| /* send set features command to nand flash memory device */ |
| static int nf_mem_set_features(hpnfc_state_t *hpnfc, uint8_t feat_addr, |
| uint8_t feat_val, uint8_t mem, uint8_t thread, |
| uint8_t vol_id) |
| { |
| uint32_t reg; |
| int status; |
| |
| /* wait for thread ready */ |
| status = wait_for_thread(hpnfc, thread); |
| if (status) |
| return status; |
| |
| reg = 0; |
| WRITE_FIELD(reg, HPNFC_CMD_REG1_FADDR, feat_addr); |
| WRITE_FIELD(reg, HPNFC_CMD_REG1_BANK, mem); |
| /* set feature address and bank number */ |
| IOWR_32(hpnfc->reg + HPNFC_CMD_REG1, reg); |
| /* set feature - value*/ |
| IOWR_32(hpnfc->reg + HPNFC_CMD_REG2, feat_val); |
| |
| reg = 0; |
| /* select PIO mode */ |
| WRITE_FIELD(reg, HPNFC_CMD_REG0_CT, HPNFC_CMD_REG0_CT_PIO); |
| /* thread number */ |
| WRITE_FIELD(reg, HPNFC_CMD_REG0_TN, thread); |
| /* volume ID */ |
| WRITE_FIELD(reg, HPNFC_CMD_REG0_VOL_ID, vol_id); |
| /* disabled interrupt */ |
| WRITE_FIELD(reg, HPNFC_CMD_REG0_INT, 0); |
| /* select set feature command */ |
| WRITE_FIELD(reg, HPNFC_CMD_REG0_PIO_CC, HPNFC_CMD_REG0_PIO_CC_SF); |
| /* execute command */ |
| IOWR_32(hpnfc->reg + HPNFC_CMD_REG0, reg); |
| |
| return 0; |
| } |
| |
| /* send reset command to nand flash memory device */ |
| static int nf_mem_reset(hpnfc_state_t *hpnfc, uint8_t mem, uint8_t thread, |
| uint8_t vol_id) |
| { |
| uint32_t reg = 0; |
| int status; |
| |
| /* wait for thread ready */ |
| status = wait_for_thread(hpnfc, thread); |
| if (status) |
| return status; |
| |
| WRITE_FIELD(reg, HPNFC_CMD_REG1_BANK, mem); |
| IOWR_32(hpnfc->reg + HPNFC_CMD_REG1, reg); |
| |
| reg = 0; |
| |
| /* select PIO mode */ |
| WRITE_FIELD(reg, HPNFC_CMD_REG0_CT, HPNFC_CMD_REG0_CT_PIO); |
| /* thread number */ |
| WRITE_FIELD(reg, HPNFC_CMD_REG0_TN, thread); |
| /* volume ID */ |
| WRITE_FIELD(reg, HPNFC_CMD_REG0_VOL_ID, vol_id); |
| /* disabled interrupt */ |
| WRITE_FIELD(reg, HPNFC_CMD_REG0_INT, 0); |
| /* select reset command */ |
| WRITE_FIELD(reg, HPNFC_CMD_REG0_PIO_CC, HPNFC_CMD_REG0_PIO_CC_RST); |
| /* execute command */ |
| IOWR_32(hpnfc->reg + HPNFC_CMD_REG0, reg); |
| |
| return 0; |
| } |
| |
| /* function returns thread status */ |
| static uint32_t hpnfc_get_thrd_status(hpnfc_state_t *hpnfc, uint8_t thread) |
| { |
| uint32_t reg; |
| |
| IOWR_32(hpnfc->reg + HPNFC_CMD_STATUS_PTR, thread); |
| reg = IORD_32(hpnfc->reg + HPNFC_CMD_STATUS); |
| return reg; |
| } |
| |
| /* Wait until operation is finished */ |
| static int hpnfc_pio_check_finished(hpnfc_state_t *hpnfc, uint8_t thread) |
| { |
| uint32_t thrd_status; |
| unsigned long timeout = jiffies + msecs_to_jiffies(1000); |
| |
| /* wait for fail or complete status */ |
| do { |
| thrd_status = hpnfc_get_thrd_status(hpnfc, thread); |
| thrd_status &= (HPNFC_CDMA_CS_COMP_MASK | HPNFC_CDMA_CS_FAIL_MASK); |
| } while ((thrd_status == 0) && time_before(jiffies, timeout)); |
| |
| if (time_after_eq(jiffies, timeout)) { |
| dev_err(hpnfc->dev, "Timeout while waiting for PIO command finished\n"); |
| return -ETIMEDOUT; |
| } |
| |
| if (thrd_status & HPNFC_CDMA_CS_FAIL_MASK) |
| return -EIO; |
| if (thrd_status & HPNFC_CDMA_CS_COMP_MASK) |
| return 0; |
| |
| return -EIO; |
| } |
| |
| /* checks what is the best work mode */ |
| static void hpnfc_check_the_best_mode(hpnfc_state_t *hpnfc, |
| uint8_t *work_mode, |
| uint8_t *nf_timing_mode) |
| { |
| uint32_t reg; |
| |
| *work_mode = HPNFC_WORK_MODE_ASYNC; |
| *nf_timing_mode = 0; |
| |
| if (hpnfc->dev_type != HPNFC_DEV_PARAMS_0_DEV_TYPE_ONFI) |
| return; |
| |
| /* check if DDR is supported */ |
| reg = IORD_32(hpnfc->reg + HPNFC_ONFI_TIME_MOD_0); |
| reg = READ_FIELD(reg, HPNFC_ONFI_TIME_MOD_0_DDR); |
| if (reg) |
| *work_mode = HPNFC_WORK_MODE_NV_DDR; |
| |
| /* check if DDR2 is supported */ |
| reg = IORD_32(hpnfc->reg + HPNFC_ONFI_TIME_MOD_1); |
| reg = READ_FIELD(reg, HPNFC_ONFI_TIME_MOD_1_DDR2); |
| if (reg) |
| *work_mode = HPNFC_WORK_MODE_NV_DDR2; |
| |
| /* check if DDR is supported */ |
| reg = READ_FIELD(reg, HPNFC_ONFI_TIME_MOD_1_DDR3); |
| if (reg) |
| *work_mode = HPNFC_WORK_MODE_NV_DDR3; |
| |
| switch (*work_mode) { |
| case HPNFC_WORK_MODE_NV_DDR: |
| reg = IORD_32(hpnfc->reg + HPNFC_ONFI_TIME_MOD_0); |
| reg = READ_FIELD(reg, HPNFC_ONFI_TIME_MOD_0_DDR); |
| break; |
| case HPNFC_WORK_MODE_NV_DDR2: |
| case HPNFC_WORK_MODE_TOGG: |
| reg = IORD_32(hpnfc->reg + HPNFC_ONFI_TIME_MOD_1); |
| reg = READ_FIELD(reg, HPNFC_ONFI_TIME_MOD_1_DDR2); |
| break; |
| case HPNFC_WORK_MODE_NV_DDR3: |
| reg = IORD_32(hpnfc->reg + HPNFC_ONFI_TIME_MOD_1); |
| reg = READ_FIELD(reg, HPNFC_ONFI_TIME_MOD_1_DDR3); |
| break; |
| case HPNFC_WORK_MODE_ASYNC: |
| default: |
| reg = IORD_32(hpnfc->reg + HPNFC_ONFI_TIME_MOD_0); |
| reg = READ_FIELD(reg, HPNFC_ONFI_TIME_MOD_0_SDR); |
| } |
| |
| /* calculate from timing mode 1 */ |
| reg >>= 1; |
| while (reg != 0) { |
| reg >>= 1; |
| *nf_timing_mode += 1; |
| } |
| } |
| |
| /* set NAND flash memory device work mode */ |
| static int nf_mem_set_work_mode(hpnfc_state_t *hpnfc, uint8_t work_mode, |
| uint8_t timing_mode) |
| { |
| uint8_t flash_work_mode = timing_mode; |
| int i, status; |
| |
| switch (work_mode) { |
| case HPNFC_WORK_MODE_NV_DDR: |
| flash_work_mode |= (1 << 4); |
| break; |
| case HPNFC_WORK_MODE_NV_DDR2: |
| case HPNFC_WORK_MODE_TOGG: |
| flash_work_mode |= (2 << 4); |
| break; |
| case HPNFC_WORK_MODE_NV_DDR3: |
| flash_work_mode |= (3 << 4); |
| break; |
| case HPNFC_WORK_MODE_ASYNC: |
| default: |
| break; |
| } |
| |
| /* send SET FEATURES command */ |
| for (i = 0; i < hpnfc->devnum; i++) |
| nf_mem_set_features(hpnfc, 0x01, flash_work_mode, i, i, 0); |
| for (i = 0; i < hpnfc->devnum; i++) { |
| status = hpnfc_pio_check_finished(hpnfc, i); |
| if (status) |
| return status; |
| } |
| |
| /* wait for controller IDLE */ |
| status = hpnfc_wait_for_idle(hpnfc); |
| if (status) |
| return status; |
| |
| return 0; |
| } |
| |
| static void hpnfc_apply_phy_settings(hpnfc_state_t *hpnfc, uint32_t settings[]) |
| { |
| IOWR_32(hpnfc->reg + HPNFC_PHY_CTRL_REG, settings[0]); |
| IOWR_32(hpnfc->reg + HPNFC_PHY_TSEL_REG, settings[1]); |
| IOWR_32(hpnfc->reg + HPNFC_PHY_DQ_TIMING_REG, settings[2]); |
| IOWR_32(hpnfc->reg + HPNFC_PHY_DQS_TIMING_REG, settings[3]); |
| IOWR_32(hpnfc->reg + HPNFC_PHY_GATE_LPBK_CTRL_REG, settings[4]); |
| IOWR_32(hpnfc->reg + HPNFC_PHY_DLL_MASTER_CTRL_REG, settings[5]); |
| IOWR_32(hpnfc->reg + HPNFC_PHY_DLL_SLAVE_CTRL_REG, settings[6]); |
| } |
| |
| /* |
| * Sets desired work mode to HPNFC controller and all NAND flash devices |
| * Each memory is processed in separate thread, starting from thread 0 |
| */ |
| static int hpnfc_set_work_mode(hpnfc_state_t *hpnfc, uint8_t work_mode, |
| uint8_t timing_mode) |
| { |
| uint32_t reg; |
| uint32_t dll_phy_ctrl; |
| int i, status; |
| |
| reg = (IORD_32(hpnfc->reg + HPNFC_DEV_PARAMS_1)) & 0xFF; |
| reg = READ_FIELD(reg, HPNFC_DEV_PARAMS_1_READID_5); |
| if (reg == 0x01) |
| /* exit if memory works in NV-DDR3 mode */ |
| return -EINVAL; |
| |
| /* set NF memory in known work mode by sending the reset command */ |
| reg = 0; |
| /* select SDR mode in the controller */ |
| WRITE_FIELD(reg, HPNFC_COMMON_SETT_OPR_MODE, |
| HPNFC_COMMON_SETT_OPR_MODE_SDR); |
| IOWR_32(hpnfc->reg + HPNFC_COMMON_SETT, reg); |
| |
| /* select default timings */ |
| hpnfc_apply_phy_settings(hpnfc, phy_timings_async); |
| |
| /* send reset command to all nand flash memory devices*/ |
| for (i = 0; i < hpnfc->devnum; i++) { |
| status = nf_mem_reset(hpnfc, i, i, 0); |
| if (status) |
| return status; |
| } |
| /* wait until reset commands is finished*/ |
| for (i = 0; i < hpnfc->devnum; i++) { |
| status = hpnfc_pio_check_finished(hpnfc, i); |
| if (status) |
| return status; |
| } |
| |
| /* set NAND flash memory work mode */ |
| status = nf_mem_set_work_mode(hpnfc, work_mode, timing_mode); |
| if (status) |
| return status; |
| |
| /* set dll_rst_n in dll_phy_ctrl to 0 */ |
| dll_phy_ctrl = IORD_32(hpnfc->reg + HPNFC_DLL_PHY_CTRL); |
| dll_phy_ctrl &= ~HPNFC_DLL_PHY_CTRL_DLL_RST_N_MASK; |
| IOWR_32(hpnfc->reg + HPNFC_DLL_PHY_CTRL, dll_phy_ctrl ); |
| |
| /* |
| * set value of other PHY registers according to PHY user guide |
| * currently all values for nf_clk = 100 MHz |
| */ |
| switch (work_mode) { |
| case HPNFC_WORK_MODE_NV_DDR: |
| dev_info(hpnfc->dev, "Switch to NV_DDR mode %d \n", timing_mode); |
| hpnfc_apply_phy_settings(hpnfc, phy_timings_ddr); |
| break; |
| |
| case HPNFC_WORK_MODE_NV_DDR2: |
| dev_info(hpnfc->dev, "Switch to NV_DDR2 mode %d \n", timing_mode); |
| hpnfc_apply_phy_settings(hpnfc, phy_timings_ddr2); |
| dll_phy_ctrl &= ~HPNFC_DLL_PHY_CTRL_EXTENDED_RD_MODE_MASK; |
| break; |
| |
| case HPNFC_WORK_MODE_TOGG: |
| dev_info(hpnfc->dev, "Switch to toggle DDR mode\n"); |
| hpnfc_apply_phy_settings(hpnfc, phy_timings_toggle); |
| break; |
| |
| case HPNFC_WORK_MODE_ASYNC: |
| default: |
| dev_info(hpnfc->dev, "Switch to SDR mode %d \n", timing_mode); |
| hpnfc_apply_phy_settings(hpnfc, phy_timings_async); |
| |
| reg = 0; |
| WRITE_FIELD(reg, HPNFC_ASYNC_TOGGLE_TIMINGS_TRH, 3); |
| WRITE_FIELD(reg, HPNFC_ASYNC_TOGGLE_TIMINGS_TRP, 4); |
| WRITE_FIELD(reg, HPNFC_ASYNC_TOGGLE_TIMINGS_TWH, 3); |
| WRITE_FIELD(reg, HPNFC_ASYNC_TOGGLE_TIMINGS_TWP, 4); |
| IOWR_32(hpnfc->reg + HPNFC_ASYNC_TOGGLE_TIMINGS, reg); |
| |
| dll_phy_ctrl |= HPNFC_DLL_PHY_CTRL_EXTENDED_RD_MODE_MASK; |
| dll_phy_ctrl |= HPNFC_DLL_PHY_CTRL_EXTENDED_WR_MODE_MASK; |
| } |
| |
| /* set HPNFC controller work mode */ |
| reg = IORD_32(hpnfc->reg + HPNFC_COMMON_SETT); |
| switch (work_mode) { |
| case HPNFC_WORK_MODE_NV_DDR: |
| WRITE_FIELD(reg, HPNFC_COMMON_SETT_OPR_MODE, |
| HPNFC_COMMON_SETT_OPR_MODE_NV_DDR); |
| break; |
| case HPNFC_WORK_MODE_TOGG: |
| case HPNFC_WORK_MODE_NV_DDR2: |
| case HPNFC_WORK_MODE_NV_DDR3: |
| WRITE_FIELD(reg, HPNFC_COMMON_SETT_OPR_MODE, |
| HPNFC_COMMON_SETT_OPR_MODE_TOGGLE); |
| break; |
| |
| case HPNFC_WORK_MODE_ASYNC: |
| default: |
| WRITE_FIELD(reg, HPNFC_COMMON_SETT_OPR_MODE, |
| HPNFC_COMMON_SETT_OPR_MODE_SDR); |
| } |
| IOWR_32(hpnfc->reg + HPNFC_COMMON_SETT, reg); |
| |
| /* set dll_rst_n in dll_phy_ctrl to 1 */ |
| dll_phy_ctrl |= HPNFC_DLL_PHY_CTRL_DLL_RST_N_MASK; |
| IOWR_32(hpnfc->reg + HPNFC_DLL_PHY_CTRL, dll_phy_ctrl ); |
| |
| /* wait for controller IDLE */ |
| return hpnfc_wait_for_idle(hpnfc); |
| } |
| |
| static void hpnfc_ecc_config(hpnfc_state_t *hpnfc, bool ecc, bool edet) |
| { |
| uint32_t reg = 0; |
| uint32_t corr_str = hpnfc->corr_cap / 8 - 1; |
| |
| if (ecc) { |
| WRITE_FIELD(reg, HPNFC_ECC_CONFIG_0_CORR_STR, corr_str); |
| reg |= HPNFC_ECC_CONFIG_0_ECC_EN_MASK; |
| } |
| |
| if (edet) |
| reg |= HPNFC_ECC_CONFIG_0_ERASE_DET_EN_MASK; |
| |
| IOWR_32(hpnfc->reg + HPNFC_ECC_CONFIG_0, reg); |
| } |
| |
| static void hpnfc_ecc_check_config(hpnfc_state_t *hpnfc, bool ecc, int page) |
| { |
| if (unlikely(page < hpnfc->zos_page_end)) |
| hpnfc_ecc_config(hpnfc, ecc, false); |
| else |
| hpnfc_ecc_config(hpnfc, ecc, true); |
| } |
| |
| static void hpnfc_clear_interrupt(hpnfc_state_t *hpnfc, |
| hpnfc_irq_status_t *irq_status) |
| { |
| IOWR_32(hpnfc->reg + HPNFC_INTR_STATUS, irq_status->status); |
| IOWR_32(hpnfc->reg + HPNFC_TRD_COMP_INT_STATUS, irq_status->trd_status); |
| IOWR_32(hpnfc->reg + HPNFC_TRD_ERR_INT_STATUS, irq_status->trd_error); |
| } |
| |
| static void hpnfc_read_int_status(hpnfc_state_t *hpnfc, |
| hpnfc_irq_status_t *irq_status) |
| { |
| irq_status->status = IORD_32(hpnfc->reg + HPNFC_INTR_STATUS); |
| irq_status->trd_status = IORD_32(hpnfc->reg + HPNFC_TRD_COMP_INT_STATUS); |
| irq_status->trd_error = IORD_32(hpnfc->reg + HPNFC_TRD_ERR_INT_STATUS); |
| } |
| |
| static inline uint32_t irq_detected(hpnfc_state_t *hpnfc, |
| hpnfc_irq_status_t *irq_status) |
| { |
| hpnfc_read_int_status(hpnfc, irq_status); |
| |
| return irq_status->status || irq_status->trd_status |
| || irq_status->trd_error; |
| } |
| |
| static irqreturn_t hpnfc_isr(int irq, void *dev_id) |
| { |
| struct hpnfc_state_t *hpnfc = dev_id; |
| hpnfc_irq_status_t irq_status; |
| irqreturn_t result = IRQ_NONE; |
| |
| spin_lock(&hpnfc->irq_lock); |
| |
| if (irq_detected(hpnfc, &irq_status)) { |
| /* handle interrupt */ |
| /* first acknowledge it */ |
| hpnfc_clear_interrupt(hpnfc, &irq_status); |
| /* store the status in the device context for someone to read */ |
| hpnfc->irq_status.status |= irq_status.status; |
| hpnfc->irq_status.trd_status |= irq_status.trd_status; |
| hpnfc->irq_status.trd_error |= irq_status.trd_error; |
| /* notify anyone who cares that it happened */ |
| complete(&hpnfc->complete); |
| /* tell the OS that we've handled this */ |
| result = IRQ_HANDLED; |
| } |
| spin_unlock(&hpnfc->irq_lock); |
| return result; |
| } |
| |
| static void wait_for_irq(struct hpnfc_state_t *hpnfc, |
| hpnfc_irq_status_t *irq_mask, |
| hpnfc_irq_status_t *irq_status) |
| { |
| unsigned long comp_res; |
| unsigned long timeout = msecs_to_jiffies(10000); |
| |
| do { |
| comp_res = |
| wait_for_completion_timeout(&hpnfc->complete, timeout); |
| spin_lock_irq(&hpnfc->irq_lock); |
| *irq_status = hpnfc->irq_status; |
| |
| if ((irq_status->status & irq_mask->status) |
| || (irq_status->trd_status & irq_mask->trd_status) |
| || (irq_status->trd_error & irq_mask->trd_error) |
| ) { |
| hpnfc->irq_status.status &= ~irq_mask->status; |
| hpnfc->irq_status.trd_status &= ~irq_mask->trd_status; |
| hpnfc->irq_status.trd_error &= ~irq_mask->trd_error; |
| spin_unlock_irq(&hpnfc->irq_lock); |
| /* our interrupt was detected */ |
| break; |
| } |
| |
| /* |
| * these are not the interrupts you are looking for; need to |
| * wait again |
| */ |
| spin_unlock_irq(&hpnfc->irq_lock); |
| } while (comp_res != 0); |
| |
| if (comp_res == 0) { |
| /* timeout */ |
| dev_err(hpnfc->dev, "timeout occurred:" |
| "\t status = 0x%x, mask = 0x%x\n" |
| "\t trd_status = 0x%x, trd_status mask= 0x%x\n" |
| "\t trd_error = 0x%x, trd_error mask = 0x%x\n", |
| irq_status->status, irq_mask->status, |
| irq_status->trd_status, irq_mask->trd_status, |
| irq_status->trd_error, irq_mask->trd_error |
| ); |
| |
| memset(irq_status, 0, sizeof(hpnfc_irq_status_t)); |
| } |
| } |
| |
| static void hpnfc_irq_cleanup(int irqnum, struct hpnfc_state_t *hpnfc) |
| { |
| /* disable interrupts */ |
| IOWR_32(hpnfc->reg + HPNFC_INTR_ENABLE, HPNFC_INTR_ENABLE_INTR_EN_MASK); |
| } |
| |
| /* |
| * We need to buffer some data for some of the NAND core routines. |
| * The operations manage buffering that data. |
| */ |
| static void reset_buf(struct hpnfc_state_t *hpnfc) |
| { |
| hpnfc->buf.head = hpnfc->buf.tail = 0; |
| memset(&hpnfc->buf.buf[0], 0, 20); |
| } |
| |
| static void write_byte_to_buf(struct hpnfc_state_t *hpnfc, uint8_t byte) |
| { |
| hpnfc->buf.buf[hpnfc->buf.tail++] = byte; |
| } |
| |
| static void write_dword_to_buf(struct hpnfc_state_t *hpnfc, uint32_t dword) |
| { |
| memcpy(&hpnfc->buf.buf[hpnfc->buf.tail], &dword, 4); |
| hpnfc->buf.tail += 4; |
| } |
| |
| static uint8_t* get_buf_ptr(struct hpnfc_state_t *hpnfc) |
| { |
| return &hpnfc->buf.buf[hpnfc->buf.tail]; |
| } |
| |
| static void increase_buff_ptr(struct hpnfc_state_t *hpnfc, uint32_t size) |
| { |
| hpnfc->buf.tail += size; |
| } |
| |
| /* wait until NAND flash device is ready */ |
| static int wait_for_rb_ready(hpnfc_state_t *hpnfc) |
| { |
| uint32_t reg; |
| unsigned long timeout = jiffies + msecs_to_jiffies(1000); |
| |
| do { |
| reg = IORD_32(hpnfc->reg + HPNFC_RBN_SETTINGS); |
| reg = (reg >> hpnfc->chip_nr) & 0x01; |
| } while ((reg == 0) && time_before(jiffies, timeout)); |
| |
| if (time_after_eq(jiffies, timeout)) { |
| dev_err(hpnfc->dev, |
| "Timeout while waiting for flash device %d ready\n", |
| hpnfc->chip_nr); |
| return -ETIMEDOUT; |
| } |
| return 0; |
| } |
| |
| static int wait_for_thread(hpnfc_state_t *hpnfc, int8_t thread) |
| { |
| uint32_t reg; |
| unsigned long timeout = jiffies + msecs_to_jiffies(1000); |
| |
| do { |
| /* get busy status of all threads */ |
| reg = IORD_32(hpnfc->reg + HPNFC_TRD_STATUS); |
| /* mask all threads but selected */ |
| reg &= (1 << thread); |
| } while (reg && time_before(jiffies, timeout)); |
| |
| if (time_after_eq(jiffies, timeout)) { |
| dev_err(hpnfc->dev, "Timeout while waiting for thread %d\n", thread); |
| return -ETIMEDOUT; |
| } |
| |
| return 0; |
| } |
| |
| static int hpnfc_wait_for_idle(hpnfc_state_t *hpnfc) |
| { |
| uint32_t reg; |
| unsigned long timeout = jiffies + msecs_to_jiffies(1000); |
| |
| do { |
| reg = IORD_32(hpnfc->reg + HPNFC_CTRL_STATUS); |
| } while ((reg & HPNFC_CTRL_STATUS_CTRL_BUSY_MASK) |
| && time_before(jiffies, timeout)); |
| |
| if (time_after_eq(jiffies, timeout)) { |
| dev_err(hpnfc->dev, "Timeout while waiting for controller idle\n"); |
| return -ETIMEDOUT; |
| } |
| |
| return 0; |
| } |
| |
| /* This function waits for device initialization */ |
| static int wait_for_init_complete(hpnfc_state_t *hpnfc) |
| { |
| uint32_t reg; |
| unsigned long timeout = jiffies + msecs_to_jiffies(10000); |
| |
| do { /* get ctrl status register */ |
| reg = IORD_32(hpnfc->reg + HPNFC_CTRL_STATUS); |
| } while (((reg & HPNFC_CTRL_STATUS_INIT_COMP_MASK) == 0) |
| && time_before(jiffies, timeout)); |
| |
| if (time_after_eq(jiffies, timeout)) { |
| dev_err(hpnfc->dev, |
| "Timeout while waiting for controller init complete\n"); |
| return -ETIMEDOUT; |
| } |
| |
| return 0; |
| } |
| |
| /* execute generic command on HPNFC controller */ |
| static int hpnfc_generic_cmd_send(hpnfc_state_t *hpnfc, uint8_t thread_nr, |
| uint64_t mini_ctrl_cmd, uint8_t use_intr) |
| { |
| uint32_t reg = 0; |
| uint8_t status; |
| |
| uint32_t mini_ctrl_cmd_l = mini_ctrl_cmd & 0xFFFFFFFF; |
| uint32_t mini_ctrl_cmd_h = mini_ctrl_cmd >> 32; |
| |
| status = wait_for_thread(hpnfc, thread_nr); |
| if (status) |
| return status; |
| |
| IOWR_32(hpnfc->reg + HPNFC_CMD_REG2, mini_ctrl_cmd_l); |
| IOWR_32(hpnfc->reg + HPNFC_CMD_REG3, mini_ctrl_cmd_h); |
| |
| /* select generic command */ |
| WRITE_FIELD(reg, HPNFC_CMD_REG0_CT, HPNFC_CMD_REG0_CT_GEN); |
| /* thread number */ |
| WRITE_FIELD(reg, HPNFC_CMD_REG0_TN, thread_nr); |
| if (use_intr) |
| reg |= HPNFC_CMD_REG0_INT_MASK; |
| |
| /* issue command */ |
| IOWR_32(hpnfc->reg + HPNFC_CMD_REG0, reg); |
| |
| return 0; |
| } |
| |
| /* prepare generic command on HPNFC controller */ |
| static int hpnfc_generic_cmd_command(hpnfc_state_t *hpnfc, uint32_t command, |
| uint64_t addr, uint8_t use_intr) |
| { |
| uint64_t mini_ctrl_cmd = 0; |
| uint8_t thread_nr = hpnfc->chip_nr; |
| int status; |
| |
| switch (command) { |
| case HPNFC_GCMD_LAY_INSTR_RDPP: |
| mini_ctrl_cmd |= HPNFC_GCMD_LAY_TWB_MASK; |
| break; |
| case HPNFC_GCMD_LAY_INSTR_RDID: |
| mini_ctrl_cmd |= HPNFC_GCMD_LAY_TWB_MASK; |
| break; |
| default: |
| break; |
| } |
| |
| WRITE_FIELD64(mini_ctrl_cmd, HPNFC_GCMD_LAY_INSTR, command); |
| |
| WRITE_FIELD64(mini_ctrl_cmd, HPNFC_GCMD_LAY_CS, hpnfc->chip_nr); |
| WRITE_FIELD64(mini_ctrl_cmd, HPNFC_GCMD_LAY_INPUT_ADDR0, addr); |
| |
| /* send command */ |
| status = hpnfc_generic_cmd_send(hpnfc, thread_nr, mini_ctrl_cmd, use_intr); |
| if (status) |
| return status; |
| |
| /* wait for thread ready*/ |
| status = wait_for_thread(hpnfc, thread_nr); |
| if (status) |
| return status; |
| return 0; |
| } |
| |
| /* prepare generic command used to data transfer on HPNFC controller */ |
| static int hpnfc_generic_cmd_data(hpnfc_state_t *hpnfc, |
| generic_data_t *generic_data) |
| { |
| uint64_t mini_ctrl_cmd = 0; |
| uint8_t thread_nr = hpnfc->chip_nr; |
| |
| WRITE_FIELD64(mini_ctrl_cmd, HPNFC_GCMD_LAY_CS, hpnfc->chip_nr); |
| WRITE_FIELD64(mini_ctrl_cmd, HPNFC_GCMD_LAY_INSTR, |
| HPNFC_GCMD_LAY_INSTR_DATA); |
| WRITE_FIELD64(mini_ctrl_cmd, HPNFC_GCMD_DIR, generic_data->direction); |
| |
| if (generic_data->ecc_en) |
| mini_ctrl_cmd |= HPNFC_GCMD_ECC_EN_MASK; |
| |
| if (generic_data->scr_en) |
| mini_ctrl_cmd |= HPNFC_GCMD_SCR_EN_MASK; |
| |
| if (generic_data->erpg_en) |
| mini_ctrl_cmd |= HPNFC_GCMD_ERPG_EN_MASK; |
| |
| WRITE_FIELD64(mini_ctrl_cmd, HPNFC_GCMD_SECT_SIZE, |
| (uint64_t)generic_data->sec_size); |
| WRITE_FIELD64(mini_ctrl_cmd, HPNFC_GCMD_SECT_CNT, |
| (uint64_t)generic_data->sec_cnt); |
| WRITE_FIELD64(mini_ctrl_cmd, HPNFC_GCMD_LAST_SIZE, |
| (uint64_t)generic_data->last_sec_size); |
| WRITE_FIELD64(mini_ctrl_cmd, HPNFC_GCMD_CORR_CAP, |
| (uint64_t)generic_data->corr_cap); |
| |
| return hpnfc_generic_cmd_send(hpnfc, thread_nr, mini_ctrl_cmd, |
| generic_data->use_intr); |
| } |
| |
| /* wait for data on slave dma interface */ |
| static int hpnfc_wait_on_sdma_trigg(hpnfc_state_t *hpnfc, |
| uint8_t* out_sdma_trd, |
| uint32_t* out_sdma_size) |
| { |
| hpnfc_irq_status_t irq_mask, irq_status; |
| |
| irq_mask.trd_status = 0; |
| irq_mask.trd_error = 0; |
| irq_mask.status = HPNFC_INTR_STATUS_SDMA_TRIGG_MASK |
| | HPNFC_INTR_STATUS_SDMA_ERR_MASK |
| | HPNFC_INTR_STATUS_UNSUPP_CMD_MASK; |
| |
| wait_for_irq(hpnfc, &irq_mask, &irq_status); |
| if (irq_status.status == 0) { |
| dev_err(hpnfc->dev, "Timeout while waiting for SDMA\n"); |
| return -ETIMEDOUT; |
| } |
| |
| if (irq_status.status & HPNFC_INTR_STATUS_SDMA_TRIGG_MASK) { |
| *out_sdma_size = IORD_32(hpnfc->reg + HPNFC_SDMA_SIZE); |
| *out_sdma_trd = IORD_32(hpnfc->reg + HPNFC_SDMA_TRD_NUM); |
| *out_sdma_trd = READ_FIELD(*out_sdma_trd, HPNFC_SDMA_TRD_NUM_SDMA_TRD); |
| } else { |
| dev_err(hpnfc->dev, "SDMA error - irq_status %x\n", irq_status.status); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| /* read data from slave DMA interface */ |
| static int dma_read_data(hpnfc_state_t *hpnfc, void *buf, uint32_t size) |
| { |
| int i; |
| uint32_t *buf32 = buf; |
| |
| if (size & 3) { |
| return -1; |
| } |
| for (i = 0; i < size / hpnfc->bytesPerSdmaAccess; i++) { |
| *buf32 = IORD_32(hpnfc->slave_dma); |
| buf32++; |
| } |
| |
| return 0; |
| } |
| |
| static void hpnfc_read_buf32(struct mtd_info *mtd, uint8_t *buf, int len) |
| { |
| int status; |
| uint8_t sdmatrd_num; |
| uint32_t sdma_size; |
| generic_data_t generic_data; |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| |
| memset(&generic_data, 0, sizeof(generic_data_t)); |
| generic_data.sec_cnt = 1; |
| generic_data.last_sec_size = len; |
| generic_data.direction = HPNFC_GCMD_DIR_READ; |
| |
| /* wait for finishing operation */ |
| status = wait_for_rb_ready(hpnfc); |
| if (status) |
| return; |
| |
| /* prepare controller to read data in generic mode */ |
| status = hpnfc_generic_cmd_data(hpnfc, &generic_data); |
| if (status) |
| return; |
| |
| /* wait until data is read on slave DMA */ |
| status = hpnfc_wait_on_sdma_trigg(hpnfc, &sdmatrd_num, &sdma_size); |
| if (status) |
| return; |
| |
| /* read data */ |
| status = dma_read_data(hpnfc, buf, sdma_size); |
| if (status) |
| return; |
| |
| hpnfc->offset += len; |
| } |
| |
| static void hpnfc_read_buf64(struct mtd_info *mtd, uint8_t *buf, int len) |
| { |
| int status; |
| uint8_t sdmatrd_num; |
| uint32_t sdma_size; |
| generic_data_t generic_data; |
| uint32_t tmp[2]; |
| uint8_t sub_size = 4; |
| int i = 0; |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| |
| memset(&generic_data, 0, sizeof(generic_data_t)); |
| generic_data.sec_cnt = 1; |
| generic_data.last_sec_size = sub_size; |
| generic_data.direction = HPNFC_GCMD_DIR_READ; |
| |
| /* read is made by 4 bytes because of platform limitation */ |
| while (len) { |
| /* send change column command */ |
| status = hpnfc_generic_cmd_command(hpnfc, |
| HPNFC_GCMD_LAY_INSTR_CHRC, |
| hpnfc->offset, |
| 0); |
| if (status) |
| return; |
| |
| /* wait for finishing operation */ |
| status = wait_for_rb_ready(hpnfc); |
| if (status) |
| return; |
| |
| /* prepare controller to read data in generic mode */ |
| status = hpnfc_generic_cmd_data(hpnfc, &generic_data); |
| if (status) |
| return; |
| |
| /* wait until data is read on slave DMA */ |
| status = hpnfc_wait_on_sdma_trigg(hpnfc, &sdmatrd_num, &sdma_size); |
| if (status) |
| return; |
| |
| /* read data */ |
| status = dma_read_data(hpnfc, tmp, sdma_size); |
| if (status) |
| return; |
| |
| if (len < sub_size) |
| sub_size = len; |
| |
| memcpy(buf + i, &tmp[0], sub_size); |
| |
| len -= sub_size; |
| hpnfc->offset += sub_size; |
| i += sub_size; |
| } |
| } |
| |
| static void hpnfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| |
| if (hpnfc->bytesPerSdmaAccess == 8) |
| hpnfc_read_buf64(mtd, buf, len); |
| else |
| hpnfc_read_buf32(mtd, buf, len); |
| } |
| |
| static int read_parameter_page64(hpnfc_state_t *hpnfc, uint32_t size) |
| { |
| int status; |
| uint8_t sdmatrd_num; |
| uint32_t sdma_size; |
| generic_data_t generic_data; |
| uint32_t tmp[2]; |
| const uint8_t sub_size = 4; |
| uint32_t offset = 0; |
| |
| memset(&generic_data, 0, sizeof(generic_data_t)); |
| generic_data.sec_cnt = 1; |
| generic_data.last_sec_size = sub_size; |
| generic_data.direction = HPNFC_GCMD_DIR_READ; |
| |
| /* execute read parameter page instruction */ |
| status = hpnfc_generic_cmd_command(hpnfc, HPNFC_GCMD_LAY_INSTR_RDPP, 0, 0); |
| if (status) |
| return status; |
| |
| /* wait for finishing operation */ |
| status = wait_for_rb_ready(hpnfc); |
| if (status) |
| return status; |
| |
| /* read is made by 4 bytes because of platform limitation */ |
| while (size) { |
| /* prepare controller to read data in generic mode */ |
| status = hpnfc_generic_cmd_data(hpnfc, &generic_data); |
| if (status) |
| return status; |
| |
| /* wait until data is read on slave DMA */ |
| status = hpnfc_wait_on_sdma_trigg(hpnfc, &sdmatrd_num, &sdma_size); |
| if (status) |
| return status; |
| |
| /* read data (part of parameter page) */ |
| status = dma_read_data(hpnfc, tmp, sdma_size); |
| if (status) |
| return status; |
| |
| write_dword_to_buf(hpnfc, tmp[0]); |
| |
| size -= sub_size; |
| offset += sub_size; |
| |
| /* send change column command */ |
| status = hpnfc_generic_cmd_command(hpnfc, |
| HPNFC_GCMD_LAY_INSTR_CHRC, offset, |
| 0); |
| if (status) |
| return status; |
| |
| /* wait for finishing operation */ |
| status = wait_for_rb_ready(hpnfc); |
| if (status) |
| return status; |
| |
| } |
| |
| return 0; |
| } |
| |
| static int read_parameter_page32(hpnfc_state_t *hpnfc, uint32_t size) |
| { |
| int status; |
| uint8_t sdmatrd_num; |
| uint32_t sdma_size; |
| generic_data_t generic_data; |
| uint8_t *buffer; |
| |
| memset(&generic_data, 0, sizeof(generic_data_t)); |
| generic_data.sec_cnt = 1; |
| generic_data.last_sec_size = size; |
| generic_data.direction = HPNFC_GCMD_DIR_READ; |
| |
| /* execute read parameter page instruction */ |
| status = hpnfc_generic_cmd_command(hpnfc, HPNFC_GCMD_LAY_INSTR_RDPP, 0, 0); |
| if (status) |
| return status; |
| |
| /* wait for finishing operation */ |
| status = wait_for_rb_ready(hpnfc); |
| if (status) |
| return status; |
| |
| /* prepare controller to read data in generic mode */ |
| status = hpnfc_generic_cmd_data(hpnfc, &generic_data); |
| if (status) |
| return status; |
| |
| /* wait until data is read on slave DMA */ |
| status = hpnfc_wait_on_sdma_trigg(hpnfc, &sdmatrd_num, &sdma_size); |
| if (status) |
| return status; |
| |
| buffer = get_buf_ptr(hpnfc); |
| /* read data (part of parameter page) */ |
| status = dma_read_data(hpnfc, buffer, sdma_size); |
| if (status) |
| return status; |
| |
| increase_buff_ptr(hpnfc, sdma_size); |
| |
| return 0; |
| } |
| |
| static int read_parameter_page(hpnfc_state_t *hpnfc, uint32_t size) |
| { |
| if (hpnfc->bytesPerSdmaAccess == 8) |
| return read_parameter_page64(hpnfc, size); |
| else |
| return read_parameter_page32(hpnfc, size); |
| } |
| |
| static int nf_mem_read_id(hpnfc_state_t *hpnfc, uint8_t address, uint32_t size) |
| { |
| int status; |
| uint8_t sdmatrd_num; |
| uint32_t sdma_size; |
| generic_data_t generic_data; |
| uint32_t tmp[4]; |
| |
| memset(&generic_data, 0, sizeof(generic_data_t)); |
| generic_data.sec_cnt = 1; |
| generic_data.last_sec_size = size; |
| generic_data.direction = HPNFC_GCMD_DIR_READ; |
| |
| /* execute read ID instruction */ |
| status = hpnfc_generic_cmd_command(hpnfc, HPNFC_GCMD_LAY_INSTR_RDID, |
| address, 0); |
| if (status) |
| return status; |
| |
| /* wait for finishing operation */ |
| status = wait_for_rb_ready(hpnfc); |
| if (status) |
| return status; |
| |
| /* prepare controller to read data in generic mode */ |
| status = hpnfc_generic_cmd_data(hpnfc, &generic_data); |
| if (status) |
| return status; |
| |
| /* wait until data is read on slave DMA */ |
| status = hpnfc_wait_on_sdma_trigg(hpnfc, &sdmatrd_num, &sdma_size); |
| if (status) |
| return status; |
| |
| /* read data (flash id) */ |
| status = dma_read_data(hpnfc, tmp, sdma_size); |
| if (status) |
| return status; |
| |
| memcpy(&hpnfc->buf.buf[hpnfc->buf.tail], tmp, sdma_size); |
| hpnfc->buf.tail += sdma_size; |
| |
| return 0; |
| } |
| |
| static void hpnfc_get_max_banks(hpnfc_state_t *hpnfc) |
| { |
| uint32_t reg; |
| |
| reg = IORD_32(hpnfc->reg + HPNFC_CTRL_FEATURES); |
| hpnfc->max_banks = READ_FIELD(reg, HPNFC_CTRL_FEATURES_N_BANKS); |
| |
| if (maxchips < hpnfc->max_banks) |
| hpnfc->max_banks = maxchips; |
| } |
| |
| static void hpnfc_get_dma_data_width(hpnfc_state_t *hpnfc) |
| { |
| uint32_t reg; |
| |
| reg = IORD_32(hpnfc->reg + HPNFC_CTRL_FEATURES); |
| |
| if (READ_FIELD(reg, HPNFC_CTRL_FEATURES_DMA_DWITH_64)) |
| hpnfc->bytesPerSdmaAccess = 8; |
| else |
| hpnfc->bytesPerSdmaAccess = 4; |
| } |
| |
| static int hpnfc_dev_info(hpnfc_state_t *hpnfc) |
| { |
| uint32_t reg; |
| struct mtd_info *mtd = nand_to_mtd(&hpnfc->nand); |
| |
| reg = IORD_32(hpnfc->reg + HPNFC_DEV_PARAMS_0); |
| hpnfc->dev_type = READ_FIELD(reg, HPNFC_DEV_PARAMS_0_DEV_TYPE); |
| |
| switch (hpnfc->dev_type) { |
| case HPNFC_DEV_PARAMS_0_DEV_TYPE_ONFI: |
| dev_info(hpnfc->dev, "Detected ONFI device:\n"); |
| break; |
| case HPNFC_DEV_PARAMS_0_DEV_TYPE_JEDEC: |
| dev_info(hpnfc->dev, "Detected JEDEC device:\n"); |
| break; |
| default: |
| dev_info(hpnfc->dev, "Device type was not detected.\n"); |
| } |
| |
| hpnfc->spare_size = mtd->oobsize; |
| hpnfc->main_size = mtd->writesize; |
| dev_info(hpnfc->dev, "-- Page main area size: %u\n", hpnfc->main_size); |
| dev_info(hpnfc->dev, "-- Page spare area size: %u\n", hpnfc->spare_size); |
| |
| hpnfc->devnum = hpnfc->nand.numchips; |
| hpnfc->chip_nr = 0; |
| |
| return 0; |
| } |
| |
| static void hpnfc_cdma_desc_prepare(hpnfc_cdma_desc_t* cdma_desc, char nf_mem, |
| uint32_t flash_ptr, char* mem_ptr, |
| uint16_t ctype) |
| { |
| memset(cdma_desc, 0, sizeof(hpnfc_cdma_desc_t)); |
| |
| /* set fields for one descriptor */ |
| cdma_desc->flash_pointer = (nf_mem << HPNFC_CDMA_CFPTR_MEM_SHIFT) |
| + flash_ptr; |
| cdma_desc->command_flags |= HPNFC_CDMA_CF_DMA_MASTER; |
| cdma_desc->command_flags |= HPNFC_CDMA_CF_INT; |
| |
| cdma_desc->memory_pointer = (uintptr_t)mem_ptr; |
| |
| cdma_desc->command_type = ctype; |
| |
| /* ensure desc is written before poking hardware */ |
| wmb(); |
| } |
| |
| static uint8_t hpnfc_check_desc_error(uint32_t desc_status) |
| { |
| if (desc_status & HPNFC_CDMA_CS_ERP_MASK) |
| return HPNFC_STAT_ERASED; |
| |
| if (desc_status & HPNFC_CDMA_CS_UNCE_MASK) |
| return HPNFC_STAT_ECC_UNCORR; |
| |
| if (desc_status & HPNFC_CDMA_CS_ERR_MASK) { |
| pr_err(CADENCE_NAND_NAME ":CDMA descriptor error flag detected.\n"); |
| return HPNFC_STAT_FAIL; |
| } |
| |
| if (READ_FIELD(desc_status, HPNFC_CDMA_CS_MAXERR)) |
| return HPNFC_STAT_ECC_CORR; |
| |
| if (desc_status & HPNFC_CDMA_CS_FAIL_MASK) |
| return HPNFC_STAT_FAIL; |
| |
| return HPNFC_STAT_OK; |
| } |
| |
| static int hpnfc_wait_cdma_finish(hpnfc_cdma_desc_t* cdma_desc) |
| { |
| hpnfc_cdma_desc_t *desc_ptr; |
| uint32_t desc_status; |
| uint8_t status = HPNFC_STAT_BUSY; |
| |
| desc_ptr = cdma_desc; |
| do { |
| desc_status = desc_ptr->status; |
| if (desc_status & HPNFC_CDMA_CS_FAIL_MASK) { |
| status = hpnfc_check_desc_error(desc_status); |
| pr_err(CADENCE_NAND_NAME ":CDMA error %x\n", desc_status); |
| break; |
| } |
| if (desc_status & HPNFC_CDMA_CS_COMP_MASK) { |
| /* descriptor finished with no errors */ |
| if (desc_ptr->command_flags & HPNFC_CDMA_CF_CONT) |
| /* not last descriptor */ |
| desc_ptr = |
| (hpnfc_cdma_desc_t*)(uintptr_t)desc_ptr->next_pointer; |
| else |
| /* last descriptor */ |
| status = hpnfc_check_desc_error(desc_status); |
| } |
| } while (status == HPNFC_STAT_BUSY); |
| |
| return status; |
| } |
| |
| static int hpnfc_cdma_send(hpnfc_state_t *hpnfc, uint8_t thread) |
| { |
| uint32_t reg = 0; |
| int status; |
| |
| status = wait_for_thread(hpnfc, thread); |
| if (status) |
| return status; |
| |
| IOWR_32(hpnfc->reg + HPNFC_CMD_REG2, (uint32_t )hpnfc->dma_cdma_desc); |
| IOWR_32(hpnfc->reg + HPNFC_CMD_REG3, 0 ); |
| |
| /* select CDMA mode */ |
| WRITE_FIELD(reg, HPNFC_CMD_REG0_CT, HPNFC_CMD_REG0_CT_CDMA); |
| /* thread number */ |
| WRITE_FIELD(reg, HPNFC_CMD_REG0_TN, thread); |
| /* issue command */ |
| IOWR_32(hpnfc->reg + HPNFC_CMD_REG0, reg); |
| |
| return 0; |
| } |
| |
| static uint32_t hpnfc_cdma_send_and_wait(hpnfc_state_t *hpnfc, uint8_t thread) |
| { |
| int status; |
| hpnfc_irq_status_t irq_mask, irq_status; |
| |
| status = hpnfc_cdma_send(hpnfc, thread); |
| if (status) |
| return status; |
| |
| irq_mask.trd_status = 1 << thread; |
| irq_mask.trd_error = 1 << thread; |
| irq_mask.status = HPNFC_INTR_STATUS_CDMA_TERR_MASK; |
| wait_for_irq(hpnfc, &irq_mask, &irq_status); |
| |
| if ((irq_status.status == 0) && (irq_status.trd_status == 0) |
| && (irq_status.trd_error == 0)) { |
| dev_err(hpnfc->dev, "CDMA command timeout\n"); |
| return -ETIMEDOUT; |
| } |
| if (irq_status.status & irq_mask.status) { |
| dev_err(hpnfc->dev, "CDMA command failed\n"); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int hpnfc_hw_init(hpnfc_state_t *hpnfc) |
| { |
| uint32_t reg; |
| int status; |
| |
| status = wait_for_init_complete(hpnfc); |
| if (status) |
| return status; |
| |
| #if 0 |
| tmp = HPNFC_DMA_SETTINGS_OTE_MASK; |
| WRITE_FIELD(tmp, HPNFC_DMA_SETTINGS_BURST_SEL, 127); |
| IOWR_32(hpnfc->reg + HPNFC_DMA_SETTINGS, tmp); |
| #endif |
| |
| /* disable cache and multiplane */ |
| IOWR_32(hpnfc->reg + HPNFC_MULTIPLANE_CFG, 0); |
| IOWR_32(hpnfc->reg + HPNFC_CACHE_CFG, 0); |
| |
| /* enable interrupts */ |
| reg = HPNFC_INTR_ENABLE_INTR_EN_MASK |
| | HPNFC_INTR_ENABLE_CDMA_TERR_EN_MASK |
| | HPNFC_INTR_ENABLE_DDMA_TERR_EN_MASK |
| | HPNFC_INTR_ENABLE_UNSUPP_CMD_EN_MASK |
| | HPNFC_INTR_ENABLE_SDMA_TRIGG_EN_MASK |
| | HPNFC_INTR_ENABLE_SDMA_ERR_EN_MASK; |
| IOWR_32(hpnfc->reg + HPNFC_INTR_ENABLE, reg); |
| /* clear all interrupts */ |
| IOWR_32(hpnfc->reg + HPNFC_INTR_STATUS, 0xFFFFFFFF); |
| /* enable signaling thread error interrupts for all threads */ |
| IOWR_32(hpnfc->reg + HPNFC_TRD_ERR_INT_STATUS_EN, 0xFF); |
| |
| return 0; |
| } |
| |
| static int hpnfc_read_bch_cfg(struct hpnfc_state_t *hpnfc) |
| { |
| uint32_t reg; |
| |
| reg = IORD_32(hpnfc->reg + HPNFC_BCH_CFG_0); |
| hpnfc->bch_cfg.corr_caps[0] = READ_FIELD(reg, HPNFC_BCH_CFG_0_CORR_CAP_0); |
| hpnfc->bch_cfg.corr_caps[1] = READ_FIELD(reg, HPNFC_BCH_CFG_0_CORR_CAP_1); |
| hpnfc->bch_cfg.corr_caps[2] = READ_FIELD(reg, HPNFC_BCH_CFG_0_CORR_CAP_2); |
| hpnfc->bch_cfg.corr_caps[3] = READ_FIELD(reg, HPNFC_BCH_CFG_0_CORR_CAP_3); |
| |
| reg = IORD_32(hpnfc->reg + HPNFC_BCH_CFG_1); |
| hpnfc->bch_cfg.corr_caps[4] = READ_FIELD(reg, HPNFC_BCH_CFG_1_CORR_CAP_4); |
| hpnfc->bch_cfg.corr_caps[5] = READ_FIELD(reg, HPNFC_BCH_CFG_1_CORR_CAP_5); |
| hpnfc->bch_cfg.corr_caps[6] = READ_FIELD(reg, HPNFC_BCH_CFG_1_CORR_CAP_6); |
| hpnfc->bch_cfg.corr_caps[7] = READ_FIELD(reg, HPNFC_BCH_CFG_1_CORR_CAP_7); |
| |
| reg = IORD_32(hpnfc->reg + HPNFC_BCH_CFG_2); |
| hpnfc->bch_cfg.sector_sizes[0] = READ_FIELD(reg, HPNFC_BCH_CFG_2_SECT_0); |
| hpnfc->bch_cfg.sector_sizes[1] = READ_FIELD(reg, HPNFC_BCH_CFG_2_SECT_1); |
| |
| return 0; |
| } |
| |
| /* calculate size of check bit size per one sector */ |
| static int bch_calculate_ecc_size(struct hpnfc_state_t *hpnfc, |
| uint32_t *check_bit_size) |
| { |
| uint8_t mult = 14; |
| uint32_t tmp, corr_cap = hpnfc->nand.ecc.strength; |
| uint32_t max_sector_size, sector_size = hpnfc->nand.ecc.size; |
| int i; |
| |
| *check_bit_size = 0; |
| |
| for (i = 0; i < HPNFC_BCH_MAX_NUM_SECTOR_SIZES; i++) { |
| if (sector_size == hpnfc->bch_cfg.sector_sizes[i]) { |
| break; |
| } |
| } |
| |
| if (i >= HPNFC_BCH_MAX_NUM_SECTOR_SIZES) { |
| dev_err(hpnfc->dev, |
| "Wrong ECC configuration, ECC sector size:%u" |
| "is not supported. List of supported sector sizes\n", sector_size); |
| for (i = 0; i < HPNFC_BCH_MAX_NUM_SECTOR_SIZES; i++) { |
| if (hpnfc->bch_cfg.sector_sizes[i] == 0) |
| break; |
| dev_err(hpnfc->dev, |
| "%u ", hpnfc->bch_cfg.sector_sizes[i]); |
| } |
| return -1; |
| } |
| |
| if (hpnfc->bch_cfg.sector_sizes[1] > hpnfc->bch_cfg.sector_sizes[0]) |
| max_sector_size = hpnfc->bch_cfg.sector_sizes[1]; |
| else |
| max_sector_size = hpnfc->bch_cfg.sector_sizes[0]; |
| |
| switch (max_sector_size) { |
| case 256: |
| mult = 12; |
| break; |
| case 512: |
| mult = 13; |
| break; |
| case 1024: |
| mult = 14; |
| break; |
| case 2048: |
| mult = 15; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| for (i = 0; i < HPNFC_BCH_MAX_NUM_CORR_CAPS; i++) { |
| if (corr_cap == hpnfc->bch_cfg.corr_caps[i]) |
| break; |
| } |
| |
| if (i >= HPNFC_BCH_MAX_NUM_CORR_CAPS) { |
| dev_err(hpnfc->dev, |
| "Wrong ECC configuration, correction capability:%d" |
| "is not supported. List of supported corrections: \n", corr_cap); |
| for (i = 0; i < HPNFC_BCH_MAX_NUM_CORR_CAPS; i++) { |
| if (hpnfc->bch_cfg.corr_caps[i] == 0) |
| break; |
| dev_err(hpnfc->dev, |
| "%d ", hpnfc->bch_cfg.corr_caps[i]); |
| } |
| return -1; |
| } |
| |
| hpnfc->sector_size = sector_size; |
| hpnfc->corr_cap = corr_cap; |
| |
| tmp = (mult * corr_cap) / 16; |
| /* round up */ |
| if ((tmp * 16) < (mult * corr_cap)) |
| tmp++; |
| |
| /* check bit size per one sector */ |
| *check_bit_size = 2 * tmp; |
| |
| return 0; |
| } |
| |
| #define TT_SPARE_AREA 1 |
| #define TT_MAIN_SPARE_AREAS 2 |
| #define TT_RAW_SPARE_AREA 3 |
| #define TT_MAIN_AREA 4 |
| #define TT_RAW_ZOS_SPARE_AREA 5 |
| |
| static int hpnfc_prepare_data_size(hpnfc_state_t *hpnfc, int transfer_type) |
| { |
| uint32_t reg = 0; |
| uint32_t sec_size = 0, last_sec_size, offset, sec_cnt; |
| uint32_t ecc_size = hpnfc->nand.ecc.bytes; |
| |
| if (hpnfc->curr_trans_type == transfer_type) |
| return 0; |
| |
| switch (transfer_type) { |
| case TT_SPARE_AREA: |
| offset = hpnfc->main_size - hpnfc->sector_size; |
| ecc_size = ecc_size * (offset / hpnfc->sector_size); |
| offset = offset + ecc_size; |
| sec_cnt = 1; |
| last_sec_size = hpnfc->sector_size + hpnfc->usnused_spare_size; |
| break; |
| case TT_MAIN_SPARE_AREAS: |
| offset = 0; |
| sec_cnt = hpnfc->sector_count; |
| last_sec_size = hpnfc->sector_size + hpnfc->usnused_spare_size; |
| sec_size = hpnfc->sector_size; |
| break; |
| case TT_RAW_SPARE_AREA: |
| offset = hpnfc->main_size - hpnfc->sector_size; |
| ecc_size = ecc_size * (offset / hpnfc->sector_size); |
| offset = offset + ecc_size; |
| sec_cnt = 1; |
| last_sec_size = hpnfc->usnused_spare_size; |
| break; |
| case TT_MAIN_AREA: |
| offset = 0; |
| sec_cnt = hpnfc->sector_count; |
| last_sec_size = hpnfc->sector_size; |
| sec_size = hpnfc->sector_size; |
| break; |
| case TT_RAW_ZOS_SPARE_AREA: |
| offset = hpnfc->main_size + ecc_size * hpnfc->sector_count; |
| sec_cnt = 1; |
| last_sec_size = hpnfc->usnused_spare_size; |
| break; |
| default: |
| dev_err(hpnfc->dev, "Data size preparation failed \n"); |
| return -EINVAL; |
| } |
| |
| reg = 0; |
| WRITE_FIELD(reg, HPNFC_TRAN_CFG_0_OFFSET, offset); |
| WRITE_FIELD(reg, HPNFC_TRAN_CFG_0_SEC_CNT, sec_cnt); |
| IOWR_32(hpnfc->reg + HPNFC_TRAN_CFG_0, reg); |
| |
| reg = 0; |
| WRITE_FIELD(reg, HPNFC_TRAN_CFG_1_LAST_SEC_SIZE, last_sec_size); |
| WRITE_FIELD(reg, HPNFC_TRAN_CFG_1_SECTOR_SIZE, sec_size); |
| IOWR_32(hpnfc->reg + HPNFC_TRAN_CFG_1, reg); |
| |
| return 0; |
| } |
| |
| /* write data to flash memory using CDMA command */ |
| static int cdma_write_data(struct mtd_info *mtd, int page, bool with_ecc) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| dma_addr_t dma_buf = hpnfc->buf.dma_buf; |
| hpnfc_cdma_desc_t *cdma_desc = hpnfc->cdma_desc; |
| uint8_t thread_nr = hpnfc->chip_nr; |
| int status = 0; |
| |
| hpnfc_ecc_check_config(hpnfc, with_ecc & hpnfc->ecc_enabled, page); |
| |
| dma_sync_single_for_device(hpnfc->dev, dma_buf, |
| hpnfc->main_size + mtd->oobsize, DMA_TO_DEVICE); |
| |
| hpnfc_cdma_desc_prepare(cdma_desc, hpnfc->chip_nr, page, (void*)dma_buf, |
| HPNFC_CDMA_CT_WR); |
| |
| status = hpnfc_cdma_send_and_wait(hpnfc, thread_nr); |
| |
| dma_sync_single_for_cpu(hpnfc->dev, dma_buf, |
| hpnfc->main_size + mtd->oobsize, DMA_TO_DEVICE); |
| if (status) |
| return status; |
| |
| status = hpnfc_wait_cdma_finish(hpnfc->cdma_desc); |
| |
| if (status == HPNFC_STAT_ECC_CORR) { |
| dev_err(hpnfc->dev, "CDMA write operation failed\n"); |
| status = -EIO; |
| } |
| |
| return status; |
| } |
| |
| /* get corrected ECC errors of last read operation */ |
| static uint32_t get_ecc_count(hpnfc_state_t *hpnfc) |
| { |
| return READ_FIELD(hpnfc->cdma_desc->status, HPNFC_CDMA_CS_MAXERR); |
| } |
| |
| /* read data from flash memory using CDMA command */ |
| static int cdma_read_data(struct mtd_info *mtd, int page, bool with_ecc, |
| uint32_t *ecc_err_count) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| dma_addr_t dma_buf = hpnfc->buf.dma_buf; |
| hpnfc_cdma_desc_t *cdma_desc = hpnfc->cdma_desc; |
| uint8_t thread_nr = hpnfc->chip_nr; |
| int status; |
| |
| hpnfc_ecc_check_config(hpnfc, with_ecc & hpnfc->ecc_enabled, page); |
| |
| dma_sync_single_for_device(hpnfc->dev, dma_buf, |
| hpnfc->main_size + mtd->oobsize, |
| DMA_FROM_DEVICE); |
| |
| hpnfc_cdma_desc_prepare(cdma_desc, hpnfc->chip_nr, page, (void*)dma_buf, |
| HPNFC_CDMA_CT_RD); |
| |
| status = hpnfc_cdma_send_and_wait(hpnfc, thread_nr); |
| |
| dma_sync_single_for_cpu(hpnfc->dev, dma_buf, |
| hpnfc->main_size + mtd->oobsize, DMA_FROM_DEVICE); |
| |
| status = hpnfc_wait_cdma_finish(hpnfc->cdma_desc); |
| if (status == HPNFC_STAT_ECC_CORR && ecc_err_count) |
| *ecc_err_count = get_ecc_count(hpnfc); |
| |
| return status; |
| } |
| |
| static int write_zos_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| int status = 0; |
| |
| nand_randomize_page(&hpnfc->randomizer, NULL, buf, page); |
| memcpy(hpnfc->buf.buf, buf, mtd->oobsize); |
| status = hpnfc_prepare_data_size(hpnfc, TT_RAW_ZOS_SPARE_AREA); |
| if (status) { |
| dev_err(hpnfc->dev, "write oob failed\n"); |
| return status; |
| } |
| |
| return cdma_write_data(mtd, page, false); |
| } |
| |
| static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| int status = 0; |
| |
| /* to protect spare data by ECC |
| * we send also one ECC sector set to 0xFF */ |
| memset(hpnfc->buf.buf, 0xFF, hpnfc->sector_size); |
| nand_randomize_page(&hpnfc->randomizer, NULL, buf, page); |
| memcpy(hpnfc->buf.buf + hpnfc->sector_size, buf, mtd->oobsize); |
| status = hpnfc_prepare_data_size(hpnfc, TT_SPARE_AREA); |
| if (status) { |
| dev_err(hpnfc->dev, "write oob failed\n"); |
| return status; |
| } |
| |
| return cdma_write_data(mtd, page, true); |
| } |
| |
| static int read_zos_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| int status = 0; |
| |
| status = hpnfc_prepare_data_size(hpnfc, TT_RAW_ZOS_SPARE_AREA); |
| if (status) |
| return -EIO; |
| |
| status = cdma_read_data(mtd, page, 0, NULL); |
| |
| switch (status) { |
| case HPNFC_STAT_ERASED: |
| memset(buf, 0xff, mtd->oobsize); |
| break; |
| case HPNFC_STAT_ECC_UNCORR: |
| case HPNFC_STAT_OK: |
| case HPNFC_STAT_ECC_CORR: |
| memcpy(buf, hpnfc->buf.buf, mtd->oobsize); |
| nand_randomize_page(&hpnfc->randomizer, NULL, buf, page); |
| break; |
| default: |
| dev_err(hpnfc->dev, "read oob failed\n"); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| int status = 0; |
| |
| status = hpnfc_prepare_data_size(hpnfc, TT_SPARE_AREA); |
| if (status) |
| return -EIO; |
| |
| status = cdma_read_data(mtd, page, 1, NULL); |
| |
| switch (status) { |
| case HPNFC_STAT_ERASED: |
| memset(buf, 0xff, mtd->oobsize); |
| break; |
| case HPNFC_STAT_ECC_UNCORR: |
| status = hpnfc_prepare_data_size(hpnfc, TT_RAW_SPARE_AREA); |
| if (status) { |
| return -EIO; |
| } |
| status = cdma_read_data(mtd, page, 0, NULL); |
| if (status) { |
| dev_err(hpnfc->dev, "read oob failed\n"); |
| return -EIO; |
| } |
| memcpy(buf, hpnfc->buf.buf, mtd->oobsize); |
| nand_randomize_page(&hpnfc->randomizer, NULL, buf, page); |
| break; |
| case HPNFC_STAT_OK: |
| case HPNFC_STAT_ECC_CORR: |
| memcpy(buf, hpnfc->buf.buf + hpnfc->sector_size, mtd->oobsize); |
| nand_randomize_page(&hpnfc->randomizer, NULL, buf, page); |
| break; |
| default: |
| dev_err(hpnfc->dev, "read oob failed\n"); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * writes a page. user specifies type, and this function handles the |
| * configuration details. |
| */ |
| static int write_page(struct mtd_info *mtd, struct nand_chip *chip, |
| const uint8_t *buf, bool oob, bool with_ecc, |
| int page) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| int status = 0; |
| |
| memcpy(hpnfc->buf.buf, buf, mtd->writesize); |
| nand_randomize_page(&hpnfc->randomizer, hpnfc->buf.buf, |
| oob? chip->oob_poi: NULL, page); |
| |
| if (oob) |
| /* transfer the data to the spare area */ |
| memcpy(hpnfc->buf.buf + mtd->writesize, chip->oob_poi, mtd->oobsize); |
| else |
| /* just set spare data to 0xFF */ |
| memset(hpnfc->buf.buf + mtd->writesize, 0xFF, mtd->oobsize); |
| |
| if (unlikely(page < hpnfc->zos_page_end)) |
| status = hpnfc_prepare_data_size(hpnfc, TT_MAIN_AREA); |
| else |
| status = hpnfc_prepare_data_size(hpnfc, TT_MAIN_SPARE_AREAS); |
| |
| if (status) { |
| dev_err(hpnfc->dev, "write page failed\n"); |
| return -EIO; |
| } |
| |
| return cdma_write_data(mtd, page, with_ecc); |
| } |
| |
| static int hpnfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, |
| const uint8_t *buf, int oob_required, int page) |
| { |
| /* |
| * for regular page writes, we let HW handle all the ECC |
| * data written to the device. |
| */ |
| return write_page(mtd, chip, buf, oob_required ? true : false, |
| true, page); |
| } |
| |
| static int hpnfc_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, |
| const uint8_t *buf, int oob_required, int page) |
| { |
| /* |
| * for raw page writes, we want to disable ECC and simply write |
| * whatever data is in the buffer. |
| */ |
| return write_page(mtd, chip, buf, oob_required ? true : false, |
| false, page); |
| } |
| |
| static int hpnfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, |
| int page) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| |
| if (unlikely(page < hpnfc->zos_page_end)) |
| return write_zos_oob_data(mtd, chip->oob_poi, page); |
| return write_oob_data(mtd, chip->oob_poi, page); |
| } |
| |
| static int hpnfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, |
| int page) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| |
| if (unlikely(page < hpnfc->zos_page_end)) |
| return read_zos_oob_data(mtd, chip->oob_poi, page); |
| return read_oob_data(mtd, chip->oob_poi, page); |
| } |
| |
| static int hpnfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, |
| uint8_t *buf, int oob_required, int page) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| int status = 0; |
| uint32_t ecc_err_count = 0; |
| |
| if (unlikely(page < hpnfc->zos_page_end)) |
| status = hpnfc_prepare_data_size(hpnfc, TT_MAIN_AREA); |
| else |
| status = hpnfc_prepare_data_size(hpnfc, TT_MAIN_SPARE_AREAS); |
| if (status) |
| return -EIO; |
| |
| status = cdma_read_data(mtd, page, 1, &ecc_err_count); |
| switch (status) { |
| case HPNFC_STAT_ERASED: |
| memset(buf, 0xff, mtd->writesize); |
| if (oob_required) |
| memset(chip->oob_poi, 0xff, mtd->oobsize); |
| break; |
| case HPNFC_STAT_ECC_UNCORR: |
| status = nand_check_erased_ecc_chunk(hpnfc->buf.buf, |
| mtd->writesize + mtd->oobsize, |
| NULL, 0, |
| NULL, 0, |
| chip->ecc.strength); |
| if (status < 0) { |
| dev_err(hpnfc->dev, "read page ecc err:%d\n", status); |
| mtd->ecc_stats.failed++; |
| return status; |
| } else { |
| mtd->ecc_stats.corrected += status; |
| ecc_err_count = status; |
| } |
| |
| memcpy(buf, hpnfc->buf.buf, mtd->writesize); |
| if (oob_required) |
| memcpy(chip->oob_poi, hpnfc->buf.buf + mtd->writesize, |
| mtd->oobsize); |
| break; |
| case HPNFC_STAT_ECC_CORR: |
| if (ecc_err_count) |
| mtd->ecc_stats.corrected += ecc_err_count; |
| case HPNFC_STAT_OK: |
| memcpy(buf, hpnfc->buf.buf, mtd->writesize); |
| if (oob_required) |
| memcpy(chip->oob_poi, hpnfc->buf.buf + mtd->writesize, |
| mtd->oobsize); |
| nand_randomize_page(&hpnfc->randomizer, buf, |
| oob_required ? chip->oob_poi: NULL, page); |
| break; |
| default: |
| dev_err(hpnfc->dev, "read page failed:%d\n", status); |
| return -EIO; |
| } |
| |
| return ecc_err_count; |
| } |
| |
| static int hpnfc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, |
| uint8_t *buf, int oob_required, int page) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| int pages_per_block, status, with_ecc; |
| |
| /* block 0 special handling */ |
| pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift); |
| if (unlikely(page < pages_per_block)) |
| with_ecc = 0; |
| else |
| with_ecc = 1; |
| |
| if (unlikely(page < hpnfc->zos_page_end)) |
| status = hpnfc_prepare_data_size(hpnfc, TT_MAIN_AREA); |
| else |
| status = hpnfc_prepare_data_size(hpnfc, TT_MAIN_SPARE_AREAS); |
| if (status) |
| return -EIO; |
| |
| status = cdma_read_data(mtd, page, with_ecc, NULL); |
| switch (status) { |
| case HPNFC_STAT_ERASED: |
| memset(hpnfc->buf.buf, 0xff, mtd->writesize + mtd->oobsize); |
| if (oob_required) |
| memset(chip->oob_poi, 0xff, mtd->oobsize); |
| break; |
| case HPNFC_STAT_ECC_UNCORR: |
| status = cdma_read_data(mtd, page, 0, NULL); |
| if (status) { |
| dev_err(hpnfc->dev, "read page failed\n"); |
| return -EIO; |
| } |
| |
| nand_check_erased_ecc_chunk(hpnfc->buf.buf, |
| mtd->writesize + mtd->oobsize, |
| NULL, 0, |
| NULL, 0, |
| chip->ecc.strength); |
| |
| memcpy(buf, hpnfc->buf.buf, mtd->writesize); |
| if (oob_required) |
| memcpy(chip->oob_poi, hpnfc->buf.buf + mtd->writesize, |
| mtd->oobsize); |
| break; |
| case HPNFC_STAT_ECC_CORR: |
| case HPNFC_STAT_OK: |
| memcpy(buf, hpnfc->buf.buf, mtd->writesize); |
| if (oob_required) |
| memcpy(chip->oob_poi, hpnfc->buf.buf + mtd->writesize, |
| mtd->oobsize); |
| nand_randomize_page(&hpnfc->randomizer, buf, |
| oob_required ? chip->oob_poi: NULL, page); |
| break; |
| default: |
| dev_err(hpnfc->dev, "read raw page failed\n"); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static uint8_t hpnfc_read_byte(struct mtd_info *mtd) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| uint8_t result = 0xff; |
| |
| if (hpnfc->buf.head < hpnfc->buf.tail) |
| result = hpnfc->buf.buf[hpnfc->buf.head++]; |
| |
| return result; |
| } |
| |
| static void hpnfc_select_chip(struct mtd_info *mtd, int chip) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| |
| hpnfc->chip_nr = chip; |
| } |
| |
| static int hpnfc_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) |
| { |
| return 0; |
| } |
| |
| static int hpnfc_erase(struct mtd_info *mtd, int page) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| int status; |
| uint8_t thread_nr = hpnfc->chip_nr; |
| |
| hpnfc_cdma_desc_prepare(hpnfc->cdma_desc, hpnfc->chip_nr, page, NULL, |
| HPNFC_CDMA_CT_ERASE); |
| /* make sure we are running */ |
| __set_current_state(TASK_RUNNING); |
| status = hpnfc_cdma_send_and_wait(hpnfc, thread_nr); |
| if (status) { |
| dev_err(hpnfc->dev, "erase operation failed\n"); |
| return -EIO; |
| } |
| |
| status = hpnfc_wait_cdma_finish(hpnfc->cdma_desc); |
| if (status) |
| return status; |
| |
| return 0; |
| } |
| |
| static void hpnfc_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, |
| int page) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| uint32_t reg; |
| |
| hpnfc->offset = 0; |
| switch (cmd) { |
| case NAND_CMD_PAGEPROG: |
| break; |
| case NAND_CMD_STATUS: |
| reset_buf(hpnfc); |
| reg = IORD_32(hpnfc->reg + HPNFC_RBN_SETTINGS); |
| if ((reg >> hpnfc->chip_nr) & 0x01) |
| write_byte_to_buf(hpnfc, 0xE0); |
| else |
| write_byte_to_buf(hpnfc, 0x80); |
| break; |
| case NAND_CMD_READID: |
| reset_buf(hpnfc); |
| nf_mem_read_id(hpnfc, col, 8); |
| break; |
| case NAND_CMD_PARAM: |
| reset_buf(hpnfc); |
| read_parameter_page(hpnfc, 4096); |
| break; |
| case NAND_CMD_READ0: |
| case NAND_CMD_SEQIN: |
| break; |
| case NAND_CMD_RESET: |
| /* resets a specific device connected to the core */ |
| break; |
| case NAND_CMD_READOOB: |
| break; |
| case NAND_CMD_RNDOUT: |
| hpnfc->offset = col; |
| break; |
| default: |
| dev_warn(hpnfc->dev, "unsupported command received 0x%x\n", cmd); |
| break; |
| } |
| } |
| |
| static int hpnfc_ooblayout_ecc(struct mtd_info *mtd, int section, |
| struct mtd_oob_region *oobregion) |
| { |
| return -ENOTSUPP; |
| } |
| |
| static int hpnfc_ooblayout_free(struct mtd_info *mtd, int section, |
| struct mtd_oob_region *oobregion) |
| { |
| struct hpnfc_state_t *hpnfc = mtd_to_hpnfc(mtd); |
| |
| if (section) |
| return -ERANGE; |
| |
| oobregion->offset = 2; |
| oobregion->length = hpnfc->usnused_spare_size - 2; |
| |
| return 0; |
| } |
| |
| static const struct mtd_ooblayout_ops hpnfc_ooblayout_ops = { |
| .ecc = hpnfc_ooblayout_ecc, |
| .free = hpnfc_ooblayout_free, |
| }; |
| |
| static int cadence_hpnfc_init(struct hpnfc_state_t *hpnfc) |
| { |
| u32 val; |
| int ret, status; |
| uint32_t ecc_per_sec_size; |
| uint8_t timing_mode; |
| uint8_t work_mode; |
| struct mtd_info *mtd = nand_to_mtd(&hpnfc->nand); |
| |
| hpnfc->buf.buf = devm_kzalloc(hpnfc->dev, 16 * 1024, GFP_DMA | GFP_KERNEL); |
| if (!hpnfc->buf.buf) |
| return -ENOMEM; |
| |
| hpnfc->cdma_desc = dmam_alloc_coherent(hpnfc->dev, |
| sizeof(hpnfc_cdma_desc_t), |
| &hpnfc->dma_cdma_desc, |
| GFP_KERNEL | GFP_DMA); |
| if (!hpnfc->dma_cdma_desc) |
| return -ENOMEM; |
| |
| if (devm_request_irq(hpnfc->dev, hpnfc->irq, hpnfc_isr, IRQF_SHARED, |
| CADENCE_NAND_NAME, hpnfc)) { |
| dev_err(hpnfc->dev, "Unable to allocate IRQ\n"); |
| return -ENODEV; |
| } |
| |
| /* now that our ISR is registered, we can enable interrupts */ |
| mtd->name = CADENCE_NAND_NAME; |
| mtd->priv = &hpnfc->nand; |
| mtd->dev.parent = hpnfc->dev; |
| |
| /* register the driver with the NAND core subsystem */ |
| hpnfc->nand.select_chip = hpnfc_select_chip; |
| hpnfc->nand.cmdfunc = hpnfc_cmdfunc; |
| hpnfc->nand.read_byte = hpnfc_read_byte; |
| hpnfc->nand.waitfunc = hpnfc_waitfunc; |
| hpnfc->nand.read_buf = hpnfc_read_buf; |
| hpnfc->nand.onfi_set_features = nand_onfi_get_set_features_notsupp; |
| hpnfc->nand.onfi_get_features = nand_onfi_get_set_features_notsupp; |
| |
| hpnfc_get_max_banks(hpnfc); |
| hpnfc_get_dma_data_width(hpnfc); |
| hpnfc_read_bch_cfg(hpnfc); |
| |
| spin_lock_init(&hpnfc->irq_lock); |
| init_completion(&hpnfc->complete); |
| |
| ret = hpnfc_hw_init(hpnfc); |
| if (ret) |
| goto failed_req_irq; |
| |
| hpnfc_set_work_mode(hpnfc, HPNFC_WORK_MODE_ASYNC, 0); |
| |
| /* |
| * scan for NAND devices attached to the controller |
| * this is the first stage in a two step process to register |
| * with the nand subsystem |
| */ |
| dev_info(hpnfc->dev, "Start scanning...\n"); |
| if (nand_scan_ident(mtd, hpnfc->max_banks, NULL)) { |
| dev_warn(hpnfc->dev, "nand_scan_ident failed. Try again\n"); |
| /* set async timing to maximum and try once again */ |
| IOWR_32(hpnfc->reg + HPNFC_ASYNC_TOGGLE_TIMINGS, 0x18181818); |
| ret = nand_scan_ident(mtd, hpnfc->max_banks, NULL); |
| if (ret) { |
| dev_warn(hpnfc->dev, "nand_scan_ident failed\n"); |
| goto failed_req_irq; |
| } |
| } |
| dev_info(hpnfc->dev, "Scanning finished.\n"); |
| |
| if (!of_property_read_u32(hpnfc->dev->of_node, "zos-end", &val)) |
| hpnfc->zos_page_end = (int)(val >> hpnfc->nand.page_shift); |
| |
| /* Get info about memory parameters */ |
| if (hpnfc_dev_info(hpnfc)) { |
| dev_err(hpnfc->dev, "HW controller dev info failed\n"); |
| ret = -ENXIO; |
| goto failed_req_irq; |
| } |
| |
| /* allocate the right size buffer now */ |
| devm_kfree(hpnfc->dev, hpnfc->buf.buf); |
| hpnfc->buf.buf = devm_kzalloc(hpnfc->dev, |
| mtd->writesize + mtd->oobsize, |
| GFP_DMA | GFP_KERNEL); |
| if (!hpnfc->buf.buf) { |
| ret = -ENOMEM; |
| goto failed_req_irq; |
| } |
| |
| /* Is 32-bit DMA supported? */ |
| ret = dma_set_mask(hpnfc->dev, DMA_BIT_MASK(32)); |
| if (ret) { |
| dev_err(hpnfc->dev, "no usable DMA configuration\n"); |
| goto failed_req_irq; |
| } |
| |
| hpnfc->buf.dma_buf = |
| dma_map_single(hpnfc->dev, hpnfc->buf.buf, |
| mtd->writesize + mtd->oobsize, |
| DMA_BIDIRECTIONAL); |
| if (dma_mapping_error(hpnfc->dev, hpnfc->buf.dma_buf)) { |
| dev_err(hpnfc->dev, "Failed to map DMA buffer\n"); |
| ret = -EIO; |
| goto failed_req_irq; |
| } |
| |
| /* Bad block management */ |
| hpnfc->nand.bbt_options |= NAND_BBT_USE_FLASH; |
| hpnfc->nand.options |= NAND_NO_SUBPAGE_WRITE; |
| |
| /* Error correction */ |
| hpnfc->nand.ecc.mode = NAND_ECC_HW; |
| status = bch_calculate_ecc_size(hpnfc, &ecc_per_sec_size); |
| if (status) { |
| hpnfc->ecc_enabled = 0; |
| hpnfc->corr_cap = 0; |
| hpnfc->sector_count = 1; |
| hpnfc->sector_size = hpnfc->main_size; |
| hpnfc->nand.ecc.strength = 2; |
| } else { |
| dev_info(hpnfc->dev, |
| "ECC enabled, correction capability: %d, sector size %u\n", |
| hpnfc->corr_cap, hpnfc->sector_size); |
| hpnfc->ecc_enabled = 1; |
| hpnfc->sector_count = hpnfc->main_size / hpnfc->sector_size; |
| } |
| IOWR_32(hpnfc->reg + HPNFC_ECC_CONFIG_1, 0); |
| |
| if ((hpnfc->sector_count * ecc_per_sec_size) >= |
| (hpnfc->spare_size - HPNFC_MINIMUM_SPARE_SIZE)) { |
| /* to small spare area to hanlde such big ECC */ |
| ret = -EIO; |
| goto failed_req_irq; |
| } |
| |
| hpnfc->usnused_spare_size = hpnfc->spare_size |
| - hpnfc->sector_count * ecc_per_sec_size; |
| |
| if (hpnfc->usnused_spare_size > HPNFC_MAX_SPARE_SIZE_PER_SECTOR) |
| hpnfc->usnused_spare_size = HPNFC_MAX_SPARE_SIZE_PER_SECTOR; |
| |
| hpnfc->nand.ecc.bytes = ecc_per_sec_size; |
| mtd_set_ooblayout(mtd, &hpnfc_ooblayout_ops); |
| /* override the default read operations */ |
| hpnfc->nand.ecc.read_page = hpnfc_read_page; |
| hpnfc->nand.ecc.read_page_raw = hpnfc_read_page_raw; |
| hpnfc->nand.ecc.write_page = hpnfc_write_page; |
| hpnfc->nand.ecc.write_page_raw = hpnfc_write_page_raw; |
| hpnfc->nand.ecc.read_oob = hpnfc_read_oob; |
| hpnfc->nand.ecc.write_oob = hpnfc_write_oob; |
| hpnfc->nand.erase = hpnfc_erase; |
| |
| dev_info(hpnfc->dev, "mtd->writesize %u, mtd->oobsize %u\n", |
| mtd->writesize, mtd->oobsize); |
| dev_info(hpnfc->dev, "mtd->erasesize 0x%x, mtd->size 0x%llx\n", |
| mtd->erasesize, mtd->size); |
| |
| ret = nand_randomize_init(&hpnfc->randomizer, mtd->erasesize, |
| mtd->writesize, hpnfc->usnused_spare_size, |
| hpnfc->random_data, RANDOM_DATA_LENGTH, |
| hpnfc->zos_page_end); |
| if (ret) |
| goto failed_req_irq; |
| |
| if (disable_ddr == 0) { |
| hpnfc_check_the_best_mode(hpnfc, &work_mode, &timing_mode); |
| status = hpnfc_set_work_mode(hpnfc, work_mode, timing_mode); |
| if (status) { |
| ret = -EIO; |
| goto failed_req_irq; |
| } |
| } |
| |
| if (nand_scan_tail(mtd)) { |
| ret = -ENXIO; |
| goto failed_req_irq; |
| } |
| |
| /* HW handles all ECC, so hide real spare size from MTD layer */ |
| mtd->oobsize = hpnfc->usnused_spare_size; |
| |
| ret = mtd_device_register(mtd, NULL, 0); |
| if (ret) { |
| dev_err(hpnfc->dev, "Failed to register MTD: %d\n", |
| ret); |
| goto failed_req_irq; |
| } |
| return 0; |
| |
| failed_req_irq: |
| hpnfc_irq_cleanup(hpnfc->irq, hpnfc); |
| |
| return ret; |
| |
| } |
| |
| static void cadence_hpnfc_remove(struct hpnfc_state_t *hpnfc) |
| { |
| struct mtd_info *mtd = nand_to_mtd(&hpnfc->nand); |
| |
| hpnfc_irq_cleanup(hpnfc->irq, hpnfc); |
| dma_unmap_single(hpnfc->dev, hpnfc->buf.dma_buf, |
| mtd->writesize + mtd->oobsize, |
| DMA_BIDIRECTIONAL); |
| } |
| |
| struct cadence_hpnfc_dt { |
| struct hpnfc_state_t hpnfc; |
| struct clk *clk; |
| struct clk *ecc_clk; |
| struct clk *sys_clk; |
| struct reset_control *rst; |
| struct reset_control *reg_rst; |
| }; |
| |
| static const struct of_device_id cadence_hpnfc_dt_ids[] = { |
| { .compatible = "cdns,hpnfc-dt" }, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(of, cadence_hpnfc_dt_ids); |
| |
| static int cadence_hpnfc_dt_probe(struct platform_device *pdev) |
| { |
| struct resource *res; |
| struct cadence_hpnfc_dt *dt; |
| struct hpnfc_state_t *hpnfc; |
| struct device *dev = &pdev->dev; |
| int ret; |
| |
| dt = devm_kzalloc(dev, sizeof(*dt), GFP_KERNEL); |
| if (!dt) |
| return -ENOMEM; |
| hpnfc = &dt->hpnfc; |
| |
| hpnfc->random_data = devm_kzalloc(dev, RANDOM_DATA_LENGTH, GFP_KERNEL); |
| if (!hpnfc->random_data) |
| return -ENOMEM; |
| |
| hpnfc->dev = dev; |
| nand_set_flash_node(&hpnfc->nand, dev->of_node); |
| |
| dt->rst = devm_reset_control_get_optional(dev, "host"); |
| if (IS_ERR(dt->rst)) |
| return PTR_ERR(dt->rst); |
| dt->reg_rst = devm_reset_control_get_optional(dev, "reg"); |
| if (IS_ERR(dt->reg_rst)) |
| return PTR_ERR(dt->reg_rst); |
| |
| reset_control_reset(dt->rst); |
| reset_control_reset(dt->reg_rst); |
| |
| hpnfc->irq = platform_get_irq(pdev, 0); |
| if (hpnfc->irq < 0) { |
| dev_err(dev, "no irq defined\n"); |
| return hpnfc->irq; |
| } |
| dev_info(dev, "IRQ: nr %d\n", hpnfc->irq ); |
| |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| hpnfc->reg = devm_ioremap_resource(dev, res); |
| if (IS_ERR(hpnfc->reg)) { |
| dev_err(dev, "devm_ioremap_resource res 0 failed\n"); |
| return PTR_ERR(hpnfc->reg); |
| } |
| |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
| hpnfc->slave_dma = devm_ioremap_resource(dev, res); |
| if (IS_ERR(hpnfc->slave_dma)) { |
| dev_err(dev, "devm_ioremap_resource res 1 failed\n"); |
| return PTR_ERR(hpnfc->slave_dma); |
| } |
| |
| dt->clk = devm_clk_get(dev, "core"); |
| if (!IS_ERR(dt->clk)) |
| clk_prepare_enable(dt->clk); |
| |
| dt->ecc_clk = devm_clk_get(dev, "ecc"); |
| if (!IS_ERR(dt->ecc_clk)) |
| clk_prepare_enable(dt->ecc_clk); |
| |
| dt->sys_clk = devm_clk_get(dev, "sys"); |
| if (!IS_ERR(dt->sys_clk)) |
| clk_prepare_enable(dt->sys_clk); |
| |
| ret = cadence_hpnfc_init(hpnfc); |
| if (ret) |
| goto fail_clk; |
| |
| platform_set_drvdata(pdev, dt); |
| return 0; |
| |
| fail_clk: |
| clk_disable_unprepare(dt->clk); |
| clk_disable_unprepare(dt->ecc_clk); |
| clk_disable_unprepare(dt->sys_clk); |
| return ret; |
| } |
| |
| static int cadence_hpnfc_dt_remove(struct platform_device *pdev) |
| { |
| struct cadence_hpnfc_dt *dt = platform_get_drvdata(pdev); |
| |
| cadence_hpnfc_remove(&dt->hpnfc); |
| clk_disable_unprepare(dt->clk); |
| clk_disable_unprepare(dt->ecc_clk); |
| clk_disable_unprepare(dt->sys_clk); |
| |
| return 0; |
| } |
| |
| static struct platform_driver cadence_hpnfc_dt_driver = { |
| .probe = cadence_hpnfc_dt_probe, |
| .remove = cadence_hpnfc_dt_remove, |
| .driver = { |
| .name = CADENCE_NAND_NAME, |
| .of_match_table = cadence_hpnfc_dt_ids, |
| }, |
| }; |
| module_platform_driver(cadence_hpnfc_dt_driver); |
| |
| MODULE_LICENSE("GPL"); |
| MODULE_AUTHOR("Cadence"); |
| MODULE_DESCRIPTION("DT driver for Cadence NAND flash controller"); |