1 /* Blackfin General Purpose Ports (GPIO) model 2 For "new style" GPIOs on BF54x parts. 3 4 Copyright (C) 2010-2024 Free Software Foundation, Inc. 5 Contributed by Analog Devices, Inc. and Mike Frysinger. 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_gpio2.h" 28 29 struct bfin_gpio 30 { 31 bu32 base; 32 33 /* Only accessed indirectly via dir_{set,clear}. */ 34 bu16 dir; 35 36 /* Make sure hardware MMRs are aligned. */ 37 bu16 _pad; 38 39 /* Order after here is important -- matches hardware MMR layout. */ 40 bu16 BFIN_MMR_16(fer); 41 bu16 BFIN_MMR_16(data); 42 bu16 BFIN_MMR_16(set); 43 bu16 BFIN_MMR_16(clear); 44 bu16 BFIN_MMR_16(dir_set); 45 bu16 BFIN_MMR_16(dir_clear); 46 bu16 BFIN_MMR_16(inen); 47 bu32 mux; 48 }; 49 #define mmr_base() offsetof(struct bfin_gpio, fer) 50 #define mmr_offset(mmr) (offsetof(struct bfin_gpio, mmr) - mmr_base()) 51 52 static const char * const mmr_names[] = 53 { 54 "PORTIO_FER", "PORTIO", "PORTIO_SET", "PORTIO_CLEAR", "PORTIO_DIR_SET", 55 "PORTIO_DIR_CLEAR", "PORTIO_INEN", "PORTIO_MUX", 56 }; 57 #define mmr_name(off) mmr_names[(off) / 4] 58 59 static unsigned 60 bfin_gpio_io_write_buffer (struct hw *me, const void *source, int space, 61 address_word addr, unsigned nr_bytes) 62 { 63 struct bfin_gpio *port = hw_data (me); 64 bu32 mmr_off; 65 bu32 value; 66 bu16 *value16p; 67 bu32 *value32p; 68 void *valuep; 69 70 mmr_off = addr - port->base; 71 72 /* Invalid access mode is higher priority than missing register. */ 73 if (mmr_off == mmr_offset (mux)) 74 { 75 if (!dv_bfin_mmr_require_32 (me, addr, nr_bytes, true)) 76 return 0; 77 } 78 else 79 if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true)) 80 return 0; 81 82 if (nr_bytes == 4) 83 value = dv_load_4 (source); 84 else 85 value = dv_load_2 (source); 86 valuep = (void *)((uintptr_t)port + mmr_base() + mmr_off); 87 value16p = valuep; 88 value32p = valuep; 89 90 HW_TRACE_WRITE (); 91 92 switch (mmr_off) 93 { 94 case mmr_offset(fer): 95 case mmr_offset(data): 96 case mmr_offset(inen): 97 *value16p = value; 98 break; 99 case mmr_offset(clear): 100 /* We want to clear the related data MMR. */ 101 dv_w1c_2 (&port->data, value, -1); 102 break; 103 case mmr_offset(set): 104 /* We want to set the related data MMR. */ 105 port->data |= value; 106 break; 107 case mmr_offset(dir_clear): 108 dv_w1c_2 (&port->dir, value, -1); 109 break; 110 case mmr_offset(dir_set): 111 port->dir |= value; 112 break; 113 case mmr_offset(mux): 114 *value32p = value; 115 break; 116 default: 117 dv_bfin_mmr_invalid (me, addr, nr_bytes, true); 118 return 0; 119 } 120 121 /* If tweaking output pins, make sure we send updated port info. */ 122 switch (mmr_off) 123 { 124 case mmr_offset(data): 125 case mmr_offset(set): 126 case mmr_offset(clear): 127 case mmr_offset(dir_set): 128 { 129 int i; 130 bu32 bit; 131 132 for (i = 0; i < 16; ++i) 133 { 134 bit = (1 << i); 135 136 if (!(port->inen & bit)) 137 hw_port_event (me, i, !!(port->data & bit)); 138 } 139 140 break; 141 } 142 } 143 144 return nr_bytes; 145 } 146 147 static unsigned 148 bfin_gpio_io_read_buffer (struct hw *me, void *dest, int space, 149 address_word addr, unsigned nr_bytes) 150 { 151 struct bfin_gpio *port = hw_data (me); 152 bu32 mmr_off; 153 bu16 *value16p; 154 bu32 *value32p; 155 void *valuep; 156 157 mmr_off = addr - port->base; 158 159 /* Invalid access mode is higher priority than missing register. */ 160 if (mmr_off == mmr_offset (mux)) 161 { 162 if (!dv_bfin_mmr_require_32 (me, addr, nr_bytes, false)) 163 return 0; 164 } 165 else 166 if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, false)) 167 return 0; 168 169 valuep = (void *)((uintptr_t)port + mmr_base() + mmr_off); 170 value16p = valuep; 171 value32p = valuep; 172 173 HW_TRACE_READ (); 174 175 switch (mmr_off) 176 { 177 case mmr_offset(data): 178 case mmr_offset(clear): 179 case mmr_offset(set): 180 dv_store_2 (dest, port->data); 181 break; 182 case mmr_offset(dir_clear): 183 case mmr_offset(dir_set): 184 dv_store_2 (dest, port->dir); 185 break; 186 case mmr_offset(fer): 187 case mmr_offset(inen): 188 dv_store_2 (dest, *value16p); 189 break; 190 case mmr_offset(mux): 191 dv_store_4 (dest, *value32p); 192 break; 193 default: 194 dv_bfin_mmr_invalid (me, addr, nr_bytes, false); 195 return 0; 196 } 197 198 return nr_bytes; 199 } 200 201 static const struct hw_port_descriptor bfin_gpio_ports[] = 202 { 203 { "p0", 0, 0, bidirect_port, }, 204 { "p1", 1, 0, bidirect_port, }, 205 { "p2", 2, 0, bidirect_port, }, 206 { "p3", 3, 0, bidirect_port, }, 207 { "p4", 4, 0, bidirect_port, }, 208 { "p5", 5, 0, bidirect_port, }, 209 { "p6", 6, 0, bidirect_port, }, 210 { "p7", 7, 0, bidirect_port, }, 211 { "p8", 8, 0, bidirect_port, }, 212 { "p9", 9, 0, bidirect_port, }, 213 { "p10", 10, 0, bidirect_port, }, 214 { "p11", 11, 0, bidirect_port, }, 215 { "p12", 12, 0, bidirect_port, }, 216 { "p13", 13, 0, bidirect_port, }, 217 { "p14", 14, 0, bidirect_port, }, 218 { "p15", 15, 0, bidirect_port, }, 219 { NULL, 0, 0, 0, }, 220 }; 221 222 static void 223 bfin_gpio_port_event (struct hw *me, int my_port, struct hw *source, 224 int source_port, int level) 225 { 226 struct bfin_gpio *port = hw_data (me); 227 bu32 bit = (1 << my_port); 228 229 /* Normalize the level value. A simulated device can send any value 230 it likes to us, but in reality we only care about 0 and 1. This 231 lets us assume only those two values below. */ 232 level = !!level; 233 234 HW_TRACE ((me, "pin %i set to %i", my_port, level)); 235 236 /* Only screw with state if this pin is set as an input, and the 237 input is actually enabled, and it isn't in peripheral mode. */ 238 if ((port->dir & bit) || !(port->inen & bit) || !(port->fer & bit)) 239 { 240 HW_TRACE ((me, "ignoring level due to DIR=%i INEN=%i FER=%i", 241 !!(port->dir & bit), !!(port->inen & bit), 242 !!(port->fer & bit))); 243 return; 244 } 245 246 hw_port_event (me, my_port, level); 247 } 248 249 static void 250 attach_bfin_gpio_regs (struct hw *me, struct bfin_gpio *port) 251 { 252 address_word attach_address; 253 int attach_space; 254 unsigned attach_size; 255 reg_property_spec reg; 256 257 if (hw_find_property (me, "reg") == NULL) 258 hw_abort (me, "Missing \"reg\" property"); 259 260 if (!hw_find_reg_array_property (me, "reg", 0, ®)) 261 hw_abort (me, "\"reg\" property must contain three addr/size entries"); 262 263 hw_unit_address_to_attach_address (hw_parent (me), 264 ®.address, 265 &attach_space, &attach_address, me); 266 hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me); 267 268 if (attach_size != BFIN_MMR_GPIO2_SIZE) 269 hw_abort (me, "\"reg\" size must be %#x", BFIN_MMR_GPIO2_SIZE); 270 271 hw_attach_address (hw_parent (me), 272 0, attach_space, attach_address, attach_size, me); 273 274 port->base = attach_address; 275 } 276 277 static void 278 bfin_gpio_finish (struct hw *me) 279 { 280 struct bfin_gpio *port; 281 282 port = HW_ZALLOC (me, struct bfin_gpio); 283 284 set_hw_data (me, port); 285 set_hw_io_read_buffer (me, bfin_gpio_io_read_buffer); 286 set_hw_io_write_buffer (me, bfin_gpio_io_write_buffer); 287 set_hw_ports (me, bfin_gpio_ports); 288 set_hw_port_event (me, bfin_gpio_port_event); 289 290 attach_bfin_gpio_regs (me, port); 291 } 292 293 const struct hw_descriptor dv_bfin_gpio2_descriptor[] = 294 { 295 {"bfin_gpio2", bfin_gpio_finish,}, 296 {NULL, NULL}, 297 }; 298