/*      $NetBSD: zssp.c,v 1.15 2021/08/07 16:19:08 thorpej Exp $        */
/*      $OpenBSD: zaurus_ssp.c,v 1.6 2005/04/08 21:58:49 uwe Exp $      */

/*
* Copyright (c) 2005 Uwe Stuehler <[email protected]>
*
* 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.15 2021/08/07 16:19:08 thorpej Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/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>

#define GPIO_ADS7846_CS_C3000   14      /* SSP SFRM */
#define GPIO_MAX1111_CS_C3000   20
#define GPIO_TG_CS_C3000        53
#define GPIO_ADS7846_CS_C860    24      /* SSP SFRM */
#define GPIO_MAX1111_CS_C860    20
#define GPIO_TG_CS_C860         19

#define SSCR0_ADS7846_C3000     0x06ab /* 12bit/Microwire/div by 7 */
#define SSCR0_MAX1111           0x0387
#define SSCR0_LZ9JG18           0x01ab
#define SSCR0_ADS7846_C860      0x00ab /* 12bit/Microwire/div by 7 */

struct zssp_ads7846 {
       u_int gpio;
       uint32_t sscr0;
};
struct zssp_max1111 {
       u_int gpio;
       uint32_t sscr0;
};
struct zssp_lz9jg18 {
       u_int gpio;
       uint32_t sscr0;
       int sclk_pin;
       int sfrm_pin;
       int txd_pin;
       int rxd_pin;
};

struct zssp_softc {
       device_t sc_dev;
       bus_space_tag_t sc_iot;
       bus_space_handle_t sc_ioh;
       bus_addr_t sc_ssp;
       struct zssp_ads7846 ads7846;
       struct zssp_max1111 max1111;
       struct zssp_lz9jg18 lz9jg18;
};

static int      zssp_match(device_t, cfdata_t, void *);
static void     zssp_attach(device_t, device_t, void *);
static int      zssp_search(device_t, cfdata_t, const int *, void *);
static int      zssp_print(void *, const char *);

CFATTACH_DECL_NEW(zssp, sizeof(struct zssp_softc),
       zssp_match, zssp_attach, NULL, NULL);

static void     zssp_init(void);
static bool     zssp_resume(device_t dv, const pmf_qual_t *);

static struct zssp_softc *zssp_sc;

static int
zssp_match(device_t parent, cfdata_t cf, void *aux)
{

       if (zssp_sc != NULL)
               return 0;
       return 1;
}

static void
zssp_attach(device_t parent, device_t self, void *aux)
{
       struct zssp_softc *sc = device_private(self);

       sc->sc_dev = self;
       zssp_sc = sc;

       aprint_normal("\n");
       aprint_naive("\n");

       sc->sc_iot = &pxa2x0_bs_tag;
       if (ZAURUS_ISC1000 || ZAURUS_ISC3000) {
               sc->sc_ssp = PXA2X0_SSP1_BASE;
               sc->ads7846.gpio     = GPIO_ADS7846_CS_C3000;
               sc->ads7846.sscr0    = SSCR0_ADS7846_C3000;
               sc->max1111.gpio     = GPIO_MAX1111_CS_C3000;
               sc->max1111.sscr0    = SSCR0_MAX1111;
               sc->lz9jg18.gpio     = GPIO_TG_CS_C3000;
               sc->lz9jg18.sscr0    = SSCR0_LZ9JG18;
               sc->lz9jg18.sclk_pin = 19;
               sc->lz9jg18.sfrm_pin = 14;
               sc->lz9jg18.txd_pin  = 87;
               sc->lz9jg18.rxd_pin  = 86;
       } else {
               sc->sc_ssp = PXA2X0_SSP_BASE;
               sc->ads7846.gpio     = GPIO_ADS7846_CS_C860;
               sc->ads7846.sscr0    = SSCR0_ADS7846_C860;
               sc->max1111.gpio     = GPIO_MAX1111_CS_C860;
               sc->max1111.sscr0    = SSCR0_MAX1111;
               sc->lz9jg18.gpio     = GPIO_TG_CS_C860;
               sc->lz9jg18.sscr0    = SSCR0_LZ9JG18;
               sc->lz9jg18.sclk_pin = 23;
               sc->lz9jg18.sfrm_pin = 24;
               sc->lz9jg18.txd_pin  = 25;
               sc->lz9jg18.rxd_pin  = 26;
       }

       if (bus_space_map(sc->sc_iot, sc->sc_ssp, PXA2X0_SSP_SIZE,
           0, &sc->sc_ioh)) {
               aprint_error_dev(sc->sc_dev, "can't map bus space\n");
               return;
       }

       if (!pmf_device_register(sc->sc_dev, NULL, zssp_resume))
               aprint_error_dev(sc->sc_dev,
                   "couldn't establish power handler\n");

       zssp_init();

       /* Attach all devices */
       config_search(self, NULL,
           CFARGS(.search = zssp_search));
}

static int
zssp_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
{
       struct zssp_attach_args aa;

       aa.zaa_name = cf->cf_name;

       if (config_probe(parent, cf, &aa))
               config_attach(parent, cf, &aa, zssp_print, CFARGS_NONE);

       return 0;
}

static int
zssp_print(void *aux, const char *name)
{

       return UNCONF;
}

/*
* 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;

       if (__predict_false(zssp_sc == NULL)) {
               aprint_error("%s: not configured.\n", __func__);
               return;
       }
       sc = zssp_sc;

       pxa2x0_clkman_config(CKEN_SSP, 1);

       bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSCR0, sc->lz9jg18.sscr0);
       bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSCR1, 0);

       pxa2x0_gpio_set_function(sc->ads7846.gpio, GPIO_OUT|GPIO_SET);
       pxa2x0_gpio_set_function(sc->max1111.gpio, GPIO_OUT|GPIO_SET);
       pxa2x0_gpio_set_function(sc->lz9jg18.gpio, GPIO_OUT|GPIO_SET);
}

static bool
zssp_resume(device_t dv, const pmf_qual_t *qual)
{
       int s;

       s = splhigh();
       zssp_init();
       splx(s);

       return true;
}

/*
* 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;

       if (__predict_false(zssp_sc == NULL)) {
               aprint_error("%s: not configured.\n", __func__);
               return;
       }
       sc = zssp_sc;

       /* 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(sc->ads7846.gpio);
       if (ic != ZSSP_IC_LZ9JG18)
               pxa2x0_gpio_set_bit(sc->lz9jg18.gpio);
       if (ic != ZSSP_IC_MAX1111)
               pxa2x0_gpio_set_bit(sc->max1111.gpio);

       /* activate the chosen one */
       switch (ic) {
       case ZSSP_IC_ADS7846:
               bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSCR0,
                   sc->ads7846.sscr0);
               pxa2x0_gpio_clear_bit(sc->ads7846.gpio);
               delay(1);       /* ADS7846 Tcss = 100ns */
               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(sc->lz9jg18.gpio);
               break;
       case ZSSP_IC_MAX1111:
               pxa2x0_gpio_clear_bit(sc->max1111.gpio);
               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;

       if (__predict_false(zssp_sc == NULL)) {
               aprint_error("%s: not configured.\n", __func__);
               return 0;
       }
       sc = zssp_sc;

       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(sc->ads7846.gpio);
       pxa2x0_gpio_set_bit(sc->lz9jg18.gpio);
       pxa2x0_gpio_set_bit(sc->max1111.gpio);

       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:
               aprint_error("zssp: zssp_ic_send: invalid IC %d\n", ic);
               return 0;
       }
}

int
zssp_read_max1111(uint32_t cmd)
{
       struct zssp_softc *sc;
       int data[3];
       int voltage[3]; /* voltage[0]: dummy */
       int i;
       int s;

       if (__predict_false(zssp_sc == NULL)) {
               aprint_error("%s: not configured.\n", __func__);
               return 0;
       }
       sc = zssp_sc;

       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, sc->max1111.sscr0);

       pxa2x0_gpio_set_bit(sc->lz9jg18.gpio);
       pxa2x0_gpio_set_bit(sc->ads7846.gpio);
       pxa2x0_gpio_clear_bit(sc->max1111.gpio);

       delay(1);

       memset(data, 0, sizeof(data));
       data[0] = cmd;
       for (i = 0; i < __arraycount(data); i++) {
               bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR, data[i]);
               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 */
               voltage[i] = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
                   SSP_SSDR);
       }

       pxa2x0_gpio_set_bit(sc->lz9jg18.gpio);
       pxa2x0_gpio_set_bit(sc->ads7846.gpio);
       pxa2x0_gpio_set_bit(sc->max1111.gpio);

       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[1] & 0xc0) != 0 || (voltage[2] & 0x3f) != 0)
               return -1;
       return ((voltage[1] << 2) & 0xfc) | ((voltage[2] >> 6) & 0x03);
}

/* XXX - only does CS_ADS7846 */
uint32_t
zssp_read_ads7846(uint32_t cmd)
{
       struct zssp_softc *sc;
       uint32_t val;
       int s;

       if (__predict_false(zssp_sc == NULL)) {
               aprint_error("%s: not configured\n", __func__);
               return 0;
       }
       sc = zssp_sc;

       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, sc->ads7846.sscr0);

       pxa2x0_gpio_set_bit(sc->lz9jg18.gpio);
       pxa2x0_gpio_set_bit(sc->max1111.gpio);
       pxa2x0_gpio_clear_bit(sc->ads7846.gpio);
       delay(1);       /* ADS7846 Tcss = 100ns */

       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(sc->ads7846.gpio);

       splx(s);

       return val;
}

void
zssp_write_lz9jg18(uint32_t data)
{
       struct zssp_softc *sc;
       int sclk_fn;
       int sfrm_fn;
       int txd_fn;
       int rxd_fn;
       int i;
       int s;

       KASSERT(zssp_sc != NULL);
       sc = zssp_sc;

       /* XXX this creates a DAC command from a backlight duty value. */
       data = 0x40 | (data & 0x1f);

       s = splhigh();

       sclk_fn = pxa2x0_gpio_get_function(sc->lz9jg18.sclk_pin);
       sfrm_fn = pxa2x0_gpio_get_function(sc->lz9jg18.sfrm_pin);
       txd_fn = pxa2x0_gpio_get_function(sc->lz9jg18.txd_pin);
       rxd_fn = pxa2x0_gpio_get_function(sc->lz9jg18.rxd_pin);

       pxa2x0_gpio_set_function(sc->lz9jg18.sfrm_pin, GPIO_OUT | GPIO_SET);
       pxa2x0_gpio_set_function(sc->lz9jg18.sclk_pin, GPIO_OUT | GPIO_CLR);
       pxa2x0_gpio_set_function(sc->lz9jg18.txd_pin, GPIO_OUT | GPIO_CLR);
       pxa2x0_gpio_set_function(sc->lz9jg18.rxd_pin, GPIO_IN);

       pxa2x0_gpio_set_bit(sc->max1111.gpio);
       pxa2x0_gpio_set_bit(sc->ads7846.gpio);
       pxa2x0_gpio_clear_bit(sc->lz9jg18.gpio);

       delay(10);

       for (i = 0; i < 8; i++) {
               if (data & 0x80)
                       pxa2x0_gpio_set_bit(sc->lz9jg18.txd_pin);
               else
                       pxa2x0_gpio_clear_bit(sc->lz9jg18.txd_pin);
               delay(10);
               pxa2x0_gpio_set_bit(sc->lz9jg18.sclk_pin);
               delay(10);
               pxa2x0_gpio_clear_bit(sc->lz9jg18.sclk_pin);
               delay(10);
               data <<= 1;
       }

       pxa2x0_gpio_clear_bit(sc->lz9jg18.txd_pin);
       pxa2x0_gpio_set_bit(sc->lz9jg18.gpio);

       pxa2x0_gpio_set_function(sc->lz9jg18.sclk_pin, sclk_fn);
       pxa2x0_gpio_set_function(sc->lz9jg18.sfrm_pin, sfrm_fn);
       pxa2x0_gpio_set_function(sc->lz9jg18.txd_pin, txd_fn);
       pxa2x0_gpio_set_function(sc->lz9jg18.rxd_pin, rxd_fn);

       splx(s);
}