1/* Blackfin Enhanced Parallel Port Interface (EPPI) model 2 For "new style" PPIs on BF54x/etc... parts. 3 4 Copyright (C) 2010-2023 Free Software Foundation, Inc. 5 Contributed by Analog Devices, Inc. 6 7 This file is part of simulators. 8 9 This program is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 3 of the License, or 12 (at your option) any later version. 13 14 This program is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 21 22/* This must come before any other includes. */ 23#include "defs.h" 24 25#include "sim-main.h" 26#include "devices.h" 27#include "dv-bfin_eppi.h" 28#include "gui.h" 29 30/* XXX: TX is merely a stub. */ 31 32struct bfin_eppi 33{ 34 /* This top portion matches common dv_bfin struct. */ 35 bu32 base; 36 struct hw *dma_master; 37 bool acked; 38 39 struct hw_event *handler; 40 char saved_byte; 41 int saved_count; 42 43 /* GUI state. */ 44 void *gui_state; 45 int color; 46 47 /* Order after here is important -- matches hardware MMR layout. */ 48 bu16 BFIN_MMR_16(status); 49 bu16 BFIN_MMR_16(hcount); 50 bu16 BFIN_MMR_16(hdelay); 51 bu16 BFIN_MMR_16(vcount); 52 bu16 BFIN_MMR_16(vdelay); 53 bu16 BFIN_MMR_16(frame); 54 bu16 BFIN_MMR_16(line); 55 bu16 BFIN_MMR_16(clkdiv); 56 bu32 control, fs1w_hbl, fs1p_avpl, fsw2_lvb, fs2p_lavf, clip, err; 57}; 58#define mmr_base() offsetof(struct bfin_eppi, status) 59#define mmr_offset(mmr) (offsetof(struct bfin_eppi, mmr) - mmr_base()) 60 61static const char * const mmr_names[] = 62{ 63 "EPPI_STATUS", "EPPI_HCOUNT", "EPPI_HDELAY", "EPPI_VCOUNT", "EPPI_VDELAY", 64 "EPPI_FRAME", "EPPI_LINE", "EPPI_CLKDIV", "EPPI_CONTROL", "EPPI_FS1W_HBL", 65 "EPPI_FS1P_AVPL", "EPPI_FS2W_LVB", "EPPI_FS2P_LAVF", "EPPI_CLIP", "EPPI_ERR", 66}; 67#define mmr_name(off) (mmr_names[(off) / 4] ? : "<INV>") 68 69static void 70bfin_eppi_gui_setup (struct bfin_eppi *eppi) 71{ 72 /* If we are in RX mode, nothing to do. */ 73 if (!(eppi->control & PORT_DIR)) 74 return; 75 76 eppi->gui_state = bfin_gui_setup (eppi->gui_state, 77 eppi->control & PORT_EN, 78 eppi->hcount, 79 eppi->vcount, 80 eppi->color); 81} 82 83static unsigned 84bfin_eppi_io_write_buffer (struct hw *me, const void *source, 85 int space, address_word addr, unsigned nr_bytes) 86{ 87 struct bfin_eppi *eppi = hw_data (me); 88 bu32 mmr_off; 89 bu32 value; 90 bu16 *value16p; 91 bu32 *value32p; 92 void *valuep; 93 94 /* Invalid access mode is higher priority than missing register. */ 95 if (!dv_bfin_mmr_require_16_32 (me, addr, nr_bytes, true)) 96 return 0; 97 98 if (nr_bytes == 4) 99 value = dv_load_4 (source); 100 else 101 value = dv_load_2 (source); 102 103 mmr_off = addr - eppi->base; 104 valuep = (void *)((uintptr_t)eppi + mmr_base() + mmr_off); 105 value16p = valuep; 106 value32p = valuep; 107 108 HW_TRACE_WRITE (); 109 110 switch (mmr_off) 111 { 112 case mmr_offset(status): 113 if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true)) 114 return 0; 115 dv_w1c_2 (value16p, value, 0x1ff); 116 break; 117 case mmr_offset(hcount): 118 case mmr_offset(hdelay): 119 case mmr_offset(vcount): 120 case mmr_offset(vdelay): 121 case mmr_offset(frame): 122 case mmr_offset(line): 123 case mmr_offset(clkdiv): 124 if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true)) 125 return 0; 126 *value16p = value; 127 break; 128 case mmr_offset(control): 129 *value32p = value; 130 bfin_eppi_gui_setup (eppi); 131 break; 132 case mmr_offset(fs1w_hbl): 133 case mmr_offset(fs1p_avpl): 134 case mmr_offset(fsw2_lvb): 135 case mmr_offset(fs2p_lavf): 136 case mmr_offset(clip): 137 case mmr_offset(err): 138 if (!dv_bfin_mmr_require_32 (me, addr, nr_bytes, true)) 139 return 0; 140 *value32p = value; 141 break; 142 default: 143 dv_bfin_mmr_invalid (me, addr, nr_bytes, true); 144 return 0; 145 } 146 147 return nr_bytes; 148} 149 150static unsigned 151bfin_eppi_io_read_buffer (struct hw *me, void *dest, 152 int space, address_word addr, unsigned nr_bytes) 153{ 154 struct bfin_eppi *eppi = hw_data (me); 155 bu32 mmr_off; 156 bu16 *value16p; 157 bu32 *value32p; 158 void *valuep; 159 160 /* Invalid access mode is higher priority than missing register. */ 161 if (!dv_bfin_mmr_require_16_32 (me, addr, nr_bytes, true)) 162 return 0; 163 164 mmr_off = addr - eppi->base; 165 valuep = (void *)((uintptr_t)eppi + mmr_base() + mmr_off); 166 value16p = valuep; 167 value32p = valuep; 168 169 HW_TRACE_READ (); 170 171 switch (mmr_off) 172 { 173 case mmr_offset(status): 174 case mmr_offset(hcount): 175 case mmr_offset(hdelay): 176 case mmr_offset(vcount): 177 case mmr_offset(vdelay): 178 case mmr_offset(frame): 179 case mmr_offset(line): 180 case mmr_offset(clkdiv): 181 if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, false)) 182 return 0; 183 dv_store_2 (dest, *value16p); 184 break; 185 case mmr_offset(control): 186 case mmr_offset(fs1w_hbl): 187 case mmr_offset(fs1p_avpl): 188 case mmr_offset(fsw2_lvb): 189 case mmr_offset(fs2p_lavf): 190 case mmr_offset(clip): 191 case mmr_offset(err): 192 if (!dv_bfin_mmr_require_32 (me, addr, nr_bytes, false)) 193 return 0; 194 dv_store_4 (dest, *value32p); 195 break; 196 default: 197 dv_bfin_mmr_invalid (me, addr, nr_bytes, false); 198 return 0; 199 } 200 201 return nr_bytes; 202} 203 204static unsigned 205bfin_eppi_dma_read_buffer (struct hw *me, void *dest, int space, 206 unsigned_word addr, unsigned nr_bytes) 207{ 208 HW_TRACE_DMA_READ (); 209 return 0; 210} 211 212static unsigned 213bfin_eppi_dma_write_buffer (struct hw *me, const void *source, 214 int space, unsigned_word addr, 215 unsigned nr_bytes, 216 int violate_read_only_section) 217{ 218 struct bfin_eppi *eppi = hw_data (me); 219 220 HW_TRACE_DMA_WRITE (); 221 222 return bfin_gui_update (eppi->gui_state, source, nr_bytes); 223} 224 225static const struct hw_port_descriptor bfin_eppi_ports[] = 226{ 227 { "stat", 0, 0, output_port, }, 228 { NULL, 0, 0, 0, }, 229}; 230 231static void 232attach_bfin_eppi_regs (struct hw *me, struct bfin_eppi *eppi) 233{ 234 address_word attach_address; 235 int attach_space; 236 unsigned attach_size; 237 reg_property_spec reg; 238 239 if (hw_find_property (me, "reg") == NULL) 240 hw_abort (me, "Missing \"reg\" property"); 241 242 if (!hw_find_reg_array_property (me, "reg", 0, ®)) 243 hw_abort (me, "\"reg\" property must contain three addr/size entries"); 244 245 hw_unit_address_to_attach_address (hw_parent (me), 246 ®.address, 247 &attach_space, &attach_address, me); 248 hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me); 249 250 if (attach_size != BFIN_MMR_EPPI_SIZE) 251 hw_abort (me, "\"reg\" size must be %#x", BFIN_MMR_EPPI_SIZE); 252 253 hw_attach_address (hw_parent (me), 254 0, attach_space, attach_address, attach_size, me); 255 256 eppi->base = attach_address; 257} 258 259static void 260bfin_eppi_finish (struct hw *me) 261{ 262 struct bfin_eppi *eppi; 263 const char *color; 264 265 eppi = HW_ZALLOC (me, struct bfin_eppi); 266 267 set_hw_data (me, eppi); 268 set_hw_io_read_buffer (me, bfin_eppi_io_read_buffer); 269 set_hw_io_write_buffer (me, bfin_eppi_io_write_buffer); 270 set_hw_dma_read_buffer (me, bfin_eppi_dma_read_buffer); 271 set_hw_dma_write_buffer (me, bfin_eppi_dma_write_buffer); 272 set_hw_ports (me, bfin_eppi_ports); 273 274 attach_bfin_eppi_regs (me, eppi); 275 276 /* Initialize the EPPI. */ 277 if (hw_find_property (me, "color")) 278 color = hw_find_string_property (me, "color"); 279 else 280 color = NULL; 281 eppi->color = bfin_gui_color (color); 282} 283 284const struct hw_descriptor dv_bfin_eppi_descriptor[] = 285{ 286 {"bfin_eppi", bfin_eppi_finish,}, 287 {NULL, NULL}, 288}; 289