linux/drivers/spi/spi-axi-spi-engine.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * SPI-Engine SPI controller driver
   4 * Copyright 2015 Analog Devices Inc.
   5 *  Author: Lars-Peter Clausen <lars@metafoo.de>
   6 */
   7
   8#include <linux/clk.h>
   9#include <linux/interrupt.h>
  10#include <linux/io.h>
  11#include <linux/of.h>
  12#include <linux/module.h>
  13#include <linux/platform_device.h>
  14#include <linux/spi/spi.h>
  15
  16#define SPI_ENGINE_VERSION_MAJOR(x)     ((x >> 16) & 0xff)
  17#define SPI_ENGINE_VERSION_MINOR(x)     ((x >> 8) & 0xff)
  18#define SPI_ENGINE_VERSION_PATCH(x)     (x & 0xff)
  19
  20#define SPI_ENGINE_REG_VERSION                  0x00
  21
  22#define SPI_ENGINE_REG_RESET                    0x40
  23
  24#define SPI_ENGINE_REG_INT_ENABLE               0x80
  25#define SPI_ENGINE_REG_INT_PENDING              0x84
  26#define SPI_ENGINE_REG_INT_SOURCE               0x88
  27
  28#define SPI_ENGINE_REG_SYNC_ID                  0xc0
  29
  30#define SPI_ENGINE_REG_CMD_FIFO_ROOM            0xd0
  31#define SPI_ENGINE_REG_SDO_FIFO_ROOM            0xd4
  32#define SPI_ENGINE_REG_SDI_FIFO_LEVEL           0xd8
  33
  34#define SPI_ENGINE_REG_CMD_FIFO                 0xe0
  35#define SPI_ENGINE_REG_SDO_DATA_FIFO            0xe4
  36#define SPI_ENGINE_REG_SDI_DATA_FIFO            0xe8
  37#define SPI_ENGINE_REG_SDI_DATA_FIFO_PEEK       0xec
  38
  39#define SPI_ENGINE_INT_CMD_ALMOST_EMPTY         BIT(0)
  40#define SPI_ENGINE_INT_SDO_ALMOST_EMPTY         BIT(1)
  41#define SPI_ENGINE_INT_SDI_ALMOST_FULL          BIT(2)
  42#define SPI_ENGINE_INT_SYNC                     BIT(3)
  43
  44#define SPI_ENGINE_CONFIG_CPHA                  BIT(0)
  45#define SPI_ENGINE_CONFIG_CPOL                  BIT(1)
  46#define SPI_ENGINE_CONFIG_3WIRE                 BIT(2)
  47
  48#define SPI_ENGINE_INST_TRANSFER                0x0
  49#define SPI_ENGINE_INST_ASSERT                  0x1
  50#define SPI_ENGINE_INST_WRITE                   0x2
  51#define SPI_ENGINE_INST_MISC                    0x3
  52
  53#define SPI_ENGINE_CMD_REG_CLK_DIV              0x0
  54#define SPI_ENGINE_CMD_REG_CONFIG               0x1
  55
  56#define SPI_ENGINE_MISC_SYNC                    0x0
  57#define SPI_ENGINE_MISC_SLEEP                   0x1
  58
  59#define SPI_ENGINE_TRANSFER_WRITE               0x1
  60#define SPI_ENGINE_TRANSFER_READ                0x2
  61
  62#define SPI_ENGINE_CMD(inst, arg1, arg2) \
  63        (((inst) << 12) | ((arg1) << 8) | (arg2))
  64
  65#define SPI_ENGINE_CMD_TRANSFER(flags, n) \
  66        SPI_ENGINE_CMD(SPI_ENGINE_INST_TRANSFER, (flags), (n))
  67#define SPI_ENGINE_CMD_ASSERT(delay, cs) \
  68        SPI_ENGINE_CMD(SPI_ENGINE_INST_ASSERT, (delay), (cs))
  69#define SPI_ENGINE_CMD_WRITE(reg, val) \
  70        SPI_ENGINE_CMD(SPI_ENGINE_INST_WRITE, (reg), (val))
  71#define SPI_ENGINE_CMD_SLEEP(delay) \
  72        SPI_ENGINE_CMD(SPI_ENGINE_INST_MISC, SPI_ENGINE_MISC_SLEEP, (delay))
  73#define SPI_ENGINE_CMD_SYNC(id) \
  74        SPI_ENGINE_CMD(SPI_ENGINE_INST_MISC, SPI_ENGINE_MISC_SYNC, (id))
  75
  76struct spi_engine_program {
  77        unsigned int length;
  78        uint16_t instructions[];
  79};
  80
  81struct spi_engine {
  82        struct clk *clk;
  83        struct clk *ref_clk;
  84
  85        spinlock_t lock;
  86
  87        void __iomem *base;
  88
  89        struct spi_message *msg;
  90        struct spi_engine_program *p;
  91        unsigned cmd_length;
  92        const uint16_t *cmd_buf;
  93
  94        struct spi_transfer *tx_xfer;
  95        unsigned int tx_length;
  96        const uint8_t *tx_buf;
  97
  98        struct spi_transfer *rx_xfer;
  99        unsigned int rx_length;
 100        uint8_t *rx_buf;
 101
 102        unsigned int sync_id;
 103        unsigned int completed_id;
 104
 105        unsigned int int_enable;
 106};
 107
 108static void spi_engine_program_add_cmd(struct spi_engine_program *p,
 109        bool dry, uint16_t cmd)
 110{
 111        if (!dry)
 112                p->instructions[p->length] = cmd;
 113        p->length++;
 114}
 115
 116static unsigned int spi_engine_get_config(struct spi_device *spi)
 117{
 118        unsigned int config = 0;
 119
 120        if (spi->mode & SPI_CPOL)
 121                config |= SPI_ENGINE_CONFIG_CPOL;
 122        if (spi->mode & SPI_CPHA)
 123                config |= SPI_ENGINE_CONFIG_CPHA;
 124        if (spi->mode & SPI_3WIRE)
 125                config |= SPI_ENGINE_CONFIG_3WIRE;
 126
 127        return config;
 128}
 129
 130static unsigned int spi_engine_get_clk_div(struct spi_engine *spi_engine,
 131        struct spi_device *spi, struct spi_transfer *xfer)
 132{
 133        unsigned int clk_div;
 134
 135        clk_div = DIV_ROUND_UP(clk_get_rate(spi_engine->ref_clk),
 136                xfer->speed_hz * 2);
 137        if (clk_div > 255)
 138                clk_div = 255;
 139        else if (clk_div > 0)
 140                clk_div -= 1;
 141
 142        return clk_div;
 143}
 144
 145static void spi_engine_gen_xfer(struct spi_engine_program *p, bool dry,
 146        struct spi_transfer *xfer)
 147{
 148        unsigned int len = xfer->len;
 149
 150        while (len) {
 151                unsigned int n = min(len, 256U);
 152                unsigned int flags = 0;
 153
 154                if (xfer->tx_buf)
 155                        flags |= SPI_ENGINE_TRANSFER_WRITE;
 156                if (xfer->rx_buf)
 157                        flags |= SPI_ENGINE_TRANSFER_READ;
 158
 159                spi_engine_program_add_cmd(p, dry,
 160                        SPI_ENGINE_CMD_TRANSFER(flags, n - 1));
 161                len -= n;
 162        }
 163}
 164
 165static void spi_engine_gen_sleep(struct spi_engine_program *p, bool dry,
 166        struct spi_engine *spi_engine, unsigned int clk_div,
 167        struct spi_transfer *xfer)
 168{
 169        unsigned int spi_clk = clk_get_rate(spi_engine->ref_clk);
 170        unsigned int t;
 171        int delay;
 172
 173        delay = spi_delay_to_ns(&xfer->delay, xfer);
 174        if (delay < 0)
 175                return;
 176        delay /= 1000;
 177
 178        if (delay == 0)
 179                return;
 180
 181        t = DIV_ROUND_UP(delay * spi_clk, (clk_div + 1) * 2);
 182        while (t) {
 183                unsigned int n = min(t, 256U);
 184
 185                spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_SLEEP(n - 1));
 186                t -= n;
 187        }
 188}
 189
 190static void spi_engine_gen_cs(struct spi_engine_program *p, bool dry,
 191                struct spi_device *spi, bool assert)
 192{
 193        unsigned int mask = 0xff;
 194
 195        if (assert)
 196                mask ^= BIT(spi->chip_select);
 197
 198        spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_ASSERT(1, mask));
 199}
 200
 201static int spi_engine_compile_message(struct spi_engine *spi_engine,
 202        struct spi_message *msg, bool dry, struct spi_engine_program *p)
 203{
 204        struct spi_device *spi = msg->spi;
 205        struct spi_transfer *xfer;
 206        int clk_div, new_clk_div;
 207        bool cs_change = true;
 208
 209        clk_div = -1;
 210
 211        spi_engine_program_add_cmd(p, dry,
 212                SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CONFIG,
 213                        spi_engine_get_config(spi)));
 214
 215        list_for_each_entry(xfer, &msg->transfers, transfer_list) {
 216                new_clk_div = spi_engine_get_clk_div(spi_engine, spi, xfer);
 217                if (new_clk_div != clk_div) {
 218                        clk_div = new_clk_div;
 219                        spi_engine_program_add_cmd(p, dry,
 220                                SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CLK_DIV,
 221                                        clk_div));
 222                }
 223
 224                if (cs_change)
 225                        spi_engine_gen_cs(p, dry, spi, true);
 226
 227                spi_engine_gen_xfer(p, dry, xfer);
 228                spi_engine_gen_sleep(p, dry, spi_engine, clk_div, xfer);
 229
 230                cs_change = xfer->cs_change;
 231                if (list_is_last(&xfer->transfer_list, &msg->transfers))
 232                        cs_change = !cs_change;
 233
 234                if (cs_change)
 235                        spi_engine_gen_cs(p, dry, spi, false);
 236        }
 237
 238        return 0;
 239}
 240
 241static void spi_engine_xfer_next(struct spi_engine *spi_engine,
 242        struct spi_transfer **_xfer)
 243{
 244        struct spi_message *msg = spi_engine->msg;
 245        struct spi_transfer *xfer = *_xfer;
 246
 247        if (!xfer) {
 248                xfer = list_first_entry(&msg->transfers,
 249                        struct spi_transfer, transfer_list);
 250        } else if (list_is_last(&xfer->transfer_list, &msg->transfers)) {
 251                xfer = NULL;
 252        } else {
 253                xfer = list_next_entry(xfer, transfer_list);
 254        }
 255
 256        *_xfer = xfer;
 257}
 258
 259static void spi_engine_tx_next(struct spi_engine *spi_engine)
 260{
 261        struct spi_transfer *xfer = spi_engine->tx_xfer;
 262
 263        do {
 264                spi_engine_xfer_next(spi_engine, &xfer);
 265        } while (xfer && !xfer->tx_buf);
 266
 267        spi_engine->tx_xfer = xfer;
 268        if (xfer) {
 269                spi_engine->tx_length = xfer->len;
 270                spi_engine->tx_buf = xfer->tx_buf;
 271        } else {
 272                spi_engine->tx_buf = NULL;
 273        }
 274}
 275
 276static void spi_engine_rx_next(struct spi_engine *spi_engine)
 277{
 278        struct spi_transfer *xfer = spi_engine->rx_xfer;
 279
 280        do {
 281                spi_engine_xfer_next(spi_engine, &xfer);
 282        } while (xfer && !xfer->rx_buf);
 283
 284        spi_engine->rx_xfer = xfer;
 285        if (xfer) {
 286                spi_engine->rx_length = xfer->len;
 287                spi_engine->rx_buf = xfer->rx_buf;
 288        } else {
 289                spi_engine->rx_buf = NULL;
 290        }
 291}
 292
 293static bool spi_engine_write_cmd_fifo(struct spi_engine *spi_engine)
 294{
 295        void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_CMD_FIFO;
 296        unsigned int n, m, i;
 297        const uint16_t *buf;
 298
 299        n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_CMD_FIFO_ROOM);
 300        while (n && spi_engine->cmd_length) {
 301                m = min(n, spi_engine->cmd_length);
 302                buf = spi_engine->cmd_buf;
 303                for (i = 0; i < m; i++)
 304                        writel_relaxed(buf[i], addr);
 305                spi_engine->cmd_buf += m;
 306                spi_engine->cmd_length -= m;
 307                n -= m;
 308        }
 309
 310        return spi_engine->cmd_length != 0;
 311}
 312
 313static bool spi_engine_write_tx_fifo(struct spi_engine *spi_engine)
 314{
 315        void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDO_DATA_FIFO;
 316        unsigned int n, m, i;
 317        const uint8_t *buf;
 318
 319        n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDO_FIFO_ROOM);
 320        while (n && spi_engine->tx_length) {
 321                m = min(n, spi_engine->tx_length);
 322                buf = spi_engine->tx_buf;
 323                for (i = 0; i < m; i++)
 324                        writel_relaxed(buf[i], addr);
 325                spi_engine->tx_buf += m;
 326                spi_engine->tx_length -= m;
 327                n -= m;
 328                if (spi_engine->tx_length == 0)
 329                        spi_engine_tx_next(spi_engine);
 330        }
 331
 332        return spi_engine->tx_length != 0;
 333}
 334
 335static bool spi_engine_read_rx_fifo(struct spi_engine *spi_engine)
 336{
 337        void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDI_DATA_FIFO;
 338        unsigned int n, m, i;
 339        uint8_t *buf;
 340
 341        n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDI_FIFO_LEVEL);
 342        while (n && spi_engine->rx_length) {
 343                m = min(n, spi_engine->rx_length);
 344                buf = spi_engine->rx_buf;
 345                for (i = 0; i < m; i++)
 346                        buf[i] = readl_relaxed(addr);
 347                spi_engine->rx_buf += m;
 348                spi_engine->rx_length -= m;
 349                n -= m;
 350                if (spi_engine->rx_length == 0)
 351                        spi_engine_rx_next(spi_engine);
 352        }
 353
 354        return spi_engine->rx_length != 0;
 355}
 356
 357static irqreturn_t spi_engine_irq(int irq, void *devid)
 358{
 359        struct spi_master *master = devid;
 360        struct spi_engine *spi_engine = spi_master_get_devdata(master);
 361        unsigned int disable_int = 0;
 362        unsigned int pending;
 363
 364        pending = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
 365
 366        if (pending & SPI_ENGINE_INT_SYNC) {
 367                writel_relaxed(SPI_ENGINE_INT_SYNC,
 368                        spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
 369                spi_engine->completed_id = readl_relaxed(
 370                        spi_engine->base + SPI_ENGINE_REG_SYNC_ID);
 371        }
 372
 373        spin_lock(&spi_engine->lock);
 374
 375        if (pending & SPI_ENGINE_INT_CMD_ALMOST_EMPTY) {
 376                if (!spi_engine_write_cmd_fifo(spi_engine))
 377                        disable_int |= SPI_ENGINE_INT_CMD_ALMOST_EMPTY;
 378        }
 379
 380        if (pending & SPI_ENGINE_INT_SDO_ALMOST_EMPTY) {
 381                if (!spi_engine_write_tx_fifo(spi_engine))
 382                        disable_int |= SPI_ENGINE_INT_SDO_ALMOST_EMPTY;
 383        }
 384
 385        if (pending & (SPI_ENGINE_INT_SDI_ALMOST_FULL | SPI_ENGINE_INT_SYNC)) {
 386                if (!spi_engine_read_rx_fifo(spi_engine))
 387                        disable_int |= SPI_ENGINE_INT_SDI_ALMOST_FULL;
 388        }
 389
 390        if (pending & SPI_ENGINE_INT_SYNC) {
 391                if (spi_engine->msg &&
 392                    spi_engine->completed_id == spi_engine->sync_id) {
 393                        struct spi_message *msg = spi_engine->msg;
 394
 395                        kfree(spi_engine->p);
 396                        msg->status = 0;
 397                        msg->actual_length = msg->frame_length;
 398                        spi_engine->msg = NULL;
 399                        spi_finalize_current_message(master);
 400                        disable_int |= SPI_ENGINE_INT_SYNC;
 401                }
 402        }
 403
 404        if (disable_int) {
 405                spi_engine->int_enable &= ~disable_int;
 406                writel_relaxed(spi_engine->int_enable,
 407                        spi_engine->base + SPI_ENGINE_REG_INT_ENABLE);
 408        }
 409
 410        spin_unlock(&spi_engine->lock);
 411
 412        return IRQ_HANDLED;
 413}
 414
 415static int spi_engine_transfer_one_message(struct spi_master *master,
 416        struct spi_message *msg)
 417{
 418        struct spi_engine_program p_dry, *p;
 419        struct spi_engine *spi_engine = spi_master_get_devdata(master);
 420        unsigned int int_enable = 0;
 421        unsigned long flags;
 422        size_t size;
 423
 424        p_dry.length = 0;
 425        spi_engine_compile_message(spi_engine, msg, true, &p_dry);
 426
 427        size = sizeof(*p->instructions) * (p_dry.length + 1);
 428        p = kzalloc(sizeof(*p) + size, GFP_KERNEL);
 429        if (!p)
 430                return -ENOMEM;
 431        spi_engine_compile_message(spi_engine, msg, false, p);
 432
 433        spin_lock_irqsave(&spi_engine->lock, flags);
 434        spi_engine->sync_id = (spi_engine->sync_id + 1) & 0xff;
 435        spi_engine_program_add_cmd(p, false,
 436                SPI_ENGINE_CMD_SYNC(spi_engine->sync_id));
 437
 438        spi_engine->msg = msg;
 439        spi_engine->p = p;
 440
 441        spi_engine->cmd_buf = p->instructions;
 442        spi_engine->cmd_length = p->length;
 443        if (spi_engine_write_cmd_fifo(spi_engine))
 444                int_enable |= SPI_ENGINE_INT_CMD_ALMOST_EMPTY;
 445
 446        spi_engine_tx_next(spi_engine);
 447        if (spi_engine_write_tx_fifo(spi_engine))
 448                int_enable |= SPI_ENGINE_INT_SDO_ALMOST_EMPTY;
 449
 450        spi_engine_rx_next(spi_engine);
 451        if (spi_engine->rx_length != 0)
 452                int_enable |= SPI_ENGINE_INT_SDI_ALMOST_FULL;
 453
 454        int_enable |= SPI_ENGINE_INT_SYNC;
 455
 456        writel_relaxed(int_enable,
 457                spi_engine->base + SPI_ENGINE_REG_INT_ENABLE);
 458        spi_engine->int_enable = int_enable;
 459        spin_unlock_irqrestore(&spi_engine->lock, flags);
 460
 461        return 0;
 462}
 463
 464static int spi_engine_probe(struct platform_device *pdev)
 465{
 466        struct spi_engine *spi_engine;
 467        struct spi_master *master;
 468        unsigned int version;
 469        int irq;
 470        int ret;
 471
 472        irq = platform_get_irq(pdev, 0);
 473        if (irq <= 0)
 474                return -ENXIO;
 475
 476        spi_engine = devm_kzalloc(&pdev->dev, sizeof(*spi_engine), GFP_KERNEL);
 477        if (!spi_engine)
 478                return -ENOMEM;
 479
 480        master = spi_alloc_master(&pdev->dev, 0);
 481        if (!master)
 482                return -ENOMEM;
 483
 484        spi_master_set_devdata(master, spi_engine);
 485
 486        spin_lock_init(&spi_engine->lock);
 487
 488        spi_engine->clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
 489        if (IS_ERR(spi_engine->clk)) {
 490                ret = PTR_ERR(spi_engine->clk);
 491                goto err_put_master;
 492        }
 493
 494        spi_engine->ref_clk = devm_clk_get(&pdev->dev, "spi_clk");
 495        if (IS_ERR(spi_engine->ref_clk)) {
 496                ret = PTR_ERR(spi_engine->ref_clk);
 497                goto err_put_master;
 498        }
 499
 500        ret = clk_prepare_enable(spi_engine->clk);
 501        if (ret)
 502                goto err_put_master;
 503
 504        ret = clk_prepare_enable(spi_engine->ref_clk);
 505        if (ret)
 506                goto err_clk_disable;
 507
 508        spi_engine->base = devm_platform_ioremap_resource(pdev, 0);
 509        if (IS_ERR(spi_engine->base)) {
 510                ret = PTR_ERR(spi_engine->base);
 511                goto err_ref_clk_disable;
 512        }
 513
 514        version = readl(spi_engine->base + SPI_ENGINE_REG_VERSION);
 515        if (SPI_ENGINE_VERSION_MAJOR(version) != 1) {
 516                dev_err(&pdev->dev, "Unsupported peripheral version %u.%u.%c\n",
 517                        SPI_ENGINE_VERSION_MAJOR(version),
 518                        SPI_ENGINE_VERSION_MINOR(version),
 519                        SPI_ENGINE_VERSION_PATCH(version));
 520                ret = -ENODEV;
 521                goto err_ref_clk_disable;
 522        }
 523
 524        writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_RESET);
 525        writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
 526        writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE);
 527
 528        ret = request_irq(irq, spi_engine_irq, 0, pdev->name, master);
 529        if (ret)
 530                goto err_ref_clk_disable;
 531
 532        master->dev.of_node = pdev->dev.of_node;
 533        master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_3WIRE;
 534        master->bits_per_word_mask = SPI_BPW_MASK(8);
 535        master->max_speed_hz = clk_get_rate(spi_engine->ref_clk) / 2;
 536        master->transfer_one_message = spi_engine_transfer_one_message;
 537        master->num_chipselect = 8;
 538
 539        ret = spi_register_master(master);
 540        if (ret)
 541                goto err_free_irq;
 542
 543        platform_set_drvdata(pdev, master);
 544
 545        return 0;
 546err_free_irq:
 547        free_irq(irq, master);
 548err_ref_clk_disable:
 549        clk_disable_unprepare(spi_engine->ref_clk);
 550err_clk_disable:
 551        clk_disable_unprepare(spi_engine->clk);
 552err_put_master:
 553        spi_master_put(master);
 554        return ret;
 555}
 556
 557static int spi_engine_remove(struct platform_device *pdev)
 558{
 559        struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
 560        struct spi_engine *spi_engine = spi_master_get_devdata(master);
 561        int irq = platform_get_irq(pdev, 0);
 562
 563        spi_unregister_master(master);
 564
 565        free_irq(irq, master);
 566
 567        spi_master_put(master);
 568
 569        writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
 570        writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE);
 571        writel_relaxed(0x01, spi_engine->base + SPI_ENGINE_REG_RESET);
 572
 573        clk_disable_unprepare(spi_engine->ref_clk);
 574        clk_disable_unprepare(spi_engine->clk);
 575
 576        return 0;
 577}
 578
 579static const struct of_device_id spi_engine_match_table[] = {
 580        { .compatible = "adi,axi-spi-engine-1.00.a" },
 581        { },
 582};
 583MODULE_DEVICE_TABLE(of, spi_engine_match_table);
 584
 585static struct platform_driver spi_engine_driver = {
 586        .probe = spi_engine_probe,
 587        .remove = spi_engine_remove,
 588        .driver = {
 589                .name = "spi-engine",
 590                .of_match_table = spi_engine_match_table,
 591        },
 592};
 593module_platform_driver(spi_engine_driver);
 594
 595MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
 596MODULE_DESCRIPTION("Analog Devices SPI engine peripheral driver");
 597MODULE_LICENSE("GPL");
 598