1/* Blackfin General Purpose Ports (GPIO) model
2
3   Copyright (C) 2010-2011 Free Software Foundation, Inc.
4   Contributed by Analog Devices, Inc.
5
6   This file is part of simulators.
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 3 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
20
21#include "config.h"
22
23#include "sim-main.h"
24#include "devices.h"
25#include "dv-bfin_gpio.h"
26
27struct bfin_gpio
28{
29  bu32 base;
30
31  /* Order after here is important -- matches hardware MMR layout.  */
32  bu16 BFIN_MMR_16(data);
33  bu16 BFIN_MMR_16(clear);
34  bu16 BFIN_MMR_16(set);
35  bu16 BFIN_MMR_16(toggle);
36  bu16 BFIN_MMR_16(maska);
37  bu16 BFIN_MMR_16(maska_clear);
38  bu16 BFIN_MMR_16(maska_set);
39  bu16 BFIN_MMR_16(maska_toggle);
40  bu16 BFIN_MMR_16(maskb);
41  bu16 BFIN_MMR_16(maskb_clear);
42  bu16 BFIN_MMR_16(maskb_set);
43  bu16 BFIN_MMR_16(maskb_toggle);
44  bu16 BFIN_MMR_16(dir);
45  bu16 BFIN_MMR_16(polar);
46  bu16 BFIN_MMR_16(edge);
47  bu16 BFIN_MMR_16(both);
48  bu16 BFIN_MMR_16(inen);
49};
50#define mmr_base()      offsetof(struct bfin_gpio, data)
51#define mmr_offset(mmr) (offsetof(struct bfin_gpio, mmr) - mmr_base())
52
53static const char * const mmr_names[] =
54{
55  "PORTIO", "PORTIO_CLEAR", "PORTIO_SET", "PORTIO_TOGGLE", "PORTIO_MASKA",
56  "PORTIO_MASKA_CLEAR", "PORTIO_MASKA_SET", "PORTIO_MASKA_TOGGLE",
57  "PORTIO_MASKB", "PORTIO_MASKB_CLEAR", "PORTIO_MASKB_SET",
58  "PORTIO_MASKB_TOGGLE", "PORTIO_DIR", "PORTIO_POLAR", "PORTIO_EDGE",
59  "PORTIO_BOTH", "PORTIO_INEN",
60};
61#define mmr_name(off) mmr_names[(off) / 4]
62
63static unsigned
64bfin_gpio_io_write_buffer (struct hw *me, const void *source, int space,
65			   address_word addr, unsigned nr_bytes)
66{
67  struct bfin_gpio *port = hw_data (me);
68  bu32 mmr_off;
69  bu16 value;
70  bu16 *valuep;
71
72  value = dv_load_2 (source);
73  mmr_off = addr - port->base;
74  valuep = (void *)((unsigned long)port + mmr_base() + mmr_off);
75
76  HW_TRACE_WRITE ();
77
78  dv_bfin_mmr_require_16 (me, addr, nr_bytes, true);
79
80  switch (mmr_off)
81    {
82    case mmr_offset(data):
83    case mmr_offset(maska):
84    case mmr_offset(maskb):
85    case mmr_offset(dir):
86    case mmr_offset(polar):
87    case mmr_offset(edge):
88    case mmr_offset(both):
89    case mmr_offset(inen):
90      *valuep = value;
91      break;
92    case mmr_offset(clear):
93    case mmr_offset(maska_clear):
94    case mmr_offset(maskb_clear):
95      /* We want to clear the related data MMR.  */
96      valuep -= 2;
97      dv_w1c_2 (valuep, value, -1);
98      break;
99    case mmr_offset(set):
100    case mmr_offset(maska_set):
101    case mmr_offset(maskb_set):
102      /* We want to set the related data MMR.  */
103      valuep -= 4;
104      *valuep |= value;
105      break;
106    case mmr_offset(toggle):
107    case mmr_offset(maska_toggle):
108    case mmr_offset(maskb_toggle):
109      /* We want to toggle the related data MMR.  */
110      valuep -= 6;
111      *valuep ^= value;
112      break;
113    default:
114      dv_bfin_mmr_invalid (me, addr, nr_bytes, true);
115      break;
116    }
117
118  return nr_bytes;
119}
120
121static unsigned
122bfin_gpio_io_read_buffer (struct hw *me, void *dest, int space,
123			  address_word addr, unsigned nr_bytes)
124{
125  struct bfin_gpio *port = hw_data (me);
126  bu32 mmr_off;
127  bu16 *valuep;
128
129  mmr_off = addr - port->base;
130  valuep = (void *)((unsigned long)port + mmr_base() + mmr_off);
131
132  HW_TRACE_READ ();
133
134  dv_bfin_mmr_require_16 (me, addr, nr_bytes, false);
135
136  switch (mmr_off)
137    {
138    case mmr_offset(data):
139    case mmr_offset(clear):
140    case mmr_offset(set):
141    case mmr_offset(toggle):
142      dv_store_2 (dest, port->data);
143      break;
144    case mmr_offset(maska):
145    case mmr_offset(maska_clear):
146    case mmr_offset(maska_set):
147    case mmr_offset(maska_toggle):
148      dv_store_2 (dest, port->maska);
149      break;
150    case mmr_offset(maskb):
151    case mmr_offset(maskb_clear):
152    case mmr_offset(maskb_set):
153    case mmr_offset(maskb_toggle):
154      dv_store_2 (dest, port->maskb);
155      break;
156    case mmr_offset(dir):
157    case mmr_offset(polar):
158    case mmr_offset(edge):
159    case mmr_offset(both):
160    case mmr_offset(inen):
161      dv_store_2 (dest, *valuep);
162      break;
163    default:
164      dv_bfin_mmr_invalid (me, addr, nr_bytes, false);
165      break;
166    }
167
168  return nr_bytes;
169}
170
171static const struct hw_port_descriptor bfin_gpio_ports[] =
172{
173  { "mask_a", 0, 0, output_port, },
174  { "mask_b", 1, 0, output_port, },
175  { "p0",     0, 0, input_port, },
176  { "p1",     1, 0, input_port, },
177  { "p2",     2, 0, input_port, },
178  { "p3",     3, 0, input_port, },
179  { "p4",     4, 0, input_port, },
180  { "p5",     5, 0, input_port, },
181  { "p6",     6, 0, input_port, },
182  { "p7",     7, 0, input_port, },
183  { "p8",     8, 0, input_port, },
184  { "p9",     9, 0, input_port, },
185  { "p10",   10, 0, input_port, },
186  { "p11",   11, 0, input_port, },
187  { "p12",   12, 0, input_port, },
188  { "p13",   13, 0, input_port, },
189  { "p14",   14, 0, input_port, },
190  { NULL, 0, 0, 0, },
191};
192
193static void
194bfin_gpio_port_event (struct hw *me, int my_port, struct hw *source,
195		      int source_port, int level)
196{
197  struct bfin_gpio *port = hw_data (me);
198  bool olvl, nlvl;
199  bu32 bit = (1 << my_port);
200
201  /* Normalize the level value.  A simulated device can send any value
202     it likes to us, but in reality we only care about 0 and 1.  This
203     lets us assume only those two values below.  */
204  level = !!level;
205
206  HW_TRACE ((me, "pin %i set to %i", my_port, level));
207
208  /* Only screw with state if this pin is set as an input, and the
209     input is actually enabled.  */
210  if ((port->dir & bit) || !(port->inen & bit))
211    {
212      HW_TRACE ((me, "ignoring level/int due to DIR=%i INEN=%i",
213		 !!(port->dir & bit), !!(port->inen & bit)));
214      return;
215    }
216
217  /* Get the old pin state for calculating an interrupt.  */
218  olvl = !!(port->data & bit);
219
220  /* Update the new pin state.  */
221  port->data = (port->data & ~bit) | (level << my_port);
222
223  /* See if this state transition will generate an interrupt.  */
224  nlvl = !!(port->data & bit);
225
226  if (port->edge & bit)
227    {
228      /* Pin is edge triggered.  */
229      if (port->both & bit)
230	{
231	  /* Both edges.  */
232	  if (olvl == nlvl)
233	    {
234	      HW_TRACE ((me, "ignoring int due to EDGE=%i BOTH=%i lvl=%i->%i",
235			 !!(port->edge & bit), !!(port->both & bit),
236			 olvl, nlvl));
237	      return;
238	    }
239	}
240      else
241	{
242	  /* Just one edge.  */
243	  if (!(((port->polar & bit) && olvl > nlvl)
244		|| (!(port->polar & bit) && olvl < nlvl)))
245	    {
246	      HW_TRACE ((me, "ignoring int due to EDGE=%i POLAR=%i lvl=%i->%i",
247			 !!(port->edge & bit), !!(port->polar & bit),
248			 olvl, nlvl));
249	      return;
250	    }
251	}
252    }
253  else
254    {
255      /* Pin is level triggered.  */
256      if (nlvl == !!(port->polar & bit))
257	{
258	  HW_TRACE ((me, "ignoring int due to EDGE=%i POLAR=%i lvl=%i",
259		     !!(port->edge & bit), !!(port->polar & bit), nlvl));
260	  return;
261	}
262    }
263
264  /* If the masks allow it, push the interrupt even higher.  */
265  if (port->maska & bit)
266    {
267      HW_TRACE ((me, "pin %i triggered an int via mask a", my_port));
268      hw_port_event (me, 0, 1);
269    }
270  if (port->maskb & bit)
271    {
272      HW_TRACE ((me, "pin %i triggered an int via mask b", my_port));
273      hw_port_event (me, 1, 1);
274    }
275}
276
277static void
278attach_bfin_gpio_regs (struct hw *me, struct bfin_gpio *port)
279{
280  address_word attach_address;
281  int attach_space;
282  unsigned attach_size;
283  reg_property_spec reg;
284
285  if (hw_find_property (me, "reg") == NULL)
286    hw_abort (me, "Missing \"reg\" property");
287
288  if (!hw_find_reg_array_property (me, "reg", 0, &reg))
289    hw_abort (me, "\"reg\" property must contain three addr/size entries");
290
291  hw_unit_address_to_attach_address (hw_parent (me),
292				     &reg.address,
293				     &attach_space, &attach_address, me);
294  hw_unit_size_to_attach_size (hw_parent (me), &reg.size, &attach_size, me);
295
296  if (attach_size != BFIN_MMR_GPIO_SIZE)
297    hw_abort (me, "\"reg\" size must be %#x", BFIN_MMR_GPIO_SIZE);
298
299  hw_attach_address (hw_parent (me),
300		     0, attach_space, attach_address, attach_size, me);
301
302  port->base = attach_address;
303}
304
305static void
306bfin_gpio_finish (struct hw *me)
307{
308  struct bfin_gpio *port;
309
310  port = HW_ZALLOC (me, struct bfin_gpio);
311
312  set_hw_data (me, port);
313  set_hw_io_read_buffer (me, bfin_gpio_io_read_buffer);
314  set_hw_io_write_buffer (me, bfin_gpio_io_write_buffer);
315  set_hw_ports (me, bfin_gpio_ports);
316  set_hw_port_event (me, bfin_gpio_port_event);
317
318  attach_bfin_gpio_regs (me, port);
319}
320
321const struct hw_descriptor dv_bfin_gpio_descriptor[] =
322{
323  {"bfin_gpio", bfin_gpio_finish,},
324  {NULL, NULL},
325};
326