xref: /netbsd-src/sys/arch/powerpc/booke/dev/pq3nandfcm.c (revision baea4824597a9343770518acb29cfacdb5083dad)
1125ea6a8Smatt /*-
2125ea6a8Smatt  * Copyright (c) 2011 The NetBSD Foundation, Inc.
3125ea6a8Smatt  * All rights reserved.
4125ea6a8Smatt  *
5125ea6a8Smatt  * This code is derived from software contributed to The NetBSD Foundation
6125ea6a8Smatt  * by Matt Thomas of 3am Software Foundry.
7125ea6a8Smatt  *
8125ea6a8Smatt  * Redistribution and use in source and binary forms, with or without
9125ea6a8Smatt  * modification, are permitted provided that the following conditions
10125ea6a8Smatt  * are met:
11125ea6a8Smatt  * 1. Redistributions of source code must retain the above copyright
12125ea6a8Smatt  *    notice, this list of conditions and the following disclaimer.
13125ea6a8Smatt  * 2. Redistributions in binary form must reproduce the above copyright
14125ea6a8Smatt  *    notice, this list of conditions and the following disclaimer in the
15125ea6a8Smatt  *    documentation and/or other materials provided with the distribution.
16125ea6a8Smatt  *
17125ea6a8Smatt  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18125ea6a8Smatt  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19125ea6a8Smatt  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20125ea6a8Smatt  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21125ea6a8Smatt  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22125ea6a8Smatt  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23125ea6a8Smatt  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24125ea6a8Smatt  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25125ea6a8Smatt  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26125ea6a8Smatt  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27125ea6a8Smatt  * POSSIBILITY OF SUCH DAMAGE.
28125ea6a8Smatt  */
29125ea6a8Smatt 
30125ea6a8Smatt #define LBC_PRIVATE
31125ea6a8Smatt 
32125ea6a8Smatt #include <sys/cdefs.h>
33*baea4824Sriastradh __KERNEL_RCSID(0, "$NetBSD: pq3nandfcm.c,v 1.5 2023/05/10 00:08:07 riastradh Exp $");
34125ea6a8Smatt 
35125ea6a8Smatt #include <sys/param.h>
36125ea6a8Smatt #include <sys/systm.h>
37125ea6a8Smatt #include <sys/device.h>
38125ea6a8Smatt #include <sys/cpu.h>
39125ea6a8Smatt 
406e7a8e52Sdyoung #include <sys/bus.h>
41125ea6a8Smatt 
42125ea6a8Smatt #include <powerpc/booke/cpuvar.h>
43125ea6a8Smatt #include <powerpc/booke/e500reg.h>
44125ea6a8Smatt #include <powerpc/booke/obiovar.h>
45125ea6a8Smatt 
46125ea6a8Smatt #include <dev/nand/nand.h>
47125ea6a8Smatt #include <dev/nand/onfi.h>
48125ea6a8Smatt 
49125ea6a8Smatt static int  pq3nandfcm_match(device_t, cfdata_t, void *);
50125ea6a8Smatt static void pq3nandfcm_attach(device_t, device_t, void *);
51125ea6a8Smatt static int  pq3nandfcm_detach(device_t, int);
52125ea6a8Smatt 
53125ea6a8Smatt static void pq3nandfcm_select(device_t, bool);
54125ea6a8Smatt static void pq3nandfcm_command(device_t, uint8_t);
55125ea6a8Smatt static void pq3nandfcm_address(device_t, uint8_t);
56125ea6a8Smatt static void pq3nandfcm_busy(device_t);
57125ea6a8Smatt static void pq3nandfcm_read_byte(device_t, uint8_t *);
58125ea6a8Smatt static void pq3nandfcm_write_byte(device_t, uint8_t);
59125ea6a8Smatt static void pq3nandfcm_read_buf(device_t, void *, size_t);
60125ea6a8Smatt static void pq3nandfcm_write_buf(device_t, const void *, size_t);
61125ea6a8Smatt 
62125ea6a8Smatt struct pq3nandfcm_softc {
63125ea6a8Smatt 	device_t sc_dev;
64125ea6a8Smatt 	bus_space_tag_t sc_window_bst;
65125ea6a8Smatt 	bus_space_handle_t sc_window_bsh;
66125ea6a8Smatt 	bus_size_t sc_window_size;
67125ea6a8Smatt 
68125ea6a8Smatt 	struct nand_interface sc_nandif;
69125ea6a8Smatt 	device_t sc_nanddev;
70125ea6a8Smatt 
71125ea6a8Smatt 	struct pq3obio_softc *sc_obio;
72125ea6a8Smatt 	struct pq3lbc_softc *sc_lbc;
73125ea6a8Smatt 
74125ea6a8Smatt 	u_int	sc_cs;
75125ea6a8Smatt 
76125ea6a8Smatt };
77125ea6a8Smatt 
78125ea6a8Smatt CFATTACH_DECL_NEW(pq3nandfcm, sizeof(struct pq3nandfcm_softc),
79125ea6a8Smatt      pq3nandfcm_match, pq3nandfcm_attach, pq3nandfcm_detach, NULL);
80125ea6a8Smatt 
81125ea6a8Smatt int
pq3nandfcm_match(device_t parent,cfdata_t cf,void * aux)82125ea6a8Smatt pq3nandfcm_match(device_t parent, cfdata_t cf, void *aux)
83125ea6a8Smatt {
84125ea6a8Smatt 	struct generic_attach_args * const ga = aux;
85125ea6a8Smatt 	struct pq3obio_softc * const psc = device_private(parent);
86125ea6a8Smatt 	struct pq3lbc_softc * const lbc = &psc->sc_lbcs[ga->ga_cs];
87125ea6a8Smatt 
88125ea6a8Smatt 	if ((lbc->lbc_br & BR_V) == 0)
89125ea6a8Smatt 		return 0;
90125ea6a8Smatt 
91125ea6a8Smatt 	if (__SHIFTOUT(lbc->lbc_br,BR_MSEL) != BR_MSEL_FCM)
92125ea6a8Smatt 		return 0;
93125ea6a8Smatt 
94125ea6a8Smatt 	return 1;
95125ea6a8Smatt }
96125ea6a8Smatt 
97125ea6a8Smatt void
pq3nandfcm_attach(device_t parent,device_t self,void * aux)98125ea6a8Smatt pq3nandfcm_attach(device_t parent, device_t self, void *aux)
99125ea6a8Smatt {
100125ea6a8Smatt 	struct generic_attach_args * const ga = aux;
101125ea6a8Smatt 	struct pq3nandfcm_softc * const sc = device_private(self);
102125ea6a8Smatt 	struct pq3obio_softc * const psc = device_private(parent);
103125ea6a8Smatt 	struct pq3lbc_softc * const lbc = &psc->sc_lbcs[ga->ga_cs];
104125ea6a8Smatt 
105125ea6a8Smatt 	sc->sc_dev = self;
106125ea6a8Smatt 	sc->sc_obio = psc;
107125ea6a8Smatt 	sc->sc_lbc = lbc;
108125ea6a8Smatt }
109125ea6a8Smatt 
110125ea6a8Smatt int
pq3nandfcm_detach(device_t self,int flags)111125ea6a8Smatt pq3nandfcm_detach(device_t self, int flags)
112125ea6a8Smatt {
113125ea6a8Smatt 	struct pq3nandfcm_softc * const sc = device_private(self);
114*baea4824Sriastradh 	int error;
115*baea4824Sriastradh 
116*baea4824Sriastradh 	error = config_detach_children(self, flags);
117*baea4824Sriastradh 	if (error)
118*baea4824Sriastradh 		return error;
119125ea6a8Smatt 
120125ea6a8Smatt 	pmf_device_deregister(self);
121125ea6a8Smatt 
122125ea6a8Smatt 	bus_space_unmap(sc->sc_window_bst, sc->sc_window_bsh,
123125ea6a8Smatt 	    sc->sc_window_size);
124*baea4824Sriastradh 	return 0;
125125ea6a8Smatt }
126125ea6a8Smatt void
pq3nandfcm_command(device_t self,uint8_t command)127125ea6a8Smatt pq3nandfcm_command(device_t self, uint8_t command)
128125ea6a8Smatt {
129125ea6a8Smatt 	struct pq3nandfcm_softc * const sc = device_private(self);
130125ea6a8Smatt 
131125ea6a8Smatt 	lbc_lock(sc->sc_obio);
132125ea6a8Smatt 	lbc_write_4(sc->sc_obio, FCR, __SHIFTIN(command, FCR_CMD0));
133125ea6a8Smatt 	lbc_write_4(sc->sc_obio, FIR, __SHIFTIN(FIR_OP_CM0, FIR_OP0));
134125ea6a8Smatt 	lbc_write_4(sc->sc_obio, LSOR, sc->sc_cs);
135125ea6a8Smatt 	lbc_unlock(sc->sc_obio);
136125ea6a8Smatt 
137125ea6a8Smatt }
138125ea6a8Smatt 
139125ea6a8Smatt void
pq3nandfcm_address(device_t self,uint8_t address)140125ea6a8Smatt pq3nandfcm_address(device_t self, uint8_t address)
141125ea6a8Smatt {
142125ea6a8Smatt 	struct pq3nandfcm_softc * const sc = device_private(self);
143125ea6a8Smatt 
144125ea6a8Smatt 	lbc_lock(sc->sc_obio);
145125ea6a8Smatt 	lbc_write_4(sc->sc_obio, MDR, __SHIFTIN(address, MDR_AS0));
146125ea6a8Smatt 	lbc_write_4(sc->sc_obio, FIR, __SHIFTIN(FIR_OP_UA, FIR_OP0));
147125ea6a8Smatt 	lbc_write_4(sc->sc_obio, LSOR, sc->sc_cs);
148125ea6a8Smatt 	lbc_unlock(sc->sc_obio);
149125ea6a8Smatt }
150125ea6a8Smatt 
151125ea6a8Smatt void
pq3nandfcm_busy(device_t self)152125ea6a8Smatt pq3nandfcm_busy(device_t self)
153125ea6a8Smatt {
154125ea6a8Smatt 	struct pq3nandfcm_softc * const sc = device_private(self);
155125ea6a8Smatt 
156125ea6a8Smatt 	lbc_lock(sc->sc_obio);
157125ea6a8Smatt 	for (;;) {
158125ea6a8Smatt 		uint32_t v = lbc_read_4(sc->sc_obio, LTESR);
159125ea6a8Smatt 		if ((v & LTESR_CC) == 0) {
160125ea6a8Smatt 			/*
161125ea6a8Smatt 			 * The command is done but the device might not
162125ea6a8Smatt 			 * be ready since the CC doesn't check for that.
163125ea6a8Smatt 			 */
164125ea6a8Smatt 			break;
165125ea6a8Smatt 		}
166125ea6a8Smatt 		DELAY(1);
167125ea6a8Smatt 	}
168125ea6a8Smatt 	lbc_unlock(sc->sc_obio);
169125ea6a8Smatt }
170125ea6a8Smatt 
171125ea6a8Smatt void
pq3nandfcm_read_byte(device_t self,uint8_t * valp)172125ea6a8Smatt pq3nandfcm_read_byte(device_t self, uint8_t *valp)
173125ea6a8Smatt {
174125ea6a8Smatt 	struct pq3nandfcm_softc * const sc = device_private(self);
175125ea6a8Smatt 
176125ea6a8Smatt 	lbc_lock(sc->sc_obio);
177125ea6a8Smatt 	/*
178125ea6a8Smatt 	 * Make sure the device is ready before reading the byte.
179125ea6a8Smatt 	 */
180125ea6a8Smatt 	lbc_write_4(sc->sc_obio, FIR, __SHIFTIN(FIR_OP_RSW, FIR_OP0));
181125ea6a8Smatt 	lbc_write_4(sc->sc_obio, LSOR, sc->sc_cs);
182125ea6a8Smatt 	uint32_t v = lbc_read_4(sc->sc_obio, MDR);
183125ea6a8Smatt 	lbc_unlock(sc->sc_obio);
184125ea6a8Smatt 
185125ea6a8Smatt 	*valp = (uint8_t) v;
186125ea6a8Smatt }
187125ea6a8Smatt 
188125ea6a8Smatt void
pq3nandfcm_write_byte(device_t self,uint8_t val)189125ea6a8Smatt pq3nandfcm_write_byte(device_t self, uint8_t val)
190125ea6a8Smatt {
191125ea6a8Smatt 	struct pq3nandfcm_softc * const sc = device_private(self);
192125ea6a8Smatt 
193125ea6a8Smatt 	lbc_lock(sc->sc_obio);
194125ea6a8Smatt 	lbc_write_4(sc->sc_obio, MDR, val);
195125ea6a8Smatt 	/*
196125ea6a8Smatt 	 * Make sure the device is ready before writing the byte.
197125ea6a8Smatt 	 */
198125ea6a8Smatt 	lbc_write_4(sc->sc_obio, FIR, __SHIFTIN(FIR_OP_WS, FIR_OP0));
199125ea6a8Smatt 	lbc_write_4(sc->sc_obio, LSOR, sc->sc_cs);
200125ea6a8Smatt 	lbc_unlock(sc->sc_obio);
201125ea6a8Smatt }
202125ea6a8Smatt 
203125ea6a8Smatt void
pq3nandfcm_read_buf(device_t self,void * buf,size_t len)204125ea6a8Smatt pq3nandfcm_read_buf(device_t self, void *buf, size_t len)
205125ea6a8Smatt {
206125ea6a8Smatt 	struct pq3nandfcm_softc * const sc = device_private(self);
207125ea6a8Smatt 	bus_size_t offset = 0;
208125ea6a8Smatt 	uint32_t *dp32 = buf;
209125ea6a8Smatt 
210125ea6a8Smatt 	KASSERT(len < 4096);
211125ea6a8Smatt 	KASSERT((len & 3) == 0);
212125ea6a8Smatt 	KASSERT(((uintptr_t)dp32 & 3) == 0);
213125ea6a8Smatt 
214125ea6a8Smatt 	lbc_lock(sc->sc_obio);
215125ea6a8Smatt 	lbc_write_4(sc->sc_obio, FCR, len);
216125ea6a8Smatt 	lbc_write_4(sc->sc_obio, FIR, __SHIFTIN(FIR_OP_RBW, FIR_OP0));
217125ea6a8Smatt 	lbc_write_4(sc->sc_obio, LSOR, sc->sc_cs);
218125ea6a8Smatt 
219125ea6a8Smatt 	while (lbc_read_4(sc->sc_obio, LTESR) & LTESR_CC) {
220125ea6a8Smatt 		DELAY(1);
221125ea6a8Smatt 	}
222125ea6a8Smatt 	for (offset = 0; len >= 4; offset += 4, len -= 4) {
223125ea6a8Smatt 		*dp32++ = fcm_buf_read(sc, offset);
224125ea6a8Smatt 	}
225125ea6a8Smatt 	if (len) {
226125ea6a8Smatt 		const uint32_t mask = ~0 >> (8 * len);
227125ea6a8Smatt 		const uint32_t data = fcm_buf_read(sc, offset);
228125ea6a8Smatt 		*dp32 = (data & ~mask) | (*dp32 & mask);
229125ea6a8Smatt 	}
230125ea6a8Smatt 	lbc_unlock(sc->sc_obio);
231125ea6a8Smatt }
232125ea6a8Smatt 
233125ea6a8Smatt void
pq3nandfcm_write_buf(device_t self,const void * buf,size_t len)234125ea6a8Smatt pq3nandfcm_write_buf(device_t self, const void *buf, size_t len)
235125ea6a8Smatt {
236125ea6a8Smatt 	struct pq3nandfcm_softc * const sc = device_private(self);
237125ea6a8Smatt 	bus_size_t offset = 0;
238125ea6a8Smatt 	const uint32_t *dp32 = buf;
239125ea6a8Smatt 
240125ea6a8Smatt 	KASSERT(len < 4096);
241125ea6a8Smatt 	KASSERT((len & 3) == 0);
242125ea6a8Smatt 	KASSERT(((uintptr_t)dp32 & 3) == 0);
243125ea6a8Smatt 
244125ea6a8Smatt 	lbc_lock(sc->sc_obio);
245125ea6a8Smatt 	lbc_write_4(sc->sc_obio, FCR, len);
246125ea6a8Smatt 
247125ea6a8Smatt 	/*
248125ea6a8Smatt 	 * First we need to copy to the FCM buffer.  There will be a few extra
249125ea6a8Smatt 	 * bytes at the end but we don't care.
250125ea6a8Smatt 	 */
251125ea6a8Smatt 	for (len = roundup2(len, 4); offset < len; offset += 4, dp32++) {
252125ea6a8Smatt 		fcm_buf_write(sc, offset, *dp32);
253125ea6a8Smatt 	}
254125ea6a8Smatt 
255125ea6a8Smatt 	/*
256125ea6a8Smatt 	 * W
257125ea6a8Smatt 	 */
258125ea6a8Smatt 	lbc_write_4(sc->sc_obio, FIR, __SHIFTIN(FIR_OP_WB, FIR_OP0));
259125ea6a8Smatt 	lbc_write_4(sc->sc_obio, LSOR, sc->sc_cs);
260125ea6a8Smatt 	while (lbc_read_4(sc->sc_obio, LTESR) & LTESR_CC) {
261125ea6a8Smatt 		DELAY(1);
262125ea6a8Smatt 	}
263125ea6a8Smatt 	lbc_unlock(sc->sc_obio);
264125ea6a8Smatt }
265