|  | /* spi_dw.c - Designware SPI driver implementation */ | 
|  |  | 
|  | /* | 
|  | * Copyright (c) 2015 Intel Corporation. | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include <errno.h> | 
|  |  | 
|  | #include <kernel.h> | 
|  | #include <arch/cpu.h> | 
|  |  | 
|  | #include <misc/__assert.h> | 
|  | #include <board.h> | 
|  | #include <device.h> | 
|  | #include <init.h> | 
|  |  | 
|  | #include <sys_io.h> | 
|  | #include <clock_control.h> | 
|  | #include <misc/util.h> | 
|  |  | 
|  | #include <spi.h> | 
|  | #include <spi_dw.h> | 
|  |  | 
|  | #ifdef CONFIG_IOAPIC | 
|  | #include <drivers/ioapic.h> | 
|  | #endif | 
|  |  | 
|  | #define SYS_LOG_DOMAIN "SPI DW" | 
|  | #define SYS_LOG_LEVEL CONFIG_SYS_LOG_SPI_LEVEL | 
|  | #include <misc/sys_log.h> | 
|  |  | 
|  | #if (CONFIG_SYS_LOG_SPI_LEVEL == 4) | 
|  | #define DBG_COUNTER_INIT()	\ | 
|  | uint32_t __cnt = 0 | 
|  | #define DBG_COUNTER_INC()	\ | 
|  | (__cnt++) | 
|  | #define DBG_COUNTER_RESULT()	\ | 
|  | (__cnt) | 
|  | #else | 
|  | #define DBG_COUNTER_INIT() {; } | 
|  | #define DBG_COUNTER_INC() {; } | 
|  | #define DBG_COUNTER_RESULT() 0 | 
|  | #endif | 
|  |  | 
|  | #ifdef SPI_DW_SPI_CLOCK | 
|  | #define SPI_DW_CLK_DIVIDER(ssi_clk_hz) \ | 
|  | ((SPI_DW_SPI_CLOCK / ssi_clk_hz) & 0xFFFF) | 
|  | /* provision for soc.h providing a clock that is different than CPU clock */ | 
|  | #else | 
|  | #define SPI_DW_CLK_DIVIDER(ssi_clk_hz) \ | 
|  | ((CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / ssi_clk_hz) & 0xFFFF) | 
|  | #endif | 
|  |  | 
|  | static void completed(struct device *dev, int error) | 
|  | { | 
|  | const struct spi_dw_config *info = dev->config->config_info; | 
|  | struct spi_dw_data *spi = dev->driver_data; | 
|  |  | 
|  | if (error) { | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * There are several situations here. | 
|  | * 1. spi_write w rx_buf - need last_tx && rx_buf_len zero to be done. | 
|  | * 2. spi_write w/o rx_buf - only need to determine when write is done. | 
|  | * 3. spi_read - need rx_buf_len zero. | 
|  | */ | 
|  | if (spi->tx_buf && spi->rx_buf) { | 
|  | if (!spi->last_tx || spi->rx_buf_len) | 
|  | return; | 
|  | } else if (spi->tx_buf) { | 
|  | if (!spi->last_tx) | 
|  | return; | 
|  | } else { /* or, spi->rx_buf!=0 */ | 
|  | if (spi->rx_buf_len) | 
|  | return; | 
|  | } | 
|  |  | 
|  | out: | 
|  | /* need to give time for FIFOs to drain before issuing more commands */ | 
|  | while (test_bit_sr_busy(info->regs)) { | 
|  | } | 
|  |  | 
|  | spi->error = error; | 
|  |  | 
|  | /* Disabling interrupts */ | 
|  | write_imr(DW_SPI_IMR_MASK, info->regs); | 
|  | /* Disabling the controller */ | 
|  | clear_bit_ssienr(info->regs); | 
|  |  | 
|  | _spi_control_cs(dev, 0); | 
|  |  | 
|  | SYS_LOG_DBG("SPI transaction completed %s error", | 
|  | error ? "with" : "without"); | 
|  |  | 
|  | k_sem_give(&spi->device_sync_sem); | 
|  | } | 
|  |  | 
|  | static void push_data(struct device *dev) | 
|  | { | 
|  | const struct spi_dw_config *info = dev->config->config_info; | 
|  | struct spi_dw_data *spi = dev->driver_data; | 
|  | uint32_t data = 0; | 
|  | uint32_t f_tx; | 
|  | DBG_COUNTER_INIT(); | 
|  |  | 
|  | if (spi->rx_buf) { | 
|  | f_tx = DW_SPI_FIFO_DEPTH - read_txflr(info->regs) - | 
|  | read_rxflr(info->regs); | 
|  | if ((int)f_tx < 0) { | 
|  | f_tx = 0; /* if rx-fifo is full, hold off tx */ | 
|  | } | 
|  | } else { | 
|  | f_tx = DW_SPI_FIFO_DEPTH - read_txflr(info->regs); | 
|  | } | 
|  | if (f_tx && (spi->tx_buf_len == 0)) { | 
|  | /* room in fifo, yet nothing to send */ | 
|  | spi->last_tx = 1; /* setting last_tx indicates TX is done */ | 
|  | } | 
|  | while (f_tx) { | 
|  | if (spi->tx_buf && spi->tx_buf_len > 0) { | 
|  | switch (spi->dfs) { | 
|  | case 1: | 
|  | data = UNALIGNED_GET((uint8_t *)(spi->tx_buf)); | 
|  | break; | 
|  | case 2: | 
|  | data = UNALIGNED_GET((uint16_t *)(spi->tx_buf)); | 
|  | break; | 
|  | #ifndef CONFIG_ARC | 
|  | case 4: | 
|  | data = UNALIGNED_GET((uint32_t *)(spi->tx_buf)); | 
|  | break; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | spi->tx_buf += spi->dfs; | 
|  | spi->tx_buf_len--; | 
|  | } else if (spi->rx_buf && spi->rx_buf_len > 0) { | 
|  | /* No need to push more than necessary */ | 
|  | if (spi->rx_buf_len - spi->fifo_diff <= 0) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | data = 0; | 
|  | } else { | 
|  | /* Nothing to push anymore */ | 
|  | break; | 
|  | } | 
|  |  | 
|  | write_dr(data, info->regs); | 
|  | f_tx--; | 
|  | spi->fifo_diff++; | 
|  | DBG_COUNTER_INC(); | 
|  | } | 
|  |  | 
|  | if (spi->last_tx) { | 
|  | write_txftlr(0, info->regs); | 
|  | /* prevents any further interrupts demanding TX fifo fill */ | 
|  | } | 
|  |  | 
|  | SYS_LOG_DBG("Pushed: %d", DBG_COUNTER_RESULT()); | 
|  | } | 
|  |  | 
|  | static void pull_data(struct device *dev) | 
|  | { | 
|  | const struct spi_dw_config *info = dev->config->config_info; | 
|  | struct spi_dw_data *spi = dev->driver_data; | 
|  | uint32_t data = 0; | 
|  | DBG_COUNTER_INIT(); | 
|  |  | 
|  | while (read_rxflr(info->regs)) { | 
|  | data = read_dr(info->regs); | 
|  | DBG_COUNTER_INC(); | 
|  |  | 
|  | if (spi->rx_buf && spi->rx_buf_len > 0) { | 
|  | switch (spi->dfs) { | 
|  | case 1: | 
|  | UNALIGNED_PUT(data, (uint8_t *)spi->rx_buf); | 
|  | break; | 
|  | case 2: | 
|  | UNALIGNED_PUT(data, (uint16_t *)spi->rx_buf); | 
|  | break; | 
|  | #ifndef CONFIG_ARC | 
|  | case 4: | 
|  | UNALIGNED_PUT(data, (uint32_t *)spi->rx_buf); | 
|  | break; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | spi->rx_buf += spi->dfs; | 
|  | spi->rx_buf_len--; | 
|  | } | 
|  |  | 
|  | spi->fifo_diff--; | 
|  | } | 
|  |  | 
|  | if (!spi->rx_buf_len && spi->tx_buf_len < DW_SPI_FIFO_DEPTH) { | 
|  | write_rxftlr(spi->tx_buf_len - 1, info->regs); | 
|  | } else if (read_rxftlr(info->regs) >= spi->rx_buf_len) { | 
|  | write_rxftlr(spi->rx_buf_len - 1, info->regs); | 
|  | } | 
|  |  | 
|  | SYS_LOG_DBG("Pulled: %d", DBG_COUNTER_RESULT()); | 
|  | } | 
|  |  | 
|  | static inline bool _spi_dw_is_controller_ready(struct device *dev) | 
|  | { | 
|  | const struct spi_dw_config *info = dev->config->config_info; | 
|  |  | 
|  | if (test_bit_ssienr(info->regs) || test_bit_sr_busy(info->regs)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static int spi_dw_configure(struct device *dev, | 
|  | struct spi_config *config) | 
|  | { | 
|  | const struct spi_dw_config *info = dev->config->config_info; | 
|  | struct spi_dw_data *spi = dev->driver_data; | 
|  | uint32_t flags = config->config; | 
|  | uint32_t ctrlr0 = 0; | 
|  | uint32_t mode; | 
|  |  | 
|  | SYS_LOG_DBG("%s: %p (0x%x), %p", __func__, dev, info->regs, config); | 
|  |  | 
|  | /* Check status */ | 
|  | if (!_spi_dw_is_controller_ready(dev)) { | 
|  | SYS_LOG_DBG("%s: Controller is busy", __func__); | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | /* Word size */ | 
|  | ctrlr0 |= DW_SPI_CTRLR0_DFS(SPI_WORD_SIZE_GET(flags)); | 
|  |  | 
|  | /* Determine how many bytes are required per-frame */ | 
|  | spi->dfs = SPI_DFS_TO_BYTES(SPI_WORD_SIZE_GET(flags)); | 
|  |  | 
|  | /* SPI mode */ | 
|  | mode = SPI_MODE(flags); | 
|  | if (mode & SPI_MODE_CPOL) { | 
|  | ctrlr0 |= DW_SPI_CTRLR0_SCPOL; | 
|  | } | 
|  |  | 
|  | if (mode & SPI_MODE_CPHA) { | 
|  | ctrlr0 |= DW_SPI_CTRLR0_SCPH; | 
|  | } | 
|  |  | 
|  | if (mode & SPI_MODE_LOOP) { | 
|  | ctrlr0 |= DW_SPI_CTRLR0_SRL; | 
|  | } | 
|  |  | 
|  | /* Installing the configuration */ | 
|  | write_ctrlr0(ctrlr0, info->regs); | 
|  |  | 
|  | /* | 
|  | * Configure the rate. Use this small hack to allow the user to call | 
|  | * spi_configure() with both a divider (as the driver was initially | 
|  | * written) and a frequency (as the SPI API suggests to). The clock | 
|  | * divider is a 16bit value, hence we can fairly, and safely, assume | 
|  | * that everything above this value is a frequency. The trade-off is | 
|  | * that if one wants to use a bus frequency of 64kHz (or less), it has | 
|  | * the use a divider... | 
|  | */ | 
|  | if (config->max_sys_freq > 0xffff) { | 
|  | write_baudr(SPI_DW_CLK_DIVIDER(config->max_sys_freq), | 
|  | info->regs); | 
|  | } else { | 
|  | write_baudr(config->max_sys_freq, info->regs); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int spi_dw_slave_select(struct device *dev, uint32_t slave) | 
|  | { | 
|  | struct spi_dw_data *spi = dev->driver_data; | 
|  |  | 
|  | SYS_LOG_DBG("%s: %p %d", __func__, dev, slave); | 
|  |  | 
|  | if (slave == 0 || slave > 16) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | spi->slave = 1 << (slave - 1); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int spi_dw_transceive(struct device *dev, | 
|  | const void *tx_buf, uint32_t tx_buf_len, | 
|  | void *rx_buf, uint32_t rx_buf_len) | 
|  | { | 
|  | const struct spi_dw_config *info = dev->config->config_info; | 
|  | struct spi_dw_data *spi = dev->driver_data; | 
|  | uint32_t rx_thsld = DW_SPI_RXFTLR_DFLT; | 
|  | uint32_t imask; | 
|  |  | 
|  | SYS_LOG_DBG("%s: %p, %p, %u, %p, %u", | 
|  | __func__, dev, tx_buf, tx_buf_len, rx_buf, rx_buf_len); | 
|  |  | 
|  | /* Check status */ | 
|  | if (!_spi_dw_is_controller_ready(dev)) { | 
|  | SYS_LOG_DBG("%s: Controller is busy", __func__); | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | /* Set buffers info */ | 
|  | spi->tx_buf = tx_buf; | 
|  | spi->tx_buf_len = tx_buf_len/spi->dfs; | 
|  | spi->rx_buf = rx_buf; | 
|  | if (rx_buf) { | 
|  | spi->rx_buf_len = rx_buf_len/spi->dfs; | 
|  | } else { | 
|  | spi->rx_buf_len = 0; /* must be zero if no buffer */ | 
|  | } | 
|  | spi->fifo_diff = 0; | 
|  | spi->last_tx = 0; | 
|  |  | 
|  | /* Tx Threshold */ | 
|  | write_txftlr(DW_SPI_TXFTLR_DFLT, info->regs); | 
|  |  | 
|  | /* Does Rx thresholds needs to be lower? */ | 
|  | if (spi->rx_buf_len && spi->rx_buf_len < DW_SPI_FIFO_DEPTH) { | 
|  | rx_thsld = spi->rx_buf_len - 1; | 
|  | } else if (!spi->rx_buf_len && spi->tx_buf_len < DW_SPI_FIFO_DEPTH) { | 
|  | rx_thsld = spi->tx_buf_len - 1; | 
|  | /* TODO: why? */ | 
|  | } | 
|  |  | 
|  | write_rxftlr(rx_thsld, info->regs); | 
|  |  | 
|  | /* Slave select */ | 
|  | write_ser(spi->slave, info->regs); | 
|  |  | 
|  | _spi_control_cs(dev, 1); | 
|  |  | 
|  | /* Enable interrupts */ | 
|  | imask = DW_SPI_IMR_UNMASK; | 
|  | if (!rx_buf) { | 
|  | /* if there is no rx buffer, keep all rx interrupts masked */ | 
|  | imask &= DW_SPI_IMR_MASK_RX; | 
|  | } | 
|  |  | 
|  | write_imr(imask, info->regs); | 
|  |  | 
|  | /* Enable the controller */ | 
|  | set_bit_ssienr(info->regs); | 
|  |  | 
|  | k_sem_take(&spi->device_sync_sem, K_FOREVER); | 
|  |  | 
|  | if (spi->error) { | 
|  | spi->error = 0; | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void spi_dw_isr(void *arg) | 
|  | { | 
|  | struct device *dev = (struct device *)arg; | 
|  | const struct spi_dw_config *info = dev->config->config_info; | 
|  | uint32_t error = 0; | 
|  | uint32_t int_status; | 
|  |  | 
|  | int_status = read_isr(info->regs); | 
|  |  | 
|  | SYS_LOG_DBG("SPI int_status 0x%x - (tx: %d, rx: %d)", | 
|  | int_status, read_txflr(info->regs), read_rxflr(info->regs)); | 
|  |  | 
|  | if (int_status & DW_SPI_ISR_ERRORS_MASK) { | 
|  | error = 1; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (int_status & DW_SPI_ISR_RXFIS) { | 
|  | pull_data(dev); | 
|  | } | 
|  |  | 
|  | if (int_status & DW_SPI_ISR_TXEIS) { | 
|  | push_data(dev); | 
|  | } | 
|  |  | 
|  | out: | 
|  | clear_interrupts(info->regs); | 
|  | completed(dev, error); | 
|  | } | 
|  |  | 
|  | static const struct spi_driver_api dw_spi_api = { | 
|  | .configure = spi_dw_configure, | 
|  | .slave_select = spi_dw_slave_select, | 
|  | .transceive = spi_dw_transceive, | 
|  | }; | 
|  |  | 
|  | int spi_dw_init(struct device *dev) | 
|  | { | 
|  | const struct spi_dw_config *info = dev->config->config_info; | 
|  | struct spi_dw_data *spi = dev->driver_data; | 
|  |  | 
|  | _clock_config(dev); | 
|  | _clock_on(dev); | 
|  |  | 
|  | #if 0 /* TODO: Not correct version for every target. Don't check. */ | 
|  | #ifndef CONFIG_SOC_QUARK_SE_C1000_SS | 
|  | if (read_ssi_comp_version(info->regs) != DW_SSI_COMP_VERSION) { | 
|  | dev->driver_api = NULL; | 
|  | _clock_off(dev); | 
|  | return -EPERM; | 
|  | } | 
|  | #endif | 
|  | #endif | 
|  |  | 
|  | info->config_func(); | 
|  |  | 
|  | k_sem_init(&spi->device_sync_sem, 0, UINT_MAX); | 
|  |  | 
|  | _spi_config_cs(dev); | 
|  |  | 
|  | /* Masking interrupt and making sure controller is disabled */ | 
|  | write_imr(DW_SPI_IMR_MASK, info->regs); | 
|  | clear_bit_ssienr(info->regs); | 
|  |  | 
|  | SYS_LOG_DBG("Designware SPI driver initialized on device: %p", dev); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | #ifdef CONFIG_SPI_0 | 
|  | void spi_config_0_irq(void); | 
|  |  | 
|  | struct spi_dw_data spi_dw_data_port_0; | 
|  |  | 
|  | const struct spi_dw_config spi_dw_config_0 = { | 
|  | .regs = SPI_DW_PORT_0_REGS, | 
|  | #ifdef CONFIG_SPI_DW_CLOCK_GATE | 
|  | .clock_data = UINT_TO_POINTER(CONFIG_SPI_0_CLOCK_GATE_SUBSYS), | 
|  | #endif /* CONFIG_SPI_DW_CLOCK_GATE */ | 
|  | #ifdef CONFIG_SPI_DW_CS_GPIO | 
|  | .cs_gpio_name = CONFIG_SPI_0_CS_GPIO_PORT, | 
|  | .cs_gpio_pin = CONFIG_SPI_0_CS_GPIO_PIN, | 
|  | #endif | 
|  | .config_func = spi_config_0_irq | 
|  | }; | 
|  |  | 
|  | DEVICE_AND_API_INIT(spi_dw_port_0, CONFIG_SPI_0_NAME, spi_dw_init, | 
|  | &spi_dw_data_port_0, &spi_dw_config_0, | 
|  | POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, | 
|  | &dw_spi_api); | 
|  |  | 
|  | void spi_config_0_irq(void) | 
|  | { | 
|  | #ifdef CONFIG_SPI_DW_INTERRUPT_SINGLE_LINE | 
|  | IRQ_CONNECT(SPI_DW_PORT_0_IRQ, CONFIG_SPI_0_IRQ_PRI, | 
|  | spi_dw_isr, DEVICE_GET(spi_dw_port_0), SPI_DW_IRQ_FLAGS); | 
|  | irq_enable(SPI_DW_PORT_0_IRQ); | 
|  | _spi_int_unmask(SPI_DW_PORT_0_INT_MASK); | 
|  | #else /* SPI_DW_INTERRUPT_SEPARATED_LINES */ | 
|  | IRQ_CONNECT(IRQ_SPI0_RX_AVAIL, CONFIG_SPI_0_IRQ_PRI, | 
|  | spi_dw_isr, DEVICE_GET(spi_dw_port_0), SPI_DW_IRQ_FLAGS); | 
|  | IRQ_CONNECT(IRQ_SPI0_TX_REQ, CONFIG_SPI_0_IRQ_PRI, | 
|  | spi_dw_isr, DEVICE_GET(spi_dw_port_0), SPI_DW_IRQ_FLAGS); | 
|  | IRQ_CONNECT(IRQ_SPI0_ERR_INT, CONFIG_SPI_0_IRQ_PRI, | 
|  | spi_dw_isr, DEVICE_GET(spi_dw_port_0), SPI_DW_IRQ_FLAGS); | 
|  |  | 
|  | irq_enable(IRQ_SPI0_RX_AVAIL); | 
|  | irq_enable(IRQ_SPI0_TX_REQ); | 
|  | irq_enable(IRQ_SPI0_ERR_INT); | 
|  |  | 
|  | _spi_int_unmask(SPI_DW_PORT_0_RX_INT_MASK); | 
|  | _spi_int_unmask(SPI_DW_PORT_0_TX_INT_MASK); | 
|  | _spi_int_unmask(SPI_DW_PORT_0_ERROR_INT_MASK); | 
|  | #endif | 
|  | } | 
|  | #endif /* CONFIG_SPI_0 */ | 
|  | #ifdef CONFIG_SPI_1 | 
|  | void spi_config_1_irq(void); | 
|  |  | 
|  | struct spi_dw_data spi_dw_data_port_1; | 
|  |  | 
|  | static const struct spi_dw_config spi_dw_config_1 = { | 
|  | .regs = SPI_DW_PORT_1_REGS, | 
|  | #ifdef CONFIG_SPI_DW_CLOCK_GATE | 
|  | .clock_data = UINT_TO_POINTER(CONFIG_SPI_1_CLOCK_GATE_SUBSYS), | 
|  | #endif /* CONFIG_SPI_DW_CLOCK_GATE */ | 
|  | #ifdef CONFIG_SPI_DW_CS_GPIO | 
|  | .cs_gpio_name = CONFIG_SPI_1_CS_GPIO_PORT, | 
|  | .cs_gpio_pin = CONFIG_SPI_1_CS_GPIO_PIN, | 
|  | #endif | 
|  | .config_func = spi_config_1_irq | 
|  | }; | 
|  |  | 
|  | DEVICE_AND_API_INIT(spi_dw_port_1, CONFIG_SPI_1_NAME, spi_dw_init, | 
|  | &spi_dw_data_port_1, &spi_dw_config_1, | 
|  | POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, | 
|  | &dw_spi_api); | 
|  |  | 
|  | void spi_config_1_irq(void) | 
|  | { | 
|  | #ifdef CONFIG_SPI_DW_INTERRUPT_SINGLE_LINE | 
|  | IRQ_CONNECT(SPI_DW_PORT_1_IRQ, CONFIG_SPI_1_IRQ_PRI, | 
|  | spi_dw_isr, DEVICE_GET(spi_dw_port_1), SPI_DW_IRQ_FLAGS); | 
|  | irq_enable(SPI_DW_PORT_1_IRQ); | 
|  | _spi_int_unmask(SPI_DW_PORT_1_INT_MASK); | 
|  | #else /* SPI_DW_INTERRUPT_SEPARATED_LINES */ | 
|  | IRQ_CONNECT(IRQ_SPI1_RX_AVAIL, CONFIG_SPI_1_IRQ_PRI, | 
|  | spi_dw_isr, DEVICE_GET(spi_dw_port_1), SPI_DW_IRQ_FLAGS); | 
|  | IRQ_CONNECT(IRQ_SPI1_TX_REQ, CONFIG_SPI_1_IRQ_PRI, | 
|  | spi_dw_isr, DEVICE_GET(spi_dw_port_1), SPI_DW_IRQ_FLAGS); | 
|  | IRQ_CONNECT(IRQ_SPI1_ERR_INT, CONFIG_SPI_1_IRQ_PRI, | 
|  | spi_dw_isr, DEVICE_GET(spi_dw_port_1), SPI_DW_IRQ_FLAGS); | 
|  |  | 
|  | irq_enable(IRQ_SPI1_RX_AVAIL); | 
|  | irq_enable(IRQ_SPI1_TX_REQ); | 
|  | irq_enable(IRQ_SPI1_ERR_INT); | 
|  |  | 
|  | _spi_int_unmask(SPI_DW_PORT_1_RX_INT_MASK); | 
|  | _spi_int_unmask(SPI_DW_PORT_1_TX_INT_MASK); | 
|  | _spi_int_unmask(SPI_DW_PORT_1_ERROR_INT_MASK); | 
|  | #endif | 
|  | } | 
|  | #endif /* CONFIG_SPI_1 */ |