diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 10 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/fsl_qspi.c | 9 | ||||
-rw-r--r-- | drivers/spi/ich.c | 4 | ||||
-rw-r--r-- | drivers/spi/mvebu_a3700_spi.c | 41 | ||||
-rw-r--r-- | drivers/spi/nxp_fspi.c | 17 | ||||
-rw-r--r-- | drivers/spi/octeon_spi.c | 5 | ||||
-rw-r--r-- | drivers/spi/renesas_rpc_spi.c | 3 | ||||
-rw-r--r-- | drivers/spi/spi-qup.c | 803 | ||||
-rw-r--r-- | drivers/spi/xilinx_spi.c | 8 | ||||
-rw-r--r-- | drivers/spi/zynq_qspi.c | 84 | ||||
-rw-r--r-- | drivers/spi/zynq_spi.c | 35 |
12 files changed, 968 insertions, 52 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 5df97c80fa..f7a9852565 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -271,6 +271,16 @@ config PL022_SPI controller. If you have an embedded system with an AMBA(R) bus and a PL022 controller, say Y or M here. +config SPI_QUP + bool "Qualcomm SPI controller with QUP interface" + depends on ARCH_IPQ40XX + help + Qualcomm Universal Peripheral (QUP) core is an AHB slave that + provides a common data path (an output FIFO and an input FIFO) + for serial peripheral interface (SPI) mini-core. SPI in master + mode supports up to 50MHz, up to four chip selects, programmable + data path from 4 bits to 32 bits and numerous protocol variants. + config RENESAS_RPC_SPI bool "Renesas RPC SPI driver" depends on RCAR_GEN3 || RZA1 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index b5c9ff1af8..d9b5bd9b79 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_OCTEON_SPI) += octeon_spi.o obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o obj-$(CONFIG_PIC32_SPI) += pic32_spi.o obj-$(CONFIG_PL022_SPI) += pl022_spi.o +obj-$(CONFIG_SPI_QUP) += spi-qup.o obj-$(CONFIG_RENESAS_RPC_SPI) += renesas_rpc_spi.o obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o diff --git a/drivers/spi/fsl_qspi.c b/drivers/spi/fsl_qspi.c index eec968e5ec..128f95877f 100644 --- a/drivers/spi/fsl_qspi.c +++ b/drivers/spi/fsl_qspi.c @@ -24,19 +24,20 @@ */ #include <common.h> +#include <dm.h> +#include <dm/device_compat.h> #include <log.h> -#include <asm/io.h> +#include <spi.h> +#include <spi-mem.h> #include <linux/bitops.h> #include <linux/delay.h> #include <linux/libfdt.h> #include <linux/sizes.h> #include <linux/iopoll.h> -#include <dm.h> #include <linux/iopoll.h> #include <linux/sizes.h> #include <linux/err.h> -#include <spi.h> -#include <spi-mem.h> +#include <asm/io.h> DECLARE_GLOBAL_DATA_PTR; diff --git a/drivers/spi/ich.c b/drivers/spi/ich.c index e1336b89c5..a91cb78568 100644 --- a/drivers/spi/ich.c +++ b/drivers/spi/ich.c @@ -615,6 +615,7 @@ static int ich_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op) return ret; } +#if !CONFIG_IS_ENABLED(OF_PLATDATA) /** * ich_spi_get_basics() - Get basic information about the ICH device * @@ -657,6 +658,7 @@ static int ich_spi_get_basics(struct udevice *bus, bool can_probe, return ret; } +#endif /** * ich_get_mmap_bus() - Handle the get_mmap() method for a bus @@ -946,10 +948,10 @@ static int ich_spi_child_pre_probe(struct udevice *dev) static int ich_spi_ofdata_to_platdata(struct udevice *dev) { struct ich_spi_platdata *plat = dev_get_platdata(dev); - int ret; #if !CONFIG_IS_ENABLED(OF_PLATDATA) struct ich_spi_priv *priv = dev_get_priv(dev); + int ret; ret = ich_spi_get_basics(dev, true, &priv->pch, &plat->ich_version, &plat->mmio_base); diff --git a/drivers/spi/mvebu_a3700_spi.c b/drivers/spi/mvebu_a3700_spi.c index e860b9ec64..eb13cf349e 100644 --- a/drivers/spi/mvebu_a3700_spi.c +++ b/drivers/spi/mvebu_a3700_spi.c @@ -15,6 +15,7 @@ #include <asm/io.h> #include <dm/device_compat.h> #include <linux/bitops.h> +#include <asm/gpio.h> DECLARE_GLOBAL_DATA_PTR; @@ -27,6 +28,7 @@ DECLARE_GLOBAL_DATA_PTR; #define MVEBU_SPI_A3700_SPI_EN_0 BIT(16) #define MVEBU_SPI_A3700_CLK_PRESCALE_MASK 0x1f +#define MAX_CS_COUNT 4 /* SPI registers */ struct spi_reg { @@ -39,16 +41,23 @@ struct spi_reg { struct mvebu_spi_platdata { struct spi_reg *spireg; struct clk clk; + struct gpio_desc cs_gpios[MAX_CS_COUNT]; }; -static void spi_cs_activate(struct spi_reg *reg, int cs) +static void spi_cs_activate(struct mvebu_spi_platdata *plat, int cs) { - setbits_le32(®->ctrl, MVEBU_SPI_A3700_SPI_EN_0 << cs); + if (CONFIG_IS_ENABLED(DM_GPIO) && dm_gpio_is_valid(&plat->cs_gpios[cs])) + dm_gpio_set_value(&plat->cs_gpios[cs], 1); + else + setbits_le32(&plat->spireg->ctrl, MVEBU_SPI_A3700_SPI_EN_0 << cs); } -static void spi_cs_deactivate(struct spi_reg *reg, int cs) +static void spi_cs_deactivate(struct mvebu_spi_platdata *plat, int cs) { - clrbits_le32(®->ctrl, MVEBU_SPI_A3700_SPI_EN_0 << cs); + if (CONFIG_IS_ENABLED(DM_GPIO) && dm_gpio_is_valid(&plat->cs_gpios[cs])) + dm_gpio_set_value(&plat->cs_gpios[cs], 0); + else + clrbits_le32(&plat->spireg->ctrl, MVEBU_SPI_A3700_SPI_EN_0 << cs); } /** @@ -150,7 +159,7 @@ static int mvebu_spi_xfer(struct udevice *dev, unsigned int bitlen, /* Activate CS */ if (flags & SPI_XFER_BEGIN) { debug("SPI: activate cs.\n"); - spi_cs_activate(reg, spi_chip_select(dev)); + spi_cs_activate(plat, spi_chip_select(dev)); } /* Send and/or receive */ @@ -169,7 +178,7 @@ static int mvebu_spi_xfer(struct udevice *dev, unsigned int bitlen, return ret; debug("SPI: deactivate cs.\n"); - spi_cs_deactivate(reg, spi_chip_select(dev)); + spi_cs_deactivate(plat, spi_chip_select(dev)); } return 0; @@ -247,6 +256,26 @@ static int mvebu_spi_probe(struct udevice *bus) writel(data, ®->cfg); + /* Set up CS GPIOs in device tree, if any */ + if (CONFIG_IS_ENABLED(DM_GPIO) && gpio_get_list_count(bus, "cs-gpios") > 0) { + int i; + + for (i = 0; i < ARRAY_SIZE(plat->cs_gpios); i++) { + ret = gpio_request_by_name(bus, "cs-gpios", i, &plat->cs_gpios[i], 0); + if (ret < 0 || !dm_gpio_is_valid(&plat->cs_gpios[i])) { + /* Use the native CS function for this line */ + continue; + } + + ret = dm_gpio_set_dir_flags(&plat->cs_gpios[i], + GPIOD_IS_OUT | GPIOD_ACTIVE_LOW); + if (ret) { + dev_err(bus, "Setting cs %d error\n", i); + return ret; + } + } + } + return 0; } diff --git a/drivers/spi/nxp_fspi.c b/drivers/spi/nxp_fspi.c index c507437f2e..9661e9e10d 100644 --- a/drivers/spi/nxp_fspi.c +++ b/drivers/spi/nxp_fspi.c @@ -34,12 +34,13 @@ */ #include <common.h> -#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> #include <malloc.h> #include <spi.h> #include <spi-mem.h> -#include <dm.h> -#include <clk.h> +#include <asm/io.h> #include <linux/bitops.h> #include <linux/kernel.h> #include <linux/sizes.h> @@ -520,7 +521,7 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f, fspi_writel(f, FSPI_LCKER_LOCK, f->iobase + FSPI_LCKCR); } -#if CONFIG_IS_ENABLED(CONFIG_CLK) +#if CONFIG_IS_ENABLED(CLK) static int nxp_fspi_clk_prep_enable(struct nxp_fspi *f) { int ret; @@ -808,7 +809,7 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f) int ret, i; u32 reg; -#if CONFIG_IS_ENABLED(CONFIG_CLK) +#if CONFIG_IS_ENABLED(CLK) /* disable and unprepare clock to avoid glitch pass to controller */ nxp_fspi_clk_disable_unprep(f); @@ -898,7 +899,7 @@ static int nxp_fspi_claim_bus(struct udevice *dev) static int nxp_fspi_set_speed(struct udevice *bus, uint speed) { -#if CONFIG_IS_ENABLED(CONFIG_CLK) +#if CONFIG_IS_ENABLED(CLK) struct nxp_fspi *f = dev_get_priv(bus); int ret; @@ -924,7 +925,7 @@ static int nxp_fspi_set_mode(struct udevice *bus, uint mode) static int nxp_fspi_ofdata_to_platdata(struct udevice *bus) { struct nxp_fspi *f = dev_get_priv(bus); -#if CONFIG_IS_ENABLED(CONFIG_CLK) +#if CONFIG_IS_ENABLED(CLK) int ret; #endif @@ -950,7 +951,7 @@ static int nxp_fspi_ofdata_to_platdata(struct udevice *bus) f->ahb_addr = map_physmem(ahb_addr, ahb_size, MAP_NOCACHE); f->memmap_phy_size = ahb_size; -#if CONFIG_IS_ENABLED(CONFIG_CLK) +#if CONFIG_IS_ENABLED(CLK) ret = clk_get_by_name(bus, "fspi_en", &f->clk_en); if (ret) { dev_err(bus, "failed to get fspi_en clock\n"); diff --git a/drivers/spi/octeon_spi.c b/drivers/spi/octeon_spi.c index 83fe6330a1..7e88e5580f 100644 --- a/drivers/spi/octeon_spi.c +++ b/drivers/spi/octeon_spi.c @@ -519,7 +519,10 @@ static int octeon_spi_set_speed(struct udevice *bus, uint max_hz) if (max_hz > OCTEON_SPI_MAX_CLOCK_HZ) max_hz = OCTEON_SPI_MAX_CLOCK_HZ; - clk_rate = clk_get_rate(&priv->clk); + if (device_is_compatible(bus, "cavium,thunderx-spi")) + clk_rate = 100000000; + else + clk_rate = clk_get_rate(&priv->clk); if (IS_ERR_VALUE(clk_rate)) return -EINVAL; diff --git a/drivers/spi/renesas_rpc_spi.c b/drivers/spi/renesas_rpc_spi.c index 3ea59b8fb8..d0ff918af8 100644 --- a/drivers/spi/renesas_rpc_spi.c +++ b/drivers/spi/renesas_rpc_spi.c @@ -448,12 +448,13 @@ static const struct dm_spi_ops rpc_spi_ops = { }; static const struct udevice_id rpc_spi_ids[] = { + { .compatible = "renesas,rpc-r7s72100" }, { .compatible = "renesas,rpc-r8a7795" }, { .compatible = "renesas,rpc-r8a7796" }, { .compatible = "renesas,rpc-r8a77965" }, { .compatible = "renesas,rpc-r8a77970" }, { .compatible = "renesas,rpc-r8a77995" }, - { .compatible = "renesas,rpc-r7s72100" }, + { .compatible = "renesas,rcar-gen3-rpc" }, { } }; diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c new file mode 100644 index 0000000000..6f8df55fa5 --- /dev/null +++ b/drivers/spi/spi-qup.c @@ -0,0 +1,803 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Qualcomm QUP SPI controller + * FIFO and Block modes supported, no DMA + * mode support + * + * Copyright (c) 2020 Sartura Ltd. + * + * Author: Robert Marko <robert.marko@sartura.hr> + * Author: Luka Kovacic <luka.kovacic@sartura.hr> + * + * Based on stock U-boot and Linux drivers + */ + +#include <asm/gpio.h> +#include <asm/io.h> +#include <clk.h> +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <linux/delay.h> +#include <spi.h> + +#define QUP_CONFIG 0x0000 +#define QUP_STATE 0x0004 +#define QUP_IO_M_MODES 0x0008 +#define QUP_SW_RESET 0x000c +#define QUP_OPERATIONAL 0x0018 +#define QUP_ERROR_FLAGS 0x001c +#define QUP_ERROR_FLAGS_EN 0x0020 +#define QUP_OPERATIONAL_MASK 0x0028 +#define QUP_HW_VERSION 0x0030 +#define QUP_MX_OUTPUT_CNT 0x0100 +#define QUP_OUTPUT_FIFO 0x0110 +#define QUP_MX_WRITE_CNT 0x0150 +#define QUP_MX_INPUT_CNT 0x0200 +#define QUP_MX_READ_CNT 0x0208 +#define QUP_INPUT_FIFO 0x0218 + +#define SPI_CONFIG 0x0300 +#define SPI_IO_CONTROL 0x0304 +#define SPI_ERROR_FLAGS 0x0308 +#define SPI_ERROR_FLAGS_EN 0x030c + +/* QUP_CONFIG fields */ +#define QUP_CONFIG_SPI_MODE BIT(8) +#define QUP_CONFIG_CLOCK_AUTO_GATE BIT(13) +#define QUP_CONFIG_NO_INPUT BIT(7) +#define QUP_CONFIG_NO_OUTPUT BIT(6) +#define QUP_CONFIG_N 0x001f + +/* QUP_STATE fields */ +#define QUP_STATE_VALID BIT(2) +#define QUP_STATE_RESET 0 +#define QUP_STATE_RUN 1 +#define QUP_STATE_PAUSE 3 +#define QUP_STATE_MASK 3 +#define QUP_STATE_CLEAR 2 + +/* QUP_IO_M_MODES fields */ +#define QUP_IO_M_PACK_EN BIT(15) +#define QUP_IO_M_UNPACK_EN BIT(14) +#define QUP_IO_M_INPUT_MODE_MASK_SHIFT 12 +#define QUP_IO_M_OUTPUT_MODE_MASK_SHIFT 10 +#define QUP_IO_M_INPUT_MODE_MASK (3 << QUP_IO_M_INPUT_MODE_MASK_SHIFT) +#define QUP_IO_M_OUTPUT_MODE_MASK (3 << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT) + +#define QUP_IO_M_OUTPUT_BLOCK_SIZE(x) (((x) & (0x03 << 0)) >> 0) +#define QUP_IO_M_OUTPUT_FIFO_SIZE(x) (((x) & (0x07 << 2)) >> 2) +#define QUP_IO_M_INPUT_BLOCK_SIZE(x) (((x) & (0x03 << 5)) >> 5) +#define QUP_IO_M_INPUT_FIFO_SIZE(x) (((x) & (0x07 << 7)) >> 7) + +#define QUP_IO_M_MODE_FIFO 0 +#define QUP_IO_M_MODE_BLOCK 1 +#define QUP_IO_M_MODE_DMOV 2 +#define QUP_IO_M_MODE_BAM 3 + +/* QUP_OPERATIONAL fields */ +#define QUP_OP_IN_BLOCK_READ_REQ BIT(13) +#define QUP_OP_OUT_BLOCK_WRITE_REQ BIT(12) +#define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11) +#define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10) +#define QUP_OP_IN_SERVICE_FLAG BIT(9) +#define QUP_OP_OUT_SERVICE_FLAG BIT(8) +#define QUP_OP_IN_FIFO_FULL BIT(7) +#define QUP_OP_OUT_FIFO_FULL BIT(6) +#define QUP_OP_IN_FIFO_NOT_EMPTY BIT(5) +#define QUP_OP_OUT_FIFO_NOT_EMPTY BIT(4) + +/* QUP_ERROR_FLAGS and QUP_ERROR_FLAGS_EN fields */ +#define QUP_ERROR_OUTPUT_OVER_RUN BIT(5) +#define QUP_ERROR_INPUT_UNDER_RUN BIT(4) +#define QUP_ERROR_OUTPUT_UNDER_RUN BIT(3) +#define QUP_ERROR_INPUT_OVER_RUN BIT(2) + +/* SPI_CONFIG fields */ +#define SPI_CONFIG_HS_MODE BIT(10) +#define SPI_CONFIG_INPUT_FIRST BIT(9) +#define SPI_CONFIG_LOOPBACK BIT(8) + +/* SPI_IO_CONTROL fields */ +#define SPI_IO_C_FORCE_CS BIT(11) +#define SPI_IO_C_CLK_IDLE_HIGH BIT(10) +#define SPI_IO_C_MX_CS_MODE BIT(8) +#define SPI_IO_C_CS_N_POLARITY_0 BIT(4) +#define SPI_IO_C_CS_SELECT(x) (((x) & 3) << 2) +#define SPI_IO_C_CS_SELECT_MASK 0x000c +#define SPI_IO_C_TRISTATE_CS BIT(1) +#define SPI_IO_C_NO_TRI_STATE BIT(0) + +/* SPI_ERROR_FLAGS and SPI_ERROR_FLAGS_EN fields */ +#define SPI_ERROR_CLK_OVER_RUN BIT(1) +#define SPI_ERROR_CLK_UNDER_RUN BIT(0) + +#define SPI_NUM_CHIPSELECTS 4 + +#define SPI_DELAY_THRESHOLD 1 +#define SPI_DELAY_RETRY 10 + +#define SPI_RESET_STATE 0 +#define SPI_RUN_STATE 1 +#define SPI_CORE_RESET 0 +#define SPI_CORE_RUNNING 1 + +#define DUMMY_DATA_VAL 0 +#define TIMEOUT_CNT 100 + +#define QUP_STATE_VALID_BIT 2 +#define QUP_CONFIG_MINI_CORE_MSK (0x0F << 8) +#define QUP_CONFIG_MINI_CORE_SPI BIT(8) +#define QUP_CONF_INPUT_MSK BIT(7) +#define QUP_CONF_INPUT_ENA (0 << 7) +#define QUP_CONF_NO_INPUT BIT(7) +#define QUP_CONF_OUTPUT_MSK BIT(6) +#define QUP_CONF_OUTPUT_ENA (0 << 6) +#define QUP_CONF_NO_OUTPUT BIT(6) +#define QUP_STATE_RUN_STATE 0x1 +#define QUP_STATE_RESET_STATE 0x0 +#define QUP_STATE_PAUSE_STATE 0x3 +#define SPI_BIT_WORD_MSK 0x1F +#define SPI_8_BIT_WORD 0x07 +#define LOOP_BACK_MSK BIT(8) +#define NO_LOOP_BACK (0 << 8) +#define SLAVE_OPERATION_MSK BIT(5) +#define SLAVE_OPERATION (0 << 5) +#define CLK_ALWAYS_ON (0 << 9) +#define MX_CS_MODE BIT(8) +#define CS_POLARITY_MASK BIT(4) +#define NO_TRI_STATE BIT(0) +#define FORCE_CS_MSK BIT(11) +#define FORCE_CS_EN BIT(11) +#define FORCE_CS_DIS (0 << 11) +#define OUTPUT_BIT_SHIFT_MSK BIT(16) +#define OUTPUT_BIT_SHIFT_EN BIT(16) +#define INPUT_BLOCK_MODE_MSK (0x03 << 12) +#define INPUT_BLOCK_MODE (0x01 << 12) +#define OUTPUT_BLOCK_MODE_MSK (0x03 << 10) +#define OUTPUT_BLOCK_MODE (0x01 << 10) +#define INPUT_BAM_MODE (0x3 << 12) +#define OUTPUT_BAM_MODE (0x3 << 10) +#define PACK_EN (0x1 << 15) +#define UNPACK_EN (0x1 << 14) +#define PACK_EN_MSK (0x1 << 15) +#define UNPACK_EN_MSK (0x1 << 14) +#define OUTPUT_SERVICE_MSK (0x1 << 8) +#define INPUT_SERVICE_MSK (0x1 << 9) +#define OUTPUT_SERVICE_DIS (0x1 << 8) +#define INPUT_SERVICE_DIS (0x1 << 9) +#define BLSP0_SPI_DEASSERT_WAIT_REG 0x0310 +#define QUP_DATA_AVAILABLE_FOR_READ BIT(5) +#define SPI_INPUT_BLOCK_SIZE 4 +#define SPI_OUTPUT_BLOCK_SIZE 4 +#define SPI_BITLEN_MSK 0x07 +#define MAX_COUNT_SIZE 0xffff + +struct qup_spi_priv { + phys_addr_t base; + struct clk clk; + u32 num_cs; + struct gpio_desc cs_gpios[SPI_NUM_CHIPSELECTS]; + bool cs_high; + u32 core_state; +}; + +static int qup_spi_set_cs(struct udevice *dev, unsigned int cs, bool enable) +{ + struct qup_spi_priv *priv = dev_get_priv(dev); + + debug("%s: cs=%d enable=%d\n", __func__, cs, enable); + + if (cs >= SPI_NUM_CHIPSELECTS) + return -ENODEV; + + if (!dm_gpio_is_valid(&priv->cs_gpios[cs])) + return -EINVAL; + + if (priv->cs_high) + enable = !enable; + + return dm_gpio_set_value(&priv->cs_gpios[cs], enable ? 1 : 0); +} + +/* + * Function to write data to OUTPUT FIFO + */ +static void qup_spi_write_byte(struct udevice *dev, unsigned char data) +{ + struct udevice *bus = dev_get_parent(dev); + struct qup_spi_priv *priv = dev_get_priv(bus); + /* Wait for space in the FIFO */ + while ((readl(priv->base + QUP_OPERATIONAL) & QUP_OP_OUT_FIFO_FULL)) + udelay(1); + + /* Write the byte of data */ + writel(data, priv->base + QUP_OUTPUT_FIFO); +} + +/* + * Function to read data from Input FIFO + */ +static unsigned char qup_spi_read_byte(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct qup_spi_priv *priv = dev_get_priv(bus); + /* Wait for Data in FIFO */ + while (!(readl(priv->base + QUP_OPERATIONAL) & QUP_DATA_AVAILABLE_FOR_READ)) { + printf("Stuck at FIFO data wait\n"); + udelay(1); + } + + /* Read a byte of data */ + return readl(priv->base + QUP_INPUT_FIFO) & 0xff; +} + +/* + * Function to check wheather Input or Output FIFO + * has data to be serviced + */ +static int qup_spi_check_fifo_status(struct udevice *dev, u32 reg_addr) +{ + struct udevice *bus = dev_get_parent(dev); + struct qup_spi_priv *priv = dev_get_priv(bus); + unsigned int count = TIMEOUT_CNT; + unsigned int status_flag; + unsigned int val; + + do { + val = readl(priv->base + reg_addr); + count--; + if (count == 0) + return -ETIMEDOUT; + + status_flag = ((val & QUP_OP_OUT_SERVICE_FLAG) | (val & QUP_OP_IN_SERVICE_FLAG)); + } while (!status_flag); + + return 0; +} + +/* + * Function to configure Input and Output enable/disable + */ +static void qup_spi_enable_io_config(struct udevice *dev, u32 write_cnt, u32 read_cnt) +{ + struct udevice *bus = dev_get_parent(dev); + struct qup_spi_priv *priv = dev_get_priv(bus); + + if (write_cnt) { + clrsetbits_le32(priv->base + QUP_CONFIG, + QUP_CONF_OUTPUT_MSK, QUP_CONF_OUTPUT_ENA); + } else { + clrsetbits_le32(priv->base + QUP_CONFIG, + QUP_CONF_OUTPUT_MSK, QUP_CONF_NO_OUTPUT); + } + + if (read_cnt) { + clrsetbits_le32(priv->base + QUP_CONFIG, + QUP_CONF_INPUT_MSK, QUP_CONF_INPUT_ENA); + } else { + clrsetbits_le32(priv->base + QUP_CONFIG, + QUP_CONF_INPUT_MSK, QUP_CONF_NO_INPUT); + } +} + +static int check_bit_state(struct udevice *dev, u32 reg_addr, int bit_num, int val, + int us_delay) +{ + struct udevice *bus = dev_get_parent(dev); + struct qup_spi_priv *priv = dev_get_priv(bus); + unsigned int count = TIMEOUT_CNT; + unsigned int bit_val = ((readl(priv->base + reg_addr) >> bit_num) & 0x01); + + while (bit_val != val) { + count--; + if (count == 0) + return -ETIMEDOUT; + udelay(us_delay); + bit_val = ((readl(priv->base + reg_addr) >> bit_num) & 0x01); + } + + return 0; +} + +/* + * Check whether QUPn State is valid + */ +static int check_qup_state_valid(struct udevice *dev) +{ + return check_bit_state(dev, QUP_STATE, QUP_STATE_VALID, 1, 1); +} + +/* + * Configure QUPn Core state + */ +static int qup_spi_config_spi_state(struct udevice *dev, unsigned int state) +{ + struct udevice *bus = dev_get_parent(dev); + struct qup_spi_priv *priv = dev_get_priv(bus); + u32 val; + int ret; + + ret = check_qup_state_valid(dev); + if (ret != 0) + return ret; + + switch (state) { + case SPI_RUN_STATE: + /* Set the state to RUN */ + val = ((readl(priv->base + QUP_STATE) & ~QUP_STATE_MASK) + | QUP_STATE_RUN); + writel(val, priv->base + QUP_STATE); + ret = check_qup_state_valid(dev); + if (ret != 0) + return ret; + priv->core_state = SPI_CORE_RUNNING; + break; + case SPI_RESET_STATE: + /* Set the state to RESET */ + val = ((readl(priv->base + QUP_STATE) & ~QUP_STATE_MASK) + | QUP_STATE_RESET); + writel(val, priv->base + QUP_STATE); + ret = check_qup_state_valid(dev); + if (ret != 0) + return ret; + priv->core_state = SPI_CORE_RESET; + break; + default: + printf("Unsupported QUP SPI state: %d\n", state); + ret = -EINVAL; + break; + } + return ret; +} + +/* + * Function to read bytes number of data from the Input FIFO + */ +static int __qup_spi_blsp_spi_read(struct udevice *dev, u8 *data_buffer, unsigned int bytes) +{ + struct udevice *bus = dev_get_parent(dev); + struct qup_spi_priv *priv = dev_get_priv(bus); + u32 val; + unsigned int i; + unsigned int read_bytes = bytes; + unsigned int fifo_count; + int ret = 0; + int state_config; + + /* Configure no of bytes to read */ + state_config = qup_spi_config_spi_state(dev, SPI_RESET_STATE); + if (state_config) + return state_config; + + /* Configure input and output enable */ + qup_spi_enable_io_config(dev, 0, read_bytes); + + writel(bytes, priv->base + QUP_MX_INPUT_CNT); + + state_config = qup_spi_config_spi_state(dev, SPI_RUN_STATE); + if (state_config) + return state_config; + + while (read_bytes) { + ret = qup_spi_check_fifo_status(dev, QUP_OPERATIONAL); + if (ret != 0) + goto out; + + val = readl(priv->base + QUP_OPERATIONAL); + if (val & QUP_OP_IN_SERVICE_FLAG) { + /* + * acknowledge to hw that software will + * read input data + */ + val &= QUP_OP_IN_SERVICE_FLAG; + writel(val, priv->base + QUP_OPERATIONAL); + + fifo_count = ((read_bytes > SPI_INPUT_BLOCK_SIZE) ? + SPI_INPUT_BLOCK_SIZE : read_bytes); + + for (i = 0; i < fifo_count; i++) { + *data_buffer = qup_spi_read_byte(dev); + data_buffer++; + read_bytes--; + } + } + } + +out: + /* + * Put the SPI Core back in the Reset State + * to end the transfer + */ + (void)qup_spi_config_spi_state(dev, SPI_RESET_STATE); + + return ret; +} + +static int qup_spi_blsp_spi_read(struct udevice *dev, u8 *data_buffer, unsigned int bytes) +{ + int length, ret; + + while (bytes) { + length = (bytes < MAX_COUNT_SIZE) ? bytes : MAX_COUNT_SIZE; + + ret = __qup_spi_blsp_spi_read(dev, data_buffer, length); + if (ret != 0) + return ret; + + data_buffer += length; + bytes -= length; + } + + return 0; +} + +/* + * Function to write data to the Output FIFO + */ +static int __qup_blsp_spi_write(struct udevice *dev, const u8 *cmd_buffer, unsigned int bytes) +{ + struct udevice *bus = dev_get_parent(dev); + struct qup_spi_priv *priv = dev_get_priv(bus); + u32 val; + unsigned int i; + unsigned int write_len = bytes; + unsigned int read_len = bytes; + unsigned int fifo_count; + int ret = 0; + int state_config; + + state_config = qup_spi_config_spi_state(dev, SPI_RESET_STATE); + if (state_config) + return state_config; + + writel(bytes, priv->base + QUP_MX_OUTPUT_CNT); + writel(bytes, priv->base + QUP_MX_INPUT_CNT); + state_config = qup_spi_config_spi_state(dev, SPI_RUN_STATE); + if (state_config) + return state_config; + + /* Configure input and output enable */ + qup_spi_enable_io_config(dev, write_len, read_len); + + /* + * read_len considered to ensure that we read the dummy data for the + * write we performed. This is needed to ensure with WR-RD transaction + * to get the actual data on the subsequent read cycle that happens + */ + while (write_len || read_len) { + ret = qup_spi_check_fifo_status(dev, QUP_OPERATIONAL); + if (ret != 0) + goto out; + + val = readl(priv->base + QUP_OPERATIONAL); + if (val & QUP_OP_OUT_SERVICE_FLAG) { + /* + * acknowledge to hw that software will write + * expected output data + */ + val &= QUP_OP_OUT_SERVICE_FLAG; + writel(val, priv->base + QUP_OPERATIONAL); + + if (write_len > SPI_OUTPUT_BLOCK_SIZE) + fifo_count = SPI_OUTPUT_BLOCK_SIZE; + else + fifo_count = write_len; + + for (i = 0; i < fifo_count; i++) { + /* Write actual data to output FIFO */ + qup_spi_write_byte(dev, *cmd_buffer); + cmd_buffer++; + write_len--; + } + } + if (val & QUP_OP_IN_SERVICE_FLAG) { + /* + * acknowledge to hw that software + * will read input data + */ + val &= QUP_OP_IN_SERVICE_FLAG; + writel(val, priv->base + QUP_OPERATIONAL); + + if (read_len > SPI_INPUT_BLOCK_SIZE) + fifo_count = SPI_INPUT_BLOCK_SIZE; + else + fifo_count = read_len; + + for (i = 0; i < fifo_count; i++) { + /* Read dummy data for the data written */ + (void)qup_spi_read_byte(dev); + + /* Decrement the write count after reading the + * dummy data from the device. This is to make + * sure we read dummy data before we write the + * data to fifo + */ + read_len--; + } + } + } +out: + /* + * Put the SPI Core back in the Reset State + * to end the transfer + */ + (void)qup_spi_config_spi_state(dev, SPI_RESET_STATE); + + return ret; +} + +static int qup_spi_blsp_spi_write(struct udevice *dev, const u8 *cmd_buffer, unsigned int bytes) +{ + int length, ret; + + while (bytes) { + length = (bytes < MAX_COUNT_SIZE) ? bytes : MAX_COUNT_SIZE; + + ret = __qup_blsp_spi_write(dev, cmd_buffer, length); + if (ret != 0) + return ret; + + cmd_buffer += length; + bytes -= length; + } + + return 0; +} + +static int qup_spi_set_speed(struct udevice *dev, uint speed) +{ + return 0; +} + +static int qup_spi_set_mode(struct udevice *dev, uint mode) +{ + struct qup_spi_priv *priv = dev_get_priv(dev); + unsigned int clk_idle_state; + unsigned int input_first_mode; + u32 val; + + switch (mode) { + case SPI_MODE_0: + clk_idle_state = 0; + input_first_mode = SPI_CONFIG_INPUT_FIRST; + break; + case SPI_MODE_1: + clk_idle_state = 0; + input_first_mode = 0; + break; + case SPI_MODE_2: + clk_idle_state = 1; + input_first_mode = SPI_CONFIG_INPUT_FIRST; + break; + case SPI_MODE_3: + clk_idle_state = 1; + input_first_mode = 0; + break; + default: + printf("Unsupported spi mode: %d\n", mode); + return -EINVAL; + } + + if (mode & SPI_CS_HIGH) + priv->cs_high = true; + else + priv->cs_high = false; + + val = readl(priv->base + SPI_CONFIG); + val |= input_first_mode; + writel(val, priv->base + SPI_CONFIG); + + val = readl(priv->base + SPI_IO_CONTROL); + if (clk_idle_state) + val |= SPI_IO_C_CLK_IDLE_HIGH; + else + val &= ~SPI_IO_C_CLK_IDLE_HIGH; + + writel(val, priv->base + SPI_IO_CONTROL); + + return 0; +} + +static void qup_spi_reset(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct qup_spi_priv *priv = dev_get_priv(bus); + + /* Driver may not be probed yet */ + if (!priv) + return; + + writel(0x1, priv->base + QUP_SW_RESET); + udelay(5); +} + +static int qup_spi_hw_init(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct qup_spi_priv *priv = dev_get_priv(bus); + int ret; + + /* QUPn module configuration */ + qup_spi_reset(dev); + + /* Set the QUPn state */ + ret = qup_spi_config_spi_state(dev, SPI_RESET_STATE); + if (ret) + return ret; + + /* + * Configure Mini core to SPI core with Input Output enabled, + * SPI master, N = 8 bits + */ + clrsetbits_le32(priv->base + QUP_CONFIG, (QUP_CONFIG_MINI_CORE_MSK | + QUP_CONF_INPUT_MSK | + QUP_CONF_OUTPUT_MSK | + SPI_BIT_WORD_MSK), + (QUP_CONFIG_MINI_CORE_SPI | + QUP_CONF_INPUT_ENA | + QUP_CONF_OUTPUT_ENA | + SPI_8_BIT_WORD)); + + /* + * Configure Input first SPI protocol, + * SPI master mode and no loopback + */ + clrsetbits_le32(priv->base + SPI_CONFIG, (LOOP_BACK_MSK | + SLAVE_OPERATION_MSK), + (NO_LOOP_BACK | + SLAVE_OPERATION)); + + /* + * Configure SPI IO Control Register + * CLK_ALWAYS_ON = 0 + * MX_CS_MODE = 0 + * NO_TRI_STATE = 1 + */ + writel((CLK_ALWAYS_ON | NO_TRI_STATE), priv->base + SPI_IO_CONTROL); + + /* + * Configure SPI IO Modes. + * OUTPUT_BIT_SHIFT_EN = 1 + * INPUT_MODE = Block Mode + * OUTPUT MODE = Block Mode + */ + + clrsetbits_le32(priv->base + QUP_IO_M_MODES, (OUTPUT_BIT_SHIFT_MSK | + INPUT_BLOCK_MODE_MSK | + OUTPUT_BLOCK_MODE_MSK), + (OUTPUT_BIT_SHIFT_EN | + INPUT_BLOCK_MODE | + OUTPUT_BLOCK_MODE)); + + /* Disable Error mask */ + writel(0, priv->base + SPI_ERROR_FLAGS_EN); + writel(0, priv->base + QUP_ERROR_FLAGS_EN); + writel(0, priv->base + BLSP0_SPI_DEASSERT_WAIT_REG); + + return ret; +} + +static int qup_spi_claim_bus(struct udevice *dev) +{ + int ret; + + ret = qup_spi_hw_init(dev); + if (ret) + return -EIO; + + return 0; +} + +static int qup_spi_release_bus(struct udevice *dev) +{ + /* Reset the SPI hardware */ + qup_spi_reset(dev); + + return 0; +} + +static int qup_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev_get_parent(dev); + struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); + unsigned int len; + const u8 *txp = dout; + u8 *rxp = din; + int ret = 0; + + if (bitlen & SPI_BITLEN_MSK) { + printf("Invalid bit length\n"); + return -EINVAL; + } + + len = bitlen >> 3; + + if (flags & SPI_XFER_BEGIN) { + ret = qup_spi_hw_init(dev); + if (ret != 0) + return ret; + + ret = qup_spi_set_cs(bus, slave_plat->cs, false); + if (ret != 0) + return ret; + } + + if (dout != NULL) { + ret = qup_spi_blsp_spi_write(dev, txp, len); + if (ret != 0) + return ret; + } + + if (din != NULL) { + ret = qup_spi_blsp_spi_read(dev, rxp, len); + if (ret != 0) + return ret; + } + + if (flags & SPI_XFER_END) { + ret = qup_spi_set_cs(bus, slave_plat->cs, true); + if (ret != 0) + return ret; + } + + return ret; +} + +static int qup_spi_probe(struct udevice *dev) +{ + struct qup_spi_priv *priv = dev_get_priv(dev); + int ret; + + priv->base = dev_read_addr(dev); + if (priv->base == FDT_ADDR_T_NONE) + return -EINVAL; + + ret = clk_get_by_index(dev, 0, &priv->clk); + if (ret) + return ret; + + ret = clk_enable(&priv->clk); + if (ret < 0) + return ret; + + priv->num_cs = dev_read_u32_default(dev, "num-cs", 1); + + ret = gpio_request_list_by_name(dev, "cs-gpios", priv->cs_gpios, + priv->num_cs, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + if (ret < 0) { + printf("Can't get %s cs gpios: %d\n", dev->name, ret); + return -EINVAL; + } + + return 0; +} + +static const struct dm_spi_ops qup_spi_ops = { + .claim_bus = qup_spi_claim_bus, + .release_bus = qup_spi_release_bus, + .xfer = qup_spi_xfer, + .set_speed = qup_spi_set_speed, + .set_mode = qup_spi_set_mode, + /* + * cs_info is not needed, since we require all chip selects to be + * in the device tree explicitly + */ +}; + +static const struct udevice_id qup_spi_ids[] = { + { .compatible = "qcom,spi-qup-v1.1.1", }, + { .compatible = "qcom,spi-qup-v2.1.1", }, + { .compatible = "qcom,spi-qup-v2.2.1", }, + { } +}; + +U_BOOT_DRIVER(spi_qup) = { + .name = "spi_qup", + .id = UCLASS_SPI, + .of_match = qup_spi_ids, + .ops = &qup_spi_ops, + .priv_auto_alloc_size = sizeof(struct qup_spi_priv), + .probe = qup_spi_probe, +}; diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c index 348630faf3..47a5571aec 100644 --- a/drivers/spi/xilinx_spi.c +++ b/drivers/spi/xilinx_spi.c @@ -214,7 +214,7 @@ static void xilinx_spi_startup_block(struct udevice *dev, unsigned int bytes, struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); const unsigned char *txp = dout; unsigned char *rxp = din; - u32 reg, count; + u32 reg; u32 txbytes = bytes; u32 rxbytes = bytes; @@ -224,10 +224,10 @@ static void xilinx_spi_startup_block(struct udevice *dev, unsigned int bytes, * it sets txp to the initial value for the normal operation. */ for ( ; priv->startup < 2; priv->startup++) { - count = xilinx_spi_fill_txfifo(bus, txp, txbytes); + xilinx_spi_fill_txfifo(bus, txp, txbytes); reg = readl(®s->spicr) & ~SPICR_MASTER_INHIBIT; writel(reg, ®s->spicr); - count = xilinx_spi_read_rxfifo(bus, rxp, rxbytes); + xilinx_spi_read_rxfifo(bus, rxp, rxbytes); txp = din; if (priv->startup) { @@ -251,7 +251,7 @@ static int xilinx_spi_xfer(struct udevice *dev, unsigned int bitlen, unsigned char *rxp = din; u32 txbytes = bytes; u32 rxbytes = bytes; - u32 reg, count, timeout; + u32 reg, count; int ret; debug("spi_xfer: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", diff --git a/drivers/spi/zynq_qspi.c b/drivers/spi/zynq_qspi.c index 3f39ef05f2..f2eddec950 100644 --- a/drivers/spi/zynq_qspi.c +++ b/drivers/spi/zynq_qspi.c @@ -6,8 +6,10 @@ * Xilinx Zynq Quad-SPI(QSPI) controller driver (master mode only) */ +#include <clk.h> #include <common.h> #include <dm.h> +#include <dm/device_compat.h> #include <log.h> #include <malloc.h> #include <spi.h> @@ -105,17 +107,29 @@ static int zynq_qspi_ofdata_to_platdata(struct udevice *bus) plat->regs = (struct zynq_qspi_regs *)fdtdec_get_addr(blob, node, "reg"); - /* FIXME: Use 166MHz as a suitable default */ - plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", - 166666666); - plat->speed_hz = plat->frequency / 2; - - debug("%s: regs=%p max-frequency=%d\n", __func__, - plat->regs, plat->frequency); - return 0; } +/** + * zynq_qspi_init_hw - Initialize the hardware + * @priv: Pointer to the zynq_qspi_priv structure + * + * The default settings of the QSPI controller's configurable parameters on + * reset are + * - Master mode + * - Baud rate divisor is set to 2 + * - Threshold value for TX FIFO not full interrupt is set to 1 + * - Flash memory interface mode enabled + * - Size of the word to be transferred as 8 bit + * This function performs the following actions + * - Disable and clear all the interrupts + * - Enable manual slave select + * - Enable auto start + * - Deselect all the chip select lines + * - Set the size of the word to be transferred as 32 bit + * - Set the little endian mode of TX FIFO and + * - Enable the QSPI controller + */ static void zynq_qspi_init_hw(struct zynq_qspi_priv *priv) { struct zynq_qspi_regs *regs = priv->regs; @@ -159,19 +173,45 @@ static int zynq_qspi_probe(struct udevice *bus) { struct zynq_qspi_platdata *plat = dev_get_platdata(bus); struct zynq_qspi_priv *priv = dev_get_priv(bus); + struct clk clk; + unsigned long clock; + int ret; priv->regs = plat->regs; priv->fifo_depth = ZYNQ_QSPI_FIFO_DEPTH; + ret = clk_get_by_name(bus, "ref_clk", &clk); + if (ret < 0) { + dev_err(bus, "failed to get clock\n"); + return ret; + } + + clock = clk_get_rate(&clk); + if (IS_ERR_VALUE(clock)) { + dev_err(bus, "failed to get rate\n"); + return clock; + } + + ret = clk_enable(&clk); + if (ret && ret != -ENOSYS) { + dev_err(bus, "failed to enable clock\n"); + return ret; + } + /* init the zynq spi hw */ zynq_qspi_init_hw(priv); + plat->frequency = clock; + plat->speed_hz = plat->frequency / 2; + + debug("%s: max-frequency=%d\n", __func__, plat->speed_hz); + return 0; } -/* +/** * zynq_qspi_read_data - Copy data to RX buffer - * @zqspi: Pointer to the zynq_qspi structure + * @priv: Pointer to the zynq_qspi_priv structure * @data: The 32 bit variable where data is stored * @size: Number of bytes to be copied from data to RX buffer */ @@ -214,9 +254,9 @@ static void zynq_qspi_read_data(struct zynq_qspi_priv *priv, u32 data, u8 size) priv->bytes_to_receive = 0; } -/* +/** * zynq_qspi_write_data - Copy data from TX buffer - * @zqspi: Pointer to the zynq_qspi structure + * @priv: Pointer to the zynq_qspi_priv structure * @data: Pointer to the 32 bit variable where data is to be copied * @size: Number of bytes to be copied from TX buffer to data */ @@ -263,6 +303,11 @@ static void zynq_qspi_write_data(struct zynq_qspi_priv *priv, priv->bytes_to_transfer = 0; } +/** + * zynq_qspi_chipselect - Select or deselect the chip select line + * @priv: Pointer to the zynq_qspi_priv structure + * @is_on: Select(1) or deselect (0) the chip select line + */ static void zynq_qspi_chipselect(struct zynq_qspi_priv *priv, int is_on) { u32 confr; @@ -282,9 +327,10 @@ static void zynq_qspi_chipselect(struct zynq_qspi_priv *priv, int is_on) writel(confr, ®s->cr); } -/* +/** * zynq_qspi_fill_tx_fifo - Fills the TX FIFO with as many bytes as possible - * @zqspi: Pointer to the zynq_qspi structure + * @priv: Pointer to the zynq_qspi_priv structure + * @size: Number of bytes to be copied to fifo */ static void zynq_qspi_fill_tx_fifo(struct zynq_qspi_priv *priv, u32 size) { @@ -322,9 +368,9 @@ static void zynq_qspi_fill_tx_fifo(struct zynq_qspi_priv *priv, u32 size) } } -/* +/** * zynq_qspi_irq_poll - Interrupt service routine of the QSPI controller - * @zqspi: Pointer to the zynq_qspi structure + * @priv: Pointer to the zynq_qspi structure * * This function handles TX empty and Mode Fault interrupts only. * On TX empty interrupt this function reads the received data from RX FIFO and @@ -410,11 +456,9 @@ static int zynq_qspi_irq_poll(struct zynq_qspi_priv *priv) return 0; } -/* +/** * zynq_qspi_start_transfer - Initiates the QSPI transfer - * @qspi: Pointer to the spi_device structure - * @transfer: Pointer to the spi_transfer structure which provide information - * about next transfer parameters + * @priv: Pointer to the zynq_qspi_priv structure * * This function fills the TX FIFO, starts the QSPI transfer, and waits for the * transfer to be completed. diff --git a/drivers/spi/zynq_spi.c b/drivers/spi/zynq_spi.c index 9923931e36..cb911c34f6 100644 --- a/drivers/spi/zynq_spi.c +++ b/drivers/spi/zynq_spi.c @@ -8,10 +8,12 @@ #include <common.h> #include <dm.h> +#include <dm/device_compat.h> #include <log.h> #include <malloc.h> #include <spi.h> #include <time.h> +#include <clk.h> #include <asm/io.h> #include <linux/bitops.h> #include <linux/delay.h> @@ -79,17 +81,10 @@ static int zynq_spi_ofdata_to_platdata(struct udevice *bus) plat->regs = dev_read_addr_ptr(bus); - /* FIXME: Use 250MHz as a suitable default */ - plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", - 250000000); plat->deactivate_delay_us = fdtdec_get_int(blob, node, "spi-deactivate-delay", 0); plat->activate_delay_us = fdtdec_get_int(blob, node, "spi-activate-delay", 0); - plat->speed_hz = plat->frequency / 2; - - debug("%s: regs=%p max-frequency=%d\n", __func__, - plat->regs, plat->frequency); return 0; } @@ -128,13 +123,39 @@ static int zynq_spi_probe(struct udevice *bus) { struct zynq_spi_platdata *plat = dev_get_platdata(bus); struct zynq_spi_priv *priv = dev_get_priv(bus); + struct clk clk; + unsigned long clock; + int ret; priv->regs = plat->regs; priv->fifo_depth = ZYNQ_SPI_FIFO_DEPTH; + ret = clk_get_by_name(bus, "ref_clk", &clk); + if (ret < 0) { + dev_err(bus, "failed to get clock\n"); + return ret; + } + + clock = clk_get_rate(&clk); + if (IS_ERR_VALUE(clock)) { + dev_err(bus, "failed to get rate\n"); + return clock; + } + + ret = clk_enable(&clk); + if (ret && ret != -ENOSYS) { + dev_err(bus, "failed to enable clock\n"); + return ret; + } + /* init the zynq spi hw */ zynq_spi_init_hw(priv); + plat->frequency = clock; + plat->speed_hz = plat->frequency / 2; + + debug("%s: max-frequency=%d\n", __func__, plat->speed_hz); + return 0; } |