uboot/drivers/spi/pl022_spi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2012
   4 * Armando Visconti, ST Microelectronics, armando.visconti@st.com.
   5 *
   6 * (C) Copyright 2018
   7 * Quentin Schulz, Bootlin, quentin.schulz@bootlin.com
   8 *
   9 * Driver for ARM PL022 SPI Controller.
  10 */
  11
  12#include <clk.h>
  13#include <common.h>
  14#include <dm.h>
  15#include <dm/platform_data/spi_pl022.h>
  16#include <linux/io.h>
  17#include <spi.h>
  18
  19#define SSP_CR0         0x000
  20#define SSP_CR1         0x004
  21#define SSP_DR          0x008
  22#define SSP_SR          0x00C
  23#define SSP_CPSR        0x010
  24#define SSP_IMSC        0x014
  25#define SSP_RIS         0x018
  26#define SSP_MIS         0x01C
  27#define SSP_ICR         0x020
  28#define SSP_DMACR       0x024
  29#define SSP_CSR         0x030 /* vendor extension */
  30#define SSP_ITCR        0x080
  31#define SSP_ITIP        0x084
  32#define SSP_ITOP        0x088
  33#define SSP_TDR         0x08C
  34
  35#define SSP_PID0        0xFE0
  36#define SSP_PID1        0xFE4
  37#define SSP_PID2        0xFE8
  38#define SSP_PID3        0xFEC
  39
  40#define SSP_CID0        0xFF0
  41#define SSP_CID1        0xFF4
  42#define SSP_CID2        0xFF8
  43#define SSP_CID3        0xFFC
  44
  45/* SSP Control Register 0  - SSP_CR0 */
  46#define SSP_CR0_SPO             (0x1 << 6)
  47#define SSP_CR0_SPH             (0x1 << 7)
  48#define SSP_CR0_BIT_MODE(x)     ((x) - 1)
  49#define SSP_SCR_MIN             (0x00)
  50#define SSP_SCR_MAX             (0xFF)
  51#define SSP_SCR_SHFT            8
  52#define DFLT_CLKRATE            2
  53
  54/* SSP Control Register 1  - SSP_CR1 */
  55#define SSP_CR1_MASK_SSE        (0x1 << 1)
  56
  57#define SSP_CPSR_MIN            (0x02)
  58#define SSP_CPSR_MAX            (0xFE)
  59#define DFLT_PRESCALE           (0x40)
  60
  61/* SSP Status Register - SSP_SR */
  62#define SSP_SR_MASK_TFE         (0x1 << 0) /* Transmit FIFO empty */
  63#define SSP_SR_MASK_TNF         (0x1 << 1) /* Transmit FIFO not full */
  64#define SSP_SR_MASK_RNE         (0x1 << 2) /* Receive FIFO not empty */
  65#define SSP_SR_MASK_RFF         (0x1 << 3) /* Receive FIFO full */
  66#define SSP_SR_MASK_BSY         (0x1 << 4) /* Busy Flag */
  67
  68struct pl022_spi_slave {
  69        void *base;
  70        unsigned int freq;
  71};
  72
  73/*
  74 * ARM PL022 exists in different 'flavors'.
  75 * This drivers currently support the standard variant (0x00041022), that has a
  76 * 16bit wide and 8 locations deep TX/RX FIFO.
  77 */
  78static int pl022_is_supported(struct pl022_spi_slave *ps)
  79{
  80        /* PL022 version is 0x00041022 */
  81        if ((readw(ps->base + SSP_PID0) == 0x22) &&
  82            (readw(ps->base + SSP_PID1) == 0x10) &&
  83            ((readw(ps->base + SSP_PID2) & 0xf) == 0x04) &&
  84            (readw(ps->base + SSP_PID3) == 0x00))
  85                return 1;
  86
  87        return 0;
  88}
  89
  90static int pl022_spi_probe(struct udevice *bus)
  91{
  92        struct pl022_spi_pdata *plat = dev_get_platdata(bus);
  93        struct pl022_spi_slave *ps = dev_get_priv(bus);
  94
  95        ps->base = ioremap(plat->addr, plat->size);
  96        ps->freq = plat->freq;
  97
  98        /* Check the PL022 version */
  99        if (!pl022_is_supported(ps))
 100                return -ENOTSUPP;
 101
 102        /* 8 bits per word, high polarity and default clock rate */
 103        writew(SSP_CR0_BIT_MODE(8), ps->base + SSP_CR0);
 104        writew(DFLT_PRESCALE, ps->base + SSP_CPSR);
 105
 106        return 0;
 107}
 108
 109static void flush(struct pl022_spi_slave *ps)
 110{
 111        do {
 112                while (readw(ps->base + SSP_SR) & SSP_SR_MASK_RNE)
 113                        readw(ps->base + SSP_DR);
 114        } while (readw(ps->base + SSP_SR) & SSP_SR_MASK_BSY);
 115}
 116
 117static int pl022_spi_claim_bus(struct udevice *dev)
 118{
 119        struct udevice *bus = dev->parent;
 120        struct pl022_spi_slave *ps = dev_get_priv(bus);
 121        u16 reg;
 122
 123        /* Enable the SPI hardware */
 124        reg = readw(ps->base + SSP_CR1);
 125        reg |= SSP_CR1_MASK_SSE;
 126        writew(reg, ps->base + SSP_CR1);
 127
 128        flush(ps);
 129
 130        return 0;
 131}
 132
 133static int pl022_spi_release_bus(struct udevice *dev)
 134{
 135        struct udevice *bus = dev->parent;
 136        struct pl022_spi_slave *ps = dev_get_priv(bus);
 137        u16 reg;
 138
 139        flush(ps);
 140
 141        /* Disable the SPI hardware */
 142        reg = readw(ps->base + SSP_CR1);
 143        reg &= ~SSP_CR1_MASK_SSE;
 144        writew(reg, ps->base + SSP_CR1);
 145
 146        return 0;
 147}
 148
 149static int pl022_spi_xfer(struct udevice *dev, unsigned int bitlen,
 150                          const void *dout, void *din, unsigned long flags)
 151{
 152        struct udevice *bus = dev->parent;
 153        struct pl022_spi_slave *ps = dev_get_priv(bus);
 154        u32             len_tx = 0, len_rx = 0, len;
 155        u32             ret = 0;
 156        const u8        *txp = dout;
 157        u8              *rxp = din, value;
 158
 159        if (bitlen == 0)
 160                /* Finish any previously submitted transfers */
 161                return 0;
 162
 163        /*
 164         * TODO: The controller can do non-multiple-of-8 bit
 165         * transfers, but this driver currently doesn't support it.
 166         *
 167         * It's also not clear how such transfers are supposed to be
 168         * represented as a stream of bytes...this is a limitation of
 169         * the current SPI interface.
 170         */
 171        if (bitlen % 8) {
 172                /* Errors always terminate an ongoing transfer */
 173                flags |= SPI_XFER_END;
 174                return -1;
 175        }
 176
 177        len = bitlen / 8;
 178
 179        while (len_tx < len) {
 180                if (readw(ps->base + SSP_SR) & SSP_SR_MASK_TNF) {
 181                        value = txp ? *txp++ : 0;
 182                        writew(value, ps->base + SSP_DR);
 183                        len_tx++;
 184                }
 185
 186                if (readw(ps->base + SSP_SR) & SSP_SR_MASK_RNE) {
 187                        value = readw(ps->base + SSP_DR);
 188                        if (rxp)
 189                                *rxp++ = value;
 190                        len_rx++;
 191                }
 192        }
 193
 194        while (len_rx < len_tx) {
 195                if (readw(ps->base + SSP_SR) & SSP_SR_MASK_RNE) {
 196                        value = readw(ps->base + SSP_DR);
 197                        if (rxp)
 198                                *rxp++ = value;
 199                        len_rx++;
 200                }
 201        }
 202
 203        return ret;
 204}
 205
 206static inline u32 spi_rate(u32 rate, u16 cpsdvsr, u16 scr)
 207{
 208        return rate / (cpsdvsr * (1 + scr));
 209}
 210
 211static int pl022_spi_set_speed(struct udevice *bus, uint speed)
 212{
 213        struct pl022_spi_slave *ps = dev_get_priv(bus);
 214        u16 scr = SSP_SCR_MIN, cr0 = 0, cpsr = SSP_CPSR_MIN, best_scr = scr,
 215            best_cpsr = cpsr;
 216        u32 min, max, best_freq = 0, tmp;
 217        u32 rate = ps->freq;
 218        bool found = false;
 219
 220        max = spi_rate(rate, SSP_CPSR_MIN, SSP_SCR_MIN);
 221        min = spi_rate(rate, SSP_CPSR_MAX, SSP_SCR_MAX);
 222
 223        if (speed > max || speed < min) {
 224                pr_err("Tried to set speed to %dHz but min=%d and max=%d\n",
 225                       speed, min, max);
 226                return -EINVAL;
 227        }
 228
 229        while (cpsr <= SSP_CPSR_MAX && !found) {
 230                while (scr <= SSP_SCR_MAX) {
 231                        tmp = spi_rate(rate, cpsr, scr);
 232
 233                        if (abs(speed - tmp) < abs(speed - best_freq)) {
 234                                best_freq = tmp;
 235                                best_cpsr = cpsr;
 236                                best_scr = scr;
 237
 238                                if (tmp == speed) {
 239                                        found = true;
 240                                        break;
 241                                }
 242                        }
 243
 244                        scr++;
 245                }
 246                cpsr += 2;
 247                scr = SSP_SCR_MIN;
 248        }
 249
 250        writew(best_cpsr, ps->base + SSP_CPSR);
 251        cr0 = readw(ps->base + SSP_CR0);
 252        writew(cr0 | (best_scr << SSP_SCR_SHFT), ps->base + SSP_CR0);
 253
 254        return 0;
 255}
 256
 257static int pl022_spi_set_mode(struct udevice *bus, uint mode)
 258{
 259        struct pl022_spi_slave *ps = dev_get_priv(bus);
 260        u16 reg;
 261
 262        reg = readw(ps->base + SSP_CR0);
 263        reg &= ~(SSP_CR0_SPH | SSP_CR0_SPO);
 264        if (mode & SPI_CPHA)
 265                reg |= SSP_CR0_SPH;
 266        if (mode & SPI_CPOL)
 267                reg |= SSP_CR0_SPO;
 268        writew(reg, ps->base + SSP_CR0);
 269
 270        return 0;
 271}
 272
 273static int pl022_cs_info(struct udevice *bus, uint cs,
 274                         struct spi_cs_info *info)
 275{
 276        return 0;
 277}
 278
 279static const struct dm_spi_ops pl022_spi_ops = {
 280        .claim_bus      = pl022_spi_claim_bus,
 281        .release_bus    = pl022_spi_release_bus,
 282        .xfer           = pl022_spi_xfer,
 283        .set_speed      = pl022_spi_set_speed,
 284        .set_mode       = pl022_spi_set_mode,
 285        .cs_info        = pl022_cs_info,
 286};
 287
 288#if !CONFIG_IS_ENABLED(OF_PLATDATA)
 289static int pl022_spi_ofdata_to_platdata(struct udevice *bus)
 290{
 291        struct pl022_spi_pdata *plat = bus->platdata;
 292        const void *fdt = gd->fdt_blob;
 293        int node = dev_of_offset(bus);
 294        struct clk clkdev;
 295        int ret;
 296
 297        plat->addr = fdtdec_get_addr_size(fdt, node, "reg", &plat->size);
 298
 299        ret = clk_get_by_index(bus, 0, &clkdev);
 300        if (ret)
 301                return ret;
 302
 303        plat->freq = clk_get_rate(&clkdev);
 304
 305        return 0;
 306}
 307
 308static const struct udevice_id pl022_spi_ids[] = {
 309        { .compatible = "arm,pl022-spi" },
 310        { }
 311};
 312#endif
 313
 314U_BOOT_DRIVER(pl022_spi) = {
 315        .name   = "pl022_spi",
 316        .id     = UCLASS_SPI,
 317#if !CONFIG_IS_ENABLED(OF_PLATDATA)
 318        .of_match = pl022_spi_ids,
 319        .ofdata_to_platdata = pl022_spi_ofdata_to_platdata,
 320#endif
 321        .ops    = &pl022_spi_ops,
 322        .platdata_auto_alloc_size = sizeof(struct pl022_spi_pdata),
 323        .priv_auto_alloc_size = sizeof(struct pl022_spi_slave),
 324        .probe  = pl022_spi_probe,
 325};
 326