| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * SPI bus driver for the Ingenic SoCs |
| * Copyright (c) 2017-2021 Artur Rojek <contact@artur-rojek.eu> |
| * Copyright (c) 2017-2021 Paul Cercueil <paul@crapouillou.net> |
| * Copyright (c) 2022 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com> |
| */ |
| |
| #include <linux/clk.h> |
| #include <linux/delay.h> |
| #include <linux/dmaengine.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/iopoll.h> |
| #include <linux/module.h> |
| #include <linux/of.h> |
| #include <linux/platform_device.h> |
| #include <linux/regmap.h> |
| #include <linux/spi/spi.h> |
| |
| #define REG_SSIDR 0x0 |
| #define REG_SSICR0 0x4 |
| #define REG_SSICR1 0x8 |
| #define REG_SSISR 0xc |
| #define REG_SSIGR 0x18 |
| |
| #define REG_SSICR0_TENDIAN_LSB BIT(19) |
| #define REG_SSICR0_RENDIAN_LSB BIT(17) |
| #define REG_SSICR0_SSIE BIT(15) |
| #define REG_SSICR0_LOOP BIT(10) |
| #define REG_SSICR0_EACLRUN BIT(7) |
| #define REG_SSICR0_FSEL BIT(6) |
| #define REG_SSICR0_TFLUSH BIT(2) |
| #define REG_SSICR0_RFLUSH BIT(1) |
| |
| #define REG_SSICR1_FRMHL_MASK (BIT(31) | BIT(30)) |
| #define REG_SSICR1_FRMHL BIT(30) |
| #define REG_SSICR1_LFST BIT(25) |
| #define REG_SSICR1_UNFIN BIT(23) |
| #define REG_SSICR1_PHA BIT(1) |
| #define REG_SSICR1_POL BIT(0) |
| |
| #define REG_SSISR_END BIT(7) |
| #define REG_SSISR_BUSY BIT(6) |
| #define REG_SSISR_TFF BIT(5) |
| #define REG_SSISR_RFE BIT(4) |
| #define REG_SSISR_RFHF BIT(2) |
| #define REG_SSISR_UNDR BIT(1) |
| #define REG_SSISR_OVER BIT(0) |
| |
| #define SPI_INGENIC_FIFO_SIZE 128u |
| |
| struct jz_soc_info { |
| u32 bits_per_word_mask; |
| struct reg_field flen_field; |
| bool has_trendian; |
| |
| unsigned int max_speed_hz; |
| unsigned int max_native_cs; |
| }; |
| |
| struct ingenic_spi { |
| const struct jz_soc_info *soc_info; |
| struct clk *clk; |
| struct resource *mem_res; |
| |
| struct regmap *map; |
| struct regmap_field *flen_field; |
| }; |
| |
| static int spi_ingenic_wait(struct ingenic_spi *priv, |
| unsigned long mask, |
| bool condition) |
| { |
| unsigned int val; |
| |
| return regmap_read_poll_timeout(priv->map, REG_SSISR, val, |
| !!(val & mask) == condition, |
| 100, 10000); |
| } |
| |
| static void spi_ingenic_set_cs(struct spi_device *spi, bool disable) |
| { |
| struct ingenic_spi *priv = spi_controller_get_devdata(spi->controller); |
| |
| if (disable) { |
| regmap_clear_bits(priv->map, REG_SSICR1, REG_SSICR1_UNFIN); |
| regmap_clear_bits(priv->map, REG_SSISR, |
| REG_SSISR_UNDR | REG_SSISR_OVER); |
| |
| spi_ingenic_wait(priv, REG_SSISR_END, true); |
| } else { |
| regmap_set_bits(priv->map, REG_SSICR1, REG_SSICR1_UNFIN); |
| } |
| |
| regmap_set_bits(priv->map, REG_SSICR0, |
| REG_SSICR0_RFLUSH | REG_SSICR0_TFLUSH); |
| } |
| |
| static void spi_ingenic_prepare_transfer(struct ingenic_spi *priv, |
| struct spi_device *spi, |
| struct spi_transfer *xfer) |
| { |
| unsigned long clk_hz = clk_get_rate(priv->clk); |
| u32 cdiv, speed_hz = xfer->speed_hz ?: spi->max_speed_hz, |
| bits_per_word = xfer->bits_per_word ?: spi->bits_per_word; |
| |
| cdiv = clk_hz / (speed_hz * 2); |
| cdiv = clamp(cdiv, 1u, 0x100u) - 1; |
| |
| regmap_write(priv->map, REG_SSIGR, cdiv); |
| |
| regmap_field_write(priv->flen_field, bits_per_word - 2); |
| } |
| |
| static void spi_ingenic_finalize_transfer(void *controller) |
| { |
| spi_finalize_current_transfer(controller); |
| } |
| |
| static struct dma_async_tx_descriptor * |
| spi_ingenic_prepare_dma(struct spi_controller *ctlr, struct dma_chan *chan, |
| struct sg_table *sg, enum dma_transfer_direction dir, |
| unsigned int bits) |
| { |
| struct ingenic_spi *priv = spi_controller_get_devdata(ctlr); |
| struct dma_slave_config cfg = { |
| .direction = dir, |
| .src_addr = priv->mem_res->start + REG_SSIDR, |
| .dst_addr = priv->mem_res->start + REG_SSIDR, |
| }; |
| struct dma_async_tx_descriptor *desc; |
| dma_cookie_t cookie; |
| int ret; |
| |
| if (bits > 16) { |
| cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
| cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
| cfg.src_maxburst = cfg.dst_maxburst = 4; |
| } else if (bits > 8) { |
| cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
| cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; |
| cfg.src_maxburst = cfg.dst_maxburst = 2; |
| } else { |
| cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; |
| cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; |
| cfg.src_maxburst = cfg.dst_maxburst = 1; |
| } |
| |
| ret = dmaengine_slave_config(chan, &cfg); |
| if (ret) |
| return ERR_PTR(ret); |
| |
| desc = dmaengine_prep_slave_sg(chan, sg->sgl, sg->nents, dir, |
| DMA_PREP_INTERRUPT); |
| if (!desc) |
| return ERR_PTR(-ENOMEM); |
| |
| if (dir == DMA_DEV_TO_MEM) { |
| desc->callback = spi_ingenic_finalize_transfer; |
| desc->callback_param = ctlr; |
| } |
| |
| cookie = dmaengine_submit(desc); |
| |
| ret = dma_submit_error(cookie); |
| if (ret) { |
| dmaengine_desc_free(desc); |
| return ERR_PTR(ret); |
| } |
| |
| return desc; |
| } |
| |
| static int spi_ingenic_dma_tx(struct spi_controller *ctlr, |
| struct spi_transfer *xfer, unsigned int bits) |
| { |
| struct dma_async_tx_descriptor *rx_desc, *tx_desc; |
| |
| rx_desc = spi_ingenic_prepare_dma(ctlr, ctlr->dma_rx, |
| &xfer->rx_sg, DMA_DEV_TO_MEM, bits); |
| if (IS_ERR(rx_desc)) |
| return PTR_ERR(rx_desc); |
| |
| tx_desc = spi_ingenic_prepare_dma(ctlr, ctlr->dma_tx, |
| &xfer->tx_sg, DMA_MEM_TO_DEV, bits); |
| if (IS_ERR(tx_desc)) { |
| dmaengine_terminate_async(ctlr->dma_rx); |
| dmaengine_desc_free(rx_desc); |
| return PTR_ERR(tx_desc); |
| } |
| |
| dma_async_issue_pending(ctlr->dma_rx); |
| dma_async_issue_pending(ctlr->dma_tx); |
| |
| return 1; |
| } |
| |
| #define SPI_INGENIC_TX(x) \ |
| static int spi_ingenic_tx##x(struct ingenic_spi *priv, \ |
| struct spi_transfer *xfer) \ |
| { \ |
| unsigned int count = xfer->len / (x / 8); \ |
| unsigned int prefill = min(count, SPI_INGENIC_FIFO_SIZE); \ |
| const u##x *tx_buf = xfer->tx_buf; \ |
| u##x *rx_buf = xfer->rx_buf; \ |
| unsigned int i, val; \ |
| int err; \ |
| \ |
| /* Fill up the TX fifo */ \ |
| for (i = 0; i < prefill; i++) { \ |
| val = tx_buf ? tx_buf[i] : 0; \ |
| \ |
| regmap_write(priv->map, REG_SSIDR, val); \ |
| } \ |
| \ |
| for (i = 0; i < count; i++) { \ |
| err = spi_ingenic_wait(priv, REG_SSISR_RFE, false); \ |
| if (err) \ |
| return err; \ |
| \ |
| regmap_read(priv->map, REG_SSIDR, &val); \ |
| if (rx_buf) \ |
| rx_buf[i] = val; \ |
| \ |
| if (i < count - prefill) { \ |
| val = tx_buf ? tx_buf[i + prefill] : 0; \ |
| \ |
| regmap_write(priv->map, REG_SSIDR, val); \ |
| } \ |
| } \ |
| \ |
| return 0; \ |
| } |
| SPI_INGENIC_TX(8) |
| SPI_INGENIC_TX(16) |
| SPI_INGENIC_TX(32) |
| #undef SPI_INGENIC_TX |
| |
| static int spi_ingenic_transfer_one(struct spi_controller *ctlr, |
| struct spi_device *spi, |
| struct spi_transfer *xfer) |
| { |
| struct ingenic_spi *priv = spi_controller_get_devdata(ctlr); |
| unsigned int bits = xfer->bits_per_word ?: spi->bits_per_word; |
| bool can_dma = ctlr->can_dma && ctlr->can_dma(ctlr, spi, xfer); |
| |
| spi_ingenic_prepare_transfer(priv, spi, xfer); |
| |
| if (ctlr->cur_msg_mapped && can_dma) |
| return spi_ingenic_dma_tx(ctlr, xfer, bits); |
| |
| if (bits > 16) |
| return spi_ingenic_tx32(priv, xfer); |
| |
| if (bits > 8) |
| return spi_ingenic_tx16(priv, xfer); |
| |
| return spi_ingenic_tx8(priv, xfer); |
| } |
| |
| static int spi_ingenic_prepare_message(struct spi_controller *ctlr, |
| struct spi_message *message) |
| { |
| struct ingenic_spi *priv = spi_controller_get_devdata(ctlr); |
| struct spi_device *spi = message->spi; |
| unsigned int cs = REG_SSICR1_FRMHL << spi_get_chipselect(spi, 0); |
| unsigned int ssicr0_mask = REG_SSICR0_LOOP | REG_SSICR0_FSEL; |
| unsigned int ssicr1_mask = REG_SSICR1_PHA | REG_SSICR1_POL | cs; |
| unsigned int ssicr0 = 0, ssicr1 = 0; |
| |
| if (priv->soc_info->has_trendian) { |
| ssicr0_mask |= REG_SSICR0_RENDIAN_LSB | REG_SSICR0_TENDIAN_LSB; |
| |
| if (spi->mode & SPI_LSB_FIRST) |
| ssicr0 |= REG_SSICR0_RENDIAN_LSB | REG_SSICR0_TENDIAN_LSB; |
| } else { |
| ssicr1_mask |= REG_SSICR1_LFST; |
| |
| if (spi->mode & SPI_LSB_FIRST) |
| ssicr1 |= REG_SSICR1_LFST; |
| } |
| |
| if (spi->mode & SPI_LOOP) |
| ssicr0 |= REG_SSICR0_LOOP; |
| if (spi_get_chipselect(spi, 0)) |
| ssicr0 |= REG_SSICR0_FSEL; |
| |
| if (spi->mode & SPI_CPHA) |
| ssicr1 |= REG_SSICR1_PHA; |
| if (spi->mode & SPI_CPOL) |
| ssicr1 |= REG_SSICR1_POL; |
| if (spi->mode & SPI_CS_HIGH) |
| ssicr1 |= cs; |
| |
| regmap_update_bits(priv->map, REG_SSICR0, ssicr0_mask, ssicr0); |
| regmap_update_bits(priv->map, REG_SSICR1, ssicr1_mask, ssicr1); |
| |
| return 0; |
| } |
| |
| static int spi_ingenic_prepare_hardware(struct spi_controller *ctlr) |
| { |
| struct ingenic_spi *priv = spi_controller_get_devdata(ctlr); |
| int ret; |
| |
| ret = clk_prepare_enable(priv->clk); |
| if (ret) |
| return ret; |
| |
| regmap_write(priv->map, REG_SSICR0, REG_SSICR0_EACLRUN); |
| regmap_write(priv->map, REG_SSICR1, 0); |
| regmap_write(priv->map, REG_SSISR, 0); |
| regmap_set_bits(priv->map, REG_SSICR0, REG_SSICR0_SSIE); |
| |
| return 0; |
| } |
| |
| static int spi_ingenic_unprepare_hardware(struct spi_controller *ctlr) |
| { |
| struct ingenic_spi *priv = spi_controller_get_devdata(ctlr); |
| |
| regmap_clear_bits(priv->map, REG_SSICR0, REG_SSICR0_SSIE); |
| |
| clk_disable_unprepare(priv->clk); |
| |
| return 0; |
| } |
| |
| static bool spi_ingenic_can_dma(struct spi_controller *ctlr, |
| struct spi_device *spi, |
| struct spi_transfer *xfer) |
| { |
| struct dma_slave_caps caps; |
| int ret; |
| |
| ret = dma_get_slave_caps(ctlr->dma_tx, &caps); |
| if (ret) { |
| dev_err(&spi->dev, "Unable to get slave caps: %d\n", ret); |
| return false; |
| } |
| |
| return !caps.max_sg_burst || |
| xfer->len <= caps.max_sg_burst * SPI_INGENIC_FIFO_SIZE; |
| } |
| |
| static int spi_ingenic_request_dma(struct spi_controller *ctlr, |
| struct device *dev) |
| { |
| ctlr->dma_tx = dma_request_slave_channel(dev, "tx"); |
| if (!ctlr->dma_tx) |
| return -ENODEV; |
| |
| ctlr->dma_rx = dma_request_slave_channel(dev, "rx"); |
| |
| if (!ctlr->dma_rx) |
| return -ENODEV; |
| |
| ctlr->can_dma = spi_ingenic_can_dma; |
| |
| return 0; |
| } |
| |
| static void spi_ingenic_release_dma(void *data) |
| { |
| struct spi_controller *ctlr = data; |
| |
| if (ctlr->dma_tx) |
| dma_release_channel(ctlr->dma_tx); |
| if (ctlr->dma_rx) |
| dma_release_channel(ctlr->dma_rx); |
| } |
| |
| static const struct regmap_config spi_ingenic_regmap_config = { |
| .reg_bits = 32, |
| .val_bits = 32, |
| .reg_stride = 4, |
| .max_register = REG_SSIGR, |
| }; |
| |
| static int spi_ingenic_probe(struct platform_device *pdev) |
| { |
| const struct jz_soc_info *pdata; |
| struct device *dev = &pdev->dev; |
| struct spi_controller *ctlr; |
| struct ingenic_spi *priv; |
| void __iomem *base; |
| int num_cs, ret; |
| |
| pdata = of_device_get_match_data(dev); |
| if (!pdata) { |
| dev_err(dev, "Missing platform data.\n"); |
| return -EINVAL; |
| } |
| |
| ctlr = devm_spi_alloc_host(dev, sizeof(*priv)); |
| if (!ctlr) { |
| dev_err(dev, "Unable to allocate SPI controller.\n"); |
| return -ENOMEM; |
| } |
| |
| priv = spi_controller_get_devdata(ctlr); |
| priv->soc_info = pdata; |
| |
| priv->clk = devm_clk_get(dev, NULL); |
| if (IS_ERR(priv->clk)) { |
| return dev_err_probe(dev, PTR_ERR(priv->clk), |
| "Unable to get clock.\n"); |
| } |
| |
| base = devm_platform_get_and_ioremap_resource(pdev, 0, &priv->mem_res); |
| if (IS_ERR(base)) |
| return PTR_ERR(base); |
| |
| priv->map = devm_regmap_init_mmio(dev, base, &spi_ingenic_regmap_config); |
| if (IS_ERR(priv->map)) |
| return PTR_ERR(priv->map); |
| |
| priv->flen_field = devm_regmap_field_alloc(dev, priv->map, |
| pdata->flen_field); |
| if (IS_ERR(priv->flen_field)) |
| return PTR_ERR(priv->flen_field); |
| |
| if (device_property_read_u32(dev, "num-cs", &num_cs)) |
| num_cs = pdata->max_native_cs; |
| |
| platform_set_drvdata(pdev, ctlr); |
| |
| ctlr->prepare_transfer_hardware = spi_ingenic_prepare_hardware; |
| ctlr->unprepare_transfer_hardware = spi_ingenic_unprepare_hardware; |
| ctlr->prepare_message = spi_ingenic_prepare_message; |
| ctlr->set_cs = spi_ingenic_set_cs; |
| ctlr->transfer_one = spi_ingenic_transfer_one; |
| ctlr->mode_bits = SPI_MODE_3 | SPI_LSB_FIRST | SPI_LOOP | SPI_CS_HIGH; |
| ctlr->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX; |
| ctlr->max_dma_len = SPI_INGENIC_FIFO_SIZE; |
| ctlr->bits_per_word_mask = pdata->bits_per_word_mask; |
| ctlr->min_speed_hz = 7200; |
| ctlr->max_speed_hz = pdata->max_speed_hz; |
| ctlr->use_gpio_descriptors = true; |
| ctlr->max_native_cs = pdata->max_native_cs; |
| ctlr->num_chipselect = num_cs; |
| ctlr->dev.of_node = pdev->dev.of_node; |
| |
| if (spi_ingenic_request_dma(ctlr, dev)) |
| dev_warn(dev, "DMA not available.\n"); |
| |
| ret = devm_add_action_or_reset(dev, spi_ingenic_release_dma, ctlr); |
| if (ret) { |
| dev_err(dev, "Unable to add action.\n"); |
| return ret; |
| } |
| |
| ret = devm_spi_register_controller(dev, ctlr); |
| if (ret) |
| dev_err(dev, "Unable to register SPI controller.\n"); |
| |
| return ret; |
| } |
| |
| static const struct jz_soc_info jz4750_soc_info = { |
| .bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 17), |
| .flen_field = REG_FIELD(REG_SSICR1, 4, 7), |
| .has_trendian = false, |
| |
| .max_speed_hz = 54000000, |
| .max_native_cs = 2, |
| }; |
| |
| static const struct jz_soc_info jz4780_soc_info = { |
| .bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 32), |
| .flen_field = REG_FIELD(REG_SSICR1, 3, 7), |
| .has_trendian = true, |
| |
| .max_speed_hz = 54000000, |
| .max_native_cs = 2, |
| }; |
| |
| static const struct jz_soc_info x1000_soc_info = { |
| .bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 32), |
| .flen_field = REG_FIELD(REG_SSICR1, 3, 7), |
| .has_trendian = true, |
| |
| .max_speed_hz = 50000000, |
| .max_native_cs = 2, |
| }; |
| |
| static const struct jz_soc_info x2000_soc_info = { |
| .bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 32), |
| .flen_field = REG_FIELD(REG_SSICR1, 3, 7), |
| .has_trendian = true, |
| |
| .max_speed_hz = 50000000, |
| .max_native_cs = 1, |
| }; |
| |
| static const struct of_device_id spi_ingenic_of_match[] = { |
| { .compatible = "ingenic,jz4750-spi", .data = &jz4750_soc_info }, |
| { .compatible = "ingenic,jz4775-spi", .data = &jz4780_soc_info }, |
| { .compatible = "ingenic,jz4780-spi", .data = &jz4780_soc_info }, |
| { .compatible = "ingenic,x1000-spi", .data = &x1000_soc_info }, |
| { .compatible = "ingenic,x2000-spi", .data = &x2000_soc_info }, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(of, spi_ingenic_of_match); |
| |
| static struct platform_driver spi_ingenic_driver = { |
| .driver = { |
| .name = "spi-ingenic", |
| .of_match_table = spi_ingenic_of_match, |
| }, |
| .probe = spi_ingenic_probe, |
| }; |
| |
| module_platform_driver(spi_ingenic_driver); |
| MODULE_DESCRIPTION("SPI bus driver for the Ingenic SoCs"); |
| MODULE_AUTHOR("Artur Rojek <contact@artur-rojek.eu>"); |
| MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); |
| MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>"); |
| MODULE_LICENSE("GPL"); |