/* * Writing to FRAMECNT in REG_CONTROL will reset the frame count, taking * a shortcut requires an explicit clear.
*/ if (frames == len) {
mchp_corespi_write(spi, REG_COMMAND, COMMAND_CLRFRAMECNT); return;
}
/* * The lower 16 bits of the frame count are stored in the control reg * for legacy reasons, but the upper 16 written to a different register: * FRAMESUP. While both the upper and lower bits can be *READ* from the * FRAMESUP register, writing to the lower 16 bits is (supposedly) a NOP. * * The driver used to disable the controller while modifying the frame * count, and mask off the lower 16 bits of len while writing to * FRAMES_UP. When the driver was changed to disable the controller as * infrequently as possible, it was discovered that the logic of * lenpart = len & 0xffff_0000 * write(REG_FRAMESUP, lenpart) * would actually write zeros into the lower 16 bits on an mpfs250t-es, * despite documentation stating these bits were read-only. * Writing len unmasked into FRAMES_UP ensures those bits aren't zeroed * on an mpfs250t-es and will be a NOP for the lower 16 bits on hardware * that matches the documentation.
*/
lenpart = len & 0xffff;
control = mchp_corespi_read(spi, REG_CONTROL);
control &= ~CONTROL_FRAMECNT_MASK;
control |= lenpart << CONTROL_FRAMECNT_SHIFT;
mchp_corespi_write(spi, REG_CONTROL, control);
mchp_corespi_write(spi, REG_FRAMESUP, len);
}
staticinlinevoid mchp_corespi_write_fifo(struct mchp_corespi *spi, int fifo_max)
{ int i = 0;
/* * Disable the SPI controller. Writes to the frame size have * no effect when the controller is enabled.
*/
control = mchp_corespi_read(spi, REG_CONTROL);
control &= ~CONTROL_ENABLE;
mchp_corespi_write(spi, REG_CONTROL, control);
mchp_corespi_write(spi, REG_FRAME_SIZE, bt);
control |= CONTROL_ENABLE;
mchp_corespi_write(spi, REG_CONTROL, control);
}
/* * Only deassert chip select immediately. Writing to some registers * requires the controller to be disabled, which results in the * output pins being tristated and can cause the SCLK and MOSI lines * to transition. Therefore asserting the chip select is deferred * until just before writing to the TX FIFO, to ensure the device * doesn't see any spurious clock transitions whilst CS is enabled.
*/ if (((spi->mode & SPI_CS_HIGH) == 0) == disable)
mchp_corespi_write(corespi, REG_SLAVE_SELECT, reg);
}
/* * Active high targets need to be specifically set to their inactive * states during probe by adding them to the "control group" & thus * driving their select line low.
*/ if (spi->mode & SPI_CS_HIGH) {
reg = mchp_corespi_read(corespi, REG_SLAVE_SELECT);
reg |= BIT(spi_get_chipselect(spi, 0));
corespi->pending_slave_select = reg;
mchp_corespi_write(corespi, REG_SLAVE_SELECT, reg);
} return 0;
}
control &= ~CONTROL_ENABLE;
mchp_corespi_write(spi, REG_CONTROL, control);
control |= CONTROL_MASTER;
control &= ~CONTROL_MODE_MASK;
control |= MOTOROLA_MODE;
/* * The controller must be configured so that it doesn't remove Chip * Select until the entire message has been transferred, even if at * some points TX FIFO becomes empty. * * BIGFIFO mode is also enabled, which sets the fifo depth to 32 frames * for the 8 bit transfers that this driver uses.
*/
control |= CONTROL_SPS | CONTROL_BIGFIFO;
/* max. possible spi clock rate is the apb clock rate */
clk_hz = clk_get_rate(spi->clk);
host->max_speed_hz = clk_hz;
mchp_corespi_enable_ints(spi);
/* * It is required to enable direct mode, otherwise control over the chip * select is relinquished to the hardware. SSELOUT is enabled too so we * can deal with active high targets.
*/
spi->pending_slave_select = SSELOUT | SSEL_DIRECT;
mchp_corespi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select);
control = mchp_corespi_read(spi, REG_CONTROL);
control &= ~CONTROL_RESET;
control |= CONTROL_ENABLE;
/* * There are two possible clock modes for the controller generated * clock's division ratio: * CLK_MODE = 0: 1 / (2^(CLK_GEN + 1)) where CLK_GEN = 0 to 15. * CLK_MODE = 1: 1 / (2 * CLK_GEN + 1) where CLK_GEN = 0 to 255. * First try mode 1, fall back to 0 and if we have tried both modes and * we /still/ can't get a good setting, we then throw the toys out of * the pram and give up * clk_gen is the register name for the clock divider on MPFS.
*/
clk_gen = DIV_ROUND_UP(clk_hz, 2 * spi_hz) - 1; if (clk_gen > CLK_GEN_MODE1_MAX || clk_gen <= CLK_GEN_MIN) {
clk_gen = DIV_ROUND_UP(clk_hz, spi_hz);
clk_gen = fls(clk_gen) - 1;
ret = mchp_corespi_calculate_clkgen(spi, (unsignedlong)xfer->speed_hz); if (ret) {
dev_err(&host->dev, "failed to set clk_gen for target %u Hz\n", xfer->speed_hz); return ret;
}
spi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(spi->regs)) return PTR_ERR(spi->regs);
spi->irq = platform_get_irq(pdev, 0); if (spi->irq < 0) return spi->irq;
ret = devm_request_irq(&pdev->dev, spi->irq, mchp_corespi_interrupt,
IRQF_SHARED, dev_name(&pdev->dev), host); if (ret) return dev_err_probe(&pdev->dev, ret, "could not request irq\n");
spi->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(spi->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(spi->clk), "could not get clk\n");
mchp_corespi_init(host, spi);
ret = devm_spi_register_controller(&pdev->dev, host); if (ret) {
mchp_corespi_disable(spi); return dev_err_probe(&pdev->dev, ret, "unable to register host for SPI controller\n");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.