1/* $NetBSD: i28f128.c,v 1.2.2.3 2004/09/21 13:16:12 skrll Exp $ */
2
3/*
4 * Copyright (c) 2003 Naoto Shimazaki.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY NAOTO SHIMAZAKI AND CONTRIBUTORS ``AS IS''
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE NAOTO OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26 * THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * Flash Memory Writer
31 */
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: i28f128.c,v 1.2.2.3 2004/09/21 13:16:12 skrll Exp $");
34
35#include <lib/libsa/stand.h>
36
37#include "extern.h"
38
39#include "i28f128reg.h"
40
41#define USE_TWIDDLE
42
43/*
44 * XXX
45 * this function is too much specific for the device.
46 */
47int
48i28f128_probe(void *base)
49{
50	static const u_int8_t	vendor_code[] = {
51		0x89,	/* manufacturer code: 	intel */
52		0x18,	/* device code:		28F128 */
53	};
54
55	static const u_int8_t	idstr[] = {
56		'Q', 'R', 'Y',
57		0x01, 0x00,
58		0x31, 0x00,
59		0xff
60	};
61
62	int	i;
63
64	/* start Common Flash Interface Query */
65	REGWRITE_2(base, 0, 0x98);
66
67	/* read CFI Query ID string */
68	for (i = 0; idstr[i] != 0xff; i++) {
69		if (REGREAD_2(base, (0x10 + i) << 1) != idstr[i])
70			return 1;
71	}
72
73	/* read manufacturer code and device code */
74	if (REGREAD_2(base, 0x00) != vendor_code[0])
75		return 1;
76	if (REGREAD_2(base, 0x02) != vendor_code[1])
77		return 1;
78
79	REGWRITE_2(base, 0, 0xff);
80	return 0;
81}
82
83static int
84block_erase(void *addr)
85{
86	int	status;
87
88	REGWRITE_2(addr, 0, I28F128_BLK_ERASE_1ST);
89	REGWRITE_2(addr, 0, I28F128_BLK_ERASE_2ND);
90
91	do {
92		status = REGREAD_2(addr, 0);
93	} while (!ISSET(status, I28F128_S_READY));
94
95	REGWRITE_2(addr, 0, I28F128_CLEAR_STATUS);
96	REGWRITE_2(addr, 0, I28F128_RESET);
97
98	return status & (I28F128_S_ERASE_SUSPEND
99			 | I28F128_S_ERASE_ERROR
100			 | I28F128_S_BLOCK_LOCKED);
101}
102
103static int
104word_program(void *addr, u_int16_t data)
105{
106	int	status;
107
108	REGWRITE_2(addr, 0, I28F128_WORDBYTE_PROG);
109	REGWRITE_2(addr, 0, data);
110
111	do {
112		status = REGREAD_2(addr, 0);
113	} while (!ISSET(status, I28F128_S_READY));
114
115	REGWRITE_2(addr, 0, I28F128_CLEAR_STATUS);
116	REGWRITE_2(addr, 0, I28F128_RESET);
117
118	return status & (I28F128_S_PROG_ERROR
119			 | I28F128_S_LOW_VOLTAGE
120			 | I28F128_S_PROG_SUSPEND
121			 | I28F128_S_BLOCK_LOCKED);
122}
123
124static int
125block_write(void *dst, const void *src)
126{
127	int		status;
128	const u_int16_t	*p;
129	u_int16_t	*q;
130	const u_int16_t	*fence;
131	int		i;
132	const int	wbuf_count = I28F128_WBUF_SIZE >> 1;
133
134	/* dst must be aligned to block boundary. */
135	if (I28F128_BLOCK_MASK & (u_int32_t) dst)
136		return -1;
137
138	if (memcmp(dst, src, I28F128_BLOCK_SIZE) == 0)
139		return 0;
140
141	if ((status = block_erase(dst)) != 0)
142		return status;
143
144	p = src;
145	q = dst;
146	fence = p + (I28F128_BLOCK_SIZE >> 1);
147	do {
148		do {
149			REGWRITE_2(dst, 0, I28F128_WRITE_BUFFER);
150			status = REGREAD_2(dst, 0);
151		} while (!ISSET(status, I28F128_XS_BUF_AVAIL));
152
153		REGWRITE_2(dst, 0, wbuf_count - 1);
154
155		for (i = wbuf_count; i > 0; i--, p++, q++)
156			REGWRITE_2(q, 0, *p);
157
158		REGWRITE_2(dst, 0, I28F128_WBUF_CONFIRM);
159
160		do {
161			REGWRITE_2(dst, 0, I28F128_READ_STATUS);
162			status = REGREAD_2(dst, 0);
163		} while (!(status & I28F128_S_READY));
164
165	} while (p < fence);
166
167	REGWRITE_2(dst, 0, I28F128_CLEAR_STATUS);
168	REGWRITE_2(dst, 0, I28F128_RESET);
169
170	return 0;
171}
172
173int
174i28f128_region_write(void *dst, const void *src, size_t len)
175{
176	int		status;
177	const u_int16_t	*p = src;
178	u_int16_t	*q = dst;
179
180	/* dst must be aligned to block boundary. */
181	if (I28F128_BLOCK_MASK & (u_int32_t) dst)
182		return -1;
183
184	while (len >= I28F128_BLOCK_SIZE) {
185		if ((status = block_write(q, p)) != 0)
186			return status;
187		putchar('b');
188		p += I28F128_BLOCK_SIZE >> 1;
189		q += I28F128_BLOCK_SIZE >> 1;
190		len -= I28F128_BLOCK_SIZE;
191	}
192
193	if (len > 0) {
194		if (memcmp(p, q, len) == 0)
195			return 0;
196		if ((status = block_erase(q)) != 0)
197			return status;
198		for (; len > 0; len -= 2) {
199#ifdef USE_TWIDDLE
200			if (((u_int32_t) q % 4096) == 0)
201				twiddle();
202#endif
203			if ((status = word_program(q++, *p++)) != 0)
204				return status;
205		}
206		printf("w");
207	}
208
209	putchar('\n');
210	return 0;
211}
212