1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2008
4 * Sergei Poselenov, Emcraft Systems, sposelenov@emcraft.com.
5 */
6
7#include <config.h>
8
9#if defined(CFG_SYS_NAND_BASE)
10#include <nand.h>
11#include <linux/errno.h>
12#include <linux/mtd/rawnand.h>
13#include <asm/io.h>
14
15static int state;
16static void sc_nand_write_byte(struct mtd_info *mtd, u_char byte);
17static void sc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len);
18static u_char sc_nand_read_byte(struct mtd_info *mtd);
19static u16 sc_nand_read_word(struct mtd_info *mtd);
20static void sc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len);
21static int sc_nand_device_ready(struct mtd_info *mtdinfo);
22
23#define FPGA_NAND_CMD_MASK		(0x7 << 28)
24#define FPGA_NAND_CMD_COMMAND		(0x0 << 28)
25#define FPGA_NAND_CMD_ADDR		(0x1 << 28)
26#define FPGA_NAND_CMD_READ		(0x2 << 28)
27#define FPGA_NAND_CMD_WRITE		(0x3 << 28)
28#define FPGA_NAND_BUSY			(0x1 << 15)
29#define FPGA_NAND_ENABLE		(0x1 << 31)
30#define FPGA_NAND_DATA_SHIFT		16
31
32/**
33 * sc_nand_write_byte -  write one byte to the chip
34 * @mtd:	MTD device structure
35 * @byte:	pointer to data byte to write
36 */
37static void sc_nand_write_byte(struct mtd_info *mtd, u_char byte)
38{
39	sc_nand_write_buf(mtd, (const uchar *)&byte, sizeof(byte));
40}
41
42/**
43 * sc_nand_write_buf -  write buffer to chip
44 * @mtd:	MTD device structure
45 * @buf:	data buffer
46 * @len:	number of bytes to write
47 */
48static void sc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
49{
50	int i;
51	struct nand_chip *this = mtd_to_nand(mtd);
52
53	for (i = 0; i < len; i++) {
54		out_be32(this->IO_ADDR_W,
55			 state | (buf[i] << FPGA_NAND_DATA_SHIFT));
56	}
57}
58
59
60/**
61 * sc_nand_read_byte -  read one byte from the chip
62 * @mtd:	MTD device structure
63 */
64static u_char sc_nand_read_byte(struct mtd_info *mtd)
65{
66	u8 byte;
67	sc_nand_read_buf(mtd, (uchar *)&byte, sizeof(byte));
68	return byte;
69}
70
71/**
72 * sc_nand_read_word -  read one word from the chip
73 * @mtd:	MTD device structure
74 */
75static u16 sc_nand_read_word(struct mtd_info *mtd)
76{
77	u16 word;
78	sc_nand_read_buf(mtd, (uchar *)&word, sizeof(word));
79	return word;
80}
81
82/**
83 * sc_nand_read_buf -  read chip data into buffer
84 * @mtd:	MTD device structure
85 * @buf:	buffer to store date
86 * @len:	number of bytes to read
87 */
88static void sc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
89{
90	int i;
91	struct nand_chip *this = mtd_to_nand(mtd);
92	int val;
93
94	val = (state & FPGA_NAND_ENABLE) | FPGA_NAND_CMD_READ;
95
96	out_be32(this->IO_ADDR_W, val);
97	for (i = 0; i < len; i++) {
98		buf[i] = (in_be32(this->IO_ADDR_R) >> FPGA_NAND_DATA_SHIFT) & 0xff;
99	}
100}
101
102/**
103 * sc_nand_device_ready - Check the NAND device is ready for next command.
104 * @mtd:	MTD device structure
105 */
106static int sc_nand_device_ready(struct mtd_info *mtdinfo)
107{
108	struct nand_chip *this = mtd_to_nand(mtdinfo);
109
110	if (in_be32(this->IO_ADDR_W) & FPGA_NAND_BUSY)
111		return 0; /* busy */
112	return 1;
113}
114
115/**
116 * sc_nand_hwcontrol - NAND control functions wrapper.
117 * @mtd:	MTD device structure
118 * @cmd:	Command
119 */
120static void sc_nand_hwcontrol(struct mtd_info *mtdinfo, int cmd, unsigned int ctrl)
121{
122	if (ctrl & NAND_CTRL_CHANGE) {
123		state &= ~(FPGA_NAND_CMD_MASK | FPGA_NAND_ENABLE);
124
125		switch (ctrl & (NAND_ALE | NAND_CLE)) {
126		case 0:
127			state |= FPGA_NAND_CMD_WRITE;
128			break;
129
130		case NAND_ALE:
131			state |= FPGA_NAND_CMD_ADDR;
132			break;
133
134		case NAND_CLE:
135			state |= FPGA_NAND_CMD_COMMAND;
136			break;
137
138		default:
139			printf("%s: unknown ctrl %#x\n", __FUNCTION__, ctrl);
140		}
141
142		if (ctrl & NAND_NCE)
143			state |= FPGA_NAND_ENABLE;
144	}
145
146	if (cmd != NAND_CMD_NONE)
147		sc_nand_write_byte(mtdinfo, cmd);
148}
149
150int board_nand_init(struct nand_chip *nand)
151{
152	nand->cmd_ctrl = sc_nand_hwcontrol;
153	nand->ecc.mode = NAND_ECC_SOFT;
154	nand->dev_ready = sc_nand_device_ready;
155	nand->read_byte = sc_nand_read_byte;
156	nand->read_word = sc_nand_read_word;
157	nand->write_buf = sc_nand_write_buf;
158	nand->read_buf = sc_nand_read_buf;
159
160	return 0;
161}
162
163#endif
164