/** * I/O handling lifted from drivers/spi/spi-omap2-mcspi.c: * Copyright (C) 2019 Laurentiu-Cristian Duca * * RTDM integration by: * Copyright (C) 2016 Philippe Gerum * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "spi-master.h" #define RTDM_SUBCLASS_OMAP2_MCSPI 3 #define OMAP4_MCSPI_REG_OFFSET 0x100 #define OMAP2_MCSPI_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH) #define OMAP2_MCSPI_MAX_FREQ 48000000 #define OMAP2_MCSPI_DRIVER_MAX_FREQ 40000000 #define OMAP2_MCSPI_MAX_DIVIDER 4096 #define OMAP2_MCSPI_MAX_FIFODEPTH 64 #define OMAP2_MCSPI_MAX_FIFOWCNT 0xFFFF #define SPI_AUTOSUSPEND_TIMEOUT 2000 #define PM_NEGATIVE_DELAY -2000 #define OMAP2_MCSPI_REVISION 0x00 #define OMAP2_MCSPI_SYSCONFIG 0x10 #define OMAP2_MCSPI_SYSSTATUS 0x14 #define OMAP2_MCSPI_IRQSTATUS 0x18 #define OMAP2_MCSPI_IRQENABLE 0x1c #define OMAP2_MCSPI_WAKEUPENABLE 0x20 #define OMAP2_MCSPI_SYST 0x24 #define OMAP2_MCSPI_MODULCTRL 0x28 #define OMAP2_MCSPI_XFERLEVEL 0x7c /* per-channel (chip select) banks, 0x14 bytes each, first is: */ #define OMAP2_MCSPI_CHANNELBANK_SIZE 0x14 #define OMAP2_MCSPI_CHCONF0 0x2c #define OMAP2_MCSPI_CHSTAT0 0x30 #define OMAP2_MCSPI_CHCTRL0 0x34 #define OMAP2_MCSPI_TX0 0x38 #define OMAP2_MCSPI_RX0 0x3c /* per-register bitmasks: */ #define OMAP2_MCSPI_IRQSTATUS_EOW BIT(17) #define OMAP2_MCSPI_IRQSTATUS_RX1_FULL BIT(6) #define OMAP2_MCSPI_IRQSTATUS_TX1_EMPTY BIT(4) #define OMAP2_MCSPI_IRQSTATUS_RX0_FULL BIT(2) #define OMAP2_MCSPI_IRQSTATUS_TX0_EMPTY BIT(0) #define OMAP2_MCSPI_IRQENABLE_EOW BIT(17) #define OMAP2_MCSPI_IRQENABLE_RX1_FULL BIT(6) #define OMAP2_MCSPI_IRQENABLE_TX1_EMPTY BIT(4) #define OMAP2_MCSPI_IRQENABLE_RX0_FULL BIT(2) #define OMAP2_MCSPI_IRQENABLE_TX0_EMPTY BIT(0) #define OMAP2_MCSPI_MODULCTRL_SINGLE BIT(0) #define OMAP2_MCSPI_MODULCTRL_MS BIT(2) #define OMAP2_MCSPI_MODULCTRL_STEST BIT(3) #define OMAP2_MCSPI_CHCONF_PHA BIT(0) #define OMAP2_MCSPI_CHCONF_POL BIT(1) #define OMAP2_MCSPI_CHCONF_CLKD_MASK (0x0f << 2) #define OMAP2_MCSPI_CHCONF_EPOL BIT(6) #define OMAP2_MCSPI_CHCONF_WL_MASK (0x1f << 7) #define OMAP2_MCSPI_CHCONF_TRM_RX_ONLY BIT(12) #define OMAP2_MCSPI_CHCONF_TRM_TX_ONLY BIT(13) #define OMAP2_MCSPI_CHCONF_TRM_MASK (0x03 << 12) #define OMAP2_MCSPI_CHCONF_DMAW BIT(14) #define OMAP2_MCSPI_CHCONF_DMAR BIT(15) #define OMAP2_MCSPI_CHCONF_DPE0 BIT(16) #define OMAP2_MCSPI_CHCONF_DPE1 BIT(17) #define OMAP2_MCSPI_CHCONF_IS BIT(18) #define OMAP2_MCSPI_CHCONF_TURBO BIT(19) #define OMAP2_MCSPI_CHCONF_FORCE BIT(20) #define OMAP2_MCSPI_CHCONF_FFET BIT(27) #define OMAP2_MCSPI_CHCONF_FFER BIT(28) #define OMAP2_MCSPI_CHCONF_CLKG BIT(29) #define OMAP2_MCSPI_CHSTAT_RXS BIT(0) #define OMAP2_MCSPI_CHSTAT_TXS BIT(1) #define OMAP2_MCSPI_CHSTAT_EOT BIT(2) #define OMAP2_MCSPI_CHSTAT_TXFFE BIT(3) #define OMAP2_MCSPI_CHCTRL_EN BIT(0) #define OMAP2_MCSPI_CHCTRL_EXTCLK_MASK (0xff << 8) #define OMAP2_MCSPI_WAKEUPENABLE_WKEN BIT(0) #define OMAP2_MCSPI_SYSCONFIG_CLOCKACTIVITY_MASK (0x3 << 8) #define OMAP2_MCSPI_SYSCONFIG_SIDLEMODE_MASK (0x3 << 3) #define OMAP2_MCSPI_SYSCONFIG_SOFTRESET BIT(1) #define OMAP2_MCSPI_SYSCONFIG_AUTOIDLE BIT(0) #define OMAP2_MCSPI_SYSSTATUS_RESETDONE BIT(0) /* current version supports max 2 CS per module */ #define OMAP2_MCSPI_CS_N 2 #define MCSPI_PINDIR_D0_IN_D1_OUT 0 #define MCSPI_PINDIR_D0_OUT_D1_IN 1 struct omap2_mcspi_platform_config { unsigned short num_cs; unsigned int regs_offset; unsigned int pin_dir:1; }; struct omap2_mcspi_cs { /* CS channel */ void __iomem *regs; unsigned long phys; u8 chosen; }; struct spi_master_omap2_mcspi { struct rtdm_spi_master master; void __iomem *regs; unsigned long phys; rtdm_irq_t irqh; const u8 *tx_buf; u8 *rx_buf; int tx_len; int rx_len; int fifo_depth; rtdm_event_t transfer_done; rtdm_lock_t lock; unsigned int pin_dir:1; struct omap2_mcspi_cs cs[OMAP2_MCSPI_CS_N]; /* logging */ int n_rx_full; int n_tx_empty; int n_interrupts; }; struct spi_slave_omap2_mcspi { struct rtdm_spi_remote_slave slave; void *io_virt; dma_addr_t io_dma; size_t io_len; }; static inline struct spi_slave_omap2_mcspi * to_slave_omap2_mcspi(struct rtdm_spi_remote_slave *slave) { return container_of(slave, struct spi_slave_omap2_mcspi, slave); } static inline struct spi_master_omap2_mcspi * to_master_omap2_mcspi(struct rtdm_spi_remote_slave *slave) { return container_of(slave->master, struct spi_master_omap2_mcspi, master); } static inline struct device * master_to_kdev(struct rtdm_spi_master *master) { return &master->controller->dev; } static inline u32 mcspi_rd_reg(struct spi_master_omap2_mcspi *spim, unsigned int reg) { return readl(spim->regs + reg); } static inline void mcspi_wr_reg(struct spi_master_omap2_mcspi *spim, unsigned int reg, u32 val) { writel(val, spim->regs + reg); } static inline u32 mcspi_rd_cs_reg(struct spi_master_omap2_mcspi *spim, int cs_id, unsigned int reg) { return readl(spim->cs[cs_id].regs + reg); } static inline void mcspi_wr_cs_reg(struct spi_master_omap2_mcspi *spim, int cs_id, unsigned int reg, u32 val) { writel(val, spim->cs[cs_id].regs + reg); } static void omap2_mcspi_init_hw(struct spi_master_omap2_mcspi *spim) { u32 l; l = mcspi_rd_reg(spim, OMAP2_MCSPI_SYSCONFIG); /* CLOCKACTIVITY = 3h: OCP and Functional clocks are maintained */ l |= OMAP2_MCSPI_SYSCONFIG_CLOCKACTIVITY_MASK; /* SIDLEMODE = 1h: ignore idle requests */ l &= ~OMAP2_MCSPI_SYSCONFIG_SIDLEMODE_MASK; l |= 0x1 << 3; /* AUTOIDLE=0: OCP clock is free-running */ l &= ~OMAP2_MCSPI_SYSCONFIG_AUTOIDLE; mcspi_wr_reg(spim, OMAP2_MCSPI_SYSCONFIG, l); /* Initialise the hardware with the default polarities (only omap2) */ mcspi_wr_reg(spim, OMAP2_MCSPI_WAKEUPENABLE, OMAP2_MCSPI_WAKEUPENABLE_WKEN); /* Setup single-channel master mode */ l = mcspi_rd_reg(spim, OMAP2_MCSPI_MODULCTRL); /* MS=0 => spi master */ l &= ~(OMAP2_MCSPI_MODULCTRL_STEST | OMAP2_MCSPI_MODULCTRL_MS); l |= OMAP2_MCSPI_MODULCTRL_SINGLE; mcspi_wr_reg(spim, OMAP2_MCSPI_MODULCTRL, l); } static void omap2_mcspi_reset_hw(struct spi_master_omap2_mcspi *spim) { u32 l; l = mcspi_rd_reg(spim, OMAP2_MCSPI_SYSCONFIG); l |= OMAP2_MCSPI_SYSCONFIG_SOFTRESET; mcspi_wr_reg(spim, OMAP2_MCSPI_SYSCONFIG, l); /* wait until reset is done */ do { l = mcspi_rd_reg(spim, OMAP2_MCSPI_SYSSTATUS); cpu_relax(); } while (!(l & OMAP2_MCSPI_SYSSTATUS_RESETDONE)); } static void omap2_mcspi_chip_select(struct rtdm_spi_remote_slave *slave, bool active) { struct spi_master_omap2_mcspi *spim = to_master_omap2_mcspi(slave); u32 l; /* FORCE: manual SPIEN assertion to keep SPIEN active */ l = mcspi_rd_cs_reg(spim, slave->chip_select, OMAP2_MCSPI_CHCONF0); /* "active" is the logical state, not the impedance level. */ if (active) l |= OMAP2_MCSPI_CHCONF_FORCE; else l &= ~OMAP2_MCSPI_CHCONF_FORCE; mcspi_wr_cs_reg(spim, slave->chip_select, OMAP2_MCSPI_CHCONF0, l); /* Flash post-writes */ l = mcspi_rd_cs_reg(spim, slave->chip_select, OMAP2_MCSPI_CHCONF0); } static u32 omap2_mcspi_calc_divisor(u32 speed_hz) { u32 div; for (div = 0; div < 15; div++) if (speed_hz >= (OMAP2_MCSPI_MAX_FREQ >> div)) return div; return 15; } /* channel 0 enable/disable */ static void omap2_mcspi_channel_enable(struct rtdm_spi_remote_slave *slave, int enable) { struct spi_master_omap2_mcspi *spim = to_master_omap2_mcspi(slave); u32 l; l = mcspi_rd_cs_reg(spim, slave->chip_select, OMAP2_MCSPI_CHCTRL0); if (enable) l |= OMAP2_MCSPI_CHCTRL_EN; else l &= ~OMAP2_MCSPI_CHCTRL_EN; mcspi_wr_cs_reg(spim, slave->chip_select, OMAP2_MCSPI_CHCTRL0, l); /* Flash post-writes */ l = mcspi_rd_cs_reg(spim, slave->chip_select, OMAP2_MCSPI_CHCTRL0); } /* called only when no transfer is active to this device */ static int omap2_mcspi_configure(struct rtdm_spi_remote_slave *slave) { struct spi_master_omap2_mcspi *spim = to_master_omap2_mcspi(slave); struct rtdm_spi_config *config = &slave->config; u32 l = 0, clkd = 0, div = 1, extclk = 0, clkg = 0, word_len; u32 speed_hz = OMAP2_MCSPI_MAX_FREQ; u32 chctrl0; /* The configuration parameters can be loaded in MCSPI_CH(i)CONF * only when the channel is disabled */ omap2_mcspi_channel_enable(slave, 0); l = mcspi_rd_cs_reg(spim, slave->chip_select, OMAP2_MCSPI_CHCONF0); /* Set clock frequency. */ speed_hz = (u32) config->speed_hz; if (speed_hz > OMAP2_MCSPI_DRIVER_MAX_FREQ) { dev_warn(slave_to_kdev(slave), "maximum clock frequency is %d", OMAP2_MCSPI_DRIVER_MAX_FREQ); } speed_hz = min_t(u32, speed_hz, OMAP2_MCSPI_DRIVER_MAX_FREQ); if (speed_hz < (OMAP2_MCSPI_MAX_FREQ / OMAP2_MCSPI_MAX_DIVIDER)) { clkd = omap2_mcspi_calc_divisor(speed_hz); speed_hz = OMAP2_MCSPI_MAX_FREQ >> clkd; clkg = 0; } else { div = (OMAP2_MCSPI_MAX_FREQ + speed_hz - 1) / speed_hz; speed_hz = OMAP2_MCSPI_MAX_FREQ / div; clkd = (div - 1) & 0xf; extclk = (div - 1) >> 4; clkg = OMAP2_MCSPI_CHCONF_CLKG; } /* set clock divisor */ l &= ~OMAP2_MCSPI_CHCONF_CLKD_MASK; l |= clkd << 2; /* set clock granularity */ l &= ~OMAP2_MCSPI_CHCONF_CLKG; l |= clkg; if (clkg) { chctrl0 = mcspi_rd_cs_reg(spim, slave->chip_select, OMAP2_MCSPI_CHCTRL0); chctrl0 &= ~OMAP2_MCSPI_CHCTRL_EXTCLK_MASK; chctrl0 |= extclk << 8; mcspi_wr_cs_reg(spim, slave->chip_select, OMAP2_MCSPI_CHCTRL0, chctrl0); } if (spim->pin_dir == MCSPI_PINDIR_D0_IN_D1_OUT) { l &= ~OMAP2_MCSPI_CHCONF_IS; l &= ~OMAP2_MCSPI_CHCONF_DPE1; l |= OMAP2_MCSPI_CHCONF_DPE0; } else { l |= OMAP2_MCSPI_CHCONF_IS; l |= OMAP2_MCSPI_CHCONF_DPE1; l &= ~OMAP2_MCSPI_CHCONF_DPE0; } /* wordlength */ word_len = config->bits_per_word; /* TODO: allow word_len != 8 */ if (word_len != 8) { dev_err(slave_to_kdev(slave), "word_len(%d) != 8.\n", word_len); return -EIO; } l &= ~OMAP2_MCSPI_CHCONF_WL_MASK; l |= (word_len - 1) << 7; /* set chipselect polarity; manage with FORCE */ if (!(config->mode & SPI_CS_HIGH)) /* CS active-low */ l |= OMAP2_MCSPI_CHCONF_EPOL; else l &= ~OMAP2_MCSPI_CHCONF_EPOL; /* set SPI mode 0..3 */ if (config->mode & SPI_CPOL) l |= OMAP2_MCSPI_CHCONF_POL; else l &= ~OMAP2_MCSPI_CHCONF_POL; if (config->mode & SPI_CPHA) l |= OMAP2_MCSPI_CHCONF_PHA; else l &= ~OMAP2_MCSPI_CHCONF_PHA; mcspi_wr_cs_reg(spim, slave->chip_select, OMAP2_MCSPI_CHCONF0, l); l = mcspi_rd_cs_reg(spim, slave->chip_select, OMAP2_MCSPI_CHCONF0); omap2_mcspi_chip_select(slave, 0); return 0; } static void mcspi_rd_fifo(struct spi_master_omap2_mcspi *spim, int cs_id) { u8 byte; int i; /* Receiver register must be read to remove source of interrupt */ for (i = 0; i < spim->fifo_depth; i++) { byte = mcspi_rd_cs_reg(spim, cs_id, OMAP2_MCSPI_RX0); if (spim->rx_buf && (spim->rx_len > 0)) *spim->rx_buf++ = byte; spim->rx_len--; } } static void mcspi_wr_fifo(struct spi_master_omap2_mcspi *spim, int cs_id) { u8 byte; int i; /* load transmitter register to remove the source of the interrupt */ for (i = 0; i < spim->fifo_depth; i++) { if (spim->tx_len <= 0) byte = 0; else byte = spim->tx_buf ? *spim->tx_buf++ : 0; mcspi_wr_cs_reg(spim, cs_id, OMAP2_MCSPI_TX0, byte); spim->tx_len--; } } static void mcspi_wr_fifo_bh(struct spi_master_omap2_mcspi *spim, int cs_id) { u8 byte; int i; rtdm_lockctx_t c; rtdm_lock_get_irqsave(&spim->lock, c); for (i = 0; i < spim->fifo_depth; i++) { if (spim->tx_len <= 0) byte = 0; else byte = spim->tx_buf ? *spim->tx_buf++ : 0; mcspi_wr_cs_reg(spim, cs_id, OMAP2_MCSPI_TX0, byte); spim->tx_len--; } rtdm_lock_put_irqrestore(&spim->lock, c); } static int omap2_mcspi_interrupt(rtdm_irq_t *irqh) { struct spi_master_omap2_mcspi *spim; u32 l; int i, cs_id = 0; spim = rtdm_irq_get_arg(irqh, struct spi_master_omap2_mcspi); rtdm_lock_get(&spim->lock); for (i = 0; i < OMAP2_MCSPI_CS_N; i++) if (spim->cs[i].chosen) { cs_id = i; break; } spim->n_interrupts++; l = mcspi_rd_reg(spim, OMAP2_MCSPI_IRQSTATUS); if ((l & OMAP2_MCSPI_IRQSTATUS_RX0_FULL) || (l & OMAP2_MCSPI_IRQSTATUS_RX1_FULL)) { mcspi_rd_fifo(spim, cs_id); spim->n_rx_full++; } if ((l & OMAP2_MCSPI_IRQSTATUS_TX0_EMPTY) || (l & OMAP2_MCSPI_IRQSTATUS_TX1_EMPTY)) { if (spim->tx_len > 0) mcspi_wr_fifo(spim, cs_id); spim->n_tx_empty++; } /* write 1 to OMAP2_MCSPI_IRQSTATUS field to reset it */ mcspi_wr_reg(spim, OMAP2_MCSPI_IRQSTATUS, l); if ((spim->tx_len <= 0) && (spim->rx_len <= 0)) { /* disable interrupts */ mcspi_wr_reg(spim, OMAP2_MCSPI_IRQENABLE, 0); rtdm_event_signal(&spim->transfer_done); } rtdm_lock_put(&spim->lock); return RTDM_IRQ_HANDLED; } static int omap2_mcspi_disable_fifo(struct rtdm_spi_remote_slave *slave, int cs_id) { struct spi_master_omap2_mcspi *spim = to_master_omap2_mcspi(slave); u32 chconf; chconf = mcspi_rd_cs_reg(spim, cs_id, OMAP2_MCSPI_CHCONF0); chconf &= ~(OMAP2_MCSPI_CHCONF_FFER | OMAP2_MCSPI_CHCONF_FFET); mcspi_wr_cs_reg(spim, cs_id, OMAP2_MCSPI_CHCONF0, chconf); return 0; } static int omap2_mcspi_set_fifo(struct rtdm_spi_remote_slave *slave) { struct spi_master_omap2_mcspi *spim = to_master_omap2_mcspi(slave); unsigned int wcnt; int max_fifo_depth, fifo_depth, bytes_per_word; u32 chconf, xferlevel; chconf = mcspi_rd_cs_reg(spim, slave->chip_select, OMAP2_MCSPI_CHCONF0); bytes_per_word = 1; max_fifo_depth = OMAP2_MCSPI_MAX_FIFODEPTH / 2; if (spim->tx_len < max_fifo_depth) { fifo_depth = spim->tx_len; wcnt = spim->tx_len / bytes_per_word; } else { fifo_depth = max_fifo_depth; wcnt = max_fifo_depth * (spim->tx_len / max_fifo_depth) / bytes_per_word; } if (wcnt > OMAP2_MCSPI_MAX_FIFOWCNT) { dev_err(slave_to_kdev(slave), "%s: wcnt=%d: too many bytes in a transfer.\n", __func__, wcnt); return -EINVAL; } chconf |= OMAP2_MCSPI_CHCONF_FFER; chconf |= OMAP2_MCSPI_CHCONF_FFET; mcspi_wr_cs_reg(spim, slave->chip_select, OMAP2_MCSPI_CHCONF0, chconf); spim->fifo_depth = fifo_depth; xferlevel = wcnt << 16; xferlevel |= (fifo_depth - 1) << 8; xferlevel |= fifo_depth - 1; mcspi_wr_reg(spim, OMAP2_MCSPI_XFERLEVEL, xferlevel); return 0; } static int do_transfer_irq_bh(struct rtdm_spi_remote_slave *slave) { struct spi_master_omap2_mcspi *spim = to_master_omap2_mcspi(slave); u32 chconf, l; int ret; int i; /* configure to send and receive */ chconf = mcspi_rd_cs_reg(spim, slave->chip_select, OMAP2_MCSPI_CHCONF0); chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; chconf &= ~OMAP2_MCSPI_CHCONF_TURBO; mcspi_wr_cs_reg(spim, slave->chip_select, OMAP2_MCSPI_CHCONF0, chconf); /* fifo can be enabled on a single channel */ if (slave->chip_select == 0) { if (spim->cs[1].chosen) omap2_mcspi_disable_fifo(slave, 1); } else { if (spim->cs[0].chosen) omap2_mcspi_disable_fifo(slave, 0); } ret = omap2_mcspi_set_fifo(slave); if (ret) return ret; omap2_mcspi_channel_enable(slave, 1); /* Set slave->chip_select as chosen */ for (i = 0; i < OMAP2_MCSPI_CS_N; i++) if (i == slave->chip_select) spim->cs[i].chosen = 1; else spim->cs[i].chosen = 0; /* The interrupt status bit should always be reset * after the channel is enabled * and before the event is enabled as an interrupt source. */ /* write 1 to OMAP2_MCSPI_IRQSTATUS field to reset it */ l = mcspi_rd_reg(spim, OMAP2_MCSPI_IRQSTATUS); mcspi_wr_reg(spim, OMAP2_MCSPI_IRQSTATUS, l); spim->n_interrupts = 0; spim->n_rx_full = 0; spim->n_tx_empty = 0; /* Enable interrupts last. */ /* support only two channels */ if (slave->chip_select == 0) l = OMAP2_MCSPI_IRQENABLE_TX0_EMPTY | OMAP2_MCSPI_IRQENABLE_RX0_FULL; else l = OMAP2_MCSPI_IRQENABLE_TX1_EMPTY | OMAP2_MCSPI_IRQENABLE_RX1_FULL; mcspi_wr_reg(spim, OMAP2_MCSPI_IRQENABLE, l); /* TX_EMPTY will be raised only after data is transfered */ mcspi_wr_fifo_bh(spim, slave->chip_select); /* wait for transfer completion */ ret = rtdm_event_wait(&spim->transfer_done); omap2_mcspi_channel_enable(slave, 0); if (ret) return ret; /* spim->tx_len and spim->rx_len should be 0 */ if (spim->tx_len || spim->rx_len) return -EIO; return 0; } static int do_transfer_irq(struct rtdm_spi_remote_slave *slave) { struct spi_master_omap2_mcspi *spim = to_master_omap2_mcspi(slave); int len, first_size, last_size, ret; len = spim->tx_len; if (len < (OMAP2_MCSPI_MAX_FIFODEPTH / 2)) goto label_last; first_size = (OMAP2_MCSPI_MAX_FIFODEPTH / 2) * (len / (OMAP2_MCSPI_MAX_FIFODEPTH / 2)); spim->tx_len = first_size; spim->rx_len = first_size; ret = do_transfer_irq_bh(slave); if (ret) return ret; label_last: last_size = len % (OMAP2_MCSPI_MAX_FIFODEPTH / 2); if (last_size == 0) return ret; spim->tx_len = last_size; spim->rx_len = last_size; ret = do_transfer_irq_bh(slave); return ret; } static int omap2_mcspi_transfer_iobufs(struct rtdm_spi_remote_slave *slave) { struct spi_master_omap2_mcspi *spim = to_master_omap2_mcspi(slave); struct spi_slave_omap2_mcspi *mapped_data = to_slave_omap2_mcspi(slave); int ret; if (mapped_data->io_len == 0) return -EINVAL; /* No I/O buffers set. */ spim->tx_len = mapped_data->io_len / 2; spim->rx_len = spim->tx_len; spim->tx_buf = mapped_data->io_virt + spim->rx_len; spim->rx_buf = mapped_data->io_virt; ret = do_transfer_irq(slave); return ret ? : 0; } static int omap2_mcspi_transfer_iobufs_n(struct rtdm_spi_remote_slave *slave, int len) { struct spi_master_omap2_mcspi *spim = to_master_omap2_mcspi(slave); struct spi_slave_omap2_mcspi *mapped_data = to_slave_omap2_mcspi(slave); int ret; if ((mapped_data->io_len == 0) || (len <= 0) || (len > (mapped_data->io_len / 2))) return -EINVAL; spim->tx_len = len; spim->rx_len = len; spim->tx_buf = mapped_data->io_virt + mapped_data->io_len / 2; spim->rx_buf = mapped_data->io_virt; ret = do_transfer_irq(slave); return ret ? : 0; } static ssize_t omap2_mcspi_read(struct rtdm_spi_remote_slave *slave, void *rx, size_t len) { struct spi_master_omap2_mcspi *spim = to_master_omap2_mcspi(slave); int ret; spim->tx_len = len; spim->rx_len = len; spim->tx_buf = NULL; spim->rx_buf = rx; ret = do_transfer_irq(slave); return ret ? : len; } static ssize_t omap2_mcspi_write(struct rtdm_spi_remote_slave *slave, const void *tx, size_t len) { struct spi_master_omap2_mcspi *spim = to_master_omap2_mcspi(slave); int ret; spim->tx_len = len; spim->rx_len = len; spim->tx_buf = tx; spim->rx_buf = NULL; ret = do_transfer_irq(slave); return ret ? : len; } static int set_iobufs(struct spi_slave_omap2_mcspi *mapped_data, size_t len) { dma_addr_t dma; void *p; if (len == 0) return -EINVAL; len = L1_CACHE_ALIGN(len) * 2; if (len == mapped_data->io_len) return 0; if (mapped_data->io_len) return -EINVAL; /* I/O buffers may not be resized. */ /* * Since we need the I/O buffers to be set for starting a * transfer, there is no need for serializing this routine and * transfer_iobufs(), provided io_len is set last. * * NOTE: We don't need coherent memory until we actually get * DMA transfers working, this code is a bit ahead of * schedule. * * Revisit: this assumes DMA mask is 4Gb. */ p = dma_alloc_coherent(NULL, len, &dma, GFP_KERNEL); if (p == NULL) return -ENOMEM; mapped_data->io_dma = dma; mapped_data->io_virt = p; /* * May race with transfer_iobufs(), must be assigned after all * the rest is set up, enforcing a membar. */ smp_mb(); mapped_data->io_len = len; return 0; } static int omap2_mcspi_set_iobufs(struct rtdm_spi_remote_slave *slave, struct rtdm_spi_iobufs *p) { struct spi_slave_omap2_mcspi *mapped_data = to_slave_omap2_mcspi(slave); int ret; ret = set_iobufs(mapped_data, p->io_len); if (ret) return ret; p->i_offset = 0; p->o_offset = mapped_data->io_len / 2; p->map_len = mapped_data->io_len; return 0; } static int omap2_mcspi_mmap_iobufs(struct rtdm_spi_remote_slave *slave, struct vm_area_struct *vma) { struct spi_slave_omap2_mcspi *mapped_data = to_slave_omap2_mcspi(slave); /* * dma_alloc_coherent() delivers non-cached memory, make sure * to return consistent mapping attributes. Typically, mixing * memory attributes across address spaces referring to the * same physical area is architecturally wrong on ARM. */ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); return rtdm_mmap_kmem(vma, mapped_data->io_virt); } static void omap2_mcspi_mmap_release(struct rtdm_spi_remote_slave *slave) { struct spi_slave_omap2_mcspi *mapped_data = to_slave_omap2_mcspi(slave); dma_free_coherent(NULL, mapped_data->io_len, mapped_data->io_virt, mapped_data->io_dma); mapped_data->io_len = 0; } static struct rtdm_spi_remote_slave * omap2_mcspi_attach_slave(struct rtdm_spi_master *master, struct spi_device *spi) { struct spi_master_omap2_mcspi *spim; struct spi_slave_omap2_mcspi *mapped_data; int ret; if ((spi->chip_select >= OMAP2_MCSPI_CS_N) || (OMAP2_MCSPI_CS_N > 2)) { /* Error in the case of native CS requested with CS > 1 */ dev_err(&spi->dev, "%s: only two native CS per spi module are supported\n", __func__); return ERR_PTR(-EINVAL); } mapped_data = kzalloc(sizeof(*mapped_data), GFP_KERNEL); if (mapped_data == NULL) return ERR_PTR(-ENOMEM); ret = rtdm_spi_add_remote_slave(&mapped_data->slave, master, spi); if (ret) { dev_err(&spi->dev, "%s: failed to attach slave\n", __func__); kfree(mapped_data); return ERR_PTR(ret); } spim = container_of(master, struct spi_master_omap2_mcspi, master); spim->cs[spi->chip_select].chosen = 0; spim->cs[spi->chip_select].regs = spim->regs + spi->chip_select * OMAP2_MCSPI_CHANNELBANK_SIZE; spim->cs[spi->chip_select].phys = spim->phys + spi->chip_select * OMAP2_MCSPI_CHANNELBANK_SIZE; return &mapped_data->slave; } static void omap2_mcspi_detach_slave(struct rtdm_spi_remote_slave *slave) { struct spi_slave_omap2_mcspi *mapped_data = to_slave_omap2_mcspi(slave); rtdm_spi_remove_remote_slave(slave); kfree(mapped_data); } static struct rtdm_spi_master_ops omap2_mcspi_master_ops = { .configure = omap2_mcspi_configure, .chip_select = omap2_mcspi_chip_select, .set_iobufs = omap2_mcspi_set_iobufs, .mmap_iobufs = omap2_mcspi_mmap_iobufs, .mmap_release = omap2_mcspi_mmap_release, .transfer_iobufs = omap2_mcspi_transfer_iobufs, .transfer_iobufs_n = omap2_mcspi_transfer_iobufs_n, .write = omap2_mcspi_write, .read = omap2_mcspi_read, .attach_slave = omap2_mcspi_attach_slave, .detach_slave = omap2_mcspi_detach_slave, }; static struct omap2_mcspi_platform_config omap2_pdata = { .regs_offset = 0, }; static struct omap2_mcspi_platform_config omap4_pdata = { .regs_offset = OMAP4_MCSPI_REG_OFFSET, }; static const struct of_device_id omap_mcspi_of_match[] = { { .compatible = "ti,omap2-mcspi", .data = &omap2_pdata, }, { /* beaglebone black */ .compatible = "ti,omap4-mcspi", .data = &omap4_pdata, }, { /* Sentinel */ }, }; MODULE_DEVICE_TABLE(of, omap_mcspi_of_match); static int omap2_mcspi_probe(struct platform_device *pdev) { struct spi_master_omap2_mcspi *spim; struct rtdm_spi_master *master; struct spi_controller *ctlr; struct resource *r; int ret, irq; u32 regs_offset = 0; const struct omap2_mcspi_platform_config *pdata; const struct of_device_id *match; u32 num_cs = 1; unsigned int pin_dir = MCSPI_PINDIR_D0_IN_D1_OUT; match = of_match_device(omap_mcspi_of_match, &pdev->dev); if (match) { pdata = match->data; regs_offset = pdata->regs_offset; } else { dev_err(&pdev->dev, "%s: cannot find a match with device tree\n" "of '%s' or '%s'", __func__, omap_mcspi_of_match[0].compatible, omap_mcspi_of_match[1].compatible); return -ENOENT; } master = rtdm_spi_alloc_master(&pdev->dev, struct spi_master_omap2_mcspi, master); if (master == NULL) return -ENOMEM; master->subclass = RTDM_SUBCLASS_OMAP2_MCSPI; master->ops = &omap2_mcspi_master_ops; platform_set_drvdata(pdev, master); ctlr = master->controller; /* flags understood by this controller driver */ ctlr->mode_bits = OMAP2_MCSPI_SPI_MODE_BITS; /* TODO: SPI_BPW_RANGE_MASK(4, 32); */ ctlr->bits_per_word_mask = SPI_BPW_MASK(8); of_property_read_u32(pdev->dev.of_node, "ti,spi-num-cs", &num_cs); ctlr->num_chipselect = num_cs; if (of_get_property(pdev->dev.of_node, "ti,pindir-d0-out-d1-in", NULL)) { pin_dir = MCSPI_PINDIR_D0_OUT_D1_IN; } ctlr->max_speed_hz = OMAP2_MCSPI_MAX_FREQ; ctlr->min_speed_hz = OMAP2_MCSPI_MAX_FREQ >> 15; ctlr->dev.of_node = pdev->dev.of_node; spim = container_of(master, struct spi_master_omap2_mcspi, master); rtdm_event_init(&spim->transfer_done, 0); rtdm_lock_init(&spim->lock); spim->pin_dir = pin_dir; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); spim->regs = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(spim->regs)) { dev_err(&pdev->dev, "%s: cannot map I/O memory\n", __func__); ret = PTR_ERR(spim->regs); goto fail; } spim->phys = r->start + regs_offset; spim->regs += regs_offset; irq = irq_of_parse_and_map(pdev->dev.of_node, 0); if (irq <= 0) { ret = irq ?: -ENODEV; dev_err(&pdev->dev, "%s: irq_of_parse_and_map: %d\n", __func__, irq); goto fail; } ret = rtdm_irq_request(&spim->irqh, irq, omap2_mcspi_interrupt, 0, dev_name(&pdev->dev), spim); if (ret) { dev_err(&pdev->dev, "%s: cannot request IRQ%d\n", __func__, irq); goto fail_unclk; } ret = rtdm_spi_add_master(&spim->master); if (ret) { dev_err(&pdev->dev, "%s: failed to add master\n", __func__); goto fail_unclk; } pm_runtime_use_autosuspend(&pdev->dev); /* if delay is negative and the use_autosuspend flag is set * then runtime suspends are prevented. */ pm_runtime_set_autosuspend_delay(&pdev->dev, PM_NEGATIVE_DELAY); pm_runtime_enable(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "%s: pm_runtime_get_sync error %d\n", __func__, ret); return ret; } omap2_mcspi_reset_hw(spim); omap2_mcspi_init_hw(spim); dev_info(&pdev->dev, "success\n"); return 0; fail_unclk: fail: spi_controller_put(ctlr); return ret; } static int omap2_mcspi_remove(struct platform_device *pdev) { struct rtdm_spi_master *master = platform_get_drvdata(pdev); struct spi_master_omap2_mcspi *spim; spim = container_of(master, struct spi_master_omap2_mcspi, master); omap2_mcspi_reset_hw(spim); pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); rtdm_irq_free(&spim->irqh); rtdm_spi_remove_master(master); return 0; } static struct platform_driver omap2_mcspi_spi_driver = { .driver = { .name = "omap2_mcspi_rt", .of_match_table = omap_mcspi_of_match, }, .probe = omap2_mcspi_probe, .remove = omap2_mcspi_remove, }; module_platform_driver(omap2_mcspi_spi_driver); MODULE_LICENSE("GPL");