| 1 | --- a/include/linux/spi/spi.h |
| 2 | +++ b/include/linux/spi/spi.h |
| 3 | @@ -507,6 +507,8 @@ struct spi_transfer { |
| 4 | u16 delay_usecs; |
| 5 | u32 speed_hz; |
| 6 | |
| 7 | + unsigned last_in_message_list; |
| 8 | + |
| 9 | struct list_head transfer_list; |
| 10 | }; |
| 11 | |
| 12 | --- a/drivers/spi/Kconfig |
| 13 | +++ b/drivers/spi/Kconfig |
| 14 | @@ -179,6 +179,14 @@ config SPI_GPIO_OLD |
| 15 | |
| 16 | If unsure, say N. |
| 17 | |
| 18 | +config SPI_CNS21XX |
| 19 | + tristate "Cavium Netowrks CNS21xx SPI master" |
| 20 | + depends on ARCH_CNS21XX && EXPERIMENTAL |
| 21 | + select SPI_BITBANG |
| 22 | + help |
| 23 | + This driver supports the buil-in SPI controller of the Cavium Networks |
| 24 | + CNS21xx SoCs. |
| 25 | + |
| 26 | config SPI_IMX |
| 27 | tristate "Freescale i.MX SPI controllers" |
| 28 | depends on ARCH_MXC |
| 29 | --- a/drivers/spi/Makefile |
| 30 | +++ b/drivers/spi/Makefile |
| 31 | @@ -19,6 +19,7 @@ obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5x |
| 32 | obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o |
| 33 | obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o |
| 34 | obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o |
| 35 | +obj-$(CONFIG_SPI_CNS21XX) += spi-cns21xx.o |
| 36 | obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o |
| 37 | obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o |
| 38 | obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o |
| 39 | --- a/drivers/spi/spi-bitbang.c |
| 40 | +++ b/drivers/spi/spi-bitbang.c |
| 41 | @@ -330,6 +330,13 @@ static void bitbang_work(struct work_str |
| 42 | */ |
| 43 | if (!m->is_dma_mapped) |
| 44 | t->rx_dma = t->tx_dma = 0; |
| 45 | + |
| 46 | + if (t->transfer_list.next == &m->transfers) { |
| 47 | + t->last_in_message_list = 1; |
| 48 | + } else { |
| 49 | + t->last_in_message_list = 0; |
| 50 | + } |
| 51 | + |
| 52 | status = bitbang->txrx_bufs(spi, t); |
| 53 | } |
| 54 | if (status > 0) |
| 55 | --- /dev/null |
| 56 | +++ b/drivers/spi/spi-cns21xx.c |
| 57 | @@ -0,0 +1,521 @@ |
| 58 | +/* |
| 59 | + * Copyright (c) 2008 Cavium Networks |
| 60 | + * Copyright (c) 2010-2012 Gabor Juhos <juhosg@openwrt.org> |
| 61 | + * |
| 62 | + * This file is free software; you can redistribute it and/or modify |
| 63 | + * it under the terms of the GNU General Public License, Version 2, as |
| 64 | + * published by the Free Software Foundation. |
| 65 | + */ |
| 66 | + |
| 67 | +#include <linux/init.h> |
| 68 | +#include <linux/module.h> |
| 69 | +#include <linux/spinlock.h> |
| 70 | +#include <linux/workqueue.h> |
| 71 | +#include <linux/interrupt.h> |
| 72 | +#include <linux/delay.h> |
| 73 | +#include <linux/errno.h> |
| 74 | +#include <linux/platform_device.h> |
| 75 | +#include <linux/io.h> |
| 76 | +#include <linux/spi/spi.h> |
| 77 | +#include <linux/spi/spi_bitbang.h> |
| 78 | + |
| 79 | +#include <mach/hardware.h> |
| 80 | +#include <mach/cns21xx.h> |
| 81 | + |
| 82 | +#define DRIVER_NAME "cns21xx-spi" |
| 83 | + |
| 84 | +#ifdef CONFIG_CNS21XX_SPI_DEBUG |
| 85 | +#define DBG(fmt, args...) pr_info("[CNS21XX_SPI_DEBUG]" fmt, ## args) |
| 86 | +#else |
| 87 | +#define DBG(fmt, args...) do {} while (0) |
| 88 | +#endif /* CNS21XX_SPI_DEBUG */ |
| 89 | + |
| 90 | +#define SPI_REG_CFG 0x40 |
| 91 | +#define SPI_REG_STAT 0x44 |
| 92 | +#define SPI_REG_BIT_RATE 0x48 |
| 93 | +#define SPI_REG_TX_CTRL 0x4c |
| 94 | +#define SPI_REG_TX_DATA 0x50 |
| 95 | +#define SPI_REG_RX_CTRL 0x54 |
| 96 | +#define SPI_REG_RX_DATA 0x58 |
| 97 | +#define SPI_REG_FIFO_TX_CFG 0x5c |
| 98 | +#define SPI_REG_FIFO_TX_CTRL 0x60 |
| 99 | +#define SPI_REG_FIFO_RX_CFG 0x64 |
| 100 | +#define SPI_REG_INTR_STAT 0x68 |
| 101 | +#define SPI_REG_INTR_ENA 0x6c |
| 102 | + |
| 103 | +#define CFG_SPI_EN BIT(31) |
| 104 | +#define CFG_SPI_CLKPOL BIT(14) |
| 105 | +#define CFG_SPI_CLKPHA BIT(13) |
| 106 | +#define CFG_SPI_MASTER_EN BIT(11) |
| 107 | +#define CFG_SPI_CHAR_LEN_M 0x3 |
| 108 | +#define CFG_SPI_CHAR_LEN_8BITS 0 |
| 109 | +#define CFG_SPI_CHAR_LEN_16BITS 1 |
| 110 | +#define CFG_SPI_CHAR_LEN_24BITS 2 |
| 111 | +#define CFG_SPI_CHAR_LEN_32BITS 3 |
| 112 | + |
| 113 | +#define STAT_SPI_BUSY_STA BIT(1) |
| 114 | + |
| 115 | +#define BIT_RATE_DIV_1 0 |
| 116 | +#define BIT_RATE_DIV_2 1 |
| 117 | +#define BIT_RATE_DIV_4 2 |
| 118 | +#define BIT_RATE_DIV_8 3 |
| 119 | +#define BIT_RATE_DIV_16 4 |
| 120 | +#define BIT_RATE_DIV_32 5 |
| 121 | +#define BIT_RATE_DIV_64 6 |
| 122 | +#define BIT_RATE_DIV_128 7 |
| 123 | + |
| 124 | +#define TX_CTRL_SPI_TXDAT_EOF BIT(2) |
| 125 | +#define TX_CTRL_SPI_TXCH_NUM_M 0x3 |
| 126 | +#define TX_CTRL_CLEAR_MASK (TX_CTRL_SPI_TXDAT_EOF | \ |
| 127 | + TX_CTRL_SPI_TXCH_NUM_M) |
| 128 | + |
| 129 | +#define RX_CTRL_SPI_RXDAT_EOF BIT(2) |
| 130 | +#define RX_CTRL_SPI_RXCH_NUM_M 0x3 |
| 131 | + |
| 132 | +#define INTR_STAT_SPI_TXBF_UNRN_FG BIT(7) |
| 133 | +#define INTR_STAT_SPI_RXBF_OVRN_FG BIT(6) |
| 134 | +#define INTR_STAT_SPI_TXFF_UNRN_FG BIT(5) |
| 135 | +#define INTR_STAT_SPI_RXFF_OVRN_FG BIT(4) |
| 136 | +#define INTR_STAT_SPI_TXBUF_FG BIT(3) |
| 137 | +#define INTR_STAT_SPI_RXBUF_FG BIT(2) |
| 138 | +#define INTR_STAT_SPI_TXFF_FG BIT(1) |
| 139 | +#define INTR_STAT_SPI_RXFF_FG BIT(0) |
| 140 | + |
| 141 | +#define INTR_STAT_CLEAR_MASK (INTR_STAT_SPI_TXBF_UNRN_FG | \ |
| 142 | + INTR_STAT_SPI_RXBF_OVRN_FG | \ |
| 143 | + INTR_STAT_SPI_TXFF_UNRN_FG | \ |
| 144 | + INTR_STAT_SPI_RXFF_OVRN_FG) |
| 145 | + |
| 146 | +#define FIFO_TX_CFG_SPI_TXFF_THRED_M 0x3 |
| 147 | +#define FIFO_TX_CFG_SPI_TXFF_THRED_S 4 |
| 148 | +#define FIFO_TX_CFG_SPI_TXFF_THRED_2 0 |
| 149 | +#define FIFO_TX_CFG_SPI_TXFF_THRED_4 1 |
| 150 | +#define FIFO_TX_CFG_SPI_TXFF_THRED_6 0 |
| 151 | +#define FIFO_TX_CFG_SPI_TXFF_STATUS_M 0xf |
| 152 | + |
| 153 | +#define FIFO_RX_CFG_SPI_RXFF_THRED_M 0x3 |
| 154 | +#define FIFO_RX_CFG_SPI_RXFF_THRED_S 4 |
| 155 | +#define FIFO_RX_CFG_SPI_RXFF_THRED_2 0 |
| 156 | +#define FIFO_RX_CFG_SPI_RXFF_THRED_4 1 |
| 157 | +#define FIFO_RX_CFG_SPI_RXFF_THRED_6 0 |
| 158 | +#define FIFO_RX_CFG_SPI_RXFF_STATUS_M 0xf |
| 159 | + |
| 160 | +#define CNS21XX_SPI_NUM_BIT_RATES 8 |
| 161 | + |
| 162 | +struct cns21xx_spi { |
| 163 | + struct spi_bitbang bitbang; |
| 164 | + |
| 165 | + struct spi_master *master; |
| 166 | + struct device *dev; |
| 167 | + void __iomem *base; |
| 168 | + struct resource *region; |
| 169 | + |
| 170 | + unsigned freq_max; |
| 171 | + unsigned freq_min; |
| 172 | + |
| 173 | +}; |
| 174 | + |
| 175 | +static inline struct cns21xx_spi *to_hw(struct spi_device *spi) |
| 176 | +{ |
| 177 | + return spi_master_get_devdata(spi->master); |
| 178 | +} |
| 179 | + |
| 180 | +static inline u32 cns21xx_spi_rr(struct cns21xx_spi *hw, unsigned int reg) |
| 181 | +{ |
| 182 | + return __raw_readl(hw->base + reg); |
| 183 | +} |
| 184 | + |
| 185 | +static inline void cns21xx_spi_wr(struct cns21xx_spi *hw, u32 val, |
| 186 | + unsigned int reg) |
| 187 | +{ |
| 188 | + __raw_writel(val, hw->base + reg); |
| 189 | +} |
| 190 | + |
| 191 | +#define CNS21XX_SPI_RETRY_COUNT 100 |
| 192 | +static inline int cns21xx_spi_wait(struct cns21xx_spi *hw, unsigned int reg, |
| 193 | + u32 mask, u32 val) |
| 194 | +{ |
| 195 | + int retry_cnt = 0; |
| 196 | + |
| 197 | + do { |
| 198 | + if ((cns21xx_spi_rr(hw, reg) & mask) == val) |
| 199 | + break; |
| 200 | + |
| 201 | + if (++retry_cnt > CNS21XX_SPI_RETRY_COUNT) { |
| 202 | + dev_err(hw->dev, "timeout waiting on register %02x\n", |
| 203 | + reg); |
| 204 | + return -EIO; |
| 205 | + } |
| 206 | + } while (1); |
| 207 | + |
| 208 | + return 0; |
| 209 | +} |
| 210 | + |
| 211 | +static int cns21xx_spi_txrx_word(struct cns21xx_spi *hw, u8 tx_channel, |
| 212 | + u8 tx_eof_flag, u32 tx_data, u32 *rx_data) |
| 213 | +{ |
| 214 | + unsigned int tx_ctrl; |
| 215 | + u8 rx_channel; |
| 216 | + u8 rx_eof_flag; |
| 217 | + int err = 0; |
| 218 | + |
| 219 | + err = cns21xx_spi_wait(hw, SPI_REG_STAT, STAT_SPI_BUSY_STA, 0); |
| 220 | + if (err) |
| 221 | + return err; |
| 222 | + |
| 223 | + err = cns21xx_spi_wait(hw, SPI_REG_INTR_STAT, INTR_STAT_SPI_TXBUF_FG, |
| 224 | + INTR_STAT_SPI_TXBUF_FG); |
| 225 | + if (err) |
| 226 | + return err; |
| 227 | + |
| 228 | + tx_ctrl = cns21xx_spi_rr(hw, SPI_REG_TX_CTRL); |
| 229 | + tx_ctrl &= ~(TX_CTRL_CLEAR_MASK); |
| 230 | + tx_ctrl |= (tx_channel & TX_CTRL_SPI_TXCH_NUM_M); |
| 231 | + tx_ctrl |= (tx_eof_flag) ? TX_CTRL_SPI_TXDAT_EOF : 0; |
| 232 | + cns21xx_spi_wr(hw, tx_ctrl, SPI_REG_TX_CTRL); |
| 233 | + |
| 234 | + cns21xx_spi_wr(hw, tx_data, SPI_REG_TX_DATA); |
| 235 | + |
| 236 | + err = cns21xx_spi_wait(hw, SPI_REG_INTR_STAT, INTR_STAT_SPI_RXBUF_FG, |
| 237 | + INTR_STAT_SPI_RXBUF_FG); |
| 238 | + if (err) |
| 239 | + return err; |
| 240 | + |
| 241 | + rx_channel = cns21xx_spi_rr(hw, SPI_REG_RX_CTRL) & |
| 242 | + RX_CTRL_SPI_RXCH_NUM_M; |
| 243 | + |
| 244 | + rx_eof_flag = (cns21xx_spi_rr(hw, SPI_REG_RX_CTRL) & |
| 245 | + RX_CTRL_SPI_RXDAT_EOF) ? 1 : 0; |
| 246 | + |
| 247 | + *rx_data = cns21xx_spi_rr(hw, SPI_REG_RX_DATA); |
| 248 | + |
| 249 | + if ((tx_channel != rx_channel) || (tx_eof_flag != rx_eof_flag)) |
| 250 | + return -EPROTO; |
| 251 | + |
| 252 | + return 0; |
| 253 | +} |
| 254 | + |
| 255 | +static void cns21xx_spi_chipselect(struct spi_device *spi, int value) |
| 256 | +{ |
| 257 | + struct cns21xx_spi *hw = to_hw(spi); |
| 258 | + unsigned int spi_config; |
| 259 | + unsigned int tx_ctrl; |
| 260 | + |
| 261 | + switch (value) { |
| 262 | + case BITBANG_CS_INACTIVE: |
| 263 | + break; |
| 264 | + |
| 265 | + case BITBANG_CS_ACTIVE: |
| 266 | + spi_config = cns21xx_spi_rr(hw, SPI_REG_CFG); |
| 267 | + |
| 268 | + if (spi->mode & SPI_CPHA) |
| 269 | + spi_config |= CFG_SPI_CLKPHA; |
| 270 | + else |
| 271 | + spi_config &= ~CFG_SPI_CLKPHA; |
| 272 | + |
| 273 | + if (spi->mode & SPI_CPOL) |
| 274 | + spi_config |= CFG_SPI_CLKPOL; |
| 275 | + else |
| 276 | + spi_config &= ~CFG_SPI_CLKPOL; |
| 277 | + |
| 278 | + cns21xx_spi_wr(hw, spi_config, SPI_REG_CFG); |
| 279 | + |
| 280 | + tx_ctrl = cns21xx_spi_rr(hw, SPI_REG_TX_CTRL); |
| 281 | + tx_ctrl &= ~(TX_CTRL_CLEAR_MASK); |
| 282 | + tx_ctrl |= (spi->chip_select & TX_CTRL_SPI_TXCH_NUM_M); |
| 283 | + cns21xx_spi_wr(hw, tx_ctrl, SPI_REG_TX_CTRL); |
| 284 | + |
| 285 | + break; |
| 286 | + } |
| 287 | +} |
| 288 | + |
| 289 | +static int cns21xx_spi_setup(struct spi_device *spi) |
| 290 | +{ |
| 291 | + struct cns21xx_spi *hw = to_hw(spi); |
| 292 | + |
| 293 | + if (spi->bits_per_word != 8) { |
| 294 | + dev_err(&spi->dev, "%s: invalid bits_per_word=%u\n", |
| 295 | + __func__, spi->bits_per_word); |
| 296 | + return -EINVAL; |
| 297 | + } |
| 298 | + |
| 299 | + if (spi->max_speed_hz == 0) |
| 300 | + spi->max_speed_hz = hw->freq_max; |
| 301 | + |
| 302 | + if (spi->max_speed_hz > hw->freq_max || |
| 303 | + spi->max_speed_hz < hw->freq_min) { |
| 304 | + dev_err(&spi->dev, "%s: max_speed_hz=%u out of range\n", |
| 305 | + __func__, spi->max_speed_hz); |
| 306 | + return -EINVAL; |
| 307 | + } |
| 308 | + |
| 309 | + return 0; |
| 310 | +} |
| 311 | + |
| 312 | +static int cns21xx_spi_setup_transfer(struct spi_device *spi, |
| 313 | + struct spi_transfer *t) |
| 314 | +{ |
| 315 | + struct cns21xx_spi *hw = to_hw(spi); |
| 316 | + u8 bits_per_word; |
| 317 | + u32 hz; |
| 318 | + int i; |
| 319 | + |
| 320 | + bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; |
| 321 | + hz = t ? t->speed_hz : spi->max_speed_hz; |
| 322 | + |
| 323 | + if (!bits_per_word) |
| 324 | + bits_per_word = spi->bits_per_word; |
| 325 | + |
| 326 | + if (!hz) |
| 327 | + hz = spi->max_speed_hz; |
| 328 | + |
| 329 | + if (bits_per_word != 8) { |
| 330 | + dev_err(&spi->dev, "%s: invalid bits_per_word=%u\n", |
| 331 | + __func__, bits_per_word); |
| 332 | + return -EINVAL; |
| 333 | + } |
| 334 | + |
| 335 | + if (hz > spi->max_speed_hz || hz > hw->freq_max || hz < hw->freq_min) { |
| 336 | + dev_err(&spi->dev, "%s: max_speed_hz=%u out of range\n", |
| 337 | + __func__, hz); |
| 338 | + return -EINVAL; |
| 339 | + } |
| 340 | + |
| 341 | + for (i = 0; i < CNS21XX_SPI_NUM_BIT_RATES; i++) |
| 342 | + if (spi->max_speed_hz > (cns21xx_get_apb_freq() >> i)) |
| 343 | + break; |
| 344 | + |
| 345 | + DBG("max_speed:%uHz, curr_speed:%luHz, rate_index=%d\n", |
| 346 | + spi->max_speed_hz, cns21xx_get_apb_freq() / (1 << i), i); |
| 347 | + |
| 348 | + cns21xx_spi_wr(hw, i, SPI_REG_BIT_RATE); |
| 349 | + |
| 350 | + return 0; |
| 351 | +} |
| 352 | + |
| 353 | +static int cns21xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t) |
| 354 | +{ |
| 355 | + struct cns21xx_spi *hw = to_hw(spi); |
| 356 | + const unsigned char *tx_buf; |
| 357 | + unsigned char *rx_buf; |
| 358 | + u32 rx_data; |
| 359 | + int tx_eof; |
| 360 | + int err = 0; |
| 361 | + int i; |
| 362 | + |
| 363 | + tx_buf = t->tx_buf; |
| 364 | + rx_buf = t->rx_buf; |
| 365 | + tx_eof = t->last_in_message_list; |
| 366 | + |
| 367 | + DBG("txrx: tx %p, rx %p, len %d\n", tx_buf, rx_buf, t->len); |
| 368 | + |
| 369 | + if (tx_buf) { |
| 370 | + for (i = 0; i < t->len; i++) |
| 371 | + DBG("tx_buf[%02d]: 0x%02x\n", i, tx_buf[i]); |
| 372 | + |
| 373 | + for (i = 0; i < (t->len - 1); i++) { |
| 374 | + err = cns21xx_spi_txrx_word(hw, spi->chip_select, 0, |
| 375 | + tx_buf[i], &rx_data); |
| 376 | + if (err) |
| 377 | + goto done; |
| 378 | + |
| 379 | + if (rx_buf) { |
| 380 | + rx_buf[i] = rx_data; |
| 381 | + DBG("rx_buf[%02d]:0x%02x\n", i, rx_buf[i]); |
| 382 | + } |
| 383 | + } |
| 384 | + |
| 385 | + err = cns21xx_spi_txrx_word(hw, spi->chip_select, tx_eof, |
| 386 | + tx_buf[i], &rx_data); |
| 387 | + if (err) |
| 388 | + goto done; |
| 389 | + |
| 390 | + if ((tx_eof) && rx_buf) { |
| 391 | + rx_buf[i] = rx_data; |
| 392 | + DBG("rx_buf[%02d]:0x%02x\n", i, rx_buf[i]); |
| 393 | + } |
| 394 | + } else if (rx_buf) { |
| 395 | + for (i = 0; i < (t->len - 1); i++) { |
| 396 | + err = cns21xx_spi_txrx_word(hw, spi->chip_select, 0, |
| 397 | + 0xff, &rx_data); |
| 398 | + if (err) |
| 399 | + goto done; |
| 400 | + |
| 401 | + rx_buf[i] = rx_data; |
| 402 | + DBG("rx_buf[%02d]:0x%02x\n", i, rx_buf[i]); |
| 403 | + } |
| 404 | + |
| 405 | + err = cns21xx_spi_txrx_word(hw, spi->chip_select, tx_eof, |
| 406 | + 0xff, &rx_data); |
| 407 | + if (err) |
| 408 | + goto done; |
| 409 | + |
| 410 | + rx_buf[i] = rx_data; |
| 411 | + DBG("rx_buf[%02d]:0x%02x\n", i, rx_buf[i]); |
| 412 | + } |
| 413 | + |
| 414 | + done: |
| 415 | + return (err) ? err : t->len; |
| 416 | +} |
| 417 | + |
| 418 | +static void __init cns21xx_spi_hw_init(struct cns21xx_spi *hw) |
| 419 | +{ |
| 420 | + u32 t; |
| 421 | + u32 pclk; |
| 422 | + |
| 423 | + /* Setup configuration register */ |
| 424 | + cns21xx_spi_wr(hw, CFG_SPI_MASTER_EN, SPI_REG_CFG); |
| 425 | + |
| 426 | + /* Set default clock to PCLK/2 */ |
| 427 | + cns21xx_spi_wr(hw, BIT_RATE_DIV_2, SPI_REG_BIT_RATE); |
| 428 | + |
| 429 | + /* Configure SPI's Tx channel */ |
| 430 | + cns21xx_spi_wr(hw, 0, SPI_REG_TX_CTRL); |
| 431 | + |
| 432 | + /* Configure Tx FIFO Threshold */ |
| 433 | + t = cns21xx_spi_rr(hw, SPI_REG_FIFO_TX_CFG); |
| 434 | + t &= ~(FIFO_TX_CFG_SPI_TXFF_THRED_M << FIFO_TX_CFG_SPI_TXFF_THRED_S); |
| 435 | + t |= (FIFO_TX_CFG_SPI_TXFF_THRED_2 << FIFO_TX_CFG_SPI_TXFF_THRED_S); |
| 436 | + cns21xx_spi_wr(hw, t, SPI_REG_FIFO_TX_CFG); |
| 437 | + |
| 438 | + /* Configure Rx FIFO Threshold */ |
| 439 | + t = cns21xx_spi_rr(hw, SPI_REG_FIFO_RX_CFG); |
| 440 | + t &= ~(FIFO_RX_CFG_SPI_RXFF_THRED_M << FIFO_RX_CFG_SPI_RXFF_THRED_S); |
| 441 | + t |= (FIFO_RX_CFG_SPI_RXFF_THRED_2 << FIFO_RX_CFG_SPI_RXFF_THRED_S); |
| 442 | + cns21xx_spi_wr(hw, t, SPI_REG_FIFO_RX_CFG); |
| 443 | + |
| 444 | + /* Disable interrupts, and clear interrupt status */ |
| 445 | + cns21xx_spi_wr(hw, 0, SPI_REG_INTR_ENA); |
| 446 | + cns21xx_spi_wr(hw, INTR_STAT_CLEAR_MASK, SPI_REG_INTR_STAT); |
| 447 | + |
| 448 | + (void) cns21xx_spi_rr(hw, SPI_REG_RX_DATA); |
| 449 | + |
| 450 | + /* Enable SPI */ |
| 451 | + t = cns21xx_spi_rr(hw, SPI_REG_CFG); |
| 452 | + t |= CFG_SPI_EN; |
| 453 | + cns21xx_spi_wr(hw, t, SPI_REG_CFG); |
| 454 | + |
| 455 | + pclk = cns21xx_get_apb_freq(); |
| 456 | + hw->freq_max = pclk; |
| 457 | + hw->freq_min = pclk / (1 << BIT_RATE_DIV_128); |
| 458 | +} |
| 459 | + |
| 460 | +static int __init cns21xx_spi_probe(struct platform_device *pdev) |
| 461 | +{ |
| 462 | + struct cns21xx_spi *hw; |
| 463 | + struct spi_master *master; |
| 464 | + struct resource *res; |
| 465 | + int err = 0; |
| 466 | + |
| 467 | + master = spi_alloc_master(&pdev->dev, sizeof(struct cns21xx_spi)); |
| 468 | + if (!master) { |
| 469 | + dev_err(&pdev->dev, "No memory for spi_master\n"); |
| 470 | + return -ENOMEM; |
| 471 | + } |
| 472 | + |
| 473 | + hw = spi_master_get_devdata(master); |
| 474 | + |
| 475 | + platform_set_drvdata(pdev, hw); |
| 476 | + hw->master = spi_master_get(master); |
| 477 | + hw->dev = &pdev->dev; |
| 478 | + |
| 479 | + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 480 | + if (!res) { |
| 481 | + dev_dbg(&pdev->dev, "no MEM resource found\n"); |
| 482 | + err = -ENOENT; |
| 483 | + goto err_put_master; |
| 484 | + } |
| 485 | + |
| 486 | + hw->region = request_mem_region(res->start, resource_size(res), |
| 487 | + dev_name(&pdev->dev)); |
| 488 | + if (!hw->region) { |
| 489 | + dev_err(&pdev->dev, "unable to reserve iomem region\n"); |
| 490 | + err = -ENXIO; |
| 491 | + goto err_put_master; |
| 492 | + } |
| 493 | + |
| 494 | + hw->base = ioremap(res->start, resource_size(res)); |
| 495 | + if (!hw->base) { |
| 496 | + dev_err(&pdev->dev, "ioremap failed\n"); |
| 497 | + err = -ENOENT; |
| 498 | + goto err_release_region; |
| 499 | + } |
| 500 | + |
| 501 | + cns21xx_spi_hw_init(hw); |
| 502 | + |
| 503 | + master->bus_num = pdev->id; |
| 504 | + if (master->bus_num == -1) |
| 505 | + master->bus_num = 0; |
| 506 | + |
| 507 | + master->num_chipselect = 4; |
| 508 | + master->setup = cns21xx_spi_setup; |
| 509 | + |
| 510 | + hw->bitbang.master = hw->master; |
| 511 | + hw->bitbang.chipselect = cns21xx_spi_chipselect; |
| 512 | + hw->bitbang.txrx_bufs = cns21xx_spi_txrx; |
| 513 | + hw->bitbang.setup_transfer = cns21xx_spi_setup_transfer; |
| 514 | + |
| 515 | + err = spi_bitbang_start(&hw->bitbang); |
| 516 | + if (err) { |
| 517 | + dev_err(hw->dev, "unable to register SPI master\n"); |
| 518 | + goto err_unmap; |
| 519 | + } |
| 520 | + |
| 521 | + dev_info(hw->dev, "iomem at %08x\n", res->start); |
| 522 | + |
| 523 | + return 0; |
| 524 | + |
| 525 | + err_unmap: |
| 526 | + iounmap(hw->base); |
| 527 | + |
| 528 | + err_release_region: |
| 529 | + release_resource(hw->region); |
| 530 | + kfree(hw->region); |
| 531 | + |
| 532 | + err_put_master: |
| 533 | + spi_master_put(hw->bitbang.master); |
| 534 | + platform_set_drvdata(pdev, NULL); |
| 535 | + |
| 536 | + return err; |
| 537 | +} |
| 538 | + |
| 539 | +static int __devexit cns21xx_spi_remove(struct platform_device *pdev) |
| 540 | +{ |
| 541 | + struct cns21xx_spi *hw = platform_get_drvdata(pdev); |
| 542 | + |
| 543 | + spi_bitbang_stop(&hw->bitbang); |
| 544 | + iounmap(hw->base); |
| 545 | + release_resource(hw->region); |
| 546 | + kfree(hw->region); |
| 547 | + spi_master_put(hw->bitbang.master); |
| 548 | + platform_set_drvdata(pdev, NULL); |
| 549 | + |
| 550 | + return 0; |
| 551 | +} |
| 552 | + |
| 553 | +static struct platform_driver cns21xx_spi_driver = { |
| 554 | + .remove = __devexit_p(cns21xx_spi_remove), |
| 555 | + .driver = { |
| 556 | + .name = DRIVER_NAME, |
| 557 | + .owner = THIS_MODULE, |
| 558 | + }, |
| 559 | +}; |
| 560 | + |
| 561 | +static int __init cns21xx_spi_init(void) |
| 562 | +{ |
| 563 | + return platform_driver_probe(&cns21xx_spi_driver, cns21xx_spi_probe); |
| 564 | +} |
| 565 | + |
| 566 | +static void __exit cns21xx_spi_exit(void) |
| 567 | +{ |
| 568 | + platform_driver_unregister(&cns21xx_spi_driver); |
| 569 | +} |
| 570 | + |
| 571 | +module_init(cns21xx_spi_init); |
| 572 | +module_exit(cns21xx_spi_exit); |
| 573 | + |
| 574 | +MODULE_DESCRIPTION("Cavium Networks CNS21xx SPI Controller driver"); |
| 575 | +MODULE_AUTHOR("STAR Semi Corp."); |
| 576 | +MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); |
| 577 | +MODULE_LICENSE("GPL v2"); |
| 578 | +MODULE_ALIAS("platform:" DRIVER_NAME); |
| 579 | |