uboot/drivers/spi/oc_tiny_spi.c
<<
>>
Prefs
   1/*
   2 * Opencore tiny_spi driver
   3 *
   4 * http://opencores.org/project,tiny_spi
   5 *
   6 * based on bfin_spi.c
   7 * Copyright (c) 2005-2008 Analog Devices Inc.
   8 * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
   9 *
  10 * Licensed under the GPL-2 or later.
  11 */
  12
  13#include <common.h>
  14#include <asm/io.h>
  15#include <malloc.h>
  16#include <spi.h>
  17#include <asm/gpio.h>
  18
  19#define TINY_SPI_STATUS_TXE 0x1
  20#define TINY_SPI_STATUS_TXR 0x2
  21
  22struct tiny_spi_regs {
  23        unsigned rxdata;        /* Rx data reg */
  24        unsigned txdata;        /* Tx data reg */
  25        unsigned status;        /* Status reg */
  26        unsigned control;       /* Control reg */
  27        unsigned baud;          /* Baud reg */
  28};
  29
  30struct tiny_spi_host {
  31        uint base;
  32        uint freq;
  33        uint baudwidth;
  34};
  35static const struct tiny_spi_host tiny_spi_host_list[] =
  36        CONFIG_SYS_TINY_SPI_LIST;
  37
  38struct tiny_spi_slave {
  39        struct spi_slave slave;
  40        const struct tiny_spi_host *host;
  41        uint mode;
  42        uint baud;
  43        uint flg;
  44};
  45#define to_tiny_spi_slave(s) container_of(s, struct tiny_spi_slave, slave)
  46
  47int spi_cs_is_valid(unsigned int bus, unsigned int cs)
  48{
  49        return bus < ARRAY_SIZE(tiny_spi_host_list) && gpio_is_valid(cs);
  50}
  51
  52void spi_cs_activate(struct spi_slave *slave)
  53{
  54        struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
  55        unsigned int cs = slave->cs;
  56
  57        gpio_set_value(cs, tiny_spi->flg);
  58        debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs));
  59}
  60
  61void spi_cs_deactivate(struct spi_slave *slave)
  62{
  63        struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
  64        unsigned int cs = slave->cs;
  65
  66        gpio_set_value(cs, !tiny_spi->flg);
  67        debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs));
  68}
  69
  70void spi_set_speed(struct spi_slave *slave, uint hz)
  71{
  72        struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
  73        const struct tiny_spi_host *host = tiny_spi->host;
  74
  75        tiny_spi->baud = min(DIV_ROUND_UP(host->freq, hz * 2),
  76                             (1 << host->baudwidth)) - 1;
  77        debug("%s: speed %u actual %u\n", __func__, hz,
  78              host->freq / ((tiny_spi->baud + 1) * 2));
  79}
  80
  81void spi_init(void)
  82{
  83}
  84
  85struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
  86                                  unsigned int hz, unsigned int mode)
  87{
  88        struct tiny_spi_slave *tiny_spi;
  89
  90        if (!spi_cs_is_valid(bus, cs) || gpio_request(cs, "tiny_spi"))
  91                return NULL;
  92
  93        tiny_spi = malloc(sizeof(*tiny_spi));
  94        if (!tiny_spi)
  95                return NULL;
  96        memset(tiny_spi, 0, sizeof(*tiny_spi));
  97
  98        tiny_spi->slave.bus = bus;
  99        tiny_spi->slave.cs = cs;
 100        tiny_spi->host = &tiny_spi_host_list[bus];
 101        tiny_spi->mode = mode & (SPI_CPOL | SPI_CPHA);
 102        tiny_spi->flg = mode & SPI_CS_HIGH ? 1 : 0;
 103        spi_set_speed(&tiny_spi->slave, hz);
 104
 105        debug("%s: bus:%i cs:%i base:%lx\n", __func__,
 106                bus, cs, tiny_spi->host->base);
 107        return &tiny_spi->slave;
 108}
 109
 110void spi_free_slave(struct spi_slave *slave)
 111{
 112        struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
 113
 114        gpio_free(slave->cs);
 115        free(tiny_spi);
 116}
 117
 118int spi_claim_bus(struct spi_slave *slave)
 119{
 120        struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
 121        struct tiny_spi_regs *regs = (void *)tiny_spi->host->base;
 122
 123        debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
 124        gpio_direction_output(slave->cs, !tiny_spi->flg);
 125        writel(tiny_spi->mode, &regs->control);
 126        writel(tiny_spi->baud, &regs->baud);
 127        return 0;
 128}
 129
 130void spi_release_bus(struct spi_slave *slave)
 131{
 132        debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
 133}
 134
 135#ifndef CONFIG_TINY_SPI_IDLE_VAL
 136# define CONFIG_TINY_SPI_IDLE_VAL 0xff
 137#endif
 138
 139int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
 140             void *din, unsigned long flags)
 141{
 142        struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
 143        struct tiny_spi_regs *regs = (void *)tiny_spi->host->base;
 144        const u8 *txp = dout;
 145        u8 *rxp = din;
 146        uint bytes = bitlen / 8;
 147        uint i;
 148
 149        debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__,
 150                slave->bus, slave->cs, bitlen, bytes, flags);
 151        if (bitlen == 0)
 152                goto done;
 153
 154        /* assume to do 8 bits transfers */
 155        if (bitlen % 8) {
 156                flags |= SPI_XFER_END;
 157                goto done;
 158        }
 159
 160        if (flags & SPI_XFER_BEGIN)
 161                spi_cs_activate(slave);
 162
 163        /* we need to tighten the transfer loop */
 164        if (txp && rxp) {
 165                writeb(*txp++, &regs->txdata);
 166                if (bytes > 1) {
 167                        writeb(*txp++, &regs->txdata);
 168                        for (i = 2; i < bytes; i++) {
 169                                u8 rx, tx = *txp++;
 170                                while (!(readb(&regs->status) &
 171                                         TINY_SPI_STATUS_TXR))
 172                                        ;
 173                                rx = readb(&regs->txdata);
 174                                writeb(tx, &regs->txdata);
 175                                *rxp++ = rx;
 176                        }
 177                        while (!(readb(&regs->status) &
 178                                 TINY_SPI_STATUS_TXR))
 179                                ;
 180                        *rxp++ = readb(&regs->txdata);
 181                }
 182                while (!(readb(&regs->status) &
 183                         TINY_SPI_STATUS_TXE))
 184                        ;
 185                *rxp++ = readb(&regs->rxdata);
 186        } else if (rxp) {
 187                writeb(CONFIG_TINY_SPI_IDLE_VAL, &regs->txdata);
 188                if (bytes > 1) {
 189                        writeb(CONFIG_TINY_SPI_IDLE_VAL,
 190                               &regs->txdata);
 191                        for (i = 2; i < bytes; i++) {
 192                                u8 rx;
 193                                while (!(readb(&regs->status) &
 194                                         TINY_SPI_STATUS_TXR))
 195                                        ;
 196                                rx = readb(&regs->txdata);
 197                                writeb(CONFIG_TINY_SPI_IDLE_VAL,
 198                                       &regs->txdata);
 199                                *rxp++ = rx;
 200                        }
 201                        while (!(readb(&regs->status) &
 202                                 TINY_SPI_STATUS_TXR))
 203                                ;
 204                        *rxp++ = readb(&regs->txdata);
 205                }
 206                while (!(readb(&regs->status) &
 207                         TINY_SPI_STATUS_TXE))
 208                        ;
 209                *rxp++ = readb(&regs->rxdata);
 210        } else if (txp) {
 211                writeb(*txp++, &regs->txdata);
 212                if (bytes > 1) {
 213                        writeb(*txp++, &regs->txdata);
 214                        for (i = 2; i < bytes; i++) {
 215                                u8 tx = *txp++;
 216                                while (!(readb(&regs->status) &
 217                                         TINY_SPI_STATUS_TXR))
 218                                        ;
 219                                writeb(tx, &regs->txdata);
 220                        }
 221                }
 222                while (!(readb(&regs->status) &
 223                         TINY_SPI_STATUS_TXE))
 224                        ;
 225        } else {
 226                writeb(CONFIG_TINY_SPI_IDLE_VAL, &regs->txdata);
 227                if (bytes > 1) {
 228                        writeb(CONFIG_TINY_SPI_IDLE_VAL,
 229                               &regs->txdata);
 230                        for (i = 2; i < bytes; i++) {
 231                                while (!(readb(&regs->status) &
 232                                         TINY_SPI_STATUS_TXR))
 233                                        ;
 234                                writeb(CONFIG_TINY_SPI_IDLE_VAL,
 235                                       &regs->txdata);
 236                        }
 237                }
 238                while (!(readb(&regs->status) &
 239                         TINY_SPI_STATUS_TXE))
 240                        ;
 241        }
 242
 243 done:
 244        if (flags & SPI_XFER_END)
 245                spi_cs_deactivate(slave);
 246
 247        return 0;
 248}
 249