18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* sun3x_esp.c: ESP front-end for Sun3x systems. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2007,2008 Thomas Bogendoerfer (tsbogend@alpha.franken.de) 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/gfp.h> 98c2ecf20Sopenharmony_ci#include <linux/types.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <asm/sun3x.h> 198c2ecf20Sopenharmony_ci#include <asm/dma.h> 208c2ecf20Sopenharmony_ci#include <asm/dvma.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* DMA controller reg offsets */ 238c2ecf20Sopenharmony_ci#define DMA_CSR 0x00UL /* rw DMA control/status register 0x00 */ 248c2ecf20Sopenharmony_ci#define DMA_ADDR 0x04UL /* rw DMA transfer address register 0x04 */ 258c2ecf20Sopenharmony_ci#define DMA_COUNT 0x08UL /* rw DMA transfer count register 0x08 */ 268c2ecf20Sopenharmony_ci#define DMA_TEST 0x0cUL /* rw DMA test/debug register 0x0c */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "esp_scsi.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define DRV_MODULE_NAME "sun3x_esp" 338c2ecf20Sopenharmony_ci#define PFX DRV_MODULE_NAME ": " 348c2ecf20Sopenharmony_ci#define DRV_VERSION "1.000" 358c2ecf20Sopenharmony_ci#define DRV_MODULE_RELDATE "Nov 1, 2007" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * m68k always assumes readl/writel operate on little endian 398c2ecf20Sopenharmony_ci * mmio space; this is wrong at least for Sun3x, so we 408c2ecf20Sopenharmony_ci * need to workaround this until a proper way is found 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci#if 0 438c2ecf20Sopenharmony_ci#define dma_read32(REG) \ 448c2ecf20Sopenharmony_ci readl(esp->dma_regs + (REG)) 458c2ecf20Sopenharmony_ci#define dma_write32(VAL, REG) \ 468c2ecf20Sopenharmony_ci writel((VAL), esp->dma_regs + (REG)) 478c2ecf20Sopenharmony_ci#else 488c2ecf20Sopenharmony_ci#define dma_read32(REG) \ 498c2ecf20Sopenharmony_ci *(volatile u32 *)(esp->dma_regs + (REG)) 508c2ecf20Sopenharmony_ci#define dma_write32(VAL, REG) \ 518c2ecf20Sopenharmony_ci do { *(volatile u32 *)(esp->dma_regs + (REG)) = (VAL); } while (0) 528c2ecf20Sopenharmony_ci#endif 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void sun3x_esp_write8(struct esp *esp, u8 val, unsigned long reg) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci writeb(val, esp->regs + (reg * 4UL)); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic u8 sun3x_esp_read8(struct esp *esp, unsigned long reg) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci return readb(esp->regs + (reg * 4UL)); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int sun3x_esp_irq_pending(struct esp *esp) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci if (dma_read32(DMA_CSR) & (DMA_HNDL_INTR | DMA_HNDL_ERROR)) 678c2ecf20Sopenharmony_ci return 1; 688c2ecf20Sopenharmony_ci return 0; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void sun3x_esp_reset_dma(struct esp *esp) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci u32 val; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci val = dma_read32(DMA_CSR); 768c2ecf20Sopenharmony_ci dma_write32(val | DMA_RST_SCSI, DMA_CSR); 778c2ecf20Sopenharmony_ci dma_write32(val & ~DMA_RST_SCSI, DMA_CSR); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* Enable interrupts. */ 808c2ecf20Sopenharmony_ci val = dma_read32(DMA_CSR); 818c2ecf20Sopenharmony_ci dma_write32(val | DMA_INT_ENAB, DMA_CSR); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void sun3x_esp_dma_drain(struct esp *esp) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci u32 csr; 878c2ecf20Sopenharmony_ci int lim; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci csr = dma_read32(DMA_CSR); 908c2ecf20Sopenharmony_ci if (!(csr & DMA_FIFO_ISDRAIN)) 918c2ecf20Sopenharmony_ci return; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci dma_write32(csr | DMA_FIFO_STDRAIN, DMA_CSR); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci lim = 1000; 968c2ecf20Sopenharmony_ci while (dma_read32(DMA_CSR) & DMA_FIFO_ISDRAIN) { 978c2ecf20Sopenharmony_ci if (--lim == 0) { 988c2ecf20Sopenharmony_ci printk(KERN_ALERT PFX "esp%d: DMA will not drain!\n", 998c2ecf20Sopenharmony_ci esp->host->unique_id); 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci udelay(1); 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic void sun3x_esp_dma_invalidate(struct esp *esp) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci u32 val; 1098c2ecf20Sopenharmony_ci int lim; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci lim = 1000; 1128c2ecf20Sopenharmony_ci while ((val = dma_read32(DMA_CSR)) & DMA_PEND_READ) { 1138c2ecf20Sopenharmony_ci if (--lim == 0) { 1148c2ecf20Sopenharmony_ci printk(KERN_ALERT PFX "esp%d: DMA will not " 1158c2ecf20Sopenharmony_ci "invalidate!\n", esp->host->unique_id); 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci udelay(1); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci val &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB); 1228c2ecf20Sopenharmony_ci val |= DMA_FIFO_INV; 1238c2ecf20Sopenharmony_ci dma_write32(val, DMA_CSR); 1248c2ecf20Sopenharmony_ci val &= ~DMA_FIFO_INV; 1258c2ecf20Sopenharmony_ci dma_write32(val, DMA_CSR); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void sun3x_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count, 1298c2ecf20Sopenharmony_ci u32 dma_count, int write, u8 cmd) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci u32 csr; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci BUG_ON(!(cmd & ESP_CMD_DMA)); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci sun3x_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW); 1368c2ecf20Sopenharmony_ci sun3x_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED); 1378c2ecf20Sopenharmony_ci csr = dma_read32(DMA_CSR); 1388c2ecf20Sopenharmony_ci csr |= DMA_ENABLE; 1398c2ecf20Sopenharmony_ci if (write) 1408c2ecf20Sopenharmony_ci csr |= DMA_ST_WRITE; 1418c2ecf20Sopenharmony_ci else 1428c2ecf20Sopenharmony_ci csr &= ~DMA_ST_WRITE; 1438c2ecf20Sopenharmony_ci dma_write32(csr, DMA_CSR); 1448c2ecf20Sopenharmony_ci dma_write32(addr, DMA_ADDR); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci scsi_esp_cmd(esp, cmd); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int sun3x_esp_dma_error(struct esp *esp) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci u32 csr = dma_read32(DMA_CSR); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (csr & DMA_HNDL_ERROR) 1548c2ecf20Sopenharmony_ci return 1; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic const struct esp_driver_ops sun3x_esp_ops = { 1608c2ecf20Sopenharmony_ci .esp_write8 = sun3x_esp_write8, 1618c2ecf20Sopenharmony_ci .esp_read8 = sun3x_esp_read8, 1628c2ecf20Sopenharmony_ci .irq_pending = sun3x_esp_irq_pending, 1638c2ecf20Sopenharmony_ci .reset_dma = sun3x_esp_reset_dma, 1648c2ecf20Sopenharmony_ci .dma_drain = sun3x_esp_dma_drain, 1658c2ecf20Sopenharmony_ci .dma_invalidate = sun3x_esp_dma_invalidate, 1668c2ecf20Sopenharmony_ci .send_dma_cmd = sun3x_esp_send_dma_cmd, 1678c2ecf20Sopenharmony_ci .dma_error = sun3x_esp_dma_error, 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int esp_sun3x_probe(struct platform_device *dev) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct scsi_host_template *tpnt = &scsi_esp_template; 1738c2ecf20Sopenharmony_ci struct Scsi_Host *host; 1748c2ecf20Sopenharmony_ci struct esp *esp; 1758c2ecf20Sopenharmony_ci struct resource *res; 1768c2ecf20Sopenharmony_ci int err = -ENOMEM; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci host = scsi_host_alloc(tpnt, sizeof(struct esp)); 1798c2ecf20Sopenharmony_ci if (!host) 1808c2ecf20Sopenharmony_ci goto fail; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci host->max_id = 8; 1838c2ecf20Sopenharmony_ci esp = shost_priv(host); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci esp->host = host; 1868c2ecf20Sopenharmony_ci esp->dev = &dev->dev; 1878c2ecf20Sopenharmony_ci esp->ops = &sun3x_esp_ops; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci res = platform_get_resource(dev, IORESOURCE_MEM, 0); 1908c2ecf20Sopenharmony_ci if (!res || !res->start) 1918c2ecf20Sopenharmony_ci goto fail_unlink; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci esp->regs = ioremap(res->start, 0x20); 1948c2ecf20Sopenharmony_ci if (!esp->regs) 1958c2ecf20Sopenharmony_ci goto fail_unmap_regs; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci res = platform_get_resource(dev, IORESOURCE_MEM, 1); 1988c2ecf20Sopenharmony_ci if (!res || !res->start) 1998c2ecf20Sopenharmony_ci goto fail_unmap_regs; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci esp->dma_regs = ioremap(res->start, 0x10); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci esp->command_block = dma_alloc_coherent(esp->dev, 16, 2048c2ecf20Sopenharmony_ci &esp->command_block_dma, 2058c2ecf20Sopenharmony_ci GFP_KERNEL); 2068c2ecf20Sopenharmony_ci if (!esp->command_block) 2078c2ecf20Sopenharmony_ci goto fail_unmap_regs_dma; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci host->irq = err = platform_get_irq(dev, 0); 2108c2ecf20Sopenharmony_ci if (err < 0) 2118c2ecf20Sopenharmony_ci goto fail_unmap_command_block; 2128c2ecf20Sopenharmony_ci err = request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, 2138c2ecf20Sopenharmony_ci "SUN3X ESP", esp); 2148c2ecf20Sopenharmony_ci if (err < 0) 2158c2ecf20Sopenharmony_ci goto fail_unmap_command_block; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci esp->scsi_id = 7; 2188c2ecf20Sopenharmony_ci esp->host->this_id = esp->scsi_id; 2198c2ecf20Sopenharmony_ci esp->scsi_id_mask = (1 << esp->scsi_id); 2208c2ecf20Sopenharmony_ci esp->cfreq = 20000000; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci dev_set_drvdata(&dev->dev, esp); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci err = scsi_esp_register(esp); 2258c2ecf20Sopenharmony_ci if (err) 2268c2ecf20Sopenharmony_ci goto fail_free_irq; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cifail_free_irq: 2318c2ecf20Sopenharmony_ci free_irq(host->irq, esp); 2328c2ecf20Sopenharmony_cifail_unmap_command_block: 2338c2ecf20Sopenharmony_ci dma_free_coherent(esp->dev, 16, 2348c2ecf20Sopenharmony_ci esp->command_block, 2358c2ecf20Sopenharmony_ci esp->command_block_dma); 2368c2ecf20Sopenharmony_cifail_unmap_regs_dma: 2378c2ecf20Sopenharmony_ci iounmap(esp->dma_regs); 2388c2ecf20Sopenharmony_cifail_unmap_regs: 2398c2ecf20Sopenharmony_ci iounmap(esp->regs); 2408c2ecf20Sopenharmony_cifail_unlink: 2418c2ecf20Sopenharmony_ci scsi_host_put(host); 2428c2ecf20Sopenharmony_cifail: 2438c2ecf20Sopenharmony_ci return err; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic int esp_sun3x_remove(struct platform_device *dev) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct esp *esp = dev_get_drvdata(&dev->dev); 2498c2ecf20Sopenharmony_ci unsigned int irq = esp->host->irq; 2508c2ecf20Sopenharmony_ci u32 val; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci scsi_esp_unregister(esp); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* Disable interrupts. */ 2558c2ecf20Sopenharmony_ci val = dma_read32(DMA_CSR); 2568c2ecf20Sopenharmony_ci dma_write32(val & ~DMA_INT_ENAB, DMA_CSR); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci free_irq(irq, esp); 2598c2ecf20Sopenharmony_ci dma_free_coherent(esp->dev, 16, 2608c2ecf20Sopenharmony_ci esp->command_block, 2618c2ecf20Sopenharmony_ci esp->command_block_dma); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci scsi_host_put(esp->host); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic struct platform_driver esp_sun3x_driver = { 2698c2ecf20Sopenharmony_ci .probe = esp_sun3x_probe, 2708c2ecf20Sopenharmony_ci .remove = esp_sun3x_remove, 2718c2ecf20Sopenharmony_ci .driver = { 2728c2ecf20Sopenharmony_ci .name = "sun3x_esp", 2738c2ecf20Sopenharmony_ci }, 2748c2ecf20Sopenharmony_ci}; 2758c2ecf20Sopenharmony_cimodule_platform_driver(esp_sun3x_driver); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sun3x ESP SCSI driver"); 2788c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thomas Bogendoerfer (tsbogend@alpha.franken.de)"); 2798c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2808c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 2818c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:sun3x_esp"); 282