/* * abcio.c * * Handle ABC-bus I/O operations */ #include "fw.h" #include "io.h" #include "irq.h" #include "abcio.h" static struct abc_dev *abc_device[64]; static struct abc_dev *selected_dev; static uint8_t abc_devsel = -1; /* Not inlining this makes the code larger...! */ static inline __attribute__((always_inline)) void abc_select(struct abc_dev *dev) { selected_dev = dev; if (!dev) { ABC_BUSY_MASK = 0x0082; /* Only RST# and CS# */ ABC_INP = 0; } else { ABC_BUSY_MASK = dev->callback_mask | 0x0082; ABC_INP = dev->inp_data_w; } } IRQHANDLER(abc) { unsigned int what = ABC_BUSY_STATUS; unsigned int callback = selected_dev ? (what & selected_dev->callback_mask) : 0; struct abc_dev *dev = selected_dev; if (what & 0xff) { unsigned int addr = ABC_OUT_ADDR; unsigned int data = ABC_OUT_DATA; switch (addr) { case 0: dev->out_data[addr] = data; if (dev->out_cnt--) { *dev->out_buf++ = data; dev->inp_data[1] &= dev->status_first_out_mask; ABC_INP1_DATA = dev->inp_data[1]; if (!dev->out_cnt) break; /* No callback */ } if (callback & 1) dev->callback_out(dev, data); break; case 2 ... 5: dev->out_data[addr] = data; if (callback & (1 << addr)) dev->callback_cmd(dev, data, addr); break; case 1: dev = abc_device[abc_devsel = data & 0x3f]; abc_select(dev); if (dev) dev->out_data[addr] = data; break; case 7: /* XXX: broadcast reset to devices? */ abc_devsel = -1; abc_select(NULL); break; default: break; } } if (what & 0x100) { if (--dev->inp_cnt) { dev->inp_data[1] &= dev->status_first_inp_mask; ABC_INP1_DATA = dev->inp_data[1]; ABC_INP0_DATA = dev->inp_data[0] = *dev->inp_buf++; } else { ABC_INP0_DATA = dev->inp_data[0] = dev->inp_data_def; if (callback & 0x100) dev->callback_inp(dev); } } if (what & 0x200) { ABC_INP1_DATA = dev->inp_data[1]; if (callback & 0x200) dev->callback_status(dev); } ABC_BUSY_STATUS = what; } void abc_setup_out_queue(struct abc_dev *dev, void *buf, size_t len, uint8_t status) { mask_irq(ABC_IRQ); dev->out_buf = buf; dev->out_cnt = len; ABC_INP1_DATA = dev->inp_data[1] = status; dev->callback_mask |= (1 << 0); unmask_irq(ABC_IRQ); } void abc_setup_inp_queue(struct abc_dev *dev, const void *buf, size_t len, uint8_t status) { mask_irq(ABC_IRQ); dev->inp_buf = buf; dev->inp_cnt = len; ABC_INP0_DATA = dev->inp_data[0] = *dev->inp_buf++; ABC_INP1_DATA = dev->inp_data[1] = status; dev->callback_mask |= (1 << 8); unmask_irq(ABC_IRQ); } void abc_set_inp_default(struct abc_dev *dev, uint8_t val) { mask_irq(ABC_IRQ); dev->inp_data_def = val; if (!dev->inp_cnt) { dev->inp_data[0] = val; if (dev == selected_dev) ABC_INP0_DATA = val; } unmask_irq(ABC_IRQ); } void abc_set_inp_status(struct abc_dev *dev, uint8_t val) { mask_irq(ABC_IRQ); dev->inp_data[1] = val; if (dev == selected_dev) ABC_INP1_DATA = val; unmask_irq(ABC_IRQ); } void abc_register(struct abc_dev *dev, unsigned int devsel) { if (devsel > 63) return; mask_irq(ABC_IRQ); abc_device[devsel] = dev; if (devsel == abc_devsel) abc_select(dev); unmask_irq(ABC_IRQ); } void abc_init(void) { unsigned int devsel; mask_irq(ABC_IRQ); devsel = ABC_IOSEL; abc_devsel = (devsel & 0x100) ? -1U : devsel & 0x3f; abc_select(NULL); unmask_irq(ABC_IRQ); }