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-clps711x.c (4190B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  CLPS711X SPI bus driver
      4 *
      5 *  Copyright (C) 2012-2016 Alexander Shiyan <shc_work@mail.ru>
      6 */
      7
      8#include <linux/io.h>
      9#include <linux/clk.h>
     10#include <linux/gpio/consumer.h>
     11#include <linux/module.h>
     12#include <linux/of.h>
     13#include <linux/interrupt.h>
     14#include <linux/platform_device.h>
     15#include <linux/regmap.h>
     16#include <linux/mfd/syscon.h>
     17#include <linux/mfd/syscon/clps711x.h>
     18#include <linux/spi/spi.h>
     19
     20#define DRIVER_NAME		"clps711x-spi"
     21
     22#define SYNCIO_FRMLEN(x)	((x) << 8)
     23#define SYNCIO_TXFRMEN		(1 << 14)
     24
     25struct spi_clps711x_data {
     26	void __iomem		*syncio;
     27	struct regmap		*syscon;
     28	struct clk		*spi_clk;
     29
     30	u8			*tx_buf;
     31	u8			*rx_buf;
     32	unsigned int		bpw;
     33	int			len;
     34};
     35
     36static int spi_clps711x_prepare_message(struct spi_master *master,
     37					struct spi_message *msg)
     38{
     39	struct spi_clps711x_data *hw = spi_master_get_devdata(master);
     40	struct spi_device *spi = msg->spi;
     41
     42	/* Setup mode for transfer */
     43	return regmap_update_bits(hw->syscon, SYSCON_OFFSET, SYSCON3_ADCCKNSEN,
     44				  (spi->mode & SPI_CPHA) ?
     45				  SYSCON3_ADCCKNSEN : 0);
     46}
     47
     48static int spi_clps711x_transfer_one(struct spi_master *master,
     49				     struct spi_device *spi,
     50				     struct spi_transfer *xfer)
     51{
     52	struct spi_clps711x_data *hw = spi_master_get_devdata(master);
     53	u8 data;
     54
     55	clk_set_rate(hw->spi_clk, xfer->speed_hz ? : spi->max_speed_hz);
     56
     57	hw->len = xfer->len;
     58	hw->bpw = xfer->bits_per_word;
     59	hw->tx_buf = (u8 *)xfer->tx_buf;
     60	hw->rx_buf = (u8 *)xfer->rx_buf;
     61
     62	/* Initiate transfer */
     63	data = hw->tx_buf ? *hw->tx_buf++ : 0;
     64	writel(data | SYNCIO_FRMLEN(hw->bpw) | SYNCIO_TXFRMEN, hw->syncio);
     65
     66	return 1;
     67}
     68
     69static irqreturn_t spi_clps711x_isr(int irq, void *dev_id)
     70{
     71	struct spi_master *master = dev_id;
     72	struct spi_clps711x_data *hw = spi_master_get_devdata(master);
     73	u8 data;
     74
     75	/* Handle RX */
     76	data = readb(hw->syncio);
     77	if (hw->rx_buf)
     78		*hw->rx_buf++ = data;
     79
     80	/* Handle TX */
     81	if (--hw->len > 0) {
     82		data = hw->tx_buf ? *hw->tx_buf++ : 0;
     83		writel(data | SYNCIO_FRMLEN(hw->bpw) | SYNCIO_TXFRMEN,
     84		       hw->syncio);
     85	} else
     86		spi_finalize_current_transfer(master);
     87
     88	return IRQ_HANDLED;
     89}
     90
     91static int spi_clps711x_probe(struct platform_device *pdev)
     92{
     93	struct device_node *np = pdev->dev.of_node;
     94	struct spi_clps711x_data *hw;
     95	struct spi_master *master;
     96	int irq, ret;
     97
     98	irq = platform_get_irq(pdev, 0);
     99	if (irq < 0)
    100		return irq;
    101
    102	master = spi_alloc_master(&pdev->dev, sizeof(*hw));
    103	if (!master)
    104		return -ENOMEM;
    105
    106	master->use_gpio_descriptors = true;
    107	master->bus_num = -1;
    108	master->mode_bits = SPI_CPHA | SPI_CS_HIGH;
    109	master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 8);
    110	master->dev.of_node = pdev->dev.of_node;
    111	master->prepare_message = spi_clps711x_prepare_message;
    112	master->transfer_one = spi_clps711x_transfer_one;
    113
    114	hw = spi_master_get_devdata(master);
    115
    116	hw->spi_clk = devm_clk_get(&pdev->dev, NULL);
    117	if (IS_ERR(hw->spi_clk)) {
    118		ret = PTR_ERR(hw->spi_clk);
    119		goto err_out;
    120	}
    121
    122	hw->syscon = syscon_regmap_lookup_by_phandle(np, "syscon");
    123	if (IS_ERR(hw->syscon)) {
    124		ret = PTR_ERR(hw->syscon);
    125		goto err_out;
    126	}
    127
    128	hw->syncio = devm_platform_ioremap_resource(pdev, 0);
    129	if (IS_ERR(hw->syncio)) {
    130		ret = PTR_ERR(hw->syncio);
    131		goto err_out;
    132	}
    133
    134	/* Disable extended mode due hardware problems */
    135	regmap_update_bits(hw->syscon, SYSCON_OFFSET, SYSCON3_ADCCON, 0);
    136
    137	/* Clear possible pending interrupt */
    138	readl(hw->syncio);
    139
    140	ret = devm_request_irq(&pdev->dev, irq, spi_clps711x_isr, 0,
    141			       dev_name(&pdev->dev), master);
    142	if (ret)
    143		goto err_out;
    144
    145	ret = devm_spi_register_master(&pdev->dev, master);
    146	if (!ret)
    147		return 0;
    148
    149err_out:
    150	spi_master_put(master);
    151
    152	return ret;
    153}
    154
    155static const struct of_device_id clps711x_spi_dt_ids[] = {
    156	{ .compatible = "cirrus,ep7209-spi", },
    157	{ }
    158};
    159MODULE_DEVICE_TABLE(of, clps711x_spi_dt_ids);
    160
    161static struct platform_driver clps711x_spi_driver = {
    162	.driver	= {
    163		.name	= DRIVER_NAME,
    164		.of_match_table = clps711x_spi_dt_ids,
    165	},
    166	.probe	= spi_clps711x_probe,
    167};
    168module_platform_driver(clps711x_spi_driver);
    169
    170MODULE_LICENSE("GPL");
    171MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
    172MODULE_DESCRIPTION("CLPS711X SPI bus driver");
    173MODULE_ALIAS("platform:" DRIVER_NAME);