File: [local] / sys / arch / arm / sa11x0 / sa11x0_ssp.c (download)
Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:05:17 2008 UTC (16 years, 4 months ago) by nbrk
Branch: OPENBSD_4_2_BASE, MAIN
CVS Tags: jornada-partial-support-wip, HEAD Changes since 1.1: +0 -0 lines
Import of OpenBSD 4.2 release kernel tree with initial code to support
Jornada 720/728, StrongARM 1110-based handheld PC.
At this point kernel roots on NFS and boots into vfs_mountroot() and traps.
What is supported:
- glass console, Jornada framebuffer (jfb) works in 16bpp direct color mode
(needs some palette tweaks for non black/white/blue colors, i think)
- saic, SA11x0 interrupt controller (needs cleanup)
- sacom, SA11x0 UART (supported only as boot console for now)
- SA11x0 GPIO controller fully supported (but can't handle multiple interrupt
handlers on one gpio pin)
- sassp, SSP port on SA11x0 that attaches spibus
- Jornada microcontroller (jmcu) to control kbd, battery, etc throught
the SPI bus (wskbd attaches on jmcu, but not tested)
- tod functions seem work
- initial code for SA-1111 (chip companion) : this is TODO
Next important steps, i think:
- gpio and intc on sa1111
- pcmcia support for sa11x0 (and sa1111 help logic)
- REAL root on nfs when we have PCMCIA support (we may use any of supported pccard NICs)
- root on wd0! (using already supported PCMCIA-ATA)
|
/* $Id: sa11x0_ssp.c,v 1.1.1.1 2008/03/04 16:05:17 nbrk Exp $ */
/*
* StrongARM 11[01]0 Synchronous Serial Port.
* Actually it can operate using different serial protocols,
* but we are interested in Motorola SPI for now.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/endian.h>
#include <dev/spivar.h>
#include <machine/bus.h>
#include <arm/sa11x0/sa11x0_reg.h>
#include <arm/sa11x0/sa11x0_var.h>
#include <arm/sa11x0/sa11x0_gpioreg.h>
#include <arm/sa11x0/sa11x0_sspreg.h>
#define INVERTBITS(x) \
do { \
(x) = ((((x) & 0xf0) >> 4) | (((x) & 0x0f) << 4)); \
(x) = ((((x) & 0xcc) >> 2) | (((x) & 0x33) << 2)); \
(x) = ((((x) & 0xaa) >> 1) | (((x) & 0x55) << 1)); \
} while (0)
struct sassp_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
/* XXX ioh for gpioc */
bus_space_handle_t sc_gpioch;
struct spi_bus sc_bus;
int sc_active;
};
/*
* Prototypes.
*/
int sassp_match(struct device *parent, void *cf, void *aux);
void sassp_attach(struct device *parent, struct device *self, void *aux);
int sassp_shift_1(void *arg, uint8_t data);
#if 0
uint16_t sassp_shift_2(void *arg, uint16_t data);
uint32_t sassp_shift_4(void *arg, uint32_t data);
#endif
int sassp_acquire(void *arg);
void sassp_release(void *arg);
/*
* Autoconf glue.
*/
struct cfattach sassp_ca = {
sizeof(struct sassp_softc), sassp_match, sassp_attach, NULL, NULL
};
struct cfdriver sassp_cd = {
NULL,
"sassp",
DV_DULL
};
int
sassp_match(struct device *parent, void *cf, void *aux)
{
struct saip_attach_args *saa = aux;
if (saa->sai_addr == SASSP_BASE)
return (1);
return (0);
}
void
sassp_attach(struct device *parent, struct device *self, void *aux)
{
struct sassp_softc *sc = (struct sassp_softc *)self;
struct saip_attach_args *saa = aux;
struct spibus_attach_args sba;
sc->sc_iot = saa->sai_iot;
if (bus_space_map(sc->sc_iot, saa->sai_addr, SASSP_NPORTS, 0, &sc->sc_ioh)) {
printf(": can't map i/o space\n");
return;
}
if (bus_space_map(sc->sc_iot, SAGPIO_BASE, SAGPIO_NPORTS, 0, &sc->sc_gpioch)) {
printf(": can't map gpio i/o space\n");
return;
}
printf(": SA-11x0 SSP/MCP txfifo %d rxfifo %d\n", SASSP_TXFIFOLEN, SASSP_RXFIFOLEN);
#if 0
/* Force into SPI @460800 */
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SASSP_CR0, 0x07 /* DSS */ | CR0_SSE | 0x03 /* SCR */ );
/* Read back rate */
sc->sc_bus.bus_speed = 3686400 /
(2 *( (bus_space_read_4(sc->sc_iot, sc->sc_ioh, SASSP_CR0) & CR0_SCR_MASK) + 1 ));
/* Set polarity and phase */
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SASSP_CR1, CR1_SPO | CR1_SPH);
printf("%s: configured to SPI mode at speed %ld\n", sc->sc_dev.dv_xname, sc->sc_bus.bus_speed);
#endif
/*
* Attach SPI bus.
*/
sc->sc_bus.bus_cookie = sc;
sc->sc_bus.bus_shift_1 = sassp_shift_1;
sc->sc_bus.bus_acquire = sassp_acquire;
sc->sc_bus.bus_release = sassp_release;
/* attach args */
sba.sba_bus = &sc->sc_bus;
config_found(&sc->sc_dev, &sba, spibus_print);
return;
}
int
sassp_shift_1(void *arg, uint8_t data)
{
/*
* Shift a byte on a SPI bus.
*/
struct sassp_softc *sc = arg;
uint32_t timeo;
/*
* Caller should already bother about locks..
*/
/* wait for TX fifo to drain a bit if it's full */
while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, SASSP_SR) & SR_TNF) == 0)
;
/* wait for low on GPIO 10 */
timeo = 400000;
while (timeo--) {
if ( (bus_space_read_4(sc->sc_iot, sc->sc_gpioch, SAGPIO_PLR) & (1 << 10)) == 0)
break;
if (timeo == 0) {
printf("%s: SSP tx timeout\n", sc->sc_dev.dv_xname);
return(-1);
}
}
/* invert data before sending */
INVERTBITS(data);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SASSP_DR, (uint16_t)(data << 8));
/* wait for RX fifo become not empty */
timeo = 400000;
while ( ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, SASSP_SR) & SR_RNE) == 0) && timeo--)
;
if (timeo == 0)
printf("%s: WARNING, rx fifo empty after timeout\n", sc->sc_dev.dv_xname);
/* read bottom of rx fifo */
data = (uint8_t)bus_space_read_4(sc->sc_iot, sc->sc_ioh, SASSP_DR);
/* invert received data */
INVERTBITS(data);
return(data);
}
int
sassp_acquire(void *arg)
{
/*
* Wake up slave by setting GPIO 25 low.
*/
struct sassp_softc *sc = arg;
#if 0
uint32_t plmask;
#endif
/* check if this bus is already acquired */
if (sc->sc_active) {
printf("%s: WARNING, trying to acquire locked bus\n", sc->sc_dev.dv_xname);
return(1);
}
#if 0
/*
* Modify Pin Level register.
*/
plmask = bus_space_read_4(sc->sc_iot, sc->sc_gpioch, 0x0c /* XXX SAGPIO_PCR */);
plmask |= 1 << 25;
bus_space_write_4(sc->sc_iot, sc->sc_gpioch, 0x0c, plmask);
#endif
sc->sc_active = 1;
return(0);
}
void
sassp_release(void *arg)
{
/*
* Release bus by bringing GPIO 25 back to high.
*/
struct sassp_softc *sc = arg;
#if 0
uint32_t plmask;
plmask = bus_space_read_4(sc->sc_iot, sc->sc_gpioch, 0x08 /* XXX SAGPIO_PSR */);
plmask |= (1 << 25);
bus_space_write_4(sc->sc_iot, sc->sc_gpioch, 0x08, plmask);
#endif
sc->sc_active = 0;
}