Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.3 246 lines 5.9 kB view raw
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/spi.h> 18#include <linux/io.h> 19#include <linux/of.h> 20 21#define DRV_NAME "spi_altera" 22 23#define ALTERA_SPI_RXDATA 0 24#define ALTERA_SPI_TXDATA 4 25#define ALTERA_SPI_STATUS 8 26#define ALTERA_SPI_CONTROL 12 27#define ALTERA_SPI_SLAVE_SEL 20 28 29#define ALTERA_SPI_STATUS_ROE_MSK 0x8 30#define ALTERA_SPI_STATUS_TOE_MSK 0x10 31#define ALTERA_SPI_STATUS_TMT_MSK 0x20 32#define ALTERA_SPI_STATUS_TRDY_MSK 0x40 33#define ALTERA_SPI_STATUS_RRDY_MSK 0x80 34#define ALTERA_SPI_STATUS_E_MSK 0x100 35 36#define ALTERA_SPI_CONTROL_IROE_MSK 0x8 37#define ALTERA_SPI_CONTROL_ITOE_MSK 0x10 38#define ALTERA_SPI_CONTROL_ITRDY_MSK 0x40 39#define ALTERA_SPI_CONTROL_IRRDY_MSK 0x80 40#define ALTERA_SPI_CONTROL_IE_MSK 0x100 41#define ALTERA_SPI_CONTROL_SSO_MSK 0x400 42 43struct altera_spi { 44 void __iomem *base; 45 int irq; 46 int len; 47 int count; 48 int bytes_per_word; 49 unsigned long imr; 50 51 /* data buffers */ 52 const unsigned char *tx; 53 unsigned char *rx; 54}; 55 56static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev) 57{ 58 return spi_master_get_devdata(sdev->master); 59} 60 61static void altera_spi_set_cs(struct spi_device *spi, bool is_high) 62{ 63 struct altera_spi *hw = altera_spi_to_hw(spi); 64 65 if (is_high) { 66 hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK; 67 writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); 68 writel(0, hw->base + ALTERA_SPI_SLAVE_SEL); 69 } else { 70 writel(BIT(spi->chip_select), hw->base + ALTERA_SPI_SLAVE_SEL); 71 hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK; 72 writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); 73 } 74} 75 76static void altera_spi_tx_word(struct altera_spi *hw) 77{ 78 unsigned int txd = 0; 79 80 if (hw->tx) { 81 switch (hw->bytes_per_word) { 82 case 1: 83 txd = hw->tx[hw->count]; 84 break; 85 case 2: 86 txd = (hw->tx[hw->count * 2] 87 | (hw->tx[hw->count * 2 + 1] << 8)); 88 break; 89 } 90 } 91 92 writel(txd, hw->base + ALTERA_SPI_TXDATA); 93} 94 95static void altera_spi_rx_word(struct altera_spi *hw) 96{ 97 unsigned int rxd; 98 99 rxd = readl(hw->base + ALTERA_SPI_RXDATA); 100 if (hw->rx) { 101 switch (hw->bytes_per_word) { 102 case 1: 103 hw->rx[hw->count] = rxd; 104 break; 105 case 2: 106 hw->rx[hw->count * 2] = rxd; 107 hw->rx[hw->count * 2 + 1] = rxd >> 8; 108 break; 109 } 110 } 111 112 hw->count++; 113} 114 115static int altera_spi_txrx(struct spi_master *master, 116 struct spi_device *spi, struct spi_transfer *t) 117{ 118 struct altera_spi *hw = spi_master_get_devdata(master); 119 120 hw->tx = t->tx_buf; 121 hw->rx = t->rx_buf; 122 hw->count = 0; 123 hw->bytes_per_word = DIV_ROUND_UP(t->bits_per_word, 8); 124 hw->len = t->len / hw->bytes_per_word; 125 126 if (hw->irq >= 0) { 127 /* enable receive interrupt */ 128 hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK; 129 writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); 130 131 /* send the first byte */ 132 altera_spi_tx_word(hw); 133 } else { 134 while (hw->count < hw->len) { 135 altera_spi_tx_word(hw); 136 137 while (!(readl(hw->base + ALTERA_SPI_STATUS) & 138 ALTERA_SPI_STATUS_RRDY_MSK)) 139 cpu_relax(); 140 141 altera_spi_rx_word(hw); 142 } 143 spi_finalize_current_transfer(master); 144 } 145 146 return t->len; 147} 148 149static irqreturn_t altera_spi_irq(int irq, void *dev) 150{ 151 struct spi_master *master = dev; 152 struct altera_spi *hw = spi_master_get_devdata(master); 153 154 altera_spi_rx_word(hw); 155 156 if (hw->count < hw->len) { 157 altera_spi_tx_word(hw); 158 } else { 159 /* disable receive interrupt */ 160 hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK; 161 writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); 162 163 spi_finalize_current_transfer(master); 164 } 165 166 return IRQ_HANDLED; 167} 168 169static int altera_spi_probe(struct platform_device *pdev) 170{ 171 struct altera_spi *hw; 172 struct spi_master *master; 173 struct resource *res; 174 int err = -ENODEV; 175 176 master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi)); 177 if (!master) 178 return err; 179 180 /* setup the master state. */ 181 master->bus_num = pdev->id; 182 master->num_chipselect = 16; 183 master->mode_bits = SPI_CS_HIGH; 184 master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16); 185 master->dev.of_node = pdev->dev.of_node; 186 master->transfer_one = altera_spi_txrx; 187 master->set_cs = altera_spi_set_cs; 188 189 hw = spi_master_get_devdata(master); 190 191 /* find and map our resources */ 192 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 193 hw->base = devm_ioremap_resource(&pdev->dev, res); 194 if (IS_ERR(hw->base)) { 195 err = PTR_ERR(hw->base); 196 goto exit; 197 } 198 /* program defaults into the registers */ 199 hw->imr = 0; /* disable spi interrupts */ 200 writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); 201 writel(0, hw->base + ALTERA_SPI_STATUS); /* clear status reg */ 202 if (readl(hw->base + ALTERA_SPI_STATUS) & ALTERA_SPI_STATUS_RRDY_MSK) 203 readl(hw->base + ALTERA_SPI_RXDATA); /* flush rxdata */ 204 /* irq is optional */ 205 hw->irq = platform_get_irq(pdev, 0); 206 if (hw->irq >= 0) { 207 err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0, 208 pdev->name, master); 209 if (err) 210 goto exit; 211 } 212 213 err = devm_spi_register_master(&pdev->dev, master); 214 if (err) 215 goto exit; 216 dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq); 217 218 return 0; 219exit: 220 spi_master_put(master); 221 return err; 222} 223 224#ifdef CONFIG_OF 225static const struct of_device_id altera_spi_match[] = { 226 { .compatible = "ALTR,spi-1.0", }, 227 { .compatible = "altr,spi-1.0", }, 228 {}, 229}; 230MODULE_DEVICE_TABLE(of, altera_spi_match); 231#endif /* CONFIG_OF */ 232 233static struct platform_driver altera_spi_driver = { 234 .probe = altera_spi_probe, 235 .driver = { 236 .name = DRV_NAME, 237 .pm = NULL, 238 .of_match_table = of_match_ptr(altera_spi_match), 239 }, 240}; 241module_platform_driver(altera_spi_driver); 242 243MODULE_DESCRIPTION("Altera SPI driver"); 244MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); 245MODULE_LICENSE("GPL"); 246MODULE_ALIAS("platform:" DRV_NAME);