fdc_isa.c revision 132139
1/* 2 * Copyright (c) 2004 M. Warner Losh. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions, and the following disclaimer, 10 * without modification, immediately at the beginning of the file. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in 13 * the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: head/sys/dev/fdc/fdc_isa.c 132139 2004-07-14 07:04:17Z imp $"); 31 32#include <sys/param.h> 33#include <sys/bio.h> 34#include <sys/bus.h> 35#include <sys/kernel.h> 36#include <sys/module.h> 37#include <sys/rman.h> 38#include <sys/systm.h> 39#include <machine/bus.h> 40 41#include <machine/bus.h> 42 43#include <dev/fdc/fdcvar.h> 44#include <dev/fdc/fdcreg.h> 45 46#include <isa/isavar.h> 47#include <isa/isareg.h> 48 49static int fdc_isa_probe(device_t); 50static void fdctl_wr_isa(fdc_p, u_int8_t); 51 52static struct isa_pnp_id fdc_ids[] = { 53 {0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */ 54 {0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */ 55 {0} 56}; 57 58static void 59fdctl_wr_isa(fdc_p fdc, u_int8_t v) 60{ 61 bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v); 62} 63 64static int 65fdc_isa_alloc_resources(device_t dev, struct fdc_data *fdc) 66{ 67 int ispnp, nports; 68 69 ispnp = (fdc->flags & FDC_ISPNP) != 0; 70 fdc->fdc_dev = dev; 71 fdc->rid_ioport = 0; 72 fdc->rid_irq = 0; 73 fdc->rid_drq = 0; 74 fdc->rid_ctl = 1; 75 76 /* 77 * On standard ISA, we don't just use an 8 port range 78 * (e.g. 0x3f0-0x3f7) since that covers an IDE control 79 * register at 0x3f6. 80 * 81 * Isn't PC hardware wonderful. 82 */ 83 nports = ispnp ? 1 : 6; 84 85 /* 86 * Some ACPI BIOSen have _CRS objects for the floppy device that 87 * split the I/O port resource into several resources. We detect 88 * this case by checking if there are more than 2 IOPORT resources. 89 * If so, we use the resource with the smallest start address as 90 * the port RID and the largest start address as the control RID. 91 */ 92 if (bus_get_resource_count(dev, SYS_RES_IOPORT, 2) != 0) { 93 u_long min_start, max_start, tmp; 94 int i; 95 96 /* Find the min/max start addresses and their RIDs. */ 97 max_start = 0ul; 98 min_start = ~0ul; 99 for (i = 0; bus_get_resource_count(dev, SYS_RES_IOPORT, i) > 0; 100 i++) { 101 tmp = bus_get_resource_start(dev, SYS_RES_IOPORT, i); 102 KASSERT(tmp != 0, ("bogus resource")); 103 if (tmp < min_start) { 104 min_start = tmp; 105 fdc->rid_ioport = i; 106 } 107 if (tmp > max_start) { 108 max_start = tmp; 109 fdc->rid_ctl = i; 110 } 111 } 112 if (min_start + 7 != max_start) { 113 device_printf(dev, "I/O to control range incorrect\n"); 114 return (ENXIO); 115 } 116 } 117 118 fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, 119 &fdc->rid_ioport, 0ul, ~0ul, nports, RF_ACTIVE); 120 if (fdc->res_ioport == 0) { 121 device_printf(dev, "cannot reserve I/O port range (%d ports)\n", 122 nports); 123 return ENXIO; 124 } 125 fdc->portt = rman_get_bustag(fdc->res_ioport); 126 fdc->porth = rman_get_bushandle(fdc->res_ioport); 127 128 /* 129 * Some BIOSen report the device at 0x3f2-0x3f5,0x3f7 130 * and some at 0x3f0-0x3f5,0x3f7. We detect the former 131 * by checking the size and adjust the port address 132 * accordingly. 133 */ 134 if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4) 135 fdc->port_off = -2; 136 137 /* 138 * Register the control port range as rid 1 if it 139 * isn't there already. Most PnP BIOSen will have 140 * already done this but non-PnP configurations don't. 141 * 142 * And some (!!) report 0x3f2-0x3f5 and completely 143 * leave out the control register! It seems that some 144 * non-antique controller chips have a different 145 * method of programming the transfer speed which 146 * doesn't require the control register, but it's 147 * mighty bogus as the chip still responds to the 148 * address for the control register. 149 */ 150 if (bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) { 151 u_long ctlstart; 152 /* Find the control port, usually 0x3f7 */ 153 ctlstart = rman_get_start(fdc->res_ioport) + fdc->port_off + 7; 154 bus_set_resource(dev, SYS_RES_IOPORT, 1, ctlstart, 1); 155 } 156 157 /* 158 * Now (finally!) allocate the control port. 159 */ 160 fdc->res_ctl = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 161 &fdc->rid_ctl, RF_ACTIVE); 162 if (fdc->res_ctl == 0) { 163 device_printf(dev, 164 "cannot reserve control I/O port range (control port)\n"); 165 return ENXIO; 166 } 167 fdc->ctlt = rman_get_bustag(fdc->res_ctl); 168 fdc->ctlh = rman_get_bushandle(fdc->res_ctl); 169 170 fdc->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &fdc->rid_irq, 171 RF_ACTIVE | RF_SHAREABLE); 172 if (fdc->res_irq == 0) { 173 device_printf(dev, "cannot reserve interrupt line\n"); 174 return ENXIO; 175 } 176 177 if ((fdc->flags & FDC_NODMA) == 0) { 178 fdc->res_drq = bus_alloc_resource_any(dev, SYS_RES_DRQ, 179 &fdc->rid_drq, RF_ACTIVE | RF_SHAREABLE); 180 if (fdc->res_drq == 0) { 181 device_printf(dev, "cannot reserve DMA request line\n"); 182 fdc->flags |= FDC_NODMA; 183 } else 184 fdc->dmachan = rman_get_start(fdc->res_drq); 185 } 186 187 return 0; 188} 189 190static int 191fdc_isa_probe(device_t dev) 192{ 193 int error, ic_type; 194 int ispnp; 195 struct fdc_data *fdc; 196 197 fdc = device_get_softc(dev); 198 fdc->fdc_dev = dev; 199 fdc->fdctl_wr = fdctl_wr_isa; 200 201 /* Check pnp ids */ 202 error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); 203 if (error == ENXIO) 204 return (ENXIO); 205 ispnp = (error == 0); 206 207 208 /* Attempt to allocate our resources for the duration of the probe */ 209 error = fdc_isa_alloc_resources(dev, fdc); 210 if (error) 211 goto out; 212 213 /* Check that the controller is working. */ 214 if (!ispnp) { 215 error = fdc_initial_reset(fdc); 216 if (error) 217 goto out; 218 } 219 220 /* Try to determine a more specific device type. */ 221 if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { 222 switch (ic_type & 0xff) { 223 case 0x80: 224 device_set_desc(dev, "NEC 765 or clone"); 225 break; 226 case 0x81: /* not mentioned in any hardware doc */ 227 case 0x90: 228 device_set_desc(dev, 229 "Enhanced floppy controller (i82077, NE72065 or clone)"); 230 break; 231 default: 232 device_set_desc(dev, "Generic floppy controller"); 233 break; 234 } 235 } 236 237out: 238 fdc_release_resources(fdc); 239 return (error); 240} 241 242static int 243fdc_isa_attach(device_t dev) 244{ 245 int ic_type; 246 struct fdc_data *fdc; 247 int error; 248 249 fdc = device_get_softc(dev); 250 fdc->fdctl_wr = fdctl_wr_isa; 251 error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); 252 if (error == 0) 253 fdc->flags |= FDC_ISPNP; 254 if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { 255 ic_type = (u_char)ic_type; 256 switch (ic_type) { 257 case 0x80: 258 fdc->fdct = FDC_NE765; 259 break; 260 case 0x81: /* not mentioned in any hardware doc */ 261 case 0x90: 262 fdc->fdct = FDC_ENHANCED; 263 break; 264 default: 265 fdc->fdct = FDC_UNKNOWN; 266 break; 267 } 268 } 269 fdc_isa_alloc_resources(dev, fdc); 270 return (fdc_attach(dev)); 271} 272 273static device_method_t fdc_methods[] = { 274 /* Device interface */ 275 DEVMETHOD(device_probe, fdc_isa_probe), 276 DEVMETHOD(device_attach, fdc_isa_attach), 277 DEVMETHOD(device_detach, fdc_detach), 278 DEVMETHOD(device_shutdown, bus_generic_shutdown), 279 DEVMETHOD(device_suspend, bus_generic_suspend), 280 DEVMETHOD(device_resume, bus_generic_resume), 281 282 /* Bus interface */ 283 DEVMETHOD(bus_print_child, fdc_print_child), 284 DEVMETHOD(bus_read_ivar, fdc_read_ivar), 285 DEVMETHOD(bus_write_ivar, fdc_write_ivar), 286 /* Our children never use any other bus interface methods. */ 287 288 { 0, 0 } 289}; 290 291static driver_t fdc_driver = { 292 "fdc", 293 fdc_methods, 294 sizeof(struct fdc_data) 295}; 296 297DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); 298DRIVER_MODULE(fdc, acpi, fdc_driver, fdc_devclass, 0, 0); 299