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        } else {
 193                while (hw->count < hw->len) {
 194                        altera_spi_tx_word(hw);
 195
 196                        for (;;) {
 197                                altr_spi_readl(hw, ALTERA_SPI_STATUS, &val);
 198                                if (val & ALTERA_SPI_STATUS_RRDY_MSK)
 199                                        break;
 200
 201                                cpu_relax();
 202                        }
 203
 204                        altera_spi_rx_word(hw);
 205                }
 206                spi_finalize_current_transfer(master);
 207        }
 208
 209        return t->len;
 210}
 211
 212static irqreturn_t altera_spi_irq(int irq, void *dev)
 213{
 214        struct spi_master *master = dev;
 215        struct altera_spi *hw = spi_master_get_devdata(master);
 216
 217        altera_spi_rx_word(hw);
 218
 219        if (hw->count < hw->len) {
 220                altera_spi_tx_word(hw);
 221        } else {
 222                /* disable receive interrupt */
 223                hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK;
 224                altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
 225
 226                spi_finalize_current_transfer(master);
 227        }
 228
 229        return IRQ_HANDLED;
 230}
 231
 232static int altera_spi_probe(struct platform_device *pdev)
 233{
 234        const struct platform_device_id *platid = platform_get_device_id(pdev);
 235        struct altera_spi_platform_data *pdata = dev_get_platdata(&pdev->dev);
 236        enum altera_spi_type type = ALTERA_SPI_TYPE_UNKNOWN;
 237        struct altera_spi *hw;
 238        struct spi_master *master;
 239        int err = -ENODEV;
 240        u32 val;
 241        u16 i;
 242
 243        master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi));
 244        if (!master)
 245                return err;
 246
 247        /* setup the master state. */
 248        master->bus_num = pdev->id;
 249
 250        if (pdata) {
 251                if (pdata->num_chipselect > ALTERA_SPI_MAX_CS) {
 252                        dev_err(&pdev->dev,
 253                                "Invalid number of chipselect: %hu\n",
 254                                pdata->num_chipselect);
 255                        return -EINVAL;
 256                }
 257
 258                master->num_chipselect = pdata->num_chipselect;
 259                master->mode_bits = pdata->mode_bits;
 260                master->bits_per_word_mask = pdata->bits_per_word_mask;
 261        } else {
 262                master->num_chipselect = 16;
 263                master->mode_bits = SPI_CS_HIGH;
 264                master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16);
 265        }
 266
 267        master->dev.of_node = pdev->dev.of_node;
 268        master->transfer_one = altera_spi_txrx;
 269        master->set_cs = altera_spi_set_cs;
 270
 271        hw = spi_master_get_devdata(master);
 272        hw->dev = &pdev->dev;
 273
 274        if (platid)
 275                type = platid->driver_data;
 276
 277        /* find and map our resources */
 278        if (type == ALTERA_SPI_TYPE_SUBDEV) {
 279                struct resource *regoff;
 280
 281                hw->regmap = dev_get_regmap(pdev->dev.parent, NULL);
 282                if (!hw->regmap) {
 283                        dev_err(&pdev->dev, "get regmap failed\n");
 284                        goto exit;
 285                }
 286
 287                regoff = platform_get_resource(pdev, IORESOURCE_REG, 0);
 288                if (regoff)
 289                        hw->regoff = regoff->start;
 290        } else {
 291                void __iomem *res;
 292
 293                res = devm_platform_ioremap_resource(pdev, 0);
 294                if (IS_ERR(res)) {
 295                        err = PTR_ERR(res);
 296                        goto exit;
 297                }
 298
 299                hw->regmap = devm_regmap_init_mmio(&pdev->dev, res,
 300                                                   &spi_altera_config);
 301                if (IS_ERR(hw->regmap)) {
 302                        dev_err(&pdev->dev, "regmap mmio init failed\n");
 303                        err = PTR_ERR(hw->regmap);
 304                        goto exit;
 305                }
 306        }
 307
 308        /* program defaults into the registers */
 309        hw->imr = 0;            /* disable spi interrupts */
 310        altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
 311        altr_spi_writel(hw, ALTERA_SPI_STATUS, 0);      /* clear status reg */
 312        altr_spi_readl(hw, ALTERA_SPI_STATUS, &val);
 313        if (val & ALTERA_SPI_STATUS_RRDY_MSK)
 314                altr_spi_readl(hw, ALTERA_SPI_RXDATA, &val); /* flush rxdata */
 315        /* irq is optional */
 316        hw->irq = platform_get_irq(pdev, 0);
 317        if (hw->irq >= 0) {
 318                err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0,
 319                                       pdev->name, master);
 320                if (err)
 321                        goto exit;
 322        }
 323
 324        err = devm_spi_register_master(&pdev->dev, master);
 325        if (err)
 326                goto exit;
 327
 328        if (pdata) {
 329                for (i = 0; i < pdata->num_devices; i++) {
 330                        if (!spi_new_device(master, pdata->devices + i))
 331                                dev_warn(&pdev->dev,
 332                                         "unable to create SPI device: %s\n",
 333                                         pdata->devices[i].modalias);
 334                }
 335        }
 336
 337        dev_info(&pdev->dev, "regoff %u, irq %d\n", hw->regoff, hw->irq);
 338
 339        return 0;
 340exit:
 341        spi_master_put(master);
 342        return err;
 343}
 344
 345#ifdef CONFIG_OF
 346static const struct of_device_id altera_spi_match[] = {
 347        { .compatible = "ALTR,spi-1.0", },
 348        { .compatible = "altr,spi-1.0", },
 349        {},
 350};
 351MODULE_DEVICE_TABLE(of, altera_spi_match);
 352#endif /* CONFIG_OF */
 353
 354static const struct platform_device_id altera_spi_ids[] = {
 355        { DRV_NAME,             ALTERA_SPI_TYPE_UNKNOWN },
 356        { "subdev_spi_altera",  ALTERA_SPI_TYPE_SUBDEV },
 357        { }
 358};
 359MODULE_DEVICE_TABLE(platform, altera_spi_ids);
 360
 361static struct platform_driver altera_spi_driver = {
 362        .probe = altera_spi_probe,
 363        .driver = {
 364                .name = DRV_NAME,
 365                .pm = NULL,
 366                .of_match_table = of_match_ptr(altera_spi_match),
 367        },
 368        .id_table       = altera_spi_ids,
 369};
 370module_platform_driver(altera_spi_driver);
 371
 372MODULE_DESCRIPTION("Altera SPI driver");
 373MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
 374MODULE_LICENSE("GPL");
 375MODULE_ALIAS("platform:" DRV_NAME);
 376