Loading drivers/scsi/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -1779,6 +1779,7 @@ config SUN3_SCSI config SUN3X_ESP bool "Sun3x ESP SCSI" depends on SUN3X && SCSI=y select SCSI_SPI_ATTRS help The ESP was an on-board SCSI controller used on Sun 3/80 machines. Say Y here to compile in support for it. Loading drivers/scsi/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -118,7 +118,7 @@ obj-$(CONFIG_SCSI_3W_9XXX) += 3w-9xxx.o obj-$(CONFIG_SCSI_PPA) += ppa.o obj-$(CONFIG_SCSI_IMM) += imm.o obj-$(CONFIG_JAZZ_ESP) += esp_scsi.o jazz_esp.o obj-$(CONFIG_SUN3X_ESP) += NCR53C9x.o sun3x_esp.o obj-$(CONFIG_SUN3X_ESP) += esp_scsi.o sun3x_esp.o obj-$(CONFIG_SCSI_LASI700) += 53c700.o lasi700.o obj-$(CONFIG_SCSI_SNI_53C710) += 53c700.o sni_53c710.o obj-$(CONFIG_SCSI_NSP32) += nsp32.o Loading drivers/scsi/sun3x_esp.c +235 −311 Original line number Diff line number Diff line /* sun3x_esp.c: EnhancedScsiProcessor Sun3x SCSI driver code. /* sun3x_esp.c: ESP front-end for Sun3x systems. * * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de) * * Based on David S. Miller's esp driver * Copyright (C) 2007,2008 Thomas Bogendoerfer (tsbogend@alpha.franken.de) */ #include <linux/kernel.h> #include <linux/types.h> #include <linux/string.h> #include <linux/slab.h> #include <linux/blkdev.h> #include <linux/proc_fs.h> #include <linux/stat.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h> #include "scsi.h" #include <scsi/scsi_host.h> #include "NCR53C9x.h" #include <asm/sun3x.h> #include <asm/io.h> #include <asm/dma.h> #include <asm/dvma.h> #include <asm/irq.h> static void dma_barrier(struct NCR_ESP *esp); static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count); static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp); static void dma_drain(struct NCR_ESP *esp); static void dma_invalidate(struct NCR_ESP *esp); static void dma_dump_state(struct NCR_ESP *esp); static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length); static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length); static void dma_ints_off(struct NCR_ESP *esp); static void dma_ints_on(struct NCR_ESP *esp); static int dma_irq_p(struct NCR_ESP *esp); static void dma_poll(struct NCR_ESP *esp, unsigned char *vaddr); static int dma_ports_p(struct NCR_ESP *esp); static void dma_reset(struct NCR_ESP *esp); static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write); static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp); static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp); static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp); static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp); static void dma_advance_sg (Scsi_Cmnd *sp); /* Detecting ESP chips on the machine. This is the simple and easy * version. */ int sun3x_esp_detect(struct scsi_host_template *tpnt) { struct NCR_ESP *esp; struct ConfigDev *esp_dev; esp_dev = 0; esp = esp_allocate(tpnt, esp_dev, 0); /* Do command transfer with DMA */ esp->do_pio_cmds = 0; /* Required functions */ esp->dma_bytes_sent = &dma_bytes_sent; esp->dma_can_transfer = &dma_can_transfer; esp->dma_dump_state = &dma_dump_state; esp->dma_init_read = &dma_init_read; esp->dma_init_write = &dma_init_write; esp->dma_ints_off = &dma_ints_off; esp->dma_ints_on = &dma_ints_on; esp->dma_irq_p = &dma_irq_p; esp->dma_ports_p = &dma_ports_p; esp->dma_setup = &dma_setup; /* Optional functions */ esp->dma_barrier = &dma_barrier; esp->dma_invalidate = &dma_invalidate; esp->dma_drain = &dma_drain; esp->dma_irq_entry = 0; esp->dma_irq_exit = 0; esp->dma_led_on = 0; esp->dma_led_off = 0; esp->dma_poll = &dma_poll; esp->dma_reset = &dma_reset; /* virtual DMA functions */ esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one; esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl; esp->dma_mmu_release_scsi_one = &dma_mmu_release_scsi_one; esp->dma_mmu_release_scsi_sgl = &dma_mmu_release_scsi_sgl; esp->dma_advance_sg = &dma_advance_sg; /* SCSI chip speed */ esp->cfreq = 20000000; esp->eregs = (struct ESP_regs *)(SUN3X_ESP_BASE); esp->dregs = (void *)SUN3X_ESP_DMA; esp->esp_command = (volatile unsigned char *)dvma_malloc(DVMA_PAGE_SIZE); esp->esp_command_dvma = dvma_vtob((unsigned long)esp->esp_command); /* DMA controller reg offsets */ #define DMA_CSR 0x00UL /* rw DMA control/status register 0x00 */ #define DMA_ADDR 0x04UL /* rw DMA transfer address register 0x04 */ #define DMA_COUNT 0x08UL /* rw DMA transfer count register 0x08 */ #define DMA_TEST 0x0cUL /* rw DMA test/debug register 0x0c */ esp->irq = 2; if (request_irq(esp->irq, esp_intr, IRQF_DISABLED, "SUN3X SCSI", esp->ehost)) { esp_deallocate(esp); return 0; } esp->scsi_id = 7; esp->diff = 0; #include <scsi/scsi_host.h> esp_initialize(esp); #include "esp_scsi.h" /* for reasons beyond my knowledge (and which should likely be fixed) sync mode doesn't work on a 3/80 at 5mhz. but it does at 4. */ esp->sync_defp = 0x3f; #define DRV_MODULE_NAME "sun3x_esp" #define PFX DRV_MODULE_NAME ": " #define DRV_VERSION "1.000" #define DRV_MODULE_RELDATE "Nov 1, 2007" printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps, esps_in_use); esps_running = esps_in_use; return esps_in_use; /* * m68k always assumes readl/writel operate on little endian * mmio space; this is wrong at least for Sun3x, so we * need to workaround this until a proper way is found */ #if 0 #define dma_read32(REG) \ readl(esp->dma_regs + (REG)) #define dma_write32(VAL, REG) \ writel((VAL), esp->dma_regs + (REG)) #else #define dma_read32(REG) \ *(volatile u32 *)(esp->dma_regs + (REG)) #define dma_write32(VAL, REG) \ do { *(volatile u32 *)(esp->dma_regs + (REG)) = (VAL); } while (0) #endif static void sun3x_esp_write8(struct esp *esp, u8 val, unsigned long reg) { writeb(val, esp->regs + (reg * 4UL)); } static void dma_do_drain(struct NCR_ESP *esp) static u8 sun3x_esp_read8(struct esp *esp, unsigned long reg) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; int count = 500000; while((dregs->cond_reg & DMA_PEND_READ) && (--count > 0)) udelay(1); if(!count) { printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg); return readb(esp->regs + (reg * 4UL)); } dregs->cond_reg |= DMA_FIFO_STDRAIN; count = 500000; while((dregs->cond_reg & DMA_FIFO_ISDRAIN) && (--count > 0)) udelay(1); if(!count) { printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg); static dma_addr_t sun3x_esp_map_single(struct esp *esp, void *buf, size_t sz, int dir) { return dma_map_single(esp->dev, buf, sz, dir); } static int sun3x_esp_map_sg(struct esp *esp, struct scatterlist *sg, int num_sg, int dir) { return dma_map_sg(esp->dev, sg, num_sg, dir); } static void dma_barrier(struct NCR_ESP *esp) static void sun3x_esp_unmap_single(struct esp *esp, dma_addr_t addr, size_t sz, int dir) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; int count = 500000; while((dregs->cond_reg & DMA_PEND_READ) && (--count > 0)) udelay(1); dma_unmap_single(esp->dev, addr, sz, dir); } if(!count) { printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg); static void sun3x_esp_unmap_sg(struct esp *esp, struct scatterlist *sg, int num_sg, int dir) { dma_unmap_sg(esp->dev, sg, num_sg, dir); } dregs->cond_reg &= ~(DMA_ENABLE); static int sun3x_esp_irq_pending(struct esp *esp) { if (dma_read32(DMA_CSR) & (DMA_HNDL_INTR | DMA_HNDL_ERROR)) return 1; return 0; } /* This uses various DMA csr fields and the fifo flags count value to * determine how many bytes were successfully sent/received by the ESP. */ static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count) static void sun3x_esp_reset_dma(struct esp *esp) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; u32 val; int rval = dregs->st_addr - esp->esp_command_dvma; val = dma_read32(DMA_CSR); dma_write32(val | DMA_RST_SCSI, DMA_CSR); dma_write32(val & ~DMA_RST_SCSI, DMA_CSR); return rval - fifo_count; /* Enable interrupts. */ val = dma_read32(DMA_CSR); dma_write32(val | DMA_INT_ENAB, DMA_CSR); } static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp) static void sun3x_esp_dma_drain(struct esp *esp) { return sp->SCp.this_residual; } u32 csr; int lim; static void dma_drain(struct NCR_ESP *esp) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; int count = 500000; csr = dma_read32(DMA_CSR); if (!(csr & DMA_FIFO_ISDRAIN)) return; if(dregs->cond_reg & DMA_FIFO_ISDRAIN) { dregs->cond_reg |= DMA_FIFO_STDRAIN; while((dregs->cond_reg & DMA_FIFO_ISDRAIN) && (--count > 0)) udelay(1); if(!count) { printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg); } dma_write32(csr | DMA_FIFO_STDRAIN, DMA_CSR); lim = 1000; while (dma_read32(DMA_CSR) & DMA_FIFO_ISDRAIN) { if (--lim == 0) { printk(KERN_ALERT PFX "esp%d: DMA will not drain!\n", esp->host->unique_id); break; } udelay(1); } } static void dma_invalidate(struct NCR_ESP *esp) static void sun3x_esp_dma_invalidate(struct esp *esp) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; __u32 tmp; int count = 500000; u32 val; int lim; while(((tmp = dregs->cond_reg) & DMA_PEND_READ) && (--count > 0)) lim = 1000; while ((val = dma_read32(DMA_CSR)) & DMA_PEND_READ) { if (--lim == 0) { printk(KERN_ALERT PFX "esp%d: DMA will not " "invalidate!\n", esp->host->unique_id); break; } udelay(1); if(!count) { printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg); } dregs->cond_reg = tmp | DMA_FIFO_INV; dregs->cond_reg &= ~DMA_FIFO_INV; val &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB); val |= DMA_FIFO_INV; dma_write32(val, DMA_CSR); val &= ~DMA_FIFO_INV; dma_write32(val, DMA_CSR); } static void dma_dump_state(struct NCR_ESP *esp) static void sun3x_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count, u32 dma_count, int write, u8 cmd) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; u32 csr; ESPLOG(("esp%d: dma -- cond_reg<%08lx> addr<%08lx>\n", esp->esp_id, dregs->cond_reg, dregs->st_addr)); } BUG_ON(!(cmd & ESP_CMD_DMA)); static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; sun3x_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW); sun3x_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED); csr = dma_read32(DMA_CSR); csr |= DMA_ENABLE; if (write) csr |= DMA_ST_WRITE; else csr &= ~DMA_ST_WRITE; dma_write32(csr, DMA_CSR); dma_write32(addr, DMA_ADDR); dregs->st_addr = vaddress; dregs->cond_reg |= (DMA_ST_WRITE | DMA_ENABLE); scsi_esp_cmd(esp, cmd); } static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length) static int sun3x_esp_dma_error(struct esp *esp) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; u32 csr = dma_read32(DMA_CSR); /* Set up the DMA counters */ if (csr & DMA_HNDL_ERROR) return 1; dregs->st_addr = vaddress; dregs->cond_reg = ((dregs->cond_reg & ~(DMA_ST_WRITE)) | DMA_ENABLE); return 0; } static void dma_ints_off(struct NCR_ESP *esp) { DMA_INTSOFF((struct sparc_dma_registers *) esp->dregs); } static const struct esp_driver_ops sun3x_esp_ops = { .esp_write8 = sun3x_esp_write8, .esp_read8 = sun3x_esp_read8, .map_single = sun3x_esp_map_single, .map_sg = sun3x_esp_map_sg, .unmap_single = sun3x_esp_unmap_single, .unmap_sg = sun3x_esp_unmap_sg, .irq_pending = sun3x_esp_irq_pending, .reset_dma = sun3x_esp_reset_dma, .dma_drain = sun3x_esp_dma_drain, .dma_invalidate = sun3x_esp_dma_invalidate, .send_dma_cmd = sun3x_esp_send_dma_cmd, .dma_error = sun3x_esp_dma_error, }; static void dma_ints_on(struct NCR_ESP *esp) static int __devinit esp_sun3x_probe(struct platform_device *dev) { DMA_INTSON((struct sparc_dma_registers *) esp->dregs); } struct scsi_host_template *tpnt = &scsi_esp_template; struct Scsi_Host *host; struct esp *esp; struct resource *res; int err = -ENOMEM; static int dma_irq_p(struct NCR_ESP *esp) { return DMA_IRQ_P((struct sparc_dma_registers *) esp->dregs); } host = scsi_host_alloc(tpnt, sizeof(struct esp)); if (!host) goto fail; static void dma_poll(struct NCR_ESP *esp, unsigned char *vaddr) { int count = 50; dma_do_drain(esp); host->max_id = 8; esp = shost_priv(host); /* Wait till the first bits settle. */ while((*(volatile unsigned char *)vaddr == 0xff) && (--count > 0)) udelay(1); esp->host = host; esp->dev = dev; esp->ops = &sun3x_esp_ops; if(!count) { // printk("%s:%d timeout expire (data %02x)\n", __FILE__, __LINE__, // esp_read(esp->eregs->esp_fdata)); //mach_halt(); vaddr[0] = esp_read(esp->eregs->esp_fdata); vaddr[1] = esp_read(esp->eregs->esp_fdata); } res = platform_get_resource(dev, IORESOURCE_MEM, 0); if (!res && !res->start) goto fail_unlink; } esp->regs = ioremap_nocache(res->start, 0x20); if (!esp->regs) goto fail_unmap_regs; static int dma_ports_p(struct NCR_ESP *esp) { return (((struct sparc_dma_registers *) esp->dregs)->cond_reg & DMA_INT_ENAB); } res = platform_get_resource(dev, IORESOURCE_MEM, 1); if (!res && !res->start) goto fail_unmap_regs; /* Resetting various pieces of the ESP scsi driver chipset/buses. */ static void dma_reset(struct NCR_ESP *esp) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *)esp->dregs; esp->dma_regs = ioremap_nocache(res->start, 0x10); /* Punt the DVMA into a known state. */ dregs->cond_reg |= DMA_RST_SCSI; dregs->cond_reg &= ~(DMA_RST_SCSI); DMA_INTSON(dregs); } esp->command_block = dma_alloc_coherent(esp->dev, 16, &esp->command_block_dma, GFP_KERNEL); if (!esp->command_block) goto fail_unmap_regs_dma; static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; unsigned long nreg = dregs->cond_reg; host->irq = platform_get_irq(dev, 0); err = request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "SUN3X ESP", esp); if (err < 0) goto fail_unmap_command_block; // printk("dma_setup %c addr %08x cnt %08x\n", // write ? 'W' : 'R', addr, count); esp->scsi_id = 7; esp->host->this_id = esp->scsi_id; esp->scsi_id_mask = (1 << esp->scsi_id); esp->cfreq = 20000000; dma_do_drain(esp); dev_set_drvdata(&dev->dev, esp); if(write) nreg |= DMA_ST_WRITE; else { nreg &= ~(DMA_ST_WRITE); } err = scsi_esp_register(esp, &dev->dev); if (err) goto fail_free_irq; nreg |= DMA_ENABLE; dregs->cond_reg = nreg; dregs->st_addr = addr; } return 0; static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp) fail_free_irq: free_irq(host->irq, esp); fail_unmap_command_block: dma_free_coherent(esp->dev, 16, esp->command_block, esp->command_block_dma); fail_unmap_regs_dma: iounmap(esp->dma_regs); fail_unmap_regs: iounmap(esp->regs); fail_unlink: scsi_host_put(host); fail: return err; } static int __devexit esp_sun3x_remove(struct platform_device *dev) { sp->SCp.have_data_in = dvma_map((unsigned long)sp->SCp.buffer, sp->SCp.this_residual); sp->SCp.ptr = (char *)((unsigned long)sp->SCp.have_data_in); } struct esp *esp = dev_get_drvdata(&dev->dev); unsigned int irq = esp->host->irq; u32 val; static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp) { int sz = sp->SCp.buffers_residual; struct scatterlist *sg = sp->SCp.buffer; scsi_esp_unregister(esp); while (sz >= 0) { sg[sz].dma_address = dvma_map((unsigned long)sg_virt(&sg[sz]), sg[sz].length); sz--; } sp->SCp.ptr=(char *)((unsigned long)sp->SCp.buffer->dma_address); } /* Disable interrupts. */ val = dma_read32(DMA_CSR); dma_write32(val & ~DMA_INT_ENAB, DMA_CSR); static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp) { dvma_unmap((char *)sp->SCp.have_data_in); } free_irq(irq, esp); dma_free_coherent(esp->dev, 16, esp->command_block, esp->command_block_dma); static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp) { int sz = sp->use_sg - 1; struct scatterlist *sg = (struct scatterlist *)sp->request_buffer; scsi_host_put(esp->host); while(sz >= 0) { dvma_unmap((char *)sg[sz].dma_address); sz--; } return 0; } static void dma_advance_sg (Scsi_Cmnd *sp) static struct platform_driver esp_sun3x_driver = { .probe = esp_sun3x_probe, .remove = __devexit_p(esp_sun3x_remove), .driver = { .name = "sun3x_esp", }, }; static int __init sun3x_esp_init(void) { sp->SCp.ptr = (char *)((unsigned long)sp->SCp.buffer->dma_address); return platform_driver_register(&esp_sun3x_driver); } static int sun3x_esp_release(struct Scsi_Host *instance) static void __exit sun3x_esp_exit(void) { /* this code does not support being compiled as a module */ return 1; platform_driver_unregister(&esp_sun3x_driver); } static struct scsi_host_template driver_template = { .proc_name = "sun3x_esp", .proc_info = &esp_proc_info, .name = "Sun ESP 100/100a/200", .detect = sun3x_esp_detect, .release = sun3x_esp_release, .slave_alloc = esp_slave_alloc, .slave_destroy = esp_slave_destroy, .info = esp_info, .queuecommand = esp_queue, .eh_abort_handler = esp_abort, .eh_bus_reset_handler = esp_reset, .can_queue = 7, .this_id = 7, .sg_tablesize = SG_ALL, .cmd_per_lun = 1, .use_clustering = DISABLE_CLUSTERING, }; #include "scsi_module.c" MODULE_DESCRIPTION("Sun3x ESP SCSI driver"); MODULE_AUTHOR("Thomas Bogendoerfer (tsbogend@alpha.franken.de)"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); module_init(sun3x_esp_init); module_exit(sun3x_esp_exit); Loading
drivers/scsi/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -1779,6 +1779,7 @@ config SUN3_SCSI config SUN3X_ESP bool "Sun3x ESP SCSI" depends on SUN3X && SCSI=y select SCSI_SPI_ATTRS help The ESP was an on-board SCSI controller used on Sun 3/80 machines. Say Y here to compile in support for it. Loading
drivers/scsi/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -118,7 +118,7 @@ obj-$(CONFIG_SCSI_3W_9XXX) += 3w-9xxx.o obj-$(CONFIG_SCSI_PPA) += ppa.o obj-$(CONFIG_SCSI_IMM) += imm.o obj-$(CONFIG_JAZZ_ESP) += esp_scsi.o jazz_esp.o obj-$(CONFIG_SUN3X_ESP) += NCR53C9x.o sun3x_esp.o obj-$(CONFIG_SUN3X_ESP) += esp_scsi.o sun3x_esp.o obj-$(CONFIG_SCSI_LASI700) += 53c700.o lasi700.o obj-$(CONFIG_SCSI_SNI_53C710) += 53c700.o sni_53c710.o obj-$(CONFIG_SCSI_NSP32) += nsp32.o Loading
drivers/scsi/sun3x_esp.c +235 −311 Original line number Diff line number Diff line /* sun3x_esp.c: EnhancedScsiProcessor Sun3x SCSI driver code. /* sun3x_esp.c: ESP front-end for Sun3x systems. * * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de) * * Based on David S. Miller's esp driver * Copyright (C) 2007,2008 Thomas Bogendoerfer (tsbogend@alpha.franken.de) */ #include <linux/kernel.h> #include <linux/types.h> #include <linux/string.h> #include <linux/slab.h> #include <linux/blkdev.h> #include <linux/proc_fs.h> #include <linux/stat.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h> #include "scsi.h" #include <scsi/scsi_host.h> #include "NCR53C9x.h" #include <asm/sun3x.h> #include <asm/io.h> #include <asm/dma.h> #include <asm/dvma.h> #include <asm/irq.h> static void dma_barrier(struct NCR_ESP *esp); static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count); static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp); static void dma_drain(struct NCR_ESP *esp); static void dma_invalidate(struct NCR_ESP *esp); static void dma_dump_state(struct NCR_ESP *esp); static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length); static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length); static void dma_ints_off(struct NCR_ESP *esp); static void dma_ints_on(struct NCR_ESP *esp); static int dma_irq_p(struct NCR_ESP *esp); static void dma_poll(struct NCR_ESP *esp, unsigned char *vaddr); static int dma_ports_p(struct NCR_ESP *esp); static void dma_reset(struct NCR_ESP *esp); static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write); static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp); static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp); static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp); static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp); static void dma_advance_sg (Scsi_Cmnd *sp); /* Detecting ESP chips on the machine. This is the simple and easy * version. */ int sun3x_esp_detect(struct scsi_host_template *tpnt) { struct NCR_ESP *esp; struct ConfigDev *esp_dev; esp_dev = 0; esp = esp_allocate(tpnt, esp_dev, 0); /* Do command transfer with DMA */ esp->do_pio_cmds = 0; /* Required functions */ esp->dma_bytes_sent = &dma_bytes_sent; esp->dma_can_transfer = &dma_can_transfer; esp->dma_dump_state = &dma_dump_state; esp->dma_init_read = &dma_init_read; esp->dma_init_write = &dma_init_write; esp->dma_ints_off = &dma_ints_off; esp->dma_ints_on = &dma_ints_on; esp->dma_irq_p = &dma_irq_p; esp->dma_ports_p = &dma_ports_p; esp->dma_setup = &dma_setup; /* Optional functions */ esp->dma_barrier = &dma_barrier; esp->dma_invalidate = &dma_invalidate; esp->dma_drain = &dma_drain; esp->dma_irq_entry = 0; esp->dma_irq_exit = 0; esp->dma_led_on = 0; esp->dma_led_off = 0; esp->dma_poll = &dma_poll; esp->dma_reset = &dma_reset; /* virtual DMA functions */ esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one; esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl; esp->dma_mmu_release_scsi_one = &dma_mmu_release_scsi_one; esp->dma_mmu_release_scsi_sgl = &dma_mmu_release_scsi_sgl; esp->dma_advance_sg = &dma_advance_sg; /* SCSI chip speed */ esp->cfreq = 20000000; esp->eregs = (struct ESP_regs *)(SUN3X_ESP_BASE); esp->dregs = (void *)SUN3X_ESP_DMA; esp->esp_command = (volatile unsigned char *)dvma_malloc(DVMA_PAGE_SIZE); esp->esp_command_dvma = dvma_vtob((unsigned long)esp->esp_command); /* DMA controller reg offsets */ #define DMA_CSR 0x00UL /* rw DMA control/status register 0x00 */ #define DMA_ADDR 0x04UL /* rw DMA transfer address register 0x04 */ #define DMA_COUNT 0x08UL /* rw DMA transfer count register 0x08 */ #define DMA_TEST 0x0cUL /* rw DMA test/debug register 0x0c */ esp->irq = 2; if (request_irq(esp->irq, esp_intr, IRQF_DISABLED, "SUN3X SCSI", esp->ehost)) { esp_deallocate(esp); return 0; } esp->scsi_id = 7; esp->diff = 0; #include <scsi/scsi_host.h> esp_initialize(esp); #include "esp_scsi.h" /* for reasons beyond my knowledge (and which should likely be fixed) sync mode doesn't work on a 3/80 at 5mhz. but it does at 4. */ esp->sync_defp = 0x3f; #define DRV_MODULE_NAME "sun3x_esp" #define PFX DRV_MODULE_NAME ": " #define DRV_VERSION "1.000" #define DRV_MODULE_RELDATE "Nov 1, 2007" printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps, esps_in_use); esps_running = esps_in_use; return esps_in_use; /* * m68k always assumes readl/writel operate on little endian * mmio space; this is wrong at least for Sun3x, so we * need to workaround this until a proper way is found */ #if 0 #define dma_read32(REG) \ readl(esp->dma_regs + (REG)) #define dma_write32(VAL, REG) \ writel((VAL), esp->dma_regs + (REG)) #else #define dma_read32(REG) \ *(volatile u32 *)(esp->dma_regs + (REG)) #define dma_write32(VAL, REG) \ do { *(volatile u32 *)(esp->dma_regs + (REG)) = (VAL); } while (0) #endif static void sun3x_esp_write8(struct esp *esp, u8 val, unsigned long reg) { writeb(val, esp->regs + (reg * 4UL)); } static void dma_do_drain(struct NCR_ESP *esp) static u8 sun3x_esp_read8(struct esp *esp, unsigned long reg) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; int count = 500000; while((dregs->cond_reg & DMA_PEND_READ) && (--count > 0)) udelay(1); if(!count) { printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg); return readb(esp->regs + (reg * 4UL)); } dregs->cond_reg |= DMA_FIFO_STDRAIN; count = 500000; while((dregs->cond_reg & DMA_FIFO_ISDRAIN) && (--count > 0)) udelay(1); if(!count) { printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg); static dma_addr_t sun3x_esp_map_single(struct esp *esp, void *buf, size_t sz, int dir) { return dma_map_single(esp->dev, buf, sz, dir); } static int sun3x_esp_map_sg(struct esp *esp, struct scatterlist *sg, int num_sg, int dir) { return dma_map_sg(esp->dev, sg, num_sg, dir); } static void dma_barrier(struct NCR_ESP *esp) static void sun3x_esp_unmap_single(struct esp *esp, dma_addr_t addr, size_t sz, int dir) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; int count = 500000; while((dregs->cond_reg & DMA_PEND_READ) && (--count > 0)) udelay(1); dma_unmap_single(esp->dev, addr, sz, dir); } if(!count) { printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg); static void sun3x_esp_unmap_sg(struct esp *esp, struct scatterlist *sg, int num_sg, int dir) { dma_unmap_sg(esp->dev, sg, num_sg, dir); } dregs->cond_reg &= ~(DMA_ENABLE); static int sun3x_esp_irq_pending(struct esp *esp) { if (dma_read32(DMA_CSR) & (DMA_HNDL_INTR | DMA_HNDL_ERROR)) return 1; return 0; } /* This uses various DMA csr fields and the fifo flags count value to * determine how many bytes were successfully sent/received by the ESP. */ static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count) static void sun3x_esp_reset_dma(struct esp *esp) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; u32 val; int rval = dregs->st_addr - esp->esp_command_dvma; val = dma_read32(DMA_CSR); dma_write32(val | DMA_RST_SCSI, DMA_CSR); dma_write32(val & ~DMA_RST_SCSI, DMA_CSR); return rval - fifo_count; /* Enable interrupts. */ val = dma_read32(DMA_CSR); dma_write32(val | DMA_INT_ENAB, DMA_CSR); } static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp) static void sun3x_esp_dma_drain(struct esp *esp) { return sp->SCp.this_residual; } u32 csr; int lim; static void dma_drain(struct NCR_ESP *esp) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; int count = 500000; csr = dma_read32(DMA_CSR); if (!(csr & DMA_FIFO_ISDRAIN)) return; if(dregs->cond_reg & DMA_FIFO_ISDRAIN) { dregs->cond_reg |= DMA_FIFO_STDRAIN; while((dregs->cond_reg & DMA_FIFO_ISDRAIN) && (--count > 0)) udelay(1); if(!count) { printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg); } dma_write32(csr | DMA_FIFO_STDRAIN, DMA_CSR); lim = 1000; while (dma_read32(DMA_CSR) & DMA_FIFO_ISDRAIN) { if (--lim == 0) { printk(KERN_ALERT PFX "esp%d: DMA will not drain!\n", esp->host->unique_id); break; } udelay(1); } } static void dma_invalidate(struct NCR_ESP *esp) static void sun3x_esp_dma_invalidate(struct esp *esp) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; __u32 tmp; int count = 500000; u32 val; int lim; while(((tmp = dregs->cond_reg) & DMA_PEND_READ) && (--count > 0)) lim = 1000; while ((val = dma_read32(DMA_CSR)) & DMA_PEND_READ) { if (--lim == 0) { printk(KERN_ALERT PFX "esp%d: DMA will not " "invalidate!\n", esp->host->unique_id); break; } udelay(1); if(!count) { printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg); } dregs->cond_reg = tmp | DMA_FIFO_INV; dregs->cond_reg &= ~DMA_FIFO_INV; val &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB); val |= DMA_FIFO_INV; dma_write32(val, DMA_CSR); val &= ~DMA_FIFO_INV; dma_write32(val, DMA_CSR); } static void dma_dump_state(struct NCR_ESP *esp) static void sun3x_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count, u32 dma_count, int write, u8 cmd) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; u32 csr; ESPLOG(("esp%d: dma -- cond_reg<%08lx> addr<%08lx>\n", esp->esp_id, dregs->cond_reg, dregs->st_addr)); } BUG_ON(!(cmd & ESP_CMD_DMA)); static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; sun3x_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW); sun3x_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED); csr = dma_read32(DMA_CSR); csr |= DMA_ENABLE; if (write) csr |= DMA_ST_WRITE; else csr &= ~DMA_ST_WRITE; dma_write32(csr, DMA_CSR); dma_write32(addr, DMA_ADDR); dregs->st_addr = vaddress; dregs->cond_reg |= (DMA_ST_WRITE | DMA_ENABLE); scsi_esp_cmd(esp, cmd); } static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length) static int sun3x_esp_dma_error(struct esp *esp) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; u32 csr = dma_read32(DMA_CSR); /* Set up the DMA counters */ if (csr & DMA_HNDL_ERROR) return 1; dregs->st_addr = vaddress; dregs->cond_reg = ((dregs->cond_reg & ~(DMA_ST_WRITE)) | DMA_ENABLE); return 0; } static void dma_ints_off(struct NCR_ESP *esp) { DMA_INTSOFF((struct sparc_dma_registers *) esp->dregs); } static const struct esp_driver_ops sun3x_esp_ops = { .esp_write8 = sun3x_esp_write8, .esp_read8 = sun3x_esp_read8, .map_single = sun3x_esp_map_single, .map_sg = sun3x_esp_map_sg, .unmap_single = sun3x_esp_unmap_single, .unmap_sg = sun3x_esp_unmap_sg, .irq_pending = sun3x_esp_irq_pending, .reset_dma = sun3x_esp_reset_dma, .dma_drain = sun3x_esp_dma_drain, .dma_invalidate = sun3x_esp_dma_invalidate, .send_dma_cmd = sun3x_esp_send_dma_cmd, .dma_error = sun3x_esp_dma_error, }; static void dma_ints_on(struct NCR_ESP *esp) static int __devinit esp_sun3x_probe(struct platform_device *dev) { DMA_INTSON((struct sparc_dma_registers *) esp->dregs); } struct scsi_host_template *tpnt = &scsi_esp_template; struct Scsi_Host *host; struct esp *esp; struct resource *res; int err = -ENOMEM; static int dma_irq_p(struct NCR_ESP *esp) { return DMA_IRQ_P((struct sparc_dma_registers *) esp->dregs); } host = scsi_host_alloc(tpnt, sizeof(struct esp)); if (!host) goto fail; static void dma_poll(struct NCR_ESP *esp, unsigned char *vaddr) { int count = 50; dma_do_drain(esp); host->max_id = 8; esp = shost_priv(host); /* Wait till the first bits settle. */ while((*(volatile unsigned char *)vaddr == 0xff) && (--count > 0)) udelay(1); esp->host = host; esp->dev = dev; esp->ops = &sun3x_esp_ops; if(!count) { // printk("%s:%d timeout expire (data %02x)\n", __FILE__, __LINE__, // esp_read(esp->eregs->esp_fdata)); //mach_halt(); vaddr[0] = esp_read(esp->eregs->esp_fdata); vaddr[1] = esp_read(esp->eregs->esp_fdata); } res = platform_get_resource(dev, IORESOURCE_MEM, 0); if (!res && !res->start) goto fail_unlink; } esp->regs = ioremap_nocache(res->start, 0x20); if (!esp->regs) goto fail_unmap_regs; static int dma_ports_p(struct NCR_ESP *esp) { return (((struct sparc_dma_registers *) esp->dregs)->cond_reg & DMA_INT_ENAB); } res = platform_get_resource(dev, IORESOURCE_MEM, 1); if (!res && !res->start) goto fail_unmap_regs; /* Resetting various pieces of the ESP scsi driver chipset/buses. */ static void dma_reset(struct NCR_ESP *esp) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *)esp->dregs; esp->dma_regs = ioremap_nocache(res->start, 0x10); /* Punt the DVMA into a known state. */ dregs->cond_reg |= DMA_RST_SCSI; dregs->cond_reg &= ~(DMA_RST_SCSI); DMA_INTSON(dregs); } esp->command_block = dma_alloc_coherent(esp->dev, 16, &esp->command_block_dma, GFP_KERNEL); if (!esp->command_block) goto fail_unmap_regs_dma; static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write) { struct sparc_dma_registers *dregs = (struct sparc_dma_registers *) esp->dregs; unsigned long nreg = dregs->cond_reg; host->irq = platform_get_irq(dev, 0); err = request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "SUN3X ESP", esp); if (err < 0) goto fail_unmap_command_block; // printk("dma_setup %c addr %08x cnt %08x\n", // write ? 'W' : 'R', addr, count); esp->scsi_id = 7; esp->host->this_id = esp->scsi_id; esp->scsi_id_mask = (1 << esp->scsi_id); esp->cfreq = 20000000; dma_do_drain(esp); dev_set_drvdata(&dev->dev, esp); if(write) nreg |= DMA_ST_WRITE; else { nreg &= ~(DMA_ST_WRITE); } err = scsi_esp_register(esp, &dev->dev); if (err) goto fail_free_irq; nreg |= DMA_ENABLE; dregs->cond_reg = nreg; dregs->st_addr = addr; } return 0; static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp) fail_free_irq: free_irq(host->irq, esp); fail_unmap_command_block: dma_free_coherent(esp->dev, 16, esp->command_block, esp->command_block_dma); fail_unmap_regs_dma: iounmap(esp->dma_regs); fail_unmap_regs: iounmap(esp->regs); fail_unlink: scsi_host_put(host); fail: return err; } static int __devexit esp_sun3x_remove(struct platform_device *dev) { sp->SCp.have_data_in = dvma_map((unsigned long)sp->SCp.buffer, sp->SCp.this_residual); sp->SCp.ptr = (char *)((unsigned long)sp->SCp.have_data_in); } struct esp *esp = dev_get_drvdata(&dev->dev); unsigned int irq = esp->host->irq; u32 val; static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp) { int sz = sp->SCp.buffers_residual; struct scatterlist *sg = sp->SCp.buffer; scsi_esp_unregister(esp); while (sz >= 0) { sg[sz].dma_address = dvma_map((unsigned long)sg_virt(&sg[sz]), sg[sz].length); sz--; } sp->SCp.ptr=(char *)((unsigned long)sp->SCp.buffer->dma_address); } /* Disable interrupts. */ val = dma_read32(DMA_CSR); dma_write32(val & ~DMA_INT_ENAB, DMA_CSR); static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp) { dvma_unmap((char *)sp->SCp.have_data_in); } free_irq(irq, esp); dma_free_coherent(esp->dev, 16, esp->command_block, esp->command_block_dma); static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp) { int sz = sp->use_sg - 1; struct scatterlist *sg = (struct scatterlist *)sp->request_buffer; scsi_host_put(esp->host); while(sz >= 0) { dvma_unmap((char *)sg[sz].dma_address); sz--; } return 0; } static void dma_advance_sg (Scsi_Cmnd *sp) static struct platform_driver esp_sun3x_driver = { .probe = esp_sun3x_probe, .remove = __devexit_p(esp_sun3x_remove), .driver = { .name = "sun3x_esp", }, }; static int __init sun3x_esp_init(void) { sp->SCp.ptr = (char *)((unsigned long)sp->SCp.buffer->dma_address); return platform_driver_register(&esp_sun3x_driver); } static int sun3x_esp_release(struct Scsi_Host *instance) static void __exit sun3x_esp_exit(void) { /* this code does not support being compiled as a module */ return 1; platform_driver_unregister(&esp_sun3x_driver); } static struct scsi_host_template driver_template = { .proc_name = "sun3x_esp", .proc_info = &esp_proc_info, .name = "Sun ESP 100/100a/200", .detect = sun3x_esp_detect, .release = sun3x_esp_release, .slave_alloc = esp_slave_alloc, .slave_destroy = esp_slave_destroy, .info = esp_info, .queuecommand = esp_queue, .eh_abort_handler = esp_abort, .eh_bus_reset_handler = esp_reset, .can_queue = 7, .this_id = 7, .sg_tablesize = SG_ALL, .cmd_per_lun = 1, .use_clustering = DISABLE_CLUSTERING, }; #include "scsi_module.c" MODULE_DESCRIPTION("Sun3x ESP SCSI driver"); MODULE_AUTHOR("Thomas Bogendoerfer (tsbogend@alpha.franken.de)"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); module_init(sun3x_esp_init); module_exit(sun3x_esp_exit);