109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20bb67f18SThomas Bogendoerfer /* sun3x_esp.c: ESP front-end for Sun3x systems.
31da177e4SLinus Torvalds *
40bb67f18SThomas Bogendoerfer * Copyright (C) 2007,2008 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
51da177e4SLinus Torvalds */
61da177e4SLinus Torvalds
71da177e4SLinus Torvalds #include <linux/kernel.h>
85a0e3ad6STejun Heo #include <linux/gfp.h>
91da177e4SLinus Torvalds #include <linux/types.h>
101da177e4SLinus Torvalds #include <linux/delay.h>
110bb67f18SThomas Bogendoerfer #include <linux/module.h>
120bb67f18SThomas Bogendoerfer #include <linux/init.h>
130bb67f18SThomas Bogendoerfer #include <linux/platform_device.h>
140bb67f18SThomas Bogendoerfer #include <linux/dma-mapping.h>
151da177e4SLinus Torvalds #include <linux/interrupt.h>
162584cf83SDan Williams #include <linux/io.h>
171da177e4SLinus Torvalds
181da177e4SLinus Torvalds #include <asm/sun3x.h>
190bb67f18SThomas Bogendoerfer #include <asm/dma.h>
201da177e4SLinus Torvalds #include <asm/dvma.h>
211da177e4SLinus Torvalds
220bb67f18SThomas Bogendoerfer /* DMA controller reg offsets */
230bb67f18SThomas Bogendoerfer #define DMA_CSR 0x00UL /* rw DMA control/status register 0x00 */
240bb67f18SThomas Bogendoerfer #define DMA_ADDR 0x04UL /* rw DMA transfer address register 0x04 */
250bb67f18SThomas Bogendoerfer #define DMA_COUNT 0x08UL /* rw DMA transfer count register 0x08 */
260bb67f18SThomas Bogendoerfer #define DMA_TEST 0x0cUL /* rw DMA test/debug register 0x0c */
271da177e4SLinus Torvalds
280bb67f18SThomas Bogendoerfer #include <scsi/scsi_host.h>
290bb67f18SThomas Bogendoerfer
300bb67f18SThomas Bogendoerfer #include "esp_scsi.h"
310bb67f18SThomas Bogendoerfer
320bb67f18SThomas Bogendoerfer #define DRV_MODULE_NAME "sun3x_esp"
330bb67f18SThomas Bogendoerfer #define PFX DRV_MODULE_NAME ": "
340bb67f18SThomas Bogendoerfer #define DRV_VERSION "1.000"
350bb67f18SThomas Bogendoerfer #define DRV_MODULE_RELDATE "Nov 1, 2007"
360bb67f18SThomas Bogendoerfer
370bb67f18SThomas Bogendoerfer /*
380bb67f18SThomas Bogendoerfer * m68k always assumes readl/writel operate on little endian
390bb67f18SThomas Bogendoerfer * mmio space; this is wrong at least for Sun3x, so we
400bb67f18SThomas Bogendoerfer * need to workaround this until a proper way is found
411da177e4SLinus Torvalds */
420bb67f18SThomas Bogendoerfer #if 0
430bb67f18SThomas Bogendoerfer #define dma_read32(REG) \
440bb67f18SThomas Bogendoerfer readl(esp->dma_regs + (REG))
450bb67f18SThomas Bogendoerfer #define dma_write32(VAL, REG) \
460bb67f18SThomas Bogendoerfer writel((VAL), esp->dma_regs + (REG))
470bb67f18SThomas Bogendoerfer #else
480bb67f18SThomas Bogendoerfer #define dma_read32(REG) \
490bb67f18SThomas Bogendoerfer *(volatile u32 *)(esp->dma_regs + (REG))
500bb67f18SThomas Bogendoerfer #define dma_write32(VAL, REG) \
510bb67f18SThomas Bogendoerfer do { *(volatile u32 *)(esp->dma_regs + (REG)) = (VAL); } while (0)
520bb67f18SThomas Bogendoerfer #endif
530bb67f18SThomas Bogendoerfer
sun3x_esp_write8(struct esp * esp,u8 val,unsigned long reg)540bb67f18SThomas Bogendoerfer static void sun3x_esp_write8(struct esp *esp, u8 val, unsigned long reg)
551da177e4SLinus Torvalds {
560bb67f18SThomas Bogendoerfer writeb(val, esp->regs + (reg * 4UL));
570bb67f18SThomas Bogendoerfer }
581da177e4SLinus Torvalds
sun3x_esp_read8(struct esp * esp,unsigned long reg)590bb67f18SThomas Bogendoerfer static u8 sun3x_esp_read8(struct esp *esp, unsigned long reg)
600bb67f18SThomas Bogendoerfer {
610bb67f18SThomas Bogendoerfer return readb(esp->regs + (reg * 4UL));
620bb67f18SThomas Bogendoerfer }
631da177e4SLinus Torvalds
sun3x_esp_irq_pending(struct esp * esp)640bb67f18SThomas Bogendoerfer static int sun3x_esp_irq_pending(struct esp *esp)
650bb67f18SThomas Bogendoerfer {
660bb67f18SThomas Bogendoerfer if (dma_read32(DMA_CSR) & (DMA_HNDL_INTR | DMA_HNDL_ERROR))
670bb67f18SThomas Bogendoerfer return 1;
681da177e4SLinus Torvalds return 0;
691da177e4SLinus Torvalds }
701da177e4SLinus Torvalds
sun3x_esp_reset_dma(struct esp * esp)710bb67f18SThomas Bogendoerfer static void sun3x_esp_reset_dma(struct esp *esp)
720bb67f18SThomas Bogendoerfer {
730bb67f18SThomas Bogendoerfer u32 val;
741da177e4SLinus Torvalds
750bb67f18SThomas Bogendoerfer val = dma_read32(DMA_CSR);
760bb67f18SThomas Bogendoerfer dma_write32(val | DMA_RST_SCSI, DMA_CSR);
770bb67f18SThomas Bogendoerfer dma_write32(val & ~DMA_RST_SCSI, DMA_CSR);
781da177e4SLinus Torvalds
790bb67f18SThomas Bogendoerfer /* Enable interrupts. */
800bb67f18SThomas Bogendoerfer val = dma_read32(DMA_CSR);
810bb67f18SThomas Bogendoerfer dma_write32(val | DMA_INT_ENAB, DMA_CSR);
821da177e4SLinus Torvalds }
831da177e4SLinus Torvalds
sun3x_esp_dma_drain(struct esp * esp)840bb67f18SThomas Bogendoerfer static void sun3x_esp_dma_drain(struct esp *esp)
851da177e4SLinus Torvalds {
860bb67f18SThomas Bogendoerfer u32 csr;
870bb67f18SThomas Bogendoerfer int lim;
881da177e4SLinus Torvalds
890bb67f18SThomas Bogendoerfer csr = dma_read32(DMA_CSR);
900bb67f18SThomas Bogendoerfer if (!(csr & DMA_FIFO_ISDRAIN))
910bb67f18SThomas Bogendoerfer return;
921da177e4SLinus Torvalds
930bb67f18SThomas Bogendoerfer dma_write32(csr | DMA_FIFO_STDRAIN, DMA_CSR);
940bb67f18SThomas Bogendoerfer
950bb67f18SThomas Bogendoerfer lim = 1000;
960bb67f18SThomas Bogendoerfer while (dma_read32(DMA_CSR) & DMA_FIFO_ISDRAIN) {
970bb67f18SThomas Bogendoerfer if (--lim == 0) {
980bb67f18SThomas Bogendoerfer printk(KERN_ALERT PFX "esp%d: DMA will not drain!\n",
990bb67f18SThomas Bogendoerfer esp->host->unique_id);
1000bb67f18SThomas Bogendoerfer break;
1010bb67f18SThomas Bogendoerfer }
1021da177e4SLinus Torvalds udelay(1);
1030bb67f18SThomas Bogendoerfer }
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds
sun3x_esp_dma_invalidate(struct esp * esp)1060bb67f18SThomas Bogendoerfer static void sun3x_esp_dma_invalidate(struct esp *esp)
1070bb67f18SThomas Bogendoerfer {
1080bb67f18SThomas Bogendoerfer u32 val;
1090bb67f18SThomas Bogendoerfer int lim;
1101da177e4SLinus Torvalds
1110bb67f18SThomas Bogendoerfer lim = 1000;
1120bb67f18SThomas Bogendoerfer while ((val = dma_read32(DMA_CSR)) & DMA_PEND_READ) {
1130bb67f18SThomas Bogendoerfer if (--lim == 0) {
1140bb67f18SThomas Bogendoerfer printk(KERN_ALERT PFX "esp%d: DMA will not "
1150bb67f18SThomas Bogendoerfer "invalidate!\n", esp->host->unique_id);
1160bb67f18SThomas Bogendoerfer break;
1170bb67f18SThomas Bogendoerfer }
1181da177e4SLinus Torvalds udelay(1);
1191da177e4SLinus Torvalds }
1201da177e4SLinus Torvalds
1210bb67f18SThomas Bogendoerfer val &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB);
1220bb67f18SThomas Bogendoerfer val |= DMA_FIFO_INV;
1230bb67f18SThomas Bogendoerfer dma_write32(val, DMA_CSR);
1240bb67f18SThomas Bogendoerfer val &= ~DMA_FIFO_INV;
1250bb67f18SThomas Bogendoerfer dma_write32(val, DMA_CSR);
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds
sun3x_esp_send_dma_cmd(struct esp * esp,u32 addr,u32 esp_count,u32 dma_count,int write,u8 cmd)1280bb67f18SThomas Bogendoerfer static void sun3x_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count,
1290bb67f18SThomas Bogendoerfer u32 dma_count, int write, u8 cmd)
1301da177e4SLinus Torvalds {
1310bb67f18SThomas Bogendoerfer u32 csr;
1321da177e4SLinus Torvalds
1330bb67f18SThomas Bogendoerfer BUG_ON(!(cmd & ESP_CMD_DMA));
1341da177e4SLinus Torvalds
1350bb67f18SThomas Bogendoerfer sun3x_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW);
1360bb67f18SThomas Bogendoerfer sun3x_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED);
1370bb67f18SThomas Bogendoerfer csr = dma_read32(DMA_CSR);
1380bb67f18SThomas Bogendoerfer csr |= DMA_ENABLE;
1391da177e4SLinus Torvalds if (write)
1400bb67f18SThomas Bogendoerfer csr |= DMA_ST_WRITE;
1410bb67f18SThomas Bogendoerfer else
1420bb67f18SThomas Bogendoerfer csr &= ~DMA_ST_WRITE;
1430bb67f18SThomas Bogendoerfer dma_write32(csr, DMA_CSR);
1440bb67f18SThomas Bogendoerfer dma_write32(addr, DMA_ADDR);
1450bb67f18SThomas Bogendoerfer
1460bb67f18SThomas Bogendoerfer scsi_esp_cmd(esp, cmd);
1471da177e4SLinus Torvalds }
1481da177e4SLinus Torvalds
sun3x_esp_dma_error(struct esp * esp)1490bb67f18SThomas Bogendoerfer static int sun3x_esp_dma_error(struct esp *esp)
1501da177e4SLinus Torvalds {
1510bb67f18SThomas Bogendoerfer u32 csr = dma_read32(DMA_CSR);
1521da177e4SLinus Torvalds
1530bb67f18SThomas Bogendoerfer if (csr & DMA_HNDL_ERROR)
1541da177e4SLinus Torvalds return 1;
1551da177e4SLinus Torvalds
1560bb67f18SThomas Bogendoerfer return 0;
1571da177e4SLinus Torvalds }
1581da177e4SLinus Torvalds
1590bb67f18SThomas Bogendoerfer static const struct esp_driver_ops sun3x_esp_ops = {
1600bb67f18SThomas Bogendoerfer .esp_write8 = sun3x_esp_write8,
1610bb67f18SThomas Bogendoerfer .esp_read8 = sun3x_esp_read8,
1620bb67f18SThomas Bogendoerfer .irq_pending = sun3x_esp_irq_pending,
1630bb67f18SThomas Bogendoerfer .reset_dma = sun3x_esp_reset_dma,
1640bb67f18SThomas Bogendoerfer .dma_drain = sun3x_esp_dma_drain,
1650bb67f18SThomas Bogendoerfer .dma_invalidate = sun3x_esp_dma_invalidate,
1660bb67f18SThomas Bogendoerfer .send_dma_cmd = sun3x_esp_send_dma_cmd,
1670bb67f18SThomas Bogendoerfer .dma_error = sun3x_esp_dma_error,
1681da177e4SLinus Torvalds };
1691da177e4SLinus Torvalds
esp_sun3x_probe(struct platform_device * dev)1706f039790SGreg Kroah-Hartman static int esp_sun3x_probe(struct platform_device *dev)
1710bb67f18SThomas Bogendoerfer {
172*3b465a14SBart Van Assche const struct scsi_host_template *tpnt = &scsi_esp_template;
1730bb67f18SThomas Bogendoerfer struct Scsi_Host *host;
1740bb67f18SThomas Bogendoerfer struct esp *esp;
1750bb67f18SThomas Bogendoerfer struct resource *res;
1760bb67f18SThomas Bogendoerfer int err = -ENOMEM;
1771da177e4SLinus Torvalds
1780bb67f18SThomas Bogendoerfer host = scsi_host_alloc(tpnt, sizeof(struct esp));
1790bb67f18SThomas Bogendoerfer if (!host)
1800bb67f18SThomas Bogendoerfer goto fail;
1811da177e4SLinus Torvalds
1820bb67f18SThomas Bogendoerfer host->max_id = 8;
1830bb67f18SThomas Bogendoerfer esp = shost_priv(host);
1840bb67f18SThomas Bogendoerfer
1850bb67f18SThomas Bogendoerfer esp->host = host;
186e32ec657SFinn Thain esp->dev = &dev->dev;
1870bb67f18SThomas Bogendoerfer esp->ops = &sun3x_esp_ops;
1880bb67f18SThomas Bogendoerfer
1890bb67f18SThomas Bogendoerfer res = platform_get_resource(dev, IORESOURCE_MEM, 0);
1903fe68cc1SJulia Lawall if (!res || !res->start)
1910bb67f18SThomas Bogendoerfer goto fail_unlink;
1920bb67f18SThomas Bogendoerfer
1934bdc0d67SChristoph Hellwig esp->regs = ioremap(res->start, 0x20);
1940bb67f18SThomas Bogendoerfer if (!esp->regs)
1950bb67f18SThomas Bogendoerfer goto fail_unmap_regs;
1960bb67f18SThomas Bogendoerfer
1970bb67f18SThomas Bogendoerfer res = platform_get_resource(dev, IORESOURCE_MEM, 1);
1983fe68cc1SJulia Lawall if (!res || !res->start)
1990bb67f18SThomas Bogendoerfer goto fail_unmap_regs;
2000bb67f18SThomas Bogendoerfer
2014bdc0d67SChristoph Hellwig esp->dma_regs = ioremap(res->start, 0x10);
2020bb67f18SThomas Bogendoerfer
2030bb67f18SThomas Bogendoerfer esp->command_block = dma_alloc_coherent(esp->dev, 16,
2040bb67f18SThomas Bogendoerfer &esp->command_block_dma,
2050bb67f18SThomas Bogendoerfer GFP_KERNEL);
2060bb67f18SThomas Bogendoerfer if (!esp->command_block)
2070bb67f18SThomas Bogendoerfer goto fail_unmap_regs_dma;
2080bb67f18SThomas Bogendoerfer
20914b32138SSergey Shtylyov host->irq = err = platform_get_irq(dev, 0);
21014b32138SSergey Shtylyov if (err < 0)
21114b32138SSergey Shtylyov goto fail_unmap_command_block;
2120bb67f18SThomas Bogendoerfer err = request_irq(host->irq, scsi_esp_intr, IRQF_SHARED,
2130bb67f18SThomas Bogendoerfer "SUN3X ESP", esp);
2140bb67f18SThomas Bogendoerfer if (err < 0)
2150bb67f18SThomas Bogendoerfer goto fail_unmap_command_block;
2160bb67f18SThomas Bogendoerfer
2170bb67f18SThomas Bogendoerfer esp->scsi_id = 7;
2180bb67f18SThomas Bogendoerfer esp->host->this_id = esp->scsi_id;
2190bb67f18SThomas Bogendoerfer esp->scsi_id_mask = (1 << esp->scsi_id);
2200bb67f18SThomas Bogendoerfer esp->cfreq = 20000000;
2210bb67f18SThomas Bogendoerfer
2220bb67f18SThomas Bogendoerfer dev_set_drvdata(&dev->dev, esp);
2230bb67f18SThomas Bogendoerfer
22444b1b4d2SChristoph Hellwig err = scsi_esp_register(esp);
2250bb67f18SThomas Bogendoerfer if (err)
2260bb67f18SThomas Bogendoerfer goto fail_free_irq;
2270bb67f18SThomas Bogendoerfer
2280bb67f18SThomas Bogendoerfer return 0;
2290bb67f18SThomas Bogendoerfer
2300bb67f18SThomas Bogendoerfer fail_free_irq:
2310bb67f18SThomas Bogendoerfer free_irq(host->irq, esp);
2320bb67f18SThomas Bogendoerfer fail_unmap_command_block:
2330bb67f18SThomas Bogendoerfer dma_free_coherent(esp->dev, 16,
2340bb67f18SThomas Bogendoerfer esp->command_block,
2350bb67f18SThomas Bogendoerfer esp->command_block_dma);
2360bb67f18SThomas Bogendoerfer fail_unmap_regs_dma:
2370bb67f18SThomas Bogendoerfer iounmap(esp->dma_regs);
2380bb67f18SThomas Bogendoerfer fail_unmap_regs:
2390bb67f18SThomas Bogendoerfer iounmap(esp->regs);
2400bb67f18SThomas Bogendoerfer fail_unlink:
2410bb67f18SThomas Bogendoerfer scsi_host_put(host);
2420bb67f18SThomas Bogendoerfer fail:
2430bb67f18SThomas Bogendoerfer return err;
2440bb67f18SThomas Bogendoerfer }
2450bb67f18SThomas Bogendoerfer
esp_sun3x_remove(struct platform_device * dev)2466f039790SGreg Kroah-Hartman static int esp_sun3x_remove(struct platform_device *dev)
2470bb67f18SThomas Bogendoerfer {
2480bb67f18SThomas Bogendoerfer struct esp *esp = dev_get_drvdata(&dev->dev);
2490bb67f18SThomas Bogendoerfer unsigned int irq = esp->host->irq;
2500bb67f18SThomas Bogendoerfer u32 val;
2510bb67f18SThomas Bogendoerfer
2520bb67f18SThomas Bogendoerfer scsi_esp_unregister(esp);
2530bb67f18SThomas Bogendoerfer
2540bb67f18SThomas Bogendoerfer /* Disable interrupts. */
2550bb67f18SThomas Bogendoerfer val = dma_read32(DMA_CSR);
2560bb67f18SThomas Bogendoerfer dma_write32(val & ~DMA_INT_ENAB, DMA_CSR);
2570bb67f18SThomas Bogendoerfer
2580bb67f18SThomas Bogendoerfer free_irq(irq, esp);
2590bb67f18SThomas Bogendoerfer dma_free_coherent(esp->dev, 16,
2600bb67f18SThomas Bogendoerfer esp->command_block,
2610bb67f18SThomas Bogendoerfer esp->command_block_dma);
2620bb67f18SThomas Bogendoerfer
2630bb67f18SThomas Bogendoerfer scsi_host_put(esp->host);
2640bb67f18SThomas Bogendoerfer
2650bb67f18SThomas Bogendoerfer return 0;
2660bb67f18SThomas Bogendoerfer }
2670bb67f18SThomas Bogendoerfer
2680bb67f18SThomas Bogendoerfer static struct platform_driver esp_sun3x_driver = {
2690bb67f18SThomas Bogendoerfer .probe = esp_sun3x_probe,
2706f039790SGreg Kroah-Hartman .remove = esp_sun3x_remove,
2710bb67f18SThomas Bogendoerfer .driver = {
2720bb67f18SThomas Bogendoerfer .name = "sun3x_esp",
2730bb67f18SThomas Bogendoerfer },
2740bb67f18SThomas Bogendoerfer };
2752d00ffe7SLiu Shixin module_platform_driver(esp_sun3x_driver);
2760bb67f18SThomas Bogendoerfer
2770bb67f18SThomas Bogendoerfer MODULE_DESCRIPTION("Sun3x ESP SCSI driver");
2780bb67f18SThomas Bogendoerfer MODULE_AUTHOR("Thomas Bogendoerfer (tsbogend@alpha.franken.de)");
2791da177e4SLinus Torvalds MODULE_LICENSE("GPL");
2800bb67f18SThomas Bogendoerfer MODULE_VERSION(DRV_VERSION);
281ecc1241eSKay Sievers MODULE_ALIAS("platform:sun3x_esp");
282