/*
* Handles the M-Systems DiskOnChip G3 chip
*
* Copyright (C) 2011 Robert Jarzmik
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#define CREATE_TRACE_POINTS
#include "docg3.h"
/*
* This driver handles the DiskOnChip G3 flash memory.
*
* As no specification is available from M-Systems/Sandisk, this drivers lacks
* several functions available on the chip, as :
* - block erase
* - page write
* - IPL write
* - ECC fixing (lack of BCH algorith understanding)
* - powerdown / powerup
*
* The bus data width (8bits versus 16bits) is not handled (if_cfg flag), and
* the driver assumes a 16bits data bus.
*
* DocG3 relies on 2 ECC algorithms, which are handled in hardware :
* - a 1 byte Hamming code stored in the OOB for each page
* - a 7 bytes BCH code stored in the OOB for each page
* The BCH part is only used for check purpose, no correction is available as
* some information is missing. What is known is that :
* - BCH is in GF(2^14)
* - BCH is over data of 520 bytes (512 page + 7 page_info bytes
* + 1 hamming byte)
* - BCH can correct up to 4 bits (t = 4)
* - BCH syndroms are calculated in hardware, and checked in hardware as well
*
*/
static inline u8 doc_readb(struct docg3 *docg3, u16 reg)
{
u8 val = readb(docg3->base + reg);
trace_docg3_io(0, 8, reg, (int)val);
return val;
}
static inline u16 doc_readw(struct docg3 *docg3, u16 reg)
{
u16 val = readw(docg3->base + reg);
trace_docg3_io(0, 16, reg, (int)val);
return val;
}
static inline void doc_writeb(struct docg3 *docg3, u8 val, u16 reg)
{
writeb(val, docg3->base + reg);
trace_docg3_io(1, 16, reg, val);
}
static inline void doc_writew(struct docg3 *docg3, u16 val, u16 reg)
{
writew(val, docg3->base + reg);
trace_docg3_io(1, 16, reg, val);
}
static inline void doc_flash_command(struct docg3 *docg3, u8 cmd)
{
doc_writeb(docg3, cmd, DOC_FLASHCOMMAND);
}
static inline void doc_flash_sequence(struct docg3 *docg3, u8 seq)
{
doc_writeb(docg3, seq, DOC_FLASHSEQUENCE);
}
static inline void doc_flash_address(struct docg3 *docg3, u8 addr)
{
doc_writeb(docg3, addr, DOC_FLASHADDRESS);
}
static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL };
static int doc_register_readb(struct docg3 *docg3, int reg)
{
u8 val;
doc_writew(docg3, reg, DOC_READADDRESS);
val = doc_readb(docg3, reg);
doc_vdbg("Read register %04x : %02x\n", reg, val);
return val;
}
static int doc_register_readw(struct docg3 *docg3, int reg)
{
u16 val;
doc_writew(docg3, reg, DOC_READADDRESS);
val = doc_readw(docg3, reg);
doc_vdbg("Read register %04x : %04x\n", reg, val);
return val;
}
/**
* doc_delay - delay docg3 operations
* @docg3: the device
* @nbNOPs: the number of NOPs to issue
*
* As no specification is available, the right timings between chip commands are
* unknown. The only available piece of information are the observed nops on a
* working docg3 chip.
* Therefore, doc_delay relies on a busy loop of NOPs, instead of scheduler
* friendlier msleep() functions or blocking mdelay().
*/
static void doc_delay(struct docg3 *docg3, int nbNOPs)
{
int i;
doc_dbg("NOP x %d\n", nbNOPs);
for (i