162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* jazz_esp.c: ESP front-end for MIPS JAZZ systems. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2007 Thomas Bogendörfer (tsbogend@alpha.frankende) 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/gfp.h> 962306a36Sopenharmony_ci#include <linux/types.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <asm/irq.h> 1762306a36Sopenharmony_ci#include <asm/io.h> 1862306a36Sopenharmony_ci#include <asm/dma.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <asm/jazz.h> 2162306a36Sopenharmony_ci#include <asm/jazzdma.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <scsi/scsi_host.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "esp_scsi.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define DRV_MODULE_NAME "jazz_esp" 2862306a36Sopenharmony_ci#define PFX DRV_MODULE_NAME ": " 2962306a36Sopenharmony_ci#define DRV_VERSION "1.000" 3062306a36Sopenharmony_ci#define DRV_MODULE_RELDATE "May 19, 2007" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void jazz_esp_write8(struct esp *esp, u8 val, unsigned long reg) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci *(volatile u8 *)(esp->regs + reg) = val; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic u8 jazz_esp_read8(struct esp *esp, unsigned long reg) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci return *(volatile u8 *)(esp->regs + reg); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int jazz_esp_irq_pending(struct esp *esp) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci if (jazz_esp_read8(esp, ESP_STATUS) & ESP_STAT_INTR) 4562306a36Sopenharmony_ci return 1; 4662306a36Sopenharmony_ci return 0; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void jazz_esp_reset_dma(struct esp *esp) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci vdma_disable ((int)esp->dma_regs); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void jazz_esp_dma_drain(struct esp *esp) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci /* nothing to do */ 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void jazz_esp_dma_invalidate(struct esp *esp) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci vdma_disable ((int)esp->dma_regs); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void jazz_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count, 6562306a36Sopenharmony_ci u32 dma_count, int write, u8 cmd) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci BUG_ON(!(cmd & ESP_CMD_DMA)); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci jazz_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW); 7062306a36Sopenharmony_ci jazz_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED); 7162306a36Sopenharmony_ci vdma_disable ((int)esp->dma_regs); 7262306a36Sopenharmony_ci if (write) 7362306a36Sopenharmony_ci vdma_set_mode ((int)esp->dma_regs, DMA_MODE_READ); 7462306a36Sopenharmony_ci else 7562306a36Sopenharmony_ci vdma_set_mode ((int)esp->dma_regs, DMA_MODE_WRITE); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci vdma_set_addr ((int)esp->dma_regs, addr); 7862306a36Sopenharmony_ci vdma_set_count ((int)esp->dma_regs, dma_count); 7962306a36Sopenharmony_ci vdma_enable ((int)esp->dma_regs); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci scsi_esp_cmd(esp, cmd); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int jazz_esp_dma_error(struct esp *esp) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci u32 enable = vdma_get_enable((int)esp->dma_regs); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (enable & (R4030_MEM_INTR|R4030_ADDR_INTR)) 8962306a36Sopenharmony_ci return 1; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return 0; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic const struct esp_driver_ops jazz_esp_ops = { 9562306a36Sopenharmony_ci .esp_write8 = jazz_esp_write8, 9662306a36Sopenharmony_ci .esp_read8 = jazz_esp_read8, 9762306a36Sopenharmony_ci .irq_pending = jazz_esp_irq_pending, 9862306a36Sopenharmony_ci .reset_dma = jazz_esp_reset_dma, 9962306a36Sopenharmony_ci .dma_drain = jazz_esp_dma_drain, 10062306a36Sopenharmony_ci .dma_invalidate = jazz_esp_dma_invalidate, 10162306a36Sopenharmony_ci .send_dma_cmd = jazz_esp_send_dma_cmd, 10262306a36Sopenharmony_ci .dma_error = jazz_esp_dma_error, 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int esp_jazz_probe(struct platform_device *dev) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci const struct scsi_host_template *tpnt = &scsi_esp_template; 10862306a36Sopenharmony_ci struct Scsi_Host *host; 10962306a36Sopenharmony_ci struct esp *esp; 11062306a36Sopenharmony_ci struct resource *res; 11162306a36Sopenharmony_ci int err; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci host = scsi_host_alloc(tpnt, sizeof(struct esp)); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci err = -ENOMEM; 11662306a36Sopenharmony_ci if (!host) 11762306a36Sopenharmony_ci goto fail; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci host->max_id = 8; 12062306a36Sopenharmony_ci esp = shost_priv(host); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci esp->host = host; 12362306a36Sopenharmony_ci esp->dev = &dev->dev; 12462306a36Sopenharmony_ci esp->ops = &jazz_esp_ops; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci res = platform_get_resource(dev, IORESOURCE_MEM, 0); 12762306a36Sopenharmony_ci if (!res) 12862306a36Sopenharmony_ci goto fail_unlink; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci esp->regs = (void __iomem *)res->start; 13162306a36Sopenharmony_ci if (!esp->regs) 13262306a36Sopenharmony_ci goto fail_unlink; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci res = platform_get_resource(dev, IORESOURCE_MEM, 1); 13562306a36Sopenharmony_ci if (!res) 13662306a36Sopenharmony_ci goto fail_unlink; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci esp->dma_regs = (void __iomem *)res->start; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci esp->command_block = dma_alloc_coherent(esp->dev, 16, 14162306a36Sopenharmony_ci &esp->command_block_dma, 14262306a36Sopenharmony_ci GFP_KERNEL); 14362306a36Sopenharmony_ci if (!esp->command_block) 14462306a36Sopenharmony_ci goto fail_unmap_regs; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci host->irq = err = platform_get_irq(dev, 0); 14762306a36Sopenharmony_ci if (err < 0) 14862306a36Sopenharmony_ci goto fail_unmap_command_block; 14962306a36Sopenharmony_ci err = request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "ESP", esp); 15062306a36Sopenharmony_ci if (err < 0) 15162306a36Sopenharmony_ci goto fail_unmap_command_block; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci esp->scsi_id = 7; 15462306a36Sopenharmony_ci esp->host->this_id = esp->scsi_id; 15562306a36Sopenharmony_ci esp->scsi_id_mask = (1 << esp->scsi_id); 15662306a36Sopenharmony_ci esp->cfreq = 40000000; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci dev_set_drvdata(&dev->dev, esp); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci err = scsi_esp_register(esp); 16162306a36Sopenharmony_ci if (err) 16262306a36Sopenharmony_ci goto fail_free_irq; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cifail_free_irq: 16762306a36Sopenharmony_ci free_irq(host->irq, esp); 16862306a36Sopenharmony_cifail_unmap_command_block: 16962306a36Sopenharmony_ci dma_free_coherent(esp->dev, 16, 17062306a36Sopenharmony_ci esp->command_block, 17162306a36Sopenharmony_ci esp->command_block_dma); 17262306a36Sopenharmony_cifail_unmap_regs: 17362306a36Sopenharmony_cifail_unlink: 17462306a36Sopenharmony_ci scsi_host_put(host); 17562306a36Sopenharmony_cifail: 17662306a36Sopenharmony_ci return err; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic int esp_jazz_remove(struct platform_device *dev) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct esp *esp = dev_get_drvdata(&dev->dev); 18262306a36Sopenharmony_ci unsigned int irq = esp->host->irq; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci scsi_esp_unregister(esp); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci free_irq(irq, esp); 18762306a36Sopenharmony_ci dma_free_coherent(esp->dev, 16, 18862306a36Sopenharmony_ci esp->command_block, 18962306a36Sopenharmony_ci esp->command_block_dma); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci scsi_host_put(esp->host); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* work with hotplug and coldplug */ 19762306a36Sopenharmony_ciMODULE_ALIAS("platform:jazz_esp"); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic struct platform_driver esp_jazz_driver = { 20062306a36Sopenharmony_ci .probe = esp_jazz_probe, 20162306a36Sopenharmony_ci .remove = esp_jazz_remove, 20262306a36Sopenharmony_ci .driver = { 20362306a36Sopenharmony_ci .name = "jazz_esp", 20462306a36Sopenharmony_ci }, 20562306a36Sopenharmony_ci}; 20662306a36Sopenharmony_cimodule_platform_driver(esp_jazz_driver); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciMODULE_DESCRIPTION("JAZZ ESP SCSI driver"); 20962306a36Sopenharmony_ciMODULE_AUTHOR("Thomas Bogendoerfer (tsbogend@alpha.franken.de)"); 21062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 21162306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 212