Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
a3000.c
Go to the documentation of this file.
1 #include <linux/types.h>
2 #include <linux/mm.h>
3 #include <linux/ioport.h>
4 #include <linux/init.h>
5 #include <linux/slab.h>
6 #include <linux/spinlock.h>
7 #include <linux/interrupt.h>
9 #include <linux/module.h>
10 
11 #include <asm/page.h>
12 #include <asm/pgtable.h>
13 #include <asm/amigaints.h>
14 #include <asm/amigahw.h>
15 
16 #include "scsi.h"
17 #include "wd33c93.h"
18 #include "a3000.h"
19 
20 
24 };
25 
26 static irqreturn_t a3000_intr(int irq, void *data)
27 {
28  struct Scsi_Host *instance = data;
29  struct a3000_hostdata *hdata = shost_priv(instance);
30  unsigned int status = hdata->regs->ISTR;
31  unsigned long flags;
32 
33  if (!(status & ISTR_INT_P))
34  return IRQ_NONE;
35  if (status & ISTR_INTS) {
36  spin_lock_irqsave(instance->host_lock, flags);
37  wd33c93_intr(instance);
38  spin_unlock_irqrestore(instance->host_lock, flags);
39  return IRQ_HANDLED;
40  }
41  pr_warning("Non-serviced A3000 SCSI-interrupt? ISTR = %02x\n", status);
42  return IRQ_NONE;
43 }
44 
45 static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
46 {
47  struct Scsi_Host *instance = cmd->device->host;
48  struct a3000_hostdata *hdata = shost_priv(instance);
49  struct WD33C93_hostdata *wh = &hdata->wh;
50  struct a3000_scsiregs *regs = hdata->regs;
51  unsigned short cntr = CNTR_PDMD | CNTR_INTEN;
52  unsigned long addr = virt_to_bus(cmd->SCp.ptr);
53 
54  /*
55  * if the physical address has the wrong alignment, or if
56  * physical address is bad, or if it is a write and at the
57  * end of a physical memory chunk, then allocate a bounce
58  * buffer
59  */
60  if (addr & A3000_XFER_MASK) {
61  wh->dma_bounce_len = (cmd->SCp.this_residual + 511) & ~0x1ff;
63  GFP_KERNEL);
64 
65  /* can't allocate memory; use PIO */
66  if (!wh->dma_bounce_buffer) {
67  wh->dma_bounce_len = 0;
68  return 1;
69  }
70 
71  if (!dir_in) {
72  /* copy to bounce buffer for a write */
73  memcpy(wh->dma_bounce_buffer, cmd->SCp.ptr,
74  cmd->SCp.this_residual);
75  }
76 
77  addr = virt_to_bus(wh->dma_bounce_buffer);
78  }
79 
80  /* setup dma direction */
81  if (!dir_in)
82  cntr |= CNTR_DDIR;
83 
84  /* remember direction */
85  wh->dma_dir = dir_in;
86 
87  regs->CNTR = cntr;
88 
89  /* setup DMA *physical* address */
90  regs->ACR = addr;
91 
92  if (dir_in) {
93  /* invalidate any cache */
94  cache_clear(addr, cmd->SCp.this_residual);
95  } else {
96  /* push any dirty cache */
97  cache_push(addr, cmd->SCp.this_residual);
98  }
99 
100  /* start DMA */
101  mb(); /* make sure setup is completed */
102  regs->ST_DMA = 1;
103  mb(); /* make sure DMA has started before next IO */
104 
105  /* return success */
106  return 0;
107 }
108 
109 static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
110  int status)
111 {
112  struct a3000_hostdata *hdata = shost_priv(instance);
113  struct WD33C93_hostdata *wh = &hdata->wh;
114  struct a3000_scsiregs *regs = hdata->regs;
115 
116  /* disable SCSI interrupts */
117  unsigned short cntr = CNTR_PDMD;
118 
119  if (!wh->dma_dir)
120  cntr |= CNTR_DDIR;
121 
122  regs->CNTR = cntr;
123  mb(); /* make sure CNTR is updated before next IO */
124 
125  /* flush if we were reading */
126  if (wh->dma_dir) {
127  regs->FLUSH = 1;
128  mb(); /* don't allow prefetch */
129  while (!(regs->ISTR & ISTR_FE_FLG))
130  barrier();
131  mb(); /* no IO until FLUSH is done */
132  }
133 
134  /* clear a possible interrupt */
135  /* I think that this CINT is only necessary if you are
136  * using the terminal count features. HM 7 Mar 1994
137  */
138  regs->CINT = 1;
139 
140  /* stop DMA */
141  regs->SP_DMA = 1;
142  mb(); /* make sure DMA is stopped before next IO */
143 
144  /* restore the CONTROL bits (minus the direction flag) */
145  regs->CNTR = CNTR_PDMD | CNTR_INTEN;
146  mb(); /* make sure CNTR is updated before next IO */
147 
148  /* copy from a bounce buffer, if necessary */
149  if (status && wh->dma_bounce_buffer) {
150  if (SCpnt) {
151  if (wh->dma_dir && SCpnt)
152  memcpy(SCpnt->SCp.ptr, wh->dma_bounce_buffer,
153  SCpnt->SCp.this_residual);
155  wh->dma_bounce_buffer = NULL;
156  wh->dma_bounce_len = 0;
157  } else {
159  wh->dma_bounce_buffer = NULL;
160  wh->dma_bounce_len = 0;
161  }
162  }
163 }
164 
165 static int a3000_bus_reset(struct scsi_cmnd *cmd)
166 {
167  struct Scsi_Host *instance = cmd->device->host;
168 
169  /* FIXME perform bus-specific reset */
170 
171  /* FIXME 2: kill this entire function, which should
172  cause mid-layer to call wd33c93_host_reset anyway? */
173 
174  spin_lock_irq(instance->host_lock);
175  wd33c93_host_reset(cmd);
176  spin_unlock_irq(instance->host_lock);
177 
178  return SUCCESS;
179 }
180 
181 static struct scsi_host_template amiga_a3000_scsi_template = {
182  .module = THIS_MODULE,
183  .name = "Amiga 3000 built-in SCSI",
184  .proc_info = wd33c93_proc_info,
185  .proc_name = "A3000",
186  .queuecommand = wd33c93_queuecommand,
187  .eh_abort_handler = wd33c93_abort,
188  .eh_bus_reset_handler = a3000_bus_reset,
189  .eh_host_reset_handler = wd33c93_host_reset,
190  .can_queue = CAN_QUEUE,
191  .this_id = 7,
192  .sg_tablesize = SG_ALL,
193  .cmd_per_lun = CMD_PER_LUN,
194  .use_clustering = ENABLE_CLUSTERING
195 };
196 
197 static int __init amiga_a3000_scsi_probe(struct platform_device *pdev)
198 {
199  struct resource *res;
200  struct Scsi_Host *instance;
201  int error;
202  struct a3000_scsiregs *regs;
203  wd33c93_regs wdregs;
204  struct a3000_hostdata *hdata;
205 
206  res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
207  if (!res)
208  return -ENODEV;
209 
210  if (!request_mem_region(res->start, resource_size(res), "wd33c93"))
211  return -EBUSY;
212 
213  instance = scsi_host_alloc(&amiga_a3000_scsi_template,
214  sizeof(struct a3000_hostdata));
215  if (!instance) {
216  error = -ENOMEM;
217  goto fail_alloc;
218  }
219 
220  instance->irq = IRQ_AMIGA_PORTS;
221 
222  regs = (struct a3000_scsiregs *)ZTWO_VADDR(res->start);
223  regs->DAWR = DAWR_A3000;
224 
225  wdregs.SASR = &regs->SASR;
226  wdregs.SCMD = &regs->SCMD;
227 
228  hdata = shost_priv(instance);
229  hdata->wh.no_sync = 0xff;
230  hdata->wh.fast = 0;
231  hdata->wh.dma_mode = CTRL_DMA;
232  hdata->regs = regs;
233 
234  wd33c93_init(instance, wdregs, dma_setup, dma_stop, WD33C93_FS_12_15);
235  error = request_irq(IRQ_AMIGA_PORTS, a3000_intr, IRQF_SHARED,
236  "A3000 SCSI", instance);
237  if (error)
238  goto fail_irq;
239 
240  regs->CNTR = CNTR_PDMD | CNTR_INTEN;
241 
242  error = scsi_add_host(instance, NULL);
243  if (error)
244  goto fail_host;
245 
246  platform_set_drvdata(pdev, instance);
247 
248  scsi_scan_host(instance);
249  return 0;
250 
251 fail_host:
252  free_irq(IRQ_AMIGA_PORTS, instance);
253 fail_irq:
254  scsi_host_put(instance);
255 fail_alloc:
256  release_mem_region(res->start, resource_size(res));
257  return error;
258 }
259 
260 static int __exit amiga_a3000_scsi_remove(struct platform_device *pdev)
261 {
262  struct Scsi_Host *instance = platform_get_drvdata(pdev);
263  struct a3000_hostdata *hdata = shost_priv(instance);
264  struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
265 
266  hdata->regs->CNTR = 0;
267  scsi_remove_host(instance);
268  free_irq(IRQ_AMIGA_PORTS, instance);
269  scsi_host_put(instance);
270  release_mem_region(res->start, resource_size(res));
271  return 0;
272 }
273 
274 static struct platform_driver amiga_a3000_scsi_driver = {
275  .remove = __exit_p(amiga_a3000_scsi_remove),
276  .driver = {
277  .name = "amiga-a3000-scsi",
278  .owner = THIS_MODULE,
279  },
280 };
281 
282 static int __init amiga_a3000_scsi_init(void)
283 {
284  return platform_driver_probe(&amiga_a3000_scsi_driver,
285  amiga_a3000_scsi_probe);
286 }
287 module_init(amiga_a3000_scsi_init);
288 
289 static void __exit amiga_a3000_scsi_exit(void)
290 {
291  platform_driver_unregister(&amiga_a3000_scsi_driver);
292 }
293 module_exit(amiga_a3000_scsi_exit);
294 
295 MODULE_DESCRIPTION("Amiga 3000 built-in SCSI");
296 MODULE_LICENSE("GPL");
297 MODULE_ALIAS("platform:amiga-a3000-scsi");