1/*	$NetBSD: bmd.c,v 1.17 2010/08/08 09:30:29 isaki Exp $	*/
2
3/*
4 * Copyright (c) 2002 Tetsuya Isaki. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28/*
29 * Nereid bank memory disk
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: bmd.c,v 1.17 2010/08/08 09:30:29 isaki Exp $");
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/buf.h>
38#include <sys/conf.h>
39#include <sys/device.h>
40#include <sys/kernel.h>
41#include <sys/stat.h>
42#include <sys/disk.h>
43#include <sys/disklabel.h>
44#include <sys/ioctl.h>
45#include <sys/fcntl.h>
46#include <sys/proc.h>
47
48#include <machine/bus.h>
49#include <machine/cpu.h>
50
51#include <arch/x68k/dev/intiovar.h>
52
53#define BMD_ADDR1	(0xece3f0)
54#define BMD_ADDR2	(0xecebf0)
55
56#define BMD_PAGESIZE	(0x10000)	/* 64KB */
57#define BMD_BSIZE	(512)
58#define BLKS_PER_PAGE	(BMD_PAGESIZE / BMD_BSIZE)
59
60#define BMD_PAGE	(0)
61#define BMD_CTRL	(1)
62#define BMD_CTRL_ENABLE	(0x80)	/* DIP8: 1=Enable, 0=Disable */
63#define BMD_CTRL_MEMORY	(0x40)	/*       1=16M available, 0=4M available */
64#define BMD_CTRL_WINDOW	(0x20)	/* DIP6: 1=0xee0000, 0=0xef0000 */
65
66
67#define BMD_UNIT(dev)	(minor(dev) / 8)
68
69#ifdef BMD_DEBUG
70#define DPRINTF(x)	printf x
71#else
72#define DPRINTF(x)	/* nothing */
73#endif
74
75struct bmd_softc {
76	struct disk sc_dkdev;
77	bus_space_tag_t    sc_iot;
78	bus_space_handle_t sc_ioh;
79	bus_space_handle_t sc_bank;
80
81	int sc_maxpage;
82	int sc_window;
83
84	int sc_flags;
85#define BMD_OPENBLK	(0x01)
86#define BMD_OPENCHR	(0x02)
87#define BMD_OPEN	(BMD_OPENBLK | BMD_OPENCHR)
88};
89
90static int  bmd_match(device_t, cfdata_t, void *);
91static void bmd_attach(device_t, device_t, void *);
92static int  bmd_getdisklabel(struct bmd_softc *, dev_t);
93
94extern struct cfdriver bmd_cd;
95
96CFATTACH_DECL_NEW(bmd, sizeof(struct bmd_softc),
97	bmd_match, bmd_attach, NULL, NULL);
98
99dev_type_open(bmdopen);
100dev_type_close(bmdclose);
101dev_type_read(bmdread);
102dev_type_write(bmdwrite);
103dev_type_ioctl(bmdioctl);
104dev_type_strategy(bmdstrategy);
105dev_type_dump(bmddump);
106dev_type_size(bmdsize);
107
108const struct bdevsw bmd_bdevsw = {
109	bmdopen, bmdclose, bmdstrategy, bmdioctl, bmddump, bmdsize, D_DISK
110};
111
112const struct cdevsw bmd_cdevsw = {
113	bmdopen, bmdclose, bmdread, bmdwrite, bmdioctl,
114	nostop, notty, nopoll, nommap, nokqfilter, D_DISK
115};
116
117struct dkdriver bmddkdriver = { bmdstrategy };
118
119static int
120bmd_match(device_t parent, cfdata_t cf, void *aux)
121{
122	struct intio_attach_args *ia = aux;
123	bus_space_tag_t iot = ia->ia_bst;
124	bus_space_handle_t ioh;
125	int window;
126	int r;
127
128	if (ia->ia_addr == INTIOCF_ADDR_DEFAULT)
129		ia->ia_addr = BMD_ADDR1;
130
131	/* fixed parameter */
132	if (ia->ia_addr != BMD_ADDR1 && ia->ia_addr != BMD_ADDR2)
133		return (0);
134
135	/* Check CTRL addr */
136 	if (badaddr((void *)IIOV(ia->ia_addr)))
137		return (0);
138
139	ia->ia_size = 2;
140	if (bus_space_map(iot, ia->ia_addr, ia->ia_size, 0, &ioh))
141		return (0);
142
143	/* Check window addr */
144	r = bus_space_read_1(iot, ioh, BMD_CTRL);
145	bus_space_unmap(iot, ioh, ia->ia_size);
146
147	if ((r & BMD_CTRL_WINDOW))
148		window = 0xef0000;
149	else
150		window = 0xee0000;
151	if (badaddr((void *)IIOV(window)))
152		return (0);
153
154	return (1);
155}
156
157static void
158bmd_attach(device_t parent, device_t self, void *aux)
159{
160	struct bmd_softc *sc = device_private(self);
161	struct intio_attach_args *ia = aux;
162	bus_space_tag_t iot = ia->ia_bst;
163	bus_space_handle_t ioh;
164	u_int8_t r;
165
166	aprint_normal(": Nereid Bank Memory Disk\n");
167
168	/* Map I/O space */
169	ia->ia_size = 2;
170	if (bus_space_map(iot, ia->ia_addr, ia->ia_size, 0, &ioh)) {
171		aprint_error_dev(self, "can't map I/O space\n");
172		return;
173	}
174
175	sc->sc_iot = iot;
176	sc->sc_ioh = ioh;
177
178	/* read control register */
179	r = bus_space_read_1(sc->sc_iot, sc->sc_ioh, BMD_CTRL);
180
181	/* check enable-bit */
182	if ((r & BMD_CTRL_ENABLE) == 0) {
183		aprint_error_dev(self, "disabled by DIP-SW 8\n");
184		return;
185	}
186
187	if ((r & BMD_CTRL_MEMORY))
188		sc->sc_maxpage = 256;
189	else
190		sc->sc_maxpage = 64;
191
192	if ((r & BMD_CTRL_WINDOW))
193		sc->sc_window = 0xef0000;
194	else
195		sc->sc_window = 0xee0000;
196
197	/* Map bank area */
198	if (bus_space_map(iot, sc->sc_window, BMD_PAGESIZE, 0, &sc->sc_bank)) {
199		aprint_error_dev(self, "can't map bank area: 0x%x\n",
200			sc->sc_window);
201		return;
202	}
203
204	aprint_normal_dev(self, "%d MB, 0x%x(64KB) x %d pages\n",
205		(sc->sc_maxpage / 16), sc->sc_window, sc->sc_maxpage);
206
207	disk_init(&sc->sc_dkdev, device_xname(self), &bmddkdriver);
208	disk_attach(&sc->sc_dkdev);
209}
210
211int
212bmdopen(dev_t dev, int oflags, int devtype, struct lwp *l)
213{
214	struct bmd_softc *sc;
215
216	DPRINTF(("%s%d\n", __func__, unit));
217
218	sc = device_lookup_private(&bmd_cd, BMD_UNIT(dev));
219	if (sc == NULL)
220		return ENXIO;
221
222	switch (devtype) {
223	case S_IFCHR:
224		sc->sc_flags |= BMD_OPENCHR;
225		break;
226	case S_IFBLK:
227		sc->sc_flags |= BMD_OPENBLK;
228		break;
229	}
230
231	bmd_getdisklabel(sc, dev);
232
233	return (0);
234}
235
236int
237bmdclose(dev_t dev, int fflag, int devtype, struct lwp *l)
238{
239	struct bmd_softc *sc = device_lookup_private(&bmd_cd, BMD_UNIT(dev));
240
241	DPRINTF(("%s%d\n", __func__, BMD_UNIT(dev)));
242
243	switch (devtype) {
244	case S_IFCHR:
245		sc->sc_flags &= ~BMD_OPENCHR;
246		break;
247	case S_IFBLK:
248		sc->sc_flags &= ~BMD_OPENBLK;
249		break;
250	}
251
252	return (0);
253}
254
255void
256bmdstrategy(struct buf *bp)
257{
258	int unit = BMD_UNIT(bp->b_dev);
259	struct bmd_softc *sc;
260	int offset, disksize, resid;
261	int page, pg_offset, pg_resid;
262	void *data;
263
264	if (unit >= bmd_cd.cd_ndevs) {
265		bp->b_error = ENXIO;
266		goto done;
267	}
268
269	sc = device_lookup_private(&bmd_cd, BMD_UNIT(bp->b_dev));
270	if (sc == NULL) {
271		bp->b_error = ENXIO;
272		goto done;
273	}
274
275	DPRINTF(("bmdstrategy: %s blkno %d bcount %ld:",
276		(bp->b_flags & B_READ) ? "read " : "write",
277		bp->b_blkno, bp->b_bcount));
278
279	bp->b_resid = bp->b_bcount;
280	offset = (bp->b_blkno << DEV_BSHIFT);
281	disksize = sc->sc_maxpage * BMD_PAGESIZE;
282	if (offset >= disksize) {
283		/* EOF if read, EIO if write */
284		if (bp->b_flags & B_READ)
285			goto done;
286		bp->b_error = EIO;
287		goto done;
288	}
289
290	resid = bp->b_resid;
291	if (resid > disksize - offset)
292		resid = disksize - offset;
293
294	data = bp->b_data;
295	do {
296		page = offset / BMD_PAGESIZE;
297		pg_offset = offset % BMD_PAGESIZE;
298
299		/* length */
300		pg_resid = MIN(resid, BMD_PAGESIZE - pg_offset);
301
302		/* switch bank page */
303		bus_space_write_1(sc->sc_iot, sc->sc_ioh, BMD_PAGE, page);
304
305		/* XXX we should use DMA transfer? */
306		if ((bp->b_flags & B_READ)) {
307			bus_space_read_region_1(sc->sc_iot, sc->sc_bank,
308				pg_offset, data, pg_resid);
309		} else {
310			bus_space_write_region_1(sc->sc_iot, sc->sc_bank,
311				pg_offset, data, pg_resid);
312		}
313
314		data = (char *)data + pg_resid;
315		offset += pg_resid;
316		resid -= pg_resid;
317		bp->b_resid -= pg_resid;
318	} while (resid > 0);
319
320	DPRINTF(("\n"));
321
322 done:
323	biodone(bp);
324}
325
326int
327bmdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
328{
329	struct bmd_softc *sc;
330	struct disklabel dl;
331	int error;
332
333	DPRINTF(("%s%d %ld\n", __func__, BMD_UNIT(dev), cmd));
334
335	sc = device_lookup_private(&bmd_cd, BMD_UNIT(dev));
336	if (sc == NULL)
337		return ENXIO;
338
339	switch (cmd) {
340	case DIOCGDINFO:
341		*(struct disklabel *)data = *(sc->sc_dkdev.dk_label);
342		break;
343
344	case DIOCWDINFO:
345		if ((flag & FWRITE) == 0)
346			return EBADF;
347
348		error = setdisklabel(&dl, (struct disklabel *)data, 0, NULL);
349		if (error)
350			return error;
351		error = writedisklabel(dev, bmdstrategy, &dl, NULL);
352		return error;
353
354	default:
355		return EINVAL;
356	}
357	return 0;
358}
359
360int
361bmddump(dev_t dev, daddr_t blkno, void *va, size_t size)
362{
363
364	DPRINTF(("%s%d ", __func__, BMD_UNIT(dev)));
365	return ENODEV;
366}
367
368int
369bmdsize(dev_t dev)
370{
371	struct bmd_softc *sc;
372
373	DPRINTF(("%s%d ", __func__, BMD_UNIT(dev)));
374
375	sc = device_lookup_private(&bmd_cd, BMD_UNIT(dev));
376	if (sc == NULL)
377		return 0;
378
379	return (sc->sc_maxpage * BMD_PAGESIZE) >> DEV_BSHIFT;
380}
381
382int
383bmdread(dev_t dev, struct uio *uio, int ioflag)
384{
385	return physio(bmdstrategy, NULL, dev, B_READ, minphys, uio);
386}
387
388int
389bmdwrite(dev_t dev, struct uio *uio, int ioflag)
390{
391	return physio(bmdstrategy, NULL, dev, B_WRITE, minphys, uio);
392}
393
394static int
395bmd_getdisklabel(struct bmd_softc *sc, dev_t dev)
396{
397	struct disklabel *lp;
398	int part;
399
400	part = DISKPART(dev);
401	lp = sc->sc_dkdev.dk_label;
402	memset(lp, 0, sizeof(struct disklabel));
403
404	lp->d_secsize     = BMD_BSIZE;
405	lp->d_nsectors    = BLKS_PER_PAGE;
406	lp->d_ntracks     = sc->sc_maxpage;
407	lp->d_ncylinders  = 1;
408	lp->d_secpercyl   = lp->d_nsectors * lp->d_ntracks;
409	lp->d_secperunit  = lp->d_secpercyl * lp->d_ncylinders;
410
411	lp->d_type        = DTYPE_LD;
412	lp->d_rpm         = 300;	/* dummy */
413	lp->d_interleave  = 1;	/* dummy? */
414
415	lp->d_npartitions = part + 1;
416	lp->d_bbsize = 8192;
417	lp->d_sbsize = 8192;	/* ? */
418
419	lp->d_magic       = DISKMAGIC;
420	lp->d_magic2      = DISKMAGIC;
421	lp->d_checksum    = dkcksum(lp);
422
423	lp->d_partitions[part].p_size   = lp->d_secperunit;
424	lp->d_partitions[part].p_fstype = FS_BSDFFS;
425	lp->d_partitions[part].p_fsize  = 1024;
426	lp->d_partitions[part].p_frag   = 8;
427
428	return (0);
429}
430