linux/arch/arm/mach-omap2/gpmc-nand.c
<<
>>
Prefs
   1/*
   2 * gpmc-nand.c
   3 *
   4 * Copyright (C) 2009 Texas Instruments
   5 * Vimal Singh <vimalsingh@ti.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11
  12#include <linux/kernel.h>
  13#include <linux/platform_device.h>
  14#include <linux/io.h>
  15#include <linux/mtd/nand.h>
  16#include <linux/platform_data/mtd-nand-omap2.h>
  17
  18#include <asm/mach/flash.h>
  19
  20#include "gpmc.h"
  21#include "soc.h"
  22#include "gpmc-nand.h"
  23
  24/* minimum size for IO mapping */
  25#define NAND_IO_SIZE    4
  26
  27static bool gpmc_hwecc_bch_capable(enum omap_ecc ecc_opt)
  28{
  29        /* platforms which support all ECC schemes */
  30        if (soc_is_am33xx() || soc_is_am43xx() || cpu_is_omap44xx() ||
  31                 soc_is_omap54xx() || soc_is_dra7xx())
  32                return 1;
  33
  34        if (ecc_opt == OMAP_ECC_BCH4_CODE_HW_DETECTION_SW ||
  35                 ecc_opt == OMAP_ECC_BCH8_CODE_HW_DETECTION_SW) {
  36                if (cpu_is_omap24xx())
  37                        return 0;
  38                else if (cpu_is_omap3630() && (GET_OMAP_REVISION() == 0))
  39                        return 0;
  40                else
  41                        return 1;
  42        }
  43
  44        /* OMAP3xxx do not have ELM engine, so cannot support ECC schemes
  45         * which require H/W based ECC error detection */
  46        if ((cpu_is_omap34xx() || cpu_is_omap3630()) &&
  47            ((ecc_opt == OMAP_ECC_BCH4_CODE_HW) ||
  48                 (ecc_opt == OMAP_ECC_BCH8_CODE_HW)))
  49                return 0;
  50
  51        /* legacy platforms support only HAM1 (1-bit Hamming) ECC scheme */
  52        if (ecc_opt == OMAP_ECC_HAM1_CODE_HW ||
  53            ecc_opt == OMAP_ECC_HAM1_CODE_SW)
  54                return 1;
  55        else
  56                return 0;
  57}
  58
  59/* This function will go away once the device-tree convertion is complete */
  60static void gpmc_set_legacy(struct omap_nand_platform_data *gpmc_nand_data,
  61                            struct gpmc_settings *s)
  62{
  63        /* Enable RD PIN Monitoring Reg */
  64        if (gpmc_nand_data->dev_ready) {
  65                s->wait_on_read = true;
  66                s->wait_on_write = true;
  67        }
  68
  69        if (gpmc_nand_data->devsize == NAND_BUSWIDTH_16)
  70                s->device_width = GPMC_DEVWIDTH_16BIT;
  71        else
  72                s->device_width = GPMC_DEVWIDTH_8BIT;
  73}
  74
  75int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data,
  76                   struct gpmc_timings *gpmc_t)
  77{
  78        int err = 0;
  79        struct gpmc_settings s;
  80        struct platform_device *pdev;
  81        struct resource gpmc_nand_res[] = {
  82                { .flags = IORESOURCE_MEM, },
  83                { .flags = IORESOURCE_IRQ, },
  84                { .flags = IORESOURCE_IRQ, },
  85        };
  86
  87        BUG_ON(gpmc_nand_data->cs >= GPMC_CS_NUM);
  88
  89        err = gpmc_cs_request(gpmc_nand_data->cs, NAND_IO_SIZE,
  90                              (unsigned long *)&gpmc_nand_res[0].start);
  91        if (err < 0) {
  92                pr_err("omap2-gpmc: Cannot request GPMC CS %d, error %d\n",
  93                       gpmc_nand_data->cs, err);
  94                return err;
  95        }
  96        gpmc_nand_res[0].end = gpmc_nand_res[0].start + NAND_IO_SIZE - 1;
  97        gpmc_nand_res[1].start = gpmc_get_client_irq(GPMC_IRQ_FIFOEVENTENABLE);
  98        gpmc_nand_res[2].start = gpmc_get_client_irq(GPMC_IRQ_COUNT_EVENT);
  99
 100        if (gpmc_t) {
 101                err = gpmc_cs_set_timings(gpmc_nand_data->cs, gpmc_t);
 102                if (err < 0) {
 103                        pr_err("omap2-gpmc: Unable to set gpmc timings: %d\n", err);
 104                        return err;
 105                }
 106        }
 107
 108        memset(&s, 0, sizeof(struct gpmc_settings));
 109        if (gpmc_nand_data->of_node)
 110                gpmc_read_settings_dt(gpmc_nand_data->of_node, &s);
 111        else
 112                gpmc_set_legacy(gpmc_nand_data, &s);
 113
 114        s.device_nand = true;
 115        err = gpmc_cs_program_settings(gpmc_nand_data->cs, &s);
 116        if (err < 0)
 117                goto out_free_cs;
 118
 119        err = gpmc_configure(GPMC_CONFIG_WP, 0);
 120        if (err < 0)
 121                goto out_free_cs;
 122
 123        gpmc_update_nand_reg(&gpmc_nand_data->reg, gpmc_nand_data->cs);
 124
 125        if (!gpmc_hwecc_bch_capable(gpmc_nand_data->ecc_opt)) {
 126                pr_err("omap2-nand: Unsupported NAND ECC scheme selected\n");
 127                err = -EINVAL;
 128                goto out_free_cs;
 129        }
 130
 131
 132        pdev = platform_device_alloc("omap2-nand", gpmc_nand_data->cs);
 133        if (pdev) {
 134                err = platform_device_add_resources(pdev, gpmc_nand_res,
 135                                                    ARRAY_SIZE(gpmc_nand_res));
 136                if (!err)
 137                        pdev->dev.platform_data = gpmc_nand_data;
 138        } else {
 139                err = -ENOMEM;
 140        }
 141        if (err)
 142                goto out_free_pdev;
 143
 144        err = platform_device_add(pdev);
 145        if (err) {
 146                dev_err(&pdev->dev, "Unable to register NAND device\n");
 147                goto out_free_pdev;
 148        }
 149
 150        return 0;
 151
 152out_free_pdev:
 153        platform_device_put(pdev);
 154out_free_cs:
 155        gpmc_cs_free(gpmc_nand_data->cs);
 156
 157        return err;
 158}
 159