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