18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/video/mmp/hw/mmp_spi.c 48c2ecf20Sopenharmony_ci * using the spi in LCD controler for commands send 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2012 Marvell Technology Group Ltd. 78c2ecf20Sopenharmony_ci * Authors: Guoqing Li <ligq@marvell.com> 88c2ecf20Sopenharmony_ci * Lisa Du <cldu@marvell.com> 98c2ecf20Sopenharmony_ci * Zhou Zhu <zzhu3@marvell.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 168c2ecf20Sopenharmony_ci#include "mmp_ctrl.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/** 198c2ecf20Sopenharmony_ci * spi_write - write command to the SPI port 208c2ecf20Sopenharmony_ci * @data: can be 8/16/32-bit, MSB justified data to write. 218c2ecf20Sopenharmony_ci * @len: data length. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Wait bus transfer complete IRQ. 248c2ecf20Sopenharmony_ci * The caller is expected to perform the necessary locking. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Returns: 278c2ecf20Sopenharmony_ci * %-ETIMEDOUT timeout occurred 288c2ecf20Sopenharmony_ci * 0 success 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_cistatic inline int lcd_spi_write(struct spi_device *spi, u32 data) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci int timeout = 100000, isr, ret = 0; 338c2ecf20Sopenharmony_ci u32 tmp; 348c2ecf20Sopenharmony_ci void __iomem *reg_base = (void __iomem *) 358c2ecf20Sopenharmony_ci *(void **)spi_master_get_devdata(spi->master); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci /* clear ISR */ 388c2ecf20Sopenharmony_ci writel_relaxed(~SPI_IRQ_MASK, reg_base + SPU_IRQ_ISR); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci switch (spi->bits_per_word) { 418c2ecf20Sopenharmony_ci case 8: 428c2ecf20Sopenharmony_ci writel_relaxed((u8)data, reg_base + LCD_SPU_SPI_TXDATA); 438c2ecf20Sopenharmony_ci break; 448c2ecf20Sopenharmony_ci case 16: 458c2ecf20Sopenharmony_ci writel_relaxed((u16)data, reg_base + LCD_SPU_SPI_TXDATA); 468c2ecf20Sopenharmony_ci break; 478c2ecf20Sopenharmony_ci case 32: 488c2ecf20Sopenharmony_ci writel_relaxed((u32)data, reg_base + LCD_SPU_SPI_TXDATA); 498c2ecf20Sopenharmony_ci break; 508c2ecf20Sopenharmony_ci default: 518c2ecf20Sopenharmony_ci dev_err(&spi->dev, "Wrong spi bit length\n"); 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* SPI start to send command */ 558c2ecf20Sopenharmony_ci tmp = readl_relaxed(reg_base + LCD_SPU_SPI_CTRL); 568c2ecf20Sopenharmony_ci tmp &= ~CFG_SPI_START_MASK; 578c2ecf20Sopenharmony_ci tmp |= CFG_SPI_START(1); 588c2ecf20Sopenharmony_ci writel(tmp, reg_base + LCD_SPU_SPI_CTRL); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci isr = readl_relaxed(reg_base + SPU_IRQ_ISR); 618c2ecf20Sopenharmony_ci while (!(isr & SPI_IRQ_ENA_MASK)) { 628c2ecf20Sopenharmony_ci udelay(100); 638c2ecf20Sopenharmony_ci isr = readl_relaxed(reg_base + SPU_IRQ_ISR); 648c2ecf20Sopenharmony_ci if (!--timeout) { 658c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 668c2ecf20Sopenharmony_ci dev_err(&spi->dev, "spi cmd send time out\n"); 678c2ecf20Sopenharmony_ci break; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci tmp = readl_relaxed(reg_base + LCD_SPU_SPI_CTRL); 728c2ecf20Sopenharmony_ci tmp &= ~CFG_SPI_START_MASK; 738c2ecf20Sopenharmony_ci tmp |= CFG_SPI_START(0); 748c2ecf20Sopenharmony_ci writel_relaxed(tmp, reg_base + LCD_SPU_SPI_CTRL); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci writel_relaxed(~SPI_IRQ_MASK, reg_base + SPU_IRQ_ISR); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return ret; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int lcd_spi_setup(struct spi_device *spi) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci void __iomem *reg_base = (void __iomem *) 848c2ecf20Sopenharmony_ci *(void **)spi_master_get_devdata(spi->master); 858c2ecf20Sopenharmony_ci u32 tmp; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci tmp = CFG_SCLKCNT(16) | 888c2ecf20Sopenharmony_ci CFG_TXBITS(spi->bits_per_word) | 898c2ecf20Sopenharmony_ci CFG_SPI_SEL(1) | CFG_SPI_ENA(1) | 908c2ecf20Sopenharmony_ci CFG_SPI_3W4WB(1); 918c2ecf20Sopenharmony_ci writel(tmp, reg_base + LCD_SPU_SPI_CTRL); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * After set mode it need a time to pull up the spi singals, 958c2ecf20Sopenharmony_ci * or it would cause the wrong waveform when send spi command, 968c2ecf20Sopenharmony_ci * especially on pxa910h 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci tmp = readl_relaxed(reg_base + SPU_IOPAD_CONTROL); 998c2ecf20Sopenharmony_ci if ((tmp & CFG_IOPADMODE_MASK) != IOPAD_DUMB18SPI) 1008c2ecf20Sopenharmony_ci writel_relaxed(IOPAD_DUMB18SPI | 1018c2ecf20Sopenharmony_ci (tmp & ~CFG_IOPADMODE_MASK), 1028c2ecf20Sopenharmony_ci reg_base + SPU_IOPAD_CONTROL); 1038c2ecf20Sopenharmony_ci udelay(20); 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int lcd_spi_one_transfer(struct spi_device *spi, struct spi_message *m) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct spi_transfer *t; 1108c2ecf20Sopenharmony_ci int i; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci list_for_each_entry(t, &m->transfers, transfer_list) { 1138c2ecf20Sopenharmony_ci switch (spi->bits_per_word) { 1148c2ecf20Sopenharmony_ci case 8: 1158c2ecf20Sopenharmony_ci for (i = 0; i < t->len; i++) 1168c2ecf20Sopenharmony_ci lcd_spi_write(spi, ((u8 *)t->tx_buf)[i]); 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci case 16: 1198c2ecf20Sopenharmony_ci for (i = 0; i < t->len/2; i++) 1208c2ecf20Sopenharmony_ci lcd_spi_write(spi, ((u16 *)t->tx_buf)[i]); 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci case 32: 1238c2ecf20Sopenharmony_ci for (i = 0; i < t->len/4; i++) 1248c2ecf20Sopenharmony_ci lcd_spi_write(spi, ((u32 *)t->tx_buf)[i]); 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci default: 1278c2ecf20Sopenharmony_ci dev_err(&spi->dev, "Wrong spi bit length\n"); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci m->status = 0; 1328c2ecf20Sopenharmony_ci if (m->complete) 1338c2ecf20Sopenharmony_ci m->complete(m->context); 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ciint lcd_spi_register(struct mmphw_ctrl *ctrl) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct spi_master *master; 1408c2ecf20Sopenharmony_ci void **p_regbase; 1418c2ecf20Sopenharmony_ci int err; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci master = spi_alloc_master(ctrl->dev, sizeof(void *)); 1448c2ecf20Sopenharmony_ci if (!master) { 1458c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "unable to allocate SPI master\n"); 1468c2ecf20Sopenharmony_ci return -ENOMEM; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci p_regbase = spi_master_get_devdata(master); 1498c2ecf20Sopenharmony_ci *p_regbase = (void __force *)ctrl->reg_base; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* set bus num to 5 to avoid conflict with other spi hosts */ 1528c2ecf20Sopenharmony_ci master->bus_num = 5; 1538c2ecf20Sopenharmony_ci master->num_chipselect = 1; 1548c2ecf20Sopenharmony_ci master->setup = lcd_spi_setup; 1558c2ecf20Sopenharmony_ci master->transfer = lcd_spi_one_transfer; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci err = spi_register_master(master); 1588c2ecf20Sopenharmony_ci if (err < 0) { 1598c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "unable to register SPI master\n"); 1608c2ecf20Sopenharmony_ci spi_master_put(master); 1618c2ecf20Sopenharmony_ci return err; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci dev_info(&master->dev, "registered\n"); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 168