18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Freescale SPI controller driver cpm functions.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Maintainer: Kumar Gala
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2006 Polycom, Inc.
88c2ecf20Sopenharmony_ci * Copyright 2010 Freescale Semiconductor, Inc.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * CPM SPI and QE buffer descriptors mode support:
118c2ecf20Sopenharmony_ci * Copyright (c) 2009  MontaVista Software, Inc.
128c2ecf20Sopenharmony_ci * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci#include <asm/cpm.h>
158c2ecf20Sopenharmony_ci#include <soc/fsl/qe/qe.h>
168c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
178c2ecf20Sopenharmony_ci#include <linux/fsl_devices.h>
188c2ecf20Sopenharmony_ci#include <linux/kernel.h>
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/of_address.h>
218c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
228c2ecf20Sopenharmony_ci#include <linux/types.h>
238c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
248c2ecf20Sopenharmony_ci#include <linux/byteorder/generic.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "spi-fsl-cpm.h"
278c2ecf20Sopenharmony_ci#include "spi-fsl-lib.h"
288c2ecf20Sopenharmony_ci#include "spi-fsl-spi.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/* CPM1 and CPM2 are mutually exclusive. */
318c2ecf20Sopenharmony_ci#ifdef CONFIG_CPM1
328c2ecf20Sopenharmony_ci#include <asm/cpm1.h>
338c2ecf20Sopenharmony_ci#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_CH_SPI, 0)
348c2ecf20Sopenharmony_ci#else
358c2ecf20Sopenharmony_ci#include <asm/cpm2.h>
368c2ecf20Sopenharmony_ci#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK, 0, 0)
378c2ecf20Sopenharmony_ci#endif
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define	SPIE_TXB	0x00000200	/* Last char is written to tx fifo */
408c2ecf20Sopenharmony_ci#define	SPIE_RXB	0x00000100	/* Last char is written to rx buf */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* SPCOM register values */
438c2ecf20Sopenharmony_ci#define	SPCOM_STR	(1 << 23)	/* Start transmit */
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define	SPI_PRAM_SIZE	0x100
468c2ecf20Sopenharmony_ci#define	SPI_MRBLR	((unsigned int)PAGE_SIZE)
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic void *fsl_dummy_rx;
498c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(fsl_dummy_rx_lock);
508c2ecf20Sopenharmony_cistatic int fsl_dummy_rx_refcnt;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_civoid fsl_spi_cpm_reinit_txrx(struct mpc8xxx_spi *mspi)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	if (mspi->flags & SPI_QE) {
558c2ecf20Sopenharmony_ci		qe_issue_cmd(QE_INIT_TX_RX, mspi->subblock,
568c2ecf20Sopenharmony_ci			     QE_CR_PROTOCOL_UNSPECIFIED, 0);
578c2ecf20Sopenharmony_ci	} else {
588c2ecf20Sopenharmony_ci		if (mspi->flags & SPI_CPM1) {
598c2ecf20Sopenharmony_ci			out_be32(&mspi->pram->rstate, 0);
608c2ecf20Sopenharmony_ci			out_be16(&mspi->pram->rbptr,
618c2ecf20Sopenharmony_ci				 in_be16(&mspi->pram->rbase));
628c2ecf20Sopenharmony_ci			out_be32(&mspi->pram->tstate, 0);
638c2ecf20Sopenharmony_ci			out_be16(&mspi->pram->tbptr,
648c2ecf20Sopenharmony_ci				 in_be16(&mspi->pram->tbase));
658c2ecf20Sopenharmony_ci		} else {
668c2ecf20Sopenharmony_ci			cpm_command(CPM_SPI_CMD, CPM_CR_INIT_TRX);
678c2ecf20Sopenharmony_ci		}
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_spi_cpm_reinit_txrx);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic void fsl_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd;
758c2ecf20Sopenharmony_ci	struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd;
768c2ecf20Sopenharmony_ci	unsigned int xfer_len = min(mspi->count, SPI_MRBLR);
778c2ecf20Sopenharmony_ci	unsigned int xfer_ofs;
788c2ecf20Sopenharmony_ci	struct fsl_spi_reg *reg_base = mspi->reg_base;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	xfer_ofs = mspi->xfer_in_progress->len - mspi->count;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (mspi->rx_dma == mspi->dma_dummy_rx)
838c2ecf20Sopenharmony_ci		out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma);
848c2ecf20Sopenharmony_ci	else
858c2ecf20Sopenharmony_ci		out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma + xfer_ofs);
868c2ecf20Sopenharmony_ci	out_be16(&rx_bd->cbd_datlen, 0);
878c2ecf20Sopenharmony_ci	out_be16(&rx_bd->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT | BD_SC_WRAP);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (mspi->tx_dma == mspi->dma_dummy_tx)
908c2ecf20Sopenharmony_ci		out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma);
918c2ecf20Sopenharmony_ci	else
928c2ecf20Sopenharmony_ci		out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma + xfer_ofs);
938c2ecf20Sopenharmony_ci	out_be16(&tx_bd->cbd_datlen, xfer_len);
948c2ecf20Sopenharmony_ci	out_be16(&tx_bd->cbd_sc, BD_SC_READY | BD_SC_INTRPT | BD_SC_WRAP |
958c2ecf20Sopenharmony_ci				 BD_SC_LAST);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	/* start transfer */
988c2ecf20Sopenharmony_ci	mpc8xxx_spi_write_reg(&reg_base->command, SPCOM_STR);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ciint fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
1028c2ecf20Sopenharmony_ci		     struct spi_transfer *t, bool is_dma_mapped)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct device *dev = mspi->dev;
1058c2ecf20Sopenharmony_ci	struct fsl_spi_reg *reg_base = mspi->reg_base;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (is_dma_mapped) {
1088c2ecf20Sopenharmony_ci		mspi->map_tx_dma = 0;
1098c2ecf20Sopenharmony_ci		mspi->map_rx_dma = 0;
1108c2ecf20Sopenharmony_ci	} else {
1118c2ecf20Sopenharmony_ci		mspi->map_tx_dma = 1;
1128c2ecf20Sopenharmony_ci		mspi->map_rx_dma = 1;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (!t->tx_buf) {
1168c2ecf20Sopenharmony_ci		mspi->tx_dma = mspi->dma_dummy_tx;
1178c2ecf20Sopenharmony_ci		mspi->map_tx_dma = 0;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (!t->rx_buf) {
1218c2ecf20Sopenharmony_ci		mspi->rx_dma = mspi->dma_dummy_rx;
1228c2ecf20Sopenharmony_ci		mspi->map_rx_dma = 0;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci	if (t->bits_per_word == 16 && t->tx_buf) {
1258c2ecf20Sopenharmony_ci		const u16 *src = t->tx_buf;
1268c2ecf20Sopenharmony_ci		u16 *dst;
1278c2ecf20Sopenharmony_ci		int i;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		dst = kmalloc(t->len, GFP_KERNEL);
1308c2ecf20Sopenharmony_ci		if (!dst)
1318c2ecf20Sopenharmony_ci			return -ENOMEM;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		for (i = 0; i < t->len >> 1; i++)
1348c2ecf20Sopenharmony_ci			dst[i] = cpu_to_le16p(src + i);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		mspi->tx = dst;
1378c2ecf20Sopenharmony_ci		mspi->map_tx_dma = 1;
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if (mspi->map_tx_dma) {
1418c2ecf20Sopenharmony_ci		void *nonconst_tx = (void *)mspi->tx; /* shut up gcc */
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		mspi->tx_dma = dma_map_single(dev, nonconst_tx, t->len,
1448c2ecf20Sopenharmony_ci					      DMA_TO_DEVICE);
1458c2ecf20Sopenharmony_ci		if (dma_mapping_error(dev, mspi->tx_dma)) {
1468c2ecf20Sopenharmony_ci			dev_err(dev, "unable to map tx dma\n");
1478c2ecf20Sopenharmony_ci			return -ENOMEM;
1488c2ecf20Sopenharmony_ci		}
1498c2ecf20Sopenharmony_ci	} else if (t->tx_buf) {
1508c2ecf20Sopenharmony_ci		mspi->tx_dma = t->tx_dma;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (mspi->map_rx_dma) {
1548c2ecf20Sopenharmony_ci		mspi->rx_dma = dma_map_single(dev, mspi->rx, t->len,
1558c2ecf20Sopenharmony_ci					      DMA_FROM_DEVICE);
1568c2ecf20Sopenharmony_ci		if (dma_mapping_error(dev, mspi->rx_dma)) {
1578c2ecf20Sopenharmony_ci			dev_err(dev, "unable to map rx dma\n");
1588c2ecf20Sopenharmony_ci			goto err_rx_dma;
1598c2ecf20Sopenharmony_ci		}
1608c2ecf20Sopenharmony_ci	} else if (t->rx_buf) {
1618c2ecf20Sopenharmony_ci		mspi->rx_dma = t->rx_dma;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/* enable rx ints */
1658c2ecf20Sopenharmony_ci	mpc8xxx_spi_write_reg(&reg_base->mask, SPIE_RXB);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	mspi->xfer_in_progress = t;
1688c2ecf20Sopenharmony_ci	mspi->count = t->len;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	/* start CPM transfers */
1718c2ecf20Sopenharmony_ci	fsl_spi_cpm_bufs_start(mspi);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return 0;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cierr_rx_dma:
1768c2ecf20Sopenharmony_ci	if (mspi->map_tx_dma)
1778c2ecf20Sopenharmony_ci		dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
1788c2ecf20Sopenharmony_ci	return -ENOMEM;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_spi_cpm_bufs);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_civoid fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct device *dev = mspi->dev;
1858c2ecf20Sopenharmony_ci	struct spi_transfer *t = mspi->xfer_in_progress;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (mspi->map_tx_dma)
1888c2ecf20Sopenharmony_ci		dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
1898c2ecf20Sopenharmony_ci	if (mspi->map_rx_dma)
1908c2ecf20Sopenharmony_ci		dma_unmap_single(dev, mspi->rx_dma, t->len, DMA_FROM_DEVICE);
1918c2ecf20Sopenharmony_ci	mspi->xfer_in_progress = NULL;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (t->bits_per_word == 16 && t->rx_buf) {
1948c2ecf20Sopenharmony_ci		int i;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci		for (i = 0; i < t->len; i += 2)
1978c2ecf20Sopenharmony_ci			le16_to_cpus(t->rx_buf + i);
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_spi_cpm_bufs_complete);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_civoid fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	u16 len;
2058c2ecf20Sopenharmony_ci	struct fsl_spi_reg *reg_base = mspi->reg_base;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__,
2088c2ecf20Sopenharmony_ci		in_be16(&mspi->rx_bd->cbd_datlen), mspi->count);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	len = in_be16(&mspi->rx_bd->cbd_datlen);
2118c2ecf20Sopenharmony_ci	if (len > mspi->count) {
2128c2ecf20Sopenharmony_ci		WARN_ON(1);
2138c2ecf20Sopenharmony_ci		len = mspi->count;
2148c2ecf20Sopenharmony_ci	}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	/* Clear the events */
2178c2ecf20Sopenharmony_ci	mpc8xxx_spi_write_reg(&reg_base->event, events);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	mspi->count -= len;
2208c2ecf20Sopenharmony_ci	if (mspi->count)
2218c2ecf20Sopenharmony_ci		fsl_spi_cpm_bufs_start(mspi);
2228c2ecf20Sopenharmony_ci	else
2238c2ecf20Sopenharmony_ci		complete(&mspi->done);
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_spi_cpm_irq);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic void *fsl_spi_alloc_dummy_rx(void)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	mutex_lock(&fsl_dummy_rx_lock);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if (!fsl_dummy_rx)
2328c2ecf20Sopenharmony_ci		fsl_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL);
2338c2ecf20Sopenharmony_ci	if (fsl_dummy_rx)
2348c2ecf20Sopenharmony_ci		fsl_dummy_rx_refcnt++;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	mutex_unlock(&fsl_dummy_rx_lock);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return fsl_dummy_rx;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic void fsl_spi_free_dummy_rx(void)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	mutex_lock(&fsl_dummy_rx_lock);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	switch (fsl_dummy_rx_refcnt) {
2468c2ecf20Sopenharmony_ci	case 0:
2478c2ecf20Sopenharmony_ci		WARN_ON(1);
2488c2ecf20Sopenharmony_ci		break;
2498c2ecf20Sopenharmony_ci	case 1:
2508c2ecf20Sopenharmony_ci		kfree(fsl_dummy_rx);
2518c2ecf20Sopenharmony_ci		fsl_dummy_rx = NULL;
2528c2ecf20Sopenharmony_ci		fallthrough;
2538c2ecf20Sopenharmony_ci	default:
2548c2ecf20Sopenharmony_ci		fsl_dummy_rx_refcnt--;
2558c2ecf20Sopenharmony_ci		break;
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	mutex_unlock(&fsl_dummy_rx_lock);
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic unsigned long fsl_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	struct device *dev = mspi->dev;
2648c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
2658c2ecf20Sopenharmony_ci	const u32 *iprop;
2668c2ecf20Sopenharmony_ci	int size;
2678c2ecf20Sopenharmony_ci	void __iomem *spi_base;
2688c2ecf20Sopenharmony_ci	unsigned long pram_ofs = -ENOMEM;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	/* Can't use of_address_to_resource(), QE muram isn't at 0. */
2718c2ecf20Sopenharmony_ci	iprop = of_get_property(np, "reg", &size);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* QE with a fixed pram location? */
2748c2ecf20Sopenharmony_ci	if (mspi->flags & SPI_QE && iprop && size == sizeof(*iprop) * 4)
2758c2ecf20Sopenharmony_ci		return cpm_muram_alloc_fixed(iprop[2], SPI_PRAM_SIZE);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	/* QE but with a dynamic pram location? */
2788c2ecf20Sopenharmony_ci	if (mspi->flags & SPI_QE) {
2798c2ecf20Sopenharmony_ci		pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
2808c2ecf20Sopenharmony_ci		qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, mspi->subblock,
2818c2ecf20Sopenharmony_ci			     QE_CR_PROTOCOL_UNSPECIFIED, pram_ofs);
2828c2ecf20Sopenharmony_ci		return pram_ofs;
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	spi_base = of_iomap(np, 1);
2868c2ecf20Sopenharmony_ci	if (spi_base == NULL)
2878c2ecf20Sopenharmony_ci		return -EINVAL;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (mspi->flags & SPI_CPM2) {
2908c2ecf20Sopenharmony_ci		pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
2918c2ecf20Sopenharmony_ci		out_be16(spi_base, pram_ofs);
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	iounmap(spi_base);
2958c2ecf20Sopenharmony_ci	return pram_ofs;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ciint fsl_spi_cpm_init(struct mpc8xxx_spi *mspi)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	struct device *dev = mspi->dev;
3018c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
3028c2ecf20Sopenharmony_ci	const u32 *iprop;
3038c2ecf20Sopenharmony_ci	int size;
3048c2ecf20Sopenharmony_ci	unsigned long bds_ofs;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (!(mspi->flags & SPI_CPM_MODE))
3078c2ecf20Sopenharmony_ci		return 0;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (!fsl_spi_alloc_dummy_rx())
3108c2ecf20Sopenharmony_ci		return -ENOMEM;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (mspi->flags & SPI_QE) {
3138c2ecf20Sopenharmony_ci		iprop = of_get_property(np, "cell-index", &size);
3148c2ecf20Sopenharmony_ci		if (iprop && size == sizeof(*iprop))
3158c2ecf20Sopenharmony_ci			mspi->subblock = *iprop;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci		switch (mspi->subblock) {
3188c2ecf20Sopenharmony_ci		default:
3198c2ecf20Sopenharmony_ci			dev_warn(dev, "cell-index unspecified, assuming SPI1\n");
3208c2ecf20Sopenharmony_ci			fallthrough;
3218c2ecf20Sopenharmony_ci		case 0:
3228c2ecf20Sopenharmony_ci			mspi->subblock = QE_CR_SUBBLOCK_SPI1;
3238c2ecf20Sopenharmony_ci			break;
3248c2ecf20Sopenharmony_ci		case 1:
3258c2ecf20Sopenharmony_ci			mspi->subblock = QE_CR_SUBBLOCK_SPI2;
3268c2ecf20Sopenharmony_ci			break;
3278c2ecf20Sopenharmony_ci		}
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (mspi->flags & SPI_CPM1) {
3318c2ecf20Sopenharmony_ci		void *pram;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		pram = devm_platform_ioremap_resource(to_platform_device(dev),
3348c2ecf20Sopenharmony_ci						      1);
3358c2ecf20Sopenharmony_ci		if (IS_ERR(pram))
3368c2ecf20Sopenharmony_ci			mspi->pram = NULL;
3378c2ecf20Sopenharmony_ci		else
3388c2ecf20Sopenharmony_ci			mspi->pram = pram;
3398c2ecf20Sopenharmony_ci	} else {
3408c2ecf20Sopenharmony_ci		unsigned long pram_ofs = fsl_spi_cpm_get_pram(mspi);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci		if (IS_ERR_VALUE(pram_ofs))
3438c2ecf20Sopenharmony_ci			mspi->pram = NULL;
3448c2ecf20Sopenharmony_ci		else
3458c2ecf20Sopenharmony_ci			mspi->pram = cpm_muram_addr(pram_ofs);
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci	if (mspi->pram == NULL) {
3488c2ecf20Sopenharmony_ci		dev_err(dev, "can't allocate spi parameter ram\n");
3498c2ecf20Sopenharmony_ci		goto err_pram;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	bds_ofs = cpm_muram_alloc(sizeof(*mspi->tx_bd) +
3538c2ecf20Sopenharmony_ci				  sizeof(*mspi->rx_bd), 8);
3548c2ecf20Sopenharmony_ci	if (IS_ERR_VALUE(bds_ofs)) {
3558c2ecf20Sopenharmony_ci		dev_err(dev, "can't allocate bds\n");
3568c2ecf20Sopenharmony_ci		goto err_bds;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	mspi->dma_dummy_tx = dma_map_single(dev, empty_zero_page, PAGE_SIZE,
3608c2ecf20Sopenharmony_ci					    DMA_TO_DEVICE);
3618c2ecf20Sopenharmony_ci	if (dma_mapping_error(dev, mspi->dma_dummy_tx)) {
3628c2ecf20Sopenharmony_ci		dev_err(dev, "unable to map dummy tx buffer\n");
3638c2ecf20Sopenharmony_ci		goto err_dummy_tx;
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	mspi->dma_dummy_rx = dma_map_single(dev, fsl_dummy_rx, SPI_MRBLR,
3678c2ecf20Sopenharmony_ci					    DMA_FROM_DEVICE);
3688c2ecf20Sopenharmony_ci	if (dma_mapping_error(dev, mspi->dma_dummy_rx)) {
3698c2ecf20Sopenharmony_ci		dev_err(dev, "unable to map dummy rx buffer\n");
3708c2ecf20Sopenharmony_ci		goto err_dummy_rx;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	mspi->tx_bd = cpm_muram_addr(bds_ofs);
3748c2ecf20Sopenharmony_ci	mspi->rx_bd = cpm_muram_addr(bds_ofs + sizeof(*mspi->tx_bd));
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* Initialize parameter ram. */
3778c2ecf20Sopenharmony_ci	out_be16(&mspi->pram->tbase, cpm_muram_offset(mspi->tx_bd));
3788c2ecf20Sopenharmony_ci	out_be16(&mspi->pram->rbase, cpm_muram_offset(mspi->rx_bd));
3798c2ecf20Sopenharmony_ci	out_8(&mspi->pram->tfcr, CPMFCR_EB | CPMFCR_GBL);
3808c2ecf20Sopenharmony_ci	out_8(&mspi->pram->rfcr, CPMFCR_EB | CPMFCR_GBL);
3818c2ecf20Sopenharmony_ci	out_be16(&mspi->pram->mrblr, SPI_MRBLR);
3828c2ecf20Sopenharmony_ci	out_be32(&mspi->pram->rstate, 0);
3838c2ecf20Sopenharmony_ci	out_be32(&mspi->pram->rdp, 0);
3848c2ecf20Sopenharmony_ci	out_be16(&mspi->pram->rbptr, 0);
3858c2ecf20Sopenharmony_ci	out_be16(&mspi->pram->rbc, 0);
3868c2ecf20Sopenharmony_ci	out_be32(&mspi->pram->rxtmp, 0);
3878c2ecf20Sopenharmony_ci	out_be32(&mspi->pram->tstate, 0);
3888c2ecf20Sopenharmony_ci	out_be32(&mspi->pram->tdp, 0);
3898c2ecf20Sopenharmony_ci	out_be16(&mspi->pram->tbptr, 0);
3908c2ecf20Sopenharmony_ci	out_be16(&mspi->pram->tbc, 0);
3918c2ecf20Sopenharmony_ci	out_be32(&mspi->pram->txtmp, 0);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	return 0;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cierr_dummy_rx:
3968c2ecf20Sopenharmony_ci	dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
3978c2ecf20Sopenharmony_cierr_dummy_tx:
3988c2ecf20Sopenharmony_ci	cpm_muram_free(bds_ofs);
3998c2ecf20Sopenharmony_cierr_bds:
4008c2ecf20Sopenharmony_ci	if (!(mspi->flags & SPI_CPM1))
4018c2ecf20Sopenharmony_ci		cpm_muram_free(cpm_muram_offset(mspi->pram));
4028c2ecf20Sopenharmony_cierr_pram:
4038c2ecf20Sopenharmony_ci	fsl_spi_free_dummy_rx();
4048c2ecf20Sopenharmony_ci	return -ENOMEM;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_spi_cpm_init);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_civoid fsl_spi_cpm_free(struct mpc8xxx_spi *mspi)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	struct device *dev = mspi->dev;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	if (!(mspi->flags & SPI_CPM_MODE))
4138c2ecf20Sopenharmony_ci		return;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	dma_unmap_single(dev, mspi->dma_dummy_rx, SPI_MRBLR, DMA_FROM_DEVICE);
4168c2ecf20Sopenharmony_ci	dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
4178c2ecf20Sopenharmony_ci	cpm_muram_free(cpm_muram_offset(mspi->tx_bd));
4188c2ecf20Sopenharmony_ci	if (!(mspi->flags & SPI_CPM1))
4198c2ecf20Sopenharmony_ci		cpm_muram_free(cpm_muram_offset(mspi->pram));
4208c2ecf20Sopenharmony_ci	fsl_spi_free_dummy_rx();
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_spi_cpm_free);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
425