aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/brcmnand/brcmnand.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/brcmnand/brcmnand.c')
-rw-r--r--drivers/mtd/nand/brcmnand/brcmnand.c166
1 files changed, 98 insertions, 68 deletions
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c
index 12c6190c6e338b..844fc07d22cd4d 100644
--- a/drivers/mtd/nand/brcmnand/brcmnand.c
+++ b/drivers/mtd/nand/brcmnand/brcmnand.c
@@ -11,6 +11,7 @@
* GNU General Public License for more details.
*/
+#include <linux/clk.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -122,6 +123,9 @@ struct brcmnand_controller {
/* Some SoCs provide custom interrupt status register(s) */
struct brcmnand_soc *soc;
+ /* Some SoCs have a gateable clock for the controller */
+ struct clk *clk;
+
int cmd_pending;
bool dma_pending;
struct completion done;
@@ -134,7 +138,7 @@ struct brcmnand_controller {
dma_addr_t dma_pa;
/* in-memory cache of the FLASH_CACHE, used only for some commands */
- u32 flash_cache[FC_WORDS];
+ u8 flash_cache[FC_BYTES];
/* Controller revision details */
const u16 *reg_offsets;
@@ -176,10 +180,8 @@ struct brcmnand_cfg {
struct brcmnand_host {
struct list_head node;
- struct device_node *of_node;
struct nand_chip chip;
- struct mtd_info mtd;
struct platform_device *pdev;
int cs;
@@ -874,8 +876,8 @@ static struct nand_ecclayout *brcmstb_choose_ecc_layout(
static void brcmnand_wp(struct mtd_info *mtd, int wp)
{
- struct nand_chip *chip = mtd->priv;
- struct brcmnand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_controller *ctrl = host->ctrl;
if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) {
@@ -1040,8 +1042,8 @@ static void brcmnand_cmd_ctrl(struct mtd_info *mtd, int dat,
static int brcmnand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
{
- struct nand_chip *chip = mtd->priv;
- struct brcmnand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_controller *ctrl = host->ctrl;
unsigned long timeo = msecs_to_jiffies(100);
@@ -1075,7 +1077,7 @@ static int brcmnand_low_level_op(struct brcmnand_host *host,
enum brcmnand_llop_type type, u32 data,
bool last_op)
{
- struct mtd_info *mtd = &host->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
struct nand_chip *chip = &host->chip;
struct brcmnand_controller *ctrl = host->ctrl;
u32 tmp;
@@ -1114,8 +1116,8 @@ static int brcmnand_low_level_op(struct brcmnand_host *host,
static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
int column, int page_addr)
{
- struct nand_chip *chip = mtd->priv;
- struct brcmnand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_controller *ctrl = host->ctrl;
u64 addr = (u64)page_addr << chip->page_shift;
int native_cmd = 0;
@@ -1188,6 +1190,8 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
if (native_cmd == CMD_PARAMETER_READ ||
native_cmd == CMD_PARAMETER_CHANGE_COL) {
+ /* Copy flash cache word-wise */
+ u32 *flash_cache = (u32 *)ctrl->flash_cache;
int i;
brcmnand_soc_data_bus_prepare(ctrl->soc);
@@ -1197,7 +1201,11 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
* SECTOR_SIZE_1K may invalidate it
*/
for (i = 0; i < FC_WORDS; i++)
- ctrl->flash_cache[i] = brcmnand_read_fc(ctrl, i);
+ /*
+ * Flash cache is big endian for parameter pages, at
+ * least on STB SoCs
+ */
+ flash_cache[i] = be32_to_cpu(brcmnand_read_fc(ctrl, i));
brcmnand_soc_data_bus_unprepare(ctrl->soc);
@@ -1214,8 +1222,8 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
static uint8_t brcmnand_read_byte(struct mtd_info *mtd)
{
- struct nand_chip *chip = mtd->priv;
- struct brcmnand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_controller *ctrl = host->ctrl;
uint8_t ret = 0;
int addr, offs;
@@ -1250,8 +1258,7 @@ static uint8_t brcmnand_read_byte(struct mtd_info *mtd)
if (host->last_byte > 0 && offs == 0)
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, addr, -1);
- ret = ctrl->flash_cache[offs >> 2] >>
- (24 - ((offs & 0x03) << 3));
+ ret = ctrl->flash_cache[offs];
break;
case NAND_CMD_GET_FEATURES:
if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) {
@@ -1282,8 +1289,8 @@ static void brcmnand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
int len)
{
int i;
- struct nand_chip *chip = mtd->priv;
- struct brcmnand_host *host = chip->priv;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct brcmnand_host *host = nand_get_controller_data(chip);
switch (host->last_cmd) {
case NAND_CMD_SET_FEATURES:
@@ -1393,13 +1400,15 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
u64 addr, unsigned int trans, u32 *buf,
u8 *oob, u64 *err_addr)
{
- struct brcmnand_host *host = chip->priv;
+ struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_controller *ctrl = host->ctrl;
int i, j, ret = 0;
/* Clear error addresses */
brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0);
brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0);
+ brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_EXT_ADDR, 0);
+ brcmnand_write_reg(ctrl, BRCMNAND_CORR_EXT_ADDR, 0);
brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
(host->cs << 16) | ((addr >> 32) & 0xffff));
@@ -1454,7 +1463,7 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
u64 addr, unsigned int trans, u32 *buf, u8 *oob)
{
- struct brcmnand_host *host = chip->priv;
+ struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_controller *ctrl = host->ctrl;
u64 err_addr = 0;
int err;
@@ -1504,7 +1513,7 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
- struct brcmnand_host *host = chip->priv;
+ struct brcmnand_host *host = nand_get_controller_data(chip);
u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
return brcmnand_read(mtd, chip, host->last_addr,
@@ -1514,7 +1523,7 @@ static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
static int brcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
- struct brcmnand_host *host = chip->priv;
+ struct brcmnand_host *host = nand_get_controller_data(chip);
u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
int ret;
@@ -1536,7 +1545,7 @@ static int brcmnand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
static int brcmnand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
- struct brcmnand_host *host = chip->priv;
+ struct brcmnand_host *host = nand_get_controller_data(chip);
brcmnand_set_ecc_enabled(host, 0);
brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
@@ -1546,20 +1555,10 @@ static int brcmnand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
return 0;
}
-static int brcmnand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
- uint32_t data_offs, uint32_t readlen,
- uint8_t *bufpoi, int page)
-{
- struct brcmnand_host *host = chip->priv;
-
- return brcmnand_read(mtd, chip, host->last_addr + data_offs,
- readlen >> FC_SHIFT, (u32 *)bufpoi, NULL);
-}
-
static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip,
u64 addr, const u32 *buf, u8 *oob)
{
- struct brcmnand_host *host = chip->priv;
+ struct brcmnand_host *host = nand_get_controller_data(chip);
struct brcmnand_controller *ctrl = host->ctrl;
unsigned int i, j, trans = mtd->writesize >> FC_SHIFT;
int status, ret = 0;
@@ -1630,7 +1629,7 @@ out:
static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required, int page)
{
- struct brcmnand_host *host = chip->priv;
+ struct brcmnand_host *host = nand_get_controller_data(chip);
void *oob = oob_required ? chip->oob_poi : NULL;
brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
@@ -1641,7 +1640,7 @@ static int brcmnand_write_page_raw(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf,
int oob_required, int page)
{
- struct brcmnand_host *host = chip->priv;
+ struct brcmnand_host *host = nand_get_controller_data(chip);
void *oob = oob_required ? chip->oob_poi : NULL;
brcmnand_set_ecc_enabled(host, 0);
@@ -1660,7 +1659,7 @@ static int brcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
static int brcmnand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
int page)
{
- struct brcmnand_host *host = chip->priv;
+ struct brcmnand_host *host = nand_get_controller_data(chip);
int ret;
brcmnand_set_ecc_enabled(host, 0);
@@ -1806,7 +1805,7 @@ static inline int get_blk_adr_bytes(u64 size, u32 writesize)
static int brcmnand_setup_dev(struct brcmnand_host *host)
{
- struct mtd_info *mtd = &host->mtd;
+ struct mtd_info *mtd = nand_to_mtd(&host->chip);
struct nand_chip *chip = &host->chip;
struct brcmnand_controller *ctrl = host->ctrl;
struct brcmnand_cfg *cfg = &host->hwcfg;
@@ -1816,7 +1815,7 @@ static int brcmnand_setup_dev(struct brcmnand_host *host)
memset(cfg, 0, sizeof(*cfg));
- ret = of_property_read_u32(chip->flash_node,
+ ret = of_property_read_u32(nand_get_flash_node(chip),
"brcm,nand-oob-sector-size",
&oob_sector);
if (ret) {
@@ -1905,16 +1904,14 @@ static int brcmnand_setup_dev(struct brcmnand_host *host)
return 0;
}
-static int brcmnand_init_cs(struct brcmnand_host *host)
+static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
{
struct brcmnand_controller *ctrl = host->ctrl;
- struct device_node *dn = host->of_node;
struct platform_device *pdev = host->pdev;
struct mtd_info *mtd;
struct nand_chip *chip;
int ret;
u16 cfg_offs;
- struct mtd_part_parser_data ppdata = { .of_node = dn };
ret = of_property_read_u32(dn, "reg", &host->cs);
if (ret) {
@@ -1922,12 +1919,11 @@ static int brcmnand_init_cs(struct brcmnand_host *host)
return -ENXIO;
}
- mtd = &host->mtd;
+ mtd = nand_to_mtd(&host->chip);
chip = &host->chip;
- chip->flash_node = dn;
- chip->priv = host;
- mtd->priv = chip;
+ nand_set_flash_node(chip, dn);
+ nand_set_controller_data(chip, host);
mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
host->cs);
mtd->owner = THIS_MODULE;
@@ -1945,7 +1941,6 @@ static int brcmnand_init_cs(struct brcmnand_host *host)
chip->ecc.mode = NAND_ECC_HW;
chip->ecc.read_page = brcmnand_read_page;
- chip->ecc.read_subpage = brcmnand_read_subpage;
chip->ecc.write_page = brcmnand_write_page;
chip->ecc.read_page_raw = brcmnand_read_page_raw;
chip->ecc.write_page_raw = brcmnand_write_page_raw;
@@ -1993,7 +1988,7 @@ static int brcmnand_init_cs(struct brcmnand_host *host)
if (nand_scan_tail(mtd))
return -ENXIO;
- return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+ return mtd_device_register(mtd, NULL, 0);
}
static void brcmnand_save_restore_cs_config(struct brcmnand_host *host,
@@ -2067,8 +2062,8 @@ static int brcmnand_resume(struct device *dev)
}
list_for_each_entry(host, &ctrl->host_list, node) {
- struct mtd_info *mtd = &host->mtd;
- struct nand_chip *chip = mtd->priv;
+ struct nand_chip *chip = &host->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
brcmnand_save_restore_cs_config(host, 1);
@@ -2134,10 +2129,24 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
if (IS_ERR(ctrl->nand_base))
return PTR_ERR(ctrl->nand_base);
+ /* Enable clock before using NAND registers */
+ ctrl->clk = devm_clk_get(dev, "nand");
+ if (!IS_ERR(ctrl->clk)) {
+ ret = clk_prepare_enable(ctrl->clk);
+ if (ret)
+ return ret;
+ } else {
+ ret = PTR_ERR(ctrl->clk);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ ctrl->clk = NULL;
+ }
+
/* Initialize NAND revision */
ret = brcmnand_revision_init(ctrl);
if (ret)
- return ret;
+ goto err;
/*
* Most chips have this cache at a fixed offset within 'nand' block.
@@ -2146,8 +2155,10 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache");
if (res) {
ctrl->nand_fc = devm_ioremap_resource(dev, res);
- if (IS_ERR(ctrl->nand_fc))
- return PTR_ERR(ctrl->nand_fc);
+ if (IS_ERR(ctrl->nand_fc)) {
+ ret = PTR_ERR(ctrl->nand_fc);
+ goto err;
+ }
} else {
ctrl->nand_fc = ctrl->nand_base +
ctrl->reg_offsets[BRCMNAND_FC_BASE];
@@ -2157,8 +2168,10 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma");
if (res) {
ctrl->flash_dma_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(ctrl->flash_dma_base))
- return PTR_ERR(ctrl->flash_dma_base);
+ if (IS_ERR(ctrl->flash_dma_base)) {
+ ret = PTR_ERR(ctrl->flash_dma_base);
+ goto err;
+ }
flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */
flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
@@ -2167,13 +2180,16 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
ctrl->dma_desc = dmam_alloc_coherent(dev,
sizeof(*ctrl->dma_desc),
&ctrl->dma_pa, GFP_KERNEL);
- if (!ctrl->dma_desc)
- return -ENOMEM;
+ if (!ctrl->dma_desc) {
+ ret = -ENOMEM;
+ goto err;
+ }
ctrl->dma_irq = platform_get_irq(pdev, 1);
if ((int)ctrl->dma_irq < 0) {
dev_err(dev, "missing FLASH_DMA IRQ\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto err;
}
ret = devm_request_irq(dev, ctrl->dma_irq,
@@ -2182,7 +2198,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
if (ret < 0) {
dev_err(dev, "can't allocate IRQ %d: error %d\n",
ctrl->dma_irq, ret);
- return ret;
+ goto err;
}
dev_info(dev, "enabling FLASH_DMA\n");
@@ -2206,7 +2222,8 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
ctrl->irq = platform_get_irq(pdev, 0);
if ((int)ctrl->irq < 0) {
dev_err(dev, "no IRQ defined\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto err;
}
/*
@@ -2230,7 +2247,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
if (ret < 0) {
dev_err(dev, "can't allocate IRQ %d: error %d\n",
ctrl->irq, ret);
- return ret;
+ goto err;
}
for_each_available_child_of_node(dn, child) {
@@ -2238,25 +2255,36 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
struct brcmnand_host *host;
host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
- if (!host)
- return -ENOMEM;
+ if (!host) {
+ of_node_put(child);
+ ret = -ENOMEM;
+ goto err;
+ }
host->pdev = pdev;
host->ctrl = ctrl;
- host->of_node = child;
- ret = brcmnand_init_cs(host);
- if (ret)
+ ret = brcmnand_init_cs(host, child);
+ if (ret) {
+ devm_kfree(dev, host);
continue; /* Try all chip-selects */
+ }
list_add_tail(&host->node, &ctrl->host_list);
}
}
/* No chip-selects could initialize properly */
- if (list_empty(&ctrl->host_list))
- return -ENODEV;
+ if (list_empty(&ctrl->host_list)) {
+ ret = -ENODEV;
+ goto err;
+ }
return 0;
+
+err:
+ clk_disable_unprepare(ctrl->clk);
+ return ret;
+
}
EXPORT_SYMBOL_GPL(brcmnand_probe);
@@ -2266,7 +2294,9 @@ int brcmnand_remove(struct platform_device *pdev)
struct brcmnand_host *host;
list_for_each_entry(host, &ctrl->host_list, node)
- nand_release(&host->mtd);
+ nand_release(nand_to_mtd(&host->chip));
+
+ clk_disable_unprepare(ctrl->clk);
dev_set_drvdata(&pdev->dev, NULL);