| /* jazz_esp.c: ESP front-end for MIPS JAZZ systems. | |
| * | |
| * Copyright (C) 2007 Thomas Bogendörfer (tsbogend@alpha.frankende) | |
| */ | |
| #include <linux/kernel.h> | |
| #include <linux/gfp.h> | |
| #include <linux/types.h> | |
| #include <linux/module.h> | |
| #include <linux/init.h> | |
| #include <linux/interrupt.h> | |
| #include <linux/platform_device.h> | |
| #include <linux/dma-mapping.h> | |
| #include <asm/irq.h> | |
| #include <asm/io.h> | |
| #include <asm/dma.h> | |
| #include <asm/jazz.h> | |
| #include <asm/jazzdma.h> | |
| #include <scsi/scsi_host.h> | |
| #include "esp_scsi.h" | |
| #define DRV_MODULE_NAME "jazz_esp" | |
| #define PFX DRV_MODULE_NAME ": " | |
| #define DRV_VERSION "1.000" | |
| #define DRV_MODULE_RELDATE "May 19, 2007" | |
| static void jazz_esp_write8(struct esp *esp, u8 val, unsigned long reg) | |
| { | |
| *(volatile u8 *)(esp->regs + reg) = val; | |
| } | |
| static u8 jazz_esp_read8(struct esp *esp, unsigned long reg) | |
| { | |
| return *(volatile u8 *)(esp->regs + reg); | |
| } | |
| static dma_addr_t jazz_esp_map_single(struct esp *esp, void *buf, | |
| size_t sz, int dir) | |
| { | |
| return dma_map_single(esp->dev, buf, sz, dir); | |
| } | |
| static int jazz_esp_map_sg(struct esp *esp, struct scatterlist *sg, | |
| int num_sg, int dir) | |
| { | |
| return dma_map_sg(esp->dev, sg, num_sg, dir); | |
| } | |
| static void jazz_esp_unmap_single(struct esp *esp, dma_addr_t addr, | |
| size_t sz, int dir) | |
| { | |
| dma_unmap_single(esp->dev, addr, sz, dir); | |
| } | |
| static void jazz_esp_unmap_sg(struct esp *esp, struct scatterlist *sg, | |
| int num_sg, int dir) | |
| { | |
| dma_unmap_sg(esp->dev, sg, num_sg, dir); | |
| } | |
| static int jazz_esp_irq_pending(struct esp *esp) | |
| { | |
| if (jazz_esp_read8(esp, ESP_STATUS) & ESP_STAT_INTR) | |
| return 1; | |
| return 0; | |
| } | |
| static void jazz_esp_reset_dma(struct esp *esp) | |
| { | |
| vdma_disable ((int)esp->dma_regs); | |
| } | |
| static void jazz_esp_dma_drain(struct esp *esp) | |
| { | |
| /* nothing to do */ | |
| } | |
| static void jazz_esp_dma_invalidate(struct esp *esp) | |
| { | |
| vdma_disable ((int)esp->dma_regs); | |
| } | |
| static void jazz_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count, | |
| u32 dma_count, int write, u8 cmd) | |
| { | |
| BUG_ON(!(cmd & ESP_CMD_DMA)); | |
| jazz_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW); | |
| jazz_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED); | |
| vdma_disable ((int)esp->dma_regs); | |
| if (write) | |
| vdma_set_mode ((int)esp->dma_regs, DMA_MODE_READ); | |
| else | |
| vdma_set_mode ((int)esp->dma_regs, DMA_MODE_WRITE); | |
| vdma_set_addr ((int)esp->dma_regs, addr); | |
| vdma_set_count ((int)esp->dma_regs, dma_count); | |
| vdma_enable ((int)esp->dma_regs); | |
| scsi_esp_cmd(esp, cmd); | |
| } | |
| static int jazz_esp_dma_error(struct esp *esp) | |
| { | |
| u32 enable = vdma_get_enable((int)esp->dma_regs); | |
| if (enable & (R4030_MEM_INTR|R4030_ADDR_INTR)) | |
| return 1; | |
| return 0; | |
| } | |
| static const struct esp_driver_ops jazz_esp_ops = { | |
| .esp_write8 = jazz_esp_write8, | |
| .esp_read8 = jazz_esp_read8, | |
| .map_single = jazz_esp_map_single, | |
| .map_sg = jazz_esp_map_sg, | |
| .unmap_single = jazz_esp_unmap_single, | |
| .unmap_sg = jazz_esp_unmap_sg, | |
| .irq_pending = jazz_esp_irq_pending, | |
| .reset_dma = jazz_esp_reset_dma, | |
| .dma_drain = jazz_esp_dma_drain, | |
| .dma_invalidate = jazz_esp_dma_invalidate, | |
| .send_dma_cmd = jazz_esp_send_dma_cmd, | |
| .dma_error = jazz_esp_dma_error, | |
| }; | |
| static int esp_jazz_probe(struct platform_device *dev) | |
| { | |
| struct scsi_host_template *tpnt = &scsi_esp_template; | |
| struct Scsi_Host *host; | |
| struct esp *esp; | |
| struct resource *res; | |
| int err; | |
| host = scsi_host_alloc(tpnt, sizeof(struct esp)); | |
| err = -ENOMEM; | |
| if (!host) | |
| goto fail; | |
| host->max_id = 8; | |
| esp = shost_priv(host); | |
| esp->host = host; | |
| esp->dev = dev; | |
| esp->ops = &jazz_esp_ops; | |
| res = platform_get_resource(dev, IORESOURCE_MEM, 0); | |
| if (!res) | |
| goto fail_unlink; | |
| esp->regs = (void __iomem *)res->start; | |
| if (!esp->regs) | |
| goto fail_unlink; | |
| res = platform_get_resource(dev, IORESOURCE_MEM, 1); | |
| if (!res) | |
| goto fail_unlink; | |
| esp->dma_regs = (void __iomem *)res->start; | |
| esp->command_block = dma_alloc_coherent(esp->dev, 16, | |
| &esp->command_block_dma, | |
| GFP_KERNEL); | |
| if (!esp->command_block) | |
| goto fail_unmap_regs; | |
| host->irq = platform_get_irq(dev, 0); | |
| err = request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "ESP", esp); | |
| if (err < 0) | |
| goto fail_unmap_command_block; | |
| esp->scsi_id = 7; | |
| esp->host->this_id = esp->scsi_id; | |
| esp->scsi_id_mask = (1 << esp->scsi_id); | |
| esp->cfreq = 40000000; | |
| dev_set_drvdata(&dev->dev, esp); | |
| err = scsi_esp_register(esp, &dev->dev); | |
| if (err) | |
| goto fail_free_irq; | |
| return 0; | |
| fail_free_irq: | |
| free_irq(host->irq, esp); | |
| fail_unmap_command_block: | |
| dma_free_coherent(esp->dev, 16, | |
| esp->command_block, | |
| esp->command_block_dma); | |
| fail_unmap_regs: | |
| fail_unlink: | |
| scsi_host_put(host); | |
| fail: | |
| return err; | |
| } | |
| static int esp_jazz_remove(struct platform_device *dev) | |
| { | |
| struct esp *esp = dev_get_drvdata(&dev->dev); | |
| unsigned int irq = esp->host->irq; | |
| scsi_esp_unregister(esp); | |
| free_irq(irq, esp); | |
| dma_free_coherent(esp->dev, 16, | |
| esp->command_block, | |
| esp->command_block_dma); | |
| scsi_host_put(esp->host); | |
| return 0; | |
| } | |
| /* work with hotplug and coldplug */ | |
| MODULE_ALIAS("platform:jazz_esp"); | |
| static struct platform_driver esp_jazz_driver = { | |
| .probe = esp_jazz_probe, | |
| .remove = esp_jazz_remove, | |
| .driver = { | |
| .name = "jazz_esp", | |
| }, | |
| }; | |
| static int __init jazz_esp_init(void) | |
| { | |
| return platform_driver_register(&esp_jazz_driver); | |
| } | |
| static void __exit jazz_esp_exit(void) | |
| { | |
| platform_driver_unregister(&esp_jazz_driver); | |
| } | |
| MODULE_DESCRIPTION("JAZZ ESP SCSI driver"); | |
| MODULE_AUTHOR("Thomas Bogendoerfer (tsbogend@alpha.franken.de)"); | |
| MODULE_LICENSE("GPL"); | |
| MODULE_VERSION(DRV_VERSION); | |
| module_init(jazz_esp_init); | |
| module_exit(jazz_esp_exit); |