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