xref: /qemu/hw/dma/etraxfs_dma.c (revision 56c39a41)
1d2c0bd84SPaolo Bonzini /*
2d2c0bd84SPaolo Bonzini  * QEMU ETRAX DMA Controller.
3d2c0bd84SPaolo Bonzini  *
4d2c0bd84SPaolo Bonzini  * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB.
5d2c0bd84SPaolo Bonzini  *
6d2c0bd84SPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
7d2c0bd84SPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
8d2c0bd84SPaolo Bonzini  * in the Software without restriction, including without limitation the rights
9d2c0bd84SPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10d2c0bd84SPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
11d2c0bd84SPaolo Bonzini  * furnished to do so, subject to the following conditions:
12d2c0bd84SPaolo Bonzini  *
13d2c0bd84SPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
14d2c0bd84SPaolo Bonzini  * all copies or substantial portions of the Software.
15d2c0bd84SPaolo Bonzini  *
16d2c0bd84SPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17d2c0bd84SPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18d2c0bd84SPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19d2c0bd84SPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20d2c0bd84SPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21d2c0bd84SPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22d2c0bd84SPaolo Bonzini  * THE SOFTWARE.
23d2c0bd84SPaolo Bonzini  */
2464552b6bSMarkus Armbruster 
250430891cSPeter Maydell #include "qemu/osdep.h"
26d2c0bd84SPaolo Bonzini #include "hw/hw.h"
2764552b6bSMarkus Armbruster #include "hw/irq.h"
28db725815SMarkus Armbruster #include "qemu/main-loop.h"
2954d31236SMarkus Armbruster #include "sysemu/runstate.h"
30d2c0bd84SPaolo Bonzini #include "exec/address-spaces.h"
3156c39a41SPhilippe Mathieu-Daudé #include "exec/memory.h"
32d2c0bd84SPaolo Bonzini 
33d2c0bd84SPaolo Bonzini #include "hw/cris/etraxfs_dma.h"
34d2c0bd84SPaolo Bonzini 
35d2c0bd84SPaolo Bonzini #define D(x)
36d2c0bd84SPaolo Bonzini 
37d2c0bd84SPaolo Bonzini #define RW_DATA           (0x0 / 4)
38d2c0bd84SPaolo Bonzini #define RW_SAVED_DATA     (0x58 / 4)
39d2c0bd84SPaolo Bonzini #define RW_SAVED_DATA_BUF (0x5c / 4)
40d2c0bd84SPaolo Bonzini #define RW_GROUP          (0x60 / 4)
41d2c0bd84SPaolo Bonzini #define RW_GROUP_DOWN     (0x7c / 4)
42d2c0bd84SPaolo Bonzini #define RW_CMD            (0x80 / 4)
43d2c0bd84SPaolo Bonzini #define RW_CFG            (0x84 / 4)
44d2c0bd84SPaolo Bonzini #define RW_STAT           (0x88 / 4)
45d2c0bd84SPaolo Bonzini #define RW_INTR_MASK      (0x8c / 4)
46d2c0bd84SPaolo Bonzini #define RW_ACK_INTR       (0x90 / 4)
47d2c0bd84SPaolo Bonzini #define R_INTR            (0x94 / 4)
48d2c0bd84SPaolo Bonzini #define R_MASKED_INTR     (0x98 / 4)
49d2c0bd84SPaolo Bonzini #define RW_STREAM_CMD     (0x9c / 4)
50d2c0bd84SPaolo Bonzini 
51d2c0bd84SPaolo Bonzini #define DMA_REG_MAX       (0x100 / 4)
52d2c0bd84SPaolo Bonzini 
53d2c0bd84SPaolo Bonzini /* descriptors */
54d2c0bd84SPaolo Bonzini 
55d2c0bd84SPaolo Bonzini // ------------------------------------------------------------ dma_descr_group
56d2c0bd84SPaolo Bonzini typedef struct dma_descr_group {
57d2c0bd84SPaolo Bonzini   uint32_t                      next;
58d2c0bd84SPaolo Bonzini   unsigned                      eol        : 1;
59d2c0bd84SPaolo Bonzini   unsigned                      tol        : 1;
60d2c0bd84SPaolo Bonzini   unsigned                      bol        : 1;
61d2c0bd84SPaolo Bonzini   unsigned                                 : 1;
62d2c0bd84SPaolo Bonzini   unsigned                      intr       : 1;
63d2c0bd84SPaolo Bonzini   unsigned                                 : 2;
64d2c0bd84SPaolo Bonzini   unsigned                      en         : 1;
65d2c0bd84SPaolo Bonzini   unsigned                                 : 7;
66d2c0bd84SPaolo Bonzini   unsigned                      dis        : 1;
67d2c0bd84SPaolo Bonzini   unsigned                      md         : 16;
68d2c0bd84SPaolo Bonzini   struct dma_descr_group       *up;
69d2c0bd84SPaolo Bonzini   union {
70d2c0bd84SPaolo Bonzini     struct dma_descr_context   *context;
71d2c0bd84SPaolo Bonzini     struct dma_descr_group     *group;
72d2c0bd84SPaolo Bonzini   }                             down;
73d2c0bd84SPaolo Bonzini } dma_descr_group;
74d2c0bd84SPaolo Bonzini 
75d2c0bd84SPaolo Bonzini // ---------------------------------------------------------- dma_descr_context
76d2c0bd84SPaolo Bonzini typedef struct dma_descr_context {
77d2c0bd84SPaolo Bonzini   uint32_t                      next;
78d2c0bd84SPaolo Bonzini   unsigned                      eol        : 1;
79d2c0bd84SPaolo Bonzini   unsigned                                 : 3;
80d2c0bd84SPaolo Bonzini   unsigned                      intr       : 1;
81d2c0bd84SPaolo Bonzini   unsigned                                 : 1;
82d2c0bd84SPaolo Bonzini   unsigned                      store_mode : 1;
83d2c0bd84SPaolo Bonzini   unsigned                      en         : 1;
84d2c0bd84SPaolo Bonzini   unsigned                                 : 7;
85d2c0bd84SPaolo Bonzini   unsigned                      dis        : 1;
86d2c0bd84SPaolo Bonzini   unsigned                      md0        : 16;
87d2c0bd84SPaolo Bonzini   unsigned                      md1;
88d2c0bd84SPaolo Bonzini   unsigned                      md2;
89d2c0bd84SPaolo Bonzini   unsigned                      md3;
90d2c0bd84SPaolo Bonzini   unsigned                      md4;
91d2c0bd84SPaolo Bonzini   uint32_t                      saved_data;
92d2c0bd84SPaolo Bonzini   uint32_t                      saved_data_buf;
93d2c0bd84SPaolo Bonzini } dma_descr_context;
94d2c0bd84SPaolo Bonzini 
95d2c0bd84SPaolo Bonzini // ------------------------------------------------------------- dma_descr_data
96d2c0bd84SPaolo Bonzini typedef struct dma_descr_data {
97d2c0bd84SPaolo Bonzini   uint32_t                      next;
98d2c0bd84SPaolo Bonzini   uint32_t                      buf;
99d2c0bd84SPaolo Bonzini   unsigned                      eol        : 1;
100d2c0bd84SPaolo Bonzini   unsigned                                 : 2;
101d2c0bd84SPaolo Bonzini   unsigned                      out_eop    : 1;
102d2c0bd84SPaolo Bonzini   unsigned                      intr       : 1;
103d2c0bd84SPaolo Bonzini   unsigned                      wait       : 1;
104d2c0bd84SPaolo Bonzini   unsigned                                 : 2;
105d2c0bd84SPaolo Bonzini   unsigned                                 : 3;
106d2c0bd84SPaolo Bonzini   unsigned                      in_eop     : 1;
107d2c0bd84SPaolo Bonzini   unsigned                                 : 4;
108d2c0bd84SPaolo Bonzini   unsigned                      md         : 16;
109d2c0bd84SPaolo Bonzini   uint32_t                      after;
110d2c0bd84SPaolo Bonzini } dma_descr_data;
111d2c0bd84SPaolo Bonzini 
112d2c0bd84SPaolo Bonzini /* Constants */
113d2c0bd84SPaolo Bonzini enum {
114d2c0bd84SPaolo Bonzini   regk_dma_ack_pkt                         = 0x00000100,
115d2c0bd84SPaolo Bonzini   regk_dma_anytime                         = 0x00000001,
116d2c0bd84SPaolo Bonzini   regk_dma_array                           = 0x00000008,
117d2c0bd84SPaolo Bonzini   regk_dma_burst                           = 0x00000020,
118d2c0bd84SPaolo Bonzini   regk_dma_client                          = 0x00000002,
119d2c0bd84SPaolo Bonzini   regk_dma_copy_next                       = 0x00000010,
120d2c0bd84SPaolo Bonzini   regk_dma_copy_up                         = 0x00000020,
121d2c0bd84SPaolo Bonzini   regk_dma_data_at_eol                     = 0x00000001,
122d2c0bd84SPaolo Bonzini   regk_dma_dis_c                           = 0x00000010,
123d2c0bd84SPaolo Bonzini   regk_dma_dis_g                           = 0x00000020,
124d2c0bd84SPaolo Bonzini   regk_dma_idle                            = 0x00000001,
125d2c0bd84SPaolo Bonzini   regk_dma_intern                          = 0x00000004,
126d2c0bd84SPaolo Bonzini   regk_dma_load_c                          = 0x00000200,
127d2c0bd84SPaolo Bonzini   regk_dma_load_c_n                        = 0x00000280,
128d2c0bd84SPaolo Bonzini   regk_dma_load_c_next                     = 0x00000240,
129d2c0bd84SPaolo Bonzini   regk_dma_load_d                          = 0x00000140,
130d2c0bd84SPaolo Bonzini   regk_dma_load_g                          = 0x00000300,
131d2c0bd84SPaolo Bonzini   regk_dma_load_g_down                     = 0x000003c0,
132d2c0bd84SPaolo Bonzini   regk_dma_load_g_next                     = 0x00000340,
133d2c0bd84SPaolo Bonzini   regk_dma_load_g_up                       = 0x00000380,
134d2c0bd84SPaolo Bonzini   regk_dma_next_en                         = 0x00000010,
135d2c0bd84SPaolo Bonzini   regk_dma_next_pkt                        = 0x00000010,
136d2c0bd84SPaolo Bonzini   regk_dma_no                              = 0x00000000,
137d2c0bd84SPaolo Bonzini   regk_dma_only_at_wait                    = 0x00000000,
138d2c0bd84SPaolo Bonzini   regk_dma_restore                         = 0x00000020,
139d2c0bd84SPaolo Bonzini   regk_dma_rst                             = 0x00000001,
140d2c0bd84SPaolo Bonzini   regk_dma_running                         = 0x00000004,
141d2c0bd84SPaolo Bonzini   regk_dma_rw_cfg_default                  = 0x00000000,
142d2c0bd84SPaolo Bonzini   regk_dma_rw_cmd_default                  = 0x00000000,
143d2c0bd84SPaolo Bonzini   regk_dma_rw_intr_mask_default            = 0x00000000,
144d2c0bd84SPaolo Bonzini   regk_dma_rw_stat_default                 = 0x00000101,
145d2c0bd84SPaolo Bonzini   regk_dma_rw_stream_cmd_default           = 0x00000000,
146d2c0bd84SPaolo Bonzini   regk_dma_save_down                       = 0x00000020,
147d2c0bd84SPaolo Bonzini   regk_dma_save_up                         = 0x00000020,
148d2c0bd84SPaolo Bonzini   regk_dma_set_reg                         = 0x00000050,
149d2c0bd84SPaolo Bonzini   regk_dma_set_w_size1                     = 0x00000190,
150d2c0bd84SPaolo Bonzini   regk_dma_set_w_size2                     = 0x000001a0,
151d2c0bd84SPaolo Bonzini   regk_dma_set_w_size4                     = 0x000001c0,
152d2c0bd84SPaolo Bonzini   regk_dma_stopped                         = 0x00000002,
153d2c0bd84SPaolo Bonzini   regk_dma_store_c                         = 0x00000002,
154d2c0bd84SPaolo Bonzini   regk_dma_store_descr                     = 0x00000000,
155d2c0bd84SPaolo Bonzini   regk_dma_store_g                         = 0x00000004,
156d2c0bd84SPaolo Bonzini   regk_dma_store_md                        = 0x00000001,
157d2c0bd84SPaolo Bonzini   regk_dma_sw                              = 0x00000008,
158d2c0bd84SPaolo Bonzini   regk_dma_update_down                     = 0x00000020,
159d2c0bd84SPaolo Bonzini   regk_dma_yes                             = 0x00000001
160d2c0bd84SPaolo Bonzini };
161d2c0bd84SPaolo Bonzini 
162d2c0bd84SPaolo Bonzini enum dma_ch_state
163d2c0bd84SPaolo Bonzini {
164d2c0bd84SPaolo Bonzini     RST = 1,
165d2c0bd84SPaolo Bonzini     STOPPED = 2,
166d2c0bd84SPaolo Bonzini     RUNNING = 4
167d2c0bd84SPaolo Bonzini };
168d2c0bd84SPaolo Bonzini 
169d2c0bd84SPaolo Bonzini struct fs_dma_channel
170d2c0bd84SPaolo Bonzini {
171d2c0bd84SPaolo Bonzini     qemu_irq irq;
172d2c0bd84SPaolo Bonzini     struct etraxfs_dma_client *client;
173d2c0bd84SPaolo Bonzini 
174d2c0bd84SPaolo Bonzini     /* Internal status.  */
175d2c0bd84SPaolo Bonzini     int stream_cmd_src;
176d2c0bd84SPaolo Bonzini     enum dma_ch_state state;
177d2c0bd84SPaolo Bonzini 
178d2c0bd84SPaolo Bonzini     unsigned int input : 1;
179d2c0bd84SPaolo Bonzini     unsigned int eol : 1;
180d2c0bd84SPaolo Bonzini 
181d2c0bd84SPaolo Bonzini     struct dma_descr_group current_g;
182d2c0bd84SPaolo Bonzini     struct dma_descr_context current_c;
183d2c0bd84SPaolo Bonzini     struct dma_descr_data current_d;
184d2c0bd84SPaolo Bonzini 
185d2c0bd84SPaolo Bonzini     /* Control registers.  */
186d2c0bd84SPaolo Bonzini     uint32_t regs[DMA_REG_MAX];
187d2c0bd84SPaolo Bonzini };
188d2c0bd84SPaolo Bonzini 
189d2c0bd84SPaolo Bonzini struct fs_dma_ctrl
190d2c0bd84SPaolo Bonzini {
191d2c0bd84SPaolo Bonzini     MemoryRegion mmio;
192d2c0bd84SPaolo Bonzini     int nr_channels;
193d2c0bd84SPaolo Bonzini     struct fs_dma_channel *channels;
194d2c0bd84SPaolo Bonzini 
195d2c0bd84SPaolo Bonzini     QEMUBH *bh;
196d2c0bd84SPaolo Bonzini };
197d2c0bd84SPaolo Bonzini 
198d2c0bd84SPaolo Bonzini static void DMA_run(void *opaque);
199d2c0bd84SPaolo Bonzini static int channel_out_run(struct fs_dma_ctrl *ctrl, int c);
200d2c0bd84SPaolo Bonzini 
channel_reg(struct fs_dma_ctrl * ctrl,int c,int reg)201d2c0bd84SPaolo Bonzini static inline uint32_t channel_reg(struct fs_dma_ctrl *ctrl, int c, int reg)
202d2c0bd84SPaolo Bonzini {
203d2c0bd84SPaolo Bonzini     return ctrl->channels[c].regs[reg];
204d2c0bd84SPaolo Bonzini }
205d2c0bd84SPaolo Bonzini 
channel_stopped(struct fs_dma_ctrl * ctrl,int c)206d2c0bd84SPaolo Bonzini static inline int channel_stopped(struct fs_dma_ctrl *ctrl, int c)
207d2c0bd84SPaolo Bonzini {
208d2c0bd84SPaolo Bonzini     return channel_reg(ctrl, c, RW_CFG) & 2;
209d2c0bd84SPaolo Bonzini }
210d2c0bd84SPaolo Bonzini 
channel_en(struct fs_dma_ctrl * ctrl,int c)211d2c0bd84SPaolo Bonzini static inline int channel_en(struct fs_dma_ctrl *ctrl, int c)
212d2c0bd84SPaolo Bonzini {
213d2c0bd84SPaolo Bonzini     return (channel_reg(ctrl, c, RW_CFG) & 1)
214d2c0bd84SPaolo Bonzini             && ctrl->channels[c].client;
215d2c0bd84SPaolo Bonzini }
216d2c0bd84SPaolo Bonzini 
fs_channel(hwaddr addr)217d2c0bd84SPaolo Bonzini static inline int fs_channel(hwaddr addr)
218d2c0bd84SPaolo Bonzini {
219d2c0bd84SPaolo Bonzini     /* Every channel has a 0x2000 ctrl register map.  */
220d2c0bd84SPaolo Bonzini     return addr >> 13;
221d2c0bd84SPaolo Bonzini }
222d2c0bd84SPaolo Bonzini 
223d2c0bd84SPaolo Bonzini #ifdef USE_THIS_DEAD_CODE
channel_load_g(struct fs_dma_ctrl * ctrl,int c)224d2c0bd84SPaolo Bonzini static void channel_load_g(struct fs_dma_ctrl *ctrl, int c)
225d2c0bd84SPaolo Bonzini {
226d2c0bd84SPaolo Bonzini     hwaddr addr = channel_reg(ctrl, c, RW_GROUP);
227d2c0bd84SPaolo Bonzini 
228d2c0bd84SPaolo Bonzini     /* Load and decode. FIXME: handle endianness.  */
2290eeef0a4SPhilippe Mathieu-Daudé     cpu_physical_memory_read(addr, &ctrl->channels[c].current_g,
2300eeef0a4SPhilippe Mathieu-Daudé                              sizeof(ctrl->channels[c].current_g));
231d2c0bd84SPaolo Bonzini }
232d2c0bd84SPaolo Bonzini 
dump_c(int ch,struct dma_descr_context * c)233d2c0bd84SPaolo Bonzini static void dump_c(int ch, struct dma_descr_context *c)
234d2c0bd84SPaolo Bonzini {
235d2c0bd84SPaolo Bonzini     printf("%s ch=%d\n", __func__, ch);
236d2c0bd84SPaolo Bonzini     printf("next=%x\n", c->next);
237d2c0bd84SPaolo Bonzini     printf("saved_data=%x\n", c->saved_data);
238d2c0bd84SPaolo Bonzini     printf("saved_data_buf=%x\n", c->saved_data_buf);
239d2c0bd84SPaolo Bonzini     printf("eol=%x\n", (uint32_t) c->eol);
240d2c0bd84SPaolo Bonzini }
241d2c0bd84SPaolo Bonzini 
dump_d(int ch,struct dma_descr_data * d)242d2c0bd84SPaolo Bonzini static void dump_d(int ch, struct dma_descr_data *d)
243d2c0bd84SPaolo Bonzini {
244d2c0bd84SPaolo Bonzini     printf("%s ch=%d\n", __func__, ch);
245d2c0bd84SPaolo Bonzini     printf("next=%x\n", d->next);
246d2c0bd84SPaolo Bonzini     printf("buf=%x\n", d->buf);
247d2c0bd84SPaolo Bonzini     printf("after=%x\n", d->after);
248d2c0bd84SPaolo Bonzini     printf("intr=%x\n", (uint32_t) d->intr);
249d2c0bd84SPaolo Bonzini     printf("out_eop=%x\n", (uint32_t) d->out_eop);
250d2c0bd84SPaolo Bonzini     printf("in_eop=%x\n", (uint32_t) d->in_eop);
251d2c0bd84SPaolo Bonzini     printf("eol=%x\n", (uint32_t) d->eol);
252d2c0bd84SPaolo Bonzini }
253d2c0bd84SPaolo Bonzini #endif
254d2c0bd84SPaolo Bonzini 
channel_load_c(struct fs_dma_ctrl * ctrl,int c)255d2c0bd84SPaolo Bonzini static void channel_load_c(struct fs_dma_ctrl *ctrl, int c)
256d2c0bd84SPaolo Bonzini {
257d2c0bd84SPaolo Bonzini     hwaddr addr = channel_reg(ctrl, c, RW_GROUP_DOWN);
258d2c0bd84SPaolo Bonzini 
259d2c0bd84SPaolo Bonzini     /* Load and decode. FIXME: handle endianness.  */
2600eeef0a4SPhilippe Mathieu-Daudé     cpu_physical_memory_read(addr, &ctrl->channels[c].current_c,
2610eeef0a4SPhilippe Mathieu-Daudé                              sizeof(ctrl->channels[c].current_c));
262d2c0bd84SPaolo Bonzini 
263d2c0bd84SPaolo Bonzini     D(dump_c(c, &ctrl->channels[c].current_c));
264d2c0bd84SPaolo Bonzini     /* I guess this should update the current pos.  */
265d2c0bd84SPaolo Bonzini     ctrl->channels[c].regs[RW_SAVED_DATA] =
266d2c0bd84SPaolo Bonzini         (uint32_t)(unsigned long)ctrl->channels[c].current_c.saved_data;
267d2c0bd84SPaolo Bonzini     ctrl->channels[c].regs[RW_SAVED_DATA_BUF] =
268d2c0bd84SPaolo Bonzini         (uint32_t)(unsigned long)ctrl->channels[c].current_c.saved_data_buf;
269d2c0bd84SPaolo Bonzini }
270d2c0bd84SPaolo Bonzini 
channel_load_d(struct fs_dma_ctrl * ctrl,int c)271d2c0bd84SPaolo Bonzini static void channel_load_d(struct fs_dma_ctrl *ctrl, int c)
272d2c0bd84SPaolo Bonzini {
273d2c0bd84SPaolo Bonzini     hwaddr addr = channel_reg(ctrl, c, RW_SAVED_DATA);
274d2c0bd84SPaolo Bonzini 
275d2c0bd84SPaolo Bonzini     /* Load and decode. FIXME: handle endianness.  */
276883f2c59SPhilippe Mathieu-Daudé     D(printf("%s ch=%d addr=" HWADDR_FMT_plx "\n", __func__, c, addr));
2770eeef0a4SPhilippe Mathieu-Daudé     cpu_physical_memory_read(addr, &ctrl->channels[c].current_d,
2780eeef0a4SPhilippe Mathieu-Daudé                              sizeof(ctrl->channels[c].current_d));
279d2c0bd84SPaolo Bonzini 
280d2c0bd84SPaolo Bonzini     D(dump_d(c, &ctrl->channels[c].current_d));
281d2c0bd84SPaolo Bonzini     ctrl->channels[c].regs[RW_DATA] = addr;
282d2c0bd84SPaolo Bonzini }
283d2c0bd84SPaolo Bonzini 
channel_store_c(struct fs_dma_ctrl * ctrl,int c)284d2c0bd84SPaolo Bonzini static void channel_store_c(struct fs_dma_ctrl *ctrl, int c)
285d2c0bd84SPaolo Bonzini {
286d2c0bd84SPaolo Bonzini     hwaddr addr = channel_reg(ctrl, c, RW_GROUP_DOWN);
287d2c0bd84SPaolo Bonzini 
288d2c0bd84SPaolo Bonzini     /* Encode and store. FIXME: handle endianness.  */
289883f2c59SPhilippe Mathieu-Daudé     D(printf("%s ch=%d addr=" HWADDR_FMT_plx "\n", __func__, c, addr));
290d2c0bd84SPaolo Bonzini     D(dump_d(c, &ctrl->channels[c].current_d));
2910eeef0a4SPhilippe Mathieu-Daudé     cpu_physical_memory_write(addr, &ctrl->channels[c].current_c,
2920eeef0a4SPhilippe Mathieu-Daudé                               sizeof(ctrl->channels[c].current_c));
293d2c0bd84SPaolo Bonzini }
294d2c0bd84SPaolo Bonzini 
channel_store_d(struct fs_dma_ctrl * ctrl,int c)295d2c0bd84SPaolo Bonzini static void channel_store_d(struct fs_dma_ctrl *ctrl, int c)
296d2c0bd84SPaolo Bonzini {
297d2c0bd84SPaolo Bonzini     hwaddr addr = channel_reg(ctrl, c, RW_SAVED_DATA);
298d2c0bd84SPaolo Bonzini 
299d2c0bd84SPaolo Bonzini     /* Encode and store. FIXME: handle endianness.  */
300883f2c59SPhilippe Mathieu-Daudé     D(printf("%s ch=%d addr=" HWADDR_FMT_plx "\n", __func__, c, addr));
3010eeef0a4SPhilippe Mathieu-Daudé     cpu_physical_memory_write(addr, &ctrl->channels[c].current_d,
3020eeef0a4SPhilippe Mathieu-Daudé                               sizeof(ctrl->channels[c].current_d));
303d2c0bd84SPaolo Bonzini }
304d2c0bd84SPaolo Bonzini 
channel_stop(struct fs_dma_ctrl * ctrl,int c)305d2c0bd84SPaolo Bonzini static inline void channel_stop(struct fs_dma_ctrl *ctrl, int c)
306d2c0bd84SPaolo Bonzini {
307d2c0bd84SPaolo Bonzini     /* FIXME:  */
308d2c0bd84SPaolo Bonzini }
309d2c0bd84SPaolo Bonzini 
channel_start(struct fs_dma_ctrl * ctrl,int c)310d2c0bd84SPaolo Bonzini static inline void channel_start(struct fs_dma_ctrl *ctrl, int c)
311d2c0bd84SPaolo Bonzini {
312d2c0bd84SPaolo Bonzini     if (ctrl->channels[c].client)
313d2c0bd84SPaolo Bonzini     {
314d2c0bd84SPaolo Bonzini         ctrl->channels[c].eol = 0;
315d2c0bd84SPaolo Bonzini         ctrl->channels[c].state = RUNNING;
316d2c0bd84SPaolo Bonzini         if (!ctrl->channels[c].input)
317d2c0bd84SPaolo Bonzini             channel_out_run(ctrl, c);
318d2c0bd84SPaolo Bonzini     } else
319d2c0bd84SPaolo Bonzini         printf("WARNING: starting DMA ch %d with no client\n", c);
320d2c0bd84SPaolo Bonzini 
321d2c0bd84SPaolo Bonzini     qemu_bh_schedule_idle(ctrl->bh);
322d2c0bd84SPaolo Bonzini }
323d2c0bd84SPaolo Bonzini 
channel_continue(struct fs_dma_ctrl * ctrl,int c)324d2c0bd84SPaolo Bonzini static void channel_continue(struct fs_dma_ctrl *ctrl, int c)
325d2c0bd84SPaolo Bonzini {
326d2c0bd84SPaolo Bonzini     if (!channel_en(ctrl, c)
327d2c0bd84SPaolo Bonzini         || channel_stopped(ctrl, c)
328d2c0bd84SPaolo Bonzini         || ctrl->channels[c].state != RUNNING
329d2c0bd84SPaolo Bonzini         /* Only reload the current data descriptor if it has eol set.  */
330d2c0bd84SPaolo Bonzini         || !ctrl->channels[c].current_d.eol) {
331d2c0bd84SPaolo Bonzini         D(printf("continue failed ch=%d state=%d stopped=%d en=%d eol=%d\n",
332d2c0bd84SPaolo Bonzini                  c, ctrl->channels[c].state,
333d2c0bd84SPaolo Bonzini                  channel_stopped(ctrl, c),
334d2c0bd84SPaolo Bonzini                  channel_en(ctrl,c),
335d2c0bd84SPaolo Bonzini                  ctrl->channels[c].eol));
336d2c0bd84SPaolo Bonzini         D(dump_d(c, &ctrl->channels[c].current_d));
337d2c0bd84SPaolo Bonzini         return;
338d2c0bd84SPaolo Bonzini     }
339d2c0bd84SPaolo Bonzini 
340d2c0bd84SPaolo Bonzini     /* Reload the current descriptor.  */
341d2c0bd84SPaolo Bonzini     channel_load_d(ctrl, c);
342d2c0bd84SPaolo Bonzini 
343d2c0bd84SPaolo Bonzini     /* If the current descriptor cleared the eol flag and we had already
344d2c0bd84SPaolo Bonzini        reached eol state, do the continue.  */
345d2c0bd84SPaolo Bonzini     if (!ctrl->channels[c].current_d.eol && ctrl->channels[c].eol) {
346d2c0bd84SPaolo Bonzini         D(printf("continue %d ok %x\n", c,
347d2c0bd84SPaolo Bonzini                  ctrl->channels[c].current_d.next));
348d2c0bd84SPaolo Bonzini         ctrl->channels[c].regs[RW_SAVED_DATA] =
349d2c0bd84SPaolo Bonzini             (uint32_t)(unsigned long)ctrl->channels[c].current_d.next;
350d2c0bd84SPaolo Bonzini         channel_load_d(ctrl, c);
351d2c0bd84SPaolo Bonzini         ctrl->channels[c].regs[RW_SAVED_DATA_BUF] =
352d2c0bd84SPaolo Bonzini             (uint32_t)(unsigned long)ctrl->channels[c].current_d.buf;
353d2c0bd84SPaolo Bonzini 
354d2c0bd84SPaolo Bonzini         channel_start(ctrl, c);
355d2c0bd84SPaolo Bonzini     }
356d2c0bd84SPaolo Bonzini     ctrl->channels[c].regs[RW_SAVED_DATA_BUF] =
357d2c0bd84SPaolo Bonzini         (uint32_t)(unsigned long)ctrl->channels[c].current_d.buf;
358d2c0bd84SPaolo Bonzini }
359d2c0bd84SPaolo Bonzini 
channel_stream_cmd(struct fs_dma_ctrl * ctrl,int c,uint32_t v)360d2c0bd84SPaolo Bonzini static void channel_stream_cmd(struct fs_dma_ctrl *ctrl, int c, uint32_t v)
361d2c0bd84SPaolo Bonzini {
362d2c0bd84SPaolo Bonzini     unsigned int cmd = v & ((1 << 10) - 1);
363d2c0bd84SPaolo Bonzini 
364d2c0bd84SPaolo Bonzini     D(printf("%s ch=%d cmd=%x\n",
365d2c0bd84SPaolo Bonzini              __func__, c, cmd));
366d2c0bd84SPaolo Bonzini     if (cmd & regk_dma_load_d) {
367d2c0bd84SPaolo Bonzini         channel_load_d(ctrl, c);
368d2c0bd84SPaolo Bonzini         if (cmd & regk_dma_burst)
369d2c0bd84SPaolo Bonzini             channel_start(ctrl, c);
370d2c0bd84SPaolo Bonzini     }
371d2c0bd84SPaolo Bonzini 
372d2c0bd84SPaolo Bonzini     if (cmd & regk_dma_load_c) {
373d2c0bd84SPaolo Bonzini         channel_load_c(ctrl, c);
374d2c0bd84SPaolo Bonzini     }
375d2c0bd84SPaolo Bonzini }
376d2c0bd84SPaolo Bonzini 
channel_update_irq(struct fs_dma_ctrl * ctrl,int c)377d2c0bd84SPaolo Bonzini static void channel_update_irq(struct fs_dma_ctrl *ctrl, int c)
378d2c0bd84SPaolo Bonzini {
379d2c0bd84SPaolo Bonzini     D(printf("%s %d\n", __func__, c));
380d2c0bd84SPaolo Bonzini     ctrl->channels[c].regs[R_INTR] &=
381d2c0bd84SPaolo Bonzini         ~(ctrl->channels[c].regs[RW_ACK_INTR]);
382d2c0bd84SPaolo Bonzini 
383d2c0bd84SPaolo Bonzini     ctrl->channels[c].regs[R_MASKED_INTR] =
384d2c0bd84SPaolo Bonzini         ctrl->channels[c].regs[R_INTR]
385d2c0bd84SPaolo Bonzini         & ctrl->channels[c].regs[RW_INTR_MASK];
386d2c0bd84SPaolo Bonzini 
387d2c0bd84SPaolo Bonzini     D(printf("%s: chan=%d masked_intr=%x\n", __func__,
388d2c0bd84SPaolo Bonzini              c,
389d2c0bd84SPaolo Bonzini              ctrl->channels[c].regs[R_MASKED_INTR]));
390d2c0bd84SPaolo Bonzini 
391d2c0bd84SPaolo Bonzini     qemu_set_irq(ctrl->channels[c].irq,
392d2c0bd84SPaolo Bonzini                  !!ctrl->channels[c].regs[R_MASKED_INTR]);
393d2c0bd84SPaolo Bonzini }
394d2c0bd84SPaolo Bonzini 
channel_out_run(struct fs_dma_ctrl * ctrl,int c)395d2c0bd84SPaolo Bonzini static int channel_out_run(struct fs_dma_ctrl *ctrl, int c)
396d2c0bd84SPaolo Bonzini {
397d2c0bd84SPaolo Bonzini     uint32_t len;
398d2c0bd84SPaolo Bonzini     uint32_t saved_data_buf;
399d2c0bd84SPaolo Bonzini     unsigned char buf[2 * 1024];
400d2c0bd84SPaolo Bonzini 
401d2c0bd84SPaolo Bonzini     struct dma_context_metadata meta;
402d2c0bd84SPaolo Bonzini     bool send_context = true;
403d2c0bd84SPaolo Bonzini 
404d2c0bd84SPaolo Bonzini     if (ctrl->channels[c].eol)
405d2c0bd84SPaolo Bonzini         return 0;
406d2c0bd84SPaolo Bonzini 
407d2c0bd84SPaolo Bonzini     do {
408d2c0bd84SPaolo Bonzini         bool out_eop;
409d2c0bd84SPaolo Bonzini         D(printf("ch=%d buf=%x after=%x\n",
410d2c0bd84SPaolo Bonzini                  c,
411d2c0bd84SPaolo Bonzini                  (uint32_t)ctrl->channels[c].current_d.buf,
412d2c0bd84SPaolo Bonzini                  (uint32_t)ctrl->channels[c].current_d.after));
413d2c0bd84SPaolo Bonzini 
414d2c0bd84SPaolo Bonzini         if (send_context) {
415d2c0bd84SPaolo Bonzini             if (ctrl->channels[c].client->client.metadata_push) {
416d2c0bd84SPaolo Bonzini                 meta.metadata = ctrl->channels[c].current_d.md;
417d2c0bd84SPaolo Bonzini                 ctrl->channels[c].client->client.metadata_push(
418d2c0bd84SPaolo Bonzini                     ctrl->channels[c].client->client.opaque,
419d2c0bd84SPaolo Bonzini                     &meta);
420d2c0bd84SPaolo Bonzini             }
421d2c0bd84SPaolo Bonzini             send_context = false;
422d2c0bd84SPaolo Bonzini         }
423d2c0bd84SPaolo Bonzini 
424d2c0bd84SPaolo Bonzini         channel_load_d(ctrl, c);
425d2c0bd84SPaolo Bonzini         saved_data_buf = channel_reg(ctrl, c, RW_SAVED_DATA_BUF);
426d2c0bd84SPaolo Bonzini         len = (uint32_t)(unsigned long)
427d2c0bd84SPaolo Bonzini             ctrl->channels[c].current_d.after;
428d2c0bd84SPaolo Bonzini         len -= saved_data_buf;
429d2c0bd84SPaolo Bonzini 
430d2c0bd84SPaolo Bonzini         if (len > sizeof buf)
431d2c0bd84SPaolo Bonzini             len = sizeof buf;
432d2c0bd84SPaolo Bonzini         cpu_physical_memory_read (saved_data_buf, buf, len);
433d2c0bd84SPaolo Bonzini 
434d2c0bd84SPaolo Bonzini         out_eop = ((saved_data_buf + len) ==
435d2c0bd84SPaolo Bonzini                    ctrl->channels[c].current_d.after) &&
436d2c0bd84SPaolo Bonzini                    ctrl->channels[c].current_d.out_eop;
437d2c0bd84SPaolo Bonzini 
438d2c0bd84SPaolo Bonzini         D(printf("channel %d pushes %x %u bytes eop=%u\n", c,
439d2c0bd84SPaolo Bonzini                  saved_data_buf, len, out_eop));
440d2c0bd84SPaolo Bonzini 
441c3bce9d5SEdgar E. Iglesias         if (ctrl->channels[c].client->client.push) {
442c3bce9d5SEdgar E. Iglesias             if (len > 0) {
443d2c0bd84SPaolo Bonzini                 ctrl->channels[c].client->client.push(
444d2c0bd84SPaolo Bonzini                     ctrl->channels[c].client->client.opaque,
445d2c0bd84SPaolo Bonzini                     buf, len, out_eop);
446c3bce9d5SEdgar E. Iglesias             }
447c3bce9d5SEdgar E. Iglesias         } else {
448d2c0bd84SPaolo Bonzini             printf("WARNING: DMA ch%d dataloss,"
449d2c0bd84SPaolo Bonzini                    " no attached client.\n", c);
450c3bce9d5SEdgar E. Iglesias         }
451d2c0bd84SPaolo Bonzini 
452d2c0bd84SPaolo Bonzini         saved_data_buf += len;
453d2c0bd84SPaolo Bonzini 
454d2c0bd84SPaolo Bonzini         if (saved_data_buf == (uint32_t)(unsigned long)
455d2c0bd84SPaolo Bonzini                 ctrl->channels[c].current_d.after) {
456d2c0bd84SPaolo Bonzini             /* Done. Step to next.  */
457d2c0bd84SPaolo Bonzini             if (ctrl->channels[c].current_d.out_eop) {
458d2c0bd84SPaolo Bonzini                 send_context = true;
459d2c0bd84SPaolo Bonzini             }
460d2c0bd84SPaolo Bonzini             if (ctrl->channels[c].current_d.intr) {
461d2c0bd84SPaolo Bonzini                 /* data intr.  */
462d2c0bd84SPaolo Bonzini                 D(printf("signal intr %d eol=%d\n",
463d2c0bd84SPaolo Bonzini                          len, ctrl->channels[c].current_d.eol));
464d2c0bd84SPaolo Bonzini                 ctrl->channels[c].regs[R_INTR] |= (1 << 2);
465d2c0bd84SPaolo Bonzini                 channel_update_irq(ctrl, c);
466d2c0bd84SPaolo Bonzini             }
467d2c0bd84SPaolo Bonzini             channel_store_d(ctrl, c);
468d2c0bd84SPaolo Bonzini             if (ctrl->channels[c].current_d.eol) {
469d2c0bd84SPaolo Bonzini                 D(printf("channel %d EOL\n", c));
470d2c0bd84SPaolo Bonzini                 ctrl->channels[c].eol = 1;
471d2c0bd84SPaolo Bonzini 
472d2c0bd84SPaolo Bonzini                 /* Mark the context as disabled.  */
473d2c0bd84SPaolo Bonzini                 ctrl->channels[c].current_c.dis = 1;
474d2c0bd84SPaolo Bonzini                 channel_store_c(ctrl, c);
475d2c0bd84SPaolo Bonzini 
476d2c0bd84SPaolo Bonzini                 channel_stop(ctrl, c);
477d2c0bd84SPaolo Bonzini             } else {
478d2c0bd84SPaolo Bonzini                 ctrl->channels[c].regs[RW_SAVED_DATA] =
479d2c0bd84SPaolo Bonzini                     (uint32_t)(unsigned long)ctrl->
480d2c0bd84SPaolo Bonzini                         channels[c].current_d.next;
481d2c0bd84SPaolo Bonzini                 /* Load new descriptor.  */
482d2c0bd84SPaolo Bonzini                 channel_load_d(ctrl, c);
483d2c0bd84SPaolo Bonzini                 saved_data_buf = (uint32_t)(unsigned long)
484d2c0bd84SPaolo Bonzini                     ctrl->channels[c].current_d.buf;
485d2c0bd84SPaolo Bonzini             }
486d2c0bd84SPaolo Bonzini 
487d2c0bd84SPaolo Bonzini             ctrl->channels[c].regs[RW_SAVED_DATA_BUF] =
488d2c0bd84SPaolo Bonzini                             saved_data_buf;
489d2c0bd84SPaolo Bonzini             D(dump_d(c, &ctrl->channels[c].current_d));
490d2c0bd84SPaolo Bonzini         }
491d2c0bd84SPaolo Bonzini         ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = saved_data_buf;
492d2c0bd84SPaolo Bonzini     } while (!ctrl->channels[c].eol);
493d2c0bd84SPaolo Bonzini     return 1;
494d2c0bd84SPaolo Bonzini }
495d2c0bd84SPaolo Bonzini 
channel_in_process(struct fs_dma_ctrl * ctrl,int c,unsigned char * buf,int buflen,int eop)496d2c0bd84SPaolo Bonzini static int channel_in_process(struct fs_dma_ctrl *ctrl, int c,
497d2c0bd84SPaolo Bonzini                               unsigned char *buf, int buflen, int eop)
498d2c0bd84SPaolo Bonzini {
499d2c0bd84SPaolo Bonzini     uint32_t len;
500d2c0bd84SPaolo Bonzini     uint32_t saved_data_buf;
501d2c0bd84SPaolo Bonzini 
502d2c0bd84SPaolo Bonzini     if (ctrl->channels[c].eol == 1)
503d2c0bd84SPaolo Bonzini         return 0;
504d2c0bd84SPaolo Bonzini 
505d2c0bd84SPaolo Bonzini     channel_load_d(ctrl, c);
506d2c0bd84SPaolo Bonzini     saved_data_buf = channel_reg(ctrl, c, RW_SAVED_DATA_BUF);
507d2c0bd84SPaolo Bonzini     len = (uint32_t)(unsigned long)ctrl->channels[c].current_d.after;
508d2c0bd84SPaolo Bonzini     len -= saved_data_buf;
509d2c0bd84SPaolo Bonzini 
510d2c0bd84SPaolo Bonzini     if (len > buflen)
511d2c0bd84SPaolo Bonzini         len = buflen;
512d2c0bd84SPaolo Bonzini 
513d2c0bd84SPaolo Bonzini     cpu_physical_memory_write (saved_data_buf, buf, len);
514d2c0bd84SPaolo Bonzini     saved_data_buf += len;
515d2c0bd84SPaolo Bonzini 
516d2c0bd84SPaolo Bonzini     if (saved_data_buf ==
517d2c0bd84SPaolo Bonzini         (uint32_t)(unsigned long)ctrl->channels[c].current_d.after
518d2c0bd84SPaolo Bonzini         || eop) {
519d2c0bd84SPaolo Bonzini         uint32_t r_intr = ctrl->channels[c].regs[R_INTR];
520d2c0bd84SPaolo Bonzini 
521d2c0bd84SPaolo Bonzini         D(printf("in dscr end len=%d\n",
522d2c0bd84SPaolo Bonzini                  ctrl->channels[c].current_d.after
523d2c0bd84SPaolo Bonzini                  - ctrl->channels[c].current_d.buf));
524d2c0bd84SPaolo Bonzini         ctrl->channels[c].current_d.after = saved_data_buf;
525d2c0bd84SPaolo Bonzini 
526d2c0bd84SPaolo Bonzini         /* Done. Step to next.  */
527d2c0bd84SPaolo Bonzini         if (ctrl->channels[c].current_d.intr) {
528d2c0bd84SPaolo Bonzini             /* TODO: signal eop to the client.  */
529d2c0bd84SPaolo Bonzini             /* data intr.  */
530d2c0bd84SPaolo Bonzini             ctrl->channels[c].regs[R_INTR] |= 3;
531d2c0bd84SPaolo Bonzini         }
532d2c0bd84SPaolo Bonzini         if (eop) {
533d2c0bd84SPaolo Bonzini             ctrl->channels[c].current_d.in_eop = 1;
534d2c0bd84SPaolo Bonzini             ctrl->channels[c].regs[R_INTR] |= 8;
535d2c0bd84SPaolo Bonzini         }
536d2c0bd84SPaolo Bonzini         if (r_intr != ctrl->channels[c].regs[R_INTR])
537d2c0bd84SPaolo Bonzini             channel_update_irq(ctrl, c);
538d2c0bd84SPaolo Bonzini 
539d2c0bd84SPaolo Bonzini         channel_store_d(ctrl, c);
540d2c0bd84SPaolo Bonzini         D(dump_d(c, &ctrl->channels[c].current_d));
541d2c0bd84SPaolo Bonzini 
542d2c0bd84SPaolo Bonzini         if (ctrl->channels[c].current_d.eol) {
543d2c0bd84SPaolo Bonzini             D(printf("channel %d EOL\n", c));
544d2c0bd84SPaolo Bonzini             ctrl->channels[c].eol = 1;
545d2c0bd84SPaolo Bonzini 
546d2c0bd84SPaolo Bonzini             /* Mark the context as disabled.  */
547d2c0bd84SPaolo Bonzini             ctrl->channels[c].current_c.dis = 1;
548d2c0bd84SPaolo Bonzini             channel_store_c(ctrl, c);
549d2c0bd84SPaolo Bonzini 
550d2c0bd84SPaolo Bonzini             channel_stop(ctrl, c);
551d2c0bd84SPaolo Bonzini         } else {
552d2c0bd84SPaolo Bonzini             ctrl->channels[c].regs[RW_SAVED_DATA] =
553d2c0bd84SPaolo Bonzini                 (uint32_t)(unsigned long)ctrl->
554d2c0bd84SPaolo Bonzini                     channels[c].current_d.next;
555d2c0bd84SPaolo Bonzini             /* Load new descriptor.  */
556d2c0bd84SPaolo Bonzini             channel_load_d(ctrl, c);
557d2c0bd84SPaolo Bonzini             saved_data_buf = (uint32_t)(unsigned long)
558d2c0bd84SPaolo Bonzini                 ctrl->channels[c].current_d.buf;
559d2c0bd84SPaolo Bonzini         }
560d2c0bd84SPaolo Bonzini     }
561d2c0bd84SPaolo Bonzini 
562d2c0bd84SPaolo Bonzini     ctrl->channels[c].regs[RW_SAVED_DATA_BUF] = saved_data_buf;
563d2c0bd84SPaolo Bonzini     return len;
564d2c0bd84SPaolo Bonzini }
565d2c0bd84SPaolo Bonzini 
channel_in_run(struct fs_dma_ctrl * ctrl,int c)566d2c0bd84SPaolo Bonzini static inline int channel_in_run(struct fs_dma_ctrl *ctrl, int c)
567d2c0bd84SPaolo Bonzini {
568d2c0bd84SPaolo Bonzini     if (ctrl->channels[c].client->client.pull) {
569d2c0bd84SPaolo Bonzini         ctrl->channels[c].client->client.pull(
570d2c0bd84SPaolo Bonzini             ctrl->channels[c].client->client.opaque);
571d2c0bd84SPaolo Bonzini         return 1;
572d2c0bd84SPaolo Bonzini     } else
573d2c0bd84SPaolo Bonzini         return 0;
574d2c0bd84SPaolo Bonzini }
575d2c0bd84SPaolo Bonzini 
dma_rinvalid(void * opaque,hwaddr addr)576d2c0bd84SPaolo Bonzini static uint32_t dma_rinvalid (void *opaque, hwaddr addr)
577d2c0bd84SPaolo Bonzini {
578883f2c59SPhilippe Mathieu-Daudé     hw_error("Unsupported short raccess. reg=" HWADDR_FMT_plx "\n", addr);
579d2c0bd84SPaolo Bonzini     return 0;
580d2c0bd84SPaolo Bonzini }
581d2c0bd84SPaolo Bonzini 
582d2c0bd84SPaolo Bonzini static uint64_t
dma_read(void * opaque,hwaddr addr,unsigned int size)583d2c0bd84SPaolo Bonzini dma_read(void *opaque, hwaddr addr, unsigned int size)
584d2c0bd84SPaolo Bonzini {
585d2c0bd84SPaolo Bonzini     struct fs_dma_ctrl *ctrl = opaque;
586d2c0bd84SPaolo Bonzini     int c;
587d2c0bd84SPaolo Bonzini     uint32_t r = 0;
588d2c0bd84SPaolo Bonzini 
589d2c0bd84SPaolo Bonzini     if (size != 4) {
590d2c0bd84SPaolo Bonzini         dma_rinvalid(opaque, addr);
591d2c0bd84SPaolo Bonzini     }
592d2c0bd84SPaolo Bonzini 
593d2c0bd84SPaolo Bonzini     /* Make addr relative to this channel and bounded to nr regs.  */
594d2c0bd84SPaolo Bonzini     c = fs_channel(addr);
595d2c0bd84SPaolo Bonzini     addr &= 0xff;
596d2c0bd84SPaolo Bonzini     addr >>= 2;
597d2c0bd84SPaolo Bonzini     switch (addr)
598d2c0bd84SPaolo Bonzini     {
599d2c0bd84SPaolo Bonzini     case RW_STAT:
600d2c0bd84SPaolo Bonzini         r = ctrl->channels[c].state & 7;
601d2c0bd84SPaolo Bonzini         r |= ctrl->channels[c].eol << 5;
602d2c0bd84SPaolo Bonzini         r |= ctrl->channels[c].stream_cmd_src << 8;
603d2c0bd84SPaolo Bonzini         break;
604d2c0bd84SPaolo Bonzini 
605d2c0bd84SPaolo Bonzini     default:
606d2c0bd84SPaolo Bonzini         r = ctrl->channels[c].regs[addr];
607883f2c59SPhilippe Mathieu-Daudé         D(printf("%s c=%d addr=" HWADDR_FMT_plx "\n",
608d2c0bd84SPaolo Bonzini                  __func__, c, addr));
609d2c0bd84SPaolo Bonzini         break;
610d2c0bd84SPaolo Bonzini     }
611d2c0bd84SPaolo Bonzini     return r;
612d2c0bd84SPaolo Bonzini }
613d2c0bd84SPaolo Bonzini 
614d2c0bd84SPaolo Bonzini static void
dma_winvalid(void * opaque,hwaddr addr,uint32_t value)615d2c0bd84SPaolo Bonzini dma_winvalid (void *opaque, hwaddr addr, uint32_t value)
616d2c0bd84SPaolo Bonzini {
617883f2c59SPhilippe Mathieu-Daudé     hw_error("Unsupported short waccess. reg=" HWADDR_FMT_plx "\n", addr);
618d2c0bd84SPaolo Bonzini }
619d2c0bd84SPaolo Bonzini 
620d2c0bd84SPaolo Bonzini static void
dma_update_state(struct fs_dma_ctrl * ctrl,int c)621d2c0bd84SPaolo Bonzini dma_update_state(struct fs_dma_ctrl *ctrl, int c)
622d2c0bd84SPaolo Bonzini {
623d2c0bd84SPaolo Bonzini     if (ctrl->channels[c].regs[RW_CFG] & 2)
624d2c0bd84SPaolo Bonzini         ctrl->channels[c].state = STOPPED;
625d2c0bd84SPaolo Bonzini     if (!(ctrl->channels[c].regs[RW_CFG] & 1))
626d2c0bd84SPaolo Bonzini         ctrl->channels[c].state = RST;
627d2c0bd84SPaolo Bonzini }
628d2c0bd84SPaolo Bonzini 
629d2c0bd84SPaolo Bonzini static void
dma_write(void * opaque,hwaddr addr,uint64_t val64,unsigned int size)630d2c0bd84SPaolo Bonzini dma_write(void *opaque, hwaddr addr,
631d2c0bd84SPaolo Bonzini           uint64_t val64, unsigned int size)
632d2c0bd84SPaolo Bonzini {
633d2c0bd84SPaolo Bonzini     struct fs_dma_ctrl *ctrl = opaque;
634d2c0bd84SPaolo Bonzini     uint32_t value = val64;
635d2c0bd84SPaolo Bonzini     int c;
636d2c0bd84SPaolo Bonzini 
637d2c0bd84SPaolo Bonzini     if (size != 4) {
638d2c0bd84SPaolo Bonzini         dma_winvalid(opaque, addr, value);
639d2c0bd84SPaolo Bonzini     }
640d2c0bd84SPaolo Bonzini 
641d2c0bd84SPaolo Bonzini         /* Make addr relative to this channel and bounded to nr regs.  */
642d2c0bd84SPaolo Bonzini     c = fs_channel(addr);
643d2c0bd84SPaolo Bonzini     addr &= 0xff;
644d2c0bd84SPaolo Bonzini     addr >>= 2;
645d2c0bd84SPaolo Bonzini     switch (addr)
646d2c0bd84SPaolo Bonzini     {
647d2c0bd84SPaolo Bonzini     case RW_DATA:
648d2c0bd84SPaolo Bonzini         ctrl->channels[c].regs[addr] = value;
649d2c0bd84SPaolo Bonzini         break;
650d2c0bd84SPaolo Bonzini 
651d2c0bd84SPaolo Bonzini     case RW_CFG:
652d2c0bd84SPaolo Bonzini         ctrl->channels[c].regs[addr] = value;
653d2c0bd84SPaolo Bonzini         dma_update_state(ctrl, c);
654d2c0bd84SPaolo Bonzini         break;
655d2c0bd84SPaolo Bonzini     case RW_CMD:
656d2c0bd84SPaolo Bonzini         /* continue.  */
657d2c0bd84SPaolo Bonzini         if (value & ~1)
658d2c0bd84SPaolo Bonzini             printf("Invalid store to ch=%d RW_CMD %x\n",
659d2c0bd84SPaolo Bonzini                    c, value);
660d2c0bd84SPaolo Bonzini         ctrl->channels[c].regs[addr] = value;
661d2c0bd84SPaolo Bonzini         channel_continue(ctrl, c);
662d2c0bd84SPaolo Bonzini         break;
663d2c0bd84SPaolo Bonzini 
664d2c0bd84SPaolo Bonzini     case RW_SAVED_DATA:
665d2c0bd84SPaolo Bonzini     case RW_SAVED_DATA_BUF:
666d2c0bd84SPaolo Bonzini     case RW_GROUP:
667d2c0bd84SPaolo Bonzini     case RW_GROUP_DOWN:
668d2c0bd84SPaolo Bonzini         ctrl->channels[c].regs[addr] = value;
669d2c0bd84SPaolo Bonzini         break;
670d2c0bd84SPaolo Bonzini 
671d2c0bd84SPaolo Bonzini     case RW_ACK_INTR:
672d2c0bd84SPaolo Bonzini     case RW_INTR_MASK:
673d2c0bd84SPaolo Bonzini         ctrl->channels[c].regs[addr] = value;
674d2c0bd84SPaolo Bonzini         channel_update_irq(ctrl, c);
675d2c0bd84SPaolo Bonzini         if (addr == RW_ACK_INTR)
676d2c0bd84SPaolo Bonzini             ctrl->channels[c].regs[RW_ACK_INTR] = 0;
677d2c0bd84SPaolo Bonzini         break;
678d2c0bd84SPaolo Bonzini 
679d2c0bd84SPaolo Bonzini     case RW_STREAM_CMD:
680d2c0bd84SPaolo Bonzini         if (value & ~1023)
681d2c0bd84SPaolo Bonzini             printf("Invalid store to ch=%d "
682d2c0bd84SPaolo Bonzini                    "RW_STREAMCMD %x\n",
683d2c0bd84SPaolo Bonzini                    c, value);
684d2c0bd84SPaolo Bonzini         ctrl->channels[c].regs[addr] = value;
685d2c0bd84SPaolo Bonzini         D(printf("stream_cmd ch=%d\n", c));
686d2c0bd84SPaolo Bonzini         channel_stream_cmd(ctrl, c, value);
687d2c0bd84SPaolo Bonzini         break;
688d2c0bd84SPaolo Bonzini 
689d2c0bd84SPaolo Bonzini     default:
690883f2c59SPhilippe Mathieu-Daudé         D(printf("%s c=%d " HWADDR_FMT_plx "\n",
691d2c0bd84SPaolo Bonzini                  __func__, c, addr));
692d2c0bd84SPaolo Bonzini         break;
693d2c0bd84SPaolo Bonzini     }
694d2c0bd84SPaolo Bonzini }
695d2c0bd84SPaolo Bonzini 
696d2c0bd84SPaolo Bonzini static const MemoryRegionOps dma_ops = {
697d2c0bd84SPaolo Bonzini     .read = dma_read,
698d2c0bd84SPaolo Bonzini     .write = dma_write,
699d2c0bd84SPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
700d2c0bd84SPaolo Bonzini     .valid = {
701d2c0bd84SPaolo Bonzini         .min_access_size = 1,
702d2c0bd84SPaolo Bonzini         .max_access_size = 4
703d2c0bd84SPaolo Bonzini     }
704d2c0bd84SPaolo Bonzini };
705d2c0bd84SPaolo Bonzini 
etraxfs_dmac_run(void * opaque)706d2c0bd84SPaolo Bonzini static int etraxfs_dmac_run(void *opaque)
707d2c0bd84SPaolo Bonzini {
708d2c0bd84SPaolo Bonzini     struct fs_dma_ctrl *ctrl = opaque;
709d2c0bd84SPaolo Bonzini     int i;
710d2c0bd84SPaolo Bonzini     int p = 0;
711d2c0bd84SPaolo Bonzini 
712d2c0bd84SPaolo Bonzini     for (i = 0;
713d2c0bd84SPaolo Bonzini          i < ctrl->nr_channels;
714d2c0bd84SPaolo Bonzini          i++)
715d2c0bd84SPaolo Bonzini     {
716d2c0bd84SPaolo Bonzini         if (ctrl->channels[i].state == RUNNING)
717d2c0bd84SPaolo Bonzini         {
718d2c0bd84SPaolo Bonzini             if (ctrl->channels[i].input) {
719d2c0bd84SPaolo Bonzini                 p += channel_in_run(ctrl, i);
720d2c0bd84SPaolo Bonzini             } else {
721d2c0bd84SPaolo Bonzini                 p += channel_out_run(ctrl, i);
722d2c0bd84SPaolo Bonzini             }
723d2c0bd84SPaolo Bonzini         }
724d2c0bd84SPaolo Bonzini     }
725d2c0bd84SPaolo Bonzini     return p;
726d2c0bd84SPaolo Bonzini }
727d2c0bd84SPaolo Bonzini 
etraxfs_dmac_input(struct etraxfs_dma_client * client,void * buf,int len,int eop)728d2c0bd84SPaolo Bonzini int etraxfs_dmac_input(struct etraxfs_dma_client *client,
729d2c0bd84SPaolo Bonzini                        void *buf, int len, int eop)
730d2c0bd84SPaolo Bonzini {
731d2c0bd84SPaolo Bonzini     return channel_in_process(client->ctrl, client->channel,
732d2c0bd84SPaolo Bonzini                               buf, len, eop);
733d2c0bd84SPaolo Bonzini }
734d2c0bd84SPaolo Bonzini 
735d2c0bd84SPaolo Bonzini /* Connect an IRQ line with a channel.  */
etraxfs_dmac_connect(void * opaque,int c,qemu_irq * line,int input)736d2c0bd84SPaolo Bonzini void etraxfs_dmac_connect(void *opaque, int c, qemu_irq *line, int input)
737d2c0bd84SPaolo Bonzini {
738d2c0bd84SPaolo Bonzini     struct fs_dma_ctrl *ctrl = opaque;
739d2c0bd84SPaolo Bonzini     ctrl->channels[c].irq = *line;
740d2c0bd84SPaolo Bonzini     ctrl->channels[c].input = input;
741d2c0bd84SPaolo Bonzini }
742d2c0bd84SPaolo Bonzini 
etraxfs_dmac_connect_client(void * opaque,int c,struct etraxfs_dma_client * cl)743d2c0bd84SPaolo Bonzini void etraxfs_dmac_connect_client(void *opaque, int c,
744d2c0bd84SPaolo Bonzini                                  struct etraxfs_dma_client *cl)
745d2c0bd84SPaolo Bonzini {
746d2c0bd84SPaolo Bonzini     struct fs_dma_ctrl *ctrl = opaque;
747d2c0bd84SPaolo Bonzini     cl->ctrl = ctrl;
748d2c0bd84SPaolo Bonzini     cl->channel = c;
749d2c0bd84SPaolo Bonzini     ctrl->channels[c].client = cl;
750d2c0bd84SPaolo Bonzini }
751d2c0bd84SPaolo Bonzini 
752d2c0bd84SPaolo Bonzini 
DMA_run(void * opaque)753d2c0bd84SPaolo Bonzini static void DMA_run(void *opaque)
754d2c0bd84SPaolo Bonzini {
755d2c0bd84SPaolo Bonzini     struct fs_dma_ctrl *etraxfs_dmac = opaque;
756d2c0bd84SPaolo Bonzini     int p = 1;
757d2c0bd84SPaolo Bonzini 
758d2c0bd84SPaolo Bonzini     if (runstate_is_running())
759d2c0bd84SPaolo Bonzini         p = etraxfs_dmac_run(etraxfs_dmac);
760d2c0bd84SPaolo Bonzini 
761d2c0bd84SPaolo Bonzini     if (p)
762d2c0bd84SPaolo Bonzini         qemu_bh_schedule_idle(etraxfs_dmac->bh);
763d2c0bd84SPaolo Bonzini }
764d2c0bd84SPaolo Bonzini 
etraxfs_dmac_init(hwaddr base,int nr_channels)765d2c0bd84SPaolo Bonzini void *etraxfs_dmac_init(hwaddr base, int nr_channels)
766d2c0bd84SPaolo Bonzini {
767d2c0bd84SPaolo Bonzini     struct fs_dma_ctrl *ctrl = NULL;
768d2c0bd84SPaolo Bonzini 
769d2c0bd84SPaolo Bonzini     ctrl = g_malloc0(sizeof *ctrl);
770d2c0bd84SPaolo Bonzini 
771d2c0bd84SPaolo Bonzini     ctrl->bh = qemu_bh_new(DMA_run, ctrl);
772d2c0bd84SPaolo Bonzini 
773d2c0bd84SPaolo Bonzini     ctrl->nr_channels = nr_channels;
774d2c0bd84SPaolo Bonzini     ctrl->channels = g_malloc0(sizeof ctrl->channels[0] * nr_channels);
775d2c0bd84SPaolo Bonzini 
7762c9b15caSPaolo Bonzini     memory_region_init_io(&ctrl->mmio, NULL, &dma_ops, ctrl, "etraxfs-dma",
777d2c0bd84SPaolo Bonzini                           nr_channels * 0x2000);
778d2c0bd84SPaolo Bonzini     memory_region_add_subregion(get_system_memory(), base, &ctrl->mmio);
779d2c0bd84SPaolo Bonzini 
780d2c0bd84SPaolo Bonzini     return ctrl;
781d2c0bd84SPaolo Bonzini }
782