| /* |
| * 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> |
| * |
| * 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, see <http://www.gnu.org/licenses/>. |
| * |
| */ |
| #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 |
| * @data: can be 8/16/32-bit, MSB justified data to write. |
| * @len: data length. |
| * |
| * 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 *reg_base = |
| *(void **)spi_master_get_devdata(spi->master); |
| |
| /* 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 *reg_base = |
| *(void **)spi_master_get_devdata(spi->master); |
| 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 need a time to pull up the spi singals, |
| * 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_master *master; |
| void **p_regbase; |
| int err; |
| |
| master = spi_alloc_master(ctrl->dev, sizeof(void *)); |
| if (!master) { |
| dev_err(ctrl->dev, "unable to allocate SPI master\n"); |
| return -ENOMEM; |
| } |
| p_regbase = spi_master_get_devdata(master); |
| *p_regbase = ctrl->reg_base; |
| |
| /* set bus num to 5 to avoid conflict with other spi hosts */ |
| master->bus_num = 5; |
| master->num_chipselect = 1; |
| master->setup = lcd_spi_setup; |
| master->transfer = lcd_spi_one_transfer; |
| |
| err = spi_register_master(master); |
| if (err < 0) { |
| dev_err(ctrl->dev, "unable to register SPI master\n"); |
| spi_master_put(master); |
| return err; |
| } |
| |
| dev_info(&master->dev, "registered\n"); |
| |
| return 0; |
| } |