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