uboot/drivers/spi/soft_spi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2014 Google, Inc
   4 *
   5 * (C) Copyright 2002
   6 * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com.
   7 *
   8 * Influenced by code from:
   9 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
  10 */
  11
  12#include <common.h>
  13#include <dm.h>
  14#include <errno.h>
  15#include <fdtdec.h>
  16#include <malloc.h>
  17#include <spi.h>
  18#include <asm/gpio.h>
  19
  20DECLARE_GLOBAL_DATA_PTR;
  21
  22struct soft_spi_platdata {
  23        struct gpio_desc cs;
  24        struct gpio_desc sclk;
  25        struct gpio_desc mosi;
  26        struct gpio_desc miso;
  27        int spi_delay_us;
  28        int flags;
  29};
  30
  31#define SPI_MASTER_NO_RX        BIT(0)
  32#define SPI_MASTER_NO_TX        BIT(1)
  33
  34struct soft_spi_priv {
  35        unsigned int mode;
  36};
  37
  38static int soft_spi_scl(struct udevice *dev, int bit)
  39{
  40        struct udevice *bus = dev_get_parent(dev);
  41        struct soft_spi_platdata *plat = dev_get_platdata(bus);
  42
  43        dm_gpio_set_value(&plat->sclk, bit);
  44
  45        return 0;
  46}
  47
  48static int soft_spi_sda(struct udevice *dev, int bit)
  49{
  50        struct udevice *bus = dev_get_parent(dev);
  51        struct soft_spi_platdata *plat = dev_get_platdata(bus);
  52
  53        dm_gpio_set_value(&plat->mosi, bit);
  54
  55        return 0;
  56}
  57
  58static int soft_spi_cs_activate(struct udevice *dev)
  59{
  60        struct udevice *bus = dev_get_parent(dev);
  61        struct soft_spi_platdata *plat = dev_get_platdata(bus);
  62
  63        dm_gpio_set_value(&plat->cs, 0);
  64        dm_gpio_set_value(&plat->sclk, 0);
  65        dm_gpio_set_value(&plat->cs, 1);
  66
  67        return 0;
  68}
  69
  70static int soft_spi_cs_deactivate(struct udevice *dev)
  71{
  72        struct udevice *bus = dev_get_parent(dev);
  73        struct soft_spi_platdata *plat = dev_get_platdata(bus);
  74
  75        dm_gpio_set_value(&plat->cs, 0);
  76
  77        return 0;
  78}
  79
  80static int soft_spi_claim_bus(struct udevice *dev)
  81{
  82        /*
  83         * Make sure the SPI clock is in idle state as defined for
  84         * this slave.
  85         */
  86        return soft_spi_scl(dev, 0);
  87}
  88
  89static int soft_spi_release_bus(struct udevice *dev)
  90{
  91        /* Nothing to do */
  92        return 0;
  93}
  94
  95/*-----------------------------------------------------------------------
  96 * SPI transfer
  97 *
  98 * This writes "bitlen" bits out the SPI MOSI port and simultaneously clocks
  99 * "bitlen" bits in the SPI MISO port.  That's just the way SPI works.
 100 *
 101 * The source of the outgoing bits is the "dout" parameter and the
 102 * destination of the input bits is the "din" parameter.  Note that "dout"
 103 * and "din" can point to the same memory location, in which case the
 104 * input data overwrites the output data (since both are buffered by
 105 * temporary variables, this is OK).
 106 */
 107static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen,
 108                         const void *dout, void *din, unsigned long flags)
 109{
 110        struct udevice *bus = dev_get_parent(dev);
 111        struct soft_spi_priv *priv = dev_get_priv(bus);
 112        struct soft_spi_platdata *plat = dev_get_platdata(bus);
 113        uchar           tmpdin  = 0;
 114        uchar           tmpdout = 0;
 115        const u8        *txd = dout;
 116        u8              *rxd = din;
 117        int             cpha = priv->mode & SPI_CPHA;
 118        unsigned int    j;
 119
 120        debug("spi_xfer: slave %s:%s dout %08X din %08X bitlen %u\n",
 121              dev->parent->name, dev->name, *(uint *)txd, *(uint *)rxd,
 122              bitlen);
 123
 124        if (flags & SPI_XFER_BEGIN)
 125                soft_spi_cs_activate(dev);
 126
 127        for (j = 0; j < bitlen; j++) {
 128                /*
 129                 * Check if it is time to work on a new byte.
 130                 */
 131                if ((j % 8) == 0) {
 132                        if (txd)
 133                                tmpdout = *txd++;
 134                        else
 135                                tmpdout = 0;
 136                        if (j != 0) {
 137                                if (rxd)
 138                                        *rxd++ = tmpdin;
 139                        }
 140                        tmpdin  = 0;
 141                }
 142
 143                if (!cpha)
 144                        soft_spi_scl(dev, 0);
 145                if ((plat->flags & SPI_MASTER_NO_TX) == 0)
 146                        soft_spi_sda(dev, !!(tmpdout & 0x80));
 147                udelay(plat->spi_delay_us);
 148                if (cpha)
 149                        soft_spi_scl(dev, 0);
 150                else
 151                        soft_spi_scl(dev, 1);
 152                tmpdin  <<= 1;
 153                if ((plat->flags & SPI_MASTER_NO_RX) == 0)
 154                        tmpdin  |= dm_gpio_get_value(&plat->miso);
 155                tmpdout <<= 1;
 156                udelay(plat->spi_delay_us);
 157                if (cpha)
 158                        soft_spi_scl(dev, 1);
 159        }
 160        /*
 161         * If the number of bits isn't a multiple of 8, shift the last
 162         * bits over to left-justify them.  Then store the last byte
 163         * read in.
 164         */
 165        if (rxd) {
 166                if ((bitlen % 8) != 0)
 167                        tmpdin <<= 8 - (bitlen % 8);
 168                *rxd++ = tmpdin;
 169        }
 170
 171        if (flags & SPI_XFER_END)
 172                soft_spi_cs_deactivate(dev);
 173
 174        return 0;
 175}
 176
 177static int soft_spi_set_speed(struct udevice *dev, unsigned int speed)
 178{
 179        /* Accept any speed */
 180        return 0;
 181}
 182
 183static int soft_spi_set_mode(struct udevice *dev, unsigned int mode)
 184{
 185        struct soft_spi_priv *priv = dev_get_priv(dev);
 186
 187        priv->mode = mode;
 188
 189        return 0;
 190}
 191
 192static const struct dm_spi_ops soft_spi_ops = {
 193        .claim_bus      = soft_spi_claim_bus,
 194        .release_bus    = soft_spi_release_bus,
 195        .xfer           = soft_spi_xfer,
 196        .set_speed      = soft_spi_set_speed,
 197        .set_mode       = soft_spi_set_mode,
 198};
 199
 200static int soft_spi_ofdata_to_platdata(struct udevice *dev)
 201{
 202        struct soft_spi_platdata *plat = dev->platdata;
 203        const void *blob = gd->fdt_blob;
 204        int node = dev_of_offset(dev);
 205
 206        plat->spi_delay_us = fdtdec_get_int(blob, node, "spi-delay-us", 0);
 207
 208        return 0;
 209}
 210
 211static int soft_spi_probe(struct udevice *dev)
 212{
 213        struct spi_slave *slave = dev_get_parent_priv(dev);
 214        struct soft_spi_platdata *plat = dev->platdata;
 215        int cs_flags, clk_flags;
 216        int ret;
 217
 218        cs_flags = (slave && slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW;
 219        clk_flags = (slave && slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0;
 220
 221        if (gpio_request_by_name(dev, "cs-gpios", 0, &plat->cs,
 222                                 GPIOD_IS_OUT | cs_flags) ||
 223            gpio_request_by_name(dev, "gpio-sck", 0, &plat->sclk,
 224                                 GPIOD_IS_OUT | clk_flags))
 225                return -EINVAL;
 226
 227        ret = gpio_request_by_name(dev, "gpio-mosi", 0, &plat->mosi,
 228                                   GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
 229        if (ret)
 230                plat->flags |= SPI_MASTER_NO_TX;
 231
 232        ret = gpio_request_by_name(dev, "gpio-miso", 0, &plat->miso,
 233                                   GPIOD_IS_IN);
 234        if (ret)
 235                plat->flags |= SPI_MASTER_NO_RX;
 236
 237        if ((plat->flags & (SPI_MASTER_NO_RX | SPI_MASTER_NO_TX)) ==
 238            (SPI_MASTER_NO_RX | SPI_MASTER_NO_TX))
 239                return -EINVAL;
 240
 241        return 0;
 242}
 243
 244static const struct udevice_id soft_spi_ids[] = {
 245        { .compatible = "spi-gpio" },
 246        { }
 247};
 248
 249U_BOOT_DRIVER(soft_spi) = {
 250        .name   = "soft_spi",
 251        .id     = UCLASS_SPI,
 252        .of_match = soft_spi_ids,
 253        .ops    = &soft_spi_ops,
 254        .ofdata_to_platdata = soft_spi_ofdata_to_platdata,
 255        .platdata_auto_alloc_size = sizeof(struct soft_spi_platdata),
 256        .priv_auto_alloc_size = sizeof(struct soft_spi_priv),
 257        .probe  = soft_spi_probe,
 258};
 259