18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SPI controller driver for the Atheros AR71XX/AR724X/AR913X SoCs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This driver has been based on the spi-gpio.c: 88c2ecf20Sopenharmony_ci * Copyright (C) 2006,2008 David Brownell 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 188c2ecf20Sopenharmony_ci#include <linux/spi/spi_bitbang.h> 198c2ecf20Sopenharmony_ci#include <linux/bitops.h> 208c2ecf20Sopenharmony_ci#include <linux/clk.h> 218c2ecf20Sopenharmony_ci#include <linux/err.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_data/spi-ath79.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define DRV_NAME "ath79-spi" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define ATH79_SPI_RRW_DELAY_FACTOR 12000 278c2ecf20Sopenharmony_ci#define MHZ (1000 * 1000) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define AR71XX_SPI_REG_FS 0x00 /* Function Select */ 308c2ecf20Sopenharmony_ci#define AR71XX_SPI_REG_CTRL 0x04 /* SPI Control */ 318c2ecf20Sopenharmony_ci#define AR71XX_SPI_REG_IOC 0x08 /* SPI I/O Control */ 328c2ecf20Sopenharmony_ci#define AR71XX_SPI_REG_RDS 0x0c /* Read Data Shift */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define AR71XX_SPI_FS_GPIO BIT(0) /* Enable GPIO mode */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define AR71XX_SPI_IOC_DO BIT(0) /* Data Out pin */ 378c2ecf20Sopenharmony_ci#define AR71XX_SPI_IOC_CLK BIT(8) /* CLK pin */ 388c2ecf20Sopenharmony_ci#define AR71XX_SPI_IOC_CS(n) BIT(16 + (n)) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct ath79_spi { 418c2ecf20Sopenharmony_ci struct spi_bitbang bitbang; 428c2ecf20Sopenharmony_ci u32 ioc_base; 438c2ecf20Sopenharmony_ci u32 reg_ctrl; 448c2ecf20Sopenharmony_ci void __iomem *base; 458c2ecf20Sopenharmony_ci struct clk *clk; 468c2ecf20Sopenharmony_ci unsigned int rrw_delay; 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic inline u32 ath79_spi_rr(struct ath79_spi *sp, unsigned int reg) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci return ioread32(sp->base + reg); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic inline void ath79_spi_wr(struct ath79_spi *sp, unsigned int reg, u32 val) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci iowrite32(val, sp->base + reg); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic inline struct ath79_spi *ath79_spidev_to_sp(struct spi_device *spi) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci return spi_master_get_devdata(spi->master); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic inline void ath79_spi_delay(struct ath79_spi *sp, unsigned int nsecs) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci if (nsecs > sp->rrw_delay) 678c2ecf20Sopenharmony_ci ndelay(nsecs - sp->rrw_delay); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic void ath79_spi_chipselect(struct spi_device *spi, int is_active) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct ath79_spi *sp = ath79_spidev_to_sp(spi); 738c2ecf20Sopenharmony_ci int cs_high = (spi->mode & SPI_CS_HIGH) ? is_active : !is_active; 748c2ecf20Sopenharmony_ci u32 cs_bit = AR71XX_SPI_IOC_CS(spi->chip_select); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (cs_high) 778c2ecf20Sopenharmony_ci sp->ioc_base |= cs_bit; 788c2ecf20Sopenharmony_ci else 798c2ecf20Sopenharmony_ci sp->ioc_base &= ~cs_bit; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void ath79_spi_enable(struct ath79_spi *sp) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci /* enable GPIO mode */ 878c2ecf20Sopenharmony_ci ath79_spi_wr(sp, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* save CTRL register */ 908c2ecf20Sopenharmony_ci sp->reg_ctrl = ath79_spi_rr(sp, AR71XX_SPI_REG_CTRL); 918c2ecf20Sopenharmony_ci sp->ioc_base = ath79_spi_rr(sp, AR71XX_SPI_REG_IOC); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* clear clk and mosi in the base state */ 948c2ecf20Sopenharmony_ci sp->ioc_base &= ~(AR71XX_SPI_IOC_DO | AR71XX_SPI_IOC_CLK); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* TODO: setup speed? */ 978c2ecf20Sopenharmony_ci ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, 0x43); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic void ath79_spi_disable(struct ath79_spi *sp) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci /* restore CTRL register */ 1038c2ecf20Sopenharmony_ci ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, sp->reg_ctrl); 1048c2ecf20Sopenharmony_ci /* disable GPIO mode */ 1058c2ecf20Sopenharmony_ci ath79_spi_wr(sp, AR71XX_SPI_REG_FS, 0); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned int nsecs, 1098c2ecf20Sopenharmony_ci u32 word, u8 bits, unsigned flags) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct ath79_spi *sp = ath79_spidev_to_sp(spi); 1128c2ecf20Sopenharmony_ci u32 ioc = sp->ioc_base; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* clock starts at inactive polarity */ 1158c2ecf20Sopenharmony_ci for (word <<= (32 - bits); likely(bits); bits--) { 1168c2ecf20Sopenharmony_ci u32 out; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (word & (1 << 31)) 1198c2ecf20Sopenharmony_ci out = ioc | AR71XX_SPI_IOC_DO; 1208c2ecf20Sopenharmony_ci else 1218c2ecf20Sopenharmony_ci out = ioc & ~AR71XX_SPI_IOC_DO; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* setup MSB (to slave) on trailing edge */ 1248c2ecf20Sopenharmony_ci ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out); 1258c2ecf20Sopenharmony_ci ath79_spi_delay(sp, nsecs); 1268c2ecf20Sopenharmony_ci ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out | AR71XX_SPI_IOC_CLK); 1278c2ecf20Sopenharmony_ci ath79_spi_delay(sp, nsecs); 1288c2ecf20Sopenharmony_ci if (bits == 1) 1298c2ecf20Sopenharmony_ci ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci word <<= 1; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return ath79_spi_rr(sp, AR71XX_SPI_REG_RDS); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int ath79_spi_probe(struct platform_device *pdev) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct spi_master *master; 1408c2ecf20Sopenharmony_ci struct ath79_spi *sp; 1418c2ecf20Sopenharmony_ci struct ath79_spi_platform_data *pdata; 1428c2ecf20Sopenharmony_ci unsigned long rate; 1438c2ecf20Sopenharmony_ci int ret; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci master = spi_alloc_master(&pdev->dev, sizeof(*sp)); 1468c2ecf20Sopenharmony_ci if (master == NULL) { 1478c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate spi master\n"); 1488c2ecf20Sopenharmony_ci return -ENOMEM; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci sp = spi_master_get_devdata(master); 1528c2ecf20Sopenharmony_ci master->dev.of_node = pdev->dev.of_node; 1538c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, sp); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci master->use_gpio_descriptors = true; 1588c2ecf20Sopenharmony_ci master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); 1598c2ecf20Sopenharmony_ci master->flags = SPI_MASTER_GPIO_SS; 1608c2ecf20Sopenharmony_ci if (pdata) { 1618c2ecf20Sopenharmony_ci master->bus_num = pdata->bus_num; 1628c2ecf20Sopenharmony_ci master->num_chipselect = pdata->num_chipselect; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci sp->bitbang.master = master; 1668c2ecf20Sopenharmony_ci sp->bitbang.chipselect = ath79_spi_chipselect; 1678c2ecf20Sopenharmony_ci sp->bitbang.txrx_word[SPI_MODE_0] = ath79_spi_txrx_mode0; 1688c2ecf20Sopenharmony_ci sp->bitbang.flags = SPI_CS_HIGH; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci sp->base = devm_platform_ioremap_resource(pdev, 0); 1718c2ecf20Sopenharmony_ci if (IS_ERR(sp->base)) { 1728c2ecf20Sopenharmony_ci ret = PTR_ERR(sp->base); 1738c2ecf20Sopenharmony_ci goto err_put_master; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci sp->clk = devm_clk_get(&pdev->dev, "ahb"); 1778c2ecf20Sopenharmony_ci if (IS_ERR(sp->clk)) { 1788c2ecf20Sopenharmony_ci ret = PTR_ERR(sp->clk); 1798c2ecf20Sopenharmony_ci goto err_put_master; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci ret = clk_prepare_enable(sp->clk); 1838c2ecf20Sopenharmony_ci if (ret) 1848c2ecf20Sopenharmony_ci goto err_put_master; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci rate = DIV_ROUND_UP(clk_get_rate(sp->clk), MHZ); 1878c2ecf20Sopenharmony_ci if (!rate) { 1888c2ecf20Sopenharmony_ci ret = -EINVAL; 1898c2ecf20Sopenharmony_ci goto err_clk_disable; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci sp->rrw_delay = ATH79_SPI_RRW_DELAY_FACTOR / rate; 1938c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "register read/write delay is %u nsecs\n", 1948c2ecf20Sopenharmony_ci sp->rrw_delay); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci ath79_spi_enable(sp); 1978c2ecf20Sopenharmony_ci ret = spi_bitbang_start(&sp->bitbang); 1988c2ecf20Sopenharmony_ci if (ret) 1998c2ecf20Sopenharmony_ci goto err_disable; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cierr_disable: 2048c2ecf20Sopenharmony_ci ath79_spi_disable(sp); 2058c2ecf20Sopenharmony_cierr_clk_disable: 2068c2ecf20Sopenharmony_ci clk_disable_unprepare(sp->clk); 2078c2ecf20Sopenharmony_cierr_put_master: 2088c2ecf20Sopenharmony_ci spi_master_put(sp->bitbang.master); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int ath79_spi_remove(struct platform_device *pdev) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct ath79_spi *sp = platform_get_drvdata(pdev); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci spi_bitbang_stop(&sp->bitbang); 2188c2ecf20Sopenharmony_ci ath79_spi_disable(sp); 2198c2ecf20Sopenharmony_ci clk_disable_unprepare(sp->clk); 2208c2ecf20Sopenharmony_ci spi_master_put(sp->bitbang.master); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic void ath79_spi_shutdown(struct platform_device *pdev) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci ath79_spi_remove(pdev); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic const struct of_device_id ath79_spi_of_match[] = { 2318c2ecf20Sopenharmony_ci { .compatible = "qca,ar7100-spi", }, 2328c2ecf20Sopenharmony_ci { }, 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ath79_spi_of_match); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic struct platform_driver ath79_spi_driver = { 2378c2ecf20Sopenharmony_ci .probe = ath79_spi_probe, 2388c2ecf20Sopenharmony_ci .remove = ath79_spi_remove, 2398c2ecf20Sopenharmony_ci .shutdown = ath79_spi_shutdown, 2408c2ecf20Sopenharmony_ci .driver = { 2418c2ecf20Sopenharmony_ci .name = DRV_NAME, 2428c2ecf20Sopenharmony_ci .of_match_table = ath79_spi_of_match, 2438c2ecf20Sopenharmony_ci }, 2448c2ecf20Sopenharmony_ci}; 2458c2ecf20Sopenharmony_cimodule_platform_driver(ath79_spi_driver); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SPI controller driver for Atheros AR71XX/AR724X/AR913X"); 2488c2ecf20Sopenharmony_ciMODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); 2498c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2508c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRV_NAME); 251