1047980c5SChuanhong Guo // SPDX-License-Identifier: GPL-2.0 2047980c5SChuanhong Guo // 3047980c5SChuanhong Guo // SPI controller driver for Qualcomm Atheros AR934x/QCA95xx SoCs 4047980c5SChuanhong Guo // 5047980c5SChuanhong Guo // Copyright (C) 2020 Chuanhong Guo <gch981213@gmail.com> 6047980c5SChuanhong Guo // 7047980c5SChuanhong Guo // Based on spi-mt7621.c: 8047980c5SChuanhong Guo // Copyright (C) 2011 Sergiy <piratfm@gmail.com> 9047980c5SChuanhong Guo // Copyright (C) 2011-2013 Gabor Juhos <juhosg@openwrt.org> 10047980c5SChuanhong Guo // Copyright (C) 2014-2015 Felix Fietkau <nbd@nbd.name> 11047980c5SChuanhong Guo 12047980c5SChuanhong Guo #include <linux/clk.h> 13047980c5SChuanhong Guo #include <linux/io.h> 14047980c5SChuanhong Guo #include <linux/iopoll.h> 15047980c5SChuanhong Guo #include <linux/kernel.h> 16047980c5SChuanhong Guo #include <linux/module.h> 17*749396cbSRob Herring #include <linux/of.h> 18*749396cbSRob Herring #include <linux/platform_device.h> 19047980c5SChuanhong Guo #include <linux/spi/spi.h> 20047980c5SChuanhong Guo 21047980c5SChuanhong Guo #define DRIVER_NAME "spi-ar934x" 22047980c5SChuanhong Guo 23047980c5SChuanhong Guo #define AR934X_SPI_REG_FS 0x00 24047980c5SChuanhong Guo #define AR934X_SPI_ENABLE BIT(0) 25047980c5SChuanhong Guo 26047980c5SChuanhong Guo #define AR934X_SPI_REG_IOC 0x08 27047980c5SChuanhong Guo #define AR934X_SPI_IOC_INITVAL 0x70000 28047980c5SChuanhong Guo 29047980c5SChuanhong Guo #define AR934X_SPI_REG_CTRL 0x04 30047980c5SChuanhong Guo #define AR934X_SPI_CLK_MASK GENMASK(5, 0) 31047980c5SChuanhong Guo 32047980c5SChuanhong Guo #define AR934X_SPI_DATAOUT 0x10 33047980c5SChuanhong Guo 34047980c5SChuanhong Guo #define AR934X_SPI_REG_SHIFT_CTRL 0x14 35047980c5SChuanhong Guo #define AR934X_SPI_SHIFT_EN BIT(31) 36047980c5SChuanhong Guo #define AR934X_SPI_SHIFT_CS(n) BIT(28 + (n)) 37047980c5SChuanhong Guo #define AR934X_SPI_SHIFT_TERM 26 38047980c5SChuanhong Guo #define AR934X_SPI_SHIFT_VAL(cs, term, count) \ 39047980c5SChuanhong Guo (AR934X_SPI_SHIFT_EN | AR934X_SPI_SHIFT_CS(cs) | \ 40047980c5SChuanhong Guo (term) << AR934X_SPI_SHIFT_TERM | (count)) 41047980c5SChuanhong Guo 42047980c5SChuanhong Guo #define AR934X_SPI_DATAIN 0x18 43047980c5SChuanhong Guo 44047980c5SChuanhong Guo struct ar934x_spi { 45047980c5SChuanhong Guo struct spi_controller *ctlr; 46047980c5SChuanhong Guo void __iomem *base; 47047980c5SChuanhong Guo struct clk *clk; 48047980c5SChuanhong Guo unsigned int clk_freq; 49047980c5SChuanhong Guo }; 50047980c5SChuanhong Guo 51047980c5SChuanhong Guo static inline int ar934x_spi_clk_div(struct ar934x_spi *sp, unsigned int freq) 52047980c5SChuanhong Guo { 53047980c5SChuanhong Guo int div = DIV_ROUND_UP(sp->clk_freq, freq * 2) - 1; 54047980c5SChuanhong Guo 55047980c5SChuanhong Guo if (div < 0) 56047980c5SChuanhong Guo return 0; 57047980c5SChuanhong Guo else if (div > AR934X_SPI_CLK_MASK) 58047980c5SChuanhong Guo return -EINVAL; 59047980c5SChuanhong Guo else 60047980c5SChuanhong Guo return div; 61047980c5SChuanhong Guo } 62047980c5SChuanhong Guo 63047980c5SChuanhong Guo static int ar934x_spi_setup(struct spi_device *spi) 64047980c5SChuanhong Guo { 6587384599SYang Yingliang struct ar934x_spi *sp = spi_controller_get_devdata(spi->controller); 66047980c5SChuanhong Guo 67047980c5SChuanhong Guo if ((spi->max_speed_hz == 0) || 68047980c5SChuanhong Guo (spi->max_speed_hz > (sp->clk_freq / 2))) { 69047980c5SChuanhong Guo spi->max_speed_hz = sp->clk_freq / 2; 70047980c5SChuanhong Guo } else if (spi->max_speed_hz < (sp->clk_freq / 128)) { 71047980c5SChuanhong Guo dev_err(&spi->dev, "spi clock is too low\n"); 72047980c5SChuanhong Guo return -EINVAL; 73047980c5SChuanhong Guo } 74047980c5SChuanhong Guo 75047980c5SChuanhong Guo return 0; 76047980c5SChuanhong Guo } 77047980c5SChuanhong Guo 7887384599SYang Yingliang static int ar934x_spi_transfer_one_message(struct spi_controller *ctlr, 79047980c5SChuanhong Guo struct spi_message *m) 80047980c5SChuanhong Guo { 8187384599SYang Yingliang struct ar934x_spi *sp = spi_controller_get_devdata(ctlr); 82047980c5SChuanhong Guo struct spi_transfer *t = NULL; 83047980c5SChuanhong Guo struct spi_device *spi = m->spi; 84047980c5SChuanhong Guo unsigned long trx_done, trx_cur; 85047980c5SChuanhong Guo int stat = 0; 86ebe33e5aSOskari Lemmela u8 bpw, term = 0; 87047980c5SChuanhong Guo int div, i; 88047980c5SChuanhong Guo u32 reg; 89047980c5SChuanhong Guo const u8 *tx_buf; 90047980c5SChuanhong Guo u8 *buf; 91047980c5SChuanhong Guo 92047980c5SChuanhong Guo m->actual_length = 0; 93047980c5SChuanhong Guo list_for_each_entry(t, &m->transfers, transfer_list) { 94ebe33e5aSOskari Lemmela if (t->bits_per_word >= 8 && t->bits_per_word < 32) 95ebe33e5aSOskari Lemmela bpw = t->bits_per_word >> 3; 96ebe33e5aSOskari Lemmela else 97ebe33e5aSOskari Lemmela bpw = 4; 98ebe33e5aSOskari Lemmela 99047980c5SChuanhong Guo if (t->speed_hz) 100047980c5SChuanhong Guo div = ar934x_spi_clk_div(sp, t->speed_hz); 101047980c5SChuanhong Guo else 102047980c5SChuanhong Guo div = ar934x_spi_clk_div(sp, spi->max_speed_hz); 103047980c5SChuanhong Guo if (div < 0) { 104047980c5SChuanhong Guo stat = -EIO; 105047980c5SChuanhong Guo goto msg_done; 106047980c5SChuanhong Guo } 107047980c5SChuanhong Guo 108047980c5SChuanhong Guo reg = ioread32(sp->base + AR934X_SPI_REG_CTRL); 109047980c5SChuanhong Guo reg &= ~AR934X_SPI_CLK_MASK; 110047980c5SChuanhong Guo reg |= div; 111047980c5SChuanhong Guo iowrite32(reg, sp->base + AR934X_SPI_REG_CTRL); 112047980c5SChuanhong Guo iowrite32(0, sp->base + AR934X_SPI_DATAOUT); 113047980c5SChuanhong Guo 114ebe33e5aSOskari Lemmela for (trx_done = 0; trx_done < t->len; trx_done += bpw) { 115047980c5SChuanhong Guo trx_cur = t->len - trx_done; 116ebe33e5aSOskari Lemmela if (trx_cur > bpw) 117ebe33e5aSOskari Lemmela trx_cur = bpw; 118047980c5SChuanhong Guo else if (list_is_last(&t->transfer_list, &m->transfers)) 119047980c5SChuanhong Guo term = 1; 120047980c5SChuanhong Guo 121047980c5SChuanhong Guo if (t->tx_buf) { 122047980c5SChuanhong Guo tx_buf = t->tx_buf + trx_done; 123047980c5SChuanhong Guo reg = tx_buf[0]; 124047980c5SChuanhong Guo for (i = 1; i < trx_cur; i++) 125047980c5SChuanhong Guo reg = reg << 8 | tx_buf[i]; 126047980c5SChuanhong Guo iowrite32(reg, sp->base + AR934X_SPI_DATAOUT); 127047980c5SChuanhong Guo } 128047980c5SChuanhong Guo 1299e264f3fSAmit Kumar Mahapatra via Alsa-devel reg = AR934X_SPI_SHIFT_VAL(spi_get_chipselect(spi, 0), term, 130047980c5SChuanhong Guo trx_cur * 8); 131047980c5SChuanhong Guo iowrite32(reg, sp->base + AR934X_SPI_REG_SHIFT_CTRL); 132047980c5SChuanhong Guo stat = readl_poll_timeout( 133047980c5SChuanhong Guo sp->base + AR934X_SPI_REG_SHIFT_CTRL, reg, 134047980c5SChuanhong Guo !(reg & AR934X_SPI_SHIFT_EN), 0, 5); 135047980c5SChuanhong Guo if (stat < 0) 136047980c5SChuanhong Guo goto msg_done; 137047980c5SChuanhong Guo 138047980c5SChuanhong Guo if (t->rx_buf) { 139047980c5SChuanhong Guo reg = ioread32(sp->base + AR934X_SPI_DATAIN); 140047980c5SChuanhong Guo buf = t->rx_buf + trx_done; 141047980c5SChuanhong Guo for (i = 0; i < trx_cur; i++) { 142047980c5SChuanhong Guo buf[trx_cur - i - 1] = reg & 0xff; 143047980c5SChuanhong Guo reg >>= 8; 144047980c5SChuanhong Guo } 145047980c5SChuanhong Guo } 146c7028245SOskari Lemmela spi_delay_exec(&t->word_delay, t); 147047980c5SChuanhong Guo } 148047980c5SChuanhong Guo m->actual_length += t->len; 149c7028245SOskari Lemmela spi_transfer_delay_exec(t); 150047980c5SChuanhong Guo } 151047980c5SChuanhong Guo 152047980c5SChuanhong Guo msg_done: 153047980c5SChuanhong Guo m->status = stat; 15487384599SYang Yingliang spi_finalize_current_message(ctlr); 155047980c5SChuanhong Guo 156047980c5SChuanhong Guo return 0; 157047980c5SChuanhong Guo } 158047980c5SChuanhong Guo 159047980c5SChuanhong Guo static const struct of_device_id ar934x_spi_match[] = { 160047980c5SChuanhong Guo { .compatible = "qca,ar934x-spi" }, 161047980c5SChuanhong Guo {}, 162047980c5SChuanhong Guo }; 163047980c5SChuanhong Guo MODULE_DEVICE_TABLE(of, ar934x_spi_match); 164047980c5SChuanhong Guo 165047980c5SChuanhong Guo static int ar934x_spi_probe(struct platform_device *pdev) 166047980c5SChuanhong Guo { 167047980c5SChuanhong Guo struct spi_controller *ctlr; 168047980c5SChuanhong Guo struct ar934x_spi *sp; 169047980c5SChuanhong Guo void __iomem *base; 170047980c5SChuanhong Guo struct clk *clk; 171047980c5SChuanhong Guo int ret; 172047980c5SChuanhong Guo 173047980c5SChuanhong Guo base = devm_platform_ioremap_resource(pdev, 0); 174047980c5SChuanhong Guo if (IS_ERR(base)) 175047980c5SChuanhong Guo return PTR_ERR(base); 176047980c5SChuanhong Guo 177047980c5SChuanhong Guo clk = devm_clk_get(&pdev->dev, NULL); 178047980c5SChuanhong Guo if (IS_ERR(clk)) { 179047980c5SChuanhong Guo dev_err(&pdev->dev, "failed to get clock\n"); 180047980c5SChuanhong Guo return PTR_ERR(clk); 181047980c5SChuanhong Guo } 182047980c5SChuanhong Guo 183047980c5SChuanhong Guo ret = clk_prepare_enable(clk); 184047980c5SChuanhong Guo if (ret) 185047980c5SChuanhong Guo return ret; 186047980c5SChuanhong Guo 18787384599SYang Yingliang ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*sp)); 188047980c5SChuanhong Guo if (!ctlr) { 189047980c5SChuanhong Guo dev_info(&pdev->dev, "failed to allocate spi controller\n"); 190236924eeSLukas Wunner ret = -ENOMEM; 191236924eeSLukas Wunner goto err_clk_disable; 192047980c5SChuanhong Guo } 193047980c5SChuanhong Guo 194047980c5SChuanhong Guo /* disable flash mapping and expose spi controller registers */ 195047980c5SChuanhong Guo iowrite32(AR934X_SPI_ENABLE, base + AR934X_SPI_REG_FS); 196047980c5SChuanhong Guo /* restore pins to default state: CSn=1 DO=CLK=0 */ 197047980c5SChuanhong Guo iowrite32(AR934X_SPI_IOC_INITVAL, base + AR934X_SPI_REG_IOC); 198047980c5SChuanhong Guo 199047980c5SChuanhong Guo ctlr->mode_bits = SPI_LSB_FIRST; 200047980c5SChuanhong Guo ctlr->setup = ar934x_spi_setup; 201047980c5SChuanhong Guo ctlr->transfer_one_message = ar934x_spi_transfer_one_message; 202ebe33e5aSOskari Lemmela ctlr->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(24) | 203ebe33e5aSOskari Lemmela SPI_BPW_MASK(16) | SPI_BPW_MASK(8); 204047980c5SChuanhong Guo ctlr->dev.of_node = pdev->dev.of_node; 205047980c5SChuanhong Guo ctlr->num_chipselect = 3; 206047980c5SChuanhong Guo 207047980c5SChuanhong Guo dev_set_drvdata(&pdev->dev, ctlr); 208047980c5SChuanhong Guo 209047980c5SChuanhong Guo sp = spi_controller_get_devdata(ctlr); 210047980c5SChuanhong Guo sp->base = base; 211047980c5SChuanhong Guo sp->clk = clk; 212047980c5SChuanhong Guo sp->clk_freq = clk_get_rate(clk); 213047980c5SChuanhong Guo sp->ctlr = ctlr; 214047980c5SChuanhong Guo 215236924eeSLukas Wunner ret = spi_register_controller(ctlr); 216236924eeSLukas Wunner if (!ret) 217236924eeSLukas Wunner return 0; 218236924eeSLukas Wunner 219236924eeSLukas Wunner err_clk_disable: 220236924eeSLukas Wunner clk_disable_unprepare(clk); 221236924eeSLukas Wunner return ret; 222047980c5SChuanhong Guo } 223047980c5SChuanhong Guo 22424644ae0SUwe Kleine-König static void ar934x_spi_remove(struct platform_device *pdev) 225047980c5SChuanhong Guo { 226047980c5SChuanhong Guo struct spi_controller *ctlr; 227047980c5SChuanhong Guo struct ar934x_spi *sp; 228047980c5SChuanhong Guo 229047980c5SChuanhong Guo ctlr = dev_get_drvdata(&pdev->dev); 230047980c5SChuanhong Guo sp = spi_controller_get_devdata(ctlr); 231047980c5SChuanhong Guo 232236924eeSLukas Wunner spi_unregister_controller(ctlr); 233047980c5SChuanhong Guo clk_disable_unprepare(sp->clk); 234047980c5SChuanhong Guo } 235047980c5SChuanhong Guo 236047980c5SChuanhong Guo static struct platform_driver ar934x_spi_driver = { 237047980c5SChuanhong Guo .driver = { 238047980c5SChuanhong Guo .name = DRIVER_NAME, 239047980c5SChuanhong Guo .of_match_table = ar934x_spi_match, 240047980c5SChuanhong Guo }, 241047980c5SChuanhong Guo .probe = ar934x_spi_probe, 24224644ae0SUwe Kleine-König .remove_new = ar934x_spi_remove, 243047980c5SChuanhong Guo }; 244047980c5SChuanhong Guo 245047980c5SChuanhong Guo module_platform_driver(ar934x_spi_driver); 246047980c5SChuanhong Guo 247047980c5SChuanhong Guo MODULE_DESCRIPTION("SPI controller driver for Qualcomm Atheros AR934x/QCA95xx"); 248047980c5SChuanhong Guo MODULE_AUTHOR("Chuanhong Guo <gch981213@gmail.com>"); 249047980c5SChuanhong Guo MODULE_LICENSE("GPL v2"); 250047980c5SChuanhong Guo MODULE_ALIAS("platform:" DRIVER_NAME); 251