summaryrefslogblamecommitdiffstats
path: root/bsd_eth_drivers/libbsdport/sysbus.c
blob: fcd3aac44189cd382f2d3bf096c0f3c2fc987ced (plain) (tree)
















































































































































                                                                                                                                     

 



                                                                                           





































                                                                                               
                        






































































                                                                                                                                                                                                                                                                              
#include <rtems.h>
#include <rtems/pci.h>
#include <rtems/error.h>
#include <sys/errno.h>
#include <bsp.h>
#include <devicet.h>
#include <bsp/irq.h>
#include <rtems/irq.h>

#include <sys/taskqueue.h>

#include <sys/bus.h>
#include <sys/malloc.h>

#include <bsp/rtems_verscheck.h>

#if !RTEMS_REV_AT_LEAST(4,6,99) || !defined(BSP_SHARED_HANDLER_SUPPORT)

#include <bsp/bspExt.h>

#else

static void noop(const rtems_irq_connect_data *unused) {};
static int  noop1(const rtems_irq_connect_data *unused) { return 0;};

/* Finally have an ISR arg but the API still sucks.. */
static int
bspExtInstallSharedISR(int irqLine, void (*isr)(void *), void * uarg, int flags)
{
rtems_irq_connect_data suck = {0};
	suck.name   = irqLine;
	suck.hdl    = isr;
	suck.handle = uarg;
	suck.on     = noop;
	suck.off    = noop;
	suck.isOn   = noop1;
	return ! BSP_install_rtems_shared_irq_handler(&suck);
}

static int
bspExtRemoveSharedISR(int irqLine, void (*isr)(void *), void *uarg)
{
rtems_irq_connect_data suck = {0};
	suck.name   = irqLine;
	suck.hdl    = isr;
	suck.handle = uarg;
	suck.on     = noop;
	suck.off    = noop;
	suck.isOn   = noop1;
	return ! BSP_remove_rtems_irq_handler(&suck);
}
#endif


struct resource *
bus_alloc_resource_any(device_t dev, int type, int *prid, unsigned flags)
{
bus_addr_t ba;
int        isio;
	switch ( type ) {
		default:
			break;
		case SYS_RES_IOPORT:
		case SYS_RES_MEMORY:
			{
			libbsdport_u32_t d;
			pci_read_config_dword(
				dev->bushdr.pci.bus,
				dev->bushdr.pci.dev,
				dev->bushdr.pci.fun,
				*prid,
				&d);
			ba = d;
			isio = (ba & PCI_BASE_ADDRESS_SPACE_IO) ? 1 : 0;
			if ( (type == SYS_RES_IOPORT) != (isio != 0) )
				return 0;	/* wrong type */

			return (struct resource *) ba;
			}
		case SYS_RES_IRQ:
			{
			uint8_t line;
			pci_read_config_byte(
				dev->bushdr.pci.bus,
				dev->bushdr.pci.dev,
				dev->bushdr.pci.fun,
				PCI_INTERRUPT_LINE,
				&line);
			ba = line;
			/* MSI not implemented */
			return (struct resource*) ba;
			}
	}
	rtems_panic("bus_alloc_resource_any: unknown/unimplemented resource type %i\n", type);
	/* never get here */
	return (struct resource*)0;
}

struct irq_cookie {
	device_t        dev;
	driver_filter_t handler;
	void			(*work)(void*);
	void            *arg;
	/* cache methods */
	int				(*irq_check_dis)(device_t d);
	void			(*irq_en)       (device_t d);
	struct task     task;
};

static int
sysbus_isr(void *arg)
{
struct irq_cookie *info = arg;
int rval;
#ifdef DEBUG
	printk("Sysbus IRQ\n");
#endif
	/* Check if we have an IRQ pending and disable further interrupts */
	rval = info->irq_check_dis(info->dev);
	if ( FILTER_HANDLED == rval ) {
		/* enqueue work */
		taskqueue_enqueue(taskqueue_fast, &info->task);
	}
	return rval;
}

static void
sysbus_taskfn(void *arg, int pending)
{
struct irq_cookie *info = arg;
	
	/* do work */
	info->work(info->arg);
	
	/* reenable interrupts */
	if ( info->irq_en )
		info->irq_en(info->dev);
}

int
bus_setup_intr(device_t dev, struct resource *r, int flags, driver_filter_t filter, driver_intr_t handler, void *arg, void **cookiep)
{
int                rval;
struct irq_cookie *info = 0;



	if ( filter && handler ) {
		rtems_panic("bus_setup_intr for both: filter & handler not implemented\n");
	}

	if ( handler ) {
		if ( !dev->drv ) {
			device_printf(dev, "bus_setup_intr: device has no driver attached\n");
			return EINVAL;
		} else if ( !dev->drv->methods->irq_check_dis ) {
			device_printf(dev, "bus_setup_intr: driver has no 'irq_dis' method\n");
			return EINVAL;
		}
	}

	if ( ! (info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT)) )
		return ENOMEM;

	info->dev     = dev;
	info->handler = filter;
	info->work    = handler;
	info->arg     = arg;

	if ( handler ) {
		TASK_INIT(&info->task, 0, sysbus_taskfn, info);
		/* make sure taskqueue facility is initialized */
		rtems_taskqueue_initialize();
		/* install our own filter */
		filter = sysbus_isr;
		arg    = info;
		info->irq_check_dis = dev->drv->methods->irq_check_dis;
		info->irq_en        = dev->drv->methods->irq_en;
	} else {
		TASK_INIT(&info->task, 0, 0, 0);
	}

	rval = bspExtInstallSharedISR((int)r, (void (*)(void*))filter, arg, 0);

	if ( rval ) {
		free(info, M_DEVBUF);
		return rval;
	}

	*cookiep = info;
	return rval;
}

int
bus_teardown_intr(device_t dev, struct resource *r, void *cookiep)
{
int rval;
struct irq_cookie *info = cookiep;
	rval = bspExtRemoveSharedISR((int)r, (void (*)(void*))info->handler, info->arg);
	if ( 0 == rval ) {
		if ( info->task.ta_fn ) {
			taskqueue_drain(taskqueue_fast, &info->task);
		}
		free(info, M_DEVBUF);
	}
	return rval;
}

bus_space_handle_t
rman_get_bushandle(struct resource *r)
{
bus_space_handle_t h = (bus_space_handle_t)r;
bus_space_handle_t msk = (PCI_BASE_ADDRESS_SPACE_IO & h) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK;
	return h & msk;
}

bus_space_tag_t
rman_get_bustag(struct resource *r)
{
bus_space_handle_t h = (bus_space_handle_t)r;
	return (PCI_BASE_ADDRESS_SPACE_IO & h) ? bus_space_io : bus_space_mem;
}

int
bus_dma_tag_create(void *parent, unsigned alignment, unsigned bounds, uint32_t lowadd, uint32_t hiaddr, void (*filter)(void*), void *filterarg, unsigned maxsize, int nsegs, unsigned maxsegsize, unsigned flags, void (*lockfunc)(void*), void *lockarg, bus_dma_tag_t *ptag)
{
bus_dma_tag_t tag;
	if ( filter || lockfunc )
		return ENOTSUP;
	if ( ! (tag = malloc(sizeof(*tag), M_DEVBUF, M_NOWAIT)) )
		return ENOMEM;
	/* save some information */
	tag->alignment = alignment;
	tag->maxsize   = maxsize;
	tag->maxsegs   = nsegs;
	*ptag          = tag;
	return 0;
}

void
bus_dma_tag_destroy(bus_dma_tag_t tag)
{
	free(tag, M_DEVBUF);
}

int
bus_dmamem_alloc(bus_dma_tag_t tag, void **p_vaddr, unsigned flags, bus_dmamap_t *p_map)
{
uintptr_t a;
	if ( ! (*p_map = malloc(tag->maxsize + tag->alignment, M_DEVBUF, M_NOWAIT)) )
		return ENOMEM;
	a = ((uintptr_t)*p_map + tag->alignment - 1 ) & ~(tag->alignment - 1);
	*p_vaddr = (void*)a;
	return 0;
}

void
bus_dmamem_free(bus_dma_tag_t tag, void *vaddr, bus_dmamap_t map)
{
	free(map, M_DEVBUF);
}