162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for Broadcom BCM2835 auxiliary SPI Controllers 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * the driver does not rely on the native chipselects at all 662306a36Sopenharmony_ci * but only uses the gpio type chipselects 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based on: spi-bcm2835.c 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright (C) 2015 Martin Sperl 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/clk.h> 1462306a36Sopenharmony_ci#include <linux/completion.h> 1562306a36Sopenharmony_ci#include <linux/debugfs.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/err.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/io.h> 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/of.h> 2362306a36Sopenharmony_ci#include <linux/platform_device.h> 2462306a36Sopenharmony_ci#include <linux/regmap.h> 2562306a36Sopenharmony_ci#include <linux/spi/spi.h> 2662306a36Sopenharmony_ci#include <linux/spinlock.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* define polling limits */ 2962306a36Sopenharmony_cistatic unsigned int polling_limit_us = 30; 3062306a36Sopenharmony_cimodule_param(polling_limit_us, uint, 0664); 3162306a36Sopenharmony_ciMODULE_PARM_DESC(polling_limit_us, 3262306a36Sopenharmony_ci "time in us to run a transfer in polling mode - if zero no polling is used\n"); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * spi register defines 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * note there is garbage in the "official" documentation, 3862306a36Sopenharmony_ci * so some data is taken from the file: 3962306a36Sopenharmony_ci * brcm_usrlib/dag/vmcsx/vcinclude/bcm2708_chip/aux_io.h 4062306a36Sopenharmony_ci * inside of: 4162306a36Sopenharmony_ci * http://www.broadcom.com/docs/support/videocore/Brcm_Android_ICS_Graphics_Stack.tar.gz 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* SPI register offsets */ 4562306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL0 0x00 4662306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL1 0x04 4762306a36Sopenharmony_ci#define BCM2835_AUX_SPI_STAT 0x08 4862306a36Sopenharmony_ci#define BCM2835_AUX_SPI_PEEK 0x0C 4962306a36Sopenharmony_ci#define BCM2835_AUX_SPI_IO 0x20 5062306a36Sopenharmony_ci#define BCM2835_AUX_SPI_TXHOLD 0x30 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* Bitfields in CNTL0 */ 5362306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL0_SPEED 0xFFF00000 5462306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL0_SPEED_MAX 0xFFF 5562306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT 20 5662306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL0_CS 0x000E0000 5762306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL0_POSTINPUT 0x00010000 5862306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL0_VAR_CS 0x00008000 5962306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL0_VAR_WIDTH 0x00004000 6062306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL0_DOUTHOLD 0x00003000 6162306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL0_ENABLE 0x00000800 6262306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL0_IN_RISING 0x00000400 6362306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL0_CLEARFIFO 0x00000200 6462306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL0_OUT_RISING 0x00000100 6562306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL0_CPOL 0x00000080 6662306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL0_MSBF_OUT 0x00000040 6762306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL0_SHIFTLEN 0x0000003F 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* Bitfields in CNTL1 */ 7062306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL1_CSHIGH 0x00000700 7162306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL1_TXEMPTY 0x00000080 7262306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL1_IDLE 0x00000040 7362306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL1_MSBF_IN 0x00000002 7462306a36Sopenharmony_ci#define BCM2835_AUX_SPI_CNTL1_KEEP_IN 0x00000001 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* Bitfields in STAT */ 7762306a36Sopenharmony_ci#define BCM2835_AUX_SPI_STAT_TX_LVL 0xFF000000 7862306a36Sopenharmony_ci#define BCM2835_AUX_SPI_STAT_RX_LVL 0x00FF0000 7962306a36Sopenharmony_ci#define BCM2835_AUX_SPI_STAT_TX_FULL 0x00000400 8062306a36Sopenharmony_ci#define BCM2835_AUX_SPI_STAT_TX_EMPTY 0x00000200 8162306a36Sopenharmony_ci#define BCM2835_AUX_SPI_STAT_RX_FULL 0x00000100 8262306a36Sopenharmony_ci#define BCM2835_AUX_SPI_STAT_RX_EMPTY 0x00000080 8362306a36Sopenharmony_ci#define BCM2835_AUX_SPI_STAT_BUSY 0x00000040 8462306a36Sopenharmony_ci#define BCM2835_AUX_SPI_STAT_BITCOUNT 0x0000003F 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistruct bcm2835aux_spi { 8762306a36Sopenharmony_ci void __iomem *regs; 8862306a36Sopenharmony_ci struct clk *clk; 8962306a36Sopenharmony_ci int irq; 9062306a36Sopenharmony_ci u32 cntl[2]; 9162306a36Sopenharmony_ci const u8 *tx_buf; 9262306a36Sopenharmony_ci u8 *rx_buf; 9362306a36Sopenharmony_ci int tx_len; 9462306a36Sopenharmony_ci int rx_len; 9562306a36Sopenharmony_ci int pending; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci u64 count_transfer_polling; 9862306a36Sopenharmony_ci u64 count_transfer_irq; 9962306a36Sopenharmony_ci u64 count_transfer_irq_after_poll; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci struct dentry *debugfs_dir; 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#if defined(CONFIG_DEBUG_FS) 10562306a36Sopenharmony_cistatic void bcm2835aux_debugfs_create(struct bcm2835aux_spi *bs, 10662306a36Sopenharmony_ci const char *dname) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci char name[64]; 10962306a36Sopenharmony_ci struct dentry *dir; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* get full name */ 11262306a36Sopenharmony_ci snprintf(name, sizeof(name), "spi-bcm2835aux-%s", dname); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* the base directory */ 11562306a36Sopenharmony_ci dir = debugfs_create_dir(name, NULL); 11662306a36Sopenharmony_ci bs->debugfs_dir = dir; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* the counters */ 11962306a36Sopenharmony_ci debugfs_create_u64("count_transfer_polling", 0444, dir, 12062306a36Sopenharmony_ci &bs->count_transfer_polling); 12162306a36Sopenharmony_ci debugfs_create_u64("count_transfer_irq", 0444, dir, 12262306a36Sopenharmony_ci &bs->count_transfer_irq); 12362306a36Sopenharmony_ci debugfs_create_u64("count_transfer_irq_after_poll", 0444, dir, 12462306a36Sopenharmony_ci &bs->count_transfer_irq_after_poll); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic void bcm2835aux_debugfs_remove(struct bcm2835aux_spi *bs) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci debugfs_remove_recursive(bs->debugfs_dir); 13062306a36Sopenharmony_ci bs->debugfs_dir = NULL; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci#else 13362306a36Sopenharmony_cistatic void bcm2835aux_debugfs_create(struct bcm2835aux_spi *bs, 13462306a36Sopenharmony_ci const char *dname) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic void bcm2835aux_debugfs_remove(struct bcm2835aux_spi *bs) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci#endif /* CONFIG_DEBUG_FS */ 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic inline u32 bcm2835aux_rd(struct bcm2835aux_spi *bs, unsigned int reg) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci return readl(bs->regs + reg); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic inline void bcm2835aux_wr(struct bcm2835aux_spi *bs, unsigned int reg, 14962306a36Sopenharmony_ci u32 val) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci writel(val, bs->regs + reg); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic inline void bcm2835aux_rd_fifo(struct bcm2835aux_spi *bs) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci u32 data; 15762306a36Sopenharmony_ci int count = min(bs->rx_len, 3); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci data = bcm2835aux_rd(bs, BCM2835_AUX_SPI_IO); 16062306a36Sopenharmony_ci if (bs->rx_buf) { 16162306a36Sopenharmony_ci switch (count) { 16262306a36Sopenharmony_ci case 3: 16362306a36Sopenharmony_ci *bs->rx_buf++ = (data >> 16) & 0xff; 16462306a36Sopenharmony_ci fallthrough; 16562306a36Sopenharmony_ci case 2: 16662306a36Sopenharmony_ci *bs->rx_buf++ = (data >> 8) & 0xff; 16762306a36Sopenharmony_ci fallthrough; 16862306a36Sopenharmony_ci case 1: 16962306a36Sopenharmony_ci *bs->rx_buf++ = (data >> 0) & 0xff; 17062306a36Sopenharmony_ci /* fallthrough - no default */ 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci bs->rx_len -= count; 17462306a36Sopenharmony_ci bs->pending -= count; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic inline void bcm2835aux_wr_fifo(struct bcm2835aux_spi *bs) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci u32 data; 18062306a36Sopenharmony_ci u8 byte; 18162306a36Sopenharmony_ci int count; 18262306a36Sopenharmony_ci int i; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* gather up to 3 bytes to write to the FIFO */ 18562306a36Sopenharmony_ci count = min(bs->tx_len, 3); 18662306a36Sopenharmony_ci data = 0; 18762306a36Sopenharmony_ci for (i = 0; i < count; i++) { 18862306a36Sopenharmony_ci byte = bs->tx_buf ? *bs->tx_buf++ : 0; 18962306a36Sopenharmony_ci data |= byte << (8 * (2 - i)); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* and set the variable bit-length */ 19362306a36Sopenharmony_ci data |= (count * 8) << 24; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* and decrement length */ 19662306a36Sopenharmony_ci bs->tx_len -= count; 19762306a36Sopenharmony_ci bs->pending += count; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* write to the correct TX-register */ 20062306a36Sopenharmony_ci if (bs->tx_len) 20162306a36Sopenharmony_ci bcm2835aux_wr(bs, BCM2835_AUX_SPI_TXHOLD, data); 20262306a36Sopenharmony_ci else 20362306a36Sopenharmony_ci bcm2835aux_wr(bs, BCM2835_AUX_SPI_IO, data); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void bcm2835aux_spi_reset_hw(struct bcm2835aux_spi *bs) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci /* disable spi clearing fifo and interrupts */ 20962306a36Sopenharmony_ci bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, 0); 21062306a36Sopenharmony_ci bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, 21162306a36Sopenharmony_ci BCM2835_AUX_SPI_CNTL0_CLEARFIFO); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic void bcm2835aux_spi_transfer_helper(struct bcm2835aux_spi *bs) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci u32 stat = bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* check if we have data to read */ 21962306a36Sopenharmony_ci for (; bs->rx_len && (stat & BCM2835_AUX_SPI_STAT_RX_LVL); 22062306a36Sopenharmony_ci stat = bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT)) 22162306a36Sopenharmony_ci bcm2835aux_rd_fifo(bs); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* check if we have data to write */ 22462306a36Sopenharmony_ci while (bs->tx_len && 22562306a36Sopenharmony_ci (bs->pending < 12) && 22662306a36Sopenharmony_ci (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) & 22762306a36Sopenharmony_ci BCM2835_AUX_SPI_STAT_TX_FULL))) { 22862306a36Sopenharmony_ci bcm2835aux_wr_fifo(bs); 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic irqreturn_t bcm2835aux_spi_interrupt(int irq, void *dev_id) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct spi_controller *host = dev_id; 23562306a36Sopenharmony_ci struct bcm2835aux_spi *bs = spi_controller_get_devdata(host); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* IRQ may be shared, so return if our interrupts are disabled */ 23862306a36Sopenharmony_ci if (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_CNTL1) & 23962306a36Sopenharmony_ci (BCM2835_AUX_SPI_CNTL1_TXEMPTY | BCM2835_AUX_SPI_CNTL1_IDLE))) 24062306a36Sopenharmony_ci return IRQ_NONE; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* do common fifo handling */ 24362306a36Sopenharmony_ci bcm2835aux_spi_transfer_helper(bs); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (!bs->tx_len) { 24662306a36Sopenharmony_ci /* disable tx fifo empty interrupt */ 24762306a36Sopenharmony_ci bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1] | 24862306a36Sopenharmony_ci BCM2835_AUX_SPI_CNTL1_IDLE); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* and if rx_len is 0 then disable interrupts and wake up completion */ 25262306a36Sopenharmony_ci if (!bs->rx_len) { 25362306a36Sopenharmony_ci bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1]); 25462306a36Sopenharmony_ci spi_finalize_current_transfer(host); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return IRQ_HANDLED; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic int __bcm2835aux_spi_transfer_one_irq(struct spi_controller *host, 26162306a36Sopenharmony_ci struct spi_device *spi, 26262306a36Sopenharmony_ci struct spi_transfer *tfr) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct bcm2835aux_spi *bs = spi_controller_get_devdata(host); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* enable interrupts */ 26762306a36Sopenharmony_ci bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1] | 26862306a36Sopenharmony_ci BCM2835_AUX_SPI_CNTL1_TXEMPTY | 26962306a36Sopenharmony_ci BCM2835_AUX_SPI_CNTL1_IDLE); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* and wait for finish... */ 27262306a36Sopenharmony_ci return 1; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic int bcm2835aux_spi_transfer_one_irq(struct spi_controller *host, 27662306a36Sopenharmony_ci struct spi_device *spi, 27762306a36Sopenharmony_ci struct spi_transfer *tfr) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct bcm2835aux_spi *bs = spi_controller_get_devdata(host); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* update statistics */ 28262306a36Sopenharmony_ci bs->count_transfer_irq++; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* fill in registers and fifos before enabling interrupts */ 28562306a36Sopenharmony_ci bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1]); 28662306a36Sopenharmony_ci bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, bs->cntl[0]); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* fill in tx fifo with data before enabling interrupts */ 28962306a36Sopenharmony_ci while ((bs->tx_len) && 29062306a36Sopenharmony_ci (bs->pending < 12) && 29162306a36Sopenharmony_ci (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) & 29262306a36Sopenharmony_ci BCM2835_AUX_SPI_STAT_TX_FULL))) { 29362306a36Sopenharmony_ci bcm2835aux_wr_fifo(bs); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* now run the interrupt mode */ 29762306a36Sopenharmony_ci return __bcm2835aux_spi_transfer_one_irq(host, spi, tfr); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic int bcm2835aux_spi_transfer_one_poll(struct spi_controller *host, 30162306a36Sopenharmony_ci struct spi_device *spi, 30262306a36Sopenharmony_ci struct spi_transfer *tfr) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct bcm2835aux_spi *bs = spi_controller_get_devdata(host); 30562306a36Sopenharmony_ci unsigned long timeout; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* update statistics */ 30862306a36Sopenharmony_ci bs->count_transfer_polling++; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* configure spi */ 31162306a36Sopenharmony_ci bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1]); 31262306a36Sopenharmony_ci bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, bs->cntl[0]); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* set the timeout to at least 2 jiffies */ 31562306a36Sopenharmony_ci timeout = jiffies + 2 + HZ * polling_limit_us / 1000000; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* loop until finished the transfer */ 31862306a36Sopenharmony_ci while (bs->rx_len) { 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* do common fifo handling */ 32162306a36Sopenharmony_ci bcm2835aux_spi_transfer_helper(bs); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* there is still data pending to read check the timeout */ 32462306a36Sopenharmony_ci if (bs->rx_len && time_after(jiffies, timeout)) { 32562306a36Sopenharmony_ci dev_dbg_ratelimited(&spi->dev, 32662306a36Sopenharmony_ci "timeout period reached: jiffies: %lu remaining tx/rx: %d/%d - falling back to interrupt mode\n", 32762306a36Sopenharmony_ci jiffies - timeout, 32862306a36Sopenharmony_ci bs->tx_len, bs->rx_len); 32962306a36Sopenharmony_ci /* forward to interrupt handler */ 33062306a36Sopenharmony_ci bs->count_transfer_irq_after_poll++; 33162306a36Sopenharmony_ci return __bcm2835aux_spi_transfer_one_irq(host, 33262306a36Sopenharmony_ci spi, tfr); 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* and return without waiting for completion */ 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic int bcm2835aux_spi_transfer_one(struct spi_controller *host, 34162306a36Sopenharmony_ci struct spi_device *spi, 34262306a36Sopenharmony_ci struct spi_transfer *tfr) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci struct bcm2835aux_spi *bs = spi_controller_get_devdata(host); 34562306a36Sopenharmony_ci unsigned long spi_hz, clk_hz, speed; 34662306a36Sopenharmony_ci unsigned long hz_per_byte, byte_limit; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* calculate the registers to handle 34962306a36Sopenharmony_ci * 35062306a36Sopenharmony_ci * note that we use the variable data mode, which 35162306a36Sopenharmony_ci * is not optimal for longer transfers as we waste registers 35262306a36Sopenharmony_ci * resulting (potentially) in more interrupts when transferring 35362306a36Sopenharmony_ci * more than 12 bytes 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* set clock */ 35762306a36Sopenharmony_ci spi_hz = tfr->speed_hz; 35862306a36Sopenharmony_ci clk_hz = clk_get_rate(bs->clk); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (spi_hz >= clk_hz / 2) { 36162306a36Sopenharmony_ci speed = 0; 36262306a36Sopenharmony_ci } else if (spi_hz) { 36362306a36Sopenharmony_ci speed = DIV_ROUND_UP(clk_hz, 2 * spi_hz) - 1; 36462306a36Sopenharmony_ci if (speed > BCM2835_AUX_SPI_CNTL0_SPEED_MAX) 36562306a36Sopenharmony_ci speed = BCM2835_AUX_SPI_CNTL0_SPEED_MAX; 36662306a36Sopenharmony_ci } else { /* the slowest we can go */ 36762306a36Sopenharmony_ci speed = BCM2835_AUX_SPI_CNTL0_SPEED_MAX; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci /* mask out old speed from previous spi_transfer */ 37062306a36Sopenharmony_ci bs->cntl[0] &= ~(BCM2835_AUX_SPI_CNTL0_SPEED); 37162306a36Sopenharmony_ci /* set the new speed */ 37262306a36Sopenharmony_ci bs->cntl[0] |= speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci tfr->effective_speed_hz = clk_hz / (2 * (speed + 1)); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* set transmit buffers and length */ 37762306a36Sopenharmony_ci bs->tx_buf = tfr->tx_buf; 37862306a36Sopenharmony_ci bs->rx_buf = tfr->rx_buf; 37962306a36Sopenharmony_ci bs->tx_len = tfr->len; 38062306a36Sopenharmony_ci bs->rx_len = tfr->len; 38162306a36Sopenharmony_ci bs->pending = 0; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* Calculate the estimated time in us the transfer runs. Note that 38462306a36Sopenharmony_ci * there are 2 idle clocks cycles after each chunk getting 38562306a36Sopenharmony_ci * transferred - in our case the chunk size is 3 bytes, so we 38662306a36Sopenharmony_ci * approximate this by 9 cycles/byte. This is used to find the number 38762306a36Sopenharmony_ci * of Hz per byte per polling limit. E.g., we can transfer 1 byte in 38862306a36Sopenharmony_ci * 30 µs per 300,000 Hz of bus clock. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci hz_per_byte = polling_limit_us ? (9 * 1000000) / polling_limit_us : 0; 39162306a36Sopenharmony_ci byte_limit = hz_per_byte ? tfr->effective_speed_hz / hz_per_byte : 1; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* run in polling mode for short transfers */ 39462306a36Sopenharmony_ci if (tfr->len < byte_limit) 39562306a36Sopenharmony_ci return bcm2835aux_spi_transfer_one_poll(host, spi, tfr); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* run in interrupt mode for all others */ 39862306a36Sopenharmony_ci return bcm2835aux_spi_transfer_one_irq(host, spi, tfr); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int bcm2835aux_spi_prepare_message(struct spi_controller *host, 40262306a36Sopenharmony_ci struct spi_message *msg) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct spi_device *spi = msg->spi; 40562306a36Sopenharmony_ci struct bcm2835aux_spi *bs = spi_controller_get_devdata(host); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci bs->cntl[0] = BCM2835_AUX_SPI_CNTL0_ENABLE | 40862306a36Sopenharmony_ci BCM2835_AUX_SPI_CNTL0_VAR_WIDTH | 40962306a36Sopenharmony_ci BCM2835_AUX_SPI_CNTL0_MSBF_OUT; 41062306a36Sopenharmony_ci bs->cntl[1] = BCM2835_AUX_SPI_CNTL1_MSBF_IN; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* handle all the modes */ 41362306a36Sopenharmony_ci if (spi->mode & SPI_CPOL) { 41462306a36Sopenharmony_ci bs->cntl[0] |= BCM2835_AUX_SPI_CNTL0_CPOL; 41562306a36Sopenharmony_ci bs->cntl[0] |= BCM2835_AUX_SPI_CNTL0_OUT_RISING; 41662306a36Sopenharmony_ci } else { 41762306a36Sopenharmony_ci bs->cntl[0] |= BCM2835_AUX_SPI_CNTL0_IN_RISING; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1]); 42062306a36Sopenharmony_ci bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, bs->cntl[0]); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return 0; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic int bcm2835aux_spi_unprepare_message(struct spi_controller *host, 42662306a36Sopenharmony_ci struct spi_message *msg) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct bcm2835aux_spi *bs = spi_controller_get_devdata(host); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci bcm2835aux_spi_reset_hw(bs); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic void bcm2835aux_spi_handle_err(struct spi_controller *host, 43662306a36Sopenharmony_ci struct spi_message *msg) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct bcm2835aux_spi *bs = spi_controller_get_devdata(host); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci bcm2835aux_spi_reset_hw(bs); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int bcm2835aux_spi_setup(struct spi_device *spi) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci /* sanity check for native cs */ 44662306a36Sopenharmony_ci if (spi->mode & SPI_NO_CS) 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (spi_get_csgpiod(spi, 0)) 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* for dt-backwards compatibility: only support native on CS0 45362306a36Sopenharmony_ci * known things not supported with broken native CS: 45462306a36Sopenharmony_ci * * multiple chip-selects: cs0-cs2 are all 45562306a36Sopenharmony_ci * simultaniously asserted whenever there is a transfer 45662306a36Sopenharmony_ci * this even includes SPI_NO_CS 45762306a36Sopenharmony_ci * * SPI_CS_HIGH: cs are always asserted low 45862306a36Sopenharmony_ci * * cs_change: cs is deasserted after each spi_transfer 45962306a36Sopenharmony_ci * * cs_delay_usec: cs is always deasserted one SCK cycle 46062306a36Sopenharmony_ci * after the last transfer 46162306a36Sopenharmony_ci * probably more... 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_ci dev_warn(&spi->dev, 46462306a36Sopenharmony_ci "Native CS is not supported - please configure cs-gpio in device-tree\n"); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (spi_get_chipselect(spi, 0) == 0) 46762306a36Sopenharmony_ci return 0; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci dev_warn(&spi->dev, "Native CS is not working for cs > 0\n"); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return -EINVAL; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic int bcm2835aux_spi_probe(struct platform_device *pdev) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct spi_controller *host; 47762306a36Sopenharmony_ci struct bcm2835aux_spi *bs; 47862306a36Sopenharmony_ci unsigned long clk_hz; 47962306a36Sopenharmony_ci int err; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci host = devm_spi_alloc_host(&pdev->dev, sizeof(*bs)); 48262306a36Sopenharmony_ci if (!host) 48362306a36Sopenharmony_ci return -ENOMEM; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci platform_set_drvdata(pdev, host); 48662306a36Sopenharmony_ci host->mode_bits = (SPI_CPOL | SPI_CS_HIGH | SPI_NO_CS); 48762306a36Sopenharmony_ci host->bits_per_word_mask = SPI_BPW_MASK(8); 48862306a36Sopenharmony_ci /* even though the driver never officially supported native CS 48962306a36Sopenharmony_ci * allow a single native CS for legacy DT support purposes when 49062306a36Sopenharmony_ci * no cs-gpio is configured. 49162306a36Sopenharmony_ci * Known limitations for native cs are: 49262306a36Sopenharmony_ci * * multiple chip-selects: cs0-cs2 are all simultaniously asserted 49362306a36Sopenharmony_ci * whenever there is a transfer - this even includes SPI_NO_CS 49462306a36Sopenharmony_ci * * SPI_CS_HIGH: is ignores - cs are always asserted low 49562306a36Sopenharmony_ci * * cs_change: cs is deasserted after each spi_transfer 49662306a36Sopenharmony_ci * * cs_delay_usec: cs is always deasserted one SCK cycle after 49762306a36Sopenharmony_ci * a spi_transfer 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_ci host->num_chipselect = 1; 50062306a36Sopenharmony_ci host->setup = bcm2835aux_spi_setup; 50162306a36Sopenharmony_ci host->transfer_one = bcm2835aux_spi_transfer_one; 50262306a36Sopenharmony_ci host->handle_err = bcm2835aux_spi_handle_err; 50362306a36Sopenharmony_ci host->prepare_message = bcm2835aux_spi_prepare_message; 50462306a36Sopenharmony_ci host->unprepare_message = bcm2835aux_spi_unprepare_message; 50562306a36Sopenharmony_ci host->dev.of_node = pdev->dev.of_node; 50662306a36Sopenharmony_ci host->use_gpio_descriptors = true; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci bs = spi_controller_get_devdata(host); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* the main area */ 51162306a36Sopenharmony_ci bs->regs = devm_platform_ioremap_resource(pdev, 0); 51262306a36Sopenharmony_ci if (IS_ERR(bs->regs)) 51362306a36Sopenharmony_ci return PTR_ERR(bs->regs); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci bs->clk = devm_clk_get(&pdev->dev, NULL); 51662306a36Sopenharmony_ci if (IS_ERR(bs->clk)) { 51762306a36Sopenharmony_ci err = PTR_ERR(bs->clk); 51862306a36Sopenharmony_ci dev_err(&pdev->dev, "could not get clk: %d\n", err); 51962306a36Sopenharmony_ci return err; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci bs->irq = platform_get_irq(pdev, 0); 52362306a36Sopenharmony_ci if (bs->irq < 0) 52462306a36Sopenharmony_ci return bs->irq; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* this also enables the HW block */ 52762306a36Sopenharmony_ci err = clk_prepare_enable(bs->clk); 52862306a36Sopenharmony_ci if (err) { 52962306a36Sopenharmony_ci dev_err(&pdev->dev, "could not prepare clock: %d\n", err); 53062306a36Sopenharmony_ci return err; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* just checking if the clock returns a sane value */ 53462306a36Sopenharmony_ci clk_hz = clk_get_rate(bs->clk); 53562306a36Sopenharmony_ci if (!clk_hz) { 53662306a36Sopenharmony_ci dev_err(&pdev->dev, "clock returns 0 Hz\n"); 53762306a36Sopenharmony_ci err = -ENODEV; 53862306a36Sopenharmony_ci goto out_clk_disable; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* reset SPI-HW block */ 54262306a36Sopenharmony_ci bcm2835aux_spi_reset_hw(bs); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci err = devm_request_irq(&pdev->dev, bs->irq, 54562306a36Sopenharmony_ci bcm2835aux_spi_interrupt, 54662306a36Sopenharmony_ci IRQF_SHARED, 54762306a36Sopenharmony_ci dev_name(&pdev->dev), host); 54862306a36Sopenharmony_ci if (err) { 54962306a36Sopenharmony_ci dev_err(&pdev->dev, "could not request IRQ: %d\n", err); 55062306a36Sopenharmony_ci goto out_clk_disable; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci err = spi_register_controller(host); 55462306a36Sopenharmony_ci if (err) { 55562306a36Sopenharmony_ci dev_err(&pdev->dev, "could not register SPI host: %d\n", err); 55662306a36Sopenharmony_ci goto out_clk_disable; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci bcm2835aux_debugfs_create(bs, dev_name(&pdev->dev)); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci return 0; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ciout_clk_disable: 56462306a36Sopenharmony_ci clk_disable_unprepare(bs->clk); 56562306a36Sopenharmony_ci return err; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic void bcm2835aux_spi_remove(struct platform_device *pdev) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct spi_controller *host = platform_get_drvdata(pdev); 57162306a36Sopenharmony_ci struct bcm2835aux_spi *bs = spi_controller_get_devdata(host); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci bcm2835aux_debugfs_remove(bs); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci spi_unregister_controller(host); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci bcm2835aux_spi_reset_hw(bs); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* disable the HW block by releasing the clock */ 58062306a36Sopenharmony_ci clk_disable_unprepare(bs->clk); 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic const struct of_device_id bcm2835aux_spi_match[] = { 58462306a36Sopenharmony_ci { .compatible = "brcm,bcm2835-aux-spi", }, 58562306a36Sopenharmony_ci {} 58662306a36Sopenharmony_ci}; 58762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, bcm2835aux_spi_match); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic struct platform_driver bcm2835aux_spi_driver = { 59062306a36Sopenharmony_ci .driver = { 59162306a36Sopenharmony_ci .name = "spi-bcm2835aux", 59262306a36Sopenharmony_ci .of_match_table = bcm2835aux_spi_match, 59362306a36Sopenharmony_ci }, 59462306a36Sopenharmony_ci .probe = bcm2835aux_spi_probe, 59562306a36Sopenharmony_ci .remove_new = bcm2835aux_spi_remove, 59662306a36Sopenharmony_ci}; 59762306a36Sopenharmony_cimodule_platform_driver(bcm2835aux_spi_driver); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ciMODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2835 aux"); 60062306a36Sopenharmony_ciMODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>"); 60162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 602