mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-17 04:36:56 +07:00
309600c14e
Currently, the selection of ECC byte ordering for software hamming is done at compilation time, which doesn't make sense when ECC byte calculation is done in hardware and byte ordering is forced by the hardware engine. In this case, only the correction is done in software and we want to force the byte-ordering no matter the value of CONFIG_MTD_NAND_ECC_SMC. This is typically the case for the FSMC (Smart Media ordering), TMIO and TXX9NDFMC (regular byte ordering) blocks. For all other use cases (pure software implementation, SM FTL and nandecctest), we keep selecting the byte ordering based on the CONFIG_MTD_NAND_ECC_SMC value. It might not be ideal for SM FTL (I'd expect Smart Media ordering to be employed by the Smart Media FTL), but this option doesn't seem to be enabled in the existing _defconfig, so I can't tell setting sm_order to true is the right choice. Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
514 lines
14 KiB
C
514 lines
14 KiB
C
/*
|
|
* Toshiba TMIO NAND flash controller driver
|
|
*
|
|
* Slightly murky pre-git history of the driver:
|
|
*
|
|
* Copyright (c) Ian Molton 2004, 2005, 2008
|
|
* Original work, independent of sharps code. Included hardware ECC support.
|
|
* Hard ECC did not work for writes in the early revisions.
|
|
* Copyright (c) Dirk Opfer 2005.
|
|
* Modifications developed from sharps code but
|
|
* NOT containing any, ported onto Ians base.
|
|
* Copyright (c) Chris Humbert 2005
|
|
* Copyright (c) Dmitry Baryshkov 2008
|
|
* Minor fixes
|
|
*
|
|
* Parts copyright Sebastian Carlier
|
|
*
|
|
* This file is licensed under
|
|
* the terms of the GNU General Public License version 2. This program
|
|
* is licensed "as is" without any warranty of any kind, whether express
|
|
* or implied.
|
|
*
|
|
*/
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/mfd/core.h>
|
|
#include <linux/mfd/tmio.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/io.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/mtd/mtd.h>
|
|
#include <linux/mtd/rawnand.h>
|
|
#include <linux/mtd/nand_ecc.h>
|
|
#include <linux/mtd/partitions.h>
|
|
#include <linux/slab.h>
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
* NAND Flash Host Controller Configuration Register
|
|
*/
|
|
#define CCR_COMMAND 0x04 /* w Command */
|
|
#define CCR_BASE 0x10 /* l NAND Flash Control Reg Base Addr */
|
|
#define CCR_INTP 0x3d /* b Interrupt Pin */
|
|
#define CCR_INTE 0x48 /* b Interrupt Enable */
|
|
#define CCR_EC 0x4a /* b Event Control */
|
|
#define CCR_ICC 0x4c /* b Internal Clock Control */
|
|
#define CCR_ECCC 0x5b /* b ECC Control */
|
|
#define CCR_NFTC 0x60 /* b NAND Flash Transaction Control */
|
|
#define CCR_NFM 0x61 /* b NAND Flash Monitor */
|
|
#define CCR_NFPSC 0x62 /* b NAND Flash Power Supply Control */
|
|
#define CCR_NFDC 0x63 /* b NAND Flash Detect Control */
|
|
|
|
/*
|
|
* NAND Flash Control Register
|
|
*/
|
|
#define FCR_DATA 0x00 /* bwl Data Register */
|
|
#define FCR_MODE 0x04 /* b Mode Register */
|
|
#define FCR_STATUS 0x05 /* b Status Register */
|
|
#define FCR_ISR 0x06 /* b Interrupt Status Register */
|
|
#define FCR_IMR 0x07 /* b Interrupt Mask Register */
|
|
|
|
/* FCR_MODE Register Command List */
|
|
#define FCR_MODE_DATA 0x94 /* Data Data_Mode */
|
|
#define FCR_MODE_COMMAND 0x95 /* Data Command_Mode */
|
|
#define FCR_MODE_ADDRESS 0x96 /* Data Address_Mode */
|
|
|
|
#define FCR_MODE_HWECC_CALC 0xB4 /* HW-ECC Data */
|
|
#define FCR_MODE_HWECC_RESULT 0xD4 /* HW-ECC Calc result Read_Mode */
|
|
#define FCR_MODE_HWECC_RESET 0xF4 /* HW-ECC Reset */
|
|
|
|
#define FCR_MODE_POWER_ON 0x0C /* Power Supply ON to SSFDC card */
|
|
#define FCR_MODE_POWER_OFF 0x08 /* Power Supply OFF to SSFDC card */
|
|
|
|
#define FCR_MODE_LED_OFF 0x00 /* LED OFF */
|
|
#define FCR_MODE_LED_ON 0x04 /* LED ON */
|
|
|
|
#define FCR_MODE_EJECT_ON 0x68 /* Ejection events active */
|
|
#define FCR_MODE_EJECT_OFF 0x08 /* Ejection events ignored */
|
|
|
|
#define FCR_MODE_LOCK 0x6C /* Lock_Mode. Eject Switch Invalid */
|
|
#define FCR_MODE_UNLOCK 0x0C /* UnLock_Mode. Eject Switch is valid */
|
|
|
|
#define FCR_MODE_CONTROLLER_ID 0x40 /* Controller ID Read */
|
|
#define FCR_MODE_STANDBY 0x00 /* SSFDC card Changes Standby State */
|
|
|
|
#define FCR_MODE_WE 0x80
|
|
#define FCR_MODE_ECC1 0x40
|
|
#define FCR_MODE_ECC0 0x20
|
|
#define FCR_MODE_CE 0x10
|
|
#define FCR_MODE_PCNT1 0x08
|
|
#define FCR_MODE_PCNT0 0x04
|
|
#define FCR_MODE_ALE 0x02
|
|
#define FCR_MODE_CLE 0x01
|
|
|
|
#define FCR_STATUS_BUSY 0x80
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
struct tmio_nand {
|
|
struct nand_chip chip;
|
|
|
|
struct platform_device *dev;
|
|
|
|
void __iomem *ccr;
|
|
void __iomem *fcr;
|
|
unsigned long fcr_base;
|
|
|
|
unsigned int irq;
|
|
|
|
/* for tmio_nand_read_byte */
|
|
u8 read;
|
|
unsigned read_good:1;
|
|
};
|
|
|
|
static inline struct tmio_nand *mtd_to_tmio(struct mtd_info *mtd)
|
|
{
|
|
return container_of(mtd_to_nand(mtd), struct tmio_nand, chip);
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
static void tmio_nand_hwcontrol(struct nand_chip *chip, int cmd,
|
|
unsigned int ctrl)
|
|
{
|
|
struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip));
|
|
|
|
if (ctrl & NAND_CTRL_CHANGE) {
|
|
u8 mode;
|
|
|
|
if (ctrl & NAND_NCE) {
|
|
mode = FCR_MODE_DATA;
|
|
|
|
if (ctrl & NAND_CLE)
|
|
mode |= FCR_MODE_CLE;
|
|
else
|
|
mode &= ~FCR_MODE_CLE;
|
|
|
|
if (ctrl & NAND_ALE)
|
|
mode |= FCR_MODE_ALE;
|
|
else
|
|
mode &= ~FCR_MODE_ALE;
|
|
} else {
|
|
mode = FCR_MODE_STANDBY;
|
|
}
|
|
|
|
tmio_iowrite8(mode, tmio->fcr + FCR_MODE);
|
|
tmio->read_good = 0;
|
|
}
|
|
|
|
if (cmd != NAND_CMD_NONE)
|
|
tmio_iowrite8(cmd, chip->legacy.IO_ADDR_W);
|
|
}
|
|
|
|
static int tmio_nand_dev_ready(struct nand_chip *chip)
|
|
{
|
|
struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip));
|
|
|
|
return !(tmio_ioread8(tmio->fcr + FCR_STATUS) & FCR_STATUS_BUSY);
|
|
}
|
|
|
|
static irqreturn_t tmio_irq(int irq, void *__tmio)
|
|
{
|
|
struct tmio_nand *tmio = __tmio;
|
|
struct nand_chip *nand_chip = &tmio->chip;
|
|
|
|
/* disable RDYREQ interrupt */
|
|
tmio_iowrite8(0x00, tmio->fcr + FCR_IMR);
|
|
|
|
if (unlikely(!waitqueue_active(&nand_chip->controller->wq)))
|
|
dev_warn(&tmio->dev->dev, "spurious interrupt\n");
|
|
|
|
wake_up(&nand_chip->controller->wq);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/*
|
|
*The TMIO core has a RDYREQ interrupt on the posedge of #SMRB.
|
|
*This interrupt is normally disabled, but for long operations like
|
|
*erase and write, we enable it to wake us up. The irq handler
|
|
*disables the interrupt.
|
|
*/
|
|
static int tmio_nand_wait(struct nand_chip *nand_chip)
|
|
{
|
|
struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(nand_chip));
|
|
long timeout;
|
|
u8 status;
|
|
|
|
/* enable RDYREQ interrupt */
|
|
tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR);
|
|
tmio_iowrite8(0x81, tmio->fcr + FCR_IMR);
|
|
|
|
timeout = wait_event_timeout(nand_chip->controller->wq,
|
|
tmio_nand_dev_ready(nand_chip),
|
|
msecs_to_jiffies(nand_chip->state == FL_ERASING ? 400 : 20));
|
|
|
|
if (unlikely(!tmio_nand_dev_ready(nand_chip))) {
|
|
tmio_iowrite8(0x00, tmio->fcr + FCR_IMR);
|
|
dev_warn(&tmio->dev->dev, "still busy with %s after %d ms\n",
|
|
nand_chip->state == FL_ERASING ? "erase" : "program",
|
|
nand_chip->state == FL_ERASING ? 400 : 20);
|
|
|
|
} else if (unlikely(!timeout)) {
|
|
tmio_iowrite8(0x00, tmio->fcr + FCR_IMR);
|
|
dev_warn(&tmio->dev->dev, "timeout waiting for interrupt\n");
|
|
}
|
|
|
|
nand_status_op(nand_chip, &status);
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
*The TMIO controller combines two 8-bit data bytes into one 16-bit
|
|
*word. This function separates them so nand_base.c works as expected,
|
|
*especially its NAND_CMD_READID routines.
|
|
*
|
|
*To prevent stale data from being read, tmio_nand_hwcontrol() clears
|
|
*tmio->read_good.
|
|
*/
|
|
static u_char tmio_nand_read_byte(struct nand_chip *chip)
|
|
{
|
|
struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip));
|
|
unsigned int data;
|
|
|
|
if (tmio->read_good--)
|
|
return tmio->read;
|
|
|
|
data = tmio_ioread16(tmio->fcr + FCR_DATA);
|
|
tmio->read = data >> 8;
|
|
return data;
|
|
}
|
|
|
|
/*
|
|
*The TMIO controller converts an 8-bit NAND interface to a 16-bit
|
|
*bus interface, so all data reads and writes must be 16-bit wide.
|
|
*Thus, we implement 16-bit versions of the read, write, and verify
|
|
*buffer functions.
|
|
*/
|
|
static void
|
|
tmio_nand_write_buf(struct nand_chip *chip, const u_char *buf, int len)
|
|
{
|
|
struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip));
|
|
|
|
tmio_iowrite16_rep(tmio->fcr + FCR_DATA, buf, len >> 1);
|
|
}
|
|
|
|
static void tmio_nand_read_buf(struct nand_chip *chip, u_char *buf, int len)
|
|
{
|
|
struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip));
|
|
|
|
tmio_ioread16_rep(tmio->fcr + FCR_DATA, buf, len >> 1);
|
|
}
|
|
|
|
static void tmio_nand_enable_hwecc(struct nand_chip *chip, int mode)
|
|
{
|
|
struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip));
|
|
|
|
tmio_iowrite8(FCR_MODE_HWECC_RESET, tmio->fcr + FCR_MODE);
|
|
tmio_ioread8(tmio->fcr + FCR_DATA); /* dummy read */
|
|
tmio_iowrite8(FCR_MODE_HWECC_CALC, tmio->fcr + FCR_MODE);
|
|
}
|
|
|
|
static int tmio_nand_calculate_ecc(struct nand_chip *chip, const u_char *dat,
|
|
u_char *ecc_code)
|
|
{
|
|
struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip));
|
|
unsigned int ecc;
|
|
|
|
tmio_iowrite8(FCR_MODE_HWECC_RESULT, tmio->fcr + FCR_MODE);
|
|
|
|
ecc = tmio_ioread16(tmio->fcr + FCR_DATA);
|
|
ecc_code[1] = ecc; /* 000-255 LP7-0 */
|
|
ecc_code[0] = ecc >> 8; /* 000-255 LP15-8 */
|
|
ecc = tmio_ioread16(tmio->fcr + FCR_DATA);
|
|
ecc_code[2] = ecc; /* 000-255 CP5-0,11b */
|
|
ecc_code[4] = ecc >> 8; /* 256-511 LP7-0 */
|
|
ecc = tmio_ioread16(tmio->fcr + FCR_DATA);
|
|
ecc_code[3] = ecc; /* 256-511 LP15-8 */
|
|
ecc_code[5] = ecc >> 8; /* 256-511 CP5-0,11b */
|
|
|
|
tmio_iowrite8(FCR_MODE_DATA, tmio->fcr + FCR_MODE);
|
|
return 0;
|
|
}
|
|
|
|
static int tmio_nand_correct_data(struct nand_chip *chip, unsigned char *buf,
|
|
unsigned char *read_ecc,
|
|
unsigned char *calc_ecc)
|
|
{
|
|
int r0, r1;
|
|
|
|
/* assume ecc.size = 512 and ecc.bytes = 6 */
|
|
r0 = __nand_correct_data(buf, read_ecc, calc_ecc, 256, false);
|
|
if (r0 < 0)
|
|
return r0;
|
|
r1 = __nand_correct_data(buf + 256, read_ecc + 3, calc_ecc + 3, 256,
|
|
false);
|
|
if (r1 < 0)
|
|
return r1;
|
|
return r0 + r1;
|
|
}
|
|
|
|
static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio)
|
|
{
|
|
const struct mfd_cell *cell = mfd_get_cell(dev);
|
|
int ret;
|
|
|
|
if (cell->enable) {
|
|
ret = cell->enable(dev);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
/* (4Ch) CLKRUN Enable 1st spcrunc */
|
|
tmio_iowrite8(0x81, tmio->ccr + CCR_ICC);
|
|
|
|
/* (10h)BaseAddress 0x1000 spba.spba2 */
|
|
tmio_iowrite16(tmio->fcr_base, tmio->ccr + CCR_BASE);
|
|
tmio_iowrite16(tmio->fcr_base >> 16, tmio->ccr + CCR_BASE + 2);
|
|
|
|
/* (04h)Command Register I/O spcmd */
|
|
tmio_iowrite8(0x02, tmio->ccr + CCR_COMMAND);
|
|
|
|
/* (62h) Power Supply Control ssmpwc */
|
|
/* HardPowerOFF - SuspendOFF - PowerSupplyWait_4MS */
|
|
tmio_iowrite8(0x02, tmio->ccr + CCR_NFPSC);
|
|
|
|
/* (63h) Detect Control ssmdtc */
|
|
tmio_iowrite8(0x02, tmio->ccr + CCR_NFDC);
|
|
|
|
/* Interrupt status register clear sintst */
|
|
tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR);
|
|
|
|
/* After power supply, Media are reset smode */
|
|
tmio_iowrite8(FCR_MODE_POWER_ON, tmio->fcr + FCR_MODE);
|
|
tmio_iowrite8(FCR_MODE_COMMAND, tmio->fcr + FCR_MODE);
|
|
tmio_iowrite8(NAND_CMD_RESET, tmio->fcr + FCR_DATA);
|
|
|
|
/* Standby Mode smode */
|
|
tmio_iowrite8(FCR_MODE_STANDBY, tmio->fcr + FCR_MODE);
|
|
|
|
mdelay(5);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio)
|
|
{
|
|
const struct mfd_cell *cell = mfd_get_cell(dev);
|
|
|
|
tmio_iowrite8(FCR_MODE_POWER_OFF, tmio->fcr + FCR_MODE);
|
|
if (cell->disable)
|
|
cell->disable(dev);
|
|
}
|
|
|
|
static int tmio_probe(struct platform_device *dev)
|
|
{
|
|
struct tmio_nand_data *data = dev_get_platdata(&dev->dev);
|
|
struct resource *fcr = platform_get_resource(dev,
|
|
IORESOURCE_MEM, 0);
|
|
struct resource *ccr = platform_get_resource(dev,
|
|
IORESOURCE_MEM, 1);
|
|
int irq = platform_get_irq(dev, 0);
|
|
struct tmio_nand *tmio;
|
|
struct mtd_info *mtd;
|
|
struct nand_chip *nand_chip;
|
|
int retval;
|
|
|
|
if (data == NULL)
|
|
dev_warn(&dev->dev, "NULL platform data!\n");
|
|
|
|
tmio = devm_kzalloc(&dev->dev, sizeof(*tmio), GFP_KERNEL);
|
|
if (!tmio)
|
|
return -ENOMEM;
|
|
|
|
tmio->dev = dev;
|
|
|
|
platform_set_drvdata(dev, tmio);
|
|
nand_chip = &tmio->chip;
|
|
mtd = nand_to_mtd(nand_chip);
|
|
mtd->name = "tmio-nand";
|
|
mtd->dev.parent = &dev->dev;
|
|
|
|
tmio->ccr = devm_ioremap(&dev->dev, ccr->start, resource_size(ccr));
|
|
if (!tmio->ccr)
|
|
return -EIO;
|
|
|
|
tmio->fcr_base = fcr->start & 0xfffff;
|
|
tmio->fcr = devm_ioremap(&dev->dev, fcr->start, resource_size(fcr));
|
|
if (!tmio->fcr)
|
|
return -EIO;
|
|
|
|
retval = tmio_hw_init(dev, tmio);
|
|
if (retval)
|
|
return retval;
|
|
|
|
/* Set address of NAND IO lines */
|
|
nand_chip->legacy.IO_ADDR_R = tmio->fcr;
|
|
nand_chip->legacy.IO_ADDR_W = tmio->fcr;
|
|
|
|
/* Set address of hardware control function */
|
|
nand_chip->legacy.cmd_ctrl = tmio_nand_hwcontrol;
|
|
nand_chip->legacy.dev_ready = tmio_nand_dev_ready;
|
|
nand_chip->legacy.read_byte = tmio_nand_read_byte;
|
|
nand_chip->legacy.write_buf = tmio_nand_write_buf;
|
|
nand_chip->legacy.read_buf = tmio_nand_read_buf;
|
|
|
|
/* set eccmode using hardware ECC */
|
|
nand_chip->ecc.mode = NAND_ECC_HW;
|
|
nand_chip->ecc.size = 512;
|
|
nand_chip->ecc.bytes = 6;
|
|
nand_chip->ecc.strength = 2;
|
|
nand_chip->ecc.hwctl = tmio_nand_enable_hwecc;
|
|
nand_chip->ecc.calculate = tmio_nand_calculate_ecc;
|
|
nand_chip->ecc.correct = tmio_nand_correct_data;
|
|
|
|
if (data)
|
|
nand_chip->badblock_pattern = data->badblock_pattern;
|
|
|
|
/* 15 us command delay time */
|
|
nand_chip->legacy.chip_delay = 15;
|
|
|
|
retval = devm_request_irq(&dev->dev, irq, &tmio_irq, 0,
|
|
dev_name(&dev->dev), tmio);
|
|
if (retval) {
|
|
dev_err(&dev->dev, "request_irq error %d\n", retval);
|
|
goto err_irq;
|
|
}
|
|
|
|
tmio->irq = irq;
|
|
nand_chip->legacy.waitfunc = tmio_nand_wait;
|
|
|
|
/* Scan to find existence of the device */
|
|
retval = nand_scan(nand_chip, 1);
|
|
if (retval)
|
|
goto err_irq;
|
|
|
|
/* Register the partitions */
|
|
retval = mtd_device_parse_register(mtd,
|
|
data ? data->part_parsers : NULL,
|
|
NULL,
|
|
data ? data->partition : NULL,
|
|
data ? data->num_partitions : 0);
|
|
if (!retval)
|
|
return retval;
|
|
|
|
nand_release(nand_chip);
|
|
|
|
err_irq:
|
|
tmio_hw_stop(dev, tmio);
|
|
return retval;
|
|
}
|
|
|
|
static int tmio_remove(struct platform_device *dev)
|
|
{
|
|
struct tmio_nand *tmio = platform_get_drvdata(dev);
|
|
|
|
nand_release(&tmio->chip);
|
|
tmio_hw_stop(dev, tmio);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
static int tmio_suspend(struct platform_device *dev, pm_message_t state)
|
|
{
|
|
const struct mfd_cell *cell = mfd_get_cell(dev);
|
|
|
|
if (cell->suspend)
|
|
cell->suspend(dev);
|
|
|
|
tmio_hw_stop(dev, platform_get_drvdata(dev));
|
|
return 0;
|
|
}
|
|
|
|
static int tmio_resume(struct platform_device *dev)
|
|
{
|
|
const struct mfd_cell *cell = mfd_get_cell(dev);
|
|
|
|
/* FIXME - is this required or merely another attack of the broken
|
|
* SHARP platform? Looks suspicious.
|
|
*/
|
|
tmio_hw_init(dev, platform_get_drvdata(dev));
|
|
|
|
if (cell->resume)
|
|
cell->resume(dev);
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
#define tmio_suspend NULL
|
|
#define tmio_resume NULL
|
|
#endif
|
|
|
|
static struct platform_driver tmio_driver = {
|
|
.driver.name = "tmio-nand",
|
|
.driver.owner = THIS_MODULE,
|
|
.probe = tmio_probe,
|
|
.remove = tmio_remove,
|
|
.suspend = tmio_suspend,
|
|
.resume = tmio_resume,
|
|
};
|
|
|
|
module_platform_driver(tmio_driver);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_AUTHOR("Ian Molton, Dirk Opfer, Chris Humbert, Dmitry Baryshkov");
|
|
MODULE_DESCRIPTION("NAND flash driver on Toshiba Mobile IO controller");
|
|
MODULE_ALIAS("platform:tmio-nand");
|