diff options
Diffstat (limited to 'drivers/spi/spi-octeon.c')
| -rw-r--r-- | drivers/spi/spi-octeon.c | 362 |
1 files changed, 0 insertions, 362 deletions
diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c deleted file mode 100644 index 24daf964a409..000000000000 --- a/drivers/spi/spi-octeon.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2011, 2012 Cavium, Inc. - */ - -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/spi/spi.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/init.h> -#include <linux/io.h> -#include <linux/of.h> - -#include <asm/octeon/octeon.h> -#include <asm/octeon/cvmx-mpi-defs.h> - -#define OCTEON_SPI_CFG 0 -#define OCTEON_SPI_STS 0x08 -#define OCTEON_SPI_TX 0x10 -#define OCTEON_SPI_DAT0 0x80 - -#define OCTEON_SPI_MAX_BYTES 9 - -#define OCTEON_SPI_MAX_CLOCK_HZ 16000000 - -struct octeon_spi { - struct spi_master *my_master; - u64 register_base; - u64 last_cfg; - u64 cs_enax; -}; - -struct octeon_spi_setup { - u32 max_speed_hz; - u8 chip_select; - u8 mode; - u8 bits_per_word; -}; - -static void octeon_spi_wait_ready(struct octeon_spi *p) -{ - union cvmx_mpi_sts mpi_sts; - unsigned int loops = 0; - - do { - if (loops++) - __delay(500); - mpi_sts.u64 = cvmx_read_csr(p->register_base + OCTEON_SPI_STS); - } while (mpi_sts.s.busy); -} - -static int octeon_spi_do_transfer(struct octeon_spi *p, - struct spi_message *msg, - struct spi_transfer *xfer, - bool last_xfer) -{ - union cvmx_mpi_cfg mpi_cfg; - union cvmx_mpi_tx mpi_tx; - unsigned int clkdiv; - unsigned int speed_hz; - int mode; - bool cpha, cpol; - int bits_per_word; - const u8 *tx_buf; - u8 *rx_buf; - int len; - int i; - - struct octeon_spi_setup *msg_setup = spi_get_ctldata(msg->spi); - - speed_hz = msg_setup->max_speed_hz; - mode = msg_setup->mode; - cpha = mode & SPI_CPHA; - cpol = mode & SPI_CPOL; - bits_per_word = msg_setup->bits_per_word; - - if (xfer->speed_hz) - speed_hz = xfer->speed_hz; - if (xfer->bits_per_word) - bits_per_word = xfer->bits_per_word; - - if (speed_hz > OCTEON_SPI_MAX_CLOCK_HZ) - speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; - - clkdiv = octeon_get_io_clock_rate() / (2 * speed_hz); - - mpi_cfg.u64 = 0; - - mpi_cfg.s.clkdiv = clkdiv; - mpi_cfg.s.cshi = (mode & SPI_CS_HIGH) ? 1 : 0; - mpi_cfg.s.lsbfirst = (mode & SPI_LSB_FIRST) ? 1 : 0; - mpi_cfg.s.wireor = (mode & SPI_3WIRE) ? 1 : 0; - mpi_cfg.s.idlelo = cpha != cpol; - mpi_cfg.s.cslate = cpha ? 1 : 0; - mpi_cfg.s.enable = 1; - - if (msg_setup->chip_select < 4) - p->cs_enax |= 1ull << (12 + msg_setup->chip_select); - mpi_cfg.u64 |= p->cs_enax; - - if (mpi_cfg.u64 != p->last_cfg) { - p->last_cfg = mpi_cfg.u64; - cvmx_write_csr(p->register_base + OCTEON_SPI_CFG, mpi_cfg.u64); - } - tx_buf = xfer->tx_buf; - rx_buf = xfer->rx_buf; - len = xfer->len; - while (len > OCTEON_SPI_MAX_BYTES) { - for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { - u8 d; - if (tx_buf) - d = *tx_buf++; - else - d = 0; - cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d); - } - mpi_tx.u64 = 0; - mpi_tx.s.csid = msg_setup->chip_select; - mpi_tx.s.leavecs = 1; - mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0; - mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES; - cvmx_write_csr(p->register_base + OCTEON_SPI_TX, mpi_tx.u64); - - octeon_spi_wait_ready(p); - if (rx_buf) - for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { - u64 v = cvmx_read_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); - *rx_buf++ = (u8)v; - } - len -= OCTEON_SPI_MAX_BYTES; - } - - for (i = 0; i < len; i++) { - u8 d; - if (tx_buf) - d = *tx_buf++; - else - d = 0; - cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d); - } - - mpi_tx.u64 = 0; - mpi_tx.s.csid = msg_setup->chip_select; - if (last_xfer) - mpi_tx.s.leavecs = xfer->cs_change; - else - mpi_tx.s.leavecs = !xfer->cs_change; - mpi_tx.s.txnum = tx_buf ? len : 0; - mpi_tx.s.totnum = len; - cvmx_write_csr(p->register_base + OCTEON_SPI_TX, mpi_tx.u64); - - octeon_spi_wait_ready(p); - if (rx_buf) - for (i = 0; i < len; i++) { - u64 v = cvmx_read_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); - *rx_buf++ = (u8)v; - } - - if (xfer->delay_usecs) - udelay(xfer->delay_usecs); - - return xfer->len; -} - -static int octeon_spi_validate_bpw(struct spi_device *spi, u32 speed) -{ - switch (speed) { - case 8: - break; - default: - dev_err(&spi->dev, "Error: %d bits per word not supported\n", - speed); - return -EINVAL; - } - return 0; -} - -static int octeon_spi_transfer_one_message(struct spi_master *master, - struct spi_message *msg) -{ - struct octeon_spi *p = spi_master_get_devdata(master); - unsigned int total_len = 0; - int status = 0; - struct spi_transfer *xfer; - - /* - * We better have set the configuration via a call to .setup - * before we get here. - */ - if (spi_get_ctldata(msg->spi) == NULL) { - status = -EINVAL; - goto err; - } - - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - if (xfer->bits_per_word) { - status = octeon_spi_validate_bpw(msg->spi, - xfer->bits_per_word); - if (status) - goto err; - } - } - - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - bool last_xfer = &xfer->transfer_list == msg->transfers.prev; - int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer); - if (r < 0) { - status = r; - goto err; - } - total_len += r; - } -err: - msg->status = status; - msg->actual_length = total_len; - spi_finalize_current_message(master); - return status; -} - -static struct octeon_spi_setup *octeon_spi_new_setup(struct spi_device *spi) -{ - struct octeon_spi_setup *setup = kzalloc(sizeof(*setup), GFP_KERNEL); - if (!setup) - return NULL; - - setup->max_speed_hz = spi->max_speed_hz; - setup->chip_select = spi->chip_select; - setup->mode = spi->mode; - setup->bits_per_word = spi->bits_per_word; - return setup; -} - -static int octeon_spi_setup(struct spi_device *spi) -{ - int r; - struct octeon_spi_setup *new_setup; - struct octeon_spi_setup *old_setup = spi_get_ctldata(spi); - - r = octeon_spi_validate_bpw(spi, spi->bits_per_word); - if (r) - return r; - - new_setup = octeon_spi_new_setup(spi); - if (!new_setup) - return -ENOMEM; - - spi_set_ctldata(spi, new_setup); - kfree(old_setup); - - return 0; -} - -static void octeon_spi_cleanup(struct spi_device *spi) -{ - struct octeon_spi_setup *old_setup = spi_get_ctldata(spi); - spi_set_ctldata(spi, NULL); - kfree(old_setup); -} - -static int octeon_spi_nop_transfer_hardware(struct spi_master *master) -{ - return 0; -} - -static int octeon_spi_probe(struct platform_device *pdev) -{ - - struct resource *res_mem; - struct spi_master *master; - struct octeon_spi *p; - int err = -ENOENT; - - master = spi_alloc_master(&pdev->dev, sizeof(struct octeon_spi)); - if (!master) - return -ENOMEM; - p = spi_master_get_devdata(master); - platform_set_drvdata(pdev, p); - p->my_master = master; - - res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - if (res_mem == NULL) { - dev_err(&pdev->dev, "found no memory resource\n"); - err = -ENXIO; - goto fail; - } - if (!devm_request_mem_region(&pdev->dev, res_mem->start, - resource_size(res_mem), res_mem->name)) { - dev_err(&pdev->dev, "request_mem_region failed\n"); - goto fail; - } - p->register_base = (u64)devm_ioremap(&pdev->dev, res_mem->start, - resource_size(res_mem)); - - /* Dynamic bus numbering */ - master->bus_num = -1; - master->num_chipselect = 4; - master->mode_bits = SPI_CPHA | - SPI_CPOL | - SPI_CS_HIGH | - SPI_LSB_FIRST | - SPI_3WIRE; - - master->setup = octeon_spi_setup; - master->cleanup = octeon_spi_cleanup; - master->prepare_transfer_hardware = octeon_spi_nop_transfer_hardware; - master->transfer_one_message = octeon_spi_transfer_one_message; - master->unprepare_transfer_hardware = octeon_spi_nop_transfer_hardware; - - master->dev.of_node = pdev->dev.of_node; - err = spi_register_master(master); - if (err) { - dev_err(&pdev->dev, "register master failed: %d\n", err); - goto fail; - } - - dev_info(&pdev->dev, "OCTEON SPI bus driver\n"); - - return 0; -fail: - spi_master_put(master); - return err; -} - -static int octeon_spi_remove(struct platform_device *pdev) -{ - struct octeon_spi *p = platform_get_drvdata(pdev); - u64 register_base = p->register_base; - - spi_unregister_master(p->my_master); - - /* Clear the CSENA* and put everything in a known state. */ - cvmx_write_csr(register_base + OCTEON_SPI_CFG, 0); - - return 0; -} - -static struct of_device_id octeon_spi_match[] = { - { .compatible = "cavium,octeon-3010-spi", }, - {}, -}; -MODULE_DEVICE_TABLE(of, octeon_spi_match); - -static struct platform_driver octeon_spi_driver = { - .driver = { - .name = "spi-octeon", - .owner = THIS_MODULE, - .of_match_table = octeon_spi_match, - }, - .probe = octeon_spi_probe, - .remove = octeon_spi_remove, -}; - -module_platform_driver(octeon_spi_driver); - -MODULE_DESCRIPTION("Cavium, Inc. OCTEON SPI bus driver"); -MODULE_AUTHOR("David Daney"); -MODULE_LICENSE("GPL"); |
