--- zzzz-none-000/linux-4.19.183/drivers/spi/spi-bcm63xx-hsspi.c 2021-03-24 10:07:39.000000000 +0000 +++ bcm63-7530ax-756/linux-4.19.183/drivers/spi/spi-bcm63xx-hsspi.c 2023-06-28 08:54:19.000000000 +0000 @@ -20,6 +20,17 @@ #include #include #include +#if defined(CONFIG_BCM_KF_SPI) +#include +#include + +/* Broadcom Legacy SPI device driver flags */ +#define SPIDEV_CONTROLLER_STATE_SET BIT(31) +#define SPIDEV_CONTROLLER_STATE_GATE_CLK_SSOFF BIT(29) + +#define spidev_ctrl_data(spi) \ + ((u32)((uintptr_t)(spi)->controller_data)) +#endif #define HSSPI_GLOBAL_CTRL_REG 0x0 #define GLOBAL_CTRL_CS_POLARITY_SHIFT 0 @@ -55,6 +66,9 @@ #define PINGPONG_CMD_SS_SHIFT 12 #define HSSPI_PINGPONG_STATUS_REG(x) (0x84 + (x) * 0x40) +#ifdef CONFIG_BCM_KF_SPI +#define HSSPI_PINGPONG_STATUS_SRC_BUSY BIT(1) +#endif #define HSSPI_PROFILE_CLK_CTRL_REG(x) (0x100 + (x) * 0x20) #define CLK_CTRL_FREQ_CTRL_MASK 0x0000ffff @@ -76,7 +90,6 @@ #define HSSPI_FIFO_REG(x) (0x200 + (x) * 0x200) - #define HSSPI_OP_MULTIBIT BIT(11) #define HSSPI_OP_CODE_SHIFT 13 #define HSSPI_OP_SLEEP (0 << HSSPI_OP_CODE_SHIFT) @@ -93,7 +106,7 @@ #define HSSPI_MAX_SYNC_CLOCK 30000000 #define HSSPI_SPI_MAX_CS 8 -#define HSSPI_BUS_NUM 1 /* 0 is legacy SPI */ +#define HSSPI_BUS_NUM 1 /* 0 is legacy SPI */ struct bcm63xx_hsspi { struct completion done; @@ -107,8 +120,199 @@ u32 speed_hz; u8 cs_polarity; +#if defined(CONFIG_BCM_KF_SPI) + int use_cswar; + int polling; + u32 prepend_cnt; + u8 *prepend_buf; +#endif }; +#if defined(CONFIG_BCM_KF_SPI) + +static void bcm63xx_hsspi_set_clk(struct bcm63xx_hsspi *bs, + struct spi_device *spi, int hz); + +static inline int bcm63xx_hsspi_dev_no_clk_gate(struct spi_device *spi) +{ + u32 value = 0; + + /* check spi device dn first */ + if (of_property_read_u32(spi->dev.of_node, "no_clk_gate", &value) == 0) + return value; + + /* check spi dev controller data for legacy device support */ + value = spidev_ctrl_data(spi); + return ((value & SPIDEV_CONTROLLER_STATE_SET) && + !(value & SPIDEV_CONTROLLER_STATE_GATE_CLK_SSOFF)); +} + +static size_t bcm63xx_hsspi_max_message_size(struct spi_device *spi) +{ + return (HSSPI_BUFFER_LEN - HSSPI_OPCODE_LEN); +} + +static void bcm63xx_hsspi_set_clk_gate(struct bcm63xx_hsspi *bs, + struct spi_device *spi) +{ + u32 reg = 0; + + if (bcm63xx_hsspi_dev_no_clk_gate(spi)) { + mutex_lock(&bs->bus_mutex); + reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); + reg |= GLOBAL_CTRL_CLK_GATE_SSOFF; + __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG); + mutex_unlock(&bs->bus_mutex); + } +} + +static bool bcm63xx_check_msg_prependable(struct spi_master *master, + struct spi_message *msg, + struct spi_transfer *t_prepend) +{ + + struct bcm63xx_hsspi *bs = spi_master_get_devdata(master); + bool prepend = false, tx_only = false; + struct spi_transfer *t; + + /* If cs dummy workaround used, no need to prepend message */ + if (bs->use_cswar) + goto check_done; + + /* + * message must only contain n half duplex write transfer + optional + * full duplex read/write at the end. There must be no udelay between + * transfers and no cs_change request + */ + bs->prepend_cnt = 0; + list_for_each_entry(t, &msg->transfers, transfer_list) { + if (t->delay_usecs || t->cs_change) { + dev_warn(&bs->pdev->dev, + "prepend does not support delay or cs change between transfers!\n"); + break; + } + + tx_only = false; + if (t->tx_buf && !t->rx_buf) { + tx_only = true; + if (bs->prepend_cnt + t->len > + (HSSPI_BUFFER_LEN - HSSPI_OPCODE_LEN)) { + dev_warn(&bs->pdev->dev, + "exceed max prepend count abort prepending transfers!\n"); + break; + } + memcpy(bs->prepend_buf + bs->prepend_cnt, t->tx_buf, + t->len); + bs->prepend_cnt += t->len; + } else { + if (!list_is_last(&t->transfer_list, &msg->transfers)) { + dev_warn(&bs->pdev->dev, + "can not prepend message when rx/tx_rx transfer is not the last transfer!\n"); + break; + } + } + + if (list_is_last(&t->transfer_list, &msg->transfers)) { + memcpy(t_prepend, t, sizeof(struct spi_transfer)); + /* if the last is also a tx only transfer, merge all + * them into one single tx transfer + */ + if (tx_only) { + t_prepend->len = bs->prepend_cnt; + t_prepend->tx_buf = bs->prepend_buf; + bs->prepend_cnt = 0; + } + prepend = true; + } + } + +check_done: + if (!bs->use_cswar && !prepend) + dev_warn(&bs->pdev->dev, + "SPI message not prependable and cs workaround not used. SPI transfer may fail!\n"); + return prepend; +} + +static int bcm63xx_hsspi_do_prepend_txrx(struct spi_device *spi, + struct spi_transfer *t) +{ + struct bcm63xx_hsspi *bs = spi_master_get_devdata(spi->master); + unsigned int chip_select = spi->chip_select; + u16 opcode = 0; + const u8 *tx = t->tx_buf; + u8 *rx = t->rx_buf; + u32 reg = 0; + + /* shouldnt happen as we set the max_message_size in the probe. + * but check it again in case some driver does not honor the max size + */ + if (t->len + bs->prepend_cnt > (HSSPI_BUFFER_LEN - HSSPI_OPCODE_LEN)) { + dev_warn(&bs->pdev->dev, + "Prepend message large than fifo size len %d prepend %d\n", + t->len, bs->prepend_cnt); + return -EINVAL; + } + + bcm63xx_hsspi_set_clk(bs, spi, t->speed_hz); + + if (tx && rx) + opcode = HSSPI_OP_READ_WRITE; + else if (tx) + opcode = HSSPI_OP_WRITE; + else if (rx) + opcode = HSSPI_OP_READ; + + if ((opcode == HSSPI_OP_READ && t->rx_nbits == SPI_NBITS_DUAL) || + (opcode == HSSPI_OP_WRITE && t->tx_nbits == SPI_NBITS_DUAL)) { + opcode |= HSSPI_OP_MULTIBIT; + + if (t->rx_nbits == SPI_NBITS_DUAL) + reg |= 1 << MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT; + if (t->tx_nbits == SPI_NBITS_DUAL) + reg |= 1 << MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT; + } + + reg |= bs->prepend_cnt << MODE_CTRL_PREPENDBYTE_CNT_SHIFT; + __raw_writel(reg | 0xff, + bs->regs + HSSPI_PROFILE_MODE_CTRL_REG(chip_select)); + + reinit_completion(&bs->done); + if (bs->prepend_cnt) + memcpy_toio(bs->fifo + HSSPI_OPCODE_LEN, bs->prepend_buf, + bs->prepend_cnt); + if (tx) + memcpy_toio(bs->fifo + HSSPI_OPCODE_LEN + bs->prepend_cnt, tx, + t->len); + + __raw_writew(cpu_to_be16(opcode | t->len), bs->fifo); + /* enable interrupt */ + __raw_writel(HSSPI_PINGx_CMD_DONE(0), bs->regs + HSSPI_INT_MASK_REG); + + /* start the transfer */ + reg = chip_select << PINGPONG_CMD_SS_SHIFT | + chip_select << PINGPONG_CMD_PROFILE_SHIFT | + PINGPONG_COMMAND_START_NOW; + __raw_writel(reg, bs->regs + HSSPI_PINGPONG_COMMAND_REG(0)); + + if (!oops_in_progress && !bs->polling) { + if (wait_for_completion_timeout(&bs->done, HZ) == 0) { + dev_err(&bs->pdev->dev, "transfer timed out!\n"); + return -ETIMEDOUT; + } + } else { + while (__raw_readl(bs->regs + HSSPI_PINGPONG_STATUS_REG(0)) & + HSSPI_PINGPONG_STATUS_SRC_BUSY) + cpu_relax(); + } + + if (rx) + memcpy_fromio(rx, bs->fifo, t->len); + + return 0; + +} +#endif + static void bcm63xx_hsspi_set_cs(struct bcm63xx_hsspi *bs, unsigned int cs, bool active) { @@ -148,6 +352,13 @@ reg &= ~GLOBAL_CTRL_CLK_POLARITY; if (spi->mode & SPI_CPOL) reg |= GLOBAL_CTRL_CLK_POLARITY; + +#if defined(CONFIG_BCM_KF_SPI) + if (bcm63xx_hsspi_dev_no_clk_gate(spi)) + reg &= ~GLOBAL_CTRL_CLK_GATE_SSOFF; + else + reg |= GLOBAL_CTRL_CLK_GATE_SSOFF; +#endif __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG); mutex_unlock(&bs->bus_mutex); } @@ -161,9 +372,16 @@ int step_size = HSSPI_BUFFER_LEN; const u8 *tx = t->tx_buf; u8 *rx = t->rx_buf; +#if defined(CONFIG_BCM_KF_SPI) + u32 reg = 0; +#endif bcm63xx_hsspi_set_clk(bs, spi, t->speed_hz); - bcm63xx_hsspi_set_cs(bs, spi->chip_select, true); + +#if defined(CONFIG_BCM_KF_SPI) + if (bs->use_cswar) +#endif + bcm63xx_hsspi_set_cs(bs, spi->chip_select, true); if (tx && rx) opcode = HSSPI_OP_READ_WRITE; @@ -175,6 +393,20 @@ if (opcode != HSSPI_OP_READ) step_size -= HSSPI_OPCODE_LEN; +#if defined(CONFIG_BCM_KF_SPI) + if ((opcode == HSSPI_OP_READ && t->rx_nbits == SPI_NBITS_DUAL) || + (opcode == HSSPI_OP_WRITE && t->tx_nbits == SPI_NBITS_DUAL)) { + opcode |= HSSPI_OP_MULTIBIT; + + if (t->rx_nbits == SPI_NBITS_DUAL) + reg |= 1 << MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT; + if (t->tx_nbits == SPI_NBITS_DUAL) + reg |= 1 << MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT; + } + + __raw_writel(reg | 0xff, + bs->regs + HSSPI_PROFILE_MODE_CTRL_REG(chip_select)); +#else if ((opcode == HSSPI_OP_READ && t->rx_nbits == SPI_NBITS_DUAL) || (opcode == HSSPI_OP_WRITE && t->tx_nbits == SPI_NBITS_DUAL)) opcode |= HSSPI_OP_MULTIBIT; @@ -182,6 +414,7 @@ __raw_writel(1 << MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT | 1 << MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT | 0xff, bs->regs + HSSPI_PROFILE_MODE_CTRL_REG(chip_select)); +#endif while (pending > 0) { int curr_step = min_t(int, step_size, pending); @@ -191,24 +424,49 @@ memcpy_toio(bs->fifo + HSSPI_OPCODE_LEN, tx, curr_step); tx += curr_step; } - +#if defined(CONFIG_BCM_KF_SPI) + __raw_writew(cpu_to_be16(opcode | curr_step), bs->fifo); +#else __raw_writew(opcode | curr_step, bs->fifo); +#endif /* enable interrupt */ __raw_writel(HSSPI_PINGx_CMD_DONE(0), bs->regs + HSSPI_INT_MASK_REG); - +#if defined(CONFIG_BCM_KF_SPI) + /* start the transfer */ + if (!bs->use_cswar) + reg = chip_select << PINGPONG_CMD_SS_SHIFT | + chip_select << PINGPONG_CMD_PROFILE_SHIFT | + PINGPONG_COMMAND_START_NOW; + else + reg = !chip_select << PINGPONG_CMD_SS_SHIFT | + chip_select << PINGPONG_CMD_PROFILE_SHIFT | + PINGPONG_COMMAND_START_NOW; + __raw_writel(reg, bs->regs + HSSPI_PINGPONG_COMMAND_REG(0)); +#else /* start the transfer */ __raw_writel(!chip_select << PINGPONG_CMD_SS_SHIFT | chip_select << PINGPONG_CMD_PROFILE_SHIFT | PINGPONG_COMMAND_START_NOW, bs->regs + HSSPI_PINGPONG_COMMAND_REG(0)); +#endif - if (wait_for_completion_timeout(&bs->done, HZ) == 0) { - dev_err(&bs->pdev->dev, "transfer timed out!\n"); - return -ETIMEDOUT; +#ifdef CONFIG_BCM_KF_SPI + if (!oops_in_progress && !bs->polling) { +#endif + if (wait_for_completion_timeout(&bs->done, HZ) == 0) { + dev_err(&bs->pdev->dev, "transfer timed out!\n"); + return -ETIMEDOUT; + } +#ifdef CONFIG_BCM_KF_SPI + } else { + while (__raw_readl(bs->regs + + HSSPI_PINGPONG_STATUS_REG(0)) & + HSSPI_PINGPONG_STATUS_SRC_BUSY) + cpu_relax(); } - +#endif if (rx) { memcpy_fromio(rx, bs->fifo, curr_step); rx += curr_step; @@ -266,6 +524,21 @@ int status = -EINVAL; int dummy_cs; u32 reg; +#if defined(CONFIG_BCM_KF_SPI) + bool restore_polarity = true; + bool prepend = false; + struct spi_transfer t_prepend; +#endif + +#if defined(CONFIG_BCM_KF_SPI) + prepend = bcm63xx_check_msg_prependable(master, msg, &t_prepend); + if (prepend) { + status = bcm63xx_hsspi_do_prepend_txrx(spi, &t_prepend); + msg->actual_length += (t_prepend.len + bs->prepend_cnt); + bcm63xx_hsspi_set_clk_gate(bs, spi); + goto msg_done; + } +#endif /* This controller does not support keeping CS active during idle. * To work around this, we use the following ugly hack: @@ -281,8 +554,14 @@ * e. At the end restore the polarities again to their default values. */ - dummy_cs = !spi->chip_select; - bcm63xx_hsspi_set_cs(bs, dummy_cs, true); +#if defined(CONFIG_BCM_KF_SPI) + if (bs->use_cswar) { +#endif + dummy_cs = !spi->chip_select; + bcm63xx_hsspi_set_cs(bs, dummy_cs, true); +#if defined(CONFIG_BCM_KF_SPI) + } +#endif list_for_each_entry(t, &msg->transfers, transfer_list) { status = bcm63xx_hsspi_do_txrx(spi, t); @@ -294,16 +573,57 @@ if (t->delay_usecs) udelay(t->delay_usecs); +#if defined(CONFIG_BCM_KF_SPI) + /* + * cs_change rules: + * (1) cs_change = 0 && last_xfer = 0: + * Do not touch the CS. On to the next xfer. + * (2) cs_change = 1 && last_xfer = 0: + * Set cs = false before the next xfer. + * (3) cs_change = 0 && last_xfer = 1: + * We want CS to be deactivated. So do NOT set cs = false, + * instead just restore the original polarity. This has the + * same effect of deactivating the CS. + * (4) cs_change = 1 && last_xfer = 1: + * We want to keep CS active. So do NOT set cs = false, and + * make sure we do NOT reverse polarity. + */ + if (t->cs_change + && !list_is_last(&t->transfer_list, &msg->transfers)) +#else if (t->cs_change) +#endif bcm63xx_hsspi_set_cs(bs, spi->chip_select, false); +#if defined(CONFIG_BCM_KF_SPI) + + restore_polarity = !t->cs_change; +#endif } +#if defined(CONFIG_BCM_KF_SPI) + if (restore_polarity && bs->use_cswar) { + mutex_lock(&bs->bus_mutex); + reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); + reg &= ~GLOBAL_CTRL_CS_POLARITY_MASK; + reg |= bs->cs_polarity; + __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG); + mutex_unlock(&bs->bus_mutex); + } + + /* restore the default clk gate setting in case some + * spidev turn it off + */ + bcm63xx_hsspi_set_clk_gate(bs, spi); + +msg_done: +#else mutex_lock(&bs->bus_mutex); reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG); reg &= ~GLOBAL_CTRL_CS_POLARITY_MASK; reg |= bs->cs_polarity; __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG); mutex_unlock(&bs->bus_mutex); +#endif msg->status = status; spi_finalize_current_message(master); @@ -330,21 +650,50 @@ { struct spi_master *master; struct bcm63xx_hsspi *bs; +#if defined(CONFIG_BCM_KF_SPI) + struct resource res_mem; +#else struct resource *res_mem; +#endif void __iomem *regs; struct device *dev = &pdev->dev; struct clk *clk, *pll_clk = NULL; int irq, ret; u32 reg, rate, num_cs = HSSPI_SPI_MAX_CS; +#if defined(CONFIG_BCM_KF_SPI) + int polling = 0; + irq = irq_of_parse_and_map(pdev->dev.of_node, 0); +#else irq = platform_get_irq(pdev, 0); +#endif + +#if defined(CONFIG_BCM_KF_SPI) + if (!irq) { + /* + * switch to polling if intr is not defined and + * for better throughput as well + */ + dev_err(dev, "spi driver using polling mode\n"); + polling = 1; + } +#else if (irq < 0) { dev_err(dev, "no irq: %d\n", irq); return irq; } +#endif + +#if defined(CONFIG_BCM_KF_SPI) + ret = of_address_to_resource(pdev->dev.of_node, 0, &res_mem); + if (ret) + return -EINVAL; + regs = devm_ioremap_resource(dev, &res_mem); +#else res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(dev, res_mem); +#endif if (IS_ERR(regs)) return PTR_ERR(regs); @@ -389,7 +738,20 @@ bs->pll_clk = pll_clk; bs->regs = regs; bs->speed_hz = rate; - bs->fifo = (u8 __iomem *)(bs->regs + HSSPI_FIFO_REG(0)); + bs->fifo = (u8 __iomem *) (bs->regs + HSSPI_FIFO_REG(0)); +#if defined(CONFIG_BCM_KF_SPI) + bs->polling = polling; + bs->prepend_buf = kmalloc(HSSPI_BUFFER_LEN, GFP_KERNEL); + if (!bs->prepend_buf) { + ret = -ENOMEM; + goto out_put_master; + } + + /* check if dummy cs workaround is needed */ + if (of_property_read_u32 + (dev->of_node, "use_cs_workaround", &bs->use_cswar)) + bs->use_cswar = 0; +#endif mutex_init(&bs->bus_mutex); init_completion(&bs->done); @@ -407,8 +769,14 @@ master->num_chipselect = num_cs; master->setup = bcm63xx_hsspi_setup; master->transfer_one_message = bcm63xx_hsspi_transfer_one; +#if defined(CONFIG_BCM_KF_SPI) + if (bs->use_cswar == 0) { + master->max_transfer_size = bcm63xx_hsspi_max_message_size; + master->max_message_size = bcm63xx_hsspi_max_message_size; + } +#endif master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | - SPI_RX_DUAL | SPI_TX_DUAL; + SPI_RX_DUAL | SPI_TX_DUAL; master->bits_per_word_mask = SPI_BPW_MASK(8); master->auto_runtime_pm = true; @@ -426,12 +794,17 @@ __raw_writel(reg | GLOBAL_CTRL_CLK_GATE_SSOFF, bs->regs + HSSPI_GLOBAL_CTRL_REG); +#if defined(CONFIG_BCM_KF_SPI) + if (bs->polling == 0) { +#endif ret = devm_request_irq(dev, irq, bcm63xx_hsspi_interrupt, IRQF_SHARED, pdev->name, bs); if (ret) goto out_put_master; - +#if defined(CONFIG_BCM_KF_SPI) + } +#endif /* register and we are done */ ret = devm_spi_register_master(dev, master); if (ret) @@ -448,7 +821,6 @@ return ret; } - static int bcm63xx_hsspi_remove(struct platform_device *pdev) { struct spi_master *master = platform_get_drvdata(pdev); @@ -503,19 +875,20 @@ bcm63xx_hsspi_resume); static const struct of_device_id bcm63xx_hsspi_of_match[] = { - { .compatible = "brcm,bcm6328-hsspi", }, - { }, + {.compatible = "brcm,bcm6328-hsspi",}, + {}, }; + MODULE_DEVICE_TABLE(of, bcm63xx_hsspi_of_match); static struct platform_driver bcm63xx_hsspi_driver = { .driver = { - .name = "bcm63xx-hsspi", - .pm = &bcm63xx_hsspi_pm_ops, - .of_match_table = bcm63xx_hsspi_of_match, - }, - .probe = bcm63xx_hsspi_probe, - .remove = bcm63xx_hsspi_remove, + .name = "bcm63xx-hsspi", + .pm = &bcm63xx_hsspi_pm_ops, + .of_match_table = bcm63xx_hsspi_of_match, + }, + .probe = bcm63xx_hsspi_probe, + .remove = bcm63xx_hsspi_remove, }; module_platform_driver(bcm63xx_hsspi_driver);