summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand/raw/sunxi_nand.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/raw/sunxi_nand.c')
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand.c120
1 files changed, 41 insertions, 79 deletions
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index ea953e31933e..13e3e0198d15 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -172,10 +172,10 @@ struct sunxi_nand_chip_sel {
/**
* struct sunxi_nand_hw_ecc - stores information related to HW ECC support
*
- * @mode: the sunxi ECC mode field deduced from ECC requirements
+ * @ecc_ctl: ECC_CTL register value for this NAND chip
*/
struct sunxi_nand_hw_ecc {
- int mode;
+ u32 ecc_ctl;
};
/**
@@ -193,7 +193,7 @@ struct sunxi_nand_hw_ecc {
struct sunxi_nand_chip {
struct list_head node;
struct nand_chip nand;
- struct sunxi_nand_hw_ecc *ecc;
+ struct sunxi_nand_hw_ecc ecc;
unsigned long clk_rate;
u32 timing_cfg;
u32 timing_ctl;
@@ -421,7 +421,7 @@ static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs)
struct sunxi_nand_chip_sel *sel;
u32 ctl;
- if (cs > 0 && cs >= sunxi_nand->nsels)
+ if (cs >= sunxi_nand->nsels)
return;
ctl = readl(nfc->regs + NFC_REG_CTL) &
@@ -689,26 +689,15 @@ static void sunxi_nfc_hw_ecc_enable(struct nand_chip *nand)
{
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
- u32 ecc_ctl;
-
- ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
- ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE |
- NFC_ECC_BLOCK_SIZE_MSK);
- ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(sunxi_nand->ecc->mode) |
- NFC_ECC_EXCEPTION | NFC_ECC_PIPELINE;
-
- if (nand->ecc.size == 512)
- ecc_ctl |= NFC_ECC_BLOCK_512;
- writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL);
+ writel(sunxi_nand->ecc.ecc_ctl, nfc->regs + NFC_REG_ECC_CTL);
}
static void sunxi_nfc_hw_ecc_disable(struct nand_chip *nand)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
- writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN,
- nfc->regs + NFC_REG_ECC_CTL);
+ writel(0, nfc->regs + NFC_REG_ECC_CTL);
}
static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf)
@@ -1604,12 +1593,19 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section,
return 0;
}
+ /*
+ * The controller does not provide access to OOB bytes
+ * past the end of the ECC data.
+ */
+ if (section == ecc->steps && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
+ return -ERANGE;
+
oobregion->offset = section * (ecc->bytes + 4);
if (section < ecc->steps)
oobregion->length = 4;
else
- oobregion->offset = mtd->oobsize - oobregion->offset;
+ oobregion->length = mtd->oobsize - oobregion->offset;
return 0;
}
@@ -1619,11 +1615,6 @@ static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = {
.free = sunxi_nand_ooblayout_free,
};
-static void sunxi_nand_hw_ecc_ctrl_cleanup(struct sunxi_nand_chip *sunxi_nand)
-{
- kfree(sunxi_nand->ecc);
-}
-
static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
struct nand_ecc_ctrl *ecc,
struct device_node *np)
@@ -1634,7 +1625,6 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
struct mtd_info *mtd = nand_to_mtd(nand);
struct nand_device *nanddev = mtd_to_nanddev(mtd);
int nsectors;
- int ret;
int i;
if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) {
@@ -1669,10 +1659,6 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
if (ecc->size != 512 && ecc->size != 1024)
return -EINVAL;
- sunxi_nand->ecc = kzalloc(sizeof(*sunxi_nand->ecc), GFP_KERNEL);
- if (!sunxi_nand->ecc)
- return -ENOMEM;
-
/* Prefer 1k ECC chunk over 512 ones */
if (ecc->size == 512 && mtd->writesize > 512) {
ecc->size = 1024;
@@ -1693,12 +1679,9 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
if (i >= ARRAY_SIZE(strengths)) {
dev_err(nfc->dev, "unsupported strength\n");
- ret = -ENOTSUPP;
- goto err;
+ return -ENOTSUPP;
}
- sunxi_nand->ecc->mode = i;
-
/* HW ECC always request ECC bytes for 1024 bytes blocks */
ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8);
@@ -1707,10 +1690,8 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
nsectors = mtd->writesize / ecc->size;
- if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) {
- ret = -EINVAL;
- goto err;
- }
+ if (mtd->oobsize < ((ecc->bytes + 4) * nsectors))
+ return -EINVAL;
ecc->read_oob = sunxi_nfc_hw_ecc_read_oob;
ecc->write_oob = sunxi_nfc_hw_ecc_write_oob;
@@ -1732,26 +1713,13 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
ecc->read_oob_raw = nand_read_oob_std;
ecc->write_oob_raw = nand_write_oob_std;
- return 0;
-
-err:
- kfree(sunxi_nand->ecc);
+ sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(i) | NFC_ECC_EXCEPTION |
+ NFC_ECC_PIPELINE | NFC_ECC_EN;
- return ret;
-}
+ if (ecc->size == 512)
+ sunxi_nand->ecc.ecc_ctl |= NFC_ECC_BLOCK_512;
-static void sunxi_nand_ecc_cleanup(struct sunxi_nand_chip *sunxi_nand)
-{
- struct nand_ecc_ctrl *ecc = &sunxi_nand->nand.ecc;
-
- switch (ecc->engine_type) {
- case NAND_ECC_ENGINE_TYPE_ON_HOST:
- sunxi_nand_hw_ecc_ctrl_cleanup(sunxi_nand);
- break;
- case NAND_ECC_ENGINE_TYPE_NONE:
- default:
- break;
- }
+ return 0;
}
static int sunxi_nand_attach_chip(struct nand_chip *nand)
@@ -1950,6 +1918,24 @@ static const struct nand_controller_ops sunxi_nand_controller_ops = {
.exec_op = sunxi_nfc_exec_op,
};
+static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
+{
+ struct sunxi_nand_chip *sunxi_nand;
+ struct nand_chip *chip;
+ int ret;
+
+ while (!list_empty(&nfc->chips)) {
+ sunxi_nand = list_first_entry(&nfc->chips,
+ struct sunxi_nand_chip,
+ node);
+ chip = &sunxi_nand->nand;
+ ret = mtd_device_unregister(nand_to_mtd(chip));
+ WARN_ON(ret);
+ nand_cleanup(chip);
+ list_del(&sunxi_nand->node);
+ }
+}
+
static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
struct device_node *np)
{
@@ -2041,18 +2027,13 @@ static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
{
struct device_node *np = dev->of_node;
struct device_node *nand_np;
- int nchips = of_get_child_count(np);
int ret;
- if (nchips > 8) {
- dev_err(dev, "too many NAND chips: %d (max = 8)\n", nchips);
- return -EINVAL;
- }
-
for_each_child_of_node(np, nand_np) {
ret = sunxi_nand_chip_init(dev, nfc, nand_np);
if (ret) {
of_node_put(nand_np);
+ sunxi_nand_chips_cleanup(nfc);
return ret;
}
}
@@ -2060,25 +2041,6 @@ static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
return 0;
}
-static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
-{
- struct sunxi_nand_chip *sunxi_nand;
- struct nand_chip *chip;
- int ret;
-
- while (!list_empty(&nfc->chips)) {
- sunxi_nand = list_first_entry(&nfc->chips,
- struct sunxi_nand_chip,
- node);
- chip = &sunxi_nand->nand;
- ret = mtd_device_unregister(nand_to_mtd(chip));
- WARN_ON(ret);
- nand_cleanup(chip);
- sunxi_nand_ecc_cleanup(sunxi_nand);
- list_del(&sunxi_nand->node);
- }
-}
-
static int sunxi_nfc_dma_init(struct sunxi_nfc *nfc, struct resource *r)
{
int ret;