Merge remote-tracking branch 'korg_git/nand/next' into mtd/next
This commit is contained in:
Коммит
e8166841a6
|
@ -37,6 +37,4 @@ examples:
|
|||
compatible = "fsl,imx27-nand";
|
||||
reg = <0xd8000000 0x1000>;
|
||||
interrupts = <29>;
|
||||
nand-bus-width = <8>;
|
||||
nand-ecc-mode = "hw";
|
||||
};
|
||||
|
|
|
@ -102,6 +102,31 @@ allOf:
|
|||
- const: rx
|
||||
- const: cmd
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,ipq806x-nand
|
||||
|
||||
then:
|
||||
properties:
|
||||
qcom,boot-partitions:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-matrix
|
||||
items:
|
||||
items:
|
||||
- description: offset
|
||||
- description: size
|
||||
description:
|
||||
Boot partition use a different layout where the 4 bytes of spare
|
||||
data are not protected by ECC. Use this to declare these special
|
||||
partitions by defining first the offset and then the size.
|
||||
|
||||
It's in the form of <offset1 size1 offset2 size2 offset3 ...>
|
||||
and should be declared in ascending order.
|
||||
|
||||
Refer to the ipq8064 example on how to use this special binding.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -135,6 +160,8 @@ examples:
|
|||
nand-ecc-strength = <4>;
|
||||
nand-bus-width = <8>;
|
||||
|
||||
qcom,boot-partitions = <0x0 0x58a0000>;
|
||||
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
#address-cells = <1>;
|
||||
|
|
|
@ -347,17 +347,17 @@ static int anfc_select_target(struct nand_chip *chip, int target)
|
|||
|
||||
/* Update clock frequency */
|
||||
if (nfc->cur_clk != anand->clk) {
|
||||
clk_disable_unprepare(nfc->controller_clk);
|
||||
ret = clk_set_rate(nfc->controller_clk, anand->clk);
|
||||
clk_disable_unprepare(nfc->bus_clk);
|
||||
ret = clk_set_rate(nfc->bus_clk, anand->clk);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev, "Failed to change clock rate\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(nfc->controller_clk);
|
||||
ret = clk_prepare_enable(nfc->bus_clk);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev,
|
||||
"Failed to re-enable the controller clock\n");
|
||||
"Failed to re-enable the bus clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1043,7 +1043,13 @@ static int anfc_setup_interface(struct nand_chip *chip, int target,
|
|||
DQS_BUFF_SEL_OUT(dqs_mode);
|
||||
}
|
||||
|
||||
anand->clk = ANFC_XLNX_SDR_DFLT_CORE_CLK;
|
||||
if (nand_interface_is_sdr(conf)) {
|
||||
anand->clk = ANFC_XLNX_SDR_DFLT_CORE_CLK;
|
||||
} else {
|
||||
/* ONFI timings are defined in picoseconds */
|
||||
anand->clk = div_u64((u64)NSEC_PER_SEC * 1000,
|
||||
conf->timings.nvddr.tCK_min);
|
||||
}
|
||||
|
||||
/*
|
||||
* Due to a hardware bug in the ZynqMP SoC, SDR timing modes 0-1 work
|
||||
|
|
|
@ -679,8 +679,10 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
|||
pci_set_master(pdev);
|
||||
|
||||
cafe = kzalloc(sizeof(*cafe), GFP_KERNEL);
|
||||
if (!cafe)
|
||||
return -ENOMEM;
|
||||
if (!cafe) {
|
||||
err = -ENOMEM;
|
||||
goto out_disable_device;
|
||||
}
|
||||
|
||||
mtd = nand_to_mtd(&cafe->nand);
|
||||
mtd->dev.parent = &pdev->dev;
|
||||
|
@ -801,6 +803,8 @@ static int cafe_nand_probe(struct pci_dev *pdev,
|
|||
pci_iounmap(pdev, cafe->mmio);
|
||||
out_free_mtd:
|
||||
kfree(cafe);
|
||||
out_disable_device:
|
||||
pci_disable_device(pdev);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
@ -822,6 +826,7 @@ static void cafe_nand_remove(struct pci_dev *pdev)
|
|||
pci_iounmap(pdev, cafe->mmio);
|
||||
dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
|
||||
kfree(cafe);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id cafe_nand_tbl[] = {
|
||||
|
|
|
@ -1304,7 +1304,6 @@ static void meson_nfc_nand_chip_cleanup(struct meson_nfc *nfc)
|
|||
mtd = nand_to_mtd(&meson_chip->nand);
|
||||
WARN_ON(mtd_device_unregister(mtd));
|
||||
|
||||
meson_nfc_free_buffer(&meson_chip->nand);
|
||||
nand_cleanup(&meson_chip->nand);
|
||||
list_del(&meson_chip->node);
|
||||
}
|
||||
|
|
|
@ -80,8 +80,10 @@
|
|||
#define DISABLE_STATUS_AFTER_WRITE 4
|
||||
#define CW_PER_PAGE 6
|
||||
#define UD_SIZE_BYTES 9
|
||||
#define UD_SIZE_BYTES_MASK GENMASK(18, 9)
|
||||
#define ECC_PARITY_SIZE_BYTES_RS 19
|
||||
#define SPARE_SIZE_BYTES 23
|
||||
#define SPARE_SIZE_BYTES_MASK GENMASK(26, 23)
|
||||
#define NUM_ADDR_CYCLES 27
|
||||
#define STATUS_BFR_READ 30
|
||||
#define SET_RD_MODE_AFTER_STATUS 31
|
||||
|
@ -102,6 +104,7 @@
|
|||
#define ECC_MODE 4
|
||||
#define ECC_PARITY_SIZE_BYTES_BCH 8
|
||||
#define ECC_NUM_DATA_BYTES 16
|
||||
#define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16)
|
||||
#define ECC_FORCE_CLK_OPEN 30
|
||||
|
||||
/* NAND_DEV_CMD1 bits */
|
||||
|
@ -238,6 +241,9 @@ nandc_set_reg(chip, reg, \
|
|||
* @bam_ce - the array of BAM command elements
|
||||
* @cmd_sgl - sgl for NAND BAM command pipe
|
||||
* @data_sgl - sgl for NAND BAM consumer/producer pipe
|
||||
* @last_data_desc - last DMA desc in data channel (tx/rx).
|
||||
* @last_cmd_desc - last DMA desc in command channel.
|
||||
* @txn_done - completion for NAND transfer.
|
||||
* @bam_ce_pos - the index in bam_ce which is available for next sgl
|
||||
* @bam_ce_start - the index in bam_ce which marks the start position ce
|
||||
* for current sgl. It will be used for size calculation
|
||||
|
@ -250,14 +256,14 @@ nandc_set_reg(chip, reg, \
|
|||
* @rx_sgl_start - start index in data sgl for rx.
|
||||
* @wait_second_completion - wait for second DMA desc completion before making
|
||||
* the NAND transfer completion.
|
||||
* @txn_done - completion for NAND transfer.
|
||||
* @last_data_desc - last DMA desc in data channel (tx/rx).
|
||||
* @last_cmd_desc - last DMA desc in command channel.
|
||||
*/
|
||||
struct bam_transaction {
|
||||
struct bam_cmd_element *bam_ce;
|
||||
struct scatterlist *cmd_sgl;
|
||||
struct scatterlist *data_sgl;
|
||||
struct dma_async_tx_descriptor *last_data_desc;
|
||||
struct dma_async_tx_descriptor *last_cmd_desc;
|
||||
struct completion txn_done;
|
||||
u32 bam_ce_pos;
|
||||
u32 bam_ce_start;
|
||||
u32 cmd_sgl_pos;
|
||||
|
@ -267,25 +273,23 @@ struct bam_transaction {
|
|||
u32 rx_sgl_pos;
|
||||
u32 rx_sgl_start;
|
||||
bool wait_second_completion;
|
||||
struct completion txn_done;
|
||||
struct dma_async_tx_descriptor *last_data_desc;
|
||||
struct dma_async_tx_descriptor *last_cmd_desc;
|
||||
};
|
||||
|
||||
/*
|
||||
* This data type corresponds to the nand dma descriptor
|
||||
* @dma_desc - low level DMA engine descriptor
|
||||
* @list - list for desc_info
|
||||
* @dir - DMA transfer direction
|
||||
*
|
||||
* @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by
|
||||
* ADM
|
||||
* @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM
|
||||
* @sgl_cnt - number of SGL in bam_sgl. Only used by BAM
|
||||
* @dma_desc - low level DMA engine descriptor
|
||||
* @dir - DMA transfer direction
|
||||
*/
|
||||
struct desc_info {
|
||||
struct dma_async_tx_descriptor *dma_desc;
|
||||
struct list_head node;
|
||||
|
||||
enum dma_data_direction dir;
|
||||
union {
|
||||
struct scatterlist adm_sgl;
|
||||
struct {
|
||||
|
@ -293,7 +297,7 @@ struct desc_info {
|
|||
int sgl_cnt;
|
||||
};
|
||||
};
|
||||
struct dma_async_tx_descriptor *dma_desc;
|
||||
enum dma_data_direction dir;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -337,52 +341,64 @@ struct nandc_regs {
|
|||
/*
|
||||
* NAND controller data struct
|
||||
*
|
||||
* @dev: parent device
|
||||
*
|
||||
* @base: MMIO base
|
||||
*
|
||||
* @core_clk: controller clock
|
||||
* @aon_clk: another controller clock
|
||||
*
|
||||
* @regs: a contiguous chunk of memory for DMA register
|
||||
* writes. contains the register values to be
|
||||
* written to controller
|
||||
*
|
||||
* @props: properties of current NAND controller,
|
||||
* initialized via DT match data
|
||||
*
|
||||
* @controller: base controller structure
|
||||
* @host_list: list containing all the chips attached to the
|
||||
* controller
|
||||
* @dev: parent device
|
||||
* @base: MMIO base
|
||||
* @base_phys: physical base address of controller registers
|
||||
* @base_dma: dma base address of controller registers
|
||||
* @core_clk: controller clock
|
||||
* @aon_clk: another controller clock
|
||||
*
|
||||
* @chan: dma channel
|
||||
* @cmd_crci: ADM DMA CRCI for command flow control
|
||||
* @data_crci: ADM DMA CRCI for data flow control
|
||||
*
|
||||
* @desc_list: DMA descriptor list (list of desc_infos)
|
||||
*
|
||||
* @data_buffer: our local DMA buffer for page read/writes,
|
||||
* used when we can't use the buffer provided
|
||||
* by upper layers directly
|
||||
* @reg_read_buf: local buffer for reading back registers via DMA
|
||||
*
|
||||
* @base_phys: physical base address of controller registers
|
||||
* @base_dma: dma base address of controller registers
|
||||
* @reg_read_dma: contains dma address for register read buffer
|
||||
*
|
||||
* @buf_size/count/start: markers for chip->legacy.read_buf/write_buf
|
||||
* functions
|
||||
* @reg_read_buf: local buffer for reading back registers via DMA
|
||||
* @reg_read_dma: contains dma address for register read buffer
|
||||
* @reg_read_pos: marker for data read in reg_read_buf
|
||||
*
|
||||
* @regs: a contiguous chunk of memory for DMA register
|
||||
* writes. contains the register values to be
|
||||
* written to controller
|
||||
* @cmd1/vld: some fixed controller register values
|
||||
* @props: properties of current NAND controller,
|
||||
* initialized via DT match data
|
||||
* @max_cwperpage: maximum QPIC codewords required. calculated
|
||||
* from all connected NAND devices pagesize
|
||||
*
|
||||
* @reg_read_pos: marker for data read in reg_read_buf
|
||||
*
|
||||
* @cmd1/vld: some fixed controller register values
|
||||
*/
|
||||
struct qcom_nand_controller {
|
||||
struct nand_controller controller;
|
||||
struct list_head host_list;
|
||||
|
||||
struct device *dev;
|
||||
|
||||
void __iomem *base;
|
||||
phys_addr_t base_phys;
|
||||
dma_addr_t base_dma;
|
||||
|
||||
struct clk *core_clk;
|
||||
struct clk *aon_clk;
|
||||
|
||||
struct nandc_regs *regs;
|
||||
struct bam_transaction *bam_txn;
|
||||
|
||||
const struct qcom_nandc_props *props;
|
||||
|
||||
struct nand_controller controller;
|
||||
struct list_head host_list;
|
||||
|
||||
union {
|
||||
/* will be used only by QPIC for BAM DMA */
|
||||
struct {
|
||||
|
@ -400,64 +416,89 @@ struct qcom_nand_controller {
|
|||
};
|
||||
|
||||
struct list_head desc_list;
|
||||
struct bam_transaction *bam_txn;
|
||||
|
||||
u8 *data_buffer;
|
||||
__le32 *reg_read_buf;
|
||||
|
||||
phys_addr_t base_phys;
|
||||
dma_addr_t base_dma;
|
||||
dma_addr_t reg_read_dma;
|
||||
|
||||
int buf_size;
|
||||
int buf_count;
|
||||
int buf_start;
|
||||
unsigned int max_cwperpage;
|
||||
|
||||
__le32 *reg_read_buf;
|
||||
dma_addr_t reg_read_dma;
|
||||
int reg_read_pos;
|
||||
|
||||
struct nandc_regs *regs;
|
||||
|
||||
u32 cmd1, vld;
|
||||
const struct qcom_nandc_props *props;
|
||||
};
|
||||
|
||||
/*
|
||||
* NAND special boot partitions
|
||||
*
|
||||
* @page_offset: offset of the partition where spare data is not protected
|
||||
* by ECC (value in pages)
|
||||
* @page_offset: size of the partition where spare data is not protected
|
||||
* by ECC (value in pages)
|
||||
*/
|
||||
struct qcom_nand_boot_partition {
|
||||
u32 page_offset;
|
||||
u32 page_size;
|
||||
};
|
||||
|
||||
/*
|
||||
* NAND chip structure
|
||||
*
|
||||
* @boot_partitions: array of boot partitions where offset and size of the
|
||||
* boot partitions are stored
|
||||
*
|
||||
* @chip: base NAND chip structure
|
||||
* @node: list node to add itself to host_list in
|
||||
* qcom_nand_controller
|
||||
*
|
||||
* @nr_boot_partitions: count of the boot partitions where spare data is not
|
||||
* protected by ECC
|
||||
*
|
||||
* @cs: chip select value for this chip
|
||||
* @cw_size: the number of bytes in a single step/codeword
|
||||
* of a page, consisting of all data, ecc, spare
|
||||
* and reserved bytes
|
||||
* @cw_data: the number of bytes within a codeword protected
|
||||
* by ECC
|
||||
* @use_ecc: request the controller to use ECC for the
|
||||
* upcoming read/write
|
||||
* @bch_enabled: flag to tell whether BCH ECC mode is used
|
||||
* @ecc_bytes_hw: ECC bytes used by controller hardware for this
|
||||
* chip
|
||||
* @status: value to be returned if NAND_CMD_STATUS command
|
||||
* is executed
|
||||
*
|
||||
* @last_command: keeps track of last command on this chip. used
|
||||
* for reading correct status
|
||||
*
|
||||
* @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for
|
||||
* ecc/non-ecc mode for the current nand flash
|
||||
* device
|
||||
*
|
||||
* @status: value to be returned if NAND_CMD_STATUS command
|
||||
* is executed
|
||||
* @codeword_fixup: keep track of the current layout used by
|
||||
* the driver for read/write operation.
|
||||
* @use_ecc: request the controller to use ECC for the
|
||||
* upcoming read/write
|
||||
* @bch_enabled: flag to tell whether BCH ECC mode is used
|
||||
*/
|
||||
struct qcom_nand_host {
|
||||
struct qcom_nand_boot_partition *boot_partitions;
|
||||
|
||||
struct nand_chip chip;
|
||||
struct list_head node;
|
||||
|
||||
int nr_boot_partitions;
|
||||
|
||||
int cs;
|
||||
int cw_size;
|
||||
int cw_data;
|
||||
bool use_ecc;
|
||||
bool bch_enabled;
|
||||
int ecc_bytes_hw;
|
||||
int spare_bytes;
|
||||
int bbm_size;
|
||||
u8 status;
|
||||
|
||||
int last_command;
|
||||
|
||||
u32 cfg0, cfg1;
|
||||
|
@ -466,23 +507,30 @@ struct qcom_nand_host {
|
|||
u32 ecc_bch_cfg;
|
||||
u32 clrflashstatus;
|
||||
u32 clrreadstatus;
|
||||
|
||||
u8 status;
|
||||
bool codeword_fixup;
|
||||
bool use_ecc;
|
||||
bool bch_enabled;
|
||||
};
|
||||
|
||||
/*
|
||||
* This data type corresponds to the NAND controller properties which varies
|
||||
* among different NAND controllers.
|
||||
* @ecc_modes - ecc mode for NAND
|
||||
* @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
|
||||
* @is_bam - whether NAND controller is using BAM
|
||||
* @is_qpic - whether NAND CTRL is part of qpic IP
|
||||
* @qpic_v2 - flag to indicate QPIC IP version 2
|
||||
* @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset
|
||||
* @use_codeword_fixup - whether NAND has different layout for boot partitions
|
||||
*/
|
||||
struct qcom_nandc_props {
|
||||
u32 ecc_modes;
|
||||
u32 dev_cmd_reg_start;
|
||||
bool is_bam;
|
||||
bool is_qpic;
|
||||
bool qpic_v2;
|
||||
u32 dev_cmd_reg_start;
|
||||
bool use_codeword_fixup;
|
||||
};
|
||||
|
||||
/* Frees the BAM transaction memory */
|
||||
|
@ -1701,7 +1749,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
|
||||
oob_size1 = host->bbm_size;
|
||||
|
||||
if (qcom_nandc_is_last_cw(ecc, cw)) {
|
||||
if (qcom_nandc_is_last_cw(ecc, cw) && !host->codeword_fixup) {
|
||||
data_size2 = ecc->size - data_size1 -
|
||||
((ecc->steps - 1) * 4);
|
||||
oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw +
|
||||
|
@ -1782,7 +1830,7 @@ check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf,
|
|||
}
|
||||
|
||||
for_each_set_bit(cw, &uncorrectable_cws, ecc->steps) {
|
||||
if (qcom_nandc_is_last_cw(ecc, cw)) {
|
||||
if (qcom_nandc_is_last_cw(ecc, cw) && !host->codeword_fixup) {
|
||||
data_size = ecc->size - ((ecc->steps - 1) * 4);
|
||||
oob_size = (ecc->steps * 4) + host->ecc_bytes_hw;
|
||||
} else {
|
||||
|
@ -1940,7 +1988,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
|
|||
for (i = 0; i < ecc->steps; i++) {
|
||||
int data_size, oob_size;
|
||||
|
||||
if (qcom_nandc_is_last_cw(ecc, i)) {
|
||||
if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) {
|
||||
data_size = ecc->size - ((ecc->steps - 1) << 2);
|
||||
oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
|
||||
host->spare_bytes;
|
||||
|
@ -2037,6 +2085,69 @@ static int copy_last_cw(struct qcom_nand_host *host, int page)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool qcom_nandc_is_boot_partition(struct qcom_nand_host *host, int page)
|
||||
{
|
||||
struct qcom_nand_boot_partition *boot_partition;
|
||||
u32 start, end;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Since the frequent access will be to the non-boot partitions like rootfs,
|
||||
* optimize the page check by:
|
||||
*
|
||||
* 1. Checking if the page lies after the last boot partition.
|
||||
* 2. Checking from the boot partition end.
|
||||
*/
|
||||
|
||||
/* First check the last boot partition */
|
||||
boot_partition = &host->boot_partitions[host->nr_boot_partitions - 1];
|
||||
start = boot_partition->page_offset;
|
||||
end = start + boot_partition->page_size;
|
||||
|
||||
/* Page is after the last boot partition end. This is NOT a boot partition */
|
||||
if (page > end)
|
||||
return false;
|
||||
|
||||
/* Actually check if it's a boot partition */
|
||||
if (page < end && page >= start)
|
||||
return true;
|
||||
|
||||
/* Check the other boot partitions starting from the second-last partition */
|
||||
for (i = host->nr_boot_partitions - 2; i >= 0; i--) {
|
||||
boot_partition = &host->boot_partitions[i];
|
||||
start = boot_partition->page_offset;
|
||||
end = start + boot_partition->page_size;
|
||||
|
||||
if (page < end && page >= start)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void qcom_nandc_codeword_fixup(struct qcom_nand_host *host, int page)
|
||||
{
|
||||
bool codeword_fixup = qcom_nandc_is_boot_partition(host, page);
|
||||
|
||||
/* Skip conf write if we are already in the correct mode */
|
||||
if (codeword_fixup == host->codeword_fixup)
|
||||
return;
|
||||
|
||||
host->codeword_fixup = codeword_fixup;
|
||||
|
||||
host->cw_data = codeword_fixup ? 512 : 516;
|
||||
host->spare_bytes = host->cw_size - host->ecc_bytes_hw -
|
||||
host->bbm_size - host->cw_data;
|
||||
|
||||
host->cfg0 &= ~(SPARE_SIZE_BYTES_MASK | UD_SIZE_BYTES_MASK);
|
||||
host->cfg0 |= host->spare_bytes << SPARE_SIZE_BYTES |
|
||||
host->cw_data << UD_SIZE_BYTES;
|
||||
|
||||
host->ecc_bch_cfg &= ~ECC_NUM_DATA_BYTES_MASK;
|
||||
host->ecc_bch_cfg |= host->cw_data << ECC_NUM_DATA_BYTES;
|
||||
host->ecc_buf_cfg = (host->cw_data - 1) << NUM_STEPS;
|
||||
}
|
||||
|
||||
/* implements ecc->read_page() */
|
||||
static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf,
|
||||
int oob_required, int page)
|
||||
|
@ -2045,6 +2156,9 @@ static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf,
|
|||
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
|
||||
u8 *data_buf, *oob_buf = NULL;
|
||||
|
||||
if (host->nr_boot_partitions)
|
||||
qcom_nandc_codeword_fixup(host, page);
|
||||
|
||||
nand_read_page_op(chip, page, 0, NULL, 0);
|
||||
data_buf = buf;
|
||||
oob_buf = oob_required ? chip->oob_poi : NULL;
|
||||
|
@ -2064,6 +2178,9 @@ static int qcom_nandc_read_page_raw(struct nand_chip *chip, uint8_t *buf,
|
|||
int cw, ret;
|
||||
u8 *data_buf = buf, *oob_buf = chip->oob_poi;
|
||||
|
||||
if (host->nr_boot_partitions)
|
||||
qcom_nandc_codeword_fixup(host, page);
|
||||
|
||||
for (cw = 0; cw < ecc->steps; cw++) {
|
||||
ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, oob_buf,
|
||||
page, cw);
|
||||
|
@ -2084,6 +2201,9 @@ static int qcom_nandc_read_oob(struct nand_chip *chip, int page)
|
|||
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
|
||||
struct nand_ecc_ctrl *ecc = &chip->ecc;
|
||||
|
||||
if (host->nr_boot_partitions)
|
||||
qcom_nandc_codeword_fixup(host, page);
|
||||
|
||||
clear_read_regs(nandc);
|
||||
clear_bam_transaction(nandc);
|
||||
|
||||
|
@ -2104,6 +2224,9 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf,
|
|||
u8 *data_buf, *oob_buf;
|
||||
int i, ret;
|
||||
|
||||
if (host->nr_boot_partitions)
|
||||
qcom_nandc_codeword_fixup(host, page);
|
||||
|
||||
nand_prog_page_begin_op(chip, page, 0, NULL, 0);
|
||||
|
||||
clear_read_regs(nandc);
|
||||
|
@ -2119,7 +2242,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf,
|
|||
for (i = 0; i < ecc->steps; i++) {
|
||||
int data_size, oob_size;
|
||||
|
||||
if (qcom_nandc_is_last_cw(ecc, i)) {
|
||||
if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) {
|
||||
data_size = ecc->size - ((ecc->steps - 1) << 2);
|
||||
oob_size = (ecc->steps << 2) + host->ecc_bytes_hw +
|
||||
host->spare_bytes;
|
||||
|
@ -2176,6 +2299,9 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip,
|
|||
u8 *data_buf, *oob_buf;
|
||||
int i, ret;
|
||||
|
||||
if (host->nr_boot_partitions)
|
||||
qcom_nandc_codeword_fixup(host, page);
|
||||
|
||||
nand_prog_page_begin_op(chip, page, 0, NULL, 0);
|
||||
clear_read_regs(nandc);
|
||||
clear_bam_transaction(nandc);
|
||||
|
@ -2194,7 +2320,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip,
|
|||
data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
|
||||
oob_size1 = host->bbm_size;
|
||||
|
||||
if (qcom_nandc_is_last_cw(ecc, i)) {
|
||||
if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) {
|
||||
data_size2 = ecc->size - data_size1 -
|
||||
((ecc->steps - 1) << 2);
|
||||
oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw +
|
||||
|
@ -2254,6 +2380,9 @@ static int qcom_nandc_write_oob(struct nand_chip *chip, int page)
|
|||
int data_size, oob_size;
|
||||
int ret;
|
||||
|
||||
if (host->nr_boot_partitions)
|
||||
qcom_nandc_codeword_fixup(host, page);
|
||||
|
||||
host->use_ecc = true;
|
||||
clear_bam_transaction(nandc);
|
||||
|
||||
|
@ -2915,6 +3044,74 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc)
|
|||
|
||||
static const char * const probes[] = { "cmdlinepart", "ofpart", "qcomsmem", NULL };
|
||||
|
||||
static int qcom_nand_host_parse_boot_partitions(struct qcom_nand_controller *nandc,
|
||||
struct qcom_nand_host *host,
|
||||
struct device_node *dn)
|
||||
{
|
||||
struct nand_chip *chip = &host->chip;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
struct qcom_nand_boot_partition *boot_partition;
|
||||
struct device *dev = nandc->dev;
|
||||
int partitions_count, i, j, ret;
|
||||
|
||||
if (!of_find_property(dn, "qcom,boot-partitions", NULL))
|
||||
return 0;
|
||||
|
||||
partitions_count = of_property_count_u32_elems(dn, "qcom,boot-partitions");
|
||||
if (partitions_count <= 0) {
|
||||
dev_err(dev, "Error parsing boot partition\n");
|
||||
return partitions_count ? partitions_count : -EINVAL;
|
||||
}
|
||||
|
||||
host->nr_boot_partitions = partitions_count / 2;
|
||||
host->boot_partitions = devm_kcalloc(dev, host->nr_boot_partitions,
|
||||
sizeof(*host->boot_partitions), GFP_KERNEL);
|
||||
if (!host->boot_partitions) {
|
||||
host->nr_boot_partitions = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0, j = 0; i < host->nr_boot_partitions; i++, j += 2) {
|
||||
boot_partition = &host->boot_partitions[i];
|
||||
|
||||
ret = of_property_read_u32_index(dn, "qcom,boot-partitions", j,
|
||||
&boot_partition->page_offset);
|
||||
if (ret) {
|
||||
dev_err(dev, "Error parsing boot partition offset at index %d\n", i);
|
||||
host->nr_boot_partitions = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (boot_partition->page_offset % mtd->writesize) {
|
||||
dev_err(dev, "Boot partition offset not multiple of writesize at index %i\n",
|
||||
i);
|
||||
host->nr_boot_partitions = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Convert offset to nand pages */
|
||||
boot_partition->page_offset /= mtd->writesize;
|
||||
|
||||
ret = of_property_read_u32_index(dn, "qcom,boot-partitions", j + 1,
|
||||
&boot_partition->page_size);
|
||||
if (ret) {
|
||||
dev_err(dev, "Error parsing boot partition size at index %d\n", i);
|
||||
host->nr_boot_partitions = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (boot_partition->page_size % mtd->writesize) {
|
||||
dev_err(dev, "Boot partition size not multiple of writesize at index %i\n",
|
||||
i);
|
||||
host->nr_boot_partitions = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Convert size to nand pages */
|
||||
boot_partition->page_size /= mtd->writesize;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
|
||||
struct qcom_nand_host *host,
|
||||
struct device_node *dn)
|
||||
|
@ -2972,6 +3169,14 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
|
|||
if (ret)
|
||||
nand_cleanup(chip);
|
||||
|
||||
if (nandc->props->use_codeword_fixup) {
|
||||
ret = qcom_nand_host_parse_boot_partitions(nandc, host, dn);
|
||||
if (ret) {
|
||||
nand_cleanup(chip);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -3137,6 +3342,7 @@ static int qcom_nandc_remove(struct platform_device *pdev)
|
|||
static const struct qcom_nandc_props ipq806x_nandc_props = {
|
||||
.ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT),
|
||||
.is_bam = false,
|
||||
.use_codeword_fixup = true,
|
||||
.dev_cmd_reg_start = 0x0,
|
||||
};
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ static const struct mtd_ooblayout_ops oob_sm_ops = {
|
|||
.free = oob_sm_ooblayout_free,
|
||||
};
|
||||
|
||||
/* NOTE: This layout is is not compatabable with SmartMedia, */
|
||||
/* NOTE: This layout is not compatabable with SmartMedia, */
|
||||
/* because the 256 byte devices have page depenent oob layout */
|
||||
/* However it does preserve the bad block markers */
|
||||
/* If you use smftl, it will bypass this and work correctly */
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
|
||||
spinand-objs := core.o ato.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
|
||||
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2022 Aidan MacDonald
|
||||
*
|
||||
* Author: Aidan MacDonald <aidanmacdonald.0x0@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mtd/spinand.h>
|
||||
|
||||
|
||||
#define SPINAND_MFR_ATO 0x9b
|
||||
|
||||
|
||||
static SPINAND_OP_VARIANTS(read_cache_variants,
|
||||
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
|
||||
|
||||
static SPINAND_OP_VARIANTS(write_cache_variants,
|
||||
SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
|
||||
SPINAND_PROG_LOAD(true, 0, NULL, 0));
|
||||
|
||||
static SPINAND_OP_VARIANTS(update_cache_variants,
|
||||
SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
|
||||
SPINAND_PROG_LOAD(false, 0, NULL, 0));
|
||||
|
||||
|
||||
static int ato25d1ga_ooblayout_ecc(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section > 3)
|
||||
return -ERANGE;
|
||||
|
||||
region->offset = (16 * section) + 8;
|
||||
region->length = 8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ato25d1ga_ooblayout_free(struct mtd_info *mtd, int section,
|
||||
struct mtd_oob_region *region)
|
||||
{
|
||||
if (section > 3)
|
||||
return -ERANGE;
|
||||
|
||||
if (section) {
|
||||
region->offset = (16 * section);
|
||||
region->length = 8;
|
||||
} else {
|
||||
/* first byte of section 0 is reserved for the BBM */
|
||||
region->offset = 1;
|
||||
region->length = 7;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mtd_ooblayout_ops ato25d1ga_ooblayout = {
|
||||
.ecc = ato25d1ga_ooblayout_ecc,
|
||||
.free = ato25d1ga_ooblayout_free,
|
||||
};
|
||||
|
||||
|
||||
static const struct spinand_info ato_spinand_table[] = {
|
||||
SPINAND_INFO("ATO25D1GA",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x12),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(1, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
SPINAND_HAS_QE_BIT,
|
||||
SPINAND_ECCINFO(&ato25d1ga_ooblayout, NULL)),
|
||||
};
|
||||
|
||||
static const struct spinand_manufacturer_ops ato_spinand_manuf_ops = {
|
||||
};
|
||||
|
||||
const struct spinand_manufacturer ato_spinand_manufacturer = {
|
||||
.id = SPINAND_MFR_ATO,
|
||||
.name = "ATO",
|
||||
.chips = ato_spinand_table,
|
||||
.nchips = ARRAY_SIZE(ato_spinand_table),
|
||||
.ops = &ato_spinand_manuf_ops,
|
||||
};
|
|
@ -927,6 +927,7 @@ static const struct nand_ops spinand_ops = {
|
|||
};
|
||||
|
||||
static const struct spinand_manufacturer *spinand_manufacturers[] = {
|
||||
&ato_spinand_manufacturer,
|
||||
&gigadevice_spinand_manufacturer,
|
||||
¯onix_spinand_manufacturer,
|
||||
µn_spinand_manufacturer,
|
||||
|
|
|
@ -260,6 +260,7 @@ struct spinand_manufacturer {
|
|||
};
|
||||
|
||||
/* SPI NAND manufacturers */
|
||||
extern const struct spinand_manufacturer ato_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer macronix_spinand_manufacturer;
|
||||
extern const struct spinand_manufacturer micron_spinand_manufacturer;
|
||||
|
|
Загрузка…
Ссылка в новой задаче