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.13-rc1 321 lines 7.7 kB view raw
1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 2011, 2012 Cavium, Inc. 7 */ 8 9#include <linux/platform_device.h> 10#include <linux/interrupt.h> 11#include <linux/spi/spi.h> 12#include <linux/module.h> 13#include <linux/delay.h> 14#include <linux/init.h> 15#include <linux/io.h> 16#include <linux/of.h> 17 18#include <asm/octeon/octeon.h> 19#include <asm/octeon/cvmx-mpi-defs.h> 20 21#define OCTEON_SPI_CFG 0 22#define OCTEON_SPI_STS 0x08 23#define OCTEON_SPI_TX 0x10 24#define OCTEON_SPI_DAT0 0x80 25 26#define OCTEON_SPI_MAX_BYTES 9 27 28#define OCTEON_SPI_MAX_CLOCK_HZ 16000000 29 30struct octeon_spi { 31 u64 register_base; 32 u64 last_cfg; 33 u64 cs_enax; 34}; 35 36struct octeon_spi_setup { 37 u32 max_speed_hz; 38 u8 chip_select; 39 u8 mode; 40 u8 bits_per_word; 41}; 42 43static void octeon_spi_wait_ready(struct octeon_spi *p) 44{ 45 union cvmx_mpi_sts mpi_sts; 46 unsigned int loops = 0; 47 48 do { 49 if (loops++) 50 __delay(500); 51 mpi_sts.u64 = cvmx_read_csr(p->register_base + OCTEON_SPI_STS); 52 } while (mpi_sts.s.busy); 53} 54 55static int octeon_spi_do_transfer(struct octeon_spi *p, 56 struct spi_message *msg, 57 struct spi_transfer *xfer, 58 bool last_xfer) 59{ 60 union cvmx_mpi_cfg mpi_cfg; 61 union cvmx_mpi_tx mpi_tx; 62 unsigned int clkdiv; 63 unsigned int speed_hz; 64 int mode; 65 bool cpha, cpol; 66 const u8 *tx_buf; 67 u8 *rx_buf; 68 int len; 69 int i; 70 71 struct octeon_spi_setup *msg_setup = spi_get_ctldata(msg->spi); 72 73 speed_hz = msg_setup->max_speed_hz; 74 mode = msg_setup->mode; 75 cpha = mode & SPI_CPHA; 76 cpol = mode & SPI_CPOL; 77 78 if (xfer->speed_hz) 79 speed_hz = xfer->speed_hz; 80 81 if (speed_hz > OCTEON_SPI_MAX_CLOCK_HZ) 82 speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; 83 84 clkdiv = octeon_get_io_clock_rate() / (2 * speed_hz); 85 86 mpi_cfg.u64 = 0; 87 88 mpi_cfg.s.clkdiv = clkdiv; 89 mpi_cfg.s.cshi = (mode & SPI_CS_HIGH) ? 1 : 0; 90 mpi_cfg.s.lsbfirst = (mode & SPI_LSB_FIRST) ? 1 : 0; 91 mpi_cfg.s.wireor = (mode & SPI_3WIRE) ? 1 : 0; 92 mpi_cfg.s.idlelo = cpha != cpol; 93 mpi_cfg.s.cslate = cpha ? 1 : 0; 94 mpi_cfg.s.enable = 1; 95 96 if (msg_setup->chip_select < 4) 97 p->cs_enax |= 1ull << (12 + msg_setup->chip_select); 98 mpi_cfg.u64 |= p->cs_enax; 99 100 if (mpi_cfg.u64 != p->last_cfg) { 101 p->last_cfg = mpi_cfg.u64; 102 cvmx_write_csr(p->register_base + OCTEON_SPI_CFG, mpi_cfg.u64); 103 } 104 tx_buf = xfer->tx_buf; 105 rx_buf = xfer->rx_buf; 106 len = xfer->len; 107 while (len > OCTEON_SPI_MAX_BYTES) { 108 for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { 109 u8 d; 110 if (tx_buf) 111 d = *tx_buf++; 112 else 113 d = 0; 114 cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d); 115 } 116 mpi_tx.u64 = 0; 117 mpi_tx.s.csid = msg_setup->chip_select; 118 mpi_tx.s.leavecs = 1; 119 mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0; 120 mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES; 121 cvmx_write_csr(p->register_base + OCTEON_SPI_TX, mpi_tx.u64); 122 123 octeon_spi_wait_ready(p); 124 if (rx_buf) 125 for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { 126 u64 v = cvmx_read_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); 127 *rx_buf++ = (u8)v; 128 } 129 len -= OCTEON_SPI_MAX_BYTES; 130 } 131 132 for (i = 0; i < len; i++) { 133 u8 d; 134 if (tx_buf) 135 d = *tx_buf++; 136 else 137 d = 0; 138 cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d); 139 } 140 141 mpi_tx.u64 = 0; 142 mpi_tx.s.csid = msg_setup->chip_select; 143 if (last_xfer) 144 mpi_tx.s.leavecs = xfer->cs_change; 145 else 146 mpi_tx.s.leavecs = !xfer->cs_change; 147 mpi_tx.s.txnum = tx_buf ? len : 0; 148 mpi_tx.s.totnum = len; 149 cvmx_write_csr(p->register_base + OCTEON_SPI_TX, mpi_tx.u64); 150 151 octeon_spi_wait_ready(p); 152 if (rx_buf) 153 for (i = 0; i < len; i++) { 154 u64 v = cvmx_read_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); 155 *rx_buf++ = (u8)v; 156 } 157 158 if (xfer->delay_usecs) 159 udelay(xfer->delay_usecs); 160 161 return xfer->len; 162} 163 164static int octeon_spi_transfer_one_message(struct spi_master *master, 165 struct spi_message *msg) 166{ 167 struct octeon_spi *p = spi_master_get_devdata(master); 168 unsigned int total_len = 0; 169 int status = 0; 170 struct spi_transfer *xfer; 171 172 /* 173 * We better have set the configuration via a call to .setup 174 * before we get here. 175 */ 176 if (spi_get_ctldata(msg->spi) == NULL) { 177 status = -EINVAL; 178 goto err; 179 } 180 181 list_for_each_entry(xfer, &msg->transfers, transfer_list) { 182 bool last_xfer = &xfer->transfer_list == msg->transfers.prev; 183 int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer); 184 if (r < 0) { 185 status = r; 186 goto err; 187 } 188 total_len += r; 189 } 190err: 191 msg->status = status; 192 msg->actual_length = total_len; 193 spi_finalize_current_message(master); 194 return status; 195} 196 197static struct octeon_spi_setup *octeon_spi_new_setup(struct spi_device *spi) 198{ 199 struct octeon_spi_setup *setup = kzalloc(sizeof(*setup), GFP_KERNEL); 200 if (!setup) 201 return NULL; 202 203 setup->max_speed_hz = spi->max_speed_hz; 204 setup->chip_select = spi->chip_select; 205 setup->mode = spi->mode; 206 setup->bits_per_word = spi->bits_per_word; 207 return setup; 208} 209 210static int octeon_spi_setup(struct spi_device *spi) 211{ 212 struct octeon_spi_setup *new_setup; 213 struct octeon_spi_setup *old_setup = spi_get_ctldata(spi); 214 215 new_setup = octeon_spi_new_setup(spi); 216 if (!new_setup) 217 return -ENOMEM; 218 219 spi_set_ctldata(spi, new_setup); 220 kfree(old_setup); 221 222 return 0; 223} 224 225static void octeon_spi_cleanup(struct spi_device *spi) 226{ 227 struct octeon_spi_setup *old_setup = spi_get_ctldata(spi); 228 spi_set_ctldata(spi, NULL); 229 kfree(old_setup); 230} 231 232static int octeon_spi_probe(struct platform_device *pdev) 233{ 234 struct resource *res_mem; 235 struct spi_master *master; 236 struct octeon_spi *p; 237 int err = -ENOENT; 238 239 master = spi_alloc_master(&pdev->dev, sizeof(struct octeon_spi)); 240 if (!master) 241 return -ENOMEM; 242 p = spi_master_get_devdata(master); 243 platform_set_drvdata(pdev, master); 244 245 res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 246 247 if (res_mem == NULL) { 248 dev_err(&pdev->dev, "found no memory resource\n"); 249 err = -ENXIO; 250 goto fail; 251 } 252 if (!devm_request_mem_region(&pdev->dev, res_mem->start, 253 resource_size(res_mem), res_mem->name)) { 254 dev_err(&pdev->dev, "request_mem_region failed\n"); 255 goto fail; 256 } 257 p->register_base = (u64)devm_ioremap(&pdev->dev, res_mem->start, 258 resource_size(res_mem)); 259 260 /* Dynamic bus numbering */ 261 master->bus_num = -1; 262 master->num_chipselect = 4; 263 master->mode_bits = SPI_CPHA | 264 SPI_CPOL | 265 SPI_CS_HIGH | 266 SPI_LSB_FIRST | 267 SPI_3WIRE; 268 269 master->setup = octeon_spi_setup; 270 master->cleanup = octeon_spi_cleanup; 271 master->transfer_one_message = octeon_spi_transfer_one_message; 272 master->bits_per_word_mask = SPI_BPW_MASK(8); 273 274 master->dev.of_node = pdev->dev.of_node; 275 err = devm_spi_register_master(&pdev->dev, master); 276 if (err) { 277 dev_err(&pdev->dev, "register master failed: %d\n", err); 278 goto fail; 279 } 280 281 dev_info(&pdev->dev, "OCTEON SPI bus driver\n"); 282 283 return 0; 284fail: 285 spi_master_put(master); 286 return err; 287} 288 289static int octeon_spi_remove(struct platform_device *pdev) 290{ 291 struct spi_master *master = platform_get_drvdata(pdev); 292 struct octeon_spi *p = spi_master_get_devdata(master); 293 u64 register_base = p->register_base; 294 295 /* Clear the CSENA* and put everything in a known state. */ 296 cvmx_write_csr(register_base + OCTEON_SPI_CFG, 0); 297 298 return 0; 299} 300 301static struct of_device_id octeon_spi_match[] = { 302 { .compatible = "cavium,octeon-3010-spi", }, 303 {}, 304}; 305MODULE_DEVICE_TABLE(of, octeon_spi_match); 306 307static struct platform_driver octeon_spi_driver = { 308 .driver = { 309 .name = "spi-octeon", 310 .owner = THIS_MODULE, 311 .of_match_table = octeon_spi_match, 312 }, 313 .probe = octeon_spi_probe, 314 .remove = octeon_spi_remove, 315}; 316 317module_platform_driver(octeon_spi_driver); 318 319MODULE_DESCRIPTION("Cavium, Inc. OCTEON SPI bus driver"); 320MODULE_AUTHOR("David Daney"); 321MODULE_LICENSE("GPL");