1
2/* Common Flash Interface structures
3 * See http://support.intel.com/design/flash/technote/index.htm
4 * $Id: cfi.h,v 1.1.1.1 2008/10/15 03:27:31 james26_jang Exp $
5 */
6
7#ifndef __MTD_CFI_H__
8#define __MTD_CFI_H__
9
10#include <linux/config.h>
11#include <linux/delay.h>
12#include <linux/types.h>
13#include <linux/interrupt.h>
14#include <linux/mtd/flashchip.h>
15#include <linux/mtd/cfi_endian.h>
16
17/*
18 * You can optimize the code size and performance by defining only
19 * the geometry(ies) available on your hardware.
20 * CFIDEV_INTERLEAVE_n, where  represents the interleave (number of chips to fill the bus width)
21 * CFIDEV_BUSWIDTH_n, where n is the bus width in bytes (1, 2 or 4 bytes)
22 *
23 * By default, all (known) geometries are supported.
24 */
25
26#ifndef CONFIG_MTD_CFI_GEOMETRY
27
28#define CFIDEV_INTERLEAVE_1 (1)
29#define CFIDEV_INTERLEAVE_2 (2)
30#define CFIDEV_INTERLEAVE_4 (4)
31
32#define CFIDEV_BUSWIDTH_1 (1)
33#define CFIDEV_BUSWIDTH_2 (2)
34#define CFIDEV_BUSWIDTH_4 (4)
35
36#else
37
38#ifdef CONFIG_MTD_CFI_I1
39#define CFIDEV_INTERLEAVE_1 (1)
40#endif
41#ifdef CONFIG_MTD_CFI_I2
42#define CFIDEV_INTERLEAVE_2 (2)
43#endif
44#ifdef CONFIG_MTD_CFI_I4
45#define CFIDEV_INTERLEAVE_4 (4)
46#endif
47
48#ifdef CONFIG_MTD_CFI_B1
49#define CFIDEV_BUSWIDTH_1 (1)
50#endif
51#ifdef CONFIG_MTD_CFI_B2
52#define CFIDEV_BUSWIDTH_2 (2)
53#endif
54#ifdef CONFIG_MTD_CFI_B4
55#define CFIDEV_BUSWIDTH_4 (4)
56#endif
57
58#endif
59
60/*
61 * The following macros are used to select the code to execute:
62 *   cfi_buswidth_is_*()
63 *   cfi_interleave_is_*()
64 *   [where * is either 1, 2 or 4]
65 * Those macros should be used with 'if' statements.  If only one of few
66 * geometry arrangements are selected, they expand to constants thus allowing
67 * the compiler (most of them being 0) to optimize away all the unneeded code,
68 * while still validating the syntax (which is not possible with embedded
69 * #if ... #endif constructs).
70 */
71
72#ifdef CFIDEV_INTERLEAVE_1
73# ifdef CFIDEV_INTERLEAVE
74#  undef CFIDEV_INTERLEAVE
75#  define CFIDEV_INTERLEAVE (cfi->interleave)
76# else
77#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_1
78# endif
79# define cfi_interleave_is_1() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_1)
80#else
81# define cfi_interleave_is_1() (0)
82#endif
83
84#ifdef CFIDEV_INTERLEAVE_2
85# ifdef CFIDEV_INTERLEAVE
86#  undef CFIDEV_INTERLEAVE
87#  define CFIDEV_INTERLEAVE (cfi->interleave)
88# else
89#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_2
90# endif
91# define cfi_interleave_is_2() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_2)
92#else
93# define cfi_interleave_is_2() (0)
94#endif
95
96#ifdef CFIDEV_INTERLEAVE_4
97# ifdef CFIDEV_INTERLEAVE
98#  undef CFIDEV_INTERLEAVE
99#  define CFIDEV_INTERLEAVE (cfi->interleave)
100# else
101#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_4
102# endif
103# define cfi_interleave_is_4() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_4)
104#else
105# define cfi_interleave_is_4() (0)
106#endif
107
108#ifndef CFIDEV_INTERLEAVE
109#error You must define at least one interleave to support!
110#endif
111
112#ifdef CFIDEV_BUSWIDTH_1
113# ifdef CFIDEV_BUSWIDTH
114#  undef CFIDEV_BUSWIDTH
115#  define CFIDEV_BUSWIDTH (map->buswidth)
116# else
117#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_1
118# endif
119# define cfi_buswidth_is_1() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_1)
120#else
121# define cfi_buswidth_is_1() (0)
122#endif
123
124#ifdef CFIDEV_BUSWIDTH_2
125# ifdef CFIDEV_BUSWIDTH
126#  undef CFIDEV_BUSWIDTH
127#  define CFIDEV_BUSWIDTH (map->buswidth)
128# else
129#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_2
130# endif
131# define cfi_buswidth_is_2() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_2)
132#else
133# define cfi_buswidth_is_2() (0)
134#endif
135
136#ifdef CFIDEV_BUSWIDTH_4
137# ifdef CFIDEV_BUSWIDTH
138#  undef CFIDEV_BUSWIDTH
139#  define CFIDEV_BUSWIDTH (map->buswidth)
140# else
141#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_4
142# endif
143# define cfi_buswidth_is_4() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_4)
144#else
145# define cfi_buswidth_is_4() (0)
146#endif
147
148#ifndef CFIDEV_BUSWIDTH
149#error You must define at least one bus width to support!
150#endif
151
152/* NB: these values must represents the number of bytes needed to meet the
153 *     device type (x8, x16, x32).  Eg. a 32 bit device is 4 x 8 bytes.
154 *     These numbers are used in calculations.
155 */
156#define CFI_DEVICETYPE_X8  (8 / 8)
157#define CFI_DEVICETYPE_X16 (16 / 8)
158#define CFI_DEVICETYPE_X32 (32 / 8)
159
160/* NB: We keep these structures in memory in HOST byteorder, except
161 * where individually noted.
162 */
163
164/* Basic Query Structure */
165struct cfi_ident {
166  __u8  qry[3];
167  __u16 P_ID;
168  __u16 P_ADR;
169  __u16 A_ID;
170  __u16 A_ADR;
171  __u8  VccMin;
172  __u8  VccMax;
173  __u8  VppMin;
174  __u8  VppMax;
175  __u8  WordWriteTimeoutTyp;
176  __u8  BufWriteTimeoutTyp;
177  __u8  BlockEraseTimeoutTyp;
178  __u8  ChipEraseTimeoutTyp;
179  __u8  WordWriteTimeoutMax;
180  __u8  BufWriteTimeoutMax;
181  __u8  BlockEraseTimeoutMax;
182  __u8  ChipEraseTimeoutMax;
183  __u8  DevSize;
184  __u16 InterfaceDesc;
185  __u16 MaxBufWriteSize;
186  __u8  NumEraseRegions;
187  __u32 EraseRegionInfo[0]; /* Not host ordered */
188} __attribute__((packed));
189
190/* Extended Query Structure for both PRI and ALT */
191
192struct cfi_extquery {
193  __u8  pri[3];
194  __u8  MajorVersion;
195  __u8  MinorVersion;
196} __attribute__((packed));
197
198/* Vendor-Specific PRI for Intel/Sharp Extended Command Set (0x0001) */
199
200struct cfi_pri_intelext {
201  __u8  pri[3];
202  __u8  MajorVersion;
203  __u8  MinorVersion;
204  __u32 FeatureSupport;
205  __u8  SuspendCmdSupport;
206  __u16 BlkStatusRegMask;
207  __u8  VccOptimal;
208  __u8  VppOptimal;
209} __attribute__((packed));
210
211struct cfi_pri_query {
212  __u8  NumFields;
213  __u32 ProtField[1]; /* Not host ordered */
214} __attribute__((packed));
215
216struct cfi_bri_query {
217  __u8  PageModeReadCap;
218  __u8  NumFields;
219  __u32 ConfField[1]; /* Not host ordered */
220} __attribute__((packed));
221
222#define P_ID_NONE 0
223#define P_ID_INTEL_EXT 1
224#define P_ID_AMD_STD 2
225#define P_ID_INTEL_STD 3
226#define P_ID_AMD_EXT 4
227#define P_ID_MITSUBISHI_STD 256
228#define P_ID_MITSUBISHI_EXT 257
229#define P_ID_RESERVED 65535
230
231
232#define CFI_MODE_CFI	0
233#define CFI_MODE_JEDEC	1
234
235struct cfi_private {
236	__u16 cmdset;
237	void *cmdset_priv;
238	int interleave;
239	int device_type;
240	int cfi_mode;		/* Are we a JEDEC device pretending to be CFI? */
241	int addr_unlock1;
242	int addr_unlock2;
243	int fast_prog;
244	struct mtd_info *(*cmdset_setup)(struct map_info *);
245	struct cfi_ident *cfiq; /* For now only one. We insist that all devs
246				  must be of the same type. */
247	int mfr, id;
248	int numchips;
249	unsigned long chipshift; /* Because they're of the same type */
250	const char *im_name;	 /* inter_module name for cmdset_setup */
251	struct flchip chips[0];  /* per-chip data structure for each chip */
252};
253
254#define MAX_CFI_CHIPS 8 /* Entirely arbitrary to avoid realloc() */
255
256/*
257 * Returns the command address according to the given geometry.
258 */
259static inline __u32 cfi_build_cmd_addr(__u32 cmd_ofs, int interleave, int type)
260{
261	return (cmd_ofs * type) * interleave;
262}
263
264/*
265 * Transforms the CFI command for the given geometry (bus width & interleave.
266 */
267static inline __u32 cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi)
268{
269	__u32 val = 0;
270
271	if (cfi_buswidth_is_1()) {
272		/* 1 x8 device */
273		val = cmd;
274	} else if (cfi_buswidth_is_2()) {
275		if (cfi_interleave_is_1()) {
276			/* 1 x16 device in x16 mode */
277			val = cpu_to_cfi16(cmd);
278		} else if (cfi_interleave_is_2()) {
279			/* 2 (x8, x16 or x32) devices in x8 mode */
280			val = cpu_to_cfi16((cmd << 8) | cmd);
281		}
282	} else if (cfi_buswidth_is_4()) {
283		if (cfi_interleave_is_1()) {
284			/* 1 x32 device in x32 mode */
285			val = cpu_to_cfi32(cmd);
286		} else if (cfi_interleave_is_2()) {
287			/* 2 x16 device in x16 mode */
288			val = cpu_to_cfi32((cmd << 16) | cmd);
289		} else if (cfi_interleave_is_4()) {
290			/* 4 (x8, x16 or x32) devices in x8 mode */
291			val = (cmd << 16) | cmd;
292			val = cpu_to_cfi32((val << 8) | val);
293		}
294	}
295	return val;
296}
297#define CMD(x)  cfi_build_cmd((x), map, cfi)
298
299/*
300 * Read a value according to the bus width.
301 */
302
303static inline __u32 cfi_read(struct map_info *map, __u32 addr)
304{
305	if (cfi_buswidth_is_1()) {
306		return map->read8(map, addr);
307	} else if (cfi_buswidth_is_2()) {
308		return map->read16(map, addr);
309	} else if (cfi_buswidth_is_4()) {
310		return map->read32(map, addr);
311	} else {
312		return 0;
313	}
314}
315
316/*
317 * Write a value according to the bus width.
318 */
319
320static inline void cfi_write(struct map_info *map, __u32 val, __u32 addr)
321{
322	if (cfi_buswidth_is_1()) {
323		map->write8(map, val, addr);
324	} else if (cfi_buswidth_is_2()) {
325		map->write16(map, val, addr);
326	} else if (cfi_buswidth_is_4()) {
327		map->write32(map, val, addr);
328	}
329}
330
331/*
332 * Sends a CFI command to a bank of flash for the given geometry.
333 *
334 * Returns the offset in flash where the command was written.
335 * If prev_val is non-null, it will be set to the value at the command address,
336 * before the command was written.
337 */
338static inline __u32 cfi_send_gen_cmd(u_char cmd, __u32 cmd_addr, __u32 base,
339				struct map_info *map, struct cfi_private *cfi,
340				int type, __u32 *prev_val)
341{
342	__u32 val;
343	__u32 addr = base + cfi_build_cmd_addr(cmd_addr, CFIDEV_INTERLEAVE, type);
344
345	val = cfi_build_cmd(cmd, map, cfi);
346
347	if (prev_val)
348		*prev_val = cfi_read(map, addr);
349
350	cfi_write(map, val, addr);
351
352	return addr - base;
353}
354
355static inline __u8 cfi_read_query(struct map_info *map, __u32 addr)
356{
357	if (cfi_buswidth_is_1()) {
358		return map->read8(map, addr);
359	} else if (cfi_buswidth_is_2()) {
360		return cfi16_to_cpu(map->read16(map, addr));
361	} else if (cfi_buswidth_is_4()) {
362		return cfi32_to_cpu(map->read32(map, addr));
363	} else {
364		return 0;
365	}
366}
367
368static inline void cfi_udelay(int us)
369{
370#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
371	if (current->need_resched) {
372		unsigned long t = us * HZ / 1000000;
373		if (t < 1)
374			t = 1;
375		set_current_state(TASK_UNINTERRUPTIBLE);
376		schedule_timeout(t);
377	}
378	else
379#endif
380		udelay(us);
381}
382static inline void cfi_spin_lock(spinlock_t *mutex)
383{
384	spin_lock_bh(mutex);
385}
386
387static inline void cfi_spin_unlock(spinlock_t *mutex)
388{
389	spin_unlock_bh(mutex);
390}
391
392
393#endif /* __MTD_CFI_H__ */
394