/*-
 * Copyright 2013 Broadcom Corporation
 *
 * Copyright (c) 2009-2010 Micron Technology, Inc.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * 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.
 *
 * Henry Pan <hspan@micron.com>
 *
 * based on nand.h
 */
#ifndef __LINUX_MTD_SPI_NAND_H
#define __LINUX_MTD_SPI_NAND_H

#include <linux/mtd/mtd.h>

#ifdef CONFIG_BOARD_RTS3915
#define RTS_SPINFC_BASEADDR 0xb8038000
#endif

#ifdef CONFIG_BOARD_RTS3903
#define RTS_SPINFC_BASEADDR 0xb8030000
#endif

/* cmd */
#define CMD_READ			0x13
#define CMD_READ_RDM			0x03
#define CMD_PROG_PAGE_CLRCACHE		0x02
#define CMD_PROG_PAGE			0x84
#define CMD_PROG_PAGE_EXC		0x10
#define CMD_ERASE_BLK			0xd8
#define CMD_WR_ENABLE			0x06
#define CMD_WR_DISABLE			0x04
#define CMD_READ_ID			0x9f
#define CMD_RESET			0xff
#define CMD_READ_REG			0x0f
#define CMD_WRITE_REG			0x1f
#define CMD_SEL_SOFTWAREDIE	0xc2

/* feature/ status reg */
#define REG_BLOCK_LOCK			0xa0
#define REG_OTP				0xb0
#define REG_STATUS			0xc0/* timing */
#define REG_ECC				0x90/* only for xt26g02b*/

/* status */
#define STATUS_OIP_MASK			0x01
#define STATUS_READY			(0 << 0)
#define STATUS_BUSY			(1 << 0)

#define STATUS_E_FAIL_MASK		0x04
#define STATUS_E_FAIL			(1 << 2)

#define STATUS_P_FAIL_MASK		0x08
#define STATUS_P_FAIL			(1 << 3)

#define STATUS_ECC_MASK			0x30
#define STATUS_ECC_1BIT_CORRECTED	(1 << 4)
#define STATUS_ECC_ERROR		(2 << 4)
#define STATUS_ECC_RESERVED		(3 << 4)

/*ECC enable defines*/
#define OTP_ECC_MASK			0x10
#define OTP_ECC_OFF			0
#define OTP_ECC_ON			1

#define ECC_DISABLED
#define ECC_IN_NAND
#define ECC_SOFT

/* block lock */
#define BL_ALL_LOCKED      0x38
#define BL_1_2_LOCKED      0x30
#define BL_1_4_LOCKED      0x28
#define BL_1_8_LOCKED      0x20
#define BL_1_16_LOCKED     0x18
#define BL_1_32_LOCKED     0x10
#define BL_1_64_LOCKED     0x08
#define BL_ALL_UNLOCKED    0

#define SPI_NAND_PAGESIZE	0x800
#define SPI_NAND_OOBSIZE	0x40
#define SPI_NAND_COMMAND_BUFFER_SIZE	0x20

#define CMD_TYPE_1CH_RX	0x01
#define CMD_TYPE_1CH_TX	0x02
#define CMD_TYPE_READ		0x04
#define CMD_TYPE_WRITE		0x08

#define DATA_SINGLE_CH		0x00
#define DATA_DUAL_CH		0x40
#define DATA_QUAD_CH		0x80
#define ADDR_SINGLE_CH		0x00
#define ADDR_DUAL_CH		0x10
#define ADDR_QUAD_CH		0x20
#define TRANSMIT_MODE		0x00
#define RECEIVE_MODE		0x0c

#define WR_QUAD_II_T				0x04
#define WR_QUAD_I_T				0x03
#define WR_DUAL_II_T				0x02
#define WR_DUAL_I_T				0x01
#define WR_MULTI_NONE_T			0x00

#define RD_QUAD_IO_T				0x10
#define RD_QUAD_O_T				0x08
#define RD_DUAL_IO_T				0x04
#define RD_DUAL_O_T				0x02
#define FRD_SINGLE_T				0x01

#define ERROR_THRESHOLD			0x06
#define WR_ECC_EN					0x01
#define WR_ECC_DIS					0
#define ECC_STATUS_CLEAR			0x02
#define ECC_THRESHOLD_MASK		0x3c
#define RD_ECC_EN					0x01
#define RD_ECC_DIS					0
#define RD_ECC_SUCCESS				0
#define RD_ECC_FAIL					1

#define PLANE_WITH_2				1
#define PLANE_WITH_1				0
#define OTP_TYPE_1					0x2
#define OTP_TYPE_2					0x4
#define SW_DIE_WITH2				0x8

#define AUTO_GETFEAT_TIMEOUT		0x10
#define AUTO_GETFEAT_IDLE			0x08
#define ECC_ERROR					0x04
#define SPIC_BUSY					0x01
#define SPIC_READY					0x00

#ifdef CONFIG_BOARD_RTS3915
#define DISABLE_INTERRUPT	0x1f
#define RTS_SPINAND_PAX			0xb803808c
#define RTS_SPINAND_LAX			0xb8038090
#define RTS_SPINAND_READ_CTL		0xb80380c8
#endif

#ifdef CONFIG_BOARD_RTS3903
#define RTS_SPINAND_PAX			0xb803008c
#define RTS_SPINAND_LAX			0xb8030090
#define RTS_SPINAND_READ_CTL		0xb80300c8
#endif

#define AUTO_READ_ADDR1_DATA1	0x00
#define AUTO_READ_ADDR1_DATA2	0x02
#define AUTO_READ_ADDR1_DATA4	0x04

struct spinand_info {
	struct nand_ecclayout *ecclayout;
	struct spi_device *spi;
	void *priv;
};

struct spinand_state {
	uint32_t	col;
	uint32_t	row;
	int		buf_ptr;
	u8		*buf;
};

struct spinand_cmd {
	u8		cmd;
	u32		n_addr;		/* Number of address */
	u8		addr[3];	/* Reg Offset */
	u32		n_dummy;	/* Dummy use */
	u32		n_tx;		/* Number of tx bytes */
	u8		*tx_buf;	/* Tx buf */
	u32		n_rx;		/* Number of rx bytes */
	u8		*rx_buf;	/* Rx buf */
	u8		cmd_type;
};


/***** spi_flash.api/portmap
 * DESCRIPTION
 *  This is the structure used for accessing the spi_flash register
 *  portmap.
 * EXAMPLE
 *  struct spi_flash_portmap *portmap;
 *  portmap = (struct spi_nandflash_portmap *) spi_flash_BASE;
 *  foo = INP(portmap->ctrlr0 );
 * SOURCE
 */
struct spi_nandflash_portmap {
/*                                                      */
	struct {
		volatile uint32_t ctrlr0;    /* Control Reg 0           (0x000) */
		volatile uint32_t ssienr;    /* SPIC enable Reg1         */
		volatile uint32_t baudr;		/*baud rate 			(0x008)*/
		volatile uint32_t ureset;    /* reset controller  */
		volatile uint32_t ucmd;	    /*spi nand flash command	(0x010)*/
		volatile uint32_t uaddr;     /* user read/write address	 */
		volatile uint32_t addr_len; /* user read/write address length(0x018) */
		volatile uint32_t data_len;        /* user read/write data length  */
		volatile uint32_t page_read_addr_len; /* auto read page read address length(0x020)*/
		volatile uint32_t read_dummy_len; /*read dummy length   */
		volatile uint32_t program_dummy_len;       /* program dummy length     */
		volatile uint32_t getfeature_dummy_len;       /* get features dummy length     */		
		volatile uint32_t dummy0[4];		/*0x030, 0x034, 0x038, 0x03c*/
		volatile uint32_t read_page_cmd;    /* read page command in auto mode (0x040) */
		volatile uint32_t random_read_cmd; /*random read command in auto mode */
		volatile uint32_t get_features_cmd;    /* get features command in auto mode (0x048) */
		volatile uint32_t get_features_addr;	   /* get features adderss in auto mode*/
		volatile uint32_t get_features_stop_value;       /* get features stop value     (0x050) */
		volatile uint32_t get_features_stop_mask;       /* get features stop mask */
		volatile uint32_t get_features_value;   /* get feature command return value       (0x058) */
		volatile uint32_t auto_get_features_ctrl;			/*(0x05c)*/
		volatile uint32_t auto_read_type;	    /*auto read type*/
		volatile uint32_t plane_ctrl;       /* plane setting */
		volatile uint32_t sr;			/*status register (0x068)*/
		volatile uint32_t isr;    /* interrupt status register */
		volatile uint32_t imr;		/* interrupt mask register(0x070)*/
		volatile uint32_t isr_cr;    /* interrupt clear register  */
		volatile uint32_t dummy2[2];			/*(0x078), (0x07c)*/
		volatile uint32_t la_status;	    /*logic address status register (0x080)*/
		volatile uint32_t pa0;     /* physical address of logic block0 */
		volatile uint32_t pa1;    /* physical address of logic block1 (0x088) */
		volatile uint32_t pax;        /* physical address of logic blockx  */
		volatile uint32_t lax; 		/*logic address of logic blockx (0x090)*/
		volatile uint32_t current_page;	/*It is page offset in block which is in cache*/
		volatile uint32_t dummy3[2];	/*0x098, 0x09c*/
		volatile uint32_t main_state;       /* main state (0x0a0)*/
		volatile uint32_t la_find_state;  /*logic find state */
		volatile uint32_t auto_read_state;	/*auto read state(0x0a8)*/
		volatile uint32_t protocol_state;       /* protocal state */
		volatile uint32_t gp_state;			/*gp state (0xb0)*/
		volatile uint32_t dummy4[3]; /*0x0b4, 0x0b8, 0x0bc*/
		volatile uint32_t ecc_write_ctrl;    /* ecc write control register (0x0c0)*/
		volatile uint32_t dummy5;
		volatile uint32_t ecc_read_ctrl;    /* ecc read control register (0x0c8)*/
		volatile uint32_t ecc_read_status;    /* ecc read statue register*/
		volatile uint32_t dummy6[8];			/*0x0d0, 0x0d4, 0x0d8, 0x0dc, 0x0e0, 0x0e4, 0x0e8, 0x0ec*/
		volatile uint32_t debug_addr0;    /* debug address0 (0x0f0) */
		volatile uint32_t debug_addr1;        /* debug address1  */
		volatile uint32_t dummy7[2];			/*0x0f8 , 0x0fc*/
		volatile uint32_t da;				/*data address 0x100*/
		volatile uint32_t dv;				/*data value */
		volatile uint32_t dummy8[958];		/*0x108~0xffc*/
		volatile uint32_t data_fifo[528];		/*0x1000~0x1840*/
	};
};

struct spinand_slave {
	unsigned int max_write_size;
	unsigned short flags;
	unsigned char read_cmd;
	unsigned char read_type;
	unsigned char read_dummy;
	unsigned char write_cmd;
	unsigned char write_type;
	unsigned char write_dummy;
};

struct rts_spinand_info {
	int		cur_chip;
	uint32_t	data_buf_size;

	uint8_t		marking_block_bad;
	uint8_t		raw_oob_mode;

	/* Functions with altered behaviour */
	int		(*hooked_read_oob)(struct mtd_info *mtd,
				loff_t from, struct mtd_oob_ops *ops);
	int		(*hooked_write_oob)(struct mtd_info *mtd,
				loff_t to, struct mtd_oob_ops *ops);
	int		(*hooked_block_markbad)(struct mtd_info *mtd,
				loff_t ofs);

	/* spi nand flash controller  */
	struct spi_nandflash_portmap	*base_addr;
	struct spinand_state	*spinand_state;
	struct spinand_slave spi_dev;

	/*logic address when encounter bad block*/
	uint32_t logic_ad;
};


static inline struct spi_nandflash_portmap *get_rts_spinfc_base(void)
{
	return (struct spi_nandflash_portmap *)RTS_SPINFC_BASEADDR;
}

struct spinand_flash_dev {
	char *name;
	int id;
	unsigned long pagesize;
	unsigned long chipsize;
	unsigned long erasesize;
	unsigned long options;
	unsigned long flags;
	unsigned char read_cmd;
	unsigned char read_type;
	unsigned char read_dummy;
	unsigned char write_cmd;
	unsigned char write_type;
	unsigned char write_dummy;
};

static const struct spinand_flash_dev spinand_flash_params_table[] = {
	{"MX35LF1GE4AB",		0xC212C2, 0x800, 0x80,   0x20000, 0, PLANE_WITH_1|OTP_TYPE_1, \
						0x03,  ADDR_SINGLE_CH|DATA_SINGLE_CH, 0x8, 0x02,  ADDR_SINGLE_CH|DATA_SINGLE_CH, 0},

	{"MX35LF2GE4AB",		0xC222C2, 0x800, 0x100,  0x20000, 0, PLANE_WITH_2|OTP_TYPE_1, \
						0x6B,  ADDR_SINGLE_CH|DATA_QUAD_CH, 0x8, 0x02,  ADDR_SINGLE_CH|DATA_SINGLE_CH, 0},

	{"W25N01GVZE1G",	0xEFAA21, 0x800, 0x80,  0x20000, 0, PLANE_WITH_1|OTP_TYPE_2, \
						0x6B,  ADDR_SINGLE_CH|DATA_QUAD_CH, 0x8, 0x32,  ADDR_SINGLE_CH|DATA_QUAD_CH, 0},

	{"W25M02GVZE1G",	0xEFAB21, 0x800, 0x100,  0x20000, 0, OTP_TYPE_2|SW_DIE_WITH2, \
						0x6B,  ADDR_SINGLE_CH|DATA_QUAD_CH, 0x8, 0x02,  ADDR_SINGLE_CH|DATA_SINGLE_CH, 0},
};


extern int spinand_mtd(struct mtd_info *mtd);
extern void spinand_mtd_release(struct mtd_info *mtd);

#endif /* __LINUX_MTD_SPI_NAND_H */

