Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Jay Fang | 2278 | 96.12% | 3 | 33.33% |
Yang Yingliang | 43 | 1.81% | 1 | 11.11% |
Devyn Liu | 25 | 1.05% | 2 | 22.22% |
oujiefeng | 19 | 0.80% | 1 | 11.11% |
Yeqi Fu | 3 | 0.13% | 1 | 11.11% |
Uwe Kleine-König | 2 | 0.08% | 1 | 11.11% |
Total | 2370 | 9 |
// SPDX-License-Identifier: GPL-2.0-only // // HiSilicon SPI Controller Driver for Kunpeng SoCs // // Copyright (c) 2021 HiSilicon Technologies Co., Ltd. // Author: Jay Fang <f.fangjian@huawei.com> // // This code is based on spi-dw-core.c. #include <linux/acpi.h> #include <linux/bitfield.h> #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/property.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/spi/spi.h> /* Register offsets */ #define HISI_SPI_CSCR 0x00 /* cs control register */ #define HISI_SPI_CR 0x04 /* spi common control register */ #define HISI_SPI_ENR 0x08 /* spi enable register */ #define HISI_SPI_FIFOC 0x0c /* fifo level control register */ #define HISI_SPI_IMR 0x10 /* interrupt mask register */ #define HISI_SPI_DIN 0x14 /* data in register */ #define HISI_SPI_DOUT 0x18 /* data out register */ #define HISI_SPI_SR 0x1c /* status register */ #define HISI_SPI_RISR 0x20 /* raw interrupt status register */ #define HISI_SPI_ISR 0x24 /* interrupt status register */ #define HISI_SPI_ICR 0x28 /* interrupt clear register */ #define HISI_SPI_VERSION 0xe0 /* version register */ /* Bit fields in HISI_SPI_CR */ #define CR_LOOP_MASK GENMASK(1, 1) #define CR_CPOL_MASK GENMASK(2, 2) #define CR_CPHA_MASK GENMASK(3, 3) #define CR_DIV_PRE_MASK GENMASK(11, 4) #define CR_DIV_POST_MASK GENMASK(19, 12) #define CR_BPW_MASK GENMASK(24, 20) #define CR_SPD_MODE_MASK GENMASK(25, 25) /* Bit fields in HISI_SPI_FIFOC */ #define FIFOC_TX_MASK GENMASK(5, 3) #define FIFOC_RX_MASK GENMASK(11, 9) /* Bit fields in HISI_SPI_IMR, 4 bits */ #define IMR_RXOF BIT(0) /* Receive Overflow */ #define IMR_RXTO BIT(1) /* Receive Timeout */ #define IMR_RX BIT(2) /* Receive */ #define IMR_TX BIT(3) /* Transmit */ #define IMR_MASK (IMR_RXOF | IMR_RXTO | IMR_RX | IMR_TX) /* Bit fields in HISI_SPI_SR, 5 bits */ #define SR_TXE BIT(0) /* Transmit FIFO empty */ #define SR_TXNF BIT(1) /* Transmit FIFO not full */ #define SR_RXNE BIT(2) /* Receive FIFO not empty */ #define SR_RXF BIT(3) /* Receive FIFO full */ #define SR_BUSY BIT(4) /* Busy Flag */ /* Bit fields in HISI_SPI_ISR, 4 bits */ #define ISR_RXOF BIT(0) /* Receive Overflow */ #define ISR_RXTO BIT(1) /* Receive Timeout */ #define ISR_RX BIT(2) /* Receive */ #define ISR_TX BIT(3) /* Transmit */ #define ISR_MASK (ISR_RXOF | ISR_RXTO | ISR_RX | ISR_TX) /* Bit fields in HISI_SPI_ICR, 2 bits */ #define ICR_RXOF BIT(0) /* Receive Overflow */ #define ICR_RXTO BIT(1) /* Receive Timeout */ #define ICR_MASK (ICR_RXOF | ICR_RXTO) #define DIV_POST_MAX 0xFF #define DIV_POST_MIN 0x00 #define DIV_PRE_MAX 0xFE #define DIV_PRE_MIN 0x02 #define CLK_DIV_MAX ((1 + DIV_POST_MAX) * DIV_PRE_MAX) #define CLK_DIV_MIN ((1 + DIV_POST_MIN) * DIV_PRE_MIN) #define DEFAULT_NUM_CS 1 #define HISI_SPI_WAIT_TIMEOUT_MS 10UL enum hisi_spi_rx_level_trig { HISI_SPI_RX_1, HISI_SPI_RX_4, HISI_SPI_RX_8, HISI_SPI_RX_16, HISI_SPI_RX_32, HISI_SPI_RX_64, HISI_SPI_RX_128 }; enum hisi_spi_tx_level_trig { HISI_SPI_TX_1_OR_LESS, HISI_SPI_TX_4_OR_LESS, HISI_SPI_TX_8_OR_LESS, HISI_SPI_TX_16_OR_LESS, HISI_SPI_TX_32_OR_LESS, HISI_SPI_TX_64_OR_LESS, HISI_SPI_TX_128_OR_LESS }; enum hisi_spi_frame_n_bytes { HISI_SPI_N_BYTES_NULL, HISI_SPI_N_BYTES_U8, HISI_SPI_N_BYTES_U16, HISI_SPI_N_BYTES_U32 = 4 }; /* Slave spi_dev related */ struct hisi_chip_data { u32 cr; u32 speed_hz; /* baud rate */ u16 clk_div; /* baud rate divider */ /* clk_div = (1 + div_post) * div_pre */ u8 div_post; /* value from 0 to 255 */ u8 div_pre; /* value from 2 to 254 (even only!) */ }; struct hisi_spi { struct device *dev; void __iomem *regs; int irq; u32 fifo_len; /* depth of the FIFO buffer */ /* Current message transfer state info */ const void *tx; unsigned int tx_len; void *rx; unsigned int rx_len; u8 n_bytes; /* current is a 1/2/4 bytes op */ struct dentry *debugfs; struct debugfs_regset32 regset; }; #define HISI_SPI_DBGFS_REG(_name, _off) \ { \ .name = _name, \ .offset = _off, \ } static const struct debugfs_reg32 hisi_spi_regs[] = { HISI_SPI_DBGFS_REG("CSCR", HISI_SPI_CSCR), HISI_SPI_DBGFS_REG("CR", HISI_SPI_CR), HISI_SPI_DBGFS_REG("ENR", HISI_SPI_ENR), HISI_SPI_DBGFS_REG("FIFOC", HISI_SPI_FIFOC), HISI_SPI_DBGFS_REG("IMR", HISI_SPI_IMR), HISI_SPI_DBGFS_REG("SR", HISI_SPI_SR), HISI_SPI_DBGFS_REG("RISR", HISI_SPI_RISR), HISI_SPI_DBGFS_REG("ISR", HISI_SPI_ISR), HISI_SPI_DBGFS_REG("ICR", HISI_SPI_ICR), HISI_SPI_DBGFS_REG("VERSION", HISI_SPI_VERSION), }; static int hisi_spi_debugfs_init(struct hisi_spi *hs) { char name[32]; struct spi_controller *host; host = container_of(hs->dev, struct spi_controller, dev); snprintf(name, 32, "hisi_spi%d", host->bus_num); hs->debugfs = debugfs_create_dir(name, NULL); if (IS_ERR(hs->debugfs)) return -ENOMEM; hs->regset.regs = hisi_spi_regs; hs->regset.nregs = ARRAY_SIZE(hisi_spi_regs); hs->regset.base = hs->regs; debugfs_create_regset32("registers", 0400, hs->debugfs, &hs->regset); return 0; } static u32 hisi_spi_busy(struct hisi_spi *hs) { return readl(hs->regs + HISI_SPI_SR) & SR_BUSY; } static u32 hisi_spi_rx_not_empty(struct hisi_spi *hs) { return readl(hs->regs + HISI_SPI_SR) & SR_RXNE; } static u32 hisi_spi_tx_not_full(struct hisi_spi *hs) { return readl(hs->regs + HISI_SPI_SR) & SR_TXNF; } static void hisi_spi_flush_fifo(struct hisi_spi *hs) { unsigned long limit = loops_per_jiffy << 1; do { while (hisi_spi_rx_not_empty(hs)) readl(hs->regs + HISI_SPI_DOUT); } while (hisi_spi_busy(hs) && limit--); } /* Disable the controller and all interrupts */ static void hisi_spi_disable(struct hisi_spi *hs) { writel(0, hs->regs + HISI_SPI_ENR); writel(IMR_MASK, hs->regs + HISI_SPI_IMR); writel(ICR_MASK, hs->regs + HISI_SPI_ICR); } static u8 hisi_spi_n_bytes(struct spi_transfer *transfer) { if (transfer->bits_per_word <= 8) return HISI_SPI_N_BYTES_U8; else if (transfer->bits_per_word <= 16) return HISI_SPI_N_BYTES_U16; else return HISI_SPI_N_BYTES_U32; } static void hisi_spi_reader(struct hisi_spi *hs) { u32 max = min_t(u32, hs->rx_len, hs->fifo_len); u32 rxw; while (hisi_spi_rx_not_empty(hs) && max--) { rxw = readl(hs->regs + HISI_SPI_DOUT); /* Check the transfer's original "rx" is not null */ if (hs->rx) { switch (hs->n_bytes) { case HISI_SPI_N_BYTES_U8: *(u8 *)(hs->rx) = rxw; break; case HISI_SPI_N_BYTES_U16: *(u16 *)(hs->rx) = rxw; break; case HISI_SPI_N_BYTES_U32: *(u32 *)(hs->rx) = rxw; break; } hs->rx += hs->n_bytes; } --hs->rx_len; } } static void hisi_spi_writer(struct hisi_spi *hs) { u32 max = min_t(u32, hs->tx_len, hs->fifo_len); u32 txw = 0; while (hisi_spi_tx_not_full(hs) && max--) { /* Check the transfer's original "tx" is not null */ if (hs->tx) { switch (hs->n_bytes) { case HISI_SPI_N_BYTES_U8: txw = *(u8 *)(hs->tx); break; case HISI_SPI_N_BYTES_U16: txw = *(u16 *)(hs->tx); break; case HISI_SPI_N_BYTES_U32: txw = *(u32 *)(hs->tx); break; } hs->tx += hs->n_bytes; } writel(txw, hs->regs + HISI_SPI_DIN); --hs->tx_len; } } static void __hisi_calc_div_reg(struct hisi_chip_data *chip) { chip->div_pre = DIV_PRE_MAX; while (chip->div_pre >= DIV_PRE_MIN) { if (chip->clk_div % chip->div_pre == 0) break; chip->div_pre -= 2; } if (chip->div_pre > chip->clk_div) chip->div_pre = chip->clk_div; chip->div_post = (chip->clk_div / chip->div_pre) - 1; } static u32 hisi_calc_effective_speed(struct spi_controller *host, struct hisi_chip_data *chip, u32 speed_hz) { u32 effective_speed; /* Note clock divider doesn't support odd numbers */ chip->clk_div = DIV_ROUND_UP(host->max_speed_hz, speed_hz) + 1; chip->clk_div &= 0xfffe; if (chip->clk_div > CLK_DIV_MAX) chip->clk_div = CLK_DIV_MAX; effective_speed = host->max_speed_hz / chip->clk_div; if (chip->speed_hz != effective_speed) { __hisi_calc_div_reg(chip); chip->speed_hz = effective_speed; } return effective_speed; } static u32 hisi_spi_prepare_cr(struct spi_device *spi) { u32 cr = FIELD_PREP(CR_SPD_MODE_MASK, 1); cr |= FIELD_PREP(CR_CPHA_MASK, (spi->mode & SPI_CPHA) ? 1 : 0); cr |= FIELD_PREP(CR_CPOL_MASK, (spi->mode & SPI_CPOL) ? 1 : 0); cr |= FIELD_PREP(CR_LOOP_MASK, (spi->mode & SPI_LOOP) ? 1 : 0); return cr; } static void hisi_spi_hw_init(struct hisi_spi *hs) { hisi_spi_disable(hs); /* FIFO default config */ writel(FIELD_PREP(FIFOC_TX_MASK, HISI_SPI_TX_64_OR_LESS) | FIELD_PREP(FIFOC_RX_MASK, HISI_SPI_RX_16), hs->regs + HISI_SPI_FIFOC); hs->fifo_len = 256; } static irqreturn_t hisi_spi_irq(int irq, void *dev_id) { struct spi_controller *host = dev_id; struct hisi_spi *hs = spi_controller_get_devdata(host); u32 irq_status = readl(hs->regs + HISI_SPI_ISR) & ISR_MASK; if (!irq_status) return IRQ_NONE; if (!host->cur_msg) return IRQ_HANDLED; /* Error handling */ if (irq_status & ISR_RXOF) { dev_err(hs->dev, "interrupt_transfer: fifo overflow\n"); host->cur_msg->status = -EIO; goto finalize_transfer; } /* * Read data from the Rx FIFO every time. If there is * nothing left to receive, finalize the transfer. */ hisi_spi_reader(hs); if (!hs->rx_len) goto finalize_transfer; /* Send data out when Tx FIFO IRQ triggered */ if (irq_status & ISR_TX) hisi_spi_writer(hs); return IRQ_HANDLED; finalize_transfer: hisi_spi_disable(hs); spi_finalize_current_transfer(host); return IRQ_HANDLED; } static int hisi_spi_transfer_one(struct spi_controller *host, struct spi_device *spi, struct spi_transfer *transfer) { struct hisi_spi *hs = spi_controller_get_devdata(host); struct hisi_chip_data *chip = spi_get_ctldata(spi); u32 cr = chip->cr; /* Update per transfer options for speed and bpw */ transfer->effective_speed_hz = hisi_calc_effective_speed(host, chip, transfer->speed_hz); cr |= FIELD_PREP(CR_DIV_PRE_MASK, chip->div_pre); cr |= FIELD_PREP(CR_DIV_POST_MASK, chip->div_post); cr |= FIELD_PREP(CR_BPW_MASK, transfer->bits_per_word - 1); writel(cr, hs->regs + HISI_SPI_CR); hisi_spi_flush_fifo(hs); hs->n_bytes = hisi_spi_n_bytes(transfer); hs->tx = transfer->tx_buf; hs->tx_len = transfer->len / hs->n_bytes; hs->rx = transfer->rx_buf; hs->rx_len = hs->tx_len; /* * Ensure that the transfer data above has been updated * before the interrupt to start. */ smp_mb(); /* Enable all interrupts and the controller */ writel(~(u32)IMR_MASK, hs->regs + HISI_SPI_IMR); writel(1, hs->regs + HISI_SPI_ENR); return 1; } static void hisi_spi_handle_err(struct spi_controller *host, struct spi_message *msg) { struct hisi_spi *hs = spi_controller_get_devdata(host); hisi_spi_disable(hs); /* * Wait for interrupt handler that is * already in timeout to complete. */ msleep(HISI_SPI_WAIT_TIMEOUT_MS); } static int hisi_spi_setup(struct spi_device *spi) { struct hisi_chip_data *chip; /* Only alloc on first setup */ chip = spi_get_ctldata(spi); if (!chip) { chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; spi_set_ctldata(spi, chip); } chip->cr = hisi_spi_prepare_cr(spi); return 0; } static void hisi_spi_cleanup(struct spi_device *spi) { struct hisi_chip_data *chip = spi_get_ctldata(spi); kfree(chip); spi_set_ctldata(spi, NULL); } static int hisi_spi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct spi_controller *host; struct hisi_spi *hs; int ret, irq; irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; host = devm_spi_alloc_host(dev, sizeof(*hs)); if (!host) return -ENOMEM; platform_set_drvdata(pdev, host); hs = spi_controller_get_devdata(host); hs->dev = dev; hs->irq = irq; hs->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hs->regs)) return PTR_ERR(hs->regs); /* Specify maximum SPI clocking speed (host only) by firmware */ ret = device_property_read_u32(dev, "spi-max-frequency", &host->max_speed_hz); if (ret) { dev_err(dev, "failed to get max SPI clocking speed, ret=%d\n", ret); return -EINVAL; } if (host->max_speed_hz == 0) return dev_err_probe(dev, -EINVAL, "spi-max-frequency can't be 0\n"); ret = device_property_read_u16(dev, "num-cs", &host->num_chipselect); if (ret) host->num_chipselect = DEFAULT_NUM_CS; host->use_gpio_descriptors = true; host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); host->bus_num = pdev->id; host->setup = hisi_spi_setup; host->cleanup = hisi_spi_cleanup; host->transfer_one = hisi_spi_transfer_one; host->handle_err = hisi_spi_handle_err; host->dev.fwnode = dev->fwnode; host->min_speed_hz = DIV_ROUND_UP(host->max_speed_hz, CLK_DIV_MAX); hisi_spi_hw_init(hs); ret = devm_request_irq(dev, hs->irq, hisi_spi_irq, 0, dev_name(dev), host); if (ret < 0) { dev_err(dev, "failed to get IRQ=%d, ret=%d\n", hs->irq, ret); return ret; } ret = spi_register_controller(host); if (ret) { dev_err(dev, "failed to register spi host, ret=%d\n", ret); return ret; } if (hisi_spi_debugfs_init(hs)) dev_info(dev, "failed to create debugfs dir\n"); dev_info(dev, "hw version:0x%x max-freq:%u kHz\n", readl(hs->regs + HISI_SPI_VERSION), host->max_speed_hz / 1000); return 0; } static void hisi_spi_remove(struct platform_device *pdev) { struct spi_controller *host = platform_get_drvdata(pdev); struct hisi_spi *hs = spi_controller_get_devdata(host); debugfs_remove_recursive(hs->debugfs); spi_unregister_controller(host); } static const struct acpi_device_id hisi_spi_acpi_match[] = { {"HISI03E1", 0}, {} }; MODULE_DEVICE_TABLE(acpi, hisi_spi_acpi_match); static struct platform_driver hisi_spi_driver = { .probe = hisi_spi_probe, .remove_new = hisi_spi_remove, .driver = { .name = "hisi-kunpeng-spi", .acpi_match_table = hisi_spi_acpi_match, }, }; module_platform_driver(hisi_spi_driver); MODULE_AUTHOR("Jay Fang <f.fangjian@huawei.com>"); MODULE_DESCRIPTION("HiSilicon SPI Controller Driver for Kunpeng SoCs"); MODULE_LICENSE("GPL v2");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1