1139825Simp/*- 2125735Sgrehan * Copyright 2004 by Peter Grehan. All rights reserved. 3125735Sgrehan * 4125735Sgrehan * Redistribution and use in source and binary forms, with or without 5125735Sgrehan * modification, are permitted provided that the following conditions 6125735Sgrehan * are met: 7125735Sgrehan * 1. Redistributions of source code must retain the above copyright 8125735Sgrehan * notice, this list of conditions and the following disclaimer. 9125735Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 10125735Sgrehan * notice, this list of conditions and the following disclaimer in the 11125735Sgrehan * documentation and/or other materials provided with the distribution. 12125735Sgrehan * 3. The name of the author may not be used to endorse or promote products 13125735Sgrehan * derived from this software without specific prior written permission. 14125735Sgrehan * 15125735Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16125735Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17125735Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18125735Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19125735Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20125735Sgrehan * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21125735Sgrehan * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22125735Sgrehan * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23125735Sgrehan * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24125735Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25125735Sgrehan * SUCH DAMAGE. 26125735Sgrehan * 27125735Sgrehan */ 28125735Sgrehan#include <sys/cdefs.h> 29125735Sgrehan__FBSDID("$FreeBSD$"); 30125735Sgrehan 31125735Sgrehan/* 32125735Sgrehan * Mac 'Kauai' PCI ATA controller 33125735Sgrehan */ 34125735Sgrehan#include <sys/param.h> 35125735Sgrehan#include <sys/systm.h> 36125735Sgrehan#include <sys/kernel.h> 37125735Sgrehan#include <sys/module.h> 38125735Sgrehan#include <sys/bus.h> 39125735Sgrehan#include <sys/malloc.h> 40125735Sgrehan#include <sys/sema.h> 41125735Sgrehan#include <sys/taskqueue.h> 42125735Sgrehan#include <vm/uma.h> 43125735Sgrehan#include <machine/stdarg.h> 44125735Sgrehan#include <machine/resource.h> 45125735Sgrehan#include <machine/bus.h> 46125735Sgrehan#include <sys/rman.h> 47125735Sgrehan#include <sys/ata.h> 48125735Sgrehan#include <dev/ata/ata-all.h> 49144457Sgrehan#include <ata_if.h> 50125735Sgrehan 51125735Sgrehan#include <dev/ofw/openfirm.h> 52183882Snwhitehorn#include <dev/ofw/ofw_bus.h> 53183409Snwhitehorn#include <machine/intr_machdep.h> 54125735Sgrehan 55125735Sgrehan#include <dev/pci/pcivar.h> 56125735Sgrehan#include <dev/pci/pcireg.h> 57125735Sgrehan 58183409Snwhitehorn#include "ata_dbdma.h" 59183409Snwhitehorn 60125735Sgrehan#define ATA_KAUAI_REGOFFSET 0x2000 61183409Snwhitehorn#define ATA_KAUAI_DBDMAOFFSET 0x1000 62125735Sgrehan 63125735Sgrehan/* 64126394Sgrehan * Offset to alt-control register from base 65126394Sgrehan */ 66126394Sgrehan#define ATA_KAUAI_ALTOFFSET (ATA_KAUAI_REGOFFSET + 0x160) 67126394Sgrehan 68126394Sgrehan/* 69126394Sgrehan * Define the gap between registers 70126394Sgrehan */ 71126394Sgrehan#define ATA_KAUAI_REGGAP 16 72126394Sgrehan 73126394Sgrehan/* 74183409Snwhitehorn * PIO and DMA access registers 75183409Snwhitehorn */ 76183409Snwhitehorn#define PIO_CONFIG_REG (ATA_KAUAI_REGOFFSET + 0x200) 77183409Snwhitehorn#define UDMA_CONFIG_REG (ATA_KAUAI_REGOFFSET + 0x210) 78183409Snwhitehorn#define DMA_IRQ_REG (ATA_KAUAI_REGOFFSET + 0x300) 79183409Snwhitehorn 80183409Snwhitehorn#define USE_DBDMA_IRQ 0 81183409Snwhitehorn 82183409Snwhitehorn/* 83125735Sgrehan * Define the kauai pci bus attachment. 84125735Sgrehan */ 85125735Sgrehanstatic int ata_kauai_probe(device_t dev); 86183409Snwhitehornstatic int ata_kauai_attach(device_t dev); 87200171Smavstatic int ata_kauai_setmode(device_t dev, int target, int mode); 88183409Snwhitehornstatic int ata_kauai_begin_transaction(struct ata_request *request); 89125735Sgrehan 90125735Sgrehanstatic device_method_t ata_kauai_methods[] = { 91125735Sgrehan /* Device interface */ 92125735Sgrehan DEVMETHOD(device_probe, ata_kauai_probe), 93183409Snwhitehorn DEVMETHOD(device_attach, ata_kauai_attach), 94125735Sgrehan DEVMETHOD(device_detach, bus_generic_detach), 95125735Sgrehan DEVMETHOD(device_shutdown, bus_generic_shutdown), 96125735Sgrehan DEVMETHOD(device_suspend, bus_generic_suspend), 97125735Sgrehan DEVMETHOD(device_resume, bus_generic_resume), 98125735Sgrehan 99144457Sgrehan /* ATA interface */ 100144457Sgrehan DEVMETHOD(ata_setmode, ata_kauai_setmode), 101249213Smarius DEVMETHOD_END 102125735Sgrehan}; 103125735Sgrehan 104183409Snwhitehornstruct ata_kauai_softc { 105183409Snwhitehorn struct ata_dbdma_channel sc_ch; 106183409Snwhitehorn 107183409Snwhitehorn struct resource *sc_memr; 108183409Snwhitehorn 109183409Snwhitehorn int shasta; 110183409Snwhitehorn 111183409Snwhitehorn uint32_t udmaconf[2]; 112183409Snwhitehorn uint32_t wdmaconf[2]; 113183409Snwhitehorn uint32_t pioconf[2]; 114183409Snwhitehorn}; 115183409Snwhitehorn 116125735Sgrehanstatic driver_t ata_kauai_driver = { 117125735Sgrehan "ata", 118125735Sgrehan ata_kauai_methods, 119183409Snwhitehorn sizeof(struct ata_kauai_softc), 120125735Sgrehan}; 121125735Sgrehan 122249213SmariusDRIVER_MODULE(ata, pci, ata_kauai_driver, ata_devclass, NULL, NULL); 123144359SgrehanMODULE_DEPEND(ata, ata, 1, 1, 1); 124125735Sgrehan 125125735Sgrehan/* 126125735Sgrehan * PCI ID search table 127125735Sgrehan */ 128249213Smariusstatic const struct kauai_pci_dev { 129249213Smarius u_int32_t kpd_devid; 130249213Smarius const char *kpd_desc; 131125735Sgrehan} kauai_pci_devlist[] = { 132125735Sgrehan { 0x0033106b, "Uninorth2 Kauai ATA Controller" }, 133125735Sgrehan { 0x003b106b, "Intrepid Kauai ATA Controller" }, 134125735Sgrehan { 0x0043106b, "K2 Kauai ATA Controller" }, 135183409Snwhitehorn { 0x0050106b, "Shasta Kauai ATA Controller" }, 136175668Sjulian { 0x0069106b, "Intrepid-2 Kauai ATA Controller" }, 137125735Sgrehan { 0, NULL } 138125735Sgrehan}; 139125735Sgrehan 140183409Snwhitehorn/* 141183409Snwhitehorn * IDE transfer timings 142183409Snwhitehorn */ 143183409Snwhitehorn#define KAUAI_PIO_MASK 0xff000fff 144183409Snwhitehorn#define KAUAI_DMA_MASK 0x00fff000 145183409Snwhitehorn#define KAUAI_UDMA_MASK 0x0000ffff 146183409Snwhitehorn 147183409Snwhitehornstatic const u_int pio_timing_kauai[] = { 148183409Snwhitehorn 0x08000a92, /* PIO0 */ 149183409Snwhitehorn 0x0800060f, /* PIO1 */ 150183409Snwhitehorn 0x0800038b, /* PIO2 */ 151183409Snwhitehorn 0x05000249, /* PIO3 */ 152183409Snwhitehorn 0x04000148 /* PIO4 */ 153183409Snwhitehorn}; 154249213Smarius 155183409Snwhitehornstatic const u_int pio_timing_shasta[] = { 156183409Snwhitehorn 0x0a000c97, /* PIO0 */ 157183409Snwhitehorn 0x07000712, /* PIO1 */ 158183409Snwhitehorn 0x040003cd, /* PIO2 */ 159183409Snwhitehorn 0x0400028b, /* PIO3 */ 160183409Snwhitehorn 0x0400010a /* PIO4 */ 161183409Snwhitehorn}; 162183409Snwhitehorn 163183409Snwhitehornstatic const u_int dma_timing_kauai[] = { 164183409Snwhitehorn 0x00618000, /* WDMA0 */ 165183409Snwhitehorn 0x00209000, /* WDMA1 */ 166183409Snwhitehorn 0x00148000 /* WDMA2 */ 167183409Snwhitehorn}; 168249213Smarius 169183409Snwhitehornstatic const u_int dma_timing_shasta[] = { 170183409Snwhitehorn 0x00820800, /* WDMA0 */ 171183409Snwhitehorn 0x0028b000, /* WDMA1 */ 172183409Snwhitehorn 0x001ca000 /* WDMA2 */ 173183409Snwhitehorn}; 174183409Snwhitehorn 175183409Snwhitehornstatic const u_int udma_timing_kauai[] = { 176183409Snwhitehorn 0x000070c1, /* UDMA0 */ 177183409Snwhitehorn 0x00005d81, /* UDMA1 */ 178183409Snwhitehorn 0x00004a61, /* UDMA2 */ 179183409Snwhitehorn 0x00003a51, /* UDMA3 */ 180183409Snwhitehorn 0x00002a31, /* UDMA4 */ 181183409Snwhitehorn 0x00002921 /* UDMA5 */ 182183409Snwhitehorn}; 183249213Smarius 184183409Snwhitehornstatic const u_int udma_timing_shasta[] = { 185183409Snwhitehorn 0x00035901, /* UDMA0 */ 186183409Snwhitehorn 0x000348b1, /* UDMA1 */ 187183409Snwhitehorn 0x00033881, /* UDMA2 */ 188183409Snwhitehorn 0x00033861, /* UDMA3 */ 189183409Snwhitehorn 0x00033841, /* UDMA4 */ 190183409Snwhitehorn 0x00033031, /* UDMA5 */ 191183409Snwhitehorn 0x00033021 /* UDMA6 */ 192183409Snwhitehorn}; 193183409Snwhitehorn 194125735Sgrehanstatic int 195125735Sgrehanata_kauai_probe(device_t dev) 196125735Sgrehan{ 197183409Snwhitehorn struct ata_kauai_softc *sc; 198125735Sgrehan u_int32_t devid; 199183409Snwhitehorn phandle_t node; 200183882Snwhitehorn const char *compatstring = NULL; 201256555Sandreast int i, found; 202125735Sgrehan 203125735Sgrehan found = 0; 204125735Sgrehan devid = pci_get_devid(dev); 205125735Sgrehan for (i = 0; kauai_pci_devlist[i].kpd_desc != NULL; i++) { 206125735Sgrehan if (devid == kauai_pci_devlist[i].kpd_devid) { 207125735Sgrehan found = 1; 208125735Sgrehan device_set_desc(dev, kauai_pci_devlist[i].kpd_desc); 209125735Sgrehan } 210125735Sgrehan } 211125735Sgrehan 212125735Sgrehan if (!found) 213125735Sgrehan return (ENXIO); 214125735Sgrehan 215183882Snwhitehorn node = ofw_bus_get_node(dev); 216183409Snwhitehorn sc = device_get_softc(dev); 217183409Snwhitehorn bzero(sc, sizeof(struct ata_kauai_softc)); 218183409Snwhitehorn 219183882Snwhitehorn compatstring = ofw_bus_get_compat(dev); 220217756Snwhitehorn if (compatstring != NULL && strcmp(compatstring,"shasta-ata") == 0) 221183409Snwhitehorn sc->shasta = 1; 222183409Snwhitehorn 223208168Snwhitehorn /* Pre-K2 controllers apparently need this hack */ 224208168Snwhitehorn if (!sc->shasta && 225208168Snwhitehorn (compatstring == NULL || strcmp(compatstring, "K2-UATA") != 0)) 226183882Snwhitehorn bus_set_resource(dev, SYS_RES_IRQ, 0, 39, 1); 227183409Snwhitehorn 228256555Sandreast return (ata_probe(dev)); 229256555Sandreast} 230256555Sandreast 231256555Sandreast#if USE_DBDMA_IRQ 232256555Sandreaststatic int 233256555Sandreastata_kauai_dma_interrupt(struct ata_kauai_softc *sc) 234256555Sandreast{ 235256555Sandreast /* Clear the DMA interrupt bits */ 236256555Sandreast 237256555Sandreast bus_write_4(sc->sc_memr, DMA_IRQ_REG, 0x80000000); 238256555Sandreast 239256555Sandreast return ata_interrupt(sc); 240256555Sandreast} 241256555Sandreast#endif 242256555Sandreast 243256555Sandreaststatic int 244256555Sandreastata_kauai_attach(device_t dev) 245256555Sandreast{ 246256555Sandreast struct ata_kauai_softc *sc = device_get_softc(dev); 247256555Sandreast struct ata_channel *ch; 248256555Sandreast int i, rid; 249256555Sandreast#if USE_DBDMA_IRQ 250256555Sandreast int dbdma_irq_rid = 1; 251256555Sandreast struct resource *dbdma_irq; 252256555Sandreast void *cookie; 253256555Sandreast#endif 254256555Sandreast 255256555Sandreast ch = &sc->sc_ch.sc_ch; 256256555Sandreast 257126394Sgrehan rid = PCIR_BARS; 258183409Snwhitehorn sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 259183409Snwhitehorn RF_ACTIVE); 260183409Snwhitehorn if (sc->sc_memr == NULL) { 261125735Sgrehan device_printf(dev, "could not allocate memory\n"); 262125735Sgrehan return (ENXIO); 263125735Sgrehan } 264125735Sgrehan 265125735Sgrehan /* 266125735Sgrehan * Set up the resource vectors 267125735Sgrehan */ 268145221Sgrehan for (i = ATA_DATA; i <= ATA_COMMAND; i++) { 269183409Snwhitehorn ch->r_io[i].res = sc->sc_memr; 270126394Sgrehan ch->r_io[i].offset = i*ATA_KAUAI_REGGAP + ATA_KAUAI_REGOFFSET; 271125735Sgrehan } 272183409Snwhitehorn ch->r_io[ATA_CONTROL].res = sc->sc_memr; 273145311Sgrehan ch->r_io[ATA_CONTROL].offset = ATA_KAUAI_ALTOFFSET; 274145772Sgrehan ata_default_registers(dev); 275125735Sgrehan 276217756Snwhitehorn ch->unit = 0; 277217756Snwhitehorn ch->flags |= ATA_USE_16BIT; 278256555Sandreast 279217756Snwhitehorn /* XXX: ATAPI DMA is unreliable. We should find out why. */ 280217756Snwhitehorn ch->flags |= ATA_NO_ATAPI_DMA; 281145772Sgrehan ata_generic_hw(dev); 282125735Sgrehan 283183409Snwhitehorn pci_enable_busmaster(dev); 284183409Snwhitehorn 285183409Snwhitehorn /* Init DMA engine */ 286183409Snwhitehorn 287183409Snwhitehorn sc->sc_ch.dbdma_rid = 1; 288183409Snwhitehorn sc->sc_ch.dbdma_regs = sc->sc_memr; 289183409Snwhitehorn sc->sc_ch.dbdma_offset = ATA_KAUAI_DBDMAOFFSET; 290183409Snwhitehorn 291183409Snwhitehorn ata_dbdma_dmainit(dev); 292183409Snwhitehorn 293183409Snwhitehorn#if USE_DBDMA_IRQ 294183409Snwhitehorn /* Bind to DBDMA interrupt as well */ 295183409Snwhitehorn if ((dbdma_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 296183409Snwhitehorn &dbdma_irq_rid, RF_SHAREABLE | RF_ACTIVE)) != NULL) { 297183409Snwhitehorn bus_setup_intr(dev, dbdma_irq, ATA_INTR_FLAGS, NULL, 298183409Snwhitehorn (driver_intr_t *)ata_kauai_dma_interrupt, sc,&cookie); 299183409Snwhitehorn } 300183409Snwhitehorn#endif 301183409Snwhitehorn 302183409Snwhitehorn /* Set up initial mode */ 303184314Snwhitehorn sc->pioconf[0] = sc->pioconf[1] = 304184314Snwhitehorn bus_read_4(sc->sc_memr, PIO_CONFIG_REG) & 0x0f000fff; 305183409Snwhitehorn 306183409Snwhitehorn sc->udmaconf[0] = sc->udmaconf[1] = 0; 307183409Snwhitehorn sc->wdmaconf[0] = sc->wdmaconf[1] = 0; 308183409Snwhitehorn 309183409Snwhitehorn /* Magic FCR value from Apple */ 310183409Snwhitehorn bus_write_4(sc->sc_memr, 0, 0x00000007); 311183409Snwhitehorn 312183409Snwhitehorn /* Set begin_transaction */ 313183409Snwhitehorn sc->sc_ch.sc_ch.hw.begin_transaction = ata_kauai_begin_transaction; 314183409Snwhitehorn 315183409Snwhitehorn return ata_attach(dev); 316183409Snwhitehorn} 317183409Snwhitehorn 318200171Smavstatic int 319200171Smavata_kauai_setmode(device_t dev, int target, int mode) 320144457Sgrehan{ 321200171Smav struct ata_kauai_softc *sc = device_get_softc(dev); 322144457Sgrehan 323200171Smav mode = min(mode,sc->shasta ? ATA_UDMA6 : ATA_UDMA5); 324183409Snwhitehorn 325183409Snwhitehorn if (sc->shasta) { 326183409Snwhitehorn switch (mode & ATA_DMA_MASK) { 327183409Snwhitehorn case ATA_UDMA0: 328200171Smav sc->udmaconf[target] 329183409Snwhitehorn = udma_timing_shasta[mode & ATA_MODE_MASK]; 330183409Snwhitehorn break; 331183409Snwhitehorn case ATA_WDMA0: 332200171Smav sc->udmaconf[target] = 0; 333200171Smav sc->wdmaconf[target] 334183409Snwhitehorn = dma_timing_shasta[mode & ATA_MODE_MASK]; 335183409Snwhitehorn break; 336183409Snwhitehorn default: 337200171Smav sc->pioconf[target] 338183409Snwhitehorn = pio_timing_shasta[(mode & ATA_MODE_MASK) - 339183409Snwhitehorn ATA_PIO0]; 340183409Snwhitehorn break; 341183409Snwhitehorn } 342183409Snwhitehorn } else { 343183409Snwhitehorn switch (mode & ATA_DMA_MASK) { 344183409Snwhitehorn case ATA_UDMA0: 345200171Smav sc->udmaconf[target] 346183409Snwhitehorn = udma_timing_kauai[mode & ATA_MODE_MASK]; 347183409Snwhitehorn break; 348183409Snwhitehorn case ATA_WDMA0: 349200171Smav sc->udmaconf[target] = 0; 350200171Smav sc->wdmaconf[target] 351183409Snwhitehorn = dma_timing_kauai[mode & ATA_MODE_MASK]; 352183409Snwhitehorn break; 353183409Snwhitehorn default: 354200171Smav sc->pioconf[target] 355183409Snwhitehorn = pio_timing_kauai[(mode & ATA_MODE_MASK) 356183409Snwhitehorn - ATA_PIO0]; 357183409Snwhitehorn break; 358183409Snwhitehorn } 359183409Snwhitehorn } 360200171Smav 361200171Smav return (mode); 362144457Sgrehan} 363183409Snwhitehorn 364183409Snwhitehornstatic int 365183409Snwhitehornata_kauai_begin_transaction(struct ata_request *request) 366183409Snwhitehorn{ 367183409Snwhitehorn struct ata_kauai_softc *sc = device_get_softc(request->parent); 368183409Snwhitehorn 369200171Smav bus_write_4(sc->sc_memr, UDMA_CONFIG_REG, sc->udmaconf[request->unit]); 370183409Snwhitehorn bus_write_4(sc->sc_memr, PIO_CONFIG_REG, 371200171Smav sc->wdmaconf[request->unit] | sc->pioconf[request->unit]); 372183409Snwhitehorn 373183409Snwhitehorn return ata_begin_transaction(request); 374183409Snwhitehorn} 375