* [PATCH 1/6] spi: Add Qualcomm QUP SPI controller driver
2020-10-08 20:05 [PATCH 0/6] IPQ40xx: Improve support Robert Marko
@ 2020-10-08 20:05 ` Robert Marko
2020-10-23 0:28 ` Tom Rini
2020-10-08 20:05 ` [PATCH 2/6] IPQ40xx: Add SPI support Robert Marko
` (4 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Robert Marko @ 2020-10-08 20:05 UTC (permalink / raw)
To: u-boot
This patch adds support for the Qualcomm QUP SPI controller that is commonly found in most of Qualcomm SoC-s.
Driver currently supports v1.1.1, v2.1.1 and v2.2.1 HW.
FIFO and Block modes are supported, no support for DMA mode is planned.
Signed-off-by: Robert Marko <robert.marko@sartura.hr>
Signed-off-by: Luka Kovacic <luka.kovacic@sartura.hr>
Cc: Luka Perkov <luka.perkov@sartura.hr>
---
MAINTAINERS | 1 +
doc/device-tree-bindings/spi/spi-qup.txt | 33 +
drivers/spi/Kconfig | 10 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-qup.c | 803 +++++++++++++++++++++++
5 files changed, 848 insertions(+)
create mode 100644 doc/device-tree-bindings/spi/spi-qup.txt
create mode 100644 drivers/spi/spi-qup.c
diff --git a/MAINTAINERS b/MAINTAINERS
index ed5e354873..aa6cbb965d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -241,6 +241,7 @@ F: include/dt-bindings/clock/qcom,ipq4019-gcc.h
F: include/dt-bindings/reset/qcom,ipq4019-reset.h
F: drivers/reset/reset-ipq4019.c
F: drivers/phy/phy-qcom-ipq4019-usb.c
+F: drivers/spi/spi-qup.c
ARM MARVELL KIRKWOOD ARMADA-XP ARMADA-38X ARMADA-37XX ARMADA-7K/8K
M: Stefan Roese <sr@denx.de>
diff --git a/doc/device-tree-bindings/spi/spi-qup.txt b/doc/device-tree-bindings/spi/spi-qup.txt
new file mode 100644
index 0000000000..3697df2631
--- /dev/null
+++ b/doc/device-tree-bindings/spi/spi-qup.txt
@@ -0,0 +1,33 @@
+Qualcomm QUP SPI controller Device Tree Bindings
+-------------------------------------------
+
+Required properties:
+- compatible : Should be "qcom,spi-qup-v1.1.1", "qcom,spi-qup-v2.1.1"
+ or "qcom,spi-qup-v2.2.1"
+- reg : Physical base address and size of SPI registers map.
+- clock : Clock phandle (see clock bindings for details).
+- #address-cells : Number of cells required to define a chip select
+ address on the SPI bus. Should be set to 1.
+- #size-cells : Should be zero.
+- pinctrl-names : Must be "default"
+- pinctrl-n : At least one pinctrl phandle
+- cs-gpios : Should specify GPIOs used for chipselects.
+ The gpios will be referred to as reg = <index> in the
+ SPI child nodes.
+
+Optional properties:
+- num-cs : total number of chipselects
+
+Example:
+
+ blsp1_spi1: spi at 78b5000 {
+ compatible = "qcom,spi-qup-v2.2.1";
+ reg = <0x78b5000 0x600>;
+ clock = <&gcc 23>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-names = "spi";
+ pinctrl-0 = <&blsp_spi0>;
+ num-cs = <2>;
+ cs-gpios = <&soc_gpios 54 GPIO_ACTIVE_HIGH>, <&soc_gpios 4 GPIO_ACTIVE_HIGH>;
+ };
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/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,
+};
--
2.28.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 2/6] IPQ40xx: Add SPI support
2020-10-08 20:05 [PATCH 0/6] IPQ40xx: Improve support Robert Marko
2020-10-08 20:05 ` [PATCH 1/6] spi: Add Qualcomm QUP SPI controller driver Robert Marko
@ 2020-10-08 20:05 ` Robert Marko
2020-10-23 0:28 ` Tom Rini
2020-10-08 20:05 ` [PATCH 3/6] net: Add IPQ40xx MDIO driver Robert Marko
` (3 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Robert Marko @ 2020-10-08 20:05 UTC (permalink / raw)
To: u-boot
Since we have SPI driver for IPQ40xx QUP SPI controller, lets add the necessary nodes, pinctrl and clocks.
Signed-off-by: Robert Marko <robert.marko@sartura.hr>
Cc: Luka Perkov <luka.perkov@sartura.hr>
---
arch/arm/dts/qcom-ipq4019.dtsi | 12 ++++++++++++
arch/arm/mach-ipq40xx/clock-ipq4019.c | 17 +++++++++++++++--
arch/arm/mach-ipq40xx/pinctrl-ipq4019.c | 3 +++
3 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/arch/arm/dts/qcom-ipq4019.dtsi b/arch/arm/dts/qcom-ipq4019.dtsi
index e0e4188e5d..936ef74f94 100644
--- a/arch/arm/dts/qcom-ipq4019.dtsi
+++ b/arch/arm/dts/qcom-ipq4019.dtsi
@@ -22,6 +22,7 @@
aliases {
serial0 = &blsp1_uart1;
+ spi0 = &blsp1_spi1;
};
reserved-memory {
@@ -89,6 +90,17 @@
gpio-count = <100>;
gpio-bank-name="soc";
#gpio-cells = <2>;
+ u-boot,dm-pre-reloc;
+ };
+
+ blsp1_spi1: spi at 78b5000 {
+ compatible = "qcom,spi-qup-v2.2.1";
+ reg = <0x78b5000 0x600>;
+ clocks = <&gcc GCC_BLSP1_QUP1_SPI_APPS_CLK>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ u-boot,dm-pre-reloc;
};
usb3_ss_phy: ssphy at 9a000 {
diff --git a/arch/arm/mach-ipq40xx/clock-ipq4019.c b/arch/arm/mach-ipq40xx/clock-ipq4019.c
index 83a688e625..d5b5f4cb79 100644
--- a/arch/arm/mach-ipq40xx/clock-ipq4019.c
+++ b/arch/arm/mach-ipq40xx/clock-ipq4019.c
@@ -2,7 +2,7 @@
/*
* Clock drivers for Qualcomm IPQ40xx
*
- * Copyright (c) 2019 Sartura Ltd.
+ * Copyright (c) 2020 Sartura Ltd.
*
* Author: Robert Marko <robert.marko@sartura.hr>
*
@@ -24,7 +24,7 @@ ulong msm_set_rate(struct clk *clk, ulong rate)
switch (clk->id) {
case GCC_BLSP1_UART1_APPS_CLK: /*UART1*/
/* This clock is already initialized by SBL1 */
- return 0;
+ return 0;
break;
default:
return 0;
@@ -47,8 +47,21 @@ static ulong msm_clk_set_rate(struct clk *clk, ulong rate)
return msm_set_rate(clk, rate);
}
+static int msm_enable(struct clk *clk)
+{
+ switch (clk->id) {
+ case GCC_BLSP1_QUP1_SPI_APPS_CLK: /*SPI1*/
+ /* This clock is already initialized by SBL1 */
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+}
+
static struct clk_ops msm_clk_ops = {
.set_rate = msm_clk_set_rate,
+ .enable = msm_enable,
};
static const struct udevice_id msm_clk_ids[] = {
diff --git a/arch/arm/mach-ipq40xx/pinctrl-ipq4019.c b/arch/arm/mach-ipq40xx/pinctrl-ipq4019.c
index 06a57f2e5e..1f283516cb 100644
--- a/arch/arm/mach-ipq40xx/pinctrl-ipq4019.c
+++ b/arch/arm/mach-ipq40xx/pinctrl-ipq4019.c
@@ -18,6 +18,9 @@ static const struct pinctrl_function msm_pinctrl_functions[] = {
{"blsp_uart0_0", 1}, /* Only for GPIO:16,17 */
{"blsp_uart0_1", 2}, /* Only for GPIO:60,61 */
{"blsp_uart1", 1},
+ {"blsp_spi0_0", 1}, /* Only for GPIO:12,13,14,15 */
+ {"blsp_spi0_1", 2}, /* Only for GPIO:54,55,56,57 */
+ {"blsp_spi1", 2},
};
static const char *ipq4019_get_function_name(struct udevice *dev,
--
2.28.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 3/6] net: Add IPQ40xx MDIO driver
2020-10-08 20:05 [PATCH 0/6] IPQ40xx: Improve support Robert Marko
2020-10-08 20:05 ` [PATCH 1/6] spi: Add Qualcomm QUP SPI controller driver Robert Marko
2020-10-08 20:05 ` [PATCH 2/6] IPQ40xx: Add SPI support Robert Marko
@ 2020-10-08 20:05 ` Robert Marko
2020-10-23 0:28 ` Tom Rini
2020-10-08 20:05 ` [PATCH 4/6] IPQ40xx: Add support for MDIO Robert Marko
` (2 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Robert Marko @ 2020-10-08 20:05 UTC (permalink / raw)
To: u-boot
This adds the driver for the IPQ40xx built-in MDIO.
This will be needed to support future PHY driver.
Signed-off-by: Robert Marko <robert.marko@sartura.hr>
Cc: Luka Perkov <luka.perkov@sartura.hr>
---
MAINTAINERS | 1 +
drivers/net/Kconfig | 7 ++
drivers/net/Makefile | 1 +
drivers/net/mdio-ipq4019.c | 146 +++++++++++++++++++++++++++++++++++++
4 files changed, 155 insertions(+)
create mode 100644 drivers/net/mdio-ipq4019.c
diff --git a/MAINTAINERS b/MAINTAINERS
index aa6cbb965d..5f1a815c88 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -242,6 +242,7 @@ F: include/dt-bindings/reset/qcom,ipq4019-reset.h
F: drivers/reset/reset-ipq4019.c
F: drivers/phy/phy-qcom-ipq4019-usb.c
F: drivers/spi/spi-qup.c
+F: drivers/net/mdio-ipq4019.c
ARM MARVELL KIRKWOOD ARMADA-XP ARMADA-38X ARMADA-37XX ARMADA-7K/8K
M: Stefan Roese <sr@denx.de>
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index a0d2d21a55..09a4688d69 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -701,6 +701,13 @@ config MDIO_MUX_I2CREG
an I2C chip. The board it was developed for uses a mux controlled by
on-board FPGA which in turn is accessed as a chip over I2C.
+config MDIO_IPQ4019
+ bool "Qualcomm IPQ4019 MDIO interface support"
+ depends on DM_MDIO
+ help
+ This driver supports the MDIO interface found in Qualcomm
+ IPQ40xx series Soc-s.
+
config MVMDIO
bool "Marvell MDIO interface support"
depends on DM_MDIO
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 03f01921ea..9cd4b36f57 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_LAN91C96) += lan91c96.o
obj-$(CONFIG_LPC32XX_ETH) += lpc32xx_eth.o
obj-$(CONFIG_MACB) += macb.o
obj-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o
+obj-$(CONFIG_MDIO_IPQ4019) += mdio-ipq4019.o
obj-$(CONFIG_MDIO_MUX_I2CREG) += mdio_mux_i2creg.o
obj-$(CONFIG_MDIO_MUX_SANDBOX) += mdio_mux_sandbox.o
obj-$(CONFIG_MPC8XX_FEC) += mpc8xx_fec.o
diff --git a/drivers/net/mdio-ipq4019.c b/drivers/net/mdio-ipq4019.c
new file mode 100644
index 0000000000..bc68e1d506
--- /dev/null
+++ b/drivers/net/mdio-ipq4019.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Qualcomm IPQ4019 MDIO driver
+ *
+ * Copyright (c) 2020 Sartura Ltd.
+ *
+ * Author: Luka Kovacic <luka.kovacic@sartura.hr>
+ * Author: Robert Marko <robert.marko@sartura.hr>
+ *
+ * Based on Linux driver
+ */
+
+#include <asm/io.h>
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <linux/bitops.h>
+#include <linux/iopoll.h>
+#include <miiphy.h>
+#include <phy.h>
+
+#define MDIO_MODE_REG 0x40
+#define MDIO_ADDR_REG 0x44
+#define MDIO_DATA_WRITE_REG 0x48
+#define MDIO_DATA_READ_REG 0x4c
+#define MDIO_CMD_REG 0x50
+#define MDIO_CMD_ACCESS_BUSY BIT(16)
+#define MDIO_CMD_ACCESS_START BIT(8)
+#define MDIO_CMD_ACCESS_CODE_READ 0
+#define MDIO_CMD_ACCESS_CODE_WRITE 1
+
+/* 0 = Clause 22, 1 = Clause 45 */
+#define MDIO_MODE_BIT BIT(8)
+
+#define IPQ4019_MDIO_TIMEOUT 10000
+#define IPQ4019_MDIO_SLEEP 10
+
+struct ipq4019_mdio_priv {
+ phys_addr_t mdio_base;
+};
+
+static int ipq4019_mdio_wait_busy(struct ipq4019_mdio_priv *priv)
+{
+ unsigned int busy;
+
+ return readl_poll_sleep_timeout(priv->mdio_base + MDIO_CMD_REG, busy,
+ (busy & MDIO_CMD_ACCESS_BUSY) == 0, IPQ4019_MDIO_SLEEP,
+ IPQ4019_MDIO_TIMEOUT);
+}
+
+int ipq4019_mdio_read(struct udevice *dev, int addr, int devad, int reg)
+{
+ struct ipq4019_mdio_priv *priv = dev_get_priv(dev);
+ unsigned int cmd;
+
+ if (ipq4019_mdio_wait_busy(priv))
+ return -ETIMEDOUT;
+
+ /* Issue the phy address and reg */
+ writel((addr << 8) | reg, priv->mdio_base + MDIO_ADDR_REG);
+
+ cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_READ;
+
+ /* Issue read command */
+ writel(cmd, priv->mdio_base + MDIO_CMD_REG);
+
+ /* Wait read complete */
+ if (ipq4019_mdio_wait_busy(priv))
+ return -ETIMEDOUT;
+
+ /* Read and return data */
+ return readl(priv->mdio_base + MDIO_DATA_READ_REG);
+}
+
+int ipq4019_mdio_write(struct udevice *dev, int addr, int devad,
+ int reg, u16 val)
+{
+ struct ipq4019_mdio_priv *priv = dev_get_priv(dev);
+ unsigned int cmd;
+
+ if (ipq4019_mdio_wait_busy(priv))
+ return -ETIMEDOUT;
+
+ /* Issue the phy addreass and reg */
+ writel((addr << 8) | reg, priv->mdio_base + MDIO_ADDR_REG);
+
+ /* Issue write data */
+ writel(val, priv->mdio_base + MDIO_DATA_WRITE_REG);
+
+ cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_WRITE;
+
+ /* Issue write command */
+ writel(cmd, priv->mdio_base + MDIO_CMD_REG);
+
+ /* Wait for write complete */
+
+ if (ipq4019_mdio_wait_busy(priv))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static const struct mdio_ops ipq4019_mdio_ops = {
+ .read = ipq4019_mdio_read,
+ .write = ipq4019_mdio_write,
+};
+
+static int ipq4019_mdio_bind(struct udevice *dev)
+{
+ if (ofnode_valid(dev->node))
+ device_set_name(dev, ofnode_get_name(dev->node));
+
+ return 0;
+}
+
+static int ipq4019_mdio_probe(struct udevice *dev)
+{
+ struct ipq4019_mdio_priv *priv = dev_get_priv(dev);
+ unsigned int data;
+
+ priv->mdio_base = dev_read_addr(dev);
+ if (priv->mdio_base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ /* Enter Clause 22 mode */
+ data = readl(priv->mdio_base + MDIO_MODE_REG);
+ data &= ~MDIO_MODE_BIT;
+ writel(data, priv->mdio_base + MDIO_MODE_REG);
+
+ return 0;
+}
+
+static const struct udevice_id ipq4019_mdio_ids[] = {
+ { .compatible = "qcom,ipq4019-mdio", },
+ { }
+};
+
+U_BOOT_DRIVER(ipq4019_mdio) = {
+ .name = "ipq4019_mdio",
+ .id = UCLASS_MDIO,
+ .of_match = ipq4019_mdio_ids,
+ .bind = ipq4019_mdio_bind,
+ .probe = ipq4019_mdio_probe,
+ .ops = &ipq4019_mdio_ops,
+ .priv_auto_alloc_size = sizeof(struct ipq4019_mdio_priv),
+};
--
2.28.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 4/6] IPQ40xx: Add support for MDIO
2020-10-08 20:05 [PATCH 0/6] IPQ40xx: Improve support Robert Marko
` (2 preceding siblings ...)
2020-10-08 20:05 ` [PATCH 3/6] net: Add IPQ40xx MDIO driver Robert Marko
@ 2020-10-08 20:05 ` Robert Marko
2020-10-23 0:29 ` Tom Rini
2020-10-08 20:05 ` [PATCH 5/6] rng: Add Qualcomm MSM PRNG driver Robert Marko
2020-10-08 20:05 ` [PATCH 6/6] IPQ40xx: Add PRNG support Robert Marko
5 siblings, 1 reply; 15+ messages in thread
From: Robert Marko @ 2020-10-08 20:05 UTC (permalink / raw)
To: u-boot
Lets add the necessary DTS node and pinctrl properties for newly added MDIO driver.
Signed-off-by: Robert Marko <robert.marko@sartura.hr>
Cc: Luka Perkov <luka.perkov@sartura.hr>
---
arch/arm/dts/qcom-ipq4019.dtsi | 28 +++++++++++++++++++++++++
arch/arm/mach-ipq40xx/pinctrl-ipq4019.c | 4 ++++
2 files changed, 32 insertions(+)
diff --git a/arch/arm/dts/qcom-ipq4019.dtsi b/arch/arm/dts/qcom-ipq4019.dtsi
index 936ef74f94..031691e5d2 100644
--- a/arch/arm/dts/qcom-ipq4019.dtsi
+++ b/arch/arm/dts/qcom-ipq4019.dtsi
@@ -103,6 +103,34 @@
u-boot,dm-pre-reloc;
};
+ mdio: mdio at 90000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "qcom,ipq4019-mdio";
+ reg = <0x90000 0x64>;
+ status = "disabled";
+
+ ethphy0: ethernet-phy at 0 {
+ reg = <0>;
+ };
+
+ ethphy1: ethernet-phy at 1 {
+ reg = <1>;
+ };
+
+ ethphy2: ethernet-phy at 2 {
+ reg = <2>;
+ };
+
+ ethphy3: ethernet-phy at 3 {
+ reg = <3>;
+ };
+
+ ethphy4: ethernet-phy at 4 {
+ reg = <4>;
+ };
+ };
+
usb3_ss_phy: ssphy at 9a000 {
compatible = "qcom,usb-ss-ipq4019-phy";
#phy-cells = <0>;
diff --git a/arch/arm/mach-ipq40xx/pinctrl-ipq4019.c b/arch/arm/mach-ipq40xx/pinctrl-ipq4019.c
index 1f283516cb..3e365f8cc8 100644
--- a/arch/arm/mach-ipq40xx/pinctrl-ipq4019.c
+++ b/arch/arm/mach-ipq40xx/pinctrl-ipq4019.c
@@ -21,6 +21,10 @@ static const struct pinctrl_function msm_pinctrl_functions[] = {
{"blsp_spi0_0", 1}, /* Only for GPIO:12,13,14,15 */
{"blsp_spi0_1", 2}, /* Only for GPIO:54,55,56,57 */
{"blsp_spi1", 2},
+ {"mdio_0", 1}, /* Only for GPIO6 */
+ {"mdio_1", 2}, /* Only for GPIO53 */
+ {"mdc_0", 1}, /* Only for GPIO7 */
+ {"mdc_1", 2}, /* Only for GPIO52 */
};
static const char *ipq4019_get_function_name(struct udevice *dev,
--
2.28.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 5/6] rng: Add Qualcomm MSM PRNG driver
2020-10-08 20:05 [PATCH 0/6] IPQ40xx: Improve support Robert Marko
` (3 preceding siblings ...)
2020-10-08 20:05 ` [PATCH 4/6] IPQ40xx: Add support for MDIO Robert Marko
@ 2020-10-08 20:05 ` Robert Marko
2020-10-23 0:29 ` Tom Rini
2020-10-08 20:05 ` [PATCH 6/6] IPQ40xx: Add PRNG support Robert Marko
5 siblings, 1 reply; 15+ messages in thread
From: Robert Marko @ 2020-10-08 20:05 UTC (permalink / raw)
To: u-boot
Add support for the hardware pseudo random number generator found in Qualcomm SoC-s.
Signed-off-by: Robert Marko <robert.marko@sartura.hr>
Cc: Luka Perkov <luka.perkov@sartura.hr>
---
MAINTAINERS | 1 +
drivers/rng/Kconfig | 7 +++
drivers/rng/Makefile | 1 +
drivers/rng/msm_rng.c | 143 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 152 insertions(+)
create mode 100644 drivers/rng/msm_rng.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 5f1a815c88..8a1bc97443 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -243,6 +243,7 @@ F: drivers/reset/reset-ipq4019.c
F: drivers/phy/phy-qcom-ipq4019-usb.c
F: drivers/spi/spi-qup.c
F: drivers/net/mdio-ipq4019.c
+F: drivers/rng/msm_rng.c
ARM MARVELL KIRKWOOD ARMADA-XP ARMADA-38X ARMADA-37XX ARMADA-7K/8K
M: Stefan Roese <sr@denx.de>
diff --git a/drivers/rng/Kconfig b/drivers/rng/Kconfig
index e4b22d79eb..11001c8ae7 100644
--- a/drivers/rng/Kconfig
+++ b/drivers/rng/Kconfig
@@ -24,6 +24,13 @@ config RNG_SANDBOX
Enable random number generator for sandbox. This is an
emulation of a rng device.
+config RNG_MSM
+ bool "Qualcomm SoCs Random Number Generator support"
+ depends on DM_RNG
+ help
+ This driver provides support for the Random Number
+ Generator hardware found on Qualcomm SoCs.
+
config RNG_STM32MP1
bool "Enable random number generator for STM32MP1"
depends on ARCH_STM32MP
diff --git a/drivers/rng/Makefile b/drivers/rng/Makefile
index 44a0003917..8953406882 100644
--- a/drivers/rng/Makefile
+++ b/drivers/rng/Makefile
@@ -6,5 +6,6 @@
obj-$(CONFIG_DM_RNG) += rng-uclass.o
obj-$(CONFIG_RNG_MESON) += meson-rng.o
obj-$(CONFIG_RNG_SANDBOX) += sandbox_rng.o
+obj-$(CONFIG_RNG_MSM) += msm_rng.o
obj-$(CONFIG_RNG_STM32MP1) += stm32mp1_rng.o
obj-$(CONFIG_RNG_ROCKCHIP) += rockchip_rng.o
diff --git a/drivers/rng/msm_rng.c b/drivers/rng/msm_rng.c
new file mode 100644
index 0000000000..d51119303a
--- /dev/null
+++ b/drivers/rng/msm_rng.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * PRNG driver for Qualcomm IPQ40xx
+ *
+ * Copyright (c) 2020 Sartura Ltd.
+ *
+ * Author: Robert Marko <robert.marko@sartura.hr>
+ *
+ * Based on Linux driver
+ */
+
+#include <asm/io.h>
+#include <clk.h>
+#include <common.h>
+#include <dm.h>
+#include <linux/bitops.h>
+#include <rng.h>
+
+/* Device specific register offsets */
+#define PRNG_DATA_OUT 0x0000
+#define PRNG_STATUS 0x0004
+#define PRNG_LFSR_CFG 0x0100
+#define PRNG_CONFIG 0x0104
+
+/* Device specific register masks and config values */
+#define PRNG_LFSR_CFG_MASK 0x0000ffff
+#define PRNG_LFSR_CFG_CLOCKS 0x0000dddd
+#define PRNG_CONFIG_HW_ENABLE BIT(1)
+#define PRNG_STATUS_DATA_AVAIL BIT(0)
+
+#define MAX_HW_FIFO_DEPTH 16
+#define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4)
+#define WORD_SZ 4
+
+struct msm_rng_priv {
+ phys_addr_t base;
+ struct clk clk;
+};
+
+static int msm_rng_read(struct udevice *dev, void *data, size_t len)
+{
+ struct msm_rng_priv *priv = dev_get_priv(dev);
+ size_t currsize = 0;
+ u32 *retdata = data;
+ size_t maxsize;
+ u32 val;
+
+ /* calculate max size bytes to transfer back to caller */
+ maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, len);
+
+ /* read random data from hardware */
+ do {
+ val = readl_relaxed(priv->base + PRNG_STATUS);
+ if (!(val & PRNG_STATUS_DATA_AVAIL))
+ break;
+
+ val = readl_relaxed(priv->base + PRNG_DATA_OUT);
+ if (!val)
+ break;
+
+ *retdata++ = val;
+ currsize += WORD_SZ;
+
+ /* make sure we stay on 32bit boundary */
+ if ((maxsize - currsize) < WORD_SZ)
+ break;
+ } while (currsize < maxsize);
+
+ return 0;
+}
+
+static int msm_rng_enable(struct msm_rng_priv *priv, int enable)
+{
+ u32 val;
+
+ if (enable) {
+ /* Enable PRNG only if it is not already enabled */
+ val = readl_relaxed(priv->base + PRNG_CONFIG);
+ if (val & PRNG_CONFIG_HW_ENABLE) {
+ val = readl_relaxed(priv->base + PRNG_LFSR_CFG);
+ val &= ~PRNG_LFSR_CFG_MASK;
+ val |= PRNG_LFSR_CFG_CLOCKS;
+ writel(val, priv->base + PRNG_LFSR_CFG);
+
+ val = readl_relaxed(priv->base + PRNG_CONFIG);
+ val |= PRNG_CONFIG_HW_ENABLE;
+ writel(val, priv->base + PRNG_CONFIG);
+ }
+ } else {
+ val = readl_relaxed(priv->base + PRNG_CONFIG);
+ val &= ~PRNG_CONFIG_HW_ENABLE;
+ writel(val, priv->base + PRNG_CONFIG);
+ }
+
+ return 0;
+}
+
+static int msm_rng_probe(struct udevice *dev)
+{
+ struct msm_rng_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;
+
+ return msm_rng_enable(priv, 1);
+}
+
+static int msm_rng_remove(struct udevice *dev)
+{
+ struct msm_rng_priv *priv = dev_get_priv(dev);
+
+ return msm_rng_enable(priv, 0);
+}
+
+static const struct dm_rng_ops msm_rng_ops = {
+ .read = msm_rng_read,
+};
+
+static const struct udevice_id msm_rng_match[] = {
+ { .compatible = "qcom,prng", },
+ {},
+};
+
+U_BOOT_DRIVER(msm_rng) = {
+ .name = "msm-rng",
+ .id = UCLASS_RNG,
+ .of_match = msm_rng_match,
+ .ops = &msm_rng_ops,
+ .probe = msm_rng_probe,
+ .remove = msm_rng_remove,
+ .priv_auto_alloc_size = sizeof(struct msm_rng_priv),
+};
--
2.28.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 5/6] rng: Add Qualcomm MSM PRNG driver
2020-10-08 20:05 ` [PATCH 5/6] rng: Add Qualcomm MSM PRNG driver Robert Marko
@ 2020-10-23 0:29 ` Tom Rini
2020-12-14 17:22 ` Heinrich Schuchardt
0 siblings, 1 reply; 15+ messages in thread
From: Tom Rini @ 2020-10-23 0:29 UTC (permalink / raw)
To: u-boot
On Thu, Oct 08, 2020 at 10:05:13PM +0200, Robert Marko wrote:
> Add support for the hardware pseudo random number generator found in Qualcomm SoC-s.
>
> Signed-off-by: Robert Marko <robert.marko@sartura.hr>
> Cc: Luka Perkov <luka.perkov@sartura.hr>
Applied to u-boot/master, thanks!
--
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 659 bytes
Desc: not available
URL: <https://lists.denx.de/pipermail/u-boot/attachments/20201022/d5ab8cc5/attachment.sig>
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 5/6] rng: Add Qualcomm MSM PRNG driver
2020-10-23 0:29 ` Tom Rini
@ 2020-12-14 17:22 ` Heinrich Schuchardt
2020-12-14 17:32 ` Robert Marko
0 siblings, 1 reply; 15+ messages in thread
From: Heinrich Schuchardt @ 2020-12-14 17:22 UTC (permalink / raw)
To: u-boot
On 23.10.20 02:29, Tom Rini wrote:
> On Thu, Oct 08, 2020 at 10:05:13PM +0200, Robert Marko wrote:
>> Add support for the hardware pseudo random number generator found in Qualcomm SoC-s.
>
>>
>> Signed-off-by: Robert Marko <robert.marko@sartura.hr>
>> Cc: Luka Perkov <luka.perkov@sartura.hr>
>
> Applied to u-boot/master, thanks!
>
Hello Robert,
Can we make CONFIG_RNG_MSM depend on CONFIG_ARCH_IPQ40XX so it is not
shown as choice for other architectures?
Should CONFIG_RNG_MSM default to yes on ARCH_IPQ40XX?
Best regards
Heinrich
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 5/6] rng: Add Qualcomm MSM PRNG driver
2020-12-14 17:22 ` Heinrich Schuchardt
@ 2020-12-14 17:32 ` Robert Marko
0 siblings, 0 replies; 15+ messages in thread
From: Robert Marko @ 2020-12-14 17:32 UTC (permalink / raw)
To: u-boot
On Mon, Dec 14, 2020 at 6:22 PM Heinrich Schuchardt <xypron.debian@gmx.de>
wrote:
> On 23.10.20 02:29, Tom Rini wrote:
> > On Thu, Oct 08, 2020 at 10:05:13PM +0200, Robert Marko wrote:
> >> Add support for the hardware pseudo random number generator found in
> Qualcomm SoC-s.
> >
> >>
> >> Signed-off-by: Robert Marko <robert.marko@sartura.hr>
> >> Cc: Luka Perkov <luka.perkov@sartura.hr>
> >
> > Applied to u-boot/master, thanks!
> >
>
> Hello Robert,
>
> Can we make CONFIG_RNG_MSM depend on CONFIG_ARCH_IPQ40XX so it is not
> shown as choice for other architectures?
>
Sure, as it's currently the only architecture using it.
>
> Should CONFIG_RNG_MSM default to yes on ARCH_IPQ40XX?
>
It's currently enabled per board defconfig, I don't think it's vital enough
to
default to yes.
Regards,
Robert
>
> Best regards
>
> Heinrich
>
>
>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 6/6] IPQ40xx: Add PRNG support
2020-10-08 20:05 [PATCH 0/6] IPQ40xx: Improve support Robert Marko
` (4 preceding siblings ...)
2020-10-08 20:05 ` [PATCH 5/6] rng: Add Qualcomm MSM PRNG driver Robert Marko
@ 2020-10-08 20:05 ` Robert Marko
2020-10-23 0:29 ` Tom Rini
5 siblings, 1 reply; 15+ messages in thread
From: Robert Marko @ 2020-10-08 20:05 UTC (permalink / raw)
To: u-boot
Since we now have the driver for Qualcomm PRNG HW, lets use it and add the necessary clocks and nodes.
Signed-off-by: Robert Marko <robert.marko@sartura.hr>
Cc: Luka Perkov <luka.perkov@sartura.hr>
---
arch/arm/dts/qcom-ipq4019.dtsi | 7 +++++++
arch/arm/mach-ipq40xx/clock-ipq4019.c | 4 ++++
2 files changed, 11 insertions(+)
diff --git a/arch/arm/dts/qcom-ipq4019.dtsi b/arch/arm/dts/qcom-ipq4019.dtsi
index 031691e5d2..7a52ea2c4e 100644
--- a/arch/arm/dts/qcom-ipq4019.dtsi
+++ b/arch/arm/dts/qcom-ipq4019.dtsi
@@ -60,6 +60,13 @@
u-boot,dm-pre-reloc;
};
+ rng: rng at 22000 {
+ compatible = "qcom,prng";
+ reg = <0x22000 0x140>;
+ clocks = <&gcc GCC_PRNG_AHB_CLK>;
+ status = "disabled";
+ };
+
reset: gcc-reset at 1800000 {
compatible = "qcom,gcc-reset-ipq4019";
reg = <0x1800000 0x60000>;
diff --git a/arch/arm/mach-ipq40xx/clock-ipq4019.c b/arch/arm/mach-ipq40xx/clock-ipq4019.c
index d5b5f4cb79..31ae9719e8 100644
--- a/arch/arm/mach-ipq40xx/clock-ipq4019.c
+++ b/arch/arm/mach-ipq40xx/clock-ipq4019.c
@@ -54,6 +54,10 @@ static int msm_enable(struct clk *clk)
/* This clock is already initialized by SBL1 */
return 0;
break;
+ case GCC_PRNG_AHB_CLK: /*PRNG*/
+ /* This clock is already initialized by SBL1 */
+ return 0;
+ break;
default:
return 0;
}
--
2.28.0
^ permalink raw reply related [flat|nested] 15+ messages in thread