Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Greg Kroah-Hartman | 2141 | 95.54% | 3 | 15.79% |
Fabian Krueger | 33 | 1.47% | 4 | 21.05% |
Linus Torvalds | 21 | 0.94% | 1 | 5.26% |
Mao Wenan | 18 | 0.80% | 2 | 10.53% |
Sergiu Cuciurean | 11 | 0.49% | 1 | 5.26% |
Geordan Neukum | 6 | 0.27% | 2 | 10.53% |
Nathan Chancellor | 3 | 0.13% | 1 | 5.26% |
Dan Carpenter | 3 | 0.13% | 1 | 5.26% |
Simon Sandström | 2 | 0.09% | 1 | 5.26% |
Christoph Hellwig | 1 | 0.04% | 1 | 5.26% |
Chandra Annamaneni | 1 | 0.04% | 1 | 5.26% |
Wambui Karuga | 1 | 0.04% | 1 | 5.26% |
Total | 2241 | 19 |
// SPDX-License-Identifier: GPL-2.0+ /* * KP2000 SPI controller driver * * Copyright (C) 2014-2018 Daktronics * Author: Matt Sickler <matt.sickler@daktronics.com> * Very loosely based on spi-omap2-mcspi.c */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io-64-nonatomic-lo-hi.h> #include <linux/module.h> #include <linux/device.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/err.h> #include <linux/clk.h> #include <linux/io.h> #include <linux/slab.h> #include <linux/pm_runtime.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/gcd.h> #include <linux/spi/spi.h> #include <linux/spi/flash.h> #include <linux/mtd/partitions.h> #include "kpc.h" static struct mtd_partition p2kr0_spi0_parts[] = { { .name = "SLOT_0", .size = 7798784, .offset = 0, }, { .name = "SLOT_1", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, { .name = "SLOT_2", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, { .name = "SLOT_3", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, { .name = "CS0_EXTRA", .size = MTDPART_SIZ_FULL, .offset = MTDPART_OFS_NXTBLK}, }; static struct mtd_partition p2kr0_spi1_parts[] = { { .name = "SLOT_4", .size = 7798784, .offset = 0, }, { .name = "SLOT_5", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, { .name = "SLOT_6", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, { .name = "SLOT_7", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, { .name = "CS1_EXTRA", .size = MTDPART_SIZ_FULL, .offset = MTDPART_OFS_NXTBLK}, }; static struct flash_platform_data p2kr0_spi0_pdata = { .name = "SPI0", .nr_parts = ARRAY_SIZE(p2kr0_spi0_parts), .parts = p2kr0_spi0_parts, }; static struct flash_platform_data p2kr0_spi1_pdata = { .name = "SPI1", .nr_parts = ARRAY_SIZE(p2kr0_spi1_parts), .parts = p2kr0_spi1_parts, }; static struct spi_board_info p2kr0_board_info[] = { { .modalias = "n25q256a11", .bus_num = 1, .chip_select = 0, .mode = SPI_MODE_0, .platform_data = &p2kr0_spi0_pdata }, { .modalias = "n25q256a11", .bus_num = 1, .chip_select = 1, .mode = SPI_MODE_0, .platform_data = &p2kr0_spi1_pdata }, }; /*************** * SPI Defines * ***************/ #define KP_SPI_REG_CONFIG 0x0 /* 0x00 */ #define KP_SPI_REG_STATUS 0x1 /* 0x08 */ #define KP_SPI_REG_FFCTRL 0x2 /* 0x10 */ #define KP_SPI_REG_TXDATA 0x3 /* 0x18 */ #define KP_SPI_REG_RXDATA 0x4 /* 0x20 */ #define KP_SPI_CLK 48000000 #define KP_SPI_MAX_FIFODEPTH 64 #define KP_SPI_MAX_FIFOWCNT 0xFFFF #define KP_SPI_REG_CONFIG_TRM_TXRX 0 #define KP_SPI_REG_CONFIG_TRM_RX 1 #define KP_SPI_REG_CONFIG_TRM_TX 2 #define KP_SPI_REG_STATUS_RXS 0x01 #define KP_SPI_REG_STATUS_TXS 0x02 #define KP_SPI_REG_STATUS_EOT 0x04 #define KP_SPI_REG_STATUS_TXFFE 0x10 #define KP_SPI_REG_STATUS_TXFFF 0x20 #define KP_SPI_REG_STATUS_RXFFE 0x40 #define KP_SPI_REG_STATUS_RXFFF 0x80 /****************** * SPI Structures * ******************/ struct kp_spi { struct spi_master *master; u64 __iomem *base; struct device *dev; }; struct kp_spi_controller_state { void __iomem *base; s64 conf_cache; }; union kp_spi_config { /* use this to access individual elements */ struct __packed spi_config_bitfield { unsigned int pha : 1; /* spim_clk Phase */ unsigned int pol : 1; /* spim_clk Polarity */ unsigned int epol : 1; /* spim_csx Polarity */ unsigned int dpe : 1; /* Transmission Enable */ unsigned int wl : 5; /* Word Length */ unsigned int : 3; unsigned int trm : 2; /* TxRx Mode */ unsigned int cs : 4; /* Chip Select */ unsigned int wcnt : 7; /* Word Count */ unsigned int ffen : 1; /* FIFO Enable */ unsigned int spi_en : 1; /* SPI Enable */ unsigned int : 5; } bitfield; /* use this to grab the whole register */ u32 reg; }; union kp_spi_status { struct __packed spi_status_bitfield { unsigned int rx : 1; /* Rx Status */ unsigned int tx : 1; /* Tx Status */ unsigned int eo : 1; /* End of Transfer */ unsigned int : 1; unsigned int txffe : 1; /* Tx FIFO Empty */ unsigned int txfff : 1; /* Tx FIFO Full */ unsigned int rxffe : 1; /* Rx FIFO Empty */ unsigned int rxfff : 1; /* Rx FIFO Full */ unsigned int : 24; } bitfield; u32 reg; }; union kp_spi_ffctrl { struct __packed spi_ffctrl_bitfield { unsigned int ffstart : 1; /* FIFO Start */ unsigned int : 31; } bitfield; u32 reg; }; /*************** * SPI Helpers * ***************/ static inline u64 kp_spi_read_reg(struct kp_spi_controller_state *cs, int idx) { u64 __iomem *addr = cs->base; addr += idx; if ((idx == KP_SPI_REG_CONFIG) && (cs->conf_cache >= 0)) return cs->conf_cache; return readq(addr); } static inline void kp_spi_write_reg(struct kp_spi_controller_state *cs, int idx, u64 val) { u64 __iomem *addr = cs->base; addr += idx; writeq(val, addr); if (idx == KP_SPI_REG_CONFIG) cs->conf_cache = val; } static int kp_spi_wait_for_reg_bit(struct kp_spi_controller_state *cs, int idx, unsigned long bit) { unsigned long timeout; timeout = jiffies + msecs_to_jiffies(1000); while (!(kp_spi_read_reg(cs, idx) & bit)) { if (time_after(jiffies, timeout)) { if (!(kp_spi_read_reg(cs, idx) & bit)) return -ETIMEDOUT; else return 0; } cpu_relax(); } return 0; } static unsigned kp_spi_txrx_pio(struct spi_device *spidev, struct spi_transfer *transfer) { struct kp_spi_controller_state *cs = spidev->controller_state; unsigned int count = transfer->len; unsigned int c = count; int i; int res; u8 *rx = transfer->rx_buf; const u8 *tx = transfer->tx_buf; int processed = 0; if (tx) { for (i = 0 ; i < c ; i++) { char val = *tx++; res = kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_TXS); if (res < 0) goto out; kp_spi_write_reg(cs, KP_SPI_REG_TXDATA, val); processed++; } } else if (rx) { for (i = 0 ; i < c ; i++) { char test = 0; kp_spi_write_reg(cs, KP_SPI_REG_TXDATA, 0x00); res = kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_RXS); if (res < 0) goto out; test = kp_spi_read_reg(cs, KP_SPI_REG_RXDATA); *rx++ = test; processed++; } } if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_EOT) < 0) { //TODO: Figure out how to abort transaction?? //Ths has never happened in practice though... } out: return processed; } /***************** * SPI Functions * *****************/ static int kp_spi_setup(struct spi_device *spidev) { union kp_spi_config sc; struct kp_spi *kpspi = spi_master_get_devdata(spidev->master); struct kp_spi_controller_state *cs; /* setup controller state */ cs = spidev->controller_state; if (!cs) { cs = kzalloc(sizeof(*cs), GFP_KERNEL); if (!cs) return -ENOMEM; cs->base = kpspi->base; cs->conf_cache = -1; spidev->controller_state = cs; } /* set config register */ sc.bitfield.wl = spidev->bits_per_word - 1; sc.bitfield.cs = spidev->chip_select; sc.bitfield.spi_en = 0; sc.bitfield.trm = 0; sc.bitfield.ffen = 0; kp_spi_write_reg(spidev->controller_state, KP_SPI_REG_CONFIG, sc.reg); return 0; } static int kp_spi_transfer_one_message(struct spi_master *master, struct spi_message *m) { struct kp_spi_controller_state *cs; struct spi_device *spidev; struct kp_spi *kpspi; struct spi_transfer *transfer; union kp_spi_config sc; int status = 0; spidev = m->spi; kpspi = spi_master_get_devdata(master); m->actual_length = 0; m->status = 0; cs = spidev->controller_state; /* reject invalid messages and transfers */ if (list_empty(&m->transfers)) return -EINVAL; /* validate input */ list_for_each_entry(transfer, &m->transfers, transfer_list) { const void *tx_buf = transfer->tx_buf; void *rx_buf = transfer->rx_buf; unsigned int len = transfer->len; if (transfer->speed_hz > KP_SPI_CLK || (len && !(rx_buf || tx_buf))) { dev_dbg(kpspi->dev, " transfer: %d Hz, %d %s%s, %d bpw\n", transfer->speed_hz, len, tx_buf ? "tx" : "", rx_buf ? "rx" : "", transfer->bits_per_word); dev_dbg(kpspi->dev, " transfer -EINVAL\n"); return -EINVAL; } if (transfer->speed_hz && transfer->speed_hz < (KP_SPI_CLK >> 15)) { dev_dbg(kpspi->dev, "speed_hz %d below minimum %d Hz\n", transfer->speed_hz, KP_SPI_CLK >> 15); dev_dbg(kpspi->dev, " speed_hz -EINVAL\n"); return -EINVAL; } } /* assert chip select to start the sequence*/ sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG); sc.bitfield.spi_en = 1; kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg); /* work */ if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_EOT) < 0) { dev_info(kpspi->dev, "EOT timed out\n"); goto out; } /* do the transfers for this message */ list_for_each_entry(transfer, &m->transfers, transfer_list) { if (!transfer->tx_buf && !transfer->rx_buf && transfer->len) { status = -EINVAL; goto error; } /* transfer */ if (transfer->len) { unsigned int word_len = spidev->bits_per_word; unsigned int count; /* set up the transfer... */ sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG); /* ...direction */ if (transfer->tx_buf) sc.bitfield.trm = KP_SPI_REG_CONFIG_TRM_TX; else if (transfer->rx_buf) sc.bitfield.trm = KP_SPI_REG_CONFIG_TRM_RX; /* ...word length */ if (transfer->bits_per_word) word_len = transfer->bits_per_word; sc.bitfield.wl = word_len - 1; /* ...chip select */ sc.bitfield.cs = spidev->chip_select; /* ...and write the new settings */ kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg); /* do the transfer */ count = kp_spi_txrx_pio(spidev, transfer); m->actual_length += count; if (count != transfer->len) { status = -EIO; goto error; } } if (transfer->delay.value) ndelay(spi_delay_to_ns(&transfer->delay, transfer)); } /* de-assert chip select to end the sequence */ sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG); sc.bitfield.spi_en = 0; kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg); out: /* done work */ spi_finalize_current_message(master); return 0; error: m->status = status; return status; } static void kp_spi_cleanup(struct spi_device *spidev) { struct kp_spi_controller_state *cs = spidev->controller_state; kfree(cs); } /****************** * Probe / Remove * ******************/ static int kp_spi_probe(struct platform_device *pldev) { struct kpc_core_device_platdata *drvdata; struct spi_master *master; struct kp_spi *kpspi; struct resource *r; int status = 0; int i; drvdata = pldev->dev.platform_data; if (!drvdata) { dev_err(&pldev->dev, "%s: platform_data is NULL\n", __func__); return -ENODEV; } master = spi_alloc_master(&pldev->dev, sizeof(struct kp_spi)); if (!master) { dev_err(&pldev->dev, "%s: master allocation failed\n", __func__); return -ENOMEM; } /* set up the spi functions */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; master->bits_per_word_mask = (unsigned int)SPI_BPW_RANGE_MASK(4, 32); master->setup = kp_spi_setup; master->transfer_one_message = kp_spi_transfer_one_message; master->cleanup = kp_spi_cleanup; platform_set_drvdata(pldev, master); kpspi = spi_master_get_devdata(master); kpspi->master = master; kpspi->dev = &pldev->dev; master->num_chipselect = 4; if (pldev->id != -1) master->bus_num = pldev->id; r = platform_get_resource(pldev, IORESOURCE_MEM, 0); if (!r) { dev_err(&pldev->dev, "%s: Unable to get platform resources\n", __func__); status = -ENODEV; goto free_master; } kpspi->base = devm_ioremap(&pldev->dev, r->start, resource_size(r)); status = spi_register_master(master); if (status < 0) { dev_err(&pldev->dev, "Unable to register SPI device\n"); goto free_master; } /* register the slave boards */ #define NEW_SPI_DEVICE_FROM_BOARD_INFO_TABLE(table) \ for (i = 0 ; i < ARRAY_SIZE(table) ; i++) { \ spi_new_device(master, &table[i]); \ } switch ((drvdata->card_id & 0xFFFF0000) >> 16) { case PCI_DEVICE_ID_DAKTRONICS_KADOKA_P2KR0: NEW_SPI_DEVICE_FROM_BOARD_INFO_TABLE(p2kr0_board_info); break; default: dev_err(&pldev->dev, "Unknown hardware, cant know what partition table to use!\n"); goto free_master; } return status; free_master: spi_master_put(master); return status; } static int kp_spi_remove(struct platform_device *pldev) { struct spi_master *master = platform_get_drvdata(pldev); spi_unregister_master(master); return 0; } static struct platform_driver kp_spi_driver = { .driver = { .name = KP_DRIVER_NAME_SPI, }, .probe = kp_spi_probe, .remove = kp_spi_remove, }; module_platform_driver(kp_spi_driver); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:kp_spi");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1