diff options
| author | Tom Rini <[email protected]> | 2023-02-10 13:41:27 -0500 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2023-02-10 13:41:27 -0500 |
| commit | a1e6b529e57c622e862e93fa6da03d9504565089 (patch) | |
| tree | 1b756fb08296e17f4edec6f4dd40c7a6e2fb9a80 /drivers | |
| parent | 8b301102e246350a0ccedc370f7c9923b02f86f2 (diff) | |
| parent | add396d66703c6c422353f950e584fae2a786a20 (diff) | |
Merge branch '2023-02-10-assorted-updates-and-additions'
- DM_SERIAL conversion for bcm7xxx, button input driver, qcom updates,
environment and network related cleanup, ftmac100 update, add a
IS_ENABLED conversion that was just missed.
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/button/button-gpio.c | 17 | ||||
| -rw-r--r-- | drivers/button/button-uclass.c | 10 | ||||
| -rw-r--r-- | drivers/gpio/qcom_pmic_gpio.c | 16 | ||||
| -rw-r--r-- | drivers/i2c/Kconfig | 12 | ||||
| -rw-r--r-- | drivers/i2c/Makefile | 1 | ||||
| -rw-r--r-- | drivers/i2c/qup_i2c.c | 579 | ||||
| -rw-r--r-- | drivers/input/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/input/Makefile | 1 | ||||
| -rw-r--r-- | drivers/input/button_kbd.c | 126 | ||||
| -rw-r--r-- | drivers/net/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/net/Makefile | 1 | ||||
| -rw-r--r-- | drivers/net/dwc_eth_qos.c | 35 | ||||
| -rw-r--r-- | drivers/net/dwc_eth_qos.h | 4 | ||||
| -rw-r--r-- | drivers/net/dwc_eth_qos_qcom.c | 612 | ||||
| -rw-r--r-- | drivers/net/ftmac100.c | 119 | ||||
| -rw-r--r-- | drivers/net/ftmac100.h | 9 |
16 files changed, 1541 insertions, 18 deletions
diff --git a/drivers/button/button-gpio.c b/drivers/button/button-gpio.c index dbb000622c7..7b5b3affe2d 100644 --- a/drivers/button/button-gpio.c +++ b/drivers/button/button-gpio.c @@ -13,6 +13,7 @@ struct button_gpio_priv { struct gpio_desc gpio; + int linux_code; }; static enum button_state_t button_gpio_get_state(struct udevice *dev) @@ -29,6 +30,17 @@ static enum button_state_t button_gpio_get_state(struct udevice *dev) return ret ? BUTTON_ON : BUTTON_OFF; } +static int button_gpio_get_code(struct udevice *dev) +{ + struct button_gpio_priv *priv = dev_get_priv(dev); + int code = priv->linux_code; + + if (!code) + return -ENODATA; + + return code; +} + static int button_gpio_probe(struct udevice *dev) { struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev); @@ -43,7 +55,9 @@ static int button_gpio_probe(struct udevice *dev) if (ret) return ret; - return 0; + ret = dev_read_u32(dev, "linux,code", &priv->linux_code); + + return ret; } static int button_gpio_remove(struct udevice *dev) @@ -92,6 +106,7 @@ static int button_gpio_bind(struct udevice *parent) static const struct button_ops button_gpio_ops = { .get_state = button_gpio_get_state, + .get_code = button_gpio_get_code, }; static const struct udevice_id button_gpio_ids[] = { diff --git a/drivers/button/button-uclass.c b/drivers/button/button-uclass.c index e33ed7d01d1..032191d61ab 100644 --- a/drivers/button/button-uclass.c +++ b/drivers/button/button-uclass.c @@ -38,6 +38,16 @@ enum button_state_t button_get_state(struct udevice *dev) return ops->get_state(dev); } +int button_get_code(struct udevice *dev) +{ + struct button_ops *ops = button_get_ops(dev); + + if (!ops->get_code) + return -ENOSYS; + + return ops->get_code(dev); +} + UCLASS_DRIVER(button) = { .id = UCLASS_BUTTON, .name = "button", diff --git a/drivers/gpio/qcom_pmic_gpio.c b/drivers/gpio/qcom_pmic_gpio.c index 3be1be8692c..65feb453ebc 100644 --- a/drivers/gpio/qcom_pmic_gpio.c +++ b/drivers/gpio/qcom_pmic_gpio.c @@ -303,9 +303,25 @@ static int qcom_pwrkey_get_value(struct udevice *dev, unsigned offset) } } +/* + * Since pmic buttons modelled as GPIO, we need empty direction functions + * to trick u-boot button driver + */ +static int qcom_pwrkey_direction_input(struct udevice *dev, unsigned int offset) +{ + return 0; +} + +static int qcom_pwrkey_direction_output(struct udevice *dev, unsigned int offset, int value) +{ + return -EOPNOTSUPP; +} + static const struct dm_gpio_ops qcom_pwrkey_ops = { .get_value = qcom_pwrkey_get_value, .get_function = qcom_pwrkey_get_function, + .direction_input = qcom_pwrkey_direction_input, + .direction_output = qcom_pwrkey_direction_output, }; static int qcom_pwrkey_probe(struct udevice *dev) diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 76e19918aad..427074bff83 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -580,6 +580,18 @@ config SYS_I2C_OCTEON chips have several I2C ports and all are provided, controlled by the device tree. +config SYS_I2C_QUP + bool "Qualcomm QUP I2C controller" + depends on ARCH_SNAPDRAGON + help + Support for Qualcomm QUP I2C controller based on Qualcomm Universal + Peripherals (QUP) engine. The QUP engine is an advanced high + performance slave port that provides a common data path (an output + FIFO and an input FIFO) for I2C and SPI interfaces. The I2C/SPI QUP + controller is publicly documented in the Snapdragon 410E (APQ8016E) + Technical Reference Manual, chapter "6.1 Qualcomm Universal + Peripherals Engine (QUP)". + config SYS_I2C_S3C24X0 bool "Samsung I2C driver" depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5) && DM_I2C diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index cde0597290c..8a70b5ba88e 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_SYS_I2C_NPCM) += npcm_i2c.o obj-$(CONFIG_SYS_I2C_OCORES) += ocores_i2c.o obj-$(CONFIG_SYS_I2C_OCTEON) += octeon_i2c.o obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o +obj-$(CONFIG_SYS_I2C_QUP) += qup_i2c.o obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o obj-$(CONFIG_SYS_I2C_ROCKCHIP) += rk_i2c.o diff --git a/drivers/i2c/qup_i2c.c b/drivers/i2c/qup_i2c.c new file mode 100644 index 00000000000..5ae3cccd4ac --- /dev/null +++ b/drivers/i2c/qup_i2c.c @@ -0,0 +1,579 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2009-2013, 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2014, Sony Mobile Communications AB. + * Copyright (c) 2022-2023, Sumit Garg <[email protected]> + * + * Inspired by corresponding driver in Linux: drivers/i2c/busses/i2c-qup.c + */ + +#include <init.h> +#include <env.h> +#include <common.h> +#include <log.h> +#include <dm/device_compat.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/compat.h> +#include <linux/bitops.h> +#include <asm/io.h> +#include <i2c.h> +#include <watchdog.h> +#include <fdtdec.h> +#include <clk.h> +#include <reset.h> +#include <asm/arch/gpio.h> +#include <cpu_func.h> +#include <asm/system.h> +#include <asm/gpio.h> +#include <dm.h> +#include <dm/pinctrl.h> + +/* QUP Registers */ +#define QUP_CONFIG 0x000 +#define QUP_STATE 0x004 +#define QUP_IO_MODE 0x008 +#define QUP_SW_RESET 0x00c +#define QUP_OPERATIONAL 0x018 +#define QUP_ERROR_FLAGS 0x01c /* NOT USED */ +#define QUP_ERROR_FLAGS_EN 0x020 /* NOT USED */ +#define QUP_TEST_CTRL 0x024 /* NOT USED */ +#define QUP_OPERATIONAL_MASK 0x028 /* NOT USED */ +#define QUP_HW_VERSION 0x030 +#define QUP_MX_OUTPUT_CNT 0x100 +#define QUP_OUT_DEBUG 0x108 /* NOT USED */ +#define QUP_OUT_FIFO_CNT 0x10C /* NOT USED */ +#define QUP_OUT_FIFO_BASE 0x110 +#define QUP_MX_WRITE_CNT 0x150 +#define QUP_MX_INPUT_CNT 0x200 +#define QUP_MX_READ_CNT 0x208 +#define QUP_IN_READ_CUR 0x20C /* NOT USED */ +#define QUP_IN_DEBUG 0x210 /* NOT USED */ +#define QUP_IN_FIFO_CNT 0x214 /* NOT USED */ +#define QUP_IN_FIFO_BASE 0x218 +#define QUP_I2C_CLK_CTL 0x400 +#define QUP_I2C_STATUS 0x404 /* NOT USED */ +#define QUP_I2C_MASTER_GEN 0x408 +#define QUP_I2C_MASTER_BUS_CLR 0x40C /* NOT USED */ + +/* QUP States and reset values */ +#define QUP_RESET_STATE 0 +#define QUP_RUN_STATE 1 +#define QUP_PAUSE_STATE 3 +#define QUP_STATE_MASK 3 + +#define QUP_STATE_VALID BIT(2) +#define QUP_I2C_MAST_GEN BIT(4) +#define QUP_I2C_FLUSH BIT(6) + +#define QUP_OPERATIONAL_RESET 0x000ff0 +#define QUP_I2C_STATUS_RESET 0xfffffc + +/* QUP OPERATIONAL FLAGS */ +#define QUP_I2C_NACK_FLAG BIT(3) +#define QUP_OUT_NOT_EMPTY BIT(4) +#define QUP_IN_NOT_EMPTY BIT(5) +#define QUP_OUT_FULL BIT(6) +#define QUP_OUT_SVC_FLAG BIT(8) +#define QUP_IN_SVC_FLAG BIT(9) +#define QUP_MX_OUTPUT_DONE BIT(10) +#define QUP_MX_INPUT_DONE BIT(11) +#define OUT_BLOCK_WRITE_REQ BIT(12) +#define IN_BLOCK_READ_REQ BIT(13) + +/* + * QUP engine acting as I2C controller is referred to as + * I2C mini core, following are related macros. + */ +#define QUP_NO_OUTPUT BIT(6) +#define QUP_NO_INPUT BIT(7) +#define QUP_CLOCK_AUTO_GATE BIT(13) +#define QUP_I2C_MINI_CORE (2 << 8) +#define QUP_I2C_N_VAL_V2 7 + +/* Packing/Unpacking words in FIFOs, and IO modes */ +#define QUP_OUTPUT_BLK_MODE BIT(10) +#define QUP_OUTPUT_BAM_MODE (BIT(10) | BIT(11)) +#define QUP_INPUT_BLK_MODE BIT(12) +#define QUP_INPUT_BAM_MODE (BIT(12) | BIT(13)) +#define QUP_BAM_MODE (QUP_OUTPUT_BAM_MODE | QUP_INPUT_BAM_MODE) +#define QUP_BLK_MODE (QUP_OUTPUT_BLK_MODE | QUP_INPUT_BLK_MODE) +#define QUP_UNPACK_EN BIT(14) +#define QUP_PACK_EN BIT(15) + +#define QUP_REPACK_EN (QUP_UNPACK_EN | QUP_PACK_EN) +#define QUP_V2_TAGS_EN 1 + +#define QUP_OUTPUT_BLOCK_SIZE(x) (((x) >> 0) & 0x03) +#define QUP_OUTPUT_FIFO_SIZE(x) (((x) >> 2) & 0x07) +#define QUP_INPUT_BLOCK_SIZE(x) (((x) >> 5) & 0x03) +#define QUP_INPUT_FIFO_SIZE(x) (((x) >> 7) & 0x07) + +/* QUP v2 tags */ +#define QUP_TAG_V2_START 0x81 +#define QUP_TAG_V2_DATAWR 0x82 +#define QUP_TAG_V2_DATAWR_STOP 0x83 +#define QUP_TAG_V2_DATARD 0x85 +#define QUP_TAG_V2_DATARD_NACK 0x86 +#define QUP_TAG_V2_DATARD_STOP 0x87 + +#define QUP_I2C_MX_CONFIG_DURING_RUN BIT(31) + +/* Minimum transfer timeout for i2c transfers in micro seconds */ +#define TOUT_CNT (2 * 1000 * 1000) + +/* Default values. Use these if FW query fails */ +#define DEFAULT_CLK_FREQ I2C_SPEED_STANDARD_RATE +#define DEFAULT_SRC_CLK 19200000 + +/* + * Max tags length (start, stop and maximum 2 bytes address) for each QUP + * data transfer + */ +#define QUP_MAX_TAGS_LEN 4 +/* Max data length for each DATARD tags */ +#define RECV_MAX_DATA_LEN 254 +/* TAG length for DATA READ in RX FIFO */ +#define READ_RX_TAGS_LEN 2 + +struct qup_i2c_priv { + phys_addr_t base; + struct clk core; + struct clk iface; + u32 in_fifo_sz; + u32 out_fifo_sz; + u32 clk_ctl; + u32 config_run; +}; + +static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg) +{ + return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0); +} + +static int qup_i2c_poll_state_mask(struct qup_i2c_priv *qup, + u32 req_state, u32 req_mask) +{ + int retries = 1; + u32 state; + + /* + * State transition takes 3 AHB clocks cycles + 3 I2C master clock + * cycles. So retry once after a 1uS delay. + */ + do { + state = readl(qup->base + QUP_STATE); + + if (state & QUP_STATE_VALID && + (state & req_mask) == req_state) + return 0; + + udelay(1); + } while (retries--); + + return -ETIMEDOUT; +} + +static int qup_i2c_poll_state(struct qup_i2c_priv *qup, u32 req_state) +{ + return qup_i2c_poll_state_mask(qup, req_state, QUP_STATE_MASK); +} + +static int qup_i2c_poll_state_valid(struct qup_i2c_priv *qup) +{ + return qup_i2c_poll_state_mask(qup, 0, 0); +} + +static int qup_i2c_poll_state_i2c_master(struct qup_i2c_priv *qup) +{ + return qup_i2c_poll_state_mask(qup, QUP_I2C_MAST_GEN, QUP_I2C_MAST_GEN); +} + +static int qup_i2c_change_state(struct qup_i2c_priv *qup, u32 state) +{ + if (qup_i2c_poll_state_valid(qup) != 0) + return -EIO; + + writel(state, qup->base + QUP_STATE); + + if (qup_i2c_poll_state(qup, state) != 0) + return -EIO; + return 0; +} + +/* + * Function to check wheather Input or Output FIFO + * has data to be serviced + */ +static int qup_i2c_check_fifo_status(struct qup_i2c_priv *qup, u32 reg_addr, + u32 flags) +{ + unsigned long count = TOUT_CNT; + u32 val, status_flag; + int ret = 0; + + do { + val = readl(qup->base + reg_addr); + status_flag = val & flags; + + if (!count) { + printf("%s, timeout\n", __func__); + ret = -ETIMEDOUT; + break; + } + + count--; + udelay(1); + } while (!status_flag); + + return ret; +} + +/* + * Function to configure Input and Output enable/disable + */ +static void qup_i2c_enable_io_config(struct qup_i2c_priv *qup, u32 write_cnt, + u32 read_cnt) +{ + u32 qup_config = QUP_I2C_MINI_CORE | QUP_I2C_N_VAL_V2; + + writel(qup->config_run | write_cnt, qup->base + QUP_MX_WRITE_CNT); + + if (read_cnt) + writel(qup->config_run | read_cnt, qup->base + QUP_MX_READ_CNT); + else + qup_config |= QUP_NO_INPUT; + + writel(qup_config, qup->base + QUP_CONFIG); +} + +static unsigned int qup_i2c_read_word(struct qup_i2c_priv *qup) +{ + return readl(qup->base + QUP_IN_FIFO_BASE); +} + +static void qup_i2c_write_word(struct qup_i2c_priv *qup, u32 word) +{ + writel(word, qup->base + QUP_OUT_FIFO_BASE); +} + +static int qup_i2c_blsp_read(struct qup_i2c_priv *qup, unsigned int addr, + bool last, u8 *buffer, unsigned int bytes) +{ + unsigned int i, j, word; + int ret = 0; + + /* FIFO mode size limitation, for larger size implement block mode */ + if (bytes > (qup->in_fifo_sz - READ_RX_TAGS_LEN)) + return -EINVAL; + + qup_i2c_enable_io_config(qup, QUP_MAX_TAGS_LEN, + bytes + READ_RX_TAGS_LEN); + + if (last) + qup_i2c_write_word(qup, QUP_TAG_V2_START | addr << 8 | + QUP_TAG_V2_DATARD_STOP << 16 | + bytes << 24); + else + qup_i2c_write_word(qup, QUP_TAG_V2_START | addr << 8 | + QUP_TAG_V2_DATARD << 16 | bytes << 24); + + ret = qup_i2c_change_state(qup, QUP_RUN_STATE); + if (ret) + return ret; + + ret = qup_i2c_check_fifo_status(qup, QUP_OPERATIONAL, QUP_OUT_SVC_FLAG); + if (ret) + return ret; + writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL); + + ret = qup_i2c_check_fifo_status(qup, QUP_OPERATIONAL, QUP_IN_SVC_FLAG); + if (ret) + return ret; + writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL); + + word = qup_i2c_read_word(qup); + *(buffer++) = (word >> (8 * READ_RX_TAGS_LEN)) & 0xff; + if (bytes > 1) + *(buffer++) = (word >> (8 * (READ_RX_TAGS_LEN + 1))) & 0xff; + + for (i = 2; i < bytes; i += 4) { + word = qup_i2c_read_word(qup); + + for (j = 0; j < 4; j++) { + if ((i + j) == bytes) + break; + *buffer = (word >> (j * 8)) & 0xff; + buffer++; + } + } + + ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE); + return ret; +} + +static int qup_i2c_blsp_write(struct qup_i2c_priv *qup, unsigned int addr, + bool first, bool last, const u8 *buffer, + unsigned int bytes) +{ + unsigned int i; + u32 word = 0; + int ret = 0; + + /* FIFO mode size limitation, for larger size implement block mode */ + if (bytes > (qup->out_fifo_sz - QUP_MAX_TAGS_LEN)) + return -EINVAL; + + qup_i2c_enable_io_config(qup, bytes + QUP_MAX_TAGS_LEN, 0); + + if (first) { + ret = qup_i2c_change_state(qup, QUP_RUN_STATE); + if (ret) + return ret; + + writel(qup->clk_ctl, qup->base + QUP_I2C_CLK_CTL); + + ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE); + if (ret) + return ret; + } + + if (last) + qup_i2c_write_word(qup, QUP_TAG_V2_START | addr << 8 | + QUP_TAG_V2_DATAWR_STOP << 16 | + bytes << 24); + else + qup_i2c_write_word(qup, QUP_TAG_V2_START | addr << 8 | + QUP_TAG_V2_DATAWR << 16 | bytes << 24); + + for (i = 0; i < bytes; i++) { + /* Write the byte of data */ + word |= *buffer << ((i % 4) * 8); + if ((i % 4) == 3) { + qup_i2c_write_word(qup, word); + word = 0; + } + buffer++; + } + + if ((i % 4) != 0) + qup_i2c_write_word(qup, word); + + ret = qup_i2c_change_state(qup, QUP_RUN_STATE); + if (ret) + return ret; + + ret = qup_i2c_check_fifo_status(qup, QUP_OPERATIONAL, QUP_OUT_SVC_FLAG); + if (ret) + return ret; + writel(QUP_OUT_SVC_FLAG, qup->base + QUP_OPERATIONAL); + + ret = qup_i2c_change_state(qup, QUP_PAUSE_STATE); + return ret; +} + +static void qup_i2c_conf_mode_v2(struct qup_i2c_priv *qup) +{ + u32 io_mode = QUP_REPACK_EN; + + writel(0, qup->base + QUP_MX_OUTPUT_CNT); + writel(0, qup->base + QUP_MX_INPUT_CNT); + + writel(io_mode, qup->base + QUP_IO_MODE); +} + +static int qup_i2c_xfer_v2(struct udevice *bus, struct i2c_msg msgs[], int num) +{ + struct qup_i2c_priv *qup = dev_get_priv(bus); + int ret, idx = 0; + u32 i2c_addr; + + writel(1, qup->base + QUP_SW_RESET); + ret = qup_i2c_poll_state(qup, QUP_RESET_STATE); + if (ret) + goto out; + + /* Configure QUP as I2C mini core */ + writel(QUP_I2C_MINI_CORE | QUP_I2C_N_VAL_V2 | QUP_NO_INPUT, + qup->base + QUP_CONFIG); + writel(QUP_V2_TAGS_EN, qup->base + QUP_I2C_MASTER_GEN); + + if (qup_i2c_poll_state_i2c_master(qup)) { + ret = -EIO; + goto out; + } + + qup_i2c_conf_mode_v2(qup); + + for (idx = 0; idx < num; idx++) { + struct i2c_msg *m = &msgs[idx]; + + qup->config_run = !idx ? 0 : QUP_I2C_MX_CONFIG_DURING_RUN; + i2c_addr = i2c_8bit_addr_from_msg(m); + + if (m->flags & I2C_M_RD) + ret = qup_i2c_blsp_read(qup, i2c_addr, idx == (num - 1), + m->buf, m->len); + else + ret = qup_i2c_blsp_write(qup, i2c_addr, idx == 0, + idx == (num - 1), m->buf, + m->len); + if (ret) + break; + } +out: + qup_i2c_change_state(qup, QUP_RESET_STATE); + return ret; +} + +static int qup_i2c_enable_clocks(struct udevice *dev, struct qup_i2c_priv *qup) +{ + int ret; + + ret = clk_enable(&qup->core); + if (ret) { + dev_err(dev, "clk_enable failed %d\n", ret); + return ret; + } + + ret = clk_enable(&qup->iface); + if (ret) { + dev_err(dev, "clk_enable failed %d\n", ret); + return ret; + } + + return 0; +} + +static int qup_i2c_probe(struct udevice *dev) +{ + static const int blk_sizes[] = {4, 16, 32}; + struct qup_i2c_priv *qup = dev_get_priv(dev); + u32 io_mode, hw_ver, size, size_idx; + int ret; + + qup->base = (phys_addr_t)dev_read_addr_ptr(dev); + if (!qup->base) + return -EINVAL; + + ret = clk_get_by_name(dev, "core", &qup->core); + if (ret) { + pr_err("clk_get_by_name(core) failed: %d\n", ret); + return ret; + } + ret = clk_get_by_name(dev, "iface", &qup->iface); + if (ret) { + pr_err("clk_get_by_name(iface) failed: %d\n", ret); + return ret; + } + qup_i2c_enable_clocks(dev, qup); + + writel(1, qup->base + QUP_SW_RESET); + ret = qup_i2c_poll_state_valid(qup); + if (ret) + return ret; + + hw_ver = readl(qup->base + QUP_HW_VERSION); + dev_dbg(dev, "Revision %x\n", hw_ver); + + io_mode = readl(qup->base + QUP_IO_MODE); + + /* + * The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag' + * associated with each byte written/received + */ + size_idx = QUP_OUTPUT_BLOCK_SIZE(io_mode); + if (size_idx >= ARRAY_SIZE(blk_sizes)) { + ret = -EIO; + return ret; + } + size = QUP_OUTPUT_FIFO_SIZE(io_mode); + qup->out_fifo_sz = blk_sizes[size_idx] * (2 << size); + + size_idx = QUP_INPUT_BLOCK_SIZE(io_mode); + if (size_idx >= ARRAY_SIZE(blk_sizes)) { + ret = -EIO; + return ret; + } + size = QUP_INPUT_FIFO_SIZE(io_mode); + qup->in_fifo_sz = blk_sizes[size_idx] * (2 << size); + + dev_dbg(dev, "IN:fifo:%d, OUT:fifo:%d\n", qup->in_fifo_sz, + qup->out_fifo_sz); + + return 0; +} + +static int qup_i2c_set_bus_speed(struct udevice *dev, unsigned int clk_freq) +{ + struct qup_i2c_priv *qup = dev_get_priv(dev); + unsigned int src_clk_freq; + int fs_div, hs_div; + + /* We support frequencies up to FAST Mode Plus (1MHz) */ + if (!clk_freq || clk_freq > I2C_SPEED_FAST_PLUS_RATE) { + dev_err(dev, "clock frequency not supported %d\n", clk_freq); + return -EINVAL; + } + + src_clk_freq = clk_get_rate(&qup->iface); + if ((int)src_clk_freq < 0) { + src_clk_freq = DEFAULT_SRC_CLK; + dev_dbg(dev, "using default core freq %d\n", src_clk_freq); + } + + dev_dbg(dev, "src_clk_freq %u\n", src_clk_freq); + dev_dbg(dev, "clk_freq %u\n", clk_freq); + + hs_div = 3; + if (clk_freq <= I2C_SPEED_STANDARD_RATE) { + fs_div = ((src_clk_freq / clk_freq) / 2) - 3; + qup->clk_ctl = (hs_div << 8) | (fs_div & 0xff); + } else { + /* 33%/66% duty cycle */ + fs_div = ((src_clk_freq / clk_freq) - 6) * 2 / 3; + qup->clk_ctl = ((fs_div / 2) << 16) | (hs_div << 8) | (fs_div & 0xff); + } + + dev_dbg(dev, "clk_ctl %u\n", qup->clk_ctl); + + return 0; +} + +/* Probe to see if a chip is present. */ +static int qup_i2c_probe_chip(struct udevice *dev, uint chip_addr, + uint chip_flags) +{ + struct qup_i2c_priv *qup = dev_get_priv(dev); + u32 hw_ver = readl(qup->base + QUP_HW_VERSION); + + return hw_ver ? 0 : -1; +} + +static const struct dm_i2c_ops qup_i2c_ops = { + .xfer = qup_i2c_xfer_v2, + .probe_chip = qup_i2c_probe_chip, + .set_bus_speed = qup_i2c_set_bus_speed, +}; + +/* + * Currently this driver only supports v2.x of QUP I2C controller, hence + * functions above are named with a _v2 suffix. So when we have the + * v1.1.1 support added as per the Linux counterpart then it should be easy + * to add corresponding functions named with a _v1 suffix. + */ +static const struct udevice_id qup_i2c_ids[] = { + { .compatible = "qcom,i2c-qup-v2.1.1" }, + { .compatible = "qcom,i2c-qup-v2.2.1" }, + {} +}; + +U_BOOT_DRIVER(i2c_qup) = { + .name = "i2c_qup", + .id = UCLASS_I2C, + .of_match = qup_i2c_ids, + .probe = qup_i2c_probe, + .priv_auto = sizeof(struct qup_i2c_priv), + .ops = &qup_i2c_ops, +}; diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 1c534be0058..32360d94c05 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -46,6 +46,15 @@ config APPLE_SPI_KEYB laptops based on Apple SoCs. These keyboards use an Apple-specific HID-over-SPI protocol. +config BUTTON_KEYBOARD + bool "Buttons as keyboard" + depends on BUTTON_GPIO + depends on DM_KEYBOARD + help + Enable support for mapping buttons to keycode events. Use linux,code button driver + dt node to define button-event mapping. + For example, an arrows and enter may be implemented to navigate boot menu. + config CROS_EC_KEYB bool "Enable Chrome OS EC keyboard support" depends on INPUT diff --git a/drivers/input/Makefile b/drivers/input/Makefile index ded76bddb22..14c0ea73254 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_$(SPL_TPL_)CROS_EC_KEYB) += cros_ec_keyb.o obj-$(CONFIG_$(SPL_TPL_)OF_CONTROL) += key_matrix.o obj-$(CONFIG_$(SPL_TPL_)DM_KEYBOARD) += input.o keyboard-uclass.o +obj-$(CONFIG_BUTTON_KEYBOARD) += button_kbd.o ifndef CONFIG_SPL_BUILD diff --git a/drivers/input/button_kbd.c b/drivers/input/button_kbd.c new file mode 100644 index 00000000000..99e65f12f01 --- /dev/null +++ b/drivers/input/button_kbd.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2023 Dzmitry Sankouski <[email protected]> + */ + +#include <stdlib.h> +#include <common.h> +#include <dm.h> +#include <fdtdec.h> +#include <input.h> +#include <keyboard.h> +#include <button.h> +#include <dm/device-internal.h> +#include <log.h> +#include <asm/io.h> +#include <asm/gpio.h> +#include <linux/delay.h> +#include <linux/input.h> + +/** + * struct button_kbd_priv - driver private data + * + * @input: input configuration + * @button_size: number of buttons found + * @old_state: a pointer to old button states array. Used to determine button state change. + */ +struct button_kbd_priv { + struct input_config *input; + u32 button_size; + u32 *old_state; +}; + +static int button_kbd_start(struct udevice *dev) +{ + struct button_kbd_priv *priv = dev_get_priv(dev); + int i = 0; + struct udevice *button_gpio_devp; + + uclass_foreach_dev_probe(UCLASS_BUTTON, button_gpio_devp) { + struct button_uc_plat *uc_plat = dev_get_uclass_plat(button_gpio_devp); + /* Ignore the top-level button node */ + if (!uc_plat->label) + continue; + debug("Found button %s #%d - %s, probing...\n", + uc_plat->label, i, button_gpio_devp->name); + i++; + } + + priv->button_size = i; + priv->old_state = calloc(i, sizeof(int)); + + return 0; +} + +int button_read_keys(struct input_config *input) +{ + struct button_kbd_priv *priv = dev_get_priv(input->dev); + struct udevice *button_gpio_devp; + struct uclass *uc; + int i = 0; + u32 code, state, state_changed = 0; + + uclass_id_foreach_dev(UCLASS_BUTTON, button_gpio_devp, uc) { + struct button_uc_plat *uc_plat = dev_get_uclass_plat(button_gpio_devp); + /* Ignore the top-level button node */ + if (!uc_plat->label) + continue; + code = button_get_code(button_gpio_devp); + if (!code) + continue; + + state = button_get_state(button_gpio_devp); + state_changed = state != priv->old_state[i]; + + if (state_changed) { + debug("%s: %d\n", uc_plat->label, code); + priv->old_state[i] = state; + input_add_keycode(input, code, state); + } + i++; + } + return 0; +} + +static const struct keyboard_ops button_kbd_ops = { + .start = button_kbd_start, +}; + +static int button_kbd_probe(struct udevice *dev) +{ + struct button_kbd_priv *priv = dev_get_priv(dev); + struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev); + struct stdio_dev *sdev = &uc_priv->sdev; + struct input_config *input = &uc_priv->input; + int ret = 0; + + input_init(input, false); + input_add_tables(input, false); + + /* Register the device. */ + priv->input = input; + input->dev = dev; + input->read_keys = button_read_keys; + strcpy(sdev->name, "button-kbd"); + ret = input_stdio_register(sdev); + if (ret) { + debug("%s: input_stdio_register() failed\n", __func__); + return ret; + } + + return 0; +} + +static const struct udevice_id button_kbd_ids[] = { + { .compatible = "button-kbd" }, + { } +}; + +U_BOOT_DRIVER(button_kbd) = { + .name = "button_kbd", + .id = UCLASS_KEYBOARD, + .of_match = button_kbd_ids, + .ops = &button_kbd_ops, + .priv_auto = sizeof(struct button_kbd_priv), + .probe = button_kbd_probe, +}; diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 62d2c03849a..ceadee98a1e 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -242,6 +242,13 @@ config DWC_ETH_QOS_TEGRA186 The Synopsys Designware Ethernet QOS IP block with specific configuration used in NVIDIA's Tegra186 chip. +config DWC_ETH_QOS_QCOM + bool "Synopsys DWC Ethernet QOS device support for Qcom SoCs" + depends on DWC_ETH_QOS + help + The Synopsys Designware Ethernet QOS IP block with specific + configuration used in Qcom QCS404 SoC. + config E1000 bool "Intel PRO/1000 Gigabit Ethernet support" depends on PCI @@ -406,6 +413,7 @@ config FSL_FM_10GEC_REGULAR_NOTATION config FTMAC100 bool "Ftmac100 Ethernet Support" + select MII help This MAC is present in Andestech SoCs. diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 90fbb02ab02..75daa5e694c 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o obj-$(CONFIG_DSA_SANDBOX) += dsa_sandbox.o obj-$(CONFIG_DWC_ETH_QOS) += dwc_eth_qos.o obj-$(CONFIG_DWC_ETH_QOS_IMX) += dwc_eth_qos_imx.o +obj-$(CONFIG_DWC_ETH_QOS_QCOM) += dwc_eth_qos_qcom.o obj-$(CONFIG_E1000) += e1000.o obj-$(CONFIG_E1000_SPI) += e1000_spi.o obj-$(CONFIG_EEPRO100) += eepro100.o diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index afc47b56ff5..112deb546de 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -774,10 +774,13 @@ static int eqos_start(struct udevice *dev) pr_err("eqos_calibrate_pads() failed: %d", ret); goto err_stop_resets; } - rate = eqos->config->ops->eqos_get_tick_clk_rate(dev); - val = (rate / 1000000) - 1; - writel(val, &eqos->mac_regs->us_tic_counter); + if (eqos->config->ops->eqos_get_tick_clk_rate) { + rate = eqos->config->ops->eqos_get_tick_clk_rate(dev); + + val = (rate / 1000000) - 1; + writel(val, &eqos->mac_regs->us_tic_counter); + } /* * if PHY was already connected and configured, @@ -849,12 +852,19 @@ static int eqos_start(struct udevice *dev) rx_fifo_sz = (val >> EQOS_MAC_HW_FEATURE1_RXFIFOSIZE_SHIFT) & EQOS_MAC_HW_FEATURE1_RXFIFOSIZE_MASK; - /* - * r/tx_fifo_sz is encoded as log2(n / 128). Undo that by shifting. - * r/tqs is encoded as (n / 256) - 1. - */ - tqs = (128 << tx_fifo_sz) / 256 - 1; - rqs = (128 << rx_fifo_sz) / 256 - 1; + /* r/tx_fifo_sz is encoded as log2(n / 128). Undo that by shifting */ + tx_fifo_sz = 128 << tx_fifo_sz; + rx_fifo_sz = 128 << rx_fifo_sz; + + /* Allow platform to override TX/RX fifo size */ + if (eqos->tx_fifo_sz) + tx_fifo_sz = eqos->tx_fifo_sz; + if (eqos->rx_fifo_sz) + rx_fifo_sz = eqos->rx_fifo_sz; + + /* r/tqs is encoded as (n / 256) - 1 */ + tqs = tx_fifo_sz / 256 - 1; + rqs = rx_fifo_sz / 256 - 1; clrsetbits_le32(&eqos->mtl_regs->txq0_operation_mode, EQOS_MTL_TXQ0_OPERATION_MODE_TQS_MASK << @@ -1702,6 +1712,13 @@ static const struct udevice_id eqos_ids[] = { }, #endif +#if IS_ENABLED(CONFIG_DWC_ETH_QOS_QCOM) + { + .compatible = "qcom,qcs404-ethqos", + .data = (ulong)&eqos_qcom_config + }, +#endif + { } }; diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h index 8fccd6f0572..fddbe9336c9 100644 --- a/drivers/net/dwc_eth_qos.h +++ b/drivers/net/dwc_eth_qos.h @@ -253,6 +253,7 @@ struct eqos_priv { struct eqos_mtl_regs *mtl_regs; struct eqos_dma_regs *dma_regs; struct eqos_tegra186_regs *tegra186_regs; + void *eqos_qcom_rgmii_regs; struct reset_ctl reset_ctl; struct gpio_desc phy_reset_gpio; struct clk clk_master_bus; @@ -276,6 +277,8 @@ struct eqos_priv { bool started; bool reg_access_ok; bool clk_ck_enabled; + unsigned int tx_fifo_sz, rx_fifo_sz; + u32 reset_delays[3]; }; void eqos_inval_desc_generic(void *desc); @@ -285,3 +288,4 @@ void eqos_flush_buffer_generic(void *buf, size_t size); int eqos_null_ops(struct udevice *dev); extern struct eqos_config eqos_imx_config; +extern struct eqos_config eqos_qcom_config; diff --git a/drivers/net/dwc_eth_qos_qcom.c b/drivers/net/dwc_eth_qos_qcom.c new file mode 100644 index 00000000000..df83f1c5f9e --- /dev/null +++ b/drivers/net/dwc_eth_qos_qcom.c @@ -0,0 +1,612 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022-2023 Sumit Garg <[email protected]> + * + * Qcom DWMAC specific glue layer + */ + +#include <common.h> +#include <asm/global_data.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <phy.h> +#include <reset.h> +#include <syscon.h> +#include <linux/bitops.h> +#include <linux/delay.h> + +#include "dwc_eth_qos.h" + +/* RGMII_IO_MACRO_CONFIG fields */ +#define RGMII_CONFIG_FUNC_CLK_EN BIT(30) +#define RGMII_CONFIG_POS_NEG_DATA_SEL BIT(23) +#define RGMII_CONFIG_GPIO_CFG_RX_INT GENMASK(21, 20) +#define RGMII_CONFIG_GPIO_CFG_TX_INT GENMASK(19, 17) +#define RGMII_CONFIG_MAX_SPD_PRG_9 GENMASK(16, 8) +#define RGMII_CONFIG_MAX_SPD_PRG_2 GENMASK(7, 6) +#define RGMII_CONFIG_INTF_SEL GENMASK(5, 4) +#define RGMII_CONFIG_BYPASS_TX_ID_EN BIT(3) +#define RGMII_CONFIG_LOOPBACK_EN BIT(2) +#define RGMII_CONFIG_PROG_SWAP BIT(1) +#define RGMII_CONFIG_DDR_MODE BIT(0) + +/* SDCC_HC_REG_DLL_CONFIG fields */ +#define SDCC_DLL_CONFIG_DLL_RST BIT(30) +#define SDCC_DLL_CONFIG_PDN BIT(29) +#define SDCC_DLL_CONFIG_MCLK_FREQ GENMASK(26, 24) +#define SDCC_DLL_CONFIG_CDR_SELEXT GENMASK(23, 20) +#define SDCC_DLL_CONFIG_CDR_EXT_EN BIT(19) +#define SDCC_DLL_CONFIG_CK_OUT_EN BIT(18) +#define SDCC_DLL_CONFIG_CDR_EN BIT(17) +#define SDCC_DLL_CONFIG_DLL_EN BIT(16) +#define SDCC_DLL_MCLK_GATING_EN BIT(5) +#define SDCC_DLL_CDR_FINE_PHASE GENMASK(3, 2) + +/* SDCC_HC_REG_DDR_CONFIG fields */ +#define SDCC_DDR_CONFIG_PRG_DLY_EN BIT(31) +#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY GENMASK(26, 21) +#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE GENMASK(29, 27) +#define SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN BIT(30) +#define SDCC_DDR_CONFIG_PRG_RCLK_DLY GENMASK(8, 0) + +/* SDCC_HC_REG_DLL_CONFIG2 fields */ +#define SDCC_DLL_CONFIG2_DLL_CLOCK_DIS BIT(21) +#define SDCC_DLL_CONFIG2_MCLK_FREQ_CALC GENMASK(17, 10) +#define SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL GENMASK(3, 2) +#define SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW BIT(1) +#define SDCC_DLL_CONFIG2_DDR_CAL_EN BIT(0) + +/* SDC4_STATUS bits */ +#define SDC4_STATUS_DLL_LOCK BIT(7) + +/* RGMII_IO_MACRO_CONFIG2 fields */ +#define RGMII_CONFIG2_RSVD_CONFIG15 GENMASK(31, 17) +#define RGMII_CONFIG2_RGMII_CLK_SEL_CFG BIT(16) +#define RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN BIT(13) +#define RGMII_CONFIG2_CLK_DIVIDE_SEL BIT(12) +#define RGMII_CONFIG2_RX_PROG_SWAP BIT(7) +#define RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL BIT(6) +#define RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN BIT(5) + +struct dwmac_rgmii_regs { + u32 io_macro_config; /* 0x00 */ + u32 sdcc_hc_dll_config; /* 0x04 */ + u32 reserved_1; /* 0x08 */ + u32 sdcc_hc_ddr_config; /* 0x0c */ + u32 sdcc_hc_dll_config2; /* 0x10 */ + u32 sdc4_status; /* 0x14 */ + u32 sdcc_usr_ctl; /* 0x18 */ + u32 io_macro_config2; /* 0x1c */ + u32 io_macro_debug1; /* 0x20 */ + u32 reserved_2; /* 0x24 */ + u32 emac_sys_low_power_dbg; /* 0x28 */ + u32 reserved_3[53]; /* upto 0x100 */ +}; + +static struct dwmac_rgmii_regs emac_v2_3_0_por = { + .io_macro_config = 0x00C01343, + .sdcc_hc_dll_config = 0x2004642C, + .sdcc_hc_ddr_config = 0x00000000, + .sdcc_hc_dll_config2 = 0x00200000, + .sdcc_usr_ctl = 0x00010800, + .io_macro_config2 = 0x00002060 +}; + +static void ethqos_set_func_clk_en(struct dwmac_rgmii_regs *regs) +{ + setbits_le32(®s->io_macro_config, RGMII_CONFIG_FUNC_CLK_EN); +} + +static int ethqos_dll_configure(struct udevice *dev, + struct dwmac_rgmii_regs *regs) +{ + unsigned int val; + int retry = 1000; + + /* Set CDR_EN */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EN); + + /* Set CDR_EXT_EN */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CDR_EXT_EN); + + /* Clear CK_OUT_EN */ + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN); + + /* Set DLL_EN */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN); + + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_MCLK_GATING_EN); + + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CDR_FINE_PHASE); + + /* Wait for CK_OUT_EN clear */ + do { + val = readl(®s->sdcc_hc_dll_config); + val &= SDCC_DLL_CONFIG_CK_OUT_EN; + if (!val) + break; + mdelay(1); + retry--; + } while (retry > 0); + if (!retry) + dev_err(dev, "Clear CK_OUT_EN timedout\n"); + + /* Set CK_OUT_EN */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_CK_OUT_EN); + + /* Wait for CK_OUT_EN set */ + retry = 1000; + do { + val = readl(®s->sdcc_hc_dll_config); + val &= SDCC_DLL_CONFIG_CK_OUT_EN; + if (val) + break; + mdelay(1); + retry--; + } while (retry > 0); + if (!retry) + dev_err(dev, "Set CK_OUT_EN timedout\n"); + + /* Set DDR_CAL_EN */ + setbits_le32(®s->sdcc_hc_dll_config2, SDCC_DLL_CONFIG2_DDR_CAL_EN); + + clrbits_le32(®s->sdcc_hc_dll_config2, + SDCC_DLL_CONFIG2_DLL_CLOCK_DIS); + + clrsetbits_le32(®s->sdcc_hc_dll_config2, + SDCC_DLL_CONFIG2_MCLK_FREQ_CALC, 0x1A << 10); + + clrsetbits_le32(®s->sdcc_hc_dll_config2, + SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL, BIT(2)); + + setbits_le32(®s->sdcc_hc_dll_config2, + SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW); + + return 0; +} + +static int ethqos_rgmii_macro_init(struct udevice *dev, + struct dwmac_rgmii_regs *regs, + unsigned long speed) +{ + /* Disable loopback mode */ + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN); + + /* Select RGMII, write 0 to interface select */ + clrbits_le32(®s->io_macro_config, RGMII_CONFIG_INTF_SEL); + + switch (speed) { + case SPEED_1000: + setbits_le32(®s->io_macro_config, RGMII_CONFIG_DDR_MODE); + clrbits_le32(®s->io_macro_config, + RGMII_CONFIG_BYPASS_TX_ID_EN); + setbits_le32(®s->io_macro_config, + RGMII_CONFIG_POS_NEG_DATA_SEL); + setbits_le32(®s->io_macro_config, RGMII_CONFIG_PROG_SWAP); + + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL); + setbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RSVD_CONFIG15); + setbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RX_PROG_SWAP); + + /* Set PRG_RCLK_DLY to 57 for 1.8 ns delay */ + clrsetbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_PRG_RCLK_DLY, 57); + setbits_le32(®s->sdcc_hc_ddr_config, SDCC_DDR_CONFIG_PRG_DLY_EN); + + setbits_le32(®s->io_macro_config, RGMII_CONFIG_LOOPBACK_EN); + break; + + case SPEED_100: + setbits_le32(®s->io_macro_config, RGMII_CONFIG_DDR_MODE); + setbits_le32(®s->io_macro_config, + RGMII_CONFIG_BYPASS_TX_ID_EN); + clrbits_le32(®s->io_macro_config, + RGMII_CONFIG_POS_NEG_DATA_SEL); + clrbits_le32(®s->io_macro_config, RGMII_CONFIG_PROG_SWAP); + clrsetbits_le32(®s->io_macro_config, + RGMII_CONFIG_MAX_SPD_PRG_2, BIT(6)); + + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL); + setbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RSVD_CONFIG15); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RX_PROG_SWAP); + + /* Write 0x5 to PRG_RCLK_DLY_CODE */ + clrsetbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE, + (BIT(29) | BIT(27))); + setbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY); + setbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN); + + setbits_le32(®s->io_macro_config, RGMII_CONFIG_LOOPBACK_EN); + break; + + case SPEED_10: + setbits_le32(®s->io_macro_config, RGMII_CONFIG_DDR_MODE); + setbits_le32(®s->io_macro_config, + RGMII_CONFIG_BYPASS_TX_ID_EN); + clrbits_le32(®s->io_macro_config, + RGMII_CONFIG_POS_NEG_DATA_SEL); + clrbits_le32(®s->io_macro_config, RGMII_CONFIG_PROG_SWAP); + clrsetbits_le32(®s->io_macro_config, + RGMII_CONFIG_MAX_SPD_PRG_9, + BIT(12) | GENMASK(9, 8)); + + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RSVD_CONFIG15); + clrbits_le32(®s->io_macro_config2, + RGMII_CONFIG2_RX_PROG_SWAP); + + /* Write 0x5 to PRG_RCLK_DLY_CODE */ + clrsetbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE, + (BIT(29) | BIT(27))); + setbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY); + setbits_le32(®s->sdcc_hc_ddr_config, + SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN); + + setbits_le32(®s->io_macro_config, RGMII_CONFIG_LOOPBACK_EN); + break; + + default: + dev_err(dev, "Invalid speed %ld\n", speed); + return -EINVAL; + } + + return 0; +} + +static int ethqos_configure(struct udevice *dev, + struct dwmac_rgmii_regs *regs, + unsigned long speed) +{ + unsigned int retry = 1000; + + /* Reset to POR values and enable clk */ + writel(emac_v2_3_0_por.io_macro_config, ®s->io_macro_config); + writel(emac_v2_3_0_por.sdcc_hc_dll_config, ®s->sdcc_hc_dll_config); + writel(emac_v2_3_0_por.sdcc_hc_ddr_config, ®s->sdcc_hc_ddr_config); + writel(emac_v2_3_0_por.sdcc_hc_dll_config2, ®s->sdcc_hc_dll_config2); + writel(emac_v2_3_0_por.sdcc_usr_ctl, ®s->sdcc_usr_ctl); + writel(emac_v2_3_0_por.io_macro_config2, ®s->io_macro_config2); + + ethqos_set_func_clk_en(regs); + + /* Initialize the DLL first */ + + /* Set DLL_RST */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST); + + /* Set PDN */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN); + + /* Clear DLL_RST */ + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_RST); + + /* Clear PDN */ + clrbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_PDN); + + if (speed == SPEED_1000) { + /* Set DLL_EN */ + setbits_le32(®s->sdcc_hc_dll_config, SDCC_DLL_CONFIG_DLL_EN); + + /* Set CK_OUT_EN */ + setbits_le32(®s->sdcc_hc_dll_config, + SDCC_DLL_CONFIG_CK_OUT_EN); + + /* Set USR_CTL bit 26 with mask of 3 bits */ + clrsetbits_le32(®s->sdcc_usr_ctl, GENMASK(26, 24), BIT(26)); + + /* wait for DLL LOCK */ + do { + mdelay(1); + if (readl(®s->sdc4_status) & SDC4_STATUS_DLL_LOCK) + break; + retry--; + } while (retry > 0); + if (!retry) + dev_err(dev, "Timeout while waiting for DLL lock\n"); + + ethqos_dll_configure(dev, regs); + } + + ethqos_rgmii_macro_init(dev, regs, speed); + + return 0; +} + +static void ethqos_rgmii_dump(struct udevice *dev, + struct dwmac_rgmii_regs *regs) +{ + dev_dbg(dev, "Rgmii register dump\n"); + dev_dbg(dev, "RGMII_IO_MACRO_CONFIG: %08x\n", + readl(®s->io_macro_config)); + dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG: %08x\n", + readl(®s->sdcc_hc_dll_config)); + dev_dbg(dev, "SDCC_HC_REG_DDR_CONFIG: %08x\n", + readl(®s->sdcc_hc_ddr_config)); + dev_dbg(dev, "SDCC_HC_REG_DLL_CONFIG2: %08x\n", + readl(®s->sdcc_hc_dll_config2)); + dev_dbg(dev, "SDC4_STATUS: %08x\n", + readl(®s->sdc4_status)); + dev_dbg(dev, "SDCC_USR_CTL: %08x\n", + readl(®s->sdcc_usr_ctl)); + dev_dbg(dev, "RGMII_IO_MACRO_CONFIG2: %08x\n", + readl(®s->io_macro_config2)); + dev_dbg(dev, "RGMII_IO_MACRO_DEBUG1: %08x\n", + readl(®s->io_macro_debug1)); + dev_dbg(dev, "EMAC_SYSTEM_LOW_POWER_DEBUG: %08x\n", + readl(®s->emac_sys_low_power_dbg)); +} + +static int qcom_eqos_rgmii_set_speed(struct udevice *dev, + void *rgmii_regs, + unsigned long speed) +{ + int ret; + + ethqos_rgmii_dump(dev, rgmii_regs); + + ret = ethqos_configure(dev, rgmii_regs, speed); + if (ret) + return ret; + + ethqos_rgmii_dump(dev, rgmii_regs); + + return 0; +} + +static int qcom_eqos_rgmii_reset(struct udevice *dev, void *rgmii_regs) +{ + ethqos_set_func_clk_en(rgmii_regs); + + return 0; +} + +static int eqos_start_clks_qcom(struct udevice *dev) +{ + if (IS_ENABLED(CONFIG_CLK)) { + struct clk_bulk clocks; + int ret; + + ret = clk_get_bulk(dev, &clocks); + if (ret) + return ret; + + ret = clk_enable_bulk(&clocks); + if (ret) + return ret; + } + + debug("%s: OK\n", __func__); + return 0; +} + +static int eqos_stop_clks_qcom(struct udevice *dev) +{ + if (IS_ENABLED(CONFIG_CLK)) { + struct clk_bulk clocks; + int ret; + + ret = clk_get_bulk(dev, &clocks); + if (ret) + return ret; + + ret = clk_disable_bulk(&clocks); + if (ret) + return ret; + } + + debug("%s: OK\n", __func__); + return 0; +} + +static int eqos_start_resets_qcom(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + int ret; + + debug("%s(dev=%p):\n", __func__, dev); + + if (!eqos->phy) { + ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 0); + if (ret < 0) { + pr_err("dm_gpio_set_value(phy_reset, assert) failed: %d", ret); + return ret; + } + + udelay(eqos->reset_delays[0]); + + ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 1); + if (ret < 0) { + pr_err("dm_gpio_set_value(phy_reset, deassert) failed: %d", ret); + return ret; + } + + udelay(eqos->reset_delays[1]); + + ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 0); + if (ret < 0) { + pr_err("dm_gpio_set_value(phy_reset, deassert) failed: %d", ret); + return ret; + } + + udelay(eqos->reset_delays[2]); + } + + ret = reset_deassert(&eqos->reset_ctl); + if (ret < 0) { + pr_err("reset_deassert() failed: %d", ret); + return ret; + } + + ret = qcom_eqos_rgmii_reset(dev, eqos->eqos_qcom_rgmii_regs); + if (ret < 0) { + pr_err("qcom rgmii_reset failed: %d", ret); + return ret; + } + + debug("%s: OK\n", __func__); + return 0; +} + +/* Clock rates */ +#define RGMII_1000_NOM_CLK_FREQ (250 * 1000 * 1000UL) +#define RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ (50 * 1000 * 1000UL) +#define RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ (5 * 1000 * 1000UL) + +static int eqos_set_tx_clk_speed_qcom(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + ulong rate; + int ret; + + debug("%s(dev=%p):\n", __func__, dev); + + switch (eqos->phy->speed) { + case SPEED_1000: + rate = RGMII_1000_NOM_CLK_FREQ; + break; + case SPEED_100: + rate = RGMII_ID_MODE_100_LOW_SVS_CLK_FREQ; + break; + case SPEED_10: + rate = RGMII_ID_MODE_10_LOW_SVS_CLK_FREQ; + break; + default: + pr_err("invalid speed %d", eqos->phy->speed); + return -EINVAL; + } + + ret = clk_set_rate(&eqos->clk_tx, rate); + if (ret < 0) { + pr_err("clk_set_rate(tx_clk, %lu) failed: %d", rate, ret); + return ret; + } + + ret = qcom_eqos_rgmii_set_speed(dev, eqos->eqos_qcom_rgmii_regs, + eqos->phy->speed); + if (ret < 0) { + pr_err("qcom set_speed: %d, failed: %d", eqos->phy->speed, ret); + return ret; + } + + return 0; +} + +static int eqos_probe_resources_qcom(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + phy_interface_t interface; + int reset_flags = GPIOD_IS_OUT; + int ret; + + debug("%s(dev=%p):\n", __func__, dev); + + interface = eqos->config->interface(dev); + + if (interface == PHY_INTERFACE_MODE_NA) { + pr_err("Invalid PHY interface\n"); + return -EINVAL; + } + + eqos->max_speed = dev_read_u32_default(dev, "max-speed", 0); + + eqos->tx_fifo_sz = dev_read_u32_default(dev, "tx-fifo-depth", 0); + eqos->rx_fifo_sz = dev_read_u32_default(dev, "rx-fifo-depth", 0); + + ret = reset_get_by_name(dev, "emac", &eqos->reset_ctl); + if (ret) { + pr_err("reset_get_by_name(rst) failed: %d", ret); + return ret; + } + + if (dev_read_bool(dev, "snps,reset-active-low")) + reset_flags |= GPIOD_ACTIVE_LOW; + + ret = gpio_request_by_name(dev, "snps,reset-gpio", 0, + &eqos->phy_reset_gpio, reset_flags); + if (ret == 0) { + ret = dev_read_u32_array(dev, "snps,reset-delays-us", + eqos->reset_delays, 3); + } else if (ret == -ENOENT) { + ret = 0; + } + + eqos->eqos_qcom_rgmii_regs = (void *)dev_read_addr_name(dev, "rgmii"); + if ((fdt_addr_t)eqos->eqos_qcom_rgmii_regs == FDT_ADDR_T_NONE) { + pr_err("Invalid RGMII address\n"); + return -EINVAL; + } + + ret = clk_get_by_name(dev, "rgmii", &eqos->clk_tx); + if (ret) { + pr_err("clk_get_by_name(tx) failed: %d", ret); + return -EINVAL; + } + + debug("%s: OK\n", __func__); + return 0; +} + +static int eqos_remove_resources_qcom(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + debug("%s(dev=%p):\n", __func__, dev); + + clk_free(&eqos->clk_tx); + dm_gpio_free(dev, &eqos->phy_reset_gpio); + reset_free(&eqos->reset_ctl); + + debug("%s: OK\n", __func__); + return 0; +} + +static struct eqos_ops eqos_qcom_ops = { + .eqos_inval_desc = eqos_inval_desc_generic, + .eqos_flush_desc = eqos_flush_desc_generic, + .eqos_inval_buffer = eqos_inval_buffer_generic, + .eqos_flush_buffer = eqos_flush_buffer_generic, + .eqos_probe_resources = eqos_probe_resources_qcom, + .eqos_remove_resources = eqos_remove_resources_qcom, + .eqos_stop_resets = eqos_null_ops, + .eqos_start_resets = eqos_start_resets_qcom, + .eqos_stop_clks = eqos_stop_clks_qcom, + .eqos_start_clks = eqos_start_clks_qcom, + .eqos_calibrate_pads = eqos_null_ops, + .eqos_disable_calibration = eqos_null_ops, + .eqos_set_tx_clk_speed = eqos_set_tx_clk_speed_qcom, + .eqos_get_enetaddr = eqos_null_ops, +}; + +struct eqos_config __maybe_unused eqos_qcom_config = { + .reg_access_always_ok = false, + .mdio_wait = 10, + .swr_wait = 50, + .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB, + .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_250_300, + .axi_bus_width = EQOS_AXI_WIDTH_64, + .interface = dev_read_phy_mode, + .ops = &eqos_qcom_ops +}; diff --git a/drivers/net/ftmac100.c b/drivers/net/ftmac100.c index f710c271c64..fae3adc3de3 100644 --- a/drivers/net/ftmac100.c +++ b/drivers/net/ftmac100.c @@ -12,9 +12,13 @@ #include <env.h> #include <malloc.h> #include <net.h> +#include <phy.h> +#include <miiphy.h> +#include <dm/device_compat.h> #include <asm/global_data.h> #include <linux/delay.h> #include <linux/io.h> +#include <linux/iopoll.h> #include "ftmac100.h" #include <dm.h> @@ -23,12 +27,16 @@ DECLARE_GLOBAL_DATA_PTR; #define ETH_ZLEN 60 +/* Timeout for a mdio read/write operation */ +#define FTMAC100_MDIO_TIMEOUT_USEC 10000 + struct ftmac100_data { struct ftmac100_txdes txdes[1]; struct ftmac100_rxdes rxdes[PKTBUFSRX]; int rx_index; const char *name; - phys_addr_t iobase; + struct ftmac100 *ftmac100; + struct mii_dev *bus; }; /* @@ -36,7 +44,7 @@ struct ftmac100_data { */ static void ftmac100_reset(struct ftmac100_data *priv) { - struct ftmac100 *ftmac100 = (struct ftmac100 *)(uintptr_t)priv->iobase; + struct ftmac100 *ftmac100 = priv->ftmac100; debug ("%s()\n", __func__); @@ -57,7 +65,7 @@ static void ftmac100_reset(struct ftmac100_data *priv) static void ftmac100_set_mac(struct ftmac100_data *priv , const unsigned char *mac) { - struct ftmac100 *ftmac100 = (struct ftmac100 *)(uintptr_t)priv->iobase; + struct ftmac100 *ftmac100 = priv->ftmac100; unsigned int maddr = mac[0] << 8 | mac[1]; unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; @@ -72,7 +80,7 @@ static void ftmac100_set_mac(struct ftmac100_data *priv , */ static void _ftmac100_halt(struct ftmac100_data *priv) { - struct ftmac100 *ftmac100 = (struct ftmac100 *)(uintptr_t)priv->iobase; + struct ftmac100 *ftmac100 = priv->ftmac100; debug ("%s()\n", __func__); writel (0, &ftmac100->maccr); } @@ -82,7 +90,7 @@ static void _ftmac100_halt(struct ftmac100_data *priv) */ static int _ftmac100_init(struct ftmac100_data *priv, unsigned char enetaddr[6]) { - struct ftmac100 *ftmac100 = (struct ftmac100 *)(uintptr_t)priv->iobase; + struct ftmac100 *ftmac100 = priv->ftmac100; struct ftmac100_txdes *txdes = priv->txdes; struct ftmac100_rxdes *rxdes = priv->rxdes; unsigned int maccr; @@ -187,7 +195,7 @@ static int __ftmac100_recv(struct ftmac100_data *priv) */ static int _ftmac100_send(struct ftmac100_data *priv, void *packet, int length) { - struct ftmac100 *ftmac100 = (struct ftmac100 *)(uintptr_t)priv->iobase; + struct ftmac100 *ftmac100 = priv->ftmac100; struct ftmac100_txdes *curr_des = priv->txdes; ulong start; @@ -314,7 +322,7 @@ static int ftmac100_of_to_plat(struct udevice *dev) struct eth_pdata *pdata = dev_get_plat(dev); const char *mac; pdata->iobase = dev_read_addr(dev); - priv->iobase = pdata->iobase; + priv->ftmac100 = phys_to_virt(pdata->iobase); mac = dtbmacaddr(0); if (mac) memcpy(pdata->enetaddr , mac , 6); @@ -322,10 +330,104 @@ static int ftmac100_of_to_plat(struct udevice *dev) return 0; } +/* + * struct mii_bus functions + */ +static int ftmac100_mdio_read(struct mii_dev *bus, int addr, int devad, + int reg) +{ + struct ftmac100_data *priv = bus->priv; + struct ftmac100 *ftmac100 = priv->ftmac100; + int phycr = FTMAC100_PHYCR_PHYAD(addr) | + FTMAC100_PHYCR_REGAD(reg) | + FTMAC100_PHYCR_MIIRD; + int ret; + + writel(phycr, &ftmac100->phycr); + + ret = readl_poll_timeout(&ftmac100->phycr, phycr, + !(phycr & FTMAC100_PHYCR_MIIRD), + FTMAC100_MDIO_TIMEOUT_USEC); + if (ret) + pr_err("%s: mdio read failed (addr=0x%x reg=0x%x)\n", + bus->name, addr, reg); + else + ret = phycr & FTMAC100_PHYCR_MIIRDATA; + + return ret; +} + +static int ftmac100_mdio_write(struct mii_dev *bus, int addr, int devad, + int reg, u16 value) +{ + struct ftmac100_data *priv = bus->priv; + struct ftmac100 *ftmac100 = priv->ftmac100; + int phycr = FTMAC100_PHYCR_PHYAD(addr) | + FTMAC100_PHYCR_REGAD(reg) | + FTMAC100_PHYCR_MIIWR; + int ret; + + writel(value, &ftmac100->phywdata); + writel(phycr, &ftmac100->phycr); + + ret = readl_poll_timeout(&ftmac100->phycr, phycr, + !(phycr & FTMAC100_PHYCR_MIIWR), + FTMAC100_MDIO_TIMEOUT_USEC); + if (ret) + pr_err("%s: mdio write failed (addr=0x%x reg=0x%x)\n", + bus->name, addr, reg); + + return ret; +} + +static int ftmac100_mdio_init(struct udevice *dev) +{ + struct ftmac100_data *priv = dev_get_priv(dev); + struct mii_dev *bus; + int ret; + + bus = mdio_alloc(); + if (!bus) + return -ENOMEM; + + bus->read = ftmac100_mdio_read; + bus->write = ftmac100_mdio_write; + bus->priv = priv; + + ret = mdio_register_seq(bus, dev_seq(dev)); + if (ret) { + mdio_free(bus); + return ret; + } + + priv->bus = bus; + + return 0; +} + static int ftmac100_probe(struct udevice *dev) { struct ftmac100_data *priv = dev_get_priv(dev); priv->name = dev->name; + int ret = 0; + + ret = ftmac100_mdio_init(dev); + if (ret) { + dev_err(dev, "Failed to initialize mdiobus: %d\n", ret); + goto out; + } + +out: + return ret; +} + +static int ftmac100_remove(struct udevice *dev) +{ + struct ftmac100_data *priv = dev_get_priv(dev); + + mdio_unregister(priv->bus); + mdio_free(priv->bus); + return 0; } @@ -348,12 +450,13 @@ static const struct udevice_id ftmac100_ids[] = { }; U_BOOT_DRIVER(ftmac100) = { - .name = "nds32_mac", + .name = "ftmac100", .id = UCLASS_ETH, .of_match = ftmac100_ids, .bind = ftmac100_bind, .of_to_plat = ftmac100_of_to_plat, .probe = ftmac100_probe, + .remove = ftmac100_remove, .ops = &ftmac100_ops, .priv_auto = sizeof(struct ftmac100_data), .plat_auto = sizeof(struct eth_pdata), diff --git a/drivers/net/ftmac100.h b/drivers/net/ftmac100.h index 75a49f628a6..21d339f835b 100644 --- a/drivers/net/ftmac100.h +++ b/drivers/net/ftmac100.h @@ -93,6 +93,15 @@ struct ftmac100 { #define FTMAC100_MACCR_RX_BROADPKT (1 << 17) /* + * PHY control register + */ +#define FTMAC100_PHYCR_MIIRDATA 0xffff +#define FTMAC100_PHYCR_PHYAD(x) (((x) & 0x1f) << 16) +#define FTMAC100_PHYCR_REGAD(x) (((x) & 0x1f) << 21) +#define FTMAC100_PHYCR_MIIWR BIT(27) +#define FTMAC100_PHYCR_MIIRD BIT(26) + +/* * Transmit descriptor, aligned to 16 bytes */ struct ftmac100_txdes { |
