xref: /netbsd-src/external/gpl3/gdb/dist/sim/bfin/dv-bfin_dma.c (revision 1f4e7eb9e5e045e008f1894823a8e4e6c9f46890)
1 /* Blackfin Direct Memory Access (DMA) Channel model.
2 
3    Copyright (C) 2010-2024 Free Software Foundation, Inc.
4    Contributed by Analog Devices, Inc.
5 
6    This file is part of simulators.
7 
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
20 
21 /* This must come before any other includes.  */
22 #include "defs.h"
23 
24 #include <stdlib.h>
25 
26 #include "sim-main.h"
27 #include "devices.h"
28 #include "hw-device.h"
29 #include "dv-bfin_dma.h"
30 #include "dv-bfin_dmac.h"
31 
32 /* Note: This DMA implementation requires the producer to be the master when
33          the peer is MDMA.  The source is always a slave.  This way we don't
34          have the two DMA devices thrashing each other with one trying to
35          write and the other trying to read.  */
36 
37 struct bfin_dma
38 {
39   /* This top portion matches common dv_bfin struct.  */
40   bu32 base;
41   struct hw *dma_master;
42   bool acked;
43 
44   struct hw_event *handler;
45   unsigned ele_size;
46   struct hw *hw_peer;
47 
48   /* Order after here is important -- matches hardware MMR layout.  */
49   union {
50     struct { bu16 ndpl, ndph; };
51     bu32 next_desc_ptr;
52   };
53   union {
54     struct { bu16 sal, sah; };
55     bu32 start_addr;
56   };
57   bu16 BFIN_MMR_16 (config);
58   bu32 _pad0;
59   bu16 BFIN_MMR_16 (x_count);
60   bs16 BFIN_MMR_16 (x_modify);
61   bu16 BFIN_MMR_16 (y_count);
62   bs16 BFIN_MMR_16 (y_modify);
63   bu32 curr_desc_ptr, curr_addr;
64   bu16 BFIN_MMR_16 (irq_status);
65   bu16 BFIN_MMR_16 (peripheral_map);
66   bu16 BFIN_MMR_16 (curr_x_count);
67   bu32 _pad1;
68   bu16 BFIN_MMR_16 (curr_y_count);
69   bu32 _pad2;
70 };
71 #define mmr_base()      offsetof(struct bfin_dma, next_desc_ptr)
72 #define mmr_offset(mmr) (offsetof(struct bfin_dma, mmr) - mmr_base())
73 
74 static const char * const mmr_names[] =
75 {
76   "NEXT_DESC_PTR", "START_ADDR", "CONFIG", "<INV>", "X_COUNT", "X_MODIFY",
77   "Y_COUNT", "Y_MODIFY", "CURR_DESC_PTR", "CURR_ADDR", "IRQ_STATUS",
78   "PERIPHERAL_MAP", "CURR_X_COUNT", "<INV>", "CURR_Y_COUNT", "<INV>",
79 };
80 #define mmr_name(off) mmr_names[(off) / 4]
81 
82 static bool
83 bfin_dma_enabled (struct bfin_dma *dma)
84 {
85   return (dma->config & DMAEN);
86 }
87 
88 static bool
89 bfin_dma_running (struct bfin_dma *dma)
90 {
91   return (dma->irq_status & DMA_RUN);
92 }
93 
94 static struct hw *
95 bfin_dma_get_peer (struct hw *me, struct bfin_dma *dma)
96 {
97   if (dma->hw_peer)
98     return dma->hw_peer;
99   return dma->hw_peer = bfin_dmac_get_peer (me, dma->peripheral_map);
100 }
101 
102 static void
103 bfin_dma_process_desc (struct hw *me, struct bfin_dma *dma)
104 {
105   bu8 ndsize = (dma->config & NDSIZE) >> NDSIZE_SHIFT;
106   bu16 _flows[9], *flows = _flows;
107 
108   HW_TRACE ((me, "dma starting up %#x", dma->config));
109 
110   switch (dma->config & WDSIZE)
111     {
112     case WDSIZE_32:
113       dma->ele_size = 4;
114       break;
115     case WDSIZE_16:
116       dma->ele_size = 2;
117       break;
118     default:
119       dma->ele_size = 1;
120       break;
121     }
122 
123   /* Address has to be mutiple of transfer size.  */
124   if (dma->start_addr & (dma->ele_size - 1))
125     dma->irq_status |= DMA_ERR;
126 
127   if (dma->ele_size != (unsigned) abs (dma->x_modify))
128     hw_abort (me, "DMA config (striding) %#x not supported (x_modify: %d)",
129 	      dma->config, dma->x_modify);
130 
131   switch (dma->config & DMAFLOW)
132     {
133     case DMAFLOW_AUTO:
134     case DMAFLOW_STOP:
135       if (ndsize)
136 	hw_abort (me, "DMA config error: DMAFLOW_{AUTO,STOP} requires NDSIZE_0");
137       break;
138     case DMAFLOW_ARRAY:
139       if (ndsize == 0 || ndsize > 7)
140 	hw_abort (me, "DMA config error: DMAFLOW_ARRAY requires NDSIZE 1...7");
141       sim_read (hw_system (me), dma->curr_desc_ptr, flows, ndsize * 2);
142       break;
143     case DMAFLOW_SMALL:
144       if (ndsize == 0 || ndsize > 8)
145 	hw_abort (me, "DMA config error: DMAFLOW_SMALL requires NDSIZE 1...8");
146       sim_read (hw_system (me), dma->next_desc_ptr, flows, ndsize * 2);
147       break;
148     case DMAFLOW_LARGE:
149       if (ndsize == 0 || ndsize > 9)
150 	hw_abort (me, "DMA config error: DMAFLOW_LARGE requires NDSIZE 1...9");
151       sim_read (hw_system (me), dma->next_desc_ptr, flows, ndsize * 2);
152       break;
153     default:
154       hw_abort (me, "DMA config error: invalid DMAFLOW %#x", dma->config);
155     }
156 
157   if (ndsize)
158     {
159       bu8 idx;
160       bu16 *stores[] = {
161 	&dma->sal,
162 	&dma->sah,
163 	&dma->config,
164 	&dma->x_count,
165 	(void *) &dma->x_modify,
166 	&dma->y_count,
167 	(void *) &dma->y_modify,
168       };
169 
170       switch (dma->config & DMAFLOW)
171 	{
172 	case DMAFLOW_LARGE:
173 	  dma->ndph = _flows[1];
174 	  --ndsize;
175 	  ++flows;
176 	  ATTRIBUTE_FALLTHROUGH;
177 	case DMAFLOW_SMALL:
178 	  dma->ndpl = _flows[0];
179 	  --ndsize;
180 	  ++flows;
181 	  break;
182 	}
183 
184       for (idx = 0; idx < ndsize; ++idx)
185 	*stores[idx] = flows[idx];
186     }
187 
188   dma->curr_desc_ptr = dma->next_desc_ptr;
189   dma->curr_addr = dma->start_addr;
190   dma->curr_x_count = dma->x_count ? : 0xffff;
191   dma->curr_y_count = dma->y_count ? : 0xffff;
192 }
193 
194 static int
195 bfin_dma_finish_x (struct hw *me, struct bfin_dma *dma)
196 {
197   /* XXX: This would be the time to process the next descriptor.  */
198   /* XXX: Should this toggle Enable in dma->config ?  */
199 
200   if (dma->config & DI_EN)
201     hw_port_event (me, 0, 1);
202 
203   if ((dma->config & DMA2D) && dma->curr_y_count > 1)
204     {
205       dma->curr_y_count -= 1;
206       dma->curr_x_count = dma->x_count;
207 
208       /* With 2D, last X transfer does not modify curr_addr.  */
209       dma->curr_addr = dma->curr_addr - dma->x_modify + dma->y_modify;
210 
211       return 1;
212     }
213 
214   switch (dma->config & DMAFLOW)
215     {
216     case DMAFLOW_STOP:
217       HW_TRACE ((me, "dma is complete"));
218       dma->irq_status = (dma->irq_status & ~DMA_RUN) | DMA_DONE;
219       return 0;
220     default:
221       bfin_dma_process_desc (me, dma);
222       return 1;
223     }
224 }
225 
226 static void bfin_dma_hw_event_callback (struct hw *, void *);
227 
228 static void
229 bfin_dma_reschedule (struct hw *me, unsigned delay)
230 {
231   struct bfin_dma *dma = hw_data (me);
232   if (dma->handler)
233     {
234       hw_event_queue_deschedule (me, dma->handler);
235       dma->handler = NULL;
236     }
237   if (!delay)
238     return;
239   HW_TRACE ((me, "scheduling next process in %u", delay));
240   dma->handler = hw_event_queue_schedule (me, delay,
241 					  bfin_dma_hw_event_callback, dma);
242 }
243 
244 /* Chew through the DMA over and over.  */
245 static void
246 bfin_dma_hw_event_callback (struct hw *me, void *data)
247 {
248   struct bfin_dma *dma = data;
249   struct hw *peer;
250   struct dv_bfin *bfin_peer;
251   bu8 buf[4096];
252   unsigned ret, nr_bytes, ele_count;
253 
254   dma->handler = NULL;
255   peer = bfin_dma_get_peer (me, dma);
256   bfin_peer = hw_data (peer);
257   ret = 0;
258   if (dma->x_modify < 0)
259     /* XXX: This sucks performance wise.  */
260     nr_bytes = dma->ele_size;
261   else
262     nr_bytes = min (sizeof (buf), dma->curr_x_count * dma->ele_size);
263 
264   /* Pumping a chunk!  */
265   bfin_peer->dma_master = me;
266   bfin_peer->acked = false;
267   if (dma->config & WNR)
268     {
269       HW_TRACE ((me, "dma transfer to 0x%08lx length %u",
270 		 (unsigned long) dma->curr_addr, nr_bytes));
271 
272       ret = hw_dma_read_buffer (peer, buf, 0, dma->curr_addr, nr_bytes);
273       /* Has the DMA stalled ?  abort for now.  */
274       if (ret == 0)
275 	goto reschedule;
276       /* XXX: How to handle partial DMA transfers ?  */
277       if (ret % dma->ele_size)
278 	goto error;
279       ret = sim_write (hw_system (me), dma->curr_addr, buf, ret);
280     }
281   else
282     {
283       HW_TRACE ((me, "dma transfer from 0x%08lx length %u",
284 		 (unsigned long) dma->curr_addr, nr_bytes));
285 
286       ret = sim_read (hw_system (me), dma->curr_addr, buf, nr_bytes);
287       if (ret == 0)
288 	goto reschedule;
289       /* XXX: How to handle partial DMA transfers ?  */
290       if (ret % dma->ele_size)
291 	goto error;
292       ret = hw_dma_write_buffer (peer, buf, 0, dma->curr_addr, ret, 0);
293       if (ret == 0)
294 	goto reschedule;
295     }
296 
297   /* Ignore partial writes.  */
298   ele_count = ret / dma->ele_size;
299   dma->curr_addr += ele_count * dma->x_modify;
300   dma->curr_x_count -= ele_count;
301 
302   if ((!dma->acked && dma->curr_x_count) || bfin_dma_finish_x (me, dma))
303     /* Still got work to do, so schedule again.  */
304  reschedule:
305     bfin_dma_reschedule (me, ret ? 1 : 5000);
306 
307   return;
308 
309  error:
310   /* Don't reschedule on errors ...  */
311   dma->irq_status |= DMA_ERR;
312 }
313 
314 static unsigned
315 bfin_dma_io_write_buffer (struct hw *me, const void *source, int space,
316 			  address_word addr, unsigned nr_bytes)
317 {
318   struct bfin_dma *dma = hw_data (me);
319   bu32 mmr_off;
320   bu32 value;
321   bu16 *value16p;
322   bu32 *value32p;
323   void *valuep;
324 
325   /* Invalid access mode is higher priority than missing register.  */
326   if (!dv_bfin_mmr_require_16_32 (me, addr, nr_bytes, true))
327     return 0;
328 
329   if (nr_bytes == 4)
330     value = dv_load_4 (source);
331   else
332     value = dv_load_2 (source);
333 
334   mmr_off = addr % dma->base;
335   valuep = (void *)((uintptr_t)dma + mmr_base() + mmr_off);
336   value16p = valuep;
337   value32p = valuep;
338 
339   HW_TRACE_WRITE ();
340 
341   /* XXX: All registers are RO when DMA is enabled (except IRQ_STATUS).
342           But does the HW discard writes or send up IVGHW ?  The sim
343           simply discards atm ... */
344   switch (mmr_off)
345     {
346     case mmr_offset(next_desc_ptr):
347     case mmr_offset(start_addr):
348     case mmr_offset(curr_desc_ptr):
349     case mmr_offset(curr_addr):
350       /* Don't require 32bit access as all DMA MMRs can be used as 16bit.  */
351       if (!bfin_dma_running (dma))
352 	{
353 	  if (nr_bytes == 4)
354 	    *value32p = value;
355 	  else
356 	    *value16p = value;
357 	}
358       else
359 	HW_TRACE ((me, "discarding write while dma running"));
360       break;
361     case mmr_offset(x_count):
362     case mmr_offset(x_modify):
363     case mmr_offset(y_count):
364     case mmr_offset(y_modify):
365       if (!bfin_dma_running (dma))
366 	*value16p = value;
367       break;
368     case mmr_offset(peripheral_map):
369       if (!bfin_dma_running (dma))
370 	{
371 	  *value16p = (*value16p & CTYPE) | (value & ~CTYPE);
372 	  /* Clear peripheral peer so it gets looked up again.  */
373 	  dma->hw_peer = NULL;
374 	}
375       else
376 	HW_TRACE ((me, "discarding write while dma running"));
377       break;
378     case mmr_offset(config):
379       /* XXX: How to handle updating CONFIG of a running channel ?  */
380       if (nr_bytes == 4)
381 	*value32p = value;
382       else
383 	*value16p = value;
384 
385       if (bfin_dma_enabled (dma))
386 	{
387 	  dma->irq_status |= DMA_RUN;
388 	  bfin_dma_process_desc (me, dma);
389 	  /* The writer is the master.  */
390 	  if (!(dma->peripheral_map & CTYPE) || (dma->config & WNR))
391 	    bfin_dma_reschedule (me, 1);
392 	}
393       else
394 	{
395 	  dma->irq_status &= ~DMA_RUN;
396 	  bfin_dma_reschedule (me, 0);
397 	}
398       break;
399     case mmr_offset(irq_status):
400       dv_w1c_2 (value16p, value, DMA_DONE | DMA_ERR);
401       break;
402     case mmr_offset(curr_x_count):
403     case mmr_offset(curr_y_count):
404       if (!bfin_dma_running (dma))
405 	*value16p = value;
406       else
407 	HW_TRACE ((me, "discarding write while dma running"));
408       break;
409     default:
410       /* XXX: The HW lets the pad regions be read/written ...  */
411       dv_bfin_mmr_invalid (me, addr, nr_bytes, true);
412       return 0;
413     }
414 
415   return nr_bytes;
416 }
417 
418 static unsigned
419 bfin_dma_io_read_buffer (struct hw *me, void *dest, int space,
420 			 address_word addr, unsigned nr_bytes)
421 {
422   struct bfin_dma *dma = hw_data (me);
423   bu32 mmr_off;
424   bu16 *value16p;
425   bu32 *value32p;
426   void *valuep;
427 
428   /* Invalid access mode is higher priority than missing register.  */
429   if (!dv_bfin_mmr_require_16_32 (me, addr, nr_bytes, false))
430     return 0;
431 
432   mmr_off = addr % dma->base;
433   valuep = (void *)((uintptr_t)dma + mmr_base() + mmr_off);
434   value16p = valuep;
435   value32p = valuep;
436 
437   HW_TRACE_READ ();
438 
439   /* Hardware lets you read all MMRs as 16 or 32 bits, even reserved.  */
440   if (nr_bytes == 4)
441     dv_store_4 (dest, *value32p);
442   else
443     dv_store_2 (dest, *value16p);
444 
445   return nr_bytes;
446 }
447 
448 static unsigned
449 bfin_dma_dma_read_buffer (struct hw *me, void *dest, int space,
450 			  unsigned_word addr, unsigned nr_bytes)
451 {
452   struct bfin_dma *dma = hw_data (me);
453   unsigned ret, ele_count;
454 
455   HW_TRACE_DMA_READ ();
456 
457   /* If someone is trying to read from me, I have to be enabled.  */
458   if (!bfin_dma_enabled (dma) && !bfin_dma_running (dma))
459     return 0;
460 
461   /* XXX: handle x_modify ...  */
462   ret = sim_read (hw_system (me), dma->curr_addr, dest, nr_bytes);
463   /* Ignore partial writes.  */
464   ele_count = ret / dma->ele_size;
465   /* Has the DMA stalled ?  abort for now.  */
466   if (!ele_count)
467     return 0;
468 
469   dma->curr_addr += ele_count * dma->x_modify;
470   dma->curr_x_count -= ele_count;
471 
472   if (dma->curr_x_count == 0)
473     bfin_dma_finish_x (me, dma);
474 
475   return ret;
476 }
477 
478 static unsigned
479 bfin_dma_dma_write_buffer (struct hw *me, const void *source,
480 			   int space, unsigned_word addr,
481 			   unsigned nr_bytes,
482 			   int violate_read_only_section)
483 {
484   struct bfin_dma *dma = hw_data (me);
485   unsigned ret, ele_count;
486 
487   HW_TRACE_DMA_WRITE ();
488 
489   /* If someone is trying to write to me, I have to be enabled.  */
490   if (!bfin_dma_enabled (dma) && !bfin_dma_running (dma))
491     return 0;
492 
493   /* XXX: handle x_modify ...  */
494   ret = sim_write (hw_system (me), dma->curr_addr, source, nr_bytes);
495   /* Ignore partial writes.  */
496   ele_count = ret / dma->ele_size;
497   /* Has the DMA stalled ?  abort for now.  */
498   if (!ele_count)
499     return 0;
500 
501   dma->curr_addr += ele_count * dma->x_modify;
502   dma->curr_x_count -= ele_count;
503 
504   if (dma->curr_x_count == 0)
505     bfin_dma_finish_x (me, dma);
506 
507   return ret;
508 }
509 
510 static const struct hw_port_descriptor bfin_dma_ports[] =
511 {
512   { "di", 0, 0, output_port, }, /* DMA Interrupt */
513   { NULL, 0, 0, 0, },
514 };
515 
516 static void
517 attach_bfin_dma_regs (struct hw *me, struct bfin_dma *dma)
518 {
519   address_word attach_address;
520   int attach_space;
521   unsigned attach_size;
522   reg_property_spec reg;
523 
524   if (hw_find_property (me, "reg") == NULL)
525     hw_abort (me, "Missing \"reg\" property");
526 
527   if (!hw_find_reg_array_property (me, "reg", 0, &reg))
528     hw_abort (me, "\"reg\" property must contain three addr/size entries");
529 
530   hw_unit_address_to_attach_address (hw_parent (me),
531 				     &reg.address,
532 				     &attach_space, &attach_address, me);
533   hw_unit_size_to_attach_size (hw_parent (me), &reg.size, &attach_size, me);
534 
535   if (attach_size != BFIN_MMR_DMA_SIZE)
536     hw_abort (me, "\"reg\" size must be %#x", BFIN_MMR_DMA_SIZE);
537 
538   hw_attach_address (hw_parent (me),
539 		     0, attach_space, attach_address, attach_size, me);
540 
541   dma->base = attach_address;
542 }
543 
544 static void
545 bfin_dma_finish (struct hw *me)
546 {
547   struct bfin_dma *dma;
548 
549   dma = HW_ZALLOC (me, struct bfin_dma);
550 
551   set_hw_data (me, dma);
552   set_hw_io_read_buffer (me, bfin_dma_io_read_buffer);
553   set_hw_io_write_buffer (me, bfin_dma_io_write_buffer);
554   set_hw_dma_read_buffer (me, bfin_dma_dma_read_buffer);
555   set_hw_dma_write_buffer (me, bfin_dma_dma_write_buffer);
556   set_hw_ports (me, bfin_dma_ports);
557 
558   attach_bfin_dma_regs (me, dma);
559 
560   /* Initialize the DMA Channel.  */
561   dma->peripheral_map = bfin_dmac_default_pmap (me);
562 }
563 
564 const struct hw_descriptor dv_bfin_dma_descriptor[] =
565 {
566   {"bfin_dma", bfin_dma_finish,},
567   {NULL, NULL},
568 };
569