1/* Blackfin General Purpose Ports (GPIO) model
2   For "new style" GPIOs on BF54x parts.
3
4   Copyright (C) 2010-2020 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
28struct 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
51static 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
58static unsigned
59bfin_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
146static unsigned
147bfin_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
200static 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
221static void
222bfin_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
248static void
249attach_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
276static void
277bfin_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
292const struct hw_descriptor dv_bfin_gpio2_descriptor[] =
293{
294  {"bfin_gpio2", bfin_gpio_finish,},
295  {NULL, NULL},
296};
297