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