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/hardware.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 (!spi_cs_is_valid(bus, cs))
  47                return NULL;
  48
  49        switch (bus) {
  50        case 0:
  51                regs = (void *)ATMEL_BASE_SPI0;
  52                break;
  53#ifdef ATMEL_BASE_SPI1
  54        case 1:
  55                regs = (void *)ATMEL_BASE_SPI1;
  56                break;
  57#endif
  58#ifdef ATMEL_BASE_SPI2
  59        case 2:
  60                regs = (void *)ATMEL_BASE_SPI2;
  61                break;
  62#endif
  63#ifdef ATMEL_BASE_SPI3
  64        case 3:
  65                regs = (void *)ATMEL_BASE_SPI3;
  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#if defined(CONFIG_AT91SAM9X5)
  96                        | ATMEL_SPI_MR_WDRBT
  97#endif
  98                        | ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf);
  99        spi_writel(as, CSR(cs), csrx);
 100
 101        return &as->slave;
 102}
 103
 104void spi_free_slave(struct spi_slave *slave)
 105{
 106        struct atmel_spi_slave *as = to_atmel_spi(slave);
 107
 108        free(as);
 109}
 110
 111int spi_claim_bus(struct spi_slave *slave)
 112{
 113        struct atmel_spi_slave *as = to_atmel_spi(slave);
 114
 115        /* Enable the SPI hardware */
 116        spi_writel(as, CR, ATMEL_SPI_CR_SPIEN);
 117
 118        /*
 119         * Select the slave. This should set SCK to the correct
 120         * initial state, etc.
 121         */
 122        spi_writel(as, MR, as->mr);
 123
 124        return 0;
 125}
 126
 127void spi_release_bus(struct spi_slave *slave)
 128{
 129        struct atmel_spi_slave *as = to_atmel_spi(slave);
 130
 131        /* Disable the SPI hardware */
 132        spi_writel(as, CR, ATMEL_SPI_CR_SPIDIS);
 133}
 134
 135int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
 136                const void *dout, void *din, unsigned long flags)
 137{
 138        struct atmel_spi_slave *as = to_atmel_spi(slave);
 139        unsigned int    len_tx;
 140        unsigned int    len_rx;
 141        unsigned int    len;
 142        u32             status;
 143        const u8        *txp = dout;
 144        u8              *rxp = din;
 145        u8              value;
 146
 147        if (bitlen == 0)
 148                /* Finish any previously submitted transfers */
 149                goto out;
 150
 151        /*
 152         * TODO: The controller can do non-multiple-of-8 bit
 153         * transfers, but this driver currently doesn't support it.
 154         *
 155         * It's also not clear how such transfers are supposed to be
 156         * represented as a stream of bytes...this is a limitation of
 157         * the current SPI interface.
 158         */
 159        if (bitlen % 8) {
 160                /* Errors always terminate an ongoing transfer */
 161                flags |= SPI_XFER_END;
 162                goto out;
 163        }
 164
 165        len = bitlen / 8;
 166
 167        /*
 168         * The controller can do automatic CS control, but it is
 169         * somewhat quirky, and it doesn't really buy us much anyway
 170         * in the context of U-Boot.
 171         */
 172        if (flags & SPI_XFER_BEGIN) {
 173                spi_cs_activate(slave);
 174                /*
 175                 * sometimes the RDR is not empty when we get here,
 176                 * in theory that should not happen, but it DOES happen.
 177                 * Read it here to be on the safe side.
 178                 * That also clears the OVRES flag. Required if the
 179                 * following loop exits due to OVRES!
 180                 */
 181                spi_readl(as, RDR);
 182        }
 183
 184        for (len_tx = 0, len_rx = 0; len_rx < len; ) {
 185                status = spi_readl(as, SR);
 186
 187                if (status & ATMEL_SPI_SR_OVRES)
 188                        return -1;
 189
 190                if (len_tx < len && (status & ATMEL_SPI_SR_TDRE)) {
 191                        if (txp)
 192                                value = *txp++;
 193                        else
 194                                value = 0;
 195                        spi_writel(as, TDR, value);
 196                        len_tx++;
 197                }
 198                if (status & ATMEL_SPI_SR_RDRF) {
 199                        value = spi_readl(as, RDR);
 200                        if (rxp)
 201                                *rxp++ = value;
 202                        len_rx++;
 203                }
 204        }
 205
 206out:
 207        if (flags & SPI_XFER_END) {
 208                /*
 209                 * Wait until the transfer is completely done before
 210                 * we deactivate CS.
 211                 */
 212                do {
 213                        status = spi_readl(as, SR);
 214                } while (!(status & ATMEL_SPI_SR_TXEMPTY));
 215
 216                spi_cs_deactivate(slave);
 217        }
 218
 219        return 0;
 220}
 221