uboot/drivers/spi/atmel_spi.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2007 Atmel Corporation
   3 *
   4 * See file CREDITS for list of people who contributed to this
   5 * project.
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License as
   9 * published by the Free Software Foundation; either version 2 of
  10 * the License, or (at your option) any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  20 * MA 02111-1307 USA
  21 */
  22#include <common.h>
  23#include <spi.h>
  24#include <malloc.h>
  25
  26#include <asm/io.h>
  27
  28#include <asm/arch/clk.h>
  29#include <asm/arch/memory-map.h>
  30
  31#include "atmel_spi.h"
  32
  33void spi_init()
  34{
  35
  36}
  37
  38struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
  39                        unsigned int max_hz, unsigned int mode)
  40{
  41        struct atmel_spi_slave  *as;
  42        unsigned int            scbr;
  43        u32                     csrx;
  44        void                    *regs;
  45
  46        if (cs > 3 || !spi_cs_is_valid(bus, cs))
  47                return NULL;
  48
  49        switch (bus) {
  50        case 0:
  51                regs = (void *)SPI0_BASE;
  52                break;
  53#ifdef SPI1_BASE
  54        case 1:
  55                regs = (void *)SPI1_BASE;
  56                break;
  57#endif
  58#ifdef SPI2_BASE
  59        case 2:
  60                regs = (void *)SPI2_BASE;
  61                break;
  62#endif
  63#ifdef SPI3_BASE
  64        case 3:
  65                regs = (void *)SPI3_BASE;
  66                break;
  67#endif
  68        default:
  69                return NULL;
  70        }
  71
  72
  73        scbr = (get_spi_clk_rate(bus) + max_hz - 1) / max_hz;
  74        if (scbr > ATMEL_SPI_CSRx_SCBR_MAX)
  75                /* Too low max SCK rate */
  76                return NULL;
  77        if (scbr < 1)
  78                scbr = 1;
  79
  80        csrx = ATMEL_SPI_CSRx_SCBR(scbr);
  81        csrx |= ATMEL_SPI_CSRx_BITS(ATMEL_SPI_BITS_8);
  82        if (!(mode & SPI_CPHA))
  83                csrx |= ATMEL_SPI_CSRx_NCPHA;
  84        if (mode & SPI_CPOL)
  85                csrx |= ATMEL_SPI_CSRx_CPOL;
  86
  87        as = malloc(sizeof(struct atmel_spi_slave));
  88        if (!as)
  89                return NULL;
  90
  91        as->slave.bus = bus;
  92        as->slave.cs = cs;
  93        as->regs = regs;
  94        as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS
  95                        | ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf);
  96        spi_writel(as, CSR(cs), csrx);
  97
  98        return &as->slave;
  99}
 100
 101void spi_free_slave(struct spi_slave *slave)
 102{
 103        struct atmel_spi_slave *as = to_atmel_spi(slave);
 104
 105        free(as);
 106}
 107
 108int spi_claim_bus(struct spi_slave *slave)
 109{
 110        struct atmel_spi_slave *as = to_atmel_spi(slave);
 111
 112        /* Enable the SPI hardware */
 113        spi_writel(as, CR, ATMEL_SPI_CR_SPIEN);
 114
 115        /*
 116         * Select the slave. This should set SCK to the correct
 117         * initial state, etc.
 118         */
 119        spi_writel(as, MR, as->mr);
 120
 121        return 0;
 122}
 123
 124void spi_release_bus(struct spi_slave *slave)
 125{
 126        struct atmel_spi_slave *as = to_atmel_spi(slave);
 127
 128        /* Disable the SPI hardware */
 129        spi_writel(as, CR, ATMEL_SPI_CR_SPIDIS);
 130}
 131
 132int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
 133                const void *dout, void *din, unsigned long flags)
 134{
 135        struct atmel_spi_slave *as = to_atmel_spi(slave);
 136        unsigned int    len_tx;
 137        unsigned int    len_rx;
 138        unsigned int    len;
 139        int             ret;
 140        u32             status;
 141        const u8        *txp = dout;
 142        u8              *rxp = din;
 143        u8              value;
 144
 145        ret = 0;
 146        if (bitlen == 0)
 147                /* Finish any previously submitted transfers */
 148                goto out;
 149
 150        /*
 151         * TODO: The controller can do non-multiple-of-8 bit
 152         * transfers, but this driver currently doesn't support it.
 153         *
 154         * It's also not clear how such transfers are supposed to be
 155         * represented as a stream of bytes...this is a limitation of
 156         * the current SPI interface.
 157         */
 158        if (bitlen % 8) {
 159                /* Errors always terminate an ongoing transfer */
 160                flags |= SPI_XFER_END;
 161                goto out;
 162        }
 163
 164        len = bitlen / 8;
 165
 166        /*
 167         * The controller can do automatic CS control, but it is
 168         * somewhat quirky, and it doesn't really buy us much anyway
 169         * in the context of U-Boot.
 170         */
 171        if (flags & SPI_XFER_BEGIN)
 172                spi_cs_activate(slave);
 173
 174        for (len_tx = 0, len_rx = 0; len_rx < len; ) {
 175                status = spi_readl(as, SR);
 176
 177                if (status & ATMEL_SPI_SR_OVRES)
 178                        return -1;
 179
 180                if (len_tx < len && (status & ATMEL_SPI_SR_TDRE)) {
 181                        if (txp)
 182                                value = *txp++;
 183                        else
 184                                value = 0;
 185                        spi_writel(as, TDR, value);
 186                        len_tx++;
 187                }
 188                if (status & ATMEL_SPI_SR_RDRF) {
 189                        value = spi_readl(as, RDR);
 190                        if (rxp)
 191                                *rxp++ = value;
 192                        len_rx++;
 193                }
 194        }
 195
 196out:
 197        if (flags & SPI_XFER_END) {
 198                /*
 199                 * Wait until the transfer is completely done before
 200                 * we deactivate CS.
 201                 */
 202                do {
 203                        status = spi_readl(as, SR);
 204                } while (!(status & ATMEL_SPI_SR_TXEMPTY));
 205
 206                spi_cs_deactivate(slave);
 207        }
 208
 209        return 0;
 210}
 211