Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * OpenCores tiny SPI master driver
0004  *
0005  * https://opencores.org/project,tiny_spi
0006  *
0007  * Copyright (C) 2011 Thomas Chou <thomas@wytron.com.tw>
0008  *
0009  * Based on spi_s3c24xx.c, which is:
0010  * Copyright (c) 2006 Ben Dooks
0011  * Copyright (c) 2006 Simtec Electronics
0012  *  Ben Dooks <ben@simtec.co.uk>
0013  */
0014 
0015 #include <linux/interrupt.h>
0016 #include <linux/errno.h>
0017 #include <linux/module.h>
0018 #include <linux/platform_device.h>
0019 #include <linux/spi/spi.h>
0020 #include <linux/spi/spi_bitbang.h>
0021 #include <linux/spi/spi_oc_tiny.h>
0022 #include <linux/io.h>
0023 #include <linux/of.h>
0024 
0025 #define DRV_NAME "spi_oc_tiny"
0026 
0027 #define TINY_SPI_RXDATA 0
0028 #define TINY_SPI_TXDATA 4
0029 #define TINY_SPI_STATUS 8
0030 #define TINY_SPI_CONTROL 12
0031 #define TINY_SPI_BAUD 16
0032 
0033 #define TINY_SPI_STATUS_TXE 0x1
0034 #define TINY_SPI_STATUS_TXR 0x2
0035 
0036 struct tiny_spi {
0037     /* bitbang has to be first */
0038     struct spi_bitbang bitbang;
0039     struct completion done;
0040 
0041     void __iomem *base;
0042     int irq;
0043     unsigned int freq;
0044     unsigned int baudwidth;
0045     unsigned int baud;
0046     unsigned int speed_hz;
0047     unsigned int mode;
0048     unsigned int len;
0049     unsigned int txc, rxc;
0050     const u8 *txp;
0051     u8 *rxp;
0052 };
0053 
0054 static inline struct tiny_spi *tiny_spi_to_hw(struct spi_device *sdev)
0055 {
0056     return spi_master_get_devdata(sdev->master);
0057 }
0058 
0059 static unsigned int tiny_spi_baud(struct spi_device *spi, unsigned int hz)
0060 {
0061     struct tiny_spi *hw = tiny_spi_to_hw(spi);
0062 
0063     return min(DIV_ROUND_UP(hw->freq, hz * 2), (1U << hw->baudwidth)) - 1;
0064 }
0065 
0066 static int tiny_spi_setup_transfer(struct spi_device *spi,
0067                    struct spi_transfer *t)
0068 {
0069     struct tiny_spi *hw = tiny_spi_to_hw(spi);
0070     unsigned int baud = hw->baud;
0071 
0072     if (t) {
0073         if (t->speed_hz && t->speed_hz != hw->speed_hz)
0074             baud = tiny_spi_baud(spi, t->speed_hz);
0075     }
0076     writel(baud, hw->base + TINY_SPI_BAUD);
0077     writel(hw->mode, hw->base + TINY_SPI_CONTROL);
0078     return 0;
0079 }
0080 
0081 static int tiny_spi_setup(struct spi_device *spi)
0082 {
0083     struct tiny_spi *hw = tiny_spi_to_hw(spi);
0084 
0085     if (spi->max_speed_hz != hw->speed_hz) {
0086         hw->speed_hz = spi->max_speed_hz;
0087         hw->baud = tiny_spi_baud(spi, hw->speed_hz);
0088     }
0089     hw->mode = spi->mode & SPI_MODE_X_MASK;
0090     return 0;
0091 }
0092 
0093 static inline void tiny_spi_wait_txr(struct tiny_spi *hw)
0094 {
0095     while (!(readb(hw->base + TINY_SPI_STATUS) &
0096          TINY_SPI_STATUS_TXR))
0097         cpu_relax();
0098 }
0099 
0100 static inline void tiny_spi_wait_txe(struct tiny_spi *hw)
0101 {
0102     while (!(readb(hw->base + TINY_SPI_STATUS) &
0103          TINY_SPI_STATUS_TXE))
0104         cpu_relax();
0105 }
0106 
0107 static int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
0108 {
0109     struct tiny_spi *hw = tiny_spi_to_hw(spi);
0110     const u8 *txp = t->tx_buf;
0111     u8 *rxp = t->rx_buf;
0112     unsigned int i;
0113 
0114     if (hw->irq >= 0) {
0115         /* use interrupt driven data transfer */
0116         hw->len = t->len;
0117         hw->txp = t->tx_buf;
0118         hw->rxp = t->rx_buf;
0119         hw->txc = 0;
0120         hw->rxc = 0;
0121 
0122         /* send the first byte */
0123         if (t->len > 1) {
0124             writeb(hw->txp ? *hw->txp++ : 0,
0125                    hw->base + TINY_SPI_TXDATA);
0126             hw->txc++;
0127             writeb(hw->txp ? *hw->txp++ : 0,
0128                    hw->base + TINY_SPI_TXDATA);
0129             hw->txc++;
0130             writeb(TINY_SPI_STATUS_TXR, hw->base + TINY_SPI_STATUS);
0131         } else {
0132             writeb(hw->txp ? *hw->txp++ : 0,
0133                    hw->base + TINY_SPI_TXDATA);
0134             hw->txc++;
0135             writeb(TINY_SPI_STATUS_TXE, hw->base + TINY_SPI_STATUS);
0136         }
0137 
0138         wait_for_completion(&hw->done);
0139     } else {
0140         /* we need to tighten the transfer loop */
0141         writeb(txp ? *txp++ : 0, hw->base + TINY_SPI_TXDATA);
0142         for (i = 1; i < t->len; i++) {
0143             writeb(txp ? *txp++ : 0, hw->base + TINY_SPI_TXDATA);
0144 
0145             if (rxp || (i != t->len - 1))
0146                 tiny_spi_wait_txr(hw);
0147             if (rxp)
0148                 *rxp++ = readb(hw->base + TINY_SPI_TXDATA);
0149         }
0150         tiny_spi_wait_txe(hw);
0151         if (rxp)
0152             *rxp++ = readb(hw->base + TINY_SPI_RXDATA);
0153     }
0154 
0155     return t->len;
0156 }
0157 
0158 static irqreturn_t tiny_spi_irq(int irq, void *dev)
0159 {
0160     struct tiny_spi *hw = dev;
0161 
0162     writeb(0, hw->base + TINY_SPI_STATUS);
0163     if (hw->rxc + 1 == hw->len) {
0164         if (hw->rxp)
0165             *hw->rxp++ = readb(hw->base + TINY_SPI_RXDATA);
0166         hw->rxc++;
0167         complete(&hw->done);
0168     } else {
0169         if (hw->rxp)
0170             *hw->rxp++ = readb(hw->base + TINY_SPI_TXDATA);
0171         hw->rxc++;
0172         if (hw->txc < hw->len) {
0173             writeb(hw->txp ? *hw->txp++ : 0,
0174                    hw->base + TINY_SPI_TXDATA);
0175             hw->txc++;
0176             writeb(TINY_SPI_STATUS_TXR,
0177                    hw->base + TINY_SPI_STATUS);
0178         } else {
0179             writeb(TINY_SPI_STATUS_TXE,
0180                    hw->base + TINY_SPI_STATUS);
0181         }
0182     }
0183     return IRQ_HANDLED;
0184 }
0185 
0186 #ifdef CONFIG_OF
0187 #include <linux/of_gpio.h>
0188 
0189 static int tiny_spi_of_probe(struct platform_device *pdev)
0190 {
0191     struct tiny_spi *hw = platform_get_drvdata(pdev);
0192     struct device_node *np = pdev->dev.of_node;
0193     u32 val;
0194 
0195     if (!np)
0196         return 0;
0197     hw->bitbang.master->dev.of_node = pdev->dev.of_node;
0198     if (!of_property_read_u32(np, "clock-frequency", &val))
0199         hw->freq = val;
0200     if (!of_property_read_u32(np, "baud-width", &val))
0201         hw->baudwidth = val;
0202     return 0;
0203 }
0204 #else /* !CONFIG_OF */
0205 static int tiny_spi_of_probe(struct platform_device *pdev)
0206 {
0207     return 0;
0208 }
0209 #endif /* CONFIG_OF */
0210 
0211 static int tiny_spi_probe(struct platform_device *pdev)
0212 {
0213     struct tiny_spi_platform_data *platp = dev_get_platdata(&pdev->dev);
0214     struct tiny_spi *hw;
0215     struct spi_master *master;
0216     int err = -ENODEV;
0217 
0218     master = spi_alloc_master(&pdev->dev, sizeof(struct tiny_spi));
0219     if (!master)
0220         return err;
0221 
0222     /* setup the master state. */
0223     master->bus_num = pdev->id;
0224     master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
0225     master->setup = tiny_spi_setup;
0226     master->use_gpio_descriptors = true;
0227 
0228     hw = spi_master_get_devdata(master);
0229     platform_set_drvdata(pdev, hw);
0230 
0231     /* setup the state for the bitbang driver */
0232     hw->bitbang.master = master;
0233     hw->bitbang.setup_transfer = tiny_spi_setup_transfer;
0234     hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs;
0235 
0236     /* find and map our resources */
0237     hw->base = devm_platform_ioremap_resource(pdev, 0);
0238     if (IS_ERR(hw->base)) {
0239         err = PTR_ERR(hw->base);
0240         goto exit;
0241     }
0242     /* irq is optional */
0243     hw->irq = platform_get_irq(pdev, 0);
0244     if (hw->irq >= 0) {
0245         init_completion(&hw->done);
0246         err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0,
0247                        pdev->name, hw);
0248         if (err)
0249             goto exit;
0250     }
0251     /* find platform data */
0252     if (platp) {
0253         hw->freq = platp->freq;
0254         hw->baudwidth = platp->baudwidth;
0255     } else {
0256         err = tiny_spi_of_probe(pdev);
0257         if (err)
0258             goto exit;
0259     }
0260 
0261     /* register our spi controller */
0262     err = spi_bitbang_start(&hw->bitbang);
0263     if (err)
0264         goto exit;
0265     dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
0266 
0267     return 0;
0268 
0269 exit:
0270     spi_master_put(master);
0271     return err;
0272 }
0273 
0274 static int tiny_spi_remove(struct platform_device *pdev)
0275 {
0276     struct tiny_spi *hw = platform_get_drvdata(pdev);
0277     struct spi_master *master = hw->bitbang.master;
0278 
0279     spi_bitbang_stop(&hw->bitbang);
0280     spi_master_put(master);
0281     return 0;
0282 }
0283 
0284 #ifdef CONFIG_OF
0285 static const struct of_device_id tiny_spi_match[] = {
0286     { .compatible = "opencores,tiny-spi-rtlsvn2", },
0287     {},
0288 };
0289 MODULE_DEVICE_TABLE(of, tiny_spi_match);
0290 #endif /* CONFIG_OF */
0291 
0292 static struct platform_driver tiny_spi_driver = {
0293     .probe = tiny_spi_probe,
0294     .remove = tiny_spi_remove,
0295     .driver = {
0296         .name = DRV_NAME,
0297         .pm = NULL,
0298         .of_match_table = of_match_ptr(tiny_spi_match),
0299     },
0300 };
0301 module_platform_driver(tiny_spi_driver);
0302 
0303 MODULE_DESCRIPTION("OpenCores tiny SPI driver");
0304 MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
0305 MODULE_LICENSE("GPL");
0306 MODULE_ALIAS("platform:" DRV_NAME);