linux/drivers/scsi/sun3x_esp.c
<<
>>
Prefs
   1/* sun3x_esp.c: ESP front-end for Sun3x systems.
   2 *
   3 * Copyright (C) 2007,2008 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
   4 */
   5
   6#include <linux/kernel.h>
   7#include <linux/gfp.h>
   8#include <linux/types.h>
   9#include <linux/delay.h>
  10#include <linux/module.h>
  11#include <linux/init.h>
  12#include <linux/platform_device.h>
  13#include <linux/dma-mapping.h>
  14#include <linux/interrupt.h>
  15#include <linux/io.h>
  16
  17#include <asm/sun3x.h>
  18#include <asm/dma.h>
  19#include <asm/dvma.h>
  20
  21/* DMA controller reg offsets */
  22#define DMA_CSR         0x00UL  /* rw  DMA control/status register    0x00   */
  23#define DMA_ADDR        0x04UL  /* rw  DMA transfer address register  0x04   */
  24#define DMA_COUNT       0x08UL  /* rw  DMA transfer count register    0x08   */
  25#define DMA_TEST        0x0cUL  /* rw  DMA test/debug register        0x0c   */
  26
  27#include <scsi/scsi_host.h>
  28
  29#include "esp_scsi.h"
  30
  31#define DRV_MODULE_NAME         "sun3x_esp"
  32#define PFX DRV_MODULE_NAME     ": "
  33#define DRV_VERSION             "1.000"
  34#define DRV_MODULE_RELDATE      "Nov 1, 2007"
  35
  36/*
  37 * m68k always assumes readl/writel operate on little endian
  38 * mmio space; this is wrong at least for Sun3x, so we
  39 * need to workaround this until a proper way is found
  40 */
  41#if 0
  42#define dma_read32(REG) \
  43        readl(esp->dma_regs + (REG))
  44#define dma_write32(VAL, REG) \
  45        writel((VAL), esp->dma_regs + (REG))
  46#else
  47#define dma_read32(REG) \
  48        *(volatile u32 *)(esp->dma_regs + (REG))
  49#define dma_write32(VAL, REG) \
  50        do { *(volatile u32 *)(esp->dma_regs + (REG)) = (VAL); } while (0)
  51#endif
  52
  53static void sun3x_esp_write8(struct esp *esp, u8 val, unsigned long reg)
  54{
  55        writeb(val, esp->regs + (reg * 4UL));
  56}
  57
  58static u8 sun3x_esp_read8(struct esp *esp, unsigned long reg)
  59{
  60        return readb(esp->regs + (reg * 4UL));
  61}
  62
  63static dma_addr_t sun3x_esp_map_single(struct esp *esp, void *buf,
  64                                      size_t sz, int dir)
  65{
  66        return dma_map_single(esp->dev, buf, sz, dir);
  67}
  68
  69static int sun3x_esp_map_sg(struct esp *esp, struct scatterlist *sg,
  70                                  int num_sg, int dir)
  71{
  72        return dma_map_sg(esp->dev, sg, num_sg, dir);
  73}
  74
  75static void sun3x_esp_unmap_single(struct esp *esp, dma_addr_t addr,
  76                                  size_t sz, int dir)
  77{
  78        dma_unmap_single(esp->dev, addr, sz, dir);
  79}
  80
  81static void sun3x_esp_unmap_sg(struct esp *esp, struct scatterlist *sg,
  82                              int num_sg, int dir)
  83{
  84        dma_unmap_sg(esp->dev, sg, num_sg, dir);
  85}
  86
  87static int sun3x_esp_irq_pending(struct esp *esp)
  88{
  89        if (dma_read32(DMA_CSR) & (DMA_HNDL_INTR | DMA_HNDL_ERROR))
  90                return 1;
  91        return 0;
  92}
  93
  94static void sun3x_esp_reset_dma(struct esp *esp)
  95{
  96        u32 val;
  97
  98        val = dma_read32(DMA_CSR);
  99        dma_write32(val | DMA_RST_SCSI, DMA_CSR);
 100        dma_write32(val & ~DMA_RST_SCSI, DMA_CSR);
 101
 102        /* Enable interrupts.  */
 103        val = dma_read32(DMA_CSR);
 104        dma_write32(val | DMA_INT_ENAB, DMA_CSR);
 105}
 106
 107static void sun3x_esp_dma_drain(struct esp *esp)
 108{
 109        u32 csr;
 110        int lim;
 111
 112        csr = dma_read32(DMA_CSR);
 113        if (!(csr & DMA_FIFO_ISDRAIN))
 114                return;
 115
 116        dma_write32(csr | DMA_FIFO_STDRAIN, DMA_CSR);
 117
 118        lim = 1000;
 119        while (dma_read32(DMA_CSR) & DMA_FIFO_ISDRAIN) {
 120                if (--lim == 0) {
 121                        printk(KERN_ALERT PFX "esp%d: DMA will not drain!\n",
 122                               esp->host->unique_id);
 123                        break;
 124                }
 125                udelay(1);
 126        }
 127}
 128
 129static void sun3x_esp_dma_invalidate(struct esp *esp)
 130{
 131        u32 val;
 132        int lim;
 133
 134        lim = 1000;
 135        while ((val = dma_read32(DMA_CSR)) & DMA_PEND_READ) {
 136                if (--lim == 0) {
 137                        printk(KERN_ALERT PFX "esp%d: DMA will not "
 138                               "invalidate!\n", esp->host->unique_id);
 139                        break;
 140                }
 141                udelay(1);
 142        }
 143
 144        val &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB);
 145        val |= DMA_FIFO_INV;
 146        dma_write32(val, DMA_CSR);
 147        val &= ~DMA_FIFO_INV;
 148        dma_write32(val, DMA_CSR);
 149}
 150
 151static void sun3x_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count,
 152                                  u32 dma_count, int write, u8 cmd)
 153{
 154        u32 csr;
 155
 156        BUG_ON(!(cmd & ESP_CMD_DMA));
 157
 158        sun3x_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW);
 159        sun3x_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED);
 160        csr = dma_read32(DMA_CSR);
 161        csr |= DMA_ENABLE;
 162        if (write)
 163                csr |= DMA_ST_WRITE;
 164        else
 165                csr &= ~DMA_ST_WRITE;
 166        dma_write32(csr, DMA_CSR);
 167        dma_write32(addr, DMA_ADDR);
 168
 169        scsi_esp_cmd(esp, cmd);
 170}
 171
 172static int sun3x_esp_dma_error(struct esp *esp)
 173{
 174        u32 csr = dma_read32(DMA_CSR);
 175
 176        if (csr & DMA_HNDL_ERROR)
 177                return 1;
 178
 179        return 0;
 180}
 181
 182static const struct esp_driver_ops sun3x_esp_ops = {
 183        .esp_write8     =       sun3x_esp_write8,
 184        .esp_read8      =       sun3x_esp_read8,
 185        .map_single     =       sun3x_esp_map_single,
 186        .map_sg         =       sun3x_esp_map_sg,
 187        .unmap_single   =       sun3x_esp_unmap_single,
 188        .unmap_sg       =       sun3x_esp_unmap_sg,
 189        .irq_pending    =       sun3x_esp_irq_pending,
 190        .reset_dma      =       sun3x_esp_reset_dma,
 191        .dma_drain      =       sun3x_esp_dma_drain,
 192        .dma_invalidate =       sun3x_esp_dma_invalidate,
 193        .send_dma_cmd   =       sun3x_esp_send_dma_cmd,
 194        .dma_error      =       sun3x_esp_dma_error,
 195};
 196
 197static int esp_sun3x_probe(struct platform_device *dev)
 198{
 199        struct scsi_host_template *tpnt = &scsi_esp_template;
 200        struct Scsi_Host *host;
 201        struct esp *esp;
 202        struct resource *res;
 203        int err = -ENOMEM;
 204
 205        host = scsi_host_alloc(tpnt, sizeof(struct esp));
 206        if (!host)
 207                goto fail;
 208
 209        host->max_id = 8;
 210        esp = shost_priv(host);
 211
 212        esp->host = host;
 213        esp->dev = dev;
 214        esp->ops = &sun3x_esp_ops;
 215
 216        res = platform_get_resource(dev, IORESOURCE_MEM, 0);
 217        if (!res || !res->start)
 218                goto fail_unlink;
 219
 220        esp->regs = ioremap_nocache(res->start, 0x20);
 221        if (!esp->regs)
 222                goto fail_unmap_regs;
 223
 224        res = platform_get_resource(dev, IORESOURCE_MEM, 1);
 225        if (!res || !res->start)
 226                goto fail_unmap_regs;
 227
 228        esp->dma_regs = ioremap_nocache(res->start, 0x10);
 229
 230        esp->command_block = dma_alloc_coherent(esp->dev, 16,
 231                                                &esp->command_block_dma,
 232                                                GFP_KERNEL);
 233        if (!esp->command_block)
 234                goto fail_unmap_regs_dma;
 235
 236        host->irq = platform_get_irq(dev, 0);
 237        err = request_irq(host->irq, scsi_esp_intr, IRQF_SHARED,
 238                          "SUN3X ESP", esp);
 239        if (err < 0)
 240                goto fail_unmap_command_block;
 241
 242        esp->scsi_id = 7;
 243        esp->host->this_id = esp->scsi_id;
 244        esp->scsi_id_mask = (1 << esp->scsi_id);
 245        esp->cfreq = 20000000;
 246
 247        dev_set_drvdata(&dev->dev, esp);
 248
 249        err = scsi_esp_register(esp, &dev->dev);
 250        if (err)
 251                goto fail_free_irq;
 252
 253        return 0;
 254
 255fail_free_irq:
 256        free_irq(host->irq, esp);
 257fail_unmap_command_block:
 258        dma_free_coherent(esp->dev, 16,
 259                          esp->command_block,
 260                          esp->command_block_dma);
 261fail_unmap_regs_dma:
 262        iounmap(esp->dma_regs);
 263fail_unmap_regs:
 264        iounmap(esp->regs);
 265fail_unlink:
 266        scsi_host_put(host);
 267fail:
 268        return err;
 269}
 270
 271static int esp_sun3x_remove(struct platform_device *dev)
 272{
 273        struct esp *esp = dev_get_drvdata(&dev->dev);
 274        unsigned int irq = esp->host->irq;
 275        u32 val;
 276
 277        scsi_esp_unregister(esp);
 278
 279        /* Disable interrupts.  */
 280        val = dma_read32(DMA_CSR);
 281        dma_write32(val & ~DMA_INT_ENAB, DMA_CSR);
 282
 283        free_irq(irq, esp);
 284        dma_free_coherent(esp->dev, 16,
 285                          esp->command_block,
 286                          esp->command_block_dma);
 287
 288        scsi_host_put(esp->host);
 289
 290        return 0;
 291}
 292
 293static struct platform_driver esp_sun3x_driver = {
 294        .probe          = esp_sun3x_probe,
 295        .remove         = esp_sun3x_remove,
 296        .driver = {
 297                .name   = "sun3x_esp",
 298        },
 299};
 300
 301static int __init sun3x_esp_init(void)
 302{
 303        return platform_driver_register(&esp_sun3x_driver);
 304}
 305
 306static void __exit sun3x_esp_exit(void)
 307{
 308        platform_driver_unregister(&esp_sun3x_driver);
 309}
 310
 311MODULE_DESCRIPTION("Sun3x ESP SCSI driver");
 312MODULE_AUTHOR("Thomas Bogendoerfer (tsbogend@alpha.franken.de)");
 313MODULE_LICENSE("GPL");
 314MODULE_VERSION(DRV_VERSION);
 315
 316module_init(sun3x_esp_init);
 317module_exit(sun3x_esp_exit);
 318MODULE_ALIAS("platform:sun3x_esp");
 319