NetBSD/sys/arch/zaurus/dev/zssp.c
2006-12-16 05:17:58 +00:00

408 lines
10 KiB
C

/* $OpenBSD: zaurus_ssp.c,v 1.6 2005/04/08 21:58:49 uwe Exp $ */
/*
* Copyright (c) 2005 Uwe Stuehler <uwe@bsdx.de>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: zssp.c,v 1.1 2006/12/16 05:23:49 ober Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <machine/bus.h>
#include <arm/xscale/pxa2x0reg.h>
#include <arm/xscale/pxa2x0var.h>
#include <arm/xscale/pxa2x0_gpio.h>
#include <zaurus/dev/zsspvar.h>
#include <zaurus/zaurus/zaurus_var.h>
#include "ioconf.h"
#define GPIO_ADS7846_CS_C3000 14 /* SSP SFRM */
#define GPIO_MAX1111_CS_C3000 20
#define GPIO_TG_CS_C3000 53
#define SSCR0_ADS7846_C3000 0x06ab
#define SSCR0_LZ9JG18 0x01ab
#define SSCR0_MAX1111 0x0387
struct zssp_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
};
static int zssp_match(struct device *, struct cfdata *, void *);
static void zssp_attach(struct device *, struct device *, void *);
static void zssp_init(void);
static void zssp_powerhook(int, void *);
CFATTACH_DECL(zssp, sizeof(struct zssp_softc),
zssp_match, zssp_attach, NULL, NULL);
static int
zssp_match(struct device *parent, struct cfdata *cf, void *aux)
{
return 1;
}
static void
zssp_attach(struct device *parent, struct device *self, void *aux)
{
struct zssp_softc *sc = (struct zssp_softc *)self;
sc->sc_iot = &pxa2x0_bs_tag;
if (bus_space_map(sc->sc_iot, PXA2X0_SSP1_BASE, PXA2X0_SSP_SIZE,
0, &sc->sc_ioh)) {
printf(": can't map bus space\n");
return;
}
printf("\n");
if (powerhook_establish(sc->sc_dev.dv_xname, zssp_powerhook, sc)
== NULL) {
printf("%s: can't establish power hook\n",
sc->sc_dev.dv_xname);
}
zssp_init();
}
/*
* Initialize the dedicated SSP unit and disable all chip selects.
* This function is called with interrupts disabled.
*/
static void
zssp_init(void)
{
struct zssp_softc *sc;
KASSERT(zssp_cd.cd_ndevs > 0 && zssp_cd.cd_devs[0] != NULL);
sc = (struct zssp_softc *)zssp_cd.cd_devs[0];
pxa2x0_clkman_config(CKEN_SSP, 1);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSCR0, SSCR0_LZ9JG18);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSCR1, 0);
pxa2x0_gpio_set_function(GPIO_ADS7846_CS_C3000, GPIO_OUT|GPIO_SET);
pxa2x0_gpio_set_function(GPIO_MAX1111_CS_C3000, GPIO_OUT|GPIO_SET);
pxa2x0_gpio_set_function(GPIO_TG_CS_C3000, GPIO_OUT|GPIO_SET);
}
static void
zssp_powerhook(int why, void *arg)
{
int s;
if (why == PWR_RESUME) {
s = splhigh();
zssp_init();
splx(s);
}
}
/*
* Transmit a single data word to one of the ICs, keep the chip selected
* afterwards, and don't wait for data to be returned in SSDR. Interrupts
* must be held off until zssp_ic_stop() gets called.
*/
void
zssp_ic_start(int ic, uint32_t data)
{
struct zssp_softc *sc;
KASSERT(zssp_cd.cd_ndevs > 0 && zssp_cd.cd_devs[0] != NULL);
sc = (struct zssp_softc *)zssp_cd.cd_devs[0];
/* disable other ICs */
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSCR0, 0);
if (ic != ZSSP_IC_ADS7846)
pxa2x0_gpio_set_bit(GPIO_ADS7846_CS_C3000);
if (ic != ZSSP_IC_LZ9JG18)
pxa2x0_gpio_set_bit(GPIO_TG_CS_C3000);
if (ic != ZSSP_IC_MAX1111)
pxa2x0_gpio_set_bit(GPIO_MAX1111_CS_C3000);
/* activate the chosen one */
switch (ic) {
case ZSSP_IC_ADS7846:
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSCR0,
SSCR0_ADS7846_C3000);
pxa2x0_gpio_clear_bit(GPIO_ADS7846_CS_C3000);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR, data);
while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR)
& SSSR_TNF) != SSSR_TNF)
continue; /* poll */
break;
case ZSSP_IC_LZ9JG18:
pxa2x0_gpio_clear_bit(GPIO_TG_CS_C3000);
break;
case ZSSP_IC_MAX1111:
pxa2x0_gpio_clear_bit(GPIO_MAX1111_CS_C3000);
break;
}
}
/*
* Read the last value from SSDR and deactivate all chip-selects.
*/
uint32_t
zssp_ic_stop(int ic)
{
struct zssp_softc *sc;
uint32_t rv;
KASSERT(zssp_cd.cd_ndevs > 0 && zssp_cd.cd_devs[0] != NULL);
sc = (struct zssp_softc *)zssp_cd.cd_devs[0];
switch (ic) {
case ZSSP_IC_ADS7846:
/* read result of last command */
while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR)
& SSSR_RNE) != SSSR_RNE)
continue; /* poll */
rv = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR);
break;
case ZSSP_IC_LZ9JG18:
case ZSSP_IC_MAX1111:
/* last value received is irrelevant or undefined */
default:
rv = 0;
break;
}
pxa2x0_gpio_set_bit(GPIO_ADS7846_CS_C3000);
pxa2x0_gpio_set_bit(GPIO_TG_CS_C3000);
pxa2x0_gpio_set_bit(GPIO_MAX1111_CS_C3000);
return rv;
}
/*
* Activate one of the chip-select lines, transmit one word value in
* each direction, and deactivate the chip-select again.
*/
uint32_t
zssp_ic_send(int ic, uint32_t data)
{
switch (ic) {
case ZSSP_IC_MAX1111:
return (zssp_read_max1111(data));
case ZSSP_IC_ADS7846:
return (zssp_read_ads7846(data));
case ZSSP_IC_LZ9JG18:
zssp_write_lz9jg18(data);
return 0;
default:
printf("zssp_ic_send: invalid IC %d\n", ic);
return 0;
}
}
int
zssp_read_max1111(uint32_t cmd)
{
struct zssp_softc *sc;
int voltage[2];
int i;
int s;
KASSERT(zssp_cd.cd_ndevs > 0 && zssp_cd.cd_devs[0] != NULL);
sc = (struct zssp_softc *)zssp_cd.cd_devs[0];
s = splhigh();
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSCR0, 0);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSCR0, SSCR0_MAX1111);
pxa2x0_gpio_set_bit(GPIO_TG_CS_C3000);
pxa2x0_gpio_set_bit(GPIO_ADS7846_CS_C3000);
pxa2x0_gpio_clear_bit(GPIO_MAX1111_CS_C3000);
delay(1);
/* Send the command word and read a dummy word back. */
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR, cmd);
while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR)
& SSSR_TNF) != SSSR_TNF)
continue; /* poll */
/* XXX is this delay necessary? */
delay(1);
while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR)
& SSSR_RNE) != SSSR_RNE)
continue; /* poll */
(void) bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR);
for (i = 0; i < 2; i++) {
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR, 0);
while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR)
& SSSR_TNF) != SSSR_TNF)
continue; /* poll */
/* XXX again, is this delay necessary? */
delay(1);
while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR)
& SSSR_RNE) != SSSR_RNE)
continue; /* poll */
voltage[i] = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
SSP_SSDR);
}
pxa2x0_gpio_set_bit(GPIO_TG_CS_C3000);
pxa2x0_gpio_set_bit(GPIO_ADS7846_CS_C3000);
pxa2x0_gpio_set_bit(GPIO_MAX1111_CS_C3000);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSCR0, 0);
splx(s);
/* XXX no idea what this means, but it's what Linux would do. */
if ((voltage[0] & 0xc0) != 0 || (voltage[1] & 0x3f) != 0) {
voltage[0] = -1;
} else {
voltage[0] = ((voltage[0] << 2) & 0xfc) |
((voltage[1] >> 6) & 0x03);
}
return voltage[0];
}
/* XXX - only does CS_ADS7846 */
uint32_t
zssp_read_ads7846(uint32_t cmd)
{
struct zssp_softc *sc;
sc = (struct zssp_softc *)zssp_cd.cd_devs[0];
unsigned int cr0;
int s;
uint32_t val;
if (zssp_cd.cd_ndevs < 1 || zssp_cd.cd_devs[0] == NULL) {
printf("zssp_read_ads7846: not configured\n");
return 0;
}
sc = (struct zssp_softc *)zssp_cd.cd_devs[0];
s = splhigh();
if (1) {
cr0 = SSCR0_ADS7846_C3000;
} else {
cr0 = 0x00ab;
}
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSCR0, 0);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSCR0, cr0);
pxa2x0_gpio_set_bit(GPIO_TG_CS_C3000);
pxa2x0_gpio_set_bit(GPIO_MAX1111_CS_C3000);
pxa2x0_gpio_clear_bit(GPIO_ADS7846_CS_C3000);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR, cmd);
while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR)
& SSSR_TNF) != SSSR_TNF)
continue; /* poll */
delay(1);
while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR)
& SSSR_RNE) != SSSR_RNE)
continue; /* poll */
val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR);
pxa2x0_gpio_set_bit(GPIO_ADS7846_CS_C3000); /* deselect */
splx(s);
return val;
}
void
zssp_write_lz9jg18(uint32_t data)
{
int s;
int sclk_pin, sclk_fn;
int sfrm_pin, sfrm_fn;
int txd_pin, txd_fn;
int rxd_pin, rxd_fn;
int i;
/* XXX this creates a DAC command from a backlight duty value. */
data = 0x40 | (data & 0x1f);
if (ZAURUS_ISC3000) {
/* C3000 */
sclk_pin = 19;
sfrm_pin = 14;
txd_pin = 87;
rxd_pin = 86;
} else {
sclk_pin = 23;
sfrm_pin = 24;
txd_pin = 25;
rxd_pin = 26;
}
s = splhigh();
sclk_fn = pxa2x0_gpio_get_function(sclk_pin);
sfrm_fn = pxa2x0_gpio_get_function(sfrm_pin);
txd_fn = pxa2x0_gpio_get_function(txd_pin);
rxd_fn = pxa2x0_gpio_get_function(rxd_pin);
pxa2x0_gpio_set_function(sfrm_pin, GPIO_OUT | GPIO_SET);
pxa2x0_gpio_set_function(sclk_pin, GPIO_OUT | GPIO_CLR);
pxa2x0_gpio_set_function(txd_pin, GPIO_OUT | GPIO_CLR);
pxa2x0_gpio_set_function(rxd_pin, GPIO_IN);
pxa2x0_gpio_set_bit(GPIO_MAX1111_CS_C3000);
pxa2x0_gpio_set_bit(GPIO_ADS7846_CS_C3000);
pxa2x0_gpio_clear_bit(GPIO_TG_CS_C3000);
delay(10);
for (i = 0; i < 8; i++) {
if (data & 0x80)
pxa2x0_gpio_set_bit(txd_pin);
else
pxa2x0_gpio_clear_bit(txd_pin);
delay(10);
pxa2x0_gpio_set_bit(sclk_pin);
delay(10);
pxa2x0_gpio_clear_bit(sclk_pin);
delay(10);
data <<= 1;
}
pxa2x0_gpio_clear_bit(txd_pin);
pxa2x0_gpio_set_bit(GPIO_TG_CS_C3000);
pxa2x0_gpio_set_function(sclk_pin, sclk_fn);
pxa2x0_gpio_set_function(sfrm_pin, sfrm_fn);
pxa2x0_gpio_set_function(txd_pin, txd_fn);
pxa2x0_gpio_set_function(rxd_pin, rxd_fn);
splx(s);
}