| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * linux/drivers/video/mmp/hw/mmp_spi.c |
| * using the spi in LCD controler for commands send |
| * |
| * Copyright (C) 2012 Marvell Technology Group Ltd. |
| * Authors: Guoqing Li <ligq@marvell.com> |
| * Lisa Du <cldu@marvell.com> |
| * Zhou Zhu <zzhu3@marvell.com> |
| */ |
| #include <linux/errno.h> |
| #include <linux/delay.h> |
| #include <linux/err.h> |
| #include <linux/io.h> |
| #include <linux/spi/spi.h> |
| #include "mmp_ctrl.h" |
| |
| /** |
| * spi_write - write command to the SPI port |
| * @spi: the SPI device. |
| * @data: can be 8/16/32-bit, MSB justified data to write. |
| * |
| * Wait bus transfer complete IRQ. |
| * The caller is expected to perform the necessary locking. |
| * |
| * Returns: |
| * %-ETIMEDOUT timeout occurred |
| * 0 success |
| */ |
| static inline int lcd_spi_write(struct spi_device *spi, u32 data) |
| { |
| int timeout = 100000, isr, ret = 0; |
| u32 tmp; |
| void __iomem *reg_base = (void __iomem *) |
| *(void **) spi_controller_get_devdata(spi->controller); |
| |
| /* clear ISR */ |
| writel_relaxed(~SPI_IRQ_MASK, reg_base + SPU_IRQ_ISR); |
| |
| switch (spi->bits_per_word) { |
| case 8: |
| writel_relaxed((u8)data, reg_base + LCD_SPU_SPI_TXDATA); |
| break; |
| case 16: |
| writel_relaxed((u16)data, reg_base + LCD_SPU_SPI_TXDATA); |
| break; |
| case 32: |
| writel_relaxed((u32)data, reg_base + LCD_SPU_SPI_TXDATA); |
| break; |
| default: |
| dev_err(&spi->dev, "Wrong spi bit length\n"); |
| } |
| |
| /* SPI start to send command */ |
| tmp = readl_relaxed(reg_base + LCD_SPU_SPI_CTRL); |
| tmp &= ~CFG_SPI_START_MASK; |
| tmp |= CFG_SPI_START(1); |
| writel(tmp, reg_base + LCD_SPU_SPI_CTRL); |
| |
| isr = readl_relaxed(reg_base + SPU_IRQ_ISR); |
| while (!(isr & SPI_IRQ_ENA_MASK)) { |
| udelay(100); |
| isr = readl_relaxed(reg_base + SPU_IRQ_ISR); |
| if (!--timeout) { |
| ret = -ETIMEDOUT; |
| dev_err(&spi->dev, "spi cmd send time out\n"); |
| break; |
| } |
| } |
| |
| tmp = readl_relaxed(reg_base + LCD_SPU_SPI_CTRL); |
| tmp &= ~CFG_SPI_START_MASK; |
| tmp |= CFG_SPI_START(0); |
| writel_relaxed(tmp, reg_base + LCD_SPU_SPI_CTRL); |
| |
| writel_relaxed(~SPI_IRQ_MASK, reg_base + SPU_IRQ_ISR); |
| |
| return ret; |
| } |
| |
| static int lcd_spi_setup(struct spi_device *spi) |
| { |
| void __iomem *reg_base = (void __iomem *) |
| *(void **) spi_controller_get_devdata(spi->controller); |
| u32 tmp; |
| |
| tmp = CFG_SCLKCNT(16) | |
| CFG_TXBITS(spi->bits_per_word) | |
| CFG_SPI_SEL(1) | CFG_SPI_ENA(1) | |
| CFG_SPI_3W4WB(1); |
| writel(tmp, reg_base + LCD_SPU_SPI_CTRL); |
| |
| /* |
| * After set mode it needs some time to pull up the spi signals, |
| * or it would cause the wrong waveform when send spi command, |
| * especially on pxa910h |
| */ |
| tmp = readl_relaxed(reg_base + SPU_IOPAD_CONTROL); |
| if ((tmp & CFG_IOPADMODE_MASK) != IOPAD_DUMB18SPI) |
| writel_relaxed(IOPAD_DUMB18SPI | |
| (tmp & ~CFG_IOPADMODE_MASK), |
| reg_base + SPU_IOPAD_CONTROL); |
| udelay(20); |
| return 0; |
| } |
| |
| static int lcd_spi_one_transfer(struct spi_device *spi, struct spi_message *m) |
| { |
| struct spi_transfer *t; |
| int i; |
| |
| list_for_each_entry(t, &m->transfers, transfer_list) { |
| switch (spi->bits_per_word) { |
| case 8: |
| for (i = 0; i < t->len; i++) |
| lcd_spi_write(spi, ((u8 *)t->tx_buf)[i]); |
| break; |
| case 16: |
| for (i = 0; i < t->len/2; i++) |
| lcd_spi_write(spi, ((u16 *)t->tx_buf)[i]); |
| break; |
| case 32: |
| for (i = 0; i < t->len/4; i++) |
| lcd_spi_write(spi, ((u32 *)t->tx_buf)[i]); |
| break; |
| default: |
| dev_err(&spi->dev, "Wrong spi bit length\n"); |
| } |
| } |
| |
| m->status = 0; |
| if (m->complete) |
| m->complete(m->context); |
| return 0; |
| } |
| |
| int lcd_spi_register(struct mmphw_ctrl *ctrl) |
| { |
| struct spi_controller *ctlr; |
| void **p_regbase; |
| int err; |
| |
| ctlr = spi_alloc_master(ctrl->dev, sizeof(void *)); |
| if (!ctlr) { |
| dev_err(ctrl->dev, "unable to allocate SPI master\n"); |
| return -ENOMEM; |
| } |
| p_regbase = spi_controller_get_devdata(ctlr); |
| *p_regbase = (void __force *)ctrl->reg_base; |
| |
| /* set bus num to 5 to avoid conflict with other spi hosts */ |
| ctlr->bus_num = 5; |
| ctlr->num_chipselect = 1; |
| ctlr->setup = lcd_spi_setup; |
| ctlr->transfer = lcd_spi_one_transfer; |
| |
| err = spi_register_controller(ctlr); |
| if (err < 0) { |
| dev_err(ctrl->dev, "unable to register SPI master\n"); |
| spi_controller_put(ctlr); |
| return err; |
| } |
| |
| dev_info(&ctlr->dev, "registered\n"); |
| |
| return 0; |
| } |