| // SPDX-License-Identifier: GPL-2.0-only | 
 | /* | 
 |  *  Copyright (c) 2008-2014 STMicroelectronics Limited | 
 |  * | 
 |  *  Author: Angus Clark <Angus.Clark@st.com> | 
 |  *          Patrice Chotard <patrice.chotard@st.com> | 
 |  *          Lee Jones <lee.jones@linaro.org> | 
 |  * | 
 |  *  SPI host mode controller driver, used in STMicroelectronics devices. | 
 |  */ | 
 |  | 
 | #include <linux/clk.h> | 
 | #include <linux/delay.h> | 
 | #include <linux/interrupt.h> | 
 | #include <linux/io.h> | 
 | #include <linux/module.h> | 
 | #include <linux/pinctrl/consumer.h> | 
 | #include <linux/platform_device.h> | 
 | #include <linux/of.h> | 
 | #include <linux/of_irq.h> | 
 | #include <linux/pm_runtime.h> | 
 | #include <linux/spi/spi.h> | 
 | #include <linux/spi/spi_bitbang.h> | 
 |  | 
 | /* SSC registers */ | 
 | #define SSC_BRG				0x000 | 
 | #define SSC_TBUF			0x004 | 
 | #define SSC_RBUF			0x008 | 
 | #define SSC_CTL				0x00C | 
 | #define SSC_IEN				0x010 | 
 | #define SSC_I2C				0x018 | 
 |  | 
 | /* SSC Control */ | 
 | #define SSC_CTL_DATA_WIDTH_9		0x8 | 
 | #define SSC_CTL_DATA_WIDTH_MSK		0xf | 
 | #define SSC_CTL_BM			0xf | 
 | #define SSC_CTL_HB			BIT(4) | 
 | #define SSC_CTL_PH			BIT(5) | 
 | #define SSC_CTL_PO			BIT(6) | 
 | #define SSC_CTL_SR			BIT(7) | 
 | #define SSC_CTL_MS			BIT(8) | 
 | #define SSC_CTL_EN			BIT(9) | 
 | #define SSC_CTL_LPB			BIT(10) | 
 | #define SSC_CTL_EN_TX_FIFO		BIT(11) | 
 | #define SSC_CTL_EN_RX_FIFO		BIT(12) | 
 | #define SSC_CTL_EN_CLST_RX		BIT(13) | 
 |  | 
 | /* SSC Interrupt Enable */ | 
 | #define SSC_IEN_TEEN			BIT(2) | 
 |  | 
 | #define FIFO_SIZE			8 | 
 |  | 
 | struct spi_st { | 
 | 	/* SSC SPI Controller */ | 
 | 	void __iomem		*base; | 
 | 	struct clk		*clk; | 
 | 	struct device		*dev; | 
 |  | 
 | 	/* SSC SPI current transaction */ | 
 | 	const u8		*tx_ptr; | 
 | 	u8			*rx_ptr; | 
 | 	u16			bytes_per_word; | 
 | 	unsigned int		words_remaining; | 
 | 	unsigned int		baud; | 
 | 	struct completion	done; | 
 | }; | 
 |  | 
 | /* Load the TX FIFO */ | 
 | static void ssc_write_tx_fifo(struct spi_st *spi_st) | 
 | { | 
 | 	unsigned int count, i; | 
 | 	uint32_t word = 0; | 
 |  | 
 | 	if (spi_st->words_remaining > FIFO_SIZE) | 
 | 		count = FIFO_SIZE; | 
 | 	else | 
 | 		count = spi_st->words_remaining; | 
 |  | 
 | 	for (i = 0; i < count; i++) { | 
 | 		if (spi_st->tx_ptr) { | 
 | 			if (spi_st->bytes_per_word == 1) { | 
 | 				word = *spi_st->tx_ptr++; | 
 | 			} else { | 
 | 				word = *spi_st->tx_ptr++; | 
 | 				word = *spi_st->tx_ptr++ | (word << 8); | 
 | 			} | 
 | 		} | 
 | 		writel_relaxed(word, spi_st->base + SSC_TBUF); | 
 | 	} | 
 | } | 
 |  | 
 | /* Read the RX FIFO */ | 
 | static void ssc_read_rx_fifo(struct spi_st *spi_st) | 
 | { | 
 | 	unsigned int count, i; | 
 | 	uint32_t word = 0; | 
 |  | 
 | 	if (spi_st->words_remaining > FIFO_SIZE) | 
 | 		count = FIFO_SIZE; | 
 | 	else | 
 | 		count = spi_st->words_remaining; | 
 |  | 
 | 	for (i = 0; i < count; i++) { | 
 | 		word = readl_relaxed(spi_st->base + SSC_RBUF); | 
 |  | 
 | 		if (spi_st->rx_ptr) { | 
 | 			if (spi_st->bytes_per_word == 1) { | 
 | 				*spi_st->rx_ptr++ = (uint8_t)word; | 
 | 			} else { | 
 | 				*spi_st->rx_ptr++ = (word >> 8); | 
 | 				*spi_st->rx_ptr++ = word & 0xff; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	spi_st->words_remaining -= count; | 
 | } | 
 |  | 
 | static int spi_st_transfer_one(struct spi_controller *host, | 
 | 			       struct spi_device *spi, struct spi_transfer *t) | 
 | { | 
 | 	struct spi_st *spi_st = spi_controller_get_devdata(host); | 
 | 	uint32_t ctl = 0; | 
 |  | 
 | 	/* Setup transfer */ | 
 | 	spi_st->tx_ptr = t->tx_buf; | 
 | 	spi_st->rx_ptr = t->rx_buf; | 
 |  | 
 | 	if (spi->bits_per_word > 8) { | 
 | 		/* | 
 | 		 * Anything greater than 8 bits-per-word requires 2 | 
 | 		 * bytes-per-word in the RX/TX buffers | 
 | 		 */ | 
 | 		spi_st->bytes_per_word = 2; | 
 | 		spi_st->words_remaining = t->len / 2; | 
 |  | 
 | 	} else if (spi->bits_per_word == 8 && !(t->len & 0x1)) { | 
 | 		/* | 
 | 		 * If transfer is even-length, and 8 bits-per-word, then | 
 | 		 * implement as half-length 16 bits-per-word transfer | 
 | 		 */ | 
 | 		spi_st->bytes_per_word = 2; | 
 | 		spi_st->words_remaining = t->len / 2; | 
 |  | 
 | 		/* Set SSC_CTL to 16 bits-per-word */ | 
 | 		ctl = readl_relaxed(spi_st->base + SSC_CTL); | 
 | 		writel_relaxed((ctl | 0xf), spi_st->base + SSC_CTL); | 
 |  | 
 | 		readl_relaxed(spi_st->base + SSC_RBUF); | 
 |  | 
 | 	} else { | 
 | 		spi_st->bytes_per_word = 1; | 
 | 		spi_st->words_remaining = t->len; | 
 | 	} | 
 |  | 
 | 	reinit_completion(&spi_st->done); | 
 |  | 
 | 	/* Start transfer by writing to the TX FIFO */ | 
 | 	ssc_write_tx_fifo(spi_st); | 
 | 	writel_relaxed(SSC_IEN_TEEN, spi_st->base + SSC_IEN); | 
 |  | 
 | 	/* Wait for transfer to complete */ | 
 | 	wait_for_completion(&spi_st->done); | 
 |  | 
 | 	/* Restore SSC_CTL if necessary */ | 
 | 	if (ctl) | 
 | 		writel_relaxed(ctl, spi_st->base + SSC_CTL); | 
 |  | 
 | 	spi_finalize_current_transfer(spi->controller); | 
 |  | 
 | 	return t->len; | 
 | } | 
 |  | 
 | /* the spi->mode bits understood by this driver: */ | 
 | #define MODEBITS  (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_LOOP | SPI_CS_HIGH) | 
 | static int spi_st_setup(struct spi_device *spi) | 
 | { | 
 | 	struct spi_st *spi_st = spi_controller_get_devdata(spi->controller); | 
 | 	u32 spi_st_clk, sscbrg, var; | 
 | 	u32 hz = spi->max_speed_hz; | 
 |  | 
 | 	if (!hz)  { | 
 | 		dev_err(&spi->dev, "max_speed_hz unspecified\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	if (!spi_get_csgpiod(spi, 0)) { | 
 | 		dev_err(&spi->dev, "no valid gpio assigned\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	spi_st_clk = clk_get_rate(spi_st->clk); | 
 |  | 
 | 	/* Set SSC_BRF */ | 
 | 	sscbrg = spi_st_clk / (2 * hz); | 
 | 	if (sscbrg < 0x07 || sscbrg > BIT(16)) { | 
 | 		dev_err(&spi->dev, | 
 | 			"baudrate %d outside valid range %d\n", sscbrg, hz); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	spi_st->baud = spi_st_clk / (2 * sscbrg); | 
 | 	if (sscbrg == BIT(16)) /* 16-bit counter wraps */ | 
 | 		sscbrg = 0x0; | 
 |  | 
 | 	writel_relaxed(sscbrg, spi_st->base + SSC_BRG); | 
 |  | 
 | 	dev_dbg(&spi->dev, | 
 | 		"setting baudrate:target= %u hz, actual= %u hz, sscbrg= %u\n", | 
 | 		hz, spi_st->baud, sscbrg); | 
 |  | 
 | 	/* Set SSC_CTL and enable SSC */ | 
 | 	var = readl_relaxed(spi_st->base + SSC_CTL); | 
 | 	var |= SSC_CTL_MS; | 
 |  | 
 | 	if (spi->mode & SPI_CPOL) | 
 | 		var |= SSC_CTL_PO; | 
 | 	else | 
 | 		var &= ~SSC_CTL_PO; | 
 |  | 
 | 	if (spi->mode & SPI_CPHA) | 
 | 		var |= SSC_CTL_PH; | 
 | 	else | 
 | 		var &= ~SSC_CTL_PH; | 
 |  | 
 | 	if ((spi->mode & SPI_LSB_FIRST) == 0) | 
 | 		var |= SSC_CTL_HB; | 
 | 	else | 
 | 		var &= ~SSC_CTL_HB; | 
 |  | 
 | 	if (spi->mode & SPI_LOOP) | 
 | 		var |= SSC_CTL_LPB; | 
 | 	else | 
 | 		var &= ~SSC_CTL_LPB; | 
 |  | 
 | 	var &= ~SSC_CTL_DATA_WIDTH_MSK; | 
 | 	var |= (spi->bits_per_word - 1); | 
 |  | 
 | 	var |= SSC_CTL_EN_TX_FIFO | SSC_CTL_EN_RX_FIFO; | 
 | 	var |= SSC_CTL_EN; | 
 |  | 
 | 	writel_relaxed(var, spi_st->base + SSC_CTL); | 
 |  | 
 | 	/* Clear the status register */ | 
 | 	readl_relaxed(spi_st->base + SSC_RBUF); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* Interrupt fired when TX shift register becomes empty */ | 
 | static irqreturn_t spi_st_irq(int irq, void *dev_id) | 
 | { | 
 | 	struct spi_st *spi_st = (struct spi_st *)dev_id; | 
 |  | 
 | 	/* Read RX FIFO */ | 
 | 	ssc_read_rx_fifo(spi_st); | 
 |  | 
 | 	/* Fill TX FIFO */ | 
 | 	if (spi_st->words_remaining) { | 
 | 		ssc_write_tx_fifo(spi_st); | 
 | 	} else { | 
 | 		/* TX/RX complete */ | 
 | 		writel_relaxed(0x0, spi_st->base + SSC_IEN); | 
 | 		/* | 
 | 		 * read SSC_IEN to ensure that this bit is set | 
 | 		 * before re-enabling interrupt | 
 | 		 */ | 
 | 		readl(spi_st->base + SSC_IEN); | 
 | 		complete(&spi_st->done); | 
 | 	} | 
 |  | 
 | 	return IRQ_HANDLED; | 
 | } | 
 |  | 
 | static int spi_st_probe(struct platform_device *pdev) | 
 | { | 
 | 	struct device_node *np = pdev->dev.of_node; | 
 | 	struct spi_controller *host; | 
 | 	struct spi_st *spi_st; | 
 | 	int irq, ret = 0; | 
 | 	u32 var; | 
 |  | 
 | 	host = spi_alloc_host(&pdev->dev, sizeof(*spi_st)); | 
 | 	if (!host) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	host->dev.of_node		= np; | 
 | 	host->mode_bits			= MODEBITS; | 
 | 	host->setup			= spi_st_setup; | 
 | 	host->transfer_one		= spi_st_transfer_one; | 
 | 	host->bits_per_word_mask	= SPI_BPW_MASK(8) | SPI_BPW_MASK(16); | 
 | 	host->auto_runtime_pm		= true; | 
 | 	host->bus_num			= pdev->id; | 
 | 	host->use_gpio_descriptors	= true; | 
 | 	spi_st				= spi_controller_get_devdata(host); | 
 |  | 
 | 	spi_st->clk = devm_clk_get(&pdev->dev, "ssc"); | 
 | 	if (IS_ERR(spi_st->clk)) { | 
 | 		dev_err(&pdev->dev, "Unable to request clock\n"); | 
 | 		ret = PTR_ERR(spi_st->clk); | 
 | 		goto put_host; | 
 | 	} | 
 |  | 
 | 	ret = clk_prepare_enable(spi_st->clk); | 
 | 	if (ret) | 
 | 		goto put_host; | 
 |  | 
 | 	init_completion(&spi_st->done); | 
 |  | 
 | 	/* Get resources */ | 
 | 	spi_st->base = devm_platform_ioremap_resource(pdev, 0); | 
 | 	if (IS_ERR(spi_st->base)) { | 
 | 		ret = PTR_ERR(spi_st->base); | 
 | 		goto clk_disable; | 
 | 	} | 
 |  | 
 | 	/* Disable I2C and Reset SSC */ | 
 | 	writel_relaxed(0x0, spi_st->base + SSC_I2C); | 
 | 	var = readw_relaxed(spi_st->base + SSC_CTL); | 
 | 	var |= SSC_CTL_SR; | 
 | 	writel_relaxed(var, spi_st->base + SSC_CTL); | 
 |  | 
 | 	udelay(1); | 
 | 	var = readl_relaxed(spi_st->base + SSC_CTL); | 
 | 	var &= ~SSC_CTL_SR; | 
 | 	writel_relaxed(var, spi_st->base + SSC_CTL); | 
 |  | 
 | 	/* Set SSC into target mode before reconfiguring PIO pins */ | 
 | 	var = readl_relaxed(spi_st->base + SSC_CTL); | 
 | 	var &= ~SSC_CTL_MS; | 
 | 	writel_relaxed(var, spi_st->base + SSC_CTL); | 
 |  | 
 | 	irq = irq_of_parse_and_map(np, 0); | 
 | 	if (!irq) { | 
 | 		dev_err(&pdev->dev, "IRQ missing or invalid\n"); | 
 | 		ret = -EINVAL; | 
 | 		goto clk_disable; | 
 | 	} | 
 |  | 
 | 	ret = devm_request_irq(&pdev->dev, irq, spi_st_irq, 0, | 
 | 			       pdev->name, spi_st); | 
 | 	if (ret) { | 
 | 		dev_err(&pdev->dev, "Failed to request irq %d\n", irq); | 
 | 		goto clk_disable; | 
 | 	} | 
 |  | 
 | 	/* by default the device is on */ | 
 | 	pm_runtime_set_active(&pdev->dev); | 
 | 	pm_runtime_enable(&pdev->dev); | 
 |  | 
 | 	platform_set_drvdata(pdev, host); | 
 |  | 
 | 	ret = devm_spi_register_controller(&pdev->dev, host); | 
 | 	if (ret) { | 
 | 		dev_err(&pdev->dev, "Failed to register host\n"); | 
 | 		goto rpm_disable; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 |  | 
 | rpm_disable: | 
 | 	pm_runtime_disable(&pdev->dev); | 
 | clk_disable: | 
 | 	clk_disable_unprepare(spi_st->clk); | 
 | put_host: | 
 | 	spi_controller_put(host); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static void spi_st_remove(struct platform_device *pdev) | 
 | { | 
 | 	struct spi_controller *host = platform_get_drvdata(pdev); | 
 | 	struct spi_st *spi_st = spi_controller_get_devdata(host); | 
 |  | 
 | 	pm_runtime_disable(&pdev->dev); | 
 |  | 
 | 	clk_disable_unprepare(spi_st->clk); | 
 |  | 
 | 	pinctrl_pm_select_sleep_state(&pdev->dev); | 
 | } | 
 |  | 
 | static int __maybe_unused spi_st_runtime_suspend(struct device *dev) | 
 | { | 
 | 	struct spi_controller *host = dev_get_drvdata(dev); | 
 | 	struct spi_st *spi_st = spi_controller_get_devdata(host); | 
 |  | 
 | 	writel_relaxed(0, spi_st->base + SSC_IEN); | 
 | 	pinctrl_pm_select_sleep_state(dev); | 
 |  | 
 | 	clk_disable_unprepare(spi_st->clk); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int __maybe_unused spi_st_runtime_resume(struct device *dev) | 
 | { | 
 | 	struct spi_controller *host = dev_get_drvdata(dev); | 
 | 	struct spi_st *spi_st = spi_controller_get_devdata(host); | 
 | 	int ret; | 
 |  | 
 | 	ret = clk_prepare_enable(spi_st->clk); | 
 | 	pinctrl_pm_select_default_state(dev); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int __maybe_unused spi_st_suspend(struct device *dev) | 
 | { | 
 | 	struct spi_controller *host = dev_get_drvdata(dev); | 
 | 	int ret; | 
 |  | 
 | 	ret = spi_controller_suspend(host); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	return pm_runtime_force_suspend(dev); | 
 | } | 
 |  | 
 | static int __maybe_unused spi_st_resume(struct device *dev) | 
 | { | 
 | 	struct spi_controller *host = dev_get_drvdata(dev); | 
 | 	int ret; | 
 |  | 
 | 	ret = spi_controller_resume(host); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	return pm_runtime_force_resume(dev); | 
 | } | 
 |  | 
 | static const struct dev_pm_ops spi_st_pm = { | 
 | 	SET_SYSTEM_SLEEP_PM_OPS(spi_st_suspend, spi_st_resume) | 
 | 	SET_RUNTIME_PM_OPS(spi_st_runtime_suspend, spi_st_runtime_resume, NULL) | 
 | }; | 
 |  | 
 | static const struct of_device_id stm_spi_match[] = { | 
 | 	{ .compatible = "st,comms-ssc4-spi", }, | 
 | 	{}, | 
 | }; | 
 | MODULE_DEVICE_TABLE(of, stm_spi_match); | 
 |  | 
 | static struct platform_driver spi_st_driver = { | 
 | 	.driver = { | 
 | 		.name = "spi-st", | 
 | 		.pm = pm_sleep_ptr(&spi_st_pm), | 
 | 		.of_match_table = of_match_ptr(stm_spi_match), | 
 | 	}, | 
 | 	.probe = spi_st_probe, | 
 | 	.remove = spi_st_remove, | 
 | }; | 
 | module_platform_driver(spi_st_driver); | 
 |  | 
 | MODULE_AUTHOR("Patrice Chotard <patrice.chotard@st.com>"); | 
 | MODULE_DESCRIPTION("STM SSC SPI driver"); | 
 | MODULE_LICENSE("GPL v2"); |