162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SPI controller driver for the Mikrotik RB4xx boards 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org> 662306a36Sopenharmony_ci * Copyright (C) 2015 Bert Vermeulen <bert@biot.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This file was based on the patches for Linux 2.6.27.39 published by 962306a36Sopenharmony_ci * MikroTik for their RouterBoard 4xx series devices. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/clk.h> 1662306a36Sopenharmony_ci#include <linux/spi/spi.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <asm/mach-ath79/ar71xx_regs.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct rb4xx_spi { 2262306a36Sopenharmony_ci void __iomem *base; 2362306a36Sopenharmony_ci struct clk *clk; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic inline u32 rb4xx_read(struct rb4xx_spi *rbspi, u32 reg) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci return __raw_readl(rbspi->base + reg); 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic inline void rb4xx_write(struct rb4xx_spi *rbspi, u32 reg, u32 value) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci __raw_writel(value, rbspi->base + reg); 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic inline void do_spi_clk(struct rb4xx_spi *rbspi, u32 spi_ioc, int value) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci u32 regval; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci regval = spi_ioc; 4162306a36Sopenharmony_ci if (value & BIT(0)) 4262306a36Sopenharmony_ci regval |= AR71XX_SPI_IOC_DO; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval); 4562306a36Sopenharmony_ci rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void do_spi_byte(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci int i; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci for (i = 7; i >= 0; i--) 5362306a36Sopenharmony_ci do_spi_clk(rbspi, spi_ioc, byte >> i); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* The CS2 pin is used to clock in a second bit per clock cycle. */ 5762306a36Sopenharmony_cistatic inline void do_spi_clk_two(struct rb4xx_spi *rbspi, u32 spi_ioc, 5862306a36Sopenharmony_ci u8 value) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci u32 regval; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci regval = spi_ioc; 6362306a36Sopenharmony_ci if (value & BIT(1)) 6462306a36Sopenharmony_ci regval |= AR71XX_SPI_IOC_DO; 6562306a36Sopenharmony_ci if (value & BIT(0)) 6662306a36Sopenharmony_ci regval |= AR71XX_SPI_IOC_CS2; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval); 6962306a36Sopenharmony_ci rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* Two bits at a time, msb first */ 7362306a36Sopenharmony_cistatic void do_spi_byte_two(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci do_spi_clk_two(rbspi, spi_ioc, byte >> 6); 7662306a36Sopenharmony_ci do_spi_clk_two(rbspi, spi_ioc, byte >> 4); 7762306a36Sopenharmony_ci do_spi_clk_two(rbspi, spi_ioc, byte >> 2); 7862306a36Sopenharmony_ci do_spi_clk_two(rbspi, spi_ioc, byte >> 0); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void rb4xx_set_cs(struct spi_device *spi, bool enable) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct rb4xx_spi *rbspi = spi_controller_get_devdata(spi->controller); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* 8662306a36Sopenharmony_ci * Setting CS is done along with bitbanging the actual values, 8762306a36Sopenharmony_ci * since it's all on the same hardware register. However the 8862306a36Sopenharmony_ci * CPLD needs CS deselected after every command. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci if (enable) 9162306a36Sopenharmony_ci rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, 9262306a36Sopenharmony_ci AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int rb4xx_transfer_one(struct spi_controller *host, 9662306a36Sopenharmony_ci struct spi_device *spi, struct spi_transfer *t) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct rb4xx_spi *rbspi = spi_controller_get_devdata(host); 9962306a36Sopenharmony_ci int i; 10062306a36Sopenharmony_ci u32 spi_ioc; 10162306a36Sopenharmony_ci u8 *rx_buf; 10262306a36Sopenharmony_ci const u8 *tx_buf; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* 10562306a36Sopenharmony_ci * Prime the SPI register with the SPI device selected. The m25p80 boot 10662306a36Sopenharmony_ci * flash and CPLD share the CS0 pin. This works because the CPLD's 10762306a36Sopenharmony_ci * command set was designed to almost not clash with that of the 10862306a36Sopenharmony_ci * boot flash. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci if (spi_get_chipselect(spi, 0) == 2) 11162306a36Sopenharmony_ci /* MMC */ 11262306a36Sopenharmony_ci spi_ioc = AR71XX_SPI_IOC_CS0; 11362306a36Sopenharmony_ci else 11462306a36Sopenharmony_ci /* Boot flash and CPLD */ 11562306a36Sopenharmony_ci spi_ioc = AR71XX_SPI_IOC_CS1; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci tx_buf = t->tx_buf; 11862306a36Sopenharmony_ci rx_buf = t->rx_buf; 11962306a36Sopenharmony_ci for (i = 0; i < t->len; ++i) { 12062306a36Sopenharmony_ci if (t->tx_nbits == SPI_NBITS_DUAL) 12162306a36Sopenharmony_ci /* CPLD can use two-wire transfers */ 12262306a36Sopenharmony_ci do_spi_byte_two(rbspi, spi_ioc, tx_buf[i]); 12362306a36Sopenharmony_ci else 12462306a36Sopenharmony_ci do_spi_byte(rbspi, spi_ioc, tx_buf[i]); 12562306a36Sopenharmony_ci if (!rx_buf) 12662306a36Sopenharmony_ci continue; 12762306a36Sopenharmony_ci rx_buf[i] = rb4xx_read(rbspi, AR71XX_SPI_REG_RDS); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci spi_finalize_current_transfer(host); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic int rb4xx_spi_probe(struct platform_device *pdev) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct spi_controller *host; 13762306a36Sopenharmony_ci struct clk *ahb_clk; 13862306a36Sopenharmony_ci struct rb4xx_spi *rbspi; 13962306a36Sopenharmony_ci int err; 14062306a36Sopenharmony_ci void __iomem *spi_base; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci spi_base = devm_platform_ioremap_resource(pdev, 0); 14362306a36Sopenharmony_ci if (IS_ERR(spi_base)) 14462306a36Sopenharmony_ci return PTR_ERR(spi_base); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci host = devm_spi_alloc_host(&pdev->dev, sizeof(*rbspi)); 14762306a36Sopenharmony_ci if (!host) 14862306a36Sopenharmony_ci return -ENOMEM; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci ahb_clk = devm_clk_get(&pdev->dev, "ahb"); 15162306a36Sopenharmony_ci if (IS_ERR(ahb_clk)) 15262306a36Sopenharmony_ci return PTR_ERR(ahb_clk); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci host->dev.of_node = pdev->dev.of_node; 15562306a36Sopenharmony_ci host->bus_num = 0; 15662306a36Sopenharmony_ci host->num_chipselect = 3; 15762306a36Sopenharmony_ci host->mode_bits = SPI_TX_DUAL; 15862306a36Sopenharmony_ci host->bits_per_word_mask = SPI_BPW_MASK(8); 15962306a36Sopenharmony_ci host->flags = SPI_CONTROLLER_MUST_TX; 16062306a36Sopenharmony_ci host->transfer_one = rb4xx_transfer_one; 16162306a36Sopenharmony_ci host->set_cs = rb4xx_set_cs; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci rbspi = spi_controller_get_devdata(host); 16462306a36Sopenharmony_ci rbspi->base = spi_base; 16562306a36Sopenharmony_ci rbspi->clk = ahb_clk; 16662306a36Sopenharmony_ci platform_set_drvdata(pdev, rbspi); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci err = devm_spi_register_controller(&pdev->dev, host); 16962306a36Sopenharmony_ci if (err) { 17062306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register SPI host\n"); 17162306a36Sopenharmony_ci return err; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci err = clk_prepare_enable(ahb_clk); 17562306a36Sopenharmony_ci if (err) 17662306a36Sopenharmony_ci return err; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* Enable SPI */ 17962306a36Sopenharmony_ci rb4xx_write(rbspi, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void rb4xx_spi_remove(struct platform_device *pdev) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct rb4xx_spi *rbspi = platform_get_drvdata(pdev); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci clk_disable_unprepare(rbspi->clk); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic const struct of_device_id rb4xx_spi_dt_match[] = { 19262306a36Sopenharmony_ci { .compatible = "mikrotik,rb4xx-spi" }, 19362306a36Sopenharmony_ci { }, 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rb4xx_spi_dt_match); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic struct platform_driver rb4xx_spi_drv = { 19862306a36Sopenharmony_ci .probe = rb4xx_spi_probe, 19962306a36Sopenharmony_ci .remove_new = rb4xx_spi_remove, 20062306a36Sopenharmony_ci .driver = { 20162306a36Sopenharmony_ci .name = "rb4xx-spi", 20262306a36Sopenharmony_ci .of_match_table = of_match_ptr(rb4xx_spi_dt_match), 20362306a36Sopenharmony_ci }, 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cimodule_platform_driver(rb4xx_spi_drv); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciMODULE_DESCRIPTION("Mikrotik RB4xx SPI controller driver"); 20962306a36Sopenharmony_ciMODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); 21062306a36Sopenharmony_ciMODULE_AUTHOR("Bert Vermeulen <bert@biot.com>"); 21162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 212