irq: Add names to most io module external irqs
[simavr] / simavr / sim / avr_ioport.c
1 /*
2         avr_ioport.c
3
4         Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
5
6         This file is part of simavr.
7
8         simavr 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         simavr 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 simavr.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include <stdio.h>
23 #include "avr_ioport.h"
24
25 static uint8_t avr_ioport_read(struct avr_t * avr, avr_io_addr_t addr, void * param)
26 {
27         avr_ioport_t * p = (avr_ioport_t *)param;
28         uint8_t ddr = avr->data[p->r_ddr];
29         uint8_t v = (avr->data[p->r_pin] & ~ddr) | (avr->data[p->r_port] & ddr);
30         avr->data[addr] = v;
31         // made to trigger potential watchpoints
32         v = avr_core_watch_read(avr, addr);
33 //              printf("** PIN%c(%02x) = %02x\n", p->name, addr, v);
34
35         return v;
36 }
37
38 static void avr_ioport_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
39 {
40         avr_ioport_t * p = (avr_ioport_t *)param;
41         uint8_t oldv = avr->data[addr];
42
43         avr_core_watch_write(avr, addr, v);
44         if (v != oldv) {
45                 //      printf("PORT%c(%02x) = %02x (was %02x)\n", p->name, addr, v, oldv);
46                 int mask = v ^ oldv;
47
48                 // raise the internal IRQ callbacks
49                 for (int i = 0; i < 8; i++)
50                         if (mask & (1 << i))
51                                 avr_raise_irq(p->io.irq + i, (v >> i) & 1);
52                 avr_raise_irq(p->io.irq + IOPORT_IRQ_PIN_ALL, v);
53         }
54 }
55
56 /*
57  * This is a reasonably new behaviour for the io-ports. Writing 1's to the PIN register
58  * toggles the PORT equivalent bit (regardless of direction
59  */
60 static void avr_ioport_pin_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
61 {
62         avr_ioport_t * p = (avr_ioport_t *)param;
63
64         avr_ioport_write(avr, p->r_port, avr->data[p->r_port] ^ v, param);
65 }
66
67 /*
68  * This is a the callback for the DDR register. There is nothing much to do here, apart
69  * from triggering an IRQ in case any 'client' code is interested in the information.
70  */
71 static void avr_ioport_ddr_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
72 {
73         avr_ioport_t * p = (avr_ioport_t *)param;
74
75         avr_raise_irq(p->io.irq + IOPORT_IRQ_DIRECTION_ALL, v);
76         avr_core_watch_write(avr, addr, v);
77 }
78
79 /*
80  * this is our "main" pin change callback, it can be triggered by either the
81  * AVR code, or any external piece of code that see fit to do it.
82  * Either way, this will raise pin change interrupts, if needed
83  */
84 void avr_ioport_irq_notify(struct avr_irq_t * irq, uint32_t value, void * param)
85 {
86         avr_ioport_t * p = (avr_ioport_t *)param;
87         avr_t * avr = p->io.avr;
88
89         int output = value & 0x100;
90         value &= 0xff;
91         uint8_t mask = 1 << irq->irq;
92                 // set the real PIN bit. ddr doesn't matter here as it's masked when read.
93         avr->data[p->r_pin] &= ~mask;
94         if (value)
95                 avr->data[p->r_pin] |= mask;
96
97         if (output)     // if the IRQ was marked as Output, also do the IO write
98                 avr_ioport_write(avr, p->r_port, (avr->data[p->r_port] & ~mask) | (value ? mask : 0), p);
99
100         if (p->r_pcint) {
101                 // if the pcint bit is on, try to raise it
102                 int raise = avr->data[p->r_pcint] & mask;
103                 if (raise)
104                         avr_raise_interrupt(avr, &p->pcint);
105         }
106 }
107
108 static void avr_ioport_reset(avr_io_t * port)
109 {
110         avr_ioport_t * p = (avr_ioport_t *)port;
111         for (int i = 0; i < IOPORT_IRQ_PIN_ALL; i++) 
112                 avr_irq_register_notify(p->io.irq + i, avr_ioport_irq_notify, p);
113 }
114
115 static int avr_ioport_ioctl(struct avr_io_t * port, uint32_t ctl, void * io_param)
116 {
117         avr_ioport_t * p = (avr_ioport_t *)port;
118         avr_t * avr = p->io.avr;
119         int res = -1;
120
121         switch(ctl) {
122                 case AVR_IOCTL_IOPORT_GETIRQ_REGBIT: {
123                         avr_ioport_getirq_t * r = (avr_ioport_getirq_t*)io_param;
124
125                         if (r->bit.reg == p->r_port || r->bit.reg == p->r_pin || r->bit.reg == p->r_ddr) {
126                                 // it's us ! check the special case when the "all pins" irq is requested
127                                 int o = 0;
128                                 if (r->bit.mask == 0xff)
129                                         r->irq[o++] = &p->io.irq[IOPORT_IRQ_PIN_ALL];
130                                 else {
131                                         // otherwise fil up the ones needed
132                                         for (int bi = 0; bi < 8; bi++)
133                                                 if (r->bit.mask & (1 << bi))
134                                                         r->irq[o++] = &p->io.irq[r->bit.bit + bi];
135                                 }
136                                 if (o < 8)
137                                         r->irq[o] = NULL;
138                                 return o;
139                         }
140                 }       break;
141                 default: {
142                         /*
143                          * Return the port state if the IOCTL matches us.
144                          */
145                         if (ctl == AVR_IOCTL_IOPORT_GETSTATE(p->name)) {
146                                 avr_ioport_state_t state = {
147                                         .name = p->name,
148                                         .port = avr->data[p->r_port],
149                                         .ddr = avr->data[p->r_ddr],
150                                         .pin = avr->data[p->r_pin],
151                                 };
152                                 if (io_param)
153                                         *((avr_ioport_state_t*)io_param) = state;
154                                 res = 0;
155                         }
156                 }
157         }
158
159         return res;
160 }
161
162 static const char * irq_names[IOPORT_IRQ_COUNT] = {
163         [IOPORT_IRQ_PIN0] = "=pin0",
164         [IOPORT_IRQ_PIN1] = "=pin1",
165         [IOPORT_IRQ_PIN2] = "=pin2",
166         [IOPORT_IRQ_PIN3] = "=pin3",
167         [IOPORT_IRQ_PIN4] = "=pin4",
168         [IOPORT_IRQ_PIN5] = "=pin5",
169         [IOPORT_IRQ_PIN6] = "=pin6",
170         [IOPORT_IRQ_PIN7] = "=pin7",
171         [IOPORT_IRQ_PIN_ALL] = "=all",
172         [IOPORT_IRQ_DIRECTION_ALL] = ">ddr",
173 };
174
175 static  avr_io_t        _io = {
176         .kind = "port",
177         .reset = avr_ioport_reset,
178         .ioctl = avr_ioport_ioctl,
179         .irq_names = irq_names,
180 };
181
182 void avr_ioport_init(avr_t * avr, avr_ioport_t * p)
183 {
184         p->io = _io;
185 //      printf("%s PIN%c 0x%02x DDR%c 0x%02x PORT%c 0x%02x\n", __FUNCTION__,
186 //              p->name, p->r_pin,
187 //              p->name, p->r_ddr,
188 //              p->name, p->r_port);
189         
190         avr_register_io(avr, &p->io);
191         avr_register_vector(avr, &p->pcint);
192         // allocate this module's IRQ
193         avr_io_setirqs(&p->io, AVR_IOCTL_IOPORT_GETIRQ(p->name), IOPORT_IRQ_COUNT, NULL);
194
195         avr_register_io_write(avr, p->r_port, avr_ioport_write, p);
196         avr_register_io_read(avr, p->r_pin, avr_ioport_read, p);
197         avr_register_io_write(avr, p->r_pin, avr_ioport_pin_write, p);
198         avr_register_io_write(avr, p->r_ddr, avr_ioport_ddr_write, p);
199 }
200