Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 71ec5155 authored by Sascha Hauer's avatar Sascha Hauer Committed by David Woodhouse
Browse files

mxc_nand: Add v3 (i.MX51) Support

parent 6e85dfdc
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -481,7 +481,7 @@ config MTD_NAND_MPC5121_NFC


config MTD_NAND_MXC
config MTD_NAND_MXC
	tristate "MXC NAND support"
	tristate "MXC NAND support"
	depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3
	depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 || ARCH_MX51
	help
	help
	  This enables the driver for the NAND flash controller on the
	  This enables the driver for the NAND flash controller on the
	  MXC processors.
	  MXC processors.
+221 −1
Original line number Original line Diff line number Diff line
@@ -39,6 +39,8 @@


#define nfc_is_v21()		(cpu_is_mx25() || cpu_is_mx35())
#define nfc_is_v21()		(cpu_is_mx25() || cpu_is_mx35())
#define nfc_is_v1()		(cpu_is_mx31() || cpu_is_mx27() || cpu_is_mx21())
#define nfc_is_v1()		(cpu_is_mx31() || cpu_is_mx27() || cpu_is_mx21())
#define nfc_is_v3_2()		cpu_is_mx51()
#define nfc_is_v3()		nfc_is_v3_2()


/* Addresses for NFC registers */
/* Addresses for NFC registers */
#define NFC_V1_V2_BUF_SIZE		(host->regs + 0x00)
#define NFC_V1_V2_BUF_SIZE		(host->regs + 0x00)
@@ -80,6 +82,54 @@
#define NFC_ID				(1 << 4)
#define NFC_ID				(1 << 4)
#define NFC_STATUS			(1 << 5)
#define NFC_STATUS			(1 << 5)


#define NFC_V3_FLASH_CMD		(host->regs_axi + 0x00)
#define NFC_V3_FLASH_ADDR0		(host->regs_axi + 0x04)

#define NFC_V3_CONFIG1			(host->regs_axi + 0x34)
#define NFC_V3_CONFIG1_SP_EN		(1 << 0)
#define NFC_V3_CONFIG1_RBA(x)		(((x) & 0x7 ) << 4)

#define NFC_V3_ECC_STATUS_RESULT	(host->regs_axi + 0x38)

#define NFC_V3_LAUNCH			(host->regs_axi + 0x40)

#define NFC_V3_WRPROT			(host->regs_ip + 0x0)
#define NFC_V3_WRPROT_LOCK_TIGHT	(1 << 0)
#define NFC_V3_WRPROT_LOCK		(1 << 1)
#define NFC_V3_WRPROT_UNLOCK		(1 << 2)
#define NFC_V3_WRPROT_BLS_UNLOCK	(2 << 6)

#define NFC_V3_WRPROT_UNLOCK_BLK_ADD0   (host->regs_ip + 0x04)

#define NFC_V3_CONFIG2			(host->regs_ip + 0x24)
#define NFC_V3_CONFIG2_PS_512			(0 << 0)
#define NFC_V3_CONFIG2_PS_2048			(1 << 0)
#define NFC_V3_CONFIG2_PS_4096			(2 << 0)
#define NFC_V3_CONFIG2_ONE_CYCLE		(1 << 2)
#define NFC_V3_CONFIG2_ECC_EN			(1 << 3)
#define NFC_V3_CONFIG2_2CMD_PHASES		(1 << 4)
#define NFC_V3_CONFIG2_NUM_ADDR_PHASE0		(1 << 5)
#define NFC_V3_CONFIG2_ECC_MODE_8		(1 << 6)
#define NFC_V3_CONFIG2_PPB(x)			(((x) & 0x3) << 7)
#define NFC_V3_CONFIG2_NUM_ADDR_PHASE1(x)	(((x) & 0x3) << 12)
#define NFC_V3_CONFIG2_INT_MSK			(1 << 15)
#define NFC_V3_CONFIG2_ST_CMD(x)		(((x) & 0xff) << 24)
#define NFC_V3_CONFIG2_SPAS(x)			(((x) & 0xff) << 16)

#define NFC_V3_CONFIG3				(host->regs_ip + 0x28)
#define NFC_V3_CONFIG3_ADD_OP(x)		(((x) & 0x3) << 0)
#define NFC_V3_CONFIG3_FW8			(1 << 3)
#define NFC_V3_CONFIG3_SBB(x)			(((x) & 0x7) << 8)
#define NFC_V3_CONFIG3_NUM_OF_DEVICES(x)	(((x) & 0x7) << 12)
#define NFC_V3_CONFIG3_RBB_MODE			(1 << 15)
#define NFC_V3_CONFIG3_NO_SDMA			(1 << 20)

#define NFC_V3_IPC			(host->regs_ip + 0x2C)
#define NFC_V3_IPC_CREQ			(1 << 0)
#define NFC_V3_IPC_INT			(1 << 31)

#define NFC_V3_DELAY_LINE		(host->regs_ip + 0x34)

struct mxc_nand_host {
struct mxc_nand_host {
	struct mtd_info		mtd;
	struct mtd_info		mtd;
	struct nand_chip	nand;
	struct nand_chip	nand;
@@ -91,6 +141,8 @@ struct mxc_nand_host {


	void __iomem		*base;
	void __iomem		*base;
	void __iomem		*regs;
	void __iomem		*regs;
	void __iomem		*regs_axi;
	void __iomem		*regs_ip;
	int			status_request;
	int			status_request;
	struct clk		*clk;
	struct clk		*clk;
	int			clk_act;
	int			clk_act;
@@ -169,6 +221,20 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
	return IRQ_HANDLED;
	return IRQ_HANDLED;
}
}


static int check_int_v3(struct mxc_nand_host *host)
{
	uint32_t tmp;

	tmp = readl(NFC_V3_IPC);
	if (!(tmp & NFC_V3_IPC_INT))
		return 0;

	tmp &= ~NFC_V3_IPC_INT;
	writel(tmp, NFC_V3_IPC);

	return 1;
}

static int check_int_v1_v2(struct mxc_nand_host *host)
static int check_int_v1_v2(struct mxc_nand_host *host)
{
{
	uint32_t tmp;
	uint32_t tmp;
@@ -209,6 +275,18 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq)
	}
	}
}
}


static void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq)
{
	/* fill command */
	writel(cmd, NFC_V3_FLASH_CMD);

	/* send out command */
	writel(NFC_CMD, NFC_V3_LAUNCH);

	/* Wait for operation to complete */
	wait_op_done(host, useirq);
}

/* This function issues the specified command to the NAND device and
/* This function issues the specified command to the NAND device and
 * waits for completion. */
 * waits for completion. */
static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq)
static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq)
@@ -237,6 +315,17 @@ static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq)
	}
	}
}
}


static void send_addr_v3(struct mxc_nand_host *host, uint16_t addr, int islast)
{
	/* fill address */
	writel(addr, NFC_V3_FLASH_ADDR0);

	/* send out address */
	writel(NFC_ADDR, NFC_V3_LAUNCH);

	wait_op_done(host, 0);
}

/* This function sends an address (or partial address) to the
/* This function sends an address (or partial address) to the
 * NAND device. The address is used to select the source/destination for
 * NAND device. The address is used to select the source/destination for
 * a NAND command. */
 * a NAND command. */
@@ -251,6 +340,22 @@ static void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islas
	wait_op_done(host, islast);
	wait_op_done(host, islast);
}
}


static void send_page_v3(struct mtd_info *mtd, unsigned int ops)
{
	struct nand_chip *nand_chip = mtd->priv;
	struct mxc_nand_host *host = nand_chip->priv;
	uint32_t tmp;

	tmp = readl(NFC_V3_CONFIG1);
	tmp &= ~(7 << 4);
	writel(tmp, NFC_V3_CONFIG1);

	/* transfer data from NFC ram to nand */
	writel(ops, NFC_V3_LAUNCH);

	wait_op_done(host, false);
}

static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops)
static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops)
{
{
	struct nand_chip *nand_chip = mtd->priv;
	struct nand_chip *nand_chip = mtd->priv;
@@ -274,6 +379,16 @@ static void send_page_v1_v2(struct mtd_info *mtd, unsigned int ops)
	}
	}
}
}


static void send_read_id_v3(struct mxc_nand_host *host)
{
	/* Read ID into main buffer */
	writel(NFC_ID, NFC_V3_LAUNCH);

	wait_op_done(host, true);

	memcpy(host->data_buf, host->main_area0, 16);
}

/* Request the NANDFC to perform a read of the NAND device ID. */
/* Request the NANDFC to perform a read of the NAND device ID. */
static void send_read_id_v1_v2(struct mxc_nand_host *host)
static void send_read_id_v1_v2(struct mxc_nand_host *host)
{
{
@@ -299,6 +414,14 @@ static void send_read_id_v1_v2(struct mxc_nand_host *host)
	memcpy(host->data_buf, host->main_area0, 16);
	memcpy(host->data_buf, host->main_area0, 16);
}
}


static uint16_t get_dev_status_v3(struct mxc_nand_host *host)
{
	writew(NFC_STATUS, NFC_V3_LAUNCH);
	wait_op_done(host, true);

	return readl(NFC_V3_CONFIG1) >> 16;
}

/* This function requests the NANDFC to perform a read of the
/* This function requests the NANDFC to perform a read of the
 * NAND device status and returns the current status. */
 * NAND device status and returns the current status. */
static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host)
static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host)
@@ -381,7 +504,10 @@ static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat,


	no_subpages = mtd->writesize >> 9;
	no_subpages = mtd->writesize >> 9;


	if (nfc_is_v21())
		ecc_stat = readl(NFC_V1_V2_ECC_STATUS_RESULT);
		ecc_stat = readl(NFC_V1_V2_ECC_STATUS_RESULT);
	else
		ecc_stat = readl(NFC_V3_ECC_STATUS_RESULT);


	do {
	do {
		err = ecc_stat & ecc_bit_mask;
		err = ecc_stat & ecc_bit_mask;
@@ -643,6 +769,72 @@ static void preset_v1_v2(struct mtd_info *mtd)
	writew(0x4, NFC_V1_V2_WRPROT);
	writew(0x4, NFC_V1_V2_WRPROT);
}
}


static void preset_v3(struct mtd_info *mtd)
{
	struct nand_chip *chip = mtd->priv;
	struct mxc_nand_host *host = chip->priv;
	uint32_t config2, config3;
	int i, addr_phases;

	writel(NFC_V3_CONFIG1_RBA(0), NFC_V3_CONFIG1);
	writel(NFC_V3_IPC_CREQ, NFC_V3_IPC);

	/* Unlock the internal RAM Buffer */
	writel(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK,
			NFC_V3_WRPROT);

	/* Blocks to be unlocked */
	for (i = 0; i < NAND_MAX_CHIPS; i++)
		writel(0x0 |	(0xffff << 16),
				NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2));

	writel(0, NFC_V3_IPC);

	config2 = NFC_V3_CONFIG2_ONE_CYCLE |
		NFC_V3_CONFIG2_2CMD_PHASES |
		NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) |
		NFC_V3_CONFIG2_ST_CMD(0x70) |
		NFC_V3_CONFIG2_NUM_ADDR_PHASE0;

	if (chip->ecc.mode == NAND_ECC_HW)
		config2 |= NFC_V3_CONFIG2_ECC_EN;

	addr_phases = fls(chip->pagemask) >> 3;

	if (mtd->writesize == 2048) {
		config2 |= NFC_V3_CONFIG2_PS_2048;
		config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases);
	} else if (mtd->writesize == 4096) {
		config2 |= NFC_V3_CONFIG2_PS_4096;
		config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases);
	} else {
		config2 |= NFC_V3_CONFIG2_PS_512;
		config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases - 1);
	}

	if (mtd->writesize) {
		config2 |= NFC_V3_CONFIG2_PPB(ffs(mtd->erasesize / mtd->writesize) - 6);
		host->eccsize = get_eccsize(mtd);
		if (host->eccsize == 8)
			config2 |= NFC_V3_CONFIG2_ECC_MODE_8;
	}

	writel(config2, NFC_V3_CONFIG2);

	config3 = NFC_V3_CONFIG3_NUM_OF_DEVICES(0) |
			NFC_V3_CONFIG3_NO_SDMA |
			NFC_V3_CONFIG3_RBB_MODE |
			NFC_V3_CONFIG3_SBB(6) | /* Reset default */
			NFC_V3_CONFIG3_ADD_OP(0);

	if (!(chip->options & NAND_BUSWIDTH_16))
		config3 |= NFC_V3_CONFIG3_FW8;

	writel(config3, NFC_V3_CONFIG3);

	writel(0, NFC_V3_DELAY_LINE);
}

/* Used by the upper layer to write command to NAND Flash for
/* Used by the upper layer to write command to NAND Flash for
 * different operations to be carried out on NAND Flash */
 * different operations to be carried out on NAND Flash */
static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
@@ -843,6 +1035,30 @@ static int __init mxcnd_probe(struct platform_device *pdev)
		oob_smallpage = &nandv1_hw_eccoob_smallpage;
		oob_smallpage = &nandv1_hw_eccoob_smallpage;
		oob_largepage = &nandv1_hw_eccoob_largepage;
		oob_largepage = &nandv1_hw_eccoob_largepage;
		this->ecc.bytes = 3;
		this->ecc.bytes = 3;
		host->eccsize = 1;
	} else if (nfc_is_v3_2()) {
		res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
		if (!res) {
			err = -ENODEV;
			goto eirq;
		}
		host->regs_ip = ioremap(res->start, resource_size(res));
		if (!host->regs_ip) {
			err = -ENOMEM;
			goto eirq;
		}
		host->regs_axi = host->base + 0x1e00;
		host->spare0 = host->base + 0x1000;
		host->spare_len = 64;
		host->preset = preset_v3;
		host->send_cmd = send_cmd_v3;
		host->send_addr = send_addr_v3;
		host->send_page = send_page_v3;
		host->send_read_id = send_read_id_v3;
		host->check_int = check_int_v3;
		host->get_dev_status = get_dev_status_v3;
		oob_smallpage = &nandv2_hw_eccoob_smallpage;
		oob_largepage = &nandv2_hw_eccoob_largepage;
	} else
	} else
		BUG();
		BUG();


@@ -918,6 +1134,8 @@ static int __init mxcnd_probe(struct platform_device *pdev)
escan:
escan:
	free_irq(host->irq, host);
	free_irq(host->irq, host);
eirq:
eirq:
	if (host->regs_ip)
		iounmap(host->regs_ip);
	iounmap(host->base);
	iounmap(host->base);
eres:
eres:
	clk_put(host->clk);
	clk_put(host->clk);
@@ -937,6 +1155,8 @@ static int __devexit mxcnd_remove(struct platform_device *pdev)


	nand_release(&host->mtd);
	nand_release(&host->mtd);
	free_irq(host->irq, host);
	free_irq(host->irq, host);
	if (host->regs_ip)
		iounmap(host->regs_ip);
	iounmap(host->base);
	iounmap(host->base);
	kfree(host);
	kfree(host);