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/spi/spi_bitbang.h> 22#include <linux/io.h> 23#include <linux/of.h> 24 25#define DRV_NAME "spi_altera" 26 27#define ALTERA_SPI_RXDATA 0 28#define ALTERA_SPI_TXDATA 4 29#define ALTERA_SPI_STATUS 8 30#define ALTERA_SPI_CONTROL 12 31#define ALTERA_SPI_SLAVE_SEL 20 32 33#define ALTERA_SPI_STATUS_ROE_MSK 0x8 34#define ALTERA_SPI_STATUS_TOE_MSK 0x10 35#define ALTERA_SPI_STATUS_TMT_MSK 0x20 36#define ALTERA_SPI_STATUS_TRDY_MSK 0x40 37#define ALTERA_SPI_STATUS_RRDY_MSK 0x80 38#define ALTERA_SPI_STATUS_E_MSK 0x100 39 40#define ALTERA_SPI_CONTROL_IROE_MSK 0x8 41#define ALTERA_SPI_CONTROL_ITOE_MSK 0x10 42#define ALTERA_SPI_CONTROL_ITRDY_MSK 0x40 43#define ALTERA_SPI_CONTROL_IRRDY_MSK 0x80 44#define ALTERA_SPI_CONTROL_IE_MSK 0x100 45#define ALTERA_SPI_CONTROL_SSO_MSK 0x400 46 47struct altera_spi { 48 /* bitbang has to be first */ 49 struct spi_bitbang bitbang; 50 struct completion done; 51 52 void __iomem *base; 53 int irq; 54 int len; 55 int count; 56 int bytes_per_word; 57 unsigned long imr; 58 59 /* data buffers */ 60 const unsigned char *tx; 61 unsigned char *rx; 62}; 63 64static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev) 65{ 66 return spi_master_get_devdata(sdev->master); 67} 68 69static void altera_spi_chipsel(struct spi_device *spi, int value) 70{ 71 struct altera_spi *hw = altera_spi_to_hw(spi); 72 73 if (spi->mode & SPI_CS_HIGH) { 74 switch (value) { 75 case BITBANG_CS_INACTIVE: 76 writel(1 << spi->chip_select, 77 hw->base + ALTERA_SPI_SLAVE_SEL); 78 hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK; 79 writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); 80 break; 81 82 case BITBANG_CS_ACTIVE: 83 hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK; 84 writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); 85 writel(0, hw->base + ALTERA_SPI_SLAVE_SEL); 86 break; 87 } 88 } else { 89 switch (value) { 90 case BITBANG_CS_INACTIVE: 91 hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK; 92 writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); 93 break; 94 95 case BITBANG_CS_ACTIVE: 96 writel(1 << spi->chip_select, 97 hw->base + ALTERA_SPI_SLAVE_SEL); 98 hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK; 99 writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); 100 break; 101 } 102 } 103} 104 105static inline unsigned int hw_txbyte(struct altera_spi *hw, int count) 106{ 107 if (hw->tx) { 108 switch (hw->bytes_per_word) { 109 case 1: 110 return hw->tx[count]; 111 case 2: 112 return (hw->tx[count * 2] 113 | (hw->tx[count * 2 + 1] << 8)); 114 } 115 } 116 return 0; 117} 118 119static int altera_spi_txrx(struct spi_device *spi, struct spi_transfer *t) 120{ 121 struct altera_spi *hw = altera_spi_to_hw(spi); 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 writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA); 136 137 wait_for_completion(&hw->done); 138 /* disable receive interrupt */ 139 hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK; 140 writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); 141 } else { 142 while (hw->count < hw->len) { 143 unsigned int rxd; 144 145 writel(hw_txbyte(hw, hw->count), 146 hw->base + ALTERA_SPI_TXDATA); 147 148 while (!(readl(hw->base + ALTERA_SPI_STATUS) & 149 ALTERA_SPI_STATUS_RRDY_MSK)) 150 cpu_relax(); 151 152 rxd = readl(hw->base + ALTERA_SPI_RXDATA); 153 if (hw->rx) { 154 switch (hw->bytes_per_word) { 155 case 1: 156 hw->rx[hw->count] = rxd; 157 break; 158 case 2: 159 hw->rx[hw->count * 2] = rxd; 160 hw->rx[hw->count * 2 + 1] = rxd >> 8; 161 break; 162 } 163 } 164 165 hw->count++; 166 } 167 } 168 169 return hw->count * hw->bytes_per_word; 170} 171 172static irqreturn_t altera_spi_irq(int irq, void *dev) 173{ 174 struct altera_spi *hw = dev; 175 unsigned int rxd; 176 177 rxd = readl(hw->base + ALTERA_SPI_RXDATA); 178 if (hw->rx) { 179 switch (hw->bytes_per_word) { 180 case 1: 181 hw->rx[hw->count] = rxd; 182 break; 183 case 2: 184 hw->rx[hw->count * 2] = rxd; 185 hw->rx[hw->count * 2 + 1] = rxd >> 8; 186 break; 187 } 188 } 189 190 hw->count++; 191 192 if (hw->count < hw->len) 193 writel(hw_txbyte(hw, hw->count), hw->base + ALTERA_SPI_TXDATA); 194 else 195 complete(&hw->done); 196 197 return IRQ_HANDLED; 198} 199 200static int altera_spi_probe(struct platform_device *pdev) 201{ 202 struct altera_spi *hw; 203 struct spi_master *master; 204 struct resource *res; 205 int err = -ENODEV; 206 207 master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi)); 208 if (!master) 209 return err; 210 211 /* setup the master state. */ 212 master->bus_num = pdev->id; 213 master->num_chipselect = 16; 214 master->mode_bits = SPI_CS_HIGH; 215 master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16); 216 master->dev.of_node = pdev->dev.of_node; 217 218 hw = spi_master_get_devdata(master); 219 platform_set_drvdata(pdev, hw); 220 221 /* setup the state for the bitbang driver */ 222 hw->bitbang.master = master; 223 hw->bitbang.chipselect = altera_spi_chipsel; 224 hw->bitbang.txrx_bufs = altera_spi_txrx; 225 226 /* find and map our resources */ 227 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 228 hw->base = devm_ioremap_resource(&pdev->dev, res); 229 if (IS_ERR(hw->base)) { 230 err = PTR_ERR(hw->base); 231 goto exit; 232 } 233 /* program defaults into the registers */ 234 hw->imr = 0; /* disable spi interrupts */ 235 writel(hw->imr, hw->base + ALTERA_SPI_CONTROL); 236 writel(0, hw->base + ALTERA_SPI_STATUS); /* clear status reg */ 237 if (readl(hw->base + ALTERA_SPI_STATUS) & ALTERA_SPI_STATUS_RRDY_MSK) 238 readl(hw->base + ALTERA_SPI_RXDATA); /* flush rxdata */ 239 /* irq is optional */ 240 hw->irq = platform_get_irq(pdev, 0); 241 if (hw->irq >= 0) { 242 init_completion(&hw->done); 243 err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0, 244 pdev->name, hw); 245 if (err) 246 goto exit; 247 } 248 249 /* register our spi controller */ 250 err = spi_bitbang_start(&hw->bitbang); 251 if (err) 252 goto exit; 253 dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq); 254 255 return 0; 256exit: 257 spi_master_put(master); 258 return err; 259} 260 261static int altera_spi_remove(struct platform_device *dev) 262{ 263 struct altera_spi *hw = platform_get_drvdata(dev); 264 struct spi_master *master = hw->bitbang.master; 265 266 spi_bitbang_stop(&hw->bitbang); 267 spi_master_put(master); 268 return 0; 269} 270 271#ifdef CONFIG_OF 272static const struct of_device_id altera_spi_match[] = { 273 { .compatible = "ALTR,spi-1.0", }, 274 { .compatible = "altr,spi-1.0", }, 275 {}, 276}; 277MODULE_DEVICE_TABLE(of, altera_spi_match); 278#endif /* CONFIG_OF */ 279 280static struct platform_driver altera_spi_driver = { 281 .probe = altera_spi_probe, 282 .remove = altera_spi_remove, 283 .driver = { 284 .name = DRV_NAME, 285 .pm = NULL, 286 .of_match_table = of_match_ptr(altera_spi_match), 287 }, 288}; 289module_platform_driver(altera_spi_driver); 290 291MODULE_DESCRIPTION("Altera SPI driver"); 292MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); 293MODULE_LICENSE("GPL"); 294MODULE_ALIAS("platform:" DRV_NAME); 295