162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/drivers/video/mmp/hw/mmp_spi.c
462306a36Sopenharmony_ci * using the spi in LCD controler for commands send
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2012 Marvell Technology Group Ltd.
762306a36Sopenharmony_ci * Authors:  Guoqing Li <ligq@marvell.com>
862306a36Sopenharmony_ci *          Lisa Du <cldu@marvell.com>
962306a36Sopenharmony_ci *          Zhou Zhu <zzhu3@marvell.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/err.h>
1462306a36Sopenharmony_ci#include <linux/io.h>
1562306a36Sopenharmony_ci#include <linux/spi/spi.h>
1662306a36Sopenharmony_ci#include "mmp_ctrl.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/**
1962306a36Sopenharmony_ci * spi_write - write command to the SPI port
2062306a36Sopenharmony_ci * @spi:  the SPI device.
2162306a36Sopenharmony_ci * @data: can be 8/16/32-bit, MSB justified data to write.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Wait bus transfer complete IRQ.
2462306a36Sopenharmony_ci * The caller is expected to perform the necessary locking.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * Returns:
2762306a36Sopenharmony_ci *   %-ETIMEDOUT	timeout occurred
2862306a36Sopenharmony_ci *   0			success
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_cistatic inline int lcd_spi_write(struct spi_device *spi, u32 data)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	int timeout = 100000, isr, ret = 0;
3362306a36Sopenharmony_ci	u32 tmp;
3462306a36Sopenharmony_ci	void __iomem *reg_base = (void __iomem *)
3562306a36Sopenharmony_ci		*(void **)spi_master_get_devdata(spi->master);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	/* clear ISR */
3862306a36Sopenharmony_ci	writel_relaxed(~SPI_IRQ_MASK, reg_base + SPU_IRQ_ISR);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	switch (spi->bits_per_word) {
4162306a36Sopenharmony_ci	case 8:
4262306a36Sopenharmony_ci		writel_relaxed((u8)data, reg_base + LCD_SPU_SPI_TXDATA);
4362306a36Sopenharmony_ci		break;
4462306a36Sopenharmony_ci	case 16:
4562306a36Sopenharmony_ci		writel_relaxed((u16)data, reg_base + LCD_SPU_SPI_TXDATA);
4662306a36Sopenharmony_ci		break;
4762306a36Sopenharmony_ci	case 32:
4862306a36Sopenharmony_ci		writel_relaxed((u32)data, reg_base + LCD_SPU_SPI_TXDATA);
4962306a36Sopenharmony_ci		break;
5062306a36Sopenharmony_ci	default:
5162306a36Sopenharmony_ci		dev_err(&spi->dev, "Wrong spi bit length\n");
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* SPI start to send command */
5562306a36Sopenharmony_ci	tmp = readl_relaxed(reg_base + LCD_SPU_SPI_CTRL);
5662306a36Sopenharmony_ci	tmp &= ~CFG_SPI_START_MASK;
5762306a36Sopenharmony_ci	tmp |= CFG_SPI_START(1);
5862306a36Sopenharmony_ci	writel(tmp, reg_base + LCD_SPU_SPI_CTRL);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	isr = readl_relaxed(reg_base + SPU_IRQ_ISR);
6162306a36Sopenharmony_ci	while (!(isr & SPI_IRQ_ENA_MASK)) {
6262306a36Sopenharmony_ci		udelay(100);
6362306a36Sopenharmony_ci		isr = readl_relaxed(reg_base + SPU_IRQ_ISR);
6462306a36Sopenharmony_ci		if (!--timeout) {
6562306a36Sopenharmony_ci			ret = -ETIMEDOUT;
6662306a36Sopenharmony_ci			dev_err(&spi->dev, "spi cmd send time out\n");
6762306a36Sopenharmony_ci			break;
6862306a36Sopenharmony_ci		}
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	tmp = readl_relaxed(reg_base + LCD_SPU_SPI_CTRL);
7262306a36Sopenharmony_ci	tmp &= ~CFG_SPI_START_MASK;
7362306a36Sopenharmony_ci	tmp |= CFG_SPI_START(0);
7462306a36Sopenharmony_ci	writel_relaxed(tmp, reg_base + LCD_SPU_SPI_CTRL);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	writel_relaxed(~SPI_IRQ_MASK, reg_base + SPU_IRQ_ISR);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return ret;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic int lcd_spi_setup(struct spi_device *spi)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	void __iomem *reg_base = (void __iomem *)
8462306a36Sopenharmony_ci		*(void **)spi_master_get_devdata(spi->master);
8562306a36Sopenharmony_ci	u32 tmp;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	tmp = CFG_SCLKCNT(16) |
8862306a36Sopenharmony_ci		CFG_TXBITS(spi->bits_per_word) |
8962306a36Sopenharmony_ci		CFG_SPI_SEL(1) | CFG_SPI_ENA(1) |
9062306a36Sopenharmony_ci		CFG_SPI_3W4WB(1);
9162306a36Sopenharmony_ci	writel(tmp, reg_base + LCD_SPU_SPI_CTRL);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/*
9462306a36Sopenharmony_ci	 * After set mode it need a time to pull up the spi singals,
9562306a36Sopenharmony_ci	 * or it would cause the wrong waveform when send spi command,
9662306a36Sopenharmony_ci	 * especially on pxa910h
9762306a36Sopenharmony_ci	 */
9862306a36Sopenharmony_ci	tmp = readl_relaxed(reg_base + SPU_IOPAD_CONTROL);
9962306a36Sopenharmony_ci	if ((tmp & CFG_IOPADMODE_MASK) != IOPAD_DUMB18SPI)
10062306a36Sopenharmony_ci		writel_relaxed(IOPAD_DUMB18SPI |
10162306a36Sopenharmony_ci			(tmp & ~CFG_IOPADMODE_MASK),
10262306a36Sopenharmony_ci			reg_base + SPU_IOPAD_CONTROL);
10362306a36Sopenharmony_ci	udelay(20);
10462306a36Sopenharmony_ci	return 0;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic int lcd_spi_one_transfer(struct spi_device *spi, struct spi_message *m)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct spi_transfer *t;
11062306a36Sopenharmony_ci	int i;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	list_for_each_entry(t, &m->transfers, transfer_list) {
11362306a36Sopenharmony_ci		switch (spi->bits_per_word) {
11462306a36Sopenharmony_ci		case 8:
11562306a36Sopenharmony_ci			for (i = 0; i < t->len; i++)
11662306a36Sopenharmony_ci				lcd_spi_write(spi, ((u8 *)t->tx_buf)[i]);
11762306a36Sopenharmony_ci			break;
11862306a36Sopenharmony_ci		case 16:
11962306a36Sopenharmony_ci			for (i = 0; i < t->len/2; i++)
12062306a36Sopenharmony_ci				lcd_spi_write(spi, ((u16 *)t->tx_buf)[i]);
12162306a36Sopenharmony_ci			break;
12262306a36Sopenharmony_ci		case 32:
12362306a36Sopenharmony_ci			for (i = 0; i < t->len/4; i++)
12462306a36Sopenharmony_ci				lcd_spi_write(spi, ((u32 *)t->tx_buf)[i]);
12562306a36Sopenharmony_ci			break;
12662306a36Sopenharmony_ci		default:
12762306a36Sopenharmony_ci			dev_err(&spi->dev, "Wrong spi bit length\n");
12862306a36Sopenharmony_ci		}
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	m->status = 0;
13262306a36Sopenharmony_ci	if (m->complete)
13362306a36Sopenharmony_ci		m->complete(m->context);
13462306a36Sopenharmony_ci	return 0;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ciint lcd_spi_register(struct mmphw_ctrl *ctrl)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct spi_master *master;
14062306a36Sopenharmony_ci	void **p_regbase;
14162306a36Sopenharmony_ci	int err;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	master = spi_alloc_master(ctrl->dev, sizeof(void *));
14462306a36Sopenharmony_ci	if (!master) {
14562306a36Sopenharmony_ci		dev_err(ctrl->dev, "unable to allocate SPI master\n");
14662306a36Sopenharmony_ci		return -ENOMEM;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci	p_regbase = spi_master_get_devdata(master);
14962306a36Sopenharmony_ci	*p_regbase = (void __force *)ctrl->reg_base;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* set bus num to 5 to avoid conflict with other spi hosts */
15262306a36Sopenharmony_ci	master->bus_num = 5;
15362306a36Sopenharmony_ci	master->num_chipselect = 1;
15462306a36Sopenharmony_ci	master->setup = lcd_spi_setup;
15562306a36Sopenharmony_ci	master->transfer = lcd_spi_one_transfer;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	err = spi_register_master(master);
15862306a36Sopenharmony_ci	if (err < 0) {
15962306a36Sopenharmony_ci		dev_err(ctrl->dev, "unable to register SPI master\n");
16062306a36Sopenharmony_ci		spi_master_put(master);
16162306a36Sopenharmony_ci		return err;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	dev_info(&master->dev, "registered\n");
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return 0;
16762306a36Sopenharmony_ci}
168