235 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// SPI controller driver for Qualcomm Atheros AR934x/QCA95xx SoCs4//5// Copyright (C) 2020 Chuanhong Guo <gch981213@gmail.com>6//7// Based on spi-mt7621.c:8// Copyright (C) 2011 Sergiy <piratfm@gmail.com>9// Copyright (C) 2011-2013 Gabor Juhos <juhosg@openwrt.org>10// Copyright (C) 2014-2015 Felix Fietkau <nbd@nbd.name>11 12#include <linux/clk.h>13#include <linux/io.h>14#include <linux/iopoll.h>15#include <linux/kernel.h>16#include <linux/module.h>17#include <linux/of.h>18#include <linux/platform_device.h>19#include <linux/spi/spi.h>20 21#define DRIVER_NAME "spi-ar934x"22 23#define AR934X_SPI_REG_FS 0x0024#define AR934X_SPI_ENABLE BIT(0)25 26#define AR934X_SPI_REG_IOC 0x0827#define AR934X_SPI_IOC_INITVAL 0x7000028 29#define AR934X_SPI_REG_CTRL 0x0430#define AR934X_SPI_CLK_MASK GENMASK(5, 0)31 32#define AR934X_SPI_DATAOUT 0x1033 34#define AR934X_SPI_REG_SHIFT_CTRL 0x1435#define AR934X_SPI_SHIFT_EN BIT(31)36#define AR934X_SPI_SHIFT_CS(n) BIT(28 + (n))37#define AR934X_SPI_SHIFT_TERM 2638#define AR934X_SPI_SHIFT_VAL(cs, term, count) \39 (AR934X_SPI_SHIFT_EN | AR934X_SPI_SHIFT_CS(cs) | \40 (term) << AR934X_SPI_SHIFT_TERM | (count))41 42#define AR934X_SPI_DATAIN 0x1843 44struct ar934x_spi {45 struct spi_controller *ctlr;46 void __iomem *base;47 struct clk *clk;48 unsigned int clk_freq;49};50 51static inline int ar934x_spi_clk_div(struct ar934x_spi *sp, unsigned int freq)52{53 int div = DIV_ROUND_UP(sp->clk_freq, freq * 2) - 1;54 55 if (div < 0)56 return 0;57 else if (div > AR934X_SPI_CLK_MASK)58 return -EINVAL;59 else60 return div;61}62 63static int ar934x_spi_setup(struct spi_device *spi)64{65 struct ar934x_spi *sp = spi_controller_get_devdata(spi->controller);66 67 if ((spi->max_speed_hz == 0) ||68 (spi->max_speed_hz > (sp->clk_freq / 2))) {69 spi->max_speed_hz = sp->clk_freq / 2;70 } else if (spi->max_speed_hz < (sp->clk_freq / 128)) {71 dev_err(&spi->dev, "spi clock is too low\n");72 return -EINVAL;73 }74 75 return 0;76}77 78static int ar934x_spi_transfer_one_message(struct spi_controller *ctlr,79 struct spi_message *m)80{81 struct ar934x_spi *sp = spi_controller_get_devdata(ctlr);82 struct spi_transfer *t = NULL;83 struct spi_device *spi = m->spi;84 unsigned long trx_done, trx_cur;85 int stat = 0;86 u8 bpw, term = 0;87 int div, i;88 u32 reg;89 const u8 *tx_buf;90 u8 *buf;91 92 m->actual_length = 0;93 list_for_each_entry(t, &m->transfers, transfer_list) {94 if (t->bits_per_word >= 8 && t->bits_per_word < 32)95 bpw = t->bits_per_word >> 3;96 else97 bpw = 4;98 99 if (t->speed_hz)100 div = ar934x_spi_clk_div(sp, t->speed_hz);101 else102 div = ar934x_spi_clk_div(sp, spi->max_speed_hz);103 if (div < 0) {104 stat = -EIO;105 goto msg_done;106 }107 108 reg = ioread32(sp->base + AR934X_SPI_REG_CTRL);109 reg &= ~AR934X_SPI_CLK_MASK;110 reg |= div;111 iowrite32(reg, sp->base + AR934X_SPI_REG_CTRL);112 iowrite32(0, sp->base + AR934X_SPI_DATAOUT);113 114 for (trx_done = 0; trx_done < t->len; trx_done += bpw) {115 trx_cur = t->len - trx_done;116 if (trx_cur > bpw)117 trx_cur = bpw;118 else if (list_is_last(&t->transfer_list, &m->transfers))119 term = 1;120 121 if (t->tx_buf) {122 tx_buf = t->tx_buf + trx_done;123 reg = tx_buf[0];124 for (i = 1; i < trx_cur; i++)125 reg = reg << 8 | tx_buf[i];126 iowrite32(reg, sp->base + AR934X_SPI_DATAOUT);127 }128 129 reg = AR934X_SPI_SHIFT_VAL(spi_get_chipselect(spi, 0), term,130 trx_cur * 8);131 iowrite32(reg, sp->base + AR934X_SPI_REG_SHIFT_CTRL);132 stat = readl_poll_timeout(133 sp->base + AR934X_SPI_REG_SHIFT_CTRL, reg,134 !(reg & AR934X_SPI_SHIFT_EN), 0, 5);135 if (stat < 0)136 goto msg_done;137 138 if (t->rx_buf) {139 reg = ioread32(sp->base + AR934X_SPI_DATAIN);140 buf = t->rx_buf + trx_done;141 for (i = 0; i < trx_cur; i++) {142 buf[trx_cur - i - 1] = reg & 0xff;143 reg >>= 8;144 }145 }146 spi_delay_exec(&t->word_delay, t);147 }148 m->actual_length += t->len;149 spi_transfer_delay_exec(t);150 }151 152msg_done:153 m->status = stat;154 spi_finalize_current_message(ctlr);155 156 return 0;157}158 159static const struct of_device_id ar934x_spi_match[] = {160 { .compatible = "qca,ar934x-spi" },161 {},162};163MODULE_DEVICE_TABLE(of, ar934x_spi_match);164 165static int ar934x_spi_probe(struct platform_device *pdev)166{167 struct spi_controller *ctlr;168 struct ar934x_spi *sp;169 void __iomem *base;170 struct clk *clk;171 172 base = devm_platform_ioremap_resource(pdev, 0);173 if (IS_ERR(base))174 return PTR_ERR(base);175 176 clk = devm_clk_get_enabled(&pdev->dev, NULL);177 if (IS_ERR(clk)) {178 dev_err(&pdev->dev, "failed to get clock\n");179 return PTR_ERR(clk);180 }181 182 ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*sp));183 if (!ctlr) {184 dev_info(&pdev->dev, "failed to allocate spi controller\n");185 return -ENOMEM;186 }187 188 /* disable flash mapping and expose spi controller registers */189 iowrite32(AR934X_SPI_ENABLE, base + AR934X_SPI_REG_FS);190 /* restore pins to default state: CSn=1 DO=CLK=0 */191 iowrite32(AR934X_SPI_IOC_INITVAL, base + AR934X_SPI_REG_IOC);192 193 ctlr->mode_bits = SPI_LSB_FIRST;194 ctlr->setup = ar934x_spi_setup;195 ctlr->transfer_one_message = ar934x_spi_transfer_one_message;196 ctlr->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(24) |197 SPI_BPW_MASK(16) | SPI_BPW_MASK(8);198 ctlr->dev.of_node = pdev->dev.of_node;199 ctlr->num_chipselect = 3;200 201 dev_set_drvdata(&pdev->dev, ctlr);202 203 sp = spi_controller_get_devdata(ctlr);204 sp->base = base;205 sp->clk = clk;206 sp->clk_freq = clk_get_rate(clk);207 sp->ctlr = ctlr;208 209 return spi_register_controller(ctlr);210}211 212static void ar934x_spi_remove(struct platform_device *pdev)213{214 struct spi_controller *ctlr;215 216 ctlr = dev_get_drvdata(&pdev->dev);217 spi_unregister_controller(ctlr);218}219 220static struct platform_driver ar934x_spi_driver = {221 .driver = {222 .name = DRIVER_NAME,223 .of_match_table = ar934x_spi_match,224 },225 .probe = ar934x_spi_probe,226 .remove_new = ar934x_spi_remove,227};228 229module_platform_driver(ar934x_spi_driver);230 231MODULE_DESCRIPTION("SPI controller driver for Qualcomm Atheros AR934x/QCA95xx");232MODULE_AUTHOR("Chuanhong Guo <gch981213@gmail.com>");233MODULE_LICENSE("GPL v2");234MODULE_ALIAS("platform:" DRIVER_NAME);235