1/*-
2 * Copyright (c) KATO Takenori, 1996, 1997.
3 *
4 * All rights reserved.  Unpublished rights reserved under the copyright
5 * laws of Japan.
6 *
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer as
13 *    the first lines of this file unmodified.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. The name of the author may not be used to endorse or promote products
18 *    derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * $FreeBSD$
32 */
33
34#include "opt_pc98.h"
35
36#include <sys/param.h>
37#include <sys/systm.h>
38
39#include <sys/bio.h>
40#include <sys/bus.h>
41#include <sys/conf.h>
42#include <sys/kernel.h>
43#include <sys/sysctl.h>
44#include <cam/cam.h>
45#include <cam/cam_ccb.h>
46#include <geom/geom_disk.h>
47#include <machine/md_var.h>
48#include <pc98/pc98/pc98_machdep.h>
49
50static	int	ad_geom_method = AD_GEOM_ADJUST_COMPATIDE;
51
52TUNABLE_INT("machdep.ad_geom_method", &ad_geom_method);
53SYSCTL_INT(_machdep, OID_AUTO, ad_geom_method, CTLFLAG_RW, &ad_geom_method, 0,
54    "IDE disk geometry conversion method");
55
56/*
57 * Initialize DMA controller
58 */
59void
60pc98_init_dmac(void)
61{
62	outb(0x439, (inb(0x439) & 0xfb));	/* DMA Accsess Control over 1MB */
63	outb(0x29, (0x0c | 0));				/* Bank Mode Reg. 16M mode */
64	outb(0x29, (0x0c | 1));				/* Bank Mode Reg. 16M mode */
65	outb(0x29, (0x0c | 2));				/* Bank Mode Reg. 16M mode */
66	outb(0x29, (0x0c | 3));				/* Bank Mode Reg. 16M mode */
67	outb(0x11, 0x50);
68}
69
70#ifdef EPSON_MEMWIN
71/*
72 * Disconnect phisical memory in 15-16MB region.
73 *
74 * EPSON PC-486GR, P, SR, SE, HX, HG and HA only.  Other system support
75 * this feature with software DIP switch.
76 */
77static void
78init_epson_memwin(void)
79{
80	/* Disable 15MB-16MB caching. */
81	switch (epson_machine_id) {
82	case EPSON_PC486_HX:
83	case EPSON_PC486_HG:
84	case EPSON_PC486_HA:
85		/* Cache control start. */
86		outb(0x43f, 0x42);
87		outw(0xc40, 0x0033);
88
89		/* Disable 0xF00000-0xFFFFFF. */
90		outb(0xc48, 0x49);
91		outb(0xc4c, 0x00);
92		outb(0xc48, 0x48);
93		outb(0xc4c, 0xf0);
94		outb(0xc48, 0x4d);
95		outb(0xc4c, 0x00);
96		outb(0xc48, 0x4c);
97		outb(0xc4c, 0xff);
98		outb(0xc48, 0x4f);
99		outb(0xc4c, 0x00);
100
101		/* Cache control end. */
102		outb(0x43f, 0x40);
103		break;
104
105	case EPSON_PC486_GR:
106	case EPSON_PC486_P:
107	case EPSON_PC486_GR_SUPER:
108	case EPSON_PC486_GR_PLUS:
109	case EPSON_PC486_SE:
110	case EPSON_PC486_SR:
111		/* Disable 0xF00000-0xFFFFFF. */
112		outb(0x43f, 0x42);
113		outb(0x467, 0xe0);
114		outb(0x567, 0xd8);
115
116		outb(0x43f, 0x40);
117		outb(0x467, 0xe0);
118		outb(0x567, 0xe0);
119		break;
120	}
121
122	/* Disable 15MB-16MB RAM and enable memory window. */
123	outb(0x43b, inb(0x43b) & 0xfd);	/* Clear bit1. */
124}
125#endif
126
127/*
128 * Get physical memory size
129 */
130unsigned int
131pc98_getmemsize(unsigned int *base, unsigned int *ext)
132{
133	unsigned int under16, over16;
134
135	/* available conventional memory size */
136	*base = ((PC98_SYSTEM_PARAMETER(0x501) & 7) + 1) * 128;
137
138	/* available protected memory size under 16MB */
139	under16 = PC98_SYSTEM_PARAMETER(0x401) * 128 + 1024;
140#ifdef EPSON_MEMWIN
141	if (pc98_machine_type & M_EPSON_PC98) {
142		if (under16 > (15 * 1024))
143			/* chop under16 memory to 15MB */
144			under16 = 15 * 1024;
145		init_epson_memwin();
146	}
147#endif
148
149	/* available protected memory size over 16MB / 1MB */
150	over16  = PC98_SYSTEM_PARAMETER(0x594);
151	over16 += PC98_SYSTEM_PARAMETER(0x595) * 256;
152
153	if (over16 > 0)
154		*ext = (16 + over16) * 1024 - 1024;
155	else
156		*ext = under16 - 1024;
157
158	return (under16);
159}
160
161/*
162 * Read a geometry information of SCSI HDD from BIOS work area.
163 *
164 * XXX - Before reading BIOS work area, we should check whether
165 * host adapter support it.
166 */
167int
168scsi_da_bios_params(struct ccb_calc_geometry *ccg)
169{
170	u_char *tmp;
171	int	target;
172
173	target = ccg->ccb_h.target_id;
174	tmp = (u_char *)&PC98_SYSTEM_PARAMETER(0x460 + target*4);
175	if ((PC98_SYSTEM_PARAMETER(0x482) & ((1 << target)&0xff)) != 0) {
176		ccg->secs_per_track = *tmp;
177		ccg->cylinders = ((*(tmp+3)<<8)|*(tmp+2))&0xfff;
178#if 0
179		switch (*(tmp + 3) & 0x30) {
180		case 0x00:
181			disk_parms->secsiz = 256;
182			printf("Warning!: not supported.\n");
183			break;
184		case 0x10:
185			disk_parms->secsiz = 512;
186			break;
187		case 0x20:
188			disk_parms->secsiz = 1024;
189			break;
190		default:
191			disk_parms->secsiz = 512;
192			printf("Warning!: not supported. But force to 512\n");
193			break;
194		}
195#endif
196		if (*(tmp+3) & 0x40) {
197			ccg->cylinders += (*(tmp+1)&0xf0)<<8;
198			ccg->heads = *(tmp+1)&0x0f;
199		} else {
200			ccg->heads = *(tmp+1);
201		}
202		return (1);
203	}
204
205	return (0);
206}
207
208/*
209 * Adjust the geometry of the IDE HDD.
210 */
211
212/* IDE BIOS compatible mode. */
213static	void
214pc98_ata_disk_geom_adjust_idebios(struct disk *disk)
215{
216
217	if (disk->d_mediasize < MEDIASIZE_4_3G) {
218		disk->d_fwsectors = 17;
219		disk->d_fwheads = 8;
220	} else if (disk->d_mediasize < MEDIASIZE_29_5G) {
221		disk->d_fwsectors = 63;
222		if (disk->d_fwheads != 15)	/* Allow 15H63S. */
223			disk->d_fwheads = 16;
224	} else if (disk->d_mediasize < MEDIASIZE_31_5G) {
225		disk->d_fwsectors = 63;
226		disk->d_fwheads = 16;
227	} else if (disk->d_mediasize < MEDIASIZE_127G) {
228		disk->d_fwsectors = 255;
229		disk->d_fwheads = 16;
230	} else {
231		/* XXX */
232		disk->d_fwsectors = 255;
233		disk->d_fwheads = 255;
234	}
235}
236
237/* SCSI BIOS compatible mode. */
238static	void
239pc98_ata_disk_geom_adjust_scsibios(struct disk *disk)
240{
241
242	if (disk->d_mediasize < MEDIASIZE_8G) {
243		disk->d_fwsectors = 32;
244		disk->d_fwheads = 8;
245	} else if (disk->d_mediasize < MEDIASIZE_32G) {
246		disk->d_fwsectors = 128;
247		disk->d_fwheads = 8;
248	} else if (disk->d_mediasize < MEDIASIZE_60G) {
249		/* Compatible with IFC-USP 1.2. */
250		disk->d_fwsectors = 128;
251		disk->d_fwheads = 15;
252	} else if (disk->d_mediasize < MEDIASIZE_120G) {
253		disk->d_fwsectors = 255;
254		disk->d_fwheads = 15;
255	} else {
256		/* XXX */
257		disk->d_fwsectors = 255;
258		disk->d_fwheads = 255;
259	}
260}
261
262/* Compatible with the revision 1.28. */
263static	void
264pc98_ata_disk_geom_adjust_cyl16bit(struct disk *disk)
265{
266	off_t totsec = disk->d_mediasize / disk->d_sectorsize;
267	off_t cyl = totsec / disk->d_fwsectors / disk->d_fwheads;
268
269	/*
270	 * It is impossible to have more than 65535 cylinders, so if
271	 * we have more then try to adjust.  This is lame, but it is
272	 * only POC.
273	 */
274	if (cyl > 65355) {
275		if (totsec < 17*8*65535) {
276			disk->d_fwsectors = 17;
277			disk->d_fwheads = 8;
278		} else if (totsec < 63*16*65535) {
279			disk->d_fwsectors = 63;
280			disk->d_fwheads = 16;
281		} else if (totsec < 255*16*65535) {
282			disk->d_fwsectors = 255;
283			disk->d_fwheads = 16;
284		} else {
285			disk->d_fwsectors = 255;
286			disk->d_fwheads = 255;
287		}
288	}
289}
290
291void
292pc98_ata_disk_firmware_geom_adjust(struct disk *disk)
293{
294	u_int	oldsectors, oldheads;
295
296	oldsectors = disk->d_fwsectors;
297	oldheads = disk->d_fwheads;
298
299	switch (ad_geom_method) {
300	case AD_GEOM_ADJUST_COMPATIDE:
301		pc98_ata_disk_geom_adjust_idebios(disk);
302		break;
303	case AD_GEOM_ADJUST_COMPATSCSI:
304		pc98_ata_disk_geom_adjust_scsibios(disk);
305		break;
306	case AD_GEOM_ADJUST_COMPATCYL16:
307		pc98_ata_disk_geom_adjust_cyl16bit(disk);
308		break;
309	default:
310		/* Do nothing. */
311		break;
312	}
313
314	if (bootverbose &&
315	    (oldsectors != disk->d_fwsectors || oldheads != disk->d_fwheads))
316		printf(
317		    "%s%d: geometry adjusted from [%dH/%dS] to [%dH/%dS]\n",
318		    disk->d_name, disk->d_unit,
319		    oldheads, oldsectors,
320		    disk->d_fwheads, disk->d_fwsectors);
321}
322