cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

spi-oc-tiny.c (7246B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * OpenCores tiny SPI master driver
      4 *
      5 * https://opencores.org/project,tiny_spi
      6 *
      7 * Copyright (C) 2011 Thomas Chou <thomas@wytron.com.tw>
      8 *
      9 * Based on spi_s3c24xx.c, which is:
     10 * Copyright (c) 2006 Ben Dooks
     11 * Copyright (c) 2006 Simtec Electronics
     12 *	Ben Dooks <ben@simtec.co.uk>
     13 */
     14
     15#include <linux/interrupt.h>
     16#include <linux/errno.h>
     17#include <linux/module.h>
     18#include <linux/platform_device.h>
     19#include <linux/spi/spi.h>
     20#include <linux/spi/spi_bitbang.h>
     21#include <linux/spi/spi_oc_tiny.h>
     22#include <linux/io.h>
     23#include <linux/of.h>
     24
     25#define DRV_NAME "spi_oc_tiny"
     26
     27#define TINY_SPI_RXDATA 0
     28#define TINY_SPI_TXDATA 4
     29#define TINY_SPI_STATUS 8
     30#define TINY_SPI_CONTROL 12
     31#define TINY_SPI_BAUD 16
     32
     33#define TINY_SPI_STATUS_TXE 0x1
     34#define TINY_SPI_STATUS_TXR 0x2
     35
     36struct tiny_spi {
     37	/* bitbang has to be first */
     38	struct spi_bitbang bitbang;
     39	struct completion done;
     40
     41	void __iomem *base;
     42	int irq;
     43	unsigned int freq;
     44	unsigned int baudwidth;
     45	unsigned int baud;
     46	unsigned int speed_hz;
     47	unsigned int mode;
     48	unsigned int len;
     49	unsigned int txc, rxc;
     50	const u8 *txp;
     51	u8 *rxp;
     52};
     53
     54static inline struct tiny_spi *tiny_spi_to_hw(struct spi_device *sdev)
     55{
     56	return spi_master_get_devdata(sdev->master);
     57}
     58
     59static unsigned int tiny_spi_baud(struct spi_device *spi, unsigned int hz)
     60{
     61	struct tiny_spi *hw = tiny_spi_to_hw(spi);
     62
     63	return min(DIV_ROUND_UP(hw->freq, hz * 2), (1U << hw->baudwidth)) - 1;
     64}
     65
     66static int tiny_spi_setup_transfer(struct spi_device *spi,
     67				   struct spi_transfer *t)
     68{
     69	struct tiny_spi *hw = tiny_spi_to_hw(spi);
     70	unsigned int baud = hw->baud;
     71
     72	if (t) {
     73		if (t->speed_hz && t->speed_hz != hw->speed_hz)
     74			baud = tiny_spi_baud(spi, t->speed_hz);
     75	}
     76	writel(baud, hw->base + TINY_SPI_BAUD);
     77	writel(hw->mode, hw->base + TINY_SPI_CONTROL);
     78	return 0;
     79}
     80
     81static int tiny_spi_setup(struct spi_device *spi)
     82{
     83	struct tiny_spi *hw = tiny_spi_to_hw(spi);
     84
     85	if (spi->max_speed_hz != hw->speed_hz) {
     86		hw->speed_hz = spi->max_speed_hz;
     87		hw->baud = tiny_spi_baud(spi, hw->speed_hz);
     88	}
     89	hw->mode = spi->mode & SPI_MODE_X_MASK;
     90	return 0;
     91}
     92
     93static inline void tiny_spi_wait_txr(struct tiny_spi *hw)
     94{
     95	while (!(readb(hw->base + TINY_SPI_STATUS) &
     96		 TINY_SPI_STATUS_TXR))
     97		cpu_relax();
     98}
     99
    100static inline void tiny_spi_wait_txe(struct tiny_spi *hw)
    101{
    102	while (!(readb(hw->base + TINY_SPI_STATUS) &
    103		 TINY_SPI_STATUS_TXE))
    104		cpu_relax();
    105}
    106
    107static int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
    108{
    109	struct tiny_spi *hw = tiny_spi_to_hw(spi);
    110	const u8 *txp = t->tx_buf;
    111	u8 *rxp = t->rx_buf;
    112	unsigned int i;
    113
    114	if (hw->irq >= 0) {
    115		/* use interrupt driven data transfer */
    116		hw->len = t->len;
    117		hw->txp = t->tx_buf;
    118		hw->rxp = t->rx_buf;
    119		hw->txc = 0;
    120		hw->rxc = 0;
    121
    122		/* send the first byte */
    123		if (t->len > 1) {
    124			writeb(hw->txp ? *hw->txp++ : 0,
    125			       hw->base + TINY_SPI_TXDATA);
    126			hw->txc++;
    127			writeb(hw->txp ? *hw->txp++ : 0,
    128			       hw->base + TINY_SPI_TXDATA);
    129			hw->txc++;
    130			writeb(TINY_SPI_STATUS_TXR, hw->base + TINY_SPI_STATUS);
    131		} else {
    132			writeb(hw->txp ? *hw->txp++ : 0,
    133			       hw->base + TINY_SPI_TXDATA);
    134			hw->txc++;
    135			writeb(TINY_SPI_STATUS_TXE, hw->base + TINY_SPI_STATUS);
    136		}
    137
    138		wait_for_completion(&hw->done);
    139	} else {
    140		/* we need to tighten the transfer loop */
    141		writeb(txp ? *txp++ : 0, hw->base + TINY_SPI_TXDATA);
    142		for (i = 1; i < t->len; i++) {
    143			writeb(txp ? *txp++ : 0, hw->base + TINY_SPI_TXDATA);
    144
    145			if (rxp || (i != t->len - 1))
    146				tiny_spi_wait_txr(hw);
    147			if (rxp)
    148				*rxp++ = readb(hw->base + TINY_SPI_TXDATA);
    149		}
    150		tiny_spi_wait_txe(hw);
    151		if (rxp)
    152			*rxp++ = readb(hw->base + TINY_SPI_RXDATA);
    153	}
    154
    155	return t->len;
    156}
    157
    158static irqreturn_t tiny_spi_irq(int irq, void *dev)
    159{
    160	struct tiny_spi *hw = dev;
    161
    162	writeb(0, hw->base + TINY_SPI_STATUS);
    163	if (hw->rxc + 1 == hw->len) {
    164		if (hw->rxp)
    165			*hw->rxp++ = readb(hw->base + TINY_SPI_RXDATA);
    166		hw->rxc++;
    167		complete(&hw->done);
    168	} else {
    169		if (hw->rxp)
    170			*hw->rxp++ = readb(hw->base + TINY_SPI_TXDATA);
    171		hw->rxc++;
    172		if (hw->txc < hw->len) {
    173			writeb(hw->txp ? *hw->txp++ : 0,
    174			       hw->base + TINY_SPI_TXDATA);
    175			hw->txc++;
    176			writeb(TINY_SPI_STATUS_TXR,
    177			       hw->base + TINY_SPI_STATUS);
    178		} else {
    179			writeb(TINY_SPI_STATUS_TXE,
    180			       hw->base + TINY_SPI_STATUS);
    181		}
    182	}
    183	return IRQ_HANDLED;
    184}
    185
    186#ifdef CONFIG_OF
    187#include <linux/of_gpio.h>
    188
    189static int tiny_spi_of_probe(struct platform_device *pdev)
    190{
    191	struct tiny_spi *hw = platform_get_drvdata(pdev);
    192	struct device_node *np = pdev->dev.of_node;
    193	u32 val;
    194
    195	if (!np)
    196		return 0;
    197	hw->bitbang.master->dev.of_node = pdev->dev.of_node;
    198	if (!of_property_read_u32(np, "clock-frequency", &val))
    199		hw->freq = val;
    200	if (!of_property_read_u32(np, "baud-width", &val))
    201		hw->baudwidth = val;
    202	return 0;
    203}
    204#else /* !CONFIG_OF */
    205static int tiny_spi_of_probe(struct platform_device *pdev)
    206{
    207	return 0;
    208}
    209#endif /* CONFIG_OF */
    210
    211static int tiny_spi_probe(struct platform_device *pdev)
    212{
    213	struct tiny_spi_platform_data *platp = dev_get_platdata(&pdev->dev);
    214	struct tiny_spi *hw;
    215	struct spi_master *master;
    216	int err = -ENODEV;
    217
    218	master = spi_alloc_master(&pdev->dev, sizeof(struct tiny_spi));
    219	if (!master)
    220		return err;
    221
    222	/* setup the master state. */
    223	master->bus_num = pdev->id;
    224	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
    225	master->setup = tiny_spi_setup;
    226	master->use_gpio_descriptors = true;
    227
    228	hw = spi_master_get_devdata(master);
    229	platform_set_drvdata(pdev, hw);
    230
    231	/* setup the state for the bitbang driver */
    232	hw->bitbang.master = master;
    233	hw->bitbang.setup_transfer = tiny_spi_setup_transfer;
    234	hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs;
    235
    236	/* find and map our resources */
    237	hw->base = devm_platform_ioremap_resource(pdev, 0);
    238	if (IS_ERR(hw->base)) {
    239		err = PTR_ERR(hw->base);
    240		goto exit;
    241	}
    242	/* irq is optional */
    243	hw->irq = platform_get_irq(pdev, 0);
    244	if (hw->irq >= 0) {
    245		init_completion(&hw->done);
    246		err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0,
    247				       pdev->name, hw);
    248		if (err)
    249			goto exit;
    250	}
    251	/* find platform data */
    252	if (platp) {
    253		hw->freq = platp->freq;
    254		hw->baudwidth = platp->baudwidth;
    255	} else {
    256		err = tiny_spi_of_probe(pdev);
    257		if (err)
    258			goto exit;
    259	}
    260
    261	/* register our spi controller */
    262	err = spi_bitbang_start(&hw->bitbang);
    263	if (err)
    264		goto exit;
    265	dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
    266
    267	return 0;
    268
    269exit:
    270	spi_master_put(master);
    271	return err;
    272}
    273
    274static int tiny_spi_remove(struct platform_device *pdev)
    275{
    276	struct tiny_spi *hw = platform_get_drvdata(pdev);
    277	struct spi_master *master = hw->bitbang.master;
    278
    279	spi_bitbang_stop(&hw->bitbang);
    280	spi_master_put(master);
    281	return 0;
    282}
    283
    284#ifdef CONFIG_OF
    285static const struct of_device_id tiny_spi_match[] = {
    286	{ .compatible = "opencores,tiny-spi-rtlsvn2", },
    287	{},
    288};
    289MODULE_DEVICE_TABLE(of, tiny_spi_match);
    290#endif /* CONFIG_OF */
    291
    292static struct platform_driver tiny_spi_driver = {
    293	.probe = tiny_spi_probe,
    294	.remove = tiny_spi_remove,
    295	.driver = {
    296		.name = DRV_NAME,
    297		.pm = NULL,
    298		.of_match_table = of_match_ptr(tiny_spi_match),
    299	},
    300};
    301module_platform_driver(tiny_spi_driver);
    302
    303MODULE_DESCRIPTION("OpenCores tiny SPI driver");
    304MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
    305MODULE_LICENSE("GPL");
    306MODULE_ALIAS("platform:" DRV_NAME);