162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* sun3x_esp.c: ESP front-end for Sun3x systems. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2007,2008 Thomas Bogendoerfer (tsbogend@alpha.franken.de) 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/gfp.h> 962306a36Sopenharmony_ci#include <linux/types.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <asm/sun3x.h> 1962306a36Sopenharmony_ci#include <asm/dma.h> 2062306a36Sopenharmony_ci#include <asm/dvma.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* DMA controller reg offsets */ 2362306a36Sopenharmony_ci#define DMA_CSR 0x00UL /* rw DMA control/status register 0x00 */ 2462306a36Sopenharmony_ci#define DMA_ADDR 0x04UL /* rw DMA transfer address register 0x04 */ 2562306a36Sopenharmony_ci#define DMA_COUNT 0x08UL /* rw DMA transfer count register 0x08 */ 2662306a36Sopenharmony_ci#define DMA_TEST 0x0cUL /* rw DMA test/debug register 0x0c */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <scsi/scsi_host.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "esp_scsi.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define DRV_MODULE_NAME "sun3x_esp" 3362306a36Sopenharmony_ci#define PFX DRV_MODULE_NAME ": " 3462306a36Sopenharmony_ci#define DRV_VERSION "1.000" 3562306a36Sopenharmony_ci#define DRV_MODULE_RELDATE "Nov 1, 2007" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * m68k always assumes readl/writel operate on little endian 3962306a36Sopenharmony_ci * mmio space; this is wrong at least for Sun3x, so we 4062306a36Sopenharmony_ci * need to workaround this until a proper way is found 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci#if 0 4362306a36Sopenharmony_ci#define dma_read32(REG) \ 4462306a36Sopenharmony_ci readl(esp->dma_regs + (REG)) 4562306a36Sopenharmony_ci#define dma_write32(VAL, REG) \ 4662306a36Sopenharmony_ci writel((VAL), esp->dma_regs + (REG)) 4762306a36Sopenharmony_ci#else 4862306a36Sopenharmony_ci#define dma_read32(REG) \ 4962306a36Sopenharmony_ci *(volatile u32 *)(esp->dma_regs + (REG)) 5062306a36Sopenharmony_ci#define dma_write32(VAL, REG) \ 5162306a36Sopenharmony_ci do { *(volatile u32 *)(esp->dma_regs + (REG)) = (VAL); } while (0) 5262306a36Sopenharmony_ci#endif 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void sun3x_esp_write8(struct esp *esp, u8 val, unsigned long reg) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci writeb(val, esp->regs + (reg * 4UL)); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic u8 sun3x_esp_read8(struct esp *esp, unsigned long reg) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci return readb(esp->regs + (reg * 4UL)); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int sun3x_esp_irq_pending(struct esp *esp) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci if (dma_read32(DMA_CSR) & (DMA_HNDL_INTR | DMA_HNDL_ERROR)) 6762306a36Sopenharmony_ci return 1; 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic void sun3x_esp_reset_dma(struct esp *esp) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci u32 val; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci val = dma_read32(DMA_CSR); 7662306a36Sopenharmony_ci dma_write32(val | DMA_RST_SCSI, DMA_CSR); 7762306a36Sopenharmony_ci dma_write32(val & ~DMA_RST_SCSI, DMA_CSR); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Enable interrupts. */ 8062306a36Sopenharmony_ci val = dma_read32(DMA_CSR); 8162306a36Sopenharmony_ci dma_write32(val | DMA_INT_ENAB, DMA_CSR); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic void sun3x_esp_dma_drain(struct esp *esp) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci u32 csr; 8762306a36Sopenharmony_ci int lim; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci csr = dma_read32(DMA_CSR); 9062306a36Sopenharmony_ci if (!(csr & DMA_FIFO_ISDRAIN)) 9162306a36Sopenharmony_ci return; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci dma_write32(csr | DMA_FIFO_STDRAIN, DMA_CSR); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci lim = 1000; 9662306a36Sopenharmony_ci while (dma_read32(DMA_CSR) & DMA_FIFO_ISDRAIN) { 9762306a36Sopenharmony_ci if (--lim == 0) { 9862306a36Sopenharmony_ci printk(KERN_ALERT PFX "esp%d: DMA will not drain!\n", 9962306a36Sopenharmony_ci esp->host->unique_id); 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci udelay(1); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic void sun3x_esp_dma_invalidate(struct esp *esp) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci u32 val; 10962306a36Sopenharmony_ci int lim; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci lim = 1000; 11262306a36Sopenharmony_ci while ((val = dma_read32(DMA_CSR)) & DMA_PEND_READ) { 11362306a36Sopenharmony_ci if (--lim == 0) { 11462306a36Sopenharmony_ci printk(KERN_ALERT PFX "esp%d: DMA will not " 11562306a36Sopenharmony_ci "invalidate!\n", esp->host->unique_id); 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci udelay(1); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci val &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB); 12262306a36Sopenharmony_ci val |= DMA_FIFO_INV; 12362306a36Sopenharmony_ci dma_write32(val, DMA_CSR); 12462306a36Sopenharmony_ci val &= ~DMA_FIFO_INV; 12562306a36Sopenharmony_ci dma_write32(val, DMA_CSR); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void sun3x_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count, 12962306a36Sopenharmony_ci u32 dma_count, int write, u8 cmd) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci u32 csr; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci BUG_ON(!(cmd & ESP_CMD_DMA)); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci sun3x_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW); 13662306a36Sopenharmony_ci sun3x_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED); 13762306a36Sopenharmony_ci csr = dma_read32(DMA_CSR); 13862306a36Sopenharmony_ci csr |= DMA_ENABLE; 13962306a36Sopenharmony_ci if (write) 14062306a36Sopenharmony_ci csr |= DMA_ST_WRITE; 14162306a36Sopenharmony_ci else 14262306a36Sopenharmony_ci csr &= ~DMA_ST_WRITE; 14362306a36Sopenharmony_ci dma_write32(csr, DMA_CSR); 14462306a36Sopenharmony_ci dma_write32(addr, DMA_ADDR); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci scsi_esp_cmd(esp, cmd); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int sun3x_esp_dma_error(struct esp *esp) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci u32 csr = dma_read32(DMA_CSR); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (csr & DMA_HNDL_ERROR) 15462306a36Sopenharmony_ci return 1; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic const struct esp_driver_ops sun3x_esp_ops = { 16062306a36Sopenharmony_ci .esp_write8 = sun3x_esp_write8, 16162306a36Sopenharmony_ci .esp_read8 = sun3x_esp_read8, 16262306a36Sopenharmony_ci .irq_pending = sun3x_esp_irq_pending, 16362306a36Sopenharmony_ci .reset_dma = sun3x_esp_reset_dma, 16462306a36Sopenharmony_ci .dma_drain = sun3x_esp_dma_drain, 16562306a36Sopenharmony_ci .dma_invalidate = sun3x_esp_dma_invalidate, 16662306a36Sopenharmony_ci .send_dma_cmd = sun3x_esp_send_dma_cmd, 16762306a36Sopenharmony_ci .dma_error = sun3x_esp_dma_error, 16862306a36Sopenharmony_ci}; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int esp_sun3x_probe(struct platform_device *dev) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci const struct scsi_host_template *tpnt = &scsi_esp_template; 17362306a36Sopenharmony_ci struct Scsi_Host *host; 17462306a36Sopenharmony_ci struct esp *esp; 17562306a36Sopenharmony_ci struct resource *res; 17662306a36Sopenharmony_ci int err = -ENOMEM; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci host = scsi_host_alloc(tpnt, sizeof(struct esp)); 17962306a36Sopenharmony_ci if (!host) 18062306a36Sopenharmony_ci goto fail; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci host->max_id = 8; 18362306a36Sopenharmony_ci esp = shost_priv(host); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci esp->host = host; 18662306a36Sopenharmony_ci esp->dev = &dev->dev; 18762306a36Sopenharmony_ci esp->ops = &sun3x_esp_ops; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci res = platform_get_resource(dev, IORESOURCE_MEM, 0); 19062306a36Sopenharmony_ci if (!res || !res->start) 19162306a36Sopenharmony_ci goto fail_unlink; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci esp->regs = ioremap(res->start, 0x20); 19462306a36Sopenharmony_ci if (!esp->regs) 19562306a36Sopenharmony_ci goto fail_unmap_regs; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci res = platform_get_resource(dev, IORESOURCE_MEM, 1); 19862306a36Sopenharmony_ci if (!res || !res->start) 19962306a36Sopenharmony_ci goto fail_unmap_regs; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci esp->dma_regs = ioremap(res->start, 0x10); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci esp->command_block = dma_alloc_coherent(esp->dev, 16, 20462306a36Sopenharmony_ci &esp->command_block_dma, 20562306a36Sopenharmony_ci GFP_KERNEL); 20662306a36Sopenharmony_ci if (!esp->command_block) 20762306a36Sopenharmony_ci goto fail_unmap_regs_dma; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci host->irq = err = platform_get_irq(dev, 0); 21062306a36Sopenharmony_ci if (err < 0) 21162306a36Sopenharmony_ci goto fail_unmap_command_block; 21262306a36Sopenharmony_ci err = request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, 21362306a36Sopenharmony_ci "SUN3X ESP", esp); 21462306a36Sopenharmony_ci if (err < 0) 21562306a36Sopenharmony_ci goto fail_unmap_command_block; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci esp->scsi_id = 7; 21862306a36Sopenharmony_ci esp->host->this_id = esp->scsi_id; 21962306a36Sopenharmony_ci esp->scsi_id_mask = (1 << esp->scsi_id); 22062306a36Sopenharmony_ci esp->cfreq = 20000000; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci dev_set_drvdata(&dev->dev, esp); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci err = scsi_esp_register(esp); 22562306a36Sopenharmony_ci if (err) 22662306a36Sopenharmony_ci goto fail_free_irq; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return 0; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cifail_free_irq: 23162306a36Sopenharmony_ci free_irq(host->irq, esp); 23262306a36Sopenharmony_cifail_unmap_command_block: 23362306a36Sopenharmony_ci dma_free_coherent(esp->dev, 16, 23462306a36Sopenharmony_ci esp->command_block, 23562306a36Sopenharmony_ci esp->command_block_dma); 23662306a36Sopenharmony_cifail_unmap_regs_dma: 23762306a36Sopenharmony_ci iounmap(esp->dma_regs); 23862306a36Sopenharmony_cifail_unmap_regs: 23962306a36Sopenharmony_ci iounmap(esp->regs); 24062306a36Sopenharmony_cifail_unlink: 24162306a36Sopenharmony_ci scsi_host_put(host); 24262306a36Sopenharmony_cifail: 24362306a36Sopenharmony_ci return err; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int esp_sun3x_remove(struct platform_device *dev) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct esp *esp = dev_get_drvdata(&dev->dev); 24962306a36Sopenharmony_ci unsigned int irq = esp->host->irq; 25062306a36Sopenharmony_ci u32 val; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci scsi_esp_unregister(esp); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Disable interrupts. */ 25562306a36Sopenharmony_ci val = dma_read32(DMA_CSR); 25662306a36Sopenharmony_ci dma_write32(val & ~DMA_INT_ENAB, DMA_CSR); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci free_irq(irq, esp); 25962306a36Sopenharmony_ci dma_free_coherent(esp->dev, 16, 26062306a36Sopenharmony_ci esp->command_block, 26162306a36Sopenharmony_ci esp->command_block_dma); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci scsi_host_put(esp->host); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic struct platform_driver esp_sun3x_driver = { 26962306a36Sopenharmony_ci .probe = esp_sun3x_probe, 27062306a36Sopenharmony_ci .remove = esp_sun3x_remove, 27162306a36Sopenharmony_ci .driver = { 27262306a36Sopenharmony_ci .name = "sun3x_esp", 27362306a36Sopenharmony_ci }, 27462306a36Sopenharmony_ci}; 27562306a36Sopenharmony_cimodule_platform_driver(esp_sun3x_driver); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ciMODULE_DESCRIPTION("Sun3x ESP SCSI driver"); 27862306a36Sopenharmony_ciMODULE_AUTHOR("Thomas Bogendoerfer (tsbogend@alpha.franken.de)"); 27962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 28062306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 28162306a36Sopenharmony_ciMODULE_ALIAS("platform:sun3x_esp"); 282