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