/* $NetBSD: wzero3_ssp.c,v 1.5 2012/01/21 19:44:29 nonaka Exp $ */
/*-
* Copyright (C) 2010 NONAKA Kimihiro <nonaka@netbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: wzero3_ssp.c,v 1.5 2012/01/21 19:44:29 nonaka Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/mutex.h>
#include <sys/pmf.h>
#include <sys/bus.h>
#include <machine/bootinfo.h>
#include <machine/platid.h>
#include <machine/platid_mask.h>
#include <arm/xscale/pxa2x0reg.h>
#include <arm/xscale/pxa2x0var.h>
#include <arm/xscale/pxa2x0_gpio.h>
#include <hpcarm/dev/wzero3_reg.h>
#include <hpcarm/dev/wzero3_sspvar.h>
#define WS003SH_SSCR0_MAX1233 0x0000048f /* 16bit/SPI/div by 5 */
#define WS007SH_SSCR0_ADS7846 0x000006ab /* 12bit/Microwire/div by 7 */
#define WS011SH_SSCR0_AK4184_TP 0x0010068f /* 32bit/SPI/div by 7 */
#define WS011SH_SSCR0_AK4184_KEYPAD 0x0000068f /* 16bit/SPI/div by 7 */
struct wzero3ssp_model;
struct wzero3ssp_softc {
device_t sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
kmutex_t sc_mtx;
const struct wzero3ssp_model *sc_model;
};
static int wzero3ssp_match(device_t, cfdata_t, void *);
static void wzero3ssp_attach(device_t, device_t, void *);
CFATTACH_DECL_NEW(wzero3ssp, sizeof(struct wzero3ssp_softc),
wzero3ssp_match, wzero3ssp_attach, NULL, NULL);
static void wzero3ssp_init(struct wzero3ssp_softc *);
static bool wzero3ssp_resume(device_t dv, const pmf_qual_t *);
static uint32_t wzero3ssp_read_ads7846(struct wzero3ssp_softc *, uint32_t);
static uint32_t wzero3ssp_read_max1233(struct wzero3ssp_softc *, uint32_t,
uint32_t);
static uint32_t wzero3ssp_read_ak4184_tp(struct wzero3ssp_softc *, uint32_t);
static uint16_t wzero3ssp_read_ak4184_keypad(struct wzero3ssp_softc *, uint32_t,
uint32_t);
static struct wzero3ssp_softc *wzero3ssp_sc;
static const struct wzero3ssp_model {
platid_mask_t *platid;
u_long sspaddr;
} wzero3ssp_table[] = {
/* WS003SH */
{
&platid_mask_MACH_SHARP_WZERO3_WS003SH,
PXA2X0_SSP2_BASE,
},
/* WS004SH */
{
&platid_mask_MACH_SHARP_WZERO3_WS004SH,
PXA2X0_SSP2_BASE,
},
/* WS007SH */
{
&platid_mask_MACH_SHARP_WZERO3_WS007SH,
PXA2X0_SSP1_BASE,
},
/* WS011SH */
{
&platid_mask_MACH_SHARP_WZERO3_WS011SH,
PXA2X0_SSP1_BASE,
},
#if 0
/* WS0020H */
{
&platid_mask_MACH_SHARP_WZERO3_WS020SH,
PXA2X0_SSP1_BASE,
},
#endif
{
NULL, 0,
},
};
static const struct wzero3ssp_model *
wzero3ssp_lookup(void)
{
const struct wzero3ssp_model *model;
for (model = wzero3ssp_table; model->platid != NULL; model++) {
if (platid_match(&platid, model->platid)) {
return model;
}
}
return NULL;
}
static int
wzero3ssp_match(device_t parent, cfdata_t cf, void *aux)
{
if (strcmp(cf->cf_name, "wzero3ssp") != 0)
return 0;
if (wzero3ssp_lookup() == NULL)
return 0;
if (wzero3ssp_sc != NULL)
return 0;
return 1;
}
static void
wzero3ssp_attach(device_t parent, device_t self, void *aux)
{
struct wzero3ssp_softc *sc = device_private(self);
sc->sc_dev = self;
wzero3ssp_sc = sc;
aprint_normal("\n");
aprint_naive("\n");
sc->sc_model = wzero3ssp_lookup();
if (sc->sc_model == NULL) {
aprint_error_dev(self, "unknown model\n");
return;
}
mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_TTY);
sc->sc_iot = &pxa2x0_bs_tag;
if (bus_space_map(sc->sc_iot, sc->sc_model->sspaddr, 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, wzero3ssp_resume))
aprint_error_dev(sc->sc_dev,
"couldn't establish power handler\n");
wzero3ssp_init(sc);
}
/*
* Initialize the dedicated SSP unit and disable all chip selects.
* This function is called with interrupts disabled.
*/
static void
wzero3ssp_init(struct wzero3ssp_softc *sc)
{
if (sc->sc_model->sspaddr == PXA2X0_SSP1_BASE)
pxa2x0_clkman_config(CKEN_SSP2, 1);
else if (sc->sc_model->sspaddr == PXA2X0_SSP2_BASE)
pxa2x0_clkman_config(CKEN_SSP3, 1);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSCR0, 0);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSCR1, 0);
/* XXX */
if (platid_match(&platid, &platid_mask_MACH_SHARP_WZERO3_WS003SH)
|| platid_match(&platid, &platid_mask_MACH_SHARP_WZERO3_WS004SH)) {
pxa2x0_gpio_set_function(39/*GPIO_WS003SH_XXX*/,
GPIO_OUT|GPIO_SET);
pxa2x0_gpio_set_function(GPIO_WS003SH_MAX1233_CS,
GPIO_OUT|GPIO_SET);
}
if (platid_match(&platid, &platid_mask_MACH_SHARP_WZERO3_WS007SH)) {
pxa2x0_gpio_set_function(GPIO_WS007SH_ADS7846_CS,
GPIO_OUT|GPIO_SET);
}
if (platid_match(&platid, &platid_mask_MACH_SHARP_WZERO3_WS011SH)) {
pxa2x0_gpio_set_function(GPIO_WS011SH_AK4184_CS,
GPIO_OUT|GPIO_SET);
}
}
static bool
wzero3ssp_resume(device_t dv, const pmf_qual_t *qual)
{
struct wzero3ssp_softc *sc = device_private(dv);
mutex_enter(&sc->sc_mtx);
wzero3ssp_init(sc);
mutex_exit(&sc->sc_mtx);
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 wzero3ssp_ic_stop() gets called.
*/
void
wzero3ssp_ic_start(int ic, uint32_t cmd)
{
struct wzero3ssp_softc *sc;
KASSERT(wzero3ssp_sc != NULL);
sc = wzero3ssp_sc;
mutex_enter(&sc->sc_mtx);
/* disable other ICs */
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSCR0, 0);
if (platid_match(&platid, &platid_mask_MACH_SHARP_WZERO3_WS007SH)) {
if (ic != WZERO3_SSP_IC_ADS7846)
pxa2x0_gpio_set_bit(GPIO_WS007SH_ADS7846_CS);
}
if (platid_match(&platid, &platid_mask_MACH_SHARP_WZERO3_WS011SH)) {
if (ic != WZERO3_SSP_IC_AK4184_TP
&& ic != WZERO3_SSP_IC_AK4184_KEYPAD)
pxa2x0_gpio_set_bit(GPIO_WS011SH_AK4184_CS);
}
/* activate the chosen one */
switch (ic) {
case WZERO3_SSP_IC_ADS7846:
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,
WS007SH_SSCR0_ADS7846);
pxa2x0_gpio_clear_bit(GPIO_WS007SH_ADS7846_CS);
bus_space_write_1(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 */
break;
case WZERO3_SSP_IC_AK4184_TP:
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,
WS011SH_SSCR0_AK4184_TP);
pxa2x0_gpio_clear_bit(GPIO_WS011SH_AK4184_CS);
(void) bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR);
while (!(bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR)
& SSSR_TNF))
continue; /* poll */
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR, cmd << 16);
while (bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR)
& SSSR_BUSY)
continue; /* poll */
break;
case WZERO3_SSP_IC_MAX1233:
case WZERO3_SSP_IC_AK4184_KEYPAD:
case WZERO3_SSP_IC_NUM:
default:
break;
}
}
/*
* Read the last value from SSDR and deactivate all chip-selects.
*/
uint32_t
wzero3ssp_ic_stop(int ic)
{
struct wzero3ssp_softc *sc;
uint32_t rv;
KASSERT(wzero3ssp_sc != NULL);
sc = wzero3ssp_sc;
switch (ic) {
case WZERO3_SSP_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);
pxa2x0_gpio_set_bit(GPIO_WS007SH_ADS7846_CS);
break;
case WZERO3_SSP_IC_AK4184_TP:
/* 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);
pxa2x0_gpio_set_bit(GPIO_WS011SH_AK4184_CS);
break;
case WZERO3_SSP_IC_MAX1233:
case WZERO3_SSP_IC_AK4184_KEYPAD:
case WZERO3_SSP_IC_NUM:
default:
rv = 0;
break;
}
mutex_exit(&sc->sc_mtx);
return rv;
}
/*
* Activate one of the chip-select lines, transmit one word value in
* each direction, and deactivate the chip-select again.
*/
uint32_t
wzero3ssp_ic_send(int ic, uint32_t data, uint32_t data2)
{
struct wzero3ssp_softc *sc;
if (wzero3ssp_sc == NULL) {
aprint_error("%s: not configured\n", __func__);
return 0;
}
sc = wzero3ssp_sc;
switch (ic) {
case WZERO3_SSP_IC_ADS7846:
return wzero3ssp_read_ads7846(sc, data);
case WZERO3_SSP_IC_MAX1233:
return wzero3ssp_read_max1233(sc, data, data2);
case WZERO3_SSP_IC_AK4184_TP:
return wzero3ssp_read_ak4184_tp(sc, data);
case WZERO3_SSP_IC_AK4184_KEYPAD:
return wzero3ssp_read_ak4184_keypad(sc, data, data2);
case WZERO3_SSP_IC_NUM:
default:
aprint_error("%s: invalid IC %d\n", __func__, ic);
return 0;
}
}
static uint32_t
wzero3ssp_read_ads7846(struct wzero3ssp_softc *sc, uint32_t cmd)
{
uint32_t rv;
mutex_enter(&sc->sc_mtx);
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,
WS007SH_SSCR0_ADS7846);
pxa2x0_gpio_clear_bit(GPIO_WS007SH_ADS7846_CS);
/* send cmd */
while (!(bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR) & SSSR_TNF))
continue; /* poll */
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_BUSY)
continue; /* poll */
while (!(bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR) & SSSR_RNE))
continue; /* poll */
rv = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR);
pxa2x0_gpio_set_bit(GPIO_WS007SH_ADS7846_CS);
mutex_exit(&sc->sc_mtx);
return rv;
}
static uint32_t
wzero3ssp_read_max1233(struct wzero3ssp_softc *sc, uint32_t cmd, uint32_t data)
{
uint32_t rv;
mutex_enter(&sc->sc_mtx);
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,
WS003SH_SSCR0_MAX1233);
pxa2x0_gpio_set_bit(39/*GPIO_WS003SH_XXX*/);
pxa2x0_gpio_clear_bit(GPIO_WS003SH_MAX1233_CS);
/* send cmd */
while (!(bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR) & SSSR_TNF))
continue; /* poll */
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_BUSY)
continue; /* poll */
while (!(bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR) & SSSR_RNE))
continue; /* poll */
(void)bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR);
while (!(bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR) & SSSR_TNF))
continue; /* poll */
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_BUSY)
continue; /* poll */
while (!(bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR) & SSSR_RNE))
continue; /* poll */
rv = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR);
pxa2x0_gpio_set_bit(GPIO_WS003SH_MAX1233_CS);
mutex_exit(&sc->sc_mtx);
return rv;
}
static uint32_t
wzero3ssp_read_ak4184_tp(struct wzero3ssp_softc *sc, uint32_t cmd)
{
uint32_t rv;
mutex_enter(&sc->sc_mtx);
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,
WS011SH_SSCR0_AK4184_TP);
pxa2x0_gpio_clear_bit(GPIO_WS011SH_AK4184_CS);
/* clear rx fifo */
(void) bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR);
/* send cmd */
while (!(bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR) & SSSR_TNF))
continue; /* poll */
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR, cmd << 16);
while (bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR) & SSSR_BUSY)
continue; /* poll */
while (!(bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR) & SSSR_RNE))
continue; /* poll */
rv = bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR);
pxa2x0_gpio_set_bit(GPIO_WS011SH_AK4184_CS);
mutex_exit(&sc->sc_mtx);
return rv;
}
static uint16_t
wzero3ssp_read_ak4184_keypad(struct wzero3ssp_softc *sc, uint32_t cmd,
uint32_t data)
{
uint16_t rv;
mutex_enter(&sc->sc_mtx);
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,
WS011SH_SSCR0_AK4184_KEYPAD);
pxa2x0_gpio_clear_bit(GPIO_WS011SH_AK4184_CS);
/* clear rx fifo */
(void) bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSDR);
/* send cmd */
while (!(bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR) & SSSR_TNF))
continue; /* poll */
bus_space_write_2(sc->sc_iot, sc->sc_ioh, SSP_SSDR, (uint16_t)cmd);
while (bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR) & SSSR_BUSY)
continue; /* poll */
while (!(bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR) & SSSR_RNE))
continue; /* poll */
(void) bus_space_read_2(sc->sc_iot, sc->sc_ioh, SSP_SSDR);
while (!(bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR) & SSSR_TNF))
continue; /* poll */
bus_space_write_2(sc->sc_iot, sc->sc_ioh, SSP_SSDR, (uint16_t)data);
while (bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR) & SSSR_BUSY)
continue; /* poll */
while (!(bus_space_read_4(sc->sc_iot, sc->sc_ioh, SSP_SSSR) & SSSR_RNE))
continue; /* poll */
rv = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SSP_SSDR);
pxa2x0_gpio_set_bit(GPIO_WS011SH_AK4184_CS);
mutex_exit(&sc->sc_mtx);
return rv;
}