1132214Snjl/*- 2132214Snjl * Copyright (c) 2004 Nate Lawson (SDG) 3132214Snjl * All rights reserved. 4132214Snjl * 5132214Snjl * Redistribution and use in source and binary forms, with or without 6132214Snjl * modification, are permitted provided that the following conditions 7132214Snjl * are met: 8132214Snjl * 1. Redistributions of source code must retain the above copyright 9132214Snjl * notice, this list of conditions and the following disclaimer. 10132214Snjl * 2. Redistributions in binary form must reproduce the above copyright 11132214Snjl * notice, this list of conditions and the following disclaimer in the 12132214Snjl * documentation and/or other materials provided with the distribution. 13132214Snjl * 14132214Snjl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15132214Snjl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16132214Snjl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17132214Snjl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18132214Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19132214Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20132214Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21132214Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22132214Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23132214Snjl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24132214Snjl * SUCH DAMAGE. 25132214Snjl */ 26132214Snjl 27132214Snjl#include <sys/cdefs.h> 28132214Snjl__FBSDID("$FreeBSD: releng/10.2/sys/dev/fdc/fdc_acpi.c 246128 2013-01-30 18:01:20Z sbz $"); 29132214Snjl 30132214Snjl#include <sys/param.h> 31132214Snjl#include <sys/kernel.h> 32132214Snjl#include <sys/bio.h> 33132214Snjl#include <sys/bus.h> 34132214Snjl#include <sys/malloc.h> 35132214Snjl#include <sys/module.h> 36132214Snjl#include <sys/proc.h> 37132214Snjl 38193530Sjkim#include <contrib/dev/acpica/include/acpi.h> 39193530Sjkim 40132214Snjl#include <dev/acpica/acpivar.h> 41132214Snjl#include <dev/fdc/fdcvar.h> 42132214Snjl 43132214Snjlstatic int fdc_acpi_probe(device_t dev); 44132214Snjlstatic int fdc_acpi_attach(device_t dev); 45132810Snjlstatic int fdc_acpi_probe_children(device_t bus, device_t dev, 46132810Snjl void *fde); 47132214Snjlstatic ACPI_STATUS fdc_acpi_probe_child(ACPI_HANDLE h, device_t *dev, 48132214Snjl int level, void *arg); 49132214Snjl 50132810Snjl/* Maximum number of child devices of a controller (4 floppy + 1 tape.) */ 51132810Snjl#define ACPI_FDC_MAXDEVS 5 52132214Snjl 53135355Snjl/* Standard size of buffer returned by the _FDE method. */ 54135355Snjl#define ACPI_FDC_FDE_LEN (ACPI_FDC_MAXDEVS * sizeof(uint32_t)) 55135355Snjl 56132810Snjl/* 57132810Snjl * Parameters for the tape drive (5th device). Some BIOS authors use this 58132810Snjl * for all drives, not just the tape drive (e.g., ASUS K8V). This isn't 59132810Snjl * grossly incompatible with the spec since it says the first four devices 60132810Snjl * are simple booleans. 61132810Snjl */ 62132810Snjl#define ACPI_FD_UNKNOWN 0 63132810Snjl#define ACPI_FD_PRESENT 1 64132810Snjl#define ACPI_FD_NEVER_PRESENT 2 65132810Snjl 66132214Snjl/* Temporary buf length for evaluating _FDE and _FDI. */ 67132214Snjl#define ACPI_FDC_BUFLEN 1024 68132214Snjl 69132214Snjl/* Context for walking FDC child devices. */ 70132214Snjlstruct fdc_walk_ctx { 71132810Snjl uint32_t fd_present[ACPI_FDC_MAXDEVS]; 72132214Snjl int index; 73132214Snjl device_t acpi_dev; 74132214Snjl device_t dev; 75132214Snjl}; 76132214Snjl 77132214Snjlstatic int 78132214Snjlfdc_acpi_probe(device_t dev) 79132214Snjl{ 80132214Snjl device_t bus; 81132214Snjl static char *fdc_ids[] = { "PNP0700", "PNP0701", NULL }; 82132214Snjl 83132214Snjl bus = device_get_parent(dev); 84132214Snjl if (ACPI_ID_PROBE(bus, dev, fdc_ids) == NULL) 85132214Snjl return (ENXIO); 86132214Snjl 87132214Snjl if (ACPI_SUCCESS(ACPI_EVALUATE_OBJECT(bus, dev, "_FDE", NULL, NULL))) 88132214Snjl device_set_desc(dev, "floppy drive controller (FDE)"); 89132214Snjl else 90132214Snjl device_set_desc(dev, "floppy drive controller"); 91132214Snjl return (0); 92132214Snjl} 93132214Snjl 94132214Snjlstatic int 95132214Snjlfdc_acpi_attach(device_t dev) 96132214Snjl{ 97132214Snjl struct fdc_data *sc; 98132214Snjl ACPI_BUFFER buf; 99208650Sjkim ACPI_OBJECT *obj; 100132214Snjl device_t bus; 101200554Sjkim int error; 102132214Snjl 103132214Snjl /* Get our softc and use the same accessor as ISA. */ 104132214Snjl sc = device_get_softc(dev); 105132214Snjl sc->fdc_dev = dev; 106132214Snjl 107132214Snjl /* Initialize variables and get a temporary buffer for _FDE. */ 108132214Snjl error = ENXIO; 109132214Snjl buf.Length = ACPI_FDC_BUFLEN; 110132214Snjl buf.Pointer = malloc(buf.Length, M_TEMP, M_NOWAIT | M_ZERO); 111132214Snjl if (buf.Pointer == NULL) 112132214Snjl goto out; 113132214Snjl 114132214Snjl /* Allocate resources the same as the ISA attachment. */ 115132214Snjl error = fdc_isa_alloc_resources(dev, sc); 116132214Snjl if (error != 0) 117132214Snjl goto out; 118132214Snjl 119132214Snjl /* Call common attach code in fdc(4) first. */ 120132214Snjl error = fdc_attach(dev); 121132214Snjl if (error != 0) 122132214Snjl goto out; 123132214Snjl 124132214Snjl /* 125132214Snjl * Enumerate _FDE, which lists floppy drives that are present. If 126132214Snjl * this fails, fall back to the ISA hints-based probe method. 127132214Snjl */ 128132214Snjl bus = device_get_parent(dev); 129135355Snjl if (ACPI_FAILURE(ACPI_EVALUATE_OBJECT(bus, dev, "_FDE", NULL, &buf))) { 130200554Sjkim error = fdc_hints_probe(dev); 131200554Sjkim goto out; 132135355Snjl } 133135355Snjl 134135355Snjl /* Add fd child devices as specified. */ 135208650Sjkim obj = buf.Pointer; 136208650Sjkim error = fdc_acpi_probe_children(bus, dev, obj->Buffer.Pointer); 137135355Snjl 138135938Sjhbout: 139132214Snjl if (buf.Pointer) 140132214Snjl free(buf.Pointer, M_TEMP); 141132214Snjl if (error != 0) 142132214Snjl fdc_release_resources(sc); 143132214Snjl 144132214Snjl return (error); 145132214Snjl} 146132214Snjl 147132810Snjlstatic int 148132810Snjlfdc_acpi_probe_children(device_t bus, device_t dev, void *fde) 149132810Snjl{ 150132810Snjl struct fdc_walk_ctx *ctx; 151132810Snjl devclass_t fd_dc; 152132810Snjl int i; 153132810Snjl 154132810Snjl /* Setup the context and walk all child devices. */ 155132810Snjl ctx = malloc(sizeof(struct fdc_walk_ctx), M_TEMP, M_NOWAIT); 156132810Snjl if (ctx == NULL) { 157132810Snjl device_printf(dev, "no memory for walking children\n"); 158132810Snjl return (ENOMEM); 159132810Snjl } 160132810Snjl bcopy(fde, ctx->fd_present, sizeof(ctx->fd_present)); 161132810Snjl ctx->index = 0; 162132810Snjl ctx->dev = dev; 163132810Snjl ctx->acpi_dev = bus; 164132810Snjl ACPI_SCAN_CHILDREN(ctx->acpi_dev, dev, 1, fdc_acpi_probe_child, 165132810Snjl ctx); 166132810Snjl 167132810Snjl /* Add any devices not represented by an AML Device handle/node. */ 168132810Snjl fd_dc = devclass_find("fd"); 169132810Snjl for (i = 0; i < ACPI_FDC_MAXDEVS; i++) 170132810Snjl if (ctx->fd_present[i] == ACPI_FD_PRESENT && 171132810Snjl devclass_get_device(fd_dc, i) == NULL) { 172132810Snjl if (fdc_add_child(dev, "fd", i) == NULL) 173132810Snjl device_printf(dev, "fd add failed\n"); 174132810Snjl } 175132810Snjl free(ctx, M_TEMP); 176132810Snjl 177132810Snjl /* Attach any children found during the probe. */ 178132810Snjl return (bus_generic_attach(dev)); 179132810Snjl} 180132810Snjl 181132214Snjlstatic ACPI_STATUS 182132214Snjlfdc_acpi_probe_child(ACPI_HANDLE h, device_t *dev, int level, void *arg) 183132214Snjl{ 184132214Snjl struct fdc_walk_ctx *ctx; 185134534Snjl device_t child, old_child; 186132214Snjl ACPI_BUFFER buf; 187132214Snjl ACPI_OBJECT *pkg, *obj; 188132214Snjl ACPI_STATUS status; 189132214Snjl 190132810Snjl ctx = (struct fdc_walk_ctx *)arg; 191132810Snjl buf.Pointer = NULL; 192132810Snjl 193132214Snjl /* 194132214Snjl * The first four ints are booleans that indicate whether fd0-3 are 195132214Snjl * present or not. The last is for a tape device, which we don't 196132214Snjl * bother supporting for now. 197132214Snjl */ 198132214Snjl if (ctx->index > 3) 199132214Snjl return (AE_OK); 200132214Snjl 201132810Snjl /* This device is not present, move on to the next. */ 202132810Snjl if (ctx->fd_present[ctx->index] != ACPI_FD_PRESENT) 203132810Snjl goto out; 204132810Snjl 205132810Snjl /* Create a device for the child with the given index. */ 206132810Snjl child = fdc_add_child(ctx->dev, "fd", ctx->index); 207132810Snjl if (child == NULL) 208132810Snjl goto out; 209134534Snjl old_child = *dev; 210132810Snjl *dev = child; 211132810Snjl 212132214Snjl /* Get temporary buffer for _FDI probe. */ 213132214Snjl buf.Length = ACPI_FDC_BUFLEN; 214132214Snjl buf.Pointer = malloc(buf.Length, M_TEMP, M_NOWAIT | M_ZERO); 215132214Snjl if (buf.Pointer == NULL) 216132214Snjl goto out; 217132214Snjl 218134534Snjl /* 219134534Snjl * Evaluate _FDI to get drive type to pass to the child. We use the 220134534Snjl * old child here since it has a valid ACPI_HANDLE since it is a 221134534Snjl * child of acpi. A better way to implement this would be to make fdc 222134534Snjl * support the ACPI handle ivar for its children. 223134534Snjl */ 224134534Snjl status = ACPI_EVALUATE_OBJECT(ctx->acpi_dev, old_child, "_FDI", NULL, 225134534Snjl &buf); 226132214Snjl if (ACPI_FAILURE(status)) { 227132810Snjl if (status != AE_NOT_FOUND) 228132810Snjl device_printf(ctx->dev, "_FDI failed - %#x\n", status); 229132214Snjl goto out; 230132214Snjl } 231132214Snjl pkg = (ACPI_OBJECT *)buf.Pointer; 232132214Snjl if (!ACPI_PKG_VALID(pkg, 16)) { 233132214Snjl device_printf(ctx->dev, "invalid _FDI package\n"); 234132214Snjl goto out; 235132214Snjl } 236132214Snjl obj = &pkg->Package.Elements[1]; 237132214Snjl if (obj == NULL || obj->Type != ACPI_TYPE_INTEGER) { 238132214Snjl device_printf(ctx->dev, "invalid type object in _FDI\n"); 239132214Snjl goto out; 240132214Snjl } 241132214Snjl fdc_set_fdtype(child, obj->Integer.Value); 242132214Snjl 243132214Snjlout: 244132214Snjl ctx->index++; 245132214Snjl if (buf.Pointer) 246132214Snjl free(buf.Pointer, M_TEMP); 247132214Snjl return (AE_OK); 248132214Snjl} 249132214Snjl 250132214Snjlstatic device_method_t fdc_acpi_methods[] = { 251132214Snjl /* Device interface */ 252132214Snjl DEVMETHOD(device_probe, fdc_acpi_probe), 253132214Snjl DEVMETHOD(device_attach, fdc_acpi_attach), 254132214Snjl DEVMETHOD(device_detach, fdc_detach), 255132214Snjl 256132214Snjl /* Bus interface */ 257132214Snjl DEVMETHOD(bus_print_child, fdc_print_child), 258132214Snjl DEVMETHOD(bus_read_ivar, fdc_read_ivar), 259132214Snjl DEVMETHOD(bus_write_ivar, fdc_write_ivar), 260132214Snjl 261246128Ssbz DEVMETHOD_END 262132214Snjl}; 263132214Snjl 264132214Snjlstatic driver_t fdc_acpi_driver = { 265132214Snjl "fdc", 266132214Snjl fdc_acpi_methods, 267132214Snjl sizeof(struct fdc_data) 268132214Snjl}; 269132214Snjl 270132214SnjlDRIVER_MODULE(fdc, acpi, fdc_acpi_driver, fdc_devclass, 0, 0); 271