4 Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
6 This file is part of simavr.
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.
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.
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/>.
23 #include "avr_ioport.h"
25 static uint8_t avr_ioport_read(struct avr_t * avr, avr_io_addr_t addr, void * param)
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);
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);
38 static void avr_ioport_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
40 avr_ioport_t * p = (avr_ioport_t *)param;
42 avr_core_watch_write(avr, addr, v);
43 // printf("PORT%c(%02x) = %02x (was %02x)\n", p->name, addr, v, oldv);
45 // raise the internal IRQ callbacks
46 for (int i = 0; i < 8; i++)
47 avr_raise_irq(p->io.irq + i, (v >> i) & 1);
48 avr_raise_irq(p->io.irq + IOPORT_IRQ_PIN_ALL, v);
52 * This is a reasonably new behaviour for the io-ports. Writing 1's to the PIN register
53 * toggles the PORT equivalent bit (regardless of direction
55 static void avr_ioport_pin_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
57 avr_ioport_t * p = (avr_ioport_t *)param;
59 avr_ioport_write(avr, p->r_port, avr->data[p->r_port] ^ v, param);
63 * This is a the callback for the DDR register. There is nothing much to do here, apart
64 * from triggering an IRQ in case any 'client' code is interested in the information,
65 * and restoring all PIN bits marked as output to PORT values.
67 static void avr_ioport_ddr_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param)
69 avr_ioport_t * p = (avr_ioport_t *)param;
71 avr_raise_irq(p->io.irq + IOPORT_IRQ_DIRECTION_ALL, v);
72 avr_core_watch_write(avr, addr, v);
74 const uint8_t oldpin = avr->data[p->r_pin];
75 const uint8_t pin = (oldpin & ~v) | (avr->data[p->r_port] & v);
76 avr_core_watch_write(avr, p->r_pin, pin);
77 for (int i = 0; i < 8; i++)
78 if (((oldpin ^ pin) >> i) & 1)
79 avr_raise_irq(p->io.irq + i, (pin >> i) & 1);
80 avr_raise_irq(p->io.irq + IOPORT_IRQ_PIN_ALL, pin);
84 * this is our "main" pin change callback, it can be triggered by either the
85 * AVR code, or any external piece of code that see fit to do it.
86 * Either way, this will raise pin change interrupts, if needed
88 void avr_ioport_irq_notify(struct avr_irq_t * irq, uint32_t value, void * param)
90 avr_ioport_t * p = (avr_ioport_t *)param;
91 avr_t * avr = p->io.avr;
93 int output = value & AVR_IOPORT_OUTPUT;
95 uint8_t mask = 1 << irq->irq;
96 // set the real PIN bit. ddr doesn't matter here as it's masked when read.
97 avr->data[p->r_pin] &= ~mask;
99 avr->data[p->r_pin] |= mask;
101 if (output) // if the IRQ was marked as Output, also do the IO write
102 avr_ioport_write(avr, p->r_port, (avr->data[p->r_port] & ~mask) | (value ? mask : 0), p);
105 // if the pcint bit is on, try to raise it
106 int raise = avr->data[p->r_pcint] & mask;
108 avr_raise_interrupt(avr, &p->pcint);
112 static void avr_ioport_reset(avr_io_t * port)
114 avr_ioport_t * p = (avr_ioport_t *)port;
115 for (int i = 0; i < IOPORT_IRQ_PIN_ALL; i++)
116 avr_irq_register_notify(p->io.irq + i, avr_ioport_irq_notify, p);
119 static int avr_ioport_ioctl(struct avr_io_t * port, uint32_t ctl, void * io_param)
121 avr_ioport_t * p = (avr_ioport_t *)port;
122 avr_t * avr = p->io.avr;
126 case AVR_IOCTL_IOPORT_GETIRQ_REGBIT: {
127 avr_ioport_getirq_t * r = (avr_ioport_getirq_t*)io_param;
129 if (r->bit.reg == p->r_port || r->bit.reg == p->r_pin || r->bit.reg == p->r_ddr) {
130 // it's us ! check the special case when the "all pins" irq is requested
132 if (r->bit.mask == 0xff)
133 r->irq[o++] = &p->io.irq[IOPORT_IRQ_PIN_ALL];
135 // otherwise fill up the ones needed
136 for (int bi = 0; bi < 8; bi++)
137 if (r->bit.mask & (1 << bi))
138 r->irq[o++] = &p->io.irq[r->bit.bit + bi];
147 * Return the port state if the IOCTL matches us.
149 if (ctl == AVR_IOCTL_IOPORT_GETSTATE(p->name)) {
150 avr_ioport_state_t state = {
152 .port = avr->data[p->r_port],
153 .ddr = avr->data[p->r_ddr],
154 .pin = avr->data[p->r_pin],
157 *((avr_ioport_state_t*)io_param) = state;
166 static const char * irq_names[IOPORT_IRQ_COUNT] = {
167 [IOPORT_IRQ_PIN0] = "=pin0",
168 [IOPORT_IRQ_PIN1] = "=pin1",
169 [IOPORT_IRQ_PIN2] = "=pin2",
170 [IOPORT_IRQ_PIN3] = "=pin3",
171 [IOPORT_IRQ_PIN4] = "=pin4",
172 [IOPORT_IRQ_PIN5] = "=pin5",
173 [IOPORT_IRQ_PIN6] = "=pin6",
174 [IOPORT_IRQ_PIN7] = "=pin7",
175 [IOPORT_IRQ_PIN_ALL] = "=all",
176 [IOPORT_IRQ_DIRECTION_ALL] = ">ddr",
179 static avr_io_t _io = {
181 .reset = avr_ioport_reset,
182 .ioctl = avr_ioport_ioctl,
183 .irq_names = irq_names,
186 void avr_ioport_init(avr_t * avr, avr_ioport_t * p)
189 // printf("%s PIN%c 0x%02x DDR%c 0x%02x PORT%c 0x%02x\n", __FUNCTION__,
190 // p->name, p->r_pin,
191 // p->name, p->r_ddr,
192 // p->name, p->r_port);
194 avr_register_io(avr, &p->io);
195 avr_register_vector(avr, &p->pcint);
196 // allocate this module's IRQ
197 avr_io_setirqs(&p->io, AVR_IOCTL_IOPORT_GETIRQ(p->name), IOPORT_IRQ_COUNT, NULL);
199 for (int i = 0; i < IOPORT_IRQ_COUNT; i++)
200 p->io.irq[i].flags |= IRQ_FLAG_FILTERED;
202 avr_register_io_write(avr, p->r_port, avr_ioport_write, p);
203 avr_register_io_read(avr, p->r_pin, avr_ioport_read, p);
204 avr_register_io_write(avr, p->r_pin, avr_ioport_pin_write, p);
205 avr_register_io_write(avr, p->r_ddr, avr_ioport_ddr_write, p);