fdc_isa.c revision 132271
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 132271 2004-07-16 23:07:38Z 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 64int 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 struct fdc_data *fdc; 195 196 fdc = device_get_softc(dev); 197 fdc->fdc_dev = dev; 198 fdc->fdctl_wr = fdctl_wr_isa; 199 200 /* Check pnp ids */ 201 error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); 202 if (error == ENXIO) 203 return (ENXIO); 204 205 /* Attempt to allocate our resources for the duration of the probe */ 206 error = fdc_isa_alloc_resources(dev, fdc); 207 if (error) 208 goto out; 209 210 /* Check that the controller is working. */ 211 error = fdc_initial_reset(fdc); 212 if (error) 213 goto out; 214 215 /* Try to determine a more specific device type. */ 216 if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { 217 switch (ic_type & 0xff) { 218 case 0x80: 219 device_set_desc(dev, "NEC 765 or clone"); 220 break; 221 case 0x81: /* not mentioned in any hardware doc */ 222 case 0x90: 223 device_set_desc(dev, 224 "Enhanced floppy controller (i82077, NE72065 or clone)"); 225 break; 226 default: 227 device_set_desc(dev, "Generic floppy controller"); 228 break; 229 } 230 } 231 232out: 233 fdc_release_resources(fdc); 234 return (error); 235} 236 237static int 238fdc_isa_attach(device_t dev) 239{ 240 int ic_type; 241 struct fdc_data *fdc; 242 int error; 243 244 fdc = device_get_softc(dev); 245 fdc->fdctl_wr = fdctl_wr_isa; 246 error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); 247 if (error == 0) 248 fdc->flags |= FDC_ISPNP; 249 if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { 250 switch (ic_type & 0xff) { 251 case 0x80: 252 fdc->fdct = FDC_NE765; 253 break; 254 case 0x81: /* not mentioned in any hardware doc */ 255 case 0x90: 256 fdc->fdct = FDC_ENHANCED; 257 break; 258 default: 259 fdc->fdct = FDC_UNKNOWN; 260 break; 261 } 262 } 263 264 error = fdc_isa_alloc_resources(dev, fdc); 265 if (error) 266 goto out; 267 error = fdc_attach(dev); 268 if (error) 269 goto out; 270 error = fdc_hints_probe(dev); 271 if (error) 272 goto out; 273out: 274 if (error) 275 fdc_release_resources(fdc); 276 return (error); 277} 278 279static device_method_t fdc_methods[] = { 280 /* Device interface */ 281 DEVMETHOD(device_probe, fdc_isa_probe), 282 DEVMETHOD(device_attach, fdc_isa_attach), 283 DEVMETHOD(device_detach, fdc_detach), 284 DEVMETHOD(device_shutdown, bus_generic_shutdown), 285 DEVMETHOD(device_suspend, bus_generic_suspend), 286 DEVMETHOD(device_resume, bus_generic_resume), 287 288 /* Bus interface */ 289 DEVMETHOD(bus_print_child, fdc_print_child), 290 DEVMETHOD(bus_read_ivar, fdc_read_ivar), 291 DEVMETHOD(bus_write_ivar, fdc_write_ivar), 292 /* Our children never use any other bus interface methods. */ 293 294 { 0, 0 } 295}; 296 297static driver_t fdc_driver = { 298 "fdc", 299 fdc_methods, 300 sizeof(struct fdc_data) 301}; 302 303DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); 304