| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 | /* * abcio.c * * Handle ABC-bus I/O operations */#include "common.h"#include "io.h"#include "irq.h"#include "abcio.h"#include "console.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 >= DEVSEL_NONE)	devsel = DEVSEL_NONE;    irqmask_t irqmask = mask_irq(ABC_IRQ);    unsigned int old_devsel = DEVSEL_NONE;    struct abc_dev *old_dev = NULL;    if (dev) {	if (dev->devsel < DEVSEL_NONE && abc_device[dev->devsel] == dev) {	    old_devsel = dev->devsel;	    if (old_devsel == devsel)		goto done;	/* Already registered at this address */	    abc_device[old_devsel] = NULL;	    if (old_devsel == abc_devsel)		abc_select(NULL);	}	dev->devsel = devsel;	if (!dev->inp_cnt)	    dev->inp_data[0] = dev->inp_data_def;    }    if (devsel < DEVSEL_NONE) {	old_dev = abc_device[devsel];	if (old_dev)	    old_dev->devsel = DEVSEL_NONE;	abc_device[devsel] = dev;	if (devsel == abc_devsel)	    abc_select(dev);    }done:    restore_irq(irqmask, ABC_IRQ);    if (dev) {	if (old_dev) {	    con_printf("abcio: Unregistered device %s from devsel %u\n",		       old_dev->name, devsel);	}	if (old_devsel < DEVSEL_NONE) {	    if (devsel == old_devsel) {		/* Print nothing */	    } else if (devsel < DEVSEL_NONE) {		con_printf("abcio: Moved device %s from devsel %u to %u\n",			   dev->name, old_devsel, devsel);	    } else {		con_printf("abcio: Unregistered device %s from devsel %u\n",			   dev->name, old_devsel);	    }	} else {	    if (devsel < DEVSEL_NONE) {		con_printf("abcio: Registered device %s on devsel %u\n",			   dev->name, devsel);	    } else {		con_printf("abcio: Device %s is disabled\n", dev->name);	    }	}    }}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 */}
 |