Port-arm archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

SPI support on OMAP



                        Hi port-arm,

I am trying to implement the SPI bus driver on OMAP, in order to
implement the touchscreen driver on the Nokia N900 (TSC2005). I think I
am getting close, but kind of stuck at the moment - I would therefore
welcome feedback already.

During my latest tests I got the following error:
=== BEGIN PASTE ===
panic: kernel diagnostic assertion "!cpu_intr_p()" failed: file
".../sys/kern/kern_condvar.c", line 142
=== END PASTE ===

I still have no functional keypad driver to obtain a backtrace, but I
suppose this is triggered within spi_wait() whenever a transfer is
started. I'll welcome an explanation of the issue here :/

Anyway, I am attaching both drivers in their current state to this mail
(work in progress). omap2_spi.c and omap2_spireg.h should both be in
sys/arch/arm/omap, while tsc2005.c and tsc2005reg.h go in sys/dev/spi.

They also require the following changes to compile:

sys/arch/arm/omap/files.omap2:
=== BEGIN PASTE ===
# OMAP2 SPI controllers
device  omapspi: spibus
attach  omapspi at obio with omap2_spi
file    arch/arm/omap/omap2_spi.c             (omap2 | omap3) & omapspi
=== END PASTE

sys/arch/evbarm/conf/N900: (or whichever other OMAP kernel)
=== BEGIN PASTE ===
# SPI devices
omapspi0        at obio0 addr 0x48098000 size 0x1000 intr 65
omapspi1        at obio0 addr 0x4809a000 size 0x1000 intr 66
omapspi2        at obio0 addr 0x480b8000 size 0x1000 intr 91
omapspi3        at obio0 addr 0x480ba000 size 0x1000 intr 48

spi*            at omapspi?

# Touchscreen
tsc2005ts0      at spi0 slave 0 intr 196
=== END PASTE ===

sys/dev/spi/files.spi:
=== BEGIN PASTE ===
# TSC2005 touchscreen
device  tsc2005ts: wsmousedev
attach  tsc2005ts at spi
file    dev/spi/tsc2005.c               tsc2005ts
=== END PASTE ===

I am also using the patch that I posted earlier on current-users@ (to
let tsc2005 interrupt on the GPIO bus).

Cheers,
-- 
khorben
/* $NetBSD$ */

/*
 * Copyright (c) 2013 Pierre Pronchery <khorben%defora.org@localhost>
 * 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. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * 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$");

#include "opt_omap.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#if 0
#include <sys/conf.h>
#endif
#include <sys/bus.h>
#if 0
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/mutex.h>
#endif

#include <dev/spi/spivar.h>

#include <arm/omap/omap2_obiovar.h>

#include <arm/omap/omap2_reg.h>
#include <arm/omap/omap2_spireg.h>

#define OMAP2_MCSPI_MAX_CHANNELS        4

struct omap2_spi_channel {
        SIMPLEQ_HEAD(,spi_transfer) queue;
        struct spi_transfer     *transfer;
        struct spi_chunk        *wchunk;
        struct spi_chunk        *rchunk;

        bool                    running;
};

struct omap2_spi_softc {
        device_t                        sc_dev;
        bus_space_tag_t                 sc_iot;
        bus_space_handle_t              sc_ioh;
        void                            *sc_intr;

        struct spi_controller           sc_spi;

        struct omap2_spi_channel        sc_channels[OMAP2_MCSPI_MAX_CHANNELS];
};

#define SPI_READ_REG(sc, reg)           \
        bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))
#define SPI_WRITE_REG(sc, reg, val)     \
        bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))

static int      omap2_spi_match(device_t, cfdata_t, void *);
static void     omap2_spi_attach(device_t, device_t, void *);

static int      omap2_spi_configure(void *, int, int, int);
static int      omap2_spi_transfer(void *, struct spi_transfer *);

static void     omap2_spi_start(struct omap2_spi_softc * const, int);
static void     omap2_spi_stop(struct omap2_spi_softc * const, int);
static int      omap2_spi_intr(void *v);

static int      omap2_spi_reset(struct omap2_spi_softc * const);
static void     omap2_spi_send(struct omap2_spi_softc * const, int);
static void     omap2_spi_recv(struct omap2_spi_softc * const, int);

CFATTACH_DECL_NEW(omap2_spi, sizeof(struct omap2_spi_softc),
    omap2_spi_match, omap2_spi_attach, NULL, NULL);


static int
omap2_spi_match(device_t parent, cfdata_t match, void *opaque)
{
        struct obio_attach_args *obio = opaque;

#if defined(OMAP_3430) || defined(OMAP_3530)
        if (obio->obio_addr == SPI1_BASE_3530 ||
            obio->obio_addr == SPI2_BASE_3530 ||
            obio->obio_addr == SPI3_BASE_3530 ||
            obio->obio_addr == SPI4_BASE_3530)
                return 1;
#endif

        return 0;
}

static void
omap2_spi_attach(device_t parent, device_t self, void *opaque)
{
        struct omap2_spi_softc *sc = device_private(self);
        struct obio_attach_args *obio = opaque;
        int nslaves;
        uint32_t rev;
        struct spibus_attach_args sba;
        int i;

        aprint_naive("\n");

        sc->sc_dev = self;
        sc->sc_iot = obio->obio_iot;

#if defined(OMAP_3430) || defined(OMAP_3530)
        switch (obio->obio_addr) {
                case SPI1_BASE_3530:
                        nslaves = 4;
                        break;
                case SPI2_BASE_3530:
                case SPI3_BASE_3530:
                        nslaves = 2;
                        break;
                case SPI4_BASE_3530:
                        nslaves = 1;
                        break;
                default:
                        return;
        }
#endif

        if (bus_space_map(obio->obio_iot, obio->obio_addr, obio->obio_size, 0,
                                &sc->sc_ioh) != 0) {
                aprint_error(": couldn't map address space\n");
                return;
        }

        rev = SPI_READ_REG(sc, OMAP2_MCSPI_REVISION);
        aprint_normal(": rev %u.%u\n",
                        (unsigned int)((rev & OMAP2_MCSPI_REV_MAJ_MASK)
                                >> OMAP2_MCSPI_REV_MAJ_SHIFT),
                        (unsigned int)((rev & OMAP2_MCSPI_REV_MIN_MASK)
                                >> OMAP2_MCSPI_REV_MIN_SHIFT));

        omap2_spi_reset(sc);

        if (obio->obio_intr < 0) {
                sc->sc_intr = NULL;
        } else {
                sc->sc_intr = intr_establish(obio->obio_intr, IPL_BIO,
                                IST_LEVEL, omap2_spi_intr, sc);
                if (sc->sc_intr == NULL) {
                        aprint_error_dev(self,
                                        "couldn't establish interrupt\n");
                }
        }

        sc->sc_spi.sct_cookie = sc;
        sc->sc_spi.sct_configure = omap2_spi_configure;
        sc->sc_spi.sct_transfer = omap2_spi_transfer;
        sc->sc_spi.sct_nslaves = nslaves;

        /* initialize the channels */
        for (i = 0; i < nslaves; i++) {
                SIMPLEQ_INIT(&sc->sc_channels[i].queue);
        }

        sba.sba_controller = &sc->sc_spi;

        config_found_ia(self, "spibus", &sba, spibus_print);
}

static int
omap2_spi_configure(void *cookie, int slave, int mode, int speed)
{
        struct omap2_spi_softc *sc = cookie;
        uint32_t conf = 0;
        uint32_t div;

        if (slave >= sc->sc_spi.sct_nslaves)
                return EINVAL;

        if (speed <= 0)
                return EINVAL;

        /* set the speed */
        /* XXX implement lower speeds */
        if (speed <= 187500)
                div = 0x8;
        else if (speed <= 375000)
                div = 0x7;
        else if (speed <= 750000)
                div = 0x6;
        else if (speed <= 1500000)
                div = 0x5;
        else if (speed <= 3000000)
                div = 0x4;
        else if (speed <= 6000000)
                div = 0x3;
        else if (speed <= 12000000)
                div = 0x2;
        else if (speed <= 24000000)
                div = 0x1;
        else
                div = 0;
        conf |= (div << OMAP2_MCSPI_CHXCONF_CLKD_SHIFT);

        /* set the word size to 8 bits */
        conf &= ~OMAP2_MCSPI_CHXCONF_WL;
        conf |= (0x7 << OMAP2_MCSPI_CHXCONF_WL_SHIFT);

        /* disable the FIFO */
        conf &= ~OMAP2_MCSPI_CHXCONF_FFER;
        conf &= ~OMAP2_MCSPI_CHXCONF_FFEW;

        switch (mode) {
                case SPI_MODE_0:
                        break;
                case SPI_MODE_1:
                        conf |= OMAP2_MCSPI_CHXCONF_PHA;
                        break;
                case SPI_MODE_2:
                        conf |= OMAP2_MCSPI_CHXCONF_POL;
                        break;
                case SPI_MODE_3:
                        conf |= OMAP2_MCSPI_CHXCONF_POL
                                | OMAP2_MCSPI_CHXCONF_PHA;
                        break;
                default:
                        return EINVAL;
        }

        SPI_WRITE_REG(sc, OMAP2_MCSPI_CHXCONF(slave), conf);

        return 0;
}

static int
omap2_spi_transfer(void *cookie, struct spi_transfer *st)
{
        struct omap2_spi_softc *sc = cookie;
        int s;
        int channel;

        channel = st->st_slave;
        s = splbio();
        spi_transq_enqueue(&sc->sc_channels[channel].queue, st);
        omap2_spi_start(sc, channel);
        splx(s);
        return 0;
}

static void
omap2_spi_start(struct omap2_spi_softc * const sc, int channel)
{
        struct omap2_spi_channel *chan;
        struct spi_transfer *st;
        uint32_t enableval = 0;
        uint32_t ctrlreg;
        uint32_t ctrlval;
        uint32_t u32;

        chan = &sc->sc_channels[channel];
        if (chan->running)
                return;

        if ((st = spi_transq_first(&chan->queue)) == NULL)
                return;
        spi_transq_dequeue(&chan->queue);

        chan->running = true;

        KASSERT(chan->transfer == NULL);
        chan->transfer = st;
        chan->rchunk = chan->wchunk = st->st_chunks;

        switch (channel) {
                case 0:
                        enableval = OMAP2_MCSPI_IRQENABLE_RX0_FULL
                                | OMAP2_MCSPI_IRQENABLE_TX0_UNDERFLOW
                                | OMAP2_MCSPI_IRQENABLE_TX0_EMPTY;
                        break;
                case 1:
                        enableval = OMAP2_MCSPI_IRQENABLE_RX1_FULL
                                | OMAP2_MCSPI_IRQENABLE_TX1_UNDERFLOW
                                | OMAP2_MCSPI_IRQENABLE_TX1_EMPTY;
                        break;
                case 2:
                        enableval = OMAP2_MCSPI_IRQENABLE_RX2_FULL
                                | OMAP2_MCSPI_IRQENABLE_TX2_UNDERFLOW
                                | OMAP2_MCSPI_IRQENABLE_TX2_EMPTY;
                        break;
                case 3:
                        enableval = OMAP2_MCSPI_IRQENABLE_RX3_FULL
                                | OMAP2_MCSPI_IRQENABLE_TX3_UNDERFLOW
                                | OMAP2_MCSPI_IRQENABLE_TX3_EMPTY;
                        break;
        }

        /* enable interrupts */
        u32 = SPI_READ_REG(sc, OMAP2_MCSPI_IRQENABLE);
        SPI_WRITE_REG(sc, OMAP2_MCSPI_IRQENABLE, u32 | enableval);

        /* start the channel */
        ctrlreg = OMAP2_MCSPI_CHXCTRL(channel);
        u32 = SPI_READ_REG(sc, ctrlreg);
        ctrlval = OMAP2_MCSPI_CHXCTRL_EN;
        SPI_WRITE_REG(sc, ctrlreg, u32 | ctrlval);
}

static void
omap2_spi_stop(struct omap2_spi_softc * const sc, int channel)
{
        struct omap2_spi_channel *chan;
        uint32_t ctrlreg;
        uint32_t u32;

        chan = &sc->sc_channels[channel];

        chan->running = false;

        /* stop the channel */
        ctrlreg = OMAP2_MCSPI_CHXCTRL(channel);
        u32 = SPI_READ_REG(sc, ctrlreg);
        u32 &= ~OMAP2_MCSPI_CHXCTRL_EN;
        SPI_WRITE_REG(sc, ctrlreg, u32);
}

static int
omap2_spi_intr(void *v)
{
        struct omap2_spi_softc *sc = v;
        int i;
        struct omap2_spi_channel *chan;
        struct spi_transfer *st;
        uint32_t status;
        uint32_t u32;
        uint32_t tx_empty = 0;
        uint32_t rx_full = 0;

        /* reset the channel status bits */
        status = SPI_READ_REG(sc, OMAP2_MCSPI_IRQSTATUS);
        u32 = 0;

        /* check every channel */
        for (i = 0; i < sc->sc_spi.sct_nslaves; i++) {
                chan = &sc->sc_channels[i];

                switch (i) {
                        case 0:
                                u32 |= OMAP2_MCSPI_IRQSTATUS_RX0_OVERFLOW
                                        | OMAP2_MCSPI_IRQSTATUS_RX0_FULL
                                        | OMAP2_MCSPI_IRQSTATUS_TX0_UNDERFLOW
                                        | OMAP2_MCSPI_IRQSTATUS_TX0_EMPTY;
                                tx_empty = status
                                        & OMAP2_MCSPI_IRQSTATUS_TX0_EMPTY;
                                rx_full = status
                                        & OMAP2_MCSPI_IRQSTATUS_RX0_FULL;
                                break;
                        case 1:
                                u32 |= OMAP2_MCSPI_IRQSTATUS_RX1_FULL
                                        | OMAP2_MCSPI_IRQSTATUS_TX1_UNDERFLOW
                                        | OMAP2_MCSPI_IRQSTATUS_TX1_EMPTY;
                                tx_empty = status
                                        & OMAP2_MCSPI_IRQSTATUS_TX1_EMPTY;
                                rx_full = status
                                        & OMAP2_MCSPI_IRQSTATUS_RX1_FULL;
                                break;
                        case 2:
                                u32 |= OMAP2_MCSPI_IRQSTATUS_RX2_FULL
                                        | OMAP2_MCSPI_IRQSTATUS_TX2_UNDERFLOW
                                        | OMAP2_MCSPI_IRQSTATUS_TX2_EMPTY;
                                tx_empty = status
                                        & OMAP2_MCSPI_IRQSTATUS_TX2_EMPTY;
                                rx_full = status
                                        & OMAP2_MCSPI_IRQSTATUS_RX2_FULL;
                                break;
                        case 3:
                                u32 |= OMAP2_MCSPI_IRQSTATUS_RX3_FULL
                                        | OMAP2_MCSPI_IRQSTATUS_TX3_UNDERFLOW
                                        | OMAP2_MCSPI_IRQSTATUS_TX3_EMPTY;
                                tx_empty = status
                                        & OMAP2_MCSPI_IRQSTATUS_TX3_EMPTY;
                                rx_full = status
                                        & OMAP2_MCSPI_IRQSTATUS_RX3_FULL;
                                break;
                }

                if (tx_empty && chan->wchunk != NULL) {
                        omap2_spi_send(sc, i);
                }

                if (rx_full) {
                        omap2_spi_recv(sc, i);
                }

                if (chan->wchunk == NULL && chan->rchunk == NULL) {
                        st = chan->transfer;
                        chan->transfer = NULL;

                        KASSERT(st != NULL);
                        spi_done(st, 0);

                        spi_transq_dequeue(&chan->queue);
                        if ((st = spi_transq_first(&chan->queue)) == NULL)
                                omap2_spi_stop(sc, i);
                        else
                                chan->transfer = st;

                }
        }

        /* acknowledge the interrupt */
        SPI_WRITE_REG(sc, OMAP2_MCSPI_IRQSTATUS, u32);

        return 1;
}

static int
omap2_spi_reset(struct omap2_spi_softc * const sc)
{
        int retry;
        uint32_t status;
        int i;

        /* proceed to a software reset */
        SPI_WRITE_REG(sc, OMAP2_MCSPI_SYSCONFIG,
                        OMAP2_MCSPI_SYSCONFIG_SOFTRESET);

        /* poll until the reset is done */
        retry = 1000;
        status = SPI_READ_REG(sc, OMAP2_MCSPI_SYSSTATUS);
        while ((status & OMAP2_MCSPI_SYSSTATUS_RESETDONE) == 0) {
                if (--retry == 0)
                        return EBUSY;
                delay(1000);
                status = SPI_READ_REG(sc, OMAP2_MCSPI_SYSSTATUS);
        }

        /* disable every channel available */
        for (i = 0; i < sc->sc_spi.sct_nslaves; i++) {
                SPI_WRITE_REG(sc, OMAP2_MCSPI_CHXCTRL(i), 0);
        }

        /* disable and clear all interrupts */
        SPI_WRITE_REG(sc, OMAP2_MCSPI_IRQENABLE, 0);
        SPI_WRITE_REG(sc, OMAP2_MCSPI_IRQSTATUS, 0x1777f);

        /* set the module in master and multichannel mode */
        SPI_WRITE_REG(sc, OMAP2_MCSPI_MODULCTRL, 0);

        return 0;
}

static void
omap2_spi_send(struct omap2_spi_softc * const sc, int channel)
{
        struct spi_chunk *chunk;
        const uint32_t stat = OMAP2_MCSPI_CHXSTAT(channel);
        uint32_t u32;
        uint32_t data;

        if ((chunk = sc->sc_channels[channel].wchunk) == NULL)
                return;

        if (chunk->chunk_wresid) {
                u32 = SPI_READ_REG(sc, stat);
                if ((u32 & OMAP2_MCSPI_CHXSTAT_TXS) == 0)
                        return;
                if (chunk->chunk_wptr) {
                        data = *chunk->chunk_wptr++;
                        SPI_WRITE_REG(sc, OMAP2_MCSPI_TX(channel), data);
                }
        }

        if (chunk->chunk_wresid-- == 0) {
                sc->sc_channels[channel].wchunk
                        = sc->sc_channels[channel].wchunk->chunk_next;
        }
}

static void
omap2_spi_recv(struct omap2_spi_softc * const sc, int channel)
{
        struct spi_chunk *chunk;
        const uint32_t stat = OMAP2_MCSPI_CHXSTAT(channel);
        uint32_t u32;
        uint32_t data;

        if ((chunk = sc->sc_channels[channel].rchunk) == NULL)
                return;

        if (chunk->chunk_rresid) {
                u32 = SPI_READ_REG(sc, stat);
                if ((u32 & OMAP2_MCSPI_CHXSTAT_RXS) == 0)
                        return;
                data = SPI_READ_REG(sc, OMAP2_MCSPI_RX(channel));
                if (chunk->chunk_rptr) {
                        *chunk->chunk_rptr++ = data & 0xff;
                }
        }

        if (chunk->chunk_rresid-- == 0) {
                sc->sc_channels[channel].rchunk
                        = sc->sc_channels[channel].rchunk->chunk_next;
        }
}
/* $NetBSD$ */

/*
 * Copyright (c) 2013 Pierre Pronchery <khorben%defora.org@localhost>
 * 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. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * 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.
 */

#ifndef _OMAP2_SPIREG_H
#define _OMAP2_SPIREG_H

/*
 * Multichannel SPI Controller registers
 */

#define OMAP2_MCSPI_REVISION                    0x00
#define  OMAP2_MCSPI_REV_MAJ_MASK               __BITS(4,7)
#define  OMAP2_MCSPI_REV_MAJ_SHIFT              4
#define  OMAP2_MCSPI_REV_MIN_MASK               __BITS(0,3)
#define  OMAP2_MCSPI_REV_MIN_SHIFT              0

#define OMAP2_MCSPI_SYSCONFIG                   0x10
#define  OMAP2_MCSPI_SYSCONFIG_CLOCKACTIVITY    __BITS(8,9)
#define  OMAP2_MCSPI_SYSCONFIG_SIDLEMODE        __BITS(3,4)
#define  OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP        __BIT(2)
#define  OMAP2_MCSPI_SYSCONFIG_SOFTRESET        __BIT(1)
#define  OMAP2_MCSPI_SYSCONFIG_AUTOIDLE         __BIT(0)

#define OMAP2_MCSPI_SYSSTATUS                   0x14
#define  OMAP2_MCSPI_SYSSTATUS_RESETDONE        __BIT(0)

#define OMAP2_MCSPI_IRQSTATUS                   0x18
#define  OMAP2_MCSPI_IRQSTATUS_EOW              __BIT(16)
#define  OMAP2_MCSPI_IRQSTATUS_RX3_FULL         __BIT(14)
#define  OMAP2_MCSPI_IRQSTATUS_TX3_UNDERFLOW    __BIT(13)
#define  OMAP2_MCSPI_IRQSTATUS_TX3_EMPTY        __BIT(12)
#define  OMAP2_MCSPI_IRQSTATUS_RX2_FULL         __BIT(10)
#define  OMAP2_MCSPI_IRQSTATUS_TX2_UNDERFLOW    __BIT(9)
#define  OMAP2_MCSPI_IRQSTATUS_TX2_EMPTY        __BIT(8)
#define  OMAP2_MCSPI_IRQSTATUS_RX1_FULL         __BIT(6)
#define  OMAP2_MCSPI_IRQSTATUS_TX1_UNDERFLOW    __BIT(5)
#define  OMAP2_MCSPI_IRQSTATUS_TX1_EMPTY        __BIT(4)
#define  OMAP2_MCSPI_IRQSTATUS_RX0_OVERFLOW     __BIT(3)
#define  OMAP2_MCSPI_IRQSTATUS_RX0_FULL         __BIT(2)
#define  OMAP2_MCSPI_IRQSTATUS_TX0_UNDERFLOW    __BIT(1)
#define  OMAP2_MCSPI_IRQSTATUS_TX0_EMPTY        __BIT(0)

#define OMAP2_MCSPI_IRQENABLE                   0x1c
#define  OMAP2_MCSPI_IRQENABLE_EOWKE            __BIT(17)
#define  OMAP2_MCSPI_IRQENABLE_WKE              __BIT(16)
#define  OMAP2_MCSPI_IRQENABLE_RX3_FULL         __BIT(14)
#define  OMAP2_MCSPI_IRQENABLE_TX3_UNDERFLOW    __BIT(13)
#define  OMAP2_MCSPI_IRQENABLE_TX3_EMPTY        __BIT(12)
#define  OMAP2_MCSPI_IRQENABLE_RX2_FULL         __BIT(10)
#define  OMAP2_MCSPI_IRQENABLE_TX2_UNDERFLOW    __BIT(9)
#define  OMAP2_MCSPI_IRQENABLE_TX2_EMPTY        __BIT(8)
#define  OMAP2_MCSPI_IRQENABLE_RX1_FULL         __BIT(6)
#define  OMAP2_MCSPI_IRQENABLE_TX1_UNDERFLOW    __BIT(5)
#define  OMAP2_MCSPI_IRQENABLE_TX1_EMPTY        __BIT(4)
#define  OMAP2_MCSPI_IRQENABLE_RX0_OVERFLOW     __BIT(3)
#define  OMAP2_MCSPI_IRQENABLE_RX0_FULL         __BIT(2)
#define  OMAP2_MCSPI_IRQENABLE_TX0_UNDERFLOW    __BIT(1)
#define  OMAP2_MCSPI_IRQENABLE_TX0_EMPTY        __BIT(0)

#define OMAP2_MCSPI_WAKEUPENABLE                0x20

#define OMAP2_MCSPI_SYST                        0x24
#define  OMAP2_MCSPI_SYST_SSB                   __BIT(11)
#define  OMAP2_MCSPI_SYST_SPIENDIR              __BIT(10)
#define  OMAP2_MCSPI_SYST_SPIDATDIR1            __BIT(9)
#define  OMAP2_MCSPI_SYST_SPIDATDIR0            __BIT(8)
#define  OMAP2_MCSPI_SYST_WAKD                  __BIT(7)
#define  OMAP2_MCSPI_SYST_SPICLK                __BIT(6)
#define  OMAP2_MCSPI_SYST_SPIDAT_1              __BIT(5)
#define  OMAP2_MCSPI_SYST_SPIDAT_0              __BIT(4)
#define  OMAP2_MCSPI_SYST_SPIEN_3               __BIT(3)
#define  OMAP2_MCSPI_SYST_SPIEN_2               __BIT(2)
#define  OMAP2_MCSPI_SYST_SPIEN_1               __BIT(1)
#define  OMAP2_MCSPI_SYST_SPIEN_0               __BIT(0)

#define OMAP2_MCSPI_MODULCTRL                   0x28
#define  OMAP2_MCSPI_MODULCTRL_SYSTEM_TEST      __BIT(3)
#define  OMAP2_MCSPI_MODULCTRL_MS               __BIT(2)
#define  OMAP2_MCSPI_MODULCTRL_SINGLE           __BIT(0)

#define OMAP2_MCSPI_CHXCONF(channel)            (0x2c + (channel * 0x14))
#define OMAP2_MCSPI_CH0CONF                     OMAP2_MCSPI_CHXCONF(0)
#define OMAP2_MCSPI_CH1CONF                     OMAP2_MCSPI_CHXCONF(1)
#define OMAP2_MCSPI_CH2CONF                     OMAP2_MCSPI_CHXCONF(2)
#define OMAP2_MCSPI_CH3CONF                     OMAP2_MCSPI_CH0CONF(3)
#define  OMAP2_MCSPI_CHXCONF_FFER               __BIT(28)
#define  OMAP2_MCSPI_CHXCONF_FFEW               __BIT(27)
#define  OMAP2_MCSPI_CHXCONF_TCS                __BITS(25,26)
#define  OMAP2_MCSPI_CHXCONF_SBE                __BIT(23)
#define  OMAP2_MCSPI_CHXCONF_TRM                __BITS(12,13)
#define  OMAP2_MCSPI_CHXCONF_TRM_SHIFT          12
#define  OMAP2_MCSPI_CHXCONF_WL                 __BITS(7,11)
#define  OMAP2_MCSPI_CHXCONF_WL_SHIFT           7
#define  OMAP2_MCSPI_CHXCONF_EPOL               __BIT(6)
#define  OMAP2_MCSPI_CHXCONF_CLKD               __BITS(2,5)
#define  OMAP2_MCSPI_CHXCONF_CLKD_SHIFT         2
#define  OMAP2_MCSPI_CHXCONF_POL                __BIT(1)
#define  OMAP2_MCSPI_CHXCONF_PHA                __BIT(0)

#define OMAP2_MCSPI_CHXSTAT(channel)            (0x30 + ((channel) * 0x14))
#define OMAP2_MCSPI_CH0STAT                     OMAP2_MCSPI_CHXSTAT(0)
#define OMAP2_MCSPI_CH1STAT                     OMAP2_MCSPI_CHXSTAT(1)
#define OMAP2_MCSPI_CH2STAT                     OMAP2_MCSPI_CHXSTAT(2)
#define OMAP2_MCSPI_CH3STAT                     OMAP2_MCSPI_CHXSTAT(3)
# define OMAP2_MCSPI_CHXSTAT_EOT                __BIT(2)
# define OMAP2_MCSPI_CHXSTAT_TXS                __BIT(1)
# define OMAP2_MCSPI_CHXSTAT_RXS                __BIT(0)

#define OMAP2_MCSPI_CHXCTRL(channel)            (0x34 + ((channel) * 0x14))
#define OMAP2_MCSPI_CH0CTRL                     OMAP2_MCSPI_CHXCTRL(0)
#define OMAP2_MCSPI_CH1CTRL                     OMAP2_MCSPI_CHXCTRL(1)
#define OMAP2_MCSPI_CH2CTRL                     OMAP2_MCSPI_CHXCTRL(2)
#define OMAP2_MCSPI_CH3CTRL                     OMAP2_MCSPI_CHXCTRL(3)
#define  OMAP2_MCSPI_CHXCTRL_EN                 __BIT(0)

#define OMAP2_MCSPI_TX(channel)                 (0x38 + ((channel) * 0x14))
#define OMAP2_MCSPI_TX0                         OMAP2_MCSPI_TX(0)
#define OMAP2_MCSPI_TX1                         OMAP2_MCSPI_TX(1)
#define OMAP2_MCSPI_TX2                         OMAP2_MCSPI_TX(2)
#define OMAP2_MCSPI_TX3                         OMAP2_MCSPI_TX(3)

#define OMAP2_MCSPI_RX(channel)                 (0x3c + (channel * 0x14))
#define OMAP2_MCSPI_RX0                         OMAP2_MCSPI_RX(0)
#define OMAP2_MCSPI_RX1                         OMAP2_MCSPI_RX(1)
#define OMAP2_MCSPI_RX2                         OMAP2_MCSPI_RX(2)
#define OMAP2_MCSPI_RX3                         OMAP2_MCSPI_RX(3)

#define OMAP2_MCSPI_XFERLEVEL                   0x7c
#define  OMAP2_MCSPI_XFERLEVEL_WCNT             __BITS(16,31)
#define  OMAP2_MCSPI_XFERLEVEL_AFL              __BITS(8,13)
#define  OMAP2_MCSPI_XFERLEVEL_AEL              __BITS(0,5)

#endif /* !_OMAP2_SPIREG_H */
/* $NetBSD$ */

/*
 * Copyright (c) 2013 Pierre Pronchery <khorben%defora.org@localhost>
 * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``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 FOUNDATION OR CONTRIBUTORS
 * 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.
 */

/*
 * Texas Instruments Touch Screen Controller:
 * - 2005
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD$");

#include <sys/param.h>
#include <sys/device.h>

#include <dev/spi/spivar.h>

#include <dev/spi/tsc2005reg.h>

/* variables */
struct tsc2005_softc {
        device_t                sc_dev;
        struct spi_handle       *sc_spi;

        void *                  sc_intr;
};

static int      tsc2005_match(device_t, cfdata_t, void *);
static void     tsc2005_attach(device_t, device_t, void *);
static int      tsc2005_detach(device_t, int);

static int      tsc2005_reset(struct tsc2005_softc *);

static int      tsc2005_intr(void *);

static int      tsc2005_read_reg(struct tsc2005_softc *, int, uint16_t *);
#if 0
static int      tsc2005_write_reg(struct tsc2005_softc *, int, uint16_t);
#endif

CFATTACH_DECL_NEW(tsc2005ts, sizeof(struct tsc2005_softc),
                tsc2005_match, tsc2005_attach, tsc2005_detach, NULL);

static int
tsc2005_match(device_t parent, cfdata_t match, void *aux)
{
        struct spi_attach_args *sa = aux;

        spi_configure(sa->sa_handle, SPI_MODE_0, 6000000);
        return 1;
}

static void
tsc2005_attach(device_t parent, device_t self, void *aux)
{
        struct tsc2005_softc *sc = device_private(self);
        struct spi_attach_args *sa = aux;

        sc->sc_dev = self;
        sc->sc_spi = sa->sa_handle;

        aprint_normal(": TSC2005 touchscreen\n");
        aprint_naive(": TSC2005 touchscreen\n");

        tsc2005_reset(sc);

        sc->sc_intr = intr_establish(sa->sa_intr, IPL_BIO, IST_LEVEL_LOW,
                        tsc2005_intr, sc);
        if (sc->sc_intr == NULL) {
                aprint_error_dev(sc->sc_dev, "couldn't establish interrupt\n");
        }

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

static int
tsc2005_detach(device_t self, int flags)
{
        struct tsc2005_softc *sc = device_private(self);

        if (sc->sc_intr) {
                intr_disestablish(sc->sc_intr);
        }

        pmf_device_deregister(self);

        return 0;
}

static int
tsc2005_reset(struct tsc2005_softc *sc)
{
        uint8_t u8;

        aprint_normal_dev(sc->sc_dev, "%s()\n", __func__);

        /* send a Control Byte 1 with the SWRST bit set */
        u8 = 0x82;
        spi_send(sc->sc_spi, sizeof(u8), &u8);

        /* send a Control Byte 1 with the SWRST bit unset */
        u8 = 0x80;
        spi_send(sc->sc_spi, sizeof(u8), &u8);
        return 0;
}

static int
tsc2005_intr(void *v)
{
        struct tsc2005_softc *sc = v;
        uint16_t status;
        uint16_t x;
        uint16_t y;
        uint16_t z;

        tsc2005_read_reg(sc, TSC2005_REG_STATUS, &status);
        tsc2005_read_reg(sc, TSC2005_REG_X, &x);
        tsc2005_read_reg(sc, TSC2005_REG_Y, &y);
        tsc2005_read_reg(sc, TSC2005_REG_Z1, &z);
        aprint_normal_dev(sc->sc_dev, "X: %u, Y:%u, Z:%u\n", x, y, z);
        return 0;
}

static int
tsc2005_read_reg(struct tsc2005_softc *sc, int reg, uint16_t *data)
{
        uint8_t buf[2];

        buf[0] = reg << 3 | 1;
        spi_send_recv(sc->sc_spi, sizeof(buf), buf, sizeof(buf), buf);
        memcpy(data, buf, sizeof(data));
        return 0;
}

#if 0
static int
tsc2005_write_reg(struct tsc2005_softc *sc, int reg, uint16_t data)
{
        uint8_t buf[3];

        buf[0] = reg << 3;
        memcpy(&buf[1], &data, sizeof(data));
        return spi_send(sc->sc_spi, sizeof(buf), buf);
}
#endif
/* $NetBSD$ */

/*
 * Copyright (c) 2013 Pierre Pronchery <khorben%defora.org@localhost>
 * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``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 FOUNDATION OR CONTRIBUTORS
 * 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.
 */

#ifndef _DEV_SPI_TSC2005REG_H_
#define _DEV_SPI_TSC2005REG_H_

/*
 * Texas Instruments Touch Screen Controller:
 * - 2005
 */

#define TSC2005_REG_X           0x00
#define TSC2005_REG_Y           0x01
#define TSC2005_REG_Z1          0x02
#define TSC2005_REG_Z2          0x03
#define TSC2005_REG_AUX         0x04
#define TSC2005_REG_TEMP1       0x05
#define TSC2005_REG_TEMP2       0x06
#define TSC2005_REG_STATUS      0x07
#define TSC2005_REG_AUX_HIGH    0x08
#define TSC2005_REG_AUX_LOW     0x09
#define TSC2005_REG_TEMP_HIGH   0x0a
#define TSC2005_REG_TEMP_LOW    0x0b
#define TSC2005_REG_CFR0        0x0c
#define TSC2005_REG_CFR1        0x0d
#define TSC2005_REG_CFR2        0x0e
#define TSC2005_REG_CFSS        0x0f

#endif  /* _DEV_SPI_SPIVAR_H_ */


Home | Main Index | Thread Index | Old Index