|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | // | 
|  | // spi-mt7621.c -- MediaTek MT7621 SPI controller driver | 
|  | // | 
|  | // Copyright (C) 2011 Sergiy <[email protected]> | 
|  | // Copyright (C) 2011-2013 Gabor Juhos <[email protected]> | 
|  | // Copyright (C) 2014-2015 Felix Fietkau <[email protected]> | 
|  | // | 
|  | // Some parts are based on spi-orion.c: | 
|  | //   Author: Shadi Ammouri <[email protected]> | 
|  | //   Copyright (C) 2007-2008 Marvell Ltd. | 
|  |  | 
|  | #include <linux/clk.h> | 
|  | #include <linux/delay.h> | 
|  | #include <linux/io.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/of_device.h> | 
|  | #include <linux/reset.h> | 
|  | #include <linux/spi/spi.h> | 
|  |  | 
|  | #define DRIVER_NAME		"spi-mt7621" | 
|  |  | 
|  | /* in usec */ | 
|  | #define RALINK_SPI_WAIT_MAX_LOOP 2000 | 
|  |  | 
|  | /* SPISTAT register bit field */ | 
|  | #define SPISTAT_BUSY		BIT(0) | 
|  |  | 
|  | #define MT7621_SPI_TRANS	0x00 | 
|  | #define SPITRANS_BUSY		BIT(16) | 
|  |  | 
|  | #define MT7621_SPI_OPCODE	0x04 | 
|  | #define MT7621_SPI_DATA0	0x08 | 
|  | #define MT7621_SPI_DATA4	0x18 | 
|  | #define SPI_CTL_TX_RX_CNT_MASK	0xff | 
|  | #define SPI_CTL_START		BIT(8) | 
|  |  | 
|  | #define MT7621_SPI_MASTER	0x28 | 
|  | #define MASTER_MORE_BUFMODE	BIT(2) | 
|  | #define MASTER_FULL_DUPLEX	BIT(10) | 
|  | #define MASTER_RS_CLK_SEL	GENMASK(27, 16) | 
|  | #define MASTER_RS_CLK_SEL_SHIFT	16 | 
|  | #define MASTER_RS_SLAVE_SEL	GENMASK(31, 29) | 
|  |  | 
|  | #define MT7621_SPI_MOREBUF	0x2c | 
|  | #define MT7621_SPI_POLAR	0x38 | 
|  | #define MT7621_SPI_SPACE	0x3c | 
|  |  | 
|  | #define MT7621_CPHA		BIT(5) | 
|  | #define MT7621_CPOL		BIT(4) | 
|  | #define MT7621_LSB_FIRST	BIT(3) | 
|  |  | 
|  | struct mt7621_spi { | 
|  | struct spi_controller	*master; | 
|  | void __iomem		*base; | 
|  | unsigned int		sys_freq; | 
|  | unsigned int		speed; | 
|  | struct clk		*clk; | 
|  | int			pending_write; | 
|  | }; | 
|  |  | 
|  | static inline struct mt7621_spi *spidev_to_mt7621_spi(struct spi_device *spi) | 
|  | { | 
|  | return spi_controller_get_devdata(spi->master); | 
|  | } | 
|  |  | 
|  | static inline u32 mt7621_spi_read(struct mt7621_spi *rs, u32 reg) | 
|  | { | 
|  | return ioread32(rs->base + reg); | 
|  | } | 
|  |  | 
|  | static inline void mt7621_spi_write(struct mt7621_spi *rs, u32 reg, u32 val) | 
|  | { | 
|  | iowrite32(val, rs->base + reg); | 
|  | } | 
|  |  | 
|  | static void mt7621_spi_set_cs(struct spi_device *spi, int enable) | 
|  | { | 
|  | struct mt7621_spi *rs = spidev_to_mt7621_spi(spi); | 
|  | int cs = spi->chip_select; | 
|  | u32 polar = 0; | 
|  | u32 master; | 
|  |  | 
|  | /* | 
|  | * Select SPI device 7, enable "more buffer mode" and disable | 
|  | * full-duplex (only half-duplex really works on this chip | 
|  | * reliably) | 
|  | */ | 
|  | master = mt7621_spi_read(rs, MT7621_SPI_MASTER); | 
|  | master |= MASTER_RS_SLAVE_SEL | MASTER_MORE_BUFMODE; | 
|  | master &= ~MASTER_FULL_DUPLEX; | 
|  | mt7621_spi_write(rs, MT7621_SPI_MASTER, master); | 
|  |  | 
|  | rs->pending_write = 0; | 
|  |  | 
|  | if (enable) | 
|  | polar = BIT(cs); | 
|  | mt7621_spi_write(rs, MT7621_SPI_POLAR, polar); | 
|  | } | 
|  |  | 
|  | static int mt7621_spi_prepare(struct spi_device *spi, unsigned int speed) | 
|  | { | 
|  | struct mt7621_spi *rs = spidev_to_mt7621_spi(spi); | 
|  | u32 rate; | 
|  | u32 reg; | 
|  |  | 
|  | dev_dbg(&spi->dev, "speed:%u\n", speed); | 
|  |  | 
|  | rate = DIV_ROUND_UP(rs->sys_freq, speed); | 
|  | dev_dbg(&spi->dev, "rate-1:%u\n", rate); | 
|  |  | 
|  | if (rate > 4097) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (rate < 2) | 
|  | rate = 2; | 
|  |  | 
|  | reg = mt7621_spi_read(rs, MT7621_SPI_MASTER); | 
|  | reg &= ~MASTER_RS_CLK_SEL; | 
|  | reg |= (rate - 2) << MASTER_RS_CLK_SEL_SHIFT; | 
|  | rs->speed = speed; | 
|  |  | 
|  | reg &= ~MT7621_LSB_FIRST; | 
|  | if (spi->mode & SPI_LSB_FIRST) | 
|  | reg |= MT7621_LSB_FIRST; | 
|  |  | 
|  | /* | 
|  | * This SPI controller seems to be tested on SPI flash only and some | 
|  | * bits are swizzled under other SPI modes probably due to incorrect | 
|  | * wiring inside the silicon. Only mode 0 works correctly. | 
|  | */ | 
|  | reg &= ~(MT7621_CPHA | MT7621_CPOL); | 
|  |  | 
|  | mt7621_spi_write(rs, MT7621_SPI_MASTER, reg); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static inline int mt7621_spi_wait_till_ready(struct mt7621_spi *rs) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < RALINK_SPI_WAIT_MAX_LOOP; i++) { | 
|  | u32 status; | 
|  |  | 
|  | status = mt7621_spi_read(rs, MT7621_SPI_TRANS); | 
|  | if ((status & SPITRANS_BUSY) == 0) | 
|  | return 0; | 
|  | cpu_relax(); | 
|  | udelay(1); | 
|  | } | 
|  |  | 
|  | return -ETIMEDOUT; | 
|  | } | 
|  |  | 
|  | static void mt7621_spi_read_half_duplex(struct mt7621_spi *rs, | 
|  | int rx_len, u8 *buf) | 
|  | { | 
|  | int tx_len; | 
|  |  | 
|  | /* | 
|  | * Combine with any pending write, and perform one or more half-duplex | 
|  | * transactions reading 'len' bytes. Data to be written is already in | 
|  | * MT7621_SPI_DATA. | 
|  | */ | 
|  | tx_len = rs->pending_write; | 
|  | rs->pending_write = 0; | 
|  |  | 
|  | while (rx_len || tx_len) { | 
|  | int i; | 
|  | u32 val = (min(tx_len, 4) * 8) << 24; | 
|  | int rx = min(rx_len, 32); | 
|  |  | 
|  | if (tx_len > 4) | 
|  | val |= (tx_len - 4) * 8; | 
|  | val |= (rx * 8) << 12; | 
|  | mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val); | 
|  |  | 
|  | tx_len = 0; | 
|  |  | 
|  | val = mt7621_spi_read(rs, MT7621_SPI_TRANS); | 
|  | val |= SPI_CTL_START; | 
|  | mt7621_spi_write(rs, MT7621_SPI_TRANS, val); | 
|  |  | 
|  | mt7621_spi_wait_till_ready(rs); | 
|  |  | 
|  | for (i = 0; i < rx; i++) { | 
|  | if ((i % 4) == 0) | 
|  | val = mt7621_spi_read(rs, MT7621_SPI_DATA0 + i); | 
|  | *buf++ = val & 0xff; | 
|  | val >>= 8; | 
|  | } | 
|  |  | 
|  | rx_len -= i; | 
|  | } | 
|  | } | 
|  |  | 
|  | static inline void mt7621_spi_flush(struct mt7621_spi *rs) | 
|  | { | 
|  | mt7621_spi_read_half_duplex(rs, 0, NULL); | 
|  | } | 
|  |  | 
|  | static void mt7621_spi_write_half_duplex(struct mt7621_spi *rs, | 
|  | int tx_len, const u8 *buf) | 
|  | { | 
|  | int len = rs->pending_write; | 
|  | int val = 0; | 
|  |  | 
|  | if (len & 3) { | 
|  | val = mt7621_spi_read(rs, MT7621_SPI_OPCODE + (len & ~3)); | 
|  | if (len < 4) { | 
|  | val <<= (4 - len) * 8; | 
|  | val = swab32(val); | 
|  | } | 
|  | } | 
|  |  | 
|  | while (tx_len > 0) { | 
|  | if (len >= 36) { | 
|  | rs->pending_write = len; | 
|  | mt7621_spi_flush(rs); | 
|  | len = 0; | 
|  | } | 
|  |  | 
|  | val |= *buf++ << (8 * (len & 3)); | 
|  | len++; | 
|  | if ((len & 3) == 0) { | 
|  | if (len == 4) | 
|  | /* The byte-order of the opcode is weird! */ | 
|  | val = swab32(val); | 
|  | mt7621_spi_write(rs, MT7621_SPI_OPCODE + len - 4, val); | 
|  | val = 0; | 
|  | } | 
|  | tx_len -= 1; | 
|  | } | 
|  |  | 
|  | if (len & 3) { | 
|  | if (len < 4) { | 
|  | val = swab32(val); | 
|  | val >>= (4 - len) * 8; | 
|  | } | 
|  | mt7621_spi_write(rs, MT7621_SPI_OPCODE + (len & ~3), val); | 
|  | } | 
|  |  | 
|  | rs->pending_write = len; | 
|  | } | 
|  |  | 
|  | static int mt7621_spi_transfer_one_message(struct spi_controller *master, | 
|  | struct spi_message *m) | 
|  | { | 
|  | struct mt7621_spi *rs = spi_controller_get_devdata(master); | 
|  | struct spi_device *spi = m->spi; | 
|  | unsigned int speed = spi->max_speed_hz; | 
|  | struct spi_transfer *t = NULL; | 
|  | int status = 0; | 
|  |  | 
|  | mt7621_spi_wait_till_ready(rs); | 
|  |  | 
|  | list_for_each_entry(t, &m->transfers, transfer_list) | 
|  | if (t->speed_hz < speed) | 
|  | speed = t->speed_hz; | 
|  |  | 
|  | if (mt7621_spi_prepare(spi, speed)) { | 
|  | status = -EIO; | 
|  | goto msg_done; | 
|  | } | 
|  |  | 
|  | /* Assert CS */ | 
|  | mt7621_spi_set_cs(spi, 1); | 
|  |  | 
|  | m->actual_length = 0; | 
|  | list_for_each_entry(t, &m->transfers, transfer_list) { | 
|  | if ((t->rx_buf) && (t->tx_buf)) { | 
|  | /* | 
|  | * This controller will shift some extra data out | 
|  | * of spi_opcode if (mosi_bit_cnt > 0) && | 
|  | * (cmd_bit_cnt == 0). So the claimed full-duplex | 
|  | * support is broken since we have no way to read | 
|  | * the MISO value during that bit. | 
|  | */ | 
|  | status = -EIO; | 
|  | goto msg_done; | 
|  | } else if (t->rx_buf) { | 
|  | mt7621_spi_read_half_duplex(rs, t->len, t->rx_buf); | 
|  | } else if (t->tx_buf) { | 
|  | mt7621_spi_write_half_duplex(rs, t->len, t->tx_buf); | 
|  | } | 
|  | m->actual_length += t->len; | 
|  | } | 
|  |  | 
|  | /* Flush data and deassert CS */ | 
|  | mt7621_spi_flush(rs); | 
|  | mt7621_spi_set_cs(spi, 0); | 
|  |  | 
|  | msg_done: | 
|  | m->status = status; | 
|  | spi_finalize_current_message(master); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int mt7621_spi_setup(struct spi_device *spi) | 
|  | { | 
|  | struct mt7621_spi *rs = spidev_to_mt7621_spi(spi); | 
|  |  | 
|  | if ((spi->max_speed_hz == 0) || | 
|  | (spi->max_speed_hz > (rs->sys_freq / 2))) | 
|  | spi->max_speed_hz = rs->sys_freq / 2; | 
|  |  | 
|  | if (spi->max_speed_hz < (rs->sys_freq / 4097)) { | 
|  | dev_err(&spi->dev, "setup: requested speed is too low %d Hz\n", | 
|  | spi->max_speed_hz); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct of_device_id mt7621_spi_match[] = { | 
|  | { .compatible = "ralink,mt7621-spi" }, | 
|  | {}, | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(of, mt7621_spi_match); | 
|  |  | 
|  | static int mt7621_spi_probe(struct platform_device *pdev) | 
|  | { | 
|  | const struct of_device_id *match; | 
|  | struct spi_controller *master; | 
|  | struct mt7621_spi *rs; | 
|  | void __iomem *base; | 
|  | struct resource *r; | 
|  | int status = 0; | 
|  | struct clk *clk; | 
|  | int ret; | 
|  |  | 
|  | match = of_match_device(mt7621_spi_match, &pdev->dev); | 
|  | if (!match) | 
|  | return -EINVAL; | 
|  |  | 
|  | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | base = devm_ioremap_resource(&pdev->dev, r); | 
|  | if (IS_ERR(base)) | 
|  | return PTR_ERR(base); | 
|  |  | 
|  | clk = devm_clk_get(&pdev->dev, NULL); | 
|  | if (IS_ERR(clk)) { | 
|  | dev_err(&pdev->dev, "unable to get SYS clock, err=%d\n", | 
|  | status); | 
|  | return PTR_ERR(clk); | 
|  | } | 
|  |  | 
|  | status = clk_prepare_enable(clk); | 
|  | if (status) | 
|  | return status; | 
|  |  | 
|  | master = spi_alloc_master(&pdev->dev, sizeof(*rs)); | 
|  | if (!master) { | 
|  | dev_info(&pdev->dev, "master allocation failed\n"); | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | master->mode_bits = SPI_LSB_FIRST; | 
|  | master->flags = SPI_CONTROLLER_HALF_DUPLEX; | 
|  | master->setup = mt7621_spi_setup; | 
|  | master->transfer_one_message = mt7621_spi_transfer_one_message; | 
|  | master->bits_per_word_mask = SPI_BPW_MASK(8); | 
|  | master->dev.of_node = pdev->dev.of_node; | 
|  | master->num_chipselect = 2; | 
|  |  | 
|  | dev_set_drvdata(&pdev->dev, master); | 
|  |  | 
|  | rs = spi_controller_get_devdata(master); | 
|  | rs->base = base; | 
|  | rs->clk = clk; | 
|  | rs->master = master; | 
|  | rs->sys_freq = clk_get_rate(rs->clk); | 
|  | rs->pending_write = 0; | 
|  | dev_info(&pdev->dev, "sys_freq: %u\n", rs->sys_freq); | 
|  |  | 
|  | ret = device_reset(&pdev->dev); | 
|  | if (ret) { | 
|  | dev_err(&pdev->dev, "SPI reset failed!\n"); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | return devm_spi_register_controller(&pdev->dev, master); | 
|  | } | 
|  |  | 
|  | static int mt7621_spi_remove(struct platform_device *pdev) | 
|  | { | 
|  | struct spi_controller *master; | 
|  | struct mt7621_spi *rs; | 
|  |  | 
|  | master = dev_get_drvdata(&pdev->dev); | 
|  | rs = spi_controller_get_devdata(master); | 
|  |  | 
|  | clk_disable_unprepare(rs->clk); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | MODULE_ALIAS("platform:" DRIVER_NAME); | 
|  |  | 
|  | static struct platform_driver mt7621_spi_driver = { | 
|  | .driver = { | 
|  | .name = DRIVER_NAME, | 
|  | .of_match_table = mt7621_spi_match, | 
|  | }, | 
|  | .probe = mt7621_spi_probe, | 
|  | .remove = mt7621_spi_remove, | 
|  | }; | 
|  |  | 
|  | module_platform_driver(mt7621_spi_driver); | 
|  |  | 
|  | MODULE_DESCRIPTION("MT7621 SPI driver"); | 
|  | MODULE_AUTHOR("Felix Fietkau <[email protected]>"); | 
|  | MODULE_LICENSE("GPL"); |