/* * abcio.c * * Handle ABC-bus I/O operations */ #include "common.h" #include "io.h" #include "irq.h" #include "abcio.h" __sbss struct abc_dev *_abc_selected_dev; static __bss_hot struct abc_dev *abc_device[65]; /* 65 == post-RST# = always NULL */ static __sdata uint8_t abc_devsel = 64; #define EVENT_MASK_ALWAYS 0x0082 /* RST# and CS# */ static inline __attribute__((always_inline)) uint16_t event_mask(const struct abc_dev *dev) { if (!dev) return EVENT_MASK_ALWAYS; return EVENT_MASK_ALWAYS | dev->callback_mask | (dev->out_cnt ? 1 : 0) | (dev->inp_cnt ? 0x100 : 0); } /* Not inlining this makes the code larger...! */ static inline __attribute__((always_inline)) void refresh_dev(struct abc_dev *dev) { if (dev == abc_selected_dev()) { ABC_BUSY_MASK = event_mask(dev); ABC_INP = dev ? dev->inp_data_w : 0; } } static inline __attribute__((always_inline)) void abc_select(struct abc_dev *dev) { _abc_selected_dev = dev; refresh_dev(dev); } IRQHANDLER(abc,0) { unsigned int what = ABC_BUSY_STATUS; struct abc_dev *dev = abc_selected_dev(); if (what & 0xff) { unsigned int addr = ABC_OUT_ADDR; unsigned int data = ABC_OUT_DATA; switch (addr) { case 0: if (!dev) break; 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, don't set out_data[] */ } goto handle_out; case 1: dev = abc_device[abc_devsel = data & 0x3f]; abc_select(dev); /* fall through */ case 2 ... 5: if (!dev) break; goto handle_out; handle_out: dev->out_data[addr] = data; if (dev->callback_mask & (1 << addr)) dev->callback_out[addr](dev, data, addr); break; case 7: /* Bus reset */ { struct abc_dev **devp; uint8_t old_devsel = abc_devsel; abc_devsel = DEVSEL_NONE; abc_select(NULL); /* Broadcast to all interested devices */ devp = &abc_device[0]; while (devp <= &abc_device[63]) { dev = *devp++; if (dev && (dev->callback_mask & 0x0080)) dev->callback_rst(dev, old_devsel, 7); } dev = NULL; break; } default: break; } } if (dev) { if (what & 0x100) { if (dev->inp_cnt && --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++; /* No callback */ } else { uint8_t old_data = ABC_INP0_DATA; ABC_INP0_DATA = dev->inp_data[0] = dev->inp_data_def; if (dev->callback_mask & 0x100) dev->callback_inp[0](dev, old_data, 0); } } if (what & 0x200) { uint8_t old_data = ABC_INP1_DATA; ABC_INP1_DATA = dev->inp_data[1]; if (dev->callback_mask & 0x200) dev->callback_inp[1](dev, old_data, 1); } } /* May need to change event_mask here */ ABC_BUSY = what | (event_mask(dev) << 16); } void __hot abc_setup_out_queue(struct abc_dev *dev, void *buf, size_t len, uint8_t status) { irqmask_t irqmask = mask_irq(ABC_IRQ); dev->out_buf = buf; dev->out_cnt = len; dev->inp_data[1] = status; refresh_dev(dev); /* Update registers as needed */ restore_irq(irqmask, ABC_IRQ); } void __hot abc_setup_inp_queue(struct abc_dev *dev, const void *buf, size_t len, uint8_t status) { irqmask_t irqmask = mask_irq(ABC_IRQ); dev->inp_buf = buf; dev->inp_cnt = len; dev->inp_data[0] = *dev->inp_buf++; dev->inp_data[1] = status; refresh_dev(dev); /* Update registers as needed */ restore_irq(irqmask, ABC_IRQ); } void __hot abc_set_inp_default(struct abc_dev *dev, uint8_t val) { irqmask_t irqmask = mask_irq(ABC_IRQ); dev->inp_data_def = val; if (!dev->inp_cnt) { dev->inp_data[0] = val; if (dev == abc_selected_dev()) ABC_INP0_DATA = val; } restore_irq(irqmask, ABC_IRQ); } void __hot abc_set_inp_status(struct abc_dev *dev, uint8_t val) { irqmask_t irqmask = mask_irq(ABC_IRQ); dev->inp_data[1] = val; if (dev == abc_selected_dev()) ABC_INP1_DATA = val; restore_irq(irqmask, ABC_IRQ); } /* * This can be called before or after abc_init()! */ void abc_register(struct abc_dev *dev, unsigned int devsel) { if (devsel > 63) return; irqmask_t irqmask = mask_irq(ABC_IRQ); if (!dev->inp_cnt) dev->inp_data[0] = dev->inp_data_def; abc_device[devsel] = dev; if (devsel == abc_devsel) abc_select(dev); restore_irq(irqmask, ABC_IRQ); } void abc_init(void) { unsigned int devsel; mask_irq(ABC_IRQ); devsel = ABC_IOSEL; abc_devsel = (devsel & 0x100) ? DEVSEL_NONE : (devsel & 63); abc_select(abc_device[abc_devsel]); unmask_irq(ABC_IRQ); /* Start ABC bus unconditionally */ }