[BACK]Return to sa1111_gpio.c CVS log [TXT][DIR] Up to [local] / sys / arch / arm / sa11x0

File: [local] / sys / arch / arm / sa11x0 / sa1111_gpio.c (download)

Revision 1.1, Wed Mar 5 13:34:26 2008 UTC (16 years, 2 months ago) by nbrk
Branch: MAIN
CVS Tags: HEAD

driver for SA-1111 GPIO controller;
it has three GPIO banks (GPIO_A, B, C):
bank A controls 4 i/o lines;
bank B controls 6 lines;
bank C controls 8 lines;
Implemented functions for value (level) and direction changes.

/*	$Id: sa1111_gpio.c,v 1.1 2008/03/05 13:34:26 nbrk Exp $	*/
#include <sys/cdefs.h>

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/evcount.h>
#include <sys/queue.h>
#include <uvm/uvm_extern.h>

#include <machine/bus.h>
#include <machine/intr.h>
#include <machine/lock.h>

#include <arm/sa11x0/sa11x1_var.h>
#include <arm/sa11x0/sa1111_reg.h>
#include <arm/sa11x0/sa1111_intr.h>
#include <arm/sa11x0/sa1111_gpiovar.h>

/*
 * SA-1111 GPIO controller.
 * It featurs three blocks:
 *  - GPIO_A[0:3]
 *  - GPIO_B[0:5]
 *  - GPIO_C[0:7]
 * We can control data value (level) and direction individually for each pin.
 */

struct sacgpio_softc {
	struct device	sc_dev;
	bus_space_tag_t	sc_bust;
	bus_space_handle_t	sc_bush;
};

/* autoconf glue */
int	sacgpio_match(struct device *parent, void *cf, void *aux);
void	sacgpio_attach(struct device *parent, struct device *self, void *aux);

struct cfattach	sacgpio_ca = {
	sizeof(struct sacgpio_softc),
	sacgpio_match,
	sacgpio_attach,
	NULL,
	NULL
};
struct cfdriver	sacgpio_cd = {
	NULL,
	"sacgpio",
	DV_DULL
};

struct sacgpio_softc *sacgpio_sc;

int
sacgpio_match(struct device *parent, void *cf, void *aux)
{
	struct sacc_attach_args *saa = aux;

	if (saa->sac_typecookie == SACC_TYPE_GPIO && sacgpio_sc == NULL)
		return(1);

	return(0);
}

void
sacgpio_attach(struct device *parent, struct device *self, void *aux)
{
	struct sacgpio_softc *sc = (struct sacgpio_softc*)self;
	struct sacc_attach_args *saa = aux;
	sc->sc_bust = saa->sac_iot;
	sc->sc_bush = saa->sac_bush;

	/*
	 * Nothing to do. Just register default sc and return.
	 */
	printf(": SA-1111 GPIO controller\n");

	sacgpio_sc = sc;
}

/*
 * Read value of port x.
 */
int
sa1111_gpio_get_bit(int gpiobank, uint8_t gpio)
{
	struct sacgpio_softc *sc = sacgpio_sc;
	bus_addr_t	reg;

	if (sc == NULL)
		panic("sa1111_gpio_get_bit: sacgpio not configured");

	/* select bank */
	switch (gpiobank) {
		case SACGPIO_BANKA:
			/* block A has only 4 pins */
			if (gpio > 3)
				panic("%s: sa1111_gpio_get_bit: bogus gpio %d in bank A", 
						sc->sc_dev.dv_xname, gpio);
			reg = SACCGPIOA_DVR;
			break;
		case SACGPIO_BANKB:
			/* block B has only 6 pins */
			if (gpio > 5)
				panic("%s: sa1111_gpio_get_bit: bogus gpio %d in bank B",
						sc->sc_dev.dv_xname, gpio);
			reg = SACCGPIOB_DVR;
			break;
		case SACGPIO_BANKC:
			reg = SACCGPIOC_DVR;
			break;
		default:
			panic("%s: sa1111_gpio_get_bit: bogus gpio bank 0x%x", 
					sc->sc_dev.dv_xname, gpiobank);
	}

	return( bus_space_read_4(sc->sc_bust, sc->sc_bush, reg) & (1 << gpio) );
}

/*
 * Set value of port x to 1.
 */
void
sa1111_gpio_set_bit(int gpiobank, uint8_t gpio)
{
	struct sacgpio_softc *sc = sacgpio_sc;
	bus_addr_t	reg;
	uint32_t	oldval;

	if (sc == NULL)
		panic("sa1111_gpio_set_bit: sacgpio not configured");

	/* select bank */
	switch (gpiobank) {
		case SACGPIO_BANKA:
			/* block A has only 4 pins */
			if (gpio > 3)
				panic("%s: sa1111_gpio_set_bit: bogus gpio %d in bank A", 
						sc->sc_dev.dv_xname, gpio);
			reg = SACCGPIOA_DVR;
			break;
		case SACGPIO_BANKB:
			/* block B has only 6 pins */
			if (gpio > 5)
				panic("%s: sa1111_gpio_set_bit: bogus gpio %d in bank B",
						sc->sc_dev.dv_xname, gpio);
			reg = SACCGPIOB_DVR;
			break;
		case SACGPIO_BANKC:
			reg = SACCGPIOC_DVR;
			break;
		default:
			panic("%s: sa1111_gpio_set_bit: bogus gpio bank 0x%x", 
					sc->sc_dev.dv_xname, gpiobank);
	}
	oldval = bus_space_read_4(sc->sc_bust, sc->sc_bush, reg);
	bus_space_write_4(sc->sc_bust, sc->sc_bush, reg, oldval | 1 << gpio);
}

/*
 * Set value of port x to 0.
 */
void
sa1111_gpio_clear_bit(int gpiobank, uint8_t gpio)
{
	struct sacgpio_softc *sc = sacgpio_sc;
	bus_addr_t	reg;
	uint32_t	oldval;

	if (sc == NULL)
		panic("sa1111_gpio_glear_bit: sacgpio not configured");

	/* select bank */
	switch (gpiobank) {
		case SACGPIO_BANKA:
			/* block A has only 4 pins */
			if (gpio > 3)
				panic("%s: sa1111_gpio_clear_bit: bogus gpio %d in bank A", 
						sc->sc_dev.dv_xname, gpio);
			reg = SACCGPIOA_DVR;
			break;
		case SACGPIO_BANKB:
			/* block B has only 6 pins */
			if (gpio > 5)
				panic("%s: sa1111_gpio_clear_bit: bogus gpio %d in bank B",
						sc->sc_dev.dv_xname, gpio);
			reg = SACCGPIOB_DVR;
			break;
		case SACGPIO_BANKC:
			reg = SACCGPIOC_DVR;
			break;
		default:
			panic("%s: sa1111_gpio_clear_sit: bogus gpio bank 0x%x", 
					sc->sc_dev.dv_xname, gpiobank);
	}
	oldval = bus_space_read_4(sc->sc_bust, sc->sc_bush, reg);
	bus_space_write_4(sc->sc_bust, sc->sc_bush, reg, oldval & ~(1 << gpio));
}

/*
 * Set port direction.
 */
void
sa1111_gpio_set_dir(int gpiobank, uint8_t gpio, int dir)
{
	struct sacgpio_softc *sc = sacgpio_sc;
	bus_addr_t	reg;
	uint32_t	olddir;

	if (sc == NULL)
		panic("sa1111_gpio_set_dir: sacgpio not configured");

	/* select bank */
	switch (gpiobank) {
		case SACGPIO_BANKA:
			/* block A has only 4 pins */
			if (gpio > 3)
				panic("%s: sa1111_gpio_set_dir: bogus gpio %d in bank A", 
						sc->sc_dev.dv_xname, gpio);
			reg = SACCGPIOA_DDR;
			break;
		case SACGPIO_BANKB:
			/* block B has only 6 pins */
			if (gpio > 5)
				panic("%s: sa1111_gpio_set_dir: bogus gpio %d in bank B",
						sc->sc_dev.dv_xname, gpio);
			reg = SACCGPIOB_DDR;
			break;
		case SACGPIO_BANKC:
			reg = SACCGPIOC_DDR;
			break;
		default:
			panic("%s: sa1111_gpio_set_dir: bogus gpio bank 0x%x", 
					sc->sc_dev.dv_xname, gpiobank);
	}
	/* direction */
	olddir = bus_space_read_4(sc->sc_bust, sc->sc_bush, reg);
	switch (dir) {
		case SACGPIO_DIR_INPUT:
			olddir |= 1 << gpio;
			break;
		case SACGPIO_DIR_OUTPUT:
			olddir &= ~(1 << gpio);
			break;
		default:
			panic("%s: sa1111_gpio_set_dir: bogus direction 0x%x",
					sc->sc_dev.dv_xname, dir);
	}
	bus_space_write_4(sc->sc_bust, sc->sc_bush, reg, olddir);
}

/*
 * Get port direction.
 */
u_int
sa1111_gpio_get_dir(int gpiobank, uint8_t gpio)
{
	struct sacgpio_softc *sc = sacgpio_sc;
	bus_addr_t	reg;
	int	dir;

	if (sc == NULL)
		panic("sa1111_gpio_get_dir: sacgpio not configured");

	/* select bank */
	switch (gpiobank) {
		case SACGPIO_BANKA:
			/* block A has only 4 pins */
			if (gpio > 3)
				panic("%s: sa1111_gpio_get_dir: bogus gpio %d in bank A", 
						sc->sc_dev.dv_xname, gpio);
			reg = SACCGPIOA_DDR;
			break;
		case SACGPIO_BANKB:
			/* block B has only 6 pins */
			if (gpio > 5)
				panic("%s: sa1111_gpio_get_dir: bogus gpio %d in bank B",
						sc->sc_dev.dv_xname, gpio);
			reg = SACCGPIOB_DDR;
			break;
		case SACGPIO_BANKC:
			reg = SACCGPIOC_DDR;
			break;
		default:
			panic("%s: sa1111_gpio_get_dir: bogus gpio bank 0x%x", 
					sc->sc_dev.dv_xname, gpiobank);
	}

	dir = bus_space_read_4(sc->sc_bust, sc->sc_bush, reg) & ~(1 << gpio);

	return (dir ? SACGPIO_DIR_INPUT : SACGPIO_DIR_OUTPUT);
}