twe_freebsd.c revision 67684
167555Smsmith/*- 267555Smsmith * Copyright (c) 2000 Michael Smith 367555Smsmith * Copyright (c) 2000 BSDi 467555Smsmith * All rights reserved. 567555Smsmith * 667555Smsmith * Redistribution and use in source and binary forms, with or without 767555Smsmith * modification, are permitted provided that the following conditions 867555Smsmith * are met: 967555Smsmith * 1. Redistributions of source code must retain the above copyright 1067555Smsmith * notice, this list of conditions and the following disclaimer. 1167555Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1267555Smsmith * notice, this list of conditions and the following disclaimer in the 1367555Smsmith * documentation and/or other materials provided with the distribution. 1467555Smsmith * 1567555Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1667555Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1767555Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1867555Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1967555Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2067555Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2167555Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2267555Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2367555Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2467555Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2567555Smsmith * SUCH DAMAGE. 2667555Smsmith * 2767555Smsmith * $FreeBSD: head/sys/dev/twe/twe_freebsd.c 67684 2000-10-27 05:58:45Z msmith $ 2867555Smsmith */ 2967555Smsmith 3067555Smsmith/* 3167555Smsmith * FreeBSD-specific code. 3267555Smsmith */ 3367555Smsmith 3467555Smsmith#include <dev/twe/twe_compat.h> 3567555Smsmith#include <dev/twe/twereg.h> 3667555Smsmith#include <dev/twe/tweio.h> 3767555Smsmith#include <dev/twe/twevar.h> 3867555Smsmith#include <dev/twe/twe_tables.h> 3967555Smsmith 4067555Smsmith#include <sys/devicestat.h> 4167555Smsmith 4267555Smsmithstatic devclass_t twe_devclass; 4367555Smsmith 4467555Smsmith/******************************************************************************** 4567555Smsmith ******************************************************************************** 4667555Smsmith Control device interface 4767555Smsmith ******************************************************************************** 4867555Smsmith ********************************************************************************/ 4967555Smsmith 5067555Smsmithstatic d_open_t twe_open; 5167555Smsmithstatic d_close_t twe_close; 5267555Smsmithstatic d_ioctl_t twe_ioctl_wrapper; 5367555Smsmith 5467555Smsmith#define TWE_CDEV_MAJOR 146 5567555Smsmith 5667555Smsmithstatic struct cdevsw twe_cdevsw = { 5767555Smsmith twe_open, 5867555Smsmith twe_close, 5967555Smsmith noread, 6067555Smsmith nowrite, 6167555Smsmith twe_ioctl_wrapper, 6267555Smsmith nopoll, 6367555Smsmith nommap, 6467555Smsmith nostrategy, 6567555Smsmith "twe", 6667555Smsmith TWE_CDEV_MAJOR, 6767555Smsmith nodump, 6867555Smsmith nopsize, 6967555Smsmith 0, 7067555Smsmith -1 7167555Smsmith}; 7267555Smsmith 7367555Smsmith/******************************************************************************** 7467555Smsmith * Accept an open operation on the control device. 7567555Smsmith */ 7667555Smsmithstatic int 7767555Smsmithtwe_open(dev_t dev, int flags, int fmt, struct proc *p) 7867555Smsmith{ 7967555Smsmith int unit = minor(dev); 8067555Smsmith struct twe_softc *sc = devclass_get_softc(twe_devclass, unit); 8167555Smsmith 8267555Smsmith sc->twe_state |= TWE_STATE_OPEN; 8367555Smsmith return(0); 8467555Smsmith} 8567555Smsmith 8667555Smsmith/******************************************************************************** 8767555Smsmith * Accept the last close on the control device. 8867555Smsmith */ 8967555Smsmithstatic int 9067555Smsmithtwe_close(dev_t dev, int flags, int fmt, struct proc *p) 9167555Smsmith{ 9267555Smsmith int unit = minor(dev); 9367555Smsmith struct twe_softc *sc = devclass_get_softc(twe_devclass, unit); 9467555Smsmith 9567555Smsmith sc->twe_state &= ~TWE_STATE_OPEN; 9667555Smsmith return (0); 9767555Smsmith} 9867555Smsmith 9967555Smsmith/******************************************************************************** 10067555Smsmith * Handle controller-specific control operations. 10167555Smsmith */ 10267555Smsmithstatic int 10367555Smsmithtwe_ioctl_wrapper(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p) 10467555Smsmith{ 10567555Smsmith struct twe_softc *sc = (struct twe_softc *)dev->si_drv1; 10667555Smsmith 10767555Smsmith return(twe_ioctl(sc, cmd, addr)); 10867555Smsmith} 10967555Smsmith 11067555Smsmith/******************************************************************************** 11167555Smsmith ******************************************************************************** 11267555Smsmith PCI device interface 11367555Smsmith ******************************************************************************** 11467555Smsmith ********************************************************************************/ 11567555Smsmith 11667555Smsmithstatic int twe_probe(device_t dev); 11767555Smsmithstatic int twe_attach(device_t dev); 11867555Smsmithstatic void twe_free(struct twe_softc *sc); 11967555Smsmithstatic int twe_detach(device_t dev); 12067555Smsmithstatic int twe_shutdown(device_t dev); 12167555Smsmithstatic int twe_suspend(device_t dev); 12267555Smsmithstatic int twe_resume(device_t dev); 12367555Smsmithstatic void twe_pci_intr(void *arg); 12467555Smsmithstatic void twe_intrhook(void *arg); 12567555Smsmith 12667555Smsmithstatic device_method_t twe_methods[] = { 12767555Smsmith /* Device interface */ 12867555Smsmith DEVMETHOD(device_probe, twe_probe), 12967555Smsmith DEVMETHOD(device_attach, twe_attach), 13067555Smsmith DEVMETHOD(device_detach, twe_detach), 13167555Smsmith DEVMETHOD(device_shutdown, twe_shutdown), 13267555Smsmith DEVMETHOD(device_suspend, twe_suspend), 13367555Smsmith DEVMETHOD(device_resume, twe_resume), 13467555Smsmith 13567555Smsmith DEVMETHOD(bus_print_child, bus_generic_print_child), 13667555Smsmith DEVMETHOD(bus_driver_added, bus_generic_driver_added), 13767555Smsmith { 0, 0 } 13867555Smsmith}; 13967555Smsmith 14067555Smsmithstatic driver_t twe_pci_driver = { 14167555Smsmith "twe", 14267555Smsmith twe_methods, 14367555Smsmith sizeof(struct twe_softc) 14467555Smsmith}; 14567555Smsmith 14667555Smsmith#ifdef TWE_OVERRIDE 14767555SmsmithDRIVER_MODULE(Xtwe, pci, twe_pci_driver, twe_devclass, 0, 0); 14867555Smsmith#else 14967555SmsmithDRIVER_MODULE(twe, pci, twe_pci_driver, twe_devclass, 0, 0); 15067555Smsmith#endif 15167555Smsmith 15267555Smsmith/******************************************************************************** 15367555Smsmith * Match a 3ware Escalade ATA RAID controller. 15467555Smsmith */ 15567555Smsmithstatic int 15667555Smsmithtwe_probe(device_t dev) 15767555Smsmith{ 15867555Smsmith 15967555Smsmith debug_called(4); 16067555Smsmith 16167555Smsmith if ((pci_get_vendor(dev) == TWE_VENDOR_ID) && 16267684Smsmith ((pci_get_device(dev) == TWE_DEVICE_ID) || 16367684Smsmith (pci_get_device(dev) == TWE_DEVICE_ID_ASIC))) { 16467555Smsmith device_set_desc(dev, TWE_DEVICE_NAME); 16567555Smsmith#ifdef TWE_OVERRIDE 16667555Smsmith return(0); 16767555Smsmith#else 16867555Smsmith return(-10); 16967555Smsmith#endif 17067555Smsmith } 17167555Smsmith return(ENXIO); 17267555Smsmith} 17367555Smsmith 17467555Smsmith/******************************************************************************** 17567555Smsmith * Allocate resources, initialise the controller. 17667555Smsmith */ 17767555Smsmithstatic int 17867555Smsmithtwe_attach(device_t dev) 17967555Smsmith{ 18067555Smsmith struct twe_softc *sc; 18167555Smsmith int rid, error; 18267555Smsmith u_int32_t command; 18367555Smsmith 18467555Smsmith debug_called(4); 18567555Smsmith 18667555Smsmith /* 18767555Smsmith * Initialise the softc structure. 18867555Smsmith */ 18967555Smsmith sc = device_get_softc(dev); 19067555Smsmith sc->twe_dev = dev; 19167555Smsmith 19267555Smsmith /* 19367555Smsmith * Make sure we are going to be able to talk to this board. 19467555Smsmith */ 19567555Smsmith command = pci_read_config(dev, PCIR_COMMAND, 2); 19667555Smsmith if ((command & PCIM_CMD_PORTEN) == 0) { 19767555Smsmith twe_printf(sc, "register window not available\n"); 19867555Smsmith return(ENXIO); 19967555Smsmith } 20067555Smsmith /* 20167555Smsmith * Force the busmaster enable bit on, in case the BIOS forgot. 20267555Smsmith */ 20367555Smsmith command |= PCIM_CMD_BUSMASTEREN; 20467555Smsmith pci_write_config(dev, PCIR_COMMAND, command, 2); 20567555Smsmith 20667555Smsmith /* 20767555Smsmith * Allocate the PCI register window. 20867555Smsmith */ 20967555Smsmith rid = TWE_IO_CONFIG_REG; 21067555Smsmith if ((sc->twe_io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE)) == NULL) { 21167555Smsmith twe_printf(sc, "can't allocate register window\n"); 21267555Smsmith twe_free(sc); 21367555Smsmith return(ENXIO); 21467555Smsmith } 21567555Smsmith sc->twe_btag = rman_get_bustag(sc->twe_io); 21667555Smsmith sc->twe_bhandle = rman_get_bushandle(sc->twe_io); 21767555Smsmith 21867555Smsmith /* 21967555Smsmith * Allocate the parent bus DMA tag appropriate for PCI. 22067555Smsmith */ 22167555Smsmith if (bus_dma_tag_create(NULL, /* parent */ 22267555Smsmith 1, 0, /* alignment, boundary */ 22367555Smsmith BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 22467555Smsmith BUS_SPACE_MAXADDR, /* highaddr */ 22567555Smsmith NULL, NULL, /* filter, filterarg */ 22667555Smsmith MAXBSIZE, TWE_MAX_SGL_LENGTH, /* maxsize, nsegments */ 22767555Smsmith BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 22867555Smsmith BUS_DMA_ALLOCNOW, /* flags */ 22967555Smsmith &sc->twe_parent_dmat)) { 23067555Smsmith twe_printf(sc, "can't allocate parent DMA tag\n"); 23167555Smsmith twe_free(sc); 23267555Smsmith return(ENOMEM); 23367555Smsmith } 23467555Smsmith 23567555Smsmith /* 23667555Smsmith * Allocate and connect our interrupt. 23767555Smsmith */ 23867555Smsmith rid = 0; 23967555Smsmith if ((sc->twe_irq = bus_alloc_resource(sc->twe_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE)) == NULL) { 24067555Smsmith twe_printf(sc, "can't allocate interrupt\n"); 24167555Smsmith twe_free(sc); 24267555Smsmith return(ENXIO); 24367555Smsmith } 24467555Smsmith if (bus_setup_intr(sc->twe_dev, sc->twe_irq, INTR_TYPE_BIO, twe_pci_intr, sc, &sc->twe_intr)) { 24567555Smsmith twe_printf(sc, "can't set up interrupt\n"); 24667555Smsmith twe_free(sc); 24767555Smsmith return(ENXIO); 24867555Smsmith } 24967555Smsmith 25067555Smsmith /* 25167555Smsmith * Create DMA tag for mapping objects into controller-addressable space. 25267555Smsmith */ 25367555Smsmith if (bus_dma_tag_create(sc->twe_parent_dmat, /* parent */ 25467555Smsmith 1, 0, /* alignment, boundary */ 25567555Smsmith BUS_SPACE_MAXADDR, /* lowaddr */ 25667555Smsmith BUS_SPACE_MAXADDR, /* highaddr */ 25767555Smsmith NULL, NULL, /* filter, filterarg */ 25867555Smsmith MAXBSIZE, TWE_MAX_SGL_LENGTH, /* maxsize, nsegments */ 25967555Smsmith BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 26067555Smsmith 0, /* flags */ 26167555Smsmith &sc->twe_buffer_dmat)) { 26267555Smsmith twe_printf(sc, "can't allocate data buffer DMA tag\n"); 26367555Smsmith twe_free(sc); 26467555Smsmith return(ENOMEM); 26567555Smsmith } 26667555Smsmith 26767555Smsmith /* 26867555Smsmith * Initialise the controller and driver core. 26967555Smsmith */ 27067555Smsmith if ((error = twe_setup(sc))) 27167555Smsmith return(error); 27267555Smsmith 27367555Smsmith /* 27467555Smsmith * Print some information about the controller and configuration. 27567555Smsmith */ 27667555Smsmith twe_describe_controller(sc); 27767555Smsmith 27867555Smsmith /* 27967555Smsmith * Create the control device. 28067555Smsmith */ 28167555Smsmith sc->twe_dev_t = make_dev(&twe_cdevsw, device_get_unit(sc->twe_dev), UID_ROOT, GID_OPERATOR, 28267555Smsmith S_IRUSR | S_IWUSR, "twe%d", device_get_unit(sc->twe_dev)); 28367555Smsmith sc->twe_dev_t->si_drv1 = sc; 28467555Smsmith /* 28567555Smsmith * Schedule ourselves to bring the controller up once interrupts are available. 28667555Smsmith * This isn't strictly necessary, since we disable interrupts while probing the 28767555Smsmith * controller, but it is more in keeping with common practice for other disk 28867555Smsmith * devices. 28967555Smsmith */ 29067555Smsmith sc->twe_ich.ich_func = twe_intrhook; 29167555Smsmith sc->twe_ich.ich_arg = sc; 29267555Smsmith if (config_intrhook_establish(&sc->twe_ich) != 0) { 29367555Smsmith twe_printf(sc, "can't establish configuration hook\n"); 29467555Smsmith twe_free(sc); 29567555Smsmith return(ENXIO); 29667555Smsmith } 29767555Smsmith 29867555Smsmith return(0); 29967555Smsmith} 30067555Smsmith 30167555Smsmith/******************************************************************************** 30267555Smsmith * Free all of the resources associated with (sc). 30367555Smsmith * 30467555Smsmith * Should not be called if the controller is active. 30567555Smsmith */ 30667555Smsmithstatic void 30767555Smsmithtwe_free(struct twe_softc *sc) 30867555Smsmith{ 30967555Smsmith struct twe_request *tr; 31067555Smsmith 31167555Smsmith debug_called(4); 31267555Smsmith 31367555Smsmith /* throw away any command buffers */ 31467555Smsmith while ((tr = twe_dequeue_free(sc)) != NULL) 31567555Smsmith twe_free_request(tr); 31667555Smsmith 31767555Smsmith /* destroy the data-transfer DMA tag */ 31867555Smsmith if (sc->twe_buffer_dmat) 31967555Smsmith bus_dma_tag_destroy(sc->twe_buffer_dmat); 32067555Smsmith 32167555Smsmith /* disconnect the interrupt handler */ 32267555Smsmith if (sc->twe_intr) 32367555Smsmith bus_teardown_intr(sc->twe_dev, sc->twe_irq, sc->twe_intr); 32467555Smsmith if (sc->twe_irq != NULL) 32567555Smsmith bus_release_resource(sc->twe_dev, SYS_RES_IRQ, 0, sc->twe_irq); 32667555Smsmith 32767555Smsmith /* destroy the parent DMA tag */ 32867555Smsmith if (sc->twe_parent_dmat) 32967555Smsmith bus_dma_tag_destroy(sc->twe_parent_dmat); 33067555Smsmith 33167555Smsmith /* release the register window mapping */ 33267555Smsmith if (sc->twe_io != NULL) 33367555Smsmith bus_release_resource(sc->twe_dev, SYS_RES_IOPORT, TWE_IO_CONFIG_REG, sc->twe_io); 33467555Smsmith 33567555Smsmith /* destroy control device */ 33667555Smsmith if (sc->twe_dev_t != (dev_t)NULL) 33767555Smsmith destroy_dev(sc->twe_dev_t); 33867555Smsmith} 33967555Smsmith 34067555Smsmith/******************************************************************************** 34167555Smsmith * Disconnect from the controller completely, in preparation for unload. 34267555Smsmith */ 34367555Smsmithstatic int 34467555Smsmithtwe_detach(device_t dev) 34567555Smsmith{ 34667555Smsmith struct twe_softc *sc = device_get_softc(dev); 34767555Smsmith int s, error; 34867555Smsmith 34967555Smsmith debug_called(4); 35067555Smsmith 35167555Smsmith error = EBUSY; 35267555Smsmith s = splbio(); 35367555Smsmith if (sc->twe_state & TWE_STATE_OPEN) 35467555Smsmith goto out; 35567555Smsmith 35667555Smsmith /* 35767555Smsmith * Shut the controller down. 35867555Smsmith */ 35967555Smsmith if ((error = twe_shutdown(dev))) 36067555Smsmith goto out; 36167555Smsmith 36267555Smsmith twe_free(sc); 36367555Smsmith 36467555Smsmith error = 0; 36567555Smsmith out: 36667555Smsmith splx(s); 36767555Smsmith return(error); 36867555Smsmith} 36967555Smsmith 37067555Smsmith/******************************************************************************** 37167555Smsmith * Bring the controller down to a dormant state and detach all child devices. 37267555Smsmith * 37367555Smsmith * Note that we can assume that the bioq on the controller is empty, as we won't 37467555Smsmith * allow shutdown if any device is open. 37567555Smsmith */ 37667555Smsmithstatic int 37767555Smsmithtwe_shutdown(device_t dev) 37867555Smsmith{ 37967555Smsmith struct twe_softc *sc = device_get_softc(dev); 38067555Smsmith int i, s, error; 38167555Smsmith 38267555Smsmith debug_called(4); 38367555Smsmith 38467555Smsmith s = splbio(); 38567555Smsmith error = 0; 38667555Smsmith 38767555Smsmith /* 38867555Smsmith * Delete all our child devices. 38967555Smsmith */ 39067555Smsmith for (i = 0; i < TWE_MAX_UNITS; i++) { 39167555Smsmith if (sc->twe_drive[i].td_disk != 0) { 39267555Smsmith if ((error = device_delete_child(sc->twe_dev, sc->twe_drive[i].td_disk)) != 0) 39367555Smsmith goto out; 39467555Smsmith sc->twe_drive[i].td_disk = 0; 39567555Smsmith } 39667555Smsmith } 39767555Smsmith 39867555Smsmith /* 39967555Smsmith * Bring the controller down. 40067555Smsmith */ 40167555Smsmith twe_deinit(sc); 40267555Smsmith 40367555Smsmith out: 40467555Smsmith splx(s); 40567555Smsmith return(error); 40667555Smsmith} 40767555Smsmith 40867555Smsmith/******************************************************************************** 40967555Smsmith * Bring the controller to a quiescent state, ready for system suspend. 41067555Smsmith */ 41167555Smsmithstatic int 41267555Smsmithtwe_suspend(device_t dev) 41367555Smsmith{ 41467555Smsmith struct twe_softc *sc = device_get_softc(dev); 41567555Smsmith int s; 41667555Smsmith 41767555Smsmith debug_called(4); 41867555Smsmith 41967555Smsmith s = splbio(); 42067555Smsmith sc->twe_state |= TWE_STATE_SUSPEND; 42167555Smsmith 42267555Smsmith twe_disable_interrupts(sc); 42367555Smsmith splx(s); 42467555Smsmith 42567555Smsmith return(0); 42667555Smsmith} 42767555Smsmith 42867555Smsmith/******************************************************************************** 42967555Smsmith * Bring the controller back to a state ready for operation. 43067555Smsmith */ 43167555Smsmithstatic int 43267555Smsmithtwe_resume(device_t dev) 43367555Smsmith{ 43467555Smsmith struct twe_softc *sc = device_get_softc(dev); 43567555Smsmith 43667555Smsmith debug_called(4); 43767555Smsmith 43867555Smsmith sc->twe_state &= ~TWE_STATE_SUSPEND; 43967555Smsmith twe_enable_interrupts(sc); 44067555Smsmith 44167555Smsmith return(0); 44267555Smsmith} 44367555Smsmith 44467555Smsmith/******************************************************************************* 44567555Smsmith * Take an interrupt, or be poked by other code to look for interrupt-worthy 44667555Smsmith * status. 44767555Smsmith */ 44867555Smsmithstatic void 44967555Smsmithtwe_pci_intr(void *arg) 45067555Smsmith{ 45167555Smsmith twe_intr((struct twe_softc *)arg); 45267555Smsmith} 45367555Smsmith 45467555Smsmith/******************************************************************************** 45567555Smsmith * Delayed-startup hook 45667555Smsmith */ 45767555Smsmithstatic void 45867555Smsmithtwe_intrhook(void *arg) 45967555Smsmith{ 46067555Smsmith struct twe_softc *sc = (struct twe_softc *)arg; 46167555Smsmith 46267555Smsmith /* pull ourselves off the intrhook chain */ 46367555Smsmith config_intrhook_disestablish(&sc->twe_ich); 46467555Smsmith 46567555Smsmith /* call core startup routine */ 46667555Smsmith twe_init(sc); 46767555Smsmith} 46867555Smsmith 46967555Smsmith/******************************************************************************** 47067555Smsmith * Given a detected drive, attach it to the bio interface. 47167555Smsmith * 47267555Smsmith * This is called from twe_init. 47367555Smsmith */ 47467555Smsmithvoid 47567555Smsmithtwe_attach_drive(struct twe_softc *sc, struct twe_drive *dr) 47667555Smsmith{ 47767555Smsmith char buf[80]; 47867555Smsmith int error; 47967555Smsmith 48067555Smsmith dr->td_disk = device_add_child(sc->twe_dev, NULL, -1); 48167555Smsmith if (dr->td_disk == NULL) { 48267555Smsmith twe_printf(sc, "device_add_child failed\n"); 48367555Smsmith return; 48467555Smsmith } 48567555Smsmith device_set_ivars(dr->td_disk, dr); 48667555Smsmith 48767555Smsmith /* 48867555Smsmith * XXX It would make sense to test the online/initialising bits, but they seem to be 48967555Smsmith * always set... 49067555Smsmith */ 49167555Smsmith sprintf(buf, "%s, %s", twe_describe_code(twe_table_unittype, dr->td_type), 49267555Smsmith twe_describe_code(twe_table_unitstate, dr->td_state & TWE_PARAM_UNITSTATUS_MASK)); 49367555Smsmith device_set_desc_copy(dr->td_disk, buf); 49467555Smsmith 49567555Smsmith if ((error = bus_generic_attach(sc->twe_dev)) != 0) 49667555Smsmith twe_printf(sc, "bus_generic_attach returned %d\n", error); 49767555Smsmith} 49867555Smsmith 49967555Smsmith/******************************************************************************** 50067555Smsmith ******************************************************************************** 50167555Smsmith Disk device 50267555Smsmith ******************************************************************************** 50367555Smsmith ********************************************************************************/ 50467555Smsmith 50567555Smsmith/* 50667555Smsmith * Disk device softc 50767555Smsmith */ 50867555Smsmithstruct twed_softc 50967555Smsmith{ 51067555Smsmith device_t twed_dev; 51167555Smsmith dev_t twed_dev_t; 51267555Smsmith struct twe_softc *twed_controller; /* parent device softc */ 51367555Smsmith struct twe_drive *twed_drive; /* drive data in parent softc */ 51467555Smsmith struct disk twed_disk; /* generic disk handle */ 51567555Smsmith struct devstat twed_stats; /* accounting */ 51667555Smsmith struct disklabel twed_label; /* synthetic label */ 51767555Smsmith int twed_flags; 51867555Smsmith#define TWED_OPEN (1<<0) /* drive is open (can't shut down) */ 51967555Smsmith}; 52067555Smsmith 52167555Smsmith/* 52267555Smsmith * Disk device bus interface 52367555Smsmith */ 52467555Smsmithstatic int twed_probe(device_t dev); 52567555Smsmithstatic int twed_attach(device_t dev); 52667555Smsmithstatic int twed_detach(device_t dev); 52767555Smsmith 52867555Smsmithstatic device_method_t twed_methods[] = { 52967555Smsmith DEVMETHOD(device_probe, twed_probe), 53067555Smsmith DEVMETHOD(device_attach, twed_attach), 53167555Smsmith DEVMETHOD(device_detach, twed_detach), 53267555Smsmith { 0, 0 } 53367555Smsmith}; 53467555Smsmith 53567555Smsmithstatic driver_t twed_driver = { 53667555Smsmith "twed", 53767555Smsmith twed_methods, 53867555Smsmith sizeof(struct twed_softc) 53967555Smsmith}; 54067555Smsmith 54167555Smsmithstatic devclass_t twed_devclass; 54267555Smsmith#ifdef TWE_OVERRIDE 54367555SmsmithDRIVER_MODULE(Xtwed, Xtwe, twed_driver, twed_devclass, 0, 0); 54467555Smsmith#else 54567555SmsmithDRIVER_MODULE(twed, twe, twed_driver, twed_devclass, 0, 0); 54667555Smsmith#endif 54767555Smsmith 54867555Smsmith/* 54967555Smsmith * Disk device control interface. 55067555Smsmith */ 55167555Smsmithstatic d_open_t twed_open; 55267555Smsmithstatic d_close_t twed_close; 55367555Smsmithstatic d_strategy_t twed_strategy; 55467555Smsmith 55567555Smsmith#define TWED_CDEV_MAJOR 147 55667555Smsmith 55767555Smsmithstatic struct cdevsw twed_cdevsw = { 55867555Smsmith twed_open, 55967555Smsmith twed_close, 56067555Smsmith physread, 56167555Smsmith physwrite, 56267555Smsmith noioctl, 56367555Smsmith nopoll, 56467555Smsmith nommap, 56567555Smsmith twed_strategy, 56667555Smsmith "twed", 56767555Smsmith TWED_CDEV_MAJOR, 56867555Smsmith nodump, 56967555Smsmith nopsize, 57067555Smsmith D_DISK, 57167555Smsmith -1 57267555Smsmith}; 57367555Smsmith 57467555Smsmithstatic struct cdevsw tweddisk_cdevsw; 57567555Smsmith#ifdef FREEBSD_4 57667555Smsmithstatic int disks_registered = 0; 57767555Smsmith#endif 57867555Smsmith 57967555Smsmith/******************************************************************************** 58067555Smsmith * Handle open from generic layer. 58167555Smsmith * 58267555Smsmith * Note that this is typically only called by the diskslice code, and not 58367555Smsmith * for opens on subdevices (eg. slices, partitions). 58467555Smsmith */ 58567555Smsmithstatic int 58667555Smsmithtwed_open(dev_t dev, int flags, int fmt, struct proc *p) 58767555Smsmith{ 58867555Smsmith struct twed_softc *sc = (struct twed_softc *)dev->si_drv1; 58967555Smsmith struct disklabel *label; 59067555Smsmith 59167555Smsmith debug_called(4); 59267555Smsmith 59367555Smsmith if (sc == NULL) 59467555Smsmith return (ENXIO); 59567555Smsmith 59667555Smsmith /* check that the controller is up and running */ 59767555Smsmith if (sc->twed_controller->twe_state & TWE_STATE_SHUTDOWN) 59867555Smsmith return(ENXIO); 59967555Smsmith 60067555Smsmith /* build synthetic label */ 60167555Smsmith label = &sc->twed_disk.d_label; 60267555Smsmith bzero(label, sizeof(*label)); 60367555Smsmith label->d_type = DTYPE_ESDI; 60467555Smsmith label->d_secsize = TWE_BLOCK_SIZE; 60567555Smsmith label->d_nsectors = sc->twed_drive->td_sectors; 60667555Smsmith label->d_ntracks = sc->twed_drive->td_heads; 60767555Smsmith label->d_ncylinders = sc->twed_drive->td_cylinders; 60867555Smsmith label->d_secpercyl = sc->twed_drive->td_sectors * sc->twed_drive->td_heads; 60967555Smsmith label->d_secperunit = sc->twed_drive->td_size; 61067555Smsmith 61167555Smsmith sc->twed_flags |= TWED_OPEN; 61267555Smsmith return (0); 61367555Smsmith} 61467555Smsmith 61567555Smsmith/******************************************************************************** 61667555Smsmith * Handle last close of the disk device. 61767555Smsmith */ 61867555Smsmithstatic int 61967555Smsmithtwed_close(dev_t dev, int flags, int fmt, struct proc *p) 62067555Smsmith{ 62167555Smsmith struct twed_softc *sc = (struct twed_softc *)dev->si_drv1; 62267555Smsmith 62367555Smsmith debug_called(4); 62467555Smsmith 62567555Smsmith if (sc == NULL) 62667555Smsmith return (ENXIO); 62767555Smsmith 62867555Smsmith sc->twed_flags &= ~TWED_OPEN; 62967555Smsmith return (0); 63067555Smsmith} 63167555Smsmith 63267555Smsmith/******************************************************************************** 63367555Smsmith * Handle an I/O request. 63467555Smsmith */ 63567555Smsmithstatic void 63667555Smsmithtwed_strategy(twe_bio *bp) 63767555Smsmith{ 63867555Smsmith struct twed_softc *sc = (struct twed_softc *)TWE_BIO_SOFTC(bp); 63967555Smsmith 64067555Smsmith debug_called(4); 64167555Smsmith 64267555Smsmith /* bogus disk? */ 64367555Smsmith if (sc == NULL) { 64467555Smsmith TWE_BIO_SET_ERROR(bp, EINVAL); 64567555Smsmith return; 64667555Smsmith } 64767555Smsmith 64867555Smsmith /* do-nothing operation? */ 64967555Smsmith if (TWE_BIO_LENGTH(bp) == 0) { 65067555Smsmith TWE_BIO_RESID(bp) = 0; 65167555Smsmith TWE_BIO_DONE(bp); 65267555Smsmith return; 65367555Smsmith } 65467555Smsmith 65567555Smsmith /* perform accounting */ 65667555Smsmith TWE_BIO_STATS_START(bp); 65767555Smsmith 65867555Smsmith /* pass the bio to the controller - it can work out who we are */ 65967555Smsmith twe_submit_bio(sc->twed_controller, bp); 66067555Smsmith return; 66167555Smsmith} 66267555Smsmith 66367555Smsmith/******************************************************************************** 66467555Smsmith * Handle completion of an I/O request. 66567555Smsmith */ 66667555Smsmithvoid 66767555Smsmithtwed_intr(twe_bio *bp) 66867555Smsmith{ 66967555Smsmith debug_called(4); 67067555Smsmith 67167555Smsmith /* if no error, transfer completed */ 67267555Smsmith if (!TWE_BIO_HAS_ERROR(bp)) 67367555Smsmith TWE_BIO_RESID(bp) = 0; 67467555Smsmith 67567555Smsmith TWE_BIO_STATS_END(bp); 67667555Smsmith TWE_BIO_DONE(bp); 67767555Smsmith} 67867555Smsmith 67967555Smsmith/******************************************************************************** 68067555Smsmith * Default probe stub. 68167555Smsmith */ 68267555Smsmithstatic int 68367555Smsmithtwed_probe(device_t dev) 68467555Smsmith{ 68567555Smsmith return (0); 68667555Smsmith} 68767555Smsmith 68867555Smsmith/******************************************************************************** 68967555Smsmith * Attach a unit to the controller. 69067555Smsmith */ 69167555Smsmithstatic int 69267555Smsmithtwed_attach(device_t dev) 69367555Smsmith{ 69467555Smsmith struct twed_softc *sc; 69567555Smsmith device_t parent; 69667555Smsmith dev_t dsk; 69767555Smsmith 69867555Smsmith debug_called(4); 69967555Smsmith 70067555Smsmith /* initialise our softc */ 70167555Smsmith sc = device_get_softc(dev); 70267555Smsmith parent = device_get_parent(dev); 70367555Smsmith sc->twed_controller = (struct twe_softc *)device_get_softc(parent); 70467555Smsmith sc->twed_drive = device_get_ivars(dev); 70567555Smsmith sc->twed_dev = dev; 70667555Smsmith 70767555Smsmith /* report the drive */ 70867555Smsmith twed_printf(sc, "%uMB (%u sectors)\n", 70967555Smsmith sc->twed_drive->td_size / ((1024 * 1024) / TWE_BLOCK_SIZE), 71067555Smsmith sc->twed_drive->td_size); 71167555Smsmith 71267555Smsmith devstat_add_entry(&sc->twed_stats, "twed", device_get_unit(dev), TWE_BLOCK_SIZE, 71367555Smsmith DEVSTAT_NO_ORDERED_TAGS, 71467555Smsmith DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER, 71567555Smsmith DEVSTAT_PRIORITY_ARRAY); 71667555Smsmith 71767555Smsmith /* attach a generic disk device to ourselves */ 71867555Smsmith dsk = disk_create(device_get_unit(dev), &sc->twed_disk, 0, &twed_cdevsw, &tweddisk_cdevsw); 71967555Smsmith dsk->si_drv1 = sc; 72067555Smsmith dsk->si_drv2 = &sc->twed_drive->td_unit; 72167555Smsmith sc->twed_dev_t = dsk; 72267555Smsmith#ifdef FREEBSD_4 72367555Smsmith disks_registered++; 72467555Smsmith#endif 72567555Smsmith 72667555Smsmith /* set the maximum I/O size to the theoretical maximum allowed by the S/G list size */ 72767555Smsmith dsk->si_iosize_max = (TWE_MAX_SGL_LENGTH - 1) * PAGE_SIZE; 72867555Smsmith 72967555Smsmith return (0); 73067555Smsmith} 73167555Smsmith 73267555Smsmith/******************************************************************************** 73367555Smsmith * Disconnect ourselves from the system. 73467555Smsmith */ 73567555Smsmithstatic int 73667555Smsmithtwed_detach(device_t dev) 73767555Smsmith{ 73867555Smsmith struct twed_softc *sc = (struct twed_softc *)device_get_softc(dev); 73967555Smsmith 74067555Smsmith debug_called(4); 74167555Smsmith 74267555Smsmith if (sc->twed_flags & TWED_OPEN) 74367555Smsmith return(EBUSY); 74467555Smsmith 74567555Smsmith devstat_remove_entry(&sc->twed_stats); 74667555Smsmith#ifdef FREEBSD_4 74767555Smsmith if (--disks_registered == 0) 74867555Smsmith cdevsw_remove(&tweddisk_cdevsw); 74967555Smsmith#else 75067555Smsmith disk_destroy(sc->twed_dev_t); 75167555Smsmith#endif 75267555Smsmith 75367555Smsmith return(0); 75467555Smsmith} 75567555Smsmith 75667555Smsmith/******************************************************************************** 75767555Smsmith ******************************************************************************** 75867555Smsmith Misc 75967555Smsmith ******************************************************************************** 76067555Smsmith ********************************************************************************/ 76167555Smsmith 76267555Smsmithstatic void twe_setup_data_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error); 76367555Smsmithstatic void twe_setup_request_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error); 76467555Smsmith 76567555Smsmith/******************************************************************************** 76667555Smsmith * Allocate a command buffer 76767555Smsmith */ 76867555SmsmithMALLOC_DEFINE(TWE_MALLOC_CLASS, "twe commands", "twe commands"); 76967555Smsmith 77067555Smsmithstruct twe_request * 77167555Smsmithtwe_allocate_request(struct twe_softc *sc) 77267555Smsmith{ 77367555Smsmith struct twe_request *tr; 77467555Smsmith 77567555Smsmith if ((tr = malloc(sizeof(struct twe_request), TWE_MALLOC_CLASS, M_NOWAIT)) == NULL) 77667555Smsmith return(NULL); 77767555Smsmith bzero(tr, sizeof(*tr)); 77867555Smsmith tr->tr_sc = sc; 77967555Smsmith if (bus_dmamap_create(sc->twe_buffer_dmat, 0, &tr->tr_cmdmap)) { 78067555Smsmith twe_free_request(tr); 78167555Smsmith return(NULL); 78267555Smsmith } 78367555Smsmith if (bus_dmamap_create(sc->twe_buffer_dmat, 0, &tr->tr_dmamap)) { 78467555Smsmith bus_dmamap_destroy(sc->twe_buffer_dmat, tr->tr_cmdmap); 78567555Smsmith twe_free_request(tr); 78667555Smsmith return(NULL); 78767555Smsmith } 78867555Smsmith return(tr); 78967555Smsmith} 79067555Smsmith 79167555Smsmith/******************************************************************************** 79267555Smsmith * Permanently discard a command buffer. 79367555Smsmith */ 79467555Smsmithvoid 79567555Smsmithtwe_free_request(struct twe_request *tr) 79667555Smsmith{ 79767555Smsmith struct twe_softc *sc = tr->tr_sc; 79867555Smsmith 79967555Smsmith debug_called(4); 80067555Smsmith 80167555Smsmith bus_dmamap_destroy(sc->twe_buffer_dmat, tr->tr_cmdmap); 80267555Smsmith bus_dmamap_destroy(sc->twe_buffer_dmat, tr->tr_dmamap); 80367555Smsmith free(tr, TWE_MALLOC_CLASS); 80467555Smsmith} 80567555Smsmith 80667555Smsmith/******************************************************************************** 80767555Smsmith * Map/unmap (tr)'s command and data in the controller's addressable space. 80867555Smsmith * 80967555Smsmith * These routines ensure that the data which the controller is going to try to 81067555Smsmith * access is actually visible to the controller, in a machine-independant 81167555Smsmith * fasion. Due to a hardware limitation, I/O buffers must be 512-byte aligned 81267555Smsmith * and we take care of that here as well. 81367555Smsmith */ 81467555Smsmithstatic void 81567555Smsmithtwe_setup_data_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) 81667555Smsmith{ 81767555Smsmith struct twe_request *tr = (struct twe_request *)arg; 81867555Smsmith TWE_Command *cmd = &tr->tr_command; 81967555Smsmith int i; 82067555Smsmith 82167555Smsmith debug_called(4); 82267555Smsmith 82367555Smsmith /* save base of first segment in command (applicable if there only one segment) */ 82467555Smsmith tr->tr_dataphys = segs[0].ds_addr; 82567555Smsmith 82667555Smsmith /* correct command size for s/g list size */ 82767555Smsmith tr->tr_command.generic.size += 2 * nsegments; 82867555Smsmith 82967555Smsmith /* 83067555Smsmith * Due to the fact that parameter and I/O commands have the scatter/gather list in 83167555Smsmith * different places, we need to determine which sort of command this actually is 83267555Smsmith * before we can populate it correctly. 83367555Smsmith */ 83467555Smsmith switch(cmd->generic.opcode) { 83567555Smsmith case TWE_OP_GET_PARAM: 83667555Smsmith case TWE_OP_SET_PARAM: 83767555Smsmith cmd->generic.sgl_offset = 2; 83867555Smsmith for (i = 0; i < nsegments; i++) { 83967555Smsmith cmd->param.sgl[i].address = segs[i].ds_addr; 84067555Smsmith cmd->param.sgl[i].length = segs[i].ds_len; 84167555Smsmith } 84267555Smsmith for (; i < TWE_MAX_SGL_LENGTH; i++) { /* XXX necessary? */ 84367555Smsmith cmd->param.sgl[i].address = 0; 84467555Smsmith cmd->param.sgl[i].length = 0; 84567555Smsmith } 84667555Smsmith break; 84767555Smsmith case TWE_OP_READ: 84867555Smsmith case TWE_OP_WRITE: 84967555Smsmith cmd->generic.sgl_offset = 3; 85067555Smsmith for (i = 0; i < nsegments; i++) { 85167555Smsmith cmd->io.sgl[i].address = segs[i].ds_addr; 85267555Smsmith cmd->io.sgl[i].length = segs[i].ds_len; 85367555Smsmith } 85467555Smsmith for (; i < TWE_MAX_SGL_LENGTH; i++) { /* XXX necessary? */ 85567555Smsmith cmd->io.sgl[i].address = 0; 85667555Smsmith cmd->io.sgl[i].length = 0; 85767555Smsmith } 85867555Smsmith break; 85967555Smsmith default: 86067555Smsmith /* no s/g list, nothing to do */ 86167555Smsmith } 86267555Smsmith} 86367555Smsmith 86467555Smsmithstatic void 86567555Smsmithtwe_setup_request_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error) 86667555Smsmith{ 86767555Smsmith struct twe_request *tr = (struct twe_request *)arg; 86867555Smsmith 86967555Smsmith debug_called(4); 87067555Smsmith 87167555Smsmith /* command can't cross a page boundary */ 87267555Smsmith tr->tr_cmdphys = segs[0].ds_addr; 87367555Smsmith} 87467555Smsmith 87567555Smsmithvoid 87667555Smsmithtwe_map_request(struct twe_request *tr) 87767555Smsmith{ 87867555Smsmith struct twe_softc *sc = tr->tr_sc; 87967555Smsmith 88067555Smsmith debug_called(4); 88167555Smsmith 88267555Smsmith 88367555Smsmith /* 88467555Smsmith * Map the command into bus space. 88567555Smsmith */ 88667555Smsmith bus_dmamap_load(sc->twe_buffer_dmat, tr->tr_cmdmap, &tr->tr_command, sizeof(tr->tr_command), 88767555Smsmith twe_setup_request_dmamap, tr, 0); 88867555Smsmith bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_cmdmap, BUS_DMASYNC_PREWRITE); 88967555Smsmith 89067555Smsmith /* 89167555Smsmith * If the command involves data, map that too. 89267555Smsmith */ 89367555Smsmith if (tr->tr_data != NULL) { 89467555Smsmith 89567555Smsmith /* 89667555Smsmith * Data must be 64-byte aligned; allocate a fixup buffer if it's not. 89767555Smsmith */ 89867555Smsmith if (((vm_offset_t)tr->tr_data % TWE_ALIGNMENT) != 0) { 89967555Smsmith tr->tr_realdata = tr->tr_data; /* save pointer to 'real' data */ 90067555Smsmith tr->tr_flags |= TWE_CMD_ALIGNBUF; 90167555Smsmith tr->tr_data = malloc(tr->tr_length, TWE_MALLOC_CLASS, M_NOWAIT); /* XXX check result here */ 90267555Smsmith } 90367555Smsmith 90467555Smsmith /* 90567555Smsmith * Map the data buffer into bus space and build the s/g list. 90667555Smsmith */ 90767555Smsmith bus_dmamap_load(sc->twe_buffer_dmat, tr->tr_dmamap, tr->tr_data, tr->tr_length, 90867555Smsmith twe_setup_data_dmamap, tr, 0); 90967555Smsmith if (tr->tr_flags & TWE_CMD_DATAIN) 91067555Smsmith bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_dmamap, BUS_DMASYNC_PREREAD); 91167555Smsmith if (tr->tr_flags & TWE_CMD_DATAOUT) { 91267555Smsmith /* if we're using an alignment buffer, and we're writing data, copy the real data out */ 91367555Smsmith if (tr->tr_flags & TWE_CMD_ALIGNBUF) 91467555Smsmith bcopy(tr->tr_realdata, tr->tr_data, tr->tr_length); 91567555Smsmith bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_dmamap, BUS_DMASYNC_PREWRITE); 91667555Smsmith } 91767555Smsmith } 91867555Smsmith} 91967555Smsmith 92067555Smsmithvoid 92167555Smsmithtwe_unmap_request(struct twe_request *tr) 92267555Smsmith{ 92367555Smsmith struct twe_softc *sc = tr->tr_sc; 92467555Smsmith 92567555Smsmith debug_called(4); 92667555Smsmith 92767555Smsmith /* 92867555Smsmith * Unmap the command from bus space. 92967555Smsmith */ 93067555Smsmith bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_cmdmap, BUS_DMASYNC_POSTWRITE); 93167555Smsmith bus_dmamap_unload(sc->twe_buffer_dmat, tr->tr_cmdmap); 93267555Smsmith 93367555Smsmith /* 93467555Smsmith * If the command involved data, unmap that too. 93567555Smsmith */ 93667555Smsmith if (tr->tr_data != NULL) { 93767555Smsmith 93867555Smsmith if (tr->tr_flags & TWE_CMD_DATAIN) { 93967555Smsmith bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_dmamap, BUS_DMASYNC_POSTREAD); 94067555Smsmith /* if we're using an alignment buffer, and we're reading data, copy the real data in */ 94167555Smsmith if (tr->tr_flags & TWE_CMD_ALIGNBUF) 94267555Smsmith bcopy(tr->tr_data, tr->tr_realdata, tr->tr_length); 94367555Smsmith } 94467555Smsmith if (tr->tr_flags & TWE_CMD_DATAOUT) 94567555Smsmith bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_dmamap, BUS_DMASYNC_POSTWRITE); 94667555Smsmith 94767555Smsmith bus_dmamap_unload(sc->twe_buffer_dmat, tr->tr_dmamap); 94867555Smsmith } 94967555Smsmith 95067555Smsmith /* free alignment buffer if it was used */ 95167555Smsmith if (tr->tr_flags & TWE_CMD_ALIGNBUF) { 95267555Smsmith free(tr->tr_data, TWE_MALLOC_CLASS); 95367555Smsmith tr->tr_data = tr->tr_realdata; /* restore 'real' data pointer */ 95467555Smsmith } 95567555Smsmith} 95667555Smsmith 95767555Smsmith#ifdef TWE_DEBUG 95867555Smsmith/******************************************************************************** 95967555Smsmith * Print current controller status, call from DDB. 96067555Smsmith */ 96167555Smsmithvoid 96267555Smsmithtwe_report(void) 96367555Smsmith{ 96467555Smsmith struct twe_softc *sc; 96567555Smsmith int i, s; 96667555Smsmith 96767555Smsmith s = splbio(); 96867555Smsmith for (i = 0; (sc = devclass_get_softc(twe_devclass, i)) != NULL; i++) 96967555Smsmith twe_print_controller(sc); 97067555Smsmith splx(s); 97167555Smsmith} 97267555Smsmith#endif 973