Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  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 
 52 static void sun3x_esp_write8(struct esp *esp, u8 val, unsigned long reg)
 53 {
 54         writeb(val, esp->regs + (reg * 4UL));
 55 }
 56 
 57 static u8 sun3x_esp_read8(struct esp *esp, unsigned long reg)
 58 {
 59         return readb(esp->regs + (reg * 4UL));
 60 }
 61 
 62 static 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 
 68 static 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 
 74 static 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 
 80 static 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 
 86 static 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 
 93 static 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 
106 static 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 
128 static 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 
150 static 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 
171 static 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 
181 static 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 
196 static 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 
254 fail_free_irq:
255         free_irq(host->irq, esp);
256 fail_unmap_command_block:
257         dma_free_coherent(esp->dev, 16,
258                           esp->command_block,
259                           esp->command_block_dma);
260 fail_unmap_regs_dma:
261         iounmap(esp->dma_regs);
262 fail_unmap_regs:
263         iounmap(esp->regs);
264 fail_unlink:
265         scsi_host_put(host);
266 fail:
267         return err;
268 }
269 
270 static 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 
292 static 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         },
298 };
299 
300 static int __init sun3x_esp_init(void)
301 {
302         return platform_driver_register(&esp_sun3x_driver);
303 }
304 
305 static void __exit sun3x_esp_exit(void)
306 {
307         platform_driver_unregister(&esp_sun3x_driver);
308 }
309 
310 MODULE_DESCRIPTION("Sun3x ESP SCSI driver");
311 MODULE_AUTHOR("Thomas Bogendoerfer (tsbogend@alpha.franken.de)");
312 MODULE_LICENSE("GPL");
313 MODULE_VERSION(DRV_VERSION);
314 
315 module_init(sun3x_esp_init);
316 module_exit(sun3x_esp_exit);
317 
  This page was automatically generated by the LXR engine.