linux/drivers/spi/spi-altera.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Altera SPI driver
   4 *
   5 * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw>
   6 *
   7 * Based on spi_s3c24xx.c, which is:
   8 * Copyright (c) 2006 Ben Dooks
   9 * Copyright (c) 2006 Simtec Electronics
  10 *      Ben Dooks <ben@simtec.co.uk>
  11 */
  12
  13#include <linux/interrupt.h>
  14#include <linux/errno.h>
  15#include <linux/module.h>
  16#include <linux/platform_device.h>
  17#include <linux/spi/altera.h>
  18#include <linux/spi/spi.h>
  19#include <linux/io.h>
  20#include <linux/of.h>
  21
  22#define DRV_NAME "spi_altera"
  23
  24#define ALTERA_SPI_RXDATA       0
  25#define ALTERA_SPI_TXDATA       4
  26#define ALTERA_SPI_STATUS       8
  27#define ALTERA_SPI_CONTROL      12
  28#define ALTERA_SPI_SLAVE_SEL    20
  29
  30#define ALTERA_SPI_STATUS_ROE_MSK       0x8
  31#define ALTERA_SPI_STATUS_TOE_MSK       0x10
  32#define ALTERA_SPI_STATUS_TMT_MSK       0x20
  33#define ALTERA_SPI_STATUS_TRDY_MSK      0x40
  34#define ALTERA_SPI_STATUS_RRDY_MSK      0x80
  35#define ALTERA_SPI_STATUS_E_MSK         0x100
  36
  37#define ALTERA_SPI_CONTROL_IROE_MSK     0x8
  38#define ALTERA_SPI_CONTROL_ITOE_MSK     0x10
  39#define ALTERA_SPI_CONTROL_ITRDY_MSK    0x40
  40#define ALTERA_SPI_CONTROL_IRRDY_MSK    0x80
  41#define ALTERA_SPI_CONTROL_IE_MSK       0x100
  42#define ALTERA_SPI_CONTROL_SSO_MSK      0x400
  43
  44#define ALTERA_SPI_MAX_CS               32
  45
  46enum altera_spi_type {
  47        ALTERA_SPI_TYPE_UNKNOWN,
  48        ALTERA_SPI_TYPE_SUBDEV,
  49};
  50
  51struct altera_spi {
  52        int irq;
  53        int len;
  54        int count;
  55        int bytes_per_word;
  56        u32 imr;
  57
  58        /* data buffers */
  59        const unsigned char *tx;
  60        unsigned char *rx;
  61
  62        struct regmap *regmap;
  63        u32 regoff;
  64        struct device *dev;
  65};
  66
  67static const struct regmap_config spi_altera_config = {
  68        .reg_bits = 32,
  69        .reg_stride = 4,
  70        .val_bits = 32,
  71        .fast_io = true,
  72};
  73
  74static int altr_spi_writel(struct altera_spi *hw, unsigned int reg,
  75                           unsigned int val)
  76{
  77        int ret;
  78
  79        ret = regmap_write(hw->regmap, hw->regoff + reg, val);
  80        if (ret)
  81                dev_err(hw->dev, "fail to write reg 0x%x val 0x%x: %d\n",
  82                        reg, val, ret);
  83
  84        return ret;
  85}
  86
  87static int altr_spi_readl(struct altera_spi *hw, unsigned int reg,
  88                          unsigned int *val)
  89{
  90        int ret;
  91
  92        ret = regmap_read(hw->regmap, hw->regoff + reg, val);
  93        if (ret)
  94                dev_err(hw->dev, "fail to read reg 0x%x: %d\n", reg, ret);
  95
  96        return ret;
  97}
  98
  99static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev)
 100{
 101        return spi_master_get_devdata(sdev->master);
 102}
 103
 104static void altera_spi_set_cs(struct spi_device *spi, bool is_high)
 105{
 106        struct altera_spi *hw = altera_spi_to_hw(spi);
 107
 108        if (is_high) {
 109                hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK;
 110                altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
 111                altr_spi_writel(hw, ALTERA_SPI_SLAVE_SEL, 0);
 112        } else {
 113                altr_spi_writel(hw, ALTERA_SPI_SLAVE_SEL,
 114                                BIT(spi->chip_select));
 115                hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK;
 116                altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
 117        }
 118}
 119
 120static void altera_spi_tx_word(struct altera_spi *hw)
 121{
 122        unsigned int txd = 0;
 123
 124        if (hw->tx) {
 125                switch (hw->bytes_per_word) {
 126                case 1:
 127                        txd = hw->tx[hw->count];
 128                        break;
 129                case 2:
 130                        txd = (hw->tx[hw->count * 2]
 131                                | (hw->tx[hw->count * 2 + 1] << 8));
 132                        break;
 133                case 4:
 134                        txd = (hw->tx[hw->count * 4]
 135                                | (hw->tx[hw->count * 4 + 1] << 8)
 136                                | (hw->tx[hw->count * 4 + 2] << 16)
 137                                | (hw->tx[hw->count * 4 + 3] << 24));
 138                        break;
 139
 140                }
 141        }
 142
 143        altr_spi_writel(hw, ALTERA_SPI_TXDATA, txd);
 144}
 145
 146static void altera_spi_rx_word(struct altera_spi *hw)
 147{
 148        unsigned int rxd;
 149
 150        altr_spi_readl(hw, ALTERA_SPI_RXDATA, &rxd);
 151        if (hw->rx) {
 152                switch (hw->bytes_per_word) {
 153                case 1:
 154                        hw->rx[hw->count] = rxd;
 155                        break;
 156                case 2:
 157                        hw->rx[hw->count * 2] = rxd;
 158                        hw->rx[hw->count * 2 + 1] = rxd >> 8;
 159                        break;
 160                case 4:
 161                        hw->rx[hw->count * 4] = rxd;
 162                        hw->rx[hw->count * 4 + 1] = rxd >> 8;
 163                        hw->rx[hw->count * 4 + 2] = rxd >> 16;
 164                        hw->rx[hw->count * 4 + 3] = rxd >> 24;
 165                        break;
 166
 167                }
 168        }
 169
 170        hw->count++;
 171}
 172
 173static int altera_spi_txrx(struct spi_master *master,
 174        struct spi_device *spi, struct spi_transfer *t)
 175{
 176        struct altera_spi *hw = spi_master_get_devdata(master);
 177        u32 val;
 178
 179        hw->tx = t->tx_buf;
 180        hw->rx = t->rx_buf;
 181        hw->count = 0;
 182        hw->bytes_per_word = DIV_ROUND_UP(t->bits_per_word, 8);
 183        hw->len = t->len / hw->bytes_per_word;
 184
 185        if (hw->irq >= 0) {
 186                /* enable receive interrupt */
 187                hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK;
 188                altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
 189
 190                /* send the first byte */
 191                altera_spi_tx_word(hw);
 192
 193                return 1;
 194        }
 195
 196        while (hw->count < hw->len) {
 197                altera_spi_tx_word(hw);
 198
 199                for (;;) {
 200                        altr_spi_readl(hw, ALTERA_SPI_STATUS, &val);
 201                        if (val & ALTERA_SPI_STATUS_RRDY_MSK)
 202                                break;
 203
 204                        cpu_relax();
 205                }
 206
 207                altera_spi_rx_word(hw);
 208        }
 209        spi_finalize_current_transfer(master);
 210
 211        return 0;
 212}
 213
 214static irqreturn_t altera_spi_irq(int irq, void *dev)
 215{
 216        struct spi_master *master = dev;
 217        struct altera_spi *hw = spi_master_get_devdata(master);
 218
 219        altera_spi_rx_word(hw);
 220
 221        if (hw->count < hw->len) {
 222                altera_spi_tx_word(hw);
 223        } else {
 224                /* disable receive interrupt */
 225                hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK;
 226                altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
 227
 228                spi_finalize_current_transfer(master);
 229        }
 230
 231        return IRQ_HANDLED;
 232}
 233
 234static int altera_spi_probe(struct platform_device *pdev)
 235{
 236        const struct platform_device_id *platid = platform_get_device_id(pdev);
 237        struct altera_spi_platform_data *pdata = dev_get_platdata(&pdev->dev);
 238        enum altera_spi_type type = ALTERA_SPI_TYPE_UNKNOWN;
 239        struct altera_spi *hw;
 240        struct spi_master *master;
 241        int err = -ENODEV;
 242        u32 val;
 243        u16 i;
 244
 245        master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi));
 246        if (!master)
 247                return err;
 248
 249        /* setup the master state. */
 250        master->bus_num = pdev->id;
 251
 252        if (pdata) {
 253                if (pdata->num_chipselect > ALTERA_SPI_MAX_CS) {
 254                        dev_err(&pdev->dev,
 255                                "Invalid number of chipselect: %hu\n",
 256                                pdata->num_chipselect);
 257                        err = -EINVAL;
 258                        goto exit;
 259                }
 260
 261                master->num_chipselect = pdata->num_chipselect;
 262                master->mode_bits = pdata->mode_bits;
 263                master->bits_per_word_mask = pdata->bits_per_word_mask;
 264        } else {
 265                master->num_chipselect = 16;
 266                master->mode_bits = SPI_CS_HIGH;
 267                master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16);
 268        }
 269
 270        master->dev.of_node = pdev->dev.of_node;
 271        master->transfer_one = altera_spi_txrx;
 272        master->set_cs = altera_spi_set_cs;
 273
 274        hw = spi_master_get_devdata(master);
 275        hw->dev = &pdev->dev;
 276
 277        if (platid)
 278                type = platid->driver_data;
 279
 280        /* find and map our resources */
 281        if (type == ALTERA_SPI_TYPE_SUBDEV) {
 282                struct resource *regoff;
 283
 284                hw->regmap = dev_get_regmap(pdev->dev.parent, NULL);
 285                if (!hw->regmap) {
 286                        dev_err(&pdev->dev, "get regmap failed\n");
 287                        goto exit;
 288                }
 289
 290                regoff = platform_get_resource(pdev, IORESOURCE_REG, 0);
 291                if (regoff)
 292                        hw->regoff = regoff->start;
 293        } else {
 294                void __iomem *res;
 295
 296                res = devm_platform_ioremap_resource(pdev, 0);
 297                if (IS_ERR(res)) {
 298                        err = PTR_ERR(res);
 299                        goto exit;
 300                }
 301
 302                hw->regmap = devm_regmap_init_mmio(&pdev->dev, res,
 303                                                   &spi_altera_config);
 304                if (IS_ERR(hw->regmap)) {
 305                        dev_err(&pdev->dev, "regmap mmio init failed\n");
 306                        err = PTR_ERR(hw->regmap);
 307                        goto exit;
 308                }
 309        }
 310
 311        /* program defaults into the registers */
 312        hw->imr = 0;            /* disable spi interrupts */
 313        altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
 314        altr_spi_writel(hw, ALTERA_SPI_STATUS, 0);      /* clear status reg */
 315        altr_spi_readl(hw, ALTERA_SPI_STATUS, &val);
 316        if (val & ALTERA_SPI_STATUS_RRDY_MSK)
 317                altr_spi_readl(hw, ALTERA_SPI_RXDATA, &val); /* flush rxdata */
 318        /* irq is optional */
 319        hw->irq = platform_get_irq(pdev, 0);
 320        if (hw->irq >= 0) {
 321                err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0,
 322                                       pdev->name, master);
 323                if (err)
 324                        goto exit;
 325        }
 326
 327        err = devm_spi_register_master(&pdev->dev, master);
 328        if (err)
 329                goto exit;
 330
 331        if (pdata) {
 332                for (i = 0; i < pdata->num_devices; i++) {
 333                        if (!spi_new_device(master, pdata->devices + i))
 334                                dev_warn(&pdev->dev,
 335                                         "unable to create SPI device: %s\n",
 336                                         pdata->devices[i].modalias);
 337                }
 338        }
 339
 340        dev_info(&pdev->dev, "regoff %u, irq %d\n", hw->regoff, hw->irq);
 341
 342        return 0;
 343exit:
 344        spi_master_put(master);
 345        return err;
 346}
 347
 348#ifdef CONFIG_OF
 349static const struct of_device_id altera_spi_match[] = {
 350        { .compatible = "ALTR,spi-1.0", },
 351        { .compatible = "altr,spi-1.0", },
 352        {},
 353};
 354MODULE_DEVICE_TABLE(of, altera_spi_match);
 355#endif /* CONFIG_OF */
 356
 357static const struct platform_device_id altera_spi_ids[] = {
 358        { DRV_NAME,             ALTERA_SPI_TYPE_UNKNOWN },
 359        { "subdev_spi_altera",  ALTERA_SPI_TYPE_SUBDEV },
 360        { }
 361};
 362MODULE_DEVICE_TABLE(platform, altera_spi_ids);
 363
 364static struct platform_driver altera_spi_driver = {
 365        .probe = altera_spi_probe,
 366        .driver = {
 367                .name = DRV_NAME,
 368                .pm = NULL,
 369                .of_match_table = of_match_ptr(altera_spi_match),
 370        },
 371        .id_table       = altera_spi_ids,
 372};
 373module_platform_driver(altera_spi_driver);
 374
 375MODULE_DESCRIPTION("Altera SPI driver");
 376MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
 377MODULE_LICENSE("GPL");
 378MODULE_ALIAS("platform:" DRV_NAME);
 379