18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2008-2014 STMicroelectronics Limited 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Angus Clark <Angus.Clark@st.com> 68c2ecf20Sopenharmony_ci * Patrice Chotard <patrice.chotard@st.com> 78c2ecf20Sopenharmony_ci * Lee Jones <lee.jones@linaro.org> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * SPI master mode controller driver, used in STMicroelectronics devices. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/clk.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/of.h> 208c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 218c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 228c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 238c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 248c2ecf20Sopenharmony_ci#include <linux/spi/spi_bitbang.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* SSC registers */ 278c2ecf20Sopenharmony_ci#define SSC_BRG 0x000 288c2ecf20Sopenharmony_ci#define SSC_TBUF 0x004 298c2ecf20Sopenharmony_ci#define SSC_RBUF 0x008 308c2ecf20Sopenharmony_ci#define SSC_CTL 0x00C 318c2ecf20Sopenharmony_ci#define SSC_IEN 0x010 328c2ecf20Sopenharmony_ci#define SSC_I2C 0x018 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* SSC Control */ 358c2ecf20Sopenharmony_ci#define SSC_CTL_DATA_WIDTH_9 0x8 368c2ecf20Sopenharmony_ci#define SSC_CTL_DATA_WIDTH_MSK 0xf 378c2ecf20Sopenharmony_ci#define SSC_CTL_BM 0xf 388c2ecf20Sopenharmony_ci#define SSC_CTL_HB BIT(4) 398c2ecf20Sopenharmony_ci#define SSC_CTL_PH BIT(5) 408c2ecf20Sopenharmony_ci#define SSC_CTL_PO BIT(6) 418c2ecf20Sopenharmony_ci#define SSC_CTL_SR BIT(7) 428c2ecf20Sopenharmony_ci#define SSC_CTL_MS BIT(8) 438c2ecf20Sopenharmony_ci#define SSC_CTL_EN BIT(9) 448c2ecf20Sopenharmony_ci#define SSC_CTL_LPB BIT(10) 458c2ecf20Sopenharmony_ci#define SSC_CTL_EN_TX_FIFO BIT(11) 468c2ecf20Sopenharmony_ci#define SSC_CTL_EN_RX_FIFO BIT(12) 478c2ecf20Sopenharmony_ci#define SSC_CTL_EN_CLST_RX BIT(13) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* SSC Interrupt Enable */ 508c2ecf20Sopenharmony_ci#define SSC_IEN_TEEN BIT(2) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define FIFO_SIZE 8 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct spi_st { 558c2ecf20Sopenharmony_ci /* SSC SPI Controller */ 568c2ecf20Sopenharmony_ci void __iomem *base; 578c2ecf20Sopenharmony_ci struct clk *clk; 588c2ecf20Sopenharmony_ci struct device *dev; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* SSC SPI current transaction */ 618c2ecf20Sopenharmony_ci const u8 *tx_ptr; 628c2ecf20Sopenharmony_ci u8 *rx_ptr; 638c2ecf20Sopenharmony_ci u16 bytes_per_word; 648c2ecf20Sopenharmony_ci unsigned int words_remaining; 658c2ecf20Sopenharmony_ci unsigned int baud; 668c2ecf20Sopenharmony_ci struct completion done; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* Load the TX FIFO */ 708c2ecf20Sopenharmony_cistatic void ssc_write_tx_fifo(struct spi_st *spi_st) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci unsigned int count, i; 738c2ecf20Sopenharmony_ci uint32_t word = 0; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (spi_st->words_remaining > FIFO_SIZE) 768c2ecf20Sopenharmony_ci count = FIFO_SIZE; 778c2ecf20Sopenharmony_ci else 788c2ecf20Sopenharmony_ci count = spi_st->words_remaining; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 818c2ecf20Sopenharmony_ci if (spi_st->tx_ptr) { 828c2ecf20Sopenharmony_ci if (spi_st->bytes_per_word == 1) { 838c2ecf20Sopenharmony_ci word = *spi_st->tx_ptr++; 848c2ecf20Sopenharmony_ci } else { 858c2ecf20Sopenharmony_ci word = *spi_st->tx_ptr++; 868c2ecf20Sopenharmony_ci word = *spi_st->tx_ptr++ | (word << 8); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci writel_relaxed(word, spi_st->base + SSC_TBUF); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* Read the RX FIFO */ 948c2ecf20Sopenharmony_cistatic void ssc_read_rx_fifo(struct spi_st *spi_st) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci unsigned int count, i; 978c2ecf20Sopenharmony_ci uint32_t word = 0; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (spi_st->words_remaining > FIFO_SIZE) 1008c2ecf20Sopenharmony_ci count = FIFO_SIZE; 1018c2ecf20Sopenharmony_ci else 1028c2ecf20Sopenharmony_ci count = spi_st->words_remaining; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 1058c2ecf20Sopenharmony_ci word = readl_relaxed(spi_st->base + SSC_RBUF); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (spi_st->rx_ptr) { 1088c2ecf20Sopenharmony_ci if (spi_st->bytes_per_word == 1) { 1098c2ecf20Sopenharmony_ci *spi_st->rx_ptr++ = (uint8_t)word; 1108c2ecf20Sopenharmony_ci } else { 1118c2ecf20Sopenharmony_ci *spi_st->rx_ptr++ = (word >> 8); 1128c2ecf20Sopenharmony_ci *spi_st->rx_ptr++ = word & 0xff; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci spi_st->words_remaining -= count; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int spi_st_transfer_one(struct spi_master *master, 1208c2ecf20Sopenharmony_ci struct spi_device *spi, struct spi_transfer *t) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct spi_st *spi_st = spi_master_get_devdata(master); 1238c2ecf20Sopenharmony_ci uint32_t ctl = 0; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* Setup transfer */ 1268c2ecf20Sopenharmony_ci spi_st->tx_ptr = t->tx_buf; 1278c2ecf20Sopenharmony_ci spi_st->rx_ptr = t->rx_buf; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (spi->bits_per_word > 8) { 1308c2ecf20Sopenharmony_ci /* 1318c2ecf20Sopenharmony_ci * Anything greater than 8 bits-per-word requires 2 1328c2ecf20Sopenharmony_ci * bytes-per-word in the RX/TX buffers 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci spi_st->bytes_per_word = 2; 1358c2ecf20Sopenharmony_ci spi_st->words_remaining = t->len / 2; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci } else if (spi->bits_per_word == 8 && !(t->len & 0x1)) { 1388c2ecf20Sopenharmony_ci /* 1398c2ecf20Sopenharmony_ci * If transfer is even-length, and 8 bits-per-word, then 1408c2ecf20Sopenharmony_ci * implement as half-length 16 bits-per-word transfer 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_ci spi_st->bytes_per_word = 2; 1438c2ecf20Sopenharmony_ci spi_st->words_remaining = t->len / 2; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* Set SSC_CTL to 16 bits-per-word */ 1468c2ecf20Sopenharmony_ci ctl = readl_relaxed(spi_st->base + SSC_CTL); 1478c2ecf20Sopenharmony_ci writel_relaxed((ctl | 0xf), spi_st->base + SSC_CTL); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci readl_relaxed(spi_st->base + SSC_RBUF); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci } else { 1528c2ecf20Sopenharmony_ci spi_st->bytes_per_word = 1; 1538c2ecf20Sopenharmony_ci spi_st->words_remaining = t->len; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci reinit_completion(&spi_st->done); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Start transfer by writing to the TX FIFO */ 1598c2ecf20Sopenharmony_ci ssc_write_tx_fifo(spi_st); 1608c2ecf20Sopenharmony_ci writel_relaxed(SSC_IEN_TEEN, spi_st->base + SSC_IEN); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* Wait for transfer to complete */ 1638c2ecf20Sopenharmony_ci wait_for_completion(&spi_st->done); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Restore SSC_CTL if necessary */ 1668c2ecf20Sopenharmony_ci if (ctl) 1678c2ecf20Sopenharmony_ci writel_relaxed(ctl, spi_st->base + SSC_CTL); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci spi_finalize_current_transfer(spi->master); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return t->len; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic void spi_st_cleanup(struct spi_device *spi) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci gpio_free(spi->cs_gpio); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* the spi->mode bits understood by this driver: */ 1808c2ecf20Sopenharmony_ci#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_LOOP | SPI_CS_HIGH) 1818c2ecf20Sopenharmony_cistatic int spi_st_setup(struct spi_device *spi) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct spi_st *spi_st = spi_master_get_devdata(spi->master); 1848c2ecf20Sopenharmony_ci u32 spi_st_clk, sscbrg, var; 1858c2ecf20Sopenharmony_ci u32 hz = spi->max_speed_hz; 1868c2ecf20Sopenharmony_ci int cs = spi->cs_gpio; 1878c2ecf20Sopenharmony_ci int ret; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (!hz) { 1908c2ecf20Sopenharmony_ci dev_err(&spi->dev, "max_speed_hz unspecified\n"); 1918c2ecf20Sopenharmony_ci return -EINVAL; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (!gpio_is_valid(cs)) { 1958c2ecf20Sopenharmony_ci dev_err(&spi->dev, "%d is not a valid gpio\n", cs); 1968c2ecf20Sopenharmony_ci return -EINVAL; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci ret = gpio_request(cs, dev_name(&spi->dev)); 2008c2ecf20Sopenharmony_ci if (ret) { 2018c2ecf20Sopenharmony_ci dev_err(&spi->dev, "could not request gpio:%d\n", cs); 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ret = gpio_direction_output(cs, spi->mode & SPI_CS_HIGH); 2068c2ecf20Sopenharmony_ci if (ret) 2078c2ecf20Sopenharmony_ci goto out_free_gpio; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci spi_st_clk = clk_get_rate(spi_st->clk); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* Set SSC_BRF */ 2128c2ecf20Sopenharmony_ci sscbrg = spi_st_clk / (2 * hz); 2138c2ecf20Sopenharmony_ci if (sscbrg < 0x07 || sscbrg > BIT(16)) { 2148c2ecf20Sopenharmony_ci dev_err(&spi->dev, 2158c2ecf20Sopenharmony_ci "baudrate %d outside valid range %d\n", sscbrg, hz); 2168c2ecf20Sopenharmony_ci ret = -EINVAL; 2178c2ecf20Sopenharmony_ci goto out_free_gpio; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci spi_st->baud = spi_st_clk / (2 * sscbrg); 2218c2ecf20Sopenharmony_ci if (sscbrg == BIT(16)) /* 16-bit counter wraps */ 2228c2ecf20Sopenharmony_ci sscbrg = 0x0; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci writel_relaxed(sscbrg, spi_st->base + SSC_BRG); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci dev_dbg(&spi->dev, 2278c2ecf20Sopenharmony_ci "setting baudrate:target= %u hz, actual= %u hz, sscbrg= %u\n", 2288c2ecf20Sopenharmony_ci hz, spi_st->baud, sscbrg); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* Set SSC_CTL and enable SSC */ 2318c2ecf20Sopenharmony_ci var = readl_relaxed(spi_st->base + SSC_CTL); 2328c2ecf20Sopenharmony_ci var |= SSC_CTL_MS; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (spi->mode & SPI_CPOL) 2358c2ecf20Sopenharmony_ci var |= SSC_CTL_PO; 2368c2ecf20Sopenharmony_ci else 2378c2ecf20Sopenharmony_ci var &= ~SSC_CTL_PO; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (spi->mode & SPI_CPHA) 2408c2ecf20Sopenharmony_ci var |= SSC_CTL_PH; 2418c2ecf20Sopenharmony_ci else 2428c2ecf20Sopenharmony_ci var &= ~SSC_CTL_PH; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if ((spi->mode & SPI_LSB_FIRST) == 0) 2458c2ecf20Sopenharmony_ci var |= SSC_CTL_HB; 2468c2ecf20Sopenharmony_ci else 2478c2ecf20Sopenharmony_ci var &= ~SSC_CTL_HB; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (spi->mode & SPI_LOOP) 2508c2ecf20Sopenharmony_ci var |= SSC_CTL_LPB; 2518c2ecf20Sopenharmony_ci else 2528c2ecf20Sopenharmony_ci var &= ~SSC_CTL_LPB; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci var &= ~SSC_CTL_DATA_WIDTH_MSK; 2558c2ecf20Sopenharmony_ci var |= (spi->bits_per_word - 1); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci var |= SSC_CTL_EN_TX_FIFO | SSC_CTL_EN_RX_FIFO; 2588c2ecf20Sopenharmony_ci var |= SSC_CTL_EN; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci writel_relaxed(var, spi_st->base + SSC_CTL); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* Clear the status register */ 2638c2ecf20Sopenharmony_ci readl_relaxed(spi_st->base + SSC_RBUF); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ciout_free_gpio: 2688c2ecf20Sopenharmony_ci gpio_free(cs); 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* Interrupt fired when TX shift register becomes empty */ 2738c2ecf20Sopenharmony_cistatic irqreturn_t spi_st_irq(int irq, void *dev_id) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct spi_st *spi_st = (struct spi_st *)dev_id; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* Read RX FIFO */ 2788c2ecf20Sopenharmony_ci ssc_read_rx_fifo(spi_st); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* Fill TX FIFO */ 2818c2ecf20Sopenharmony_ci if (spi_st->words_remaining) { 2828c2ecf20Sopenharmony_ci ssc_write_tx_fifo(spi_st); 2838c2ecf20Sopenharmony_ci } else { 2848c2ecf20Sopenharmony_ci /* TX/RX complete */ 2858c2ecf20Sopenharmony_ci writel_relaxed(0x0, spi_st->base + SSC_IEN); 2868c2ecf20Sopenharmony_ci /* 2878c2ecf20Sopenharmony_ci * read SSC_IEN to ensure that this bit is set 2888c2ecf20Sopenharmony_ci * before re-enabling interrupt 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_ci readl(spi_st->base + SSC_IEN); 2918c2ecf20Sopenharmony_ci complete(&spi_st->done); 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int spi_st_probe(struct platform_device *pdev) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 3008c2ecf20Sopenharmony_ci struct spi_master *master; 3018c2ecf20Sopenharmony_ci struct spi_st *spi_st; 3028c2ecf20Sopenharmony_ci int irq, ret = 0; 3038c2ecf20Sopenharmony_ci u32 var; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci master = spi_alloc_master(&pdev->dev, sizeof(*spi_st)); 3068c2ecf20Sopenharmony_ci if (!master) 3078c2ecf20Sopenharmony_ci return -ENOMEM; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci master->dev.of_node = np; 3108c2ecf20Sopenharmony_ci master->mode_bits = MODEBITS; 3118c2ecf20Sopenharmony_ci master->setup = spi_st_setup; 3128c2ecf20Sopenharmony_ci master->cleanup = spi_st_cleanup; 3138c2ecf20Sopenharmony_ci master->transfer_one = spi_st_transfer_one; 3148c2ecf20Sopenharmony_ci master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); 3158c2ecf20Sopenharmony_ci master->auto_runtime_pm = true; 3168c2ecf20Sopenharmony_ci master->bus_num = pdev->id; 3178c2ecf20Sopenharmony_ci spi_st = spi_master_get_devdata(master); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci spi_st->clk = devm_clk_get(&pdev->dev, "ssc"); 3208c2ecf20Sopenharmony_ci if (IS_ERR(spi_st->clk)) { 3218c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to request clock\n"); 3228c2ecf20Sopenharmony_ci ret = PTR_ERR(spi_st->clk); 3238c2ecf20Sopenharmony_ci goto put_master; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci ret = clk_prepare_enable(spi_st->clk); 3278c2ecf20Sopenharmony_ci if (ret) 3288c2ecf20Sopenharmony_ci goto put_master; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci init_completion(&spi_st->done); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* Get resources */ 3338c2ecf20Sopenharmony_ci spi_st->base = devm_platform_ioremap_resource(pdev, 0); 3348c2ecf20Sopenharmony_ci if (IS_ERR(spi_st->base)) { 3358c2ecf20Sopenharmony_ci ret = PTR_ERR(spi_st->base); 3368c2ecf20Sopenharmony_ci goto clk_disable; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* Disable I2C and Reset SSC */ 3408c2ecf20Sopenharmony_ci writel_relaxed(0x0, spi_st->base + SSC_I2C); 3418c2ecf20Sopenharmony_ci var = readw_relaxed(spi_st->base + SSC_CTL); 3428c2ecf20Sopenharmony_ci var |= SSC_CTL_SR; 3438c2ecf20Sopenharmony_ci writel_relaxed(var, spi_st->base + SSC_CTL); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci udelay(1); 3468c2ecf20Sopenharmony_ci var = readl_relaxed(spi_st->base + SSC_CTL); 3478c2ecf20Sopenharmony_ci var &= ~SSC_CTL_SR; 3488c2ecf20Sopenharmony_ci writel_relaxed(var, spi_st->base + SSC_CTL); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* Set SSC into slave mode before reconfiguring PIO pins */ 3518c2ecf20Sopenharmony_ci var = readl_relaxed(spi_st->base + SSC_CTL); 3528c2ecf20Sopenharmony_ci var &= ~SSC_CTL_MS; 3538c2ecf20Sopenharmony_ci writel_relaxed(var, spi_st->base + SSC_CTL); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(np, 0); 3568c2ecf20Sopenharmony_ci if (!irq) { 3578c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "IRQ missing or invalid\n"); 3588c2ecf20Sopenharmony_ci ret = -EINVAL; 3598c2ecf20Sopenharmony_ci goto clk_disable; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, spi_st_irq, 0, 3638c2ecf20Sopenharmony_ci pdev->name, spi_st); 3648c2ecf20Sopenharmony_ci if (ret) { 3658c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to request irq %d\n", irq); 3668c2ecf20Sopenharmony_ci goto clk_disable; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* by default the device is on */ 3708c2ecf20Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 3718c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, master); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci ret = devm_spi_register_master(&pdev->dev, master); 3768c2ecf20Sopenharmony_ci if (ret) { 3778c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to register master\n"); 3788c2ecf20Sopenharmony_ci goto rpm_disable; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cirpm_disable: 3848c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 3858c2ecf20Sopenharmony_ciclk_disable: 3868c2ecf20Sopenharmony_ci clk_disable_unprepare(spi_st->clk); 3878c2ecf20Sopenharmony_ciput_master: 3888c2ecf20Sopenharmony_ci spi_master_put(master); 3898c2ecf20Sopenharmony_ci return ret; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int spi_st_remove(struct platform_device *pdev) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct spi_master *master = platform_get_drvdata(pdev); 3958c2ecf20Sopenharmony_ci struct spi_st *spi_st = spi_master_get_devdata(master); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci clk_disable_unprepare(spi_st->clk); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci pinctrl_pm_select_sleep_state(&pdev->dev); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4078c2ecf20Sopenharmony_cistatic int spi_st_runtime_suspend(struct device *dev) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 4108c2ecf20Sopenharmony_ci struct spi_st *spi_st = spi_master_get_devdata(master); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci writel_relaxed(0, spi_st->base + SSC_IEN); 4138c2ecf20Sopenharmony_ci pinctrl_pm_select_sleep_state(dev); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci clk_disable_unprepare(spi_st->clk); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return 0; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int spi_st_runtime_resume(struct device *dev) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 4238c2ecf20Sopenharmony_ci struct spi_st *spi_st = spi_master_get_devdata(master); 4248c2ecf20Sopenharmony_ci int ret; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci ret = clk_prepare_enable(spi_st->clk); 4278c2ecf20Sopenharmony_ci pinctrl_pm_select_default_state(dev); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return ret; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci#endif 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4348c2ecf20Sopenharmony_cistatic int spi_st_suspend(struct device *dev) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 4378c2ecf20Sopenharmony_ci int ret; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci ret = spi_master_suspend(master); 4408c2ecf20Sopenharmony_ci if (ret) 4418c2ecf20Sopenharmony_ci return ret; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return pm_runtime_force_suspend(dev); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic int spi_st_resume(struct device *dev) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct spi_master *master = dev_get_drvdata(dev); 4498c2ecf20Sopenharmony_ci int ret; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci ret = spi_master_resume(master); 4528c2ecf20Sopenharmony_ci if (ret) 4538c2ecf20Sopenharmony_ci return ret; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci return pm_runtime_force_resume(dev); 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci#endif 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic const struct dev_pm_ops spi_st_pm = { 4608c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(spi_st_suspend, spi_st_resume) 4618c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(spi_st_runtime_suspend, spi_st_runtime_resume, NULL) 4628c2ecf20Sopenharmony_ci}; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic const struct of_device_id stm_spi_match[] = { 4658c2ecf20Sopenharmony_ci { .compatible = "st,comms-ssc4-spi", }, 4668c2ecf20Sopenharmony_ci {}, 4678c2ecf20Sopenharmony_ci}; 4688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, stm_spi_match); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic struct platform_driver spi_st_driver = { 4718c2ecf20Sopenharmony_ci .driver = { 4728c2ecf20Sopenharmony_ci .name = "spi-st", 4738c2ecf20Sopenharmony_ci .pm = &spi_st_pm, 4748c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(stm_spi_match), 4758c2ecf20Sopenharmony_ci }, 4768c2ecf20Sopenharmony_ci .probe = spi_st_probe, 4778c2ecf20Sopenharmony_ci .remove = spi_st_remove, 4788c2ecf20Sopenharmony_ci}; 4798c2ecf20Sopenharmony_cimodule_platform_driver(spi_st_driver); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ciMODULE_AUTHOR("Patrice Chotard <patrice.chotard@st.com>"); 4828c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("STM SSC SPI driver"); 4838c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 484