drivers: spi: Add Intel SPI penwell driver
Added support for intel pch penwell spi driver. Signed-off-by: Ramesh Babu B <ramesh.babu.b@intel.com>
This commit is contained in:
parent
acfe688c4e
commit
3d44508c38
|
@ -35,6 +35,7 @@ zephyr_library_sources_ifdef(CONFIG_SPI_PL022 spi_pl022.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_SPI_ANDES_ATCSPI200 spi_andes_atcspi200.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_NXP_S32_SPI spi_nxp_s32.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_SPI_XMC4XXX spi_xmc4xxx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_SPI_PW spi_pw.c)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_SPI_ASYNC spi_signal.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE spi_handlers.c)
|
||||
|
|
|
@ -109,4 +109,6 @@ source "drivers/spi/Kconfig.nxp_s32"
|
|||
|
||||
source "drivers/spi/Kconfig.xmc4xxx"
|
||||
|
||||
source "drivers/spi/Kconfig.pw"
|
||||
|
||||
endif # SPI
|
||||
|
|
18
drivers/spi/Kconfig.pw
Normal file
18
drivers/spi/Kconfig.pw
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Copyright (c) 2023 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig SPI_PW
|
||||
bool "Penwell SPI driver"
|
||||
default y
|
||||
depends on DT_HAS_INTEL_PENWELL_SPI_ENABLED
|
||||
help
|
||||
Enable the Penwell SPI driver.
|
||||
|
||||
if SPI_PW
|
||||
|
||||
config SPI_PW_INTERRUPT
|
||||
bool "Penwell SPI Interrupt mode Support"
|
||||
help
|
||||
Enable Interrupt support for the SPI Driver.
|
||||
|
||||
endif # SPI_PW
|
897
drivers/spi/spi_pw.c
Normal file
897
drivers/spi/spi_pw.c
Normal file
|
@ -0,0 +1,897 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Intel Corporation.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT intel_penwell_spi
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/spi.h>
|
||||
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(pcie)
|
||||
BUILD_ASSERT(IS_ENABLED(CONFIG_PCIE), "DT need CONFIG_PCIE");
|
||||
#include <zephyr/drivers/pcie/pcie.h>
|
||||
#endif
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(spi_pw, CONFIG_SPI_LOG_LEVEL);
|
||||
|
||||
#include "spi_pw.h"
|
||||
|
||||
static uint32_t spi_pw_reg_read(const struct device *dev, uint32_t offset)
|
||||
{
|
||||
return sys_read32(DEVICE_MMIO_GET(dev) + offset);
|
||||
}
|
||||
|
||||
static void spi_pw_reg_write(const struct device *dev,
|
||||
uint32_t offset,
|
||||
uint32_t val)
|
||||
{
|
||||
return sys_write32(val, DEVICE_MMIO_GET(dev) + offset);
|
||||
}
|
||||
|
||||
static void spi_pw_ssp_reset(const struct device *dev)
|
||||
{
|
||||
/* Bring the controller from reset state in to operational mode */
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_RESETS, 0x00);
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_RESETS, PW_SPI_INST_RESET);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SPI_PW_INTERRUPT
|
||||
static bool is_spi_transfer_ongoing(struct spi_pw_data *spi)
|
||||
{
|
||||
return spi_context_tx_on(&spi->ctx) || spi_context_rx_on(&spi->ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void spi_pw_enable_cs_hw_ctrl(const struct device *dev)
|
||||
{
|
||||
uint32_t cs_ctrl;
|
||||
|
||||
cs_ctrl = spi_pw_reg_read(dev, PW_SPI_REG_CS_CTRL);
|
||||
cs_ctrl &= PW_SPI_CS_CTRL_HW_MODE;
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_CS_CTRL, cs_ctrl);
|
||||
}
|
||||
|
||||
static void spi_pw_cs_sw_ctrl(const struct device *dev, bool enable)
|
||||
{
|
||||
uint32_t cs_ctrl;
|
||||
|
||||
cs_ctrl = spi_pw_reg_read(dev, PW_SPI_REG_CS_CTRL);
|
||||
cs_ctrl &= ~(PW_SPI_CS_CTRL_CS_MASK);
|
||||
/* Enable chip select software control method */
|
||||
cs_ctrl |= PW_SPI_CS_CTRL_SW_MODE;
|
||||
|
||||
if (enable) {
|
||||
cs_ctrl &= PW_SPI_CS_LOW;
|
||||
} else {
|
||||
cs_ctrl |= PW_SPI_CS_HIGH;
|
||||
}
|
||||
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_CS_CTRL, cs_ctrl);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPI_PW_INTERRUPT
|
||||
static void spi_pw_intr_enable(const struct device *dev, bool rx_mask)
|
||||
{
|
||||
uint32_t ctrlr1;
|
||||
|
||||
ctrlr1 = spi_pw_reg_read(dev, PW_SPI_REG_CTRLR1);
|
||||
if (rx_mask) {
|
||||
ctrlr1 |= PW_SPI_INTR_BITS;
|
||||
} else {
|
||||
ctrlr1 |= PW_SPI_INTR_BITS;
|
||||
ctrlr1 &= ~(PW_SPI_INTR_MASK_RX);
|
||||
}
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_CTRLR1, ctrlr1);
|
||||
}
|
||||
|
||||
static void spi_pw_intr_disable(const struct device *dev)
|
||||
{
|
||||
uint32_t ctrlr1;
|
||||
|
||||
ctrlr1 = spi_pw_reg_read(dev, PW_SPI_REG_CTRLR1);
|
||||
ctrlr1 &= ~(PW_SPI_INTR_BITS);
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_CTRLR1, ctrlr1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void spi_pw_ssp_enable(const struct device *dev)
|
||||
{
|
||||
uint32_t ctrlr0;
|
||||
|
||||
ctrlr0 = spi_pw_reg_read(dev, PW_SPI_REG_CTRLR0);
|
||||
ctrlr0 |= PW_SPI_CTRLR0_SSE_BIT;
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_CTRLR0, ctrlr0);
|
||||
|
||||
}
|
||||
|
||||
static void spi_pw_ssp_disable(const struct device *dev)
|
||||
{
|
||||
uint32_t ctrlr0;
|
||||
|
||||
ctrlr0 = spi_pw_reg_read(dev, PW_SPI_REG_CTRLR0);
|
||||
ctrlr0 &= ~(PW_SPI_CTRLR0_SSE_BIT);
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_CTRLR0, ctrlr0);
|
||||
}
|
||||
|
||||
static bool is_pw_ssp_busy(const struct device *dev)
|
||||
{
|
||||
uint32_t status;
|
||||
|
||||
status = spi_pw_reg_read(dev, PW_SPI_REG_SSSR);
|
||||
return (status & PW_SPI_SSSR_BSY_BIT) ? true : false;
|
||||
}
|
||||
|
||||
static uint8_t spi_pw_get_frame_size(const struct spi_config *config)
|
||||
{
|
||||
uint8_t dfs = SPI_WORD_SIZE_GET(config->operation);
|
||||
|
||||
dfs /= PW_SPI_WIDTH_8BITS;
|
||||
|
||||
if ((dfs == 0) || (dfs > PW_SPI_FRAME_SIZE_4_BYTES)) {
|
||||
LOG_WRN("Unsupported dfs, 1-byte size will be used");
|
||||
dfs = PW_SPI_FRAME_SIZE_1_BYTE;
|
||||
}
|
||||
|
||||
return dfs;
|
||||
}
|
||||
|
||||
void spi_pw_cs_ctrl_enable(const struct device *dev, bool enable)
|
||||
{
|
||||
struct spi_pw_data *spi = dev->data;
|
||||
|
||||
if (enable == true) {
|
||||
if (spi->cs_mode == CS_SW_MODE) {
|
||||
spi_pw_cs_sw_ctrl(dev, true);
|
||||
} else if (spi->cs_mode == CS_GPIO_MODE) {
|
||||
spi_context_cs_control(&spi->ctx, true);
|
||||
}
|
||||
} else {
|
||||
if (spi->cs_mode == CS_SW_MODE) {
|
||||
spi_pw_cs_sw_ctrl(dev, false);
|
||||
} else if (spi->cs_mode == CS_GPIO_MODE) {
|
||||
spi_context_cs_control(&spi->ctx, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void spi_pw_cs_ctrl_init(const struct device *dev)
|
||||
{
|
||||
uint32_t cs_ctrl;
|
||||
struct spi_pw_data *spi = dev->data;
|
||||
|
||||
/* Enable chip select output CS0/CS1 */
|
||||
cs_ctrl = spi_pw_reg_read(dev, PW_SPI_REG_CS_CTRL);
|
||||
|
||||
if (spi->cs_output == PW_SPI_CS1_OUTPUT_SELECT) {
|
||||
cs_ctrl &= ~(PW_SPI_CS_CTRL_CS_MASK << PW_SPI_CS_EN_SHIFT);
|
||||
/* Set chip select CS1 */
|
||||
cs_ctrl |= PW_SPI_CS1_SELECT;
|
||||
} else {
|
||||
/* Set chip select CS0 */
|
||||
cs_ctrl &= ~(PW_SPI_CS_CTRL_CS_MASK << PW_SPI_CS_EN_SHIFT);
|
||||
}
|
||||
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_CS_CTRL, cs_ctrl);
|
||||
|
||||
if (spi->cs_mode == CS_HW_MODE) {
|
||||
spi_pw_enable_cs_hw_ctrl(dev);
|
||||
} else if (spi->cs_mode == CS_SW_MODE) {
|
||||
spi_pw_cs_sw_ctrl(dev, false);
|
||||
} else if (spi->cs_mode == CS_GPIO_MODE) {
|
||||
spi_pw_cs_sw_ctrl(dev, false);
|
||||
}
|
||||
}
|
||||
|
||||
static void spi_pw_tx_thld_set(const struct device *dev)
|
||||
{
|
||||
uint32_t reg_data;
|
||||
|
||||
/* Tx threshold */
|
||||
reg_data = spi_pw_reg_read(dev, PW_SPI_REG_SITF);
|
||||
/* mask high water mark bits in tx fifo reg */
|
||||
reg_data &= ~(PW_SPI_WM_MASK);
|
||||
/* mask low water mark bits in tx fifo reg */
|
||||
reg_data &= ~(PW_SPI_WM_MASK << PW_SPI_SITF_LWMTF_SHIFT);
|
||||
reg_data |= (PW_SPI_SITF_HIGH_WM_DFLT | PW_SPI_SITF_LOW_WM_DFLT);
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_SITF, reg_data);
|
||||
}
|
||||
|
||||
static void spi_pw_rx_thld_set(const struct device *dev,
|
||||
struct spi_pw_data *spi)
|
||||
{
|
||||
uint32_t reg_data;
|
||||
|
||||
/* Rx threshold */
|
||||
reg_data = spi_pw_reg_read(dev, PW_SPI_REG_SIRF);
|
||||
reg_data = (uint32_t) ~(PW_SPI_WM_MASK);
|
||||
reg_data = PW_SPI_SIRF_WM_DFLT;
|
||||
if (spi->ctx.rx_len && spi->ctx.rx_len < spi->fifo_depth) {
|
||||
reg_data = spi->ctx.rx_len - 1;
|
||||
}
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_SIRF, reg_data);
|
||||
}
|
||||
|
||||
static int spi_pw_set_data_size(const struct device *dev,
|
||||
const struct spi_config *config)
|
||||
{
|
||||
uint32_t ctrlr0;
|
||||
|
||||
ctrlr0 = spi_pw_reg_read(dev, PW_SPI_REG_CTRLR0);
|
||||
|
||||
/* Full duplex mode */
|
||||
ctrlr0 &= ~(PW_SPI_CTRLR0_MOD_BIT);
|
||||
|
||||
ctrlr0 &= PW_SPI_CTRLR0_DATA_MASK;
|
||||
ctrlr0 &= PW_SPI_CTRLR0_EDSS_MASK;
|
||||
|
||||
/* Set the word size */
|
||||
if (SPI_WORD_SIZE_GET(config->operation) == 4) {
|
||||
ctrlr0 |= PW_SPI_DATA_SIZE_4_BIT;
|
||||
} else if (SPI_WORD_SIZE_GET(config->operation) == 8) {
|
||||
ctrlr0 |= PW_SPI_DATA_SIZE_8_BIT;
|
||||
} else if (SPI_WORD_SIZE_GET(config->operation) == 16) {
|
||||
ctrlr0 |= PW_SPI_DATA_SIZE_16_BIT;
|
||||
} else if (SPI_WORD_SIZE_GET(config->operation) == 32) {
|
||||
ctrlr0 |= PW_SPI_DATA_SIZE_32_BIT;
|
||||
} else {
|
||||
LOG_ERR("Invalid word size");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_CTRLR0, ctrlr0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_pw_config_phase_polarity(const struct device *dev,
|
||||
const struct spi_config *config)
|
||||
{
|
||||
uint8_t mode;
|
||||
uint32_t ctrlr1;
|
||||
|
||||
ctrlr1 = spi_pw_reg_read(dev, PW_SPI_REG_CTRLR1);
|
||||
|
||||
mode = (SPI_MODE_GET(config->operation) & SPI_MODE_CPOL) |
|
||||
(SPI_MODE_GET(config->operation) & SPI_MODE_CPHA);
|
||||
|
||||
LOG_DBG("mode: 0x%x", (mode >> 1));
|
||||
switch (mode >> 1) {
|
||||
case SPI_PW_MODE0:
|
||||
ctrlr1 &= ~(PW_SPI_CTRL1_SPO_SPH_MASK);
|
||||
ctrlr1 &= ~(PW_SPI_CTRL1_SPO_BIT);
|
||||
ctrlr1 &= ~(PW_SPI_CTRL1_SPH_BIT);
|
||||
break;
|
||||
case SPI_PW_MODE1:
|
||||
ctrlr1 &= ~(PW_SPI_CTRL1_SPO_SPH_MASK);
|
||||
ctrlr1 |= PW_SPI_CTRL1_SPO_BIT;
|
||||
ctrlr1 &= ~(PW_SPI_CTRL1_SPH_BIT);
|
||||
break;
|
||||
case SPI_PW_MODE2:
|
||||
ctrlr1 &= ~(PW_SPI_CTRL1_SPO_SPH_MASK);
|
||||
ctrlr1 &= ~(PW_SPI_CTRL1_SPO_BIT);
|
||||
ctrlr1 |= PW_SPI_CTRL1_SPH_BIT;
|
||||
break;
|
||||
case SPI_PW_MODE3:
|
||||
ctrlr1 |= PW_SPI_CTRL1_SPO_BIT;
|
||||
ctrlr1 |= PW_SPI_CTRL1_SPH_BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set Polarity & Phase */
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_CTRLR1, ctrlr1);
|
||||
}
|
||||
|
||||
static void spi_pw_enable_clk(const struct device *dev)
|
||||
{
|
||||
uint32_t clks;
|
||||
|
||||
/*Update M:N value & enable clock */
|
||||
clks = spi_pw_reg_read(dev, PW_SPI_REG_CLKS);
|
||||
clks &= ~(PW_SPI_CLKS_MVAL_MASK);
|
||||
clks &= ~(PW_SPI_CLKS_NVAL_MASK);
|
||||
clks |= (PW_SPI_CLKS_MVAL | PW_SPI_CLKS_NVAL |
|
||||
PW_SPI_CLKS_EN_BIT | PW_SPI_CLKS_UPDATE_BIT);
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_CLKS, clks);
|
||||
}
|
||||
|
||||
static void spi_pw_config_clk(const struct device *dev,
|
||||
const struct spi_pw_config *info,
|
||||
const struct spi_config *config)
|
||||
{
|
||||
uint32_t ctrlr0, scr;
|
||||
|
||||
/* Update scr control bits */
|
||||
if (!config->frequency) {
|
||||
scr = PW_SPI_BR_2MHZ;
|
||||
} else if (config->frequency > PW_SPI_BR_MAX_FRQ) {
|
||||
scr = (info->clock_freq / PW_SPI_BR_MAX_FRQ) - 1;
|
||||
} else {
|
||||
scr = (info->clock_freq / config->frequency) - 1;
|
||||
}
|
||||
ctrlr0 = spi_pw_reg_read(dev, PW_SPI_REG_CTRLR0);
|
||||
|
||||
ctrlr0 &= ~(PW_SPI_SCR_MASK);
|
||||
ctrlr0 |= (scr << PW_SPI_SCR_SHIFT);
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_CTRLR0, ctrlr0);
|
||||
}
|
||||
|
||||
static void spi_pw_completed(const struct device *dev, int err)
|
||||
{
|
||||
struct spi_pw_data *spi = dev->data;
|
||||
|
||||
if (!err && (spi_context_tx_on(&spi->ctx) ||
|
||||
spi_context_rx_on(&spi->ctx))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* need to give time for FIFOs to drain before issuing more commands */
|
||||
while (is_pw_ssp_busy(dev)) {
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPI_PW_INTERRUPT
|
||||
/* Disabling interrupts */
|
||||
spi_pw_intr_disable(dev);
|
||||
#endif
|
||||
|
||||
/* Disabling the controller operation, which also clear's all status bits
|
||||
* in status register
|
||||
*/
|
||||
spi_pw_ssp_disable(dev);
|
||||
|
||||
spi_pw_cs_ctrl_enable(dev, false);
|
||||
|
||||
LOG_DBG("SPI transaction completed %s error\n",
|
||||
err ? "with" : "without");
|
||||
|
||||
spi_context_complete(&spi->ctx, dev, err);
|
||||
}
|
||||
|
||||
static void spi_pw_clear_intr(const struct device *dev)
|
||||
{
|
||||
uint32_t sssr;
|
||||
|
||||
sssr = spi_pw_reg_read(dev, PW_SPI_REG_SSSR);
|
||||
sssr &= ~(PW_SPI_INTR_ERRORS_MASK);
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_SSSR, sssr);
|
||||
}
|
||||
|
||||
static int spi_pw_get_tx_fifo_level(const struct device *dev)
|
||||
{
|
||||
uint32_t tx_fifo_level;
|
||||
|
||||
tx_fifo_level = spi_pw_reg_read(dev, PW_SPI_REG_SITF);
|
||||
|
||||
tx_fifo_level = ((tx_fifo_level & PW_SPI_SITF_SITFL_MASK) >>
|
||||
PW_SPI_SITF_SITFL_SHIFT);
|
||||
|
||||
return tx_fifo_level;
|
||||
}
|
||||
|
||||
static int spi_pw_get_rx_fifo_level(const struct device *dev)
|
||||
{
|
||||
uint32_t rx_fifo_level;
|
||||
|
||||
rx_fifo_level = spi_pw_reg_read(dev, PW_SPI_REG_SIRF);
|
||||
rx_fifo_level = ((rx_fifo_level & PW_SPI_SIRF_SIRFL_MASK) >>
|
||||
PW_SPI_SIRF_SIRFL_SHIFT);
|
||||
|
||||
return rx_fifo_level;
|
||||
}
|
||||
|
||||
static void spi_pw_reset_tx_fifo_level(const struct device *dev)
|
||||
{
|
||||
uint32_t tx_fifo_level;
|
||||
|
||||
tx_fifo_level = spi_pw_reg_read(dev, PW_SPI_REG_SITF);
|
||||
tx_fifo_level &= ~(PW_SPI_SITF_SITFL_MASK);
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_SITF, tx_fifo_level);
|
||||
|
||||
}
|
||||
|
||||
static void spi_pw_update_rx_fifo_level(uint32_t len,
|
||||
const struct device *dev)
|
||||
{
|
||||
uint32_t rx_fifo_level;
|
||||
|
||||
rx_fifo_level = spi_pw_reg_read(dev, PW_SPI_REG_SIRF);
|
||||
rx_fifo_level &= ~(PW_SPI_SIRF_SIRFL_MASK);
|
||||
rx_fifo_level |= (len << PW_SPI_SIRF_SIRFL_SHIFT);
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_SIRF, rx_fifo_level);
|
||||
}
|
||||
|
||||
static void spi_pw_tx_data(const struct device *dev)
|
||||
{
|
||||
struct spi_pw_data *spi = dev->data;
|
||||
uint32_t data = 0U;
|
||||
int32_t fifo_len;
|
||||
|
||||
if (spi_context_rx_on(&spi->ctx)) {
|
||||
fifo_len = spi->fifo_depth -
|
||||
spi_pw_get_tx_fifo_level(dev) -
|
||||
spi_pw_get_rx_fifo_level(dev);
|
||||
if (fifo_len < 0) {
|
||||
fifo_len = 0U;
|
||||
}
|
||||
} else {
|
||||
fifo_len = spi->fifo_depth - spi_pw_get_tx_fifo_level(dev);
|
||||
}
|
||||
|
||||
while (fifo_len > 0) {
|
||||
if (spi_context_tx_buf_on(&spi->ctx)) {
|
||||
switch (spi->dfs) {
|
||||
case 1:
|
||||
data = UNALIGNED_GET((uint8_t *)
|
||||
(spi->ctx.tx_buf));
|
||||
break;
|
||||
case 2:
|
||||
data = UNALIGNED_GET((uint16_t *)
|
||||
(spi->ctx.tx_buf));
|
||||
break;
|
||||
case 4:
|
||||
data = UNALIGNED_GET((uint32_t *)
|
||||
(spi->ctx.tx_buf));
|
||||
break;
|
||||
}
|
||||
} else if (spi_context_rx_on(&spi->ctx)) {
|
||||
if ((int)(spi->ctx.rx_len - spi->fifo_diff) <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
data = 0U;
|
||||
} else if (spi_context_tx_on(&spi->ctx)) {
|
||||
data = 0U;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
spi_pw_reg_write(dev, PW_SPI_REG_SSDR, data);
|
||||
|
||||
spi_context_update_tx(&spi->ctx, spi->dfs, 1);
|
||||
spi->fifo_diff++;
|
||||
fifo_len--;
|
||||
}
|
||||
|
||||
if (!spi_context_tx_on(&spi->ctx)) {
|
||||
spi_pw_reset_tx_fifo_level(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void spi_pw_rx_data(const struct device *dev)
|
||||
{
|
||||
struct spi_pw_data *spi = dev->data;
|
||||
|
||||
while (spi_pw_get_rx_fifo_level(dev)) {
|
||||
uint32_t data = spi_pw_reg_read(dev, PW_SPI_REG_SSDR);
|
||||
|
||||
if (spi_context_rx_buf_on(&spi->ctx)) {
|
||||
switch (spi->dfs) {
|
||||
case 1:
|
||||
UNALIGNED_PUT(data,
|
||||
(uint8_t *)spi->ctx.rx_buf);
|
||||
break;
|
||||
case 2:
|
||||
UNALIGNED_PUT(data,
|
||||
(uint16_t *)spi->ctx.rx_buf);
|
||||
break;
|
||||
case 4:
|
||||
UNALIGNED_PUT(data,
|
||||
(uint32_t *)spi->ctx.rx_buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
spi_context_update_rx(&spi->ctx, spi->dfs, 1);
|
||||
spi->fifo_diff--;
|
||||
}
|
||||
|
||||
if (!spi->ctx.rx_len && spi->ctx.tx_len < spi->fifo_depth) {
|
||||
spi_pw_update_rx_fifo_level(spi->ctx.tx_len - 1, dev);
|
||||
} else if (spi_pw_get_rx_fifo_level(dev) >= spi->ctx.rx_len) {
|
||||
spi_pw_update_rx_fifo_level(spi->ctx.rx_len - 1, dev);
|
||||
}
|
||||
}
|
||||
|
||||
static int spi_pw_transfer(const struct device *dev)
|
||||
{
|
||||
uint32_t intr_status;
|
||||
int err;
|
||||
|
||||
intr_status = spi_pw_reg_read(dev, PW_SPI_REG_SSSR);
|
||||
|
||||
if (intr_status & PW_SPI_SSSR_ROR_BIT) {
|
||||
LOG_ERR("Receive FIFO overrun");
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (intr_status & PW_SPI_SSSR_TUR_BIT) {
|
||||
LOG_ERR("Transmit FIFO underrun");
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (intr_status & PW_SPI_SSSR_TINT_BIT) {
|
||||
LOG_ERR("Receiver timeout interrupt");
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
if (intr_status & PW_SPI_SSSR_RNE_BIT) {
|
||||
spi_pw_rx_data(dev);
|
||||
}
|
||||
|
||||
if (intr_status & PW_SPI_SSSR_TNF_BIT) {
|
||||
spi_pw_tx_data(dev);
|
||||
}
|
||||
|
||||
out:
|
||||
if (err) {
|
||||
spi_pw_clear_intr(dev);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int spi_pw_configure(const struct device *dev,
|
||||
const struct spi_pw_config *info,
|
||||
struct spi_pw_data *spi,
|
||||
const struct spi_config *config)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* At this point, it's mandatory to set this on the context! */
|
||||
spi->ctx.config = config;
|
||||
|
||||
if (!spi->ctx.config->cs) {
|
||||
if (spi->cs_mode == CS_GPIO_MODE) {
|
||||
LOG_DBG("cs gpio is NULL, switch to hw mode");
|
||||
spi->cs_mode = CS_HW_MODE;
|
||||
spi_pw_enable_cs_hw_ctrl(dev);
|
||||
}
|
||||
}
|
||||
|
||||
if (config->operation & SPI_HALF_DUPLEX) {
|
||||
LOG_ERR("Half-duplex not supported");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Verify if requested op mode is relevant to this controller */
|
||||
if (config->operation & SPI_OP_MODE_SLAVE) {
|
||||
LOG_ERR("Slave mode not supported");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if ((config->operation & SPI_TRANSFER_LSB) ||
|
||||
(IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) &&
|
||||
(config->operation & (SPI_LINES_DUAL |
|
||||
SPI_LINES_QUAD |
|
||||
SPI_LINES_OCTAL)))) {
|
||||
LOG_ERR("Extended mode Unsupported configuration");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (config->operation & SPI_FRAME_FORMAT_TI) {
|
||||
LOG_ERR("TI frame format not supported");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (config->operation & SPI_HOLD_ON_CS) {
|
||||
LOG_ERR("Chip select hold not supported");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Set mode & data size */
|
||||
err = spi_pw_set_data_size(dev, config);
|
||||
|
||||
if (err) {
|
||||
LOG_ERR("Invalid data size");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Set Polarity & Phase */
|
||||
spi_pw_config_phase_polarity(dev, config);
|
||||
|
||||
/* enable clock */
|
||||
spi_pw_enable_clk(dev);
|
||||
|
||||
/* configure */
|
||||
spi_pw_config_clk(dev, info, config);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int transceive(const struct device *dev,
|
||||
const struct spi_config *config,
|
||||
const struct spi_buf_set *tx_bufs,
|
||||
const struct spi_buf_set *rx_bufs,
|
||||
bool asynchronous,
|
||||
spi_callback_t cb,
|
||||
void *userdata)
|
||||
{
|
||||
const struct spi_pw_config *info = dev->config;
|
||||
struct spi_pw_data *spi = dev->data;
|
||||
int err;
|
||||
|
||||
if (!tx_bufs && !rx_bufs) {
|
||||
LOG_ERR(" Tx & Rx buff null");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (asynchronous) {
|
||||
LOG_ERR("Async not supported");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
spi_context_lock(&spi->ctx, asynchronous, cb, userdata, config);
|
||||
|
||||
/* Configure */
|
||||
err = spi_pw_configure(dev, info, spi, config);
|
||||
if (err) {
|
||||
LOG_ERR("spi pw config fail");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Frame size in number of data bytes */
|
||||
spi->dfs = spi_pw_get_frame_size(config);
|
||||
spi_context_buffers_setup(&spi->ctx, tx_bufs, rx_bufs,
|
||||
spi->dfs);
|
||||
|
||||
spi->fifo_diff = 0U;
|
||||
|
||||
/* Tx threshold */
|
||||
spi_pw_tx_thld_set(dev);
|
||||
|
||||
/* Rx threshold */
|
||||
spi_pw_rx_thld_set(dev, spi);
|
||||
|
||||
spi_pw_cs_ctrl_enable(dev, true);
|
||||
|
||||
/* Enable ssp operation */
|
||||
spi_pw_ssp_enable(dev);
|
||||
|
||||
#ifdef CONFIG_SPI_PW_INTERRUPT
|
||||
LOG_DBG("Interrupt Mode");
|
||||
|
||||
/* Enable interrupts */
|
||||
if (rx_bufs) {
|
||||
spi_pw_intr_enable(dev, true);
|
||||
} else {
|
||||
spi_pw_intr_enable(dev, false);
|
||||
}
|
||||
|
||||
err = spi_context_wait_for_completion(&spi->ctx);
|
||||
#else
|
||||
LOG_DBG("Polling Mode");
|
||||
|
||||
do {
|
||||
err = spi_pw_transfer(dev);
|
||||
} while ((!err) && is_spi_transfer_ongoing(spi));
|
||||
|
||||
spi_pw_completed(dev, err);
|
||||
#endif
|
||||
|
||||
out:
|
||||
spi_context_release(&spi->ctx, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int spi_pw_transceive(const struct device *dev,
|
||||
const struct spi_config *config,
|
||||
const struct spi_buf_set *tx_bufs,
|
||||
const struct spi_buf_set *rx_bufs)
|
||||
{
|
||||
LOG_DBG("%p, %p, %p\n", dev, tx_bufs, rx_bufs);
|
||||
return transceive(dev, config, tx_bufs, rx_bufs,
|
||||
false, NULL, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPI_ASYNC
|
||||
static int spi_pw_transceive_async(const struct device *dev,
|
||||
const struct spi_config *config,
|
||||
const struct spi_buf_set *tx_bufs,
|
||||
const struct spi_buf_set *rx_bufs,
|
||||
spi_callback_t cb,
|
||||
void *userdata)
|
||||
{
|
||||
LOG_DBG("%p, %p, %p, %p, %p\n", dev, tx_bufs, rx_bufs,
|
||||
cb, userdata);
|
||||
|
||||
return transceive(dev, config, tx_bufs, rx_bufs, true,
|
||||
cb, userdata);
|
||||
}
|
||||
#endif /* CONFIG_SPI_ASYNC */
|
||||
|
||||
static int spi_pw_release(const struct device *dev,
|
||||
const struct spi_config *config)
|
||||
{
|
||||
struct spi_pw_data *spi = dev->data;
|
||||
|
||||
if (!spi_context_configured(&spi->ctx, config)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spi_context_unlock_unconditionally(&spi->ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPI_PW_INTERRUPT
|
||||
static void spi_pw_isr(const void *arg)
|
||||
{
|
||||
const struct device *dev = (const struct device *)arg;
|
||||
int err;
|
||||
|
||||
err = spi_pw_transfer(dev);
|
||||
spi_pw_completed(dev, err);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct spi_driver_api pw_spi_api = {
|
||||
.transceive = spi_pw_transceive,
|
||||
.release = spi_pw_release,
|
||||
#ifdef CONFIG_SPI_ASYNC
|
||||
.transceive_async = spi_pw_transceive_async,
|
||||
#endif /* CONFIG_SPI_ASYNC */
|
||||
};
|
||||
|
||||
static int spi_pw_init(const struct device *dev)
|
||||
{
|
||||
const struct spi_pw_config *info = dev->config;
|
||||
struct spi_pw_data *spi = dev->data;
|
||||
int err;
|
||||
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(pcie)
|
||||
if (info->pcie) {
|
||||
struct pcie_bar mbar;
|
||||
|
||||
if (info->pcie->bdf == PCIE_BDF_NONE) {
|
||||
LOG_ERR("Cannot probe PCI device");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!pcie_probe_mbar(info->pcie->bdf, 0, &mbar)) {
|
||||
LOG_ERR("MBAR not found");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pcie_set_cmd(info->pcie->bdf, PCIE_CONF_CMDSTAT_MEM,
|
||||
true);
|
||||
|
||||
device_map(DEVICE_MMIO_RAM_PTR(dev), mbar.phys_addr,
|
||||
mbar.size, K_MEM_CACHE_NONE);
|
||||
|
||||
pcie_set_cmd(info->pcie->bdf,
|
||||
PCIE_CONF_CMDSTAT_MASTER,
|
||||
true);
|
||||
|
||||
} else {
|
||||
DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
|
||||
}
|
||||
#else
|
||||
DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
|
||||
#endif
|
||||
|
||||
/* Bring ssp out of reset */
|
||||
spi_pw_ssp_reset(dev);
|
||||
|
||||
/* Disable ssp operation */
|
||||
spi_pw_ssp_disable(dev);
|
||||
|
||||
/* Chip select control */
|
||||
spi_pw_cs_ctrl_init(dev);
|
||||
|
||||
#if defined(CONFIG_SPI_PW_INTERRUPT)
|
||||
/* Mask interrupts */
|
||||
spi_pw_intr_disable(dev);
|
||||
|
||||
/* Init and connect IRQ */
|
||||
info->irq_config(dev);
|
||||
#endif
|
||||
|
||||
if (spi->cs_mode == CS_GPIO_MODE) {
|
||||
err = spi_context_cs_configure_all(&spi->ctx);
|
||||
if (err < 0) {
|
||||
LOG_ERR("Failed to configure CS pins: %d", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
spi_context_unlock_unconditionally(&spi->ctx);
|
||||
|
||||
LOG_DBG("SPI pw init success");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define INIT_PCIE0(n)
|
||||
#define INIT_PCIE1(n) DEVICE_PCIE_INST_INIT(n, pcie),
|
||||
#define INIT_PCIE(n) _CONCAT(INIT_PCIE, DT_INST_ON_BUS(n, pcie))(n)
|
||||
|
||||
#define DEFINE_PCIE0(n)
|
||||
#define DEFINE_PCIE1(n) DEVICE_PCIE_INST_DECLARE(n)
|
||||
#define SPI_PCIE_DEFINE(n) _CONCAT(DEFINE_PCIE, DT_INST_ON_BUS(n, pcie))(n)
|
||||
|
||||
#ifdef CONFIG_SPI_PW_INTERRUPT
|
||||
|
||||
#define SPI_INTEL_IRQ_FLAGS_SENSE0(n) 0
|
||||
#define SPI_INTEL_IRQ_FLAGS_SENSE1(n) DT_INST_IRQ(n, sense)
|
||||
#define SPI_INTEL_IRQ_FLAGS(n) \
|
||||
_CONCAT(SPI_INTEL_IRQ_FLAGS_SENSE, DT_INST_IRQ_HAS_CELL(n, sense))(n)
|
||||
|
||||
#define SPI_INTEL_IRQ_INIT(n) \
|
||||
BUILD_ASSERT(IS_ENABLED(CONFIG_DYNAMIC_INTERRUPTS), \
|
||||
"SPI PCIe requires dynamic interrupts"); \
|
||||
static void spi_##n##_irq_init(const struct device *dev) \
|
||||
{ \
|
||||
const struct spi_pw_config *info = dev->config; \
|
||||
unsigned int irq; \
|
||||
if (DT_INST_IRQN(n) == PCIE_IRQ_DETECT) { \
|
||||
irq = pcie_alloc_irq(info->pcie->bdf); \
|
||||
if (irq == PCIE_CONF_INTR_IRQ_NONE) { \
|
||||
return; \
|
||||
} \
|
||||
} else { \
|
||||
irq = DT_INST_IRQN(n); \
|
||||
pcie_conf_write(info->pcie->bdf, \
|
||||
PCIE_CONF_INTR, irq); \
|
||||
} \
|
||||
pcie_connect_dynamic_irq(info->pcie->bdf, irq, \
|
||||
DT_INST_IRQ(n, priority), \
|
||||
(void (*)(const void *))spi_pw_isr, \
|
||||
DEVICE_DT_INST_GET(n), \
|
||||
SPI_INTEL_IRQ_FLAGS(n)); \
|
||||
pcie_irq_enable(info->pcie->bdf, irq); \
|
||||
LOG_DBG("lpass spi Configure irq %d", irq); \
|
||||
}
|
||||
|
||||
#define SPI_PW_DEV_INIT(n) \
|
||||
static struct spi_pw_data spi_##n##_data = { \
|
||||
SPI_CONTEXT_INIT_LOCK(spi_##n##_data, ctx), \
|
||||
SPI_CONTEXT_INIT_SYNC(spi_##n##_data, ctx), \
|
||||
SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx) \
|
||||
.cs_mode = DT_INST_PROP(n, pw_cs_mode), \
|
||||
.cs_output = DT_INST_PROP(n, pw_cs_output), \
|
||||
.fifo_depth = DT_INST_PROP(n, pw_fifo_depth), \
|
||||
}; \
|
||||
SPI_PCIE_DEFINE(n); \
|
||||
SPI_INTEL_IRQ_INIT(n) \
|
||||
static const struct spi_pw_config spi_##n##_config = { \
|
||||
.irq_config = spi_##n##_irq_init, \
|
||||
.clock_freq = DT_INST_PROP(n, clock_frequency), \
|
||||
INIT_PCIE(n) \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(n, spi_pw_init, NULL, \
|
||||
&spi_##n##_data, &spi_##n##_config, \
|
||||
POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \
|
||||
&pw_spi_api);
|
||||
#else
|
||||
|
||||
#define SPI_PW_DEV_INIT(n) \
|
||||
static struct spi_pw_data spi_##n##_data = { \
|
||||
SPI_CONTEXT_INIT_LOCK(spi_##n##_data, ctx), \
|
||||
SPI_CONTEXT_INIT_SYNC(spi_##n##_data, ctx), \
|
||||
SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx) \
|
||||
.cs_mode = DT_INST_PROP(n, pw_cs_mode), \
|
||||
.cs_output = DT_INST_PROP(n, pw_cs_output), \
|
||||
.fifo_depth = DT_INST_PROP(n, pw_fifo_depth), \
|
||||
}; \
|
||||
SPI_PCIE_DEFINE(n); \
|
||||
static const struct spi_pw_config spi_##n##_config = { \
|
||||
.clock_freq = DT_INST_PROP(n, clock_frequency), \
|
||||
INIT_PCIE(n) \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(n, spi_pw_init, NULL, \
|
||||
&spi_##n##_data, &spi_##n##_config, \
|
||||
POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \
|
||||
&pw_spi_api);
|
||||
|
||||
#endif
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(SPI_PW_DEV_INIT)
|
224
drivers/spi/spi_pw.h
Normal file
224
drivers/spi/spi_pw.h
Normal file
|
@ -0,0 +1,224 @@
|
|||
/* spi_pw.h - Penwell SPI driver definitions */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SPI_SPI_PW_H_
|
||||
#define ZEPHYR_DRIVERS_SPI_SPI_PW_H_
|
||||
|
||||
#include "spi_context.h"
|
||||
|
||||
/* lpss penwell spi registers */
|
||||
#define PW_SPI_REG_CTRLR0 0x00
|
||||
#define PW_SPI_REG_CTRLR1 0x04
|
||||
#define PW_SPI_REG_SSSR 0x08
|
||||
#define PW_SPI_REG_SSDR 0x10
|
||||
#define PW_SPI_REG_SSTO 0x28
|
||||
#define PW_SPI_REG_SITF 0x44
|
||||
#define PW_SPI_REG_SIRF 0x48
|
||||
|
||||
#define PW_SPI_REG_CLKS 0x200
|
||||
#define PW_SPI_REG_RESETS 0x204
|
||||
#define PW_SPI_REG_ACTIVE_LTR 0x210
|
||||
#define PW_SPI_REG_IDLE_LTR 0x217
|
||||
#define PW_SPI_REG_TX_BIT_COUNT 0x218
|
||||
#define PW_SPI_REG_RX_BIT_COUNT 0x21c
|
||||
#define PW_SPI_REG_DMA_FINISH_DIS 0x220
|
||||
|
||||
#define PW_SPI_REG_CS_CTRL 0x224
|
||||
#define PW_SPI_REG_SW_SCRATCH 0x228
|
||||
#define PW_SPI_REG_CLK_GATE 0x238
|
||||
#define PW_SPI_REG_REMAP_ADDR_LO 0x240
|
||||
#define PW_SPI_REG_REMAP_ADDR_HI 0x244
|
||||
#define PW_SPI_REG_DEV_IDLE_CTRL 0x24c
|
||||
#define PW_SPI_REG_DEL_RX_CLK 0x250
|
||||
#define PW_SPI_REG_CAP 0x2fc
|
||||
|
||||
/* CTRLR0 settings */
|
||||
#define PW_SPI_CTRLR0_SSE_BIT BIT(7)
|
||||
#define PW_SPI_CTRLR0_EDSS_BIT BIT(20)
|
||||
#define PW_SPI_CTRLR0_RIM_BIT BIT(22)
|
||||
#define PW_SPI_CTRLR0_TIM_BIT BIT(23)
|
||||
#define PW_SPI_CTRLR0_MOD_BIT BIT(31)
|
||||
|
||||
#define PW_SPI_CTRLR0_DATA_MASK (~(0xf << 0))
|
||||
#define PW_SPI_CTRLR0_EDSS_MASK (~(0x1 << 20))
|
||||
|
||||
/* Data size set bits sscr0[3:0] */
|
||||
#define PW_SPI_DATA_SIZE_4_BIT 0x3
|
||||
#define PW_SPI_DATA_SIZE_8_BIT 0x7
|
||||
#define PW_SPI_DATA_SIZE_16_BIT 0xf
|
||||
#define PW_SPI_DATA_SIZE_32_BIT (PW_SPI_CTRLR0_EDSS_BIT | \
|
||||
PW_SPI_DATA_SIZE_16_BIT)
|
||||
/* Frame format sscr0[5:4] */
|
||||
#define PW_SPI_FRF_MOTOROLA (~(0x3 << 4))
|
||||
|
||||
/* SSP Baud rate sscr0[19:8] */
|
||||
#define PW_SPI_BR_2MHZ 0x31
|
||||
#define PW_SPI_BR_4MHZ 0x18
|
||||
#define PW_SPI_BR_5MHZ 0x13
|
||||
#define PW_SPI_BR_10MHZ 0x9
|
||||
#define PW_SPI_BR_20MHZ 0x5
|
||||
#define PW_SPI_BR_MAX_FRQ 20000000 /* 20 MHz */
|
||||
/* [19:8] 12 bits */
|
||||
#define PW_SPI_SCR_MASK (BIT_MASK(12) << 8)
|
||||
#define PW_SPI_SCR_SHIFT 0x8
|
||||
|
||||
/* CTRLR1 settings */
|
||||
#define PW_SPI_CTRL1_RIE_BIT BIT(0)
|
||||
#define PW_SPI_CTRL1_TIE_BIT BIT(1)
|
||||
#define PW_SPI_CTRL1_LBM_BIT BIT(2)
|
||||
#define PW_SPI_CTRL1_SPO_BIT BIT(3)
|
||||
#define PW_SPI_CTRL1_SPH_BIT BIT(4)
|
||||
#define PW_SPI_CTRL1_IFS_BIT BIT(16)
|
||||
#define PW_SPI_CTRL1_TINTE_BIT BIT(19)
|
||||
#define PW_SPI_CTRL1_RSRE_BIT BIT(20)
|
||||
#define PW_SPI_CTRL1_TSRE_BIT BIT(21)
|
||||
#define PW_SPI_CTRL1_TRAIL_BIT BIT(22)
|
||||
#define PW_SPI_CTRL1_RWOT_BIT BIT(23)
|
||||
|
||||
/* [4:3] phase & polarity mask */
|
||||
#define PW_SPI_CTRL1_SPO_SPH_MASK (BIT_MASK(2) << 3)
|
||||
|
||||
/* Status Register */
|
||||
#define PW_SPI_SSSR_TNF_BIT BIT(2)
|
||||
#define PW_SPI_SSSR_RNE_BIT BIT(3)
|
||||
#define PW_SPI_SSSR_BSY_BIT BIT(4)
|
||||
#define PW_SPI_SSSR_TFS_BIT BIT(5)
|
||||
#define PW_SPI_SSSR_RFS_BIT BIT(6)
|
||||
#define PW_SPI_SSSR_ROR_BIT BIT(7)
|
||||
#define PW_SPI_SSSR_PINT_BIT BIT(18)
|
||||
#define PW_SPI_SSSR_TINT_BIT BIT(19)
|
||||
#define PW_SPI_SSSR_TUR_BIT BIT(21)
|
||||
|
||||
/* SPI Tx FIFO Higher Water Mark [5:0] */
|
||||
#define PW_SPI_SITF_HWM_1_ENTRY 0x1
|
||||
#define PW_SPI_SITF_HWM_4_ENTRY 0x4
|
||||
#define PW_SPI_SITF_HWM_8_ENTRY 0x8
|
||||
#define PW_SPI_SITF_HWM_16_ENTRY 0x10
|
||||
#define PW_SPI_SITF_HWM_32_ENTRY 0x20
|
||||
#define PW_SPI_SITF_HWM_64_ENTRY 0x40
|
||||
|
||||
/* SPI Tx FIFO Lower Water Mark[13:8] */
|
||||
#define PW_SPI_SITF_LWM_2_ENTRY (BIT(0) << 8)
|
||||
#define PW_SPI_SITF_LWM_3_ENTRY (BIT(1) << 8)
|
||||
#define PW_SPI_SITF_LWM_4_ENTRY ((BIT(1) | BIT(0)) << 8)
|
||||
|
||||
/* SPI Tx FIFO Level SITF[21:16] */
|
||||
#define PW_SPI_SITF_SITFL_MASK (BIT_MASK(6) << 16)
|
||||
|
||||
#define PW_SPI_SITF_SITFL_SHIFT 0x10
|
||||
|
||||
/* SPI Rx FIFO water mark */
|
||||
#define PW_SPI_SIRF_WMRF_1_ENTRY 0x1
|
||||
#define PW_SPI_SIRF_WMRF_2_ENTRY 0x2
|
||||
#define PW_SPI_SIRF_WMRF_4_ENTRY 0x4
|
||||
#define PW_SPI_SITF_WMRF_8_ENTRY 0x8
|
||||
#define PW_SPI_SITF_WMRF_16_ENTRY 0x10
|
||||
#define PW_SPI_SITF_WMRF_32_ENTRY 0x20
|
||||
#define PW_SPI_SITF_WMRF_64_ENTRY 0x40
|
||||
|
||||
/* SPI Rx FIFO Level RITF[13:8] */
|
||||
#define PW_SPI_SIRF_SIRFL_MASK (BIT_MASK(6) << 8)
|
||||
#define PW_SPI_SIRF_SIRFL_SHIFT 0x8
|
||||
|
||||
/* Threshold default value */
|
||||
#define PW_SPI_WM_MASK BIT_MASK(6)
|
||||
#define PW_SPI_SITF_LWMTF_SHIFT 0x8
|
||||
#define PW_SPI_SITF_LOW_WM_DFLT BIT(PW_SPI_SITF_LWMTF_SHIFT)
|
||||
#define PW_SPI_SITF_HIGH_WM_DFLT 0x20
|
||||
#define PW_SPI_SIRF_WM_DFLT 0x28
|
||||
|
||||
/* Clocks */
|
||||
#define PW_SPI_CLKS_EN_BIT BIT(0)
|
||||
#define PW_SPI_CLKS_MVAL BIT(1)
|
||||
#define PW_SPI_CLKS_NVAL BIT(16)
|
||||
#define PW_SPI_CLKS_UPDATE_BIT BIT(31)
|
||||
|
||||
/* mval mask [15:1] */
|
||||
#define PW_SPI_CLKS_MVAL_MASK (BIT_MASK(15) << 1)
|
||||
|
||||
/* nval mask [30:16] */
|
||||
#define PW_SPI_CLKS_NVAL_MASK (BIT_MASK(15) << 16)
|
||||
|
||||
/* SPI chip select control */
|
||||
#define PW_SPI_CS_MODE_BIT 0
|
||||
#define PW_SPI_CS_STATE_BIT 1
|
||||
#define PW_SPI_CS0_POL_BIT 12
|
||||
#define PW_SPI_CS1_POL_BIT 13
|
||||
|
||||
/* ssp interrupt error bits */
|
||||
#define PW_SPI_INTR_ERRORS_MASK (PW_SPI_SSSR_TUR_BIT | \
|
||||
PW_SPI_SSSR_ROR_BIT | \
|
||||
PW_SPI_SSSR_TINT_BIT)
|
||||
|
||||
/* ssp interrupt bits */
|
||||
#define PW_SPI_INTR_BITS (PW_SPI_CTRL1_TIE_BIT | \
|
||||
PW_SPI_CTRL1_RIE_BIT | \
|
||||
PW_SPI_CTRL1_TINTE_BIT)
|
||||
|
||||
#define PW_SPI_INTR_MASK_TX (~(PW_SPI_CTRL1_TIE_BIT | \
|
||||
PW_SPI_CTRL1_TINTE_BIT))
|
||||
|
||||
#define PW_SPI_INTR_MASK_RX (PW_SPI_CTRL1_RIE_BIT)
|
||||
|
||||
/* SSP & DMA reset */
|
||||
#define PW_SPI_INST_RESET 0x7
|
||||
|
||||
/* Chip select control */
|
||||
#define PW_SPI_CS_CTRL_SW_MODE BIT(0)
|
||||
#define PW_SPI_CS_HIGH BIT(1)
|
||||
#define PW_SPI_CS_LOW (~(PW_SPI_CS_HIGH))
|
||||
#define PW_SPI_CS_CTRL_CS_MASK 0x3
|
||||
#define PW_SPI_CS_EN_SHIFT 0x8
|
||||
#define PW_SPI_CS0_SELECT (~(BIT(PW_SPI_CS_EN_SHIFT)))
|
||||
#define PW_SPI_CS1_SELECT BIT(PW_SPI_CS_EN_SHIFT)
|
||||
#define PW_SPI_CS_CTRL_HW_MODE (~(PW_SPI_CS_CTRL_SW_MODE))
|
||||
|
||||
#define PW_SPI_WIDTH_8BITS 8
|
||||
#define PW_SPI_FRAME_SIZE_1_BYTE 1
|
||||
#define PW_SPI_FRAME_SIZE_2_BYTES 2
|
||||
#define PW_SPI_FRAME_SIZE_4_BYTES 4
|
||||
|
||||
#define PW_SPI_CS1_OUTPUT_SELECT 1
|
||||
|
||||
enum spi_pw_spo_sph_mode {
|
||||
SPI_PW_MODE0 = 0,
|
||||
SPI_PW_MODE1,
|
||||
SPI_PW_MODE2,
|
||||
SPI_PW_MODE3,
|
||||
};
|
||||
|
||||
enum spi_pw_cs_mode {
|
||||
CS_HW_MODE = 0,
|
||||
CS_SW_MODE,
|
||||
CS_GPIO_MODE,
|
||||
};
|
||||
|
||||
struct spi_pw_config {
|
||||
uint32_t id;
|
||||
#ifdef CONFIG_SPI_PW_INTERRUPT
|
||||
void (*irq_config)(const struct device *dev);
|
||||
#endif
|
||||
uint32_t clock_freq;
|
||||
uint8_t op_modes;
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(pcie)
|
||||
struct pcie_dev *pcie;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct spi_pw_data {
|
||||
DEVICE_MMIO_RAM;
|
||||
struct spi_context ctx;
|
||||
uint8_t dfs;
|
||||
uint8_t fifo_diff;
|
||||
uint8_t cs_mode;
|
||||
uint8_t cs_output;
|
||||
uint32_t id;
|
||||
uint8_t fifo_depth;
|
||||
};
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SPI_SPI_PW_H_ */
|
40
dts/bindings/spi/intel,penwell-spi.yaml
Normal file
40
dts/bindings/spi/intel,penwell-spi.yaml
Normal file
|
@ -0,0 +1,40 @@
|
|||
# Copyright (c) 2023 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Intel Penwell SPI
|
||||
|
||||
compatible: "intel,penwell-spi"
|
||||
|
||||
include: [spi-controller.yaml, pcie-device.yaml]
|
||||
|
||||
properties:
|
||||
interrupts:
|
||||
required: true
|
||||
|
||||
cs-gpios:
|
||||
required: true
|
||||
|
||||
pw,cs-mode:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Chip select configuration. possible values:
|
||||
0: Hardware
|
||||
1: Software
|
||||
2: GPIO
|
||||
|
||||
pw,cs-output:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Use GSPI chip select CS0 or CS1. GSPI 0, 1 & 2 instance supports both chip selects.
|
||||
It can be configured with this DTS property. By default, CS0 is set.
|
||||
Chip select output possible values:
|
||||
0: CS0
|
||||
1: CS1
|
||||
|
||||
pw,fifo-depth:
|
||||
type: int
|
||||
required: true
|
||||
description: SPI controller with embedded Tx and Rx FIFOs.
|
Loading…
Reference in a new issue