1 /* Blackfin General Purpose Ports (GPIO) model
2 
3    Copyright (C) 2010-2020 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 
27 struct bfin_gpio
28 {
29   bu32 base;
30 
31   bu16 int_state;
32 
33   /* Order after here is important -- matches hardware MMR layout.  */
34   bu16 BFIN_MMR_16(data);
35   bu16 BFIN_MMR_16(clear);
36   bu16 BFIN_MMR_16(set);
37   bu16 BFIN_MMR_16(toggle);
38   bu16 BFIN_MMR_16(maska);
39   bu16 BFIN_MMR_16(maska_clear);
40   bu16 BFIN_MMR_16(maska_set);
41   bu16 BFIN_MMR_16(maska_toggle);
42   bu16 BFIN_MMR_16(maskb);
43   bu16 BFIN_MMR_16(maskb_clear);
44   bu16 BFIN_MMR_16(maskb_set);
45   bu16 BFIN_MMR_16(maskb_toggle);
46   bu16 BFIN_MMR_16(dir);
47   bu16 BFIN_MMR_16(polar);
48   bu16 BFIN_MMR_16(edge);
49   bu16 BFIN_MMR_16(both);
50   bu16 BFIN_MMR_16(inen);
51 };
52 #define mmr_base()      offsetof(struct bfin_gpio, data)
53 #define mmr_offset(mmr) (offsetof(struct bfin_gpio, mmr) - mmr_base())
54 
55 static const char * const mmr_names[] =
56 {
57   "PORTIO", "PORTIO_CLEAR", "PORTIO_SET", "PORTIO_TOGGLE", "PORTIO_MASKA",
58   "PORTIO_MASKA_CLEAR", "PORTIO_MASKA_SET", "PORTIO_MASKA_TOGGLE",
59   "PORTIO_MASKB", "PORTIO_MASKB_CLEAR", "PORTIO_MASKB_SET",
60   "PORTIO_MASKB_TOGGLE", "PORTIO_DIR", "PORTIO_POLAR", "PORTIO_EDGE",
61   "PORTIO_BOTH", "PORTIO_INEN",
62 };
63 #define mmr_name(off) mmr_names[(off) / 4]
64 
65 static void
bfin_gpio_forward_int(struct hw * me,struct bfin_gpio * port,bu32 mask,int dst_port)66 bfin_gpio_forward_int (struct hw *me, struct bfin_gpio *port, bu32 mask,
67 		       int dst_port)
68 {
69   HW_TRACE ((me, "resending levels on port %c", 'a' + dst_port));
70   hw_port_event (me, dst_port, !!(port->int_state & mask));
71 }
72 static void
bfin_gpio_forward_ints(struct hw * me,struct bfin_gpio * port)73 bfin_gpio_forward_ints (struct hw *me, struct bfin_gpio *port)
74 {
75   bfin_gpio_forward_int (me, port, port->maska, 0);
76   bfin_gpio_forward_int (me, port, port->maskb, 1);
77 }
78 
79 static void
bfin_gpio_forward_ouput(struct hw * me,struct bfin_gpio * port,bu32 odata)80 bfin_gpio_forward_ouput (struct hw *me, struct bfin_gpio *port, bu32 odata)
81 {
82   int pin, value, ovalue, bit;
83 
84   for (pin = 0; pin < 16; ++pin)
85     {
86       bit = 1 << pin;
87 
88       /* Make sure this is an output pin.  */
89       if (!(port->dir & bit))
90 	continue;
91 
92       /* Only signal port if the pin changes value.  */
93       value = !!(port->data & bit);
94       ovalue = !!(odata & bit);
95       if (value == ovalue)
96 	continue;
97 
98       HW_TRACE ((me, "outputting gpio %i changed to %i", pin, value));
99       hw_port_event (me, pin, value);
100     }
101 }
102 
103 static unsigned
bfin_gpio_io_write_buffer(struct hw * me,const void * source,int space,address_word addr,unsigned nr_bytes)104 bfin_gpio_io_write_buffer (struct hw *me, const void *source, int space,
105 			   address_word addr, unsigned nr_bytes)
106 {
107   struct bfin_gpio *port = hw_data (me);
108   bu32 mmr_off;
109   bu16 value;
110   bu16 *valuep;
111   bu32 data = port->data;
112 
113   /* Invalid access mode is higher priority than missing register.  */
114   if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, true))
115     return 0;
116 
117   value = dv_load_2 (source);
118   mmr_off = addr - port->base;
119   valuep = (void *)((unsigned long)port + mmr_base() + mmr_off);
120 
121   HW_TRACE_WRITE ();
122 
123   switch (mmr_off)
124     {
125     case mmr_offset(data):
126     case mmr_offset(maska):
127     case mmr_offset(maskb):
128     case mmr_offset(dir):
129     case mmr_offset(polar):
130     case mmr_offset(edge):
131     case mmr_offset(both):
132     case mmr_offset(inen):
133       *valuep = value;
134       break;
135     case mmr_offset(clear):
136     case mmr_offset(maska_clear):
137     case mmr_offset(maskb_clear):
138       /* We want to clear the related data MMR.  */
139       valuep -= 2;
140       dv_w1c_2 (valuep, value, -1);
141       break;
142     case mmr_offset(set):
143     case mmr_offset(maska_set):
144     case mmr_offset(maskb_set):
145       /* We want to set the related data MMR.  */
146       valuep -= 4;
147       *valuep |= value;
148       break;
149     case mmr_offset(toggle):
150     case mmr_offset(maska_toggle):
151     case mmr_offset(maskb_toggle):
152       /* We want to toggle the related data MMR.  */
153       valuep -= 6;
154       *valuep ^= value;
155       break;
156     default:
157       dv_bfin_mmr_invalid (me, addr, nr_bytes, true);
158       return 0;
159     }
160 
161   /* If updating masks, make sure we send updated port info.  */
162   switch (mmr_off)
163     {
164     case mmr_offset(dir):
165     case mmr_offset(data) ... mmr_offset(toggle):
166       bfin_gpio_forward_ouput (me, port, data);
167       break;
168     case mmr_offset(maska) ... mmr_offset(maska_toggle):
169       bfin_gpio_forward_int (me, port, port->maska, 0);
170       break;
171     case mmr_offset(maskb) ... mmr_offset(maskb_toggle):
172       bfin_gpio_forward_int (me, port, port->maskb, 1);
173       break;
174     }
175 
176   return nr_bytes;
177 }
178 
179 static unsigned
bfin_gpio_io_read_buffer(struct hw * me,void * dest,int space,address_word addr,unsigned nr_bytes)180 bfin_gpio_io_read_buffer (struct hw *me, void *dest, int space,
181 			  address_word addr, unsigned nr_bytes)
182 {
183   struct bfin_gpio *port = hw_data (me);
184   bu32 mmr_off;
185   bu16 *valuep;
186 
187   /* Invalid access mode is higher priority than missing register.  */
188   if (!dv_bfin_mmr_require_16 (me, addr, nr_bytes, false))
189     return 0;
190 
191   mmr_off = addr - port->base;
192   valuep = (void *)((unsigned long)port + mmr_base() + mmr_off);
193 
194   HW_TRACE_READ ();
195 
196   switch (mmr_off)
197     {
198     case mmr_offset(data):
199     case mmr_offset(clear):
200     case mmr_offset(set):
201     case mmr_offset(toggle):
202       dv_store_2 (dest, port->data);
203       break;
204     case mmr_offset(maska):
205     case mmr_offset(maska_clear):
206     case mmr_offset(maska_set):
207     case mmr_offset(maska_toggle):
208       dv_store_2 (dest, port->maska);
209       break;
210     case mmr_offset(maskb):
211     case mmr_offset(maskb_clear):
212     case mmr_offset(maskb_set):
213     case mmr_offset(maskb_toggle):
214       dv_store_2 (dest, port->maskb);
215       break;
216     case mmr_offset(dir):
217     case mmr_offset(polar):
218     case mmr_offset(edge):
219     case mmr_offset(both):
220     case mmr_offset(inen):
221       dv_store_2 (dest, *valuep);
222       break;
223     default:
224       dv_bfin_mmr_invalid (me, addr, nr_bytes, false);
225       return 0;
226     }
227 
228   return nr_bytes;
229 }
230 
231 static const struct hw_port_descriptor bfin_gpio_ports[] =
232 {
233   { "mask_a", 0, 0, output_port, },
234   { "mask_b", 1, 0, output_port, },
235   { "p0",     0, 0, bidirect_port, },
236   { "p1",     1, 0, bidirect_port, },
237   { "p2",     2, 0, bidirect_port, },
238   { "p3",     3, 0, bidirect_port, },
239   { "p4",     4, 0, bidirect_port, },
240   { "p5",     5, 0, bidirect_port, },
241   { "p6",     6, 0, bidirect_port, },
242   { "p7",     7, 0, bidirect_port, },
243   { "p8",     8, 0, bidirect_port, },
244   { "p9",     9, 0, bidirect_port, },
245   { "p10",   10, 0, bidirect_port, },
246   { "p11",   11, 0, bidirect_port, },
247   { "p12",   12, 0, bidirect_port, },
248   { "p13",   13, 0, bidirect_port, },
249   { "p14",   14, 0, bidirect_port, },
250   { "p15",   15, 0, bidirect_port, },
251   { NULL, 0, 0, 0, },
252 };
253 
254 static void
bfin_gpio_port_event(struct hw * me,int my_port,struct hw * source,int source_port,int level)255 bfin_gpio_port_event (struct hw *me, int my_port, struct hw *source,
256 		      int source_port, int level)
257 {
258   struct bfin_gpio *port = hw_data (me);
259   bool olvl, nlvl;
260   bu32 bit = (1 << my_port);
261 
262   /* Normalize the level value.  A simulated device can send any value
263      it likes to us, but in reality we only care about 0 and 1.  This
264      lets us assume only those two values below.  */
265   level = !!level;
266 
267   HW_TRACE ((me, "pin %i set to %i", my_port, level));
268 
269   /* Only screw with state if this pin is set as an input, and the
270      input is actually enabled.  */
271   if ((port->dir & bit) || !(port->inen & bit))
272     {
273       HW_TRACE ((me, "ignoring level/int due to DIR=%i INEN=%i",
274 		 !!(port->dir & bit), !!(port->inen & bit)));
275       return;
276     }
277 
278   /* Get the old pin state for calculating an interrupt.  */
279   olvl = !!(port->data & bit);
280 
281   /* Update the new pin state.  */
282   port->data = (port->data & ~bit) | (level << my_port);
283 
284   /* See if this state transition will generate an interrupt.  */
285   nlvl = !!(port->data & bit);
286 
287   if (port->edge & bit)
288     {
289       /* Pin is edge triggered.  */
290       if (port->both & bit)
291 	{
292 	  /* Both edges.  */
293 	  if (olvl == nlvl)
294 	    {
295 	      HW_TRACE ((me, "ignoring int due to EDGE=%i BOTH=%i lvl=%i->%i",
296 			 !!(port->edge & bit), !!(port->both & bit),
297 			 olvl, nlvl));
298 	      return;
299 	    }
300 	}
301       else
302 	{
303 	  /* Just one edge.  */
304 	  if (!(((port->polar & bit) && olvl > nlvl)
305 		|| (!(port->polar & bit) && olvl < nlvl)))
306 	    {
307 	      HW_TRACE ((me, "ignoring int due to EDGE=%i POLAR=%i lvl=%i->%i",
308 			 !!(port->edge & bit), !!(port->polar & bit),
309 			 olvl, nlvl));
310 	      return;
311 	    }
312 	}
313 
314       /* Send the signal up, and then fall through to clear it.  */
315       port->int_state |= bit;
316       bfin_gpio_forward_ints (me, port);
317       port->int_state &= ~bit;
318     }
319   else
320     {
321       /* Pin is level triggered.  */
322       if (nlvl == !!(port->polar & bit))
323 	{
324 	  HW_TRACE ((me, "ignoring int due to EDGE=%i POLAR=%i lvl=%i",
325 		     !!(port->edge & bit), !!(port->polar & bit), nlvl));
326 	  /* We still need to signal SIC to clear the int, so don't return.  */
327 	  port->int_state &= ~bit;
328 	}
329       else
330 	port->int_state |= bit;
331     }
332 
333   bfin_gpio_forward_ints (me, port);
334 }
335 
336 static void
attach_bfin_gpio_regs(struct hw * me,struct bfin_gpio * port)337 attach_bfin_gpio_regs (struct hw *me, struct bfin_gpio *port)
338 {
339   address_word attach_address;
340   int attach_space;
341   unsigned attach_size;
342   reg_property_spec reg;
343 
344   if (hw_find_property (me, "reg") == NULL)
345     hw_abort (me, "Missing \"reg\" property");
346 
347   if (!hw_find_reg_array_property (me, "reg", 0, &reg))
348     hw_abort (me, "\"reg\" property must contain three addr/size entries");
349 
350   hw_unit_address_to_attach_address (hw_parent (me),
351 				     &reg.address,
352 				     &attach_space, &attach_address, me);
353   hw_unit_size_to_attach_size (hw_parent (me), &reg.size, &attach_size, me);
354 
355   if (attach_size != BFIN_MMR_GPIO_SIZE)
356     hw_abort (me, "\"reg\" size must be %#x", BFIN_MMR_GPIO_SIZE);
357 
358   hw_attach_address (hw_parent (me),
359 		     0, attach_space, attach_address, attach_size, me);
360 
361   port->base = attach_address;
362 }
363 
364 static void
bfin_gpio_finish(struct hw * me)365 bfin_gpio_finish (struct hw *me)
366 {
367   struct bfin_gpio *port;
368 
369   port = HW_ZALLOC (me, struct bfin_gpio);
370 
371   set_hw_data (me, port);
372   set_hw_io_read_buffer (me, bfin_gpio_io_read_buffer);
373   set_hw_io_write_buffer (me, bfin_gpio_io_write_buffer);
374   set_hw_ports (me, bfin_gpio_ports);
375   set_hw_port_event (me, bfin_gpio_port_event);
376 
377   attach_bfin_gpio_regs (me, port);
378 }
379 
380 const struct hw_descriptor dv_bfin_gpio_descriptor[] =
381 {
382   {"bfin_gpio", bfin_gpio_finish,},
383   {NULL, NULL},
384 };
385