Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | /*
* arch/cris/arch-v32/drivers/nandflash.c
*
* Copyright (c) 2007
*
* Derived from drivers/mtd/nand/spia.c
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <arch/memmap.h>
#include <hwregs/reg_map.h>
#include <hwregs/reg_rdwr.h>
#include <hwregs/pio_defs.h>
#include <pinmux.h>
#include <asm/io.h>
#define MANUAL_ALE_CLE_CONTROL 1
#define regf_ALE a0
#define regf_CLE a1
#define regf_NCE ce0_n
#define CLE_BIT 10
#define ALE_BIT 11
#define CE_BIT 12
struct mtd_info_wrapper {
struct mtd_info info;
struct nand_chip chip;
};
/* Bitmask for control pins */
#define PIN_BITMASK ((1 << CE_BIT) | (1 << CLE_BIT) | (1 << ALE_BIT))
static struct mtd_info *crisv32_mtd;
/*
* hardware specific access to control-lines
*/
static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
unsigned long flags;
reg_pio_rw_dout dout;
struct nand_chip *this = mtd->priv;
local_irq_save(flags);
/* control bits change */
if (ctrl & NAND_CTRL_CHANGE) {
dout = REG_RD(pio, regi_pio, rw_dout);
dout.regf_NCE = (ctrl & NAND_NCE) ? 0 : 1;
#if !MANUAL_ALE_CLE_CONTROL
if (ctrl & NAND_ALE) {
/* A0 = ALE high */
this->IO_ADDR_W = (void __iomem *)REG_ADDR(pio,
regi_pio, rw_io_access1);
} else if (ctrl & NAND_CLE) {
/* A1 = CLE high */
this->IO_ADDR_W = (void __iomem *)REG_ADDR(pio,
regi_pio, rw_io_access2);
} else {
/* A1 = CLE and A0 = ALE low */
this->IO_ADDR_W = (void __iomem *)REG_ADDR(pio,
regi_pio, rw_io_access0);
}
#else
dout.regf_CLE = (ctrl & NAND_CLE) ? 1 : 0;
dout.regf_ALE = (ctrl & NAND_ALE) ? 1 : 0;
#endif
REG_WR(pio, regi_pio, rw_dout, dout);
}
/* command to chip */
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
local_irq_restore(flags);
}
/*
* read device ready pin
*/
static int crisv32_device_ready(struct mtd_info *mtd)
{
reg_pio_r_din din = REG_RD(pio, regi_pio, r_din);
return din.rdy;
}
/*
* Main initialization routine
*/
struct mtd_info *__init crisv32_nand_flash_probe(void)
{
void __iomem *read_cs;
void __iomem *write_cs;
struct mtd_info_wrapper *wrapper;
struct nand_chip *this;
int err = 0;
reg_pio_rw_man_ctrl man_ctrl = {
.regf_NCE = regk_pio_yes,
#if MANUAL_ALE_CLE_CONTROL
.regf_ALE = regk_pio_yes,
.regf_CLE = regk_pio_yes
#endif
};
reg_pio_rw_oe oe = {
.regf_NCE = regk_pio_yes,
#if MANUAL_ALE_CLE_CONTROL
.regf_ALE = regk_pio_yes,
.regf_CLE = regk_pio_yes
#endif
};
reg_pio_rw_dout dout = { .regf_NCE = 1 };
/* Allocate pio pins to pio */
crisv32_pinmux_alloc_fixed(pinmux_pio);
/* Set up CE, ALE, CLE (ce0_n, a0, a1) for manual control and output */
REG_WR(pio, regi_pio, rw_man_ctrl, man_ctrl);
REG_WR(pio, regi_pio, rw_dout, dout);
REG_WR(pio, regi_pio, rw_oe, oe);
/* Allocate memory for MTD device structure and private data */
wrapper = kzalloc(sizeof(struct mtd_info_wrapper), GFP_KERNEL);
if (!wrapper) {
printk(KERN_ERR "Unable to allocate CRISv32 NAND MTD "
"device structure.\n");
err = -ENOMEM;
return NULL;
}
read_cs = write_cs = (void __iomem *)REG_ADDR(pio, regi_pio,
rw_io_access0);
/* Get pointer to private data */
this = &wrapper->chip;
crisv32_mtd = &wrapper->info;
/* Link the private data with the MTD structure */
crisv32_mtd->priv = this;
/* Set address of NAND IO lines */
this->IO_ADDR_R = read_cs;
this->IO_ADDR_W = write_cs;
this->cmd_ctrl = crisv32_hwcontrol;
this->dev_ready = crisv32_device_ready;
/* 20 us command delay time */
this->chip_delay = 20;
this->ecc.mode = NAND_ECC_SOFT;
/* Enable the following for a flash based bad block table */
/* this->bbt_options = NAND_BBT_USE_FLASH; */
/* Scan to find existence of the device */
if (nand_scan(crisv32_mtd, 1)) {
err = -ENXIO;
goto out_mtd;
}
return crisv32_mtd;
out_mtd:
kfree(wrapper);
return NULL;
}
|