diff options
Diffstat (limited to 'drivers')
240 files changed, 17223 insertions, 4660 deletions
diff --git a/drivers/block/blkmap.c b/drivers/block/blkmap.c index 34eed1380dc..473c65b5911 100644 --- a/drivers/block/blkmap.c +++ b/drivers/block/blkmap.c @@ -17,6 +17,30 @@ struct blkmap; /** + * define BLKMAP_SLICE_LINEAR - Linear mapping to another block device + * + * This blkmap slice type is used for mapping to other existing block + * devices. + */ +#define BLKMAP_SLICE_LINEAR BIT(0) + +/** + * define BLKMAP_SLICE_MEM - Linear mapping to memory based block device + * + * This blkmap slice type is used for mapping to memory based block + * devices, like ramdisks. + */ +#define BLKMAP_SLICE_MEM BIT(1) + +/** + * define BLKMAP_SLICE_PRESERVE - Preserved blkmap slice + * + * This blkmap slice is intended to be preserved, and it's + * information passed on to a later stage, like OS. + */ +#define BLKMAP_SLICE_PRESERVE BIT(2) + +/** * struct blkmap_slice - Region mapped to a blkmap * * Common data for a region mapped to a blkmap, specialized by each @@ -25,12 +49,14 @@ struct blkmap; * @node: List node used to associate this slice with a blkmap * @blknr: Start block number of the mapping * @blkcnt: Number of blocks covered by this mapping + * @attr: Attributes of blkmap slice */ struct blkmap_slice { struct list_head node; lbaint_t blknr; lbaint_t blkcnt; + uint attr; /** * @read: - Read from slice @@ -169,6 +195,7 @@ int blkmap_map_linear(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, .slice = { .blknr = blknr, .blkcnt = blkcnt, + .attr = BLKMAP_SLICE_LINEAR, .read = blkmap_linear_read, .write = blkmap_linear_write, @@ -234,7 +261,7 @@ static void blkmap_mem_destroy(struct blkmap *bm, struct blkmap_slice *bms) } int __blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, - void *addr, bool remapped) + void *addr, bool remapped, bool preserve) { struct blkmap *bm = dev_get_plat(dev); struct blkmap_mem *bmm; @@ -248,6 +275,7 @@ int __blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, .slice = { .blknr = blknr, .blkcnt = blkcnt, + .attr = BLKMAP_SLICE_MEM, .read = blkmap_mem_read, .write = blkmap_mem_write, @@ -258,6 +286,9 @@ int __blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, .remapped = remapped, }; + if (preserve) + bmm->slice.attr |= BLKMAP_SLICE_PRESERVE; + err = blkmap_slice_add(bm, &bmm->slice); if (err) free(bmm); @@ -268,11 +299,11 @@ int __blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, int blkmap_map_mem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, void *addr) { - return __blkmap_map_mem(dev, blknr, blkcnt, addr, false); + return __blkmap_map_mem(dev, blknr, blkcnt, addr, false, false); } int blkmap_map_pmem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, - phys_addr_t paddr) + phys_addr_t paddr, bool preserve) { struct blkmap *bm = dev_get_plat(dev); struct blk_desc *bd = dev_get_uclass_plat(bm->blk); @@ -283,7 +314,7 @@ int blkmap_map_pmem(struct udevice *dev, lbaint_t blknr, lbaint_t blkcnt, if (!addr) return -ENOMEM; - err = __blkmap_map_mem(dev, blknr, blkcnt, addr, true); + err = __blkmap_map_mem(dev, blknr, blkcnt, addr, true, preserve); if (err) unmap_sysmem(addr); @@ -486,6 +517,49 @@ err: return err; } +static bool blkmap_mem_preserve_slice(struct blkmap_slice *bms) +{ + return (bms->attr & (BLKMAP_SLICE_MEM | BLKMAP_SLICE_PRESERVE)) == + (BLKMAP_SLICE_MEM | BLKMAP_SLICE_PRESERVE); +} + +int blkmap_get_preserved_pmem_slices(int (*cb)(void *ctx, u64 addr, + u64 size), void *ctx) +{ + int ret; + u64 addr, size; + struct udevice *dev; + struct uclass *uc; + struct blkmap *bm; + struct blkmap_mem *bmm; + struct blkmap_slice *bms; + struct blk_desc *bd; + + if (!cb) { + log_debug("%s: No callback passed to the function\n", __func__); + return 0; + } + + uclass_id_foreach_dev(UCLASS_BLKMAP, dev, uc) { + bm = dev_get_plat(dev); + bd = dev_get_uclass_plat(bm->blk); + + list_for_each_entry(bms, &bm->slices, node) { + if (!blkmap_mem_preserve_slice(bms)) + continue; + + bmm = container_of(bms, struct blkmap_mem, slice); + addr = (u64)(uintptr_t)bmm->addr; + size = (u64)bms->blkcnt << bd->log2blksz; + ret = cb(ctx, addr, size); + if (ret) + return ret; + } + } + + return 0; +} + int blkmap_destroy(struct udevice *dev) { int err; diff --git a/drivers/block/blkmap_helper.c b/drivers/block/blkmap_helper.c index bfba14110d2..2f1bc28ee5d 100644 --- a/drivers/block/blkmap_helper.c +++ b/drivers/block/blkmap_helper.c @@ -28,7 +28,7 @@ int blkmap_create_ramdisk(const char *label, ulong image_addr, ulong image_size, bm = dev_get_plat(bm_dev); desc = dev_get_uclass_plat(bm->blk); blknum = image_size >> desc->log2blksz; - ret = blkmap_map_pmem(bm_dev, 0, blknum, image_addr); + ret = blkmap_map_pmem(bm_dev, 0, blknum, image_addr, true); if (ret) { log_err("Unable to map %#llx at block %d : %d\n", (unsigned long long)image_addr, 0, ret); diff --git a/drivers/bootcount/Kconfig b/drivers/bootcount/Kconfig index 0080d2a165c..99b6c7534fd 100644 --- a/drivers/bootcount/Kconfig +++ b/drivers/bootcount/Kconfig @@ -183,6 +183,9 @@ config BOOTCOUNT_BOOTLIMIT counter being cleared. If set to 0, do not set a boot limit in the environment. +config BOOTCOUNT_ALTBOOTCMD + string "Alternative boot command when BOOTLIMIT is reached" + config SYS_BOOTCOUNT_SINGLEWORD bool "Use single word to pack boot count and magic value" depends on BOOTCOUNT_GENERIC diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index fe0e49f6112..8411205ee04 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_$(PHASE_)CLK_GPIO) += clk-gpio.o obj-$(CONFIG_$(PHASE_)CLK_STUB) += clk-stub.o obj-y += adi/ +obj-y += airoha/ obj-y += analogbits/ obj-y += imx/ obj-$(CONFIG_CLK_JH7110) += starfive/ diff --git a/drivers/clk/airoha/Makefile b/drivers/clk/airoha/Makefile new file mode 100644 index 00000000000..1744c5f7236 --- /dev/null +++ b/drivers/clk/airoha/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +# Core +obj-$(CONFIG_ARCH_AIROHA) += clk-airoha.o diff --git a/drivers/clk/airoha/clk-airoha.c b/drivers/clk/airoha/clk-airoha.c new file mode 100644 index 00000000000..1b2c4c98de5 --- /dev/null +++ b/drivers/clk/airoha/clk-airoha.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Based on the Linux clk-en7523.c but majorly reworked + * for U-Boot that doesn't require CCF subsystem. + * + * Major modification, support for set_rate, realtime + * get_rate and split for reset part to a different driver. + * + * Author: Lorenzo Bianconi <lorenzo@kernel.org> (original driver) + * Christian Marangi <ansuelsmth@gmail.com> + */ + +#include <clk-uclass.h> +#include <dm.h> +#include <dm/devres.h> +#include <dm/device_compat.h> +#include <dm/lists.h> +#include <regmap.h> +#include <syscon.h> + +#include <dt-bindings/clock/en7523-clk.h> + +#define REG_GSW_CLK_DIV_SEL 0x1b4 +#define REG_EMI_CLK_DIV_SEL 0x1b8 +#define REG_BUS_CLK_DIV_SEL 0x1bc +#define REG_SPI_CLK_DIV_SEL 0x1c4 +#define REG_SPI_CLK_FREQ_SEL 0x1c8 +#define REG_NPU_CLK_DIV_SEL 0x1fc + +#define REG_NP_SCU_PCIC 0x88 +#define REG_NP_SCU_SSTR 0x9c +#define REG_PCIE_XSI0_SEL_MASK GENMASK(14, 13) +#define REG_PCIE_XSI1_SEL_MASK GENMASK(12, 11) +#define REG_CRYPTO_CLKSRC2 0x20c + +#define EN7581_MAX_CLKS 9 + +struct airoha_clk_desc { + int id; + const char *name; + u32 base_reg; + u8 base_bits; + u8 base_shift; + union { + const unsigned int *base_values; + unsigned int base_value; + }; + size_t n_base_values; + + u16 div_reg; + u8 div_bits; + u8 div_shift; + u16 div_val0; + u8 div_step; + u8 div_offset; +}; + +struct airoha_clk_priv { + struct regmap *chip_scu_map; + struct airoha_clk_soc_data *data; +}; + +struct airoha_clk_soc_data { + u32 num_clocks; + const struct airoha_clk_desc *descs; +}; + +static const u32 gsw_base[] = { 400000000, 500000000 }; +static const u32 slic_base[] = { 100000000, 3125000 }; + +static const u32 emi7581_base[] = { 540000000, 480000000, 400000000, 300000000 }; +static const u32 bus7581_base[] = { 600000000, 540000000 }; +static const u32 npu7581_base[] = { 800000000, 750000000, 720000000, 600000000 }; +static const u32 crypto_base[] = { 540000000, 480000000 }; +static const u32 emmc7581_base[] = { 200000000, 150000000 }; + +static const struct airoha_clk_desc en7581_base_clks[EN7581_MAX_CLKS] = { + [EN7523_CLK_GSW] = { + .id = EN7523_CLK_GSW, + .name = "gsw", + + .base_reg = REG_GSW_CLK_DIV_SEL, + .base_bits = 1, + .base_shift = 8, + .base_values = gsw_base, + .n_base_values = ARRAY_SIZE(gsw_base), + + .div_bits = 3, + .div_shift = 0, + .div_step = 1, + .div_offset = 1, + }, + [EN7523_CLK_EMI] = { + .id = EN7523_CLK_EMI, + .name = "emi", + + .base_reg = REG_EMI_CLK_DIV_SEL, + .base_bits = 2, + .base_shift = 8, + .base_values = emi7581_base, + .n_base_values = ARRAY_SIZE(emi7581_base), + + .div_bits = 3, + .div_shift = 0, + .div_step = 1, + .div_offset = 1, + }, + [EN7523_CLK_BUS] = { + .id = EN7523_CLK_BUS, + .name = "bus", + + .base_reg = REG_BUS_CLK_DIV_SEL, + .base_bits = 1, + .base_shift = 8, + .base_values = bus7581_base, + .n_base_values = ARRAY_SIZE(bus7581_base), + + .div_bits = 3, + .div_shift = 0, + .div_step = 1, + .div_offset = 1, + }, + [EN7523_CLK_SLIC] = { + .id = EN7523_CLK_SLIC, + .name = "slic", + + .base_reg = REG_SPI_CLK_FREQ_SEL, + .base_bits = 1, + .base_shift = 0, + .base_values = slic_base, + .n_base_values = ARRAY_SIZE(slic_base), + + .div_reg = REG_SPI_CLK_DIV_SEL, + .div_bits = 5, + .div_shift = 24, + .div_val0 = 20, + .div_step = 2, + }, + [EN7523_CLK_SPI] = { + .id = EN7523_CLK_SPI, + .name = "spi", + + .base_reg = REG_SPI_CLK_DIV_SEL, + + .base_value = 400000000, + + .div_bits = 5, + .div_shift = 8, + .div_val0 = 40, + .div_step = 2, + }, + [EN7523_CLK_NPU] = { + .id = EN7523_CLK_NPU, + .name = "npu", + + .base_reg = REG_NPU_CLK_DIV_SEL, + .base_bits = 2, + .base_shift = 8, + .base_values = npu7581_base, + .n_base_values = ARRAY_SIZE(npu7581_base), + + .div_bits = 3, + .div_shift = 0, + .div_step = 1, + .div_offset = 1, + }, + [EN7523_CLK_CRYPTO] = { + .id = EN7523_CLK_CRYPTO, + .name = "crypto", + + .base_reg = REG_CRYPTO_CLKSRC2, + .base_bits = 1, + .base_shift = 0, + .base_values = crypto_base, + .n_base_values = ARRAY_SIZE(crypto_base), + }, + [EN7581_CLK_EMMC] = { + .id = EN7581_CLK_EMMC, + .name = "emmc", + + .base_reg = REG_CRYPTO_CLKSRC2, + .base_bits = 1, + .base_shift = 12, + .base_values = emmc7581_base, + .n_base_values = ARRAY_SIZE(emmc7581_base), + } +}; + +static u32 airoha_clk_get_base_rate(const struct airoha_clk_desc *desc, u32 val) +{ + if (!desc->base_bits) + return desc->base_value; + + val >>= desc->base_shift; + val &= (1 << desc->base_bits) - 1; + + if (val >= desc->n_base_values) + return 0; + + return desc->base_values[val]; +} + +static u32 airoha_clk_get_div(const struct airoha_clk_desc *desc, u32 val) +{ + if (!desc->div_bits) + return 1; + + val >>= desc->div_shift; + val &= (1 << desc->div_bits) - 1; + + if (!val && desc->div_val0) + return desc->div_val0; + + return (val + desc->div_offset) * desc->div_step; +} + +static int airoha_clk_enable(struct clk *clk) +{ + struct airoha_clk_priv *priv = dev_get_priv(clk->dev); + struct airoha_clk_soc_data *data = priv->data; + int id = clk->id; + + if (id > data->num_clocks) + return -EINVAL; + + return 0; +} + +static int airoha_clk_disable(struct clk *clk) +{ + return 0; +} + +static ulong airoha_clk_get_rate(struct clk *clk) +{ + struct airoha_clk_priv *priv = dev_get_priv(clk->dev); + struct airoha_clk_soc_data *data = priv->data; + const struct airoha_clk_desc *desc; + struct regmap *map = priv->chip_scu_map; + int id = clk->id; + u32 reg, val; + ulong rate; + int ret; + + if (id > data->num_clocks) { + dev_err(clk->dev, "Invalid clk ID %d\n", id); + return 0; + } + + desc = &data->descs[id]; + + ret = regmap_read(map, desc->base_reg, &val); + if (ret) { + dev_err(clk->dev, "Failed to read reg for clock %s\n", + desc->name); + return 0; + } + + rate = airoha_clk_get_base_rate(desc, val); + + reg = desc->div_reg ? desc->div_reg : desc->base_reg; + ret = regmap_read(map, reg, &val); + if (ret) { + dev_err(clk->dev, "Failed to read reg for clock %s\n", + desc->name); + return 0; + } + + rate /= airoha_clk_get_div(desc, val); + + return rate; +} + +static int airoha_clk_search_rate(const struct airoha_clk_desc *desc, int div, + ulong rate) +{ + int i; + + /* Single base rate */ + if (!desc->base_bits) { + if (rate != desc->base_value / div) + goto err; + + return 0; + } + + /* Check every base rate with provided divisor */ + for (i = 0; i < desc->n_base_values; i++) + if (rate == desc->base_values[i] / div) + return i; + +err: + return -EINVAL; +} + +static ulong airoha_clk_set_rate(struct clk *clk, ulong rate) +{ + struct airoha_clk_priv *priv = dev_get_priv(clk->dev); + struct airoha_clk_soc_data *data = priv->data; + const struct airoha_clk_desc *desc; + struct regmap *map = priv->chip_scu_map; + int div_val, base_val; + u32 reg, val, mask; + int id = clk->id; + int div; + int ret; + + if (id > data->num_clocks) { + dev_err(clk->dev, "Invalid clk ID %d\n", id); + return 0; + } + + desc = &data->descs[id]; + + if (!desc->base_bits && !desc->div_bits) { + dev_err(clk->dev, "Can't set rate for fixed clock %s\n", + desc->name); + return 0; + } + + if (!desc->div_bits) { + /* Divisor not supported, just search in base rate */ + div_val = 0; + base_val = airoha_clk_search_rate(desc, 1, rate); + if (base_val < 0) { + dev_err(clk->dev, "Invalid rate for clock %s\n", + desc->name); + return 0; + } + } else { + div_val = 0; + + /* Check if div0 satisfy the request */ + if (desc->div_val0) { + base_val = airoha_clk_search_rate(desc, desc->div_val0, + rate); + if (base_val >= 0) { + div_val = 0; + goto apply; + } + + /* Skip checking first divisor val */ + div_val = 1; + } + + /* Simulate rate with every divisor supported */ + for (div_val = div_val + desc->div_offset; + div_val < BIT(desc->div_bits) - 1; div_val++) { + div = div_val * desc->div_step; + + base_val = airoha_clk_search_rate(desc, div, rate); + if (base_val >= 0) + break; + } + + if (div_val == BIT(desc->div_bits) - 1) { + dev_err(clk->dev, "Invalid rate for clock %s\n", + desc->name); + return 0; + } + } + +apply: + if (desc->div_bits) { + reg = desc->div_reg ? desc->div_reg : desc->base_reg; + + mask = (BIT(desc->div_bits) - 1) << desc->div_shift; + val = div_val << desc->div_shift; + + ret = regmap_update_bits(map, reg, mask, val); + if (ret) { + dev_err(clk->dev, "Failed to update div reg for clock %s\n", + desc->name); + return 0; + } + } + + if (desc->base_bits) { + mask = (BIT(desc->base_bits) - 1) << desc->base_shift; + val = base_val << desc->base_shift; + + ret = regmap_update_bits(map, desc->base_reg, mask, val); + if (ret) { + dev_err(clk->dev, "Failed to update reg for clock %s\n", + desc->name); + return 0; + } + } + + return rate; +} + +const struct clk_ops airoha_clk_ops = { + .enable = airoha_clk_enable, + .disable = airoha_clk_disable, + .get_rate = airoha_clk_get_rate, + .set_rate = airoha_clk_set_rate, +}; + +static int airoha_clk_probe(struct udevice *dev) +{ + struct airoha_clk_priv *priv = dev_get_priv(dev); + ofnode chip_scu_node; + + chip_scu_node = ofnode_by_compatible(ofnode_null(), + "airoha,en7581-chip-scu"); + if (!ofnode_valid(chip_scu_node)) + return -EINVAL; + + priv->chip_scu_map = syscon_node_to_regmap(chip_scu_node); + if (IS_ERR(priv->chip_scu_map)) + return PTR_ERR(priv->chip_scu_map); + + priv->data = (void *)dev_get_driver_data(dev); + + return 0; +} + +static int airoha_clk_bind(struct udevice *dev) +{ + struct udevice *rst_dev; + int ret = 0; + + if (CONFIG_IS_ENABLED(RESET_AIROHA)) { + ret = device_bind_driver_to_node(dev, "airoha-reset", "reset", + dev_ofnode(dev), &rst_dev); + if (ret) + debug("Warning: failed to bind reset controller\n"); + } + + return ret; +} + +static const struct airoha_clk_soc_data en7581_data = { + .num_clocks = ARRAY_SIZE(en7581_base_clks), + .descs = en7581_base_clks, +}; + +static const struct udevice_id airoha_clk_ids[] = { + { .compatible = "airoha,en7581-scu", + .data = (ulong)&en7581_data, + }, + { } +}; + +U_BOOT_DRIVER(airoha_clk) = { + .name = "clk-airoha", + .id = UCLASS_CLK, + .of_match = airoha_clk_ids, + .probe = airoha_clk_probe, + .bind = airoha_clk_bind, + .priv_auto = sizeof(struct airoha_clk_priv), + .ops = &airoha_clk_ops, +}; diff --git a/drivers/clk/altera/clk-agilex5.c b/drivers/clk/altera/clk-agilex5.c index 716c71598bc..fb1e72ffc5c 100644 --- a/drivers/clk/altera/clk-agilex5.c +++ b/drivers/clk/altera/clk-agilex5.c @@ -1,13 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2024 Intel Corporation <www.intel.com> + * Copyright (C) 2025 Altera Corporation <www.altera.com> */ -#include <clk-uclass.h> #include <config.h> -#include <errno.h> -#include <dm.h> #include <log.h> +#include <dm.h> +#include <errno.h> #include <stdarg.h> #include <stdio.h> #include <time.h> @@ -23,9 +23,14 @@ #include <linux/types.h> #include <asm/arch/clock_manager.h> #include <dt-bindings/clock/agilex5-clock.h> +#include <wait_bit.h> +#include <clk-uclass.h> DECLARE_GLOBAL_DATA_PTR; +#define CLKMGR_CTRL_SWCTRLBTCLKEN_MASK BIT(8) +#define CLKMGR_CTRL_SWCTRLBTCLKSEL_MASK BIT(9) + struct socfpga_clk_plat { void __iomem *regs; }; @@ -36,21 +41,30 @@ struct socfpga_clk_plat { */ static void clk_write_bypass_mainpll(struct socfpga_clk_plat *plat, u32 val) { + uintptr_t base_addr = (uintptr_t)plat->regs; + CM_REG_WRITEL(plat, val, CLKMGR_MAINPLL_BYPASS); - cm_wait_for_fsm(); + wait_for_bit_le32((const void *)(base_addr + CLKMGR_STAT), CLKMGR_STAT_BUSY, + false, 20000, false); } static void clk_write_bypass_perpll(struct socfpga_clk_plat *plat, u32 val) { + uintptr_t base_addr = (uintptr_t)plat->regs; + CM_REG_WRITEL(plat, val, CLKMGR_PERPLL_BYPASS); - cm_wait_for_fsm(); + wait_for_bit_le32((const void *)(base_addr + CLKMGR_STAT), CLKMGR_STAT_BUSY, + false, 20000, false); } /* function to write the ctrl register which requires a poll of the busy bit */ static void clk_write_ctrl(struct socfpga_clk_plat *plat, u32 val) { + uintptr_t base_addr = (uintptr_t)plat->regs; + CM_REG_WRITEL(plat, val, CLKMGR_CTRL); - cm_wait_for_fsm(); + wait_for_bit_le32((const void *)(base_addr + CLKMGR_STAT), CLKMGR_STAT_BUSY, + false, 20000, false); } static const struct { @@ -59,15 +73,6 @@ static const struct { u32 mask; } membus_pll[] = { { - MEMBUS_CLKSLICE_REG, - /* - * BIT[7:7] - * Enable source synchronous mode - */ - BIT(7), - BIT(7) - }, - { MEMBUS_SYNTHCALFOSC_INIT_CENTERFREQ_REG, /* * BIT[0:0] @@ -238,6 +243,7 @@ static void clk_basic_init(struct udevice *dev, { struct socfpga_clk_plat *plat = dev_get_plat(dev); u32 vcocalib; + uintptr_t base_addr = (uintptr_t)plat->regs; if (!cfg) return; @@ -249,7 +255,8 @@ static void clk_basic_init(struct udevice *dev, CM_REG_SETBITS(plat, CLKMGR_PERPLL_PLLGLOB, CLKMGR_PLLGLOB_PD_MASK | CLKMGR_PLLGLOB_RST_MASK); - cm_wait_for_lock(CLKMGR_STAT_ALLPLL_LOCKED_MASK); + wait_for_bit_le32((const void *)(base_addr + CLKMGR_STAT), + CLKMGR_STAT_ALLPLL_LOCKED_MASK, true, 20000, false); /* Put both PLLs in bypass */ clk_write_bypass_mainpll(plat, CLKMGR_BYPASS_MAINPLL_ALL); @@ -264,9 +271,14 @@ static void clk_basic_init(struct udevice *dev, CM_REG_READL(plat, CLKMGR_CTRL) & ~CLKMGR_CTRL_BOOTMODE); } else { #ifdef CONFIG_XPL_BUILD - /* Always force clock manager into boot mode before any configuration */ - clk_write_ctrl(plat, - CM_REG_READL(plat, CLKMGR_CTRL) | CLKMGR_CTRL_BOOTMODE); + /* + * Configure HPS Internal Oscillator as default boot_clk source, + * always force clock manager into boot mode before any configuration + */ + clk_write_ctrl(plat, CM_REG_READL(plat, CLKMGR_CTRL) | + CLKMGR_CTRL_BOOTMODE | + CLKMGR_CTRL_SWCTRLBTCLKEN_MASK | + CLKMGR_CTRL_SWCTRLBTCLKSEL_MASK); #else /* Skip clock configuration in SSBL if it's not in boot mode */ if (!(CM_REG_READL(plat, CLKMGR_CTRL) & CLKMGR_CTRL_BOOTMODE)) @@ -365,7 +377,8 @@ static void clk_basic_init(struct udevice *dev, CLKMGR_PLLCX_EN_SET_MSK, CLKMGR_PERPLL_PLLC3); - cm_wait_for_lock(CLKMGR_STAT_ALLPLL_LOCKED_MASK); + wait_for_bit_le32((const void *)(base_addr + CLKMGR_STAT), + CLKMGR_STAT_ALLPLL_LOCKED_MASK, true, 20000, false); CM_REG_WRITEL(plat, CLKMGR_LOSTLOCK_SET_MASK, CLKMGR_MAINPLL_LOSTLOCK); CM_REG_WRITEL(plat, CLKMGR_LOSTLOCK_SET_MASK, CLKMGR_PERPLL_LOSTLOCK); diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 199ca6eaa37..9e3b5191767 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -97,7 +97,7 @@ static int clk_composite_disable(struct clk *clk) return 0; } -struct clk *clk_register_composite(struct device *dev, const char *name, +struct clk *clk_register_composite(struct udevice *dev, const char *name, const char * const *parent_names, int num_parents, struct clk *mux, const struct clk_ops *mux_ops, @@ -149,7 +149,7 @@ struct clk *clk_register_composite(struct device *dev, const char *name, clk = &composite->clk; clk->flags = flags; ret = clk_register(clk, UBOOT_DM_CLK_COMPOSITE, name, - parent_names[clk_composite_get_parent(clk)]); + clk_resolve_parent_clk(dev, parent_names[clk_composite_get_parent(clk)])); if (ret) { clk = ERR_PTR(ret); goto err; diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index aa210e3d15f..e692b9c2167 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -183,7 +183,7 @@ const struct clk_ops clk_divider_ops = { .set_rate = clk_divider_set_rate, }; -static struct clk *_register_divider(struct device *dev, const char *name, +static struct clk *_register_divider(struct udevice *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags, const struct clk_div_table *table) @@ -218,7 +218,8 @@ static struct clk *_register_divider(struct device *dev, const char *name, clk = &div->clk; clk->flags = flags; - ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, name, parent_name); + ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, name, + clk_resolve_parent_clk(dev, parent_name)); if (ret) { kfree(div); return ERR_PTR(ret); @@ -227,7 +228,7 @@ static struct clk *_register_divider(struct device *dev, const char *name, return clk; } -struct clk *clk_register_divider(struct device *dev, const char *name, +struct clk *clk_register_divider(struct udevice *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags) diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index 068798cf9b0..4eb8be728e6 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -37,7 +37,7 @@ const struct clk_ops ccf_clk_fixed_factor_ops = { .get_rate = clk_factor_recalc_rate, }; -struct clk *clk_hw_register_fixed_factor(struct device *dev, +struct clk *clk_hw_register_fixed_factor(struct udevice *dev, const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div) { @@ -56,7 +56,7 @@ struct clk *clk_hw_register_fixed_factor(struct device *dev, clk->flags = flags; ret = clk_register(clk, UBOOT_DM_CLK_IMX_FIXED_FACTOR, name, - parent_name); + clk_resolve_parent_clk(dev, parent_name)); if (ret) { kfree(fix); return ERR_PTR(ret); @@ -65,7 +65,7 @@ struct clk *clk_hw_register_fixed_factor(struct device *dev, return clk; } -struct clk *clk_register_fixed_factor(struct device *dev, const char *name, +struct clk *clk_register_fixed_factor(struct udevice *dev, const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div) { diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index bf1c6a93b46..256ff108991 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -117,7 +117,7 @@ const struct clk_ops clk_gate_ops = { .get_rate = clk_generic_get_rate, }; -struct clk *clk_register_gate(struct device *dev, const char *name, +struct clk *clk_register_gate(struct udevice *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock) @@ -149,7 +149,8 @@ struct clk *clk_register_gate(struct device *dev, const char *name, clk = &gate->clk; clk->flags = flags; - ret = clk_register(clk, UBOOT_DM_CLK_GATE, name, parent_name); + ret = clk_register(clk, UBOOT_DM_CLK_GATE, name, + clk_resolve_parent_clk(dev, parent_name)); if (ret) { kfree(gate); return ERR_PTR(ret); diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 62477e15d27..d7411f8f282 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -113,6 +113,11 @@ int clk_mux_fetch_parent_index(struct clk *clk, struct clk *parent) for (i = 0; i < mux->num_parents; i++) { if (!strcmp(parent->dev->name, mux->parent_names[i])) return i; + if (!strcmp(parent->dev->name, + clk_resolve_parent_clk(clk->dev, + mux->parent_names[i]))) + return i; + } return -EINVAL; @@ -159,15 +164,15 @@ const struct clk_ops clk_mux_ops = { .set_parent = clk_mux_set_parent, }; -struct clk *clk_hw_register_mux_table(struct device *dev, const char *name, +struct clk *clk_register_mux(struct udevice *dev, const char *name, const char * const *parent_names, u8 num_parents, unsigned long flags, - void __iomem *reg, u8 shift, u32 mask, - u8 clk_mux_flags, u32 *table) + void __iomem *reg, u8 shift, u8 width, + u8 clk_mux_flags) { + u32 mask = BIT(width) - 1; struct clk_mux *mux; struct clk *clk; - u8 width = 0; int ret; if (clk_mux_flags & CLK_MUX_HIWORD_MASK) { @@ -192,7 +197,7 @@ struct clk *clk_hw_register_mux_table(struct device *dev, const char *name, mux->shift = shift; mux->mask = mask; mux->flags = clk_mux_flags; - mux->table = table; + mux->table = NULL; #if IS_ENABLED(CONFIG_SANDBOX_CLK_CCF) mux->io_mux_val = *(u32 *)reg; #endif @@ -207,7 +212,8 @@ struct clk *clk_hw_register_mux_table(struct device *dev, const char *name, * for the corresponding clock (to do that define .set_parent() method). */ ret = clk_register(clk, UBOOT_DM_CLK_CCF_MUX, name, - parent_names[clk_mux_get_parent(clk)]); + clk_resolve_parent_clk(dev, + parent_names[clk_mux_get_parent(clk)])); if (ret) { kfree(mux); return ERR_PTR(ret); @@ -216,35 +222,6 @@ struct clk *clk_hw_register_mux_table(struct device *dev, const char *name, return clk; } -struct clk *clk_register_mux_table(struct device *dev, const char *name, - const char * const *parent_names, u8 num_parents, - unsigned long flags, - void __iomem *reg, u8 shift, u32 mask, - u8 clk_mux_flags, u32 *table) -{ - struct clk *clk; - - clk = clk_hw_register_mux_table(dev, name, parent_names, num_parents, - flags, reg, shift, mask, clk_mux_flags, - table); - if (IS_ERR(clk)) - return ERR_CAST(clk); - return clk; -} - -struct clk *clk_register_mux(struct device *dev, const char *name, - const char * const *parent_names, u8 num_parents, - unsigned long flags, - void __iomem *reg, u8 shift, u8 width, - u8 clk_mux_flags) -{ - u32 mask = BIT(width) - 1; - - return clk_register_mux_table(dev, name, parent_names, num_parents, - flags, reg, shift, mask, clk_mux_flags, - NULL); -} - U_BOOT_DRIVER(ccf_clk_mux) = { .name = UBOOT_DM_CLK_CCF_MUX, .id = UCLASS_CLK, diff --git a/drivers/clk/clk-stub.c b/drivers/clk/clk-stub.c index 5fbbb07b7f7..343fa5cd3fe 100644 --- a/drivers/clk/clk-stub.c +++ b/drivers/clk/clk-stub.c @@ -50,6 +50,7 @@ static struct clk_ops stub_clk_ops = { static const struct udevice_id stub_clk_ids[] = { { .compatible = "qcom,rpmcc" }, + { .compatible = "qcom,sc7280-rpmh-clk" }, { .compatible = "qcom,sm8150-rpmh-clk" }, { .compatible = "qcom,sm8250-rpmh-clk" }, { .compatible = "qcom,sm8550-rpmh-clk" }, diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 353ae476068..90b70529a47 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -420,6 +420,24 @@ int clk_get_by_name_nodev(ofnode node, const char *name, struct clk *clk) return clk_get_by_index_nodev(node, index, clk); } +const char * +clk_resolve_parent_clk(struct udevice *dev, const char *name) +{ + struct udevice *parent; + struct clk clk; + int ret; + + ret = uclass_get_device_by_name(UCLASS_CLK, name, &parent); + if (!ret) + return name; + + ret = clk_get_by_name(dev, name, &clk); + if (!clk.dev) + return name; + + return clk.dev->name; +} + int clk_release_all(struct clk *clk, unsigned int count) { unsigned int i; diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig index 56d893e0579..d17a54fb9b3 100644 --- a/drivers/clk/imx/Kconfig +++ b/drivers/clk/imx/Kconfig @@ -60,6 +60,7 @@ config SPL_CLK_IMX8MP depends on ARCH_IMX8M && SPL select SPL_CLK select SPL_CLK_CCF + select SPL_CLK_COMPOSITE_CCF help This enables SPL DM/DTS support for clock driver in i.MX8MP diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c index 64bffa3b181..14c5b92939c 100644 --- a/drivers/clk/imx/clk-composite-8m.c +++ b/drivers/clk/imx/clk-composite-8m.c @@ -151,7 +151,7 @@ const struct clk_ops imx8m_clk_mux_ops = { .set_parent = imx8m_clk_mux_set_parent, }; -struct clk *imx8m_clk_composite_flags(const char *name, +struct clk *imx8m_clk_composite_flags(struct udevice *dev, const char *name, const char * const *parent_names, int num_parents, void __iomem *reg, unsigned long flags) @@ -187,7 +187,7 @@ struct clk *imx8m_clk_composite_flags(const char *name, gate->reg = reg; gate->bit_idx = PCG_CGC_SHIFT; - clk = clk_register_composite(NULL, name, + clk = clk_register_composite(dev, name, parent_names, num_parents, &mux->clk, &imx8m_clk_mux_ops, &div->clk, &imx8m_clk_composite_divider_ops, diff --git a/drivers/clk/imx/clk-gate2.c b/drivers/clk/imx/clk-gate2.c index 65fa6b5b139..fa07b13249b 100644 --- a/drivers/clk/imx/clk-gate2.c +++ b/drivers/clk/imx/clk-gate2.c @@ -90,7 +90,7 @@ static const struct clk_ops clk_gate2_ops = { .get_rate = clk_generic_get_rate, }; -struct clk *clk_register_gate2(struct device *dev, const char *name, +struct clk *clk_register_gate2(struct udevice *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 cgr_val, u8 clk_gate2_flags, unsigned int *share_count) @@ -111,7 +111,8 @@ struct clk *clk_register_gate2(struct device *dev, const char *name, clk = &gate->clk; - ret = clk_register(clk, UBOOT_DM_CLK_IMX_GATE2, name, parent_name); + ret = clk_register(clk, UBOOT_DM_CLK_IMX_GATE2, name, + clk_resolve_parent_clk(dev, parent_name)); if (ret) { kfree(gate); return ERR_PTR(ret); diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index df9f0285e1e..13239f2f64d 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -35,6 +35,8 @@ static const char *const usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", }; static const char *const periph_sels[] = { "periph_pre", "periph_clk2", }; static const char *const periph_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll2_198m", }; +static const char *const uart_sels[] = { "pll3_80m", "osc", }; +static const char *const ecspi_sels[] = { "pll3_60m", "osc", }; static int imx6q_clk_probe(struct udevice *dev) { @@ -44,21 +46,21 @@ static int imx6q_clk_probe(struct udevice *dev) base = (void *)ANATOP_BASE_ADDR; clk_dm(IMX6QDL_CLK_PLL2, - imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc", + imx_clk_pllv3(dev, IMX_PLLV3_GENERIC, "pll2_bus", "osc", base + 0x30, 0x1)); clk_dm(IMX6QDL_CLK_PLL3_USB_OTG, - imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", + imx_clk_pllv3(dev, IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x3)); clk_dm(IMX6QDL_CLK_PLL3_60M, - imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8)); + imx_clk_fixed_factor(dev, "pll3_60m", "pll3_usb_otg", 1, 8)); clk_dm(IMX6QDL_CLK_PLL2_PFD0_352M, imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0)); clk_dm(IMX6QDL_CLK_PLL2_PFD2_396M, imx_clk_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2)); clk_dm(IMX6QDL_CLK_PLL6, - imx_clk_pllv3(IMX_PLLV3_ENET, "pll6", "osc", base + 0xe0, 0x3)); + imx_clk_pllv3(dev, IMX_PLLV3_ENET, "pll6", "osc", base + 0xe0, 0x3)); clk_dm(IMX6QDL_CLK_PLL6_ENET, - imx_clk_gate("pll6_enet", "pll6", base + 0xe0, 13)); + imx_clk_gate(dev, "pll6_enet", "pll6", base + 0xe0, 13)); /* CCM clocks */ base = dev_read_addr_ptr(dev); @@ -66,76 +68,98 @@ static int imx6q_clk_probe(struct udevice *dev) return -EINVAL; clk_dm(IMX6QDL_CLK_USDHC1_SEL, - imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1, + imx_clk_mux(dev, "usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); clk_dm(IMX6QDL_CLK_USDHC2_SEL, - imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1, + imx_clk_mux(dev, "usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); clk_dm(IMX6QDL_CLK_USDHC3_SEL, - imx_clk_mux("usdhc3_sel", base + 0x1c, 18, 1, + imx_clk_mux(dev, "usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); clk_dm(IMX6QDL_CLK_USDHC4_SEL, - imx_clk_mux("usdhc4_sel", base + 0x1c, 19, 1, + imx_clk_mux(dev, "usdhc4_sel", base + 0x1c, 19, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); + if (of_machine_is_compatible("fsl,imx6qp")) { + clk_dm(IMX6QDL_CLK_UART_SEL, + imx_clk_mux(dev, "uart_sel", base + 0x24, 6, 1, uart_sels, + ARRAY_SIZE(uart_sels))); + clk_dm(IMX6QDL_CLK_ECSPI_SEL, + imx_clk_mux(dev, "ecspi_sel", base + 0x38, 18, 1, ecspi_sels, + ARRAY_SIZE(ecspi_sels))); + } + clk_dm(IMX6QDL_CLK_USDHC1_PODF, - imx_clk_divider("usdhc1_podf", "usdhc1_sel", + imx_clk_divider(dev, "usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3)); clk_dm(IMX6QDL_CLK_USDHC2_PODF, - imx_clk_divider("usdhc2_podf", "usdhc2_sel", + imx_clk_divider(dev, "usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3)); clk_dm(IMX6QDL_CLK_USDHC3_PODF, - imx_clk_divider("usdhc3_podf", "usdhc3_sel", + imx_clk_divider(dev, "usdhc3_podf", "usdhc3_sel", base + 0x24, 19, 3)); clk_dm(IMX6QDL_CLK_USDHC4_PODF, - imx_clk_divider("usdhc4_podf", "usdhc4_sel", + imx_clk_divider(dev, "usdhc4_podf", "usdhc4_sel", base + 0x24, 22, 3)); - clk_dm(IMX6QDL_CLK_ECSPI_ROOT, - imx_clk_divider("ecspi_root", "pll3_60m", base + 0x38, 19, 6)); + if (of_machine_is_compatible("fsl,imx6qp")) { + clk_dm(IMX6QDL_CLK_UART_SERIAL_PODF, + imx_clk_divider(dev, "uart_serial_podf", "uart_sel", base + 0x24, 0, 6)); + clk_dm(IMX6QDL_CLK_ECSPI_ROOT, + imx_clk_divider(dev, "ecspi_root", "ecspi_sel", base + 0x38, 19, 6)); + } else { + clk_dm(IMX6QDL_CLK_UART_SERIAL_PODF, + imx_clk_divider(dev, "uart_serial_podf", "pll3_80m", base + 0x24, 0, 6)); + clk_dm(IMX6QDL_CLK_ECSPI_ROOT, + imx_clk_divider(dev, "ecspi_root", "pll3_60m", base + 0x38, 19, 6)); + } clk_dm(IMX6QDL_CLK_ECSPI1, - imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0)); + imx_clk_gate2(dev, "ecspi1", "ecspi_root", base + 0x6c, 0)); clk_dm(IMX6QDL_CLK_ECSPI2, - imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2)); + imx_clk_gate2(dev, "ecspi2", "ecspi_root", base + 0x6c, 2)); clk_dm(IMX6QDL_CLK_ECSPI3, - imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4)); + imx_clk_gate2(dev, "ecspi3", "ecspi_root", base + 0x6c, 4)); clk_dm(IMX6QDL_CLK_ECSPI4, - imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6)); + imx_clk_gate2(dev, "ecspi4", "ecspi_root", base + 0x6c, 6)); + clk_dm(IMX6QDL_CLK_UART_IPG, + imx_clk_gate2(dev, "uart_ipg", "ipg", base + 0x7c, 24)); + clk_dm(IMX6QDL_CLK_UART_SERIAL, + imx_clk_gate2(dev, "uart_serial", "uart_serial_podf", base + 0x7c, 26)); clk_dm(IMX6QDL_CLK_USDHC1, - imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2)); + imx_clk_gate2(dev, "usdhc1", "usdhc1_podf", base + 0x80, 2)); clk_dm(IMX6QDL_CLK_USDHC2, - imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4)); + imx_clk_gate2(dev, "usdhc2", "usdhc2_podf", base + 0x80, 4)); clk_dm(IMX6QDL_CLK_USDHC3, - imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6)); + imx_clk_gate2(dev, "usdhc3", "usdhc3_podf", base + 0x80, 6)); clk_dm(IMX6QDL_CLK_USDHC4, - imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8)); + imx_clk_gate2(dev, "usdhc4", "usdhc4_podf", base + 0x80, 8)); clk_dm(IMX6QDL_CLK_PERIPH_PRE, - imx_clk_mux("periph_pre", base + 0x18, 18, 2, periph_pre_sels, + imx_clk_mux(dev, "periph_pre", base + 0x18, 18, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels))); clk_dm(IMX6QDL_CLK_PERIPH, - imx_clk_busy_mux("periph", base + 0x14, 25, 1, base + 0x48, + imx_clk_busy_mux(dev, "periph", base + 0x14, 25, 1, base + 0x48, 5, periph_sels, ARRAY_SIZE(periph_sels))); clk_dm(IMX6QDL_CLK_AHB, - imx_clk_busy_divider("ahb", "periph", base + 0x14, 10, 3, + imx_clk_busy_divider(dev, "ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1)); clk_dm(IMX6QDL_CLK_IPG, - imx_clk_divider("ipg", "ahb", base + 0x14, 8, 2)); + imx_clk_divider(dev, "ipg", "ahb", base + 0x14, 8, 2)); clk_dm(IMX6QDL_CLK_IPG_PER, - imx_clk_divider("ipg_per", "ipg", base + 0x1c, 0, 6)); + imx_clk_divider(dev, "ipg_per", "ipg", base + 0x1c, 0, 6)); clk_dm(IMX6QDL_CLK_I2C1, - imx_clk_gate2("i2c1", "ipg_per", base + 0x70, 6)); + imx_clk_gate2(dev, "i2c1", "ipg_per", base + 0x70, 6)); clk_dm(IMX6QDL_CLK_I2C2, - imx_clk_gate2("i2c2", "ipg_per", base + 0x70, 8)); + imx_clk_gate2(dev, "i2c2", "ipg_per", base + 0x70, 8)); clk_dm(IMX6QDL_CLK_I2C3, - imx_clk_gate2("i2c3", "ipg_per", base + 0x70, 10)); + imx_clk_gate2(dev, "i2c3", "ipg_per", base + 0x70, 10)); clk_dm(IMX6QDL_CLK_PWM1, - imx_clk_gate2("pwm1", "ipg_per", base + 0x78, 16)); + imx_clk_gate2(dev, "pwm1", "ipg_per", base + 0x78, 16)); - clk_dm(IMX6QDL_CLK_ENET, imx_clk_gate2("enet", "ipg", base + 0x6c, 10)); + clk_dm(IMX6QDL_CLK_ENET, imx_clk_gate2(dev, "enet", "ipg", base + 0x6c, 10)); clk_dm(IMX6QDL_CLK_ENET_REF, - imx_clk_fixed_factor("enet_ref", "pll6_enet", 1, 1)); + imx_clk_fixed_factor(dev, "enet_ref", "pll6_enet", 1, 1)); return 0; } diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c index bb6958f0ec2..b81db516a69 100644 --- a/drivers/clk/imx/clk-imx8mm.c +++ b/drivers/clk/imx/clk-imx8mm.c @@ -14,7 +14,7 @@ #include "clk.h" -static const char * const pll_ref_sels[] = { "clock-osc-24m", "dummy", "dummy", "dummy", }; +static const char * const pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", }; static const char * const dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", }; static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; static const char * const sys_pll1_bypass_sels[] = {"sys_pll1", "sys_pll1_ref_sel", }; @@ -23,128 +23,144 @@ static const char * const sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_se static const char * const imx8mm_arm_core_sels[] = {"arm_a53_src", "arm_pll_out", }; -static const char * const imx8mm_a53_sels[] = {"clock-osc-24m", "arm_pll_out", "sys_pll2_500m", +static const char * const imx8mm_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", "sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", }; -static const char * const imx8mm_ahb_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_800m", +static const char * const imx8mm_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m", "sys_pll1_400m", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", }; #ifndef CONFIG_XPL_BUILD -static const char * const imx8mm_enet_axi_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", +static const char * const imx8mm_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_250m", "sys_pll2_200m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", }; -static const char * const imx8mm_enet_ref_sels[] = {"clock-osc-24m", "sys_pll2_125m", "sys_pll2_50m", +static const char * const imx8mm_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", "sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out", "video_pll1_out", "clk_ext4", }; -static const char * const imx8mm_enet_timer_sels[] = {"clock-osc-24m", "sys_pll2_100m", "audio_pll1_out", +static const char * const imx8mm_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", "clk_ext3", "clk_ext4", "video_pll1_out", }; -static const char * const imx8mm_enet_phy_sels[] = {"clock-osc-24m", "sys_pll2_50m", "sys_pll2_125m", +static const char * const imx8mm_enet_phy_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m", "sys_pll2_200m", "sys_pll2_500m", "video_pll1_out", "audio_pll2_out", }; #endif -static const char * const imx8mm_nand_usdhc_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", +static const char * const imx8mm_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_200m", "sys_pll1_133m", "sys_pll3_out", "sys_pll2_250m", "audio_pll1_out", }; -static const char * const imx8mm_usb_bus_sels[] = {"clock-osc-24m", "sys_pll2_500m", "sys_pll1_800m", +static const char * const imx8mm_usb_bus_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext4", "audio_pll2_out", }; -static const char * const imx8mm_usdhc1_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mm_usdhc1_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; -static const char * const imx8mm_usdhc2_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mm_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; -static const char * const imx8mm_i2c1_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mm_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mm_i2c2_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mm_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mm_i2c3_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mm_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mm_i2c4_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mm_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; +static const char * const imx8mm_uart1_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext4", + "audio_pll2_out", }; + +static const char * const imx8mm_uart2_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext3", + "audio_pll2_out", }; + +static const char * const imx8mm_uart3_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext4", + "audio_pll2_out", }; + +static const char * const imx8mm_uart4_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext3", + "audio_pll2_out", }; + #if CONFIG_IS_ENABLED(PCIE_DW_IMX) -static const char * const imx8mm_pcie1_ctrl_sels[] = {"clock-osc-24m", "sys_pll2_250m", "sys_pll2_200m", +static const char * const imx8mm_pcie1_ctrl_sels[] = {"osc_24m", "sys_pll2_250m", "sys_pll2_200m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll2_333m", "sys_pll3_out", }; -static const char * const imx8mm_pcie1_phy_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll2_500m", +static const char * const imx8mm_pcie1_phy_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll2_500m", "clk_ext1", "clk_ext2", "clk_ext3", "clk_ext4", "sys_pll1_400m", }; -static const char * const imx8mm_pcie1_aux_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll2_50m", +static const char * const imx8mm_pcie1_aux_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m", "sys_pll3_out", "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_160m", "sys_pll1_200m", }; #endif #ifndef CONFIG_XPL_BUILD -static const char * const imx8mm_pwm1_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mm_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", }; -static const char * const imx8mm_pwm2_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mm_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", }; -static const char * const imx8mm_pwm3_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mm_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll1_out", }; -static const char * const imx8mm_pwm4_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mm_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll1_out", }; #endif -static const char * const imx8mm_wdog_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_160m", +static const char * const imx8mm_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m", "vpu_pll_out", "sys_pll2_125m", "sys_pll3_out", "sys_pll1_80m", "sys_pll2_166m", }; -static const char * const imx8mm_usdhc3_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mm_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_clk", "sys_pll1_100m", }; #if CONFIG_IS_ENABLED(NXP_FSPI) -static const char * const imx8mm_qspi_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll2_333m", +static const char * const imx8mm_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll2_333m", "sys_pll2_500m", "audio_pll2_out", "sys_pll1_266m", "sys_pll3_out", "sys_pll1_100m", }; #endif -static const char * const imx8mm_usb_core_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m", +static const char * const imx8mm_usb_core_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; -static const char * const imx8mm_usb_phy_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m", +static const char * const imx8mm_usb_phy_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; #if CONFIG_IS_ENABLED(DM_SPI) -static const char * const imx8mm_ecspi1_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mm_ecspi1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; -static const char * const imx8mm_ecspi2_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mm_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; -static const char * const imx8mm_ecspi3_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mm_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; #endif @@ -156,19 +172,19 @@ static int imx8mm_clk_probe(struct udevice *dev) base = (void *)ANATOP_BASE_ADDR; clk_dm(IMX8MM_DRAM_PLL_REF_SEL, - imx_clk_mux("dram_pll_ref_sel", base + 0x50, 0, 2, + imx_clk_mux(dev, "dram_pll_ref_sel", base + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MM_ARM_PLL_REF_SEL, - imx_clk_mux("arm_pll_ref_sel", base + 0x84, 0, 2, + imx_clk_mux(dev, "arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MM_SYS_PLL1_REF_SEL, - imx_clk_mux("sys_pll1_ref_sel", base + 0x94, 0, 2, + imx_clk_mux(dev, "sys_pll1_ref_sel", base + 0x94, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MM_SYS_PLL2_REF_SEL, - imx_clk_mux("sys_pll2_ref_sel", base + 0x104, 0, 2, + imx_clk_mux(dev, "sys_pll2_ref_sel", base + 0x104, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MM_SYS_PLL3_REF_SEL, - imx_clk_mux("sys_pll3_ref_sel", base + 0x114, 0, 2, + imx_clk_mux(dev, "sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MM_DRAM_PLL, @@ -189,238 +205,254 @@ static int imx8mm_clk_probe(struct udevice *dev) /* PLL bypass out */ clk_dm(IMX8MM_DRAM_PLL_BYPASS, - imx_clk_mux_flags("dram_pll_bypass", base + 0x50, 4, 1, + imx_clk_mux_flags(dev, "dram_pll_bypass", base + 0x50, 4, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MM_ARM_PLL_BYPASS, - imx_clk_mux_flags("arm_pll_bypass", base + 0x84, 4, 1, + imx_clk_mux_flags(dev, "arm_pll_bypass", base + 0x84, 4, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MM_SYS_PLL1_BYPASS, - imx_clk_mux_flags("sys_pll1_bypass", base + 0x94, 4, 1, + imx_clk_mux_flags(dev, "sys_pll1_bypass", base + 0x94, 4, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MM_SYS_PLL2_BYPASS, - imx_clk_mux_flags("sys_pll2_bypass", base + 0x104, 4, 1, + imx_clk_mux_flags(dev, "sys_pll2_bypass", base + 0x104, 4, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MM_SYS_PLL3_BYPASS, - imx_clk_mux_flags("sys_pll3_bypass", base + 0x114, 4, 1, + imx_clk_mux_flags(dev, "sys_pll3_bypass", base + 0x114, 4, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT)); /* PLL out gate */ clk_dm(IMX8MM_DRAM_PLL_OUT, - imx_clk_gate("dram_pll_out", "dram_pll_bypass", + imx_clk_gate(dev, "dram_pll_out", "dram_pll_bypass", base + 0x50, 13)); clk_dm(IMX8MM_ARM_PLL_OUT, - imx_clk_gate("arm_pll_out", "arm_pll_bypass", + imx_clk_gate(dev, "arm_pll_out", "arm_pll_bypass", base + 0x84, 11)); clk_dm(IMX8MM_SYS_PLL1_OUT, - imx_clk_gate("sys_pll1_out", "sys_pll1_bypass", + imx_clk_gate(dev, "sys_pll1_out", "sys_pll1_bypass", base + 0x94, 11)); clk_dm(IMX8MM_SYS_PLL2_OUT, - imx_clk_gate("sys_pll2_out", "sys_pll2_bypass", + imx_clk_gate(dev, "sys_pll2_out", "sys_pll2_bypass", base + 0x104, 11)); clk_dm(IMX8MM_SYS_PLL3_OUT, - imx_clk_gate("sys_pll3_out", "sys_pll3_bypass", + imx_clk_gate(dev, "sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11)); /* SYS PLL fixed output */ clk_dm(IMX8MM_SYS_PLL1_40M, - imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20)); + imx_clk_fixed_factor(dev, "sys_pll1_40m", "sys_pll1_out", 1, 20)); clk_dm(IMX8MM_SYS_PLL1_80M, - imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10)); + imx_clk_fixed_factor(dev, "sys_pll1_80m", "sys_pll1_out", 1, 10)); clk_dm(IMX8MM_SYS_PLL1_100M, - imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8)); + imx_clk_fixed_factor(dev, "sys_pll1_100m", "sys_pll1_out", 1, 8)); clk_dm(IMX8MM_SYS_PLL1_133M, - imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6)); + imx_clk_fixed_factor(dev, "sys_pll1_133m", "sys_pll1_out", 1, 6)); clk_dm(IMX8MM_SYS_PLL1_160M, - imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5)); + imx_clk_fixed_factor(dev, "sys_pll1_160m", "sys_pll1_out", 1, 5)); clk_dm(IMX8MM_SYS_PLL1_200M, - imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4)); + imx_clk_fixed_factor(dev, "sys_pll1_200m", "sys_pll1_out", 1, 4)); clk_dm(IMX8MM_SYS_PLL1_266M, - imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3)); + imx_clk_fixed_factor(dev, "sys_pll1_266m", "sys_pll1_out", 1, 3)); clk_dm(IMX8MM_SYS_PLL1_400M, - imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2)); + imx_clk_fixed_factor(dev, "sys_pll1_400m", "sys_pll1_out", 1, 2)); clk_dm(IMX8MM_SYS_PLL1_800M, - imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1)); + imx_clk_fixed_factor(dev, "sys_pll1_800m", "sys_pll1_out", 1, 1)); clk_dm(IMX8MM_SYS_PLL2_50M, - imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20)); + imx_clk_fixed_factor(dev, "sys_pll2_50m", "sys_pll2_out", 1, 20)); clk_dm(IMX8MM_SYS_PLL2_100M, - imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10)); + imx_clk_fixed_factor(dev, "sys_pll2_100m", "sys_pll2_out", 1, 10)); clk_dm(IMX8MM_SYS_PLL2_125M, - imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8)); + imx_clk_fixed_factor(dev, "sys_pll2_125m", "sys_pll2_out", 1, 8)); clk_dm(IMX8MM_SYS_PLL2_166M, - imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6)); + imx_clk_fixed_factor(dev, "sys_pll2_166m", "sys_pll2_out", 1, 6)); clk_dm(IMX8MM_SYS_PLL2_200M, - imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5)); + imx_clk_fixed_factor(dev, "sys_pll2_200m", "sys_pll2_out", 1, 5)); clk_dm(IMX8MM_SYS_PLL2_250M, - imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4)); + imx_clk_fixed_factor(dev, "sys_pll2_250m", "sys_pll2_out", 1, 4)); clk_dm(IMX8MM_SYS_PLL2_333M, - imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3)); + imx_clk_fixed_factor(dev, "sys_pll2_333m", "sys_pll2_out", 1, 3)); clk_dm(IMX8MM_SYS_PLL2_500M, - imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2)); + imx_clk_fixed_factor(dev, "sys_pll2_500m", "sys_pll2_out", 1, 2)); clk_dm(IMX8MM_SYS_PLL2_1000M, - imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1)); + imx_clk_fixed_factor(dev, "sys_pll2_1000m", "sys_pll2_out", 1, 1)); base = dev_read_addr_ptr(dev); if (!base) return -EINVAL; clk_dm(IMX8MM_CLK_A53_SRC, - imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, + imx_clk_mux2(dev, "arm_a53_src", base + 0x8000, 24, 3, imx8mm_a53_sels, ARRAY_SIZE(imx8mm_a53_sels))); clk_dm(IMX8MM_CLK_A53_CG, - imx_clk_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28)); + imx_clk_gate3(dev, "arm_a53_cg", "arm_a53_src", base + 0x8000, 28)); clk_dm(IMX8MM_CLK_A53_DIV, - imx_clk_divider2("arm_a53_div", "arm_a53_cg", + imx_clk_divider2(dev, "arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3)); clk_dm(IMX8MM_CLK_AHB, - imx8m_clk_composite_critical("ahb", imx8mm_ahb_sels, + imx8m_clk_composite_critical(dev, "ahb", imx8mm_ahb_sels, base + 0x9000)); clk_dm(IMX8MM_CLK_IPG_ROOT, - imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1)); + imx_clk_divider2(dev, "ipg_root", "ahb", base + 0x9080, 0, 1)); clk_dm(IMX8MM_CLK_NAND_USDHC_BUS, - imx8m_clk_composite_critical("nand_usdhc_bus", + imx8m_clk_composite_critical(dev, "nand_usdhc_bus", imx8mm_nand_usdhc_sels, base + 0x8900)); clk_dm(IMX8MM_CLK_USB_BUS, - imx8m_clk_composite("usb_bus", imx8mm_usb_bus_sels, base + 0x8b80)); + imx8m_clk_composite(dev, "usb_bus", imx8mm_usb_bus_sels, base + 0x8b80)); /* IP */ #if CONFIG_IS_ENABLED(PCIE_DW_IMX) clk_dm(IMX8MM_CLK_PCIE1_CTRL, - imx8m_clk_composite("pcie1_ctrl", imx8mm_pcie1_ctrl_sels, + imx8m_clk_composite(dev, "pcie1_ctrl", imx8mm_pcie1_ctrl_sels, base + 0xa300)); clk_dm(IMX8MM_CLK_PCIE1_PHY, - imx8m_clk_composite("pcie1_phy", imx8mm_pcie1_phy_sels, + imx8m_clk_composite(dev, "pcie1_phy", imx8mm_pcie1_phy_sels, base + 0xa380)); clk_dm(IMX8MM_CLK_PCIE1_AUX, - imx8m_clk_composite("pcie1_aux", imx8mm_pcie1_aux_sels, + imx8m_clk_composite(dev, "pcie1_aux", imx8mm_pcie1_aux_sels, base + 0xa400)); #endif clk_dm(IMX8MM_CLK_USDHC1, - imx8m_clk_composite("usdhc1", imx8mm_usdhc1_sels, + imx8m_clk_composite(dev, "usdhc1", imx8mm_usdhc1_sels, base + 0xac00)); clk_dm(IMX8MM_CLK_USDHC2, - imx8m_clk_composite("usdhc2", imx8mm_usdhc2_sels, + imx8m_clk_composite(dev, "usdhc2", imx8mm_usdhc2_sels, base + 0xac80)); clk_dm(IMX8MM_CLK_I2C1, - imx8m_clk_composite("i2c1", imx8mm_i2c1_sels, base + 0xad00)); + imx8m_clk_composite(dev, "i2c1", imx8mm_i2c1_sels, base + 0xad00)); clk_dm(IMX8MM_CLK_I2C2, - imx8m_clk_composite("i2c2", imx8mm_i2c2_sels, base + 0xad80)); + imx8m_clk_composite(dev, "i2c2", imx8mm_i2c2_sels, base + 0xad80)); clk_dm(IMX8MM_CLK_I2C3, - imx8m_clk_composite("i2c3", imx8mm_i2c3_sels, base + 0xae00)); + imx8m_clk_composite(dev, "i2c3", imx8mm_i2c3_sels, base + 0xae00)); clk_dm(IMX8MM_CLK_I2C4, - imx8m_clk_composite("i2c4", imx8mm_i2c4_sels, base + 0xae80)); + imx8m_clk_composite(dev, "i2c4", imx8mm_i2c4_sels, base + 0xae80)); + clk_dm(IMX8MM_CLK_UART1, + imx8m_clk_composite(dev, "uart1", imx8mm_uart1_sels, base + 0xaf00)); + clk_dm(IMX8MM_CLK_UART2, + imx8m_clk_composite(dev, "uart2", imx8mm_uart2_sels, base + 0xaf80)); + clk_dm(IMX8MM_CLK_UART3, + imx8m_clk_composite(dev, "uart3", imx8mm_uart3_sels, base + 0xb000)); + clk_dm(IMX8MM_CLK_UART4, + imx8m_clk_composite(dev, "uart4", imx8mm_uart4_sels, base + 0xb080)); + clk_dm(IMX8MM_CLK_UART1_ROOT, + imx_clk_gate4(dev, "uart1_root_clk", "uart1", base + 0x4490, 0)); + clk_dm(IMX8MM_CLK_UART2_ROOT, + imx_clk_gate4(dev, "uart2_root_clk", "uart2", base + 0x44a0, 0)); + clk_dm(IMX8MM_CLK_UART3_ROOT, + imx_clk_gate4(dev, "uart3_root_clk", "uart3", base + 0x44b0, 0)); + clk_dm(IMX8MM_CLK_UART4_ROOT, + imx_clk_gate4(dev, "uart4_root_clk", "uart4", base + 0x44c0, 0)); clk_dm(IMX8MM_CLK_WDOG, - imx8m_clk_composite("wdog", imx8mm_wdog_sels, base + 0xb900)); + imx8m_clk_composite(dev, "wdog", imx8mm_wdog_sels, base + 0xb900)); clk_dm(IMX8MM_CLK_USDHC3, - imx8m_clk_composite("usdhc3", imx8mm_usdhc3_sels, + imx8m_clk_composite(dev, "usdhc3", imx8mm_usdhc3_sels, base + 0xbc80)); clk_dm(IMX8MM_CLK_USB_CORE_REF, - imx8m_clk_composite("usb_core_ref", imx8mm_usb_core_sels, base + 0xb100)); + imx8m_clk_composite(dev, "usb_core_ref", imx8mm_usb_core_sels, base + 0xb100)); clk_dm(IMX8MM_CLK_USB_PHY_REF, - imx8m_clk_composite("usb_phy_ref", imx8mm_usb_phy_sels, base + 0xb180)); + imx8m_clk_composite(dev, "usb_phy_ref", imx8mm_usb_phy_sels, base + 0xb180)); clk_dm(IMX8MM_CLK_I2C1_ROOT, - imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0)); + imx_clk_gate4(dev, "i2c1_root_clk", "i2c1", base + 0x4170, 0)); clk_dm(IMX8MM_CLK_I2C2_ROOT, - imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0)); + imx_clk_gate4(dev, "i2c2_root_clk", "i2c2", base + 0x4180, 0)); clk_dm(IMX8MM_CLK_I2C3_ROOT, - imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0)); + imx_clk_gate4(dev, "i2c3_root_clk", "i2c3", base + 0x4190, 0)); clk_dm(IMX8MM_CLK_I2C4_ROOT, - imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0)); + imx_clk_gate4(dev, "i2c4_root_clk", "i2c4", base + 0x41a0, 0)); clk_dm(IMX8MM_CLK_OCOTP_ROOT, - imx_clk_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0)); + imx_clk_gate4(dev, "ocotp_root_clk", "ipg_root", base + 0x4220, 0)); clk_dm(IMX8MM_CLK_USDHC1_ROOT, - imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0)); + imx_clk_gate4(dev, "usdhc1_root_clk", "usdhc1", base + 0x4510, 0)); clk_dm(IMX8MM_CLK_USDHC2_ROOT, - imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0)); + imx_clk_gate4(dev, "usdhc2_root_clk", "usdhc2", base + 0x4520, 0)); clk_dm(IMX8MM_CLK_WDOG1_ROOT, - imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0)); + imx_clk_gate4(dev, "wdog1_root_clk", "wdog", base + 0x4530, 0)); clk_dm(IMX8MM_CLK_WDOG2_ROOT, - imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0)); + imx_clk_gate4(dev, "wdog2_root_clk", "wdog", base + 0x4540, 0)); clk_dm(IMX8MM_CLK_WDOG3_ROOT, - imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0)); + imx_clk_gate4(dev, "wdog3_root_clk", "wdog", base + 0x4550, 0)); clk_dm(IMX8MM_CLK_USDHC3_ROOT, - imx_clk_gate4("usdhc3_root_clk", "usdhc3", base + 0x45e0, 0)); + imx_clk_gate4(dev, "usdhc3_root_clk", "usdhc3", base + 0x45e0, 0)); clk_dm(IMX8MM_CLK_USB1_CTRL_ROOT, - imx_clk_gate4("usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0)); + imx_clk_gate4(dev, "usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0)); /* clks not needed in SPL stage */ #ifndef CONFIG_XPL_BUILD clk_dm(IMX8MM_CLK_ENET_AXI, - imx8m_clk_composite("enet_axi", imx8mm_enet_axi_sels, + imx8m_clk_composite(dev, "enet_axi", imx8mm_enet_axi_sels, base + 0x8880)); clk_dm(IMX8MM_CLK_ENET_REF, - imx8m_clk_composite("enet_ref", imx8mm_enet_ref_sels, + imx8m_clk_composite(dev, "enet_ref", imx8mm_enet_ref_sels, base + 0xa980)); clk_dm(IMX8MM_CLK_ENET_TIMER, - imx8m_clk_composite("enet_timer", imx8mm_enet_timer_sels, + imx8m_clk_composite(dev, "enet_timer", imx8mm_enet_timer_sels, base + 0xaa00)); clk_dm(IMX8MM_CLK_ENET_PHY_REF, - imx8m_clk_composite("enet_phy", imx8mm_enet_phy_sels, + imx8m_clk_composite(dev, "enet_phy", imx8mm_enet_phy_sels, base + 0xaa80)); clk_dm(IMX8MM_CLK_ENET1_ROOT, - imx_clk_gate4("enet1_root_clk", "enet_axi", + imx_clk_gate4(dev, "enet1_root_clk", "enet_axi", base + 0x40a0, 0)); clk_dm(IMX8MM_CLK_PWM1, - imx8m_clk_composite("pwm1", imx8mm_pwm1_sels, base + 0xb380)); + imx8m_clk_composite(dev, "pwm1", imx8mm_pwm1_sels, base + 0xb380)); clk_dm(IMX8MM_CLK_PWM2, - imx8m_clk_composite("pwm2", imx8mm_pwm2_sels, base + 0xb400)); + imx8m_clk_composite(dev, "pwm2", imx8mm_pwm2_sels, base + 0xb400)); clk_dm(IMX8MM_CLK_PWM3, - imx8m_clk_composite("pwm3", imx8mm_pwm3_sels, base + 0xb480)); + imx8m_clk_composite(dev, "pwm3", imx8mm_pwm3_sels, base + 0xb480)); clk_dm(IMX8MM_CLK_PWM4, - imx8m_clk_composite("pwm4", imx8mm_pwm4_sels, base + 0xb500)); + imx8m_clk_composite(dev, "pwm4", imx8mm_pwm4_sels, base + 0xb500)); clk_dm(IMX8MM_CLK_PWM1_ROOT, - imx_clk_gate4("pwm1_root_clk", "pwm1", base + 0x4280, 0)); + imx_clk_gate4(dev, "pwm1_root_clk", "pwm1", base + 0x4280, 0)); clk_dm(IMX8MM_CLK_PWM2_ROOT, - imx_clk_gate4("pwm2_root_clk", "pwm2", base + 0x4290, 0)); + imx_clk_gate4(dev, "pwm2_root_clk", "pwm2", base + 0x4290, 0)); clk_dm(IMX8MM_CLK_PWM3_ROOT, - imx_clk_gate4("pwm3_root_clk", "pwm3", base + 0x42a0, 0)); + imx_clk_gate4(dev, "pwm3_root_clk", "pwm3", base + 0x42a0, 0)); clk_dm(IMX8MM_CLK_PWM4_ROOT, - imx_clk_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0)); + imx_clk_gate4(dev, "pwm4_root_clk", "pwm4", base + 0x42b0, 0)); #endif #if CONFIG_IS_ENABLED(PCIE_DW_IMX) clk_dm(IMX8MM_CLK_PCIE1_ROOT, - imx_clk_gate4("pcie1_root_clk", "pcie1_ctrl", base + 0x4250, 0)); + imx_clk_gate4(dev, "pcie1_root_clk", "pcie1_ctrl", base + 0x4250, 0)); #endif #if CONFIG_IS_ENABLED(DM_SPI) clk_dm(IMX8MM_CLK_ECSPI1, - imx8m_clk_composite("ecspi1", imx8mm_ecspi1_sels, base + 0xb280)); + imx8m_clk_composite(dev, "ecspi1", imx8mm_ecspi1_sels, base + 0xb280)); clk_dm(IMX8MM_CLK_ECSPI2, - imx8m_clk_composite("ecspi2", imx8mm_ecspi2_sels, base + 0xb300)); + imx8m_clk_composite(dev, "ecspi2", imx8mm_ecspi2_sels, base + 0xb300)); clk_dm(IMX8MM_CLK_ECSPI3, - imx8m_clk_composite("ecspi3", imx8mm_ecspi3_sels, base + 0xc180)); + imx8m_clk_composite(dev, "ecspi3", imx8mm_ecspi3_sels, base + 0xc180)); clk_dm(IMX8MM_CLK_ECSPI1_ROOT, - imx_clk_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0)); + imx_clk_gate4(dev, "ecspi1_root_clk", "ecspi1", base + 0x4070, 0)); clk_dm(IMX8MM_CLK_ECSPI2_ROOT, - imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0)); + imx_clk_gate4(dev, "ecspi2_root_clk", "ecspi2", base + 0x4080, 0)); clk_dm(IMX8MM_CLK_ECSPI3_ROOT, - imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); + imx_clk_gate4(dev, "ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); #endif #if CONFIG_IS_ENABLED(NXP_FSPI) clk_dm(IMX8MM_CLK_QSPI, - imx8m_clk_composite("qspi", imx8mm_qspi_sels, base + 0xab80)); + imx8m_clk_composite(dev, "qspi", imx8mm_qspi_sels, base + 0xab80)); clk_dm(IMX8MM_CLK_QSPI_ROOT, - imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0)); + imx_clk_gate4(dev, "qspi_root_clk", "qspi", base + 0x42f0, 0)); #endif clk_dm(IMX8MM_CLK_ARM, - imx_clk_mux2_flags("arm_core", base + 0x9880, 24, 1, + imx_clk_mux2_flags(dev, "arm_core", base + 0x9880, 24, 1, imx8mm_arm_core_sels, ARRAY_SIZE(imx8mm_arm_core_sels), CLK_IS_CRITICAL)); diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c index be15ebd0e25..be5b7933a8d 100644 --- a/drivers/clk/imx/clk-imx8mn.c +++ b/drivers/clk/imx/clk-imx8mn.c @@ -16,7 +16,7 @@ static u32 share_count_nand; -static const char * const pll_ref_sels[] = { "clock-osc-24m", "dummy", "dummy", "dummy", }; +static const char * const pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", }; static const char * const dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", }; static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; static const char * const sys_pll1_bypass_sels[] = {"sys_pll1", "sys_pll1_ref_sel", }; @@ -25,117 +25,133 @@ static const char * const sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_se static const char * const imx8mn_arm_core_sels[] = {"arm_a53_src", "arm_pll_out", }; -static const char * const imx8mn_a53_sels[] = {"clock-osc-24m", "arm_pll_out", "sys_pll2_500m", +static const char * const imx8mn_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", "sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", }; -static const char * const imx8mn_ahb_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_800m", +static const char * const imx8mn_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m", "sys_pll1_400m", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", "video_pll_out", }; -static const char * const imx8mn_enet_axi_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", +static const char * const imx8mn_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_250m", "sys_pll2_200m", "audio_pll1_out", "video_pll_out", "sys_pll3_out", }; #ifndef CONFIG_XPL_BUILD -static const char * const imx8mn_enet_ref_sels[] = {"clock-osc-24m", "sys_pll2_125m", "sys_pll2_50m", +static const char * const imx8mn_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", "sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out", "video_pll_out", "clk_ext4", }; -static const char * const imx8mn_enet_timer_sels[] = {"clock-osc-24m", "sys_pll2_100m", "audio_pll1_out", +static const char * const imx8mn_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", "clk_ext3", "clk_ext4", "video_pll_out", }; -static const char * const imx8mn_enet_phy_sels[] = {"clock-osc-24m", "sys_pll2_50m", "sys_pll2_125m", +static const char * const imx8mn_enet_phy_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m", "sys_pll2_200m", "sys_pll2_500m", "audio_pll1_out", "video_pll_out", "audio_pll2_out", }; #endif -static const char * const imx8mn_nand_usdhc_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", +static const char * const imx8mn_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_200m", "sys_pll1_133m", "sys_pll3_out", "sys_pll2_250m", "audio_pll1_out", }; -static const char * const imx8mn_usb_bus_sels[] = {"clock-osc-24m", "sys_pll2_500m", "sys_pll1_800m", +static const char * const imx8mn_usb_bus_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext4", "audio_pll2_out", }; -static const char * const imx8mn_usdhc1_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mn_usdhc1_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; -static const char * const imx8mn_usdhc2_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mn_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; #if CONFIG_IS_ENABLED(DM_SPI) -static const char * const imx8mn_ecspi1_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mn_ecspi1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; -static const char * const imx8mn_ecspi2_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mn_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; -static const char * const imx8mn_ecspi3_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mn_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; #endif -static const char * const imx8mn_i2c1_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mn_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mn_i2c2_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mn_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mn_i2c3_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mn_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mn_i2c4_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mn_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll_out", "audio_pll2_out", "sys_pll1_133m", }; +static const char * const imx8mn_uart1_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext4", "audio_pll2_out", }; + +static const char * const imx8mn_uart2_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mn_uart3_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext4", "audio_pll2_out", }; + +static const char * const imx8mn_uart4_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext3", "audio_pll2_out", }; + #ifndef CONFIG_XPL_BUILD -static const char * const imx8mn_pwm1_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mn_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll_out", }; -static const char * const imx8mn_pwm2_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mn_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll_out", }; -static const char * const imx8mn_pwm3_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mn_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll_out", }; -static const char * const imx8mn_pwm4_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mn_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll_out", }; #endif -static const char * const imx8mn_wdog_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_160m", +static const char * const imx8mn_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m", "m7_alt_pll", "sys_pll2_125m", "sys_pll3_out", "sys_pll1_80m", "sys_pll2_166m", }; -static const char * const imx8mn_usdhc3_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mn_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_clk", "sys_pll1_100m", }; -static const char * const imx8mn_qspi_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll2_333m", +static const char * const imx8mn_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll2_333m", "sys_pll2_500m", "audio_pll2_out", "sys_pll1_266m", "sys_pll3_out", "sys_pll1_100m", }; -static const char * const imx8mn_nand_sels[] = {"clock-osc-24m", "sys_pll2_500m", "audio_pll1_out", +static const char * const imx8mn_nand_sels[] = {"osc_24m", "sys_pll2_500m", "audio_pll1_out", "sys_pll1_400m", "audio_pll2_out", "sys_pll3_out", "sys_pll2_250m", "video_pll_out", }; -static const char * const imx8mn_usb_core_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m", +static const char * const imx8mn_usb_core_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; -static const char * const imx8mn_usb_phy_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m", +static const char * const imx8mn_usb_phy_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; @@ -148,19 +164,19 @@ static int imx8mn_clk_probe(struct udevice *dev) base = (void *)ANATOP_BASE_ADDR; clk_dm(IMX8MN_DRAM_PLL_REF_SEL, - imx_clk_mux("dram_pll_ref_sel", base + 0x50, 0, 2, + imx_clk_mux(dev, "dram_pll_ref_sel", base + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MN_ARM_PLL_REF_SEL, - imx_clk_mux("arm_pll_ref_sel", base + 0x84, 0, 2, + imx_clk_mux(dev, "arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MN_SYS_PLL1_REF_SEL, - imx_clk_mux("sys_pll1_ref_sel", base + 0x94, 0, 2, + imx_clk_mux(dev, "sys_pll1_ref_sel", base + 0x94, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MN_SYS_PLL2_REF_SEL, - imx_clk_mux("sys_pll2_ref_sel", base + 0x104, 0, 2, + imx_clk_mux(dev, "sys_pll2_ref_sel", base + 0x104, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MN_SYS_PLL3_REF_SEL, - imx_clk_mux("sys_pll3_ref_sel", base + 0x114, 0, 2, + imx_clk_mux(dev, "sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MN_DRAM_PLL, @@ -181,86 +197,86 @@ static int imx8mn_clk_probe(struct udevice *dev) /* PLL bypass out */ clk_dm(IMX8MN_DRAM_PLL_BYPASS, - imx_clk_mux_flags("dram_pll_bypass", base + 0x50, 4, 1, + imx_clk_mux_flags(dev, "dram_pll_bypass", base + 0x50, 4, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MN_ARM_PLL_BYPASS, - imx_clk_mux_flags("arm_pll_bypass", base + 0x84, 4, 1, + imx_clk_mux_flags(dev, "arm_pll_bypass", base + 0x84, 4, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MN_SYS_PLL1_BYPASS, - imx_clk_mux_flags("sys_pll1_bypass", base + 0x94, 4, 1, + imx_clk_mux_flags(dev, "sys_pll1_bypass", base + 0x94, 4, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MN_SYS_PLL2_BYPASS, - imx_clk_mux_flags("sys_pll2_bypass", base + 0x104, 4, 1, + imx_clk_mux_flags(dev, "sys_pll2_bypass", base + 0x104, 4, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MN_SYS_PLL3_BYPASS, - imx_clk_mux_flags("sys_pll3_bypass", base + 0x114, 4, 1, + imx_clk_mux_flags(dev, "sys_pll3_bypass", base + 0x114, 4, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT)); /* PLL out gate */ clk_dm(IMX8MN_DRAM_PLL_OUT, - imx_clk_gate("dram_pll_out", "dram_pll_bypass", + imx_clk_gate(dev, "dram_pll_out", "dram_pll_bypass", base + 0x50, 13)); clk_dm(IMX8MN_ARM_PLL_OUT, - imx_clk_gate("arm_pll_out", "arm_pll_bypass", + imx_clk_gate(dev, "arm_pll_out", "arm_pll_bypass", base + 0x84, 11)); clk_dm(IMX8MN_SYS_PLL1_OUT, - imx_clk_gate("sys_pll1_out", "sys_pll1_bypass", + imx_clk_gate(dev, "sys_pll1_out", "sys_pll1_bypass", base + 0x94, 11)); clk_dm(IMX8MN_SYS_PLL2_OUT, - imx_clk_gate("sys_pll2_out", "sys_pll2_bypass", + imx_clk_gate(dev, "sys_pll2_out", "sys_pll2_bypass", base + 0x104, 11)); clk_dm(IMX8MN_SYS_PLL3_OUT, - imx_clk_gate("sys_pll3_out", "sys_pll3_bypass", + imx_clk_gate(dev, "sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11)); /* SYS PLL fixed output */ clk_dm(IMX8MN_SYS_PLL1_40M, - imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20)); + imx_clk_fixed_factor(dev, "sys_pll1_40m", "sys_pll1_out", 1, 20)); clk_dm(IMX8MN_SYS_PLL1_80M, - imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10)); + imx_clk_fixed_factor(dev, "sys_pll1_80m", "sys_pll1_out", 1, 10)); clk_dm(IMX8MN_SYS_PLL1_100M, - imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8)); + imx_clk_fixed_factor(dev, "sys_pll1_100m", "sys_pll1_out", 1, 8)); clk_dm(IMX8MN_SYS_PLL1_133M, - imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6)); + imx_clk_fixed_factor(dev, "sys_pll1_133m", "sys_pll1_out", 1, 6)); clk_dm(IMX8MN_SYS_PLL1_160M, - imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5)); + imx_clk_fixed_factor(dev, "sys_pll1_160m", "sys_pll1_out", 1, 5)); clk_dm(IMX8MN_SYS_PLL1_200M, - imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4)); + imx_clk_fixed_factor(dev, "sys_pll1_200m", "sys_pll1_out", 1, 4)); clk_dm(IMX8MN_SYS_PLL1_266M, - imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3)); + imx_clk_fixed_factor(dev, "sys_pll1_266m", "sys_pll1_out", 1, 3)); clk_dm(IMX8MN_SYS_PLL1_400M, - imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2)); + imx_clk_fixed_factor(dev, "sys_pll1_400m", "sys_pll1_out", 1, 2)); clk_dm(IMX8MN_SYS_PLL1_800M, - imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1)); + imx_clk_fixed_factor(dev, "sys_pll1_800m", "sys_pll1_out", 1, 1)); clk_dm(IMX8MN_SYS_PLL2_50M, - imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20)); + imx_clk_fixed_factor(dev, "sys_pll2_50m", "sys_pll2_out", 1, 20)); clk_dm(IMX8MN_SYS_PLL2_100M, - imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10)); + imx_clk_fixed_factor(dev, "sys_pll2_100m", "sys_pll2_out", 1, 10)); clk_dm(IMX8MN_SYS_PLL2_125M, - imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8)); + imx_clk_fixed_factor(dev, "sys_pll2_125m", "sys_pll2_out", 1, 8)); clk_dm(IMX8MN_SYS_PLL2_166M, - imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6)); + imx_clk_fixed_factor(dev, "sys_pll2_166m", "sys_pll2_out", 1, 6)); clk_dm(IMX8MN_SYS_PLL2_200M, - imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5)); + imx_clk_fixed_factor(dev, "sys_pll2_200m", "sys_pll2_out", 1, 5)); clk_dm(IMX8MN_SYS_PLL2_250M, - imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4)); + imx_clk_fixed_factor(dev, "sys_pll2_250m", "sys_pll2_out", 1, 4)); clk_dm(IMX8MN_SYS_PLL2_333M, - imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3)); + imx_clk_fixed_factor(dev, "sys_pll2_333m", "sys_pll2_out", 1, 3)); clk_dm(IMX8MN_SYS_PLL2_500M, - imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2)); + imx_clk_fixed_factor(dev, "sys_pll2_500m", "sys_pll2_out", 1, 2)); clk_dm(IMX8MN_SYS_PLL2_1000M, - imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1)); + imx_clk_fixed_factor(dev, "sys_pll2_1000m", "sys_pll2_out", 1, 1)); ret = clk_get_by_name(dev, "osc_24m", &osc_24m_clk); if (ret) @@ -272,141 +288,157 @@ static int imx8mn_clk_probe(struct udevice *dev) return -EINVAL; clk_dm(IMX8MN_CLK_A53_SRC, - imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, + imx_clk_mux2(dev, "arm_a53_src", base + 0x8000, 24, 3, imx8mn_a53_sels, ARRAY_SIZE(imx8mn_a53_sels))); clk_dm(IMX8MN_CLK_A53_CG, - imx_clk_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28)); + imx_clk_gate3(dev, "arm_a53_cg", "arm_a53_src", base + 0x8000, 28)); clk_dm(IMX8MN_CLK_A53_DIV, - imx_clk_divider2("arm_a53_div", "arm_a53_cg", + imx_clk_divider2(dev, "arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3)); clk_dm(IMX8MN_CLK_AHB, - imx8m_clk_composite_critical("ahb", imx8mn_ahb_sels, + imx8m_clk_composite_critical(dev, "ahb", imx8mn_ahb_sels, base + 0x9000)); clk_dm(IMX8MN_CLK_IPG_ROOT, - imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1)); + imx_clk_divider2(dev, "ipg_root", "ahb", base + 0x9080, 0, 1)); clk_dm(IMX8MN_CLK_ENET_AXI, - imx8m_clk_composite("enet_axi", imx8mn_enet_axi_sels, + imx8m_clk_composite(dev, "enet_axi", imx8mn_enet_axi_sels, base + 0x8880)); clk_dm(IMX8MN_CLK_NAND_USDHC_BUS, - imx8m_clk_composite_critical("nand_usdhc_bus", + imx8m_clk_composite_critical(dev, "nand_usdhc_bus", imx8mn_nand_usdhc_sels, base + 0x8900)); clk_dm(IMX8MN_CLK_USB_BUS, - imx8m_clk_composite("usb_bus", imx8mn_usb_bus_sels, base + 0x8b80)); + imx8m_clk_composite(dev, "usb_bus", imx8mn_usb_bus_sels, base + 0x8b80)); /* IP */ clk_dm(IMX8MN_CLK_USDHC1, - imx8m_clk_composite("usdhc1", imx8mn_usdhc1_sels, + imx8m_clk_composite(dev, "usdhc1", imx8mn_usdhc1_sels, base + 0xac00)); clk_dm(IMX8MN_CLK_USDHC2, - imx8m_clk_composite("usdhc2", imx8mn_usdhc2_sels, + imx8m_clk_composite(dev, "usdhc2", imx8mn_usdhc2_sels, base + 0xac80)); clk_dm(IMX8MN_CLK_I2C1, - imx8m_clk_composite("i2c1", imx8mn_i2c1_sels, base + 0xad00)); + imx8m_clk_composite(dev, "i2c1", imx8mn_i2c1_sels, base + 0xad00)); clk_dm(IMX8MN_CLK_I2C2, - imx8m_clk_composite("i2c2", imx8mn_i2c2_sels, base + 0xad80)); + imx8m_clk_composite(dev, "i2c2", imx8mn_i2c2_sels, base + 0xad80)); clk_dm(IMX8MN_CLK_I2C3, - imx8m_clk_composite("i2c3", imx8mn_i2c3_sels, base + 0xae00)); + imx8m_clk_composite(dev, "i2c3", imx8mn_i2c3_sels, base + 0xae00)); clk_dm(IMX8MN_CLK_I2C4, - imx8m_clk_composite("i2c4", imx8mn_i2c4_sels, base + 0xae80)); + imx8m_clk_composite(dev, "i2c4", imx8mn_i2c4_sels, base + 0xae80)); + clk_dm(IMX8MN_CLK_UART1, + imx8m_clk_composite(dev, "uart1", imx8mn_uart1_sels, base + 0xaf00)); + clk_dm(IMX8MN_CLK_UART2, + imx8m_clk_composite(dev, "uart2", imx8mn_uart2_sels, base + 0xaf80)); + clk_dm(IMX8MN_CLK_UART3, + imx8m_clk_composite(dev, "uart3", imx8mn_uart3_sels, base + 0xb000)); + clk_dm(IMX8MN_CLK_UART4, + imx8m_clk_composite(dev, "uart4", imx8mn_uart4_sels, base + 0xb080)); clk_dm(IMX8MN_CLK_WDOG, - imx8m_clk_composite("wdog", imx8mn_wdog_sels, base + 0xb900)); + imx8m_clk_composite(dev, "wdog", imx8mn_wdog_sels, base + 0xb900)); clk_dm(IMX8MN_CLK_USDHC3, - imx8m_clk_composite("usdhc3", imx8mn_usdhc3_sels, + imx8m_clk_composite(dev, "usdhc3", imx8mn_usdhc3_sels, base + 0xbc80)); clk_dm(IMX8MN_CLK_NAND, - imx8m_clk_composite("nand", imx8mn_nand_sels, base + 0xab00)); + imx8m_clk_composite(dev, "nand", imx8mn_nand_sels, base + 0xab00)); clk_dm(IMX8MN_CLK_QSPI, - imx8m_clk_composite("qspi", imx8mn_qspi_sels, base + 0xab80)); + imx8m_clk_composite(dev, "qspi", imx8mn_qspi_sels, base + 0xab80)); clk_dm(IMX8MN_CLK_USB_CORE_REF, - imx8m_clk_composite("usb_core_ref", imx8mn_usb_core_sels, base + 0xb100)); + imx8m_clk_composite(dev, "usb_core_ref", imx8mn_usb_core_sels, base + 0xb100)); clk_dm(IMX8MN_CLK_USB_PHY_REF, - imx8m_clk_composite("usb_phy_ref", imx8mn_usb_phy_sels, base + 0xb180)); + imx8m_clk_composite(dev, "usb_phy_ref", imx8mn_usb_phy_sels, base + 0xb180)); clk_dm(IMX8MN_CLK_I2C1_ROOT, - imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0)); + imx_clk_gate4(dev, "i2c1_root_clk", "i2c1", base + 0x4170, 0)); clk_dm(IMX8MN_CLK_I2C2_ROOT, - imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0)); + imx_clk_gate4(dev, "i2c2_root_clk", "i2c2", base + 0x4180, 0)); clk_dm(IMX8MN_CLK_I2C3_ROOT, - imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0)); + imx_clk_gate4(dev, "i2c3_root_clk", "i2c3", base + 0x4190, 0)); clk_dm(IMX8MN_CLK_I2C4_ROOT, - imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0)); + imx_clk_gate4(dev, "i2c4_root_clk", "i2c4", base + 0x41a0, 0)); clk_dm(IMX8MN_CLK_OCOTP_ROOT, - imx_clk_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0)); + imx_clk_gate4(dev, "ocotp_root_clk", "ipg_root", base + 0x4220, 0)); clk_dm(IMX8MN_CLK_USDHC1_ROOT, - imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0)); + imx_clk_gate4(dev, "usdhc1_root_clk", "usdhc1", base + 0x4510, 0)); clk_dm(IMX8MN_CLK_USDHC2_ROOT, - imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0)); + imx_clk_gate4(dev, "usdhc2_root_clk", "usdhc2", base + 0x4520, 0)); clk_dm(IMX8MN_CLK_WDOG1_ROOT, - imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0)); + imx_clk_gate4(dev, "wdog1_root_clk", "wdog", base + 0x4530, 0)); clk_dm(IMX8MN_CLK_WDOG2_ROOT, - imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0)); + imx_clk_gate4(dev, "wdog2_root_clk", "wdog", base + 0x4540, 0)); clk_dm(IMX8MN_CLK_WDOG3_ROOT, - imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0)); + imx_clk_gate4(dev, "wdog3_root_clk", "wdog", base + 0x4550, 0)); clk_dm(IMX8MN_CLK_USDHC3_ROOT, - imx_clk_gate4("usdhc3_root_clk", "usdhc3", base + 0x45e0, 0)); + imx_clk_gate4(dev, "usdhc3_root_clk", "usdhc3", base + 0x45e0, 0)); clk_dm(IMX8MN_CLK_QSPI_ROOT, - imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0)); + imx_clk_gate4(dev, "qspi_root_clk", "qspi", base + 0x42f0, 0)); clk_dm(IMX8MN_CLK_NAND_ROOT, - imx_clk_gate2_shared2("nand_root_clk", "nand", base + 0x4300, 0, &share_count_nand)); + imx_clk_gate2_shared2(dev, "nand_root_clk", "nand", base + 0x4300, 0, &share_count_nand)); clk_dm(IMX8MN_CLK_NAND_USDHC_BUS_RAWNAND_CLK, - imx_clk_gate2_shared2("nand_usdhc_rawnand_clk", + imx_clk_gate2_shared2(dev, "nand_usdhc_rawnand_clk", "nand_usdhc_bus", base + 0x4300, 0, &share_count_nand)); + clk_dm(IMX8MN_CLK_UART1_ROOT, + imx_clk_gate4(dev, "uart1_root_clk", "uart1", base + 0x4490, 0)); + clk_dm(IMX8MN_CLK_UART2_ROOT, + imx_clk_gate4(dev, "uart2_root_clk", "uart2", base + 0x44a0, 0)); + clk_dm(IMX8MN_CLK_UART3_ROOT, + imx_clk_gate4(dev, "uart3_root_clk", "uart3", base + 0x44b0, 0)); + clk_dm(IMX8MN_CLK_UART4_ROOT, + imx_clk_gate4(dev, "uart4_root_clk", "uart4", base + 0x44c0, 0)); clk_dm(IMX8MN_CLK_USB1_CTRL_ROOT, - imx_clk_gate4("usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0)); + imx_clk_gate4(dev, "usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0)); /* clks not needed in SPL stage */ #ifndef CONFIG_XPL_BUILD clk_dm(IMX8MN_CLK_ENET_REF, - imx8m_clk_composite("enet_ref", imx8mn_enet_ref_sels, + imx8m_clk_composite(dev, "enet_ref", imx8mn_enet_ref_sels, base + 0xa980)); clk_dm(IMX8MN_CLK_ENET_TIMER, - imx8m_clk_composite("enet_timer", imx8mn_enet_timer_sels, + imx8m_clk_composite(dev, "enet_timer", imx8mn_enet_timer_sels, base + 0xaa00)); clk_dm(IMX8MN_CLK_ENET_PHY_REF, - imx8m_clk_composite("enet_phy", imx8mn_enet_phy_sels, + imx8m_clk_composite(dev, "enet_phy", imx8mn_enet_phy_sels, base + 0xaa80)); clk_dm(IMX8MN_CLK_ENET1_ROOT, - imx_clk_gate4("enet1_root_clk", "enet_axi", + imx_clk_gate4(dev, "enet1_root_clk", "enet_axi", base + 0x40a0, 0)); clk_dm(IMX8MN_CLK_PWM1, - imx8m_clk_composite("pwm1", imx8mn_pwm1_sels, base + 0xb380)); + imx8m_clk_composite(dev, "pwm1", imx8mn_pwm1_sels, base + 0xb380)); clk_dm(IMX8MN_CLK_PWM2, - imx8m_clk_composite("pwm2", imx8mn_pwm2_sels, base + 0xb400)); + imx8m_clk_composite(dev, "pwm2", imx8mn_pwm2_sels, base + 0xb400)); clk_dm(IMX8MN_CLK_PWM3, - imx8m_clk_composite("pwm3", imx8mn_pwm3_sels, base + 0xb480)); + imx8m_clk_composite(dev, "pwm3", imx8mn_pwm3_sels, base + 0xb480)); clk_dm(IMX8MN_CLK_PWM4, - imx8m_clk_composite("pwm4", imx8mn_pwm4_sels, base + 0xb500)); + imx8m_clk_composite(dev, "pwm4", imx8mn_pwm4_sels, base + 0xb500)); clk_dm(IMX8MN_CLK_PWM1_ROOT, - imx_clk_gate4("pwm1_root_clk", "pwm1", base + 0x4280, 0)); + imx_clk_gate4(dev, "pwm1_root_clk", "pwm1", base + 0x4280, 0)); clk_dm(IMX8MN_CLK_PWM2_ROOT, - imx_clk_gate4("pwm2_root_clk", "pwm2", base + 0x4290, 0)); + imx_clk_gate4(dev, "pwm2_root_clk", "pwm2", base + 0x4290, 0)); clk_dm(IMX8MN_CLK_PWM3_ROOT, - imx_clk_gate4("pwm3_root_clk", "pwm3", base + 0x42a0, 0)); + imx_clk_gate4(dev, "pwm3_root_clk", "pwm3", base + 0x42a0, 0)); clk_dm(IMX8MN_CLK_PWM4_ROOT, - imx_clk_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0)); + imx_clk_gate4(dev, "pwm4_root_clk", "pwm4", base + 0x42b0, 0)); #endif #if CONFIG_IS_ENABLED(DM_SPI) clk_dm(IMX8MN_CLK_ECSPI1, - imx8m_clk_composite("ecspi1", imx8mn_ecspi1_sels, base + 0xb280)); + imx8m_clk_composite(dev, "ecspi1", imx8mn_ecspi1_sels, base + 0xb280)); clk_dm(IMX8MN_CLK_ECSPI2, - imx8m_clk_composite("ecspi2", imx8mn_ecspi2_sels, base + 0xb300)); + imx8m_clk_composite(dev, "ecspi2", imx8mn_ecspi2_sels, base + 0xb300)); clk_dm(IMX8MN_CLK_ECSPI3, - imx8m_clk_composite("ecspi3", imx8mn_ecspi3_sels, base + 0xc180)); + imx8m_clk_composite(dev, "ecspi3", imx8mn_ecspi3_sels, base + 0xc180)); clk_dm(IMX8MN_CLK_ECSPI1_ROOT, - imx_clk_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0)); + imx_clk_gate4(dev, "ecspi1_root_clk", "ecspi1", base + 0x4070, 0)); clk_dm(IMX8MN_CLK_ECSPI2_ROOT, - imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0)); + imx_clk_gate4(dev, "ecspi2_root_clk", "ecspi2", base + 0x4080, 0)); clk_dm(IMX8MN_CLK_ECSPI3_ROOT, - imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); + imx_clk_gate4(dev, "ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); #endif clk_dm(IMX8MN_CLK_ARM, - imx_clk_mux2_flags("arm_core", base + 0x9880, 24, 1, + imx_clk_mux2_flags(dev, "arm_core", base + 0x9880, 24, 1, imx8mn_arm_core_sels, ARRAY_SIZE(imx8mn_arm_core_sels), CLK_IS_CRITICAL)); diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c index 1d04090ca00..bad579f8d5e 100644 --- a/drivers/clk/imx/clk-imx8mp.c +++ b/drivers/clk/imx/clk-imx8mp.c @@ -14,7 +14,7 @@ #include "clk.h" -static const char * const pll_ref_sels[] = { "clock-osc-24m", "dummy", "dummy", "dummy", }; +static const char * const pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", }; static const char * const dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", }; static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; static const char * const sys_pll1_bypass_sels[] = {"sys_pll1", "sys_pll1_ref_sel", }; @@ -23,167 +23,167 @@ static const char * const sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_se static const char * const imx8mp_arm_core_sels[] = {"arm_a53_src", "arm_pll_out", }; -static const char * const imx8mp_a53_sels[] = {"clock-osc-24m", "arm_pll_out", "sys_pll2_500m", +static const char * const imx8mp_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", "sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", }; -static const char * const imx8mp_hsio_axi_sels[] = {"clock-osc-24m", "sys_pll2_500m", "sys_pll1_800m", +static const char * const imx8mp_hsio_axi_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext4", "audio_pll2_out", }; -static const char * const imx8mp_main_axi_sels[] = {"clock-osc-24m", "sys_pll2_333m", "sys_pll1_800m", +static const char * const imx8mp_main_axi_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll1_800m", "sys_pll2_250m", "sys_pll2_1000m", "audio_pll1_out", "video_pll1_out", "sys_pll1_100m",}; -static const char * const imx8mp_enet_axi_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", +static const char * const imx8mp_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_250m", "sys_pll2_200m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", }; -static const char * const imx8mp_nand_usdhc_sels[] = {"clock-osc-24m", "sys_pll1_266m", "sys_pll1_800m", +static const char * const imx8mp_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_200m", "sys_pll1_133m", "sys_pll3_out", "sys_pll2_250m", "audio_pll1_out", }; -static const char * const imx8mp_noc_sels[] = {"clock-osc-24m", "sys_pll1_800m", "sys_pll3_out", +static const char * const imx8mp_noc_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_1000m", "sys_pll2_500m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; -static const char * const imx8mp_noc_io_sels[] = {"clock-osc-24m", "sys_pll1_800m", "sys_pll3_out", +static const char * const imx8mp_noc_io_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_1000m", "sys_pll2_500m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; -static const char * const imx8mp_ahb_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_800m", +static const char * const imx8mp_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m", "sys_pll1_400m", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", }; -static const char * const imx8mp_dram_alt_sels[] = {"clock-osc-24m", "sys_pll1_800m", "sys_pll1_100m", +static const char * const imx8mp_dram_alt_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll1_100m", "sys_pll2_500m", "sys_pll2_1000m", "sys_pll3_out", "audio_pll1_out", "sys_pll1_266m", }; -static const char * const imx8mp_dram_apb_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mp_dram_apb_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; -static const char * const imx8mp_pcie_aux_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll2_50m", +static const char * const imx8mp_pcie_aux_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m", "sys_pll3_out", "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_160m", "sys_pll1_200m", }; -static const char * const imx8mp_i2c5_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mp_i2c5_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mp_i2c6_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mp_i2c6_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mp_enet_qos_sels[] = {"clock-osc-24m", "sys_pll2_125m", "sys_pll2_50m", +static const char * const imx8mp_enet_qos_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", "sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out", "video_pll1_out", "clk_ext4", }; -static const char * const imx8mp_enet_qos_timer_sels[] = {"clock-osc-24m", "sys_pll2_100m", "audio_pll1_out", +static const char * const imx8mp_enet_qos_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", "clk_ext3", "clk_ext4", "video_pll1_out", }; -static const char * const imx8mp_usdhc1_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mp_usdhc1_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; -static const char * const imx8mp_usdhc2_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mp_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; -static const char * const imx8mp_i2c1_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mp_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mp_i2c2_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mp_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mp_i2c3_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mp_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mp_i2c4_sels[] = {"clock-osc-24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mp_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; -static const char * const imx8mp_uart1_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", +static const char * const imx8mp_uart1_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext4", "audio_pll2_out", }; -static const char * const imx8mp_uart2_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", +static const char * const imx8mp_uart2_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext3", "audio_pll2_out", }; -static const char * const imx8mp_uart3_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", +static const char * const imx8mp_uart3_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext4", "audio_pll2_out", }; -static const char * const imx8mp_uart4_sels[] = {"clock-osc-24m", "sys_pll1_80m", "sys_pll2_200m", +static const char * const imx8mp_uart4_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", "sys_pll3_out", "clk_ext2", "clk_ext3", "audio_pll2_out", }; -static const char * const imx8mp_usb_core_ref_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m", +static const char * const imx8mp_usb_core_ref_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; -static const char * const imx8mp_usb_phy_ref_sels[] = {"clock-osc-24m", "sys_pll1_100m", "sys_pll1_40m", +static const char * const imx8mp_usb_phy_ref_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; -static const char * const imx8mp_gic_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mp_gic_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll2_100m", "sys_pll1_800m", "sys_pll2_500m", "clk_ext4", "audio_pll2_out" }; -static const char * const imx8mp_pwm1_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mp_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", }; -static const char * const imx8mp_pwm2_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mp_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", }; -static const char * const imx8mp_pwm3_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mp_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll1_out", }; -static const char * const imx8mp_pwm4_sels[] = {"clock-osc-24m", "sys_pll2_100m", "sys_pll1_160m", +static const char * const imx8mp_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll1_out", }; -static const char * const imx8mp_ecspi1_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mp_ecspi1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; -static const char * const imx8mp_ecspi2_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mp_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; -static const char * const imx8mp_ecspi3_sels[] = {"clock-osc-24m", "sys_pll2_200m", "sys_pll1_40m", +static const char * const imx8mp_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; -static const char * const imx8mp_wdog_sels[] = {"clock-osc-24m", "sys_pll1_133m", "sys_pll1_160m", +static const char * const imx8mp_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m", "vpu_pll_out", "sys_pll2_125m", "sys_pll3_out", "sys_pll1_80m", "sys_pll2_166m" }; -static const char * const imx8mp_qspi_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll2_333m", +static const char * const imx8mp_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll2_333m", "sys_pll2_500m", "audio_pll2_out", "sys_pll1_266m", "sys_pll3_out", "sys_pll1_100m", }; -static const char * const imx8mp_usdhc3_sels[] = {"clock-osc-24m", "sys_pll1_400m", "sys_pll1_800m", +static const char * const imx8mp_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; -static const char * const imx8mp_enet_ref_sels[] = {"clock-osc-24m", "sys_pll2_125m", "sys_pll2_50m", +static const char * const imx8mp_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", "sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out", "video_pll1_out", "clk_ext4", }; -static const char * const imx8mp_enet_timer_sels[] = {"clock-osc-24m", "sys_pll2_100m", "audio_pll1_out", +static const char * const imx8mp_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", "clk_ext3", "clk_ext4", "video_pll1_out", }; -static const char * const imx8mp_enet_phy_ref_sels[] = {"clock-osc-24m", "sys_pll2_50m", "sys_pll2_125m", +static const char * const imx8mp_enet_phy_ref_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m", "sys_pll2_200m", "sys_pll2_500m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; @@ -199,11 +199,11 @@ static int imx8mp_clk_probe(struct udevice *dev) clk_dm(IMX8MP_CLK_DUMMY, clk_register_fixed_rate(NULL, "dummy", 0)); - clk_dm(IMX8MP_DRAM_PLL_REF_SEL, imx_clk_mux("dram_pll_ref_sel", base + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); - clk_dm(IMX8MP_ARM_PLL_REF_SEL, imx_clk_mux("arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); - clk_dm(IMX8MP_SYS_PLL1_REF_SEL, imx_clk_mux("sys_pll1_ref_sel", base + 0x94, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); - clk_dm(IMX8MP_SYS_PLL2_REF_SEL, imx_clk_mux("sys_pll2_ref_sel", base + 0x104, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); - clk_dm(IMX8MP_SYS_PLL3_REF_SEL, imx_clk_mux("sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); + clk_dm(IMX8MP_DRAM_PLL_REF_SEL, imx_clk_mux(dev, "dram_pll_ref_sel", base + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); + clk_dm(IMX8MP_ARM_PLL_REF_SEL, imx_clk_mux(dev, "arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); + clk_dm(IMX8MP_SYS_PLL1_REF_SEL, imx_clk_mux(dev, "sys_pll1_ref_sel", base + 0x94, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); + clk_dm(IMX8MP_SYS_PLL2_REF_SEL, imx_clk_mux(dev, "sys_pll2_ref_sel", base + 0x104, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); + clk_dm(IMX8MP_SYS_PLL3_REF_SEL, imx_clk_mux(dev, "sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MP_DRAM_PLL, imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx_1443x_dram_pll)); @@ -216,37 +216,37 @@ static int imx8mp_clk_probe(struct udevice *dev) clk_dm(IMX8MP_SYS_PLL3, imx_clk_pll14xx("sys_pll3", "sys_pll3_ref_sel", base + 0x114, &imx_1416x_pll)); - clk_dm(IMX8MP_DRAM_PLL_BYPASS, imx_clk_mux_flags("dram_pll_bypass", base + 0x50, 4, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT)); - clk_dm(IMX8MP_ARM_PLL_BYPASS, imx_clk_mux_flags("arm_pll_bypass", base + 0x84, 4, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT)); - clk_dm(IMX8MP_SYS_PLL1_BYPASS, imx_clk_mux_flags("sys_pll1_bypass", base + 0x94, 4, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT)); - clk_dm(IMX8MP_SYS_PLL2_BYPASS, imx_clk_mux_flags("sys_pll2_bypass", base + 0x104, 4, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT)); - clk_dm(IMX8MP_SYS_PLL3_BYPASS, imx_clk_mux_flags("sys_pll3_bypass", base + 0x114, 4, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT)); - - clk_dm(IMX8MP_DRAM_PLL_OUT, imx_clk_gate("dram_pll_out", "dram_pll_bypass", base + 0x50, 13)); - clk_dm(IMX8MP_ARM_PLL_OUT, imx_clk_gate("arm_pll_out", "arm_pll_bypass", base + 0x84, 11)); - clk_dm(IMX8MP_SYS_PLL1_OUT, imx_clk_gate("sys_pll1_out", "sys_pll1_bypass", base + 0x94, 11)); - clk_dm(IMX8MP_SYS_PLL2_OUT, imx_clk_gate("sys_pll2_out", "sys_pll2_bypass", base + 0x104, 11)); - clk_dm(IMX8MP_SYS_PLL3_OUT, imx_clk_gate("sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11)); - - clk_dm(IMX8MP_SYS_PLL1_40M, imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20)); - clk_dm(IMX8MP_SYS_PLL1_80M, imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10)); - clk_dm(IMX8MP_SYS_PLL1_100M, imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8)); - clk_dm(IMX8MP_SYS_PLL1_133M, imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6)); - clk_dm(IMX8MP_SYS_PLL1_160M, imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5)); - clk_dm(IMX8MP_SYS_PLL1_200M, imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4)); - clk_dm(IMX8MP_SYS_PLL1_266M, imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3)); - clk_dm(IMX8MP_SYS_PLL1_400M, imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2)); - clk_dm(IMX8MP_SYS_PLL1_800M, imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1)); - - clk_dm(IMX8MP_SYS_PLL2_50M, imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20)); - clk_dm(IMX8MP_SYS_PLL2_100M, imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10)); - clk_dm(IMX8MP_SYS_PLL2_125M, imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8)); - clk_dm(IMX8MP_SYS_PLL2_166M, imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6)); - clk_dm(IMX8MP_SYS_PLL2_200M, imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5)); - clk_dm(IMX8MP_SYS_PLL2_250M, imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4)); - clk_dm(IMX8MP_SYS_PLL2_333M, imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3)); - clk_dm(IMX8MP_SYS_PLL2_500M, imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2)); - clk_dm(IMX8MP_SYS_PLL2_1000M, imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1)); + clk_dm(IMX8MP_DRAM_PLL_BYPASS, imx_clk_mux_flags(dev, "dram_pll_bypass", base + 0x50, 4, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT)); + clk_dm(IMX8MP_ARM_PLL_BYPASS, imx_clk_mux_flags(dev, "arm_pll_bypass", base + 0x84, 4, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT)); + clk_dm(IMX8MP_SYS_PLL1_BYPASS, imx_clk_mux_flags(dev, "sys_pll1_bypass", base + 0x94, 4, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT)); + clk_dm(IMX8MP_SYS_PLL2_BYPASS, imx_clk_mux_flags(dev, "sys_pll2_bypass", base + 0x104, 4, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT)); + clk_dm(IMX8MP_SYS_PLL3_BYPASS, imx_clk_mux_flags(dev, "sys_pll3_bypass", base + 0x114, 4, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT)); + + clk_dm(IMX8MP_DRAM_PLL_OUT, imx_clk_gate(dev, "dram_pll_out", "dram_pll_bypass", base + 0x50, 13)); + clk_dm(IMX8MP_ARM_PLL_OUT, imx_clk_gate(dev, "arm_pll_out", "arm_pll_bypass", base + 0x84, 11)); + clk_dm(IMX8MP_SYS_PLL1_OUT, imx_clk_gate(dev, "sys_pll1_out", "sys_pll1_bypass", base + 0x94, 11)); + clk_dm(IMX8MP_SYS_PLL2_OUT, imx_clk_gate(dev, "sys_pll2_out", "sys_pll2_bypass", base + 0x104, 11)); + clk_dm(IMX8MP_SYS_PLL3_OUT, imx_clk_gate(dev, "sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11)); + + clk_dm(IMX8MP_SYS_PLL1_40M, imx_clk_fixed_factor(dev, "sys_pll1_40m", "sys_pll1_out", 1, 20)); + clk_dm(IMX8MP_SYS_PLL1_80M, imx_clk_fixed_factor(dev, "sys_pll1_80m", "sys_pll1_out", 1, 10)); + clk_dm(IMX8MP_SYS_PLL1_100M, imx_clk_fixed_factor(dev, "sys_pll1_100m", "sys_pll1_out", 1, 8)); + clk_dm(IMX8MP_SYS_PLL1_133M, imx_clk_fixed_factor(dev, "sys_pll1_133m", "sys_pll1_out", 1, 6)); + clk_dm(IMX8MP_SYS_PLL1_160M, imx_clk_fixed_factor(dev, "sys_pll1_160m", "sys_pll1_out", 1, 5)); + clk_dm(IMX8MP_SYS_PLL1_200M, imx_clk_fixed_factor(dev, "sys_pll1_200m", "sys_pll1_out", 1, 4)); + clk_dm(IMX8MP_SYS_PLL1_266M, imx_clk_fixed_factor(dev, "sys_pll1_266m", "sys_pll1_out", 1, 3)); + clk_dm(IMX8MP_SYS_PLL1_400M, imx_clk_fixed_factor(dev, "sys_pll1_400m", "sys_pll1_out", 1, 2)); + clk_dm(IMX8MP_SYS_PLL1_800M, imx_clk_fixed_factor(dev, "sys_pll1_800m", "sys_pll1_out", 1, 1)); + + clk_dm(IMX8MP_SYS_PLL2_50M, imx_clk_fixed_factor(dev, "sys_pll2_50m", "sys_pll2_out", 1, 20)); + clk_dm(IMX8MP_SYS_PLL2_100M, imx_clk_fixed_factor(dev, "sys_pll2_100m", "sys_pll2_out", 1, 10)); + clk_dm(IMX8MP_SYS_PLL2_125M, imx_clk_fixed_factor(dev, "sys_pll2_125m", "sys_pll2_out", 1, 8)); + clk_dm(IMX8MP_SYS_PLL2_166M, imx_clk_fixed_factor(dev, "sys_pll2_166m", "sys_pll2_out", 1, 6)); + clk_dm(IMX8MP_SYS_PLL2_200M, imx_clk_fixed_factor(dev, "sys_pll2_200m", "sys_pll2_out", 1, 5)); + clk_dm(IMX8MP_SYS_PLL2_250M, imx_clk_fixed_factor(dev, "sys_pll2_250m", "sys_pll2_out", 1, 4)); + clk_dm(IMX8MP_SYS_PLL2_333M, imx_clk_fixed_factor(dev, "sys_pll2_333m", "sys_pll2_out", 1, 3)); + clk_dm(IMX8MP_SYS_PLL2_500M, imx_clk_fixed_factor(dev, "sys_pll2_500m", "sys_pll2_out", 1, 2)); + clk_dm(IMX8MP_SYS_PLL2_1000M, imx_clk_fixed_factor(dev, "sys_pll2_1000m", "sys_pll2_out", 1, 1)); ret = clk_get_by_name(dev, "osc_24m", &osc_24m_clk); if (ret) @@ -262,104 +262,104 @@ static int imx8mp_clk_probe(struct udevice *dev) if (!base) return -EINVAL; - clk_dm(IMX8MP_CLK_A53_SRC, imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mp_a53_sels, ARRAY_SIZE(imx8mp_a53_sels))); - clk_dm(IMX8MP_CLK_A53_CG, imx_clk_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28)); - clk_dm(IMX8MP_CLK_A53_DIV, imx_clk_divider2("arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3)); - - clk_dm(IMX8MP_CLK_HSIO_AXI, imx8m_clk_composite("hsio_axi", imx8mp_hsio_axi_sels, base + 0x8380)); - clk_dm(IMX8MP_CLK_MAIN_AXI, imx8m_clk_composite_critical("main_axi", imx8mp_main_axi_sels, base + 0x8800)); - clk_dm(IMX8MP_CLK_ENET_AXI, imx8m_clk_composite_critical("enet_axi", imx8mp_enet_axi_sels, base + 0x8880)); - clk_dm(IMX8MP_CLK_NAND_USDHC_BUS, imx8m_clk_composite_critical("nand_usdhc_bus", imx8mp_nand_usdhc_sels, base + 0x8900)); - clk_dm(IMX8MP_CLK_NOC, imx8m_clk_composite_critical("noc", imx8mp_noc_sels, base + 0x8d00)); - clk_dm(IMX8MP_CLK_NOC_IO, imx8m_clk_composite_critical("noc_io", imx8mp_noc_io_sels, base + 0x8d80)); - - clk_dm(IMX8MP_CLK_AHB, imx8m_clk_composite_critical("ahb_root", imx8mp_ahb_sels, base + 0x9000)); - - clk_dm(IMX8MP_CLK_IPG_ROOT, imx_clk_divider2("ipg_root", "ahb_root", base + 0x9080, 0, 1)); - - clk_dm(IMX8MP_CLK_DRAM_ALT, imx8m_clk_composite("dram_alt", imx8mp_dram_alt_sels, base + 0xa000)); - clk_dm(IMX8MP_CLK_DRAM_APB, imx8m_clk_composite_critical("dram_apb", imx8mp_dram_apb_sels, base + 0xa080)); - clk_dm(IMX8MP_CLK_PCIE_AUX, imx8m_clk_composite("pcie_aux", imx8mp_pcie_aux_sels, base + 0xa400)); - clk_dm(IMX8MP_CLK_I2C5, imx8m_clk_composite("i2c5", imx8mp_i2c5_sels, base + 0xa480)); - clk_dm(IMX8MP_CLK_I2C6, imx8m_clk_composite("i2c6", imx8mp_i2c6_sels, base + 0xa500)); - clk_dm(IMX8MP_CLK_ENET_QOS, imx8m_clk_composite("enet_qos", imx8mp_enet_qos_sels, base + 0xa880)); - clk_dm(IMX8MP_CLK_ENET_QOS_TIMER, imx8m_clk_composite("enet_qos_timer", imx8mp_enet_qos_timer_sels, base + 0xa900)); - clk_dm(IMX8MP_CLK_ENET_REF, imx8m_clk_composite("enet_ref", imx8mp_enet_ref_sels, base + 0xa980)); - clk_dm(IMX8MP_CLK_ENET_TIMER, imx8m_clk_composite("enet_timer", imx8mp_enet_timer_sels, base + 0xaa00)); - clk_dm(IMX8MP_CLK_ENET_PHY_REF, imx8m_clk_composite("enet_phy_ref", imx8mp_enet_phy_ref_sels, base + 0xaa80)); - clk_dm(IMX8MP_CLK_QSPI, imx8m_clk_composite("qspi", imx8mp_qspi_sels, base + 0xab80)); - clk_dm(IMX8MP_CLK_USDHC1, imx8m_clk_composite("usdhc1", imx8mp_usdhc1_sels, base + 0xac00)); - clk_dm(IMX8MP_CLK_USDHC2, imx8m_clk_composite("usdhc2", imx8mp_usdhc2_sels, base + 0xac80)); - clk_dm(IMX8MP_CLK_I2C1, imx8m_clk_composite("i2c1", imx8mp_i2c1_sels, base + 0xad00)); - clk_dm(IMX8MP_CLK_I2C2, imx8m_clk_composite("i2c2", imx8mp_i2c2_sels, base + 0xad80)); - clk_dm(IMX8MP_CLK_I2C3, imx8m_clk_composite("i2c3", imx8mp_i2c3_sels, base + 0xae00)); - clk_dm(IMX8MP_CLK_I2C4, imx8m_clk_composite("i2c4", imx8mp_i2c4_sels, base + 0xae80)); - - clk_dm(IMX8MP_CLK_UART1, imx8m_clk_composite("uart1", imx8mp_uart1_sels, base + 0xaf00)); - clk_dm(IMX8MP_CLK_UART2, imx8m_clk_composite("uart2", imx8mp_uart2_sels, base + 0xaf80)); - clk_dm(IMX8MP_CLK_UART3, imx8m_clk_composite("uart3", imx8mp_uart3_sels, base + 0xb000)); - clk_dm(IMX8MP_CLK_UART4, imx8m_clk_composite("uart4", imx8mp_uart4_sels, base + 0xb080)); - clk_dm(IMX8MP_CLK_USB_CORE_REF, imx8m_clk_composite("usb_core_ref", imx8mp_usb_core_ref_sels, base + 0xb100)); - clk_dm(IMX8MP_CLK_USB_PHY_REF, imx8m_clk_composite("usb_phy_ref", imx8mp_usb_phy_ref_sels, base + 0xb180)); - clk_dm(IMX8MP_CLK_GIC, imx8m_clk_composite_critical("gic", imx8mp_gic_sels, base + 0xb200)); - clk_dm(IMX8MP_CLK_ECSPI1, imx8m_clk_composite("ecspi1", imx8mp_ecspi1_sels, base + 0xb280)); - clk_dm(IMX8MP_CLK_ECSPI2, imx8m_clk_composite("ecspi2", imx8mp_ecspi2_sels, base + 0xb300)); - clk_dm(IMX8MP_CLK_PWM1, imx8m_clk_composite_critical("pwm1", imx8mp_pwm1_sels, base + 0xb380)); - clk_dm(IMX8MP_CLK_PWM2, imx8m_clk_composite_critical("pwm2", imx8mp_pwm2_sels, base + 0xb400)); - clk_dm(IMX8MP_CLK_PWM3, imx8m_clk_composite_critical("pwm3", imx8mp_pwm3_sels, base + 0xb480)); - clk_dm(IMX8MP_CLK_PWM4, imx8m_clk_composite_critical("pwm4", imx8mp_pwm4_sels, base + 0xb500)); - clk_dm(IMX8MP_CLK_ECSPI3, imx8m_clk_composite("ecspi3", imx8mp_ecspi3_sels, base + 0xc180)); - - clk_dm(IMX8MP_CLK_WDOG, imx8m_clk_composite("wdog", imx8mp_wdog_sels, base + 0xb900)); - clk_dm(IMX8MP_CLK_USDHC3, imx8m_clk_composite("usdhc3", imx8mp_usdhc3_sels, base + 0xbc80)); - - clk_dm(IMX8MP_CLK_DRAM_ALT_ROOT, imx_clk_fixed_factor("dram_alt_root", "dram_alt", 1, 4)); - clk_dm(IMX8MP_CLK_DRAM_CORE, imx_clk_mux2_flags("dram_core_clk", base + 0x9800, 24, 1, imx8mp_dram_core_sels, ARRAY_SIZE(imx8mp_dram_core_sels), CLK_IS_CRITICAL)); - - clk_dm(IMX8MP_CLK_DRAM1_ROOT, imx_clk_gate4_flags("dram1_root_clk", "dram_core_clk", base + 0x4050, 0, CLK_IS_CRITICAL)); - clk_dm(IMX8MP_CLK_ECSPI1_ROOT, imx_clk_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0)); - clk_dm(IMX8MP_CLK_ECSPI2_ROOT, imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0)); - clk_dm(IMX8MP_CLK_ECSPI3_ROOT, imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); - clk_dm(IMX8MP_CLK_ENET1_ROOT, imx_clk_gate4("enet1_root_clk", "enet_axi", base + 0x40a0, 0)); - clk_dm(IMX8MP_CLK_GPIO1_ROOT, imx_clk_gate4("gpio1_root_clk", "ipg_root", base + 0x40b0, 0)); - clk_dm(IMX8MP_CLK_GPIO2_ROOT, imx_clk_gate4("gpio2_root_clk", "ipg_root", base + 0x40c0, 0)); - clk_dm(IMX8MP_CLK_GPIO3_ROOT, imx_clk_gate4("gpio3_root_clk", "ipg_root", base + 0x40d0, 0)); - clk_dm(IMX8MP_CLK_GPIO4_ROOT, imx_clk_gate4("gpio4_root_clk", "ipg_root", base + 0x40e0, 0)); - clk_dm(IMX8MP_CLK_GPIO5_ROOT, imx_clk_gate4("gpio5_root_clk", "ipg_root", base + 0x40f0, 0)); - clk_dm(IMX8MP_CLK_I2C1_ROOT, imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0)); - clk_dm(IMX8MP_CLK_I2C2_ROOT, imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0)); - clk_dm(IMX8MP_CLK_I2C3_ROOT, imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0)); - clk_dm(IMX8MP_CLK_I2C4_ROOT, imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0)); - clk_dm(IMX8MP_CLK_PCIE_ROOT, imx_clk_gate4("pcie_root_clk", "pcie_aux", base + 0x4250, 0)); - clk_dm(IMX8MP_CLK_PWM1_ROOT, imx_clk_gate4("pwm1_root_clk", "pwm1", base + 0x4280, 0)); - clk_dm(IMX8MP_CLK_PWM2_ROOT, imx_clk_gate4("pwm2_root_clk", "pwm2", base + 0x4290, 0)); - clk_dm(IMX8MP_CLK_PWM3_ROOT, imx_clk_gate4("pwm3_root_clk", "pwm3", base + 0x42a0, 0)); - clk_dm(IMX8MP_CLK_PWM4_ROOT, imx_clk_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0)); - clk_dm(IMX8MP_CLK_QOS_ROOT, imx_clk_gate4("qos_root_clk", "ipg_root", base + 0x42c0, 0)); - clk_dm(IMX8MP_CLK_QOS_ENET_ROOT, imx_clk_gate4("qos_enet_root_clk", "ipg_root", base + 0x42e0, 0)); - clk_dm(IMX8MP_CLK_QSPI_ROOT, imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0)); - clk_dm(IMX8MP_CLK_I2C5_ROOT, imx_clk_gate2("i2c5_root_clk", "i2c5", base + 0x4330, 0)); - clk_dm(IMX8MP_CLK_I2C6_ROOT, imx_clk_gate2("i2c6_root_clk", "i2c6", base + 0x4340, 0)); - clk_dm(IMX8MP_CLK_SIM_ENET_ROOT, imx_clk_gate4("sim_enet_root_clk", "enet_axi", base + 0x4400, 0)); - clk_dm(IMX8MP_CLK_ENET_QOS_ROOT, imx_clk_gate4("enet_qos_root_clk", "sim_enet_root_clk", base + 0x43b0, 0)); - clk_dm(IMX8MP_CLK_UART1_ROOT, imx_clk_gate4("uart1_root_clk", "uart1", base + 0x4490, 0)); - clk_dm(IMX8MP_CLK_UART2_ROOT, imx_clk_gate4("uart2_root_clk", "uart2", base + 0x44a0, 0)); - clk_dm(IMX8MP_CLK_UART3_ROOT, imx_clk_gate4("uart3_root_clk", "uart3", base + 0x44b0, 0)); - clk_dm(IMX8MP_CLK_UART4_ROOT, imx_clk_gate4("uart4_root_clk", "uart4", base + 0x44c0, 0)); - clk_dm(IMX8MP_CLK_USB_ROOT, imx_clk_gate2("usb_root_clk", "hsio_axi", base + 0x44d0, 0)); - clk_dm(IMX8MP_CLK_USB_SUSP, imx_clk_gate2("usb_suspend_clk", "clock-osc-24m", base + 0x44d0, 0)); - clk_dm(IMX8MP_CLK_USB_PHY_ROOT, imx_clk_gate4("usb_phy_root_clk", "usb_phy_ref", base + 0x44f0, 0)); - clk_dm(IMX8MP_CLK_USDHC1_ROOT, imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0)); - clk_dm(IMX8MP_CLK_USDHC2_ROOT, imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0)); - clk_dm(IMX8MP_CLK_WDOG1_ROOT, imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0)); - clk_dm(IMX8MP_CLK_WDOG2_ROOT, imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0)); - clk_dm(IMX8MP_CLK_WDOG3_ROOT, imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0)); - clk_dm(IMX8MP_CLK_HSIO_ROOT, imx_clk_gate4("hsio_root_clk", "ipg_root", base + 0x45c0, 0)); - - clk_dm(IMX8MP_CLK_USDHC3_ROOT, imx_clk_gate4("usdhc3_root_clk", "usdhc3", base + 0x45e0, 0)); + clk_dm(IMX8MP_CLK_A53_SRC, imx_clk_mux2(dev, "arm_a53_src", base + 0x8000, 24, 3, imx8mp_a53_sels, ARRAY_SIZE(imx8mp_a53_sels))); + clk_dm(IMX8MP_CLK_A53_CG, imx_clk_gate3(dev, "arm_a53_cg", "arm_a53_src", base + 0x8000, 28)); + clk_dm(IMX8MP_CLK_A53_DIV, imx_clk_divider2(dev, "arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3)); + + clk_dm(IMX8MP_CLK_HSIO_AXI, imx8m_clk_composite(dev, "hsio_axi", imx8mp_hsio_axi_sels, base + 0x8380)); + clk_dm(IMX8MP_CLK_MAIN_AXI, imx8m_clk_composite_critical(dev, "main_axi", imx8mp_main_axi_sels, base + 0x8800)); + clk_dm(IMX8MP_CLK_ENET_AXI, imx8m_clk_composite_critical(dev, "enet_axi", imx8mp_enet_axi_sels, base + 0x8880)); + clk_dm(IMX8MP_CLK_NAND_USDHC_BUS, imx8m_clk_composite_critical(dev, "nand_usdhc_bus", imx8mp_nand_usdhc_sels, base + 0x8900)); + clk_dm(IMX8MP_CLK_NOC, imx8m_clk_composite_critical(dev, "noc", imx8mp_noc_sels, base + 0x8d00)); + clk_dm(IMX8MP_CLK_NOC_IO, imx8m_clk_composite_critical(dev, "noc_io", imx8mp_noc_io_sels, base + 0x8d80)); + + clk_dm(IMX8MP_CLK_AHB, imx8m_clk_composite_critical(dev, "ahb_root", imx8mp_ahb_sels, base + 0x9000)); + + clk_dm(IMX8MP_CLK_IPG_ROOT, imx_clk_divider2(dev, "ipg_root", "ahb_root", base + 0x9080, 0, 1)); + + clk_dm(IMX8MP_CLK_DRAM_ALT, imx8m_clk_composite(dev, "dram_alt", imx8mp_dram_alt_sels, base + 0xa000)); + clk_dm(IMX8MP_CLK_DRAM_APB, imx8m_clk_composite_critical(dev, "dram_apb", imx8mp_dram_apb_sels, base + 0xa080)); + clk_dm(IMX8MP_CLK_PCIE_AUX, imx8m_clk_composite(dev, "pcie_aux", imx8mp_pcie_aux_sels, base + 0xa400)); + clk_dm(IMX8MP_CLK_I2C5, imx8m_clk_composite(dev, "i2c5", imx8mp_i2c5_sels, base + 0xa480)); + clk_dm(IMX8MP_CLK_I2C6, imx8m_clk_composite(dev, "i2c6", imx8mp_i2c6_sels, base + 0xa500)); + clk_dm(IMX8MP_CLK_ENET_QOS, imx8m_clk_composite(dev, "enet_qos", imx8mp_enet_qos_sels, base + 0xa880)); + clk_dm(IMX8MP_CLK_ENET_QOS_TIMER, imx8m_clk_composite(dev, "enet_qos_timer", imx8mp_enet_qos_timer_sels, base + 0xa900)); + clk_dm(IMX8MP_CLK_ENET_REF, imx8m_clk_composite(dev, "enet_ref", imx8mp_enet_ref_sels, base + 0xa980)); + clk_dm(IMX8MP_CLK_ENET_TIMER, imx8m_clk_composite(dev, "enet_timer", imx8mp_enet_timer_sels, base + 0xaa00)); + clk_dm(IMX8MP_CLK_ENET_PHY_REF, imx8m_clk_composite(dev, "enet_phy_ref", imx8mp_enet_phy_ref_sels, base + 0xaa80)); + clk_dm(IMX8MP_CLK_QSPI, imx8m_clk_composite(dev, "qspi", imx8mp_qspi_sels, base + 0xab80)); + clk_dm(IMX8MP_CLK_USDHC1, imx8m_clk_composite(dev, "usdhc1", imx8mp_usdhc1_sels, base + 0xac00)); + clk_dm(IMX8MP_CLK_USDHC2, imx8m_clk_composite(dev, "usdhc2", imx8mp_usdhc2_sels, base + 0xac80)); + clk_dm(IMX8MP_CLK_I2C1, imx8m_clk_composite(dev, "i2c1", imx8mp_i2c1_sels, base + 0xad00)); + clk_dm(IMX8MP_CLK_I2C2, imx8m_clk_composite(dev, "i2c2", imx8mp_i2c2_sels, base + 0xad80)); + clk_dm(IMX8MP_CLK_I2C3, imx8m_clk_composite(dev, "i2c3", imx8mp_i2c3_sels, base + 0xae00)); + clk_dm(IMX8MP_CLK_I2C4, imx8m_clk_composite(dev, "i2c4", imx8mp_i2c4_sels, base + 0xae80)); + + clk_dm(IMX8MP_CLK_UART1, imx8m_clk_composite(dev, "uart1", imx8mp_uart1_sels, base + 0xaf00)); + clk_dm(IMX8MP_CLK_UART2, imx8m_clk_composite(dev, "uart2", imx8mp_uart2_sels, base + 0xaf80)); + clk_dm(IMX8MP_CLK_UART3, imx8m_clk_composite(dev, "uart3", imx8mp_uart3_sels, base + 0xb000)); + clk_dm(IMX8MP_CLK_UART4, imx8m_clk_composite(dev, "uart4", imx8mp_uart4_sels, base + 0xb080)); + clk_dm(IMX8MP_CLK_USB_CORE_REF, imx8m_clk_composite(dev, "usb_core_ref", imx8mp_usb_core_ref_sels, base + 0xb100)); + clk_dm(IMX8MP_CLK_USB_PHY_REF, imx8m_clk_composite(dev, "usb_phy_ref", imx8mp_usb_phy_ref_sels, base + 0xb180)); + clk_dm(IMX8MP_CLK_GIC, imx8m_clk_composite_critical(dev, "gic", imx8mp_gic_sels, base + 0xb200)); + clk_dm(IMX8MP_CLK_ECSPI1, imx8m_clk_composite(dev, "ecspi1", imx8mp_ecspi1_sels, base + 0xb280)); + clk_dm(IMX8MP_CLK_ECSPI2, imx8m_clk_composite(dev, "ecspi2", imx8mp_ecspi2_sels, base + 0xb300)); + clk_dm(IMX8MP_CLK_PWM1, imx8m_clk_composite_critical(dev, "pwm1", imx8mp_pwm1_sels, base + 0xb380)); + clk_dm(IMX8MP_CLK_PWM2, imx8m_clk_composite_critical(dev, "pwm2", imx8mp_pwm2_sels, base + 0xb400)); + clk_dm(IMX8MP_CLK_PWM3, imx8m_clk_composite_critical(dev, "pwm3", imx8mp_pwm3_sels, base + 0xb480)); + clk_dm(IMX8MP_CLK_PWM4, imx8m_clk_composite_critical(dev, "pwm4", imx8mp_pwm4_sels, base + 0xb500)); + clk_dm(IMX8MP_CLK_ECSPI3, imx8m_clk_composite(dev, "ecspi3", imx8mp_ecspi3_sels, base + 0xc180)); + + clk_dm(IMX8MP_CLK_WDOG, imx8m_clk_composite(dev, "wdog", imx8mp_wdog_sels, base + 0xb900)); + clk_dm(IMX8MP_CLK_USDHC3, imx8m_clk_composite(dev, "usdhc3", imx8mp_usdhc3_sels, base + 0xbc80)); + + clk_dm(IMX8MP_CLK_DRAM_ALT_ROOT, imx_clk_fixed_factor(dev, "dram_alt_root", "dram_alt", 1, 4)); + clk_dm(IMX8MP_CLK_DRAM_CORE, imx_clk_mux2_flags(dev, "dram_core_clk", base + 0x9800, 24, 1, imx8mp_dram_core_sels, ARRAY_SIZE(imx8mp_dram_core_sels), CLK_IS_CRITICAL)); + + clk_dm(IMX8MP_CLK_DRAM1_ROOT, imx_clk_gate4_flags(dev, "dram1_root_clk", "dram_core_clk", base + 0x4050, 0, CLK_IS_CRITICAL)); + clk_dm(IMX8MP_CLK_ECSPI1_ROOT, imx_clk_gate4(dev, "ecspi1_root_clk", "ecspi1", base + 0x4070, 0)); + clk_dm(IMX8MP_CLK_ECSPI2_ROOT, imx_clk_gate4(dev, "ecspi2_root_clk", "ecspi2", base + 0x4080, 0)); + clk_dm(IMX8MP_CLK_ECSPI3_ROOT, imx_clk_gate4(dev, "ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); + clk_dm(IMX8MP_CLK_ENET1_ROOT, imx_clk_gate4(dev, "enet1_root_clk", "enet_axi", base + 0x40a0, 0)); + clk_dm(IMX8MP_CLK_GPIO1_ROOT, imx_clk_gate4(dev, "gpio1_root_clk", "ipg_root", base + 0x40b0, 0)); + clk_dm(IMX8MP_CLK_GPIO2_ROOT, imx_clk_gate4(dev, "gpio2_root_clk", "ipg_root", base + 0x40c0, 0)); + clk_dm(IMX8MP_CLK_GPIO3_ROOT, imx_clk_gate4(dev, "gpio3_root_clk", "ipg_root", base + 0x40d0, 0)); + clk_dm(IMX8MP_CLK_GPIO4_ROOT, imx_clk_gate4(dev, "gpio4_root_clk", "ipg_root", base + 0x40e0, 0)); + clk_dm(IMX8MP_CLK_GPIO5_ROOT, imx_clk_gate4(dev, "gpio5_root_clk", "ipg_root", base + 0x40f0, 0)); + clk_dm(IMX8MP_CLK_I2C1_ROOT, imx_clk_gate4(dev, "i2c1_root_clk", "i2c1", base + 0x4170, 0)); + clk_dm(IMX8MP_CLK_I2C2_ROOT, imx_clk_gate4(dev, "i2c2_root_clk", "i2c2", base + 0x4180, 0)); + clk_dm(IMX8MP_CLK_I2C3_ROOT, imx_clk_gate4(dev, "i2c3_root_clk", "i2c3", base + 0x4190, 0)); + clk_dm(IMX8MP_CLK_I2C4_ROOT, imx_clk_gate4(dev, "i2c4_root_clk", "i2c4", base + 0x41a0, 0)); + clk_dm(IMX8MP_CLK_PCIE_ROOT, imx_clk_gate4(dev, "pcie_root_clk", "pcie_aux", base + 0x4250, 0)); + clk_dm(IMX8MP_CLK_PWM1_ROOT, imx_clk_gate4(dev, "pwm1_root_clk", "pwm1", base + 0x4280, 0)); + clk_dm(IMX8MP_CLK_PWM2_ROOT, imx_clk_gate4(dev, "pwm2_root_clk", "pwm2", base + 0x4290, 0)); + clk_dm(IMX8MP_CLK_PWM3_ROOT, imx_clk_gate4(dev, "pwm3_root_clk", "pwm3", base + 0x42a0, 0)); + clk_dm(IMX8MP_CLK_PWM4_ROOT, imx_clk_gate4(dev, "pwm4_root_clk", "pwm4", base + 0x42b0, 0)); + clk_dm(IMX8MP_CLK_QOS_ROOT, imx_clk_gate4(dev, "qos_root_clk", "ipg_root", base + 0x42c0, 0)); + clk_dm(IMX8MP_CLK_QOS_ENET_ROOT, imx_clk_gate4(dev, "qos_enet_root_clk", "ipg_root", base + 0x42e0, 0)); + clk_dm(IMX8MP_CLK_QSPI_ROOT, imx_clk_gate4(dev, "qspi_root_clk", "qspi", base + 0x42f0, 0)); + clk_dm(IMX8MP_CLK_I2C5_ROOT, imx_clk_gate2(dev, "i2c5_root_clk", "i2c5", base + 0x4330, 0)); + clk_dm(IMX8MP_CLK_I2C6_ROOT, imx_clk_gate2(dev, "i2c6_root_clk", "i2c6", base + 0x4340, 0)); + clk_dm(IMX8MP_CLK_SIM_ENET_ROOT, imx_clk_gate4(dev, "sim_enet_root_clk", "enet_axi", base + 0x4400, 0)); + clk_dm(IMX8MP_CLK_ENET_QOS_ROOT, imx_clk_gate4(dev, "enet_qos_root_clk", "sim_enet_root_clk", base + 0x43b0, 0)); + clk_dm(IMX8MP_CLK_UART1_ROOT, imx_clk_gate4(dev, "uart1_root_clk", "uart1", base + 0x4490, 0)); + clk_dm(IMX8MP_CLK_UART2_ROOT, imx_clk_gate4(dev, "uart2_root_clk", "uart2", base + 0x44a0, 0)); + clk_dm(IMX8MP_CLK_UART3_ROOT, imx_clk_gate4(dev, "uart3_root_clk", "uart3", base + 0x44b0, 0)); + clk_dm(IMX8MP_CLK_UART4_ROOT, imx_clk_gate4(dev, "uart4_root_clk", "uart4", base + 0x44c0, 0)); + clk_dm(IMX8MP_CLK_USB_ROOT, imx_clk_gate2(dev, "usb_root_clk", "hsio_axi", base + 0x44d0, 0)); + clk_dm(IMX8MP_CLK_USB_SUSP, imx_clk_gate2(dev, "usb_suspend_clk", "osc_24m", base + 0x44d0, 0)); + clk_dm(IMX8MP_CLK_USB_PHY_ROOT, imx_clk_gate4(dev, "usb_phy_root_clk", "usb_phy_ref", base + 0x44f0, 0)); + clk_dm(IMX8MP_CLK_USDHC1_ROOT, imx_clk_gate4(dev, "usdhc1_root_clk", "usdhc1", base + 0x4510, 0)); + clk_dm(IMX8MP_CLK_USDHC2_ROOT, imx_clk_gate4(dev, "usdhc2_root_clk", "usdhc2", base + 0x4520, 0)); + clk_dm(IMX8MP_CLK_WDOG1_ROOT, imx_clk_gate4(dev, "wdog1_root_clk", "wdog", base + 0x4530, 0)); + clk_dm(IMX8MP_CLK_WDOG2_ROOT, imx_clk_gate4(dev, "wdog2_root_clk", "wdog", base + 0x4540, 0)); + clk_dm(IMX8MP_CLK_WDOG3_ROOT, imx_clk_gate4(dev, "wdog3_root_clk", "wdog", base + 0x4550, 0)); + clk_dm(IMX8MP_CLK_HSIO_ROOT, imx_clk_gate4(dev, "hsio_root_clk", "ipg_root", base + 0x45c0, 0)); + + clk_dm(IMX8MP_CLK_USDHC3_ROOT, imx_clk_gate4(dev, "usdhc3_root_clk", "usdhc3", base + 0x45e0, 0)); clk_dm(IMX8MP_CLK_ARM, - imx_clk_mux2_flags("arm_core", base + 0x9880, 24, 1, + imx_clk_mux2_flags(dev, "arm_core", base + 0x9880, 24, 1, imx8mp_arm_core_sels, ARRAY_SIZE(imx8mp_arm_core_sels), CLK_IS_CRITICAL)); diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c index ed4acd79ef7..fe6cba19758 100644 --- a/drivers/clk/imx/clk-imx8mq.c +++ b/drivers/clk/imx/clk-imx8mq.c @@ -152,31 +152,31 @@ static int imx8mq_clk_probe(struct udevice *dev) clk_dm(IMX8MQ_CLK_27M, clk_register_fixed_rate(NULL, "clock-osc-27m", 27000000)); clk_dm(IMX8MQ_DRAM_PLL1_REF_SEL, - imx_clk_mux("dram_pll_ref_sel", base + 0x60, 0, 2, + imx_clk_mux(dev, "dram_pll_ref_sel", base + 0x60, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_ARM_PLL_REF_SEL, - imx_clk_mux("arm_pll_ref_sel", base + 0x28, 0, 2, + imx_clk_mux(dev, "arm_pll_ref_sel", base + 0x28, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_GPU_PLL_REF_SEL, - imx_clk_mux("gpu_pll_ref_sel", base + 0x18, 0, 2, + imx_clk_mux(dev, "gpu_pll_ref_sel", base + 0x18, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_VPU_PLL_REF_SEL, - imx_clk_mux("vpu_pll_ref_sel", base + 0x20, 0, 2, + imx_clk_mux(dev, "vpu_pll_ref_sel", base + 0x20, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_SYS3_PLL1_REF_SEL, - imx_clk_mux("sys3_pll_ref_sel", base + 0x48, 0, 2, + imx_clk_mux(dev, "sys3_pll_ref_sel", base + 0x48, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_AUDIO_PLL1_REF_SEL, - imx_clk_mux("audio_pll1_ref_sel", base + 0x0, 0, 2, + imx_clk_mux(dev, "audio_pll1_ref_sel", base + 0x0, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_AUDIO_PLL2_REF_SEL, - imx_clk_mux("audio_pll2_ref_sel", base + 0x8, 0, 2, + imx_clk_mux(dev, "audio_pll2_ref_sel", base + 0x8, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_VIDEO_PLL1_REF_SEL, - imx_clk_mux("video_pll1_ref_sel", base + 0x10, 0, 2, + imx_clk_mux(dev, "video_pll1_ref_sel", base + 0x10, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_VIDEO2_PLL1_REF_SEL, - imx_clk_mux("video_pll2_ref_sel", base + 0x54, 0, 2, + imx_clk_mux(dev, "video_pll2_ref_sel", base + 0x54, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMX8MQ_ARM_PLL, @@ -207,140 +207,140 @@ static int imx8mq_clk_probe(struct udevice *dev) /* PLL bypass out */ clk_dm(IMX8MQ_ARM_PLL_BYPASS, - imx_clk_mux_flags("arm_pll_bypass", base + 0x28, 4, 1, + imx_clk_mux_flags(dev, "arm_pll_bypass", base + 0x28, 4, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MQ_GPU_PLL_BYPASS, - imx_clk_mux_flags("gpu_pll_bypass", base + 0x18, 4, 1, + imx_clk_mux_flags(dev, "gpu_pll_bypass", base + 0x18, 4, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MQ_VPU_PLL_BYPASS, - imx_clk_mux_flags("vpu_pll_bypass", base + 0x20, 4, 1, + imx_clk_mux_flags(dev, "vpu_pll_bypass", base + 0x20, 4, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MQ_AUDIO_PLL1_BYPASS, - imx_clk_mux_flags("audio_pll1_bypass", base + 0x0, 4, 1, + imx_clk_mux_flags(dev, "audio_pll1_bypass", base + 0x0, 4, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MQ_AUDIO_PLL2_BYPASS, - imx_clk_mux_flags("audio_pll2_bypass", base + 0x8, 4, 1, + imx_clk_mux_flags(dev, "audio_pll2_bypass", base + 0x8, 4, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MQ_VIDEO_PLL1_BYPASS, - imx_clk_mux_flags("video_pll1_bypass", base + 0x10, 4, 1, + imx_clk_mux_flags(dev, "video_pll1_bypass", base + 0x10, 4, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels), CLK_SET_RATE_PARENT)); /* PLL out gate */ clk_dm(IMX8MQ_DRAM_PLL_OUT, - imx_clk_gate("dram_pll_out", "dram_pll_ref_sel", + imx_clk_gate(dev, "dram_pll_out", "dram_pll_ref_sel", base + 0x60, 13)); clk_dm(IMX8MQ_ARM_PLL_OUT, - imx_clk_gate("arm_pll_out", "arm_pll_bypass", + imx_clk_gate(dev, "arm_pll_out", "arm_pll_bypass", base + 0x28, 11)); clk_dm(IMX8MQ_GPU_PLL_OUT, - imx_clk_gate("gpu_pll_out", "gpu_pll_bypass", + imx_clk_gate(dev, "gpu_pll_out", "gpu_pll_bypass", base + 0x18, 11)); clk_dm(IMX8MQ_VPU_PLL_OUT, - imx_clk_gate("vpu_pll_out", "vpu_pll_bypass", + imx_clk_gate(dev, "vpu_pll_out", "vpu_pll_bypass", base + 0x20, 11)); clk_dm(IMX8MQ_AUDIO_PLL1_OUT, - imx_clk_gate("audio_pll1_out", "audio_pll1_bypass", + imx_clk_gate(dev, "audio_pll1_out", "audio_pll1_bypass", base + 0x0, 11)); clk_dm(IMX8MQ_AUDIO_PLL2_OUT, - imx_clk_gate("audio_pll2_out", "audio_pll2_bypass", + imx_clk_gate(dev, "audio_pll2_out", "audio_pll2_bypass", base + 0x8, 11)); clk_dm(IMX8MQ_VIDEO_PLL1_OUT, - imx_clk_gate("video_pll1_out", "video_pll1_bypass", + imx_clk_gate(dev, "video_pll1_out", "video_pll1_bypass", base + 0x10, 11)); clk_dm(IMX8MQ_SYS1_PLL_OUT, - imx_clk_gate("sys_pll1_out", "sys1_pll", + imx_clk_gate(dev, "sys_pll1_out", "sys1_pll", base + 0x30, 11)); clk_dm(IMX8MQ_SYS2_PLL_OUT, - imx_clk_gate("sys_pll2_out", "sys2_pll", + imx_clk_gate(dev, "sys_pll2_out", "sys2_pll", base + 0x3c, 11)); clk_dm(IMX8MQ_SYS3_PLL_OUT, - imx_clk_gate("sys_pll3_out", "sys3_pll", + imx_clk_gate(dev, "sys_pll3_out", "sys3_pll", base + 0x48, 11)); clk_dm(IMX8MQ_VIDEO2_PLL_OUT, - imx_clk_gate("video_pll2_out", "video_pll2_ref_sel", + imx_clk_gate(dev, "video_pll2_out", "video_pll2_ref_sel", base + 0x54, 11)); /* SYS PLL fixed output */ clk_dm(IMX8MQ_SYS1_PLL_40M, - imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20)); + imx_clk_fixed_factor(dev, "sys_pll1_40m", "sys_pll1_out", 1, 20)); clk_dm(IMX8MQ_SYS1_PLL_80M, - imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10)); + imx_clk_fixed_factor(dev, "sys_pll1_80m", "sys_pll1_out", 1, 10)); clk_dm(IMX8MQ_SYS1_PLL_100M, - imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8)); + imx_clk_fixed_factor(dev, "sys_pll1_100m", "sys_pll1_out", 1, 8)); clk_dm(IMX8MQ_SYS1_PLL_133M, - imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6)); + imx_clk_fixed_factor(dev, "sys_pll1_133m", "sys_pll1_out", 1, 6)); clk_dm(IMX8MQ_SYS1_PLL_160M, - imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5)); + imx_clk_fixed_factor(dev, "sys_pll1_160m", "sys_pll1_out", 1, 5)); clk_dm(IMX8MQ_SYS1_PLL_200M, - imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4)); + imx_clk_fixed_factor(dev, "sys_pll1_200m", "sys_pll1_out", 1, 4)); clk_dm(IMX8MQ_SYS1_PLL_266M, - imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3)); + imx_clk_fixed_factor(dev, "sys_pll1_266m", "sys_pll1_out", 1, 3)); clk_dm(IMX8MQ_SYS1_PLL_400M, - imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2)); + imx_clk_fixed_factor(dev, "sys_pll1_400m", "sys_pll1_out", 1, 2)); clk_dm(IMX8MQ_SYS1_PLL_800M, - imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1)); + imx_clk_fixed_factor(dev, "sys_pll1_800m", "sys_pll1_out", 1, 1)); clk_dm(IMX8MQ_SYS2_PLL_50M, - imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20)); + imx_clk_fixed_factor(dev, "sys_pll2_50m", "sys_pll2_out", 1, 20)); clk_dm(IMX8MQ_SYS2_PLL_100M, - imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10)); + imx_clk_fixed_factor(dev, "sys_pll2_100m", "sys_pll2_out", 1, 10)); clk_dm(IMX8MQ_SYS2_PLL_125M, - imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8)); + imx_clk_fixed_factor(dev, "sys_pll2_125m", "sys_pll2_out", 1, 8)); clk_dm(IMX8MQ_SYS2_PLL_166M, - imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6)); + imx_clk_fixed_factor(dev, "sys_pll2_166m", "sys_pll2_out", 1, 6)); clk_dm(IMX8MQ_SYS2_PLL_200M, - imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5)); + imx_clk_fixed_factor(dev, "sys_pll2_200m", "sys_pll2_out", 1, 5)); clk_dm(IMX8MQ_SYS2_PLL_250M, - imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4)); + imx_clk_fixed_factor(dev, "sys_pll2_250m", "sys_pll2_out", 1, 4)); clk_dm(IMX8MQ_SYS2_PLL_333M, - imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3)); + imx_clk_fixed_factor(dev, "sys_pll2_333m", "sys_pll2_out", 1, 3)); clk_dm(IMX8MQ_SYS2_PLL_500M, - imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2)); + imx_clk_fixed_factor(dev, "sys_pll2_500m", "sys_pll2_out", 1, 2)); clk_dm(IMX8MQ_SYS2_PLL_1000M, - imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1)); + imx_clk_fixed_factor(dev, "sys_pll2_1000m", "sys_pll2_out", 1, 1)); clk_dm(IMX8MQ_CLK_MON_AUDIO_PLL1_DIV, - imx_clk_divider("audio_pll1_out_monitor", "audio_pll1_bypass", base + 0x78, 0, 3)); + imx_clk_divider(dev, "audio_pll1_out_monitor", "audio_pll1_bypass", base + 0x78, 0, 3)); clk_dm(IMX8MQ_CLK_MON_AUDIO_PLL2_DIV, - imx_clk_divider("audio_pll2_out_monitor", "audio_pll2_bypass", base + 0x78, 4, 3)); + imx_clk_divider(dev, "audio_pll2_out_monitor", "audio_pll2_bypass", base + 0x78, 4, 3)); clk_dm(IMX8MQ_CLK_MON_VIDEO_PLL1_DIV, - imx_clk_divider("video_pll1_out_monitor", "video_pll1_bypass", base + 0x78, 8, 3)); + imx_clk_divider(dev, "video_pll1_out_monitor", "video_pll1_bypass", base + 0x78, 8, 3)); clk_dm(IMX8MQ_CLK_MON_GPU_PLL_DIV, - imx_clk_divider("gpu_pll_out_monitor", "gpu_pll_bypass", base + 0x78, 12, 3)); + imx_clk_divider(dev, "gpu_pll_out_monitor", "gpu_pll_bypass", base + 0x78, 12, 3)); clk_dm(IMX8MQ_CLK_MON_VPU_PLL_DIV, - imx_clk_divider("vpu_pll_out_monitor", "vpu_pll_bypass", base + 0x78, 16, 3)); + imx_clk_divider(dev, "vpu_pll_out_monitor", "vpu_pll_bypass", base + 0x78, 16, 3)); clk_dm(IMX8MQ_CLK_MON_ARM_PLL_DIV, - imx_clk_divider("arm_pll_out_monitor", "arm_pll_bypass", base + 0x78, 20, 3)); + imx_clk_divider(dev, "arm_pll_out_monitor", "arm_pll_bypass", base + 0x78, 20, 3)); clk_dm(IMX8MQ_CLK_MON_SYS_PLL1_DIV, - imx_clk_divider("sys_pll1_out_monitor", "sys_pll1_out", base + 0x7c, 0, 3)); + imx_clk_divider(dev, "sys_pll1_out_monitor", "sys_pll1_out", base + 0x7c, 0, 3)); clk_dm(IMX8MQ_CLK_MON_SYS_PLL2_DIV, - imx_clk_divider("sys_pll2_out_monitor", "sys_pll2_out", base + 0x7c, 4, 3)); + imx_clk_divider(dev, "sys_pll2_out_monitor", "sys_pll2_out", base + 0x7c, 4, 3)); clk_dm(IMX8MQ_CLK_MON_SYS_PLL3_DIV, - imx_clk_divider("sys_pll3_out_monitor", "sys_pll3_out", base + 0x7c, 8, 3)); + imx_clk_divider(dev, "sys_pll3_out_monitor", "sys_pll3_out", base + 0x7c, 8, 3)); clk_dm(IMX8MQ_CLK_MON_DRAM_PLL_DIV, - imx_clk_divider("dram_pll_out_monitor", "dram_pll_out", base + 0x7c, 12, 3)); + imx_clk_divider(dev, "dram_pll_out_monitor", "dram_pll_out", base + 0x7c, 12, 3)); clk_dm(IMX8MQ_CLK_MON_VIDEO_PLL2_DIV, - imx_clk_divider("video_pll2_out_monitor", "video_pll2_out", base + 0x7c, 16, 3)); + imx_clk_divider(dev, "video_pll2_out_monitor", "video_pll2_out", base + 0x7c, 16, 3)); clk_dm(IMX8MQ_CLK_MON_SEL, - imx_clk_mux_flags("pllout_monitor_sel", base + 0x74, 0, 4, + imx_clk_mux_flags(dev, "pllout_monitor_sel", base + 0x74, 0, 4, pllout_monitor_sels, ARRAY_SIZE(pllout_monitor_sels), CLK_SET_RATE_PARENT)); clk_dm(IMX8MQ_CLK_MON_CLK2_OUT, - imx_clk_gate4("pllout_monitor_clk2", "pllout_monitor_sel", base + 0x74, 4)); + imx_clk_gate4(dev, "pllout_monitor_clk2", "pllout_monitor_sel", base + 0x74, 4)); base = dev_read_addr_ptr(dev); if (!base) { @@ -349,140 +349,140 @@ static int imx8mq_clk_probe(struct udevice *dev) } clk_dm(IMX8MQ_CLK_A53_SRC, - imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, + imx_clk_mux2(dev, "arm_a53_src", base + 0x8000, 24, 3, imx8mq_a53_sels, ARRAY_SIZE(imx8mq_a53_sels))); clk_dm(IMX8MQ_CLK_A53_CG, - imx_clk_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28)); + imx_clk_gate3(dev, "arm_a53_cg", "arm_a53_src", base + 0x8000, 28)); clk_dm(IMX8MQ_CLK_A53_DIV, - imx_clk_divider2("arm_a53_div", "arm_a53_cg", + imx_clk_divider2(dev, "arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3)); clk_dm(IMX8MQ_CLK_A53_CORE, - imx_clk_mux2("arm_a53_src", base + 0x9880, 24, 1, + imx_clk_mux2(dev, "arm_a53_src", base + 0x9880, 24, 1, imx8mq_a53_core_sels, ARRAY_SIZE(imx8mq_a53_core_sels))); clk_dm(IMX8MQ_CLK_AHB, - imx8m_clk_composite_critical("ahb", imx8mq_ahb_sels, + imx8m_clk_composite_critical(dev, "ahb", imx8mq_ahb_sels, base + 0x9000)); clk_dm(IMX8MQ_CLK_IPG_ROOT, - imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1)); + imx_clk_divider2(dev, "ipg_root", "ahb", base + 0x9080, 0, 1)); clk_dm(IMX8MQ_CLK_ENET_AXI, - imx8m_clk_composite("enet_axi", imx8mq_enet_axi_sels, + imx8m_clk_composite(dev, "enet_axi", imx8mq_enet_axi_sels, base + 0x8880)); clk_dm(IMX8MQ_CLK_NAND_USDHC_BUS, - imx8m_clk_composite_critical("nand_usdhc_bus", + imx8m_clk_composite_critical(dev, "nand_usdhc_bus", imx8mq_nand_usdhc_sels, base + 0x8900)); clk_dm(IMX8MQ_CLK_USB_BUS, - imx8m_clk_composite("usb_bus", imx8mq_usb_bus_sels, base + 0x8b80)); + imx8m_clk_composite(dev, "usb_bus", imx8mq_usb_bus_sels, base + 0x8b80)); /* DRAM */ clk_dm(IMX8MQ_CLK_DRAM_CORE, - imx_clk_mux2("dram_core_clk", base + 0x9800, 24, 1, + imx_clk_mux2(dev, "dram_core_clk", base + 0x9800, 24, 1, imx8mq_dram_core_sels, ARRAY_SIZE(imx8mq_dram_core_sels))); clk_dm(IMX8MQ_CLK_DRAM_ALT, - imx8m_clk_composite("dram_alt", imx8mq_dram_alt_sels, base + 0xa000)); + imx8m_clk_composite(dev, "dram_alt", imx8mq_dram_alt_sels, base + 0xa000)); clk_dm(IMX8MQ_CLK_DRAM_APB, - imx8m_clk_composite_critical("dram_apb", imx8mq_dram_apb_sels, base + 0xa080)); + imx8m_clk_composite_critical(dev, "dram_apb", imx8mq_dram_apb_sels, base + 0xa080)); /* IP */ clk_dm(IMX8MQ_CLK_USDHC1, - imx8m_clk_composite("usdhc1", imx8mq_usdhc1_sels, + imx8m_clk_composite(dev, "usdhc1", imx8mq_usdhc1_sels, base + 0xac00)); clk_dm(IMX8MQ_CLK_USDHC2, - imx8m_clk_composite("usdhc2", imx8mq_usdhc2_sels, + imx8m_clk_composite(dev, "usdhc2", imx8mq_usdhc2_sels, base + 0xac80)); clk_dm(IMX8MQ_CLK_I2C1, - imx8m_clk_composite("i2c1", imx8mq_i2c1_sels, base + 0xad00)); + imx8m_clk_composite(dev, "i2c1", imx8mq_i2c1_sels, base + 0xad00)); clk_dm(IMX8MQ_CLK_I2C2, - imx8m_clk_composite("i2c2", imx8mq_i2c2_sels, base + 0xad80)); + imx8m_clk_composite(dev, "i2c2", imx8mq_i2c2_sels, base + 0xad80)); clk_dm(IMX8MQ_CLK_I2C3, - imx8m_clk_composite("i2c3", imx8mq_i2c3_sels, base + 0xae00)); + imx8m_clk_composite(dev, "i2c3", imx8mq_i2c3_sels, base + 0xae00)); clk_dm(IMX8MQ_CLK_I2C4, - imx8m_clk_composite("i2c4", imx8mq_i2c4_sels, base + 0xae80)); + imx8m_clk_composite(dev, "i2c4", imx8mq_i2c4_sels, base + 0xae80)); clk_dm(IMX8MQ_CLK_WDOG, - imx8m_clk_composite("wdog", imx8mq_wdog_sels, base + 0xb900)); + imx8m_clk_composite(dev, "wdog", imx8mq_wdog_sels, base + 0xb900)); clk_dm(IMX8MQ_CLK_UART1, - imx8m_clk_composite("uart1", imx8mq_uart1_sels, base + 0xaf00)); + imx8m_clk_composite(dev, "uart1", imx8mq_uart1_sels, base + 0xaf00)); clk_dm(IMX8MQ_CLK_UART2, - imx8m_clk_composite("uart2", imx8mq_uart2_sels, base + 0xaf80)); + imx8m_clk_composite(dev, "uart2", imx8mq_uart2_sels, base + 0xaf80)); clk_dm(IMX8MQ_CLK_UART3, - imx8m_clk_composite("uart3", imx8mq_uart3_sels, base + 0xb000)); + imx8m_clk_composite(dev, "uart3", imx8mq_uart3_sels, base + 0xb000)); clk_dm(IMX8MQ_CLK_UART4, - imx8m_clk_composite("uart4", imx8mq_uart4_sels, base + 0xb080)); + imx8m_clk_composite(dev, "uart4", imx8mq_uart4_sels, base + 0xb080)); clk_dm(IMX8MQ_CLK_QSPI, - imx8m_clk_composite("qspi", imx8mq_qspi_sels, base + 0xab80)); + imx8m_clk_composite(dev, "qspi", imx8mq_qspi_sels, base + 0xab80)); clk_dm(IMX8MQ_CLK_USB_CORE_REF, - imx8m_clk_composite("usb_core_ref", imx8mq_usb_core_sels, base + 0xb100)); + imx8m_clk_composite(dev, "usb_core_ref", imx8mq_usb_core_sels, base + 0xb100)); clk_dm(IMX8MQ_CLK_USB_PHY_REF, - imx8m_clk_composite("usb_phy_ref", imx8mq_usb_phy_sels, base + 0xb180)); + imx8m_clk_composite(dev, "usb_phy_ref", imx8mq_usb_phy_sels, base + 0xb180)); clk_dm(IMX8MQ_CLK_ECSPI1, - imx8m_clk_composite("ecspi1", imx8mq_ecspi1_sels, base + 0xb280)); + imx8m_clk_composite(dev, "ecspi1", imx8mq_ecspi1_sels, base + 0xb280)); clk_dm(IMX8MQ_CLK_ECSPI2, - imx8m_clk_composite("ecspi2", imx8mq_ecspi2_sels, base + 0xb300)); + imx8m_clk_composite(dev, "ecspi2", imx8mq_ecspi2_sels, base + 0xb300)); clk_dm(IMX8MQ_CLK_ECSPI3, - imx8m_clk_composite("ecspi3", imx8mq_ecspi3_sels, base + 0xc180)); + imx8m_clk_composite(dev, "ecspi3", imx8mq_ecspi3_sels, base + 0xc180)); clk_dm(IMX8MQ_CLK_ECSPI1_ROOT, - imx_clk_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0)); + imx_clk_gate4(dev, "ecspi1_root_clk", "ecspi1", base + 0x4070, 0)); clk_dm(IMX8MQ_CLK_ECSPI2_ROOT, - imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0)); + imx_clk_gate4(dev, "ecspi2_root_clk", "ecspi2", base + 0x4080, 0)); clk_dm(IMX8MQ_CLK_ECSPI3_ROOT, - imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); + imx_clk_gate4(dev, "ecspi3_root_clk", "ecspi3", base + 0x4090, 0)); clk_dm(IMX8MQ_CLK_I2C1_ROOT, - imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0)); + imx_clk_gate4(dev, "i2c1_root_clk", "i2c1", base + 0x4170, 0)); clk_dm(IMX8MQ_CLK_I2C2_ROOT, - imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0)); + imx_clk_gate4(dev, "i2c2_root_clk", "i2c2", base + 0x4180, 0)); clk_dm(IMX8MQ_CLK_I2C3_ROOT, - imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0)); + imx_clk_gate4(dev, "i2c3_root_clk", "i2c3", base + 0x4190, 0)); clk_dm(IMX8MQ_CLK_I2C4_ROOT, - imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0)); + imx_clk_gate4(dev, "i2c4_root_clk", "i2c4", base + 0x41a0, 0)); clk_dm(IMX8MQ_CLK_UART1_ROOT, - imx_clk_gate4("uart1_root_clk", "uart1", base + 0x4490, 0)); + imx_clk_gate4(dev, "uart1_root_clk", "uart1", base + 0x4490, 0)); clk_dm(IMX8MQ_CLK_UART2_ROOT, - imx_clk_gate4("uart2_root_clk", "uart2", base + 0x44a0, 0)); + imx_clk_gate4(dev, "uart2_root_clk", "uart2", base + 0x44a0, 0)); clk_dm(IMX8MQ_CLK_UART3_ROOT, - imx_clk_gate4("uart3_root_clk", "uart3", base + 0x44b0, 0)); + imx_clk_gate4(dev, "uart3_root_clk", "uart3", base + 0x44b0, 0)); clk_dm(IMX8MQ_CLK_UART4_ROOT, - imx_clk_gate4("uart4_root_clk", "uart4", base + 0x44c0, 0)); + imx_clk_gate4(dev, "uart4_root_clk", "uart4", base + 0x44c0, 0)); clk_dm(IMX8MQ_CLK_OCOTP_ROOT, - imx_clk_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0)); + imx_clk_gate4(dev, "ocotp_root_clk", "ipg_root", base + 0x4220, 0)); clk_dm(IMX8MQ_CLK_USDHC1_ROOT, - imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0)); + imx_clk_gate4(dev, "usdhc1_root_clk", "usdhc1", base + 0x4510, 0)); clk_dm(IMX8MQ_CLK_USDHC2_ROOT, - imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0)); + imx_clk_gate4(dev, "usdhc2_root_clk", "usdhc2", base + 0x4520, 0)); clk_dm(IMX8MQ_CLK_WDOG1_ROOT, - imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0)); + imx_clk_gate4(dev, "wdog1_root_clk", "wdog", base + 0x4530, 0)); clk_dm(IMX8MQ_CLK_WDOG2_ROOT, - imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0)); + imx_clk_gate4(dev, "wdog2_root_clk", "wdog", base + 0x4540, 0)); clk_dm(IMX8MQ_CLK_WDOG3_ROOT, - imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0)); + imx_clk_gate4(dev, "wdog3_root_clk", "wdog", base + 0x4550, 0)); clk_dm(IMX8MQ_CLK_QSPI_ROOT, - imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0)); + imx_clk_gate4(dev, "qspi_root_clk", "qspi", base + 0x42f0, 0)); clk_dm(IMX8MQ_CLK_USB1_CTRL_ROOT, - imx_clk_gate4("usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0)); + imx_clk_gate4(dev, "usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0)); clk_dm(IMX8MQ_CLK_USB2_CTRL_ROOT, - imx_clk_gate4("usb2_ctrl_root_clk", "usb_bus", base + 0x44e0, 0)); + imx_clk_gate4(dev, "usb2_ctrl_root_clk", "usb_bus", base + 0x44e0, 0)); clk_dm(IMX8MQ_CLK_USB1_PHY_ROOT, - imx_clk_gate4("usb1_phy_root_clk", "usb_phy_ref", base + 0x44f0, 0)); + imx_clk_gate4(dev, "usb1_phy_root_clk", "usb_phy_ref", base + 0x44f0, 0)); clk_dm(IMX8MQ_CLK_USB2_PHY_ROOT, - imx_clk_gate4("usb2_phy_root_clk", "usb_phy_ref", base + 0x4500, 0)); + imx_clk_gate4(dev, "usb2_phy_root_clk", "usb_phy_ref", base + 0x4500, 0)); clk_dm(IMX8MQ_CLK_ENET_REF, - imx8m_clk_composite("enet_ref", imx8mq_enet_ref_sels, + imx8m_clk_composite(dev, "enet_ref", imx8mq_enet_ref_sels, base + 0xa980)); clk_dm(IMX8MQ_CLK_ENET_TIMER, - imx8m_clk_composite("enet_timer", imx8mq_enet_timer_sels, + imx8m_clk_composite(dev, "enet_timer", imx8mq_enet_timer_sels, base + 0xaa00)); clk_dm(IMX8MQ_CLK_ENET_PHY_REF, - imx8m_clk_composite("enet_phy", imx8mq_enet_phy_sels, + imx8m_clk_composite(dev, "enet_phy", imx8mq_enet_phy_sels, base + 0xaa80)); clk_dm(IMX8MQ_CLK_ENET1_ROOT, - imx_clk_gate4("enet1_root_clk", "enet_axi", + imx_clk_gate4(dev, "enet1_root_clk", "enet_axi", base + 0x40a0, 0)); clk_dm(IMX8MQ_CLK_DRAM_ALT_ROOT, - imx_clk_fixed_factor("dram_alt_root", "dram_alt", 1, 4)); + imx_clk_fixed_factor(dev, "dram_alt_root", "dram_alt", 1, 4)); return 0; } diff --git a/drivers/clk/imx/clk-imx93.c b/drivers/clk/imx/clk-imx93.c index b31e57a4a01..c3112968c17 100644 --- a/drivers/clk/imx/clk-imx93.c +++ b/drivers/clk/imx/clk-imx93.c @@ -291,15 +291,15 @@ static int imx93_clk_probe(struct udevice *dev) clk_dm(IMX93_CLK_SYS_PLL_PFD0, clk_register_fixed_rate(NULL, "sys_pll_pfd0", 1000000000)); clk_dm(IMX93_CLK_SYS_PLL_PFD0_DIV2, - imx_clk_fixed_factor("sys_pll_pfd0_div2", "sys_pll_pfd0", 1, 2)); + imx_clk_fixed_factor(dev, "sys_pll_pfd0_div2", "sys_pll_pfd0", 1, 2)); clk_dm(IMX93_CLK_SYS_PLL_PFD1, clk_register_fixed_rate(NULL, "sys_pll_pfd1", 800000000)); clk_dm(IMX93_CLK_SYS_PLL_PFD1_DIV2, - imx_clk_fixed_factor("sys_pll_pfd1_div2", "sys_pll_pfd1", 1, 2)); + imx_clk_fixed_factor(dev, "sys_pll_pfd1_div2", "sys_pll_pfd1", 1, 2)); clk_dm(IMX93_CLK_SYS_PLL_PFD2, clk_register_fixed_rate(NULL, "sys_pll_pfd2", 625000000)); clk_dm(IMX93_CLK_SYS_PLL_PFD2_DIV2, - imx_clk_fixed_factor("sys_pll_pfd2_div2", "sys_pll_pfd2", 1, 2)); + imx_clk_fixed_factor(dev, "sys_pll_pfd2_div2", "sys_pll_pfd2", 1, 2)); anatop_base = (void *)ANATOP_BASE_ADDR; @@ -338,7 +338,7 @@ static int imx93_clk_probe(struct udevice *dev) } clk_dm(IMX93_CLK_A55_SEL, - imx_clk_mux2("a55_sel", base + 0x4820, 0, 1, + imx_clk_mux2(dev, "a55_sel", base + 0x4820, 0, 1, a55_core_sels, ARRAY_SIZE(a55_core_sels))); return 0; diff --git a/drivers/clk/imx/clk-imxrt1020.c b/drivers/clk/imx/clk-imxrt1020.c index 752434cb0ad..c14afdaf236 100644 --- a/drivers/clk/imx/clk-imxrt1020.c +++ b/drivers/clk/imx/clk-imxrt1020.c @@ -38,26 +38,26 @@ static int imxrt1020_clk_probe(struct udevice *dev) base = (void *)ofnode_get_addr(ofnode_by_compatible(ofnode_null(), "fsl,imxrt-anatop")); clk_dm(IMXRT1020_CLK_PLL2_SYS, - imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_sys", "osc", + imx_clk_pllv3(dev, IMX_PLLV3_GENERIC, "pll2_sys", "osc", base + 0x30, 0x1)); clk_dm(IMXRT1020_CLK_PLL3_USB_OTG, - imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc", + imx_clk_pllv3(dev, IMX_PLLV3_USB, "pll3_usb_otg", "osc", base + 0x10, 0x1)); /* PLL bypass out */ clk_dm(IMXRT1020_CLK_PLL2_BYPASS, - imx_clk_mux_flags("pll2_bypass", base + 0x30, 16, 1, + imx_clk_mux_flags(dev, "pll2_bypass", base + 0x30, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMXRT1020_CLK_PLL3_BYPASS, - imx_clk_mux_flags("pll3_bypass", base + 0x10, 16, 1, + imx_clk_mux_flags(dev, "pll3_bypass", base + 0x10, 16, 1, pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMXRT1020_CLK_PLL3_80M, - imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6)); + imx_clk_fixed_factor(dev, "pll3_80m", "pll3_usb_otg", 1, 6)); clk_dm(IMXRT1020_CLK_PLL2_PFD0_352M, imx_clk_pfd("pll2_pfd0_352m", "pll2_sys", base + 0x100, 0)); @@ -78,51 +78,51 @@ static int imxrt1020_clk_probe(struct udevice *dev) return -EINVAL; clk_dm(IMXRT1020_CLK_PRE_PERIPH_SEL, - imx_clk_mux("pre_periph_sel", base + 0x18, 18, 2, + imx_clk_mux(dev, "pre_periph_sel", base + 0x18, 18, 2, pre_periph_sels, ARRAY_SIZE(pre_periph_sels))); clk_dm(IMXRT1020_CLK_PERIPH_SEL, - imx_clk_mux("periph_sel", base + 0x14, 25, 1, + imx_clk_mux(dev, "periph_sel", base + 0x14, 25, 1, periph_sels, ARRAY_SIZE(periph_sels))); clk_dm(IMXRT1020_CLK_USDHC1_SEL, - imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1, + imx_clk_mux(dev, "usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); clk_dm(IMXRT1020_CLK_USDHC2_SEL, - imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1, + imx_clk_mux(dev, "usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); clk_dm(IMXRT1020_CLK_LPUART_SEL, - imx_clk_mux("lpuart_sel", base + 0x24, 6, 1, + imx_clk_mux(dev, "lpuart_sel", base + 0x24, 6, 1, lpuart_sels, ARRAY_SIZE(lpuart_sels))); clk_dm(IMXRT1020_CLK_SEMC_ALT_SEL, - imx_clk_mux("semc_alt_sel", base + 0x14, 7, 1, + imx_clk_mux(dev, "semc_alt_sel", base + 0x14, 7, 1, semc_alt_sels, ARRAY_SIZE(semc_alt_sels))); clk_dm(IMXRT1020_CLK_SEMC_SEL, - imx_clk_mux("semc_sel", base + 0x14, 6, 1, + imx_clk_mux(dev, "semc_sel", base + 0x14, 6, 1, semc_sels, ARRAY_SIZE(semc_sels))); clk_dm(IMXRT1020_CLK_AHB_PODF, - imx_clk_divider("ahb_podf", "periph_sel", + imx_clk_divider(dev, "ahb_podf", "periph_sel", base + 0x14, 10, 3)); clk_dm(IMXRT1020_CLK_USDHC1_PODF, - imx_clk_divider("usdhc1_podf", "usdhc1_sel", + imx_clk_divider(dev, "usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3)); clk_dm(IMXRT1020_CLK_USDHC2_PODF, - imx_clk_divider("usdhc2_podf", "usdhc2_sel", + imx_clk_divider(dev, "usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3)); clk_dm(IMXRT1020_CLK_LPUART_PODF, - imx_clk_divider("lpuart_podf", "lpuart_sel", + imx_clk_divider(dev, "lpuart_podf", "lpuart_sel", base + 0x24, 0, 6)); clk_dm(IMXRT1020_CLK_SEMC_PODF, - imx_clk_divider("semc_podf", "semc_sel", + imx_clk_divider(dev, "semc_podf", "semc_sel", base + 0x14, 16, 3)); clk_dm(IMXRT1020_CLK_USDHC1, - imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2)); + imx_clk_gate2(dev, "usdhc1", "usdhc1_podf", base + 0x80, 2)); clk_dm(IMXRT1020_CLK_USDHC2, - imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4)); + imx_clk_gate2(dev, "usdhc2", "usdhc2_podf", base + 0x80, 4)); clk_dm(IMXRT1020_CLK_LPUART1, - imx_clk_gate2("lpuart1", "lpuart_podf", base + 0x7c, 24)); + imx_clk_gate2(dev, "lpuart1", "lpuart_podf", base + 0x7c, 24)); clk_dm(IMXRT1020_CLK_SEMC, - imx_clk_gate2("semc", "semc_podf", base + 0x74, 4)); + imx_clk_gate2(dev, "semc", "semc_podf", base + 0x74, 4)); #ifdef CONFIG_XPL_BUILD struct clk *clk, *clk1; diff --git a/drivers/clk/imx/clk-imxrt1050.c b/drivers/clk/imx/clk-imxrt1050.c index 2c029ec5a6e..ba5b48748ef 100644 --- a/drivers/clk/imx/clk-imxrt1050.c +++ b/drivers/clk/imx/clk-imxrt1050.c @@ -36,63 +36,63 @@ static int imxrt1050_clk_probe(struct udevice *dev) base = (void *)ofnode_get_addr(ofnode_by_compatible(ofnode_null(), "fsl,imxrt-anatop")); clk_dm(IMXRT1050_CLK_PLL1_REF_SEL, - imx_clk_mux("pll1_arm_ref_sel", base + 0x0, 14, 2, + imx_clk_mux(dev, "pll1_arm_ref_sel", base + 0x0, 14, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMXRT1050_CLK_PLL2_REF_SEL, - imx_clk_mux("pll2_sys_ref_sel", base + 0x30, 14, 2, + imx_clk_mux(dev, "pll2_sys_ref_sel", base + 0x30, 14, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMXRT1050_CLK_PLL3_REF_SEL, - imx_clk_mux("pll3_usb_otg_ref_sel", base + 0x10, 14, 2, + imx_clk_mux(dev, "pll3_usb_otg_ref_sel", base + 0x10, 14, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMXRT1050_CLK_PLL5_REF_SEL, - imx_clk_mux("pll5_video_ref_sel", base + 0xa0, 14, 2, + imx_clk_mux(dev, "pll5_video_ref_sel", base + 0xa0, 14, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels))); clk_dm(IMXRT1050_CLK_PLL1_ARM, - imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_arm", "pll1_arm_ref_sel", + imx_clk_pllv3(dev, IMX_PLLV3_SYS, "pll1_arm", "pll1_arm_ref_sel", base + 0x0, 0x7f)); clk_dm(IMXRT1050_CLK_PLL2_SYS, - imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_sys", "pll2_sys_ref_sel", + imx_clk_pllv3(dev, IMX_PLLV3_GENERIC, "pll2_sys", "pll2_sys_ref_sel", base + 0x30, 0x1)); clk_dm(IMXRT1050_CLK_PLL3_USB_OTG, - imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", + imx_clk_pllv3(dev, IMX_PLLV3_USB, "pll3_usb_otg", "pll3_usb_otg_ref_sel", base + 0x10, 0x1)); clk_dm(IMXRT1050_CLK_PLL5_VIDEO, - imx_clk_pllv3(IMX_PLLV3_AV, "pll5_video", "pll5_video_ref_sel", + imx_clk_pllv3(dev, IMX_PLLV3_AV, "pll5_video", "pll5_video_ref_sel", base + 0xa0, 0x7f)); /* PLL bypass out */ clk_dm(IMXRT1050_CLK_PLL1_BYPASS, - imx_clk_mux_flags("pll1_bypass", base + 0x0, 16, 1, + imx_clk_mux_flags(dev, "pll1_bypass", base + 0x0, 16, 1, pll1_bypass_sels, ARRAY_SIZE(pll1_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMXRT1050_CLK_PLL2_BYPASS, - imx_clk_mux_flags("pll2_bypass", base + 0x30, 16, 1, + imx_clk_mux_flags(dev, "pll2_bypass", base + 0x30, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMXRT1050_CLK_PLL3_BYPASS, - imx_clk_mux_flags("pll3_bypass", base + 0x10, 16, 1, + imx_clk_mux_flags(dev, "pll3_bypass", base + 0x10, 16, 1, pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMXRT1050_CLK_PLL5_BYPASS, - imx_clk_mux_flags("pll5_bypass", base + 0xa0, 16, 1, + imx_clk_mux_flags(dev, "pll5_bypass", base + 0xa0, 16, 1, pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels), CLK_SET_RATE_PARENT)); clk_dm(IMXRT1050_CLK_VIDEO_POST_DIV_SEL, - imx_clk_divider("video_post_div_sel", "pll5_video", + imx_clk_divider(dev, "video_post_div_sel", "pll5_video", base + 0xa0, 19, 2)); clk_dm(IMXRT1050_CLK_VIDEO_DIV, - imx_clk_divider("video_div", "video_post_div_sel", + imx_clk_divider(dev, "video_div", "video_post_div_sel", base + 0x170, 30, 2)); clk_dm(IMXRT1050_CLK_PLL3_80M, - imx_clk_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6)); + imx_clk_fixed_factor(dev, "pll3_80m", "pll3_usb_otg", 1, 6)); clk_dm(IMXRT1050_CLK_PLL2_PFD0_352M, imx_clk_pfd("pll2_pfd0_352m", "pll2_sys", base + 0x100, 0)); @@ -113,73 +113,73 @@ static int imxrt1050_clk_probe(struct udevice *dev) return -EINVAL; clk_dm(IMXRT1050_CLK_ARM_PODF, - imx_clk_divider("arm_podf", "pll1_arm", + imx_clk_divider(dev, "arm_podf", "pll1_arm", base + 0x10, 0, 3)); clk_dm(IMXRT1050_CLK_PRE_PERIPH_SEL, - imx_clk_mux("pre_periph_sel", base + 0x18, 18, 2, + imx_clk_mux(dev, "pre_periph_sel", base + 0x18, 18, 2, pre_periph_sels, ARRAY_SIZE(pre_periph_sels))); clk_dm(IMXRT1050_CLK_PERIPH_SEL, - imx_clk_mux("periph_sel", base + 0x14, 25, 1, + imx_clk_mux(dev, "periph_sel", base + 0x14, 25, 1, periph_sels, ARRAY_SIZE(periph_sels))); clk_dm(IMXRT1050_CLK_USDHC1_SEL, - imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1, + imx_clk_mux(dev, "usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); clk_dm(IMXRT1050_CLK_USDHC2_SEL, - imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1, + imx_clk_mux(dev, "usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels))); clk_dm(IMXRT1050_CLK_LPUART_SEL, - imx_clk_mux("lpuart_sel", base + 0x24, 6, 1, + imx_clk_mux(dev, "lpuart_sel", base + 0x24, 6, 1, lpuart_sels, ARRAY_SIZE(lpuart_sels))); clk_dm(IMXRT1050_CLK_SEMC_ALT_SEL, - imx_clk_mux("semc_alt_sel", base + 0x14, 7, 1, + imx_clk_mux(dev, "semc_alt_sel", base + 0x14, 7, 1, semc_alt_sels, ARRAY_SIZE(semc_alt_sels))); clk_dm(IMXRT1050_CLK_SEMC_SEL, - imx_clk_mux("semc_sel", base + 0x14, 6, 1, + imx_clk_mux(dev, "semc_sel", base + 0x14, 6, 1, semc_sels, ARRAY_SIZE(semc_sels))); clk_dm(IMXRT1050_CLK_LCDIF_SEL, - imx_clk_mux("lcdif_sel", base + 0x38, 15, 3, + imx_clk_mux(dev, "lcdif_sel", base + 0x38, 15, 3, lcdif_sels, ARRAY_SIZE(lcdif_sels))); clk_dm(IMXRT1050_CLK_AHB_PODF, - imx_clk_divider("ahb_podf", "periph_sel", + imx_clk_divider(dev, "ahb_podf", "periph_sel", base + 0x14, 10, 3)); clk_dm(IMXRT1050_CLK_IPG_PDOF, - imx_clk_divider("ipg_podf", "ahb_podf", + imx_clk_divider(dev, "ipg_podf", "ahb_podf", base + 0x14, 8, 2)); clk_dm(IMXRT1050_CLK_USDHC1_PODF, - imx_clk_divider("usdhc1_podf", "usdhc1_sel", + imx_clk_divider(dev, "usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3)); clk_dm(IMXRT1050_CLK_USDHC2_PODF, - imx_clk_divider("usdhc2_podf", "usdhc2_sel", + imx_clk_divider(dev, "usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3)); clk_dm(IMXRT1050_CLK_LPUART_PODF, - imx_clk_divider("lpuart_podf", "lpuart_sel", + imx_clk_divider(dev, "lpuart_podf", "lpuart_sel", base + 0x24, 0, 6)); clk_dm(IMXRT1050_CLK_SEMC_PODF, - imx_clk_divider("semc_podf", "semc_sel", + imx_clk_divider(dev, "semc_podf", "semc_sel", base + 0x14, 16, 3)); clk_dm(IMXRT1050_CLK_LCDIF_PRED, - imx_clk_divider("lcdif_pred", "lcdif_sel", + imx_clk_divider(dev, "lcdif_pred", "lcdif_sel", base + 0x38, 12, 3)); clk_dm(IMXRT1050_CLK_LCDIF_PODF, - imx_clk_divider("lcdif_podf", "lcdif_pred", + imx_clk_divider(dev, "lcdif_podf", "lcdif_pred", base + 0x18, 23, 3)); clk_dm(IMXRT1050_CLK_USDHC1, - imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2)); + imx_clk_gate2(dev, "usdhc1", "usdhc1_podf", base + 0x80, 2)); clk_dm(IMXRT1050_CLK_USDHC2, - imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4)); + imx_clk_gate2(dev, "usdhc2", "usdhc2_podf", base + 0x80, 4)); clk_dm(IMXRT1050_CLK_LPUART1, - imx_clk_gate2("lpuart1", "lpuart_podf", base + 0x7c, 24)); + imx_clk_gate2(dev, "lpuart1", "lpuart_podf", base + 0x7c, 24)); clk_dm(IMXRT1050_CLK_SEMC, - imx_clk_gate2("semc", "semc_podf", base + 0x74, 4)); + imx_clk_gate2(dev, "semc", "semc_podf", base + 0x74, 4)); clk_dm(IMXRT1050_CLK_LCDIF_APB, - imx_clk_gate2("lcdif", "lcdif_podf", base + 0x70, 28)); + imx_clk_gate2(dev, "lcdif", "lcdif_podf", base + 0x70, 28)); clk_dm(IMXRT1050_CLK_LCDIF_PIX, - imx_clk_gate2("lcdif_pix", "lcdif", base + 0x74, 10)); + imx_clk_gate2(dev, "lcdif_pix", "lcdif", base + 0x74, 10)); clk_dm(IMXRT1050_CLK_USBOH3, - imx_clk_gate2("usboh3", "pll3_usb_otg", base + 0x80, 0)); + imx_clk_gate2(dev, "usboh3", "pll3_usb_otg", base + 0x80, 0)); struct clk *clk, *clk1; diff --git a/drivers/clk/imx/clk-imxrt1170.c b/drivers/clk/imx/clk-imxrt1170.c index 88a294f4165..3f55d0d0127 100644 --- a/drivers/clk/imx/clk-imxrt1170.c +++ b/drivers/clk/imx/clk-imxrt1170.c @@ -114,20 +114,20 @@ static int imxrt1170_clk_probe(struct udevice *dev) base = (void *)ofnode_get_addr(ofnode_by_compatible(ofnode_null(), "fsl,imxrt-anatop")); clk_dm(IMXRT1170_CLK_RCOSC_48M, - imx_clk_fixed_factor("rcosc48M", "rcosc16M", 3, 1)); + imx_clk_fixed_factor(dev, "rcosc48M", "rcosc16M", 3, 1)); clk_dm(IMXRT1170_CLK_RCOSC_400M, - imx_clk_fixed_factor("rcosc400M", "rcosc16M", 25, 1)); + imx_clk_fixed_factor(dev, "rcosc400M", "rcosc16M", 25, 1)); clk_dm(IMXRT1170_CLK_RCOSC_48M_DIV2, - imx_clk_fixed_factor("rcosc48M_div2", "rcosc48M", 1, 2)); + imx_clk_fixed_factor(dev, "rcosc48M_div2", "rcosc48M", 1, 2)); clk_dm(IMXRT1170_CLK_PLL_ARM, - imx_clk_pllv3(IMX_PLLV3_SYS, "pll_arm", "osc", + imx_clk_pllv3(dev, IMX_PLLV3_SYS, "pll_arm", "osc", base + 0x200, 0xff)); clk_dm(IMXRT1170_CLK_PLL3, - imx_clk_pllv3(IMX_PLLV3_GENERICV2, "pll3_sys", "osc", + imx_clk_pllv3(dev, IMX_PLLV3_GENERICV2, "pll3_sys", "osc", base + 0x210, 1)); clk_dm(IMXRT1170_CLK_PLL2, - imx_clk_pllv3(IMX_PLLV3_GENERICV2, "pll2_sys", "osc", + imx_clk_pllv3(dev, IMX_PLLV3_GENERICV2, "pll2_sys", "osc", base + 0x240, 1)); clk_dm(IMXRT1170_CLK_PLL3_PFD0, @@ -149,7 +149,7 @@ static int imxrt1170_clk_probe(struct udevice *dev) imx_clk_pfd("pll2_pfd3", "pll2_sys", base + 0x270, 3)); clk_dm(IMXRT1170_CLK_PLL3_DIV2, - imx_clk_fixed_factor("pll3_div2", "pll3_sys", 1, 2)); + imx_clk_fixed_factor(dev, "pll3_div2", "pll3_sys", 1, 2)); /* CCM clocks */ base = dev_read_addr_ptr(dev); @@ -157,31 +157,31 @@ static int imxrt1170_clk_probe(struct udevice *dev) return -EINVAL; clk_dm(IMXRT1170_CLK_LPUART1_SEL, - imx_clk_mux("lpuart1_sel", base + (25 * 0x80), 8, 3, + imx_clk_mux(dev, "lpuart1_sel", base + (25 * 0x80), 8, 3, lpuart1_sels, ARRAY_SIZE(lpuart1_sels))); clk_dm(IMXRT1170_CLK_LPUART1, - imx_clk_divider("lpuart1", "lpuart1_sel", + imx_clk_divider(dev, "lpuart1", "lpuart1_sel", base + (25 * 0x80), 0, 8)); clk_dm(IMXRT1170_CLK_USDHC1_SEL, - imx_clk_mux("usdhc1_sel", base + (58 * 0x80), 8, 3, + imx_clk_mux(dev, "usdhc1_sel", base + (58 * 0x80), 8, 3, usdhc1_sels, ARRAY_SIZE(usdhc1_sels))); clk_dm(IMXRT1170_CLK_USDHC1, - imx_clk_divider("usdhc1", "usdhc1_sel", + imx_clk_divider(dev, "usdhc1", "usdhc1_sel", base + (58 * 0x80), 0, 8)); clk_dm(IMXRT1170_CLK_GPT1_SEL, - imx_clk_mux("gpt1_sel", base + (14 * 0x80), 8, 3, + imx_clk_mux(dev, "gpt1_sel", base + (14 * 0x80), 8, 3, gpt1_sels, ARRAY_SIZE(gpt1_sels))); clk_dm(IMXRT1170_CLK_GPT1, - imx_clk_divider("gpt1", "gpt1_sel", + imx_clk_divider(dev, "gpt1", "gpt1_sel", base + (14 * 0x80), 0, 8)); clk_dm(IMXRT1170_CLK_SEMC_SEL, - imx_clk_mux("semc_sel", base + (4 * 0x80), 8, 3, + imx_clk_mux(dev, "semc_sel", base + (4 * 0x80), 8, 3, semc_sels, ARRAY_SIZE(semc_sels))); clk_dm(IMXRT1170_CLK_SEMC, - imx_clk_divider("semc", "semc_sel", + imx_clk_divider(dev, "semc", "semc_sel", base + (4 * 0x80), 0, 8)); struct clk *clk, *clk1; diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c index c6692f2f9f5..85b6a9809e8 100644 --- a/drivers/clk/imx/clk-pllv3.c +++ b/drivers/clk/imx/clk-pllv3.c @@ -281,9 +281,9 @@ static const struct clk_ops clk_pllv3_enet_ops = { .get_rate = clk_pllv3_enet_get_rate, }; -struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, - const char *parent_name, void __iomem *base, - u32 div_mask) +struct clk *imx_clk_pllv3(struct udevice *dev, enum imx_pllv3_type type, + const char *name, const char *parent_name, + void __iomem *base, u32 div_mask) { struct clk_pllv3 *pll; struct clk *clk; @@ -339,7 +339,8 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, pll->div_mask = div_mask; clk = &pll->clk; - ret = clk_register(clk, drv_name, name, parent_name); + ret = clk_register(clk, drv_name, name, + clk_resolve_parent_clk(dev, parent_name)); if (ret) { kfree(pll); return ERR_PTR(ret); diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 27a53ae5583..7d14dbc395f 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -78,84 +78,89 @@ struct clk *imx_clk_pll14xx(const char *name, const char *parent_name, void __iomem *base, const struct imx_pll14xx_clk *pll_clk); -struct clk *clk_register_gate2(struct device *dev, const char *name, +struct clk *clk_register_gate2(struct udevice *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 cgr_val, u8 clk_gate_flags, unsigned int *share_count); -struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, - const char *parent_name, void __iomem *base, - u32 div_mask); +struct clk *imx_clk_pllv3(struct udevice *dev, enum imx_pllv3_type type, + const char *name, const char *parent_name, + void __iomem *base, u32 div_mask); -static inline struct clk *imx_clk_gate2(const char *name, const char *parent, - void __iomem *reg, u8 shift) +static inline struct clk *imx_clk_gate2(struct udevice *dev, const char *name, + const char *parent, void __iomem *reg, + u8 shift) { - return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, + return clk_register_gate2(dev, name, parent, CLK_SET_RATE_PARENT, reg, shift, 0x3, 0, NULL); } -static inline struct clk *imx_clk_gate2_shared(const char *name, +static inline struct clk *imx_clk_gate2_shared(struct udevice *dev, const char *name, const char *parent, void __iomem *reg, u8 shift, unsigned int *share_count) { - return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg, + return clk_register_gate2(dev, name, parent, CLK_SET_RATE_PARENT, reg, shift, 0x3, 0, share_count); } -static inline struct clk *imx_clk_gate2_shared2(const char *name, +static inline struct clk *imx_clk_gate2_shared2(struct udevice *dev, const char *name, const char *parent, void __iomem *reg, u8 shift, unsigned int *share_count) { - return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT | + return clk_register_gate2(dev, name, parent, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, reg, shift, 0x3, 0, share_count); } -static inline struct clk *imx_clk_gate4(const char *name, const char *parent, +static inline struct clk *imx_clk_gate4(struct udevice *dev, const char *name, const char *parent, void __iomem *reg, u8 shift) { - return clk_register_gate2(NULL, name, parent, + return clk_register_gate2(dev, name, parent, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, reg, shift, 0x3, 0, NULL); } -static inline struct clk *imx_clk_gate4_flags(const char *name, +static inline struct clk *imx_clk_gate4_flags(struct udevice *dev, const char *name, const char *parent, void __iomem *reg, u8 shift, unsigned long flags) { - return clk_register_gate2(NULL, name, parent, + return clk_register_gate2(dev, name, parent, flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, reg, shift, 0x3, 0, NULL); } -static inline struct clk *imx_clk_fixed_factor(const char *name, - const char *parent, unsigned int mult, unsigned int div) +static inline struct clk * +imx_clk_fixed_factor(struct udevice *dev, const char *name, const char *parent, + unsigned int mult, unsigned int div) { - return clk_register_fixed_factor(NULL, name, parent, + return clk_register_fixed_factor(dev, name, parent, CLK_SET_RATE_PARENT, mult, div); } -static inline struct clk *imx_clk_divider(const char *name, const char *parent, - void __iomem *reg, u8 shift, u8 width) +static inline struct clk *imx_clk_divider(struct udevice *dev, const char *name, + const char *parent, void __iomem *reg, + u8 shift, u8 width) { - return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT, + return clk_register_divider(dev, name, parent, CLK_SET_RATE_PARENT, reg, shift, width, 0); } static inline struct clk * -imx_clk_busy_divider(const char *name, const char *parent, void __iomem *reg, - u8 shift, u8 width, void __iomem *busy_reg, u8 busy_shift) +imx_clk_busy_divider(struct udevice *dev, const char *name, + const char *parent, void __iomem *reg, u8 shift, u8 width, + void __iomem *busy_reg, u8 busy_shift) { - return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT, + return clk_register_divider(dev, name, parent, CLK_SET_RATE_PARENT, reg, shift, width, 0); } -static inline struct clk *imx_clk_divider2(const char *name, const char *parent, - void __iomem *reg, u8 shift, u8 width) +static inline struct clk *imx_clk_divider2(struct udevice *dev, const char *name, + const char *parent, void __iomem *reg, + u8 shift, u8 width) { - return clk_register_divider(NULL, name, parent, + return clk_register_divider(dev, name, parent, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, reg, shift, width, 0); } @@ -167,90 +172,93 @@ struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg, u8 shift, u8 width, const char * const *parents, int num_parents, void (*fixup)(u32 *val)); -static inline struct clk *imx_clk_mux_flags(const char *name, +static inline struct clk *imx_clk_mux_flags(struct udevice *dev, const char *name, void __iomem *reg, u8 shift, u8 width, const char * const *parents, int num_parents, unsigned long flags) { - return clk_register_mux(NULL, name, parents, num_parents, + return clk_register_mux(dev, name, parents, num_parents, flags | CLK_SET_RATE_NO_REPARENT, reg, shift, width, 0); } -static inline struct clk *imx_clk_mux2_flags(const char *name, +static inline struct clk *imx_clk_mux2_flags(struct udevice *dev, const char *name, void __iomem *reg, u8 shift, u8 width, const char * const *parents, int num_parents, unsigned long flags) { - return clk_register_mux(NULL, name, parents, num_parents, + return clk_register_mux(dev, name, parents, num_parents, flags | CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE, reg, shift, width, 0); } -static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg, - u8 shift, u8 width, const char * const *parents, +static inline struct clk *imx_clk_mux(struct udevice *dev, const char *name, + void __iomem *reg, u8 shift, u8 width, const char * const *parents, int num_parents) { - return clk_register_mux(NULL, name, parents, num_parents, + return clk_register_mux(dev, name, parents, num_parents, CLK_SET_RATE_NO_REPARENT, reg, shift, width, 0); } static inline struct clk * -imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift, u8 width, +imx_clk_busy_mux(struct udevice *dev, const char *name, void __iomem *reg, u8 shift, u8 width, void __iomem *busy_reg, u8 busy_shift, const char * const *parents, int num_parents) { - return clk_register_mux(NULL, name, parents, num_parents, + return clk_register_mux(dev, name, parents, num_parents, CLK_SET_RATE_NO_REPARENT, reg, shift, width, 0); } -static inline struct clk *imx_clk_mux2(const char *name, void __iomem *reg, +static inline struct clk *imx_clk_mux2(struct udevice *dev, const char *name, void __iomem *reg, u8 shift, u8 width, const char * const *parents, int num_parents) { - return clk_register_mux(NULL, name, parents, num_parents, + return clk_register_mux(dev, name, parents, num_parents, CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE, reg, shift, width, 0); } -static inline struct clk *imx_clk_gate(const char *name, const char *parent, - void __iomem *reg, u8 shift) +static inline struct clk *imx_clk_gate(struct udevice *dev, const char *name, + const char *parent, void __iomem *reg, + u8 shift) { - return clk_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT, reg, + return clk_register_gate(dev, name, parent, CLK_SET_RATE_PARENT, reg, shift, 0, NULL); } -static inline struct clk *imx_clk_gate_flags(const char *name, const char *parent, - void __iomem *reg, u8 shift, unsigned long flags) +static inline struct clk *imx_clk_gate_flags(struct udevice *dev, const char *name, + const char *parent, void __iomem *reg, + u8 shift, unsigned long flags) { - return clk_register_gate(NULL, name, parent, flags | CLK_SET_RATE_PARENT, reg, + return clk_register_gate(dev, name, parent, flags | CLK_SET_RATE_PARENT, reg, shift, 0, NULL); } -static inline struct clk *imx_clk_gate3(const char *name, const char *parent, - void __iomem *reg, u8 shift) +static inline struct clk *imx_clk_gate3(struct udevice *dev, const char *name, + const char *parent, void __iomem *reg, + u8 shift) { - return clk_register_gate(NULL, name, parent, + return clk_register_gate(dev, name, parent, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, reg, shift, 0, NULL); } -struct clk *imx8m_clk_composite_flags(const char *name, +struct clk *imx8m_clk_composite_flags(struct udevice *dev, const char *name, const char * const *parent_names, int num_parents, void __iomem *reg, unsigned long flags); -#define __imx8m_clk_composite(name, parent_names, reg, flags) \ - imx8m_clk_composite_flags(name, parent_names, \ +#define __imx8m_clk_composite(dev, name, parent_names, reg, flags) \ + imx8m_clk_composite_flags(dev, name, parent_names, \ ARRAY_SIZE(parent_names), reg, \ flags | CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE) -#define imx8m_clk_composite(name, parent_names, reg) \ - __imx8m_clk_composite(name, parent_names, reg, 0) +#define imx8m_clk_composite(dev, name, parent_names, reg) \ + __imx8m_clk_composite(dev, name, parent_names, reg, 0) -#define imx8m_clk_composite_critical(name, parent_names, reg) \ - __imx8m_clk_composite(name, parent_names, reg, CLK_IS_CRITICAL) +#define imx8m_clk_composite_critical(dev, name, parent_names, reg) \ + __imx8m_clk_composite(dev, name, parent_names, reg, CLK_IS_CRITICAL) struct clk *imx93_clk_composite_flags(const char *name, const char * const *parent_names, diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index e412c92cafc..12893687b68 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -3,7 +3,6 @@ obj-$(CONFIG_ARCH_MEDIATEK) += clk-mtk.o # SoC Drivers -obj-$(CONFIG_MT8512) += clk-mt8512.o obj-$(CONFIG_TARGET_MT7623) += clk-mt7623.o obj-$(CONFIG_TARGET_MT7622) += clk-mt7622.o obj-$(CONFIG_TARGET_MT7629) += clk-mt7629.o @@ -13,5 +12,6 @@ obj-$(CONFIG_TARGET_MT7988) += clk-mt7988.o obj-$(CONFIG_TARGET_MT7987) += clk-mt7987.o obj-$(CONFIG_TARGET_MT8183) += clk-mt8183.o obj-$(CONFIG_TARGET_MT8365) += clk-mt8365.o +obj-$(CONFIG_TARGET_MT8512) += clk-mt8512.o obj-$(CONFIG_TARGET_MT8516) += clk-mt8516.o obj-$(CONFIG_TARGET_MT8518) += clk-mt8518.o diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index cb867acc48c..3ea01f3c969 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -31,6 +31,14 @@ config CLK_QCOM_IPQ4019 on the Snapdragon IPQ4019 SoC. This driver supports the clocks and resets exposed by the GCC hardware block. +config CLK_QCOM_IPQ9574 + bool "Qualcomm IPQ9574 GCC" + select CLK_QCOM + help + Say Y here to enable support for the Global Clock Controller + on the Snapdragon IPQ9574 SoC. This driver supports the clocks + and resets exposed by the GCC hardware block. + config CLK_QCOM_QCM2290 bool "Qualcomm QCM2290 GCC" select CLK_QCOM diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 1bc0f15005b..e13fc8c1071 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_CLK_QCOM_SDM845) += clock-sdm845.o obj-$(CONFIG_CLK_QCOM_APQ8016) += clock-apq8016.o obj-$(CONFIG_CLK_QCOM_APQ8096) += clock-apq8096.o obj-$(CONFIG_CLK_QCOM_IPQ4019) += clock-ipq4019.o +obj-$(CONFIG_CLK_QCOM_IPQ9574) += clock-ipq9574.o obj-$(CONFIG_CLK_QCOM_QCM2290) += clock-qcm2290.o obj-$(CONFIG_CLK_QCOM_QCS404) += clock-qcs404.o obj-$(CONFIG_CLK_QCOM_SA8775P) += clock-sa8775p.o diff --git a/drivers/clk/qcom/clock-apq8016.c b/drivers/clk/qcom/clock-apq8016.c index b5def55dbc2..6a53f900a9e 100644 --- a/drivers/clk/qcom/clock-apq8016.c +++ b/drivers/clk/qcom/clock-apq8016.c @@ -54,8 +54,9 @@ static struct vote_clk gcc_blsp1_ahb_clk = { }; static const struct gate_clk apq8016_clks[] = { - GATE_CLK(GCC_USB_HS_AHB_CLK, 0x41008, 0x00000001), - GATE_CLK(GCC_USB_HS_SYSTEM_CLK, 0x41004, 0x00000001), + GATE_CLK(GCC_PRNG_AHB_CLK, 0x45004, BIT(8)), + GATE_CLK(GCC_USB_HS_AHB_CLK, 0x41008, BIT(0)), + GATE_CLK(GCC_USB_HS_SYSTEM_CLK, 0x41004, BIT(0)), }; /* SDHCI */ @@ -139,15 +140,14 @@ static int apq8016_clk_enable(struct clk *clk) { struct msm_clk_priv *priv = dev_get_priv(clk->dev); - if (priv->data->num_clks < clk->id) { + if (priv->data->num_clks < clk->id || !apq8016_clks[clk->id].reg) { log_warning("%s: unknown clk id %lu\n", __func__, clk->id); return 0; } - debug("%s: clk %s\n", __func__, apq8016_clks[clk->id].name); - qcom_gate_clk_en(priv, clk->id); + debug("%s: enabling clock %s\n", __func__, apq8016_clks[clk->id].name); - return 0; + return qcom_gate_clk_en(priv, clk->id); } static struct msm_clk_data apq8016_clk_data = { diff --git a/drivers/clk/qcom/clock-ipq9574.c b/drivers/clk/qcom/clock-ipq9574.c new file mode 100644 index 00000000000..b0af4036059 --- /dev/null +++ b/drivers/clk/qcom/clock-ipq9574.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Clock drivers for Qualcomm ipq9574 + * + * (C) Copyright 2025 Linaro Ltd. + */ + +#include <linux/types.h> +#include <clk-uclass.h> +#include <dm.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <linux/bug.h> +#include <linux/bitops.h> +#include <dt-bindings/clock/qcom,ipq9574-gcc.h> +#include <dt-bindings/reset/qcom,ipq9574-gcc.h> + +#include "clock-qcom.h" + +#define GCC_BLSP1_AHB_CBCR 0x1004 +#define GCC_BLSP1_UART3_APPS_CMD_RCGR 0x402C +#define GCC_BLSP1_UART3_APPS_CBCR 0x4054 + +#define GCC_SDCC1_APPS_CBCR 0x3302C +#define GCC_SDCC1_AHB_CBCR 0x33034 +#define GCC_SDCC1_APPS_CMD_RCGR 0x33004 +#define GCC_SDCC1_ICE_CORE_CBCR 0x33030 + +static ulong ipq9574_set_rate(struct clk *clk, ulong rate) +{ + struct msm_clk_priv *priv = dev_get_priv(clk->dev); + + switch (clk->id) { + case GCC_BLSP1_UART3_APPS_CLK: + clk_rcg_set_rate_mnd(priv->base, GCC_BLSP1_UART3_APPS_CMD_RCGR, + 0, 144, 15625, CFG_CLK_SRC_GPLL0, 16); + return rate; + case GCC_SDCC1_APPS_CLK: + clk_rcg_set_rate_mnd(priv->base, GCC_SDCC1_APPS_CMD_RCGR, + 11, 0, 0, CFG_CLK_SRC_GPLL2, 16); + return rate; + default: + return -EINVAL; + } +} + +static const struct gate_clk ipq9574_clks[] = { + GATE_CLK(GCC_BLSP1_UART3_APPS_CLK, 0x4054, 0x00000001), + GATE_CLK(GCC_BLSP1_AHB_CLK, 0x1004, 0x00000001), + GATE_CLK(GCC_SDCC1_AHB_CLK, 0x33034, 0x00000001), + GATE_CLK(GCC_SDCC1_APPS_CLK, 0x3302C, 0x00000001), + GATE_CLK(GCC_SDCC1_ICE_CORE_CLK, 0x33030, 0x00000001), +}; + +static int ipq9574_enable(struct clk *clk) +{ + struct msm_clk_priv *priv = dev_get_priv(clk->dev); + + debug("%s: clk %s\n", __func__, ipq9574_clks[clk->id].name); + + if (!ipq9574_clks[clk->id].reg) + return -EINVAL; + + qcom_gate_clk_en(priv, clk->id); + + return 0; +} + +static const struct qcom_reset_map ipq9574_gcc_resets[] = { + [GCC_SDCC_BCR] = { 0x33000 }, +}; + +static struct msm_clk_data ipq9574_gcc_data = { + .resets = ipq9574_gcc_resets, + .num_resets = ARRAY_SIZE(ipq9574_gcc_resets), + .enable = ipq9574_enable, + .set_rate = ipq9574_set_rate, +}; + +static const struct udevice_id gcc_ipq9574_of_match[] = { + { + .compatible = "qcom,ipq9574-gcc", + .data = (ulong)&ipq9574_gcc_data, + }, + { } +}; + +U_BOOT_DRIVER(gcc_ipq9574) = { + .name = "gcc_ipq9574", + .id = UCLASS_NOP, + .of_match = gcc_ipq9574_of_match, + .bind = qcom_cc_bind, + .flags = DM_FLAG_PRE_RELOC | DM_FLAG_DEFAULT_PD_CTRL_OFF, +}; diff --git a/drivers/clk/qcom/clock-qcm2290.c b/drivers/clk/qcom/clock-qcm2290.c index c78705cb8cf..1326b770c3e 100644 --- a/drivers/clk/qcom/clock-qcm2290.c +++ b/drivers/clk/qcom/clock-qcm2290.c @@ -134,9 +134,7 @@ static int qcm2290_enable(struct clk *clk) break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map qcm2290_gcc_resets[] = { diff --git a/drivers/clk/qcom/clock-qcom.h b/drivers/clk/qcom/clock-qcom.h index ff336dea39c..f43edea2525 100644 --- a/drivers/clk/qcom/clock-qcom.h +++ b/drivers/clk/qcom/clock-qcom.h @@ -7,10 +7,12 @@ #include <asm/io.h> #include <linux/bitfield.h> +#include <errno.h> #define CFG_CLK_SRC_CXO (0 << 8) #define CFG_CLK_SRC_GPLL0 (1 << 8) #define CFG_CLK_SRC_GPLL0_AUX2 (2 << 8) +#define CFG_CLK_SRC_GPLL2 (2 << 8) #define CFG_CLK_SRC_GPLL9 (2 << 8) #define CFG_CLK_SRC_GPLL0_ODD (3 << 8) #define CFG_CLK_SRC_GPLL6 (4 << 8) @@ -105,14 +107,19 @@ void clk_rcg_set_rate(phys_addr_t base, uint32_t cmd_rcgr, int div, int source); void clk_phy_mux_enable(phys_addr_t base, uint32_t cmd_rcgr, bool enabled); -static inline void qcom_gate_clk_en(const struct msm_clk_priv *priv, unsigned long id) +static inline int qcom_gate_clk_en(const struct msm_clk_priv *priv, unsigned long id) { u32 val; - if (id >= priv->data->num_clks || priv->data->clks[id].reg == 0) - return; + if (id >= priv->data->num_clks || priv->data->clks[id].reg == 0) { + log_err("gcc@%#08llx: unknown clock ID %lu!\n", + priv->base, id); + return -ENOENT; + } val = readl(priv->base + priv->data->clks[id].reg); writel(val | priv->data->clks[id].en_val, priv->base + priv->data->clks[id].reg); + + return 0; } #endif diff --git a/drivers/clk/qcom/clock-sa8775p.c b/drivers/clk/qcom/clock-sa8775p.c index e31f24ed4f0..527cecf5c82 100644 --- a/drivers/clk/qcom/clock-sa8775p.c +++ b/drivers/clk/qcom/clock-sa8775p.c @@ -73,9 +73,7 @@ static int sa8775p_enable(struct clk *clk) break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map sa8775p_gcc_resets[] = { diff --git a/drivers/clk/qcom/clock-sc7280.c b/drivers/clk/qcom/clock-sc7280.c index 5d343f12051..8691f08109b 100644 --- a/drivers/clk/qcom/clock-sc7280.c +++ b/drivers/clk/qcom/clock-sc7280.c @@ -16,29 +16,64 @@ #include "clock-qcom.h" -#define USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR 0xf038 #define USB30_PRIM_MASTER_CLK_CMD_RCGR 0xf020 +#define USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR 0xf038 +#define USB30_SEC_MASTER_CLK_CMD_RCGR 0x9e020 +#define USB30_SEC_MOCK_UTMI_CLK_CMD_RCGR 0x9e038 +#define PCIE_1_AUX_CLK_CMD_RCGR 0x8d058 +#define PCIE1_PHY_RCHNG_CMD_RCGR 0x8d03c +#define PCIE_1_PIPE_CLK_PHY_MUX 0x8d054 + +static const struct freq_tbl ftbl_gcc_usb30_prim_master_clk_src[] = { + F(66666667, CFG_CLK_SRC_GPLL0_EVEN, 4.5, 0, 0), + F(133333333, CFG_CLK_SRC_GPLL0, 4.5, 0, 0), + F(200000000, CFG_CLK_SRC_GPLL0_ODD, 1, 0, 0), + F(240000000, CFG_CLK_SRC_GPLL0, 2.5, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_gcc_usb30_sec_master_clk_src[] = { + F(60000000, CFG_CLK_SRC_GPLL0_EVEN, 5, 0, 0), + F(120000000, CFG_CLK_SRC_GPLL0_EVEN, 2.5, 0, 0), + { } +}; static ulong sc7280_set_rate(struct clk *clk, ulong rate) { struct msm_clk_priv *priv = dev_get_priv(clk->dev); + const struct freq_tbl *freq; if (clk->id < priv->data->num_clks) debug("%s: %s, requested rate=%ld\n", __func__, priv->data->clks[clk->id].name, rate); switch (clk->id) { - case GCC_USB30_PRIM_MOCK_UTMI_CLK: - WARN(rate != 19200000, "Unexpected rate for USB30_PRIM_MOCK_UTMI_CLK: %lu\n", rate); - clk_rcg_set_rate(priv->base, USB30_PRIM_MASTER_CLK_CMD_RCGR, 0, CFG_CLK_SRC_CXO); - return rate; case GCC_USB30_PRIM_MASTER_CLK: - WARN(rate != 200000000, "Unexpected rate for USB30_PRIM_MASTER_CLK: %lu\n", rate); + freq = qcom_find_freq(ftbl_gcc_usb30_prim_master_clk_src, rate); clk_rcg_set_rate_mnd(priv->base, USB30_PRIM_MASTER_CLK_CMD_RCGR, - 1, 0, 0, CFG_CLK_SRC_GPLL0_ODD, 8); - clk_rcg_set_rate(priv->base, 0xf064, 0, 0); - return rate; + freq->pre_div, freq->m, freq->n, freq->src, 8); + return freq->freq; + case GCC_USB30_PRIM_MOCK_UTMI_CLK: + clk_rcg_set_rate(priv->base, USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR, 1, 0); + return 19200000; + case GCC_USB3_PRIM_PHY_AUX_CLK_SRC: + clk_rcg_set_rate(priv->base, USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR, 1, 0); + return 19200000; + case GCC_USB30_SEC_MASTER_CLK: + freq = qcom_find_freq(ftbl_gcc_usb30_sec_master_clk_src, rate); + clk_rcg_set_rate_mnd(priv->base, USB30_SEC_MASTER_CLK_CMD_RCGR, + freq->pre_div, freq->m, freq->n, freq->src, 8); + return freq->freq; + case GCC_USB30_SEC_MOCK_UTMI_CLK: + clk_rcg_set_rate(priv->base, USB30_SEC_MOCK_UTMI_CLK_CMD_RCGR, 1, 0); + return 19200000; + case GCC_USB3_SEC_PHY_AUX_CLK_SRC: + clk_rcg_set_rate(priv->base, USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR, 1, 0); + return 19200000; + case GCC_PCIE1_PHY_RCHNG_CLK: + clk_rcg_set_rate(priv->base, PCIE1_PHY_RCHNG_CMD_RCGR, 5, CFG_CLK_SRC_GPLL0_EVEN); + return 100000000; default: - return 0; + return rate; } } @@ -50,13 +85,35 @@ static const struct gate_clk sc7280_clks[] = { GATE_CLK(GCC_USB30_PRIM_MOCK_UTMI_CLK, 0xf01c, 1), GATE_CLK(GCC_USB3_PRIM_PHY_AUX_CLK, 0xf054, 1), GATE_CLK(GCC_USB3_PRIM_PHY_COM_AUX_CLK, 0xf058, 1), + GATE_CLK(GCC_CFG_NOC_USB3_SEC_AXI_CLK, 0x9e07c, 1), + GATE_CLK(GCC_USB30_SEC_MASTER_CLK, 0x9e010, 1), + GATE_CLK(GCC_AGGRE_USB3_SEC_AXI_CLK, 0x9e080, 1), + GATE_CLK(GCC_USB30_SEC_SLEEP_CLK, 0x9e018, 1), + GATE_CLK(GCC_USB30_SEC_MOCK_UTMI_CLK, 0x9e01c, 1), + GATE_CLK(GCC_USB3_SEC_PHY_AUX_CLK, 0x9e054, 1), + GATE_CLK(GCC_USB3_SEC_PHY_COM_AUX_CLK, 0x9e058, 1), + GATE_CLK(GCC_PCIE_CLKREF_EN, 0x8c004, 1), + GATE_CLK(GCC_PCIE_1_PIPE_CLK, 0x52000, BIT(30)), + GATE_CLK(GCC_PCIE_1_AUX_CLK, 0x52000, BIT(29)), + GATE_CLK(GCC_PCIE_1_CFG_AHB_CLK, 0x52000, BIT(28)), + GATE_CLK(GCC_PCIE_1_MSTR_AXI_CLK, 0x52000, BIT(27)), + GATE_CLK(GCC_PCIE_1_SLV_AXI_CLK, 0x52000, BIT(26)), + GATE_CLK(GCC_PCIE_1_SLV_Q2A_AXI_CLK, 0x52000, BIT(25)), + GATE_CLK(GCC_PCIE1_PHY_RCHNG_CLK, 0x52000, BIT(23)), + GATE_CLK(GCC_DDRSS_PCIE_SF_CLK, 0x52000, BIT(19)), + GATE_CLK(GCC_AGGRE_NOC_PCIE_TBU_CLK, 0x52000, BIT(18)), + GATE_CLK(GCC_AGGRE_NOC_PCIE_1_AXI_CLK, 0x52000, BIT(11)), + GATE_CLK(GCC_AGGRE_NOC_PCIE_CENTER_SF_AXI_CLK, 0x52008, BIT(28)), + GATE_CLK(GCC_QUPV3_WRAP0_S0_CLK, 0x52008, BIT(10)), + GATE_CLK(GCC_QUPV3_WRAP0_S1_CLK, 0x52008, BIT(11)), + GATE_CLK(GCC_QUPV3_WRAP0_S3_CLK, 0x52008, BIT(13)), }; static int sc7280_enable(struct clk *clk) { struct msm_clk_priv *priv = dev_get_priv(clk->dev); - if (priv->data->num_clks < clk->id) { + if (priv->data->num_clks <= clk->id) { debug("%s: unknown clk id %lu\n", __func__, clk->id); return 0; } @@ -71,11 +128,32 @@ static int sc7280_enable(struct clk *clk) qcom_gate_clk_en(priv, GCC_USB3_PRIM_PHY_AUX_CLK); qcom_gate_clk_en(priv, GCC_USB3_PRIM_PHY_COM_AUX_CLK); break; + case GCC_AGGRE_USB3_SEC_AXI_CLK: + qcom_gate_clk_en(priv, GCC_USB30_SEC_MASTER_CLK); + fallthrough; + case GCC_USB30_SEC_MASTER_CLK: + qcom_gate_clk_en(priv, GCC_USB3_SEC_PHY_AUX_CLK); + qcom_gate_clk_en(priv, GCC_USB3_SEC_PHY_COM_AUX_CLK); + break; + case GCC_PCIE_1_PIPE_CLK: + clk_phy_mux_enable(priv->base, PCIE_1_PIPE_CLK_PHY_MUX, true); + break; + case GCC_PCIE_1_AUX_CLK: + clk_rcg_set_rate_mnd(priv->base, PCIE_1_AUX_CLK_CMD_RCGR, 1, 0, 0, + CFG_CLK_SRC_CXO, 16); + break; + case GCC_QUPV3_WRAP0_S0_CLK: + clk_rcg_set_rate_mnd(priv->base, 0x17010, 1, 0, 0, CFG_CLK_SRC_CXO, 16); + break; + case GCC_QUPV3_WRAP0_S1_CLK: + clk_rcg_set_rate_mnd(priv->base, 0x17140, 1, 0, 0, CFG_CLK_SRC_CXO, 16); + break; + case GCC_QUPV3_WRAP0_S3_CLK: + clk_rcg_set_rate_mnd(priv->base, 0x173a0, 1, 0, 0, CFG_CLK_SRC_CXO, 16); + break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map sc7280_gcc_resets[] = { @@ -100,6 +178,20 @@ static const struct qcom_reset_map sc7280_gcc_resets[] = { static const struct qcom_power_map sc7280_gdscs[] = { [GCC_UFS_PHY_GDSC] = { 0x77004 }, [GCC_USB30_PRIM_GDSC] = { 0xf004 }, + [GCC_USB30_SEC_GDSC] = { 0x9e004 }, + [GCC_PCIE_1_GDSC] = { 0x8d004 }, +}; + +static const phys_addr_t sc7280_rcg_addrs[] = { + 0x10f020, // USB30_PRIM_MASTER_CLK_CMD_RCGR + 0x10f038, // USB30_PRIM_MOCK_UTMI_CLK_CMD_RCGR + 0x18d058, // PCIE_1_AUX_CLK_CMD_RCGR +}; + +static const char *const sc7280_rcg_names[] = { + "USB30_PRIM_MASTER_CLK_SRC", + "USB30_PRIM_MOCK_UTMI_CLK_SRC", + "GCC_PCIE_1_AUX_CLK_SRC", }; static struct msm_clk_data qcs404_gcc_data = { @@ -113,6 +205,10 @@ static struct msm_clk_data qcs404_gcc_data = { .enable = sc7280_enable, .set_rate = sc7280_set_rate, + + .dbg_rcg_addrs = sc7280_rcg_addrs, + .num_rcgs = ARRAY_SIZE(sc7280_rcg_addrs), + .dbg_rcg_names = sc7280_rcg_names, }; static const struct udevice_id gcc_sc7280_of_match[] = { diff --git a/drivers/clk/qcom/clock-sdm845.c b/drivers/clk/qcom/clock-sdm845.c index adffb0cb240..6a0bf16ba2d 100644 --- a/drivers/clk/qcom/clock-sdm845.c +++ b/drivers/clk/qcom/clock-sdm845.c @@ -162,9 +162,7 @@ static int sdm845_clk_enable(struct clk *clk) break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map sdm845_gcc_resets[] = { diff --git a/drivers/clk/qcom/clock-sm6115.c b/drivers/clk/qcom/clock-sm6115.c index 9057dfe0bb1..17c2e561758 100644 --- a/drivers/clk/qcom/clock-sm6115.c +++ b/drivers/clk/qcom/clock-sm6115.c @@ -146,9 +146,7 @@ static int sm6115_enable(struct clk *clk) break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map sm6115_gcc_resets[] = { diff --git a/drivers/clk/qcom/clock-sm8150.c b/drivers/clk/qcom/clock-sm8150.c index 88f2e678f43..7dd0d56eb43 100644 --- a/drivers/clk/qcom/clock-sm8150.c +++ b/drivers/clk/qcom/clock-sm8150.c @@ -243,9 +243,7 @@ static int sm8150_clk_enable(struct clk *clk) break; }; - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map sm8150_gcc_resets[] = { diff --git a/drivers/clk/qcom/clock-sm8250.c b/drivers/clk/qcom/clock-sm8250.c index e322a923a5c..26396847d85 100644 --- a/drivers/clk/qcom/clock-sm8250.c +++ b/drivers/clk/qcom/clock-sm8250.c @@ -195,9 +195,7 @@ static int sm8250_enable(struct clk *clk) break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map sm8250_gcc_resets[] = { diff --git a/drivers/clk/qcom/clock-sm8550.c b/drivers/clk/qcom/clock-sm8550.c index 62b5a409e8e..7c06489b9c4 100644 --- a/drivers/clk/qcom/clock-sm8550.c +++ b/drivers/clk/qcom/clock-sm8550.c @@ -220,9 +220,7 @@ static int sm8550_enable(struct clk *clk) break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map sm8550_gcc_resets[] = { diff --git a/drivers/clk/qcom/clock-sm8650.c b/drivers/clk/qcom/clock-sm8650.c index 9baaecb571f..364454644a6 100644 --- a/drivers/clk/qcom/clock-sm8650.c +++ b/drivers/clk/qcom/clock-sm8650.c @@ -217,9 +217,7 @@ static int sm8650_enable(struct clk *clk) break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map sm8650_gcc_resets[] = { diff --git a/drivers/clk/qcom/clock-x1e80100.c b/drivers/clk/qcom/clock-x1e80100.c index bd9c6ed1c8a..542d6248d65 100644 --- a/drivers/clk/qcom/clock-x1e80100.c +++ b/drivers/clk/qcom/clock-x1e80100.c @@ -174,9 +174,7 @@ static int x1e80100_enable(struct clk *clk) break; } - qcom_gate_clk_en(priv, clk->id); - - return 0; + return qcom_gate_clk_en(priv, clk->id); } static const struct qcom_reset_map x1e80100_gcc_resets[] = { diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index 3c5340df8ee..7fce1f70d13 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -70,17 +70,12 @@ static int rzg2l_cpg_clk_set(struct clk *clk, bool enable) dev_dbg(clk->dev, "%s %s clock %u\n", enable ? "enable" : "disable", is_mod_clk(clk->id) ? "module" : "core", cpg_clk_id); - if (!is_mod_clk(clk->id)) { - /* - * Non-module clocks are always on. Ignore attempts to enable - * them and reject attempts to disable them. - */ - if (enable) - return 0; - - dev_err(clk->dev, "ID %lu is not a module clock\n", clk->id); - return -EINVAL; - } + /* + * Non-module clocks are always on. Ignore attempts to enable or disable + * them. + */ + if (!is_mod_clk(clk->id)) + return 0; for (i = 0; i < data->info->num_mod_clks; i++) { if (data->info->mod_clks[i].id == cpg_clk_id) { diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig index 8bdc0944896..f44db76c182 100644 --- a/drivers/clk/sunxi/Kconfig +++ b/drivers/clk/sunxi/Kconfig @@ -122,4 +122,11 @@ config CLK_SUN50I_A64 This enables common clock driver support for platforms based on Allwinner A64 SoC. +config CLK_SUN50I_A100 + bool "Clock driver for Allwinner A100/A133" + default MACH_SUN50I_A133 + help + This enables common clock driver support for platforms based + on Allwinner A100/A133 SoCs. + endif # CLK_SUNXI diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index 90a277489dc..7ff71c756e0 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_CLK_SUN50I_H6) += clk_h6.o obj-$(CONFIG_CLK_SUN50I_H6_R) += clk_h6_r.o obj-$(CONFIG_CLK_SUN50I_H616) += clk_h616.o obj-$(CONFIG_CLK_SUN50I_A64) += clk_a64.o +obj-$(CONFIG_CLK_SUN50I_A100) += clk_a100.o diff --git a/drivers/clk/sunxi/clk_a100.c b/drivers/clk/sunxi/clk_a100.c new file mode 100644 index 00000000000..b641feb8612 --- /dev/null +++ b/drivers/clk/sunxi/clk_a100.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (C) 2023-2024 Arm Ltd. + */ + +#include <clk/sunxi.h> +#include <dt-bindings/clock/sun50i-a100-ccu.h> +#include <dt-bindings/reset/sun50i-a100-ccu.h> +#include <linux/bitops.h> + +static struct ccu_clk_gate a100_gates[] = { + [CLK_PLL_PERIPH0] = GATE(0x020, BIT(31) | BIT(27)), + + [CLK_APB1] = GATE_DUMMY, + + [CLK_DE] = GATE(0x600, BIT(31)), + [CLK_BUS_DE] = GATE(0x60c, BIT(0)), + + [CLK_BUS_MMC0] = GATE(0x84c, BIT(0)), + [CLK_BUS_MMC1] = GATE(0x84c, BIT(1)), + [CLK_BUS_MMC2] = GATE(0x84c, BIT(2)), + + [CLK_BUS_UART0] = GATE(0x90c, BIT(0)), + [CLK_BUS_UART1] = GATE(0x90c, BIT(1)), + [CLK_BUS_UART2] = GATE(0x90c, BIT(2)), + [CLK_BUS_UART3] = GATE(0x90c, BIT(3)), + [CLK_BUS_UART4] = GATE(0x90c, BIT(4)), + + [CLK_BUS_I2C0] = GATE(0x91c, BIT(0)), + [CLK_BUS_I2C1] = GATE(0x91c, BIT(1)), + [CLK_BUS_I2C2] = GATE(0x91c, BIT(2)), + [CLK_BUS_I2C3] = GATE(0x91c, BIT(3)), + + [CLK_SPI0] = GATE(0x940, BIT(31)), + [CLK_SPI1] = GATE(0x944, BIT(31)), + [CLK_SPI2] = GATE(0x948, BIT(31)), + + [CLK_BUS_SPI0] = GATE(0x96c, BIT(0)), + [CLK_BUS_SPI1] = GATE(0x96c, BIT(1)), + [CLK_BUS_SPI2] = GATE(0x96c, BIT(2)), + + [CLK_BUS_EMAC] = GATE(0x97c, BIT(0)), + + [CLK_USB_PHY0] = GATE(0xa70, BIT(29)), + [CLK_USB_OHCI0] = GATE(0xa70, BIT(31)), + + [CLK_USB_PHY1] = GATE(0xa74, BIT(29)), + [CLK_USB_OHCI1] = GATE(0xa74, BIT(31)), + + [CLK_BUS_OHCI0] = GATE(0xa8c, BIT(0)), + [CLK_BUS_OHCI1] = GATE(0xa8c, BIT(1)), + [CLK_BUS_EHCI0] = GATE(0xa8c, BIT(4)), + [CLK_BUS_EHCI1] = GATE(0xa8c, BIT(5)), + [CLK_BUS_OTG] = GATE(0xa8c, BIT(8)), + + [CLK_TCON_LCD] = GATE(0xb60, BIT(31)), + [CLK_BUS_TCON_LCD] = GATE(0xb7c, BIT(0)), +}; + +static struct ccu_reset a100_resets[] = { + [RST_BUS_DE] = RESET(0x60c, BIT(16)), + + [RST_BUS_MMC0] = RESET(0x84c, BIT(16)), + [RST_BUS_MMC1] = RESET(0x84c, BIT(17)), + [RST_BUS_MMC2] = RESET(0x84c, BIT(18)), + + [RST_BUS_UART0] = RESET(0x90c, BIT(16)), + [RST_BUS_UART1] = RESET(0x90c, BIT(17)), + [RST_BUS_UART2] = RESET(0x90c, BIT(18)), + [RST_BUS_UART3] = RESET(0x90c, BIT(19)), + [RST_BUS_UART4] = RESET(0x90c, BIT(20)), + + [RST_BUS_I2C0] = RESET(0x91c, BIT(16)), + [RST_BUS_I2C1] = RESET(0x91c, BIT(17)), + [RST_BUS_I2C2] = RESET(0x91c, BIT(18)), + [RST_BUS_I2C3] = RESET(0x91c, BIT(19)), + + [RST_BUS_SPI0] = RESET(0x96c, BIT(16)), + [RST_BUS_SPI1] = RESET(0x96c, BIT(17)), + [RST_BUS_SPI2] = RESET(0x96c, BIT(18)), + + [RST_BUS_EMAC] = RESET(0x97c, BIT(16)), + + [RST_USB_PHY0] = RESET(0xa70, BIT(30)), + + [RST_USB_PHY1] = RESET(0xa74, BIT(30)), + + [RST_BUS_OHCI0] = RESET(0xa8c, BIT(16)), + [RST_BUS_OHCI1] = RESET(0xa8c, BIT(17)), + [RST_BUS_EHCI0] = RESET(0xa8c, BIT(20)), + [RST_BUS_EHCI1] = RESET(0xa8c, BIT(21)), + [RST_BUS_OTG] = RESET(0xa8c, BIT(24)), + + [RST_BUS_TCON_LCD] = RESET(0xb7c, BIT(16)), +}; + +const struct ccu_desc a100_ccu_desc = { + .gates = a100_gates, + .resets = a100_resets, + .num_gates = ARRAY_SIZE(a100_gates), + .num_resets = ARRAY_SIZE(a100_resets), +}; diff --git a/drivers/clk/sunxi/clk_sunxi.c b/drivers/clk/sunxi/clk_sunxi.c index 2ef4f45dacf..e0765cbc6dc 100644 --- a/drivers/clk/sunxi/clk_sunxi.c +++ b/drivers/clk/sunxi/clk_sunxi.c @@ -122,6 +122,7 @@ extern const struct ccu_desc f1c100s_ccu_desc; extern const struct ccu_desc h3_ccu_desc; extern const struct ccu_desc h6_ccu_desc; extern const struct ccu_desc h616_ccu_desc; +extern const struct ccu_desc a100_ccu_desc; extern const struct ccu_desc h6_r_ccu_desc; extern const struct ccu_desc r40_ccu_desc; extern const struct ccu_desc v3s_ccu_desc; @@ -215,6 +216,10 @@ static const struct udevice_id sunxi_clk_ids[] = { { .compatible = "allwinner,sun50i-h616-r-ccu", .data = (ulong)&h6_r_ccu_desc }, #endif +#ifdef CONFIG_CLK_SUN50I_A100 + { .compatible = "allwinner,sun50i-a100-ccu", + .data = (ulong)&a100_ccu_desc }, +#endif #ifdef CONFIG_CLK_SUNIV_F1C100S { .compatible = "allwinner,suniv-f1c100s-ccu", .data = (ulong)&f1c100s_ccu_desc }, diff --git a/drivers/clk/ti/clk-k3.c b/drivers/clk/ti/clk-k3.c index bf65f573cb8..b38e559e45a 100644 --- a/drivers/clk/ti/clk-k3.c +++ b/drivers/clk/ti/clk-k3.c @@ -82,6 +82,8 @@ static const struct soc_attr ti_k3_soc_clk_data[] = { .family = "J721E", .data = &j721e_clk_platdata, }, +#endif +#if IS_ENABLED(CONFIG_SOC_K3_J7200) { .family = "J7200", .data = &j7200_clk_platdata, @@ -104,6 +106,10 @@ static const struct soc_attr ti_k3_soc_clk_data[] = { .family = "J784S4", .data = &j784s4_clk_platdata, }, + { + .family = "J742S2", + .data = &j784s4_clk_platdata, + }, #endif { /* sentinel */ } }; diff --git a/drivers/core/Makefile b/drivers/core/Makefile index 9ea57911f89..657e589c286 100644 --- a/drivers/core/Makefile +++ b/drivers/core/Makefile @@ -16,6 +16,6 @@ ifndef CONFIG_DM_DEV_READ_INLINE obj-$(CONFIG_OF_CONTROL) += read.o endif obj-$(CONFIG_$(XPL_)OF_PLATDATA) += read.o -obj-$(CONFIG_OF_CONTROL) += of_extra.o ofnode.o read_extra.o +obj-$(CONFIG_OF_CONTROL) += of_extra.o ofnode.o read_extra.o ofnode_graph.o ccflags-$(CONFIG_DM_DEBUG) += -DDEBUG diff --git a/drivers/core/ofnode_graph.c b/drivers/core/ofnode_graph.c new file mode 100644 index 00000000000..90c92af3258 --- /dev/null +++ b/drivers/core/ofnode_graph.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#define LOG_CATEGORY LOGC_DT + +#include <dm.h> +#include <log.h> +#include <dm/ofnode.h> +#include <linux/err.h> + +/** + * ofnode_graph_get_endpoint_count() - get the number of endpoints in a device ofnode + * @parent: ofnode to the device containing ports and endpoints + * + * Return: count of endpoint of this device ofnode + */ +unsigned int ofnode_graph_get_endpoint_count(ofnode parent) +{ + ofnode ports, port, endpoint; + unsigned int num = 0; + + /* Check if ports node exists */ + ports = ofnode_find_subnode(parent, "ports"); + if (ofnode_valid(ports)) + parent = ports; + + ofnode_for_each_subnode(port, parent) { + if (!strncmp(ofnode_get_name(port), "port", 4)) { + /* Port node can only contain endpoints */ + ofnode_for_each_subnode(endpoint, port) + num++; + } + }; + + log_debug("%s: detected %d endpoints\n", __func__, num); + + return num++; +} + +/** + * ofnode_graph_get_port_count() - get the number of port in a device or ports ofnode + * @parent: ofnode to the device or ports node + * + * Return: count of port of this device or ports node + */ +unsigned int ofnode_graph_get_port_count(ofnode parent) +{ + ofnode ports, port; + unsigned int num = 0; + + /* Check if ports node exists */ + ports = ofnode_find_subnode(parent, "ports"); + if (ofnode_valid(ports)) + parent = ports; + + ofnode_for_each_subnode(port, parent) + if (!strncmp(ofnode_get_name(port), "port", 4)) + num++; + + log_debug("%s: detected %d ports\n", __func__, num); + + return num++; +} + +/** + * ofnode_graph_get_port_by_id() - get the port matching a given id + * @parent: parent ofnode + * @id: id of the port + * + * Return: ofnode in given port. + */ +ofnode ofnode_graph_get_port_by_id(ofnode parent, u32 id) +{ + ofnode ports, port; + u32 port_id; + + ports = ofnode_find_subnode(parent, "ports"); + if (!ofnode_valid(ports)) + return ofnode_null(); + + /* Check ports for node with desired id */ + ofnode_for_each_subnode(port, ports) { + ofnode_read_u32(port, "reg", &port_id); + log_debug("%s: detected port %d\n", __func__, port_id); + if (port_id == id) + return port; + } + + return ofnode_null(); +} + +/** + * ofnode_graph_get_endpoint_by_regs() - get the endpoint matching a given id + * @parent: parent ofnode + * @reg_id: id of the port + * @id: id for the endpoint + * + * Return: ofnode in given endpoint or ofnode_null() if not found. + * reg and port_reg are ignored when they are -1. + */ +ofnode ofnode_graph_get_endpoint_by_regs(ofnode parent, int reg_id, int id) +{ + ofnode port, endpoint; + u32 ep_id; + + /* get the port to work with */ + if (reg_id < 0) + port = ofnode_find_subnode(parent, "port"); + else + port = ofnode_graph_get_port_by_id(parent, reg_id); + + if (!ofnode_valid(port)) { + log_debug("%s: port node is not found\n", __func__); + return ofnode_null(); + } + + if (id < 0) + return ofnode_find_subnode(port, "endpoint"); + + /* Check endpoints for node with desired id */ + ofnode_for_each_subnode(endpoint, port) { + ofnode_read_u32(endpoint, "reg", &ep_id); + log_debug("%s: detected endpoint %d\n", __func__, ep_id); + if (ep_id == id) + return endpoint; + } + + return ofnode_null(); +} + +/** + * ofnode_graph_get_remote_endpoint() - get remote endpoint node + * @endpoint: ofnode of a local endpoint + * + * Return: Remote endpoint ofnode linked with local endpoint. + */ +ofnode ofnode_graph_get_remote_endpoint(ofnode endpoint) +{ + /* Get remote endpoint node. */ + return ofnode_parse_phandle(endpoint, "remote-endpoint", 0); +} + +/** + * ofnode_graph_get_port_parent() - get port's parent node + * @endpoint: ofnode of a local endpoint + * + * Return: device ofnode associated with endpoint + */ +ofnode ofnode_graph_get_port_parent(ofnode endpoint) +{ + ofnode port = ofnode_get_parent(endpoint); + ofnode parent = ofnode_get_parent(port); + + /* check if we are on top level or in ports node */ + if (!strcmp(ofnode_get_name(parent), "ports")) + parent = ofnode_get_parent(parent); + + return parent; +} + +/** + * ofnode_graph_get_remote_port_parent() - get remote port's parent ofnode + * @endpoint: ofnode of a local endpoint + * + * Return: device ofnode associated with endpoint linked to local endpoint. + */ +ofnode ofnode_graph_get_remote_port_parent(ofnode endpoint) +{ + ofnode remote_endpoint = ofnode_graph_get_remote_endpoint(endpoint); + if (!ofnode_valid(remote_endpoint)) { + log_debug("%s: remote endpoint is not found\n", __func__); + return ofnode_null(); + } + + return ofnode_graph_get_port_parent(remote_endpoint); +} + +/** + * ofnode_graph_get_remote_port() - get remote port ofnode + * @endpoint: ofnode of a local endpoint + * + * Return: port ofnode associated with remote endpoint node linked + * to local endpoint. + */ +ofnode ofnode_graph_get_remote_port(ofnode endpoint) +{ + ofnode remote_endpoint = ofnode_graph_get_remote_endpoint(endpoint); + if (!ofnode_valid(remote_endpoint)) { + log_debug("%s: remote endpoint is not found\n", __func__); + return ofnode_null(); + } + + return ofnode_get_parent(remote_endpoint); +} + +/** + * ofnode_graph_get_remote_node() - get remote parent ofnode for given port/endpoint + * @parent: parent ofnode containing graph port/endpoint + * @port: identifier (value of reg property) of the parent port ofnode + * @endpoint: identifier (value of reg property) of the endpoint ofnode + * + * Return: device ofnode associated with endpoint linked to local endpoint. + */ +ofnode ofnode_graph_get_remote_node(ofnode parent, int port, int endpoint) +{ + ofnode endpoint_ofnode; + + endpoint_ofnode = ofnode_graph_get_endpoint_by_regs(parent, port, endpoint); + if (!ofnode_valid(endpoint_ofnode)) { + log_debug("%s: endpoint is not found\n", __func__); + return ofnode_null(); + } + + return ofnode_graph_get_remote_port_parent(endpoint_ofnode); +} diff --git a/drivers/cpu/imx8_cpu.c b/drivers/cpu/imx8_cpu.c index 53d31b3c0bf..4e1eccaa5b0 100644 --- a/drivers/cpu/imx8_cpu.c +++ b/drivers/cpu/imx8_cpu.c @@ -35,11 +35,49 @@ static const char *get_imx_type_str(u32 imxtype) { switch (imxtype) { case MXC_CPU_IMX8MM: - return "8MM"; + return "8MMQ"; /* Quad-core version of the imx8mm */ + case MXC_CPU_IMX8MML: + return "8MMQL"; /* Quad-core Lite version of the imx8mm */ + case MXC_CPU_IMX8MMD: + return "8MMD"; /* Dual-core version of the imx8mm */ + case MXC_CPU_IMX8MMDL: + return "8MMDL"; /* Dual-core Lite version of the imx8mm */ + case MXC_CPU_IMX8MMS: + return "8MMS"; /* Single-core version of the imx8mm */ + case MXC_CPU_IMX8MMSL: + return "8MMSL"; /* Single-core Lite version of the imx8mm */ case MXC_CPU_IMX8MN: - return "8MN"; + return "8MNano Quad"; /* Quad-core version */ + case MXC_CPU_IMX8MND: + return "8MNano Dual"; /* Dual-core version */ + case MXC_CPU_IMX8MNS: + return "8MNano Solo"; /* Single-core version */ + case MXC_CPU_IMX8MNL: + return "8MNano QuadLite"; /* Quad-core Lite version */ + case MXC_CPU_IMX8MNDL: + return "8MNano DualLite"; /* Dual-core Lite version */ + case MXC_CPU_IMX8MNSL: + return "8MNano SoloLite";/* Single-core Lite version of the imx8mn */ + case MXC_CPU_IMX8MNUQ: + return "8MNano UltraLite Quad";/* Quad-core UltraLite version of the imx8mn */ + case MXC_CPU_IMX8MNUD: + return "8MNano UltraLite Dual";/* Dual-core UltraLite version of the imx8mn */ + case MXC_CPU_IMX8MNUS: + return "8MNano UltraLite Solo";/* Single-core UltraLite version of the imx8mn */ case MXC_CPU_IMX8MP: - return "8MP"; + return "8MP[8]"; /* Quad-core version of the imx8mp */ + case MXC_CPU_IMX8MPD: + return "8MP Dual[3]"; /* Dual-core version of the imx8mp */ + case MXC_CPU_IMX8MPL: + return "8MP Lite[4]"; /* Quad-core Lite version of the imx8mp */ + case MXC_CPU_IMX8MP6: + return "8MP[6]"; /* Quad-core version of the imx8mp, NPU fused */ + case MXC_CPU_IMX8MQ: + return "8MQ"; /* Quad-core version of the imx8mq */ + case MXC_CPU_IMX8MQL: + return "8MQLite"; /* Quad-core Lite version of the imx8mq */ + case MXC_CPU_IMX8MD: + return "8MD"; /* Dual-core version of the imx8mq */ case MXC_CPU_IMX8QXP: case MXC_CPU_IMX8QXP_A0: return "8QXP"; @@ -177,19 +215,19 @@ static int cpu_imx_get_desc(const struct udevice *dev, char *buf, int size) ret = snprintf(buf, size, "NXP i.MX%s Rev%s %s at %u MHz", plat->type, plat->rev, plat->name, plat->freq_mhz); - if (IS_ENABLED(CONFIG_IMX9)) { + if (IS_ENABLED(CONFIG_IMX_TMU)) { switch (get_cpu_temp_grade(&minc, &maxc)) { case TEMP_AUTOMOTIVE: - grade = "Automotive temperature grade "; + grade = "Automotive temperature grade"; break; case TEMP_INDUSTRIAL: - grade = "Industrial temperature grade "; + grade = "Industrial temperature grade"; break; case TEMP_EXTCOMMERCIAL: - grade = "Extended Consumer temperature grade "; + grade = "Extended Consumer temperature grade"; break; default: - grade = "Consumer temperature grade "; + grade = "Consumer temperature grade"; break; } diff --git a/drivers/ddr/altera/Makefile b/drivers/ddr/altera/Makefile index c1d6a6b6c59..b19f3601813 100644 --- a/drivers/ddr/altera/Makefile +++ b/drivers/ddr/altera/Makefile @@ -4,7 +4,7 @@ # Wolfgang Denk, DENX Software Engineering, wd@denx.de. # # (C) Copyright 2010, Thomas Chou <thomas@wytron.com.tw> -# Copyright (C) 2014-2021 Altera Corporation <www.altera.com> +# Copyright (C) 2014-2025 Altera Corporation <www.altera.com> ifdef CONFIG_$(XPL_)ALTERA_SDRAM obj-$(CONFIG_TARGET_SOCFPGA_GEN5) += sdram_gen5.o sequencer.o @@ -12,4 +12,5 @@ obj-$(CONFIG_TARGET_SOCFPGA_ARRIA10) += sdram_arria10.o obj-$(CONFIG_TARGET_SOCFPGA_STRATIX10) += sdram_soc64.o sdram_s10.o obj-$(CONFIG_TARGET_SOCFPGA_AGILEX) += sdram_soc64.o sdram_agilex.o obj-$(CONFIG_TARGET_SOCFPGA_N5X) += sdram_soc64.o sdram_n5x.o +obj-$(CONFIG_TARGET_SOCFPGA_AGILEX5) += sdram_soc64.o sdram_agilex5.o iossm_mailbox.o endif diff --git a/drivers/ddr/altera/iossm_mailbox.c b/drivers/ddr/altera/iossm_mailbox.c new file mode 100644 index 00000000000..db9435db657 --- /dev/null +++ b/drivers/ddr/altera/iossm_mailbox.c @@ -0,0 +1,748 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Altera Corporation <www.altera.com> + * + */ + +#include <hang.h> +#include <string.h> +#include <wait_bit.h> +#include <asm/arch/base_addr_soc64.h> +#include <asm/io.h> +#include <linux/bitfield.h> +#include "iossm_mailbox.h" + +#define TIMEOUT_120000MS 120000 +#define TIMEOUT_60000MS 60000 +#define TIMEOUT TIMEOUT_120000MS +#define IOSSM_STATUS_CAL_SUCCESS BIT(0) +#define IOSSM_STATUS_CAL_FAIL BIT(1) +#define IOSSM_STATUS_CAL_BUSY BIT(2) +#define IOSSM_STATUS_COMMAND_RESPONSE_READY BIT(0) +#define IOSSM_CMD_RESPONSE_STATUS_OFFSET 0x45C +#define IOSSM_CMD_RESPONSE_DATA_0_OFFSET 0x458 +#define IOSSM_CMD_RESPONSE_DATA_1_OFFSET 0x454 +#define IOSSM_CMD_RESPONSE_DATA_2_OFFSET 0x450 +#define IOSSM_CMD_REQ_OFFSET 0x43C +#define IOSSM_CMD_PARAM_0_OFFSET 0x438 +#define IOSSM_CMD_PARAM_1_OFFSET 0x434 +#define IOSSM_CMD_PARAM_2_OFFSET 0x430 +#define IOSSM_CMD_PARAM_3_OFFSET 0x42C +#define IOSSM_CMD_PARAM_4_OFFSET 0x428 +#define IOSSM_CMD_PARAM_5_OFFSET 0x424 +#define IOSSM_CMD_PARAM_6_OFFSET 0x420 +#define IOSSM_CMD_RESPONSE_DATA_SHORT_MASK GENMASK(31, 16) +#define IOSSM_CMD_RESPONSE_DATA_SHORT(n) FIELD_GET(IOSSM_CMD_RESPONSE_DATA_SHORT_MASK, n) +#define IOSSM_STATUS_CMD_RESPONSE_ERROR_MASK GENMASK(7, 5) +#define IOSSM_STATUS_CMD_RESPONSE_ERROR(n) FIELD_GET(IOSSM_STATUS_CMD_RESPONSE_ERROR_MASK, n) +#define IOSSM_STATUS_GENERAL_ERROR_MASK GENMASK(4, 1) +#define IOSSM_STATUS_GENERAL_ERROR(n) FIELD_GET(IOSSM_STATUS_GENERAL_ERROR_MASK, n) + +/* Offset of Mailbox Read-only Registers */ +#define IOSSM_MAILBOX_HEADER_OFFSET 0x0 +#define IOSSM_MEM_INTF_INFO_0_OFFSET 0X200 +#define IOSSM_MEM_INTF_INFO_1_OFFSET 0x280 +#define IOSSM_MEM_TECHNOLOGY_INTF0_OFFSET 0x210 +#define IOSSM_MEM_TECHNOLOGY_INTF1_OFFSET 0x290 +#define IOSSM_MEM_WIDTH_INFO_INTF0_OFFSET 0x230 +#define IOSSM_MEM_WIDTH_INFO_INTF1_OFFSET 0x2B0 +#define IOSSM_MEM_TOTAL_CAPACITY_INTF0_OFFSET 0x234 +#define IOSSM_MEM_TOTAL_CAPACITY_INTF1_OFFSET 0x2B4 +#define IOSSM_ECC_ENABLE_INTF0_OFFSET 0x240 +#define IOSSM_ECC_ENABLE_INTF1_OFFSET 0x2C0 +#define IOSSM_ECC_SCRUB_STATUS_INTF0_OFFSET 0x244 +#define IOSSM_ECC_SCRUB_STATUS_INTF1_OFFSET 0x2C4 +#define IOSSM_LP_MODE_INTF0_OFFSET 0x250 +#define IOSSM_LP_MODE_INTF1_OFFSET 0x2D0 +#define IOSSM_MEM_INIT_STATUS_INTF0_OFFSET 0x260 +#define IOSSM_MEM_INIT_STATUS_INTF1_OFFSET 0x2E0 +#define IOSSM_BIST_STATUS_INTF0_OFFSET 0x264 +#define IOSSM_BIST_STATUS_INTF1_OFFSET 0x2E4 +#define IOSSM_ECC_ERR_STATUS_OFFSET 0x300 +#define IOSSM_ECC_ERR_DATA_START_OFFSET 0x310 +#define IOSSM_STATUS_OFFSET 0x400 +#define IOSSM_STATUS_CAL_INTF0_OFFSET 0x404 +#define IOSSM_STATUS_CAL_INTF1_OFFSET 0x408 + +#define ECC_INTSTATUS_SERR SOCFPGA_SYSMGR_ADDRESS + 0x9C +#define ECC_INISTATUS_DERR SOCFPGA_SYSMGR_ADDRESS + 0xA0 +#define DDR_CSR_CLKGEN_LOCKED_IO96B0_MASK BIT(16) +#define DDR_CSR_CLKGEN_LOCKED_IO96B1_MASK BIT(17) + +/* offset info of GET_MEM_INTF_INFO */ +#define INTF_IP_TYPE_MASK GENMASK(31, 29) +#define INTF_INSTANCE_ID_MASK GENMASK(28, 24) + +/* offset info of GET_MEM_CAL_STATUS */ +#define INTF_UNUSED 0x0 +#define INTF_MEM_CAL_STATUS_SUCCESS 0x1 +#define INTF_MEM_CAL_STATUS_FAIL 0x2 +#define INTF_MEM_CAL_STATUS_ONGOING 0x4 + +/* offset info of MEM_TECHNOLOGY_INTF */ +#define INTF_DDR_TYPE_MASK GENMASK(2, 0) + +/* offset info of MEM_TOTAL_CAPACITY_INTF */ +#define INTF_CAPACITY_GBITS_MASK GENMASK(7, 0) + +/* offset info of ECC_ENABLE_INTF */ +#define INTF_ECC_ENABLE_TYPE_MASK GENMASK(1, 0) + +/* cmd opcode BIST_MEM_INIT_START, BIST performed on full memory address range */ +#define BIST_FULL_MEM BIT(6) + +/* offset info of ECC_ENABLE_INTF */ +#define INTF_BIST_STATUS_MASK BIT(0) + +/* offset info of ECC_ERR_STATUS */ +#define ECC_ERR_COUNTER_MASK GENMASK(15, 0) + +/* offset info of ECC_ERR_DATA */ +#define ECC_ERR_IP_TYPE_MASK GENMASK(24, 22) +#define ECC_ERR_INSTANCE_ID_MASK GENMASK(21, 17) +#define ECC_ERR_SOURCE_ID_MASK GENMASK(16, 10) +#define ECC_ERR_TYPE_MASK GENMASK(9, 6) +#define ECC_ERR_ADDR_UPPER_MASK GENMASK(5, 0) +#define ECC_ERR_ADDR_LOWER_MASK GENMASK(31, 0) + +#define MAX_ECC_ERR_INFO_COUNT 16 + +#define IO96B_MB_REQ_SETUP(v, w, x, y, z) \ + usr_req.ip_type = v; \ + usr_req.ip_id = w; \ + usr_req.usr_cmd_type = x; \ + usr_req.usr_cmd_opcode = y; \ + usr_req.cmd_param[0] = z; \ + for (n = 1; n < NUM_CMD_PARAM; n++) \ + usr_req.cmd_param[n] = 0 +#define MAX_RETRY_COUNT 3 +#define NUM_CMD_RESPONSE_DATA 3 + +#define IO96B0_PLL_A_MASK BIT(0) +#define IO96B0_PLL_B_MASK BIT(1) +#define IO96B1_PLL_A_MASK BIT(2) +#define IO96B1_PLL_B_MASK BIT(3) + +/* supported DDR type list */ +static const char *ddr_type_list[7] = { + "DDR4", "DDR5", "DDR5_RDIMM", "LPDDR4", "LPDDR5", "QDRIV", "UNKNOWN" +}; + +/* Define an enumeration for ECC error types */ +enum ecc_error_type { + SINGLE_BIT_ERROR = 0, /* 0b0000 */ + MULTIPLE_SINGLE_BIT_ERRORS = 1, /* 0b0001 */ + DOUBLE_BIT_ERROR = 2, /* 0b0010 */ + MULTIPLE_DOUBLE_BIT_ERRORS = 3, /* 0b0011 */ + SINGLE_BIT_ERROR_SCRUBBING = 8, /* 0b1000 */ + WRITE_LINK_SINGLE_BIT_ERROR = 9, /* 0b1001 */ + WRITE_LINK_DOUBLE_BIT_ERROR = 10, /* 0b1010 */ + READ_LINK_SINGLE_BIT_ERROR = 11, /* 0b1011 */ + READ_LINK_DOUBLE_BIT_ERROR = 12, /* 0b1100 */ + READ_MODIFY_WRITE_DOUBLE_BIT_ERROR = 13 /* 0b1101 */ +}; + +/* + * ecc error info + * + * @ip_type: The IP type of the interface that produced the ECC interrupt. + * @instance_id: The instance ID of the interface that produced the ECC interrupt. + * @ecc_err_source_id: The source ID associated with the ECC event. + * @ecc_err_type: The ECC error type of the ECC event. + * @ecc_err_addr_upper: Upper 6 bits of the address of the read data that caused the ECC event. + * @ecc_err_addr_lower: Lower 32 bits of the address of the read data that caused the ECC event. + */ +struct ecc_err_info { + u32 ip_type; + u32 instance_id; + u32 source_id; + enum ecc_error_type err_type; + u32 addr_upper; + u32 addr_lower; +}; + +static int is_ddr_csr_clkgen_locked(u8 io96b_pll) +{ + int ret = 0; + const char *pll_names[MAX_IO96B_SUPPORTED][2] = { + {"io96b_0 clkgenA", "io96b_0 clkgenB"}, + {"io96b_1 clkgenA", "io96b_1 clkgenB"} + }; + u32 masks[MAX_IO96B_SUPPORTED][2] = { + {IO96B0_PLL_A_MASK, IO96B0_PLL_B_MASK}, + {IO96B1_PLL_A_MASK, IO96B1_PLL_B_MASK} + }; + u32 lock_masks[MAX_IO96B_SUPPORTED] = { + DDR_CSR_CLKGEN_LOCKED_IO96B0_MASK, + DDR_CSR_CLKGEN_LOCKED_IO96B1_MASK + }; + + for (int i = 0; i < MAX_IO96B_SUPPORTED ; i++) { + /* Check for PLL_A */ + if (io96b_pll & masks[i][0]) { + ret = wait_for_bit_le32((const void *)(ECC_INTSTATUS_SERR), lock_masks[i], + true, TIMEOUT, false); + + if (ret) { + debug("%s: ddr csr %s locked is timeout\n", + __func__, pll_names[i][0]); + goto err; + } else { + debug("%s: ddr csr %s is successfully locked\n", + __func__, pll_names[i][0]); + } + } + + /* Check for PLL_B */ + if (io96b_pll & masks[i][1]) { + ret = wait_for_bit_le32((const void *)(ECC_INISTATUS_DERR), lock_masks[i], + true, TIMEOUT, false); + + if (ret) { + debug("%s: ddr csr %s locked is timeout\n", + __func__, pll_names[i][1]); + goto err; + } else { + debug("%s: ddr csr %s is successfully locked\n", + __func__, pll_names[i][1]); + } + } + } + +err: + return ret; +} + +/* + * Mailbox request function + * This function will send the request to IOSSM mailbox and wait for response return + * + * @io96b_csr_addr: CSR address for the target IO96B + * @req: Structure contain command request for IOSSM mailbox command + * @resp_data_len: User desire extra response data fields other than + * CMD_RESPONSE_DATA_SHORT field on CMD_RESPONSE_STATUS + * @resp: Structure contain responses returned from the requested IOSSM + * mailbox command + */ +int io96b_mb_req(phys_addr_t io96b_csr_addr, struct io96b_mb_req req, + u32 resp_data_len, struct io96b_mb_resp *resp) +{ + int i, ret; + u32 cmd_req; + + if (!resp) { + ret = -EINVAL; + goto err; + } + + /* Zero initialization for responses */ + resp->cmd_resp_status = 0; + + /* Ensure CMD_REQ is cleared before write any command request */ + ret = wait_for_bit_le32((const void *)(io96b_csr_addr + IOSSM_CMD_REQ_OFFSET), + GENMASK(31, 0), false, TIMEOUT, false); + if (ret) { + printf("%s: Timeout of waiting DDR mailbox ready to be functioned!\n", + __func__); + goto err; + } + + /* Write CMD_PARAM_* */ + for (i = 0; i < NUM_CMD_PARAM ; i++) { + switch (i) { + case 0: + if (req.cmd_param[0]) + writel(req.cmd_param[0], io96b_csr_addr + IOSSM_CMD_PARAM_0_OFFSET); + break; + case 1: + if (req.cmd_param[1]) + writel(req.cmd_param[1], io96b_csr_addr + IOSSM_CMD_PARAM_1_OFFSET); + break; + case 2: + if (req.cmd_param[2]) + writel(req.cmd_param[2], io96b_csr_addr + IOSSM_CMD_PARAM_2_OFFSET); + break; + case 3: + if (req.cmd_param[3]) + writel(req.cmd_param[3], io96b_csr_addr + IOSSM_CMD_PARAM_3_OFFSET); + break; + case 4: + if (req.cmd_param[4]) + writel(req.cmd_param[4], io96b_csr_addr + IOSSM_CMD_PARAM_4_OFFSET); + break; + case 5: + if (req.cmd_param[5]) + writel(req.cmd_param[5], io96b_csr_addr + IOSSM_CMD_PARAM_5_OFFSET); + break; + case 6: + if (req.cmd_param[6]) + writel(req.cmd_param[6], io96b_csr_addr + IOSSM_CMD_PARAM_6_OFFSET); + break; + } + } + + /* Write CMD_REQ (IP_TYPE, IP_INSTANCE_ID, CMD_TYPE and CMD_OPCODE) */ + cmd_req = FIELD_PREP(CMD_TARGET_IP_TYPE_MASK, req.ip_type) | + FIELD_PREP(CMD_TARGET_IP_INSTANCE_ID_MASK, req.ip_id) | + FIELD_PREP(CMD_TYPE_MASK, req.usr_cmd_type) | + FIELD_PREP(CMD_OPCODE_MASK, req.usr_cmd_opcode); + writel(cmd_req, io96b_csr_addr + IOSSM_CMD_REQ_OFFSET); + + debug("%s: Write 0x%x to IOSSM_CMD_REQ_OFFSET 0x%llx\n", __func__, cmd_req, + io96b_csr_addr + IOSSM_CMD_REQ_OFFSET); + + /* Read CMD_RESPONSE_READY in CMD_RESPONSE_STATUS */ + ret = wait_for_bit_le32((const void *)(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET), + IOSSM_STATUS_COMMAND_RESPONSE_READY, true, TIMEOUT, false); + + /* read CMD_RESPONSE_STATUS */ + resp->cmd_resp_status = readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET); + + debug("%s: CMD_RESPONSE_STATUS 0x%llx: 0x%x\n", __func__, io96b_csr_addr + + IOSSM_CMD_RESPONSE_STATUS_OFFSET, resp->cmd_resp_status); + + if (ret) { + printf("%s: CMD_RESPONSE ERROR:\n", __func__); + + printf("%s: STATUS_GENERAL_ERROR: 0x%lx\n", __func__, + IOSSM_STATUS_GENERAL_ERROR(resp->cmd_resp_status)); + printf("%s: STATUS_CMD_RESPONSE_ERROR: 0x%lx\n", __func__, + IOSSM_STATUS_CMD_RESPONSE_ERROR(resp->cmd_resp_status)); + goto err; + } + + /* read CMD_RESPONSE_DATA_* */ + for (i = 0; i < resp_data_len; i++) { + switch (i) { + case 0: + resp->cmd_resp_data[i] = + readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_0_OFFSET); + + debug("%s: IOSSM_CMD_RESPONSE_DATA_0_OFFSET 0x%llx: 0x%x\n", __func__, + io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_0_OFFSET, + resp->cmd_resp_data[i]); + break; + case 1: + resp->cmd_resp_data[i] = + readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_1_OFFSET); + + debug("%s: IOSSM_CMD_RESPONSE_DATA_1_OFFSET 0x%llx: 0x%x\n", __func__, + io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_1_OFFSET, + resp->cmd_resp_data[i]); + break; + case 2: + resp->cmd_resp_data[i] = + readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_2_OFFSET); + + debug("%s: IOSSM_CMD_RESPONSE_DATA_2_OFFSET 0x%llx: 0x%x\n", __func__, + io96b_csr_addr + IOSSM_CMD_RESPONSE_DATA_2_OFFSET, + resp->cmd_resp_data[i]); + break; + default: + resp->cmd_resp_data[i] = 0; + printf("%s: Invalid response data\n", __func__); + } + } + + /* write CMD_RESPONSE_READY = 0 */ + clrbits_le32((u32 *)(uintptr_t)(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET), + IOSSM_STATUS_COMMAND_RESPONSE_READY); + + debug("%s: After clear CMD_RESPONSE_READY bit: 0x%llx: 0x%x\n", __func__, + io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET, + readl(io96b_csr_addr + IOSSM_CMD_RESPONSE_STATUS_OFFSET)); + +err: + return ret; +} + +/* + * Initial function to be called to set memory interface IP type and instance ID + * IP type and instance ID need to be determined before sending mailbox command + */ +void io96b_mb_init(struct io96b_info *io96b_ctrl) +{ + int i, j; + u32 mem_intf_info_0, mem_intf_info_1; + + debug("%s: num_instance %d\n", __func__, io96b_ctrl->num_instance); + + for (i = 0; i < io96b_ctrl->num_instance; i++) { + debug("%s: get memory interface IO96B %d\n", __func__, i); + io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface = 0; + + mem_intf_info_0 = readl(io96b_ctrl->io96b[i].io96b_csr_addr + + IOSSM_MEM_INTF_INFO_0_OFFSET); + mem_intf_info_1 = readl(io96b_ctrl->io96b[i].io96b_csr_addr + + IOSSM_MEM_INTF_INFO_1_OFFSET); + + io96b_ctrl->io96b[i].mb_ctrl.ip_type[0] = FIELD_GET(INTF_IP_TYPE_MASK, + mem_intf_info_0); + io96b_ctrl->io96b[i].mb_ctrl.ip_id[0] = FIELD_GET(INTF_INSTANCE_ID_MASK, + mem_intf_info_0); + io96b_ctrl->io96b[i].mb_ctrl.ip_type[1] = FIELD_GET(INTF_IP_TYPE_MASK, + mem_intf_info_1); + io96b_ctrl->io96b[i].mb_ctrl.ip_id[1] = FIELD_GET(INTF_INSTANCE_ID_MASK, + mem_intf_info_1); + + for (j = 0; j < MAX_MEM_INTERFACE_SUPPORTED; j++) { + if (io96b_ctrl->io96b[i].mb_ctrl.ip_type[j]) { + io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface++; + + debug("%s: IO96B %d mem_interface %d: ip_type_ret: 0x%x\n", + __func__, i, j, io96b_ctrl->io96b[i].mb_ctrl.ip_type[j]); + debug("%s: IO96B %d mem_interface %d: instance_id_ret: 0x%x\n", + __func__, i, j, io96b_ctrl->io96b[i].mb_ctrl.ip_id[j]); + } + } + + debug("%s: IO96B %d: num_mem_interface: 0x%x\n", __func__, i, + io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface); + } +} + +int io96b_cal_status(phys_addr_t addr) +{ + u32 cal_success, cal_fail; + phys_addr_t status_addr = addr + IOSSM_STATUS_OFFSET; + u32 start = get_timer(0); + + do { + if (get_timer(start) > TIMEOUT_60000MS) { + printf("%s: SDRAM calibration for IO96B instance 0x%llx timeout!\n", + __func__, status_addr); + hang(); + } + + udelay(1); + schedule(); + + /* Polling until getting any calibration result */ + cal_success = readl(status_addr) & IOSSM_STATUS_CAL_SUCCESS; + cal_fail = readl(status_addr) & IOSSM_STATUS_CAL_FAIL; + } while (!cal_success && !cal_fail); + + debug("%s: Calibration for IO96B instance 0x%llx done at %ld msec!\n", + __func__, status_addr, get_timer(start)); + + if (cal_success && !cal_fail) + return 0; + else + return -EPERM; +} + +void init_mem_cal(struct io96b_info *io96b_ctrl) +{ + int count, i, ret; + + /* Initialize overall calibration status */ + io96b_ctrl->overall_cal_status = false; + + if (io96b_ctrl->ckgen_lock) { + ret = is_ddr_csr_clkgen_locked(io96b_ctrl->io96b_pll); + if (ret) { + printf("%s: iossm IO96B ckgena_lock is not locked\n", __func__); + hang(); + } + } + + /* Check initial calibration status for the assigned IO96B */ + count = 0; + for (i = 0; i < io96b_ctrl->num_instance; i++) { + ret = io96b_cal_status(io96b_ctrl->io96b[i].io96b_csr_addr); + if (ret) { + io96b_ctrl->io96b[i].cal_status = false; + + printf("%s: Initial DDR calibration IO96B_%d failed %d\n", __func__, + i, ret); + + hang(); + } + + io96b_ctrl->io96b[i].cal_status = true; + + printf("%s: Initial DDR calibration IO96B_%d succeed\n", __func__, i); + + count++; + } + + if (count == io96b_ctrl->num_instance) + io96b_ctrl->overall_cal_status = true; +} + +int get_mem_technology(struct io96b_info *io96b_ctrl) +{ + int i, j, ret = 0; + u32 mem_technology_intf; + u8 ddr_type_ret; + + u32 mem_technology_intf_offset[MAX_MEM_INTERFACE_SUPPORTED] = { + IOSSM_MEM_TECHNOLOGY_INTF0_OFFSET, + IOSSM_MEM_TECHNOLOGY_INTF1_OFFSET + }; + + /* Initialize ddr type */ + io96b_ctrl->ddr_type = ddr_type_list[6]; + + /* Get and ensure all memory interface(s) same DDR type */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) { + mem_technology_intf = readl(io96b_ctrl->io96b[i].io96b_csr_addr + + mem_technology_intf_offset[j]); + + ddr_type_ret = FIELD_GET(INTF_DDR_TYPE_MASK, mem_technology_intf); + + if (!strcmp(io96b_ctrl->ddr_type, "UNKNOWN")) + io96b_ctrl->ddr_type = ddr_type_list[ddr_type_ret]; + + if (ddr_type_list[ddr_type_ret] != io96b_ctrl->ddr_type) { + printf("%s: Mismatch DDR type on IO96B_%d\n", __func__, i); + + ret = -EINVAL; + goto err; + } + } + } + +err: + return ret; +} + +int get_mem_width_info(struct io96b_info *io96b_ctrl) +{ + int i, j, ret = 0; + u32 mem_width_info; + u16 memory_size, total_memory_size = 0; + + u32 mem_total_capacity_intf_offset[MAX_MEM_INTERFACE_SUPPORTED] = { + IOSSM_MEM_TOTAL_CAPACITY_INTF0_OFFSET, + IOSSM_MEM_TOTAL_CAPACITY_INTF1_OFFSET + }; + + /* Get all memory interface(s) total memory size on all instance(s) */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + memory_size = 0; + for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) { + mem_width_info = readl(io96b_ctrl->io96b[i].io96b_csr_addr + + mem_total_capacity_intf_offset[j]); + + memory_size = memory_size + + FIELD_GET(INTF_CAPACITY_GBITS_MASK, mem_width_info); + } + + if (!memory_size) { + printf("%s: Failed to get valid memory size\n", __func__); + ret = -EINVAL; + goto err; + } + + io96b_ctrl->io96b[i].size = memory_size; + + total_memory_size = total_memory_size + memory_size; + } + + if (!total_memory_size) { + printf("%s: Failed to get valid memory size\n", __func__); + ret = -EINVAL; + } + + io96b_ctrl->overall_size = total_memory_size; + +err: + return ret; +} + +int ecc_enable_status(struct io96b_info *io96b_ctrl) +{ + int i, j, ret = 0; + u32 ecc_enable_intf; + bool ecc_stat, ecc_stat_set = false; + + u32 ecc_enable_intf_offset[MAX_MEM_INTERFACE_SUPPORTED] = { + IOSSM_ECC_ENABLE_INTF0_OFFSET, + IOSSM_ECC_ENABLE_INTF1_OFFSET + }; + + /* Initialize ECC status */ + io96b_ctrl->ecc_status = false; + + /* Get and ensure all memory interface(s) same ECC status */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) { + ecc_enable_intf = readl(io96b_ctrl->io96b[i].io96b_csr_addr + + ecc_enable_intf_offset[j]); + + ecc_stat = (FIELD_GET(INTF_ECC_ENABLE_TYPE_MASK, ecc_enable_intf) + == 0) ? false : true; + + if (!ecc_stat_set) { + io96b_ctrl->ecc_status = ecc_stat; + ecc_stat_set = true; + } + + if (ecc_stat != io96b_ctrl->ecc_status) { + printf("%s: Mismatch DDR ECC status on IO96B_%d\n", __func__, i); + + ret = -EINVAL; + goto err; + } + } + } + + debug("%s: ECC enable status: %d\n", __func__, io96b_ctrl->ecc_status); + +err: + return ret; +} + +bool is_double_bit_error(enum ecc_error_type err_type) +{ + switch (err_type) { + case DOUBLE_BIT_ERROR: + case MULTIPLE_DOUBLE_BIT_ERRORS: + case WRITE_LINK_DOUBLE_BIT_ERROR: + case READ_LINK_DOUBLE_BIT_ERROR: + case READ_MODIFY_WRITE_DOUBLE_BIT_ERROR: + return true; + + default: + return false; + } +} + +bool ecc_interrupt_status(struct io96b_info *io96b_ctrl) +{ + int i, j; + u32 ecc_err_status; + u16 ecc_err_counter; + bool ecc_error_flag = false; + + /* Get ECC double-bit error status */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + ecc_err_status = readl(io96b_ctrl->io96b[i].io96b_csr_addr + + IOSSM_ECC_ERR_STATUS_OFFSET); + ecc_err_counter = FIELD_GET(ECC_ERR_COUNTER_MASK, ecc_err_status); + debug("%s: ECC error number detected on IO96B_%d: %d\n", + __func__, i, ecc_err_counter); + + if (ecc_err_counter != 0) { + phys_addr_t address; + u32 ecc_err_data; + struct ecc_err_info err_info; + + address = io96b_ctrl->io96b[i].io96b_csr_addr + + IOSSM_ECC_ERR_DATA_START_OFFSET; + + for (j = 0; j < ecc_err_counter && j < MAX_ECC_ERR_INFO_COUNT; j++) { + ecc_err_data = readl(address); + err_info.err_type = FIELD_GET(ECC_ERR_TYPE_MASK, + ecc_err_data); + err_info.ip_type = FIELD_GET(ECC_ERR_IP_TYPE_MASK, + ecc_err_data); + err_info.instance_id = FIELD_GET(ECC_ERR_INSTANCE_ID_MASK, + ecc_err_data); + err_info.source_id = FIELD_GET(ECC_ERR_SOURCE_ID_MASK, + ecc_err_data); + err_info.addr_upper = FIELD_GET(ECC_ERR_ADDR_UPPER_MASK, + ecc_err_data); + err_info.addr_lower = readl(address + sizeof(u32)); + + debug("%s: ECC double-bit error detected on IO96B_%d:\n", + __func__, i); + debug("- error info address :0x%llx\n", address); + debug("- error ip type: %d\n", err_info.ip_type); + debug("- error instance id: %d\n", err_info.instance_id); + debug("- error source id: %d\n", err_info.source_id); + debug("- error type: %d\n", err_info.err_type); + debug("- error address upper: 0x%x\n", err_info.addr_upper); + debug("- error address lower: 0x%x\n", err_info.addr_lower); + + if (is_double_bit_error(err_info.err_type)) { + if (!ecc_error_flag) + ecc_error_flag = true; + } + + address += sizeof(u32) * 2; + } + } + } + + if (ecc_error_flag) + printf("\n%s: ECC double-bit error detected!\n", __func__); + + return ecc_error_flag; +} + +int bist_mem_init_start(struct io96b_info *io96b_ctrl) +{ + struct io96b_mb_req usr_req; + struct io96b_mb_resp usr_resp; + int i, j, n, ret = 0; + bool bist_start, bist_success; + u32 mem_init_status_intf, start; + + u32 mem_init_status_offset[MAX_MEM_INTERFACE_SUPPORTED] = { + IOSSM_MEM_INIT_STATUS_INTF0_OFFSET, + IOSSM_MEM_INIT_STATUS_INTF1_OFFSET + }; + + /* Full memory initialization BIST performed on all memory interface(s) */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + for (j = 0; j < io96b_ctrl->io96b[i].mb_ctrl.num_mem_interface; j++) { + bist_start = false; + bist_success = false; + + /* Start memory initialization BIST on full memory address */ + IO96B_MB_REQ_SETUP(io96b_ctrl->io96b[i].mb_ctrl.ip_type[j], + io96b_ctrl->io96b[i].mb_ctrl.ip_id[j], + CMD_TRIG_CONTROLLER_OP, BIST_MEM_INIT_START, + BIST_FULL_MEM); + + ret = io96b_mb_req(io96b_ctrl->io96b[i].io96b_csr_addr, + usr_req, 0, &usr_resp); + if (ret) + goto err; + + bist_start = IOSSM_CMD_RESPONSE_DATA_SHORT(usr_resp.cmd_resp_status) + & BIT(0); + + if (!bist_start) { + printf("%s: Failed to initialize memory on IO96B_%d\n", __func__, + i); + printf("%s: BIST_MEM_INIT_START Error code 0x%lx\n", __func__, + IOSSM_STATUS_CMD_RESPONSE_ERROR(usr_resp.cmd_resp_status)); + + ret = -EINVAL; + goto err; + } + + /* Polling for the initiated memory initialization BIST status */ + start = get_timer(0); + while (!bist_success) { + udelay(1); + + mem_init_status_intf = readl(io96b_ctrl->io96b[i].io96b_csr_addr + + mem_init_status_offset[j]); + + bist_success = FIELD_GET(INTF_BIST_STATUS_MASK, + mem_init_status_intf); + + if (!bist_success && (get_timer(start) > TIMEOUT)) { + printf("%s: Timeout initialize memory on IO96B_%d\n", + __func__, i); + printf("%s: BIST_MEM_INIT_STATUS Error code 0x%lx\n", + __func__, + IOSSM_STATUS_CMD_RESPONSE_ERROR(usr_resp.cmd_resp_status)); + + ret = -ETIMEDOUT; + goto err; + } + } + } + + debug("%s: Memory initialized successfully on IO96B_%d\n", __func__, i); + } + +err: + return ret; +} diff --git a/drivers/ddr/altera/iossm_mailbox.h b/drivers/ddr/altera/iossm_mailbox.h new file mode 100644 index 00000000000..6f794781d30 --- /dev/null +++ b/drivers/ddr/altera/iossm_mailbox.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025 Altera Corporation <www.altera.com> + * + */ + +#define MAX_IO96B_SUPPORTED 2 +#define MAX_MEM_INTERFACE_SUPPORTED 2 +#define NUM_CMD_RESPONSE_DATA 3 +#define NUM_CMD_PARAM 7 + +/* supported mailbox command type */ +enum iossm_mailbox_cmd_type { + CMD_NOP, + CMD_GET_SYS_INFO, + CMD_GET_MEM_INFO, + CMD_GET_MEM_CAL_INFO, + CMD_TRIG_CONTROLLER_OP, + CMD_TRIG_MEM_CAL_OP +}; + +/* supported mailbox command opcode */ +enum iossm_mailbox_cmd_opcode { + ECC_ENABLE_SET = 0x0101, + ECC_INTERRUPT_MASK = 0x0105, + ECC_WRITEBACK_ENABLE = 0x0106, + ECC_INJECT_ERROR = 0x0109, + ECC_SCRUB_MODE_0_START = 0x0202, + ECC_SCRUB_MODE_1_START = 0x0203, + BIST_STANDARD_MODE_START = 0x0301, + BIST_MEM_INIT_START = 0x0303, + BIST_SET_DATA_PATTERN_UPPER = 0x0305, + BIST_SET_DATA_PATTERN_LOWER = 0x0306, + TRIG_MEM_CAL = 0x000a +}; + +/* + * IOSSM mailbox required information + * + * @num_mem_interface: Number of memory interfaces instantiated + * @ip_type: IP type implemented on the IO96B + * @ip_instance_id: IP identifier for every IP instance implemented on the IO96B + */ +struct io96b_mb_ctrl { + u32 num_mem_interface; + u32 ip_type[2]; + u32 ip_id[2]; +}; + +/* CMD_REQ Register Definition */ +#define CMD_TARGET_IP_TYPE_MASK GENMASK(31, 29) +#define CMD_TARGET_IP_INSTANCE_ID_MASK GENMASK(28, 24) +#define CMD_TYPE_MASK GENMASK(23, 16) +#define CMD_OPCODE_MASK GENMASK(15, 0) + +/* + * IOSSM mailbox request + * @ip_type: IP type for the specified memory interface + * @ip_id: IP instance ID for the specified memory interface + * @usr_cmd_type: User desire IOSSM mailbox command type + * @usr_cmd_opcode: User desire IOSSM mailbox command opcode + * @cmd_param_*: Parameters (if applicable) for the requested IOSSM mailbox command + */ +struct io96b_mb_req { + u32 ip_type; + u32 ip_id; + u32 usr_cmd_type; + u32 usr_cmd_opcode; + u32 cmd_param[NUM_CMD_PARAM]; +}; + +/* + * IOSSM mailbox response outputs + * + * @cmd_resp_status: Command Interface status + * @cmd_resp_data_*: More spaces for command response + */ +struct io96b_mb_resp { + u32 cmd_resp_status; + u32 cmd_resp_data[NUM_CMD_RESPONSE_DATA]; +}; + +/* + * IO96B instance specific information + * + * @size: Memory size + * @io96b_csr_addr: IO96B instance CSR address + * @cal_status: IO96B instance calibration status + * @mb_ctrl: IOSSM mailbox required information + */ +struct io96b_instance { + u16 size; + phys_addr_t io96b_csr_addr; + bool cal_status; + struct io96b_mb_ctrl mb_ctrl; +}; + +/* + * Overall IO96B instance(s) information + * + * @num_instance: Number of instance(s) assigned to HPS + * @overall_cal_status: Overall calibration status for all IO96B instance(s) + * @ddr_type: DDR memory type + * @ecc_status: ECC enable status (false = disabled, true = enabled) + * @overall_size: Total DDR memory size + * @io96b[]: IO96B instance specific information + * @ckgen_lock: IO96B GEN PLL lock (false = not locked, true = locked) + * @num_port: Number of IO96B port. + * @io96b_pll: Selected IO96B PLL. Example bit 0: EMIF0 PLL A selected, + * bit 1: EMIF0 PLL B selected, bit 2 - EMIF1 PLL A selected, + * bit 3: EMIF1 PLL B selected + */ +struct io96b_info { + u8 num_instance; + bool overall_cal_status; + const char *ddr_type; + bool ecc_status; + u16 overall_size; + struct io96b_instance io96b[MAX_IO96B_SUPPORTED]; + bool ckgen_lock; + u8 num_port; + u8 io96b_pll; +}; + +int io96b_mb_req(phys_addr_t io96b_csr_addr, struct io96b_mb_req req, + u32 resp_data_len, struct io96b_mb_resp *resp); + +/* Supported IOSSM mailbox function */ +void io96b_mb_init(struct io96b_info *io96b_ctrl); +int io96b_cal_status(phys_addr_t addr); +void init_mem_cal(struct io96b_info *io96b_ctrl); +int get_mem_technology(struct io96b_info *io96b_ctrl); +int get_mem_width_info(struct io96b_info *io96b_ctrl); +int ecc_enable_status(struct io96b_info *io96b_ctrl); +int bist_mem_init_start(struct io96b_info *io96b_ctrl); +bool ecc_interrupt_status(struct io96b_info *io96b_ctrl); diff --git a/drivers/ddr/altera/sdram_agilex5.c b/drivers/ddr/altera/sdram_agilex5.c new file mode 100644 index 00000000000..801a6bbab46 --- /dev/null +++ b/drivers/ddr/altera/sdram_agilex5.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Altera Corporation <www.altera.com> + * + */ + +#include <stdlib.h> +#include <div64.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <hang.h> +#include <log.h> +#include <ram.h> +#include <reset.h> +#include <wait_bit.h> +#include <wdt.h> +#include <linux/bitfield.h> +#include <linux/sizes.h> +#include <asm/arch/firewall.h> +#include <asm/arch/reset_manager.h> +#include <asm/arch/system_manager.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include "iossm_mailbox.h" +#include "sdram_soc64.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* MPFE NOC registers */ +#define F2SDRAM_SIDEBAND_FLAGOUTSET0 0x50 +#define F2SDRAM_SIDEBAND_FLAGOUTSTATUS0 0x58 +#define SIDEBANDMGR_FLAGOUTSET0_REG SOCFPGA_F2SDRAM_MGR_ADDRESS +\ + F2SDRAM_SIDEBAND_FLAGOUTSET0 +#define SIDEBANDMGR_FLAGOUTSTATUS0_REG SOCFPGA_F2SDRAM_MGR_ADDRESS +\ + F2SDRAM_SIDEBAND_FLAGOUTSTATUS0 +#define BOOT_SCRATCH_COLD3_REG (socfpga_get_sysmgr_addr() +\ + SYSMGR_SOC64_BOOT_SCRATCH_COLD3) +#define PORT_EMIF_CONFIG_OFFSET 4 +#define EMIF_PLL_MASK GENMASK(19, 16) + +#define IO96B0_DUAL_PORT_MASK BIT(0) +#define IO96B0_DUAL_EMIF_MASK BIT(1) + +#define FIREWALL_MPFE_SCR_IO96B0_REG 0x18000d00 +#define FIREWALL_MPFE_SCR_IO96B1_REG 0x18000d04 +#define FIREWALL_MPFE_NOC_CSR_REG 0x18000d08 + +#define MEMORY_BANK_MAX_COUNT 3 + +/* Reset type */ +enum reset_type { + POR_RESET, + WARM_RESET, + COLD_RESET, + NCONFIG, + JTAG_CONFIG, + RSU_RECONFIG +}; + +phys_addr_t io96b_csr_reg_addr[] = { + 0x18400000, /* IO96B_0 CSR registers address */ + 0x18800000 /* IO96B_1 CSR registers address */ +}; + +struct dram_bank_info_s { + phys_addr_t start; + phys_size_t max_size; +}; + +struct dram_bank_info_s dram_bank_info[MEMORY_BANK_MAX_COUNT] = { + {0x80000000, 0x80000000}, /* Memory Bank 0 */ + {0x880000000, 0x780000000}, /* Memory Bank 1 */ + {0x8800000000, 0x7800000000} /* Memory Bank 2 */ +}; + +static enum reset_type get_reset_type(u32 reg) +{ + return FIELD_GET(ALT_SYSMGR_SCRATCH_REG_3_DDR_RESET_TYPE_MASK, reg); +} + +static void update_io96b_assigned_to_hps(bool dual_port_flag, bool dual_emif_flag) +{ + clrsetbits_le32(BOOT_SCRATCH_COLD3_REG, + ALT_SYSMGR_SCRATCH_REG_3_DDR_PORT_EMIF_INFO_MASK, + FIELD_PREP(ALT_SYSMGR_SCRATCH_REG_3_DDR_PORT_INFO_MASK, dual_port_flag) | + FIELD_PREP(ALT_SYSMGR_SCRATCH_REG_3_DDR_EMIF_INFO_MASK, dual_emif_flag)); + + debug("%s: update dual port dual emif info: 0x%x\n", __func__, + readl(BOOT_SCRATCH_COLD3_REG)); +} + +static void set_mpfe_config(void) +{ + /* Set mpfe_lite_intfcsel */ + setbits_le32(socfpga_get_sysmgr_addr() + SYSMGR_SOC64_MPFE_CONFIG, BIT(2)); + + /* Set mpfe_lite_active */ + setbits_le32(socfpga_get_sysmgr_addr() + SYSMGR_SOC64_MPFE_CONFIG, BIT(8)); + + debug("%s: mpfe_config: 0x%x\n", __func__, + readl(socfpga_get_sysmgr_addr() + SYSMGR_SOC64_MPFE_CONFIG)); +} + +static bool is_ddr_init_hang(void) +{ + u32 reg = readl(socfpga_get_sysmgr_addr() + + SYSMGR_SOC64_BOOT_SCRATCH_POR0); + + debug("%s: 0x%x\n", __func__, reg); + + if (reg & ALT_SYSMGR_SCRATCH_REG_POR_0_DDR_PROGRESS_MASK) + return true; + + return false; +} + +static void ddr_init_inprogress(bool start) +{ + if (start) + setbits_le32(socfpga_get_sysmgr_addr() + + SYSMGR_SOC64_BOOT_SCRATCH_POR0, + ALT_SYSMGR_SCRATCH_REG_POR_0_DDR_PROGRESS_MASK); + else + clrbits_le32(socfpga_get_sysmgr_addr() + + SYSMGR_SOC64_BOOT_SCRATCH_POR0, + ALT_SYSMGR_SCRATCH_REG_POR_0_DDR_PROGRESS_MASK); +} + +static void populate_ddr_handoff(struct udevice *dev, struct io96b_info *io96b_ctrl) +{ + struct altera_sdram_plat *plat = dev_get_plat(dev); + int i; + u32 len = SOC64_HANDOFF_SDRAM_LEN; + u32 handoff_table[len]; + + /* Read handoff for DDR configuration */ + socfpga_handoff_read((void *)SOC64_HANDOFF_SDRAM, handoff_table, len); + + /* Read handoff - dual port */ + plat->dualport = FIELD_GET(IO96B0_DUAL_PORT_MASK, handoff_table[PORT_EMIF_CONFIG_OFFSET]); + debug("%s: dualport from handoff: 0x%x\n", __func__, plat->dualport); + + if (plat->dualport) + io96b_ctrl->num_port = 2; + else + io96b_ctrl->num_port = 1; + + /* Read handoff - dual EMIF */ + plat->dualemif = FIELD_GET(IO96B0_DUAL_EMIF_MASK, handoff_table[PORT_EMIF_CONFIG_OFFSET]); + debug("%s: dualemif from handoff: 0x%x\n", __func__, plat->dualemif); + + if (plat->dualemif) + io96b_ctrl->num_instance = 2; + else + io96b_ctrl->num_instance = 1; + + io96b_ctrl->io96b_pll = FIELD_GET(EMIF_PLL_MASK, + handoff_table[PORT_EMIF_CONFIG_OFFSET]); + debug("%s: io96b enabled pll from handoff: 0x%x\n", __func__, io96b_ctrl->io96b_pll); + + update_io96b_assigned_to_hps(plat->dualport, plat->dualemif); + + /* Assign IO96B CSR base address if it is valid */ + for (i = 0; i < io96b_ctrl->num_instance; i++) { + io96b_ctrl->io96b[i].io96b_csr_addr = io96b_csr_reg_addr[i]; + debug("%s: IO96B 0x%llx CSR enabled\n", __func__, + io96b_ctrl->io96b[i].io96b_csr_addr); + } +} + +static void config_mpfe_sideband_mgr(struct udevice *dev) +{ + struct altera_sdram_plat *plat = dev_get_plat(dev); + + /* Dual port setting */ + if (plat->dualport) + setbits_le32(SIDEBANDMGR_FLAGOUTSET0_REG, BIT(4)); + + /* Dual EMIF setting */ + if (plat->dualemif) { + set_mpfe_config(); + setbits_le32(SIDEBANDMGR_FLAGOUTSET0_REG, BIT(5)); + } + + debug("%s: SIDEBANDMGR_FLAGOUTSTATUS0: 0x%x\n", __func__, + readl(SIDEBANDMGR_FLAGOUTSTATUS0_REG)); +} + +static void config_ccu_mgr(struct udevice *dev) +{ + int ret = 0; + struct altera_sdram_plat *plat = dev_get_plat(dev); + + if (plat->dualport || plat->dualemif) { + debug("%s: config interleaving on ccu reg\n", __func__); + ret = uclass_get_device_by_name(UCLASS_NOP, + "socfpga-ccu-ddr-interleaving-on", &dev); + } else { + debug("%s: config interleaving off ccu reg\n", __func__); + ret = uclass_get_device_by_name(UCLASS_NOP, + "socfpga-ccu-ddr-interleaving-off", &dev); + } + + if (ret) { + printf("interleaving on/off ccu settings init failed: %d\n", ret); + hang(); + } +} + +static void config_firewall_mpfe_csr(struct udevice *dev) +{ + int ret = 0; + + debug("%s: config Firewall setting for MPFE CSR\n", __func__); + ret = uclass_get_device_by_name(UCLASS_NOP, + "socfpga-noc-fw-mpfe-csr", &dev); + + if (ret) { + printf("Firewall setting for MPFE CSR init failed: %d\n", ret); + hang(); + } +} + +static bool hps_ocram_dbe_status(void) +{ + u32 reg = readl(BOOT_SCRATCH_COLD3_REG); + + if (reg & ALT_SYSMGR_SCRATCH_REG_3_OCRAM_DBE_MASK) + return true; + + return false; +} + +int sdram_mmr_init_full(struct udevice *dev) +{ + int i, ret = 0; + phys_size_t hw_size; + struct altera_sdram_plat *plat = dev_get_plat(dev); + struct altera_sdram_priv *priv = dev_get_priv(dev); + struct io96b_info *io96b_ctrl = malloc(sizeof(*io96b_ctrl)); + + u32 reg = readl(BOOT_SCRATCH_COLD3_REG); + enum reset_type reset_t = get_reset_type(reg); + bool full_mem_init = false; + + /* DDR initialization progress status tracking */ + bool is_ddr_hang_be4_rst = is_ddr_init_hang(); + + debug("DDR: SDRAM init in progress ...\n"); + ddr_init_inprogress(true); + + gd->bd = (struct bd_info *)malloc(sizeof(struct bd_info)); + memset(gd->bd, '\0', sizeof(struct bd_info)); + + debug("DDR: Address MPFE 0x%llx\n", plat->mpfe_base_addr); + + /* Populating DDR handoff data */ + debug("DDR: Checking SDRAM configuration in progress ...\n"); + populate_ddr_handoff(dev, io96b_ctrl); + + /* Configuring MPFE sideband manager registers - dual port & dual emif */ + config_mpfe_sideband_mgr(dev); + + /* Configuring Interleave/Non-interleave ccu registers */ + config_ccu_mgr(dev); + + /* Configure if polling is needed for IO96B GEN PLL locked */ + io96b_ctrl->ckgen_lock = true; + + /* Ensure calibration status passing */ + init_mem_cal(io96b_ctrl); + + printf("DDR: Calibration success\n"); + + /* Initiate IOSSM mailbox */ + io96b_mb_init(io96b_ctrl); + + /* DDR type, DDR size and ECC status) */ + ret = get_mem_technology(io96b_ctrl); + if (ret) { + printf("DDR: Failed to get DDR type\n"); + + goto err; + } + + ret = get_mem_width_info(io96b_ctrl); + if (ret) { + printf("DDR: Failed to get DDR size\n"); + + goto err; + } + + hw_size = (phys_size_t)io96b_ctrl->overall_size * SZ_1G / SZ_8; + + /* Get bank configuration from devicetree */ + ret = fdtdec_decode_ram_size(gd->fdt_blob, NULL, 0, NULL, + (phys_size_t *)&gd->ram_size, gd->bd); + if (ret) { + puts("DDR: Failed to decode memory node\n"); + ret = -ENXIO; + + goto err; + } + + if (gd->ram_size > hw_size) { + printf("DDR: Warning: DRAM size from device tree (%lld MiB) exceeds\n", + gd->ram_size >> 20); + printf(" the actual hardware capacity(%lld MiB). Memory configuration will be\n", + hw_size >> 20); + printf(" adjusted to match the detected hardware size.\n"); + gd->ram_size = 0; + } + + if (gd->ram_size > 0 && gd->ram_size != hw_size) { + printf("DDR: Warning: DRAM size from device tree (%lld MiB)\n", + gd->ram_size >> 20); + printf(" mismatch with hardware capacity(%lld MiB).\n", + hw_size >> 20); + } + + if (gd->ram_size == 0 && hw_size > 0) { + phys_size_t remaining_size, size_counter = 0; + u8 config_dram_banks; + + if (CONFIG_NR_DRAM_BANKS > MEMORY_BANK_MAX_COUNT) { + printf("DDR: Warning: CONFIG_NR_DRAM_BANKS(%d) is bigger than Max Memory Bank count(%d).\n", + CONFIG_NR_DRAM_BANKS, MEMORY_BANK_MAX_COUNT); + printf(" Max Memory Bank count is in use instead of CONFIG_NR_DRAM_BANKS.\n"); + config_dram_banks = MEMORY_BANK_MAX_COUNT; + } else { + config_dram_banks = CONFIG_NR_DRAM_BANKS; + } + + for (i = 0; i < config_dram_banks; i++) { + remaining_size = hw_size - size_counter; + if (remaining_size <= dram_bank_info[i].max_size) { + gd->bd->bi_dram[i].start = dram_bank_info[i].start; + gd->bd->bi_dram[i].size = remaining_size; + debug("Memory bank[%d] Starting address: 0x%llx size: 0x%llx\n", + i, gd->bd->bi_dram[i].start, gd->bd->bi_dram[i].size); + break; + } + + gd->bd->bi_dram[i].start = dram_bank_info[i].start; + gd->bd->bi_dram[i].size = dram_bank_info[i].max_size; + + debug("Memory bank[%d] Starting address: 0x%llx size: 0x%llx\n", + i, gd->bd->bi_dram[i].start, gd->bd->bi_dram[i].size); + size_counter += gd->bd->bi_dram[i].size; + } + + gd->ram_size = hw_size; + } + + printf("%s: %lld MiB\n", io96b_ctrl->ddr_type, gd->ram_size >> 20); + + ret = ecc_enable_status(io96b_ctrl); + if (ret) { + printf("DDR: Failed to get ECC enabled status\n"); + + goto err; + } + + /* Is HPS cold or warm reset? If yes, Skip full memory initialization if ECC + * enabled to preserve memory content + */ + if (io96b_ctrl->ecc_status) { + if (ecc_interrupt_status(io96b_ctrl)) { + if (CONFIG_IS_ENABLED(WDT)) { + struct udevice *wdt; + + printf("DDR: ECC error recover start now\n"); + ret = uclass_first_device_err(UCLASS_WDT, &wdt); + if (ret) { + printf("DDR: Failed to trigger watchdog reset\n"); + hang(); + } + + wdt_expire_now(wdt, 0); + } + hang(); + } + + full_mem_init = hps_ocram_dbe_status() | is_ddr_hang_be4_rst; + if (full_mem_init || !(reset_t == WARM_RESET || reset_t == COLD_RESET)) { + ret = bist_mem_init_start(io96b_ctrl); + if (ret) { + printf("DDR: Failed to fully initialize DDR memory\n"); + + goto err; + } + } + + printf("SDRAM-ECC: Initialized success\n"); + } + + sdram_size_check(gd->bd); + printf("DDR: size check success\n"); + + sdram_set_firewall(gd->bd); + + /* Firewall setting for MPFE CSR */ + config_firewall_mpfe_csr(dev); + + printf("DDR: firewall init success\n"); + + priv->info.base = gd->bd->bi_dram[0].start; + priv->info.size = gd->ram_size; + + /* Ending DDR driver initialization success tracking */ + ddr_init_inprogress(false); + + printf("DDR: init success\n"); + +err: + free(io96b_ctrl); + + return ret; +} diff --git a/drivers/ddr/altera/sdram_soc64.c b/drivers/ddr/altera/sdram_soc64.c index 10a8e64af3d..c8c9211adce 100644 --- a/drivers/ddr/altera/sdram_soc64.c +++ b/drivers/ddr/altera/sdram_soc64.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2016-2022 Intel Corporation <www.intel.com> + * Copyright (C) 2025 Altera Corporation <www.altera.com> * */ @@ -28,6 +29,7 @@ #define PGTABLE_OFF 0x4000 +#if !IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5) u32 hmc_readl(struct altera_sdram_plat *plat, u32 reg) { return readl(plat->iomhc + reg); @@ -99,8 +101,9 @@ int emif_reset(struct altera_sdram_plat *plat) debug("DDR: %s triggered successly\n", __func__); return 0; } +#endif -#if !IS_ENABLED(CONFIG_TARGET_SOCFPGA_N5X) +#if !(IS_ENABLED(CONFIG_TARGET_SOCFPGA_N5X) || IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5)) int poll_hmc_clock_status(void) { return wait_for_bit_le32((const void *)(socfpga_get_sysmgr_addr() + @@ -252,7 +255,7 @@ phys_size_t sdram_calculate_size(struct altera_sdram_plat *plat) return size; } -void sdram_set_firewall(struct bd_info *bd) +static void sdram_set_firewall_non_f2sdram(struct bd_info *bd) { u32 i; phys_size_t value; @@ -288,7 +291,7 @@ void sdram_set_firewall(struct bd_info *bd) FW_MPU_DDR_SCR_NONMPUREGION0ADDR_BASEEXT + (i * 4 * sizeof(u32))); - /* Setting non-secure MPU limit and limit extexded */ + /* Setting non-secure MPU limit and limit extended */ value = bd->bi_dram[i].start + bd->bi_dram[i].size - 1; lower = lower_32_bits(value); @@ -301,7 +304,7 @@ void sdram_set_firewall(struct bd_info *bd) FW_MPU_DDR_SCR_MPUREGION0ADDR_LIMITEXT + (i * 4 * sizeof(u32))); - /* Setting non-secure Non-MPU limit and limit extexded */ + /* Setting non-secure Non-MPU limit and limit extended */ FW_MPU_DDR_SCR_WRITEL(lower, FW_MPU_DDR_SCR_NONMPUREGION0ADDR_LIMIT + (i * 4 * sizeof(u32))); @@ -314,15 +317,77 @@ void sdram_set_firewall(struct bd_info *bd) } } +#if IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5) +static void sdram_set_firewall_f2sdram(struct bd_info *bd) +{ + u32 i, lower, upper; + phys_size_t value; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + if (!bd->bi_dram[i].size) + continue; + + value = bd->bi_dram[i].start; + + /* Keep first 1MB of SDRAM memory region as secure region when + * using ATF flow, where the ATF code is located. + */ + if (IS_ENABLED(CONFIG_SPL_ATF) && i == 0) + value += SZ_1M; + + /* Setting base and base extended */ + lower = lower_32_bits(value); + upper = upper_32_bits(value); + FW_F2SDRAM_DDR_SCR_WRITEL(lower, + FW_F2SDRAM_DDR_SCR_REGION0ADDR_BASE + + (i * 4 * sizeof(u32))); + FW_F2SDRAM_DDR_SCR_WRITEL(upper & 0xff, + FW_F2SDRAM_DDR_SCR_REGION0ADDR_BASEEXT + + (i * 4 * sizeof(u32))); + + /* Setting limit and limit extended */ + value = bd->bi_dram[i].start + bd->bi_dram[i].size - 1; + + lower = lower_32_bits(value); + upper = upper_32_bits(value); + + FW_F2SDRAM_DDR_SCR_WRITEL(lower, + FW_F2SDRAM_DDR_SCR_REGION0ADDR_LIMIT + + (i * 4 * sizeof(u32))); + FW_F2SDRAM_DDR_SCR_WRITEL(upper & 0xff, + FW_F2SDRAM_DDR_SCR_REGION0ADDR_LIMITEXT + + (i * 4 * sizeof(u32))); + + FW_F2SDRAM_DDR_SCR_WRITEL(BIT(i), FW_F2SDRAM_DDR_SCR_EN_SET); + } +} +#endif + +void sdram_set_firewall(struct bd_info *bd) +{ + sdram_set_firewall_non_f2sdram(bd); + +#if IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5) + sdram_set_firewall_f2sdram(bd); +#endif +} + static int altera_sdram_of_to_plat(struct udevice *dev) { +#if !IS_ENABLED(CONFIG_TARGET_SOCFPGA_N5X) struct altera_sdram_plat *plat = dev_get_plat(dev); fdt_addr_t addr; +#endif /* These regs info are part of DDR handoff in bitstream */ #if IS_ENABLED(CONFIG_TARGET_SOCFPGA_N5X) return 0; -#endif +#elif IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5) + addr = dev_read_addr_index(dev, 0); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + plat->mpfe_base_addr = addr; +#else addr = dev_read_addr_index(dev, 0); if (addr == FDT_ADDR_T_NONE) @@ -338,7 +403,7 @@ static int altera_sdram_of_to_plat(struct udevice *dev) if (addr == FDT_ADDR_T_NONE) return -EINVAL; plat->hmc = (void __iomem *)addr; - +#endif return 0; } @@ -385,6 +450,7 @@ static const struct udevice_id altera_sdram_ids[] = { { .compatible = "altr,sdr-ctl-s10" }, { .compatible = "intel,sdr-ctl-agilex" }, { .compatible = "intel,sdr-ctl-n5x" }, + { .compatible = "intel,sdr-ctl-agilex5" }, { /* sentinel */ } }; diff --git a/drivers/ddr/altera/sdram_soc64.h b/drivers/ddr/altera/sdram_soc64.h index 87a70a861ba..183b1a33080 100644 --- a/drivers/ddr/altera/sdram_soc64.h +++ b/drivers/ddr/altera/sdram_soc64.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (C) 2017-2019 Intel Corporation <www.intel.com> + * Copyright (C) 2025 Altera Corporation <www.altera.com> + * */ #ifndef _SDRAM_SOC64_H_ @@ -13,11 +15,19 @@ struct altera_sdram_priv { struct reset_ctl_bulk resets; }; +#if IS_ENABLED(CONFIG_TARGET_SOCFPGA_AGILEX5) +struct altera_sdram_plat { + fdt_addr_t mpfe_base_addr; + bool dualport; + bool dualemif; +}; +#else struct altera_sdram_plat { void __iomem *hmc; void __iomem *ddr_sch; void __iomem *iomhc; }; +#endif /* ECC HMC registers */ #define DDRIOCTRL 0x8 diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 3c64e894646..4b47be6b016 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -76,6 +76,13 @@ config XILINX_DPDMA this file is used as placeholder for driver. The main reason is to record compatible string and calling power domain driver. +config ADI_DMA + bool "ADI DMA driver" + depends on DMA && DMA_CHANNELS + help + Enable DMA support for Analog Devices SOCs, such as the SC5xx. + Currently this is a minimalistic driver tested against OSPI use only. + if APBH_DMA config APBH_DMA_BURST bool "Enable DMA BURST" diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 48811eaaeb3..00d765864cd 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -13,5 +13,6 @@ obj-$(CONFIG_TI_KSNAV) += keystone_nav.o keystone_nav_cfg.o obj-$(CONFIG_TI_EDMA3) += ti-edma3.o obj-$(CONFIG_DMA_LPC32XX) += lpc32xx_dma.o obj-$(CONFIG_XILINX_DPDMA) += xilinx_dpdma.o +obj-$(CONFIG_ADI_DMA) += adi_dma.o obj-y += ti/ diff --git a/drivers/dma/adi_dma.c b/drivers/dma/adi_dma.c new file mode 100644 index 00000000000..28afe488db0 --- /dev/null +++ b/drivers/dma/adi_dma.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Analog Devices DMA controller driver + * + * (C) Copyright 2024 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * Contact: Greg Malysa <greg.malysa@timesys.com> + * Contact: Ian Roberts <ian.roberts@timesys.com> + * + */ +#include <dm.h> +#include <dma.h> +#include <dma-uclass.h> +#include <dm/device_compat.h> +#include <linux/errno.h> +#include <linux/io.h> + +#define HAS_MDMA BIT(0) + +#define REG_ADDRSTART 0x04 +#define REG_CFG 0x08 +#define REG_XCNT 0x0C +#define REG_XMOD 0x10 +#define REG_STAT 0x30 + +#define BITP_DMA_CFG_MSIZE 8 +#define BITP_DMA_CFG_PSIZE 4 +#define BITM_DMA_CFG_WNR 0x00000002 +#define BITM_DMA_CFG_EN 0x00000001 +#define ENUM_DMA_CFG_XCNT_INT 0x00100000 + +#define BITP_DMA_STAT_PBWID 12 +#define BITP_DMA_STAT_ERRC 4 +#define BITM_DMA_STAT_PBWID 0x00003000 +#define BITM_DMA_STAT_ERRC 0x00000070 +#define BITM_DMA_STAT_PIRQ 0x00000004 +#define BITM_DMA_STAT_IRQERR 0x00000002 +#define BITM_DMA_STAT_IRQDONE 0x00000001 + +#define DMA_MDMA_SRC_DEFAULT_CONFIG(psize, msize) \ + (BITM_DMA_CFG_EN | ((psize) << BITP_DMA_CFG_PSIZE) | ((msize) << BITP_DMA_CFG_MSIZE)) +#define DMA_MDMA_DST_DEFAULT_CONFIG(psize, msize) \ + (BITM_DMA_CFG_EN | BITM_DMA_CFG_WNR | ENUM_DMA_CFG_XCNT_INT | \ + ((psize) << BITP_DMA_CFG_PSIZE) | ((msize) << BITP_DMA_CFG_MSIZE)) + +struct adi_dma_channel { + int id; + struct adi_dma *dma; + void __iomem *iosrc; + void __iomem *iodest; +}; + +struct adi_dma { + struct udevice *dev; + struct adi_dma_channel channels[1]; + void __iomem *ioaddr; + unsigned long hw_cfg; +}; + +static const struct udevice_id dma_dt_ids[] = { + { .compatible = "adi,mdma-controller", .data = HAS_MDMA }, + { } +}; + +static u8 adi_dma_get_msize(u32 n_bytecount, u32 n_address) +{ + /* Calculate MSIZE, PSIZE, XCNT and XMOD */ + u8 n_msize = 0; + u32 n_value = n_bytecount | n_address; + u32 n_mask = 0x1; + + for (n_msize = 0; n_msize < 5; n_msize++, n_mask <<= 1) { + if ((n_value & n_mask) == n_mask) + break; + } + + return n_msize; +} + +static int adi_dma_get_ch_error(void __iomem *ch) +{ + u32 cause = (ioread32(ch + REG_STAT) & BITM_DMA_STAT_ERRC) >> + BITP_DMA_STAT_ERRC; + switch (cause) { + case 0: + return -EINVAL; + case 1: + return -EBUSY; + case 2: + return -EFAULT; + case 3: + fallthrough; + case 5: + fallthrough; + case 6: + fallthrough; + default: + return -EIO; + } +} + +static int adi_mdma_transfer(struct udevice *dev, int direction, + dma_addr_t dst, dma_addr_t src, size_t len) +{ + struct adi_dma *priv = dev_get_priv(dev); + void __iomem *chsrc = priv->channels[0].iosrc; + void __iomem *chdst = priv->channels[0].iodest; + + int result = 0; + u32 reg; + u32 bytecount = len; + + u8 n_srcmsize; + u8 n_dstmsize; + u8 n_srcpsize; + u8 n_dstpsize; + u8 n_psize; + u32 srcconfig; + u32 dstconfig; + u8 srcpsizemax = (ioread32(chsrc + REG_STAT) & BITM_DMA_STAT_PBWID) >> + BITP_DMA_STAT_PBWID; + u8 dstpsizemax = (ioread32(chdst + REG_STAT) & BITM_DMA_STAT_PBWID) >> + BITP_DMA_STAT_PBWID; + + const u32 CLRSTAT = (BITM_DMA_STAT_IRQDONE | BITM_DMA_STAT_IRQERR | + BITM_DMA_STAT_PIRQ); + + if (len == 0) + return -EINVAL; + + /* Clear DMA status */ + iowrite32(CLRSTAT, chsrc + REG_STAT); + iowrite32(CLRSTAT, chdst + REG_STAT); + + /* Calculate MSIZE, PSIZE, XCNT and XMOD */ + n_srcmsize = adi_dma_get_msize(bytecount, src); + n_dstmsize = adi_dma_get_msize(bytecount, dst); + n_srcpsize = min(n_srcmsize, srcpsizemax); + n_dstpsize = min(n_dstmsize, dstpsizemax); + n_psize = min(n_srcpsize, n_dstpsize); + + srcconfig = DMA_MDMA_SRC_DEFAULT_CONFIG(n_psize, n_srcmsize); + dstconfig = DMA_MDMA_DST_DEFAULT_CONFIG(n_psize, n_dstmsize); + + /* Load the DMA descriptors */ + iowrite32(src, chsrc + REG_ADDRSTART); + iowrite32(bytecount >> n_srcmsize, chsrc + REG_XCNT); + iowrite32(1 << n_srcmsize, chsrc + REG_XMOD); + iowrite32(dst, chdst + REG_ADDRSTART); + iowrite32(bytecount >> n_dstmsize, chdst + REG_XCNT); + iowrite32(1 << n_dstmsize, chdst + REG_XMOD); + + iowrite32(dstconfig, chdst + REG_CFG); + iowrite32(srcconfig, chsrc + REG_CFG); + + /* Wait for DMA to complete while checking for a DMA error */ + do { + reg = ioread32(chsrc + REG_STAT); + if ((reg & BITM_DMA_STAT_IRQERR) == BITM_DMA_STAT_IRQERR) { + result = adi_dma_get_ch_error(chsrc); + break; + } + reg = ioread32(chdst + REG_STAT); + if ((reg & BITM_DMA_STAT_IRQERR) == BITM_DMA_STAT_IRQERR) { + result = adi_dma_get_ch_error(chdst); + break; + } + } while ((reg & BITM_DMA_STAT_IRQDONE) == 0); + + clrbits_32(chsrc + REG_CFG, 1); + clrbits_32(chdst + REG_CFG, 1); + + return result; +} + +static int adi_dma_init_channel(struct adi_dma *dma, + struct adi_dma_channel *channel, ofnode node) +{ + u32 offset; + + if (ofnode_read_u32(node, "adi,id", &channel->id)) { + dev_err(dma->dev, "Missing adi,id for channel %s\n", + ofnode_get_name(node)); + return -ENOENT; + } + + if (ofnode_read_u32(node, "adi,src-offset", &offset)) { + dev_err(dma->dev, "Missing adi,src-offset for channel %s\n", + ofnode_get_name(node)); + return -ENOENT; + } + + channel->iosrc = dma->ioaddr + offset; + channel->dma = dma; + + if (dma->hw_cfg & HAS_MDMA) { + if (ofnode_read_u32(node, "adi,dest-offset", &offset)) { + dev_err(dma->dev, + "Missing adi,dest-offset for channel %s\n", + ofnode_get_name(node)); + return -ENOENT; + } + channel->iodest = dma->ioaddr + offset; + } + + return 0; +} + +static int adi_dma_probe(struct udevice *dev) +{ + struct dma_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct adi_dma *priv = dev_get_priv(dev); + ofnode node, child; + + priv->hw_cfg = dev_get_driver_data(dev); + if (priv->hw_cfg & HAS_MDMA) + uc_priv->supported = DMA_SUPPORTS_MEM_TO_MEM; + + priv->ioaddr = dev_remap_addr(dev); + if (!priv->ioaddr) + return -EINVAL; + + node = dev_read_first_subnode(dev); + if (!ofnode_valid(node)) { + dev_err(dev, + "Error: device tree DMA channel config missing!\n"); + return -ENODEV; + } + + node = dev_ofnode(dev); + ofnode_for_each_subnode(child, node) { + adi_dma_init_channel(priv, priv->channels, child); + break; //Only 1 channel supported for now + } + + return 0; +} + +static const struct dma_ops adi_dma_ops = { + .transfer = adi_mdma_transfer, +}; + +U_BOOT_DRIVER(adi_dma) = { + .name = "adi_dma", + .id = UCLASS_DMA, + .of_match = dma_dt_ids, + .ops = &adi_dma_ops, + .probe = adi_dma_probe, + .priv_auto = sizeof(struct adi_dma), +}; diff --git a/drivers/dma/ti/Makefile b/drivers/dma/ti/Makefile index 94ec13ba7ca..90c20a6a3fa 100644 --- a/drivers/dma/ti/Makefile +++ b/drivers/dma/ti/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_TI_K3_PSIL) += k3-psil-data.o k3-psil-data-y += k3-psil.o k3-psil-data-$(CONFIG_SOC_K3_AM654) += k3-psil-am654.o k3-psil-data-$(CONFIG_SOC_K3_J721E) += k3-psil-j721e.o +k3-psil-data-$(CONFIG_SOC_K3_J7200) += k3-psil-j721e.o k3-psil-data-$(CONFIG_SOC_K3_J721S2) += k3-psil-j721s2.o k3-psil-data-$(CONFIG_SOC_K3_AM642) += k3-psil-am64.o k3-psil-data-$(CONFIG_SOC_K3_AM625) += k3-psil-am62.o diff --git a/drivers/dma/ti/k3-psil.c b/drivers/dma/ti/k3-psil.c index 369e679886f..39798844a8a 100644 --- a/drivers/dma/ti/k3-psil.c +++ b/drivers/dma/ti/k3-psil.c @@ -20,6 +20,8 @@ struct psil_endpoint_config *psil_get_ep_config(u32 thread_id) soc_ep_map = &am654_ep_map; else if (IS_ENABLED(CONFIG_SOC_K3_J721E)) soc_ep_map = &j721e_ep_map; + else if (IS_ENABLED(CONFIG_SOC_K3_J7200)) + soc_ep_map = &j721e_ep_map; else if (IS_ENABLED(CONFIG_SOC_K3_J721S2)) soc_ep_map = &j721s2_ep_map; else if (IS_ENABLED(CONFIG_SOC_K3_AM642)) diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index 3013c4741d0..723265ab2e5 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -36,6 +36,7 @@ #include "k3-psil-priv.h" #define K3_UDMA_MAX_RFLOWS 1024 +#define K3_UDMA_MAX_TR 2 struct udma_chan; @@ -74,7 +75,6 @@ struct udma_tchan { struct k3_nav_ring *t_ring; /* Transmit ring */ struct k3_nav_ring *tc_ring; /* Transmit Completion ring */ int tflow_id; /* applicable only for PKTDMA */ - }; #define udma_bchan udma_tchan @@ -175,6 +175,7 @@ struct udma_dev { struct udma_rflow *rflows; struct udma_match_data *match_data; + void *bc_desc; struct udma_chan *channels; u32 psil_base; @@ -1349,6 +1350,7 @@ static int udma_setup_resources(struct udma_dev *ud) struct ti_sci_resource_desc *rm_desc; struct ti_sci_resource *rm_res; struct udma_tisci_rm *tisci_rm = &ud->tisci_rm; + size_t desc_size; ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt), sizeof(unsigned long), GFP_KERNEL); @@ -1366,9 +1368,11 @@ static int udma_setup_resources(struct udma_dev *ud) ud->rflows = devm_kcalloc(dev, ud->rflow_cnt, sizeof(*ud->rflows), GFP_KERNEL); + desc_size = cppi5_trdesc_calc_size(K3_UDMA_MAX_TR, sizeof(struct cppi5_tr_type15_t)); + ud->bc_desc = devm_kzalloc(dev, ALIGN(desc_size, ARCH_DMA_MINALIGN), GFP_KERNEL); if (!ud->tchan_map || !ud->rchan_map || !ud->rflow_map || !ud->rflow_map_reserved || !ud->tchans || !ud->rchans || - !ud->rflows) + !ud->rflows || !ud->bc_desc) return -ENOMEM; /* @@ -1444,6 +1448,7 @@ static int bcdma_setup_resources(struct udma_dev *ud) struct ti_sci_resource_desc *rm_desc; struct ti_sci_resource *rm_res; struct udma_tisci_rm *tisci_rm = &ud->tisci_rm; + size_t desc_size; ud->bchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->bchan_cnt), sizeof(unsigned long), GFP_KERNEL); @@ -1460,9 +1465,12 @@ static int bcdma_setup_resources(struct udma_dev *ud) ud->rflows = devm_kcalloc(dev, ud->rchan_cnt, sizeof(*ud->rflows), GFP_KERNEL); + desc_size = cppi5_trdesc_calc_size(K3_UDMA_MAX_TR, sizeof(struct cppi5_tr_type15_t)); + ud->bc_desc = devm_kzalloc(dev, ALIGN(desc_size, ARCH_DMA_MINALIGN), GFP_KERNEL); + if (!ud->bchan_map || !ud->tchan_map || !ud->rchan_map || !ud->bchans || !ud->tchans || !ud->rchans || - !ud->rflows) + !ud->rflows || !ud->bc_desc) return -ENOMEM; /* Get resource ranges from tisci */ @@ -1718,8 +1726,7 @@ static int *udma_prep_dma_memcpy(struct udma_chan *uc, dma_addr_t dest, int num_tr; size_t tr_size = sizeof(struct cppi5_tr_type15_t); u16 tr0_cnt0, tr0_cnt1, tr1_cnt0; - unsigned long dummy; - void *tr_desc; + void *tr_desc = uc->ud->bc_desc; size_t desc_size; if (len < SZ_64K) { @@ -1748,9 +1755,6 @@ static int *udma_prep_dma_memcpy(struct udma_chan *uc, dma_addr_t dest, } desc_size = cppi5_trdesc_calc_size(num_tr, tr_size); - tr_desc = dma_alloc_coherent(desc_size, &dummy); - if (!tr_desc) - return NULL; memset(tr_desc, 0, desc_size); cppi5_trdesc_init(tr_desc, num_tr, tr_size, 0, 0); diff --git a/drivers/firmware/ti_sci_static_data.h b/drivers/firmware/ti_sci_static_data.h index 3370f80231d..2e61e0c7de1 100644 --- a/drivers/firmware/ti_sci_static_data.h +++ b/drivers/firmware/ti_sci_static_data.h @@ -16,7 +16,7 @@ struct ti_sci_resource_static_data { #if IS_ENABLED(CONFIG_K3_DM_FW) -#if IS_ENABLED(CONFIG_SOC_K3_J721E) +#if IS_ENABLED(CONFIG_SOC_K3_J721E) || IS_ENABLED(CONFIG_SOC_K3_J7200) static struct ti_sci_resource_static_data rm_static_data[] = { /* Free rings */ { diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 21361f56e69..e11109fb56d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -97,6 +97,15 @@ config SPL_DM_GPIO_LOOKUP_LABEL different gpios on different hardware versions for the same functionality in board code. +config ADI_GPIO + bool "ADI GPIO driver" + depends on DM_GPIO && ARCH_SC5XX + help + This driver supports GPIO banks on SC5xx processors. It + supports inputs and outputs but does not support pin + interrupt functionality (PINT) or other features in the + Linux version of the driver. + config ALTERA_PIO bool "Altera PIO driver" depends on DM_GPIO @@ -545,6 +554,14 @@ config DM_PCA953X Now, max 24 bits chips and PCA953X compatible chips are supported +config ADP5588_GPIO + bool "ADP5588 GPIO expander driver" + depends on DM_GPIO && DM_I2C + help + Say yes here to support GPIO functionality of ADI ADP5588 chips. + + The ADP5588 is an 18-port I2C GPIO expander and keypad controller. + config SPL_DM_PCA953X bool "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports in SPL" depends on SPL_DM_GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 3f882c065d8..d426d3a2d7b 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_$(PHASE_)DM_GPIO) += gpio-uclass.o obj-$(CONFIG_$(XPL_)DM_PCA953X) += pca953x_gpio.o +obj-$(CONFIG_ADI_GPIO) += gpio-adi-adsp.o obj-$(CONFIG_ASPEED_GPIO) += gpio-aspeed.o obj-$(CONFIG_ASPEED_G7_GPIO) += gpio-aspeed-g7.o obj-$(CONFIG_ASPEED_SGPIO) += gpio-aspeed-sgpio.o @@ -74,6 +75,7 @@ obj-$(CONFIG_NOMADIK_GPIO) += nmk_gpio.o obj-$(CONFIG_MAX7320_GPIO) += max7320_gpio.o obj-$(CONFIG_$(XPL_)MAX77663_GPIO) += max77663_gpio.o obj-$(CONFIG_SL28CPLD_GPIO) += sl28cpld-gpio.o +obj-$(CONFIG_ADP5588_GPIO) += adp5588_gpio.o obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN) += zynqmp_gpio_modepin.o obj-$(CONFIG_SLG7XL45106_I2C_GPO) += gpio_slg7xl45106.o obj-$(CONFIG_FTGPIO010) += ftgpio010.o diff --git a/drivers/gpio/adp5588_gpio.c b/drivers/gpio/adp5588_gpio.c new file mode 100644 index 00000000000..d081e169897 --- /dev/null +++ b/drivers/gpio/adp5588_gpio.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * GPIO Chip driver for Analog Devices + * ADP5588/ADP5587 I/O Expander and QWERTY Keypad Controller + * + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * Contact: Greg Malysa <greg.malysa@timesys.com> + * + * Based on Michael Hennerich's Linux driver: + * Michael Hennerich <michael.hennerich@analog.com> + * + */ + +#include <dm.h> +#include <i2c.h> +#include <asm-generic/gpio.h> + +#define ADP5588_MAXGPIO 18 +#define ADP5588_BANK(offs) ((offs) >> 3) +#define ADP5588_BIT(offs) (1u << ((offs) & 0x7)) + +#define DEV_ID 0x00 /* Device ID */ +#define GPIO_DAT_STAT1 0x14 /* GPIO Data Status, Read twice to clear */ +#define GPIO_DAT_STAT2 0x15 /* GPIO Data Status, Read twice to clear */ +#define GPIO_DAT_STAT3 0x16 /* GPIO Data Status, Read twice to clear */ +#define GPIO_DAT_OUT1 0x17 /* GPIO DATA OUT */ +#define GPIO_DAT_OUT2 0x18 /* GPIO DATA OUT */ +#define GPIO_DAT_OUT3 0x19 /* GPIO DATA OUT */ +#define GPIO_INT_EN1 0x1A /* GPIO Interrupt Enable */ +#define GPIO_INT_EN2 0x1B /* GPIO Interrupt Enable */ +#define GPIO_INT_EN3 0x1C /* GPIO Interrupt Enable */ +#define KP_GPIO1 0x1D /* Keypad or GPIO Selection */ +#define KP_GPIO2 0x1E /* Keypad or GPIO Selection */ +#define KP_GPIO3 0x1F /* Keypad or GPIO Selection */ +#define GPIO_DIR1 0x23 /* GPIO Data Direction */ +#define GPIO_DIR2 0x24 /* GPIO Data Direction */ +#define GPIO_DIR3 0x25 /* GPIO Data Direction */ +#define GPIO_PULL1 0x2C /* GPIO Pull Disable */ +#define GPIO_PULL2 0x2D /* GPIO Pull Disable */ +#define GPIO_PULL3 0x2E /* GPIO Pull Disable */ +#define ID_MASK 0x0F + +struct adp5588_gpio { + u8 dat_out[3]; + u8 dir[3]; +}; + +static int adp5588_gpio_read(struct udevice *dev, u8 reg) +{ + int ret; + u8 val; + + ret = dm_i2c_read(dev, reg, &val, 1); + + if (ret < 0) { + pr_err("%s: read error\n", __func__); + return ret; + } + + return val; +} + +static int adp5588_gpio_write(struct udevice *dev, u8 reg, u8 val) +{ + int ret; + + ret = dm_i2c_write(dev, reg, &val, 1); + if (ret < 0) { + pr_err("%s: write error\n", __func__); + return ret; + } + + return 0; +} + +static int adp5588_get_value(struct udevice *dev, u32 offset) +{ + struct adp5588_gpio *plat = dev_get_plat(dev); + unsigned int bank = ADP5588_BANK(offset); + unsigned int bit = ADP5588_BIT(offset); + int val; + + if (plat->dir[bank] & bit) + val = plat->dat_out[bank]; + else + val = adp5588_gpio_read(dev, GPIO_DAT_STAT1 + bank); + + return !!(val & bit); +} + +static int adp5588_set_value(struct udevice *dev, u32 offset, + int32_t value) +{ + unsigned int bank, bit; + int ret; + struct adp5588_gpio *plat = dev_get_plat(dev); + + bank = ADP5588_BANK(offset); + bit = ADP5588_BIT(offset); + + if (value) + plat->dat_out[bank] |= bit; + else + plat->dat_out[bank] &= ~bit; + + ret = adp5588_gpio_write(dev, GPIO_DAT_OUT1 + bank, + plat->dat_out[bank]); + + return ret; +} + +static int adp5588_direction_input(struct udevice *dev, u32 offset) +{ + int ret; + unsigned int bank; + struct adp5588_gpio *plat = dev_get_plat(dev); + + bank = ADP5588_BANK(offset); + + plat->dir[bank] &= ~ADP5588_BIT(offset); + ret = adp5588_gpio_write(dev, GPIO_DIR1 + bank, plat->dir[bank]); + + return ret; +} + +static int adp5588_direction_output(struct udevice *dev, + u32 offset, int value) +{ + int ret; + unsigned int bank, bit; + struct adp5588_gpio *plat = dev_get_plat(dev); + + bank = ADP5588_BANK(offset); + bit = ADP5588_BIT(offset); + + plat->dir[bank] |= bit; + + if (value) + plat->dat_out[bank] |= bit; + else + plat->dat_out[bank] &= ~bit; + + ret = adp5588_gpio_write(dev, GPIO_DAT_OUT1 + bank, + plat->dat_out[bank]); + ret |= adp5588_gpio_write(dev, GPIO_DIR1 + bank, + plat->dir[bank]); + + return ret; +} + +static int adp5588_ofdata_platdata(struct udevice *dev) +{ + struct adp5588_gpio *plat = dev_get_plat(dev); + struct gpio_dev_priv *priv = dev_get_uclass_priv(dev); + int node = dev_of_offset(dev); + int ret, i, revid; + + priv->gpio_count = ADP5588_MAXGPIO; + priv->bank_name = fdt_get_name(gd->fdt_blob, node, NULL); + + ret = adp5588_gpio_read(dev, DEV_ID); + if (ret < 0) + return ret; + + revid = ret & ID_MASK; + + printf("ADP5588 Detected: Rev %x, Rev ID %x\n", ret, revid); + + for (i = 0, ret = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) { + plat->dat_out[i] = adp5588_gpio_read(dev, GPIO_DAT_OUT1 + i); + plat->dir[i] = adp5588_gpio_read(dev, GPIO_DIR1 + i); + ret |= adp5588_gpio_write(dev, KP_GPIO1 + i, 0); + ret |= adp5588_gpio_write(dev, GPIO_PULL1 + i, 0); + ret |= adp5588_gpio_write(dev, GPIO_INT_EN1 + i, 0); + if (ret) { + pr_err("%s: Initialization error\n", __func__); + return ret; + } + } + + return 0; +} + +static const struct dm_gpio_ops adp5588_ops = { + .direction_input = adp5588_direction_input, + .direction_output = adp5588_direction_output, + .get_value = adp5588_get_value, + .set_value = adp5588_set_value, +}; + +static const struct udevice_id adp5588_of_match_list[] = { + { .compatible = "adi,adp5588"}, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(gpio_adp5588) = { + .name = "gpio_adp5588", + .id = UCLASS_GPIO, + .ops = &adp5588_ops, + .of_match = adp5588_of_match_list, + .of_to_plat = adp5588_ofdata_platdata, + .plat_auto = sizeof(struct adp5588_gpio), + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/gpio/gpio-adi-adsp.c b/drivers/gpio/gpio-adi-adsp.c new file mode 100644 index 00000000000..0ce00572e08 --- /dev/null +++ b/drivers/gpio/gpio-adi-adsp.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Greg Malysa <greg.malysa@timesys.com> + * Additional Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + */ + +#include <dm.h> +#include <asm-generic/gpio.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <linux/io.h> + +#define ADSP_PORT_MMIO_SIZE 0x80 +#define ADSP_PORT_PIN_SIZE 16 + +#define ADSP_PORT_REG_FER 0x00 +#define ADSP_PORT_REG_FER_SET 0x04 +#define ADSP_PORT_REG_FER_CLEAR 0x08 +#define ADSP_PORT_REG_DATA 0x0c +#define ADSP_PORT_REG_DATA_SET 0x10 +#define ADSP_PORT_REG_DATA_CLEAR 0x14 +#define ADSP_PORT_REG_DIR 0x18 +#define ADSP_PORT_REG_DIR_SET 0x1c +#define ADSP_PORT_REG_DIR_CLEAR 0x20 +#define ADSP_PORT_REG_INEN 0x24 +#define ADSP_PORT_REG_INEN_SET 0x28 +#define ADSP_PORT_REG_INEN_CLEAR 0x2c +#define ADSP_PORT_REG_PORT_MUX 0x30 +#define ADSP_PORT_REG_DATA_TGL 0x34 +#define ADSP_PORT_REG_POLAR 0x38 +#define ADSP_PORT_REG_POLAR_SET 0x3c +#define ADSP_PORT_REG_POLAR_CLEAR 0x40 +#define ADSP_PORT_REG_LOCK 0x44 +#define ADSP_PORT_REG_TRIG_TGL 0x48 + +struct adsp_gpio_priv { + void __iomem *base; + int ngpio; +}; + +static u32 get_port(unsigned int pin) +{ + return pin / ADSP_PORT_PIN_SIZE; +} + +static u32 get_offset(unsigned int pin) +{ + return pin % ADSP_PORT_PIN_SIZE; +} + +static int adsp_gpio_input(struct udevice *udev, unsigned int pin) +{ + struct adsp_gpio_priv *priv = dev_get_priv(udev); + u32 port, offset; + void __iomem *portbase; + + if (pin < priv->ngpio) { + port = get_port(pin); + offset = get_offset(pin); + portbase = priv->base + port * ADSP_PORT_MMIO_SIZE; + + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_FER_CLEAR); + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DIR_CLEAR); + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_INEN_SET); + return 0; + } + + return -EINVAL; +} + +static int adsp_gpio_output(struct udevice *udev, unsigned int pin, int value) +{ + struct adsp_gpio_priv *priv = dev_get_priv(udev); + u32 port, offset; + void __iomem *portbase; + + if (pin < priv->ngpio) { + port = get_port(pin); + offset = get_offset(pin); + portbase = priv->base + port * ADSP_PORT_MMIO_SIZE; + + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_FER_CLEAR); + + if (value) + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_SET); + else + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_CLEAR); + + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DIR_SET); + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_INEN_CLEAR); + return 0; + } + + return -EINVAL; +} + +static int adsp_gpio_get_value(struct udevice *udev, unsigned int pin) +{ + struct adsp_gpio_priv *priv = dev_get_priv(udev); + u32 port, offset; + u16 val; + void __iomem *portbase; + + if (pin < priv->ngpio) { + port = get_port(pin); + offset = get_offset(pin); + portbase = priv->base + port * ADSP_PORT_MMIO_SIZE; + + val = ioread16(portbase + ADSP_PORT_REG_DATA); + return !!(val & BIT(offset)); + } + + return 0; +} + +static int adsp_gpio_set_value(struct udevice *udev, unsigned int pin, int value) +{ + struct adsp_gpio_priv *priv = dev_get_priv(udev); + u32 port, offset; + void __iomem *portbase; + + if (pin < priv->ngpio) { + port = get_port(pin); + offset = get_offset(pin); + portbase = priv->base + port * ADSP_PORT_MMIO_SIZE; + + if (value) + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_SET); + else + iowrite16(BIT(offset), portbase + ADSP_PORT_REG_DATA_CLEAR); + } + + return 0; +} + +static const struct dm_gpio_ops adsp_gpio_ops = { + .direction_input = adsp_gpio_input, + .direction_output = adsp_gpio_output, + .get_value = adsp_gpio_get_value, + .set_value = adsp_gpio_set_value, +}; + +static int adsp_gpio_probe(struct udevice *udev) +{ + struct adsp_gpio_priv *priv = dev_get_priv(udev); + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(udev); + + uc_priv->bank_name = "adsp gpio"; + uc_priv->gpio_count = dev_read_u32_default(udev, "adi,ngpios", 0); + + if (!uc_priv->gpio_count) { + dev_err(udev, "Missing adi,ngpios property!\n"); + return -ENOENT; + } + + priv->base = dev_read_addr_ptr(udev); + priv->ngpio = uc_priv->gpio_count; + + return 0; +} + +static const struct udevice_id adsp_gpio_match[] = { + { .compatible = "adi,adsp-gpio" }, + { }, +}; + +U_BOOT_DRIVER(adi_adsp_gpio) = { + .name = "adi_adsp_gpio", + .id = UCLASS_GPIO, + .ops = &adsp_gpio_ops, + .probe = adsp_gpio_probe, + .priv_auto = sizeof(struct adsp_gpio_priv), + .of_match = adsp_gpio_match, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/gpio/pca953x_gpio.c b/drivers/gpio/pca953x_gpio.c index e84038f312e..523ca8473a8 100644 --- a/drivers/gpio/pca953x_gpio.c +++ b/drivers/gpio/pca953x_gpio.c @@ -393,6 +393,8 @@ static const struct udevice_id pca953x_ids[] = { { .compatible = "nxp,pca9575", .data = OF_957X(16, PCA_INT), }, { .compatible = "nxp,pca9698", .data = OF_953X(40, 0), }, + { .compatible = "nxp,pcal6408", .data = OF_953X(8, PCA_LATCH_INT), }, + { .compatible = "nxp,pcal6416", .data = OF_953X(16, PCA_LATCH_INT), }, { .compatible = "nxp,pcal6524", .data = OF_953X(24, PCA_LATCH_INT), }, { .compatible = "maxim,max7310", .data = OF_953X(8, 0), }, diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index cdae6825736..46e76385961 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -154,6 +154,13 @@ config SPL_DM_I2C_GPIO bindings are supported. Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt +config SYS_I2C_ADI + bool "ADI I2C driver" + depends on DM_I2C && ARCH_SC5XX + help + Add support for the ADI (Analog Devices) I2C driver as used + in SC57X, SC58X, SC59X, SC59X_64. + config SYS_I2C_AT91 bool "Atmel I2C driver" depends on DM_I2C && ARCH_AT91 diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index bebd728e7da..2713289f7db 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_$(XPL_)I2C_CROS_EC_TUNNEL) += cros_ec_tunnel.o obj-$(CONFIG_$(XPL_)I2C_CROS_EC_LDO) += cros_ec_ldo.o obj-$(CONFIG_$(XPL_)SYS_I2C_LEGACY) += i2c_core.o +obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_SYS_I2C_ASPEED) += ast_i2c.o obj-$(CONFIG_SYS_I2C_AST2600) += ast2600_i2c.o obj-$(CONFIG_SYS_I2C_AT91) += at91_i2c.o diff --git a/drivers/i2c/adi_i2c.c b/drivers/i2c/adi_i2c.c new file mode 100644 index 00000000000..4cddcfa6b7f --- /dev/null +++ b/drivers/i2c/adi_i2c.c @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Converted to driver model by Nathan Barrett-Morrison + * + * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * Contact: Greg Malysa <greg.malysa@timesys.com> + */ + +#include <clk.h> +#include <dm.h> +#include <i2c.h> +#include <mapmem.h> +#include <linux/io.h> + +#define CLKLOW(x) ((x) & 0xFF) // Periods Clock Is Held Low +#define CLKHI(y) (((y) & 0xFF) << 0x8) // Periods Clock Is High + +#define PRESCALE 0x007F // SCLKs Per Internal Time Reference (10MHz) +#define TWI_ENA 0x0080 // TWI Enable +#define SCCB 0x0200 // SCCB Compatibility Enable + +#define SEN 0x0001 // Slave Enable +#define SADD_LEN 0x0002 // Slave Address Length +#define STDVAL 0x0004 // Slave Transmit Data Valid +#define TSC_NAK 0x0008 // NAK Generated At Conclusion Of Transfer +#define GEN 0x0010 // General Call Adrress Matching Enabled + +#define SDIR 0x0001 // Slave Transfer Direction +#define GCALL 0x0002 // General Call Indicator + +#define MEN 0x0001 // Master Mode Enable +#define MADD_LEN 0x0002 // Master Address Length +#define MDIR 0x0004 // Master Transmit Direction (RX/TX*) +#define FAST 0x0008 // Use Fast Mode Timing Specs +#define STOP 0x0010 // Issue Stop Condition +#define RSTART 0x0020 // Repeat Start or Stop* At End Of Transfer +#define DCNT 0x3FC0 // Data Bytes To Transfer +#define SDAOVR 0x4000 // Serial Data Override +#define SCLOVR 0x8000 // Serial Clock Override + +#define MPROG 0x0001 // Master Transfer In Progress +#define LOSTARB 0x0002 // Lost Arbitration Indicator (Xfer Aborted) +#define ANAK 0x0004 // Address Not Acknowledged +#define DNAK 0x0008 // Data Not Acknowledged +#define BUFRDERR 0x0010 // Buffer Read Error +#define BUFWRERR 0x0020 // Buffer Write Error +#define SDASEN 0x0040 // Serial Data Sense +#define SCLSEN 0x0080 // Serial Clock Sense +#define BUSBUSY 0x0100 // Bus Busy Indicator + +#define SINIT 0x0001 // Slave Transfer Initiated +#define SCOMP 0x0002 // Slave Transfer Complete +#define SERR 0x0004 // Slave Transfer Error +#define SOVF 0x0008 // Slave Overflow +#define MCOMP 0x0010 // Master Transfer Complete +#define MERR 0x0020 // Master Transfer Error +#define XMTSERV 0x0040 // Transmit FIFO Service +#define RCVSERV 0x0080 // Receive FIFO Service + +#define XMTFLUSH 0x0001 // Transmit Buffer Flush +#define RCVFLUSH 0x0002 // Receive Buffer Flush +#define XMTINTLEN 0x0004 // Transmit Buffer Interrupt Length +#define RCVINTLEN 0x0008 // Receive Buffer Interrupt Length + +#define XMTSTAT 0x0003 // Transmit FIFO Status +#define XMT_EMPTY 0x0000 // Transmit FIFO Empty +#define XMT_HALF 0x0001 // Transmit FIFO Has 1 Byte To Write +#define XMT_FULL 0x0003 // Transmit FIFO Full (2 Bytes To Write) + +#define RCVSTAT 0x000C // Receive FIFO Status +#define RCV_EMPTY 0x0000 // Receive FIFO Empty +#define RCV_HALF 0x0004 // Receive FIFO Has 1 Byte To Read +#define RCV_FULL 0x000C // Receive FIFO Full (2 Bytes To Read) + +/* Every register is 32bit aligned, but only 16bits in size */ +#define ureg(name) u16 name; u16 __pad_##name + +struct twi_regs { + ureg(clkdiv); + ureg(control); + ureg(slave_ctl); + ureg(slave_stat); + ureg(slave_addr); + ureg(master_ctl); + ureg(master_stat); + ureg(master_addr); + ureg(int_stat); + ureg(int_mask); + ureg(fifo_ctl); + ureg(fifo_stat); + u8 __pad[0x50]; + + ureg(xmt_data8); + ureg(xmt_data16); + ureg(rcv_data8); + ureg(rcv_data16); +}; + +#undef ureg + +/* + * The way speed is changed into duty often results in integer truncation + * with 50% duty, so we'll force rounding up to the next duty by adding 1 + * to the max. In practice this will get us a speed of something like + * 385 KHz. The other limit is easy to handle as it is only 8 bits. + */ +#define I2C_SPEED_MAX 400000 +#define I2C_SPEED_TO_DUTY(speed) (5000000 / (speed)) +#define I2C_DUTY_MAX (I2C_SPEED_TO_DUTY(I2C_SPEED_MAX) + 1) +#define I2C_DUTY_MIN 0xff /* 8 bit limited */ + +#define I2C_M_COMBO 0x4 +#define I2C_M_STOP 0x2 +#define I2C_M_READ 0x1 + +/* + * All transfers are described by this data structure + */ +struct adi_i2c_msg { + u8 flags; + u32 len; /* msg length */ + u8 *buf; /* pointer to msg data */ + u32 olen; /* addr length */ + u8 *obuf; /* addr buffer */ +}; + +struct adi_i2c_dev { + struct twi_regs __iomem *base; + u32 i2c_clk; + uint speed; +}; + +/* Allow msec timeout per ~byte transfer */ +#define I2C_TIMEOUT 10 + +/** + * wait_for_completion - manage the actual i2c transfer + * @msg: the i2c msg + */ +static int wait_for_completion(struct twi_regs *twi, struct adi_i2c_msg *msg) +{ + u16 int_stat; + ulong timebase = get_timer(0); + + do { + int_stat = ioread16(&twi->int_stat); + + if (int_stat & XMTSERV) { + iowrite16(XMTSERV, &twi->int_stat); + if (msg->olen) { + iowrite16(*(msg->obuf++), &twi->xmt_data8); + --msg->olen; + } else if (!(msg->flags & I2C_M_COMBO) && msg->len) { + iowrite16(*(msg->buf++), &twi->xmt_data8); + --msg->len; + } else { + if (msg->flags & I2C_M_COMBO) + setbits_16(&twi->master_ctl, RSTART | MDIR); + else + setbits_16(&twi->master_ctl, STOP); + } + } + if (int_stat & RCVSERV) { + iowrite16(RCVSERV, &twi->int_stat); + if (msg->len) { + *(msg->buf++) = ioread16(&twi->rcv_data8); + --msg->len; + } else if (msg->flags & I2C_M_STOP) { + setbits_16(&twi->master_ctl, STOP); + } + } + if (int_stat & MERR) { + pr_err("%s: master transmit terror: %d\n", __func__, + ioread16(&twi->master_stat)); + iowrite16(MERR, &twi->int_stat); + return -EIO; + } + if (int_stat & MCOMP) { + iowrite16(MCOMP, &twi->int_stat); + if (msg->flags & I2C_M_COMBO && msg->len) { + u16 mlen = min(msg->len, 0xffu) << 6; + clrsetbits_16(&twi->master_ctl, RSTART, mlen | MEN | MDIR); + } else { + break; + } + } + + /* If we were able to do something, reset timeout */ + if (int_stat) + timebase = get_timer(0); + + } while (get_timer(timebase) < I2C_TIMEOUT); + + return 0; +} + +static int i2c_transfer(struct twi_regs *twi, u8 chip, u8 *offset, + int olen, u8 *buffer, int len, u8 flags) +{ + int ret; + u16 ctl; + + struct adi_i2c_msg msg = { + .flags = flags | (len >= 0xff ? I2C_M_STOP : 0), + .buf = buffer, + .len = len, + .obuf = offset, + .olen = olen, + }; + + /* wait for things to settle */ + while (ioread16(&twi->master_stat) & BUSBUSY) + if (!IS_ENABLED(CONFIG_SPL_BUILD) && ctrlc()) + return -EINTR; + + /* Set Transmit device address */ + iowrite16(chip, &twi->master_addr); + + /* Clear the FIFO before starting things */ + iowrite16(XMTFLUSH | RCVFLUSH, &twi->fifo_ctl); + iowrite16(0, &twi->fifo_ctl); + + /* Prime the pump */ + if (msg.olen) { + len = (msg.flags & I2C_M_COMBO) ? msg.olen : msg.olen + len; + iowrite16(*(msg.obuf++), &twi->xmt_data8); + --msg.olen; + } else if (!(msg.flags & I2C_M_READ) && msg.len) { + iowrite16(*(msg.buf++), &twi->xmt_data8); + --msg.len; + } + + /* clear int stat */ + iowrite16(-1, &twi->master_stat); + iowrite16(-1, &twi->int_stat); + iowrite16(0, &twi->int_mask); + + /* Master enable */ + ctl = ioread16(&twi->master_ctl); + ctl = (ctl & FAST) | (min(len, 0xff) << 6) | MEN | + ((msg.flags & I2C_M_READ) ? MDIR : 0); + iowrite16(ctl, &twi->master_ctl); + + /* Process the rest */ + ret = wait_for_completion(twi, &msg); + + clrbits_16(&twi->master_ctl, MEN); + clrbits_16(&twi->control, TWI_ENA); + setbits_16(&twi->control, TWI_ENA); + return ret; +} + +static int adi_i2c_read(struct twi_regs *twi, u8 chip, + u8 *offset, int olen, u8 *buffer, int len) +{ + return i2c_transfer(twi, chip, offset, olen, buffer, + len, olen ? I2C_M_COMBO : I2C_M_READ); +} + +static int adi_i2c_write(struct twi_regs *twi, u8 chip, + u8 *offset, int olen, u8 *buffer, int len) +{ + return i2c_transfer(twi, chip, offset, olen, buffer, len, 0); +} + +static int adi_i2c_set_bus_speed(struct udevice *bus, uint speed) +{ + struct adi_i2c_dev *dev = dev_get_priv(bus); + struct twi_regs *twi = dev->base; + u16 clkdiv = I2C_SPEED_TO_DUTY(speed); + + /* Set TWI interface clock */ + if (clkdiv < I2C_DUTY_MAX || clkdiv > I2C_DUTY_MIN) + return -1; + clkdiv = (clkdiv << 8) | (clkdiv & 0xff); + iowrite16(clkdiv, &twi->clkdiv); + + /* Don't turn it on */ + iowrite16(speed > 100000 ? FAST : 0, &twi->master_ctl); + + return 0; +} + +static int adi_i2c_of_to_plat(struct udevice *bus) +{ + struct adi_i2c_dev *dev = dev_get_priv(bus); + struct clk clock; + u32 ret; + + dev->base = map_sysmem(dev_read_addr(bus), sizeof(struct twi_regs)); + + if (!dev->base) + return -ENOMEM; + + dev->speed = dev_read_u32_default(bus, "clock-frequency", + I2C_SPEED_FAST_RATE); + + ret = clk_get_by_name(bus, "i2c", &clock); + if (ret < 0) + printf("%s: Can't get I2C clk: %d\n", __func__, ret); + else + dev->i2c_clk = clk_get_rate(&clock); + + return 0; +} + +static int adi_i2c_probe_chip(struct udevice *bus, u32 chip_addr, + u32 chip_flags) +{ + struct adi_i2c_dev *dev = dev_get_priv(bus); + u8 byte; + + return adi_i2c_read(dev->base, chip_addr, NULL, 0, &byte, 1); +} + +static int adi_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) +{ + struct adi_i2c_dev *dev = dev_get_priv(bus); + struct i2c_msg *dmsg, *omsg, dummy; + + memset(&dummy, 0, sizeof(struct i2c_msg)); + + /* + * We expect either two messages (one with an offset and one with the + * actual data) or one message (just data) + */ + if (nmsgs > 2 || nmsgs == 0) { + debug("%s: Only one or two messages are supported.", __func__); + return -EINVAL; + } + + omsg = nmsgs == 1 ? &dummy : msg; + dmsg = nmsgs == 1 ? msg : msg + 1; + + if (dmsg->flags & I2C_M_RD) + return adi_i2c_read(dev->base, dmsg->addr, omsg->buf, omsg->len, + dmsg->buf, dmsg->len); + else + return adi_i2c_write(dev->base, dmsg->addr, omsg->buf, omsg->len, + dmsg->buf, dmsg->len); +} + +int adi_i2c_probe(struct udevice *bus) +{ + struct adi_i2c_dev *dev = dev_get_priv(bus); + struct twi_regs *twi = dev->base; + + u16 prescale = ((dev->i2c_clk / 1000 / 1000 + 5) / 10) & 0x7F; + + /* Set TWI internal clock as 10MHz */ + iowrite16(prescale, &twi->control); + + /* Set TWI interface clock as specified */ + adi_i2c_set_bus_speed(bus, dev->speed); + + /* Enable it */ + iowrite16(TWI_ENA | prescale, &twi->control); + + return 0; +} + +static const struct dm_i2c_ops adi_i2c_ops = { + .xfer = adi_i2c_xfer, + .probe_chip = adi_i2c_probe_chip, + .set_bus_speed = adi_i2c_set_bus_speed, +}; + +static const struct udevice_id adi_i2c_ids[] = { + { .compatible = "adi-i2c", }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(i2c_adi) = { + .name = "i2c_adi", + .id = UCLASS_I2C, + .of_match = adi_i2c_ids, + .probe = adi_i2c_probe, + .of_to_plat = adi_i2c_of_to_plat, + .priv_auto = sizeof(struct adi_i2c_dev), + .ops = &adi_i2c_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 67d5ac1a742..4d9f004ebad 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -21,6 +21,13 @@ config APPLE_MBOX such as the System Management Controller (SMC) and NVMe and this driver is required to get that functionality up and running. +config IMX_MU_MBOX + bool "Enable i.MX MU MBOX support" + depends on DM_MAILBOX + help + Enable support for i.MX Messaging Unit for communication with other + processors on the SoC using mailbox interface + config SANDBOX_MBOX bool "Enable the sandbox mailbox test driver" depends on DM_MAILBOX && SANDBOX diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 6072fa1956b..574add60005 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_$(XPL_)DM_MAILBOX) += mailbox-uclass.o obj-$(CONFIG_APPLE_MBOX) += apple-mbox.o +obj-$(CONFIG_IMX_MU_MBOX) += imx-mailbox.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox-test.o obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c new file mode 100644 index 00000000000..b1e0465e7a8 --- /dev/null +++ b/drivers/mailbox/imx-mailbox.c @@ -0,0 +1,443 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 NXP + */ + +#include <asm/io.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <mailbox-uclass.h> +#include <linux/bitfield.h> +#include <linux/bug.h> +#include <linux/iopoll.h> +#include <linux/compat.h> + +/* This driver only exposes the status bits to keep with the + * polling methodology of u-boot. + */ +DECLARE_GLOBAL_DATA_PTR; + +#define IMX_MU_CHANS 24 + +#define IMX_MU_V2_PAR_OFF 0x4 +#define IMX_MU_V2_TR_MASK GENMASK(7, 0) +#define IMX_MU_V2_RR_MASK GENMASK(15, 8) + +enum imx_mu_chan_type { + IMX_MU_TYPE_TX = 0, /* Tx */ + IMX_MU_TYPE_RX = 1, /* Rx */ + IMX_MU_TYPE_TXDB = 2, /* Tx doorbell */ + IMX_MU_TYPE_RXDB = 3, /* Rx doorbell */ + IMX_MU_TYPE_RST = 4, /* Reset */ + IMX_MU_TYPE_TXDB_V2 = 5, /* Tx doorbell with S/W ACK */ +}; + +enum imx_mu_xcr { + IMX_MU_CR, + IMX_MU_GIER, + IMX_MU_GCR, + IMX_MU_TCR, + IMX_MU_RCR, + IMX_MU_xCR_MAX, +}; + +enum imx_mu_xsr { + IMX_MU_SR, + IMX_MU_GSR, + IMX_MU_TSR, + IMX_MU_RSR, + IMX_MU_xSR_MAX, +}; + +struct imx_mu_con_priv { + unsigned int idx; + enum imx_mu_chan_type type; + struct mbox_chan *chan; +}; + +enum imx_mu_type { + IMX_MU_V1, + IMX_MU_V2 = BIT(1), + IMX_MU_V2_S4 = BIT(15), + IMX_MU_V2_IRQ = BIT(16), +}; + +struct imx_mu { + void __iomem *base; + const struct imx_mu_dcfg *dcfg; + u32 num_tr; + u32 num_rr; + /* use pointers to channel as a way to reserve channels */ + struct mbox_chan *channels[IMX_MU_CHANS]; + struct imx_mu_con_priv con_priv[IMX_MU_CHANS]; +}; + +struct imx_mu_dcfg { + int (*tx)(struct imx_mu *plat, struct imx_mu_con_priv *cp, const void *data); + int (*rx)(struct imx_mu *plat, struct imx_mu_con_priv *cp); + int (*rxdb)(struct imx_mu *plat, struct imx_mu_con_priv *cp); + int (*init)(struct imx_mu *plat); + int (*of_xlate)(struct mbox_chan *chan, struct ofnode_phandle_args *args); + enum imx_mu_type type; + u32 xTR; /* Transmit Register0 */ + u32 xRR; /* Receive Register0 */ + u32 xSR[IMX_MU_xSR_MAX]; /* Status Registers */ + u32 xCR[IMX_MU_xCR_MAX]; /* Control Registers */ +}; + +#define IMX_MU_xSR_GIPn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x)))) +#define IMX_MU_xSR_RFn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x)))) +#define IMX_MU_xSR_TEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x)))) + +/* General Purpose Interrupt Enable */ +#define IMX_MU_xCR_GIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x)))) +/* Receive Interrupt Enable */ +#define IMX_MU_xCR_RIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(24 + (3 - (x)))) +/* Transmit Interrupt Enable */ +#define IMX_MU_xCR_TIEn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(20 + (3 - (x)))) +/* General Purpose Interrupt Request */ +#define IMX_MU_xCR_GIRn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(16 + (3 - (x)))) +/* MU reset */ +#define IMX_MU_xCR_RST(type) (type & IMX_MU_V2 ? BIT(0) : BIT(5)) +#define IMX_MU_xSR_RST(type) (type & IMX_MU_V2 ? BIT(0) : BIT(7)) + +static void imx_mu_write(struct imx_mu *plat, u32 val, u32 offs) +{ + iowrite32(val, plat->base + offs); +} + +static u32 imx_mu_read(struct imx_mu *plat, u32 offs) +{ + return ioread32(plat->base + offs); +} + +static u32 imx_mu_xcr_rmw(struct imx_mu *plat, enum imx_mu_xcr type, u32 set, u32 clr) +{ + u32 val; + + val = imx_mu_read(plat, plat->dcfg->xCR[type]); + val &= ~clr; + val |= set; + imx_mu_write(plat, val, plat->dcfg->xCR[type]); + + return val; +} + +/* check that the channel is open or owned by caller */ +static int imx_mu_check_channel(struct mbox_chan *chan) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + + if (plat->channels[chan->id]) { + /* if reserved check that caller owns */ + if (plat->channels[chan->id] == chan) + return 1; /* caller owns the channel */ + + return -EACCES; + } + + return 0; /* channel empty */ +} + +static int imx_mu_chan_request(struct mbox_chan *chan) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + struct imx_mu_con_priv *cp; + enum imx_mu_chan_type type; + int idx; + + type = chan->id / 4; + idx = chan->id % 4; + + if (imx_mu_check_channel(chan) < 0) /* check if channel already in use */ + return -EPERM; + + plat->channels[chan->id] = chan; + chan->con_priv = kcalloc(1, sizeof(struct imx_mu_con_priv), 0); + if (!chan->con_priv) + return -ENOMEM; + cp = chan->con_priv; + cp->idx = idx; + cp->type = type; + cp->chan = chan; + + switch (type) { + case IMX_MU_TYPE_RX: + imx_mu_xcr_rmw(plat, IMX_MU_RCR, IMX_MU_xCR_RIEn(plat->dcfg->type, idx), 0); + break; + case IMX_MU_TYPE_TXDB_V2: + case IMX_MU_TYPE_TXDB: + case IMX_MU_TYPE_RXDB: + imx_mu_xcr_rmw(plat, IMX_MU_GIER, IMX_MU_xCR_GIEn(plat->dcfg->type, idx), 0); + break; + default: + break; + } + + return 0; +} + +static int imx_mu_chan_free(struct mbox_chan *chan) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + struct imx_mu_con_priv *cp = chan->con_priv; + + if (imx_mu_check_channel(chan) <= 0) /* check that the channel is also not empty */ + return -EINVAL; + + /* if you own channel and channel is NOT empty */ + plat->channels[chan->id] = NULL; + switch (cp->type) { + case IMX_MU_TYPE_TX: + imx_mu_xcr_rmw(plat, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(plat->dcfg->type, cp->idx)); + break; + case IMX_MU_TYPE_RX: + imx_mu_xcr_rmw(plat, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(plat->dcfg->type, cp->idx)); + break; + case IMX_MU_TYPE_TXDB_V2: + case IMX_MU_TYPE_TXDB: + case IMX_MU_TYPE_RXDB: + imx_mu_xcr_rmw(plat, IMX_MU_GIER, 0, IMX_MU_xCR_GIEn(plat->dcfg->type, cp->idx)); + break; + default: + break; + } + + kfree(cp); + + return 0; +} + +static int imx_mu_send(struct mbox_chan *chan, const void *data) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + struct imx_mu_con_priv *cp = chan->con_priv; + + if (imx_mu_check_channel(chan) < 1) /* return if channel isn't owned */ + return -EPERM; + + return plat->dcfg->tx(plat, cp, data); +} + +static int imx_mu_recv(struct mbox_chan *chan, void *data) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + struct imx_mu_con_priv *cp = chan->con_priv; + u32 ctrl, val; + + if (imx_mu_check_channel(chan) < 1) /* return if channel isn't owned */ + return -EPERM; + + switch (cp->type) { + case IMX_MU_TYPE_TXDB_V2: + case IMX_MU_TYPE_RXDB: + /* check if GSR[GIRn] bit is set */ + if (readx_poll_timeout(ioread32, plat->base + plat->dcfg->xSR[IMX_MU_GSR], + val, val & BIT(cp->idx), 1000000) < 0) + return -EBUSY; + + ctrl = imx_mu_read(plat, plat->dcfg->xCR[IMX_MU_GIER]); + val = imx_mu_read(plat, plat->dcfg->xSR[IMX_MU_GSR]); + val &= IMX_MU_xSR_GIPn(plat->dcfg->type, cp->idx) & + (ctrl & IMX_MU_xCR_GIEn(plat->dcfg->type, cp->idx)); + break; + default: + dev_warn(chan->dev, "Unhandled channel type %d\n", cp->type); + return -EOPNOTSUPP; + }; + + if (val == IMX_MU_xSR_GIPn(plat->dcfg->type, cp->idx)) + plat->dcfg->rxdb(plat, cp); + + return 0; +} + +static int imx_mu_of_to_plat(struct udevice *dev) +{ + struct imx_mu *plat = dev_get_plat(dev); + fdt_addr_t addr; + + addr = dev_read_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -ENODEV; + + plat->base = (struct mu_type *)addr; + + return 0; +} + +static int imx_mu_init_generic(struct imx_mu *plat) +{ + unsigned int i; + unsigned int val; + + if (plat->num_rr > 4 || plat->num_tr > 4) { + WARN_ONCE(true, "%s not support TR/RR larger than 4\n", __func__); + return -EOPNOTSUPP; + } + + /* Set default MU configuration */ + for (i = 0; i < IMX_MU_xCR_MAX; i++) + imx_mu_write(plat, 0, plat->dcfg->xCR[i]); + + /* Clear any pending GIP */ + val = imx_mu_read(plat, plat->dcfg->xSR[IMX_MU_GSR]); + imx_mu_write(plat, val, plat->dcfg->xSR[IMX_MU_GSR]); + + /* Clear any pending RSR */ + for (i = 0; i < plat->num_rr; i++) + imx_mu_read(plat, plat->dcfg->xRR + i * 4); + + return 0; +} + +static int imx_mu_generic_of_xlate(struct mbox_chan *chan, struct ofnode_phandle_args *args) +{ + enum imx_mu_chan_type type; + int idx, cid; + + if (args->args_count != 2) { + dev_err(chan->dev, "Invalid argument count %d\n", args->args_count); + return -EINVAL; + } + + type = args->args[0]; /* channel type */ + idx = args->args[1]; /* index */ + + cid = type * 4 + idx; + if (cid >= IMX_MU_CHANS) { + dev_err(chan->dev, "Not supported channel number: %d. (type: %d, idx: %d)\n", + cid, type, idx); + return -EINVAL; + } + + chan->id = cid; + + return 0; +} + +static int imx_mu_generic_tx(struct imx_mu *plat, struct imx_mu_con_priv *cp, + const void *data) +{ + switch (cp->type) { + case IMX_MU_TYPE_TXDB_V2: + imx_mu_xcr_rmw(plat, IMX_MU_GCR, IMX_MU_xCR_GIRn(plat->dcfg->type, cp->idx), 0); + break; + default: + dev_warn(cp->chan->dev, "Send data on wrong channel type: %d\n", cp->type); + return -EINVAL; + } + + return 0; +} + +static int imx_mu_generic_rxdb(struct imx_mu *plat, struct imx_mu_con_priv *cp) +{ + imx_mu_write(plat, IMX_MU_xSR_GIPn(plat->dcfg->type, cp->idx), + plat->dcfg->xSR[IMX_MU_GSR]); + + return 0; +} + +static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = { + .tx = imx_mu_generic_tx, + .rxdb = imx_mu_generic_rxdb, + .init = imx_mu_init_generic, + .of_xlate = imx_mu_generic_of_xlate, + .type = IMX_MU_V1, + .xTR = 0x0, + .xRR = 0x10, + .xSR = {0x20, 0x20, 0x20, 0x20}, + .xCR = {0x24, 0x24, 0x24, 0x24, 0x24}, +}; + +static const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = { + .tx = imx_mu_generic_tx, + .rxdb = imx_mu_generic_rxdb, + .init = imx_mu_init_generic, + .of_xlate = imx_mu_generic_of_xlate, + .type = IMX_MU_V1, + .xTR = 0x20, + .xRR = 0x40, + .xSR = {0x60, 0x60, 0x60, 0x60}, + .xCR = {0x64, 0x64, 0x64, 0x64, 0x64}, +}; + +static const struct imx_mu_dcfg imx_mu_cfg_imx95 = { + .tx = imx_mu_generic_tx, + .rxdb = imx_mu_generic_rxdb, + .init = imx_mu_init_generic, + .of_xlate = imx_mu_generic_of_xlate, + .type = IMX_MU_V2, + .xTR = 0x200, + .xRR = 0x280, + .xSR = {0xC, 0x118, 0x124, 0x12C}, + .xCR = {0x8, 0x110, 0x114, 0x120, 0x128}, +}; + +static const struct udevice_id ids[] = { + { .compatible = "fsl,imx6sx-mu", .data = (ulong)&imx_mu_cfg_imx6sx }, + { .compatible = "fsl,imx7ulp-mu", .data = (ulong)&imx_mu_cfg_imx7ulp }, + { .compatible = "fsl,imx95-mu", .data = (ulong)&imx_mu_cfg_imx95 }, + { } +}; + +int imx_mu_of_xlate(struct mbox_chan *chan, struct ofnode_phandle_args *args) +{ + struct imx_mu *plat = dev_get_plat(chan->dev); + + return plat->dcfg->of_xlate(chan, args); +} + +struct mbox_ops imx_mu_ops = { + .of_xlate = imx_mu_of_xlate, + .request = imx_mu_chan_request, + .rfree = imx_mu_chan_free, + .send = imx_mu_send, + .recv = imx_mu_recv, +}; + +static void imx_mu_get_tr_rr(struct imx_mu *plat) +{ + u32 val; + + if (plat->dcfg->type & IMX_MU_V2) { + val = imx_mu_read(plat, IMX_MU_V2_PAR_OFF); + plat->num_tr = FIELD_GET(IMX_MU_V2_TR_MASK, val); + plat->num_rr = FIELD_GET(IMX_MU_V2_RR_MASK, val); + } else { + plat->num_tr = 4; + plat->num_rr = 4; + } +} + +static int imx_mu_probe(struct udevice *dev) +{ + struct imx_mu *plat = dev_get_plat(dev); + int ret; + + debug("%s(dev=%p)\n", __func__, dev); + + plat->dcfg = (void *)dev_get_driver_data(dev); + + imx_mu_get_tr_rr(plat); + + ret = plat->dcfg->init(plat); + if (ret) { + dev_err(dev, "Failed to init MU\n"); + return ret; + } + + return 0; +} + +U_BOOT_DRIVER(imx_mu) = { + .name = "imx-mu", + .id = UCLASS_MAILBOX, + .of_match = ids, + .of_to_plat = imx_mu_of_to_plat, + .plat_auto = sizeof(struct imx_mu), + .probe = imx_mu_probe, + .ops = &imx_mu_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/mailbox/zynqmp-ipi.c b/drivers/mailbox/zynqmp-ipi.c index 713d93a200c..851aa737c03 100644 --- a/drivers/mailbox/zynqmp-ipi.c +++ b/drivers/mailbox/zynqmp-ipi.c @@ -270,5 +270,4 @@ U_BOOT_DRIVER(zynqmp_ipi) = { .id = UCLASS_NOP, .of_match = zynqmp_ipi_ids, .probe = zynqmp_ipi_probe, - .flags = DM_FLAG_PROBE_AFTER_BIND, }; diff --git a/drivers/memory/ti-gpmc.c b/drivers/memory/ti-gpmc.c index e979c431e33..29e02f12ae0 100644 --- a/drivers/memory/ti-gpmc.c +++ b/drivers/memory/ti-gpmc.c @@ -1242,4 +1242,5 @@ U_BOOT_DRIVER(ti_gpmc) = { .of_match = gpmc_dt_ids, .probe = gpmc_probe, .flags = DM_FLAG_ALLOC_PRIV_DMA, + .priv_auto = sizeof(struct ti_gpmc), }; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index da84b35e804..834e0285097 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -468,6 +468,13 @@ config STM32MP_FUSE for STM32MP architecture. This API is needed for CMD_FUSE. +config K3_FUSE + bool "Enable TI K3 fuse wrapper providing the fuse API" + depends on MISC && CMD_FUSE && CMD_FUSE_WRITEBUFF + help + If you say Y here, you will get support for the fuse API (OTP) + for TI K3 architecture. + config STM32_RCC bool "Enable RCC driver for the STM32 SoC's family" depends on (ARCH_STM32 || ARCH_STM32MP) && MISC diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index dac805e4cdd..0b81ba2604f 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_$(XPL_)I2C_EEPROM) += i2c_eeprom.o obj-$(CONFIG_IHS_FPGA) += ihs_fpga.o obj-$(CONFIG_IMX8) += imx8/ obj-$(CONFIG_IMX_ELE) += imx_ele/ +obj-$(CONFIG_K3_FUSE) += k3_fuse.o obj-$(CONFIG_LED_STATUS) += status_led.o obj-$(CONFIG_LED_STATUS_GPIO) += gpio_led.o obj-$(CONFIG_MPC83XX_SERDES) += mpc83xx_serdes.o diff --git a/drivers/misc/k3_fuse.c b/drivers/misc/k3_fuse.c new file mode 100644 index 00000000000..4a8ff1f2523 --- /dev/null +++ b/drivers/misc/k3_fuse.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2025 Texas Instruments Incorporated, <www.ti.com> + */ + +#include <errno.h> +#include <stdio.h> +#include <fuse.h> +#include <linux/arm-smccc.h> +#include <string.h> + +#define K3_SIP_OTP_WRITEBUFF 0xC2000000 +#define K3_SIP_OTP_WRITE 0xC2000001 +#define K3_SIP_OTP_READ 0xC2000002 + +int fuse_read(u32 bank, u32 word, u32 *val) +{ + struct arm_smccc_res res; + + if (bank != 0U) { + printf("Invalid bank argument, ONLY bank 0 is supported\n"); + return -EINVAL; + } + + /* Make SiP SMC call and send the word in the parameter register */ + arm_smccc_smc(K3_SIP_OTP_READ, word, + 0, 0, 0, 0, 0, 0, &res); + + *val = res.a1; + if (res.a0 != 0) + printf("SMC call failed: Error code %lu\n", res.a0); + + return res.a0; +} + +int fuse_sense(u32 bank, u32 word, u32 *val) +{ + return -EPERM; +} + +int fuse_prog(u32 bank, u32 word, u32 val) +{ + struct arm_smccc_res res; + u32 mask = val; + + if (bank != 0U) { + printf("Invalid bank argument, ONLY bank 0 is supported\n"); + return -EINVAL; + } + + /* Make SiP SMC call and send the word, val and mask in the parameter register */ + arm_smccc_smc(K3_SIP_OTP_WRITE, word, + val, mask, 0, 0, 0, 0, &res); + + if (res.a0 != 0) + printf("SMC call failed: Error code %lu\n", res.a0); + + return res.a0; +} + +int fuse_override(u32 bank, u32 word, u32 val) +{ + return -EPERM; +} + +int fuse_writebuff(ulong addr) +{ + struct arm_smccc_res res; + + /* Make SiP SMC call and send the addr in the parameter register */ + arm_smccc_smc(K3_SIP_OTP_WRITEBUFF, (unsigned long)addr, + 0, 0, 0, 0, 0, 0, &res); + + if (res.a0 != 0) + printf("SMC call failed: Error code %lu\n", res.a0); + + return res.a0; +} diff --git a/drivers/misc/qfw_acpi.c b/drivers/misc/qfw_acpi.c index 77cebae5e3f..c6c052ac6c3 100644 --- a/drivers/misc/qfw_acpi.c +++ b/drivers/misc/qfw_acpi.c @@ -25,17 +25,18 @@ DECLARE_GLOBAL_DATA_PTR; * * @entry : BIOS linker command entry which tells where to allocate memory * (either high memory or low memory) - * @addr : The address that should be used for low memory allcation. If the + * @addr : The address that should be used for low memory allocation. If the * memory allocation request is 'ZONE_HIGH' then this parameter will * be ignored. * @return: 0 on success, or negative value on failure */ -static int bios_linker_allocate(struct udevice *dev, +static int bios_linker_allocate(struct acpi_ctx *ctx, struct udevice *dev, struct bios_linker_entry *entry, ulong *addr) { uint32_t size, align; struct fw_file *file; unsigned long aligned_addr; + struct acpi_rsdp *rsdp; align = le32_to_cpu(entry->alloc.align); /* align must be power of 2 */ @@ -58,7 +59,9 @@ static int bios_linker_allocate(struct udevice *dev, * If allocation zone is ZONE_FSEG, then we use the 'addr' passed * in which is low memory */ - if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) { + if (IS_ENABLED(CONFIG_BLOBLIST_TABLES)) { + aligned_addr = ALIGN(*addr, align); + } else if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_HIGH) { aligned_addr = (unsigned long)memalign(align, size); if (!aligned_addr) { printf("error: allocating resource\n"); @@ -83,8 +86,13 @@ static int bios_linker_allocate(struct udevice *dev, (void *)aligned_addr); file->addr = aligned_addr; + rsdp = (void *)aligned_addr; + if (!strncmp(rsdp->signature, RSDP_SIG, sizeof(rsdp->signature))) + ctx->rsdp = rsdp; + /* adjust address for low memory allocation */ - if (entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) + if (IS_ENABLED(CONFIG_BLOBLIST_TABLES) || + entry->alloc.zone == BIOS_LINKER_LOADER_ALLOC_ZONE_FSEG) *addr = (aligned_addr + size); return 0; @@ -209,19 +217,23 @@ ulong write_acpi_tables(ulong addr) qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, table_loader); for (i = 0; i < (size / sizeof(*entry)); i++) { + log_content("entry %d: addr %lx\n", i, addr); entry = table_loader + i; switch (le32_to_cpu(entry->command)) { case BIOS_LINKER_LOADER_COMMAND_ALLOCATE: - ret = bios_linker_allocate(dev, entry, &addr); + log_content(" - %s\n", entry->alloc.file); + ret = bios_linker_allocate(ctx, dev, entry, &addr); if (ret) goto out; break; case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: + log_content(" - %s\n", entry->pointer.src_file); ret = bios_linker_add_pointer(dev, entry); if (ret) goto out; break; case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: + log_content(" - %s\n", entry->cksum.file); ret = bios_linker_add_checksum(dev, entry); if (ret) goto out; @@ -246,6 +258,16 @@ out: free(table_loader); + if (!ctx->rsdp) { + printf("error: no RSDP found\n"); + return addr; + } + struct acpi_rsdp *rsdp = ctx->rsdp; + + rsdp->length = sizeof(*rsdp); + rsdp->xsdt_address = 0; + rsdp->ext_checksum = table_compute_checksum((u8 *)rsdp, sizeof(*rsdp)); + gd_set_acpi_start(acpi_get_rsdp_addr()); return addr; diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 4827834b4aa..6740591a653 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -292,6 +292,15 @@ config MMC_DW_ROCKCHIP SD 3.0, SDIO 3.0 and MMC 4.5 and supports common eMMC chips as well as removeable SD and micro-SD cards. +config MMC_SDHCI_ADI + bool "ADI SD/MMC controller support" + depends on ARCH_SC5XX + depends on DM_MMC && OF_CONTROL + depends on MMC_SDHCI && MMC_SDHCI_ADMA + help + This enables support for the SD/MMC controller included in some Analog + Devices SC5XX Socs. + config MMC_DW_SOCFPGA bool "SOCFPGA specific extensions for Synopsys DW Memory Card Interface" depends on ARCH_SOCFPGA @@ -919,6 +928,12 @@ config ESDHC_DETECT_QUIRK bool "QIXIS-based eSDHC quirk detection" depends on FSL_ESDHC && FSL_QIXIS +config ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE + bool + depends on FSL_ESDHC || FSL_ESDHC_IMX + def_bool y if ARCH_T1024 || ARCH_T1040 || ARCH_T1042 || ARCH_T2080 \ + || FSL_ESDHC_IMX + config FSL_ESDHC_IMX bool "Freescale/NXP i.MX eSDHC controller support" help diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 90e76f90769..94ed28ead71 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_MMC_SDHCI_MV) += mv_sdhci.o obj-$(CONFIG_MMC_SDHCI_NPCM) += npcm_sdhci.o obj-$(CONFIG_MMC_SDHCI_PIC32) += pic32_sdhci.o obj-$(CONFIG_MMC_SDHCI_ROCKCHIP) += rockchip_sdhci.o +obj-$(CONFIG_MMC_SDHCI_ADI) += adi_sdhci.o obj-$(CONFIG_MMC_SDHCI_S5P) += s5p_sdhci.o obj-$(CONFIG_MMC_SDHCI_SNPS) += snps_sdhci.o obj-$(CONFIG_MMC_SDHCI_STI) += sti_sdhci.o diff --git a/drivers/mmc/adi_sdhci.c b/drivers/mmc/adi_sdhci.c new file mode 100644 index 00000000000..65a22cefb71 --- /dev/null +++ b/drivers/mmc/adi_sdhci.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * Contact: Greg Malysa <greg.malysa@timesys.com> + * + * Based on Rockchip's sdhci.c file + */ + +#include <clk.h> +#include <dm.h> +#include <malloc.h> +#include <sdhci.h> +#include <asm/cache.h> + +/* 400KHz is max freq for card ID etc. Use that as min */ +#define EMMC_MIN_FREQ 400000 + +/* Check if an operation crossed a boundary of size ADMA_BOUNDARY_ALIGN */ +#define ADMA_BOUNDARY_ALGN SZ_128M +#define BOUNDARY_OK(addr, len) \ + (((addr) | (ADMA_BOUNDARY_ALGN - 1)) == (((addr) + (len) - 1) | \ + (ADMA_BOUNDARY_ALGN - 1))) + +/* We split a descriptor for every crossing of the ADMA alignment boundary, + * so we need an additional descriptor for every expected crossing. + * As I understand it, the max expected transaction size is: + * CONFIG_SYS_MMC_MAX_BLK_COUNT * MMC_MAX_BLOCK_LEN + * + * With the way the SDHCI-ADMA driver is implemented, if ADMA_MAX_LEN was a + * clean power of two, we'd only ever need +1 descriptor as the first + * descriptor that got split would then bring the remaining DMA + * destination addresses into alignment. Unfortunately, it's currently + * hardcoded to a non-power-of-two value. + * + * If that ever becomes parameterized, ADMA max length can be set to + * 0x10000, and set this to 1. + */ +#define ADMA_POTENTIAL_CROSSINGS \ + DIV_ROUND_UP((CONFIG_SYS_MMC_MAX_BLK_COUNT * MMC_MAX_BLOCK_LEN), \ + ADMA_BOUNDARY_ALGN) +/* +1 descriptor for each crossing. + */ +#define ADMA_TABLE_EXTRA_SZ (ADMA_POTENTIAL_CROSSINGS * ADMA_DESC_LEN) + +struct adi_sdhc_plat { + struct mmc_config cfg; + struct mmc mmc; +}; + +void adi_dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc, + dma_addr_t addr, int len, bool end) +{ + int tmplen, offset; + + if (likely(!len || BOUNDARY_OK(addr, len))) { + sdhci_adma_write_desc(host, desc, addr, len, end); + return; + } + + offset = addr & (ADMA_BOUNDARY_ALGN - 1); + tmplen = ADMA_BOUNDARY_ALGN - offset; + sdhci_adma_write_desc(host, desc, addr, tmplen, false); + + addr += tmplen; + len -= tmplen; + sdhci_adma_write_desc(host, desc, addr, len, end); +} + +struct sdhci_ops adi_dwcmshc_sdhci_ops = { + .adma_write_desc = adi_dwcmshc_adma_write_desc, +}; + +static int adi_dwcmshc_sdhci_probe(struct udevice *dev) +{ + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct adi_sdhc_plat *plat = dev_get_plat(dev); + struct sdhci_host *host = dev_get_priv(dev); + int max_frequency, ret; + struct clk clk; + + max_frequency = dev_read_u32_default(dev, "max-frequency", 0); + ret = clk_get_by_index(dev, 0, &clk); + + host->quirks = 0; + host->max_clk = max_frequency; + /* + * The sdhci-driver only supports 4bit and 8bit, as sdhci_setup_cfg + * doesn't allow us to clear MMC_MODE_4BIT. Consequently, we don't + * check for other bus-width values. + */ + if (host->bus_width == 8) + host->host_caps |= MMC_MODE_8BIT; + + host->mmc = &plat->mmc; + host->mmc->priv = host; + host->mmc->dev = dev; + upriv->mmc = host->mmc; + + host->ops = &adi_dwcmshc_sdhci_ops; + host->adma_desc_table = memalign(ARCH_DMA_MINALIGN, + ADMA_TABLE_SZ + ADMA_TABLE_EXTRA_SZ); + host->adma_addr = virt_to_phys(host->adma_desc_table); + + ret = sdhci_setup_cfg(&plat->cfg, host, 0, EMMC_MIN_FREQ); + if (ret) + return ret; + + return sdhci_probe(dev); +} + +static int adi_dwcmshc_sdhci_of_to_plat(struct udevice *dev) +{ + struct sdhci_host *host = dev_get_priv(dev); + + host->name = dev->name; + host->ioaddr = dev_read_addr_ptr(dev); + host->bus_width = dev_read_u32_default(dev, "bus-width", 4); + + return 0; +} + +static int adi_sdhci_bind(struct udevice *dev) +{ + struct adi_sdhc_plat *plat = dev_get_plat(dev); + + return sdhci_bind(dev, &plat->mmc, &plat->cfg); +} + +static const struct udevice_id adi_dwcmshc_sdhci_ids[] = { + { .compatible = "adi,dwc-sdhci" }, + { } +}; + +U_BOOT_DRIVER(adi_dwcmshc_sdhci_drv) = { + .name = "adi_sdhci", + .id = UCLASS_MMC, + .of_match = adi_dwcmshc_sdhci_ids, + .of_to_plat = adi_dwcmshc_sdhci_of_to_plat, + .ops = &sdhci_ops, + .bind = adi_sdhci_bind, + .probe = adi_dwcmshc_sdhci_probe, + .priv_auto = sizeof(struct sdhci_host), + .plat_auto = sizeof(struct adi_sdhc_plat), +}; diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c index d7a45ef0ad0..926113f79d3 100644 --- a/drivers/mmc/fsl_esdhc_imx.c +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -40,12 +40,6 @@ #include <linux/iopoll.h> #include <linux/dma-mapping.h> -#ifndef ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE -#ifdef CONFIG_FSL_USDHC -#define ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE 1 -#endif -#endif - DECLARE_GLOBAL_DATA_PTR; #define SDHCI_IRQ_EN_BITS (IRQSTATEN_CC | IRQSTATEN_TC | \ @@ -376,7 +370,7 @@ static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc, (timeout == 4 || timeout == 8 || timeout == 12)) timeout++; - if (IS_ENABLED(ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE)) + if (IS_ENABLED(CONFIG_ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE)) timeout = 0xE; esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16); diff --git a/drivers/mmc/msm_sdhci.c b/drivers/mmc/msm_sdhci.c index 27bb7052fca..ac77fb06bf7 100644 --- a/drivers/mmc/msm_sdhci.c +++ b/drivers/mmc/msm_sdhci.c @@ -10,6 +10,7 @@ #include <clk.h> #include <dm.h> #include <malloc.h> +#include <reset.h> #include <sdhci.h> #include <wait_bit.h> #include <asm/global_data.h> @@ -153,9 +154,18 @@ static int msm_sdc_probe(struct udevice *dev) const struct msm_sdhc_variant_info *var_info; struct sdhci_host *host = &prv->host; u32 core_version, core_minor, core_major; + struct reset_ctl bcr_rst; u32 caps; int ret; + ret = reset_get_by_index(dev, 0, &bcr_rst); + if (!ret) { + reset_assert(&bcr_rst); + udelay(200); + reset_deassert(&bcr_rst); + udelay(200); + } + host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_BROKEN_R1B; host->max_clk = 0; diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index 0b56d1405be..951e6acd34d 100644 --- a/drivers/mmc/sunxi_mmc.c +++ b/drivers/mmc/sunxi_mmc.c @@ -92,6 +92,13 @@ static int mmc_set_mod_clk(struct sunxi_mmc_priv *priv, unsigned int hz) pll = CCM_MMC_CTRL_PLL6; pll_hz = clock_get_pll6(); #endif + /* + * On the D1/R528/T113 mux source 1 refers to PLL_PERIPH0(1x), + * like for the older SoCs. However we still have the hidden + * divider of 2x, so compensate for that here. + */ + if (IS_ENABLED(CONFIG_MACH_SUN8I_R528)) + pll_hz /= 2; } div = pll_hz / hz; @@ -442,6 +449,26 @@ out: return error; } +static void sunxi_mmc_reset(void *regs) +{ + /* Reset controller */ + writel(SUNXI_MMC_GCTRL_RESET, regs + SUNXI_MMC_GCTRL); + udelay(1000); + + if (IS_ENABLED(CONFIG_SUN50I_GEN_H6) || IS_ENABLED(CONFIG_SUNXI_GEN_NCAT2)) { + /* Reset card */ + writel(SUNXI_MMC_HWRST_ASSERT, regs + SUNXI_MMC_HWRST); + udelay(10); + writel(SUNXI_MMC_HWRST_DEASSERT, regs + SUNXI_MMC_HWRST); + udelay(300); + + /* Setup FIFO R/W threshold. Needed on H616. */ + writel(SUNXI_MMC_THLDC_READ_THLD(512) | + SUNXI_MMC_THLDC_WRITE_EN | + SUNXI_MMC_THLDC_READ_EN, regs + SUNXI_MMC_THLDC); + } +} + /* non-DM code here is used by the (ARM) SPL only */ #if !CONFIG_IS_ENABLED(DM_MMC) @@ -489,9 +516,7 @@ static int sunxi_mmc_core_init(struct mmc *mmc) { struct sunxi_mmc_priv *priv = mmc->priv; - /* Reset controller */ - writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl); - udelay(1000); + sunxi_mmc_reset(priv->reg); return 0; } @@ -684,9 +709,7 @@ static int sunxi_mmc_probe(struct udevice *dev) upriv->mmc = &plat->mmc; - /* Reset controller */ - writel(SUNXI_MMC_GCTRL_RESET, &priv->reg->gctrl); - udelay(1000); + sunxi_mmc_reset(priv->reg); return 0; } diff --git a/drivers/mmc/sunxi_mmc.h b/drivers/mmc/sunxi_mmc.h index f4ae5a790c8..71865160319 100644 --- a/drivers/mmc/sunxi_mmc.h +++ b/drivers/mmc/sunxi_mmc.h @@ -37,7 +37,9 @@ struct sunxi_mmc { u32 res0; /* 0x54 reserved */ u32 a12a; /* 0x58 Auto command 12 argument */ u32 ntsr; /* 0x5c New timing set register */ - u32 res1[8]; + u32 res1[6]; + u32 hwrst; /* 0x78 Hardware Reset */ + u32 res5; u32 dmac; /* 0x80 internal DMA control */ u32 dlba; /* 0x84 internal DMA descr list base address */ u32 idst; /* 0x88 internal DMA status */ @@ -46,7 +48,8 @@ struct sunxi_mmc { u32 cbda; /* 0x94 */ u32 res2[26]; #if defined(CONFIG_SUNXI_GEN_SUN6I) || defined(CONFIG_SUN50I_GEN_H6) || defined(CONFIG_SUNXI_GEN_NCAT2) - u32 res3[17]; + u32 thldc; /* 0x100 Threshold control */ + u32 res3[16]; u32 samp_dl; u32 res4[46]; #endif @@ -57,6 +60,7 @@ struct sunxi_mmc { #define SUNXI_MMC_CLK_ENABLE (0x1 << 16) #define SUNXI_MMC_CLK_DIVIDER_MASK (0xff) +#define SUNXI_MMC_GCTRL 0x000 #define SUNXI_MMC_GCTRL_SOFT_RESET (0x1 << 0) #define SUNXI_MMC_GCTRL_FIFO_RESET (0x1 << 1) #define SUNXI_MMC_GCTRL_DMA_RESET (0x1 << 2) @@ -123,6 +127,10 @@ struct sunxi_mmc { #define SUNXI_MMC_NTSR_MODE_SEL_NEW (0x1 << 31) +#define SUNXI_MMC_HWRST 0x078 +#define SUNXI_MMC_HWRST_ASSERT (0x0 << 0) +#define SUNXI_MMC_HWRST_DEASSERT (0x1 << 0) + #define SUNXI_MMC_IDMAC_RESET (0x1 << 0) #define SUNXI_MMC_IDMAC_FIXBURST (0x1 << 1) #define SUNXI_MMC_IDMAC_ENABLE (0x1 << 7) @@ -133,6 +141,12 @@ struct sunxi_mmc { #define SUNXI_MMC_COMMON_CLK_GATE (1 << 16) #define SUNXI_MMC_COMMON_RESET (1 << 18) +#define SUNXI_MMC_THLDC 0x100 +#define SUNXI_MMC_THLDC_READ_EN (0x1 << 0) +#define SUNXI_MMC_THLDC_BSY_CLR_INT_EN (0x1 << 1) +#define SUNXI_MMC_THLDC_WRITE_EN (0x1 << 2) +#define SUNXI_MMC_THLDC_READ_THLD(x) (((x) & 0xfff) << 16) + #define SUNXI_MMC_CAL_DL_SW_EN (0x1 << 7) #endif /* _SUNXI_MMC_H */ diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 88094b81e7a..3f8edeb5093 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -208,7 +208,7 @@ int mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts, { struct mtd_partition partition = {}, *parts; const char *mtdparts = *_mtdparts; - uint64_t cur_off = 0, cur_sz = 0; + uint64_t cur_off = 0; int nparts = 0; int ret, idx; u64 sz; @@ -237,8 +237,7 @@ int mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts, return ret; if (parts[idx].size == MTD_SIZE_REMAINING) - parts[idx].size = parent->size - cur_sz; - cur_sz += parts[idx].size; + parts[idx].size = parent->size - parts[idx].offset; sz = parts[idx].size; if (sz < parent->writesize || do_div(sz, parent->writesize)) { diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 609bdffbf77..adb271dfb8f 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -190,6 +190,21 @@ config SPL_NAND_LOAD def_bool y depends on NAND_DAVINCI && ARCH_DAVINCI && SPL_NAND_SUPPORT +config NAND_CADENCE + bool "Support Cadence NAND controller as a DT device" + depends on OF_CONTROL && DM_MTD + select SYS_NAND_SELF_INIT + select SPL_SYS_NAND_SELF_INIT + select SPL_NAND_BASE + select SPL_NAND_DRIVERS + select SPL_NAND_IDENT + select SPL_NAND_INIT + select SPL_NAND_ECC + imply CMD_NAND + help + Enable the driver for NAND flash on platforms using a Cadence NAND + controller as a DT device. + config NAND_DENALI bool select SYS_NAND_SELF_INIT @@ -673,7 +688,7 @@ config SYS_NAND_PAGE_SIZE SPL_NAND_SIMPLE || (NAND_MXC && SPL_NAND_SUPPORT) || \ MVEBU_SPL_BOOT_DEVICE_NAND || \ (NAND_ATMEL && SPL_NAND_SUPPORT) || \ - SPL_GENERATE_ATMEL_PMECC_HEADER || NAND_SANDBOX + SPL_GENERATE_ATMEL_PMECC_HEADER || NAND_SANDBOX || NAND_CADENCE depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC && !NAND_MT7621 help Number of data bytes in one page for the NAND chip on the @@ -775,6 +790,13 @@ config SPL_NAND_AM33XX_BCH so those platforms should use CONFIG_SPL_NAND_SIMPLE for enabling SPL-NAND driver with software ECC correction support. +config SPL_NAND_CADENCE + bool "Support Cadence NAND controller for SPL" + depends on SPL_NAND_SUPPORT + help + This is a small implementation of the Cadence NAND controller + for use on SPL. + config SPL_NAND_DENALI bool "Support Denali NAND controller for SPL" depends on SPL_NAND_SUPPORT diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index b47a3d787ce..34cba77046a 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -10,6 +10,7 @@ NORMAL_DRIVERS=y endif obj-$(CONFIG_SPL_NAND_AM33XX_BCH) += am335x_spl_bch.o +obj-$(CONFIG_SPL_NAND_CADENCE) += cadence_spl.o obj-$(CONFIG_SPL_NAND_DENALI) += denali_spl.o obj-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o obj-$(CONFIG_SPL_NAND_LOAD) += nand_spl_load.o @@ -51,6 +52,7 @@ obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o obj-$(CONFIG_DM_NAND_ATMEL) += atmel/ obj-$(CONFIG_NAND_ARASAN) += arasan_nfc.o obj-$(CONFIG_NAND_BRCMNAND) += brcmnand/ +obj-$(CONFIG_NAND_CADENCE) += cadence_nand.o obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o obj-$(CONFIG_NAND_DENALI) += denali.o obj-$(CONFIG_NAND_DENALI_DT) += denali_dt.o diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index 56fbd64ef68..c90a4eab8df 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -1127,7 +1127,7 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand, const struct nand_data_interface *conf, struct atmel_smc_cs_conf *smcconf) { - u32 ncycles, totalcycles, timeps, mckperiodps; + u32 ncycles, totalcycles, timeps, mckperiodps, pulse; struct atmel_nand_controller *nc; int ret; @@ -1253,11 +1253,16 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand, ATMEL_SMC_MODE_TDFMODE_OPTIMIZED; /* - * Read pulse timing directly matches tRP: + * Read pulse timing would directly match tRP, + * but some NAND flash chips (S34ML01G2 and W29N02KVxxAF) + * do not work properly in timing mode 3. + * The workaround is to extend the SMC NRD pulse to meet tREA + * timing. * - * NRD_PULSE = tRP + * NRD_PULSE = max(tRP, tREA) */ - ncycles = DIV_ROUND_UP(conf->timings.sdr.tRP_min, mckperiodps); + pulse = max(conf->timings.sdr.tRP_min, conf->timings.sdr.tREA_max); + ncycles = DIV_ROUND_UP(pulse, mckperiodps); totalcycles += ncycles; ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NRD_SHIFT, ncycles); diff --git a/drivers/mtd/nand/raw/cadence_nand.c b/drivers/mtd/nand/raw/cadence_nand.c new file mode 100644 index 00000000000..27aa7f97a45 --- /dev/null +++ b/drivers/mtd/nand/raw/cadence_nand.c @@ -0,0 +1,2423 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Cadence NAND flash controller driver + * + * Copyright (C) 2019 Cadence + * + * Author: Piotr Sroka <piotrs@cadence.com> + * + */ + +#include <cadence-nand.h> +#include <clk.h> +#include <dm.h> +#include <hang.h> +#include <malloc.h> +#include <memalign.h> +#include <nand.h> +#include <reset.h> +#include <wait_bit.h> +#include <dm/device_compat.h> +#include <dm/devres.h> +#include <linux/bitfield.h> +#include <linux/bug.h> +#include <linux/delay.h> +#include <linux/dma-direction.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/ioport.h> +#include <linux/printk.h> +#include <linux/sizes.h> + +static inline struct +cdns_nand_chip *to_cdns_nand_chip(struct nand_chip *chip) +{ + return container_of(chip, struct cdns_nand_chip, chip); +} + +static inline struct +cadence_nand_info *to_cadence_nand_info(struct nand_hw_control *controller) +{ + return container_of(controller, struct cadence_nand_info, controller); +} + +static bool +cadence_nand_dma_buf_ok(struct cadence_nand_info *cadence, const void *buf, + u32 buf_len) +{ + u8 data_dma_width = cadence->caps2.data_dma_width; + + return buf && + likely(IS_ALIGNED((uintptr_t)buf, data_dma_width)) && + likely(IS_ALIGNED(buf_len, DMA_DATA_SIZE_ALIGN)); +} + +static int cadence_nand_wait_for_value(struct cadence_nand_info *cadence, + u32 reg_offset, u32 timeout_us, + u32 mask, bool is_clear) +{ + u32 val; + int ret; + + ret = readl_poll_sleep_timeout(cadence->reg + reg_offset, + val, !(val & mask) == is_clear, + 10, timeout_us); + + if (ret < 0) { + dev_err(cadence->dev, + "Timeout while waiting for reg %x with mask %x is clear %d\n", + reg_offset, mask, is_clear); + } + + return ret; +} + +static int cadence_nand_set_ecc_enable(struct cadence_nand_info *cadence, + bool enable) +{ + u32 reg; + + if (cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + reg = readl_relaxed(cadence->reg + ECC_CONFIG_0); + + if (enable) + reg |= ECC_CONFIG_0_ECC_EN; + else + reg &= ~ECC_CONFIG_0_ECC_EN; + + writel_relaxed(reg, cadence->reg + ECC_CONFIG_0); + + return 0; +} + +static void cadence_nand_set_ecc_strength(struct cadence_nand_info *cadence, + u8 corr_str_idx) +{ + u32 reg; + + if (cadence->curr_corr_str_idx == corr_str_idx) + return; + + reg = readl_relaxed(cadence->reg + ECC_CONFIG_0); + reg &= ~ECC_CONFIG_0_CORR_STR; + reg |= FIELD_PREP(ECC_CONFIG_0_CORR_STR, corr_str_idx); + writel_relaxed(reg, cadence->reg + ECC_CONFIG_0); + + cadence->curr_corr_str_idx = corr_str_idx; +} + +static int cadence_nand_get_ecc_strength_idx(struct cadence_nand_info *cadence, + u8 strength) +{ + int i, corr_str_idx = -1; + + for (i = 0; i < BCH_MAX_NUM_CORR_CAPS; i++) { + if (cadence->ecc_strengths[i] == strength) { + corr_str_idx = i; + break; + } + } + + return corr_str_idx; +} + +static int cadence_nand_set_skip_marker_val(struct cadence_nand_info *cadence, + u16 marker_value) +{ + u32 reg; + + if (cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + reg = readl_relaxed(cadence->reg + SKIP_BYTES_CONF); + reg &= ~SKIP_BYTES_MARKER_VALUE; + reg |= FIELD_PREP(SKIP_BYTES_MARKER_VALUE, + marker_value); + + writel_relaxed(reg, cadence->reg + SKIP_BYTES_CONF); + + return 0; +} + +static int cadence_nand_set_skip_bytes_conf(struct cadence_nand_info *cadence, + u8 num_of_bytes, + u32 offset_value, + int enable) +{ + u32 reg, skip_bytes_offset; + + if (cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + if (!enable) { + num_of_bytes = 0; + offset_value = 0; + } + + reg = readl_relaxed(cadence->reg + SKIP_BYTES_CONF); + reg &= ~SKIP_BYTES_NUM_OF_BYTES; + reg |= FIELD_PREP(SKIP_BYTES_NUM_OF_BYTES, + num_of_bytes); + skip_bytes_offset = FIELD_PREP(SKIP_BYTES_OFFSET_VALUE, + offset_value); + + writel_relaxed(reg, cadence->reg + SKIP_BYTES_CONF); + writel_relaxed(skip_bytes_offset, cadence->reg + SKIP_BYTES_OFFSET); + + return 0; +} + +/* Functions enables/disables hardware detection of erased data */ +static void cadence_nand_set_erase_detection(struct cadence_nand_info *cadence, + bool enable, + u8 bitflips_threshold) +{ + u32 reg; + + reg = readl_relaxed(cadence->reg + ECC_CONFIG_0); + + if (enable) + reg |= ECC_CONFIG_0_ERASE_DET_EN; + else + reg &= ~ECC_CONFIG_0_ERASE_DET_EN; + + writel_relaxed(reg, cadence->reg + ECC_CONFIG_0); + writel_relaxed(bitflips_threshold, cadence->reg + ECC_CONFIG_1); +} + +static int cadence_nand_set_access_width16(struct cadence_nand_info *cadence, + bool bit_bus16) +{ + u32 reg; + + if (cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + reg = readl_relaxed(cadence->reg + COMMON_SET); + if (!bit_bus16) + reg &= ~COMMON_SET_DEVICE_16BIT; + else + reg |= COMMON_SET_DEVICE_16BIT; + writel_relaxed(reg, cadence->reg + COMMON_SET); + + return 0; +} + +static void +cadence_nand_clear_interrupt(struct cadence_nand_info *cadence, + struct cadence_nand_irq_status *irq_status) +{ + writel_relaxed(irq_status->status, cadence->reg + INTR_STATUS); + writel_relaxed(irq_status->trd_status, + cadence->reg + TRD_COMP_INT_STATUS); + writel_relaxed(irq_status->trd_error, + cadence->reg + TRD_ERR_INT_STATUS); +} + +static void +cadence_nand_read_int_status(struct cadence_nand_info *cadence, + struct cadence_nand_irq_status *irq_status) +{ + irq_status->status = readl_relaxed(cadence->reg + INTR_STATUS); + irq_status->trd_status = readl_relaxed(cadence->reg + + TRD_COMP_INT_STATUS); + irq_status->trd_error = readl_relaxed(cadence->reg + + TRD_ERR_INT_STATUS); +} + +static u32 irq_detected(struct cadence_nand_info *cadence, + struct cadence_nand_irq_status *irq_status) +{ + cadence_nand_read_int_status(cadence, irq_status); + + return irq_status->status || irq_status->trd_status || + irq_status->trd_error; +} + +static void cadence_nand_reset_irq(struct cadence_nand_info *cadence) +{ + memset(&cadence->irq_status, 0, sizeof(cadence->irq_status)); + memset(&cadence->irq_mask, 0, sizeof(cadence->irq_mask)); +} + +/* + * This is the interrupt service routine. It handles all interrupts + * sent to this device. + */ +static irqreturn_t cadence_nand_isr(struct cadence_nand_info *cadence) +{ + struct cadence_nand_irq_status irq_status; + irqreturn_t result = IRQ_NONE; + + if (irq_detected(cadence, &irq_status)) { + /* Handle interrupt. */ + /* First acknowledge it. */ + cadence_nand_clear_interrupt(cadence, &irq_status); + /* Status in the device context for someone to read. */ + cadence->irq_status.status |= irq_status.status; + cadence->irq_status.trd_status |= irq_status.trd_status; + cadence->irq_status.trd_error |= irq_status.trd_error; + /* Tell the OS that we've handled this. */ + result = IRQ_HANDLED; + } + return result; +} + +static void cadence_nand_set_irq_mask(struct cadence_nand_info *cadence, + struct cadence_nand_irq_status *irq_mask) +{ + writel_relaxed(INTR_ENABLE_INTR_EN | irq_mask->status, + cadence->reg + INTR_ENABLE); + + writel_relaxed(irq_mask->trd_error, + cadence->reg + TRD_ERR_INT_STATUS_EN); +} + +static void +cadence_nand_wait_for_irq(struct cadence_nand_info *cadence, + struct cadence_nand_irq_status *irq_mask, + struct cadence_nand_irq_status *irq_status) +{ + irqreturn_t result = IRQ_NONE; + u32 start = get_timer(0); + + while (get_timer(start) < TIMEOUT_US) { + result = cadence_nand_isr(cadence); + + if (result == IRQ_HANDLED) { + *irq_status = cadence->irq_status; + break; + } + udelay(1); + } + + if (!result) { + /* Timeout error. */ + dev_err(cadence->dev, "timeout occurred:\n"); + dev_err(cadence->dev, "\tstatus = 0x%x, mask = 0x%x\n", + irq_status->status, irq_mask->status); + dev_err(cadence->dev, + "\ttrd_status = 0x%x, trd_status mask = 0x%x\n", + irq_status->trd_status, irq_mask->trd_status); + dev_err(cadence->dev, + "\t trd_error = 0x%x, trd_error mask = 0x%x\n", + irq_status->trd_error, irq_mask->trd_error); + } +} + +/* Execute generic command on NAND controller. */ +static int cadence_nand_generic_cmd_send(struct cadence_nand_info *cadence, + u8 chip_nr, + u64 mini_ctrl_cmd) +{ + u32 mini_ctrl_cmd_l, mini_ctrl_cmd_h, reg; + + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_CS, chip_nr); + mini_ctrl_cmd_l = mini_ctrl_cmd & 0xFFFFFFFF; + mini_ctrl_cmd_h = mini_ctrl_cmd >> 32; + + if (cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + cadence_nand_reset_irq(cadence); + + writel_relaxed(mini_ctrl_cmd_l, cadence->reg + CMD_REG2); + writel_relaxed(mini_ctrl_cmd_h, cadence->reg + CMD_REG3); + + /* Select generic command. */ + reg = FIELD_PREP(CMD_REG0_CT, CMD_REG0_CT_GEN); + /* Thread number. */ + reg |= FIELD_PREP(CMD_REG0_TN, 0); + + /* Issue command. */ + writel_relaxed(reg, cadence->reg + CMD_REG0); + cadence->buf_index = 0; + + return 0; +} + +/* Wait for data on slave DMA interface. */ +static int cadence_nand_wait_on_sdma(struct cadence_nand_info *cadence, u8 *out_sdma_trd, + u32 *out_sdma_size) +{ + struct cadence_nand_irq_status irq_mask, irq_status; + + irq_mask.trd_status = 0; + irq_mask.trd_error = 0; + irq_mask.status = INTR_STATUS_SDMA_TRIGG + | INTR_STATUS_SDMA_ERR + | INTR_STATUS_UNSUPP_CMD; + + cadence_nand_set_irq_mask(cadence, &irq_mask); + cadence_nand_wait_for_irq(cadence, &irq_mask, &irq_status); + if (irq_status.status == 0) { + dev_err(cadence->dev, "Timeout while waiting for SDMA\n"); + return -ETIMEDOUT; + } + + if (irq_status.status & INTR_STATUS_SDMA_TRIGG) { + *out_sdma_size = readl_relaxed(cadence->reg + SDMA_SIZE); + *out_sdma_trd = readl_relaxed(cadence->reg + SDMA_TRD_NUM); + *out_sdma_trd = + FIELD_GET(SDMA_TRD_NUM_SDMA_TRD, *out_sdma_trd); + } else { + dev_err(cadence->dev, "SDMA error - irq_status %x\n", + irq_status.status); + return -EIO; + } + + return 0; +} + +static void cadence_nand_get_caps(struct cadence_nand_info *cadence) +{ + u32 reg; + + reg = readl_relaxed(cadence->reg + CTRL_FEATURES); + + cadence->caps2.max_banks = 1 << FIELD_GET(CTRL_FEATURES_N_BANKS, reg); + + if (FIELD_GET(CTRL_FEATURES_DMA_DWITH64, reg)) + cadence->caps2.data_dma_width = 8; + else + cadence->caps2.data_dma_width = 4; + + if (reg & CTRL_FEATURES_CONTROL_DATA) + cadence->caps2.data_control_supp = true; + + if (reg & (CTRL_FEATURES_NVDDR_2_3 + | CTRL_FEATURES_NVDDR)) + cadence->caps2.is_phy_type_dll = true; +} + +/* Prepare CDMA descriptor. */ +static void +cadence_nand_cdma_desc_prepare(struct cadence_nand_info *cadence, + char nf_mem, u32 flash_ptr, dma_addr_t mem_ptr, + dma_addr_t ctrl_data_ptr, u16 ctype) +{ + struct cadence_nand_cdma_desc *cdma_desc = cadence->cdma_desc; + + memset(cdma_desc, 0, sizeof(struct cadence_nand_cdma_desc)); + + /* Set fields for one descriptor. */ + cdma_desc->flash_pointer = flash_ptr; + if (cadence->ctrl_rev >= 13) + cdma_desc->bank = nf_mem; + else + cdma_desc->flash_pointer |= (nf_mem << CDMA_CFPTR_MEM_SHIFT); + + cdma_desc->command_flags |= CDMA_CF_DMA_MASTER; + cdma_desc->command_flags |= CDMA_CF_INT; + + cdma_desc->memory_pointer = mem_ptr; + cdma_desc->status = 0; + cdma_desc->sync_flag_pointer = 0; + cdma_desc->sync_arguments = 0; + + cdma_desc->command_type = ctype; + cdma_desc->ctrl_data_ptr = ctrl_data_ptr; + + flush_cache((dma_addr_t)cadence->cdma_desc, + ROUND(sizeof(struct cadence_nand_cdma_desc), + ARCH_DMA_MINALIGN)); +} + +static u8 cadence_nand_check_desc_error(struct cadence_nand_info *cadence, + u32 desc_status) +{ + if (desc_status & CDMA_CS_ERP) + return STAT_ERASED; + + if (desc_status & CDMA_CS_UNCE) + return STAT_ECC_UNCORR; + + if (desc_status & CDMA_CS_ERR) { + dev_err(cadence->dev, ":CDMA desc error flag detected.\n"); + return STAT_FAIL; + } + + if (FIELD_GET(CDMA_CS_MAXERR, desc_status)) + return STAT_ECC_CORR; + + return STAT_FAIL; +} + +static int cadence_nand_cdma_finish(struct cadence_nand_info *cadence) +{ + struct cadence_nand_cdma_desc *desc_ptr = cadence->cdma_desc; + u8 status = STAT_BUSY; + + invalidate_dcache_range((dma_addr_t)cadence->cdma_desc, + (dma_addr_t)cadence->cdma_desc + + ROUND(sizeof(struct cadence_nand_cdma_desc), + ARCH_DMA_MINALIGN)); + + if (desc_ptr->status & CDMA_CS_FAIL) { + status = cadence_nand_check_desc_error(cadence, + desc_ptr->status); + dev_err(cadence->dev, ":CDMA error %x\n", desc_ptr->status); + } else if (desc_ptr->status & CDMA_CS_COMP) { + /* Descriptor finished with no errors. */ + if (desc_ptr->command_flags & CDMA_CF_CONT) { + dev_info(cadence->dev, "DMA unsupported flag is set"); + status = STAT_UNKNOWN; + } else { + /* Last descriptor. */ + status = STAT_OK; + } + } + + return status; +} + +static int cadence_nand_cdma_send(struct cadence_nand_info *cadence, + u8 thread) +{ + u32 reg; + int status; + + /* Wait for thread ready. */ + status = cadence_nand_wait_for_value(cadence, TRD_STATUS, + TIMEOUT_US, + BIT(thread), true); + if (status) + return status; + + cadence_nand_reset_irq(cadence); + + writel_relaxed((u32)cadence->dma_cdma_desc, + cadence->reg + CMD_REG2); + writel_relaxed(0, cadence->reg + CMD_REG3); + + /* Select CDMA mode. */ + reg = FIELD_PREP(CMD_REG0_CT, CMD_REG0_CT_CDMA); + /* Thread number. */ + reg |= FIELD_PREP(CMD_REG0_TN, thread); + /* Issue command. */ + writel_relaxed(reg, cadence->reg + CMD_REG0); + + return 0; +} + +/* Send SDMA command and wait for finish. */ +static u32 +cadence_nand_cdma_send_and_wait(struct cadence_nand_info *cadence, + u8 thread) +{ + struct cadence_nand_irq_status irq_mask, irq_status = {0}; + int status; + u32 val; + + irq_mask.trd_status = BIT(thread); + irq_mask.trd_error = BIT(thread); + irq_mask.status = INTR_STATUS_CDMA_TERR; + + cadence_nand_set_irq_mask(cadence, &irq_mask); + + status = cadence_nand_cdma_send(cadence, thread); + if (status) + return status; + + /* Make sure the descriptor processing is complete */ + status = readl_poll_timeout(cadence->reg + TRD_COMP_INT_STATUS, val, + (val & BIT(thread)), TIMEOUT_US); + if (status) { + pr_err("cmd thread completion timeout!\n"); + return status; + } + + cadence_nand_wait_for_irq(cadence, &irq_mask, &irq_status); + + if (irq_status.status == 0 && irq_status.trd_status == 0 && + irq_status.trd_error == 0) { + dev_err(cadence->dev, "CDMA command timeout\n"); + return -ETIMEDOUT; + } + if (irq_status.status & irq_mask.status) { + dev_err(cadence->dev, "CDMA command failed\n"); + return -EIO; + } + + return 0; +} + +/* + * ECC size depends on configured ECC strength and on maximum supported + * ECC step size. + */ +static int cadence_nand_calc_ecc_bytes(int max_step_size, int strength) +{ + int nbytes = DIV_ROUND_UP(fls(8 * max_step_size) * strength, 8); + + return ALIGN(nbytes, 2); +} + +#define CADENCE_NAND_CALC_ECC_BYTES(max_step_size) \ + static int \ + cadence_nand_calc_ecc_bytes_##max_step_size(int step_size, \ + int strength)\ + {\ + return cadence_nand_calc_ecc_bytes(max_step_size, strength);\ + } + +CADENCE_NAND_CALC_ECC_BYTES(256) +CADENCE_NAND_CALC_ECC_BYTES(512) +CADENCE_NAND_CALC_ECC_BYTES(1024) +CADENCE_NAND_CALC_ECC_BYTES(2048) +CADENCE_NAND_CALC_ECC_BYTES(4096) + +/* Function reads BCH capabilities. */ +static int cadence_nand_read_bch_caps(struct cadence_nand_info *cadence) +{ + struct nand_ecc_caps *ecc_caps = &cadence->ecc_caps; + int max_step_size = 0, nstrengths, i; + u32 reg; + + reg = readl_relaxed(cadence->reg + BCH_CFG_3); + cadence->bch_metadata_size = FIELD_GET(BCH_CFG_3_METADATA_SIZE, reg); + if (cadence->bch_metadata_size < 4) { + dev_err(cadence->dev, + "Driver needs at least 4 bytes of BCH meta data\n"); + return -EIO; + } + + reg = readl_relaxed(cadence->reg + BCH_CFG_0); + cadence->ecc_strengths[0] = FIELD_GET(BCH_CFG_0_CORR_CAP_0, reg); + cadence->ecc_strengths[1] = FIELD_GET(BCH_CFG_0_CORR_CAP_1, reg); + cadence->ecc_strengths[2] = FIELD_GET(BCH_CFG_0_CORR_CAP_2, reg); + cadence->ecc_strengths[3] = FIELD_GET(BCH_CFG_0_CORR_CAP_3, reg); + + reg = readl_relaxed(cadence->reg + BCH_CFG_1); + cadence->ecc_strengths[4] = FIELD_GET(BCH_CFG_1_CORR_CAP_4, reg); + cadence->ecc_strengths[5] = FIELD_GET(BCH_CFG_1_CORR_CAP_5, reg); + cadence->ecc_strengths[6] = FIELD_GET(BCH_CFG_1_CORR_CAP_6, reg); + cadence->ecc_strengths[7] = FIELD_GET(BCH_CFG_1_CORR_CAP_7, reg); + + reg = readl_relaxed(cadence->reg + BCH_CFG_2); + cadence->ecc_stepinfos[0].stepsize = + FIELD_GET(BCH_CFG_2_SECT_0, reg); + + cadence->ecc_stepinfos[1].stepsize = + FIELD_GET(BCH_CFG_2_SECT_1, reg); + + nstrengths = 0; + for (i = 0; i < BCH_MAX_NUM_CORR_CAPS; i++) { + if (cadence->ecc_strengths[i] != 0) + nstrengths++; + } + + ecc_caps->nstepinfos = 0; + for (i = 0; i < BCH_MAX_NUM_SECTOR_SIZES; i++) { + /* ECC strengths are common for all step infos. */ + cadence->ecc_stepinfos[i].nstrengths = nstrengths; + cadence->ecc_stepinfos[i].strengths = + cadence->ecc_strengths; + + if (cadence->ecc_stepinfos[i].stepsize != 0) + ecc_caps->nstepinfos++; + + if (cadence->ecc_stepinfos[i].stepsize > max_step_size) + max_step_size = cadence->ecc_stepinfos[i].stepsize; + } + ecc_caps->stepinfos = &cadence->ecc_stepinfos[0]; + + switch (max_step_size) { + case 256: + ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_256; + break; + case 512: + ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_512; + break; + case 1024: + ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_1024; + break; + case 2048: + ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_2048; + break; + case 4096: + ecc_caps->calc_ecc_bytes = &cadence_nand_calc_ecc_bytes_4096; + break; + default: + dev_err(cadence->dev, + "Unsupported sector size(ecc step size) %d\n", + max_step_size); + return -EIO; + } + + return 0; +} + +/* Hardware initialization. */ +static int cadence_nand_hw_init(struct cadence_nand_info *cadence) +{ + int status; + u32 reg; + + status = cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_INIT_COMP, false); + if (status) + return status; + + reg = readl_relaxed(cadence->reg + CTRL_VERSION); + cadence->ctrl_rev = FIELD_GET(CTRL_VERSION_REV, reg); + + dev_info(cadence->dev, + "%s: cadence nand controller version reg %x\n", + __func__, reg); + + /* Disable cache and multiplane. */ + writel_relaxed(0, cadence->reg + MULTIPLANE_CFG); + writel_relaxed(0, cadence->reg + CACHE_CFG); + + /* Clear all interrupts. */ + writel_relaxed(0xFFFFFFFF, cadence->reg + INTR_STATUS); + + cadence_nand_get_caps(cadence); + if (cadence_nand_read_bch_caps(cadence)) + return -EIO; + + /* + * Set IO width access to 8. + * It is because during SW device discovering width access + * is expected to be 8. + */ + status = cadence_nand_set_access_width16(cadence, false); + + return status; +} + +#define TT_MAIN_OOB_AREAS 2 +#define TT_RAW_PAGE 3 +#define TT_BBM 4 +#define TT_MAIN_OOB_AREA_EXT 5 + +/* Prepare size of data to transfer. */ +static void +cadence_nand_prepare_data_size(struct mtd_info *mtd, + int transfer_type) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + u32 sec_size = 0, offset = 0, sec_cnt = 1; + u32 last_sec_size = cdns_chip->sector_size; + u32 data_ctrl_size = 0; + u32 reg = 0; + + if (cadence->curr_trans_type == transfer_type) + return; + + switch (transfer_type) { + case TT_MAIN_OOB_AREA_EXT: + sec_cnt = cdns_chip->sector_count; + sec_size = cdns_chip->sector_size; + data_ctrl_size = cdns_chip->avail_oob_size; + break; + case TT_MAIN_OOB_AREAS: + sec_cnt = cdns_chip->sector_count; + last_sec_size = cdns_chip->sector_size + + cdns_chip->avail_oob_size; + sec_size = cdns_chip->sector_size; + break; + case TT_RAW_PAGE: + last_sec_size = mtd->writesize + mtd->oobsize; + break; + case TT_BBM: + offset = mtd->writesize + cdns_chip->bbm_offs; + last_sec_size = 8; + break; + } + + reg = 0; + reg |= FIELD_PREP(TRAN_CFG_0_OFFSET, offset); + reg |= FIELD_PREP(TRAN_CFG_0_SEC_CNT, sec_cnt); + writel_relaxed(reg, cadence->reg + TRAN_CFG_0); + + reg = 0; + reg |= FIELD_PREP(TRAN_CFG_1_LAST_SEC_SIZE, last_sec_size); + reg |= FIELD_PREP(TRAN_CFG_1_SECTOR_SIZE, sec_size); + writel_relaxed(reg, cadence->reg + TRAN_CFG_1); + + if (cadence->caps2.data_control_supp) { + reg = readl_relaxed(cadence->reg + CONTROL_DATA_CTRL); + reg &= ~CONTROL_DATA_CTRL_SIZE; + reg |= FIELD_PREP(CONTROL_DATA_CTRL_SIZE, data_ctrl_size); + writel_relaxed(reg, cadence->reg + CONTROL_DATA_CTRL); + } + + cadence->curr_trans_type = transfer_type; +} + +static int +cadence_nand_cdma_transfer(struct cadence_nand_info *cadence, u8 chip_nr, + int page, void *buf, void *ctrl_dat, u32 buf_size, + u32 ctrl_dat_size, enum dma_data_direction dir, + bool with_ecc) +{ + dma_addr_t dma_buf, dma_ctrl_dat = 0; + u8 thread_nr = chip_nr; + int status; + u16 ctype; + + if (dir == DMA_FROM_DEVICE) + ctype = CDMA_CT_RD; + else + ctype = CDMA_CT_WR; + + cadence_nand_set_ecc_enable(cadence, with_ecc); + + dma_buf = dma_map_single(buf, buf_size, dir); + if (dma_mapping_error(cadence->dev, dma_buf)) { + dev_err(cadence->dev, "Failed to map DMA buffer\n"); + return -EIO; + } + + if (ctrl_dat && ctrl_dat_size) { + dma_ctrl_dat = dma_map_single(ctrl_dat, + ctrl_dat_size, dir); + if (dma_mapping_error(cadence->dev, dma_ctrl_dat)) { + dma_unmap_single(dma_buf, + buf_size, dir); + dev_err(cadence->dev, "Failed to map DMA buffer\n"); + return -EIO; + } + } + + cadence_nand_cdma_desc_prepare(cadence, chip_nr, page, + dma_buf, dma_ctrl_dat, ctype); + + status = cadence_nand_cdma_send_and_wait(cadence, thread_nr); + + dma_unmap_single(dma_buf, + buf_size, dir); + + if (ctrl_dat && ctrl_dat_size) + dma_unmap_single(dma_ctrl_dat, + ctrl_dat_size, dir); + if (status) + return status; + + return cadence_nand_cdma_finish(cadence); +} + +static void cadence_nand_set_timings(struct cadence_nand_info *cadence, + struct cadence_nand_timings *t) +{ + writel_relaxed(t->async_toggle_timings, + cadence->reg + ASYNC_TOGGLE_TIMINGS); + writel_relaxed(t->timings0, cadence->reg + TIMINGS0); + writel_relaxed(t->timings1, cadence->reg + TIMINGS1); + writel_relaxed(t->timings2, cadence->reg + TIMINGS2); + + if (cadence->caps2.is_phy_type_dll) + writel_relaxed(t->dll_phy_ctrl, cadence->reg + DLL_PHY_CTRL); + + writel_relaxed(t->phy_ctrl, cadence->reg + PHY_CTRL); + + if (cadence->caps2.is_phy_type_dll) { + writel_relaxed(0, cadence->reg + PHY_TSEL); + writel_relaxed(2, cadence->reg + PHY_DQ_TIMING); + writel_relaxed(t->phy_dqs_timing, + cadence->reg + PHY_DQS_TIMING); + writel_relaxed(t->phy_gate_lpbk_ctrl, + cadence->reg + PHY_GATE_LPBK_CTRL); + writel_relaxed(PHY_DLL_MASTER_CTRL_BYPASS_MODE, + cadence->reg + PHY_DLL_MASTER_CTRL); + writel_relaxed(0, cadence->reg + PHY_DLL_SLAVE_CTRL); + } +} + +static int cadence_nand_select_target(struct nand_chip *chip) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + + if (chip == cadence->selected_chip) + return 0; + + if (cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + cadence_nand_set_timings(cadence, &cdns_chip->timings); + + cadence_nand_set_ecc_strength(cadence, + cdns_chip->corr_str_idx); + + cadence_nand_set_erase_detection(cadence, true, + chip->ecc.strength); + + cadence->curr_trans_type = -1; + cadence->selected_chip = chip; + + return 0; +} + +static int cadence_nand_erase(struct mtd_info *mtd, int page) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + int status; + u8 thread_nr = cdns_chip->cs[chip->cur_cs]; + + cadence_nand_cdma_desc_prepare(cadence, + cdns_chip->cs[chip->cur_cs], + page, 0, 0, + CDMA_CT_ERASE); + status = cadence_nand_cdma_send_and_wait(cadence, thread_nr); + if (status) { + dev_err(cadence->dev, "erase operation failed\n"); + return -EIO; + } + + status = cadence_nand_cdma_finish(cadence); + if (status) + return status; + + return 0; +} + +static int cadence_ecc_setup(struct mtd_info *mtd, struct nand_chip *chip, int oobavail) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + int ret; + + /* + * If .size and .strength are already set (usually by DT), + * check if they are supported by this controller. + */ + if (chip->ecc.size && chip->ecc.strength) + return nand_check_ecc_caps(chip, &cadence->ecc_caps, oobavail); + + /* + * We want .size and .strength closest to the chip's requirement + * unless NAND_ECC_MAXIMIZE is requested. + */ + if (!(chip->ecc.options & NAND_ECC_MAXIMIZE)) { + ret = nand_match_ecc_req(chip, &cadence->ecc_caps, oobavail); + if (!ret) + return 0; + } + + /* Max ECC strength is the last thing we can do */ + return nand_maximize_ecc(chip, &cadence->ecc_caps, oobavail); +} + +static int cadence_nand_read_bbm(struct mtd_info *mtd, struct nand_chip *chip, int page, u8 *buf) +{ + int status; + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + + cadence_nand_prepare_data_size(mtd, TT_BBM); + + cadence_nand_set_skip_bytes_conf(cadence, 0, 0, 0); + + /* + * Read only bad block marker from offset + * defined by a memory manufacturer. + */ + status = cadence_nand_cdma_transfer(cadence, + cdns_chip->cs[chip->cur_cs], + page, cadence->buf, NULL, + mtd->oobsize, + 0, DMA_FROM_DEVICE, false); + if (status) { + dev_err(cadence->dev, "read BBM failed\n"); + return -EIO; + } + + memcpy(buf + cdns_chip->bbm_offs, cadence->buf, cdns_chip->bbm_len); + + return 0; +} + +static int cadence_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, int oob_required, int page) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + int status; + u16 marker_val = 0xFFFF; + + status = cadence_nand_select_target(chip); + if (status) + return status; + + cadence_nand_set_skip_bytes_conf(cadence, cdns_chip->bbm_len, + mtd->writesize + + cdns_chip->bbm_offs, + 1); + + if (oob_required) { + marker_val = *(u16 *)(chip->oob_poi + + cdns_chip->bbm_offs); + } else { + /* Set oob data to 0xFF. */ + memset(cadence->buf + mtd->writesize, 0xFF, + cdns_chip->avail_oob_size); + } + + cadence_nand_set_skip_marker_val(cadence, marker_val); + + cadence_nand_prepare_data_size(mtd, TT_MAIN_OOB_AREA_EXT); + + if (cadence_nand_dma_buf_ok(cadence, buf, mtd->writesize) && + cadence->caps2.data_control_supp && !(chip->options & NAND_USE_BOUNCE_BUFFER)) { + u8 *oob; + + if (oob_required) + oob = chip->oob_poi; + else + oob = cadence->buf + mtd->writesize; + + status = cadence_nand_cdma_transfer(cadence, + cdns_chip->cs[chip->cur_cs], + page, (void *)buf, oob, + mtd->writesize, + cdns_chip->avail_oob_size, + DMA_TO_DEVICE, true); + if (status) { + dev_err(cadence->dev, "write page failed\n"); + return -EIO; + } + + return 0; + } + + if (oob_required) { + /* Transfer the data to the oob area. */ + memcpy(cadence->buf + mtd->writesize, chip->oob_poi, + cdns_chip->avail_oob_size); + } + + memcpy(cadence->buf, buf, mtd->writesize); + + cadence_nand_prepare_data_size(mtd, TT_MAIN_OOB_AREAS); + + return cadence_nand_cdma_transfer(cadence, + cdns_chip->cs[chip->cur_cs], + page, cadence->buf, NULL, + mtd->writesize + + cdns_chip->avail_oob_size, + 0, DMA_TO_DEVICE, true); +} + +static int cadence_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + + memset(cadence->buf, 0xFF, mtd->writesize); + + return cadence_nand_write_page(mtd, chip, cadence->buf, 1, page); +} + +static int cadence_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const u8 *buf, int oob_required, int page) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + int writesize = mtd->writesize; + int oobsize = mtd->oobsize; + int ecc_steps = chip->ecc.steps; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + void *tmp_buf = cadence->buf; + int oob_skip = cdns_chip->bbm_len; + size_t size = writesize + oobsize; + int i, pos, len; + int status; + + status = cadence_nand_select_target(chip); + if (status) + return status; + + /* + * Fill the buffer with 0xff first except the full page transfer. + * This simplifies the logic. + */ + if (!buf || !oob_required) + memset(tmp_buf, 0xff, size); + + cadence_nand_set_skip_bytes_conf(cadence, 0, 0, 0); + + /* Arrange the buffer for syndrome payload/ecc layout. */ + if (buf) { + for (i = 0; i < ecc_steps; i++) { + pos = i * (ecc_size + ecc_bytes); + len = ecc_size; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(tmp_buf + pos, buf, len); + buf += len; + if (len < ecc_size) { + len = ecc_size - len; + memcpy(tmp_buf + writesize + oob_skip, buf, + len); + buf += len; + } + } + } + + if (oob_required) { + const u8 *oob = chip->oob_poi; + u32 oob_data_offset = (cdns_chip->sector_count - 1) * + (cdns_chip->sector_size + chip->ecc.bytes) + + cdns_chip->sector_size + oob_skip; + + /* BBM at the beginning of the OOB area. */ + memcpy(tmp_buf + writesize, oob, oob_skip); + + /* OOB free. */ + memcpy(tmp_buf + oob_data_offset, oob, + cdns_chip->avail_oob_size); + oob += cdns_chip->avail_oob_size; + + /* OOB ECC. */ + for (i = 0; i < ecc_steps; i++) { + pos = ecc_size + i * (ecc_size + ecc_bytes); + if (i == (ecc_steps - 1)) + pos += cdns_chip->avail_oob_size; + + len = ecc_bytes; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(tmp_buf + pos, oob, len); + oob += len; + if (len < ecc_bytes) { + len = ecc_bytes - len; + memcpy(tmp_buf + writesize + oob_skip, oob, + len); + oob += len; + } + } + } + + cadence_nand_prepare_data_size(mtd, TT_RAW_PAGE); + + return cadence_nand_cdma_transfer(cadence, + cdns_chip->cs[chip->cur_cs], + page, cadence->buf, NULL, + mtd->writesize + + mtd->oobsize, + 0, DMA_TO_DEVICE, false); +} + +static int cadence_nand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return cadence_nand_write_page_raw(mtd, chip, NULL, true, page); +} + +static int cadence_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + int status; + int ecc_err_count = 0; + + status = cadence_nand_select_target(chip); + if (status) + return status; + + cadence_nand_set_skip_bytes_conf(cadence, cdns_chip->bbm_len, + mtd->writesize + + cdns_chip->bbm_offs, 1); + + /* + * If data buffer can be accessed by DMA and data_control feature + * is supported then transfer data and oob directly. + */ + if (cadence_nand_dma_buf_ok(cadence, buf, mtd->writesize) && + cadence->caps2.data_control_supp && !(chip->options & NAND_USE_BOUNCE_BUFFER)) { + u8 *oob; + + if (oob_required) + oob = chip->oob_poi; + else + oob = cadence->buf + mtd->writesize; + + cadence_nand_prepare_data_size(mtd, TT_MAIN_OOB_AREA_EXT); + status = cadence_nand_cdma_transfer(cadence, + cdns_chip->cs[chip->cur_cs], + page, buf, oob, + mtd->writesize, + cdns_chip->avail_oob_size, + DMA_FROM_DEVICE, true); + /* Otherwise use bounce buffer. */ + } else { + cadence_nand_prepare_data_size(mtd, TT_MAIN_OOB_AREAS); + status = cadence_nand_cdma_transfer(cadence, + cdns_chip->cs[chip->cur_cs], + page, cadence->buf, + NULL, mtd->writesize + + cdns_chip->avail_oob_size, + 0, DMA_FROM_DEVICE, true); + + memcpy(buf, cadence->buf, mtd->writesize); + if (oob_required) + memcpy(chip->oob_poi, + cadence->buf + mtd->writesize, + mtd->oobsize); + } + + switch (status) { + case STAT_ECC_UNCORR: + mtd->ecc_stats.failed++; + ecc_err_count++; + break; + case STAT_ECC_CORR: + ecc_err_count = FIELD_GET(CDMA_CS_MAXERR, + cadence->cdma_desc->status); + mtd->ecc_stats.corrected += ecc_err_count; + break; + case STAT_ERASED: + case STAT_OK: + break; + default: + dev_err(cadence->dev, "read page failed\n"); + return -EIO; + } + + if (oob_required) + if (cadence_nand_read_bbm(mtd, chip, page, chip->oob_poi)) + return -EIO; + + return ecc_err_count; +} + +static int cadence_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + + return cadence_nand_read_page(mtd, chip, cadence->buf, 1, page); +} + +static int cadence_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + u8 *buf, int oob_required, int page) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + int oob_skip = cdns_chip->bbm_len; + int writesize = mtd->writesize; + int ecc_steps = chip->ecc.steps; + int ecc_size = chip->ecc.size; + int ecc_bytes = chip->ecc.bytes; + void *tmp_buf = cadence->buf; + int i, pos, len; + int status; + + status = cadence_nand_select_target(chip); + if (status) + return status; + + cadence_nand_set_skip_bytes_conf(cadence, 0, 0, 0); + + cadence_nand_prepare_data_size(mtd, TT_RAW_PAGE); + status = cadence_nand_cdma_transfer(cadence, + cdns_chip->cs[chip->cur_cs], + page, cadence->buf, NULL, + mtd->writesize + + mtd->oobsize, + 0, DMA_FROM_DEVICE, false); + + switch (status) { + case STAT_ERASED: + case STAT_OK: + break; + default: + dev_err(cadence->dev, "read raw page failed\n"); + return -EIO; + } + + /* Arrange the buffer for syndrome payload/ecc layout. */ + if (buf) { + for (i = 0; i < ecc_steps; i++) { + pos = i * (ecc_size + ecc_bytes); + len = ecc_size; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(buf, tmp_buf + pos, len); + buf += len; + if (len < ecc_size) { + len = ecc_size - len; + memcpy(buf, tmp_buf + writesize + oob_skip, + len); + buf += len; + } + } + } + + if (oob_required) { + u8 *oob = chip->oob_poi; + u32 oob_data_offset = (cdns_chip->sector_count - 1) * + (cdns_chip->sector_size + chip->ecc.bytes) + + cdns_chip->sector_size + oob_skip; + + /* OOB free. */ + memcpy(oob, tmp_buf + oob_data_offset, + cdns_chip->avail_oob_size); + + /* BBM at the beginning of the OOB area. */ + memcpy(oob, tmp_buf + writesize, oob_skip); + + oob += cdns_chip->avail_oob_size; + + /* OOB ECC */ + for (i = 0; i < ecc_steps; i++) { + pos = ecc_size + i * (ecc_size + ecc_bytes); + len = ecc_bytes; + + if (i == (ecc_steps - 1)) + pos += cdns_chip->avail_oob_size; + + if (pos >= writesize) + pos += oob_skip; + else if (pos + len > writesize) + len = writesize - pos; + + memcpy(oob, tmp_buf + pos, len); + oob += len; + if (len < ecc_bytes) { + len = ecc_bytes - len; + memcpy(oob, tmp_buf + writesize + oob_skip, + len); + oob += len; + } + } + } + return 0; +} + +static int cadence_nand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return cadence_nand_read_page_raw(mtd, chip, NULL, true, page); +} + +static void cadence_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + u8 thread_nr = 0; + u32 sdma_size; + int status; + int len_in_words = len >> 2; + + /* Wait until slave DMA interface is ready to data transfer. */ + status = cadence_nand_wait_on_sdma(cadence, &thread_nr, &sdma_size); + if (status) { + pr_err("Wait on sdma failed:%x\n", status); + hang(); + } + + if (!cadence->caps1->has_dma) { + readsq(cadence->io.virt, buf, len_in_words); + + if (sdma_size > len) { + memcpy(cadence->buf, buf + (len_in_words << 2), + len - (len_in_words << 2)); + readsl(cadence->io.virt, cadence->buf, + sdma_size / 4 - len_in_words); + } + } +} + +static void cadence_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + u8 thread_nr = 0; + u32 sdma_size; + int status; + int len_in_words = len >> 2; + + /* Wait until slave DMA interface is ready to data transfer. */ + status = cadence_nand_wait_on_sdma(cadence, &thread_nr, &sdma_size); + if (status) { + pr_err("Wait on sdma failed:%x\n", status); + hang(); + } + + if (!cadence->caps1->has_dma) { + writesq(cadence->io.virt, buf, len_in_words); + + if (sdma_size > len) { + memcpy(cadence->buf, buf + (len_in_words << 2), + len - (len_in_words << 2)); + writesl(cadence->io.virt, cadence->buf, + sdma_size / 4 - len_in_words); + } + } +} + +static int cadence_nand_cmd_opcode(struct nand_chip *chip, unsigned int op_id) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + u64 mini_ctrl_cmd = 0; + int ret; + + mini_ctrl_cmd |= GCMD_LAY_TWB; + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INSTR, GCMD_LAY_INSTR_CMD); + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INPUT_CMD, op_id); + + ret = cadence_nand_generic_cmd_send(cadence, + cdns_chip->cs[chip->cur_cs], + mini_ctrl_cmd); + + if (ret) + dev_err(cadence->dev, "send cmd %x failed\n", + op_id); + + return ret; +} + +static int cadence_nand_cmd_address(struct nand_chip *chip, + unsigned int naddrs, const u8 *addrs) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + u64 address = 0; + u64 mini_ctrl_cmd = 0; + int ret; + int i; + + mini_ctrl_cmd |= GCMD_LAY_TWB; + + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INSTR, + GCMD_LAY_INSTR_ADDR); + + for (i = 0; i < naddrs; i++) + address |= (u64)addrs[i] << (8 * i); + + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INPUT_ADDR, + address); + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INPUT_ADDR_SIZE, + naddrs - 1); + + ret = cadence_nand_generic_cmd_send(cadence, + cdns_chip->cs[chip->cur_cs], + mini_ctrl_cmd); + + if (ret) + pr_err("send address %llx failed\n", address); + + return ret; +} + +static int cadence_nand_cmd_data(struct nand_chip *chip, + unsigned int len, u8 mode) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + u64 mini_ctrl_cmd = 0; + int ret; + + mini_ctrl_cmd |= GCMD_LAY_TWB; + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAY_INSTR, + GCMD_LAY_INSTR_DATA); + + if (mode) + mini_ctrl_cmd |= FIELD_PREP(GCMD_DIR, GCMD_DIR_WRITE); + + mini_ctrl_cmd |= FIELD_PREP(GCMD_SECT_CNT, 1); + mini_ctrl_cmd |= FIELD_PREP(GCMD_LAST_SIZE, len); + + ret = cadence_nand_generic_cmd_send(cadence, + cdns_chip->cs[chip->cur_cs], + mini_ctrl_cmd); + + if (ret) { + pr_err("send generic data cmd failed\n"); + return ret; + } + + return ret; +} + +static int cadence_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + int status; + + status = cadence_nand_wait_for_value(cadence, RBN_SETINGS, + TIMEOUT_US, + BIT(cdns_chip->cs[chip->cur_cs]), + false); + return status; +} + +static int cadence_nand_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + + if (section) + return -ERANGE; + + oobregion->offset = cdns_chip->bbm_len; + oobregion->length = cdns_chip->avail_oob_size + - cdns_chip->bbm_len; + + return 0; +} + +static int cadence_nand_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + + if (section) + return -ERANGE; + + oobregion->offset = cdns_chip->avail_oob_size; + oobregion->length = chip->ecc.total; + + return 0; +} + +static const struct mtd_ooblayout_ops cadence_nand_ooblayout_ops = { + .rfree = cadence_nand_ooblayout_free, + .ecc = cadence_nand_ooblayout_ecc, +}; + +static int calc_cycl(u32 timing, u32 clock) +{ + if (timing == 0 || clock == 0) + return 0; + + if ((timing % clock) > 0) + return timing / clock; + else + return timing / clock - 1; +} + +/* Calculate max data valid window. */ +static inline u32 calc_tdvw_max(u32 trp_cnt, u32 clk_period, u32 trhoh_min, + u32 board_delay_skew_min, u32 ext_mode) +{ + if (ext_mode == 0) + clk_period /= 2; + + return (trp_cnt + 1) * clk_period + trhoh_min + + board_delay_skew_min; +} + +/* Calculate data valid window. */ +static inline u32 calc_tdvw(u32 trp_cnt, u32 clk_period, u32 trhoh_min, + u32 trea_max, u32 ext_mode) +{ + if (ext_mode == 0) + clk_period /= 2; + + return (trp_cnt + 1) * clk_period + trhoh_min - trea_max; +} + +static inline int of_get_child_count(const ofnode node) +{ + return fdtdec_get_child_count(gd->fdt_blob, ofnode_to_offset(node)); +} + +static int cadence_setup_data_interface(struct mtd_info *mtd, int chipnr, + const struct nand_data_interface *conf) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(mtd_to_nand(mtd)); + const struct nand_sdr_timings *sdr; + struct cadence_nand_timings *t = &cdns_chip->timings; + u32 reg; + u32 board_delay = cadence->board_delay; + u32 clk_period = DIV_ROUND_DOWN_ULL(1000000000000ULL, + cadence->nf_clk_rate); + u32 tceh_cnt, tcs_cnt, tadl_cnt, tccs_cnt; + u32 tfeat_cnt, trhz_cnt, tvdly_cnt; + u32 trhw_cnt, twb_cnt, twh_cnt = 0, twhr_cnt; + u32 twp_cnt = 0, trp_cnt = 0, trh_cnt = 0; + u32 if_skew = cadence->caps1->if_skew; + u32 board_delay_skew_min = board_delay - if_skew; + u32 board_delay_skew_max = board_delay + if_skew; + u32 dqs_sampl_res, phony_dqs_mod; + u32 tdvw, tdvw_min, tdvw_max; + u32 ext_rd_mode, ext_wr_mode; + u32 dll_phy_dqs_timing = 0, phony_dqs_timing = 0, rd_del_sel = 0; + u32 sampling_point; + + sdr = nand_get_sdr_timings(conf); + if (IS_ERR(sdr)) + return PTR_ERR(sdr); + + memset(t, 0, sizeof(*t)); + /* Sampling point calculation. */ + if (cadence->caps2.is_phy_type_dll) + phony_dqs_mod = 2; + else + phony_dqs_mod = 1; + + dqs_sampl_res = clk_period / phony_dqs_mod; + + tdvw_min = sdr->tREA_max + board_delay_skew_max; + /* + * The idea of those calculation is to get the optimum value + * for tRP and tRH timings. If it is NOT possible to sample data + * with optimal tRP/tRH settings, the parameters will be extended. + * If clk_period is 50ns (the lowest value) this condition is met + * for SDR timing modes 1, 2, 3, 4 and 5. + * If clk_period is 20ns the condition is met only for SDR timing + * mode 5. + */ + if (sdr->tRC_min <= clk_period && + sdr->tRP_min <= (clk_period / 2) && + sdr->tREH_min <= (clk_period / 2)) { + /* Performance mode. */ + ext_rd_mode = 0; + tdvw = calc_tdvw(trp_cnt, clk_period, sdr->tRHOH_min, + sdr->tREA_max, ext_rd_mode); + tdvw_max = calc_tdvw_max(trp_cnt, clk_period, sdr->tRHOH_min, + board_delay_skew_min, + ext_rd_mode); + /* + * Check if data valid window and sampling point can be found + * and is not on the edge (ie. we have hold margin). + * If not extend the tRP timings. + */ + if (tdvw > 0) { + if (tdvw_max <= tdvw_min || + (tdvw_max % dqs_sampl_res) == 0) { + /* + * No valid sampling point so the RE pulse need + * to be widen widening by half clock cycle. + */ + ext_rd_mode = 1; + } + } else { + /* + * There is no valid window + * to be able to sample data the tRP need to be widen. + * Very safe calculations are performed here. + */ + trp_cnt = (sdr->tREA_max + board_delay_skew_max + + dqs_sampl_res) / clk_period; + ext_rd_mode = 1; + } + + } else { + /* Extended read mode. */ + u32 trh; + + ext_rd_mode = 1; + trp_cnt = calc_cycl(sdr->tRP_min, clk_period); + trh = sdr->tRC_min - ((trp_cnt + 1) * clk_period); + if (sdr->tREH_min >= trh) + trh_cnt = calc_cycl(sdr->tREH_min, clk_period); + else + trh_cnt = calc_cycl(trh, clk_period); + + tdvw = calc_tdvw(trp_cnt, clk_period, sdr->tRHOH_min, + sdr->tREA_max, ext_rd_mode); + /* + * Check if data valid window and sampling point can be found + * or if it is at the edge check if previous is valid + * - if not extend the tRP timings. + */ + if (tdvw > 0) { + tdvw_max = calc_tdvw_max(trp_cnt, clk_period, + sdr->tRHOH_min, + board_delay_skew_min, + ext_rd_mode); + + if ((((tdvw_max / dqs_sampl_res) + * dqs_sampl_res) <= tdvw_min) || + (((tdvw_max % dqs_sampl_res) == 0) && + (((tdvw_max / dqs_sampl_res - 1) + * dqs_sampl_res) <= tdvw_min))) { + /* + * Data valid window width is lower than + * sampling resolution and do not hit any + * sampling point to be sure the sampling point + * will be found the RE low pulse width will be + * extended by one clock cycle. + */ + trp_cnt = trp_cnt + 1; + } + } else { + /* + * There is no valid window to be able to sample data. + * The tRP need to be widen. + * Very safe calculations are performed here. + */ + trp_cnt = (sdr->tREA_max + board_delay_skew_max + + dqs_sampl_res) / clk_period; + } + } + + tdvw_max = calc_tdvw_max(trp_cnt, clk_period, + sdr->tRHOH_min, + board_delay_skew_min, ext_rd_mode); + + if (sdr->tWC_min <= clk_period && + (sdr->tWP_min + if_skew) <= (clk_period / 2) && + (sdr->tWH_min + if_skew) <= (clk_period / 2)) { + ext_wr_mode = 0; + } else { + u32 twh; + + ext_wr_mode = 1; + twp_cnt = calc_cycl(sdr->tWP_min + if_skew, clk_period); + if ((twp_cnt + 1) * clk_period < (sdr->tALS_min + if_skew)) + twp_cnt = calc_cycl(sdr->tALS_min + if_skew, + clk_period); + + twh = (sdr->tWC_min - (twp_cnt + 1) * clk_period); + if (sdr->tWH_min >= twh) + twh = sdr->tWH_min; + + twh_cnt = calc_cycl(twh + if_skew, clk_period); + } + + reg = FIELD_PREP(ASYNC_TOGGLE_TIMINGS_TRH, trh_cnt); + reg |= FIELD_PREP(ASYNC_TOGGLE_TIMINGS_TRP, trp_cnt); + reg |= FIELD_PREP(ASYNC_TOGGLE_TIMINGS_TWH, twh_cnt); + reg |= FIELD_PREP(ASYNC_TOGGLE_TIMINGS_TWP, twp_cnt); + t->async_toggle_timings = reg; + dev_dbg(cadence->dev, "ASYNC_TOGGLE_TIMINGS_SDR\t%x\n", reg); + + tadl_cnt = calc_cycl((sdr->tADL_min + if_skew), clk_period); + tccs_cnt = calc_cycl((sdr->tCCS_min + if_skew), clk_period); + twhr_cnt = calc_cycl((sdr->tWHR_min + if_skew), clk_period); + trhw_cnt = calc_cycl((sdr->tRHW_min + if_skew), clk_period); + reg = FIELD_PREP(TIMINGS0_TADL, tadl_cnt); + + /* + * If timing exceeds delay field in timing register + * then use maximum value. + */ + if (FIELD_FIT(TIMINGS0_TCCS, tccs_cnt)) + reg |= FIELD_PREP(TIMINGS0_TCCS, tccs_cnt); + else + reg |= TIMINGS0_TCCS; + + reg |= FIELD_PREP(TIMINGS0_TWHR, twhr_cnt); + reg |= FIELD_PREP(TIMINGS0_TRHW, trhw_cnt); + t->timings0 = reg; + dev_dbg(cadence->dev, "TIMINGS0_SDR\t%x\n", reg); + + /* The following is related to single signal so skew is not needed. */ + trhz_cnt = calc_cycl(sdr->tRHZ_max, clk_period); + trhz_cnt = trhz_cnt + 1; + twb_cnt = calc_cycl((sdr->tWB_max + board_delay), clk_period); + /* + * Because of the two stage syncflop the value must be increased by 3 + * first value is related with sync, second value is related + * with output if delay. + */ + twb_cnt = twb_cnt + 3 + 5; + /* + * The following is related to the we edge of the random data input + * sequence so skew is not needed. + */ + tvdly_cnt = calc_cycl(500000 + if_skew, clk_period); + reg = FIELD_PREP(TIMINGS1_TRHZ, trhz_cnt); + reg |= FIELD_PREP(TIMINGS1_TWB, twb_cnt); + reg |= FIELD_PREP(TIMINGS1_TVDLY, tvdly_cnt); + t->timings1 = reg; + dev_dbg(cadence->dev, "TIMINGS1_SDR\t%x\n", reg); + + tfeat_cnt = calc_cycl(sdr->tFEAT_max, clk_period); + if (tfeat_cnt < twb_cnt) + tfeat_cnt = twb_cnt; + + tceh_cnt = calc_cycl(sdr->tCEH_min, clk_period); + tcs_cnt = calc_cycl((sdr->tCS_min + if_skew), clk_period); + + reg = FIELD_PREP(TIMINGS2_TFEAT, tfeat_cnt); + reg |= FIELD_PREP(TIMINGS2_CS_HOLD_TIME, tceh_cnt); + reg |= FIELD_PREP(TIMINGS2_CS_SETUP_TIME, tcs_cnt); + t->timings2 = reg; + dev_dbg(cadence->dev, "TIMINGS2_SDR\t%x\n", reg); + + if (cadence->caps2.is_phy_type_dll) { + reg = DLL_PHY_CTRL_DLL_RST_N; + if (ext_wr_mode) + reg |= DLL_PHY_CTRL_EXTENDED_WR_MODE; + if (ext_rd_mode) + reg |= DLL_PHY_CTRL_EXTENDED_RD_MODE; + + reg |= FIELD_PREP(DLL_PHY_CTRL_RS_HIGH_WAIT_CNT, 7); + reg |= FIELD_PREP(DLL_PHY_CTRL_RS_IDLE_CNT, 7); + t->dll_phy_ctrl = reg; + dev_dbg(cadence->dev, "DLL_PHY_CTRL_SDR\t%x\n", reg); + } + + /* Sampling point calculation. */ + if ((tdvw_max % dqs_sampl_res) > 0) + sampling_point = tdvw_max / dqs_sampl_res; + else + sampling_point = (tdvw_max / dqs_sampl_res - 1); + + if (sampling_point * dqs_sampl_res > tdvw_min) { + dll_phy_dqs_timing = + FIELD_PREP(PHY_DQS_TIMING_DQS_SEL_OE_END, 4); + dll_phy_dqs_timing |= PHY_DQS_TIMING_USE_PHONY_DQS; + phony_dqs_timing = sampling_point / phony_dqs_mod; + + if ((sampling_point % 2) > 0) { + dll_phy_dqs_timing |= PHY_DQS_TIMING_PHONY_DQS_SEL; + if ((tdvw_max % dqs_sampl_res) == 0) + /* + * Calculation for sampling point at the edge + * of data and being odd number. + */ + phony_dqs_timing = (tdvw_max / dqs_sampl_res) + / phony_dqs_mod - 1; + + if (!cadence->caps2.is_phy_type_dll) + phony_dqs_timing--; + + } else { + phony_dqs_timing--; + } + rd_del_sel = phony_dqs_timing + 3; + } else { + dev_warn(cadence->dev, + "ERROR : cannot find valid sampling point\n"); + } + + reg = FIELD_PREP(PHY_CTRL_PHONY_DQS, phony_dqs_timing); + if (cadence->caps2.is_phy_type_dll) + reg |= PHY_CTRL_SDR_DQS; + t->phy_ctrl = reg; + dev_dbg(cadence->dev, "PHY_CTRL_REG_SDR\t%x\n", reg); + + if (cadence->caps2.is_phy_type_dll) { + dev_dbg(cadence->dev, "PHY_TSEL_REG_SDR\t%x\n", 0); + dev_dbg(cadence->dev, "PHY_DQ_TIMING_REG_SDR\t%x\n", 2); + dev_dbg(cadence->dev, "PHY_DQS_TIMING_REG_SDR\t%x\n", + dll_phy_dqs_timing); + t->phy_dqs_timing = dll_phy_dqs_timing; + + reg = FIELD_PREP(PHY_GATE_LPBK_CTRL_RDS, rd_del_sel); + dev_dbg(cadence->dev, "PHY_GATE_LPBK_CTRL_REG_SDR\t%x\n", + reg); + t->phy_gate_lpbk_ctrl = reg; + + dev_dbg(cadence->dev, "PHY_DLL_MASTER_CTRL_REG_SDR\t%lx\n", + PHY_DLL_MASTER_CTRL_BYPASS_MODE); + dev_dbg(cadence->dev, "PHY_DLL_SLAVE_CTRL_REG_SDR\t%x\n", 0); + } + return 0; +} + +static int cadence_nand_attach_chip(struct nand_chip *chip) +{ + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); + static struct nand_ecclayout nand_oob; + u32 ecc_size; + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + if (chip->options & NAND_BUSWIDTH_16) { + ret = cadence_nand_set_access_width16(cadence, true); + if (ret) + return ret; + } + + chip->options |= NAND_USE_BOUNCE_BUFFER; + chip->bbt_options |= NAND_BBT_USE_FLASH; + chip->bbt_options |= NAND_BBT_NO_OOB; + chip->ecc.mode = NAND_ECC_HW_SYNDROME; + + chip->options |= NAND_NO_SUBPAGE_WRITE; + + cdns_chip->bbm_offs = chip->badblockpos; + cdns_chip->bbm_offs &= ~0x01; + /* this value should be even number */ + cdns_chip->bbm_len = 2; + + ret = cadence_ecc_setup(mtd, chip, mtd->oobsize - cdns_chip->bbm_len); + if (ret) { + dev_err(cadence->dev, "ECC configuration failed\n"); + return ret; + } + + dev_dbg(cadence->dev, + "chosen ECC settings: step=%d, strength=%d, bytes=%d\n", + chip->ecc.size, chip->ecc.strength, chip->ecc.bytes); + + /* Error correction configuration. */ + cdns_chip->sector_size = chip->ecc.size; + cdns_chip->sector_count = mtd->writesize / cdns_chip->sector_size; + ecc_size = cdns_chip->sector_count * chip->ecc.bytes; + + cdns_chip->avail_oob_size = mtd->oobsize - ecc_size; + + if (cdns_chip->avail_oob_size > cadence->bch_metadata_size) + cdns_chip->avail_oob_size = cadence->bch_metadata_size; + + if ((cdns_chip->avail_oob_size + cdns_chip->bbm_len + ecc_size) + > mtd->oobsize) + cdns_chip->avail_oob_size -= 4; + + ret = cadence_nand_get_ecc_strength_idx(cadence, chip->ecc.strength); + if (ret < 0) + return -EINVAL; + + cdns_chip->corr_str_idx = (u8)ret; + + if (cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + cadence_nand_set_ecc_strength(cadence, + cdns_chip->corr_str_idx); + + cadence_nand_set_erase_detection(cadence, true, + chip->ecc.strength); + + dev_dbg(cadence->dev, + "chosen ECC settings: step=%d, strength=%d, bytes=%d\n", + chip->ecc.size, chip->ecc.strength, chip->ecc.bytes); + + /* Override the default read operations. */ + chip->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS; + chip->ecc.read_page = cadence_nand_read_page; + chip->ecc.read_page_raw = cadence_nand_read_page_raw; + chip->ecc.write_page = cadence_nand_write_page; + chip->ecc.write_page_raw = cadence_nand_write_page_raw; + chip->ecc.read_oob = cadence_nand_read_oob; + chip->ecc.write_oob = cadence_nand_write_oob; + chip->ecc.read_oob_raw = cadence_nand_read_oob_raw; + chip->ecc.write_oob_raw = cadence_nand_write_oob_raw; + chip->erase = cadence_nand_erase; + + if ((mtd->writesize + mtd->oobsize) > cadence->buf_size) + cadence->buf_size = mtd->writesize + mtd->oobsize; + + mtd_set_ooblayout(mtd, &cadence_nand_ooblayout_ops); + + nand_oob.eccbytes = cdns_chip->chip.ecc.bytes; + cdns_chip->chip.ecc.layout = &nand_oob; + + return 0; +} + +/* Dummy implementation: we don't support multiple chips */ +static void cadence_nand_select_chip(struct mtd_info *mtd, int chipnr) +{ + switch (chipnr) { + case -1: + case 0: + break; + + default: + WARN_ON(chipnr); + } +} + +static int cadence_nand_status(struct mtd_info *mtd, unsigned int command) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int ret = 0; + + ret = cadence_nand_cmd_opcode(chip, command); + if (ret) + return ret; + + ret = cadence_nand_cmd_data(chip, 1, GCMD_DIR_READ); + if (ret) + return ret; + + return 0; +} + +static int cadence_nand_readid(struct mtd_info *mtd, int offset_in_page, unsigned int command) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + u8 addrs = (u8)offset_in_page; + int ret = 0; + + ret = cadence_nand_cmd_opcode(chip, command); + if (ret) + return ret; + + ret = cadence_nand_cmd_address(chip, ONE_CYCLE, &addrs); + if (ret) + return ret; + + ret = cadence_nand_cmd_data(chip, 8, GCMD_DIR_READ); + if (ret) + return ret; + + return 0; +} + +static int cadence_nand_param(struct mtd_info *mtd, u8 offset_in_page, unsigned int command) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int ret = 0; + + ret = cadence_nand_cmd_opcode(chip, command); + if (ret) + return ret; + + ret = cadence_nand_cmd_address(chip, ONE_CYCLE, &offset_in_page); + if (ret) + return ret; + + ret = cadence_nand_waitfunc(mtd, chip); + if (ret) + return ret; + + ret = cadence_nand_cmd_data(chip, sizeof(struct nand_jedec_params), GCMD_DIR_READ); + if (ret) + return ret; + + return 0; +} + +static int cadence_nand_reset(struct mtd_info *mtd, unsigned int command) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int ret = 0; + + ret = cadence_nand_cmd_opcode(chip, command); + if (ret) + return ret; + + ret = cadence_nand_waitfunc(mtd, chip); + if (ret) + return ret; + + return 0; +} + +static int cadence_nand_features(struct mtd_info *mtd, u8 offset_in_page, u32 command) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + int ret = 0; + + ret = cadence_nand_cmd_opcode(chip, command); + if (ret) + return ret; + + ret = cadence_nand_cmd_address(chip, ONE_CYCLE, &offset_in_page); + if (ret) + return ret; + + if (command == NAND_CMD_GET_FEATURES) + ret = cadence_nand_cmd_data(chip, ONFI_SUBFEATURE_PARAM_LEN, + GCMD_DIR_READ); + else + ret = cadence_nand_cmd_data(chip, ONFI_SUBFEATURE_PARAM_LEN, + GCMD_DIR_WRITE); + + return ret; +} + +static void cadence_nand_cmdfunc(struct mtd_info *mtd, unsigned int command, + int offset_in_page, int page) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + int ret = 0; + + cadence->cmd = command; + switch (command) { + case NAND_CMD_STATUS: + ret = cadence_nand_status(mtd, command); + break; + + case NAND_CMD_READID: + ret = cadence_nand_readid(mtd, offset_in_page, command); + break; + + case NAND_CMD_PARAM: + ret = cadence_nand_param(mtd, offset_in_page, command); + break; + + case NAND_CMD_RESET: + ret = cadence_nand_reset(mtd, command); + break; + + case NAND_CMD_SET_FEATURES: + case NAND_CMD_GET_FEATURES: + ret = cadence_nand_features(mtd, offset_in_page, command); + break; + /* + * ecc will override other command for read, write and erase + */ + default: + break; + } + + if (cadence->cmd == NAND_CMD_RESET) { + ret = cadence_nand_select_target(chip); + if (ret) + dev_err(cadence->dev, "Chip select failure after reset\n"); + } + + if (ret != 0) + printf("ERROR:%s:command:0x%x\n", __func__, cadence->cmd); +} + +static int cadence_nand_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + + if (cadence_nand_wait_for_value(cadence, CTRL_STATUS, + TIMEOUT_US, + CTRL_STATUS_CTRL_BUSY, true)) + return -ETIMEDOUT; + + return 0; +} + +static u8 cadence_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct cadence_nand_info *cadence = to_cadence_nand_info(chip->controller); + u32 size = 1; + u8 val; + + if (cadence->buf_index == 0) { + if (cadence->cmd == NAND_CMD_READID) + size = 8; + else if (cadence->cmd == NAND_CMD_PARAM) + size = sizeof(struct nand_jedec_params); + else if (cadence->cmd == NAND_CMD_GET_FEATURES) + size = ONFI_SUBFEATURE_PARAM_LEN; + + cadence_nand_read_buf(mtd, &cadence->buf[0], size); + } + + val = *(&cadence->buf[0] + cadence->buf_index); + cadence->buf_index++; + + return val; +} + +static void cadence_nand_write_byte(struct mtd_info *mtd, u8 byte) +{ + cadence_nand_write_buf(mtd, &byte, 1); +} + +static int cadence_nand_chip_init(struct cadence_nand_info *cadence, ofnode node) +{ + struct cdns_nand_chip *cdns_chip; + struct nand_chip *chip; + struct mtd_info *mtd; + int ret, i; + int nsels; + u32 cs; + + if (!ofnode_get_property(node, "reg", &nsels)) + return -ENODEV; + + nsels /= sizeof(u32); + if (nsels <= 0) { + dev_err(cadence->dev, "invalid reg property size %d\n", nsels); + return -EINVAL; + } + + cdns_chip = devm_kzalloc(cadence->dev, sizeof(*cdns_chip) + + (nsels * sizeof(u8)), GFP_KERNEL); + if (!cdns_chip) + return -ENODEV; + + cdns_chip->nsels = nsels; + for (i = 0; i < nsels; i++) { + /* Retrieve CS id. */ + ret = ofnode_read_u32_index(node, "reg", i, &cs); + if (ret) { + dev_err(cadence->dev, + "could not retrieve reg property: %d\n", + ret); + goto free_buf; + } + + if (cs >= cadence->caps2.max_banks) { + dev_err(cadence->dev, + "invalid reg value: %u (max CS = %d)\n", + cs, cadence->caps2.max_banks); + ret = -EINVAL; + goto free_buf; + } + + if (test_and_set_bit(cs, &cadence->assigned_cs)) { + dev_err(cadence->dev, + "CS %d already assigned\n", cs); + ret = -EINVAL; + goto free_buf; + } + + cdns_chip->cs[i] = cs; + } + + chip = &cdns_chip->chip; + chip->controller = &cadence->controller; + nand_set_flash_node(chip, node); + mtd = nand_to_mtd(chip); + mtd->dev->parent = cadence->dev; + + chip->options |= NAND_BUSWIDTH_AUTO; + chip->select_chip = cadence_nand_select_chip; + chip->cmdfunc = cadence_nand_cmdfunc; + chip->dev_ready = cadence_nand_dev_ready; + chip->read_byte = cadence_nand_read_byte; + chip->write_byte = cadence_nand_write_byte; + chip->waitfunc = cadence_nand_waitfunc; + chip->read_buf = cadence_nand_read_buf; + chip->write_buf = cadence_nand_write_buf; + chip->setup_data_interface = cadence_setup_data_interface; + + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) { + dev_err(cadence->dev, "Chip identification failure\n"); + goto free_buf; + } + + ret = cadence_nand_attach_chip(chip); + if (ret) { + dev_err(cadence->dev, "Chip not able to attached\n"); + goto free_buf; + } + + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(cadence->dev, "could not scan the nand chip\n"); + goto free_buf; + } + + ret = nand_register(0, mtd); + if (ret) { + dev_err(cadence->dev, "Failed to register MTD: %d\n", ret); + goto free_buf; + } + + return 0; + +free_buf: + devm_kfree(cadence->dev, cdns_chip); + return ret; +} + +static int cadence_nand_chips_init(struct cadence_nand_info *cadence) +{ + struct udevice *dev = cadence->dev; + ofnode node = dev_ofnode(dev); + ofnode nand_node; + int max_cs = cadence->caps2.max_banks; + int nchips, ret; + + nchips = of_get_child_count(node); + + if (nchips > max_cs) { + dev_err(cadence->dev, + "too many NAND chips: %d (max = %d CS)\n", + nchips, max_cs); + return -EINVAL; + } + + ofnode_for_each_subnode(nand_node, node) { + ret = cadence_nand_chip_init(cadence, nand_node); + if (ret) + return ret; + } + + return 0; +} + +static int cadence_nand_init(struct cadence_nand_info *cadence) +{ + int ret; + + cadence->cdma_desc = dma_alloc_coherent(sizeof(*cadence->cdma_desc), + (unsigned long *)&cadence->dma_cdma_desc); + if (!cadence->cdma_desc) + return -ENOMEM; + + cadence->buf_size = SZ_16K; + cadence->buf = kmalloc(cadence->buf_size, GFP_KERNEL); + if (!cadence->buf) { + ret = -ENOMEM; + goto free_buf_desc; + } + + //Hardware initialization + ret = cadence_nand_hw_init(cadence); + if (ret) + goto free_buf; + + cadence->curr_corr_str_idx = 0xFF; + + ret = cadence_nand_chips_init(cadence); + if (ret) { + dev_err(cadence->dev, "Failed to register MTD: %d\n", + ret); + goto free_buf; + } + + kfree(cadence->buf); + cadence->buf = kzalloc(cadence->buf_size, GFP_KERNEL); + if (!cadence->buf) { + ret = -ENOMEM; + goto free_buf_desc; + } + + return 0; + +free_buf: + kfree(cadence->buf); + +free_buf_desc: + dma_free_coherent(cadence->cdma_desc); + + return ret; +} + +static const struct cadence_nand_dt_devdata cadence_nand_default = { + .if_skew = 0, + .has_dma = 0, +}; + +static const struct udevice_id cadence_nand_dt_ids[] = { + { + .compatible = "cdns,nand", + .data = (unsigned long)&cadence_nand_default + }, {} +}; + +static int cadence_nand_dt_probe(struct udevice *dev) +{ + struct cadence_nand_info *cadence = dev_get_priv(dev); + const struct udevice_id *of_id; + const struct cadence_nand_dt_devdata *devdata; + struct resource res; + int ret; + u32 val; + + if (!dev) { + dev_warn(dev, "Device ptr null\n"); + return -EINVAL; + } + + of_id = &cadence_nand_dt_ids[0]; + devdata = (struct cadence_nand_dt_devdata *)of_id->data; + + cadence->caps1 = devdata; + cadence->dev = dev; + + ret = clk_get_by_index(dev, 0, &cadence->clk); + if (ret) + return ret; + + ret = clk_enable(&cadence->clk); + if (ret && ret != -ENOSYS && ret != -ENOMEM) { + dev_err(dev, "failed to enable clock\n"); + return ret; + } + cadence->nf_clk_rate = clk_get_rate(&cadence->clk); + + ret = reset_get_by_index(dev, 1, &cadence->softphy_reset); + if (ret) { + if (ret != -ENOMEM) + dev_warn(dev, "Can't get softphy_reset: %d\n", ret); + } else { + reset_deassert(&cadence->softphy_reset); + } + + ret = reset_get_by_index(dev, 0, &cadence->nand_reset); + if (ret) { + if (ret != -ENOMEM) + dev_warn(dev, "Can't get nand_reset: %d\n", ret); + } else { + reset_deassert(&cadence->nand_reset); + } + + ret = dev_read_resource_byname(dev, "reg", &res); + if (ret) + return ret; + cadence->reg = devm_ioremap(dev, res.start, resource_size(&res)); + + ret = dev_read_resource_byname(dev, "sdma", &res); + if (ret) + return ret; + cadence->io.dma = res.start; + cadence->io.virt = devm_ioremap(dev, res.start, resource_size(&res)); + + ret = ofnode_read_u32(dev_ofnode(dev->parent), + "cdns,board-delay-ps", &val); + if (ret) { + val = 4830; + dev_info(cadence->dev, + "missing cdns,board-delay-ps property, %d was set\n", + val); + } + cadence->board_delay = val; + + ret = cadence_nand_init(cadence); + if (ret) + return ret; + + return 0; +} + +U_BOOT_DRIVER(cadence_nand_dt) = { + .name = "cadence-nand-dt", + .id = UCLASS_MTD, + .of_match = cadence_nand_dt_ids, + .probe = cadence_nand_dt_probe, + .priv_auto = sizeof(struct cadence_nand_info), +}; + +void board_nand_init(void) +{ + struct udevice *dev; + int ret; + + ret = uclass_get_device_by_driver(UCLASS_MTD, + DM_DRIVER_GET(cadence_nand_dt), + &dev); + if (ret && ret != -ENODEV) + pr_err("Failed to initialize Cadence NAND controller. (error %d)\n", + ret); +} diff --git a/drivers/mtd/nand/raw/cadence_spl.c b/drivers/mtd/nand/raw/cadence_spl.c new file mode 100644 index 00000000000..17058e49faa --- /dev/null +++ b/drivers/mtd/nand/raw/cadence_spl.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2024 Intel Corporation <www.intel.com> + */ + +#include <cadence-nand.h> +#include <dm.h> +#include <hang.h> +#include <nand.h> +#include <system-constants.h> + +/* Unselect after operation */ +void nand_deselect(void) +{ + struct mtd_info *mtd; + struct nand_chip *chip; + + mtd = get_nand_dev_by_index(nand_curr_device); + if (!mtd) + hang(); + chip = mtd_to_nand(mtd); + + if (chip->select_chip) + chip->select_chip(mtd, -1); +} + +static int nand_is_bad_block(int block) +{ + struct mtd_info *mtd; + struct nand_chip *chip; + loff_t ofs = block * CONFIG_SYS_NAND_BLOCK_SIZE; + + mtd = get_nand_dev_by_index(nand_curr_device); + if (!mtd) + hang(); + chip = mtd_to_nand(mtd); + + return chip->block_bad(mtd, ofs); +} + +static int nand_read_page(int block, int page, uchar *dst) +{ + struct mtd_info *mtd; + int page_addr = block * SYS_NAND_BLOCK_PAGES + page; + loff_t ofs = page_addr * CONFIG_SYS_NAND_PAGE_SIZE; + int ret; + size_t len = CONFIG_SYS_NAND_PAGE_SIZE; + + mtd = get_nand_dev_by_index(nand_curr_device); + if (!mtd) + hang(); + + ret = nand_read(mtd, ofs, &len, dst); + if (ret) + printf("nand_read failed %d\n", ret); + + return ret; +} +#include "nand_spl_loaders.c" diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index 28c851f103b..7e683f49c5e 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -618,9 +618,7 @@ static int meson_nfc_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *ch memcpy(meson_chip->data_buf, buf, mtd->writesize); memset(meson_chip->info_buf, 0, chip->ecc.steps * PER_INFO_BYTE); - - if (oob_required) - meson_nfc_set_user_byte(chip, chip->oob_poi); + meson_nfc_set_user_byte(chip, chip->oob_poi); return meson_nfc_write_page_sub(chip, page, false); } diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 1b65c6f6443..bb5460e0068 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -306,6 +306,35 @@ void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) ioread16_rep(chip->IO_ADDR_R, p, len >> 1); } +/* + * nand_bbm_get_next_page - Get the next page for bad block markers + * @chip: The NAND chip + * @page: First page to start checking for bad block marker usage + * + * Returns an integer that corresponds to the page offset within a block, for + * a page that is used to store bad block markers. If no more pages are + * available, -EINVAL is returned. + */ +int nand_bbm_get_next_page(struct nand_chip *chip, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int last_page = ((mtd->erasesize - mtd->writesize) >> + chip->page_shift) & chip->pagemask; + unsigned int bbm_flags = NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE + | NAND_BBM_LASTPAGE; + + if (page == 0 && !(chip->options & bbm_flags)) + return 0; + if (page == 0 && chip->options & NAND_BBM_FIRSTPAGE) + return 0; + if (page <= 1 && chip->options & NAND_BBM_SECONDPAGE) + return 1; + if (page <= last_page && chip->options & NAND_BBM_LASTPAGE) + return last_page; + + return -EINVAL; +} + /** * nand_block_bad - [DEFAULT] Read bad block marker from the chip * @mtd: MTD device structure @@ -315,40 +344,32 @@ void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) */ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs) { - int page, res = 0, i = 0; struct nand_chip *chip = mtd_to_nand(mtd); - u16 bad; + int first_page, page_offset; + int res; + u8 bad; - if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) - ofs += mtd->erasesize - mtd->writesize; + first_page = (int)(ofs >> chip->page_shift) & chip->pagemask; + page_offset = nand_bbm_get_next_page(chip, 0); - page = (int)(ofs >> chip->page_shift) & chip->pagemask; + while (page_offset >= 0) { + res = chip->ecc.read_oob(mtd, chip, first_page + page_offset); + if (res < 0) + return res; - do { - if (chip->options & NAND_BUSWIDTH_16) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, - chip->badblockpos & 0xFE, page); - bad = cpu_to_le16(chip->read_word(mtd)); - if (chip->badblockpos & 0x1) - bad >>= 8; - else - bad &= 0xFF; - } else { - chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, - page); - bad = chip->read_byte(mtd); - } + bad = chip->oob_poi[chip->badblockpos]; if (likely(chip->badblockbits == 8)) res = bad != 0xFF; else res = hweight8(bad) < chip->badblockbits; - ofs += mtd->writesize; - page = (int)(ofs >> chip->page_shift) & chip->pagemask; - i++; - } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)); + if (res) + return res; - return res; + page_offset = nand_bbm_get_next_page(chip, page_offset + 1); + } + + return 0; } /** diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index f15ee4f833f..2b95eb02177 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -10,6 +10,16 @@ config MULTIPLEXER if MULTIPLEXER +config SPL_MUX_MMIO + bool "MMIO register bitfield-controlled Multiplexer" + depends on MULTIPLEXER && SYSCON + help + MMIO register bitfield-controlled Multiplexer controller. + + The driver builds multiplexer controllers for bitfields in a syscon + register. For N bit wide bitfields, there will be 2^N possible + multiplexer states. + config MUX_MMIO bool "MMIO register bitfield-controlled Multiplexer" depends on MULTIPLEXER && SYSCON diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 1563404ca17..3db784faedd 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -237,6 +237,13 @@ config DWC_ETH_QOS Of Service) IP block. The IP supports many options for bus type, clocking/reset structure, and feature list. +config DWC_ETH_QOS_ADI + bool "Synopsys DWC Ethernet QOS device support for ADI SC59x-64 parts" + depends on DWC_ETH_QOS + help + The Synopsis Designware Ethernet QoS IP block with the specific + configuration used in the ADI ADSP-SC59X 64 bit SoCs + config DWC_ETH_QOS_IMX bool "Synopsys DWC Ethernet QOS device support for IMX" depends on DWC_ETH_QOS @@ -852,6 +859,7 @@ config RENESAS_ETHER_SWITCH config RENESAS_RAVB bool "Renesas Ethernet AVB MAC" depends on RCAR_64 + select BITBANGMII select PHYLIB select PHY_ETHERNET_ID help diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 80d70212971..d919d437c08 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_DM_ETH_PHY) += eth-phy-uclass.o 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_ADI) += dwc_eth_qos_adi.o obj-$(CONFIG_DWC_ETH_QOS_IMX) += dwc_eth_qos_imx.o obj-$(CONFIG_DWC_ETH_QOS_INTEL) += dwc_eth_qos_intel.o obj-$(CONFIG_DWC_ETH_QOS_ROCKCHIP) += dwc_eth_qos_rockchip.o diff --git a/drivers/net/designware.c b/drivers/net/designware.c index 0a1fff38727..0f93c25e3fe 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -33,6 +33,9 @@ #include <linux/printk.h> #include <power/regulator.h> #include "designware.h" +#if IS_ENABLED(CONFIG_ARCH_NPCM8XX) +#include <asm/arch/gmac.h> +#endif static int dw_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) { @@ -223,6 +226,136 @@ static int dw_dm_mdio_init(const char *name, void *priv) } #endif +#if IS_ENABLED(CONFIG_BITBANGMII) && IS_ENABLED(CONFIG_DM_GPIO) +static int dw_eth_bb_mdio_active(struct mii_dev *miidev) +{ + struct dw_eth_dev *priv = miidev->priv; + struct gpio_desc *desc = &priv->mdio_gpio; + + desc->flags = 0; + dm_gpio_set_dir_flags(&priv->mdio_gpio, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + + return 0; +} + +static int dw_eth_bb_mdio_tristate(struct mii_dev *miidev) +{ + struct dw_eth_dev *priv = miidev->priv; + struct gpio_desc *desc = &priv->mdio_gpio; + + desc->flags = 0; + dm_gpio_set_dir_flags(&priv->mdio_gpio, GPIOD_IS_IN); + + return 0; +} + +static int dw_eth_bb_set_mdio(struct mii_dev *miidev, int v) +{ + struct dw_eth_dev *priv = miidev->priv; + + if (v) + dm_gpio_set_value(&priv->mdio_gpio, 1); + else + dm_gpio_set_value(&priv->mdio_gpio, 0); + + return 0; +} + +static int dw_eth_bb_get_mdio(struct mii_dev *miidev, int *v) +{ + struct dw_eth_dev *priv = miidev->priv; + + *v = dm_gpio_get_value(&priv->mdio_gpio); + + return 0; +} + +static int dw_eth_bb_set_mdc(struct mii_dev *miidev, int v) +{ + struct dw_eth_dev *priv = miidev->priv; + + if (v) + dm_gpio_set_value(&priv->mdc_gpio, 1); + else + dm_gpio_set_value(&priv->mdc_gpio, 0); + + return 0; +} + +static int dw_eth_bb_delay(struct mii_dev *miidev) +{ + struct dw_eth_dev *priv = miidev->priv; + + udelay(priv->bb_delay); + return 0; +} + +static const struct bb_miiphy_bus_ops dw_eth_bb_miiphy_bus_ops = { + .mdio_active = dw_eth_bb_mdio_active, + .mdio_tristate = dw_eth_bb_mdio_tristate, + .set_mdio = dw_eth_bb_set_mdio, + .get_mdio = dw_eth_bb_get_mdio, + .set_mdc = dw_eth_bb_set_mdc, + .delay = dw_eth_bb_delay, +}; + +static int dw_bb_miiphy_read(struct mii_dev *miidev, int addr, + int devad, int reg) +{ + return bb_miiphy_read(miidev, &dw_eth_bb_miiphy_bus_ops, + addr, devad, reg); +} + +static int dw_bb_miiphy_write(struct mii_dev *miidev, int addr, + int devad, int reg, u16 value) +{ + return bb_miiphy_write(miidev, &dw_eth_bb_miiphy_bus_ops, + addr, devad, reg, value); +} + +static int dw_bb_mdio_init(const char *name, struct udevice *dev) +{ + struct dw_eth_dev *dwpriv = dev_get_priv(dev); + struct mii_dev *bus = mdio_alloc(); + int ret; + + if (!bus) { + printf("Failed to allocate MDIO bus\n"); + return -ENOMEM; + } + + debug("\n%s: use bitbang mii..\n", dev->name); + ret = gpio_request_by_name(dev, "snps,mdc-gpio", 0, + &dwpriv->mdc_gpio, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + if (ret) { + debug("no mdc-gpio\n"); + return ret; + } + ret = gpio_request_by_name(dev, "snps,mdio-gpio", 0, + &dwpriv->mdio_gpio, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + if (ret) { + debug("no mdio-gpio\n"); + return ret; + } + dwpriv->bb_delay = dev_read_u32_default(dev, "snps,bitbang-delay", 1); + + dwpriv->bus = bus; + dwpriv->dev = dev; + + snprintf(bus->name, sizeof(bus->name), "%s", name); + bus->read = dw_bb_miiphy_read; + bus->write = dw_bb_miiphy_write; +#if CONFIG_IS_ENABLED(DM_GPIO) + bus->reset = dw_mdio_reset; +#endif + bus->priv = dwpriv; + + return mdio_register(bus); +} +#endif + static void tx_descs_init(struct dw_eth_dev *priv) { struct eth_dma_regs *dma_p = priv->dma_regs_p; @@ -352,10 +485,35 @@ static int dw_adjust_link(struct dw_eth_dev *priv, struct eth_mac_regs *mac_p, (phydev->port == PORT_FIBRE) ? ", fiber mode" : ""); #ifdef CONFIG_ARCH_NPCM8XX + if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { + unsigned int start; + + /* Indirect access to VR_MII_MMD registers */ + writew((VR_MII_MMD >> 9), PCS_BA + PCS_IND_AC); + /* Set PCS_Mode to SGMII */ + clrsetbits_le16(PCS_BA + VR_MII_MMD_AN_CTRL, BIT(1), BIT(2)); + /* Set Auto Speed Mode Change */ + setbits_le16(PCS_BA + VR_MII_MMD_CTRL1, BIT(9)); + /* Indirect access to SR_MII_MMD registers */ + writew((SR_MII_MMD >> 9), PCS_BA + PCS_IND_AC); + /* Restart Auto-Negotiation */ + setbits_le16(PCS_BA + SR_MII_MMD_CTRL, BIT(9) | BIT(12)); + + printf("SGMII PHY Wait for link up \n"); + /* SGMII PHY Wait for link up */ + start = get_timer(0); + while (!(readw(PCS_BA + SR_MII_MMD_STS) & BIT(2))) { + if (get_timer(start) >= LINK_UP_TIMEOUT) { + printf("PHY link up timeout\n"); + return -ETIMEDOUT; + } + mdelay(1); + }; + } /* Pass all Multicast Frames */ setbits_le32(&mac_p->framefilt, BIT(4)); - #endif + return 0; } @@ -784,51 +942,29 @@ int designware_eth_probe(struct udevice *dev) priv->interface = pdata->phy_interface; priv->max_speed = pdata->max_speed; -#if IS_ENABLED(CONFIG_DM_MDIO) - ret = dw_dm_mdio_init(dev->name, dev); -#else - ret = dw_mdio_init(dev->name, dev); -#endif - if (ret) { - err = ret; - goto mdio_err; - } - priv->bus = miiphy_get_dev_by_name(dev->name); - priv->dev = dev; - #if IS_ENABLED(CONFIG_BITBANGMII) && IS_ENABLED(CONFIG_DM_GPIO) if (dev_read_bool(dev, "snps,bitbang-mii")) { - int bus_idx; - - debug("\n%s: use bitbang mii..\n", dev->name); - ret = gpio_request_by_name(dev, "snps,mdc-gpio", 0, - &priv->mdc_gpio, GPIOD_IS_OUT - | GPIOD_IS_OUT_ACTIVE); + ret = dw_bb_mdio_init(dev->name, dev); if (ret) { - debug("no mdc-gpio\n"); - return ret; + err = ret; + goto mdio_err; } - ret = gpio_request_by_name(dev, "snps,mdio-gpio", 0, - &priv->mdio_gpio, GPIOD_IS_OUT - | GPIOD_IS_OUT_ACTIVE); + } else +#endif + { +#if IS_ENABLED(CONFIG_DM_MDIO) + ret = dw_dm_mdio_init(dev->name, dev); +#else + ret = dw_mdio_init(dev->name, dev); +#endif if (ret) { - debug("no mdio-gpio\n"); - return ret; - } - priv->bb_delay = dev_read_u32_default(dev, "snps,bitbang-delay", 1); - - for (bus_idx = 0; bus_idx < bb_miiphy_buses_num; bus_idx++) { - if (!bb_miiphy_buses[bus_idx].priv) { - bb_miiphy_buses[bus_idx].priv = priv; - strlcpy(bb_miiphy_buses[bus_idx].name, priv->bus->name, - MDIO_NAME_LEN); - priv->bus->read = bb_miiphy_read; - priv->bus->write = bb_miiphy_write; - break; - } + err = ret; + goto mdio_err; } + priv->bus = miiphy_get_dev_by_name(dev->name); + priv->dev = dev; } -#endif + ret = dw_phy_init(priv, dev); debug("%s, ret=%d\n", __func__, ret); if (!ret) @@ -939,83 +1075,3 @@ static struct pci_device_id supported[] = { }; U_BOOT_PCI_DEVICE(eth_designware, supported); - -#if IS_ENABLED(CONFIG_BITBANGMII) && IS_ENABLED(CONFIG_DM_GPIO) -static int dw_eth_bb_mdio_active(struct bb_miiphy_bus *bus) -{ - struct dw_eth_dev *priv = bus->priv; - struct gpio_desc *desc = &priv->mdio_gpio; - - desc->flags = 0; - dm_gpio_set_dir_flags(&priv->mdio_gpio, GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); - - return 0; -} - -static int dw_eth_bb_mdio_tristate(struct bb_miiphy_bus *bus) -{ - struct dw_eth_dev *priv = bus->priv; - struct gpio_desc *desc = &priv->mdio_gpio; - - desc->flags = 0; - dm_gpio_set_dir_flags(&priv->mdio_gpio, GPIOD_IS_IN); - - return 0; -} - -static int dw_eth_bb_set_mdio(struct bb_miiphy_bus *bus, int v) -{ - struct dw_eth_dev *priv = bus->priv; - - if (v) - dm_gpio_set_value(&priv->mdio_gpio, 1); - else - dm_gpio_set_value(&priv->mdio_gpio, 0); - - return 0; -} - -static int dw_eth_bb_get_mdio(struct bb_miiphy_bus *bus, int *v) -{ - struct dw_eth_dev *priv = bus->priv; - - *v = dm_gpio_get_value(&priv->mdio_gpio); - - return 0; -} - -static int dw_eth_bb_set_mdc(struct bb_miiphy_bus *bus, int v) -{ - struct dw_eth_dev *priv = bus->priv; - - if (v) - dm_gpio_set_value(&priv->mdc_gpio, 1); - else - dm_gpio_set_value(&priv->mdc_gpio, 0); - - return 0; -} - -static int dw_eth_bb_delay(struct bb_miiphy_bus *bus) -{ - struct dw_eth_dev *priv = bus->priv; - - udelay(priv->bb_delay); - return 0; -} - -struct bb_miiphy_bus bb_miiphy_buses[] = { - { - .name = BB_MII_DEVNAME, - .mdio_active = dw_eth_bb_mdio_active, - .mdio_tristate = dw_eth_bb_mdio_tristate, - .set_mdio = dw_eth_bb_set_mdio, - .get_mdio = dw_eth_bb_get_mdio, - .set_mdc = dw_eth_bb_set_mdc, - .delay = dw_eth_bb_delay, - .priv = NULL, - } -}; - -int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses); -#endif diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c index 2279481d935..b4ec3614696 100644 --- a/drivers/net/dwc_eth_qos.c +++ b/drivers/net/dwc_eth_qos.c @@ -1632,6 +1632,12 @@ static const struct udevice_id eqos_ids[] = { .data = (ulong)&eqos_jh7110_config }, #endif +#if IS_ENABLED(CONFIG_DWC_ETH_QOS_ADI) + { + .compatible = "adi,sc59x-dwmac-eqos", + .data = (ulong)&eqos_adi_config + }, +#endif { } }; diff --git a/drivers/net/dwc_eth_qos.h b/drivers/net/dwc_eth_qos.h index 123f98d5d53..403e8203974 100644 --- a/drivers/net/dwc_eth_qos.h +++ b/drivers/net/dwc_eth_qos.h @@ -87,6 +87,7 @@ struct eqos_mac_regs { #define EQOS_MAC_MDIO_ADDRESS_CR_MASK GENMASK(11, 8) #define EQOS_MAC_MDIO_ADDRESS_CR_100_150 1 #define EQOS_MAC_MDIO_ADDRESS_CR_20_35 2 +#define EQOS_MAC_MDIO_ADDRESS_CR_150_250 4 #define EQOS_MAC_MDIO_ADDRESS_CR_250_300 5 #define EQOS_MAC_MDIO_ADDRESS_SKAP BIT(4) #define EQOS_MAC_MDIO_ADDRESS_GOC_MASK GENMASK(3, 2) @@ -301,3 +302,4 @@ extern struct eqos_config eqos_qcom_config; extern struct eqos_config eqos_stm32mp13_config; extern struct eqos_config eqos_stm32mp15_config; extern struct eqos_config eqos_jh7110_config; +extern struct eqos_config eqos_adi_config; diff --git a/drivers/net/dwc_eth_qos_adi.c b/drivers/net/dwc_eth_qos_adi.c new file mode 100644 index 00000000000..0e6a901e303 --- /dev/null +++ b/drivers/net/dwc_eth_qos_adi.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * (C) Copyright 2024 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Greg Malysa <greg.malysa@timesys.com> + * Additional Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + */ + +#include <clk.h> +#include <dm.h> +#include <net.h> +#include <phy.h> +#include <reset.h> +#include <linux/io.h> + +#include <asm/arch-adi/sc5xx/sc5xx.h> + +#include "dwc_eth_qos.h" + +static int eqos_start_resets_adi(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + /* + * Settings need to latch with the DMA reset below. Currently only + * rgmii is supported but other phy interfaces may be supported in + * the future + */ + sc5xx_enable_rgmii(); + setbits_32(&eqos->dma_regs->mode, EQOS_DMA_MODE_SWR); + + return 0; +} + +static int eqos_probe_resources_adi(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + phy_interface_t interface; + int ret; + + ret = eqos_get_base_addr_dt(dev); + if (ret) { + pr_err("eqos_get_base_addr_dt failed: %d\n", ret); + return ret; + } + + interface = eqos->config->interface(dev); + if (interface == PHY_INTERFACE_MODE_NA) { + pr_err("Invalid PHY interface\n"); + return -EINVAL; + } + + return 0; +} + +/** + * rgmii tx clock rate is set to 125 MHz regardless of phy mode, and + * by default the internal clock is always connected to 125 MHz. According + * to the HRM it is invalid for this clock to have any other speed, so + * the hardware won't work anyway if this is wrong. + */ +static ulong eqos_get_tick_clk_rate_adi(struct udevice *dev) +{ + return 125 * 1000000; +} + +static int eqos_get_enetaddr_adi(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_plat(dev); + + return eth_env_get_enetaddr("ethaddr", pdata->enetaddr); +} + +static struct eqos_ops eqos_adi_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_adi, + .eqos_remove_resources = eqos_null_ops, + .eqos_start_resets = eqos_start_resets_adi, + .eqos_stop_resets = eqos_null_ops, + .eqos_start_clks = eqos_null_ops, + .eqos_stop_clks = eqos_null_ops, + .eqos_calibrate_pads = eqos_null_ops, + .eqos_disable_calibration = eqos_null_ops, + .eqos_set_tx_clk_speed = eqos_null_ops, + .eqos_get_enetaddr = eqos_get_enetaddr_adi, + .eqos_get_tick_clk_rate = eqos_get_tick_clk_rate_adi, +}; + +struct eqos_config __maybe_unused eqos_adi_config = { + .reg_access_always_ok = true, + .mdio_wait = 20, + .swr_wait = 50, + .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB, + .config_mac_mdio = EQOS_MAC_MDIO_ADDRESS_CR_150_250, + .axi_bus_width = EQOS_AXI_WIDTH_32, + .interface = dev_read_phy_mode, + .ops = &eqos_adi_ops, +}; diff --git a/drivers/net/phy/micrel_ksz90x1.c b/drivers/net/phy/micrel_ksz90x1.c index c48ae6e88f3..ee8eae1efd9 100644 --- a/drivers/net/phy/micrel_ksz90x1.c +++ b/drivers/net/phy/micrel_ksz90x1.c @@ -389,10 +389,126 @@ U_BOOT_PHY_DRIVER(ksz9031) = { #define KSZ9131RN_DLL_ENABLE_DELAY 0 #define KSZ9131RN_DLL_DISABLE_DELAY BIT(12) +#define KSZ9131RN_COMMON_CTRL 0 +#define KSZ9131RN_COMMON_CTRL_INDIVIDUAL_LED_MODE BIT(4) + +#define KSZ9131RN_LED_ERRATA_REG 0x1e +#define KSZ9131RN_LED_ERRATA_BIT BIT(9) + +#define KSZ9131RN_CONTROL_PAD_SKEW 4 +#define KSZ9131RN_RX_DATA_PAD_SKEW 5 +#define KSZ9131RN_TX_DATA_PAD_SKEW 6 +#define KSZ9131RN_CLK_PAD_SKEW 8 + +#define KSZ9131RN_SKEW_5BIT_MAX 2400 +#define KSZ9131RN_SKEW_4BIT_MAX 800 +#define KSZ9131RN_OFFSET 700 +#define KSZ9131RN_STEP 100 + +static int ksz9131_of_load_skew_values(struct phy_device *phydev, + ofnode of_node, + u16 reg, size_t field_sz, + const char *field[], u8 numfields) +{ + int val[4] = {-(1 + KSZ9131RN_OFFSET), -(2 + KSZ9131RN_OFFSET), + -(3 + KSZ9131RN_OFFSET), -(4 + KSZ9131RN_OFFSET)}; + int skewval, skewmax = 0; + int matches = 0; + u16 maxval; + u16 newval; + u16 mask; + int i; + + /* psec properties in dts should mean x pico seconds */ + if (field_sz == 5) + skewmax = KSZ9131RN_SKEW_5BIT_MAX; + else + skewmax = KSZ9131RN_SKEW_4BIT_MAX; + + for (i = 0; i < numfields; i++) + if (!ofnode_read_s32(of_node, field[i], &skewval)) { + if (skewval < -KSZ9131RN_OFFSET) + skewval = -KSZ9131RN_OFFSET; + else if (skewval > skewmax) + skewval = skewmax; + + val[i] = skewval + KSZ9131RN_OFFSET; + matches++; + } + + if (!matches) + return 0; + + if (matches < numfields) + newval = phy_read_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, reg); + else + newval = 0; + + maxval = (field_sz == 4) ? 0xf : 0x1f; + for (i = 0; i < numfields; i++) + if (val[i] != -(i + 1 + KSZ9131RN_OFFSET)) { + mask = 0xffff; + mask ^= maxval << (field_sz * i); + newval = (newval & mask) | + (((val[i] / KSZ9131RN_STEP) & maxval) + << (field_sz * i)); + } + + return phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, reg, newval); +} + +static int ksz9131_of_load_all_skew_values(struct phy_device *phydev) +{ + const char *control_skews[2] = { "txen-skew-psec", "rxdv-skew-psec" }; + const char *clk_skews[2] = { "rxc-skew-psec", "txc-skew-psec" }; + const char *rx_data_skews[4] = { + "rxd0-skew-psec", "rxd1-skew-psec", + "rxd2-skew-psec", "rxd3-skew-psec" + }; + const char *tx_data_skews[4] = { + "txd0-skew-psec", "txd1-skew-psec", + "txd2-skew-psec", "txd3-skew-psec" + }; + struct ofnode_phandle_args phandle_args; + int ret; + + /* + * Silently ignore failure here as the device tree is not required to + * contain a phy node. + */ + if (dev_read_phandle_with_args(phydev->dev, "phy-handle", NULL, 0, 0, + &phandle_args)) + return 0; + + if (!ofnode_valid(phandle_args.node)) + return 0; + + ret = ksz9131_of_load_skew_values(phydev, phandle_args.node, + KSZ9131RN_CLK_PAD_SKEW, 5, + clk_skews, 2); + if (ret < 0) + return ret; + + ret = ksz9131_of_load_skew_values(phydev, phandle_args.node, + KSZ9131RN_CONTROL_PAD_SKEW, 4, + control_skews, 2); + if (ret < 0) + return ret; + + ret = ksz9131_of_load_skew_values(phydev, phandle_args.node, + KSZ9131RN_RX_DATA_PAD_SKEW, 4, + rx_data_skews, 4); + if (ret < 0) + return ret; + + return ksz9131_of_load_skew_values(phydev, phandle_args.node, + KSZ9131RN_TX_DATA_PAD_SKEW, 4, + tx_data_skews, 4); +} + static int ksz9131_config_rgmii_delay(struct phy_device *phydev) { - struct phy_driver *drv = phydev->drv; - u16 rxcdll_val, txcdll_val, val; + u16 rxcdll_val, txcdll_val; int ret; switch (phydev->interface) { @@ -416,24 +532,37 @@ static int ksz9131_config_rgmii_delay(struct phy_device *phydev) return 0; } - val = drv->readext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG, - KSZ9131RN_RXC_DLL_CTRL); - val &= ~KSZ9131RN_DLL_CTRL_BYPASS; - val |= rxcdll_val; - ret = drv->writeext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG, - KSZ9131RN_RXC_DLL_CTRL, val); - if (ret) + ret = phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_RXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS, + rxcdll_val); + if (ret < 0) return ret; - val = drv->readext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG, - KSZ9131RN_TXC_DLL_CTRL); + return phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_TXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS, + txcdll_val); +} + +/* Silicon Errata DS80000693B + * + * When LEDs are configured in Individual Mode, LED1 is ON in a no-link + * condition. Workaround is to set register 0x1e, bit 9, this way LED1 behaves + * according to the datasheet (off if there is no link). + */ +static int ksz9131_led_errata(struct phy_device *phydev) +{ + int reg; - val &= ~KSZ9131RN_DLL_CTRL_BYPASS; - val |= txcdll_val; - ret = drv->writeext(phydev, 0, KSZ9131RN_MMD_COMMON_CTRL_REG, - KSZ9131RN_TXC_DLL_CTRL, val); + reg = phy_read_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_COMMON_CTRL); + if (reg < 0) + return reg; - return ret; + if (!(reg & KSZ9131RN_COMMON_CTRL_INDIVIDUAL_LED_MODE)) + return 0; + + return phy_set_bits(phydev, MDIO_DEVAD_NONE, KSZ9131RN_LED_ERRATA_REG, + KSZ9131RN_LED_ERRATA_BIT); } static int ksz9131_config(struct phy_device *phydev) @@ -446,6 +575,14 @@ static int ksz9131_config(struct phy_device *phydev) return ret; } + ret = ksz9131_of_load_all_skew_values(phydev); + if (ret < 0) + return ret; + + ret = ksz9131_led_errata(phydev); + if (ret < 0) + return ret; + /* add an option to disable the gigabit feature of this PHY */ if (env_get("disable_giga")) { unsigned features; diff --git a/drivers/net/phy/miiphybb.c b/drivers/net/phy/miiphybb.c index 9f5f9b12c9f..76463da7299 100644 --- a/drivers/net/phy/miiphybb.c +++ b/drivers/net/phy/miiphybb.c @@ -17,37 +17,13 @@ #include <miiphy.h> #include <asm/global_data.h> -int bb_miiphy_init(void) -{ - int i; - - for (i = 0; i < bb_miiphy_buses_num; i++) - if (bb_miiphy_buses[i].init != NULL) - bb_miiphy_buses[i].init(&bb_miiphy_buses[i]); - - return 0; -} - -static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname) -{ - int i; - - /* Search the correct bus */ - for (i = 0; i < bb_miiphy_buses_num; i++) { - if (!strcmp(bb_miiphy_buses[i].name, devname)) { - return &bb_miiphy_buses[i]; - } - } - return NULL; -} - /***************************************************************************** * * Utility to send the preamble, address, and register (common to read * and write). */ -static void miiphy_pre(struct bb_miiphy_bus *bus, char read, - unsigned char addr, unsigned char reg) +static void miiphy_pre(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops, + char read, unsigned char addr, unsigned char reg) { int j; @@ -59,62 +35,62 @@ static void miiphy_pre(struct bb_miiphy_bus *bus, char read, * but it is safer and will be much more robust. */ - bus->mdio_active(bus); - bus->set_mdio(bus, 1); + ops->mdio_active(miidev); + ops->set_mdio(miidev, 1); for (j = 0; j < 32; j++) { - bus->set_mdc(bus, 0); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->set_mdc(miidev, 0); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); } /* send the start bit (01) and the read opcode (10) or write (10) */ - bus->set_mdc(bus, 0); - bus->set_mdio(bus, 0); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); - bus->set_mdc(bus, 0); - bus->set_mdio(bus, 1); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); - bus->set_mdc(bus, 0); - bus->set_mdio(bus, read); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); - bus->set_mdc(bus, 0); - bus->set_mdio(bus, !read); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->set_mdc(miidev, 0); + ops->set_mdio(miidev, 0); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); + ops->set_mdc(miidev, 0); + ops->set_mdio(miidev, 1); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); + ops->set_mdc(miidev, 0); + ops->set_mdio(miidev, read); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); + ops->set_mdc(miidev, 0); + ops->set_mdio(miidev, !read); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); /* send the PHY address */ for (j = 0; j < 5; j++) { - bus->set_mdc(bus, 0); + ops->set_mdc(miidev, 0); if ((addr & 0x10) == 0) { - bus->set_mdio(bus, 0); + ops->set_mdio(miidev, 0); } else { - bus->set_mdio(bus, 1); + ops->set_mdio(miidev, 1); } - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); addr <<= 1; } /* send the register address */ for (j = 0; j < 5; j++) { - bus->set_mdc(bus, 0); + ops->set_mdc(miidev, 0); if ((reg & 0x10) == 0) { - bus->set_mdio(bus, 0); + ops->set_mdio(miidev, 0); } else { - bus->set_mdio(bus, 1); + ops->set_mdio(miidev, 1); } - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); reg <<= 1; } } @@ -126,62 +102,57 @@ static void miiphy_pre(struct bb_miiphy_bus *bus, char read, * Returns: * 0 on success */ -int bb_miiphy_read(struct mii_dev *miidev, int addr, int devad, int reg) +int bb_miiphy_read(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops, + int addr, int devad, int reg) { unsigned short rdreg; /* register working value */ int v; int j; /* counter */ - struct bb_miiphy_bus *bus; - bus = bb_miiphy_getbus(miidev->name); - if (bus == NULL) { - return -1; - } - - miiphy_pre (bus, 1, addr, reg); + miiphy_pre(miidev, ops, 1, addr, reg); /* tri-state our MDIO I/O pin so we can read */ - bus->set_mdc(bus, 0); - bus->mdio_tristate(bus); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->set_mdc(miidev, 0); + ops->mdio_tristate(miidev); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); /* check the turnaround bit: the PHY should be driving it to zero */ - bus->get_mdio(bus, &v); + ops->get_mdio(miidev, &v); if (v != 0) { /* puts ("PHY didn't drive TA low\n"); */ for (j = 0; j < 32; j++) { - bus->set_mdc(bus, 0); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->set_mdc(miidev, 0); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); } /* There is no PHY, return */ return -1; } - bus->set_mdc(bus, 0); - bus->delay(bus); + ops->set_mdc(miidev, 0); + ops->delay(miidev); /* read 16 bits of register data, MSB first */ rdreg = 0; for (j = 0; j < 16; j++) { - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->set_mdc(miidev, 1); + ops->delay(miidev); rdreg <<= 1; - bus->get_mdio(bus, &v); + ops->get_mdio(miidev, &v); rdreg |= (v & 0x1); - bus->set_mdc(bus, 0); - bus->delay(bus); + ops->set_mdc(miidev, 0); + ops->delay(miidev); } - bus->set_mdc(bus, 1); - bus->delay(bus); - bus->set_mdc(bus, 0); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->set_mdc(miidev, 1); + ops->delay(miidev); + ops->set_mdc(miidev, 0); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); debug("%s[%s](0x%x) @ 0x%x = 0x%04x\n", __func__, miidev->name, reg, addr, rdreg); @@ -195,54 +166,47 @@ int bb_miiphy_read(struct mii_dev *miidev, int addr, int devad, int reg) * Returns: * 0 on success */ -int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg, - u16 value) +int bb_miiphy_write(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops, + int addr, int devad, int reg, u16 value) { - struct bb_miiphy_bus *bus; int j; /* counter */ - bus = bb_miiphy_getbus(miidev->name); - if (bus == NULL) { - /* Bus not found! */ - return -1; - } - - miiphy_pre (bus, 0, addr, reg); + miiphy_pre(miidev, ops, 0, addr, reg); /* send the turnaround (10) */ - bus->set_mdc(bus, 0); - bus->set_mdio(bus, 1); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); - bus->set_mdc(bus, 0); - bus->set_mdio(bus, 0); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->set_mdc(miidev, 0); + ops->set_mdio(miidev, 1); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); + ops->set_mdc(miidev, 0); + ops->set_mdio(miidev, 0); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); /* write 16 bits of register data, MSB first */ for (j = 0; j < 16; j++) { - bus->set_mdc(bus, 0); + ops->set_mdc(miidev, 0); if ((value & 0x00008000) == 0) { - bus->set_mdio(bus, 0); + ops->set_mdio(miidev, 0); } else { - bus->set_mdio(bus, 1); + ops->set_mdio(miidev, 1); } - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); value <<= 1; } /* * Tri-state the MDIO line. */ - bus->mdio_tristate(bus); - bus->set_mdc(bus, 0); - bus->delay(bus); - bus->set_mdc(bus, 1); - bus->delay(bus); + ops->mdio_tristate(miidev); + ops->set_mdc(miidev, 0); + ops->delay(miidev); + ops->set_mdc(miidev, 1); + ops->delay(miidev); return 0; } diff --git a/drivers/net/ravb.c b/drivers/net/ravb.c index 7286ad19598..539fd37ee59 100644 --- a/drivers/net/ravb.c +++ b/drivers/net/ravb.c @@ -354,8 +354,15 @@ static int ravb_mac_init(struct ravb_priv *eth) /* Disable MAC Interrupt */ writel(0, eth->iobase + RAVB_REG_ECSIPR); - /* Recv frame limit set register */ - writel(RFLR_RFL_MIN, eth->iobase + RAVB_REG_RFLR); + /* + * Set receive frame length + * + * The length set here describes the frame from the destination address + * up to and including the CRC data. However only the frame data, + * excluding the CRC, are transferred to memory. To allow for the + * largest frames add the CRC length to the maximum Rx descriptor size. + */ + writel(RFLR_RFL_MIN + ETH_FCS_LEN, eth->iobase + RAVB_REG_RFLR); return 0; } @@ -490,6 +497,88 @@ static void ravb_stop(struct udevice *dev) ravb_reset(dev); } +/* Bitbang MDIO access */ +static int ravb_bb_mdio_active(struct mii_dev *miidev) +{ + struct ravb_priv *eth = miidev->priv; + + setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MMD); + + return 0; +} + +static int ravb_bb_mdio_tristate(struct mii_dev *miidev) +{ + struct ravb_priv *eth = miidev->priv; + + clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MMD); + + return 0; +} + +static int ravb_bb_set_mdio(struct mii_dev *miidev, int v) +{ + struct ravb_priv *eth = miidev->priv; + + if (v) + setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDO); + else + clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDO); + + return 0; +} + +static int ravb_bb_get_mdio(struct mii_dev *miidev, int *v) +{ + struct ravb_priv *eth = miidev->priv; + + *v = (readl(eth->iobase + RAVB_REG_PIR) & PIR_MDI) >> 3; + + return 0; +} + +static int ravb_bb_set_mdc(struct mii_dev *miidev, int v) +{ + struct ravb_priv *eth = miidev->priv; + + if (v) + setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDC); + else + clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDC); + + return 0; +} + +static int ravb_bb_delay(struct mii_dev *miidev) +{ + udelay(10); + + return 0; +} + +static const struct bb_miiphy_bus_ops ravb_bb_miiphy_bus_ops = { + .mdio_active = ravb_bb_mdio_active, + .mdio_tristate = ravb_bb_mdio_tristate, + .set_mdio = ravb_bb_set_mdio, + .get_mdio = ravb_bb_get_mdio, + .set_mdc = ravb_bb_set_mdc, + .delay = ravb_bb_delay, +}; + +static int ravb_bb_miiphy_read(struct mii_dev *miidev, int addr, + int devad, int reg) +{ + return bb_miiphy_read(miidev, &ravb_bb_miiphy_bus_ops, + addr, devad, reg); +} + +static int ravb_bb_miiphy_write(struct mii_dev *miidev, int addr, + int devad, int reg, u16 value) +{ + return bb_miiphy_write(miidev, &ravb_bb_miiphy_bus_ops, + addr, devad, reg, value); +} + static int ravb_probe(struct udevice *dev) { struct eth_pdata *pdata = dev_get_plat(dev); @@ -503,7 +592,7 @@ static int ravb_probe(struct udevice *dev) ret = clk_get_bulk(dev, ð->clks); if (ret < 0) - goto err_mdio_alloc; + goto err_clk_get; mdiodev = mdio_alloc(); if (!mdiodev) { @@ -511,37 +600,39 @@ static int ravb_probe(struct udevice *dev) goto err_mdio_alloc; } - mdiodev->read = bb_miiphy_read; - mdiodev->write = bb_miiphy_write; - bb_miiphy_buses[0].priv = eth; + mdiodev->read = ravb_bb_miiphy_read; + mdiodev->write = ravb_bb_miiphy_write; + mdiodev->priv = eth; snprintf(mdiodev->name, sizeof(mdiodev->name), dev->name); ret = mdio_register(mdiodev); if (ret < 0) goto err_mdio_register; - eth->bus = miiphy_get_dev_by_name(dev->name); + eth->bus = mdiodev; /* Bring up PHY */ ret = clk_enable_bulk(ð->clks); if (ret) - goto err_mdio_register; + goto err_clk_enable; ret = ravb_reset(dev); if (ret) - goto err_mdio_reset; + goto err_clk_enable; ret = ravb_phy_config(dev); if (ret) - goto err_mdio_reset; + goto err_clk_enable; return 0; -err_mdio_reset: - clk_release_bulk(ð->clks); +err_clk_enable: + mdio_unregister(mdiodev); err_mdio_register: mdio_free(mdiodev); err_mdio_alloc: + clk_release_bulk(ð->clks); +err_clk_get: unmap_physmem(eth->iobase, MAP_NOCACHE); return ret; } @@ -560,83 +651,6 @@ static int ravb_remove(struct udevice *dev) return 0; } -static int ravb_bb_init(struct bb_miiphy_bus *bus) -{ - return 0; -} - -static int ravb_bb_mdio_active(struct bb_miiphy_bus *bus) -{ - struct ravb_priv *eth = bus->priv; - - setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MMD); - - return 0; -} - -static int ravb_bb_mdio_tristate(struct bb_miiphy_bus *bus) -{ - struct ravb_priv *eth = bus->priv; - - clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MMD); - - return 0; -} - -static int ravb_bb_set_mdio(struct bb_miiphy_bus *bus, int v) -{ - struct ravb_priv *eth = bus->priv; - - if (v) - setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDO); - else - clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDO); - - return 0; -} - -static int ravb_bb_get_mdio(struct bb_miiphy_bus *bus, int *v) -{ - struct ravb_priv *eth = bus->priv; - - *v = (readl(eth->iobase + RAVB_REG_PIR) & PIR_MDI) >> 3; - - return 0; -} - -static int ravb_bb_set_mdc(struct bb_miiphy_bus *bus, int v) -{ - struct ravb_priv *eth = bus->priv; - - if (v) - setbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDC); - else - clrbits_le32(eth->iobase + RAVB_REG_PIR, PIR_MDC); - - return 0; -} - -static int ravb_bb_delay(struct bb_miiphy_bus *bus) -{ - udelay(10); - - return 0; -} - -struct bb_miiphy_bus bb_miiphy_buses[] = { - { - .name = "ravb", - .init = ravb_bb_init, - .mdio_active = ravb_bb_mdio_active, - .mdio_tristate = ravb_bb_mdio_tristate, - .set_mdio = ravb_bb_set_mdio, - .get_mdio = ravb_bb_get_mdio, - .set_mdc = ravb_bb_set_mdc, - .delay = ravb_bb_delay, - }, -}; -int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses); - static const struct eth_ops ravb_ops = { .start = ravb_start, .send = ravb_send, @@ -658,8 +672,6 @@ int ravb_of_to_plat(struct udevice *dev) pdata->max_speed = dev_read_u32_default(dev, "max-speed", 1000); - sprintf(bb_miiphy_buses[0].name, dev->name); - return 0; } diff --git a/drivers/net/sh_eth.c b/drivers/net/sh_eth.c index f1ce994cfd5..f695a3a41d2 100644 --- a/drivers/net/sh_eth.c +++ b/drivers/net/sh_eth.c @@ -643,6 +643,97 @@ static void sh_ether_stop(struct udevice *dev) sh_eth_stop(&priv->shdev); } +/******* for bb_miiphy *******/ +static int sh_eth_bb_mdio_active(struct mii_dev *miidev) +{ + struct sh_eth_dev *eth = miidev->priv; + struct sh_eth_info *port_info = ð->port_info[eth->port]; + + sh_eth_write(port_info, sh_eth_read(port_info, PIR) | PIR_MMD, PIR); + + return 0; +} + +static int sh_eth_bb_mdio_tristate(struct mii_dev *miidev) +{ + struct sh_eth_dev *eth = miidev->priv; + struct sh_eth_info *port_info = ð->port_info[eth->port]; + + sh_eth_write(port_info, sh_eth_read(port_info, PIR) & ~PIR_MMD, PIR); + + return 0; +} + +static int sh_eth_bb_set_mdio(struct mii_dev *miidev, int v) +{ + struct sh_eth_dev *eth = miidev->priv; + struct sh_eth_info *port_info = ð->port_info[eth->port]; + + if (v) + sh_eth_write(port_info, + sh_eth_read(port_info, PIR) | PIR_MDO, PIR); + else + sh_eth_write(port_info, + sh_eth_read(port_info, PIR) & ~PIR_MDO, PIR); + + return 0; +} + +static int sh_eth_bb_get_mdio(struct mii_dev *miidev, int *v) +{ + struct sh_eth_dev *eth = miidev->priv; + struct sh_eth_info *port_info = ð->port_info[eth->port]; + + *v = (sh_eth_read(port_info, PIR) & PIR_MDI) >> 3; + + return 0; +} + +static int sh_eth_bb_set_mdc(struct mii_dev *miidev, int v) +{ + struct sh_eth_dev *eth = miidev->priv; + struct sh_eth_info *port_info = ð->port_info[eth->port]; + + if (v) + sh_eth_write(port_info, + sh_eth_read(port_info, PIR) | PIR_MDC, PIR); + else + sh_eth_write(port_info, + sh_eth_read(port_info, PIR) & ~PIR_MDC, PIR); + + return 0; +} + +static int sh_eth_bb_delay(struct mii_dev *miidev) +{ + udelay(10); + + return 0; +} + +static const struct bb_miiphy_bus_ops sh_ether_bb_miiphy_bus_ops = { + .mdio_active = sh_eth_bb_mdio_active, + .mdio_tristate = sh_eth_bb_mdio_tristate, + .set_mdio = sh_eth_bb_set_mdio, + .get_mdio = sh_eth_bb_get_mdio, + .set_mdc = sh_eth_bb_set_mdc, + .delay = sh_eth_bb_delay, +}; + +static int sh_eth_bb_miiphy_read(struct mii_dev *miidev, int addr, + int devad, int reg) +{ + return bb_miiphy_read(miidev, &sh_ether_bb_miiphy_bus_ops, + addr, devad, reg); +} + +static int sh_eth_bb_miiphy_write(struct mii_dev *miidev, int addr, + int devad, int reg, u16 value) +{ + return bb_miiphy_write(miidev, &sh_ether_bb_miiphy_bus_ops, + addr, devad, reg, value); +} + static int sh_ether_probe(struct udevice *udev) { struct eth_pdata *pdata = dev_get_plat(udev); @@ -664,16 +755,16 @@ static int sh_ether_probe(struct udevice *udev) return ret; } - mdiodev->read = bb_miiphy_read; - mdiodev->write = bb_miiphy_write; - bb_miiphy_buses[0].priv = eth; + mdiodev->read = sh_eth_bb_miiphy_read; + mdiodev->write = sh_eth_bb_miiphy_write; + mdiodev->priv = eth; snprintf(mdiodev->name, sizeof(mdiodev->name), udev->name); ret = mdio_register(mdiodev); if (ret < 0) goto err_mdio_register; - priv->bus = miiphy_get_dev_by_name(udev->name); + priv->bus = mdiodev; eth->port = CFG_SH_ETHER_USE_PORT; eth->port_info[eth->port].phy_addr = CFG_SH_ETHER_PHY_ADDR; @@ -748,8 +839,6 @@ int sh_ether_of_to_plat(struct udevice *dev) if (cell) pdata->max_speed = fdt32_to_cpu(*cell); - sprintf(bb_miiphy_buses[0].name, dev->name); - return 0; } @@ -775,91 +864,3 @@ U_BOOT_DRIVER(eth_sh_ether) = { .plat_auto = sizeof(struct eth_pdata), .flags = DM_FLAG_ALLOC_PRIV_DMA, }; - -/******* for bb_miiphy *******/ -static int sh_eth_bb_init(struct bb_miiphy_bus *bus) -{ - return 0; -} - -static int sh_eth_bb_mdio_active(struct bb_miiphy_bus *bus) -{ - struct sh_eth_dev *eth = bus->priv; - struct sh_eth_info *port_info = ð->port_info[eth->port]; - - sh_eth_write(port_info, sh_eth_read(port_info, PIR) | PIR_MMD, PIR); - - return 0; -} - -static int sh_eth_bb_mdio_tristate(struct bb_miiphy_bus *bus) -{ - struct sh_eth_dev *eth = bus->priv; - struct sh_eth_info *port_info = ð->port_info[eth->port]; - - sh_eth_write(port_info, sh_eth_read(port_info, PIR) & ~PIR_MMD, PIR); - - return 0; -} - -static int sh_eth_bb_set_mdio(struct bb_miiphy_bus *bus, int v) -{ - struct sh_eth_dev *eth = bus->priv; - struct sh_eth_info *port_info = ð->port_info[eth->port]; - - if (v) - sh_eth_write(port_info, - sh_eth_read(port_info, PIR) | PIR_MDO, PIR); - else - sh_eth_write(port_info, - sh_eth_read(port_info, PIR) & ~PIR_MDO, PIR); - - return 0; -} - -static int sh_eth_bb_get_mdio(struct bb_miiphy_bus *bus, int *v) -{ - struct sh_eth_dev *eth = bus->priv; - struct sh_eth_info *port_info = ð->port_info[eth->port]; - - *v = (sh_eth_read(port_info, PIR) & PIR_MDI) >> 3; - - return 0; -} - -static int sh_eth_bb_set_mdc(struct bb_miiphy_bus *bus, int v) -{ - struct sh_eth_dev *eth = bus->priv; - struct sh_eth_info *port_info = ð->port_info[eth->port]; - - if (v) - sh_eth_write(port_info, - sh_eth_read(port_info, PIR) | PIR_MDC, PIR); - else - sh_eth_write(port_info, - sh_eth_read(port_info, PIR) & ~PIR_MDC, PIR); - - return 0; -} - -static int sh_eth_bb_delay(struct bb_miiphy_bus *bus) -{ - udelay(10); - - return 0; -} - -struct bb_miiphy_bus bb_miiphy_buses[] = { - { - .name = "sh_eth", - .init = sh_eth_bb_init, - .mdio_active = sh_eth_bb_mdio_active, - .mdio_tristate = sh_eth_bb_mdio_tristate, - .set_mdio = sh_eth_bb_set_mdio, - .get_mdio = sh_eth_bb_get_mdio, - .set_mdc = sh_eth_bb_set_mdc, - .delay = sh_eth_bb_delay, - } -}; - -int bb_miiphy_buses_num = ARRAY_SIZE(bb_miiphy_buses); diff --git a/drivers/net/ti/am65-cpsw-nuss.c b/drivers/net/ti/am65-cpsw-nuss.c index c70b42f6bcc..3c62fc0b428 100644 --- a/drivers/net/ti/am65-cpsw-nuss.c +++ b/drivers/net/ti/am65-cpsw-nuss.c @@ -661,7 +661,7 @@ static int am65_cpsw_ofdata_parse_phy(struct udevice *dev) dev_read_u32(dev, "max-speed", (u32 *)&pdata->max_speed); if (pdata->max_speed) - dev_err(dev, "Port %u speed froced to %uMbit\n", + dev_err(dev, "Port %u speed forced to %uMbit\n", priv->port_id, pdata->max_speed); return 0; diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c index 90f81886445..e68e31a8227 100644 --- a/drivers/pci/pci_auto.c +++ b/drivers/pci/pci_auto.c @@ -107,7 +107,8 @@ static void dm_pciauto_setup_device(struct udevice *dev, } if (prefetch && - (bar_response & PCI_BASE_ADDRESS_MEM_PREFETCH)) + (bar_response & PCI_BASE_ADDRESS_MEM_PREFETCH) && + (found_mem64 || prefetch->bus_lower < 0x100000000ULL)) bar_res = prefetch; else bar_res = mem; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index e12347e8a03..d3fe90d939e 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -163,8 +163,8 @@ config PHY_RCAR_GEN2 config PHY_RCAR_GEN3 tristate "Renesas R-Car Gen3 USB PHY" - depends on PHY && RCAR_GEN3 && CLK && DM_REGULATOR - default y if RCAR_GEN3 + depends on PHY && CLK && DM_REGULATOR && (RCAR_GEN3 || RZG2L) + default y if (RCAR_GEN3 || RZG2L) help Support for the Renesas R-Car Gen3 USB PHY. This driver operates the PHY connected to EHCI USB module and controls USB OTG operation. @@ -309,5 +309,6 @@ source "drivers/phy/cadence/Kconfig" source "drivers/phy/ti/Kconfig" source "drivers/phy/qcom/Kconfig" source "drivers/phy/renesas/Kconfig" +source "drivers/phy/starfive/Kconfig" endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index c35f9294dd9..ce4ea28b299 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -44,3 +44,4 @@ obj-y += cadence/ obj-y += ti/ obj-y += qcom/ obj-y += renesas/ +obj-y += starfive/ diff --git a/drivers/phy/phy-rcar-gen3.c b/drivers/phy/phy-rcar-gen3.c index 8c004eaf4c6..d06861c4f79 100644 --- a/drivers/phy/phy-rcar-gen3.c +++ b/drivers/phy/phy-rcar-gen3.c @@ -55,6 +55,7 @@ /* VBCTRL */ #define USB2_VBCTRL_DRVVBUSSEL BIT(8) +#define USB2_VBCTRL_VBOUT BIT(0) /* LINECTRL1 */ #define USB2_LINECTRL1_DPRPD_EN BIT(19) @@ -68,6 +69,13 @@ #define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ #define USB2_ADPCTRL_DRVVBUS BIT(4) +/* RZ/G2L specific */ +#define USB2_OBINT_IDCHG_EN BIT(0) +#define USB2_LINECTRL1_USB2_IDMON BIT(0) + +/* Device flags */ +#define RCAR_GEN3_PHY_NO_ADPCTRL BIT(0) + struct rcar_gen3_phy { fdt_addr_t regs; struct clk clk; @@ -122,15 +130,50 @@ static int rcar_gen3_phy_phy_power_off(struct phy *phy) return regulator_set_enable(priv->vbus_supply, false); } +static bool rcar_gen3_phy_check_id(struct phy *phy) +{ + const u32 adpdevmask = USB2_ADPCTRL_IDDIG | USB2_ADPCTRL_OTGSESSVLD; + struct rcar_gen3_phy *priv = dev_get_priv(phy->dev); + ulong flags = dev_get_driver_data(phy->dev); + u32 val; + + if (flags & RCAR_GEN3_PHY_NO_ADPCTRL) { + val = readl(priv->regs + USB2_LINECTRL1); + return !!(val & USB2_LINECTRL1_USB2_IDMON); + } + + val = readl(priv->regs + USB2_ADPCTRL); + return (val & adpdevmask) == adpdevmask; +} + +static void rcar_gen3_phy_set_vbus(struct phy *phy, bool enable) +{ + struct rcar_gen3_phy *priv = dev_get_priv(phy->dev); + ulong flags = dev_get_driver_data(phy->dev); + u32 bits = USB2_ADPCTRL_DRVVBUS; + u64 reg = USB2_ADPCTRL; + + if (flags & RCAR_GEN3_PHY_NO_ADPCTRL) { + bits = USB2_VBCTRL_VBOUT; + reg = USB2_VBCTRL; + } + + if (enable) + setbits_le32(priv->regs + reg, bits); + else + clrbits_le32(priv->regs + reg, bits); +} + static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) { - const u32 adpdevmask = USB2_ADPCTRL_IDDIG | USB2_ADPCTRL_OTGSESSVLD; struct rcar_gen3_phy *priv = dev_get_priv(phy->dev); - u32 adpctrl; + ulong flags = dev_get_driver_data(phy->dev); if (mode == PHY_MODE_USB_OTG) { if (submode) { + u32 obint_enable_bits; + /* OTG submode is used as initialization indicator */ writel(USB2_INT_ENABLE_UCOM_INTEN | USB2_INT_ENABLE_USBH_INTB_EN | @@ -138,13 +181,16 @@ static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode, priv->regs + USB2_INT_ENABLE); setbits_le32(priv->regs + USB2_VBCTRL, USB2_VBCTRL_DRVVBUSSEL); - writel(USB2_OBINT_SESSVLDCHG | USB2_OBINT_IDDIGCHG, - priv->regs + USB2_OBINTSTA); - setbits_le32(priv->regs + USB2_OBINTEN, - USB2_OBINT_SESSVLDCHG | - USB2_OBINT_IDDIGCHG); - setbits_le32(priv->regs + USB2_ADPCTRL, - USB2_ADPCTRL_IDPULLUP); + if (flags & RCAR_GEN3_PHY_NO_ADPCTRL) { + obint_enable_bits = USB2_OBINT_IDCHG_EN; + } else { + obint_enable_bits = USB2_OBINT_SESSVLDCHG | + USB2_OBINT_IDDIGCHG; + setbits_le32(priv->regs + USB2_ADPCTRL, + USB2_ADPCTRL_IDPULLUP); + } + writel(obint_enable_bits, priv->regs + USB2_OBINTSTA); + setbits_le32(priv->regs + USB2_OBINTEN, obint_enable_bits); clrsetbits_le32(priv->regs + USB2_LINECTRL1, USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD | @@ -154,8 +200,7 @@ static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode, USB2_LINECTRL1_DMRPD_EN); } - adpctrl = readl(priv->regs + USB2_ADPCTRL); - if ((adpctrl & adpdevmask) == adpdevmask) + if (rcar_gen3_phy_check_id(phy)) mode = PHY_MODE_USB_DEVICE; else mode = PHY_MODE_USB_HOST; @@ -165,13 +210,13 @@ static int rcar_gen3_phy_phy_set_mode(struct phy *phy, enum phy_mode mode, clrbits_le32(priv->regs + USB2_COMMCTRL, USB2_COMMCTRL_OTG_PERI); setbits_le32(priv->regs + USB2_LINECTRL1, USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD); - setbits_le32(priv->regs + USB2_ADPCTRL, USB2_ADPCTRL_DRVVBUS); + rcar_gen3_phy_set_vbus(phy, true); } else if (mode == PHY_MODE_USB_DEVICE) { setbits_le32(priv->regs + USB2_COMMCTRL, USB2_COMMCTRL_OTG_PERI); clrsetbits_le32(priv->regs + USB2_LINECTRL1, USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD, USB2_LINECTRL1_DM_RPD); - clrbits_le32(priv->regs + USB2_ADPCTRL, USB2_ADPCTRL_DRVVBUS); + rcar_gen3_phy_set_vbus(phy, false); } else { dev_err(phy->dev, "Unknown mode %d\n", mode); return -EINVAL; @@ -226,7 +271,13 @@ static int rcar_gen3_phy_remove(struct udevice *dev) } static const struct udevice_id rcar_gen3_phy_of_match[] = { - { .compatible = "renesas,rcar-gen3-usb2-phy", }, + { + .compatible = "renesas,rcar-gen3-usb2-phy", + }, + { + .compatible = "renesas,rzg2l-usb2-phy", + .data = RCAR_GEN3_PHY_NO_ADPCTRL, + }, { }, }; diff --git a/drivers/phy/starfive/Kconfig b/drivers/phy/starfive/Kconfig new file mode 100644 index 00000000000..d11338ed484 --- /dev/null +++ b/drivers/phy/starfive/Kconfig @@ -0,0 +1,21 @@ +# +# PHY drivers for Starfive platforms +# + +menu "Starfive PHY driver" + +config PHY_STARFIVE_JH7110_PCIE + bool "Starfive JH7110 PCIe 2.0 PHY driver" + depends on PHY + help + Enable this to support the Starfive JH7110 PCIE 2.0/USB 3.0 PHY. + Generic PHY driver JH7110 USB 3.0/ PCIe 2.0. + +config PHY_STARFIVE_JH7110_USB2 + bool "Starfive JH7110 USB 2.0 PHY driver" + depends on PHY + help + Enable this to support the Starfive JH7110 USB 2.0 PHY. + Generic PHY driver JH7110 USB 2.0. + +endmenu diff --git a/drivers/phy/starfive/Makefile b/drivers/phy/starfive/Makefile new file mode 100644 index 00000000000..82f25aa21b7 --- /dev/null +++ b/drivers/phy/starfive/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2023 Starfive +# + +obj-$(CONFIG_PHY_STARFIVE_JH7110_PCIE) += phy-jh7110-pcie.o +obj-$(CONFIG_PHY_STARFIVE_JH7110_USB2) += phy-jh7110-usb2.o diff --git a/drivers/phy/starfive/phy-jh7110-pcie.c b/drivers/phy/starfive/phy-jh7110-pcie.c new file mode 100644 index 00000000000..a30582821d9 --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-pcie.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive JH7110 PCIe 2.0 PHY driver + * + * Copyright (C) 2024 StarFive Technology Co., Ltd. + * Author: Minda Chen <minda.chen@starfivetech.com> + */ +#include <asm/io.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <errno.h> +#include <generic-phy.h> +#include <regmap.h> +#include <soc.h> +#include <syscon.h> +#include <linux/bitops.h> +#include <linux/err.h> + +#include "phy-jh7110-usb-syscon.h" + +#define PCIE_KVCO_LEVEL_OFF 0x28 +#define PCIE_USB3_PHY_PLL_CTL_OFF 0x7c +#define PCIE_USB3_PHY_SS_MODE BIT(4) +#define PCIE_KVCO_TUNE_SIGNAL_OFF 0x80 +#define PHY_KVCO_FINE_TUNE_LEVEL 0x91 +#define PHY_KVCO_FINE_TUNE_SIGNALS 0xc + +#define PCIE_USB3_PHY_MODE 0x1 +#define PCIE_BUS_WIDTH 0x2 +#define PCIE_USB3_PHY_ENABLE 0x1 +#define PCIE_USB3_PHY_SPLIT 0x1 + +struct jh7110_pcie_phy { + struct phy *phy; + struct regmap *stg_syscon; + struct regmap *sys_syscon; + void __iomem *regs; + struct regmap_field *phy_mode; + struct regmap_field *bus_width; + struct regmap_field *usb3_phy_en; + struct regmap_field *usb_split; + enum phy_mode mode; +}; + +static int phy_pcie_mode_set(struct jh7110_pcie_phy *data, bool usb_mode) +{ + unsigned int phy_mode, width, usb3_phy, ss_mode, split; + + /* default is PCIe mode */ + if (!data->stg_syscon || !data->sys_syscon) { + if (usb_mode) { + dev_err(data->phy->dev, "doesn't support USB3 mode\n"); + return -EINVAL; + } + return 0; + } + + if (usb_mode) { + phy_mode = PCIE_USB3_PHY_MODE; + width = 0; + usb3_phy = PCIE_USB3_PHY_ENABLE; + ss_mode = PCIE_USB3_PHY_SS_MODE; + split = 0; + } else { + phy_mode = 0; + width = PCIE_BUS_WIDTH; + usb3_phy = 0; + ss_mode = 0; + split = PCIE_USB3_PHY_SPLIT; + } + + regmap_field_write(data->phy_mode, phy_mode); + regmap_field_write(data->bus_width, width); + regmap_field_write(data->usb3_phy_en, usb3_phy); + clrsetbits_le32(data->regs + PCIE_USB3_PHY_PLL_CTL_OFF, + PCIE_USB3_PHY_SS_MODE, ss_mode); + regmap_field_write(data->usb_split, split); + + return 0; +} + +static void phy_kvco_gain_set(struct jh7110_pcie_phy *phy) +{ + /* PCIe Multi-PHY PLL KVCO Gain fine tune settings: */ + writel(PHY_KVCO_FINE_TUNE_LEVEL, phy->regs + PCIE_KVCO_LEVEL_OFF); + writel(PHY_KVCO_FINE_TUNE_SIGNALS, phy->regs + PCIE_KVCO_TUNE_SIGNAL_OFF); +} + +static int jh7110_pcie_phy_set_mode(struct phy *phy, + enum phy_mode mode, int submode) +{ + struct udevice *dev = phy->dev; + struct jh7110_pcie_phy *pcie_phy = dev_get_priv(dev); + int ret; + + if (mode == pcie_phy->mode) + return 0; + + switch (mode) { + case PHY_MODE_USB_HOST: + case PHY_MODE_USB_DEVICE: + case PHY_MODE_USB_OTG: + ret = phy_pcie_mode_set(pcie_phy, 1); + if (ret) + return ret; + break; + case PHY_MODE_PCIE: + phy_pcie_mode_set(pcie_phy, 0); + break; + default: + return -EINVAL; + } + + dev_dbg(phy->dev, "Changing PHY mode to %d\n", mode); + pcie_phy->mode = mode; + + return 0; +} + +static const struct phy_ops jh7110_pcie_phy_ops = { + .set_mode = jh7110_pcie_phy_set_mode, +}; + +static int phy_stg_regfield_init(struct udevice *dev, int mode, int usb3) +{ + struct jh7110_pcie_phy *phy = dev_get_priv(dev); + struct reg_field phy_mode = REG_FIELD(mode, 20, 21); + struct reg_field bus_width = REG_FIELD(usb3, 2, 3); + struct reg_field usb3_phy_en = REG_FIELD(usb3, 4, 4); + + phy->phy_mode = devm_regmap_field_alloc(dev, phy->stg_syscon, phy_mode); + if (IS_ERR(phy->phy_mode)) { + dev_err(dev, "PHY mode reg field init failed\n"); + return PTR_ERR(phy->phy_mode); + } + + phy->bus_width = devm_regmap_field_alloc(dev, phy->stg_syscon, bus_width); + if (IS_ERR(phy->bus_width)) { + dev_err(dev, "PHY bus width reg field init failed\n"); + return PTR_ERR(phy->bus_width); + } + + phy->usb3_phy_en = devm_regmap_field_alloc(dev, phy->stg_syscon, usb3_phy_en); + if (IS_ERR(phy->usb3_phy_en)) { + dev_err(dev, "USB3 PHY enable field init failed\n"); + return PTR_ERR(phy->bus_width); + } + + return 0; +} + +static int phy_sys_regfield_init(struct udevice *dev, int split) +{ + struct jh7110_pcie_phy *phy = dev_get_priv(dev); + struct reg_field usb_split = REG_FIELD(split, USB_PDRSTN_SPLIT_BIT, USB_PDRSTN_SPLIT_BIT); + + phy->usb_split = devm_regmap_field_alloc(dev, phy->sys_syscon, usb_split); + if (IS_ERR(phy->usb_split)) { + dev_err(dev, "USB split field init failed\n"); + return PTR_ERR(phy->usb_split); + } + + return 0; +} + +static int starfive_pcie_phy_get_syscon(struct udevice *dev) +{ + struct jh7110_pcie_phy *phy = dev_get_priv(dev); + struct ofnode_phandle_args sys_phandle, stg_phandle; + int ret; + + /* get corresponding syscon phandle */ + ret = dev_read_phandle_with_args(dev, "starfive,sys-syscon", NULL, 1, 0, + &sys_phandle); + + if (ret < 0) { + dev_err(dev, "Can't get sys cfg phandle: %d\n", ret); + return ret; + } + + ret = dev_read_phandle_with_args(dev, "starfive,stg-syscon", NULL, 2, 0, + &stg_phandle); + + if (ret < 0) { + dev_err(dev, "Can't get stg cfg phandle: %d\n", ret); + return ret; + } + + phy->sys_syscon = syscon_node_to_regmap(sys_phandle.node); + /* get syscon register offset */ + if (!IS_ERR(phy->sys_syscon)) { + ret = phy_sys_regfield_init(dev, SYSCON_USB_PDRSTN_REG_OFFSET); + if (ret) + return ret; + } else { + phy->sys_syscon = NULL; + } + + phy->stg_syscon = syscon_node_to_regmap(stg_phandle.node); + if (!IS_ERR(phy->stg_syscon)) + return phy_stg_regfield_init(dev, stg_phandle.args[0], + stg_phandle.args[1]); + else + phy->stg_syscon = NULL; + + return 0; +} + +int jh7110_pcie_phy_probe(struct udevice *dev) +{ + struct jh7110_pcie_phy *phy = dev_get_priv(dev); + int rc; + + phy->regs = dev_read_addr_ptr(dev); + if (!phy->regs) + return -EINVAL; + + rc = starfive_pcie_phy_get_syscon(dev); + if (rc) + return rc; + + phy_kvco_gain_set(phy); + + return 0; +} + +static const struct udevice_id jh7110_pcie_phy[] = { + { .compatible = "starfive,jh7110-pcie-phy"}, + {}, +}; + +U_BOOT_DRIVER(jh7110_pcie_phy) = { + .name = "jh7110_pcie_phy", + .id = UCLASS_PHY, + .of_match = jh7110_pcie_phy, + .probe = jh7110_pcie_phy_probe, + .ops = &jh7110_pcie_phy_ops, + .priv_auto = sizeof(struct jh7110_pcie_phy), +}; diff --git a/drivers/phy/starfive/phy-jh7110-usb-syscon.h b/drivers/phy/starfive/phy-jh7110-usb-syscon.h new file mode 100644 index 00000000000..0eb66f0d859 --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-usb-syscon.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef PHY_JH7110_USB_SYSCON_H_ +#define PHY_JH7110_USB_SYSCON_H_ + +#define SYSCON_USB_PDRSTN_REG_OFFSET 0x18 +#define USB_PDRSTN_SPLIT_BIT 17 + +#endif diff --git a/drivers/phy/starfive/phy-jh7110-usb2.c b/drivers/phy/starfive/phy-jh7110-usb2.c new file mode 100644 index 00000000000..1a28381e0df --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-usb2.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive JH7110 USB 2.0 PHY driver + * + * Copyright (C) 2024 StarFive Technology Co., Ltd. + * Author: Minda Chen <minda.chen@starfivetech.com> + */ + +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <errno.h> +#include <generic-phy.h> +#include <regmap.h> +#include <soc.h> +#include <syscon.h> +#include <linux/bitops.h> +#include <linux/err.h> + +#include "phy-jh7110-usb-syscon.h" + +#define USB_LS_KEEPALIVE_OFF 0x4 +#define USB_LS_KEEPALIVE_ENABLE BIT(4) +#define USB_PHY_CLK_RATE 125000000 + +struct jh7110_usb2_phy { + struct phy *phy; + struct regmap *sys_syscon; + void __iomem *regs; + struct clk *usb_125m_clk; + struct clk *app_125m; + struct regmap_field *usb_split; + enum phy_mode mode; +}; + +static void usb2_set_ls_keepalive(struct jh7110_usb2_phy *phy, bool set) +{ + /* Host mode enable the LS speed keep-alive signal */ + clrsetbits_le32(phy->regs + USB_LS_KEEPALIVE_OFF, + USB_LS_KEEPALIVE_ENABLE, + set ? USB_LS_KEEPALIVE_ENABLE : 0); +} + +static int usb2_phy_set_mode(struct phy *phy, + enum phy_mode mode, int submode) +{ + struct udevice *dev = phy->dev; + struct jh7110_usb2_phy *usb2_phy = dev_get_priv(dev); + + if (mode == usb2_phy->mode) + return 0; + + switch (mode) { + case PHY_MODE_USB_HOST: + case PHY_MODE_USB_DEVICE: + case PHY_MODE_USB_OTG: + dev_dbg(dev, "Changing PHY to %d\n", mode); + usb2_phy->mode = mode; + usb2_set_ls_keepalive(usb2_phy, (mode != PHY_MODE_USB_DEVICE)); + break; + default: + return -EINVAL; + } + + /* set default split usb 2.0 only mode */ + regmap_field_write(usb2_phy->usb_split, true); + + return 0; +} + +static int jh7110_usb2_phy_init(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct jh7110_usb2_phy *usb2_phy = dev_get_priv(dev); + int ret; + + ret = clk_set_rate(usb2_phy->usb_125m_clk, USB_PHY_CLK_RATE); + if (ret < 0) { + dev_err(dev, "Failed to set 125m clock\n"); + return ret; + } + + return clk_prepare_enable(usb2_phy->app_125m); +} + +static int jh7110_usb2_phy_exit(struct phy *phy) +{ + struct udevice *dev = phy->dev; + struct jh7110_usb2_phy *usb2_phy = dev_get_priv(dev); + + clk_disable_unprepare(usb2_phy->app_125m); + + return 0; +} + +struct phy_ops jh7110_usb2_phy_ops = { + .init = jh7110_usb2_phy_init, + .exit = jh7110_usb2_phy_exit, + .set_mode = usb2_phy_set_mode, +}; + +int jh7110_usb2_phy_probe(struct udevice *dev) +{ + struct jh7110_usb2_phy *phy = dev_get_priv(dev); + ofnode node; + struct reg_field usb_split; + int ret; + + phy->regs = dev_read_addr_ptr(dev); + if (!phy->regs) + return -EINVAL; + + node = ofnode_by_compatible(ofnode_null(), "starfive,jh7110-sys-syscon"); + if (!ofnode_valid(node)) { + dev_err(dev, "Can't get syscon dev node\n"); + return -ENODEV; + } + + phy->sys_syscon = syscon_node_to_regmap(node); + if (IS_ERR(phy->sys_syscon)) { + dev_err(dev, "Can't get syscon regmap: %d\n", ret); + return PTR_ERR(phy->sys_syscon); + } + + usb_split.reg = SYSCON_USB_PDRSTN_REG_OFFSET; + usb_split.lsb = USB_PDRSTN_SPLIT_BIT; + usb_split.msb = USB_PDRSTN_SPLIT_BIT; + phy->usb_split = devm_regmap_field_alloc(dev, phy->sys_syscon, usb_split); + if (IS_ERR(phy->usb_split)) { + dev_err(dev, "USB split field init failed\n"); + return PTR_ERR(phy->usb_split); + } + + phy->usb_125m_clk = devm_clk_get(dev, "125m"); + if (IS_ERR(phy->usb_125m_clk)) { + dev_err(dev, "Failed to get 125m clock\n"); + return PTR_ERR(phy->usb_125m_clk); + } + + phy->app_125m = devm_clk_get(dev, "app_125m"); + if (IS_ERR(phy->app_125m)) { + dev_err(dev, "Failed to get app 125m clock\n"); + return PTR_ERR(phy->app_125m); + } + + return 0; +} + +static const struct udevice_id jh7110_usb2_phy[] = { + { .compatible = "starfive,jh7110-usb-phy"}, + {}, +}; + +U_BOOT_DRIVER(jh7110_usb2_phy) = { + .name = "jh7110_usb2_phy", + .id = UCLASS_PHY, + .of_match = jh7110_usb2_phy, + .probe = jh7110_usb2_phy_probe, + .ops = &jh7110_usb2_phy_ops, + .priv_auto = sizeof(struct jh7110_usb2_phy), +}; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 6ee7dc1cce8..687fb339ea0 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -178,6 +178,14 @@ config PINCTRL_APPLE both the GPIO definitions and pin control functions for each available multiplex function. +config PINCTRL_ADI + bool "ADI pinctrl driver" + depends on DM && ARCH_SC5XX + help + This driver enables pinctrl support on SC5xx processors. This + driver covers only the pin configuration functionality, and + GPIO functionality is contained in the separate GPIO driver. + config PINCTRL_AR933X bool "QCA/Athores ar933x pin control driver" depends on DM && SOC_AR933X diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 634047a91f4..6deb6aaf6eb 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -3,6 +3,7 @@ obj-y += pinctrl-uclass.o obj-$(CONFIG_$(XPL_)PINCTRL_GENERIC) += pinctrl-generic.o +obj-$(CONFIG_PINCTRL_ADI) += pinctrl-adi-adsp.o obj-$(CONFIG_PINCTRL_APPLE) += pinctrl-apple.o obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o obj-$(CONFIG_PINCTRL_AT91PIO4) += pinctrl-at91-pio4.o @@ -15,7 +16,6 @@ obj-$(CONFIG_ARCH_MTMIPS) += mtmips/ obj-$(CONFIG_ARCH_NPCM) += nuvoton/ obj-$(CONFIG_PINCTRL_QCOM) += qcom/ obj-$(CONFIG_ARCH_RENESAS) += renesas/ -obj-$(CONFIG_ARCH_RZN1) += renesas/ obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o obj-$(CONFIG_PINCTRL_SUNXI) += sunxi/ obj-$(CONFIG_$(XPL_)PINCTRL_TEGRA) += tegra/ diff --git a/drivers/pinctrl/intel/Kconfig b/drivers/pinctrl/intel/Kconfig index 316a8fe27fd..0d8bfc53c11 100644 --- a/drivers/pinctrl/intel/Kconfig +++ b/drivers/pinctrl/intel/Kconfig @@ -8,9 +8,6 @@ config INTEL_PINCTRL_DUAL_ROUTE_SUPPORT bool default y -config INTEL_PINCTRL_PADCFG_PADTOL - bool n - config INTEL_PINCTRL_IOSTANDBY bool default y diff --git a/drivers/pinctrl/pinctrl-adi-adsp.c b/drivers/pinctrl/pinctrl-adi-adsp.c new file mode 100644 index 00000000000..debf434212d --- /dev/null +++ b/drivers/pinctrl/pinctrl-adi-adsp.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Author: Greg Malysa <greg.malysa@timesys.com> + * Additional Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * + * dm pinctrl implementation for ADI ADSP SoCs + * + */ + +#include <dm.h> +#include <dm/pinctrl.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <linux/io.h> + +#define ADSP_PORT_MMIO_SIZE 0x80 +#define ADSP_PORT_PIN_SIZE 16 + +#define ADSP_PORT_PORT_MUX_BITS 2 +#define ADSP_PORT_PORT_MUX_MASK 0x03 +#define ADSP_PINCTRL_FUNCTION_COUNT 4 + +#define ADSP_PORT_REG_FER 0x00 +#define ADSP_PORT_REG_FER_SET 0x04 +#define ADSP_PORT_REG_FER_CLEAR 0x08 +#define ADSP_PORT_REG_DATA 0x0c +#define ADSP_PORT_REG_DATA_SET 0x10 +#define ADSP_PORT_REG_DATA_CLEAR 0x14 +#define ADSP_PORT_REG_DIR 0x18 +#define ADSP_PORT_REG_DIR_SET 0x1c +#define ADSP_PORT_REG_DIR_CLEAR 0x20 +#define ADSP_PORT_REG_INEN 0x24 +#define ADSP_PORT_REG_INEN_SET 0x28 +#define ADSP_PORT_REG_INEN_CLEAR 0x2c +#define ADSP_PORT_REG_PORT_MUX 0x30 +#define ADSP_PORT_REG_DATA_TGL 0x34 +#define ADSP_PORT_REG_POLAR 0x38 +#define ADSP_PORT_REG_POLAR_SET 0x3c +#define ADSP_PORT_REG_POLAR_CLEAR 0x40 +#define ADSP_PORT_REG_LOCK 0x44 +#define ADSP_PORT_REG_TRIG_TGL 0x48 + +struct adsp_pinctrl_priv { + void __iomem *base; + int npins; + char pinbuf[16]; +}; + +static u32 get_port(unsigned int pin) +{ + return pin / ADSP_PORT_PIN_SIZE; +} + +static u32 get_offset(unsigned int pin) +{ + return pin % ADSP_PORT_PIN_SIZE; +} + +static int adsp_pinctrl_pinmux_set(struct udevice *udev, unsigned int pin, unsigned int func) +{ + struct adsp_pinctrl_priv *priv = dev_get_priv(udev); + void __iomem *portbase; + u32 port, offset; + u32 val; + + if (pin >= priv->npins) + return -ENODEV; + + if (func >= ADSP_PINCTRL_FUNCTION_COUNT) + return -EINVAL; + + port = get_port(pin); + offset = get_offset(pin); + portbase = priv->base + port * ADSP_PORT_MMIO_SIZE; + + val = ioread32(portbase + ADSP_PORT_REG_PORT_MUX); + val &= ~(ADSP_PORT_PORT_MUX_MASK << (ADSP_PORT_PORT_MUX_BITS * offset)); + val |= func << (ADSP_PORT_PORT_MUX_BITS * offset); + iowrite32(val, portbase + ADSP_PORT_REG_PORT_MUX); + + iowrite32(BIT(offset), portbase + ADSP_PORT_REG_FER_SET); + return 0; +} + +static int adsp_pinctrl_set_state(struct udevice *udev, struct udevice *config) +{ + const struct fdt_property *pinlist; + int length = 0; + int ret, i; + u32 pin, function; + + pinlist = dev_read_prop(config, "adi,pins", &length); + if (!pinlist) { + dev_err(udev, "missing adi,pins property in pinctrl config node\n"); + return -EINVAL; + } + + if (length % (sizeof(uint32_t) * 2)) { + dev_err(udev, "adi,pins property must be a multiple of two uint32_ts\n"); + return -EINVAL; + } + + for (i = 0; i < length / sizeof(uint32_t); i += 2) { + ret = dev_read_u32_index(config, "adi,pins", i, &pin); + if (ret) + return ret; + + ret = dev_read_u32_index(config, "adi,pins", i + 1, &function); + if (ret) + return ret; + + ret = adsp_pinctrl_pinmux_set(udev, pin, function); + if (ret) + return ret; + } + + return 0; +} + +const struct pinctrl_ops adsp_pinctrl_ops = { + .set_state = adsp_pinctrl_set_state, +}; + +static int adsp_pinctrl_probe(struct udevice *udev) +{ + struct adsp_pinctrl_priv *priv = dev_get_priv(udev); + + priv->base = dev_read_addr_ptr(udev); + priv->npins = dev_read_u32_default(udev, "adi,npins", 0); + + if (!priv->base) { + dev_err(udev, "Missing or invalid pinctrl base address\n"); + return -ENOENT; + } + + if (!priv->npins) { + dev_err(udev, "Missing adi,npins property!\n"); + return -ENOENT; + } + + return 0; +} + +static const struct udevice_id adsp_pinctrl_match[] = { + { .compatible = "adi,adsp-pinctrl" }, + { }, +}; + +U_BOOT_DRIVER(adi_adsp_pinctrl) = { + .name = "adi_adsp_pinctrl", + .id = UCLASS_PINCTRL, + .of_match = adsp_pinctrl_match, + .probe = adsp_pinctrl_probe, + .priv_auto = sizeof(struct adsp_pinctrl_priv), + .ops = &adsp_pinctrl_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 5038cb535e3..2938635ed95 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -9,6 +9,8 @@ #include <dm.h> #include <log.h> #include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/lists.h> #include <dm/pinctrl.h> #include <asm/hardware.h> #include <linux/bitops.h> @@ -492,21 +494,58 @@ const struct pinctrl_ops at91_pinctrl_ops = { .set_state = at91_pinctrl_set_state, }; +/** + * at91_pinctrl_bind() - Iterates through all subnodes of the pinctrl device + * in the DT and binds them to U-Boot's device model. Each subnode + * typically represents a GPIO controller or pin configuration data. + * + * @dev: Pointer to the pinctrl device + * + * Returns 0 on success or negative error on failure + */ +static int at91_pinctrl_bind(struct udevice *dev) +{ + ofnode gpio_node; + struct udevice *gpio; + int ret; + + ofnode_for_each_subnode(gpio_node, dev_ofnode(dev)) { + ret = lists_bind_fdt(dev, gpio_node, &gpio, NULL, false); + if (ret) + return ret; + } + + return 0; +} + static int at91_pinctrl_probe(struct udevice *dev) { struct at91_pinctrl_priv *priv = dev_get_priv(dev); fdt_addr_t addr_base; + struct udevice *gpio_node; int index; - for (index = 0; index < MAX_GPIO_BANKS; index++) { - addr_base = devfdt_get_addr_index(dev, index); - if (addr_base == FDT_ADDR_T_NONE) - break; + if (list_empty(&dev->child_head)) { + for (index = 0; index < MAX_GPIO_BANKS; index++) { + addr_base = devfdt_get_addr_index(dev, index); + if (addr_base == FDT_ADDR_T_NONE) + break; - priv->reg_base[index] = (struct at91_port *)addr_base; + priv->reg_base[index] = (struct at91_port *)addr_base; + } + } else { + index = 0; + list_for_each_entry(gpio_node, &dev->child_head, sibling_node) { + addr_base = dev_read_addr(gpio_node); + if (addr_base == FDT_ADDR_T_NONE) + break; + + priv->reg_base[index] = (struct at91_port *)addr_base; + index++; + } } - priv->nbanks = index; + priv->nbanks = index < MAX_GPIO_BANKS ? index : MAX_GPIO_BANKS; return 0; } @@ -524,6 +563,7 @@ U_BOOT_DRIVER(atmel_sama5d3_pinctrl) = { .id = UCLASS_PINCTRL, .of_match = at91_pinctrl_match, .probe = at91_pinctrl_probe, + .bind = at91_pinctrl_bind, .priv_auto = sizeof(struct at91_pinctrl_priv), .ops = &at91_pinctrl_ops, }; diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig index d3eb6998551..f4a3942ee2f 100644 --- a/drivers/pinctrl/qcom/Kconfig +++ b/drivers/pinctrl/qcom/Kconfig @@ -7,84 +7,98 @@ config PINCTRL_QCOM menu "Qualcomm pinctrl drivers" config PINCTRL_QCOM_APQ8016 - bool "Qualcomm APQ8016 GCC" + bool "Qualcomm APQ8016 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the MSM8916 / APQ8016 Snapdragon 410 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_APQ8096 - bool "Qualcomm APQ8096 GCC" + bool "Qualcomm APQ8096 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the MSM8996 / APQ8096 Snapdragon 820 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_IPQ4019 - bool "Qualcomm IPQ4019 GCC" + bool "Qualcomm IPQ4019 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the IPQ4019 SoC, as well as the associated GPIO driver. +config PINCTRL_QCOM_IPQ9574 + bool "Qualcomm IPQ9574 Pinctrl" + select PINCTRL_QCOM + help + Say Y here to enable support for pinctrl on the IPQ9574 SoC, + as well as the associated GPIO driver. + config PINCTRL_QCOM_QCM2290 - bool "Qualcomm QCM2290 GCC" + bool "Qualcomm QCM2290 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon QCM2290 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_QCS404 - bool "Qualcomm QCS404 GCC" + bool "Qualcomm QCS404 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon QCS404 SoC, as well as the associated GPIO driver. +config PINCTRL_QCOM_SC7280 + bool "Qualcomm SC7280/QCM6490 Pinctrl" + select PINCTRL_QCOM + help + Say Y here to enable support for pinctrl on the Snapdragon SC7280 SoC, + as well as the associated GPIO driver. + config PINCTRL_QCOM_SDM845 - bool "Qualcomm SDM845 GCC" + bool "Qualcomm SDM845 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon 845 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_SM6115 - bool "Qualcomm SM6115 GCC" + bool "Qualcomm SM6115 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon SM6115 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_SM8150 - bool "Qualcomm SM8150 GCC" + bool "Qualcomm SM8150 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon SM8150 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_SM8250 - bool "Qualcomm SM8250 GCC" + bool "Qualcomm SM8250 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon SM8250 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_SM8550 - bool "Qualcomm SM8550 GCC" + bool "Qualcomm SM8550 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon SM8550 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_SM8650 - bool "Qualcomm SM8650 GCC" + bool "Qualcomm SM8650 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon SM8650 SoC, as well as the associated GPIO driver. config PINCTRL_QCOM_X1E80100 - bool "Qualcomm X1E80100 GCC" + bool "Qualcomm X1E80100 Pinctrl" select PINCTRL_QCOM help Say Y here to enable support for pinctrl on the Snapdragon X1E80100 SoC, diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile index 06d3c95f93a..94cdc6e4a62 100644 --- a/drivers/pinctrl/qcom/Makefile +++ b/drivers/pinctrl/qcom/Makefile @@ -5,9 +5,11 @@ obj-$(CONFIG_PINCTRL_QCOM) += pinctrl-qcom.o obj-$(CONFIG_PINCTRL_QCOM_APQ8016) += pinctrl-apq8016.o obj-$(CONFIG_PINCTRL_QCOM_IPQ4019) += pinctrl-ipq4019.o +obj-$(CONFIG_PINCTRL_QCOM_IPQ9574) += pinctrl-ipq9574.o obj-$(CONFIG_PINCTRL_QCOM_APQ8096) += pinctrl-apq8096.o obj-$(CONFIG_PINCTRL_QCOM_QCM2290) += pinctrl-qcm2290.o obj-$(CONFIG_PINCTRL_QCOM_QCS404) += pinctrl-qcs404.o +obj-$(CONFIG_PINCTRL_QCOM_SC7280) += pinctrl-sc7280.o obj-$(CONFIG_PINCTRL_QCOM_SDM845) += pinctrl-sdm845.o obj-$(CONFIG_PINCTRL_QCOM_SM6115) += pinctrl-sm6115.o obj-$(CONFIG_PINCTRL_QCOM_SM8150) += pinctrl-sm8150.o diff --git a/drivers/pinctrl/qcom/pinctrl-apq8016.c b/drivers/pinctrl/qcom/pinctrl-apq8016.c index 0c7437822ff..9ae07d43d93 100644 --- a/drivers/pinctrl/qcom/pinctrl-apq8016.c +++ b/drivers/pinctrl/qcom/pinctrl-apq8016.c @@ -50,8 +50,8 @@ static const char *apq8016_get_pin_name(struct udevice *dev, } } -static unsigned int apq8016_get_function_mux(__maybe_unused unsigned int pin, - unsigned int selector) +static int apq8016_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-apq8096.c b/drivers/pinctrl/qcom/pinctrl-apq8096.c index 132ece868bf..eaa927c6e22 100644 --- a/drivers/pinctrl/qcom/pinctrl-apq8096.c +++ b/drivers/pinctrl/qcom/pinctrl-apq8096.c @@ -43,8 +43,8 @@ static const char *apq8096_get_pin_name(struct udevice *dev, } } -static unsigned int apq8096_get_function_mux(__maybe_unused unsigned int pin, - unsigned int selector) +static int apq8096_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-ipq4019.c b/drivers/pinctrl/qcom/pinctrl-ipq4019.c index 3215c677b26..dafcd494df4 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq4019.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq4019.c @@ -311,8 +311,7 @@ static const char *ipq4019_get_pin_name(struct udevice *dev, return pin_name; } -static unsigned int ipq4019_get_function_mux(unsigned int pin, - unsigned int selector) +static int ipq4019_get_function_mux(unsigned int pin, unsigned int selector) { unsigned int i; const msm_pin_function *func = ipq4019_pin_functions + pin; diff --git a/drivers/pinctrl/qcom/pinctrl-ipq9574.c b/drivers/pinctrl/qcom/pinctrl-ipq9574.c new file mode 100644 index 00000000000..ce09072ae3c --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-ipq9574.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * pinctrl driver for Qualcomm ipq9574 + * + * (C) Copyright 2025 Linaro Ltd. + */ + +#include <dm.h> + +#include "pinctrl-qcom.h" + +#define MAX_PIN_NAME_LEN 32 +static char pin_name[MAX_PIN_NAME_LEN] __section(".data"); + +enum ipq9574_functions { + msm_mux_blsp0_spi, + msm_mux_blsp0_uart, + msm_mux_blsp1_i2c, + msm_mux_blsp1_spi, + msm_mux_blsp1_uart, + msm_mux_blsp2_i2c, + msm_mux_blsp2_spi, + msm_mux_blsp2_uart, + msm_mux_blsp3_i2c, + msm_mux_blsp3_spi, + msm_mux_blsp3_uart, + msm_mux_blsp4_i2c, + msm_mux_blsp4_spi, + msm_mux_blsp4_uart, + msm_mux_blsp5_i2c, + msm_mux_blsp5_uart, + msm_mux_gpio, + msm_mux_mdc, + msm_mux_mdio, + msm_mux_pcie0_clk, + msm_mux_pcie0_wake, + msm_mux_pcie1_clk, + msm_mux_pcie1_wake, + msm_mux_pcie2_clk, + msm_mux_pcie2_wake, + msm_mux_pcie3_clk, + msm_mux_pcie3_wake, + msm_mux_qspi_data, + msm_mux_qspi_clk, + msm_mux_qspi_cs, + msm_mux_sdc_data, + msm_mux_sdc_clk, + msm_mux_sdc_cmd, + msm_mux_sdc_rclk, + msm_mux_NA, +}; + +#define MSM_PIN_FUNCTION(fname) \ + [msm_mux_##fname] = {#fname, msm_mux_##fname} + +static const struct pinctrl_function msm_pinctrl_functions[] = { + MSM_PIN_FUNCTION(blsp0_spi), + MSM_PIN_FUNCTION(blsp0_uart), + MSM_PIN_FUNCTION(blsp1_i2c), + MSM_PIN_FUNCTION(blsp1_spi), + MSM_PIN_FUNCTION(blsp1_uart), + MSM_PIN_FUNCTION(blsp2_i2c), + MSM_PIN_FUNCTION(blsp2_spi), + MSM_PIN_FUNCTION(blsp2_uart), + MSM_PIN_FUNCTION(blsp3_i2c), + MSM_PIN_FUNCTION(blsp3_spi), + MSM_PIN_FUNCTION(blsp3_uart), + MSM_PIN_FUNCTION(blsp4_i2c), + MSM_PIN_FUNCTION(blsp4_spi), + MSM_PIN_FUNCTION(blsp4_uart), + MSM_PIN_FUNCTION(blsp5_i2c), + MSM_PIN_FUNCTION(blsp5_uart), + MSM_PIN_FUNCTION(gpio), + MSM_PIN_FUNCTION(mdc), + MSM_PIN_FUNCTION(mdio), + MSM_PIN_FUNCTION(pcie0_clk), + MSM_PIN_FUNCTION(pcie0_wake), + MSM_PIN_FUNCTION(pcie1_clk), + MSM_PIN_FUNCTION(pcie1_wake), + MSM_PIN_FUNCTION(pcie2_clk), + MSM_PIN_FUNCTION(pcie2_wake), + MSM_PIN_FUNCTION(pcie3_clk), + MSM_PIN_FUNCTION(pcie3_wake), + MSM_PIN_FUNCTION(qspi_data), + MSM_PIN_FUNCTION(qspi_clk), + MSM_PIN_FUNCTION(qspi_cs), + MSM_PIN_FUNCTION(sdc_data), + MSM_PIN_FUNCTION(sdc_clk), + MSM_PIN_FUNCTION(sdc_cmd), + MSM_PIN_FUNCTION(sdc_rclk), +}; + +typedef unsigned int msm_pin_function[10]; + +#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9) \ + [id] = { msm_mux_gpio, /* gpio mode */ \ + msm_mux_##f1, \ + msm_mux_##f2, \ + msm_mux_##f3, \ + msm_mux_##f4, \ + msm_mux_##f5, \ + msm_mux_##f6, \ + msm_mux_##f7, \ + msm_mux_##f8, \ + msm_mux_##f9, \ + } + +static const msm_pin_function ipq9574_pin_functions[] = { + PINGROUP(0, sdc_data, qspi_data, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(1, sdc_data, qspi_data, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(2, sdc_data, qspi_data, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(3, sdc_data, qspi_data, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(4, sdc_cmd, qspi_cs, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(5, sdc_clk, qspi_clk, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(6, sdc_data, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(7, sdc_data, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(8, sdc_data, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(9, sdc_data, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(10, sdc_rclk, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(11, blsp0_spi, blsp0_uart, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(12, blsp0_spi, blsp0_uart, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(13, blsp0_spi, blsp0_uart, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(14, blsp0_spi, blsp0_uart, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(15, blsp3_spi, blsp3_i2c, blsp3_uart, NA, NA, NA, NA, NA, NA), + PINGROUP(16, blsp3_spi, blsp3_i2c, blsp3_uart, NA, NA, NA, NA, NA, NA), + PINGROUP(17, blsp3_spi, blsp3_uart, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(18, blsp3_spi, blsp3_uart, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(19, blsp3_spi, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(20, blsp3_spi, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(21, blsp3_spi, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(22, pcie0_clk, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(23, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(24, pcie0_wake, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(25, pcie1_clk, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(26, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(27, pcie1_wake, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(28, pcie2_clk, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(29, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(30, pcie2_wake, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(31, pcie3_clk, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(32, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(33, pcie3_wake, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(34, blsp2_uart, blsp2_i2c, blsp2_spi, blsp1_uart, NA, NA, NA, NA, NA), + PINGROUP(35, blsp2_uart, blsp2_i2c, blsp2_spi, blsp1_uart, NA, NA, NA, NA, NA), + PINGROUP(36, blsp1_uart, blsp1_i2c, blsp2_spi, NA, NA, NA, NA, NA, NA), + PINGROUP(37, blsp1_uart, blsp1_i2c, blsp2_spi, NA, NA, NA, NA, NA, NA), + PINGROUP(38, mdc, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(39, mdio, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(40, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(41, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(42, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(43, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(44, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(45, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(46, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(47, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(48, blsp5_i2c, blsp5_uart, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(49, blsp5_i2c, blsp5_uart, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(50, blsp4_uart, blsp4_i2c, blsp4_spi, NA, NA, NA, NA, NA, NA), + PINGROUP(51, blsp4_uart, blsp4_i2c, blsp4_spi, NA, NA, NA, NA, NA, NA), + PINGROUP(52, blsp4_uart, blsp4_spi, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(53, blsp4_uart, blsp4_spi, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(54, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(55, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(56, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(57, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(58, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(59, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(60, NA, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(61, blsp1_spi, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(62, blsp1_spi, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(63, blsp1_spi, NA, NA, NA, NA, NA, NA, NA, NA), + PINGROUP(64, blsp1_spi, NA, NA, NA, NA, NA, NA, NA, NA), +}; + +static const char *ipq9574_get_function_name(struct udevice *dev, + unsigned int selector) +{ + return msm_pinctrl_functions[selector].name; +} + +static const char *ipq9574_get_pin_name(struct udevice *dev, + unsigned int selector) +{ + snprintf(pin_name, MAX_PIN_NAME_LEN, "gpio%u", selector); + return pin_name; +} + +static int ipq9574_get_function_mux(unsigned int pin, unsigned int selector) +{ + unsigned int i; + const msm_pin_function *func = ipq9574_pin_functions + pin; + + for (i = 0; i < 10; i++) + if ((*func)[i] == selector) + return i; + + debug("Can't find requested function for pin:selector %u:%u\n", + pin, selector); + + return -EINVAL; +} + +static const struct msm_pinctrl_data ipq9574_data = { + .pin_data = { + .pin_count = 65, + }, + .functions_count = ARRAY_SIZE(msm_pinctrl_functions), + .get_function_name = ipq9574_get_function_name, + .get_function_mux = ipq9574_get_function_mux, + .get_pin_name = ipq9574_get_pin_name, +}; + +static const struct udevice_id msm_pinctrl_ids[] = { + { .compatible = "qcom,ipq9574-tlmm", .data = (ulong)&ipq9574_data }, + { /* Sentinal */ } +}; + +U_BOOT_DRIVER(pinctrl_ipq9574) = { + .name = "pinctrl_ipq9574", + .id = UCLASS_NOP, + .of_match = msm_pinctrl_ids, + .ops = &msm_pinctrl_ops, + .bind = msm_pinctrl_bind, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/pinctrl/qcom/pinctrl-qcm2290.c b/drivers/pinctrl/qcom/pinctrl-qcm2290.c index af969e177d7..0c2222ce663 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcm2290.c +++ b/drivers/pinctrl/qcom/pinctrl-qcm2290.c @@ -38,7 +38,7 @@ static const char *qcm2290_get_pin_name(struct udevice *dev, unsigned int select return pin_name; } -static unsigned int qcm2290_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector) +static int qcm2290_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-qcom.c b/drivers/pinctrl/qcom/pinctrl-qcom.c index 26a3fba194a..24d031947a3 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcom.c +++ b/drivers/pinctrl/qcom/pinctrl-qcom.c @@ -92,7 +92,10 @@ static int msm_pinmux_set(struct udevice *dev, unsigned int pin_selector, unsigned int func_selector) { struct msm_pinctrl_priv *priv = dev_get_priv(dev); - u32 func = priv->data->get_function_mux(pin_selector, func_selector); + int func = priv->data->get_function_mux(pin_selector, func_selector); + + if (func < 0) + return func; /* Always NOP for special pins, assume they're in the correct state */ if (qcom_is_special_pin(&priv->data->pin_data, pin_selector)) diff --git a/drivers/pinctrl/qcom/pinctrl-qcom.h b/drivers/pinctrl/qcom/pinctrl-qcom.h index 49b7bfbc001..cd167e63868 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcom.h +++ b/drivers/pinctrl/qcom/pinctrl-qcom.h @@ -18,8 +18,7 @@ struct msm_pinctrl_data { int functions_count; const char *(*get_function_name)(struct udevice *dev, unsigned int selector); - unsigned int (*get_function_mux)(unsigned int pin, - unsigned int selector); + int (*get_function_mux)(unsigned int pin, unsigned int selector); const char *(*get_pin_name)(struct udevice *dev, unsigned int selector); }; diff --git a/drivers/pinctrl/qcom/pinctrl-qcs404.c b/drivers/pinctrl/qcom/pinctrl-qcs404.c index fb6defaeddf..c272595aa2a 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcs404.c +++ b/drivers/pinctrl/qcom/pinctrl-qcs404.c @@ -93,8 +93,8 @@ static const char *qcs404_get_pin_name(struct udevice *dev, } } -static unsigned int qcs404_get_function_mux(__maybe_unused unsigned int pin, - unsigned int selector) +static int qcs404_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-sc7280.c b/drivers/pinctrl/qcom/pinctrl-sc7280.c new file mode 100644 index 00000000000..fe87947fbbf --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-sc7280.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Qualcomm sc7280 pinctrl + * + * (C) Copyright 2024 Linaro Ltd. + * + */ + +#include <dm.h> + +#include "pinctrl-qcom.h" + +#define WEST 0x00000000 +#define SOUTH 0x00400000 +#define NORTH 0x00800000 + +#define MAX_PIN_NAME_LEN 32 +static char pin_name[MAX_PIN_NAME_LEN] __section(".data"); + +static const struct pinctrl_function msm_pinctrl_functions[] = { + { "qup05", 1 }, + { "gpio", 0 }, + { "pcie1_clkreqn", 3}, +}; +#define SDC_PINGROUP(pg_name, ctl, pull, drv) \ + { \ + .name = pg_name, \ + .ctl_reg = ctl, \ + .io_reg = 0, \ + .pull_bit = pull, \ + .drv_bit = drv, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = -1, \ + } + +#define UFS_RESET(pg_name, offset) \ + { \ + .name = pg_name, \ + .ctl_reg = offset, \ + .io_reg = offset + 0x4, \ + .pull_bit = 3, \ + .drv_bit = 0, \ + .oe_bit = -1, \ + .in_bit = -1, \ + .out_bit = 0, \ + } + +static const struct msm_special_pin_data sc7280_special_pins_data[] = { + [0] = UFS_RESET("ufs_reset", SOUTH + 0xbe000), + [1] = SDC_PINGROUP("sdc1_rclk", 0xb3004, 0, 6), + [2] = SDC_PINGROUP("sdc1_clk", 0xb3000, 13, 6), + [3] = SDC_PINGROUP("sdc1_cmd", 0xb3000, 11, 3), + [4] = SDC_PINGROUP("sdc1_data", 0xb3000, 9, 0), + [5] = SDC_PINGROUP("sdc2_clk", 0xb4000, 14, 6), + [6] = SDC_PINGROUP("sdc2_cmd", 0xb4000, 11, 3), + [7] = SDC_PINGROUP("sdc2_data", 0xb4000, 9, 0), +}; + +static const char *sc7280_get_function_name(struct udevice *dev, unsigned int selector) +{ + return msm_pinctrl_functions[selector].name; +} + +static const char *sc7280_get_pin_name(struct udevice *dev, unsigned int selector) +{ + if (selector >= 175 && selector <= 182) + snprintf(pin_name, MAX_PIN_NAME_LEN, + sc7280_special_pins_data[selector - 175].name); + else + snprintf(pin_name, MAX_PIN_NAME_LEN, "gpio%u", selector); + + return pin_name; +} + +static int sc7280_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector) +{ + return msm_pinctrl_functions[selector].val; +} + +static struct msm_pinctrl_data sc7280_data = { + .pin_data = { + .pin_count = 183, + .special_pins_start = 175, + .special_pins_data = sc7280_special_pins_data, + }, + .functions_count = ARRAY_SIZE(msm_pinctrl_functions), + .get_function_name = sc7280_get_function_name, + .get_function_mux = sc7280_get_function_mux, + .get_pin_name = sc7280_get_pin_name, +}; + +static const struct udevice_id msm_pinctrl_ids[] = { + { + .compatible = "qcom,sc7280-pinctrl", + .data = (ulong)&sc7280_data + }, + { /* Sentinel */ } }; + +U_BOOT_DRIVER(pinctrl_sc7280) = { + .name = "pinctrl_sc7280", + .id = UCLASS_NOP, + .of_match = msm_pinctrl_ids, + .ops = &msm_pinctrl_ops, + .bind = msm_pinctrl_bind, +}; diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845.c b/drivers/pinctrl/qcom/pinctrl-sdm845.c index f1a23f51099..3f55fc81c8e 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm845.c +++ b/drivers/pinctrl/qcom/pinctrl-sdm845.c @@ -80,8 +80,8 @@ static const char *sdm845_get_pin_name(struct udevice *dev, return pin_name; } -static unsigned int sdm845_get_function_mux(__maybe_unused unsigned int pin, - unsigned int selector) +static int sdm845_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-sm6115.c b/drivers/pinctrl/qcom/pinctrl-sm6115.c index f07f39f4ac3..7e80ea39a12 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm6115.c +++ b/drivers/pinctrl/qcom/pinctrl-sm6115.c @@ -167,7 +167,7 @@ static const char *sm6115_get_pin_name(struct udevice *dev, unsigned int selecto return pin_name; } -static unsigned int sm6115_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector) +static int sm6115_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-sm8150.c b/drivers/pinctrl/qcom/pinctrl-sm8150.c index 1fb2ffb9597..fe789e8639b 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8150.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8150.c @@ -123,8 +123,8 @@ static const char *sm8150_get_pin_name(struct udevice *dev, return pin_name; } -static unsigned int sm8150_get_function_mux(__maybe_unused unsigned int pin, - unsigned int selector) +static int sm8150_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-sm8250.c b/drivers/pinctrl/qcom/pinctrl-sm8250.c index b21cdc4d24b..d5447651695 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8250.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8250.c @@ -99,7 +99,7 @@ static const char *sm8250_get_pin_name(struct udevice *dev, unsigned int selecto return pin_name; } -static unsigned int sm8250_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector) +static int sm8250_get_function_mux(__maybe_unused unsigned int pin, unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-sm8550.c b/drivers/pinctrl/qcom/pinctrl-sm8550.c index 25b972a6d82..f7fcad0e4aa 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8550.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8550.c @@ -68,8 +68,8 @@ static const char *sm8550_get_pin_name(struct udevice *dev, return pin_name; } -static unsigned int sm8550_get_function_mux(__maybe_unused unsigned int pin, - unsigned int selector) +static int sm8550_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-sm8650.c b/drivers/pinctrl/qcom/pinctrl-sm8650.c index 9146d6abd9a..6b9d56b1058 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8650.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8650.c @@ -69,8 +69,8 @@ static const char *sm8650_get_pin_name(struct udevice *dev, return pin_name; } -static unsigned int sm8650_get_function_mux(__maybe_unused unsigned int pin, - unsigned int selector) +static int sm8650_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/qcom/pinctrl-x1e80100.c b/drivers/pinctrl/qcom/pinctrl-x1e80100.c index f39dc426d68..319667bfc16 100644 --- a/drivers/pinctrl/qcom/pinctrl-x1e80100.c +++ b/drivers/pinctrl/qcom/pinctrl-x1e80100.c @@ -72,8 +72,8 @@ static const char *x1e80100_get_pin_name(struct udevice *dev, return pin_name; } -static unsigned int x1e80100_get_function_mux(__maybe_unused unsigned int pin, - unsigned int selector) +static int x1e80100_get_function_mux(__maybe_unused unsigned int pin, + unsigned int selector) { return msm_pinctrl_functions[selector].val; } diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig index cbd61795986..65e8192a99a 100644 --- a/drivers/pinctrl/sunxi/Kconfig +++ b/drivers/pinctrl/sunxi/Kconfig @@ -124,6 +124,16 @@ config PINCTRL_SUN50I_H616_R default MACH_SUN50I_H616 select PINCTRL_SUNXI +config PINCTRL_SUN50I_A100 + bool "Support for the Allwinner A100/A133 PIO" + default MACH_SUN50I_A133 + select PINCTRL_SUNXI + +config PINCTRL_SUN50I_A100_R + bool "Support for the Allwinner A100/A133 R-PIO" + default MACH_SUN50I_A133 + select PINCTRL_SUNXI + config PINCTRL_SUN20I_D1 bool "Support for the Allwinner D1/R528 PIO" default MACH_SUN8I_R528 diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 37ea93715d1..c38edf7d4f5 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -774,6 +774,41 @@ static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h616_r_pinctrl_desc .num_banks = 1, }; +static const struct sunxi_pinctrl_function sun50i_a100_pinctrl_functions[] = { + { "emac0", 5 }, /* PH0-PH16 */ + { "gpio_in", 0 }, + { "gpio_out", 1 }, + { "mmc0", 2 }, /* PF0-PF5 */ + { "mmc1", 2 }, /* PG0-PG5 */ + { "mmc2", 3 }, /* PC0-PC16 */ + { "spi0", 4 }, /* PC2-PC4, PC7, PC12, PC15-PC16 */ +#if IS_ENABLED(CONFIG_UART0_PORT_F) + { "uart0", 3 }, /* PF2-PF4 */ +#else + { "uart0", 2 }, /* PB9-PB10 */ +#endif +}; + +static const struct sunxi_pinctrl_desc __maybe_unused sun50i_a100_pinctrl_desc = { + .functions = sun50i_a100_pinctrl_functions, + .num_functions = ARRAY_SIZE(sun50i_a100_pinctrl_functions), + .first_bank = SUNXI_GPIO_A, + .num_banks = 8, +}; + +static const struct sunxi_pinctrl_function sun50i_a100_r_pinctrl_functions[] = { + { "gpio_in", 0 }, + { "gpio_out", 1 }, + { "s_i2c0", 2 }, +}; + +static const struct sunxi_pinctrl_desc __maybe_unused sun50i_a100_r_pinctrl_desc = { + .functions = sun50i_a100_r_pinctrl_functions, + .num_functions = ARRAY_SIZE(sun50i_a100_r_pinctrl_functions), + .first_bank = SUNXI_GPIO_L, + .num_banks = 1, +}; + static const struct udevice_id sunxi_pinctrl_ids[] = { #ifdef CONFIG_PINCTRL_SUNIV_F1C100S { @@ -937,6 +972,18 @@ static const struct udevice_id sunxi_pinctrl_ids[] = { .data = (ulong)&sun50i_h616_r_pinctrl_desc, }, #endif +#ifdef CONFIG_PINCTRL_SUN50I_A100 + { + .compatible = "allwinner,sun50i-a100-pinctrl", + .data = (ulong)&sun50i_a100_pinctrl_desc, + }, +#endif +#ifdef CONFIG_PINCTRL_SUN50I_A100_R + { + .compatible = "allwinner,sun50i-a100-r-pinctrl", + .data = (ulong)&sun50i_a100_r_pinctrl_desc, + }, +#endif {} }; diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c index ad7112a05e6..b04be168bc8 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra.c @@ -23,18 +23,18 @@ static void tegra_pinctrl_set_drive(struct udevice *config, int drvcnt) return; } - drive_group[0].slwf = dev_read_u32_default(config, "nvidia,slew-rate-falling", 0); - drive_group[0].slwr = dev_read_u32_default(config, "nvidia,slew-rate-rising", 0); - drive_group[0].drvup = dev_read_u32_default(config, "nvidia,pull-up-strength", 0); - drive_group[0].drvdn = dev_read_u32_default(config, "nvidia,pull-down-strength", 0); + drive_group[0].slwf = dev_read_u32_default(config, "nvidia,slew-rate-falling", PMUX_SLWF_NONE); + drive_group[0].slwr = dev_read_u32_default(config, "nvidia,slew-rate-rising", PMUX_SLWR_NONE); + drive_group[0].drvup = dev_read_u32_default(config, "nvidia,pull-up-strength", PMUX_DRVUP_NONE); + drive_group[0].drvdn = dev_read_u32_default(config, "nvidia,pull-down-strength", PMUX_DRVDN_NONE); #ifdef TEGRA_PMX_GRPS_HAVE_LPMD - drive_group[0].lpmd = dev_read_u32_default(config, "nvidia,low-power-mode", 0); + drive_group[0].lpmd = dev_read_u32_default(config, "nvidia,low-power-mode", PMUX_LPMD_NONE); #endif #ifdef TEGRA_PMX_GRPS_HAVE_SCHMT - drive_group[0].schmt = dev_read_u32_default(config, "nvidia,schmitt", 0); + drive_group[0].schmt = dev_read_u32_default(config, "nvidia,schmitt", PMUX_SCHMT_NONE); #endif #ifdef TEGRA_PMX_GRPS_HAVE_HSM - drive_group[0].hsm = dev_read_u32_default(config, "nvidia,high-speed-mode", 0); + drive_group[0].hsm = dev_read_u32_default(config, "nvidia,high-speed-mode", PMUX_HSM_NONE); #endif for (i = 1; i < drvcnt; i++) @@ -67,6 +67,58 @@ exit: kfree(drive_group); } +#ifdef TEGRA_PMX_SOC_HAS_MIPI_PAD_CTRL_GRPS +static void tegra_pinctrl_set_mipipad(struct udevice *config, int padcnt) +{ + struct pmux_mipipadctrlgrp_config *mipipad_group; + int i, ret, pad_id; + const char *function; + const char **pads; + + mipipad_group = kmalloc_array(padcnt, sizeof(*mipipad_group), GFP_KERNEL); + if (!mipipad_group) { + log_debug("%s: cannot allocate mipi pad group array\n", __func__); + return; + } + + /* decode function id and fill the first copy of pmux_mipipadctrlgrp_config */ + function = dev_read_string(config, "nvidia,function"); + if (function) + for (i = 0; i < PMUX_FUNC_COUNT; i++) + if (tegra_pinctrl_to_func[i]) + if (!strcmp(function, tegra_pinctrl_to_func[i])) + break; + + mipipad_group[0].func = i; + + for (i = 1; i < padcnt; i++) + memcpy(&mipipad_group[i], &mipipad_group[0], sizeof(mipipad_group[0])); + + ret = dev_read_string_list(config, "nvidia,pins", &pads); + if (ret < 0) { + log_debug("%s: could not parse property nvidia,pins\n", __func__); + goto exit; + } + + for (i = 0; i < padcnt; i++) { + for (pad_id = 0; pad_id < PMUX_MIPIPADCTRLGRP_COUNT; pad_id++) + if (tegra_pinctrl_to_mipipadgrp[pad_id]) + if (!strcmp(pads[i], tegra_pinctrl_to_mipipadgrp[pad_id])) { + mipipad_group[i].grp = pad_id; + break; + } + } + + pinmux_config_mipipadctrlgrp_table(mipipad_group, padcnt); + + free(pads); +exit: + kfree(mipipad_group); +} +#else +static void tegra_pinctrl_set_mipipad(struct udevice *config, int padcnt) { } +#endif + static void tegra_pinctrl_set_pin(struct udevice *config, int pincnt) { struct pmux_pingrp_config *pinmux_group; @@ -90,31 +142,31 @@ static void tegra_pinctrl_set_pin(struct udevice *config, int pincnt) pinmux_group[0].func = i; - pinmux_group[0].pull = dev_read_u32_default(config, "nvidia,pull", 0); - pinmux_group[0].tristate = dev_read_u32_default(config, "nvidia,tristate", 0); + pinmux_group[0].pull = dev_read_u32_default(config, "nvidia,pull", PMUX_PULL_NORMAL); + pinmux_group[0].tristate = dev_read_u32_default(config, "nvidia,tristate", PMUX_TRI_TRISTATE); #ifdef TEGRA_PMX_PINS_HAVE_E_INPUT - pinmux_group[0].io = dev_read_u32_default(config, "nvidia,enable-input", 0); + pinmux_group[0].io = dev_read_u32_default(config, "nvidia,enable-input", PMUX_PIN_NONE); #endif #ifdef TEGRA_PMX_PINS_HAVE_LOCK - pinmux_group[0].lock = dev_read_u32_default(config, "nvidia,lock", 0); + pinmux_group[0].lock = dev_read_u32_default(config, "nvidia,lock", PMUX_PIN_LOCK_DEFAULT); #endif #ifdef TEGRA_PMX_PINS_HAVE_OD - pinmux_group[0].od = dev_read_u32_default(config, "nvidia,open-drain", 0); + pinmux_group[0].od = dev_read_u32_default(config, "nvidia,open-drain", PMUX_PIN_OD_DEFAULT); #endif #ifdef TEGRA_PMX_PINS_HAVE_IO_RESET - pinmux_group[0].ioreset = dev_read_u32_default(config, "nvidia,io-reset", 0); + pinmux_group[0].ioreset = dev_read_u32_default(config, "nvidia,io-reset", PMUX_PIN_IO_RESET_DEFAULT); #endif #ifdef TEGRA_PMX_PINS_HAVE_RCV_SEL - pinmux_group[0].rcv_sel = dev_read_u32_default(config, "nvidia,rcv-sel", 0); + pinmux_group[0].rcv_sel = dev_read_u32_default(config, "nvidia,rcv-sel", PMUX_PIN_RCV_SEL_DEFAULT); #endif #ifdef TEGRA_PMX_PINS_HAVE_E_IO_HV - pinmux_group[0].e_io_hv = dev_read_u32_default(config, "nvidia,io-hv", 0); + pinmux_group[0].e_io_hv = dev_read_u32_default(config, "nvidia,io-hv", PMUX_PIN_E_IO_HV_DEFAULT); #endif #ifdef TEGRA_PMX_PINS_HAVE_SCHMT - pinmux_group[0].schmt = dev_read_u32_default(config, "nvidia,schmitt", 0); + pinmux_group[0].schmt = dev_read_u32_default(config, "nvidia,schmitt", PMUX_SCHMT_NONE); #endif #ifdef TEGRA_PMX_PINS_HAVE_HSM - pinmux_group[0].hsm = dev_read_u32_default(config, "nvidia,high-speed-mode", 0); + pinmux_group[0].hsm = dev_read_u32_default(config, "nvidia,high-speed-mode", PMUX_HSM_NONE); #endif for (i = 1; i < pincnt; i++) @@ -170,6 +222,9 @@ static int tegra_pinctrl_set_state(struct udevice *dev, struct udevice *config) if (!strncmp(name, "drive_", 6)) /* Drive node is detected */ tegra_pinctrl_set_drive(child, ret); + else if (!strncmp(name, "mipi_pad_ctrl_", 14)) + /* Handle T124 specific pinconfig */ + tegra_pinctrl_set_mipipad(child, ret); else /* Pin node is detected */ tegra_pinctrl_set_pin(child, ret); @@ -236,6 +291,7 @@ static int tegra_pinctrl_bind(struct udevice *dev) static const struct udevice_id tegra_pinctrl_ids[] = { { .compatible = "nvidia,tegra30-pinmux" }, { .compatible = "nvidia,tegra114-pinmux" }, + { .compatible = "nvidia,tegra124-pinmux" }, { }, }; diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 4b81aeb7497..eed65058e66 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -116,6 +116,12 @@ config AXP717_POWER ---help--- Select this to enable support for the AXP717 PMIC found on some boards. +config AXP803_POWER + bool "AXP803 PMIC support" + select AXP_PMIC_BUS + ---help--- + Select this to enable support for the AXP803 PMIC found on some boards. + config AXP809_POWER bool "axp809 pmic support" depends on MACH_SUN9I @@ -142,10 +148,20 @@ config SY8106A_POWER endchoice +config AXP_I2C_ADDRESS + hex "AXP PMIC I2C address" + depends on ARCH_SUNXI && !SUNXI_NO_PMIC + default 0x36 if AXP305_POWER + default 0x36 if AXP313_POWER + default 0x30 if AXP152_POWER + default 0x34 + ---help--- + I2C address of the AXP PMIC, used for the SPL only. + config AXP_DCDC1_VOLT int "axp pmic dcdc1 voltage" - depends on AXP221_POWER || AXP809_POWER || AXP818_POWER - default 3300 if AXP818_POWER || MACH_SUN8I_R40 + depends on AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP803_POWER + default 3300 if AXP818_POWER || MACH_SUN8I_R40 || AXP803_POWER default 3000 if MACH_SUN6I || MACH_SUN8I || MACH_SUN9I ---help--- Set the voltage (mV) to program the axp pmic dcdc1 at, set to 0 to @@ -158,11 +174,12 @@ config AXP_DCDC1_VOLT config AXP_DCDC2_VOLT int "axp pmic dcdc2 voltage" - depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP313_POWER || AXP717_POWER + depends on AXP152_POWER || AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP313_POWER || AXP717_POWER || AXP803_POWER default 900 if AXP818_POWER default 1400 if AXP152_POWER || AXP209_POWER default 1000 if AXP313_POWER default 1000 if AXP717_POWER + default 1000 if AXP803_POWER default 1200 if MACH_SUN6I default 1100 if MACH_SUN8I default 0 if MACH_SUN9I @@ -219,7 +236,7 @@ config AXP_DCDC4_VOLT config AXP_DCDC5_VOLT int "axp pmic dcdc5 voltage" - depends on AXP221_POWER || AXP809_POWER || AXP818_POWER + depends on AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP803_POWER default 1500 if MACH_SUN6I || MACH_SUN8I || MACH_SUN9I ---help--- Set the voltage (mV) to program the axp pmic dcdc5 at, set to 0 to diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 3f4d56f5139..3363191fdc8 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_AXP313_POWER) += axp_spl.o obj-$(CONFIG_AXP717_POWER) += axp_spl.o obj-$(CONFIG_AXP809_POWER) += axp809.o obj-$(CONFIG_AXP818_POWER) += axp818.o +obj-$(CONFIG_AXP803_POWER) += axp_spl.o endif obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o obj-$(CONFIG_SY8106A_POWER) += sy8106a.o diff --git a/drivers/power/axp_spl.c b/drivers/power/axp_spl.c index 3c86eb20ab4..7c51a9b3dfb 100644 --- a/drivers/power/axp_spl.c +++ b/drivers/power/axp_spl.c @@ -36,6 +36,23 @@ static const struct axp_reg_desc_spl axp_spl_dcdc_regulators[] = { #define AXP_SHUTDOWN_REG 0x27 #define AXP_SHUTDOWN_MASK BIT(0) +#elif defined(CONFIG_AXP803_POWER) /* AXP803 */ + +static const struct axp_reg_desc_spl axp_spl_dcdc_regulators[] = { + { 0x10, BIT(0), 0x20, 0x1f, 1600, 3400, 100, NA }, + { 0x10, BIT(1), 0x21, 0x7f, 500, 1300, 10, 70 }, + { 0x10, BIT(2), 0x22, 0x7f, 500, 1300, 10, 70 }, + { 0x10, BIT(3), 0x23, 0x7f, 500, 1300, 10, 70 }, + { 0x10, BIT(4), 0x24, 0x7f, 800, 1840, 10, 32 }, + { 0x10, BIT(5), 0x25, 0x7f, 600, 1520, 10, 50 }, +}; + +#define AXP_CHIP_VERSION 0x3 +#define AXP_CHIP_VERSION_MASK 0xcf +#define AXP_CHIP_ID 0x41 +#define AXP_SHUTDOWN_REG 0x32 +#define AXP_SHUTDOWN_MASK BIT(7) + #elif defined(CONFIG_AXP313_POWER) /* AXP313 */ static const struct axp_reg_desc_spl axp_spl_dcdc_regulators[] = { diff --git a/drivers/power/domain/ti-power-domain.c b/drivers/power/domain/ti-power-domain.c index 5e7a4c5648d..c3519307340 100644 --- a/drivers/power/domain/ti-power-domain.c +++ b/drivers/power/domain/ti-power-domain.c @@ -94,6 +94,8 @@ static const struct soc_attr ti_k3_soc_pd_data[] = { .family = "J721E", .data = &j721e_pd_platdata, }, +#endif +#if IS_ENABLED(CONFIG_SOC_K3_J7200) { .family = "J7200", .data = &j7200_pd_platdata, @@ -116,6 +118,10 @@ static const struct soc_attr ti_k3_soc_pd_data[] = { .family = "J784S4", .data = &j784s4_pd_platdata, }, + { + .family = "J742S2", + .data = &j784s4_pd_platdata, + }, #endif { /* sentinel */ } }; diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 958f337c7e7..8f102a92c23 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -478,3 +478,11 @@ config DM_REGULATOR_TPS65219 features for REGULATOR TPS65219 and the family of TPS65219 PMICs. TPS65219 series of PMICs have 3 single phase BUCKs & 4 LDOs. The driver implements get/set api for value and enable. + +config REGULATOR_RZG2L_USBPHY + bool "Enable driver for RZ/G2L USB PHY VBUS supply" + depends on DM_REGULATOR + help + Enable this option to support controlling the VBUS supply in + the USB PHY peripheral of the Renesas RZ/G2L SoC. This option + is required in order to use the USB OTG port. diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index ca6c89d13b5..4382d4b3ab9 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -42,3 +42,4 @@ obj-$(CONFIG_DM_REGULATOR_TPS65941) += tps65941_regulator.o obj-$(CONFIG_DM_REGULATOR_SCMI) += scmi_regulator.o obj-$(CONFIG_$(XPL_)DM_REGULATOR_ANATOP) += anatop_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS65219) += tps65219_regulator.o +obj-$(CONFIG_REGULATOR_RZG2L_USBPHY) += rzg2l-usbphy-regulator.o diff --git a/drivers/power/regulator/rzg2l-usbphy-regulator.c b/drivers/power/regulator/rzg2l-usbphy-regulator.c new file mode 100644 index 00000000000..451f04c140e --- /dev/null +++ b/drivers/power/regulator/rzg2l-usbphy-regulator.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024 Renesas Electronics Corporation + */ + +#include <asm/io.h> +#include <dm.h> +#include <power/regulator.h> +#include <renesas/rzg2l-usbphy.h> + +#define VBENCTL 0x03c +#define VBENCTL_VBUS_SEL BIT(0) + +static int rzg2l_usbphy_regulator_set_enable(struct udevice *dev, bool enable) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(dev->parent); + + if (enable) + clrbits_le32(priv->regs + VBENCTL, VBENCTL_VBUS_SEL); + else + setbits_le32(priv->regs + VBENCTL, VBENCTL_VBUS_SEL); + + return 0; +} + +static int rzg2l_usbphy_regulator_get_enable(struct udevice *dev) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(dev->parent); + + return !!readl(priv->regs + VBENCTL) & VBENCTL_VBUS_SEL; +} + +static const struct dm_regulator_ops rzg2l_usbphy_regulator_ops = { + .get_enable = rzg2l_usbphy_regulator_get_enable, + .set_enable = rzg2l_usbphy_regulator_set_enable, +}; + +U_BOOT_DRIVER(rzg2l_usbphy_regulator) = { + .name = "rzg2l_usbphy_regulator", + .id = UCLASS_REGULATOR, + .ops = &rzg2l_usbphy_regulator_ops, +}; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 6e79868d0ef..de312656746 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -105,6 +105,14 @@ config PWM_TEGRA 32KHz clock is supported by the driver but the duty cycle is configurable. +config PWM_STM32 + bool "Enable support for STM32 PWM" + depends on DM_PWM && MFD_STM32_TIMERS + help + This enables PWM driver for STMicroelectronics STM32 pulse width + modulation. It uses STM32 timer devices that can have up to 4 output + channels, with complementary outputs and configurable polarity. + config PWM_SUNXI bool "Enable support for the Allwinner Sunxi PWM" depends on DM_PWM diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index e4d10c8dc3e..76305b93bc9 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -22,5 +22,6 @@ obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o +obj-$(CONFIG_PWM_STM32) += pwm-stm32.o obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o obj-$(CONFIG_PWM_TI_EHRPWM) += pwm-ti-ehrpwm.o diff --git a/drivers/pwm/pwm-mtk.c b/drivers/pwm/pwm-mtk.c index 5cf2eba2ba0..898e353b370 100644 --- a/drivers/pwm/pwm-mtk.c +++ b/drivers/pwm/pwm-mtk.c @@ -30,6 +30,7 @@ enum mtk_pwm_reg_ver { PWM_REG_V1, PWM_REG_V2, + PWM_REG_V3, }; static const unsigned int mtk_pwm_reg_offset_v1[] = { @@ -40,6 +41,10 @@ static const unsigned int mtk_pwm_reg_offset_v2[] = { 0x0080, 0x00c0, 0x0100, 0x0140, 0x0180, 0x01c0, 0x0200, 0x0240 }; +static const unsigned int mtk_pwm_reg_offset_v3[] = { + 0x0100, 0x0200, 0x0300, 0x0400, 0x0500, 0x600, 0x700, 0x0800 +}; + struct mtk_pwm_soc { unsigned int num_pwms; bool pwm45_fixup; @@ -60,6 +65,10 @@ static void mtk_pwm_w32(struct udevice *dev, uint channel, uint reg, uint val) u32 offset; switch (priv->soc->reg_ver) { + case PWM_REG_V3: + offset = mtk_pwm_reg_offset_v3[channel]; + break; + case PWM_REG_V2: offset = mtk_pwm_reg_offset_v2[channel]; break; @@ -203,6 +212,12 @@ static const struct mtk_pwm_soc mt7986_data = { .reg_ver = PWM_REG_V1, }; +static const struct mtk_pwm_soc mt7987_data = { + .num_pwms = 3, + .pwm45_fixup = false, + .reg_ver = PWM_REG_V3, +}; + static const struct mtk_pwm_soc mt7988_data = { .num_pwms = 8, .pwm45_fixup = false, @@ -215,6 +230,7 @@ static const struct udevice_id mtk_pwm_ids[] = { { .compatible = "mediatek,mt7629-pwm", .data = (ulong)&mt7629_data }, { .compatible = "mediatek,mt7981-pwm", .data = (ulong)&mt7981_data }, { .compatible = "mediatek,mt7986-pwm", .data = (ulong)&mt7986_data }, + { .compatible = "mediatek,mt7987-pwm", .data = (ulong)&mt7987_data }, { .compatible = "mediatek,mt7988-pwm", .data = (ulong)&mt7988_data }, { } }; diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c new file mode 100644 index 00000000000..5fa649b5903 --- /dev/null +++ b/drivers/pwm/pwm-stm32.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025, STMicroelectronics - All Rights Reserved + * Author: Cheick Traore <cheick.traore@foss.st.com> + * + * Originally based on the Linux kernel v6.10 drivers/pwm/pwm-stm32.c. + */ + +#include <div64.h> +#include <dm.h> +#include <pwm.h> +#include <asm/io.h> +#include <asm/arch/timers.h> +#include <dm/device_compat.h> +#include <linux/time.h> + +#define CCMR_CHANNEL_SHIFT 8 +#define CCMR_CHANNEL_MASK 0xFF + +struct stm32_pwm_priv { + bool have_complementary_output; + bool invert_polarity; +}; + +static u32 active_channels(struct stm32_timers_plat *plat) +{ + return readl(plat->base + TIM_CCER) & TIM_CCER_CCXE; +} + +static int stm32_pwm_set_config(struct udevice *dev, uint channel, + uint period_ns, uint duty_ns) +{ + struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev)); + struct stm32_timers_priv *priv = dev_get_priv(dev_get_parent(dev)); + unsigned long long prd, div, dty; + unsigned int prescaler = 0; + u32 ccmr, mask, shift; + + if (duty_ns > period_ns) + return -EINVAL; + + /* + * Period and prescaler values depends on clock rate + * First we need to find the minimal value for prescaler such that + * + * period_ns * clkrate + * ------------------------------ < max_arr + 1 + * NSEC_PER_SEC * (prescaler + 1) + * + * This equation is equivalent to + * + * period_ns * clkrate + * ---------------------------- < prescaler + 1 + * NSEC_PER_SEC * (max_arr + 1) + * + * Using integer division and knowing that the right hand side is + * integer, this is further equivalent to + * + * (period_ns * clkrate) // (NSEC_PER_SEC * (max_arr + 1)) ≤ prescaler + */ + + div = (unsigned long long)priv->rate * period_ns; + do_div(div, NSEC_PER_SEC); + prd = div; + + do_div(div, priv->max_arr + 1); + prescaler = div; + if (prescaler > MAX_TIM_PSC) + return -EINVAL; + + do_div(prd, prescaler + 1); + if (!prd) + return -EINVAL; + + /* + * All channels share the same prescaler and counter so when two + * channels are active at the same time we can't change them + */ + if (active_channels(plat) & ~(1 << channel * 4)) { + u32 psc, arr; + + psc = readl(plat->base + TIM_PSC); + arr = readl(plat->base + TIM_ARR); + if (psc != prescaler || arr != prd - 1) + return -EBUSY; + } + + writel(prescaler, plat->base + TIM_PSC); + writel(prd - 1, plat->base + TIM_ARR); + setbits_le32(plat->base + TIM_CR1, TIM_CR1_ARPE); + + /* Calculate the duty cycles */ + dty = prd * duty_ns; + do_div(dty, period_ns); + + writel(dty, plat->base + TIM_CCRx(channel + 1)); + + /* Configure output mode */ + shift = (channel & 0x1) * CCMR_CHANNEL_SHIFT; + ccmr = (TIM_CCMR_PE | TIM_CCMR_M1) << shift; + mask = CCMR_CHANNEL_MASK << shift; + if (channel < 2) + clrsetbits_le32(plat->base + TIM_CCMR1, mask, ccmr); + else + clrsetbits_le32(plat->base + TIM_CCMR2, mask, ccmr); + + setbits_le32(plat->base + TIM_BDTR, TIM_BDTR_MOE); + + return 0; +} + +static int stm32_pwm_set_enable(struct udevice *dev, uint channel, + bool enable) +{ + struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev)); + struct stm32_pwm_priv *priv = dev_get_priv(dev); + u32 mask; + + /* Enable channel */ + mask = TIM_CCER_CC1E << (channel * 4); + if (priv->have_complementary_output) + mask |= TIM_CCER_CC1NE << (channel * 4); + + if (enable) { + setbits_le32(plat->base + TIM_CCER, mask); + /* Make sure that registers are updated */ + setbits_le32(plat->base + TIM_EGR, TIM_EGR_UG); + /* Enable controller */ + setbits_le32(plat->base + TIM_CR1, TIM_CR1_CEN); + } else { + clrbits_le32(plat->base + TIM_CCER, mask); + /* When all channels are disabled, we can disable the controller */ + if (!active_channels(plat)) + clrbits_le32(plat->base + TIM_CR1, TIM_CR1_CEN); + } + + return 0; +} + +static int stm32_pwm_set_invert(struct udevice *dev, uint channel, + bool polarity) +{ + struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev)); + struct stm32_pwm_priv *priv = dev_get_priv(dev); + u32 mask; + + mask = TIM_CCER_CC1P << (channel * 4); + if (priv->have_complementary_output) + mask |= TIM_CCER_CC1NP << (channel * 4); + + clrsetbits_le32(plat->base + TIM_CCER, mask, polarity ? mask : 0); + + return 0; +} + +static void stm32_pwm_detect_complementary(struct udevice *dev) +{ + struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev)); + struct stm32_pwm_priv *priv = dev_get_priv(dev); + u32 ccer; + + /* + * If complementary bit doesn't exist writing 1 will have no + * effect so we can detect it. + */ + setbits_le32(plat->base + TIM_CCER, TIM_CCER_CC1NE); + ccer = readl(plat->base + TIM_CCER); + clrbits_le32(plat->base + TIM_CCER, TIM_CCER_CC1NE); + + priv->have_complementary_output = (ccer != 0); +} + +static int stm32_pwm_probe(struct udevice *dev) +{ + struct stm32_timers_priv *timer = dev_get_priv(dev_get_parent(dev)); + + if (timer->rate > 1000000000) { + dev_err(dev, "Clock freq too high (%lu)\n", timer->rate); + return -EINVAL; + } + + stm32_pwm_detect_complementary(dev); + + return 0; +} + +static const struct pwm_ops stm32_pwm_ops = { + .set_config = stm32_pwm_set_config, + .set_enable = stm32_pwm_set_enable, + .set_invert = stm32_pwm_set_invert, +}; + +static const struct udevice_id stm32_pwm_ids[] = { + { .compatible = "st,stm32-pwm" }, + { } +}; + +U_BOOT_DRIVER(stm32_pwm) = { + .name = "stm32_pwm", + .id = UCLASS_PWM, + .of_match = stm32_pwm_ids, + .ops = &stm32_pwm_ops, + .probe = stm32_pwm_probe, + .priv_auto = sizeof(struct stm32_pwm_priv), +}; diff --git a/drivers/ram/Kconfig b/drivers/ram/Kconfig index 899d7585489..2a40b0c9f81 100644 --- a/drivers/ram/Kconfig +++ b/drivers/ram/Kconfig @@ -71,7 +71,7 @@ choice depends on K3_DDRSS prompt "K3 DDRSS Arch Support" - default K3_J721E_DDRSS if SOC_K3_J721E || SOC_K3_J721S2 || SOC_K3_J784S4 + default K3_J721E_DDRSS if SOC_K3_J721E || SOC_K3_J7200 || SOC_K3_J721S2 || SOC_K3_J784S4 default K3_AM64_DDRSS if SOC_K3_AM642 default K3_AM64_DDRSS if SOC_K3_AM625 default K3_AM62A_DDRSS if SOC_K3_AM62A7 || SOC_K3_AM62P5 || SOC_K3_J722S diff --git a/drivers/ram/renesas/dbsc5/dram.c b/drivers/ram/renesas/dbsc5/dram.c index 6f78afb0ab5..ca8a7fb4783 100644 --- a/drivers/ram/renesas/dbsc5/dram.c +++ b/drivers/ram/renesas/dbsc5/dram.c @@ -1325,6 +1325,11 @@ static const u32 PI_DARRAY3_1_CSx_Fx[CS_CNT][3] = { #define DBSC_DBACEN 0x200 #define DBSC_DBRFEN 0x204 #define DBSC_DBCMD 0x208 +#define DBSC_DBCMD_CMD_OPCODE_PD 0x8 +#define DBSC_DBCMD_CMD_OPCODE_MRW 0xe +#define DBSC_DBCMD_CMD_OPCODE_MRR 0xf +#define DBSC_DBCMD_CMD_CHANNEL_ALL 0x8 +#define DBSC_DBCMD_CMD_RANK_ALL 0x4 #define DBSC_DBWAIT 0x210 #define DBSC_DBBL 0x400 #define DBSC_DBBLA 0x400 @@ -1736,24 +1741,20 @@ static void dbsc5_reg_write(void __iomem *addr, u32 data) } /** - * dbsc5_reg_write() - DRAM Command Write Access + * dbsc5_wait_dbwait() - DRAM Command Wait Access Completion * @dev: DBSC5 device - * @cmd DRAM command. * - * First, execute the dummy read to DBSC_DBCMD. - * Confirm that no DBSC command operation is in progress 0. - * Write the contents of the command to be sent to DRAM. + * Wait for DRAM access completion. This is used before sending a command + * to the DRAM to assure no other command is in flight already, or while + * waiting for MRR command to complete. */ -static void dbsc5_send_dbcmd2(struct udevice *dev, u32 cmd) +static void dbsc5_wait_dbwait(struct udevice *dev) { struct renesas_dbsc5_dram_priv *priv = dev_get_priv(dev); void __iomem *regs_dbsc_d = priv->regs + DBSC5_DBSC_D_OFFSET; u32 val; int ret; - /* dummy read */ - readl(regs_dbsc_d + DBSC_DBCMD); - ret = readl_poll_timeout(regs_dbsc_d + DBSC_DBWAIT, val, ((val & BIT(0)) == 0), 1000000); if (ret < 0) { printf("%s DBWAIT bit 0 timeout\n", __func__); @@ -1765,6 +1766,32 @@ static void dbsc5_send_dbcmd2(struct udevice *dev, u32 cmd) printf("%s DBWAIT + 0x4000 bit 0 timeout\n", __func__); hang(); } +} + +/** + * dbsc5_send_dbcmd2() - DRAM Command Write Access + * @dev: DBSC5 device + * @opcode DRAM controller opcode + * @channel DRAM controller channel (0..3) + * @rank DRAM controller rank (0..1) + * @arg Command and argument bits (command specific encoding) + * + * First, execute the dummy read to DBSC_DBCMD. + * Confirm that no DBSC command operation is in progress 0. + * Write the contents of the command to be sent to DRAM. + */ +static void dbsc5_send_dbcmd2(struct udevice *dev, const u8 opcode, + const u8 channel, const u8 rank, + const u16 arg) +{ + const u32 cmd = (opcode << 24) | (channel << 20) | (rank << 16) | arg; + struct renesas_dbsc5_dram_priv *priv = dev_get_priv(dev); + void __iomem *regs_dbsc_d = priv->regs + DBSC5_DBSC_D_OFFSET; + + /* dummy read */ + readl(regs_dbsc_d + DBSC_DBCMD); + + dbsc5_wait_dbwait(dev); dbsc5_reg_write(regs_dbsc_d + DBSC_DBCMD, cmd); } @@ -2950,10 +2977,14 @@ static u32 dbsc5_pi_training(struct udevice *dev) writel(0x21, regs_dbsc_d + DBSC_DBDFICNT(ch)); /* Dummy PDE */ - dbsc5_send_dbcmd2(dev, 0x8840000); + dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_PD, + DBSC_DBCMD_CMD_CHANNEL_ALL, + DBSC_DBCMD_CMD_RANK_ALL, 0); /* PDX */ - dbsc5_send_dbcmd2(dev, 0x8840001); + dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_PD, + DBSC_DBCMD_CMD_CHANNEL_ALL, + DBSC_DBCMD_CMD_RANK_ALL, 1); /* Wait init_complete */ for (retry = 0; retry < retry_max; retry++) { @@ -4085,25 +4116,27 @@ static u32 dbsc5_read_training(struct udevice *dev) } /** - * dbsc5_ddr_register_set() - DDR mode register setting + * dbsc5_ddr_register_mr28_set() - DDR mode register MR28 set * @dev: DBSC5 device * * Set the mode register 28 of the SDRAM. * ZQ Mode: Command-Based ZQ Calibration * ZQ interval: Background Cal Interval < 64ms */ -static void dbsc5_ddr_register_set(struct udevice *dev) +static void dbsc5_ddr_register_mr28_set(struct udevice *dev) { - dbsc5_send_dbcmd2(dev, 0xE841C24); + dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_MRW, + DBSC_DBCMD_CMD_CHANNEL_ALL, + DBSC_DBCMD_CMD_RANK_ALL, (28 << 8) | 0x24); } /** - * dbsc5_ddr_register_read() - DDR mode register read + * dbsc5_ddr_register_mr27_mr57_read() - DDR mode register MR27/MR57 read * @dev: DBSC5 device * * Set the mode register 27 and 57 of the SDRAM. */ -static void dbsc5_ddr_register_read(struct udevice *dev) +static void dbsc5_ddr_register_mr27_mr57_read(struct udevice *dev) { struct renesas_dbsc5_dram_priv *priv = dev_get_priv(dev); @@ -4111,17 +4144,21 @@ static void dbsc5_ddr_register_read(struct udevice *dev) return; /* MR27 rank0 */ - dbsc5_send_dbcmd2(dev, 0xF801B00); + dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_MRR, + DBSC_DBCMD_CMD_CHANNEL_ALL, 0, 27 << 8); /* MR57 rank0 */ - dbsc5_send_dbcmd2(dev, 0xF803900); + dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_MRR, + DBSC_DBCMD_CMD_CHANNEL_ALL, 0, 57 << 8); if (!priv->ch_have_this_cs[1]) return; /* MR27 rank1 */ - dbsc5_send_dbcmd2(dev, 0xF811B00); + dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_MRR, + DBSC_DBCMD_CMD_CHANNEL_ALL, 1, 27 << 8); /* MR57 rank1 */ - dbsc5_send_dbcmd2(dev, 0xF813900); + dbsc5_send_dbcmd2(dev, DBSC_DBCMD_CMD_OPCODE_MRR, + DBSC_DBCMD_CMD_CHANNEL_ALL, 1, 57 << 8); } /** @@ -4200,8 +4237,8 @@ static u32 dbsc5_init_ddr(struct udevice *dev) dbsc5_dbsc_regset(dev); /* Frequency selection change (F1->F2) */ - dbsc5_ddr_setval_all_ch(dev, PHY_FREQ_SEL_INDEX, 0x1); - dbsc5_ddr_setval_all_ch(dev, PHY_FREQ_SEL_MULTICAST_EN, 0x0); + dbsc5_ddr_setval_all_ch(dev, PHY_FREQ_SEL_INDEX, 0x1); + dbsc5_ddr_setval_all_ch(dev, PHY_FREQ_SEL_MULTICAST_EN, 0x0); /* dfi_init_start (start ddrphy) & execute pi_training */ phytrainingok = dbsc5_pi_training(dev); @@ -4278,10 +4315,10 @@ static u32 dbsc5_init_ddr(struct udevice *dev) /* setup DDR mode registers */ /* MRS */ - dbsc5_ddr_register_set(dev); + dbsc5_ddr_register_mr28_set(dev); /* MRR */ - dbsc5_ddr_register_read(dev); + dbsc5_ddr_register_mr27_mr57_read(dev); /* training complete, setup DBSC */ dbsc5_dbsc_regset_post(dev); @@ -4296,10 +4333,13 @@ static u32 dbsc5_init_ddr(struct udevice *dev) /** * dbsc5_get_board_data() - Obtain board specific DRAM configuration + * @dev: DBSC5 device + * @modemr0: MODEMR0 register content * * Return board specific DRAM configuration structure pointer. */ -__weak const struct renesas_dbsc5_board_config *dbsc5_get_board_data(void) +__weak const struct renesas_dbsc5_board_config * +dbsc5_get_board_data(struct udevice *dev, const u32 modemr0) { return &renesas_v4h_dbsc5_board_config; } @@ -4335,7 +4375,7 @@ static int renesas_dbsc5_dram_probe(struct udevice *dev) u32 ch, cs; /* Get board data */ - priv->dbsc5_board_config = dbsc5_get_board_data(); + priv->dbsc5_board_config = dbsc5_get_board_data(dev, modemr0); priv->ddr_phyvalid = (u32)(priv->dbsc5_board_config->bdcfg_phyvalid); priv->max_density = 0; priv->cpg_regs = (void __iomem *)ofnode_get_addr(cnode); diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 2790b168b19..a3ecea05e20 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -13,6 +13,7 @@ config REMOTEPROC depends on DM # Please keep the configuration alphabetically sorted. + config K3_SYSTEM_CONTROLLER bool "Support for TI' K3 System Controller" select REMOTEPROC @@ -22,6 +23,16 @@ config K3_SYSTEM_CONTROLLER help Say 'y' here to add support for TI' K3 System Controller. +config REMOTEPROC_ADI_SC5XX + bool "Support for ADI SC5xx SHARC cores" + select REMOTEPROC + depends on DM + depends on ARCH_SC5XX + depends on SYSCON + help + Say 'y' here to add support for loading code onto SHARC cores in + an ADSP-SC5xx SoC from Analog Devices + config REMOTEPROC_RENESAS_APMU bool "Support for Renesas R-Car Gen4 APMU start of CR52 processor" select REMOTEPROC @@ -55,6 +66,7 @@ config REMOTEPROC_TI_K3_ARM64 depends on DM depends on ARCH_K3 depends on OF_CONTROL + default y if SYS_K3_SPL_ATF help Say y here to support TI's ARM64 processor subsystems on various TI K3 family of SoCs through the remote processor @@ -70,6 +82,16 @@ config REMOTEPROC_TI_K3_DSP on various TI K3 family of SoCs through the remote processor framework. +config REMOTEPROC_TI_K3_M4F + bool "TI K3 M4F remoteproc support" + select REMOTEPROC + depends on ARCH_K3 + depends on TI_SCI_PROTOCOL + help + Say y here to support TI's M4F remote processor subsystems + on various TI K3 family of SoCs through the remote processor + framework. + config REMOTEPROC_TI_K3_R5F bool "TI K3 R5F remoteproc support" select REMOTEPROC diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 3a092b7660e..bd94ea771be 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -8,11 +8,13 @@ obj-$(CONFIG_$(XPL_)REMOTEPROC) += rproc-uclass.o rproc-elf-loader.o # Remote proc drivers - Please keep this list alphabetically sorted. obj-$(CONFIG_K3_SYSTEM_CONTROLLER) += k3_system_controller.o +obj-$(CONFIG_REMOTEPROC_ADI_SC5XX) += adi_sc5xx_rproc.o obj-$(CONFIG_REMOTEPROC_RENESAS_APMU) += renesas_apmu.o obj-$(CONFIG_REMOTEPROC_SANDBOX) += sandbox_testproc.o obj-$(CONFIG_REMOTEPROC_STM32_COPRO) += stm32_copro.o obj-$(CONFIG_REMOTEPROC_TI_K3_ARM64) += ti_k3_arm64_rproc.o obj-$(CONFIG_REMOTEPROC_TI_K3_DSP) += ti_k3_dsp_rproc.o +obj-$(CONFIG_REMOTEPROC_TI_K3_M4F) += ti_k3_m4_rproc.o obj-$(CONFIG_REMOTEPROC_TI_K3_R5F) += ti_k3_r5f_rproc.o obj-$(CONFIG_REMOTEPROC_TI_POWER) += ti_power_proc.o obj-$(CONFIG_REMOTEPROC_TI_PRU) += pru_rproc.o diff --git a/drivers/remoteproc/adi_sc5xx_rproc.c b/drivers/remoteproc/adi_sc5xx_rproc.c new file mode 100644 index 00000000000..86acd1b98c7 --- /dev/null +++ b/drivers/remoteproc/adi_sc5xx_rproc.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * Contact: Greg Malysa <greg.malysa@timesys.com> + * + * Analog Devices SC5xx remoteproc driver for loading code onto SHARC cores + */ + +#include <dm.h> +#include <regmap.h> +#include <remoteproc.h> +#include <syscon.h> +#include <dm/device_compat.h> +#include <linux/delay.h> +#include <linux/io.h> + +/* Register offsets */ +#ifdef CONFIG_SC58X +#define ADI_RCU_REG_CTL 0x00 +#define ADI_RCU_REG_STAT 0x04 +#define ADI_RCU_REG_CRCTL 0x08 +#define ADI_RCU_REG_CRSTAT 0x0c +#define ADI_RCU_REG_SIDIS 0x10 +#define ADI_RCU_REG_SISTAT 0x14 +#define ADI_RCU_REG_BCODE 0x1c +#define ADI_RCU_REG_SVECT0 0x20 +#define ADI_RCU_REG_SVECT1 0x24 +#define ADI_RCU_REG_SVECT2 0x28 +#define ADI_RCU_REG_MSG 0x60 +#define ADI_RCU_REG_MSG_SET 0x64 +#define ADI_RCU_REG_MSG_CLR 0x68 +#else +#define ADI_RCU_REG_CTL 0x00 +#define ADI_RCU_REG_STAT 0x04 +#define ADI_RCU_REG_CRCTL 0x08 +#define ADI_RCU_REG_CRSTAT 0x0c +#define ADI_RCU_REG_SRRQSTAT 0x18 +#define ADI_RCU_REG_SIDIS 0x1c +#define ADI_RCU_REG_SISTAT 0x20 +#define ADI_RCU_REG_SVECT_LCK 0x24 +#define ADI_RCU_REG_BCODE 0x28 +#define ADI_RCU_REG_SVECT0 0x2c +#define ADI_RCU_REG_SVECT1 0x30 +#define ADI_RCU_REG_SVECT2 0x34 +#define ADI_RCU_REG_MSG 0x6c +#define ADI_RCU_REG_MSG_SET 0x70 +#define ADI_RCU_REG_MSG_CLR 0x74 +#endif /* CONFIG_SC58X */ + +/* Register bit definitions */ +#define ADI_RCU_CTL_SYSRST BIT(0) + +/* Bit values for the RCU0_MSG register */ +#define RCU0_MSG_C0IDLE 0x00000100 /* Core 0 Idle */ +#define RCU0_MSG_C1IDLE 0x00000200 /* Core 1 Idle */ +#define RCU0_MSG_C2IDLE 0x00000400 /* Core 2 Idle */ +#define RCU0_MSG_CRR0 0x00001000 /* Core 0 reset request */ +#define RCU0_MSG_CRR1 0x00002000 /* Core 1 reset request */ +#define RCU0_MSG_CRR2 0x00004000 /* Core 2 reset request */ +#define RCU0_MSG_C1ACTIVATE 0x00080000 /* Core 1 Activated */ +#define RCU0_MSG_C2ACTIVATE 0x00100000 /* Core 2 Activated */ + +struct sc5xx_rproc_data { + /* Address to load to svect when rebooting core */ + u32 load_addr; + + /* RCU parameters */ + struct regmap *rcu; + u32 svect_offset; + u32 coreid; +}; + +struct block_code_flag { + u32 bcode:4, /* 0-3 */ + bflag_save:1, /* 4 */ + bflag_aux:1, /* 5 */ + breserved:1, /* 6 */ + bflag_forward:1, /* 7 */ + bflag_fill:1, /* 8 */ + bflag_quickboot:1, /* 9 */ + bflag_callback:1, /* 10 */ + bflag_init:1, /* 11 */ + bflag_ignore:1, /* 12 */ + bflag_indirect:1, /* 13 */ + bflag_first:1, /* 14 */ + bflag_final:1, /* 15 */ + bhdrchk:8, /* 16-23 */ + bhdrsign:8; /* 0xAD, 0xAC or 0xAB */ +}; + +struct ldr_hdr { + struct block_code_flag bcode_flag; + u32 target_addr; + u32 byte_count; + u32 argument; +}; + +static int is_final(struct ldr_hdr *hdr) +{ + return hdr->bcode_flag.bflag_final; +} + +static int is_empty(struct ldr_hdr *hdr) +{ + return hdr->bcode_flag.bflag_ignore || (hdr->byte_count == 0); +} + +static int adi_valid_firmware(struct ldr_hdr *adi_ldr_hdr) +{ + if (!adi_ldr_hdr->byte_count && + (adi_ldr_hdr->bcode_flag.bhdrsign == 0xAD || + adi_ldr_hdr->bcode_flag.bhdrsign == 0xAC || + adi_ldr_hdr->bcode_flag.bhdrsign == 0xAB)) + return 1; + + return 0; +} + +static int sharc_load(struct udevice *dev, ulong addr, ulong size) +{ + struct sc5xx_rproc_data *priv = dev_get_priv(dev); + size_t offset; + u8 *buf = (u8 *)addr; + struct ldr_hdr *ldr = (struct ldr_hdr *)addr; + struct ldr_hdr *block_hdr; + struct ldr_hdr *next_hdr; + + if (!adi_valid_firmware(ldr)) { + dev_err(dev, "Firmware at 0x%lx does not appear to be an LDR image\n", addr); + dev_err(dev, "Note: Signed firmware is not currently supported\n"); + return -EINVAL; + } + + do { + block_hdr = (struct ldr_hdr *)buf; + offset = sizeof(struct ldr_hdr) + (block_hdr->bcode_flag.bflag_fill ? + 0 : block_hdr->byte_count); + next_hdr = (struct ldr_hdr *)(buf + offset); + + if (block_hdr->bcode_flag.bflag_first) + priv->load_addr = (unsigned long)block_hdr->target_addr; + + if (!is_empty(block_hdr)) { + if (block_hdr->bcode_flag.bflag_fill) { + memset_io((void *)(phys_addr_t)block_hdr->target_addr, + block_hdr->argument, + block_hdr->byte_count); + } else { + memcpy_toio((void *)(phys_addr_t)block_hdr->target_addr, + buf + sizeof(struct ldr_hdr), + block_hdr->byte_count); + } + } + + if (is_final(block_hdr)) + break; + + buf += offset; + } while (1); + + return 0; +} + +static void sharc_reset(struct sc5xx_rproc_data *priv) +{ + u32 coreid = priv->coreid; + u32 val; + + /* First put core in reset. + * Clear CRSTAT bit for given coreid. + */ + regmap_write(priv->rcu, ADI_RCU_REG_CRSTAT, 1 << coreid); + + /* Set SIDIS to disable the system interface */ + regmap_read(priv->rcu, ADI_RCU_REG_SIDIS, &val); + regmap_write(priv->rcu, ADI_RCU_REG_SIDIS, val | (1 << (coreid - 1))); + + /* + * Wait for access to coreX have been disabled and all the pending + * transactions have completed + */ + udelay(50); + + /* Set CRCTL bit to put core in reset */ + regmap_read(priv->rcu, ADI_RCU_REG_CRCTL, &val); + regmap_write(priv->rcu, ADI_RCU_REG_CRCTL, val | (1 << coreid)); + + /* Poll until Core is in reset */ + while (!(regmap_read(priv->rcu, ADI_RCU_REG_CRSTAT, &val), val & (1 << coreid))) + ; + + /* Clear SIDIS to reenable the system interface */ + regmap_read(priv->rcu, ADI_RCU_REG_SIDIS, &val); + regmap_write(priv->rcu, ADI_RCU_REG_SIDIS, val & ~(1 << (coreid - 1))); + + udelay(50); + + /* Take Core out of reset */ + regmap_read(priv->rcu, ADI_RCU_REG_CRCTL, &val); + regmap_write(priv->rcu, ADI_RCU_REG_CRCTL, val & ~(1 << coreid)); + + /* Wait for done */ + udelay(50); +} + +static int sharc_start(struct udevice *dev) +{ + struct sc5xx_rproc_data *priv = dev_get_priv(dev); + + /* Write load address to appropriate SVECT for core */ + regmap_write(priv->rcu, priv->svect_offset, priv->load_addr); + + sharc_reset(priv); + + /* Clear the IDLE bit when start the SHARC core */ + regmap_write(priv->rcu, ADI_RCU_REG_MSG_CLR, RCU0_MSG_C0IDLE << priv->coreid); + + /* Notify CCES */ + regmap_write(priv->rcu, ADI_RCU_REG_MSG_SET, RCU0_MSG_C1ACTIVATE << (priv->coreid - 1)); + return 0; +} + +static const struct dm_rproc_ops sc5xx_ops = { + .load = sharc_load, + .start = sharc_start, +}; + +static int sc5xx_probe(struct udevice *dev) +{ + struct sc5xx_rproc_data *priv = dev_get_priv(dev); + u32 coreid; + + if (dev_read_u32(dev, "coreid", &coreid)) { + dev_err(dev, "Missing property coreid\n"); + return -ENOENT; + } + + priv->coreid = coreid; + switch (coreid) { + case 1: + priv->svect_offset = ADI_RCU_REG_SVECT1; + break; + case 2: + priv->svect_offset = ADI_RCU_REG_SVECT2; + break; + default: + dev_err(dev, "Invalid value %d for coreid, must be 1 or 2\n", coreid); + return -EINVAL; + } + + priv->rcu = syscon_regmap_lookup_by_phandle(dev, "adi,rcu"); + if (IS_ERR(priv->rcu)) + return PTR_ERR(priv->rcu); + + dev_err(dev, "sc5xx remoteproc core %d available\n", priv->coreid); + + return 0; +} + +static const struct udevice_id sc5xx_ids[] = { + { .compatible = "adi,sc5xx-rproc" }, + { } +}; + +U_BOOT_DRIVER(adi_sc5xx_rproc) = { + .name = "adi_sc5xx_rproc", + .of_match = sc5xx_ids, + .id = UCLASS_REMOTEPROC, + .ops = &sc5xx_ops, + .probe = sc5xx_probe, + .priv_auto = sizeof(struct sc5xx_rproc_data), + .flags = 0, +}; diff --git a/drivers/remoteproc/ti_k3_dsp_rproc.c b/drivers/remoteproc/ti_k3_dsp_rproc.c index e90f75a188c..5a7d6377283 100644 --- a/drivers/remoteproc/ti_k3_dsp_rproc.c +++ b/drivers/remoteproc/ti_k3_dsp_rproc.c @@ -15,6 +15,7 @@ #include <clk.h> #include <reset.h> #include <asm/io.h> +#include <asm/system.h> #include <power-domain.h> #include <dm/device_compat.h> #include <linux/err.h> @@ -56,7 +57,9 @@ struct k3_dsp_boot_data { * @data: Pointer to DSP specific boot data structure * @mem: Array of available memories * @num_mem: Number of available memories - * @in_use: flag to tell if the core is already in use. + * @cached_addr: Cached memory address + * @cached_size: Cached memory size + * @in_use: flag to tell if the core is already in use. */ struct k3_dsp_privdata { struct reset_ctl dsp_rst; @@ -64,6 +67,8 @@ struct k3_dsp_privdata { struct k3_dsp_boot_data *data; struct k3_dsp_mem *mem; int num_mems; + void __iomem *cached_addr; + size_t cached_size; bool in_use; }; @@ -158,6 +163,13 @@ static int k3_dsp_load(struct udevice *dev, ulong addr, ulong size) goto unprepare; } + if (dsp->cached_addr && IS_ENABLED(CONFIG_SYS_DISABLE_DCACHE_OPS)) { + dev_dbg(dev, "final flush 0x%lx to 0x%lx\n", + (ulong)dsp->cached_addr, dsp->cached_size); + __asm_invalidate_dcache_range((u64)dsp->cached_addr, + (u64)dsp->cached_addr + (u64)dsp->cached_size); + } + boot_vector = rproc_elf_get_boot_addr(dev, addr); if (boot_vector & (data->boot_align_addr - 1)) { ret = -EINVAL; @@ -253,7 +265,6 @@ static void *k3_dsp_da_to_va(struct udevice *dev, ulong da, ulong len) { struct k3_dsp_privdata *dsp = dev_get_priv(dev); phys_addr_t bus_addr, dev_addr; - void __iomem *va = NULL; size_t size; u32 offset; int i; @@ -263,6 +274,16 @@ static void *k3_dsp_da_to_va(struct udevice *dev, ulong da, ulong len) if (len <= 0) return NULL; + if (dsp->cached_addr && IS_ENABLED(CONFIG_SYS_DISABLE_DCACHE_OPS)) { + dev_dbg(dev, "flush 0x%lx to 0x%lx\n", (ulong)dsp->cached_addr, + dsp->cached_size); + __asm_invalidate_dcache_range((u64)dsp->cached_addr, + (u64)dsp->cached_addr + (u64)dsp->cached_size); + } + + dsp->cached_size = len; + dsp->cached_addr = NULL; + for (i = 0; i < dsp->num_mems; i++) { bus_addr = dsp->mem[i].bus_addr; dev_addr = dsp->mem[i].dev_addr; @@ -270,19 +291,20 @@ static void *k3_dsp_da_to_va(struct udevice *dev, ulong da, ulong len) if (da >= dev_addr && ((da + len) <= (dev_addr + size))) { offset = da - dev_addr; - va = dsp->mem[i].cpu_addr + offset; - return (__force void *)va; + dsp->cached_addr = dsp->mem[i].cpu_addr + offset; } if (da >= bus_addr && (da + len) <= (bus_addr + size)) { offset = da - bus_addr; - va = dsp->mem[i].cpu_addr + offset; - return (__force void *)va; + dsp->cached_addr = dsp->mem[i].cpu_addr + offset; } } /* Assume it is DDR region and return da */ - return map_physmem(da, len, MAP_NOCACHE); + if (!dsp->cached_addr) + dsp->cached_addr = map_physmem(da, len, MAP_NOCACHE); + + return dsp->cached_addr; } static const struct dm_rproc_ops k3_dsp_ops = { diff --git a/drivers/remoteproc/ti_k3_m4_rproc.c b/drivers/remoteproc/ti_k3_m4_rproc.c new file mode 100644 index 00000000000..31b9de71579 --- /dev/null +++ b/drivers/remoteproc/ti_k3_m4_rproc.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments' K3 M4 Remoteproc driver + * + * Copyright (C) 2024 Texas Instruments Incorporated - http://www.ti.com/ + * Hari Nagalla <hnagalla@ti.com> + */ + +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <remoteproc.h> +#include <errno.h> +#include <clk.h> +#include <reset.h> +#include <asm/io.h> +#include <power-domain.h> +#include <dm/device_compat.h> +#include <linux/err.h> +#include <linux/sizes.h> +#include <linux/soc/ti/ti_sci_protocol.h> +#include "ti_sci_proc.h" +#include <mach/security.h> + +/** + * struct k3_m4_mem - internal memory structure + * @cpu_addr: MPU virtual address of the memory region + * @bus_addr: Bus address used to access the memory region + * @dev_addr: Device address from remoteproc view + * @size: Size of the memory region + */ +struct k3_m4_mem { + void __iomem *cpu_addr; + phys_addr_t bus_addr; + phys_addr_t dev_addr; + size_t size; +}; + +/** + * struct k3_m4_mem_data - memory definitions for m4 remote core + * @name: name for this memory entry + * @dev_addr: device address for the memory entry + */ +struct k3_m4_mem_data { + const char *name; + const u32 dev_addr; +}; + +/** + * struct k3_m4_boot_data - internal data structure used for boot + * @boot_align_addr: Boot vector address alignment granularity + */ +struct k3_m4_boot_data { + u32 boot_align_addr; +}; + +/** + * struct k3_m4_privdata - Structure representing Remote processor data. + * @m4_rst: m4 rproc reset control data + * @tsp: Pointer to TISCI proc contrl handle + * @data: Pointer to DSP specific boot data structure + * @mem: Array of available memories + * @num_mem: Number of available memories + */ +struct k3_m4_privdata { + struct reset_ctl m4_rst; + struct ti_sci_proc tsp; + struct k3_m4_boot_data *data; + struct k3_m4_mem *mem; + int num_mems; +}; + +/* + * The M4 cores have a local reset that affects only the CPU, and a + * generic module reset that powers on the device and allows the M4 internal + * memories to be accessed while the local reset is asserted. This function is + * used to release the global reset on M4F to allow loading into the M4F + * internal RAMs. This helper function is invoked in k3_m4_load() before any + * actual firmware loading happens and is undone only in k3_m4_stop(). The local + * reset cannot be released on M4 cores until after the firmware images are loaded. + */ +static int k3_m4_prepare(struct udevice *dev) +{ + struct k3_m4_privdata *m4 = dev_get_priv(dev); + int ret; + + ret = ti_sci_proc_power_domain_on(&m4->tsp); + if (ret) + dev_err(dev, "cannot enable internal RAM loading, ret = %d\n", + ret); + + return ret; +} + +/* + * This function is the counterpart to k3_m4_prepare() and is used to assert + * the global reset on M4 cores. This completes the second step of powering + * down the M4 cores. The cores themselves are halted through the local reset + * in first step. This function is invoked in k3_m4_stop() after the local + * reset is asserted. + */ +static int k3_m4_unprepare(struct udevice *dev) +{ + struct k3_m4_privdata *m4 = dev_get_priv(dev); + + return ti_sci_proc_power_domain_off(&m4->tsp); +} + +/** + * k3_m4_load() - Load up the Remote processor image + * @dev: rproc device pointer + * @addr: Address at which image is available + * @size: size of the image + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_m4_load(struct udevice *dev, ulong addr, ulong size) +{ + struct k3_m4_privdata *m4 = dev_get_priv(dev); + void *image_addr = (void *)addr; + int ret; + + ret = ti_sci_proc_request(&m4->tsp); + if (ret) + return ret; + + ret = k3_m4_prepare(dev); + if (ret) { + dev_err(dev, "Prepare failed for core %d\n", + m4->tsp.proc_id); + goto proc_release; + } + + ti_secure_image_post_process(&image_addr, &size); + + ret = rproc_elf_load_image(dev, addr, size); + if (ret < 0) { + dev_err(dev, "Loading elf failed %d\n", ret); + goto unprepare; + } + +unprepare: + if (ret) + k3_m4_unprepare(dev); +proc_release: + ti_sci_proc_release(&m4->tsp); + return ret; +} + +/** + * k3_m4_start() - Start the remote processor + * @dev: rproc device pointer + * + * Return: 0 if all went ok, else return appropriate error + */ +static int k3_m4_start(struct udevice *dev) +{ + struct k3_m4_privdata *m4 = dev_get_priv(dev); + int ret; + + ret = ti_sci_proc_request(&m4->tsp); + if (ret) + return ret; + + ret = reset_deassert(&m4->m4_rst); + + ti_sci_proc_release(&m4->tsp); + + return ret; +} + +static int k3_m4_stop(struct udevice *dev) +{ + struct k3_m4_privdata *m4 = dev_get_priv(dev); + + ti_sci_proc_request(&m4->tsp); + reset_assert(&m4->m4_rst); + k3_m4_unprepare(dev); + ti_sci_proc_release(&m4->tsp); + + return 0; +} + +static void *k3_m4_da_to_va(struct udevice *dev, ulong da, ulong len) +{ + struct k3_m4_privdata *m4 = dev_get_priv(dev); + phys_addr_t bus_addr, dev_addr; + void __iomem *va = NULL; + size_t size; + u32 offset; + int i; + + if (len <= 0) + return NULL; + + for (i = 0; i < m4->num_mems; i++) { + bus_addr = m4->mem[i].bus_addr; + dev_addr = m4->mem[i].dev_addr; + size = m4->mem[i].size; + + if (da >= dev_addr && ((da + len) <= (dev_addr + size))) { + offset = da - dev_addr; + va = m4->mem[i].cpu_addr + offset; + return (__force void *)va; + } + + if (da >= bus_addr && (da + len) <= (bus_addr + size)) { + offset = da - bus_addr; + va = m4->mem[i].cpu_addr + offset; + return (__force void *)va; + } + } + + /* Assume it is DDR region and return da */ + return map_physmem(da, len, MAP_NOCACHE); +} + +static const struct dm_rproc_ops k3_m4_ops = { + .load = k3_m4_load, + .start = k3_m4_start, + .stop = k3_m4_stop, + .device_to_virt = k3_m4_da_to_va, +}; + +static int ti_sci_proc_of_to_priv(struct udevice *dev, struct ti_sci_proc *tsp) +{ + u32 ids[2]; + int ret; + + tsp->sci = ti_sci_get_by_phandle(dev, "ti,sci"); + if (IS_ERR(tsp->sci)) { + dev_err(dev, "ti_sci get failed: %ld\n", PTR_ERR(tsp->sci)); + return PTR_ERR(tsp->sci); + } + + ret = dev_read_u32_array(dev, "ti,sci-proc-ids", ids, 2); + if (ret) { + dev_err(dev, "Proc IDs not populated %d\n", ret); + return ret; + } + + tsp->ops = &tsp->sci->ops.proc_ops; + tsp->proc_id = ids[0]; + tsp->host_id = ids[1]; + tsp->dev_id = dev_read_u32_default(dev, "ti,sci-dev-id", + TI_SCI_RESOURCE_NULL); + if (tsp->dev_id == TI_SCI_RESOURCE_NULL) { + dev_err(dev, "Device ID not populated %d\n", ret); + return -ENODEV; + } + + return 0; +} + +static const struct k3_m4_mem_data am6_m4_mems[] = { + { .name = "iram", .dev_addr = 0x0 }, + { .name = "dram", .dev_addr = 0x30000 }, +}; + +static int k3_m4_of_get_memories(struct udevice *dev) +{ + struct k3_m4_privdata *m4 = dev_get_priv(dev); + int i; + + m4->num_mems = ARRAY_SIZE(am6_m4_mems); + m4->mem = calloc(m4->num_mems, sizeof(*m4->mem)); + if (!m4->mem) + return -ENOMEM; + + for (i = 0; i < m4->num_mems; i++) { + m4->mem[i].bus_addr = dev_read_addr_size_name(dev, + am6_m4_mems[i].name, + (fdt_addr_t *)&m4->mem[i].size); + if (m4->mem[i].bus_addr == FDT_ADDR_T_NONE) { + dev_err(dev, "%s bus address not found\n", + am6_m4_mems[i].name); + return -EINVAL; + } + m4->mem[i].cpu_addr = map_physmem(m4->mem[i].bus_addr, + m4->mem[i].size, + MAP_NOCACHE); + m4->mem[i].dev_addr = am6_m4_mems[i].dev_addr; + } + + return 0; +} + +/** + * k3_of_to_priv() - generate private data from device tree + * @dev: corresponding k3 m4 processor device + * @m4: pointer to driver specific private data + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_m4_of_to_priv(struct udevice *dev, struct k3_m4_privdata *m4) +{ + int ret; + + ret = reset_get_by_index(dev, 0, &m4->m4_rst); + if (ret) { + dev_err(dev, "reset_get() failed: %d\n", ret); + return ret; + } + + ret = ti_sci_proc_of_to_priv(dev, &m4->tsp); + if (ret) + return ret; + + ret = k3_m4_of_get_memories(dev); + if (ret) + return ret; + + m4->data = (struct k3_m4_boot_data *)dev_get_driver_data(dev); + + return 0; +} + +/** + * k3_m4_probe() - Basic probe + * @dev: corresponding k3 remote processor device + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_m4_probe(struct udevice *dev) +{ + struct k3_m4_privdata *m4; + int ret; + + m4 = dev_get_priv(dev); + ret = k3_m4_of_to_priv(dev, m4); + if (ret) + return ret; + + /* + * The M4 local resets are deasserted by default on Power-On-Reset. + * Assert the local resets to ensure the M4s don't execute bogus code + * in .load() callback when the module reset is released to support + * internal memory loading. This is needed for M4 cores. + */ + reset_assert(&m4->m4_rst); + + return 0; +} + +static int k3_m4_remove(struct udevice *dev) +{ + struct k3_m4_privdata *m4 = dev_get_priv(dev); + + free(m4->mem); + + return 0; +} + +static const struct k3_m4_boot_data m4_data = { + .boot_align_addr = SZ_1K, +}; + +static const struct udevice_id k3_m4_ids[] = { + { .compatible = "ti,am64-m4fss", .data = (ulong)&m4_data, }, + {} +}; + +U_BOOT_DRIVER(k3_m4) = { + .name = "k3_m4", + .of_match = k3_m4_ids, + .id = UCLASS_REMOTEPROC, + .ops = &k3_m4_ops, + .probe = k3_m4_probe, + .remove = k3_m4_remove, + .priv_auto = sizeof(struct k3_m4_privdata), +}; diff --git a/drivers/remoteproc/ti_k3_r5f_rproc.c b/drivers/remoteproc/ti_k3_r5f_rproc.c index d78b3fa1bbd..57268e7f8ff 100644 --- a/drivers/remoteproc/ti_k3_r5f_rproc.c +++ b/drivers/remoteproc/ti_k3_r5f_rproc.c @@ -886,6 +886,7 @@ static const struct udevice_id k3_r5f_rproc_ids[] = { { .compatible = "ti,j7200-r5f", .data = (ulong)&j7200_j721s2_data, }, { .compatible = "ti,j721s2-r5f", .data = (ulong)&j7200_j721s2_data, }, { .compatible = "ti,am62-r5f", .data = (ulong)&am62_data, }, + { .compatible = "ti,am64-r5f", .data = (ulong)&j7200_j721s2_data, }, {} }; @@ -930,6 +931,7 @@ static const struct udevice_id k3_r5fss_ids[] = { { .compatible = "ti,j7200-r5fss"}, { .compatible = "ti,j721s2-r5fss"}, { .compatible = "ti,am62-r5fss"}, + { .compatible = "ti,am64-r5fss"}, {} }; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index fe5c1214f57..a0d079c4555 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -49,6 +49,13 @@ config TEGRA186_RESET Enable support for manipulating Tegra's on-SoC reset signals via IPC requests to the BPMP (Boot and Power Management Processor). +config RESET_AIROHA + bool "Reset controller driver for Airoha SoCs" + depends on DM_RESET && ARCH_AIROHA + default y + help + Support for reset controller on Airoha SoCs. + config RESET_TI_SCI bool "TI System Control Interface (TI SCI) reset driver" depends on DM_RESET && TI_SCI_PROTOCOL @@ -235,4 +242,20 @@ config RESET_AT91 This enables the Reset Controller driver support for Microchip/Atmel SoCs. Mainly used to expose assert/deassert methods to other drivers that require it. + +config RESET_RZG2L_USBPHY_CTRL + bool "Enable support for Renesas RZ/G2L USB 2.0 PHY control" + depends on DM_RESET + select REGULATOR_RZG2L_USBPHY + help + Enable support for controlling USB 2.0 PHY resets on the Renesas + RZ/G2L SoC. This is required for USB 2.0 functionality to work on this + SoC. + +config RESET_SPACEMIT_K1 + bool "Support for SPACEMIT's K1 Reset driver" + depends on DM_RESET + help + Support for SPACEMIT's K1 Reset system. Basic Assert/Deassert + is supported. endmenu diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index d99a78c9828..b9494396013 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_STI_RESET) += sti-reset.o obj-$(CONFIG_STM32_RESET) += stm32-reset.o obj-$(CONFIG_TEGRA_CAR_RESET) += tegra-car-reset.o obj-$(CONFIG_TEGRA186_RESET) += tegra186-reset.o +obj-$(CONFIG_RESET_AIROHA) += reset-airoha.o obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o @@ -33,3 +34,5 @@ obj-$(CONFIG_RESET_ZYNQMP) += reset-zynqmp.o obj-$(CONFIG_RESET_DRA7) += reset-dra7.o obj-$(CONFIG_RESET_AT91) += reset-at91.o obj-$(CONFIG_$(PHASE_)RESET_JH7110) += reset-jh7110.o +obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o +obj-$(CONFIG_RESET_SPACEMIT_K1) += reset-spacemit-k1.o diff --git a/drivers/reset/reset-airoha.c b/drivers/reset/reset-airoha.c new file mode 100644 index 00000000000..e878af6167c --- /dev/null +++ b/drivers/reset/reset-airoha.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Based on Linux drivers/clk/clk-en7523.c reworked + * and detached to a dedicated driver + * + * Author: Lorenzo Bianconi <lorenzo@kernel.org> (original driver) + * Christian Marangi <ansuelsmth@gmail.com> + */ + +#include <dm.h> +#include <linux/io.h> +#include <reset-uclass.h> + +#include <dt-bindings/reset/airoha,en7581-reset.h> + +#define RST_NR_PER_BANK 32 + +#define REG_RESET_CONTROL2 0x830 +#define REG_RESET_CONTROL1 0x834 + +struct airoha_reset_priv { + const u16 *bank_ofs; + const u16 *idx_map; + void __iomem *base; +}; + +static const u16 en7581_rst_ofs[] = { + REG_RESET_CONTROL2, + REG_RESET_CONTROL1, +}; + +static const u16 en7581_rst_map[] = { + /* RST_CTRL2 */ + [EN7581_XPON_PHY_RST] = 0, + [EN7581_CPU_TIMER2_RST] = 2, + [EN7581_HSUART_RST] = 3, + [EN7581_UART4_RST] = 4, + [EN7581_UART5_RST] = 5, + [EN7581_I2C2_RST] = 6, + [EN7581_XSI_MAC_RST] = 7, + [EN7581_XSI_PHY_RST] = 8, + [EN7581_NPU_RST] = 9, + [EN7581_I2S_RST] = 10, + [EN7581_TRNG_RST] = 11, + [EN7581_TRNG_MSTART_RST] = 12, + [EN7581_DUAL_HSI0_RST] = 13, + [EN7581_DUAL_HSI1_RST] = 14, + [EN7581_HSI_RST] = 15, + [EN7581_DUAL_HSI0_MAC_RST] = 16, + [EN7581_DUAL_HSI1_MAC_RST] = 17, + [EN7581_HSI_MAC_RST] = 18, + [EN7581_WDMA_RST] = 19, + [EN7581_WOE0_RST] = 20, + [EN7581_WOE1_RST] = 21, + [EN7581_HSDMA_RST] = 22, + [EN7581_TDMA_RST] = 24, + [EN7581_EMMC_RST] = 25, + [EN7581_SOE_RST] = 26, + [EN7581_PCIE2_RST] = 27, + [EN7581_XFP_MAC_RST] = 28, + [EN7581_USB_HOST_P1_RST] = 29, + [EN7581_USB_HOST_P1_U3_PHY_RST] = 30, + /* RST_CTRL1 */ + [EN7581_PCM1_ZSI_ISI_RST] = RST_NR_PER_BANK + 0, + [EN7581_FE_PDMA_RST] = RST_NR_PER_BANK + 1, + [EN7581_FE_QDMA_RST] = RST_NR_PER_BANK + 2, + [EN7581_PCM_SPIWP_RST] = RST_NR_PER_BANK + 4, + [EN7581_CRYPTO_RST] = RST_NR_PER_BANK + 6, + [EN7581_TIMER_RST] = RST_NR_PER_BANK + 8, + [EN7581_PCM1_RST] = RST_NR_PER_BANK + 11, + [EN7581_UART_RST] = RST_NR_PER_BANK + 12, + [EN7581_GPIO_RST] = RST_NR_PER_BANK + 13, + [EN7581_GDMA_RST] = RST_NR_PER_BANK + 14, + [EN7581_I2C_MASTER_RST] = RST_NR_PER_BANK + 16, + [EN7581_PCM2_ZSI_ISI_RST] = RST_NR_PER_BANK + 17, + [EN7581_SFC_RST] = RST_NR_PER_BANK + 18, + [EN7581_UART2_RST] = RST_NR_PER_BANK + 19, + [EN7581_GDMP_RST] = RST_NR_PER_BANK + 20, + [EN7581_FE_RST] = RST_NR_PER_BANK + 21, + [EN7581_USB_HOST_P0_RST] = RST_NR_PER_BANK + 22, + [EN7581_GSW_RST] = RST_NR_PER_BANK + 23, + [EN7581_SFC2_PCM_RST] = RST_NR_PER_BANK + 25, + [EN7581_PCIE0_RST] = RST_NR_PER_BANK + 26, + [EN7581_PCIE1_RST] = RST_NR_PER_BANK + 27, + [EN7581_CPU_TIMER_RST] = RST_NR_PER_BANK + 28, + [EN7581_PCIE_HB_RST] = RST_NR_PER_BANK + 29, + [EN7581_XPON_MAC_RST] = RST_NR_PER_BANK + 31, +}; + +static int airoha_reset_update(struct airoha_reset_priv *priv, + unsigned long id, bool assert) +{ + void __iomem *addr = priv->base + priv->bank_ofs[id / RST_NR_PER_BANK]; + u32 val; + + val = readl(addr); + if (assert) + val |= BIT(id % RST_NR_PER_BANK); + else + val &= ~BIT(id % RST_NR_PER_BANK); + writel(val, addr); + + return 0; +} + +static int airoha_reset_assert(struct reset_ctl *reset_ctl) +{ + struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev); + int id = reset_ctl->id; + + return airoha_reset_update(priv, id, true); +} + +static int airoha_reset_deassert(struct reset_ctl *reset_ctl) +{ + struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev); + int id = reset_ctl->id; + + return airoha_reset_update(priv, id, false); +} + +static int airoha_reset_status(struct reset_ctl *reset_ctl) +{ + struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev); + int id = reset_ctl->id; + void __iomem *addr; + + addr = priv->base + priv->bank_ofs[id / RST_NR_PER_BANK]; + + return !!(readl(addr) & BIT(id % RST_NR_PER_BANK)); +} + +static int airoha_reset_xlate(struct reset_ctl *reset_ctl, + struct ofnode_phandle_args *args) +{ + struct airoha_reset_priv *priv = dev_get_priv(reset_ctl->dev); + + if (args->args[0] >= ARRAY_SIZE(en7581_rst_map)) + return -EINVAL; + + reset_ctl->id = priv->idx_map[args->args[0]]; + + return 0; +} + +static struct reset_ops airoha_reset_ops = { + .of_xlate = airoha_reset_xlate, + .rst_assert = airoha_reset_assert, + .rst_deassert = airoha_reset_deassert, + .rst_status = airoha_reset_status, +}; + +static int airoha_reset_probe(struct udevice *dev) +{ + struct airoha_reset_priv *priv = dev_get_priv(dev); + + priv->base = dev_remap_addr(dev); + if (!priv->base) + return -ENOMEM; + + priv->bank_ofs = en7581_rst_ofs; + priv->idx_map = en7581_rst_map; + + return 0; +} + +U_BOOT_DRIVER(airoha_reset) = { + .name = "airoha-reset", + .id = UCLASS_RESET, + .probe = airoha_reset_probe, + .ops = &airoha_reset_ops, + .priv_auto = sizeof(struct airoha_reset_priv), +}; diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c new file mode 100644 index 00000000000..622d7b9cf4f --- /dev/null +++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024 Renesas Electronics Corporation + */ + +#include <asm/io.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/device_compat.h> +#include <dm/lists.h> +#include <renesas/rzg2l-usbphy.h> +#include <reset-uclass.h> +#include <reset.h> + +#define RESET 0x000 + +#define RESET_SEL_PLLRESET BIT(12) +#define RESET_PLLRESET BIT(8) + +#define RESET_SEL_P2RESET BIT(5) +#define RESET_SEL_P1RESET BIT(4) +#define RESET_PHYRST_2 BIT(1) +#define RESET_PHYRST_1 BIT(0) + +#define PHY_RESET_MASK (RESET_PHYRST_1 | RESET_PHYRST_2) + +#define NUM_PORTS 2 + +static int rzg2l_usbphy_ctrl_assert(struct reset_ctl *reset_ctl) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(reset_ctl->dev); + u32 val; + + val = readl(priv->regs + RESET); + val |= reset_ctl->id ? RESET_PHYRST_2 : RESET_PHYRST_1; + + /* If both ports are in reset, we can also place the PLL into reset. */ + if ((val & PHY_RESET_MASK) == PHY_RESET_MASK) + val |= RESET_PLLRESET; + + writel(val, priv->regs + RESET); + return 0; +} + +static int rzg2l_usbphy_ctrl_deassert(struct reset_ctl *reset_ctl) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(reset_ctl->dev); + u32 val = reset_ctl->id ? RESET_PHYRST_2 : RESET_PHYRST_1; + + /* If either port is out of reset, the PLL must also be out of reset. */ + val |= RESET_PLLRESET; + + clrbits_le32(priv->regs + RESET, val); + return 0; +} + +static int rzg2l_usbphy_ctrl_of_xlate(struct reset_ctl *reset_ctl, + struct ofnode_phandle_args *args) +{ + if (args->args[0] >= NUM_PORTS) + return -EINVAL; + + reset_ctl->id = args->args[0]; + return 0; +} + +struct reset_ops rzg2l_usbphy_ctrl_ops = { + .rst_assert = rzg2l_usbphy_ctrl_assert, + .rst_deassert = rzg2l_usbphy_ctrl_deassert, + .of_xlate = rzg2l_usbphy_ctrl_of_xlate, +}; + +static int rzg2l_usbphy_ctrl_probe(struct udevice *dev) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_priv(dev); + struct reset_ctl rst; + int ret; + + priv->regs = dev_read_addr(dev); + + ret = reset_get_by_index(dev, 0, &rst); + if (ret < 0) { + dev_err(dev, "failed to get reset line: %d\n", ret); + return ret; + } + + ret = reset_deassert(&rst); + if (ret < 0) { + dev_err(dev, "failed to de-assert reset line: %d\n", ret); + return ret; + } + + /* put pll and phy into reset state */ + setbits_le32(priv->regs + RESET, + RESET_SEL_PLLRESET | RESET_PLLRESET | + RESET_SEL_P1RESET | RESET_PHYRST_1 | + RESET_SEL_P2RESET | RESET_PHYRST_2); + + return 0; +} + +static const struct udevice_id rzg2l_usbphy_ctrl_ids[] = { + { .compatible = "renesas,rzg2l-usbphy-ctrl", }, + { /* sentinel */ } +}; + +static int rzg2l_usbphy_ctrl_bind(struct udevice *dev) +{ + struct driver *drv; + ofnode node; + int ret; + + node = ofnode_find_subnode(dev_ofnode(dev), "regulator-vbus"); + if (!ofnode_valid(node)) { + dev_err(dev, "Failed to find vbus regulator devicetree node\n"); + return -ENOENT; + } + + drv = lists_driver_lookup_name("rzg2l_usbphy_regulator"); + if (!drv) { + dev_err(dev, "Failed to find vbus regulator driver\n"); + return -ENOENT; + } + + ret = device_bind(dev, drv, dev->name, NULL, node, NULL); + if (ret) { + dev_err(dev, "Failed to bind vbus regulator: %d\n", ret); + return ret; + } + + return 0; +} + +U_BOOT_DRIVER(rzg2l_usbphy_ctrl) = { + .name = "rzg2l_usbphy_ctrl", + .id = UCLASS_RESET, + .of_match = rzg2l_usbphy_ctrl_ids, + .bind = rzg2l_usbphy_ctrl_bind, + .probe = rzg2l_usbphy_ctrl_probe, + .ops = &rzg2l_usbphy_ctrl_ops, + .priv_auto = sizeof(struct rzg2l_usbphy_ctrl_priv), +}; diff --git a/drivers/reset/reset-spacemit-k1.c b/drivers/reset/reset-spacemit-k1.c new file mode 100644 index 00000000000..613e002fc4f --- /dev/null +++ b/drivers/reset/reset-spacemit-k1.c @@ -0,0 +1,548 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022 Spacemit Inc. + * Copyright (C) 2025 Huan Zhou <pericycle.cc@gmail.com> + */ + +#include <asm/io.h> +#include <config.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dt-bindings/reset/spacemit-k1-reset.h> +#include <linux/bitops.h> +#include <reset-uclass.h> + +/* APBC register offset */ +#define APBC_UART1_CLK_RST 0x0 +#define APBC_UART2_CLK_RST 0x4 +#define APBC_GPIO_CLK_RST 0x8 +#define APBC_PWM0_CLK_RST 0xc +#define APBC_PWM1_CLK_RST 0x10 +#define APBC_PWM2_CLK_RST 0x14 +#define APBC_PWM3_CLK_RST 0x18 +#define APBC_TWSI8_CLK_RST 0x20 +#define APBC_UART3_CLK_RST 0x24 +#define APBC_RTC_CLK_RST 0x28 +#define APBC_TWSI0_CLK_RST 0x2c +#define APBC_TWSI1_CLK_RST 0x30 +#define APBC_TIMERS1_CLK_RST 0x34 +#define APBC_TWSI2_CLK_RST 0x38 +#define APBC_AIB_CLK_RST 0x3c +#define APBC_TWSI4_CLK_RST 0x40 +#define APBC_TIMERS2_CLK_RST 0x44 +#define APBC_ONEWIRE_CLK_RST 0x48 +#define APBC_TWSI5_CLK_RST 0x4c +#define APBC_DRO_CLK_RST 0x58 +#define APBC_IR_CLK_RST 0x5c +#define APBC_TWSI6_CLK_RST 0x60 +#define APBC_TWSI7_CLK_RST 0x68 +#define APBC_TSEN_CLK_RST 0x6c + +#define APBC_UART4_CLK_RST 0x70 +#define APBC_UART5_CLK_RST 0x74 +#define APBC_UART6_CLK_RST 0x78 +#define APBC_SSP3_CLK_RST 0x7c + +#define APBC_SSPA0_CLK_RST 0x80 +#define APBC_SSPA1_CLK_RST 0x84 + +#define APBC_IPC_AP2AUD_CLK_RST 0x90 +#define APBC_UART7_CLK_RST 0x94 +#define APBC_UART8_CLK_RST 0x98 +#define APBC_UART9_CLK_RST 0x9c + +#define APBC_CAN0_CLK_RST 0xa0 +#define APBC_PWM4_CLK_RST 0xa8 +#define APBC_PWM5_CLK_RST 0xac +#define APBC_PWM6_CLK_RST 0xb0 +#define APBC_PWM7_CLK_RST 0xb4 +#define APBC_PWM8_CLK_RST 0xb8 +#define APBC_PWM9_CLK_RST 0xbc +#define APBC_PWM10_CLK_RST 0xc0 +#define APBC_PWM11_CLK_RST 0xc4 +#define APBC_PWM12_CLK_RST 0xc8 +#define APBC_PWM13_CLK_RST 0xcc +#define APBC_PWM14_CLK_RST 0xd0 +#define APBC_PWM15_CLK_RST 0xd4 +#define APBC_PWM16_CLK_RST 0xd8 +#define APBC_PWM17_CLK_RST 0xdc +#define APBC_PWM18_CLK_RST 0xe0 +#define APBC_PWM19_CLK_RST 0xe4 +/* end of APBC register offset */ + +/* MPMU register offset */ +#define MPMU_WDTPCR 0x200 +/* end of MPMU register offset */ + +/* APMU register offset */ +#define APMU_JPG_CLK_RES_CTRL 0x20 +#define APMU_CSI_CCIC2_CLK_RES_CTRL 0x24 +#define APMU_ISP_CLK_RES_CTRL 0x38 +#define APMU_LCD_CLK_RES_CTRL1 0x44 +#define APMU_LCD_SPI_CLK_RES_CTRL 0x48 +#define APMU_LCD_CLK_RES_CTRL2 0x4c +#define APMU_CCIC_CLK_RES_CTRL 0x50 +#define APMU_SDH0_CLK_RES_CTRL 0x54 +#define APMU_SDH1_CLK_RES_CTRL 0x58 +#define APMU_USB_CLK_RES_CTRL 0x5c +#define APMU_QSPI_CLK_RES_CTRL 0x60 +#define APMU_USB_CLK_RES_CTRL 0x5c +#define APMU_DMA_CLK_RES_CTRL 0x64 +#define APMU_AES_CLK_RES_CTRL 0x68 +#define APMU_VPU_CLK_RES_CTRL 0xa4 +#define APMU_GPU_CLK_RES_CTRL 0xcc +#define APMU_SDH2_CLK_RES_CTRL 0xe0 +#define APMU_PMUA_MC_CTRL 0xe8 +#define APMU_PMU_CC2_AP 0x100 +#define APMU_PMUA_EM_CLK_RES_CTRL 0x104 + +#define APMU_AUDIO_CLK_RES_CTRL 0x14c +#define APMU_HDMI_CLK_RES_CTRL 0x1B8 + +#define APMU_PCIE_CLK_RES_CTRL_0 0x3cc +#define APMU_PCIE_CLK_RES_CTRL_1 0x3d4 +#define APMU_PCIE_CLK_RES_CTRL_2 0x3dc + +#define APMU_EMAC0_CLK_RES_CTRL 0x3e4 +#define APMU_EMAC1_CLK_RES_CTRL 0x3ec +/* end of APMU register offset */ + +/* APBC2 register offset */ +#define APBC2_UART1_CLK_RST 0x00 +#define APBC2_SSP2_CLK_RST 0x04 +#define APBC2_TWSI3_CLK_RST 0x08 +#define APBC2_RTC_CLK_RST 0x0c +#define APBC2_TIMERS0_CLK_RST 0x10 +#define APBC2_KPC_CLK_RST 0x14 +#define APBC2_GPIO_CLK_RST 0x1c +/* end of APBC2 register offset */ + +enum spacemit_reset_base_type { + RST_BASE_TYPE_MPMU = 0, + RST_BASE_TYPE_APMU = 1, + RST_BASE_TYPE_APBC = 2, + RST_BASE_TYPE_APBS = 3, + RST_BASE_TYPE_CIU = 4, + RST_BASE_TYPE_DCIU = 5, + RST_BASE_TYPE_DDRC = 6, + RST_BASE_TYPE_AUDC = 7, + RST_BASE_TYPE_APBC2 = 8, +}; + +struct spacemit_reset_signal { + u32 offset; + u32 mask; + u32 deassert_val; + u32 assert_val; + enum spacemit_reset_base_type type; +}; + +struct spacemit_reset_base { + void __iomem *mpmu_base; + void __iomem *apmu_base; + void __iomem *apbc_base; + void __iomem *apbs_base; + void __iomem *ciu_base; + void __iomem *dciu_base; + void __iomem *ddrc_base; + void __iomem *audio_ctrl_base; + void __iomem *apbc2_base; +}; + +struct spacemit_reset { + struct spacemit_reset_base io_base; + const struct spacemit_reset_signal *signals; +}; + +enum { + RESET_TWSI6_SPL = 0, + RESET_TWSI8_SPL, + RESET_SDH_AXI_SPL, + RESET_SDH0_SPL, + RESET_USB_AXI_SPL, + RESET_USBP1_AXI_SPL, + RESET_USB3_0_SPL, + RESET_QSPI_SPL, + RESET_QSPI_BUS_SPL, + RESET_AES_SPL, + RESET_SDH2_SPL, + RESET_NUMBER_SPL +}; + +static const struct spacemit_reset_signal + k1_reset_signals[RESET_NUMBER] = { + [RESET_UART1] = { APBC_UART1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_UART2] = { APBC_UART2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_GPIO] = { APBC_GPIO_CLK_RST, BIT(2), 0, + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM0] = { APBC_PWM0_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM1] = { APBC_PWM1_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM2] = { APBC_PWM2_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM3] = { APBC_PWM3_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM4] = { APBC_PWM4_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM5] = { APBC_PWM5_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM6] = { APBC_PWM6_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM7] = { APBC_PWM7_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM8] = { APBC_PWM8_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM9] = { APBC_PWM9_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM10] = { APBC_PWM10_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM11] = { APBC_PWM11_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM12] = { APBC_PWM12_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM13] = { APBC_PWM13_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM14] = { APBC_PWM14_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM15] = { APBC_PWM15_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM16] = { APBC_PWM16_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM17] = { APBC_PWM17_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM18] = { APBC_PWM18_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_PWM19] = { APBC_PWM19_CLK_RST, + BIT(2) | BIT(0), + BIT(0), + BIT(2), + RST_BASE_TYPE_APBC }, + [RESET_SSP3] = { APBC_SSP3_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_UART3] = { APBC_UART3_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_RTC] = { APBC_RTC_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TWSI0] = { APBC_TWSI0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TIMERS1] = { APBC_TIMERS1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_AIB] = { APBC_AIB_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TIMERS2] = { APBC_TIMERS2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_ONEWIRE] = { APBC_ONEWIRE_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_SSPA0] = { APBC_SSPA0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_SSPA1] = { APBC_SSPA1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_DRO] = { APBC_DRO_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_IR] = { APBC_IR_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TWSI1] = { APBC_TWSI1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TSEN] = { APBC_TSEN_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TWSI2] = { APBC_TWSI2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TWSI4] = { APBC_TWSI4_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TWSI5] = { APBC_TWSI5_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TWSI6] = { APBC_TWSI6_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TWSI7] = { APBC_TWSI7_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_TWSI8] = { APBC_TWSI8_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_IPC_AP2AUD] = { APBC_IPC_AP2AUD_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_UART4] = { APBC_UART4_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_UART5] = { APBC_UART5_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_UART6] = { APBC_UART6_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_UART7] = { APBC_UART7_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_UART8] = { APBC_UART8_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_UART9] = { APBC_UART9_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + [RESET_CAN0] = { APBC_CAN0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC }, + /* MPMU */ + [RESET_WDT] = { MPMU_WDTPCR, BIT(2), 0, BIT(2), RST_BASE_TYPE_MPMU }, + /* APMU */ + [RESET_JPG] = { APMU_JPG_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_CSI] = { APMU_CSI_CCIC2_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_CCIC2_PHY] = { APMU_CSI_CCIC2_CLK_RES_CTRL, + BIT(2), + BIT(2), + 0, + RST_BASE_TYPE_APMU }, + [RESET_CCIC3_PHY] = { APMU_CSI_CCIC2_CLK_RES_CTRL, + BIT(29), + BIT(29), + 0, + RST_BASE_TYPE_APMU }, + [RESET_ISP] = { APMU_ISP_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_ISP_AHB] = { APMU_ISP_CLK_RES_CTRL, BIT(3), BIT(3), 0, RST_BASE_TYPE_APMU }, + [RESET_ISP_CI] = { APMU_ISP_CLK_RES_CTRL, BIT(16), BIT(16), 0, RST_BASE_TYPE_APMU }, + [RESET_ISP_CPP] = { APMU_ISP_CLK_RES_CTRL, BIT(27), BIT(27), 0, RST_BASE_TYPE_APMU }, + [RESET_LCD] = { APMU_LCD_CLK_RES_CTRL1, BIT(4), BIT(4), 0, RST_BASE_TYPE_APMU }, + [RESET_DSI_ESC] = { APMU_LCD_CLK_RES_CTRL1, BIT(3), BIT(3), 0, RST_BASE_TYPE_APMU }, + [RESET_V2D] = { APMU_LCD_CLK_RES_CTRL1, BIT(27), BIT(27), 0, RST_BASE_TYPE_APMU }, + [RESET_MIPI] = { APMU_LCD_CLK_RES_CTRL1, BIT(15), BIT(15), 0, RST_BASE_TYPE_APMU }, + [RESET_LCD_SPI] = { APMU_LCD_SPI_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_LCD_SPI_BUS] = { APMU_LCD_SPI_CLK_RES_CTRL, + BIT(4), + BIT(4), + 0, + RST_BASE_TYPE_APMU }, + [RESET_LCD_SPI_HBUS] = { APMU_LCD_SPI_CLK_RES_CTRL, + BIT(2), + BIT(2), + 0, + RST_BASE_TYPE_APMU }, + [RESET_LCD_MCLK] = { APMU_LCD_CLK_RES_CTRL2, BIT(9), BIT(9), 0, RST_BASE_TYPE_APMU }, + [RESET_CCIC_4X] = { APMU_CCIC_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_CCIC1_PHY] = { APMU_CCIC_CLK_RES_CTRL, BIT(2), BIT(2), 0, RST_BASE_TYPE_APMU }, + [RESET_SDH_AXI] = { APMU_SDH0_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_SDH0] = { APMU_SDH0_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_SDH1] = { APMU_SDH1_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_USB_AXI] = { APMU_USB_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_USBP1_AXI] = { APMU_USB_CLK_RES_CTRL, BIT(4), BIT(4), 0, RST_BASE_TYPE_APMU }, + [RESET_USB3_0] = { APMU_USB_CLK_RES_CTRL, + BIT(9) | BIT(10) | BIT(11), + BIT(9) | BIT(10) | BIT(11), + 0, + RST_BASE_TYPE_APMU }, + [RESET_QSPI] = { APMU_QSPI_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_QSPI_BUS] = { APMU_QSPI_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_DMA] = { APMU_DMA_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_AES] = { APMU_AES_CLK_RES_CTRL, BIT(4), BIT(4), 0, RST_BASE_TYPE_APMU }, + [RESET_VPU] = { APMU_VPU_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_GPU] = { APMU_GPU_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_SDH2] = { APMU_SDH2_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_MC] = { APMU_PMUA_MC_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_EM_AXI] = { APMU_PMUA_EM_CLK_RES_CTRL, BIT(0), BIT(0), 0, RST_BASE_TYPE_APMU }, + [RESET_EM] = { APMU_PMUA_EM_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_AUDIO_SYS] = { APMU_AUDIO_CLK_RES_CTRL, + BIT(0) | BIT(2) | BIT(3), + BIT(0) | BIT(2) | BIT(3), + 0, + RST_BASE_TYPE_APMU }, + [RESET_HDMI] = { APMU_HDMI_CLK_RES_CTRL, BIT(9), BIT(9), 0, RST_BASE_TYPE_APMU }, + [RESET_PCIE0] = { APMU_PCIE_CLK_RES_CTRL_0, + BIT(3) | BIT(4) | BIT(5) | BIT(8), + BIT(3) | BIT(4) | BIT(5), + BIT(8), + RST_BASE_TYPE_APMU }, + [RESET_PCIE1] = { APMU_PCIE_CLK_RES_CTRL_1, + BIT(3) | BIT(4) | BIT(5) | BIT(8), + BIT(3) | BIT(4) | BIT(5), + BIT(8), + RST_BASE_TYPE_APMU }, + [RESET_PCIE2] = { APMU_PCIE_CLK_RES_CTRL_2, 0x138, 0x38, 0x100, RST_BASE_TYPE_APMU }, + [RESET_EMAC0] = { APMU_EMAC0_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_EMAC1] = { APMU_EMAC1_CLK_RES_CTRL, BIT(1), BIT(1), 0, RST_BASE_TYPE_APMU }, + [RESET_SEC_UART1] = { APBC2_UART1_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 }, + [RESET_SEC_SSP2] = { APBC2_SSP2_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 }, + [RESET_SEC_TWSI3] = { APBC2_TWSI3_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 }, + [RESET_SEC_RTC] = { APBC2_RTC_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 }, + [RESET_SEC_TIMERS0] = { APBC2_TIMERS0_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 }, + [RESET_SEC_KPC] = { APBC2_KPC_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 }, + [RESET_SEC_GPIO] = { APBC2_GPIO_CLK_RST, BIT(2), 0, BIT(2), RST_BASE_TYPE_APBC2 }, + }; + +static u32 spacemit_reset_read(struct spacemit_reset *reset, u32 id) +{ + void __iomem *base; + + switch (reset->signals[id].type) { + case RST_BASE_TYPE_APMU: + base = reset->io_base.apmu_base; + break; + case RST_BASE_TYPE_APBC: + base = reset->io_base.apbc_base; + break; + default: + base = reset->io_base.apbc_base; + break; + } + + return readl(base + reset->signals[id].offset); +} + +static void spacemit_reset_write(struct spacemit_reset *reset, u32 value, u32 id) +{ + void __iomem *base; + + switch (reset->signals[id].type) { + case RST_BASE_TYPE_APMU: + base = reset->io_base.apmu_base; + break; + case RST_BASE_TYPE_APBC: + base = reset->io_base.apbc_base; + break; + default: + base = reset->io_base.apbc_base; + break; + } + + writel(value, base + reset->signals[id].offset); +} + +static void spacemit_reset_set(struct reset_ctl *rst, u32 id, bool assert) +{ + u32 value; + struct spacemit_reset *reset = dev_get_priv(rst->dev); + + value = spacemit_reset_read(reset, id); + + if (assert) { + value &= ~reset->signals[id].mask; + value |= reset->signals[id].assert_val; + } else { + value &= ~reset->signals[id].mask; + value |= reset->signals[id].deassert_val; + } + + spacemit_reset_write(reset, value, id); +} + +static int spacemit_reset_update(struct reset_ctl *rst, bool assert) +{ + if (rst->id < RESET_UART1 || rst->id >= RESET_NUMBER) + return 0; + + /* can not write to twsi8 */ + if (rst->id == RESET_TWSI8) + return 0; + + spacemit_reset_set(rst, rst->id, assert); + return 0; +} + +static int spacemit_reset_assert(struct reset_ctl *rst) +{ + return spacemit_reset_update(rst, true); +} + +static int spacemit_reset_deassert(struct reset_ctl *rst) +{ + return spacemit_reset_update(rst, false); +} + +static int spacemit_k1_reset_probe(struct udevice *dev) +{ + struct spacemit_reset *reset = dev_get_priv(dev); + + reset->io_base.mpmu_base = (void *)dev_remap_addr_index(dev, 0); + if (!reset->io_base.mpmu_base) { + pr_err("failed to map mpmu registers\n"); + goto out; + } + + reset->io_base.apmu_base = (void *)dev_remap_addr_index(dev, 1); + if (!reset->io_base.apmu_base) { + pr_err("failed to map apmu registers\n"); + goto out; + } + + reset->io_base.apbc_base = (void *)dev_remap_addr_index(dev, 2); + if (!reset->io_base.apbc_base) { + pr_err("failed to map apbc registers\n"); + goto out; + } + + reset->io_base.apbs_base = (void *)dev_remap_addr_index(dev, 3); + if (!reset->io_base.apbs_base) { + pr_err("failed to map apbs registers\n"); + goto out; + } + + reset->io_base.ciu_base = (void *)dev_remap_addr_index(dev, 4); + if (!reset->io_base.ciu_base) { + pr_err("failed to map ciu registers\n"); + goto out; + } + + reset->io_base.dciu_base = (void *)dev_remap_addr_index(dev, 5); + if (!reset->io_base.dciu_base) { + pr_err("failed to map dragon ciu registers\n"); + goto out; + } + + reset->io_base.ddrc_base = (void *)dev_remap_addr_index(dev, 6); + if (!reset->io_base.ddrc_base) { + pr_err("failed to map ddrc registers\n"); + goto out; + } + + reset->io_base.apbc2_base = (void *)dev_remap_addr_index(dev, 7); + if (!reset->io_base.apbc2_base) { + pr_err("failed to map apbc2 registers\n"); + goto out; + } + + reset->signals = k1_reset_signals; + +out: + return 0; +} + +const struct reset_ops k1_reset_ops = { + .rst_assert = spacemit_reset_assert, + .rst_deassert = spacemit_reset_deassert, +}; + +static const struct udevice_id k1_reset_ids[] = { + { .compatible = "spacemit,k1-reset", }, + {}, +}; + +U_BOOT_DRIVER(k1_reset) = { + .name = "spacemit,k1-reset", + .id = UCLASS_RESET, + .ops = &k1_reset_ops, + .of_match = k1_reset_ids, + .probe = spacemit_k1_reset_probe, + .priv_auto = sizeof(struct spacemit_reset), +}; diff --git a/drivers/rng/msm_rng.c b/drivers/rng/msm_rng.c index f790d3b60f9..aab602c5ed0 100644 --- a/drivers/rng/msm_rng.c +++ b/drivers/rng/msm_rng.c @@ -44,6 +44,11 @@ static int msm_rng_read(struct udevice *dev, void *data, size_t len) u32 *retdata = data; size_t maxsize; u32 val; + int ret; + + ret = clk_enable(&priv->clk); + if (ret < 0) + return ret; /* calculate max size bytes to transfer back to caller */ maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, len); @@ -66,6 +71,8 @@ static int msm_rng_read(struct udevice *dev, void *data, size_t len) break; } while (currsize < maxsize); + clk_disable(&priv->clk); + return 0; } @@ -76,7 +83,7 @@ static int msm_rng_enable(struct msm_rng_priv *priv, int enable) if (enable) { /* Enable PRNG only if it is not already enabled */ val = readl_relaxed(priv->base + PRNG_CONFIG); - if (val & PRNG_CONFIG_HW_ENABLE) { + if (!(val & PRNG_CONFIG_HW_ENABLE)) { val = readl_relaxed(priv->base + PRNG_LFSR_CFG); val &= ~PRNG_LFSR_CFG_MASK; val |= PRNG_LFSR_CFG_CLOCKS; @@ -118,7 +125,9 @@ static int msm_rng_probe(struct udevice *dev) if (ret < 0) return ret; - return msm_rng_enable(priv, 1); + ret = msm_rng_enable(priv, 1); + clk_disable(&priv->clk); + return ret; } static int msm_rng_remove(struct udevice *dev) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index c4f4a8d78df..84130524c2d 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -789,6 +789,12 @@ config SPL_SYS_NS16550_SERIAL default y if SYS_NS16550_SERIAL || ARCH_SUNXI || ARCH_OMAP2PLUS select SYS_NS16550 +config TPL_SYS_NS16550_SERIAL + bool "NS16550 UART or compatible legacy driver in TPL" + depends on TPL && !TPL_DM_SERIAL + default y if SPL_SYS_NS16550_SERIAL + select SYS_NS16550 + config SYS_NS16550 bool "NS16550 UART or compatible" help diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index ebe692a9963..2ef8ba20cf5 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -63,7 +63,4 @@ obj-$(CONFIG_XEN_SERIAL) += serial_xen.o obj-$(CONFIG_XTENSA_SEMIHOSTING_SERIAL) += serial_xtensa_semihosting.o obj-$(CONFIG_S5P4418_PL011_SERIAL) += serial_s5p4418_pl011.o -ifndef CONFIG_XPL_BUILD -obj-$(CONFIG_USB_TTY) += usbtty.o -endif obj-$(CONFIG_UART4_SERIAL) += serial_adi_uart4.o diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 0e267d097c5..4f7de3ea215 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -112,9 +112,9 @@ static void serial_out_dynamic(struct ns16550_plat *plat, u8 *addr, } else if (plat->reg_width == 4) { if (plat->flags & NS16550_FLAG_ENDIAN) { if (plat->flags & NS16550_FLAG_BE) - out_be32(addr, value); + out_be32((u32 *)addr, value); else - out_le32(addr, value); + out_le32((u32 *)addr, value); } else { writel(value, addr); } @@ -132,9 +132,9 @@ static int serial_in_dynamic(struct ns16550_plat *plat, u8 *addr) } else if (plat->reg_width == 4) { if (plat->flags & NS16550_FLAG_ENDIAN) { if (plat->flags & NS16550_FLAG_BE) - return in_be32(addr); + return in_be32((u32 *)addr); else - return in_le32(addr); + return in_le32((u32 *)addr); } else { return readl(addr); } @@ -294,13 +294,9 @@ void ns16550_putc(struct ns16550 *com_port, char c) #if !CONFIG_IS_ENABLED(NS16550_MIN_FUNCTIONS) char ns16550_getc(struct ns16550 *com_port) { - while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0) { -#if !defined(CONFIG_XPL_BUILD) && defined(CONFIG_USB_TTY) - extern void usbtty_poll(void); - usbtty_poll(); -#endif + while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0) schedule(); - } + return serial_in(&com_port->rbr); } diff --git a/drivers/serial/sandbox.c b/drivers/serial/sandbox.c index 77a1558db68..cc0491bc3c8 100644 --- a/drivers/serial/sandbox.c +++ b/drivers/serial/sandbox.c @@ -63,7 +63,7 @@ static int sandbox_serial_probe(struct udevice *dev) if (state->term_raw != STATE_TERM_RAW) disable_ctrlc(1); - membuff_init(&priv->buf, priv->serial_buf, sizeof(priv->serial_buf)); + membuf_init(&priv->buf, priv->serial_buf, sizeof(priv->serial_buf)); return 0; } @@ -138,15 +138,15 @@ static int sandbox_serial_pending(struct udevice *dev, bool input) return 0; os_usleep(100); - avail = membuff_putraw(&priv->buf, 100, false, &data); + avail = membuf_putraw(&priv->buf, 100, false, &data); if (!avail) return 1; /* buffer full */ count = os_read(0, data, avail); if (count > 0) - membuff_putraw(&priv->buf, count, true, &data); + membuf_putraw(&priv->buf, count, true, &data); - return membuff_avail(&priv->buf); + return membuf_avail(&priv->buf); } static int sandbox_serial_getc(struct udevice *dev) @@ -156,7 +156,7 @@ static int sandbox_serial_getc(struct udevice *dev) if (!sandbox_serial_pending(dev, true)) return -EAGAIN; /* buffer empty */ - return membuff_getbyte(&priv->buf); + return membuf_getbyte(&priv->buf); } #ifdef CONFIG_DEBUG_UART_SANDBOX diff --git a/drivers/serial/serial_mxc.c b/drivers/serial/serial_mxc.c index c5fd740be4d..28f4435d01d 100644 --- a/drivers/serial/serial_mxc.c +++ b/drivers/serial/serial_mxc.c @@ -3,6 +3,7 @@ * (c) 2007 Sascha Hauer <s.hauer@pengutronix.de> */ +#include <clk.h> #include <dm.h> #include <errno.h> #include <watchdog.h> @@ -312,7 +313,17 @@ int mxc_serial_setbrg(struct udevice *dev, int baudrate) static int mxc_serial_probe(struct udevice *dev) { struct mxc_serial_plat *plat = dev_get_plat(dev); +#if CONFIG_IS_ENABLED(CLK_CCF) + int ret; + ret = clk_get_bulk(dev, &plat->clks); + if (ret) + return ret; + + ret = clk_enable_bulk(&plat->clks); + if (ret) + return ret; +#endif _mxc_serial_init(plat->reg, plat->use_dte); return 0; diff --git a/drivers/serial/serial_stm32.c b/drivers/serial/serial_stm32.c index 1ee58142b3f..1675a9cb9d1 100644 --- a/drivers/serial/serial_stm32.c +++ b/drivers/serial/serial_stm32.c @@ -299,13 +299,19 @@ static inline struct stm32_uart_info *_debug_uart_info(void) static inline void _debug_uart_init(void) { - void __iomem *base = (void __iomem *)CONFIG_VAL(DEBUG_UART_BASE); - struct stm32_uart_info *uart_info = _debug_uart_info(); + void __maybe_unused __iomem *base = (void __iomem *)CONFIG_VAL(DEBUG_UART_BASE); + struct stm32_uart_info *uart_info __maybe_unused = _debug_uart_info(); - _stm32_serial_init(base, uart_info); - _stm32_serial_setbrg(base, uart_info, - CONFIG_DEBUG_UART_CLOCK, - CONFIG_BAUDRATE); + /* + * debug_uart_init() is only usable when SPL_BUILD is enabled + * (STM32MP1 case only) + */ + if (IS_ENABLED(CONFIG_DEBUG_UART) && IS_ENABLED(CONFIG_SPL_BUILD)) { + _stm32_serial_init(base, uart_info); + _stm32_serial_setbrg(base, uart_info, + CONFIG_DEBUG_UART_CLOCK, + CONFIG_BAUDRATE); + } } static inline void _debug_uart_putc(int c) diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c deleted file mode 100644 index b7d77fbb6a9..00000000000 --- a/drivers/serial/usbtty.c +++ /dev/null @@ -1,983 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2003 - * Gerry Hamel, geh@ti.com, Texas Instruments - * - * (C) Copyright 2006 - * Bryan O'Donoghue, bodonoghue@codehermit.ie - */ - -#include <config.h> -#include <circbuf.h> -#include <env.h> -#include <serial.h> -#include <stdio_dev.h> -#include <asm/unaligned.h> -#include "usbtty.h" -#include "usb_cdc_acm.h" -#include "usbdescriptors.h" - -#ifdef DEBUG -#define TTYDBG(fmt,args...)\ - serial_printf("[%s] %s %d: "fmt, __FILE__,__FUNCTION__,__LINE__,##args) -#else -#define TTYDBG(fmt,args...) do{}while(0) -#endif - -#if 1 -#define TTYERR(fmt,args...)\ - serial_printf("ERROR![%s] %s %d: "fmt, __FILE__,__FUNCTION__,\ - __LINE__,##args) -#else -#define TTYERR(fmt,args...) do{}while(0) -#endif - -/* - * Defines - */ -#define NUM_CONFIGS 1 -#define MAX_INTERFACES 2 -#define NUM_ENDPOINTS 3 -#define ACM_TX_ENDPOINT 3 -#define ACM_RX_ENDPOINT 2 -#define GSERIAL_TX_ENDPOINT 2 -#define GSERIAL_RX_ENDPOINT 1 -#define NUM_ACM_INTERFACES 2 -#define NUM_GSERIAL_INTERFACES 1 -#define CFG_USBD_DATA_INTERFACE_STR "Bulk Data Interface" -#define CFG_USBD_CTRL_INTERFACE_STR "Control Interface" - -/* - * Buffers to hold input and output data - */ -#define USBTTY_BUFFER_SIZE 2048 -static circbuf_t usbtty_input; -static circbuf_t usbtty_output; - -/* - * Instance variables - */ -static struct stdio_dev usbttydev; -static struct usb_device_instance device_instance[1]; -static struct usb_bus_instance bus_instance[1]; -static struct usb_configuration_instance config_instance[NUM_CONFIGS]; -static struct usb_interface_instance interface_instance[MAX_INTERFACES]; -static struct usb_alternate_instance alternate_instance[MAX_INTERFACES]; -/* one extra for control endpoint */ -static struct usb_endpoint_instance endpoint_instance[NUM_ENDPOINTS+1]; - -/* - * Global flag - */ -int usbtty_configured_flag = 0; - -/* - * Serial number - */ -static char serial_number[16]; - -/* - * Descriptors, Strings, Local variables. - */ - -/* defined and used by gadget/ep0.c */ -extern struct usb_string_descriptor **usb_strings; - -/* Indicies, References */ -static unsigned short rx_endpoint = 0; -static unsigned short tx_endpoint = 0; -static unsigned short interface_count = 0; -static struct usb_string_descriptor *usbtty_string_table[STR_COUNT]; - -/* USB Descriptor Strings */ -static u8 wstrLang[4] = {4,USB_DT_STRING,0x9,0x4}; -static u8 wstrManufacturer[2 + 2*(sizeof(CONFIG_USBD_MANUFACTURER)-1)]; -static u8 wstrProduct[2 + 2*(sizeof(CONFIG_USBD_PRODUCT_NAME)-1)]; -static u8 wstrSerial[2 + 2*(sizeof(serial_number) - 1)]; -static u8 wstrConfiguration[2 + 2*(sizeof(CFG_USBD_CONFIGURATION_STR)-1)]; -static u8 wstrDataInterface[2 + 2*(sizeof(CFG_USBD_DATA_INTERFACE_STR)-1)]; -static u8 wstrCtrlInterface[2 + 2*(sizeof(CFG_USBD_DATA_INTERFACE_STR)-1)]; - -/* Standard USB Data Structures */ -static struct usb_interface_descriptor interface_descriptors[MAX_INTERFACES]; -static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS]; -static struct usb_configuration_descriptor *configuration_descriptor = 0; -static struct usb_device_descriptor device_descriptor = { - .bLength = sizeof(struct usb_device_descriptor), - .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = cpu_to_le16(USB_BCD_VERSION), - .bDeviceSubClass = 0x00, - .bDeviceProtocol = 0x00, - .bMaxPacketSize0 = EP0_MAX_PACKET_SIZE, - .idVendor = cpu_to_le16(CONFIG_USBD_VENDORID), - .bcdDevice = cpu_to_le16(USBTTY_BCD_DEVICE), - .iManufacturer = STR_MANUFACTURER, - .iProduct = STR_PRODUCT, - .iSerialNumber = STR_SERIAL, - .bNumConfigurations = NUM_CONFIGS -}; - -/* - * Static CDC ACM specific descriptors - */ - -struct acm_config_desc { - struct usb_configuration_descriptor configuration_desc; - - /* Master Interface */ - struct usb_interface_descriptor interface_desc; - - struct usb_class_header_function_descriptor usb_class_header; - struct usb_class_call_management_descriptor usb_class_call_mgt; - struct usb_class_abstract_control_descriptor usb_class_acm; - struct usb_class_union_function_descriptor usb_class_union; - struct usb_endpoint_descriptor notification_endpoint; - - /* Slave Interface */ - struct usb_interface_descriptor data_class_interface; - struct usb_endpoint_descriptor data_endpoints[NUM_ENDPOINTS-1]; -} __attribute__((packed)); - -static struct acm_config_desc acm_configuration_descriptors[NUM_CONFIGS] = { - { - .configuration_desc ={ - .bLength = - sizeof(struct usb_configuration_descriptor), - .bDescriptorType = USB_DT_CONFIG, - .wTotalLength = - cpu_to_le16(sizeof(struct acm_config_desc)), - .bNumInterfaces = NUM_ACM_INTERFACES, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG, - .bmAttributes = - BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED, - .bMaxPower = USBTTY_MAXPOWER - }, - /* Interface 1 */ - .interface_desc = { - .bLength = sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0, - .bAlternateSetting = 0, - .bNumEndpoints = 0x01, - .bInterfaceClass = - COMMUNICATIONS_INTERFACE_CLASS_CONTROL, - .bInterfaceSubClass = COMMUNICATIONS_ACM_SUBCLASS, - .bInterfaceProtocol = COMMUNICATIONS_V25TER_PROTOCOL, - .iInterface = STR_CTRL_INTERFACE, - }, - .usb_class_header = { - .bFunctionLength = - sizeof(struct usb_class_header_function_descriptor), - .bDescriptorType = CS_INTERFACE, - .bDescriptorSubtype = USB_ST_HEADER, - .bcdCDC = cpu_to_le16(110), - }, - .usb_class_call_mgt = { - .bFunctionLength = - sizeof(struct usb_class_call_management_descriptor), - .bDescriptorType = CS_INTERFACE, - .bDescriptorSubtype = USB_ST_CMF, - .bmCapabilities = 0x00, - .bDataInterface = 0x01, - }, - .usb_class_acm = { - .bFunctionLength = - sizeof(struct usb_class_abstract_control_descriptor), - .bDescriptorType = CS_INTERFACE, - .bDescriptorSubtype = USB_ST_ACMF, - .bmCapabilities = 0x00, - }, - .usb_class_union = { - .bFunctionLength = - sizeof(struct usb_class_union_function_descriptor), - .bDescriptorType = CS_INTERFACE, - .bDescriptorSubtype = USB_ST_UF, - .bMasterInterface = 0x00, - .bSlaveInterface0 = 0x01, - }, - .notification_endpoint = { - .bLength = - sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = UDC_INT_ENDPOINT | USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize - = cpu_to_le16(CFG_USBD_SERIAL_INT_PKTSIZE), - .bInterval = 0xFF, - }, - - /* Interface 2 */ - .data_class_interface = { - .bLength = - sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0x01, - .bAlternateSetting = 0x00, - .bNumEndpoints = 0x02, - .bInterfaceClass = - COMMUNICATIONS_INTERFACE_CLASS_DATA, - .bInterfaceSubClass = DATA_INTERFACE_SUBCLASS_NONE, - .bInterfaceProtocol = DATA_INTERFACE_PROTOCOL_NONE, - .iInterface = STR_DATA_INTERFACE, - }, - .data_endpoints = { - { - .bLength = - sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = UDC_OUT_ENDPOINT | USB_DIR_OUT, - .bmAttributes = - USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = - cpu_to_le16(CFG_USBD_SERIAL_BULK_PKTSIZE), - .bInterval = 0xFF, - }, - { - .bLength = - sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = UDC_IN_ENDPOINT | USB_DIR_IN, - .bmAttributes = - USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = - cpu_to_le16(CFG_USBD_SERIAL_BULK_PKTSIZE), - .bInterval = 0xFF, - }, - }, - }, -}; - -static struct rs232_emu rs232_desc={ - .dter = 115200, - .stop_bits = 0x00, - .parity = 0x00, - .data_bits = 0x08 -}; - -/* - * Static Generic Serial specific data - */ - -struct gserial_config_desc { - - struct usb_configuration_descriptor configuration_desc; - struct usb_interface_descriptor interface_desc[NUM_GSERIAL_INTERFACES]; - struct usb_endpoint_descriptor data_endpoints[NUM_ENDPOINTS]; - -} __attribute__((packed)); - -static struct gserial_config_desc -gserial_configuration_descriptors[NUM_CONFIGS] ={ - { - .configuration_desc ={ - .bLength = sizeof(struct usb_configuration_descriptor), - .bDescriptorType = USB_DT_CONFIG, - .wTotalLength = - cpu_to_le16(sizeof(struct gserial_config_desc)), - .bNumInterfaces = NUM_GSERIAL_INTERFACES, - .bConfigurationValue = 1, - .iConfiguration = STR_CONFIG, - .bmAttributes = - BMATTRIBUTE_SELF_POWERED|BMATTRIBUTE_RESERVED, - .bMaxPower = USBTTY_MAXPOWER - }, - .interface_desc = { - { - .bLength = - sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0, - .bAlternateSetting = 0, - .bNumEndpoints = NUM_ENDPOINTS, - .bInterfaceClass = - COMMUNICATIONS_INTERFACE_CLASS_VENDOR, - .bInterfaceSubClass = - COMMUNICATIONS_NO_SUBCLASS, - .bInterfaceProtocol = - COMMUNICATIONS_NO_PROTOCOL, - .iInterface = STR_DATA_INTERFACE - }, - }, - .data_endpoints = { - { - .bLength = - sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = UDC_OUT_ENDPOINT | USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = - cpu_to_le16(CFG_USBD_SERIAL_OUT_PKTSIZE), - .bInterval= 0xFF, - }, - { - .bLength = - sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = UDC_IN_ENDPOINT | USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = - cpu_to_le16(CFG_USBD_SERIAL_IN_PKTSIZE), - .bInterval = 0xFF, - }, - { - .bLength = - sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = UDC_INT_ENDPOINT | USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_INT, - .wMaxPacketSize = - cpu_to_le16(CFG_USBD_SERIAL_INT_PKTSIZE), - .bInterval = 0xFF, - }, - }, - }, -}; - -/* - * Static Function Prototypes - */ - -static void usbtty_init_strings (void); -static void usbtty_init_instances (void); -static void usbtty_init_endpoints (void); -static void usbtty_init_terminal_type(short type); -static void usbtty_event_handler (struct usb_device_instance *device, - usb_device_event_t event, int data); -static int usbtty_cdc_setup(struct usb_device_request *request, - struct urb *urb); -static int usbtty_configured (void); -static int write_buffer (circbuf_t * buf); -static int fill_buffer (circbuf_t * buf); - -void usbtty_poll (void); - -/* utility function for converting char* to wide string used by USB */ -static void str2wide (char *str, u16 * wide) -{ - int i; - for (i = 0; i < strlen (str) && str[i]; i++){ - #if defined(__LITTLE_ENDIAN) - wide[i] = (u16) str[i]; - #elif defined(__BIG_ENDIAN) - wide[i] = ((u16)(str[i])<<8); - #else - #error "__LITTLE_ENDIAN or __BIG_ENDIAN undefined" - #endif - } -} - -/* - * Test whether a character is in the RX buffer - */ - -int usbtty_tstc(struct stdio_dev *dev) -{ - struct usb_endpoint_instance *endpoint = - &endpoint_instance[rx_endpoint]; - - /* If no input data exists, allow more RX to be accepted */ - if(usbtty_input.size <= 0){ - udc_unset_nak(endpoint->endpoint_address&0x03); - } - - usbtty_poll (); - return (usbtty_input.size > 0); -} - -/* - * Read a single byte from the usb client port. Returns 1 on success, 0 - * otherwise. When the function is succesfull, the character read is - * written into its argument c. - */ - -int usbtty_getc(struct stdio_dev *dev) -{ - char c; - struct usb_endpoint_instance *endpoint = - &endpoint_instance[rx_endpoint]; - - while (usbtty_input.size <= 0) { - udc_unset_nak(endpoint->endpoint_address&0x03); - usbtty_poll (); - } - - buf_pop (&usbtty_input, &c, 1); - udc_set_nak(endpoint->endpoint_address&0x03); - - return c; -} - -/* - * Output a single byte to the usb client port. - */ -void usbtty_putc(struct stdio_dev *dev, const char c) -{ - if (!usbtty_configured ()) - return; - - /* If \n, also do \r */ - if (c == '\n') - buf_push (&usbtty_output, "\r", 1); - - buf_push(&usbtty_output, &c, 1); - - /* Poll at end to handle new data... */ - if ((usbtty_output.size + 2) >= usbtty_output.totalsize) { - usbtty_poll (); - } -} - -/* usbtty_puts() helper function for finding the next '\n' in a string */ -static int next_nl_pos (const char *s) -{ - int i; - - for (i = 0; s[i] != '\0'; i++) { - if (s[i] == '\n') - return i; - } - return i; -} - -/* - * Output a string to the usb client port - implementing flow control - */ - -static void __usbtty_puts (const char *str, int len) -{ - int maxlen = usbtty_output.totalsize; - int space, n; - - /* break str into chunks < buffer size, if needed */ - while (len > 0) { - usbtty_poll (); - - space = maxlen - usbtty_output.size; - /* Empty buffer here, if needed, to ensure space... */ - if (space) { - write_buffer (&usbtty_output); - - n = min(space, min(len, maxlen)); - buf_push (&usbtty_output, str, n); - - str += n; - len -= n; - } - } -} - -void usbtty_puts(struct stdio_dev *dev, const char *str) -{ - int n; - int len; - - if (!usbtty_configured ()) - return; - - len = strlen (str); - /* add '\r' for each '\n' */ - while (len > 0) { - n = next_nl_pos (str); - - if (str[n] == '\n') { - __usbtty_puts(str, n); - __usbtty_puts("\r\n", 2); - str += (n + 1); - len -= (n + 1); - } else { - /* No \n found. All done. */ - __usbtty_puts (str, n); - break; - } - } - - /* Poll at end to handle new data... */ - usbtty_poll (); -} - -/* - * Initialize the usb client port. - * - */ -int drv_usbtty_init (void) -{ - int rc; - char * sn; - char * tt; - int snlen; - - /* Get serial number */ - sn = env_get("serial#"); - if (!sn) - sn = "000000000000"; - snlen = strlen(sn); - if (snlen > sizeof(serial_number) - 1) { - printf ("Warning: serial number %s is too long (%d > %lu)\n", - sn, snlen, (ulong)(sizeof(serial_number) - 1)); - snlen = sizeof(serial_number) - 1; - } - memcpy (serial_number, sn, snlen); - serial_number[snlen] = '\0'; - - /* Decide on which type of UDC device to be. - */ - tt = env_get("usbtty"); - if (!tt) - tt = "generic"; - usbtty_init_terminal_type(strcmp(tt,"cdc_acm")); - - /* prepare buffers... */ - buf_init (&usbtty_input, USBTTY_BUFFER_SIZE); - buf_init (&usbtty_output, USBTTY_BUFFER_SIZE); - - /* Now, set up USB controller and infrastructure */ - udc_init (); /* Basic USB initialization */ - - usbtty_init_strings (); - usbtty_init_instances (); - - usbtty_init_endpoints (); - - udc_startup_events (device_instance);/* Enable dev, init udc pointers */ - udc_connect (); /* Enable pullup for host detection */ - - /* Device initialization */ - memset (&usbttydev, 0, sizeof (usbttydev)); - - strcpy (usbttydev.name, "usbtty"); - usbttydev.ext = 0; /* No extensions */ - usbttydev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_OUTPUT; - usbttydev.tstc = usbtty_tstc; /* 'tstc' function */ - usbttydev.getc = usbtty_getc; /* 'getc' function */ - usbttydev.putc = usbtty_putc; /* 'putc' function */ - usbttydev.puts = usbtty_puts; /* 'puts' function */ - - rc = stdio_register (&usbttydev); - - return (rc == 0) ? 1 : rc; -} - -static void usbtty_init_strings (void) -{ - struct usb_string_descriptor *string; - - usbtty_string_table[STR_LANG] = - (struct usb_string_descriptor*)wstrLang; - - string = (struct usb_string_descriptor *) wstrManufacturer; - string->bLength = sizeof(wstrManufacturer); - string->bDescriptorType = USB_DT_STRING; - str2wide (CONFIG_USBD_MANUFACTURER, string->wData); - usbtty_string_table[STR_MANUFACTURER]=string; - - string = (struct usb_string_descriptor *) wstrProduct; - string->bLength = sizeof(wstrProduct); - string->bDescriptorType = USB_DT_STRING; - str2wide (CONFIG_USBD_PRODUCT_NAME, string->wData); - usbtty_string_table[STR_PRODUCT]=string; - - string = (struct usb_string_descriptor *) wstrSerial; - string->bLength = sizeof(serial_number); - string->bDescriptorType = USB_DT_STRING; - str2wide (serial_number, string->wData); - usbtty_string_table[STR_SERIAL]=string; - - string = (struct usb_string_descriptor *) wstrConfiguration; - string->bLength = sizeof(wstrConfiguration); - string->bDescriptorType = USB_DT_STRING; - str2wide (CFG_USBD_CONFIGURATION_STR, string->wData); - usbtty_string_table[STR_CONFIG]=string; - - string = (struct usb_string_descriptor *) wstrDataInterface; - string->bLength = sizeof(wstrDataInterface); - string->bDescriptorType = USB_DT_STRING; - str2wide (CFG_USBD_DATA_INTERFACE_STR, string->wData); - usbtty_string_table[STR_DATA_INTERFACE]=string; - - string = (struct usb_string_descriptor *) wstrCtrlInterface; - string->bLength = sizeof(wstrCtrlInterface); - string->bDescriptorType = USB_DT_STRING; - str2wide (CFG_USBD_CTRL_INTERFACE_STR, string->wData); - usbtty_string_table[STR_CTRL_INTERFACE]=string; - - /* Now, initialize the string table for ep0 handling */ - usb_strings = usbtty_string_table; -} - -#define init_wMaxPacketSize(x) le16_to_cpu(get_unaligned(\ - &ep_descriptor_ptrs[(x) - 1]->wMaxPacketSize)); - -static void usbtty_init_instances (void) -{ - int i; - - /* initialize device instance */ - memset (device_instance, 0, sizeof (struct usb_device_instance)); - device_instance->device_state = STATE_INIT; - device_instance->device_descriptor = &device_descriptor; - device_instance->event = usbtty_event_handler; - device_instance->cdc_recv_setup = usbtty_cdc_setup; - device_instance->bus = bus_instance; - device_instance->configurations = NUM_CONFIGS; - device_instance->configuration_instance_array = config_instance; - - /* initialize bus instance */ - memset (bus_instance, 0, sizeof (struct usb_bus_instance)); - bus_instance->device = device_instance; - bus_instance->endpoint_array = endpoint_instance; - bus_instance->max_endpoints = 1; - bus_instance->maxpacketsize = 64; - bus_instance->serial_number_str = serial_number; - - /* configuration instance */ - memset (config_instance, 0, - sizeof (struct usb_configuration_instance)); - config_instance->interfaces = interface_count; - config_instance->configuration_descriptor = configuration_descriptor; - config_instance->interface_instance_array = interface_instance; - - /* interface instance */ - memset (interface_instance, 0, - sizeof (struct usb_interface_instance)); - interface_instance->alternates = 1; - interface_instance->alternates_instance_array = alternate_instance; - - /* alternates instance */ - memset (alternate_instance, 0, - sizeof (struct usb_alternate_instance)); - alternate_instance->interface_descriptor = interface_descriptors; - alternate_instance->endpoints = NUM_ENDPOINTS; - alternate_instance->endpoints_descriptor_array = ep_descriptor_ptrs; - - /* endpoint instances */ - memset (&endpoint_instance[0], 0, - sizeof (struct usb_endpoint_instance)); - endpoint_instance[0].endpoint_address = 0; - endpoint_instance[0].rcv_packetSize = EP0_MAX_PACKET_SIZE; - endpoint_instance[0].rcv_attributes = USB_ENDPOINT_XFER_CONTROL; - endpoint_instance[0].tx_packetSize = EP0_MAX_PACKET_SIZE; - endpoint_instance[0].tx_attributes = USB_ENDPOINT_XFER_CONTROL; - udc_setup_ep (device_instance, 0, &endpoint_instance[0]); - - for (i = 1; i <= NUM_ENDPOINTS; i++) { - memset (&endpoint_instance[i], 0, - sizeof (struct usb_endpoint_instance)); - - endpoint_instance[i].endpoint_address = - ep_descriptor_ptrs[i - 1]->bEndpointAddress; - - endpoint_instance[i].rcv_attributes = - ep_descriptor_ptrs[i - 1]->bmAttributes; - - endpoint_instance[i].rcv_packetSize = init_wMaxPacketSize(i); - - endpoint_instance[i].tx_attributes = - ep_descriptor_ptrs[i - 1]->bmAttributes; - - endpoint_instance[i].tx_packetSize = init_wMaxPacketSize(i); - - endpoint_instance[i].tx_attributes = - ep_descriptor_ptrs[i - 1]->bmAttributes; - - urb_link_init (&endpoint_instance[i].rcv); - urb_link_init (&endpoint_instance[i].rdy); - urb_link_init (&endpoint_instance[i].tx); - urb_link_init (&endpoint_instance[i].done); - - if (endpoint_instance[i].endpoint_address & USB_DIR_IN) - endpoint_instance[i].tx_urb = - usbd_alloc_urb (device_instance, - &endpoint_instance[i]); - else - endpoint_instance[i].rcv_urb = - usbd_alloc_urb (device_instance, - &endpoint_instance[i]); - } -} - -static void usbtty_init_endpoints (void) -{ - int i; - - bus_instance->max_endpoints = NUM_ENDPOINTS + 1; - for (i = 1; i <= NUM_ENDPOINTS; i++) { - udc_setup_ep (device_instance, i, &endpoint_instance[i]); - } -} - -/* usbtty_init_terminal_type - * - * Do some late binding for our device type. - */ -static void usbtty_init_terminal_type(short type) -{ - switch(type){ - /* CDC ACM */ - case 0: - /* Assign endpoint descriptors */ - ep_descriptor_ptrs[0] = - &acm_configuration_descriptors[0].notification_endpoint; - ep_descriptor_ptrs[1] = - &acm_configuration_descriptors[0].data_endpoints[0]; - ep_descriptor_ptrs[2] = - &acm_configuration_descriptors[0].data_endpoints[1]; - - /* Enumerate Device Descriptor */ - device_descriptor.bDeviceClass = - COMMUNICATIONS_DEVICE_CLASS; - device_descriptor.idProduct = - cpu_to_le16(CONFIG_USBD_PRODUCTID_CDCACM); - - /* Assign endpoint indices */ - tx_endpoint = ACM_TX_ENDPOINT; - rx_endpoint = ACM_RX_ENDPOINT; - - /* Configuration Descriptor */ - configuration_descriptor = - (struct usb_configuration_descriptor*) - &acm_configuration_descriptors; - - /* Interface count */ - interface_count = NUM_ACM_INTERFACES; - break; - - /* BULK IN/OUT & Default */ - case 1: - default: - /* Assign endpoint descriptors */ - ep_descriptor_ptrs[0] = - &gserial_configuration_descriptors[0].data_endpoints[0]; - ep_descriptor_ptrs[1] = - &gserial_configuration_descriptors[0].data_endpoints[1]; - ep_descriptor_ptrs[2] = - &gserial_configuration_descriptors[0].data_endpoints[2]; - - /* Enumerate Device Descriptor */ - device_descriptor.bDeviceClass = 0xFF; - device_descriptor.idProduct = - cpu_to_le16(CONFIG_USBD_PRODUCTID_GSERIAL); - /* Assign endpoint indices */ - tx_endpoint = GSERIAL_TX_ENDPOINT; - rx_endpoint = GSERIAL_RX_ENDPOINT; - - /* Configuration Descriptor */ - configuration_descriptor = - (struct usb_configuration_descriptor*) - &gserial_configuration_descriptors; - - /* Interface count */ - interface_count = NUM_GSERIAL_INTERFACES; - break; - } -} - -/******************************************************************************/ - -static struct urb *next_urb (struct usb_device_instance *device, - struct usb_endpoint_instance *endpoint) -{ - struct urb *current_urb = NULL; - int space; - - /* If there's a queue, then we should add to the last urb */ - if (!endpoint->tx_queue) { - current_urb = endpoint->tx_urb; - } else { - /* Last urb from tx chain */ - current_urb = - p2surround (struct urb, link, endpoint->tx.prev); - } - - /* Make sure this one has enough room */ - space = current_urb->buffer_length - current_urb->actual_length; - if (space > 0) { - return current_urb; - } else { /* No space here */ - /* First look at done list */ - current_urb = first_urb_detached (&endpoint->done); - if (!current_urb) { - current_urb = usbd_alloc_urb (device, endpoint); - } - - urb_append (&endpoint->tx, current_urb); - endpoint->tx_queue++; - } - return current_urb; -} - -static int write_buffer (circbuf_t * buf) -{ - if (!usbtty_configured ()) { - return 0; - } - - struct usb_endpoint_instance *endpoint = - &endpoint_instance[tx_endpoint]; - struct urb *current_urb = NULL; - - /* TX data still exists - send it now - */ - if(endpoint->sent < endpoint->tx_urb->actual_length){ - if(udc_endpoint_write (endpoint)){ - /* Write pre-empted by RX */ - return -1; - } - } - - if (buf->size) { - char *dest; - - int space_avail; - int popnum, popped; - int total = 0; - - /* Break buffer into urb sized pieces, - * and link each to the endpoint - */ - while (buf->size > 0) { - - current_urb = next_urb (device_instance, endpoint); - - dest = (char*)current_urb->buffer + - current_urb->actual_length; - - space_avail = - current_urb->buffer_length - - current_urb->actual_length; - popnum = min(space_avail, (int)buf->size); - if (popnum == 0) - break; - - popped = buf_pop (buf, dest, popnum); - if (popped == 0) - break; - current_urb->actual_length += popped; - total += popped; - - /* If endpoint->last == 0, then transfers have - * not started on this endpoint - */ - if (endpoint->last == 0) { - if(udc_endpoint_write (endpoint)){ - /* Write pre-empted by RX */ - return -1; - } - } - - }/* end while */ - return total; - } - - return 0; -} - -static int fill_buffer (circbuf_t * buf) -{ - struct usb_endpoint_instance *endpoint = - &endpoint_instance[rx_endpoint]; - - if (endpoint->rcv_urb && endpoint->rcv_urb->actual_length) { - unsigned int nb = 0; - char *src = (char *) endpoint->rcv_urb->buffer; - unsigned int rx_avail = buf->totalsize - buf->size; - - if(rx_avail >= endpoint->rcv_urb->actual_length){ - - nb = endpoint->rcv_urb->actual_length; - buf_push (buf, src, nb); - endpoint->rcv_urb->actual_length = 0; - - } - return nb; - } - return 0; -} - -static int usbtty_configured (void) -{ - return usbtty_configured_flag; -} - -/******************************************************************************/ - -static void usbtty_event_handler (struct usb_device_instance *device, - usb_device_event_t event, int data) -{ - switch (event) { - case DEVICE_RESET: - case DEVICE_BUS_INACTIVE: - usbtty_configured_flag = 0; - break; - case DEVICE_CONFIGURED: - usbtty_configured_flag = 1; - break; - - case DEVICE_ADDRESS_ASSIGNED: - usbtty_init_endpoints (); - - default: - break; - } -} - -/******************************************************************************/ - -int usbtty_cdc_setup(struct usb_device_request *request, struct urb *urb) -{ - switch (request->bRequest){ - - case ACM_SET_CONTROL_LINE_STATE: /* Implies DTE ready */ - break; - case ACM_SEND_ENCAPSULATED_COMMAND : /* Required */ - break; - case ACM_SET_LINE_ENCODING : /* DTE stop/parity bits - * per character */ - break; - case ACM_GET_ENCAPSULATED_RESPONSE : /* request response */ - break; - case ACM_GET_LINE_ENCODING : /* request DTE rate, - * stop/parity bits */ - memcpy (urb->buffer , &rs232_desc, sizeof(rs232_desc)); - urb->actual_length = sizeof(rs232_desc); - - break; - default: - return 1; - } - return 0; -} - -/******************************************************************************/ - -/* - * Since interrupt handling has not yet been implemented, we use this function - * to handle polling. This is called by the tstc,getc,putc,puts routines to - * update the USB state. - */ -void usbtty_poll (void) -{ - /* New interrupts? */ - udc_irq(); - - /* Write any output data to host buffer - * (do this before checking interrupts to avoid missing one) - */ - if (usbtty_configured ()) { - write_buffer (&usbtty_output); - } - - /* New interrupts? */ - udc_irq(); - - /* Check for new data from host.. - * (do this after checking interrupts to get latest data) - */ - if (usbtty_configured ()) { - fill_buffer (&usbtty_input); - } - - /* New interrupts? */ - udc_irq(); - -} diff --git a/drivers/serial/usbtty.h b/drivers/serial/usbtty.h deleted file mode 100644 index b176a7961b8..00000000000 --- a/drivers/serial/usbtty.h +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * (C) Copyright 2003 - * Gerry Hamel, geh@ti.com, Texas Instruments - * - * (C) Copyright 2006 - * Bryan O'Donoghue, bodonoghue@codehermit.ie, CodeHermit - */ - -#ifndef __USB_TTY_H__ -#define __USB_TTY_H__ - -#include <usbdevice.h> -#if defined(CONFIG_PPC) -#include <usb/mpc8xx_udc.h> -#elif defined(CONFIG_CI_UDC) -#include <usb/ci_udc.h> -#endif - -#include <usb/udc.h> -#include <version.h> - -#ifndef CFG_USBD_CONFIGURATION_STR -#define CFG_USBD_CONFIGURATION_STR "TTY via USB" -#endif - -#define CFG_USBD_SERIAL_OUT_ENDPOINT UDC_OUT_ENDPOINT -#define CFG_USBD_SERIAL_OUT_PKTSIZE UDC_OUT_PACKET_SIZE -#define CFG_USBD_SERIAL_IN_ENDPOINT UDC_IN_ENDPOINT -#define CFG_USBD_SERIAL_IN_PKTSIZE UDC_IN_PACKET_SIZE -#define CFG_USBD_SERIAL_INT_ENDPOINT UDC_INT_ENDPOINT -#define CFG_USBD_SERIAL_INT_PKTSIZE UDC_INT_PACKET_SIZE -#define CFG_USBD_SERIAL_BULK_PKTSIZE UDC_BULK_PACKET_SIZE - -#define USBTTY_DEVICE_CLASS COMMUNICATIONS_DEVICE_CLASS - -#define USBTTY_BCD_DEVICE 0x00 -#define USBTTY_MAXPOWER 0x00 - -#define STR_LANG 0x00 -#define STR_MANUFACTURER 0x01 -#define STR_PRODUCT 0x02 -#define STR_SERIAL 0x03 -#define STR_CONFIG 0x04 -#define STR_DATA_INTERFACE 0x05 -#define STR_CTRL_INTERFACE 0x06 -#define STR_COUNT 0x07 - -#endif diff --git a/drivers/soc/soc_ti_k3.c b/drivers/soc/soc_ti_k3.c index a3acca4d394..b34cbd08e07 100644 --- a/drivers/soc/soc_ti_k3.c +++ b/drivers/soc/soc_ti_k3.c @@ -18,8 +18,12 @@ struct soc_ti_k3_plat { static const char *get_family_string(u32 idreg) { const char *family; + u32 jtag_dev_id; + u32 pkg; u32 soc; + jtag_dev_id = readl(CTRLMMR_WKUP_JTAG_DEVICE_ID); + soc = (idreg & JTAG_ID_PARTNO_MASK) >> JTAG_ID_PARTNO_SHIFT; switch (soc) { @@ -51,8 +55,16 @@ static const char *get_family_string(u32 idreg) family = "J722S"; break; case JTAG_ID_PARTNO_J784S4: - family = "J784S4"; - break; + { + /* Keep default family as J784S4 */ + family = "J784S4"; + + pkg = (jtag_dev_id & JTAG_DEV_J742S2_PKG_MASK) >> JTAG_DEV_J742S2_PKG_SHIFT; + if (pkg == JTAG_ID_PKG_J742S2) + family = "J742S2"; + + break; + } default: family = "Unknown Silicon"; }; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 96ea033082b..f475f341c9c 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -52,6 +52,13 @@ config SPI_DIRMAP if DM_SPI +config ADI_SPI3 + bool "Enable ADI SPI Driver" + depends on ARCH_SC5XX + help + Enable the ADI (Analog Devices) SPI controller driver. This + driver enables the support for SC5XX spi controller. + config ALTERA_SPI bool "Altera SPI driver" help @@ -85,7 +92,7 @@ config ATH79_SPI config ATMEL_QSPI bool "Atmel Quad SPI Controller" - depends on ARCH_AT91 + depends on ARCH_AT91 && SPI_MEM help Enable the Atmel Quad SPI controller in master mode. This driver does not support generic SPI. The implementation supports only the @@ -135,7 +142,7 @@ config BCMSTB_SPI config CORTINA_SFLASH bool "Cortina-Access Serial Flash controller driver" - depends on DM_SPI && SPI_MEM + depends on SPI_MEM help Enable the Cortina-Access Serial Flash controller driver. This driver can be used to access the SPI NOR/NAND flash on platforms embedding this diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 7051e2a00c6..21895d46429 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -19,6 +19,7 @@ obj-y += spi.o obj-$(CONFIG_SPI_MEM) += spi-mem-nodm.o endif +obj-$(CONFIG_ADI_SPI3) += adi_spi3.o obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_APPLE_SPI) += apple_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o diff --git a/drivers/spi/adi_spi3.c b/drivers/spi/adi_spi3.c new file mode 100644 index 00000000000..2125d25561a --- /dev/null +++ b/drivers/spi/adi_spi3.c @@ -0,0 +1,679 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Converted to driver model by Nathan Barrett-Morrison + * + * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * Contact: Greg Malysa <greg.malysa@timesys.com> + * Contact: Ian Roberts <ian.roberts@timesys.com> + * Contact: Piotr Wojtaszczyk <piotr.wojtaszczyk@timesys.com> + * + */ + +#include <clk.h> +#include <dm.h> +#include <mapmem.h> +#include <spi.h> +#include <spi-mem.h> +#include <dm/device_compat.h> +#include <linux/io.h> + +#define SPI_IDLE_VAL 0xff + +#define MAX_CTRL_CS 7 + +/* SPI_CONTROL */ +#define SPI_CTL_EN 0x00000001 /* Enable */ +#define SPI_CTL_MSTR 0x00000002 /* Master/Slave */ +#define SPI_CTL_PSSE 0x00000004 /* controls modf error in master mode */ +#define SPI_CTL_ODM 0x00000008 /* Open Drain Mode */ +#define SPI_CTL_CPHA 0x00000010 /* Clock Phase */ +#define SPI_CTL_CPOL 0x00000020 /* Clock Polarity */ +#define SPI_CTL_ASSEL 0x00000040 /* Slave Select Pin Control */ +#define SPI_CTL_SELST 0x00000080 /* Slave Select Polarity in transfers */ +#define SPI_CTL_EMISO 0x00000100 /*Enable MISO */ +#define SPI_CTL_SIZE 0x00000600 /*Word Transfer Size */ +#define SPI_CTL_SIZE08 0x00000000 /*SIZE: 8 bits */ +#define SPI_CTL_SIZE16 0x00000200 /*SIZE: 16 bits */ +#define SPI_CTL_SIZE32 0x00000400 /*SIZE: 32 bits */ +#define SPI_CTL_LSBF 0x00001000 /*LSB First */ +#define SPI_CTL_FCEN 0x00002000 /*Flow-Control Enable */ +#define SPI_CTL_FCCH 0x00004000 /*Flow-Control Channel Selection */ +#define SPI_CTL_FCPL 0x00008000 /*Flow-Control Polarity */ +#define SPI_CTL_FCWM 0x00030000 /*Flow-Control Water-Mark */ +#define SPI_CTL_FIFO0 0x00000000 /*FCWM: Tx empty or Rx Full */ +#define SPI_CTL_FIFO1 0x00010000 /*FCWM: Tx empty or Rx full (>=75%) */ +#define SPI_CTL_FIFO2 0x00020000 /*FCWM: Tx empty or Rx full (>=50%) */ +#define SPI_CTL_FMODE 0x00040000 /*Fast-mode Enable */ +#define SPI_CTL_MIOM 0x00300000 /*Multiple I/O Mode */ +#define SPI_CTL_MIO_DIS 0x00000000 /*MIOM: Disable */ +#define SPI_CTL_MIO_DUAL 0x00100000 /*MIOM: Enable DIOM (Dual I/O Mode) */ +#define SPI_CTL_MIO_QUAD 0x00200000 /*MIOM: Enable QUAD (Quad SPI Mode) */ +#define SPI_CTL_SOSI 0x00400000 /*Start on MOSI */ +#define SPI_CTL_MMWEM 0x40000000 /*Start on MMWEM */ +#define SPI_CTL_MMSE 0x80000000 /*Start on MMSE */ +/* SPI_RX_CONTROL */ +#define SPI_RXCTL_REN 0x00000001 /*Receive Channel Enable */ +#define SPI_RXCTL_RTI 0x00000004 /*Receive Transfer Initiate */ +#define SPI_RXCTL_RWCEN 0x00000008 /*Receive Word Counter Enable */ +#define SPI_RXCTL_RDR 0x00000070 /*Receive Data Request */ +#define SPI_RXCTL_RDR_DIS 0x00000000 /*RDR: Disabled */ +#define SPI_RXCTL_RDR_NE 0x00000010 /*RDR: RFIFO not empty */ +#define SPI_RXCTL_RDR_25 0x00000020 /*RDR: RFIFO 25% full */ +#define SPI_RXCTL_RDR_50 0x00000030 /*RDR: RFIFO 50% full */ +#define SPI_RXCTL_RDR_75 0x00000040 /*RDR: RFIFO 75% full */ +#define SPI_RXCTL_RDR_FULL 0x00000050 /*RDR: RFIFO full */ +#define SPI_RXCTL_RDO 0x00000100 /*Receive Data Over-Run */ +#define SPI_RXCTL_RRWM 0x00003000 /*FIFO Regular Water-Mark */ +#define SPI_RXCTL_RWM_0 0x00000000 /*RRWM: RFIFO Empty */ +#define SPI_RXCTL_RWM_25 0x00001000 /*RRWM: RFIFO 25% full */ +#define SPI_RXCTL_RWM_50 0x00002000 /*RRWM: RFIFO 50% full */ +#define SPI_RXCTL_RWM_75 0x00003000 /*RRWM: RFIFO 75% full */ +#define SPI_RXCTL_RUWM 0x00070000 /*FIFO Urgent Water-Mark */ +#define SPI_RXCTL_UWM_DIS 0x00000000 /*RUWM: Disabled */ +#define SPI_RXCTL_UWM_25 0x00010000 /*RUWM: RFIFO 25% full */ +#define SPI_RXCTL_UWM_50 0x00020000 /*RUWM: RFIFO 50% full */ +#define SPI_RXCTL_UWM_75 0x00030000 /*RUWM: RFIFO 75% full */ +#define SPI_RXCTL_UWM_FULL 0x00040000 /*RUWM: RFIFO full */ +/* SPI_TX_CONTROL */ +#define SPI_TXCTL_TEN 0x00000001 /*Transmit Channel Enable */ +#define SPI_TXCTL_TTI 0x00000004 /*Transmit Transfer Initiate */ +#define SPI_TXCTL_TWCEN 0x00000008 /*Transmit Word Counter Enable */ +#define SPI_TXCTL_TDR 0x00000070 /*Transmit Data Request */ +#define SPI_TXCTL_TDR_DIS 0x00000000 /*TDR: Disabled */ +#define SPI_TXCTL_TDR_NF 0x00000010 /*TDR: TFIFO not full */ +#define SPI_TXCTL_TDR_25 0x00000020 /*TDR: TFIFO 25% empty */ +#define SPI_TXCTL_TDR_50 0x00000030 /*TDR: TFIFO 50% empty */ +#define SPI_TXCTL_TDR_75 0x00000040 /*TDR: TFIFO 75% empty */ +#define SPI_TXCTL_TDR_EMPTY 0x00000050 /*TDR: TFIFO empty */ +#define SPI_TXCTL_TDU 0x00000100 /*Transmit Data Under-Run */ +#define SPI_TXCTL_TRWM 0x00003000 /*FIFO Regular Water-Mark */ +#define SPI_TXCTL_RWM_FULL 0x00000000 /*TRWM: TFIFO full */ +#define SPI_TXCTL_RWM_25 0x00001000 /*TRWM: TFIFO 25% empty */ +#define SPI_TXCTL_RWM_50 0x00002000 /*TRWM: TFIFO 50% empty */ +#define SPI_TXCTL_RWM_75 0x00003000 /*TRWM: TFIFO 75% empty */ +#define SPI_TXCTL_TUWM 0x00070000 /*FIFO Urgent Water-Mark */ +#define SPI_TXCTL_UWM_DIS 0x00000000 /*TUWM: Disabled */ +#define SPI_TXCTL_UWM_25 0x00010000 /*TUWM: TFIFO 25% empty */ +#define SPI_TXCTL_UWM_50 0x00020000 /*TUWM: TFIFO 50% empty */ +#define SPI_TXCTL_UWM_75 0x00030000 /*TUWM: TFIFO 75% empty */ +#define SPI_TXCTL_UWM_EMPTY 0x00040000 /*TUWM: TFIFO empty */ +/* SPI_CLOCK */ +#define SPI_CLK_BAUD 0x0000FFFF /*Baud Rate */ +/* SPI_DELAY */ +#define SPI_DLY_STOP 0x000000FF /*Transfer delay time */ +#define SPI_DLY_LEADX 0x00000100 /*Extended (1 SCK) LEAD Control */ +#define SPI_DLY_LAGX 0x00000200 /*Extended (1 SCK) LAG control */ +/* SPI_SSEL */ +#define SPI_SLVSEL_SSE1 0x00000002 /*SPISSEL1 Enable */ +#define SPI_SLVSEL_SSE2 0x00000004 /*SPISSEL2 Enable */ +#define SPI_SLVSEL_SSE3 0x00000008 /*SPISSEL3 Enable */ +#define SPI_SLVSEL_SSE4 0x00000010 /*SPISSEL4 Enable */ +#define SPI_SLVSEL_SSE5 0x00000020 /*SPISSEL5 Enable */ +#define SPI_SLVSEL_SSE6 0x00000040 /*SPISSEL6 Enable */ +#define SPI_SLVSEL_SSE7 0x00000080 /*SPISSEL7 Enable */ +#define SPI_SLVSEL_SSEL1 0x00000200 /*SPISSEL1 Value */ +#define SPI_SLVSEL_SSEL2 0x00000400 /*SPISSEL2 Value */ +#define SPI_SLVSEL_SSEL3 0x00000800 /*SPISSEL3 Value */ +#define SPI_SLVSEL_SSEL4 0x00001000 /*SPISSEL4 Value */ +#define SPI_SLVSEL_SSEL5 0x00002000 /*SPISSEL5 Value */ +#define SPI_SLVSEL_SSEL6 0x00004000 /*SPISSEL6 Value */ +#define SPI_SLVSEL_SSEL7 0x00008000 /*SPISSEL7 Value */ +/* SPI_RWC */ +#define SPI_RWC_VALUE 0x0000FFFF /*Received Word-Count */ +/* SPI_RWCR */ +#define SPI_RWCR_VALUE 0x0000FFFF /*Received Word-Count Reload */ +/* SPI_TWC */ +#define SPI_TWC_VALUE 0x0000FFFF /*Transmitted Word-Count */ +/* SPI_TWCR */ +#define SPI_TWCR_VALUE 0x0000FFFF /*Transmitted Word-Count Reload */ +/* SPI_IMASK */ +#define SPI_IMSK_RUWM 0x00000002 /*Receive Water-Mark Interrupt Mask */ +#define SPI_IMSK_TUWM 0x00000004 /*Transmit Water-Mark Interrupt Mask */ +#define SPI_IMSK_ROM 0x00000010 /*Receive Over-Run Interrupt Mask */ +#define SPI_IMSK_TUM 0x00000020 /*Transmit Under-Run Interrupt Mask */ +#define SPI_IMSK_TCM 0x00000040 /*Transmit Collision Interrupt Mask */ +#define SPI_IMSK_MFM 0x00000080 /*Mode Fault Interrupt Mask */ +#define SPI_IMSK_RSM 0x00000100 /*Receive Start Interrupt Mask */ +#define SPI_IMSK_TSM 0x00000200 /*Transmit Start Interrupt Mask */ +#define SPI_IMSK_RFM 0x00000400 /*Receive Finish Interrupt Mask */ +#define SPI_IMSK_TFM 0x00000800 /*Transmit Finish Interrupt Mask */ +/* SPI_IMASKCL */ +#define SPI_IMSK_CLR_RUW 0x00000002 /*Receive Water-Mark Interrupt Mask */ +#define SPI_IMSK_CLR_TUWM 0x00000004 /*Transmit Water-Mark Interrupt Mask */ +#define SPI_IMSK_CLR_ROM 0x00000010 /*Receive Over-Run Interrupt Mask */ +#define SPI_IMSK_CLR_TUM 0x00000020 /*Transmit Under-Run Interrupt Mask */ +#define SPI_IMSK_CLR_TCM 0x00000040 /*Transmit Collision Interrupt Mask */ +#define SPI_IMSK_CLR_MFM 0x00000080 /*Mode Fault Interrupt Mask */ +#define SPI_IMSK_CLR_RSM 0x00000100 /*Receive Start Interrupt Mask */ +#define SPI_IMSK_CLR_TSM 0x00000200 /*Transmit Start Interrupt Mask */ +#define SPI_IMSK_CLR_RFM 0x00000400 /*Receive Finish Interrupt Mask */ +#define SPI_IMSK_CLR_TFM 0x00000800 /*Transmit Finish Interrupt Mask */ +/* SPI_IMASKST */ +#define SPI_IMSK_SET_RUWM 0x00000002 /*Receive Water-Mark Interrupt Mask */ +#define SPI_IMSK_SET_TUWM 0x00000004 /*Transmit Water-Mark Interrupt Mask */ +#define SPI_IMSK_SET_ROM 0x00000010 /*Receive Over-Run Interrupt Mask */ +#define SPI_IMSK_SET_TUM 0x00000020 /*Transmit Under-Run Interrupt Mask */ +#define SPI_IMSK_SET_TCM 0x00000040 /*Transmit Collision Interrupt Mask */ +#define SPI_IMSK_SET_MFM 0x00000080 /*Mode Fault Interrupt Mask */ +#define SPI_IMSK_SET_RSM 0x00000100 /*Receive Start Interrupt Mask */ +#define SPI_IMSK_SET_TSM 0x00000200 /*Transmit Start Interrupt Mask */ +#define SPI_IMSK_SET_RFM 0x00000400 /*Receive Finish Interrupt Mask */ +#define SPI_IMSK_SET_TFM 0x00000800 /*Transmit Finish Interrupt Mask */ +/* SPI_STATUS */ +#define SPI_STAT_SPIF 0x00000001 /*SPI Finished */ +#define SPI_STAT_RUWM 0x00000002 /*Receive Water-Mark Breached */ +#define SPI_STAT_TUWM 0x00000004 /*Transmit Water-Mark Breached */ +#define SPI_STAT_ROE 0x00000010 /*Receive Over-Run Indication */ +#define SPI_STAT_TUE 0x00000020 /*Transmit Under-Run Indication */ +#define SPI_STAT_TCE 0x00000040 /*Transmit Collision Indication */ +#define SPI_STAT_MODF 0x00000080 /*Mode Fault Indication */ +#define SPI_STAT_RS 0x00000100 /*Receive Start Indication */ +#define SPI_STAT_TS 0x00000200 /*Transmit Start Indication */ +#define SPI_STAT_RF 0x00000400 /*Receive Finish Indication */ +#define SPI_STAT_TF 0x00000800 /*Transmit Finish Indication */ +#define SPI_STAT_RFS 0x00007000 /*SPI_RFIFO status */ +#define SPI_STAT_RFIFO_EMPTY 0x00000000 /*RFS: RFIFO Empty */ +#define SPI_STAT_RFIFO_25 0x00001000 /*RFS: RFIFO 25% Full */ +#define SPI_STAT_RFIFO_50 0x00002000 /*RFS: RFIFO 50% Full */ +#define SPI_STAT_RFIFO_75 0x00003000 /*RFS: RFIFO 75% Full */ +#define SPI_STAT_RFIFO_FULL 0x00004000 /*RFS: RFIFO Full */ +#define SPI_STAT_TFS 0x00070000 /*SPI_TFIFO status */ +#define SPI_STAT_TFIFO_FULL 0x00000000 /*TFS: TFIFO full */ +#define SPI_STAT_TFIFO_25 0x00010000 /*TFS: TFIFO 25% empty */ +#define SPI_STAT_TFIFO_50 0x00020000 /*TFS: TFIFO 50% empty */ +#define SPI_STAT_TFIFO_75 0x00030000 /*TFS: TFIFO 75% empty */ +#define SPI_STAT_TFIFO_EMPTY 0x00040000 /*TFS: TFIFO empty */ +#define SPI_STAT_FCS 0x00100000 /*Flow-Control Stall Indication */ +#define SPI_STAT_RFE 0x00400000 /*SPI_RFIFO Empty */ +#define SPI_STAT_TFF 0x00800000 /*SPI_TFIFO Full */ +/* SPI_ILAT */ +#define SPI_ILAT_RUWMI 0x00000002 /*Receive Water Mark Interrupt */ +#define SPI_ILAT_TUWMI 0x00000004 /*Transmit Water Mark Interrupt */ +#define SPI_ILAT_ROI 0x00000010 /*Receive Over-Run Indication */ +#define SPI_ILAT_TUI 0x00000020 /*Transmit Under-Run Indication */ +#define SPI_ILAT_TCI 0x00000040 /*Transmit Collision Indication */ +#define SPI_ILAT_MFI 0x00000080 /*Mode Fault Indication */ +#define SPI_ILAT_RSI 0x00000100 /*Receive Start Indication */ +#define SPI_ILAT_TSI 0x00000200 /*Transmit Start Indication */ +#define SPI_ILAT_RFI 0x00000400 /*Receive Finish Indication */ +#define SPI_ILAT_TFI 0x00000800 /*Transmit Finish Indication */ +/* SPI_ILATCL */ +#define SPI_ILAT_CLR_RUWMI 0x00000002 /*Receive Water Mark Interrupt */ +#define SPI_ILAT_CLR_TUWMI 0x00000004 /*Transmit Water Mark Interrupt */ +#define SPI_ILAT_CLR_ROI 0x00000010 /*Receive Over-Run Indication */ +#define SPI_ILAT_CLR_TUI 0x00000020 /*Transmit Under-Run Indication */ +#define SPI_ILAT_CLR_TCI 0x00000040 /*Transmit Collision Indication */ +#define SPI_ILAT_CLR_MFI 0x00000080 /*Mode Fault Indication */ +#define SPI_ILAT_CLR_RSI 0x00000100 /*Receive Start Indication */ +#define SPI_ILAT_CLR_TSI 0x00000200 /*Transmit Start Indication */ +#define SPI_ILAT_CLR_RFI 0x00000400 /*Receive Finish Indication */ +#define SPI_ILAT_CLR_TFI 0x00000800 /*Transmit Finish Indication */ +/* SPI_MMRDH */ +#define SPI_MMRDH_MERGE 0x04000000 /*Merge Enable */ +#define SPI_MMRDH_DMY_SZ 0x00007000 /*Bytes of Dummy */ +#define SPI_MMRDH_ADDR_PINS 0x00000800 /*Pins used for Address */ +#define SPI_MMRDH_ADDR_SZ 0x00000700 /*Bytes of Read Address */ +#define SPI_MMRDH_OPCODE 0x000000FF /*Read Opcode */ + +#define SPI_MMRDH_TRIDMY_OFF 24 /*Bytes of Dummy offset */ +#define SPI_MMRDH_DMY_SZ_OFF 12 /*Bytes of Dummy offset */ +#define SPI_MMRDH_ADDR_SZ_OFF 8 /*Bytes of Read Address offset */ + +#define BIT_SSEL_VAL(x) ((1 << 8) << (x)) /* Slave Select input value bit */ +#define BIT_SSEL_EN(x) (1 << (x)) /* Slave Select enable bit*/ + +struct adi_spi_regs { + u32 revid; + u32 control; + u32 rx_control; + u32 tx_control; + u32 clock; + u32 delay; + u32 ssel; + u32 rwc; + u32 rwcr; + u32 twc; + u32 twcr; + u32 reserved0; + u32 emask; + u32 emaskcl; + u32 emaskst; + u32 reserved1; + u32 status; + u32 elat; + u32 elatcl; + u32 reserved2; + u32 rfifo; + u32 reserved3; + u32 tfifo; + u32 reserved4; + u32 mmrdh; + u32 mmtop; +}; + +struct adi_spi_platdata { + u32 max_hz; + u32 bus_num; + struct adi_spi_regs __iomem *regs; +}; + +struct adi_spi_priv { + u32 control; + u32 clock; + u32 bus_num; + u32 max_cs; + struct adi_spi_regs __iomem *regs; +}; + +/** + * By convention, this driver uses the same CS numbering that is used with the SSEL bit + * definitions (both here and in the TRM on which this is based), which are 1-indexed not + * 0-indexed. The valid CS range is therefore [1,max_cs], in contrast with other drivers + * where it is [0,max_cs-1]. + */ +static int adi_spi_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + struct adi_spi_priv *priv = dev_get_priv(bus); + + if (cs == 0 || cs > priv->max_cs) { + dev_err(bus, "invalid chipselect %u\n", cs); + return -EINVAL; + } + + return 0; +} + +static int adi_spi_of_to_plat(struct udevice *bus) +{ + struct adi_spi_platdata *plat = dev_get_plat(bus); + fdt_addr_t addr; + + plat->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 500000); + plat->bus_num = dev_read_u32_default(bus, "bus-num", 0); + addr = dev_read_addr(bus); + + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + plat->regs = map_sysmem(addr, sizeof(*plat->regs)); + + return 0; +} + +static int adi_spi_probe(struct udevice *bus) +{ + struct adi_spi_platdata *plat = dev_get_plat(bus); + struct adi_spi_priv *priv = dev_get_priv(bus); + + priv->bus_num = plat->bus_num; + priv->regs = plat->regs; + priv->max_cs = dev_read_u32_default(bus, "num-cs", MAX_CTRL_CS); + + iowrite32(0x0, &plat->regs->control); + iowrite32(0x0, &plat->regs->rx_control); + iowrite32(0x0, &plat->regs->tx_control); + + return 0; +} + +static int adi_spi_remove(struct udevice *dev) +{ + return -ENODEV; +} + +static int adi_spi_claim_bus(struct udevice *dev) +{ + struct adi_spi_priv *priv; + struct udevice *bus = dev->parent; + + priv = dev_get_priv(bus); + + debug("%s: control:%i clock:%i\n", + __func__, priv->control, priv->clock); + + iowrite32(priv->control, &priv->regs->control); + iowrite32(priv->clock, &priv->regs->clock); + iowrite32(0x0, &priv->regs->delay); + + return 0; +} + +static int adi_spi_release_bus(struct udevice *dev) +{ + struct adi_spi_priv *priv; + struct udevice *bus = dev->parent; + + priv = dev_get_priv(bus); + + debug("%s: control:%i clock:%i\n", + __func__, priv->control, priv->clock); + + iowrite32(0x0, &priv->regs->rx_control); + iowrite32(0x0, &priv->regs->tx_control); + iowrite32(0x0, &priv->regs->control); + + return 0; +} + +void adi_spi_enable_ssel(struct adi_spi_priv *priv, int cs) +{ + setbits_32(&priv->regs->ssel, BIT_SSEL_EN(cs)); +} + +void adi_spi_set_ssel(struct adi_spi_priv *priv, int cs, int high) +{ + if (high) + setbits_32(&priv->regs->ssel, BIT_SSEL_VAL(cs)); + else + clrbits_32(&priv->regs->ssel, BIT_SSEL_VAL(cs)); +} + +void adi_spi_cs_activate(struct adi_spi_priv *priv, struct dm_spi_slave_plat *slave_plat) +{ + bool high = slave_plat->mode & SPI_CS_HIGH; + + adi_spi_set_ssel(priv, slave_plat->cs[0], high); + adi_spi_enable_ssel(priv, slave_plat->cs[0]); +} + +void adi_spi_cs_deactivate(struct adi_spi_priv *priv, struct dm_spi_slave_plat *slave_plat) +{ + bool high = slave_plat->mode & SPI_CS_HIGH; + + /* invert CS for matching SSEL to deactivate */ + adi_spi_set_ssel(priv, slave_plat->cs[0], !high); +} + +static void discard_rx_fifo_contents(struct adi_spi_regs *regs) +{ + while (!(ioread32(®s->status) & SPI_STAT_RFE)) + ioread32(®s->rfifo); +} + +static int adi_spi_fifo_mio_xfer(struct adi_spi_priv *priv, const u8 *tx, u8 *rx, + uint bytes, uint32_t mio_mode) +{ + u8 value; + + /* switch current SPI transfer to mio SPI mode */ + clrsetbits_32(&priv->regs->control, SPI_CTL_SOSI, mio_mode); + /* + * Data can only be transferred in one direction in multi-io SPI + * modes, trigger the transfer in respective direction. + */ + if (rx) { + iowrite32(0x0, &priv->regs->tx_control); + iowrite32(SPI_RXCTL_REN | SPI_RXCTL_RTI, &priv->regs->rx_control); + + while (bytes--) { + while (ioread32(&priv->regs->status) & + SPI_STAT_RFE) + if (ctrlc()) + return -1; + value = ioread32(&priv->regs->rfifo); + *rx++ = value; + } + } else if (tx) { + iowrite32(0x0, &priv->regs->rx_control); + iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI, &priv->regs->tx_control); + + while (bytes--) { + value = *tx++; + iowrite32(value, &priv->regs->tfifo); + while (ioread32(&priv->regs->status) & + SPI_STAT_TFF) + if (ctrlc()) + return -1; + } + + /* Wait till the tfifo is empty */ + while ((ioread32(&priv->regs->status) & SPI_STAT_TFS) != SPI_STAT_TFIFO_EMPTY) + if (ctrlc()) + return -1; + } else { + return -1; + } + return 0; +} + +static int adi_spi_fifo_1x_xfer(struct adi_spi_priv *priv, const u8 *tx, u8 *rx, + uint bytes) +{ + u8 value; + + /* + * Set current SPI transfer in normal mode and trigger + * the bi-direction transfer by tx write operation. + */ + iowrite32(priv->control, &priv->regs->control); + iowrite32(SPI_RXCTL_REN, &priv->regs->rx_control); + iowrite32(SPI_TXCTL_TEN | SPI_TXCTL_TTI, &priv->regs->tx_control); + + while (bytes--) { + value = (tx ? *tx++ : SPI_IDLE_VAL); + debug("%s: tx:%x ", __func__, value); + iowrite32(value, &priv->regs->tfifo); + while (ioread32(&priv->regs->status) & SPI_STAT_RFE) + if (ctrlc()) + return -1; + value = ioread32(&priv->regs->rfifo); + if (rx) + *rx++ = value; + debug("rx:%x\n", value); + } + return 0; +} + +static int adi_spi_fifo_xfer(struct adi_spi_priv *priv, int buswidth, + const u8 *tx, u8 *rx, uint bytes) +{ + switch (buswidth) { + case 1: + return adi_spi_fifo_1x_xfer(priv, tx, rx, bytes); + case 2: + return adi_spi_fifo_mio_xfer(priv, tx, rx, bytes, SPI_CTL_MIO_DUAL); + case 4: + return adi_spi_fifo_mio_xfer(priv, tx, rx, bytes, SPI_CTL_MIO_QUAD); + default: + return -ENOTSUPP; + } +} + +static int adi_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev->parent; + struct adi_spi_priv *priv = dev_get_priv(bus); + struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev); + + const u8 *tx = dout; + u8 *rx = din; + uint bytes = bitlen / 8; + int ret = 0; + + debug("%s: bus_num:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, + priv->bus_num, slave_plat->cs[0], bitlen, bytes, flags); + + if (flags & SPI_XFER_BEGIN) + adi_spi_cs_activate(priv, slave_plat); + + if (bitlen == 0) + goto done; + + /* we can only do 8 bit transfers */ + if (bitlen % 8) { + flags |= SPI_XFER_END; + goto done; + } + + /* Discard invalid rx data and empty rfifo */ + discard_rx_fifo_contents(priv->regs); + + ret = adi_spi_fifo_1x_xfer(priv, tx, rx, bytes); + + done: + if (flags & SPI_XFER_END) + adi_spi_cs_deactivate(priv, slave_plat); + + return ret; +} + +static int adi_spi_set_speed(struct udevice *bus, uint speed) +{ + struct adi_spi_platdata *plat = dev_get_plat(bus); + struct adi_spi_priv *priv = dev_get_priv(bus); + int ret; + u32 clock, spi_base_clk; + struct clk spi_clk; + + ret = clk_get_by_name(bus, "spi", &spi_clk); + if (ret < 0) { + dev_err(bus, "Can't get SPI clk: %d\n", ret); + return ret; + } + spi_base_clk = clk_get_rate(&spi_clk); + + if (speed > plat->max_hz) + speed = plat->max_hz; + + if (speed > spi_base_clk) + return -ENODEV; + + clock = spi_base_clk / speed; + if (clock) + clock--; + + priv->clock = clock; + + debug("%s: priv->clock: %x, speed: %x, get_spi_clk(): %x\n", + __func__, clock, speed, spi_base_clk); + + return 0; +} + +static int adi_spi_set_mode(struct udevice *bus, uint mode) +{ + struct adi_spi_priv *priv = dev_get_priv(bus); + u32 reg; + + reg = SPI_CTL_EN | SPI_CTL_MSTR; + if (mode & SPI_CPHA) + reg |= SPI_CTL_CPHA; + if (mode & SPI_CPOL) + reg |= SPI_CTL_CPOL; + if (mode & SPI_LSB_FIRST) + reg |= SPI_CTL_LSBF; + reg &= ~SPI_CTL_ASSEL; + + priv->control = reg; + + debug("%s: control=%d, cs_pol=%d\n", __func__, reg, mode & SPI_CS_HIGH ? 1 : 0); + + return 0; +} + +/** + * U-boot's version of spi-mem does not support mixed bus-width + * commands nor anything more than 1x mode. + * Using a custom exec_op implementation, we can support it. + */ +static int adi_spi_mem_exec_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + int rv = 0; + struct udevice *bus = slave->dev->parent; + struct adi_spi_priv *priv = dev_get_priv(bus); + struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(slave->dev); + u8 tmpbuf[64]; + int i; + + if ((op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes) > + sizeof(tmpbuf)) + return -ENOMEM; + + for (i = 0; i < op->cmd.nbytes; i++) + tmpbuf[i] = op->cmd.opcode >> + (8 * (op->cmd.nbytes - i - 1)); + for (i = 0; i < op->addr.nbytes; i++) + tmpbuf[i + op->cmd.nbytes] = op->addr.val >> + (8 * (op->addr.nbytes - i - 1)); + memset(tmpbuf + op->addr.nbytes + op->cmd.nbytes, 0xff, + op->dummy.nbytes); + + adi_spi_cs_activate(priv, slave_plat); + discard_rx_fifo_contents(priv->regs); + + if (op->cmd.nbytes) { + rv = adi_spi_fifo_xfer(priv, op->cmd.buswidth, + tmpbuf, NULL, op->cmd.nbytes); + if (rv != 0) + goto cleanup; + } + + if (op->addr.nbytes) { + rv = adi_spi_fifo_xfer(priv, op->addr.buswidth, + tmpbuf + op->cmd.nbytes, NULL, + op->addr.nbytes); + if (rv != 0) + goto cleanup; + } + + if (op->dummy.nbytes) { + rv = adi_spi_fifo_xfer(priv, op->dummy.buswidth, + tmpbuf + op->cmd.nbytes + + op->addr.nbytes, + NULL, op->dummy.nbytes); + if (rv != 0) + goto cleanup; + } + + if (op->data.dir == SPI_MEM_DATA_IN) + rv = adi_spi_fifo_xfer(priv, op->data.buswidth, + NULL, op->data.buf.in, + op->data.nbytes); + else if (op->data.dir == SPI_MEM_DATA_OUT) + rv = adi_spi_fifo_xfer(priv, op->data.buswidth, + op->data.buf.out, NULL, + op->data.nbytes); + +cleanup: + adi_spi_cs_deactivate(priv, slave_plat); + return rv; +} + +static const struct spi_controller_mem_ops adi_spi_mem_ops = { + .exec_op = adi_spi_mem_exec_op, +}; + +static const struct dm_spi_ops adi_spi_ops = { + .claim_bus = adi_spi_claim_bus, + .release_bus = adi_spi_release_bus, + .xfer = adi_spi_xfer, + .set_speed = adi_spi_set_speed, + .set_mode = adi_spi_set_mode, + .cs_info = adi_spi_cs_info, + .mem_ops = &adi_spi_mem_ops, +}; + +static const struct udevice_id adi_spi_ids[] = { + { .compatible = "adi,spi3" }, + { } +}; + +U_BOOT_DRIVER(adi_spi3) = { + .name = "adi_spi3", + .id = UCLASS_SPI, + .of_match = adi_spi_ids, + .ops = &adi_spi_ops, + .of_to_plat = adi_spi_of_to_plat, + .probe = adi_spi_probe, + .remove = adi_spi_remove, + .plat_auto = sizeof(struct adi_spi_platdata), + .priv_auto = sizeof(struct adi_spi_priv), + .per_child_auto = sizeof(struct spi_slave), +}; diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 3efb661803b..8aa7a83aef4 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -10,11 +10,13 @@ */ #include <malloc.h> +#include <asm/gpio.h> #include <asm/io.h> #include <clk.h> #include <dm.h> #include <errno.h> #include <fdtdec.h> +#include <log.h> #include <dm/device_compat.h> #include <linux/bitfield.h> #include <linux/bitops.h> @@ -258,6 +260,7 @@ struct atmel_qspi_caps { struct atmel_qspi_priv_ops; +#define MAX_CS_COUNT 2 struct atmel_qspi { void __iomem *regs; void __iomem *mem; @@ -267,6 +270,7 @@ struct atmel_qspi { struct udevice *dev; ulong bus_clk_rate; u32 mr; + struct gpio_desc cs_gpios[MAX_CS_COUNT]; }; struct atmel_qspi_priv_ops { @@ -395,6 +399,26 @@ static void atmel_qspi_write(u32 value, struct atmel_qspi *aq, u32 offset) writel(value, aq->regs + offset); } +static int atmel_qspi_reg_sync(struct atmel_qspi *aq) +{ + u32 val; + + return readl_poll_timeout(aq->regs + QSPI_SR2, val, + !(val & QSPI_SR2_SYNCBSY), + ATMEL_QSPI_SYNC_TIMEOUT); +} + +static int atmel_qspi_update_config(struct atmel_qspi *aq) +{ + int ret; + + ret = atmel_qspi_reg_sync(aq); + if (ret) + return ret; + atmel_qspi_write(QSPI_CR_UPDCFG, aq, QSPI_CR); + return atmel_qspi_reg_sync(aq); +} + static inline bool atmel_qspi_is_compatible(const struct spi_mem_op *op, const struct atmel_qspi_mode *mode) { @@ -458,6 +482,29 @@ static bool atmel_qspi_supports_op(struct spi_slave *slave, return true; } +/* + * Switch QSPI controller between regular SPI mode or Serial Memory Mode (SMM). + */ +static int atmel_qspi_set_serial_memory_mode(struct atmel_qspi *aq, + bool enable) +{ + int ret = 0; + + /* only write if designated state differs from current state */ + if (!!(aq->mr & QSPI_MR_SMM) != enable) { + if (enable) + aq->mr |= QSPI_MR_SMM; + else + aq->mr &= ~QSPI_MR_SMM; + atmel_qspi_write(aq->mr, aq, QSPI_MR); + + if (aq->caps->has_gclk) + ret = atmel_qspi_update_config(aq); + } + + return ret; +} + static int atmel_qspi_set_cfg(struct atmel_qspi *aq, const struct spi_mem_op *op, u32 *offset) { @@ -474,7 +521,7 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq, return mode; ifr |= atmel_qspi_modes[mode].config; - if (op->dummy.buswidth && op->dummy.nbytes) + if (op->dummy.nbytes) dummy_cycles = op->dummy.nbytes * 8 / op->dummy.buswidth; /* @@ -529,43 +576,39 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq, if (dummy_cycles) ifr |= QSPI_IFR_NBDUM(dummy_cycles); - /* Set data enable */ - if (op->data.nbytes) + /* Set data enable and data transfer type. */ + if (op->data.nbytes) { ifr |= QSPI_IFR_DATAEN; - /* - * If the QSPI controller is set in regular SPI mode, set it in - * Serial Memory Mode (SMM). - */ - if (aq->mr != QSPI_MR_SMM) { - atmel_qspi_write(QSPI_MR_SMM, aq, QSPI_MR); - aq->mr = QSPI_MR_SMM; + if (op->addr.nbytes) + ifr |= QSPI_IFR_TFRTYP_MEM; } + mode = atmel_qspi_set_serial_memory_mode(aq, true); + if (mode < 0) + return mode; + /* Clear pending interrupts */ (void)atmel_qspi_read(aq, QSPI_SR); - if (aq->caps->has_ricr) { - if (!op->addr.nbytes && op->data.dir == SPI_MEM_DATA_IN) - ifr |= QSPI_IFR_APBTFRTYP_READ; - - /* Set QSPI Instruction Frame registers */ + /* Set QSPI Instruction Frame registers. */ + if (op->addr.nbytes && !op->data.nbytes) atmel_qspi_write(iar, aq, QSPI_IAR); + + if (aq->caps->has_ricr) { if (op->data.dir == SPI_MEM_DATA_IN) atmel_qspi_write(icr, aq, QSPI_RICR); else atmel_qspi_write(icr, aq, QSPI_WICR); - atmel_qspi_write(ifr, aq, QSPI_IFR); } else { - if (op->data.dir == SPI_MEM_DATA_OUT) + if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) ifr |= QSPI_IFR_SAMA5D2_WRITE_TRSFR; - /* Set QSPI Instruction Frame registers */ - atmel_qspi_write(iar, aq, QSPI_IAR); atmel_qspi_write(icr, aq, QSPI_ICR); - atmel_qspi_write(ifr, aq, QSPI_IFR); } + atmel_qspi_write(ifr, aq, QSPI_IFR); + return 0; } @@ -597,26 +640,6 @@ static int atmel_qspi_transfer(struct atmel_qspi *aq, ATMEL_QSPI_TIMEOUT); } -static int atmel_qspi_reg_sync(struct atmel_qspi *aq) -{ - u32 val; - - return readl_poll_timeout(aq->regs + QSPI_SR2, val, - !(val & QSPI_SR2_SYNCBSY), - ATMEL_QSPI_SYNC_TIMEOUT); -} - -static int atmel_qspi_update_config(struct atmel_qspi *aq) -{ - int ret; - - ret = atmel_qspi_reg_sync(aq); - if (ret) - return ret; - atmel_qspi_write(QSPI_CR_UPDCFG, aq, QSPI_CR); - return atmel_qspi_reg_sync(aq); -} - static int atmel_qspi_sama7g5_set_cfg(struct atmel_qspi *aq, const struct spi_mem_op *op, u32 *offset) { @@ -668,17 +691,9 @@ static int atmel_qspi_sama7g5_set_cfg(struct atmel_qspi *aq, ifr |= QSPI_IFR_TFRTYP_MEM; } - /* - * If the QSPI controller is set in regular SPI mode, set it in - * Serial Memory Mode (SMM). - */ - if (aq->mr != QSPI_MR_SMM) { - atmel_qspi_write(QSPI_MR_SMM | QSPI_MR_DQSDLYEN, aq, QSPI_MR); - ret = atmel_qspi_update_config(aq); - if (ret) - return ret; - aq->mr = QSPI_MR_SMM; - } + ret = atmel_qspi_set_serial_memory_mode(aq, true); + if (ret < 0) + return ret; /* Clear pending interrupts */ (void)atmel_qspi_read(aq, QSPI_SR); @@ -902,11 +917,10 @@ static int atmel_qspi_sama7g5_set_speed(struct udevice *bus, uint hz) } /* Set the QSPI controller by default in Serial Memory Mode */ - atmel_qspi_write(QSPI_MR_SMM | QSPI_MR_DQSDLYEN, aq, QSPI_MR); - ret = atmel_qspi_update_config(aq); - if (ret) + aq->mr |= QSPI_MR_DQSDLYEN; + ret = atmel_qspi_set_serial_memory_mode(aq, true); + if (ret < 0) return ret; - aq->mr = QSPI_MR_SMM; /* Enable the QSPI controller. */ ret = atmel_qspi_reg_sync(aq); @@ -930,6 +944,135 @@ static int atmel_qspi_sama7g5_set_speed(struct udevice *bus, uint hz) return ret; } +static int atmel_qspi_claim_bus(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct atmel_qspi *aq = dev_get_priv(bus); + int ret; + + aq->mr &= ~QSPI_MR_CSMODE_MASK; + aq->mr |= QSPI_MR_CSMODE_LASTXFER | QSPI_MR_WDRBT; + atmel_qspi_write(aq->mr, aq, QSPI_MR); + + ret = atmel_qspi_set_serial_memory_mode(aq, false); + if (ret) + return log_ret(ret); + + /* de-assert all chip selects */ + if (IS_ENABLED(CONFIG_DM_GPIO)) { + for (int i = 0; i < ARRAY_SIZE(aq->cs_gpios); i++) { + if (dm_gpio_is_valid(&aq->cs_gpios[i])) + dm_gpio_set_value(&aq->cs_gpios[i], 0); + } + } + + atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR); + + return 0; +} + +static int atmel_qspi_release_bus(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct atmel_qspi *aq = dev_get_priv(bus); + + /* de-assert all chip selects */ + if (IS_ENABLED(CONFIG_DM_GPIO)) { + for (int i = 0; i < ARRAY_SIZE(aq->cs_gpios); i++) { + if (dm_gpio_is_valid(&aq->cs_gpios[i])) + dm_gpio_set_value(&aq->cs_gpios[i], 0); + } + } + + atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR); + + return 0; +} + +static int atmel_qspi_set_cs(struct udevice *dev, int value) +{ + struct udevice *bus = dev_get_parent(dev); + struct atmel_qspi *aq = dev_get_priv(bus); + int cs = spi_chip_select(dev); + + if (IS_ENABLED(CONFIG_DM_GPIO)) { + if (!dm_gpio_is_valid(&aq->cs_gpios[cs])) + return log_ret(-ENOENT); + + return dm_gpio_set_value(&aq->cs_gpios[cs], value); + } else { + return -ENOENT; + } +} + +static int atmel_qspi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev_get_parent(dev); + struct atmel_qspi *aq = dev_get_priv(bus); + unsigned int len, len_rx, len_tx; + const u8 *txp = dout; + u8 *rxp = din; + u32 reg; + int ret; + + if (bitlen == 0) + goto out; + + if (bitlen % 8) { + flags |= SPI_XFER_END; + goto out; + } + + len = bitlen / 8; + + if (flags & SPI_XFER_BEGIN) { + ret = atmel_qspi_set_cs(dev, 1); + if (ret) + return log_ret(ret); + reg = atmel_qspi_read(aq, QSPI_RD); + } + + for (len_tx = 0, len_rx = 0; len_rx < len; ) { + u32 status = atmel_qspi_read(aq, QSPI_SR); + u8 value; + + if (status & QSPI_SR_OVRES) + return log_ret(-1); + + if (len_tx < len && (status & QSPI_SR_TDRE)) { + if (txp) + value = *txp++; + else + value = 0; + atmel_qspi_write(value, aq, QSPI_TD); + len_tx++; + } + + if (status & QSPI_SR_RDRF) { + value = atmel_qspi_read(aq, QSPI_RD); + if (rxp) + *rxp++ = value; + len_rx++; + } + } + +out: + if (flags & SPI_XFER_END) { + readl_poll_timeout(aq->regs + QSPI_SR, reg, + reg & QSPI_SR_TXEMPTY, + ATMEL_QSPI_TIMEOUT); + + atmel_qspi_write(QSPI_CR_LASTXFER, aq, QSPI_CR); + + ret = atmel_qspi_set_cs(dev, 0); + if (ret) + return log_ret(ret); + } + + return 0; +} + static int atmel_qspi_set_speed(struct udevice *bus, uint hz) { struct atmel_qspi *aq = dev_get_priv(bus); @@ -939,6 +1082,7 @@ static int atmel_qspi_set_speed(struct udevice *bus, uint hz) return atmel_qspi_sama7g5_set_speed(bus, hz); /* Compute the QSPI baudrate */ + dev_dbg(bus, "bus_clk_rate: %lu, hz: %u\n", aq->bus_clk_rate, hz); scbr = DIV_ROUND_UP(aq->bus_clk_rate, hz); if (scbr > 0) scbr--; @@ -1046,10 +1190,6 @@ static int atmel_qspi_init(struct atmel_qspi *aq) /* Reset the QSPI controller */ atmel_qspi_write(QSPI_CR_SWRST, aq, QSPI_CR); - /* Set the QSPI controller by default in Serial Memory Mode */ - atmel_qspi_write(QSPI_MR_SMM, aq, QSPI_MR); - aq->mr = QSPI_MR_SMM; - /* Enable the QSPI controller */ atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR); @@ -1075,7 +1215,7 @@ static int atmel_qspi_probe(struct udevice *dev) aq->caps = (struct atmel_qspi_caps *)dev_get_driver_data(dev); if (!aq->caps) { dev_err(dev, "Could not retrieve QSPI caps\n"); - return -EINVAL; + return log_ret(-EINVAL); }; if (aq->caps->has_gclk) @@ -1083,36 +1223,53 @@ static int atmel_qspi_probe(struct udevice *dev) else aq->ops = &atmel_qspi_priv_ops; + if (IS_ENABLED(CONFIG_DM_GPIO)) { + ret = gpio_request_list_by_name(dev, "cs-gpios", aq->cs_gpios, + ARRAY_SIZE(aq->cs_gpios), 0); + if (ret < 0) { + pr_err("Can't get %s gpios! Error: %d", dev->name, ret); + return log_ret(ret); + } + + for (int i = 0; i < ARRAY_SIZE(aq->cs_gpios); i++) { + if (!dm_gpio_is_valid(&aq->cs_gpios[i])) + continue; + + dm_gpio_set_dir_flags(&aq->cs_gpios[i], + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + } + } + /* Map the registers */ ret = dev_read_resource_byname(dev, "qspi_base", &res); if (ret) { dev_err(dev, "missing registers\n"); - return ret; + return log_ret(ret); } aq->regs = devm_ioremap(dev, res.start, resource_size(&res)); if (IS_ERR(aq->regs)) - return PTR_ERR(aq->regs); + return log_ret(PTR_ERR(aq->regs)); /* Map the AHB memory */ ret = dev_read_resource_byname(dev, "qspi_mmap", &res); if (ret) { dev_err(dev, "missing AHB memory\n"); - return ret; + return log_ret(ret); } aq->mem = devm_ioremap(dev, res.start, resource_size(&res)); if (IS_ERR(aq->mem)) - return PTR_ERR(aq->mem); + return log_ret(PTR_ERR(aq->mem)); aq->mmap_size = resource_size(&res); ret = atmel_qspi_enable_clk(dev); if (ret) - return ret; + return log_ret(ret); aq->dev = dev; - return atmel_qspi_init(aq); + return log_ret(atmel_qspi_init(aq)); } static const struct spi_controller_mem_ops atmel_qspi_mem_ops = { @@ -1121,9 +1278,12 @@ static const struct spi_controller_mem_ops atmel_qspi_mem_ops = { }; static const struct dm_spi_ops atmel_qspi_ops = { + .claim_bus = atmel_qspi_claim_bus, + .release_bus = atmel_qspi_release_bus, + .xfer = atmel_qspi_xfer, + .mem_ops = &atmel_qspi_mem_ops, .set_speed = atmel_qspi_set_speed, .set_mode = atmel_qspi_set_mode, - .mem_ops = &atmel_qspi_mem_ops, }; static const struct atmel_qspi_caps atmel_sama5d2_qspi_caps = {}; diff --git a/drivers/spi/soft_spi.c b/drivers/spi/soft_spi.c index a8ec2f4f7b4..50bd7be5640 100644 --- a/drivers/spi/soft_spi.c +++ b/drivers/spi/soft_spi.c @@ -124,8 +124,19 @@ static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen, u8 *rxd = din; int cpha = !!(priv->mode & SPI_CPHA); int cidle = !!(priv->mode & SPI_CPOL); + int txrx = plat->flags; unsigned int j; + if (priv->mode & SPI_3WIRE) { + if (txd && rxd) + return -EINVAL; + + txrx = txd ? SPI_MASTER_NO_RX : SPI_MASTER_NO_TX; + dm_gpio_set_dir_flags(&plat->mosi, + txd ? GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE : + GPIOD_IS_IN | GPIOD_PULL_UP); + } + debug("spi_xfer: slave %s:%s dout %08X din %08X bitlen %u\n", dev->parent->name, dev->name, *(uint *)txd, *(uint *)rxd, bitlen); @@ -160,7 +171,7 @@ static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen, */ if (cpha) soft_spi_scl(dev, !cidle); - if ((plat->flags & SPI_MASTER_NO_TX) == 0) + if ((txrx & SPI_MASTER_NO_TX) == 0) soft_spi_sda(dev, !!(tmpdout & 0x80)); udelay(plat->spi_delay_us); @@ -174,8 +185,10 @@ static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen, else soft_spi_scl(dev, cidle); tmpdin <<= 1; - if ((plat->flags & SPI_MASTER_NO_RX) == 0) - tmpdin |= dm_gpio_get_value(&plat->miso); + if ((txrx & SPI_MASTER_NO_RX) == 0) + tmpdin |= dm_gpio_get_value((priv->mode & SPI_3WIRE) ? + &plat->mosi : + &plat->miso); tmpdout <<= 1; udelay(plat->spi_delay_us); diff --git a/drivers/sysinfo/Kconfig b/drivers/sysinfo/Kconfig index 2030e4babc9..df83df69ffb 100644 --- a/drivers/sysinfo/Kconfig +++ b/drivers/sysinfo/Kconfig @@ -31,6 +31,13 @@ config SYSINFO_RCAR3 help Support querying SoC version information for Renesas R-Car Gen3. +config SYSINFO_IOT2050 + bool "Enable sysinfo driver for the Siemens IOT2050" + depends on TARGET_IOT2050_A53 + default y if TARGET_IOT2050_A53 + help + Support querying device information for Siemens IOT2050. + config SYSINFO_SANDBOX bool "Enable sysinfo driver for the Sandbox board" help diff --git a/drivers/sysinfo/Makefile b/drivers/sysinfo/Makefile index 680dde77fe8..26ca3150999 100644 --- a/drivers/sysinfo/Makefile +++ b/drivers/sysinfo/Makefile @@ -5,6 +5,7 @@ obj-y += sysinfo-uclass.o obj-$(CONFIG_SYSINFO_GAZERBEAM) += gazerbeam.o obj-$(CONFIG_SYSINFO_GPIO) += gpio.o +obj-$(CONFIG_SYSINFO_IOT2050) += iot2050.o obj-$(CONFIG_SYSINFO_RCAR3) += rcar3.o obj-$(CONFIG_SYSINFO_SANDBOX) += sandbox.o obj-$(CONFIG_SYSINFO_SMBIOS) += smbios.o diff --git a/drivers/sysinfo/iot2050.c b/drivers/sysinfo/iot2050.c new file mode 100644 index 00000000000..579a9f4711d --- /dev/null +++ b/drivers/sysinfo/iot2050.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) Siemens AG, 2025 + */ + +#include <dm.h> +#include <sysinfo.h> +#include <net.h> +#include <u-boot/uuid.h> +#include <asm/arch/hardware.h> + +#include "iot2050.h" + +#define IOT2050_INFO_MAGIC 0x20502050 + +#define IOT2050_UUID_STR_LEN (32) + +struct iot2050_info { + u32 magic; + u16 size; + char name[20 + 1]; + char serial[16 + 1]; + char mlfb[18 + 1]; + char uuid[IOT2050_UUID_STR_LEN + 1]; + char a5e[18 + 1]; + u8 mac_addr_cnt; + u8 mac_addr[8][ARP_HLEN]; + char seboot_version[40 + 1]; + u8 padding[3]; + u32 ddr_size_mb; +} __packed; + +/** + * struct sysinfo_iot2050_priv - sysinfo private data + * @info: iot2050 board info + */ +struct sysinfo_iot2050_priv { + struct iot2050_info *info; + u8 uuid_smbios[16]; +}; + +static int sysinfo_iot2050_detect(struct udevice *dev) +{ + struct sysinfo_iot2050_priv *priv = dev_get_priv(dev); + + if (!priv->info || priv->info->magic != IOT2050_INFO_MAGIC) + return -EFAULT; + + return 0; +} + +static int sysinfo_iot2050_get_str(struct udevice *dev, int id, size_t size, + char *val) +{ + struct sysinfo_iot2050_priv *priv = dev_get_priv(dev); + + switch (id) { + case BOARD_NAME: + case SYSID_SM_BASEBOARD_VERSION: + strlcpy(val, priv->info->name, size); + break; + case SYSID_SM_SYSTEM_SERIAL: + strlcpy(val, priv->info->serial, size); + break; + case BOARD_MLFB: + case SYSID_SM_SYSTEM_VERSION: + strlcpy(val, priv->info->mlfb, size); + break; + case BOARD_UUID: + strlcpy(val, priv->info->uuid, size); + break; + case BOARD_A5E: + case SYSID_SM_BASEBOARD_PRODUCT: + strlcpy(val, priv->info->a5e, size); + break; + case BOARD_SEBOOT_VER: + case SYSID_PRIOR_STAGE_VERSION: + strlcpy(val, priv->info->seboot_version, size); + break; + default: + return -EINVAL; + }; + + val[size - 1] = '\0'; + return 0; +} + +static int sysinfo_iot2050_get_int(struct udevice *dev, int id, int *val) +{ + struct sysinfo_iot2050_priv *priv = dev_get_priv(dev); + + switch (id) { + case SYSID_BOARD_RAM_SIZE_MB: + *val = priv->info->ddr_size_mb; + return 0; + default: + return -EINVAL; + }; +} + +static int sysinfo_iot2050_get_data(struct udevice *dev, int id, void **data, + size_t *size) +{ + struct sysinfo_iot2050_priv *priv = dev_get_priv(dev); + + switch (id) { + case SYSID_SM_SYSTEM_UUID: + *data = priv->uuid_smbios; + *size = 16; + return 0; + default: + return -EINVAL; + }; +} + +static int sysinfo_iot2050_get_item_count(struct udevice *dev, int id) +{ + struct sysinfo_iot2050_priv *priv = dev_get_priv(dev); + + switch (id) { + case SYSID_BOARD_MAC_ADDR: + return priv->info->mac_addr_cnt; + default: + return -EINVAL; + }; +} + +static int sysinfo_iot2050_get_data_by_index(struct udevice *dev, int id, + int index, void **data, + size_t *size) +{ + struct sysinfo_iot2050_priv *priv = dev_get_priv(dev); + + switch (id) { + case SYSID_BOARD_MAC_ADDR: + if (index >= priv->info->mac_addr_cnt) + return -EINVAL; + *data = priv->info->mac_addr[index]; + *size = ARP_HLEN; + return 0; + default: + return -EINVAL; + }; +} + +static const struct sysinfo_ops sysinfo_iot2050_ops = { + .detect = sysinfo_iot2050_detect, + .get_str = sysinfo_iot2050_get_str, + .get_int = sysinfo_iot2050_get_int, + .get_data = sysinfo_iot2050_get_data, + .get_item_count = sysinfo_iot2050_get_item_count, + .get_data_by_index = sysinfo_iot2050_get_data_by_index, +}; + +/** + * @brief Convert the IOT2050 UUID string to the SMBIOS format + * + * @param uuid_raw The IOT2050 UUID string parsed from the eeprom + * @param uuid_smbios The buffer to hold the SMBIOS formatted UUID + */ +static void sysinfo_iot2050_convert_uuid(const char *uuid_iot2050, + u8 *uuid_smbios) +{ + char uuid_rfc4122_str[IOT2050_UUID_STR_LEN + 4 + 1] = {0}; + char *tmp = uuid_rfc4122_str; + + for (int i = 0; i < 16; i++) { + memcpy(tmp, uuid_iot2050 + i * 2, 2); + tmp += 2; + if (i == 3 || i == 5 || i == 7 || i == 9) + *tmp++ = '-'; + } + uuid_str_to_bin(uuid_rfc4122_str, uuid_smbios, UUID_STR_FORMAT_GUID); +} + +static int sysinfo_iot2050_probe(struct udevice *dev) +{ + struct sysinfo_iot2050_priv *priv = dev_get_priv(dev); + unsigned long offset; + + offset = dev_read_u32_default(dev, "offset", + TI_SRAM_SCRATCH_BOARD_EEPROM_START); + priv->info = (struct iot2050_info *)offset; + + sysinfo_iot2050_convert_uuid(priv->info->uuid, priv->uuid_smbios); + + return 0; +} + +static const struct udevice_id sysinfo_iot2050_ids[] = { + { .compatible = "siemens,sysinfo-iot2050" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(sysinfo_iot2050) = { + .name = "sysinfo_iot2050", + .id = UCLASS_SYSINFO, + .of_match = sysinfo_iot2050_ids, + .ops = &sysinfo_iot2050_ops, + .priv_auto = sizeof(struct sysinfo_iot2050_priv), + .probe = sysinfo_iot2050_probe, +}; diff --git a/drivers/sysinfo/iot2050.h b/drivers/sysinfo/iot2050.h new file mode 100644 index 00000000000..657221db096 --- /dev/null +++ b/drivers/sysinfo/iot2050.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Siemens AG, 2025 + */ + +#include <sysinfo.h> + +enum sysinfo_id_iot2050 { + BOARD_MLFB = SYSID_USER, + BOARD_A5E, + BOARD_NAME, + BOARD_UUID, + BOARD_SEBOOT_VER, +}; diff --git a/drivers/sysinfo/sysinfo-uclass.c b/drivers/sysinfo/sysinfo-uclass.c index 3c0cd51273e..f04998ef8bb 100644 --- a/drivers/sysinfo/sysinfo-uclass.c +++ b/drivers/sysinfo/sysinfo-uclass.c @@ -119,6 +119,35 @@ int sysinfo_get_data(struct udevice *dev, int id, void **data, size_t *size) return ops->get_data(dev, id, data, size); } +int sysinfo_get_item_count(struct udevice *dev, int id) +{ + struct sysinfo_priv *priv = dev_get_uclass_priv(dev); + struct sysinfo_ops *ops = sysinfo_get_ops(dev); + + if (!priv->detected) + return -EPERM; + + if (!ops->get_item_count) + return -ENOSYS; + + return ops->get_item_count(dev, id); +} + +int sysinfo_get_data_by_index(struct udevice *dev, int id, int index, + void **data, size_t *size) +{ + struct sysinfo_priv *priv = dev_get_uclass_priv(dev); + struct sysinfo_ops *ops = sysinfo_get_ops(dev); + + if (!priv->detected) + return -EPERM; + + if (!ops->get_data_by_index) + return -ENOSYS; + + return ops->get_data_by_index(dev, id, index, data, size); +} + UCLASS_DRIVER(sysinfo) = { .id = UCLASS_SYSINFO, .name = "sysinfo", diff --git a/drivers/sysreset/Kconfig b/drivers/sysreset/Kconfig index 121194e4418..475540ffac7 100644 --- a/drivers/sysreset/Kconfig +++ b/drivers/sysreset/Kconfig @@ -242,7 +242,6 @@ config SYSRESET_RAA215300 config SYSRESET_QCOM_PSHOLD bool "Support sysreset for Qualcomm SoCs via PSHOLD" - depends on ARCH_IPQ40XX help Add support for the system reboot on Qualcomm SoCs via PSHOLD. diff --git a/drivers/timer/sandbox_timer.c b/drivers/timer/sandbox_timer.c index e8b54a02965..c1baf3c69ec 100644 --- a/drivers/timer/sandbox_timer.c +++ b/drivers/timer/sandbox_timer.c @@ -18,6 +18,11 @@ void timer_test_add_offset(unsigned long offset) sandbox_timer_offset += offset; } +ulong timer_test_get_offset(void) +{ + return sandbox_timer_offset; +}; + u64 notrace timer_early_get_count(void) { return os_get_nsec() / 1000 + sandbox_timer_offset * 1000; diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index f7d8c40c448..91f6ad3bfef 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -19,6 +19,7 @@ #include <malloc.h> #include <hexdump.h> #include <scsi.h> +#include <ufs.h> #include <asm/io.h> #include <asm/dma-mapping.h> #include <linux/bitops.h> @@ -313,16 +314,12 @@ static int ufshcd_disable_tx_lcc(struct ufs_hba *hba, bool peer) ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), &tx_lanes); for (i = 0; i < tx_lanes; i++) { + unsigned int val = UIC_ARG_MIB_SEL(TX_LCC_ENABLE, + UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)); if (!peer) - err = ufshcd_dme_set(hba, - UIC_ARG_MIB_SEL(TX_LCC_ENABLE, - UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)), - 0); + err = ufshcd_dme_set(hba, val, 0); else - err = ufshcd_dme_peer_set(hba, - UIC_ARG_MIB_SEL(TX_LCC_ENABLE, - UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)), - 0); + err = ufshcd_dme_peer_set(hba, val, 0); if (err) { dev_err(hba->dev, "%s: TX LCC Disable failed, peer = %d, lane = %d, err = %d\n", __func__, peer, i, err); @@ -1034,8 +1031,8 @@ static inline void ufshcd_init_query(struct ufs_hba *hba, /** * ufshcd_query_flag() - API function for sending flag query requests */ -int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, - enum flag_idn idn, bool *flag_res) +static int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, + enum flag_idn idn, bool *flag_res) { struct ufs_query_req *request = NULL; struct ufs_query_res *response = NULL; @@ -1170,9 +1167,9 @@ out: /** * ufshcd_query_descriptor_retry - API function for sending descriptor requests */ -int ufshcd_query_descriptor_retry(struct ufs_hba *hba, enum query_opcode opcode, - enum desc_idn idn, u8 index, u8 selector, - u8 *desc_buf, int *buf_len) +static int ufshcd_query_descriptor_retry(struct ufs_hba *hba, enum query_opcode opcode, + enum desc_idn idn, u8 index, u8 selector, + u8 *desc_buf, int *buf_len) { int err; int retries; @@ -1264,8 +1261,8 @@ static void ufshcd_init_desc_sizes(struct ufs_hba *hba) * ufshcd_map_desc_id_to_length - map descriptor IDN to its length * */ -int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id, - int *desc_len) +static int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id, + int *desc_len) { switch (desc_id) { case QUERY_DESC_IDN_DEVICE: @@ -1302,15 +1299,14 @@ int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id, } return 0; } -EXPORT_SYMBOL(ufshcd_map_desc_id_to_length); /** * ufshcd_read_desc_param - read the specified descriptor parameter * */ -int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id, - int desc_index, u8 param_offset, u8 *param_read_buf, - u8 param_size) +static int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id, + int desc_index, u8 param_offset, + u8 *param_read_buf, u8 param_size) { int ret; u8 *desc_buf; @@ -1569,8 +1565,8 @@ static int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size) * ufshcd_read_string_desc - read string descriptor * */ -int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, - u8 *buf, u32 size, bool ascii) +static int ufshcd_read_string_desc(struct ufs_hba *hba, int desc_index, + u8 *buf, u32 size, bool ascii) { int err = 0; @@ -1881,7 +1877,7 @@ static void ufshcd_def_desc_sizes(struct ufs_hba *hba) hba->desc_size.hlth_desc = QUERY_DESC_HEALTH_DEF_SIZE; } -int ufs_start(struct ufs_hba *hba) +static int ufs_start(struct ufs_hba *hba) { struct ufs_dev_desc card = {0}; int ret; @@ -1962,7 +1958,7 @@ int ufshcd_probe(struct udevice *ufs_dev, struct ufs_hba_ops *hba_ops) ufshcd_ops_init(hba); - /* Read capabilties registers */ + /* Read capabilities registers */ hba->capabilities = ufshcd_readl(hba, REG_CONTROLLER_CAPABILITIES); if (hba->quirks & UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS) hba->capabilities &= ~MASK_64_ADDRESSING_SUPPORT; @@ -2001,7 +1997,7 @@ int ufshcd_probe(struct udevice *ufs_dev, struct ufs_hba_ops *hba_ops) REG_INTERRUPT_STATUS); ufshcd_writel(hba, 0, REG_INTERRUPT_ENABLE); - mb(); + mb(); /* flush previous writes */ /* Reset the attached device */ ufshcd_device_reset(hba); diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h index 00ecca350c3..53137fae3a8 100644 --- a/drivers/ufs/ufs.h +++ b/drivers/ufs/ufs.h @@ -4,6 +4,7 @@ #include <linux/types.h> #include <asm/io.h> +#include "ufshci.h" #include "unipro.h" struct udevice; @@ -14,11 +15,6 @@ struct udevice; #define RESPONSE_UPIU_SENSE_DATA_LENGTH 18 #define UFS_MAX_LUNS 0x7F -enum { - TASK_REQ_UPIU_SIZE_DWORDS = 8, - TASK_RSP_UPIU_SIZE_DWORDS = 8, - ALIGNED_UPIU_SIZE = 512, -}; /* UFS device power modes */ enum ufs_dev_pwr_mode { @@ -84,44 +80,6 @@ enum { /* Offset of the response code in the UPIU header */ #define UPIU_RSP_CODE_OFFSET 8 -/* To accommodate UFS2.0 required Command type */ -enum { - UTP_CMD_TYPE_UFS_STORAGE = 0x1, -}; - -enum { - UTP_SCSI_COMMAND = 0x00000000, - UTP_NATIVE_UFS_COMMAND = 0x10000000, - UTP_DEVICE_MANAGEMENT_FUNCTION = 0x20000000, - UTP_REQ_DESC_INT_CMD = 0x01000000, -}; - -/* UTP Transfer Request Data Direction (DD) */ -enum { - UTP_NO_DATA_TRANSFER = 0x00000000, - UTP_HOST_TO_DEVICE = 0x02000000, - UTP_DEVICE_TO_HOST = 0x04000000, -}; - -/* Overall command status values */ -enum { - OCS_SUCCESS = 0x0, - OCS_INVALID_CMD_TABLE_ATTR = 0x1, - OCS_INVALID_PRDT_ATTR = 0x2, - OCS_MISMATCH_DATA_BUF_SIZE = 0x3, - OCS_MISMATCH_RESP_UPIU_SIZE = 0x4, - OCS_PEER_COMM_FAILURE = 0x5, - OCS_ABORTED = 0x6, - OCS_FATAL_ERROR = 0x7, - OCS_INVALID_COMMAND_STATUS = 0x0F, - MASK_OCS = 0x0F, -}; - -/* The maximum length of the data byte count field in the PRDT is 256KB */ -#define PRDT_DATA_BYTE_COUNT_MAX (256 * 1024) -/* The granularity of the data byte count field in the PRDT is 32-bit */ -#define PRDT_DATA_BYTE_COUNT_PAD 4 - #define GENERAL_UPIU_REQUEST_SIZE (sizeof(struct utp_upiu_req)) #define QUERY_DESC_MAX_SIZE 255 #define QUERY_DESC_MIN_SIZE 2 @@ -130,8 +88,8 @@ enum { (sizeof(struct utp_upiu_header))) #define RESPONSE_UPIU_SENSE_DATA_LENGTH 18 #define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\ - cpu_to_be32((byte3 << 24) | (byte2 << 16) |\ - (byte1 << 8) | (byte0)) + cpu_to_be32(((byte3) << 24) | ((byte2) << 16) |\ + ((byte1) << 8) | (byte0)) /* * UFS Protocol Information Unit related definitions */ @@ -297,79 +255,6 @@ enum desc_header_offset { QUERY_DESC_DESC_TYPE_OFFSET = 0x01, }; -struct ufshcd_sg_entry { - __le32 base_addr; - __le32 upper_addr; - __le32 reserved; - __le32 size; -}; - -#define MAX_BUFF 128 -/** - * struct utp_transfer_cmd_desc - UFS Command Descriptor structure - * @command_upiu: Command UPIU Frame address - * @response_upiu: Response UPIU Frame address - * @prd_table: Physical Region Descriptor - */ -struct utp_transfer_cmd_desc { - u8 command_upiu[ALIGNED_UPIU_SIZE]; - u8 response_upiu[ALIGNED_UPIU_SIZE]; - struct ufshcd_sg_entry prd_table[MAX_BUFF]; -}; - -/** - * struct request_desc_header - Descriptor Header common to both UTRD and UTMRD - * @dword0: Descriptor Header DW0 - * @dword1: Descriptor Header DW1 - * @dword2: Descriptor Header DW2 - * @dword3: Descriptor Header DW3 - */ -struct request_desc_header { - __le32 dword_0; - __le32 dword_1; - __le32 dword_2; - __le32 dword_3; -}; - -/** - * struct utp_transfer_req_desc - UTRD structure - * @header: UTRD header DW-0 to DW-3 - * @command_desc_base_addr_lo: UCD base address low DW-4 - * @command_desc_base_addr_hi: UCD base address high DW-5 - * @response_upiu_length: response UPIU length DW-6 - * @response_upiu_offset: response UPIU offset DW-6 - * @prd_table_length: Physical region descriptor length DW-7 - * @prd_table_offset: Physical region descriptor offset DW-7 - */ -struct utp_transfer_req_desc { - /* DW 0-3 */ - struct request_desc_header header; - - /* DW 4-5*/ - __le32 command_desc_base_addr_lo; - __le32 command_desc_base_addr_hi; - - /* DW 6 */ - __le16 response_upiu_length; - __le16 response_upiu_offset; - - /* DW 7 */ - __le16 prd_table_length; - __le16 prd_table_offset; -}; - -/** - * struct utp_upiu_header - UPIU header structure - * @dword_0: UPIU header DW-0 - * @dword_1: UPIU header DW-1 - * @dword_2: UPIU header DW-2 - */ -struct utp_upiu_header { - __be32 dword_0; - __be32 dword_1; - __be32 dword_2; -}; - /** * struct utp_upiu_query - upiu request buffer structure for * query request. @@ -403,27 +288,6 @@ struct utp_upiu_cmd { u8 cdb[UFS_CDB_SIZE]; }; -/* - * UTMRD structure. - */ -struct utp_task_req_desc { - /* DW 0-3 */ - struct request_desc_header header; - - /* DW 4-11 - Task request UPIU structure */ - struct utp_upiu_header req_header; - __be32 input_param1; - __be32 input_param2; - __be32 input_param3; - __be32 __reserved1[2]; - - /* DW 12-19 - Task Management Response UPIU structure */ - struct utp_upiu_header rsp_header; - __be32 output_param1; - __be32 output_param2; - __be32 __reserved2[3]; -}; - /** * struct utp_upiu_req - general upiu request structure * @header:UPIU header structure DW-0 to DW-2 @@ -551,63 +415,6 @@ struct uic_command { int result; }; -/* GenSelectorIndex calculation macros for M-PHY attributes */ -#define UIC_ARG_MPHY_TX_GEN_SEL_INDEX(lane) (lane) -#define UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane) (PA_MAXDATALANES + (lane)) - -#define UIC_ARG_MIB_SEL(attr, sel) ((((attr) & 0xFFFF) << 16) |\ - ((sel) & 0xFFFF)) -#define UIC_ARG_MIB(attr) UIC_ARG_MIB_SEL(attr, 0) -#define UIC_ARG_ATTR_TYPE(t) (((t) & 0xFF) << 16) -#define UIC_GET_ATTR_ID(v) (((v) >> 16) & 0xFFFF) - -/* Link Status*/ -enum link_status { - UFSHCD_LINK_IS_DOWN = 1, - UFSHCD_LINK_IS_UP = 2, -}; - -#define UIC_ARG_MIB_SEL(attr, sel) ((((attr) & 0xFFFF) << 16) |\ - ((sel) & 0xFFFF)) -#define UIC_ARG_MIB(attr) UIC_ARG_MIB_SEL(attr, 0) -#define UIC_ARG_ATTR_TYPE(t) (((t) & 0xFF) << 16) -#define UIC_GET_ATTR_ID(v) (((v) >> 16) & 0xFFFF) - -/* UIC Commands */ -enum uic_cmd_dme { - UIC_CMD_DME_GET = 0x01, - UIC_CMD_DME_SET = 0x02, - UIC_CMD_DME_PEER_GET = 0x03, - UIC_CMD_DME_PEER_SET = 0x04, - UIC_CMD_DME_POWERON = 0x10, - UIC_CMD_DME_POWEROFF = 0x11, - UIC_CMD_DME_ENABLE = 0x12, - UIC_CMD_DME_RESET = 0x14, - UIC_CMD_DME_END_PT_RST = 0x15, - UIC_CMD_DME_LINK_STARTUP = 0x16, - UIC_CMD_DME_HIBER_ENTER = 0x17, - UIC_CMD_DME_HIBER_EXIT = 0x18, - UIC_CMD_DME_TEST_MODE = 0x1A, -}; - -/* UIC Config result code / Generic error code */ -enum { - UIC_CMD_RESULT_SUCCESS = 0x00, - UIC_CMD_RESULT_INVALID_ATTR = 0x01, - UIC_CMD_RESULT_FAILURE = 0x01, - UIC_CMD_RESULT_INVALID_ATTR_VALUE = 0x02, - UIC_CMD_RESULT_READ_ONLY_ATTR = 0x03, - UIC_CMD_RESULT_WRITE_ONLY_ATTR = 0x04, - UIC_CMD_RESULT_BAD_INDEX = 0x05, - UIC_CMD_RESULT_LOCKED_ATTR = 0x06, - UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX = 0x07, - UIC_CMD_RESULT_PEER_COMM_FAILURE = 0x08, - UIC_CMD_RESULT_BUSY = 0x09, - UIC_CMD_RESULT_DME_FAILURE = 0x0A, -}; - -#define MASK_UIC_COMMAND_RESULT 0xFF - /* Host <-> Device UniPro Link state */ enum uic_link_state { UIC_LINK_OFF_STATE = 0, /* Link powered down or disabled */ @@ -915,7 +722,7 @@ static inline int ufshcd_ops_get_max_pwr_mode(struct ufs_hba *hba, } static inline int ufshcd_ops_hce_enable_notify(struct ufs_hba *hba, - bool status) + bool status) { if (hba->ops && hba->ops->hce_enable_notify) return hba->ops->hce_enable_notify(hba, status); @@ -940,17 +747,6 @@ static inline int ufshcd_vops_device_reset(struct ufs_hba *hba) return 0; } -/* Controller UFSHCI version */ -enum { - UFSHCI_VERSION_10 = 0x00010000, /* 1.0 */ - UFSHCI_VERSION_11 = 0x00010100, /* 1.1 */ - UFSHCI_VERSION_20 = 0x00000200, /* 2.0 */ - UFSHCI_VERSION_21 = 0x00000210, /* 2.1 */ - UFSHCI_VERSION_30 = 0x00000300, /* 3.0 */ - UFSHCI_VERSION_31 = 0x00000310, /* 3.1 */ - UFSHCI_VERSION_40 = 0x00000400, /* 4.0 */ -}; - /* Interrupt disable masks */ enum { /* Interrupt disable mask for UFSHCI v1.0 */ @@ -964,123 +760,6 @@ enum { INTERRUPT_MASK_ALL_VER_21 = 0x71FFF, }; -/* UFSHCI Registers */ -enum { - REG_CONTROLLER_CAPABILITIES = 0x00, - REG_UFS_VERSION = 0x08, - REG_CONTROLLER_DEV_ID = 0x10, - REG_CONTROLLER_PROD_ID = 0x14, - REG_AUTO_HIBERNATE_IDLE_TIMER = 0x18, - REG_INTERRUPT_STATUS = 0x20, - REG_INTERRUPT_ENABLE = 0x24, - REG_CONTROLLER_STATUS = 0x30, - REG_CONTROLLER_ENABLE = 0x34, - REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER = 0x38, - REG_UIC_ERROR_CODE_DATA_LINK_LAYER = 0x3C, - REG_UIC_ERROR_CODE_NETWORK_LAYER = 0x40, - REG_UIC_ERROR_CODE_TRANSPORT_LAYER = 0x44, - REG_UIC_ERROR_CODE_DME = 0x48, - REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL = 0x4C, - REG_UTP_TRANSFER_REQ_LIST_BASE_L = 0x50, - REG_UTP_TRANSFER_REQ_LIST_BASE_H = 0x54, - REG_UTP_TRANSFER_REQ_DOOR_BELL = 0x58, - REG_UTP_TRANSFER_REQ_LIST_CLEAR = 0x5C, - REG_UTP_TRANSFER_REQ_LIST_RUN_STOP = 0x60, - REG_UTP_TASK_REQ_LIST_BASE_L = 0x70, - REG_UTP_TASK_REQ_LIST_BASE_H = 0x74, - REG_UTP_TASK_REQ_DOOR_BELL = 0x78, - REG_UTP_TASK_REQ_LIST_CLEAR = 0x7C, - REG_UTP_TASK_REQ_LIST_RUN_STOP = 0x80, - REG_UIC_COMMAND = 0x90, - REG_UIC_COMMAND_ARG_1 = 0x94, - REG_UIC_COMMAND_ARG_2 = 0x98, - REG_UIC_COMMAND_ARG_3 = 0x9C, - - UFSHCI_REG_SPACE_SIZE = 0xA0, - - REG_UFS_CCAP = 0x100, - REG_UFS_CRYPTOCAP = 0x104, - - UFSHCI_CRYPTO_REG_SPACE_SIZE = 0x400, -}; - -/* Controller capability masks */ -enum { - MASK_TRANSFER_REQUESTS_SLOTS = 0x0000001F, - MASK_TASK_MANAGEMENT_REQUEST_SLOTS = 0x00070000, - MASK_AUTO_HIBERN8_SUPPORT = 0x00800000, - MASK_64_ADDRESSING_SUPPORT = 0x01000000, - MASK_OUT_OF_ORDER_DATA_DELIVERY_SUPPORT = 0x02000000, - MASK_UIC_DME_TEST_MODE_SUPPORT = 0x04000000, -}; - -/* Interrupt Status 20h */ -#define UTP_TRANSFER_REQ_COMPL 0x1 -#define UIC_DME_END_PT_RESET 0x2 -#define UIC_ERROR 0x4 -#define UIC_TEST_MODE 0x8 -#define UIC_POWER_MODE 0x10 -#define UIC_HIBERNATE_EXIT 0x20 -#define UIC_HIBERNATE_ENTER 0x40 -#define UIC_LINK_LOST 0x80 -#define UIC_LINK_STARTUP 0x100 -#define UTP_TASK_REQ_COMPL 0x200 -#define UIC_COMMAND_COMPL 0x400 -#define DEVICE_FATAL_ERROR 0x800 -#define CONTROLLER_FATAL_ERROR 0x10000 -#define SYSTEM_BUS_FATAL_ERROR 0x20000 - -#define UFSHCD_UIC_PWR_MASK (UIC_HIBERNATE_ENTER |\ - UIC_HIBERNATE_EXIT |\ - UIC_POWER_MODE) - -#define UFSHCD_UIC_MASK (UIC_COMMAND_COMPL | UIC_POWER_MODE) - -#define UFSHCD_ERROR_MASK (UIC_ERROR |\ - DEVICE_FATAL_ERROR |\ - CONTROLLER_FATAL_ERROR |\ - SYSTEM_BUS_FATAL_ERROR) - -#define INT_FATAL_ERRORS (DEVICE_FATAL_ERROR |\ - CONTROLLER_FATAL_ERROR |\ - SYSTEM_BUS_FATAL_ERROR) - -/* Host Controller Enable 0x34h */ -#define CONTROLLER_ENABLE 0x1 -#define CONTROLLER_DISABLE 0x0 -/* HCS - Host Controller Status 30h */ -#define DEVICE_PRESENT 0x1 -#define UTP_TRANSFER_REQ_LIST_READY 0x2 -#define UTP_TASK_REQ_LIST_READY 0x4 -#define UIC_COMMAND_READY 0x8 -#define HOST_ERROR_INDICATOR 0x10 -#define DEVICE_ERROR_INDICATOR 0x20 -#define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK UFS_MASK(0x7, 8) - -#define UFSHCD_STATUS_READY (UTP_TRANSFER_REQ_LIST_READY |\ - UTP_TASK_REQ_LIST_READY |\ - UIC_COMMAND_READY) - -enum { - PWR_OK = 0x0, - PWR_LOCAL = 0x01, - PWR_REMOTE = 0x02, - PWR_BUSY = 0x03, - PWR_ERROR_CAP = 0x04, - PWR_FATAL_ERROR = 0x05, -}; - -/* UICCMD - UIC Command */ -#define COMMAND_OPCODE_MASK 0xFF -#define GEN_SELECTOR_INDEX_MASK 0xFFFF - -#define MIB_ATTRIBUTE_MASK UFS_MASK(0xFFFF, 16) -#define RESET_LEVEL 0xFF - -#define ATTR_SET_TYPE_MASK UFS_MASK(0xFF, 16) -#define CFG_RESULT_CODE_MASK 0xFF -#define GENERIC_ERROR_CODE_MASK 0xFF - #define ufshcd_writel(hba, val, reg) \ writel((val), (hba)->mmio_base + (reg)) #define ufshcd_readl(hba, reg) \ @@ -1103,12 +782,6 @@ static inline void ufshcd_rmwl(struct ufs_hba *hba, u32 mask, u32 val, u32 reg) ufshcd_writel(hba, tmp, reg); } -/* UTRLRSR - UTP Transfer Request Run-Stop Register 60h */ -#define UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT 0x1 - -/* UTMRLRSR - UTP Task Management Request Run-Stop Register 80h */ -#define UTP_TASK_REQ_LIST_RUN_STOP_BIT 0x1 - int ufshcd_probe(struct udevice *dev, struct ufs_hba_ops *hba_ops); #endif diff --git a/drivers/ufs/ufshci.h b/drivers/ufs/ufshci.h new file mode 100644 index 00000000000..90cbf87a3a4 --- /dev/null +++ b/drivers/ufs/ufshci.h @@ -0,0 +1,469 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#ifndef __UFSHCI_H +#define __UFSHCI_H + +enum { + TASK_REQ_UPIU_SIZE_DWORDS = 8, + TASK_RSP_UPIU_SIZE_DWORDS = 8, + ALIGNED_UPIU_SIZE = 512, +}; + +/* UFSHCI Registers */ +enum { + REG_CONTROLLER_CAPABILITIES = 0x00, + REG_MCQCAP = 0x04, + REG_UFS_VERSION = 0x08, + REG_EXT_CONTROLLER_CAPABILITIES = 0x0C, + REG_CONTROLLER_PID = 0x10, + REG_CONTROLLER_MID = 0x14, + REG_AUTO_HIBERNATE_IDLE_TIMER = 0x18, + REG_INTERRUPT_STATUS = 0x20, + REG_INTERRUPT_ENABLE = 0x24, + REG_CONTROLLER_STATUS = 0x30, + REG_CONTROLLER_ENABLE = 0x34, + REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER = 0x38, + REG_UIC_ERROR_CODE_DATA_LINK_LAYER = 0x3C, + REG_UIC_ERROR_CODE_NETWORK_LAYER = 0x40, + REG_UIC_ERROR_CODE_TRANSPORT_LAYER = 0x44, + REG_UIC_ERROR_CODE_DME = 0x48, + REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL = 0x4C, + REG_UTP_TRANSFER_REQ_LIST_BASE_L = 0x50, + REG_UTP_TRANSFER_REQ_LIST_BASE_H = 0x54, + REG_UTP_TRANSFER_REQ_DOOR_BELL = 0x58, + REG_UTP_TRANSFER_REQ_LIST_CLEAR = 0x5C, + REG_UTP_TRANSFER_REQ_LIST_RUN_STOP = 0x60, + REG_UTP_TASK_REQ_LIST_BASE_L = 0x70, + REG_UTP_TASK_REQ_LIST_BASE_H = 0x74, + REG_UTP_TASK_REQ_DOOR_BELL = 0x78, + REG_UTP_TASK_REQ_LIST_CLEAR = 0x7C, + REG_UTP_TASK_REQ_LIST_RUN_STOP = 0x80, + REG_UIC_COMMAND = 0x90, + REG_UIC_COMMAND_ARG_1 = 0x94, + REG_UIC_COMMAND_ARG_2 = 0x98, + REG_UIC_COMMAND_ARG_3 = 0x9C, + + UFSHCI_REG_SPACE_SIZE = 0xA0, + + REG_UFS_CCAP = 0x100, + REG_UFS_CRYPTOCAP = 0x104, + + REG_UFS_MEM_CFG = 0x300, + REG_UFS_MCQ_CFG = 0x380, + REG_UFS_ESILBA = 0x384, + REG_UFS_ESIUBA = 0x388, + UFSHCI_CRYPTO_REG_SPACE_SIZE = 0x400, +}; + +/* Controller capability masks */ +enum { + MASK_TRANSFER_REQUESTS_SLOTS_SDB = 0x0000001F, + MASK_TRANSFER_REQUESTS_SLOTS_MCQ = 0x000000FF, + MASK_NUMBER_OUTSTANDING_RTT = 0x0000FF00, + MASK_TASK_MANAGEMENT_REQUEST_SLOTS = 0x00070000, + MASK_EHSLUTRD_SUPPORTED = 0x00400000, + MASK_AUTO_HIBERN8_SUPPORT = 0x00800000, + MASK_64_ADDRESSING_SUPPORT = 0x01000000, + MASK_OUT_OF_ORDER_DATA_DELIVERY_SUPPORT = 0x02000000, + MASK_UIC_DME_TEST_MODE_SUPPORT = 0x04000000, + MASK_CRYPTO_SUPPORT = 0x10000000, + MASK_LSDB_SUPPORT = 0x20000000, + MASK_MCQ_SUPPORT = 0x40000000, +}; + +/* MCQ capability mask */ +enum { + MASK_EXT_IID_SUPPORT = 0x00000400, +}; + +enum { + REG_SQATTR = 0x0, + REG_SQLBA = 0x4, + REG_SQUBA = 0x8, + REG_SQDAO = 0xC, + REG_SQISAO = 0x10, + + REG_CQATTR = 0x20, + REG_CQLBA = 0x24, + REG_CQUBA = 0x28, + REG_CQDAO = 0x2C, + REG_CQISAO = 0x30, +}; + +enum { + REG_SQHP = 0x0, + REG_SQTP = 0x4, + REG_SQRTC = 0x8, + REG_SQCTI = 0xC, + REG_SQRTS = 0x10, +}; + +enum { + REG_CQHP = 0x0, + REG_CQTP = 0x4, +}; + +enum { + REG_CQIS = 0x0, + REG_CQIE = 0x4, +}; + +enum { + SQ_START = 0x0, + SQ_STOP = 0x1, + SQ_ICU = 0x2, +}; + +enum { + SQ_STS = 0x1, + SQ_CUS = 0x2, +}; + +#define SQ_ICU_ERR_CODE_MASK GENMASK(7, 4) +#define UFS_MASK(mask, offset) ((mask) << (offset)) + +/* UFS Version 08h */ +#define MINOR_VERSION_NUM_MASK UFS_MASK(0xFFFF, 0) +#define MAJOR_VERSION_NUM_MASK UFS_MASK(0xFFFF, 16) + +/* Controller UFSHCI version */ +enum { + UFSHCI_VERSION_10 = 0x00010000, /* 1.0 */ + UFSHCI_VERSION_11 = 0x00010100, /* 1.1 */ + UFSHCI_VERSION_20 = 0x00000200, /* 2.0 */ + UFSHCI_VERSION_21 = 0x00000210, /* 2.1 */ + UFSHCI_VERSION_30 = 0x00000300, /* 3.0 */ + UFSHCI_VERSION_31 = 0x00000310, /* 3.1 */ + UFSHCI_VERSION_40 = 0x00000400, /* 4.0 */ +}; + +/* + * IS - Interrupt Status - 20h + */ +#define UTP_TRANSFER_REQ_COMPL 0x1 +#define UIC_DME_END_PT_RESET 0x2 +#define UIC_ERROR 0x4 +#define UIC_TEST_MODE 0x8 +#define UIC_POWER_MODE 0x10 +#define UIC_HIBERNATE_EXIT 0x20 +#define UIC_HIBERNATE_ENTER 0x40 +#define UIC_LINK_LOST 0x80 +#define UIC_LINK_STARTUP 0x100 +#define UTP_TASK_REQ_COMPL 0x200 +#define UIC_COMMAND_COMPL 0x400 +#define DEVICE_FATAL_ERROR 0x800 +#define CONTROLLER_FATAL_ERROR 0x10000 +#define SYSTEM_BUS_FATAL_ERROR 0x20000 +#define CRYPTO_ENGINE_FATAL_ERROR 0x40000 +#define MCQ_CQ_EVENT_STATUS 0x100000 + +#define UFSHCD_UIC_HIBERN8_MASK (UIC_HIBERNATE_ENTER |\ + UIC_HIBERNATE_EXIT) + +#define UFSHCD_UIC_PWR_MASK (UFSHCD_UIC_HIBERN8_MASK |\ + UIC_POWER_MODE) + +#define UFSHCD_UIC_MASK (UIC_COMMAND_COMPL | UFSHCD_UIC_PWR_MASK) + +#define UFSHCD_ERROR_MASK (UIC_ERROR | INT_FATAL_ERRORS) + +#define INT_FATAL_ERRORS (DEVICE_FATAL_ERROR |\ + CONTROLLER_FATAL_ERROR |\ + SYSTEM_BUS_FATAL_ERROR |\ + CRYPTO_ENGINE_FATAL_ERROR |\ + UIC_LINK_LOST) + +/* HCS - Host Controller Status 30h */ +#define DEVICE_PRESENT 0x1 +#define UTP_TRANSFER_REQ_LIST_READY 0x2 +#define UTP_TASK_REQ_LIST_READY 0x4 +#define UIC_COMMAND_READY 0x8 +#define HOST_ERROR_INDICATOR 0x10 +#define DEVICE_ERROR_INDICATOR 0x20 +#define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK UFS_MASK(0x7, 8) + +#define UFSHCD_STATUS_READY (UTP_TRANSFER_REQ_LIST_READY |\ + UTP_TASK_REQ_LIST_READY |\ + UIC_COMMAND_READY) + +enum { + PWR_OK = 0x0, + PWR_LOCAL = 0x01, + PWR_REMOTE = 0x02, + PWR_BUSY = 0x03, + PWR_ERROR_CAP = 0x04, + PWR_FATAL_ERROR = 0x05, +}; + +/* HCE - Host Controller Enable 34h */ +#define CONTROLLER_ENABLE 0x1 +#define CONTROLLER_DISABLE 0x0 +#define CRYPTO_GENERAL_ENABLE 0x2 + +/* UECPA - Host UIC Error Code PHY Adapter Layer 38h */ +#define UIC_PHY_ADAPTER_LAYER_ERROR 0x80000000 +#define UIC_PHY_ADAPTER_LAYER_ERROR_CODE_MASK 0x1F +#define UIC_PHY_ADAPTER_LAYER_LANE_ERR_MASK 0xF +#define UIC_PHY_ADAPTER_LAYER_GENERIC_ERROR 0x10 + +/* UECDL - Host UIC Error Code Data Link Layer 3Ch */ +#define UIC_DATA_LINK_LAYER_ERROR 0x80000000 +#define UIC_DATA_LINK_LAYER_ERROR_CODE_MASK 0xFFFF +#define UIC_DATA_LINK_LAYER_ERROR_TCX_REP_TIMER_EXP 0x2 +#define UIC_DATA_LINK_LAYER_ERROR_AFCX_REQ_TIMER_EXP 0x4 +#define UIC_DATA_LINK_LAYER_ERROR_FCX_PRO_TIMER_EXP 0x8 +#define UIC_DATA_LINK_LAYER_ERROR_RX_BUF_OF 0x20 +#define UIC_DATA_LINK_LAYER_ERROR_PA_INIT 0x2000 +#define UIC_DATA_LINK_LAYER_ERROR_NAC_RECEIVED 0x0001 +#define UIC_DATA_LINK_LAYER_ERROR_TCx_REPLAY_TIMEOUT 0x0002 + +/* UECN - Host UIC Error Code Network Layer 40h */ +#define UIC_NETWORK_LAYER_ERROR 0x80000000 +#define UIC_NETWORK_LAYER_ERROR_CODE_MASK 0x7 +#define UIC_NETWORK_UNSUPPORTED_HEADER_TYPE 0x1 +#define UIC_NETWORK_BAD_DEVICEID_ENC 0x2 +#define UIC_NETWORK_LHDR_TRAP_PACKET_DROPPING 0x4 + +/* UECT - Host UIC Error Code Transport Layer 44h */ +#define UIC_TRANSPORT_LAYER_ERROR 0x80000000 +#define UIC_TRANSPORT_LAYER_ERROR_CODE_MASK 0x7F +#define UIC_TRANSPORT_UNSUPPORTED_HEADER_TYPE 0x1 +#define UIC_TRANSPORT_UNKNOWN_CPORTID 0x2 +#define UIC_TRANSPORT_NO_CONNECTION_RX 0x4 +#define UIC_TRANSPORT_CONTROLLED_SEGMENT_DROPPING 0x8 +#define UIC_TRANSPORT_BAD_TC 0x10 +#define UIC_TRANSPORT_E2E_CREDIT_OVERFOW 0x20 +#define UIC_TRANSPORT_SAFETY_VALUE_DROPPING 0x40 + +/* UECDME - Host UIC Error Code DME 48h */ +#define UIC_DME_ERROR 0x80000000 +#define UIC_DME_ERROR_CODE_MASK 0x1 + +/* UTRIACR - Interrupt Aggregation control register - 0x4Ch */ +#define INT_AGGR_TIMEOUT_VAL_MASK 0xFF +#define INT_AGGR_COUNTER_THRESHOLD_MASK UFS_MASK(0x1F, 8) +#define INT_AGGR_COUNTER_AND_TIMER_RESET 0x10000 +#define INT_AGGR_STATUS_BIT 0x100000 +#define INT_AGGR_PARAM_WRITE 0x1000000 +#define INT_AGGR_ENABLE 0x80000000 + +/* UTRLRSR - UTP Transfer Request Run-Stop Register 60h */ +#define UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT 0x1 + +/* UTMRLRSR - UTP Task Management Request Run-Stop Register 80h */ +#define UTP_TASK_REQ_LIST_RUN_STOP_BIT 0x1 + +/* REG_UFS_MEM_CFG - Global Config Registers 300h */ +#define MCQ_MODE_SELECT BIT(0) + +/* CQISy - CQ y Interrupt Status Register */ +#define UFSHCD_MCQ_CQIS_TAIL_ENT_PUSH_STS 0x1 + +/* UICCMD - UIC Command */ +#define COMMAND_OPCODE_MASK 0xFF +#define GEN_SELECTOR_INDEX_MASK 0xFFFF + +#define MIB_ATTRIBUTE_MASK UFS_MASK(0xFFFF, 16) +#define RESET_LEVEL 0xFF + +#define ATTR_SET_TYPE_MASK UFS_MASK(0xFF, 16) +#define CFG_RESULT_CODE_MASK 0xFF +#define GENERIC_ERROR_CODE_MASK 0xFF + +/* GenSelectorIndex calculation macros for M-PHY attributes */ +#define UIC_ARG_MPHY_TX_GEN_SEL_INDEX(lane) (lane) +#define UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane) (PA_MAXDATALANES + (lane)) + +#define UIC_ARG_MIB_SEL(attr, sel) ((((attr) & 0xFFFF) << 16) |\ + ((sel) & 0xFFFF)) +#define UIC_ARG_MIB(attr) UIC_ARG_MIB_SEL(attr, 0) +#define UIC_ARG_ATTR_TYPE(t) (((t) & 0xFF) << 16) +#define UIC_GET_ATTR_ID(v) (((v) >> 16) & 0xFFFF) + +/* Link Status*/ +enum link_status { + UFSHCD_LINK_IS_DOWN = 1, + UFSHCD_LINK_IS_UP = 2, +}; + +/* UIC Commands */ +enum uic_cmd_dme { + UIC_CMD_DME_GET = 0x01, + UIC_CMD_DME_SET = 0x02, + UIC_CMD_DME_PEER_GET = 0x03, + UIC_CMD_DME_PEER_SET = 0x04, + UIC_CMD_DME_POWERON = 0x10, + UIC_CMD_DME_POWEROFF = 0x11, + UIC_CMD_DME_ENABLE = 0x12, + UIC_CMD_DME_RESET = 0x14, + UIC_CMD_DME_END_PT_RST = 0x15, + UIC_CMD_DME_LINK_STARTUP = 0x16, + UIC_CMD_DME_HIBER_ENTER = 0x17, + UIC_CMD_DME_HIBER_EXIT = 0x18, + UIC_CMD_DME_TEST_MODE = 0x1A, +}; + +/* UIC Config result code / Generic error code */ +enum { + UIC_CMD_RESULT_SUCCESS = 0x00, + UIC_CMD_RESULT_INVALID_ATTR = 0x01, + UIC_CMD_RESULT_FAILURE = 0x01, + UIC_CMD_RESULT_INVALID_ATTR_VALUE = 0x02, + UIC_CMD_RESULT_READ_ONLY_ATTR = 0x03, + UIC_CMD_RESULT_WRITE_ONLY_ATTR = 0x04, + UIC_CMD_RESULT_BAD_INDEX = 0x05, + UIC_CMD_RESULT_LOCKED_ATTR = 0x06, + UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX = 0x07, + UIC_CMD_RESULT_PEER_COMM_FAILURE = 0x08, + UIC_CMD_RESULT_BUSY = 0x09, + UIC_CMD_RESULT_DME_FAILURE = 0x0A, +}; + +#define MASK_UIC_COMMAND_RESULT 0xFF + +#define INT_AGGR_COUNTER_THLD_VAL(c) (((c) & 0x1F) << 8) +#define INT_AGGR_TIMEOUT_VAL(t) (((t) & 0xFF) << 0) + +/* + * Request Descriptor Definitions + */ + +/* To accommodate UFS2.0 required Command type */ +enum { + UTP_CMD_TYPE_UFS_STORAGE = 0x1, +}; + +enum { + UTP_SCSI_COMMAND = 0x00000000, + UTP_REQ_DESC_INT_CMD = 0x01000000, + UTP_NATIVE_UFS_COMMAND = 0x10000000, + UTP_DEVICE_MANAGEMENT_FUNCTION = 0x20000000, +}; + +/* UTP Transfer Request Data Direction (DD) */ +enum utp_data_direction { + UTP_NO_DATA_TRANSFER = 0, + UTP_HOST_TO_DEVICE = 1, + UTP_DEVICE_TO_HOST = 2, +}; + +/* Overall command status values */ +enum utp_ocs { + OCS_SUCCESS = 0x0, + OCS_INVALID_CMD_TABLE_ATTR = 0x1, + OCS_INVALID_PRDT_ATTR = 0x2, + OCS_MISMATCH_DATA_BUF_SIZE = 0x3, + OCS_MISMATCH_RESP_UPIU_SIZE = 0x4, + OCS_PEER_COMM_FAILURE = 0x5, + OCS_ABORTED = 0x6, + OCS_FATAL_ERROR = 0x7, + OCS_DEVICE_FATAL_ERROR = 0x8, + OCS_INVALID_CRYPTO_CONFIG = 0x9, + OCS_GENERAL_CRYPTO_ERROR = 0xA, + OCS_INVALID_COMMAND_STATUS = 0x0F, +}; + +enum { + MASK_OCS = 0x0F, +}; + +/* The maximum length of the data byte count field in the PRDT is 256KB */ +#define PRDT_DATA_BYTE_COUNT_MAX SZ_256K +/* The granularity of the data byte count field in the PRDT is 32-bit */ +#define PRDT_DATA_BYTE_COUNT_PAD 4 + + +struct ufshcd_sg_entry { + __le32 base_addr; + __le32 upper_addr; + __le32 reserved; + __le32 size; +}; + +#define MAX_BUFF 128 +/** + * struct utp_transfer_cmd_desc - UFS Command Descriptor structure + * @command_upiu: Command UPIU Frame address + * @response_upiu: Response UPIU Frame address + * @prd_table: Physical Region Descriptor + */ +struct utp_transfer_cmd_desc { + u8 command_upiu[ALIGNED_UPIU_SIZE]; + u8 response_upiu[ALIGNED_UPIU_SIZE]; + struct ufshcd_sg_entry prd_table[MAX_BUFF]; +}; + +/** + * struct request_desc_header - Descriptor Header common to both UTRD and UTMRD + * @dword0: Descriptor Header DW0 + * @dword1: Descriptor Header DW1 + * @dword2: Descriptor Header DW2 + * @dword3: Descriptor Header DW3 + */ +struct request_desc_header { + __le32 dword_0; + __le32 dword_1; + __le32 dword_2; + __le32 dword_3; +}; + +/** + * struct utp_transfer_req_desc - UTRD structure + * @header: UTRD header DW-0 to DW-3 + * @command_desc_base_addr_lo: UCD base address low DW-4 + * @command_desc_base_addr_hi: UCD base address high DW-5 + * @response_upiu_length: response UPIU length DW-6 + * @response_upiu_offset: response UPIU offset DW-6 + * @prd_table_length: Physical region descriptor length DW-7 + * @prd_table_offset: Physical region descriptor offset DW-7 + */ +struct utp_transfer_req_desc { + /* DW 0-3 */ + struct request_desc_header header; + + /* DW 4-5*/ + __le32 command_desc_base_addr_lo; + __le32 command_desc_base_addr_hi; + + /* DW 6 */ + __le16 response_upiu_length; + __le16 response_upiu_offset; + + /* DW 7 */ + __le16 prd_table_length; + __le16 prd_table_offset; +}; + +/** + * struct utp_upiu_header - UPIU header structure + * @dword_0: UPIU header DW-0 + * @dword_1: UPIU header DW-1 + * @dword_2: UPIU header DW-2 + */ +struct utp_upiu_header { + __be32 dword_0; + __be32 dword_1; + __be32 dword_2; +}; + +/* + * UTMRD structure. + */ +struct utp_task_req_desc { + /* DW 0-3 */ + struct request_desc_header header; + + /* DW 4-11 - Task request UPIU structure */ + struct utp_upiu_header req_header; + __be32 input_param1; + __be32 input_param2; + __be32 input_param3; + __be32 __reserved1[2]; + + /* DW 12-19 - Task Management Response UPIU structure */ + struct utp_upiu_header rsp_header; + __be32 output_param1; + __be32 output_param2; + __be32 __reserved2[3]; +}; + +#endif diff --git a/drivers/ufs/unipro.h b/drivers/ufs/unipro.h index 6df953e6e60..56833602b77 100644 --- a/drivers/ufs/unipro.h +++ b/drivers/ufs/unipro.h @@ -34,6 +34,18 @@ /* * M-RX Configuration Attributes */ +#define RX_HS_G1_SYNC_LENGTH_CAP 0x008B +#define RX_HS_G1_PREP_LENGTH_CAP 0x008C +#define RX_MIN_ACTIVATETIME_CAPABILITY 0x008F +#define RX_HIBERN8TIME_CAPABILITY 0x0092 +#define RX_HS_G2_SYNC_LENGTH_CAP 0x0094 +#define RX_HS_G3_SYNC_LENGTH_CAP 0x0095 +#define RX_HS_G2_PREP_LENGTH_CAP 0x0096 +#define RX_HS_G3_PREP_LENGTH_CAP 0x0097 +#define RX_ADV_GRANULARITY_CAP 0x0098 +#define RX_HIBERN8TIME_CAP 0x0092 +#define RX_ADV_HIBERN8TIME_CAP 0x0099 +#define RX_ADV_MIN_ACTIVATETIME_CAP 0x009A #define RX_MODE 0x00A1 #define RX_HSRATE_SERIES 0x00A2 #define RX_HSGEAR 0x00A3 @@ -42,24 +54,27 @@ #define RX_HS_UNTERMINATED_ENABLE 0x00A6 #define RX_ENTER_HIBERN8 0x00A7 #define RX_BYPASS_8B10B_ENABLE 0x00A8 -#define RX_TERMINATION_FORCE_ENABLE 0x0089 -#define RX_MIN_ACTIVATETIME_CAPABILITY 0x008F -#define RX_HIBERN8TIME_CAPABILITY 0x0092 +#define RX_TERMINATION_FORCE_ENABLE 0x00A9 +#define RXCALCTRL 0x00B4 +#define RXSQCTRL 0x00B5 +#define CFGRXCDR8 0x00BA +#define CFGRXOVR8 0x00BD +#define CFGRXOVR6 0x00BF +#define RXDIRECTCTRL2 0x00C7 +#define CFGRXOVR4 0x00E9 #define RX_REFCLKFREQ 0x00EB #define RX_CFGCLKFREQVAL 0x00EC #define CFGWIDEINLN 0x00F0 -#define CFGRXCDR8 0x00BA #define ENARXDIRECTCFG4 0x00F2 -#define CFGRXOVR8 0x00BD -#define RXDIRECTCTRL2 0x00C7 #define ENARXDIRECTCFG3 0x00F3 -#define RXCALCTRL 0x00B4 #define ENARXDIRECTCFG2 0x00F4 -#define CFGRXOVR4 0x00E9 -#define RXSQCTRL 0x00B5 -#define CFGRXOVR6 0x00BF -#define is_mphy_tx_attr(attr) (attr < RX_MODE) +#define is_mphy_tx_attr(attr) ((attr) < RX_MODE) +#define RX_ADV_FINE_GRAN_STEP(x) ((((x) & 0x3) << 1) | 0x1) +#define SYNC_LEN_FINE(x) ((x) & 0x3F) +#define SYNC_LEN_COARSE(x) ((1 << 6) | ((x) & 0x3F)) +#define PREP_LEN(x) ((x) & 0xF) + #define RX_MIN_ACTIVATETIME_UNIT_US 100 #define HIBERN8TIME_UNIT_US 100 @@ -77,51 +92,54 @@ #define CBPRGPLL2 UNIPRO_CB_OFFSET(0x00F8) #define CBPRGTUNING UNIPRO_CB_OFFSET(0x00FB) -#define UNIPRO_CB_OFFSET(x) (0x8000 | x) +#define UNIPRO_CB_OFFSET(x) (0x8000 | (x)) /* - * PHY Adpater attributes + * PHY Adapter attributes */ -#define PA_ACTIVETXDATALANES 0x1560 -#define PA_ACTIVERXDATALANES 0x1580 -#define PA_TXTRAILINGCLOCKS 0x1564 #define PA_PHY_TYPE 0x1500 #define PA_AVAILTXDATALANES 0x1520 -#define PA_AVAILRXDATALANES 0x1540 -#define PA_MINRXTRAILINGCLOCKS 0x1543 -#define PA_TXPWRSTATUS 0x1567 -#define PA_RXPWRSTATUS 0x1582 -#define PA_TXFORCECLOCK 0x1562 -#define PA_TXPWRMODE 0x1563 -#define PA_LEGACYDPHYESCDL 0x1570 #define PA_MAXTXSPEEDFAST 0x1521 #define PA_MAXTXSPEEDSLOW 0x1522 #define PA_MAXRXSPEEDFAST 0x1541 #define PA_MAXRXSPEEDSLOW 0x1542 #define PA_TXLINKSTARTUPHS 0x1544 +#define PA_AVAILRXDATALANES 0x1540 +#define PA_MINRXTRAILINGCLOCKS 0x1543 #define PA_LOCAL_TX_LCC_ENABLE 0x155E +#define PA_ACTIVETXDATALANES 0x1560 +#define PA_CONNECTEDTXDATALANES 0x1561 +#define PA_TXFORCECLOCK 0x1562 +#define PA_TXPWRMODE 0x1563 +#define PA_TXTRAILINGCLOCKS 0x1564 #define PA_TXSPEEDFAST 0x1565 #define PA_TXSPEEDSLOW 0x1566 -#define PA_REMOTEVERINFO 0x15A0 +#define PA_TXPWRSTATUS 0x1567 #define PA_TXGEAR 0x1568 #define PA_TXTERMINATION 0x1569 #define PA_HSSERIES 0x156A +#define PA_LEGACYDPHYESCDL 0x1570 #define PA_PWRMODE 0x1571 +#define PA_ACTIVERXDATALANES 0x1580 +#define PA_CONNECTEDRXDATALANES 0x1581 +#define PA_RXPWRSTATUS 0x1582 #define PA_RXGEAR 0x1583 #define PA_RXTERMINATION 0x1584 #define PA_MAXRXPWMGEAR 0x1586 #define PA_MAXRXHSGEAR 0x1587 -#define PA_RXHSUNTERMCAP 0x15A5 -#define PA_RXLSTERMCAP 0x15A6 -#define PA_GRANULARITY 0x15AA #define PA_PACPREQTIMEOUT 0x1590 #define PA_PACPREQEOBTIMEOUT 0x1591 +#define PA_REMOTEVERINFO 0x15A0 +#define PA_LOGICALLANEMAP 0x15A1 +#define PA_SLEEPNOCONFIGTIME 0x15A2 +#define PA_STALLNOCONFIGTIME 0x15A3 +#define PA_SAVECONFIGTIME 0x15A4 +#define PA_RXHSUNTERMCAP 0x15A5 +#define PA_RXLSTERMCAP 0x15A6 #define PA_HIBERN8TIME 0x15A7 #define PA_LOCALVERINFO 0x15A9 +#define PA_GRANULARITY 0x15AA #define PA_TACTIVATE 0x15A8 -#define PA_PACPFRAMECOUNT 0x15C0 -#define PA_PACPERRORCOUNT 0x15C1 -#define PA_PHYTESTCONTROL 0x15C2 #define PA_PWRMODEUSERDATA0 0x15B0 #define PA_PWRMODEUSERDATA1 0x15B1 #define PA_PWRMODEUSERDATA2 0x15B2 @@ -134,12 +152,9 @@ #define PA_PWRMODEUSERDATA9 0x15B9 #define PA_PWRMODEUSERDATA10 0x15BA #define PA_PWRMODEUSERDATA11 0x15BB -#define PA_CONNECTEDTXDATALANES 0x1561 -#define PA_CONNECTEDRXDATALANES 0x1581 -#define PA_LOGICALLANEMAP 0x15A1 -#define PA_SLEEPNOCONFIGTIME 0x15A2 -#define PA_STALLNOCONFIGTIME 0x15A3 -#define PA_SAVECONFIGTIME 0x15A4 +#define PA_PACPFRAMECOUNT 0x15C0 +#define PA_PACPERRORCOUNT 0x15C1 +#define PA_PHYTESTCONTROL 0x15C2 #define PA_TXHSADAPTTYPE 0x15D4 /* Adapt type for PA_TXHSADAPTTYPE attribute */ @@ -151,9 +166,9 @@ #define PA_HIBERN8_TIME_UNIT_US 100 /*Other attributes*/ +#define VS_POWERSTATE 0xD083 #define VS_MPHYCFGUPDT 0xD085 #define VS_DEBUGOMC 0xD09E -#define VS_POWERSTATE 0xD083 #define VS_MPHYDISABLE 0xD0C1 #define PA_GRANULARITY_MIN_VAL 1 @@ -163,7 +178,7 @@ #define PA_MAXDATALANES 4 /* PA power modes */ -enum { +enum ufs_pa_pwr_mode { FAST_MODE = 1, SLOW_MODE = 2, FASTAUTO_MODE = 4, @@ -171,8 +186,11 @@ enum { UNCHANGED = 7, }; +#define PWRMODE_MASK 0xF +#define PWRMODE_RX_OFFSET 4 + /* PA TX/RX Frequency Series */ -enum { +enum ufs_hs_gear_rate { PA_HS_MODE_A = 1, PA_HS_MODE_B = 2, }; @@ -193,14 +211,24 @@ enum ufs_hs_gear_tag { UFS_HS_G1, /* HS Gear 1 (default for reset) */ UFS_HS_G2, /* HS Gear 2 */ UFS_HS_G3, /* HS Gear 3 */ + UFS_HS_G4, /* HS Gear 4 */ + UFS_HS_G5 /* HS Gear 5 */ +}; + +enum ufs_lanes { + UFS_LANE_DONT_CHANGE, /* Don't change Lane */ + UFS_LANE_1, /* Lane 1 (default for reset) */ + UFS_LANE_2, /* Lane 2 */ }; enum ufs_unipro_ver { UFS_UNIPRO_VER_RESERVED = 0, UFS_UNIPRO_VER_1_40 = 1, /* UniPro version 1.40 */ UFS_UNIPRO_VER_1_41 = 2, /* UniPro version 1.41 */ - UFS_UNIPRO_VER_1_6 = 3, /* UniPro version 1.6 */ - UFS_UNIPRO_VER_MAX = 4, /* UniPro unsupported version */ + UFS_UNIPRO_VER_1_6 = 3, /* UniPro version 1.6 */ + UFS_UNIPRO_VER_1_61 = 4, /* UniPro version 1.61 */ + UFS_UNIPRO_VER_1_8 = 5, /* UniPro version 1.8 */ + UFS_UNIPRO_VER_MAX = 6, /* UniPro unsupported version */ /* UniPro version field mask in PA_LOCALVERINFO */ UFS_UNIPRO_VER_MASK = 0xF, }; @@ -208,27 +236,27 @@ enum ufs_unipro_ver { /* * Data Link Layer Attributes */ +#define DL_TXPREEMPTIONCAP 0x2000 +#define DL_TC0TXMAXSDUSIZE 0x2001 +#define DL_TC0RXINITCREDITVAL 0x2002 +#define DL_TC1TXMAXSDUSIZE 0x2003 +#define DL_TC1RXINITCREDITVAL 0x2004 +#define DL_TC0TXBUFFERSIZE 0x2005 +#define DL_TC1TXBUFFERSIZE 0x2006 #define DL_TC0TXFCTHRESHOLD 0x2040 #define DL_FC0PROTTIMEOUTVAL 0x2041 #define DL_TC0REPLAYTIMEOUTVAL 0x2042 #define DL_AFC0REQTIMEOUTVAL 0x2043 #define DL_AFC0CREDITTHRESHOLD 0x2044 #define DL_TC0OUTACKTHRESHOLD 0x2045 +#define DL_PEERTC0PRESENT 0x2046 +#define DL_PEERTC0RXINITCREVAL 0x2047 #define DL_TC1TXFCTHRESHOLD 0x2060 #define DL_FC1PROTTIMEOUTVAL 0x2061 #define DL_TC1REPLAYTIMEOUTVAL 0x2062 #define DL_AFC1REQTIMEOUTVAL 0x2063 #define DL_AFC1CREDITTHRESHOLD 0x2064 #define DL_TC1OUTACKTHRESHOLD 0x2065 -#define DL_TXPREEMPTIONCAP 0x2000 -#define DL_TC0TXMAXSDUSIZE 0x2001 -#define DL_TC0RXINITCREDITVAL 0x2002 -#define DL_TC0TXBUFFERSIZE 0x2005 -#define DL_PEERTC0PRESENT 0x2046 -#define DL_PEERTC0RXINITCREVAL 0x2047 -#define DL_TC1TXMAXSDUSIZE 0x2003 -#define DL_TC1RXINITCREDITVAL 0x2004 -#define DL_TC1TXBUFFERSIZE 0x2006 #define DL_PEERTC1PRESENT 0x2066 #define DL_PEERTC1RXINITCREVAL 0x2067 diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig index 35b61497d9c..1d5e4afac6c 100644 --- a/drivers/usb/cdns3/Kconfig +++ b/drivers/usb/cdns3/Kconfig @@ -49,6 +49,13 @@ config SPL_USB_CDNS3_HOST Host controller is compliant with XHCI so it will use standard XHCI driver. +config USB_CDNS3_STARFIVE + tristate "Cadence USB3 support on Starfive platforms" + default y if STARFIVE_JH7110 + help + Say 'Y' here if you are building for Starfive platforms + that contain Cadence USB3 controller core. E.g.: JH7110. + config USB_CDNS3_TI tristate "Cadence USB3 support on TI platforms" default USB_CDNS3 diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile index d6047856091..1f00f23f704 100644 --- a/drivers/usb/cdns3/Makefile +++ b/drivers/usb/cdns3/Makefile @@ -8,4 +8,5 @@ cdns3-$(CONFIG_$(XPL_)USB_CDNS3_GADGET) += gadget.o ep0.o cdns3-$(CONFIG_$(XPL_)USB_CDNS3_HOST) += host.o +obj-$(CONFIG_USB_CDNS3_STARFIVE) += cdns3-starfive.o obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c new file mode 100644 index 00000000000..78ceb831b19 --- /dev/null +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller + * + * Copyright (C) 2024 StarFive Technology Co., Ltd. + * + * Author: Minda Chen <minda.chen@starfivetech.com> + */ + +#include <asm/io.h> +#include <clk.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <linux/usb/otg.h> +#include <malloc.h> +#include <reset.h> +#include <regmap.h> +#include <syscon.h> + +#include "core.h" + +#define USB_STRAP_HOST BIT(17) +#define USB_STRAP_DEVICE BIT(18) +#define USB_STRAP_MASK GENMASK(18, 16) + +#define USB_SUSPENDM_HOST BIT(19) +#define USB_SUSPENDM_MASK BIT(19) + +#define USB_MISC_CFG_MASK GENMASK(23, 20) +#define USB_SUSPENDM_BYPS BIT(20) +#define USB_PLL_EN BIT(22) +#define USB_REFCLK_MODE BIT(23) + +struct cdns_starfive { + struct udevice *dev; + struct regmap *stg_syscon; + struct reset_ctl_bulk resets; + struct clk_bulk clks; + u32 stg_usb_mode; + enum usb_dr_mode mode; +}; + +static void cdns_mode_init(struct cdns_starfive *data, enum usb_dr_mode mode) +{ + unsigned int strap, suspendm; + + regmap_update_bits(data->stg_syscon, data->stg_usb_mode, + USB_MISC_CFG_MASK, + USB_SUSPENDM_BYPS | USB_PLL_EN | USB_REFCLK_MODE); + + switch (mode) { + case USB_DR_MODE_HOST: + strap = USB_STRAP_HOST; + suspendm = USB_SUSPENDM_HOST; + break; + case USB_DR_MODE_PERIPHERAL: + strap = USB_STRAP_DEVICE; + suspendm = 0; + break; + default: + return; + } + + regmap_update_bits(data->stg_syscon, data->stg_usb_mode, + USB_SUSPENDM_MASK | USB_STRAP_MASK, + strap | suspendm); +} + +static void cdns_clk_rst_deinit(struct cdns_starfive *data) +{ + reset_assert_bulk(&data->resets); + clk_disable_bulk(&data->clks); +} + +static int cdns_clk_rst_init(struct cdns_starfive *data) +{ + int ret; + + ret = clk_get_bulk(data->dev, &data->clks); + if (ret) + return ret; + + ret = reset_get_bulk(data->dev, &data->resets); + if (ret) + goto err_clk; + + ret = clk_enable_bulk(&data->clks); + if (ret) { + dev_err(data->dev, "clk enable failed: %d\n", ret); + goto err_en_clk; + } + + ret = reset_deassert_bulk(&data->resets); + if (ret) { + dev_err(data->dev, "reset deassert failed: %d\n", ret); + goto err_reset; + } + + return 0; + +err_reset: + clk_disable_bulk(&data->clks); +err_en_clk: + reset_release_bulk(&data->resets); +err_clk: + clk_release_bulk(&data->clks); + + return ret; +} + +static int cdns_starfive_get_syscon(struct cdns_starfive *data) +{ + struct ofnode_phandle_args phandle; + int ret; + + ret = dev_read_phandle_with_args(data->dev, "starfive,stg-syscon", NULL, 1, 0, + &phandle); + if (ret < 0) { + dev_err(data->dev, "Can't get stg cfg phandle: %d\n", ret); + return ret; + } + + data->stg_syscon = syscon_node_to_regmap(phandle.node); + if (IS_ERR(data->stg_syscon)) { + dev_err(data->dev, "fail to get regmap: %d\n", (int)PTR_ERR(data->stg_syscon)); + return PTR_ERR(data->stg_syscon); + } + + data->stg_usb_mode = phandle.args[0]; + + return 0; +} + +static int cdns_starfive_probe(struct udevice *dev) +{ + struct cdns_starfive *data = dev_get_plat(dev); + enum usb_dr_mode dr_mode; + int ret; + + data->dev = dev; + + ret = cdns_starfive_get_syscon(data); + if (ret) + return ret; + + dr_mode = usb_get_dr_mode(dev_ofnode(dev)); + + data->mode = dr_mode; + ret = cdns_clk_rst_init(data); + if (ret) { + dev_err(data->dev, "clk reset failed: %d\n", ret); + return ret; + } + cdns_mode_init(data, dr_mode); + + return 0; +} + +static int cdns_starfive_remove(struct udevice *dev) +{ + struct cdns_starfive *data = dev_get_plat(dev); + + cdns_clk_rst_deinit(data); + return 0; +} + +static const struct udevice_id cdns_starfive_of_match[] = { + { .compatible = "starfive,jh7110-usb", }, + {}, +}; + +U_BOOT_DRIVER(cdns_starfive) = { + .name = "cdns-starfive", + .id = UCLASS_NOP, + .of_match = cdns_starfive_of_match, + .bind = cdns3_bind, + .probe = cdns_starfive_probe, + .remove = cdns_starfive_remove, + .plat_auto = sizeof(struct cdns_starfive), + .flags = DM_FLAG_OS_PREPARE, +}; diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 4cfd38ec245..4434dc15bec 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -410,6 +410,9 @@ int cdns3_bind(struct udevice *parent) name = ofnode_get_name(node); dr_mode = usb_get_dr_mode(node); + if (dr_mode == USB_DR_MODE_UNKNOWN) + dr_mode = usb_get_dr_mode(dev_ofnode(parent)); + switch (dr_mode) { #if defined(CONFIG_SPL_USB_HOST) || \ (!defined(CONFIG_XPL_BUILD) && defined(CONFIG_USB_HOST)) diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index 47874fec29e..cbb13342343 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -217,15 +217,19 @@ static int cdns3_init_otg_mode(struct cdns3 *cdns) int cdns3_drd_update_mode(struct cdns3 *cdns) { int ret = 0; + int mode; switch (cdns->dr_mode) { case USB_DR_MODE_PERIPHERAL: + mode = PHY_MODE_USB_DEVICE; ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL); break; case USB_DR_MODE_HOST: + mode = PHY_MODE_USB_HOST; ret = cdns3_set_mode(cdns, USB_DR_MODE_HOST); break; case USB_DR_MODE_OTG: + mode = PHY_MODE_USB_OTG; ret = cdns3_init_otg_mode(cdns); break; default: @@ -234,6 +238,16 @@ int cdns3_drd_update_mode(struct cdns3 *cdns) return -EINVAL; } + ret = generic_phy_set_mode(&cdns->usb2_phy, mode, 0); + if (ret) { + dev_err(cdns->dev, "Set usb 2.0 PHY mode failed %d\n", ret); + return ret; + } + + ret = generic_phy_set_mode(&cdns->usb3_phy, mode, 0); + if (ret) + dev_err(cdns->dev, "Set usb 3.0 PHY mode failed %d\n", ret); + return ret; } diff --git a/drivers/usb/emul/sandbox_keyb.c b/drivers/usb/emul/sandbox_keyb.c index db769883ba3..5ed8c2c799a 100644 --- a/drivers/usb/emul/sandbox_keyb.c +++ b/drivers/usb/emul/sandbox_keyb.c @@ -38,7 +38,7 @@ enum { * */ struct sandbox_keyb_priv { - struct membuff in; + struct membuf in; }; struct sandbox_keyb_plat { @@ -167,7 +167,7 @@ int sandbox_usb_keyb_add_string(struct udevice *dev, struct sandbox_keyb_priv *priv = dev_get_priv(dev); int ret; - ret = membuff_put(&priv->in, scancode, USB_KBD_BOOT_REPORT_SIZE); + ret = membuf_put(&priv->in, scancode, USB_KBD_BOOT_REPORT_SIZE); if (ret != USB_KBD_BOOT_REPORT_SIZE) return -ENOSPC; @@ -194,7 +194,7 @@ static int sandbox_keyb_interrupt(struct udevice *dev, struct usb_device *udev, if (length < USB_KBD_BOOT_REPORT_SIZE) return 0; - membuff_get(&priv->in, buffer, USB_KBD_BOOT_REPORT_SIZE); + membuf_get(&priv->in, buffer, USB_KBD_BOOT_REPORT_SIZE); return 0; } @@ -220,7 +220,7 @@ static int sandbox_keyb_probe(struct udevice *dev) struct sandbox_keyb_priv *priv = dev_get_priv(dev); /* Provide an 80 character keyboard buffer */ - return membuff_new(&priv->in, 80 * USB_KBD_BOOT_REPORT_SIZE); + return membuf_new(&priv->in, 80 * USB_KBD_BOOT_REPORT_SIZE); } static const struct dm_usb_ops sandbox_usb_keyb_ops = { diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 4bda224ff1a..db5f8895a33 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -35,7 +35,3 @@ endif endif obj-$(CONFIG_CI_UDC) += ci_udc.o - -# Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE -# This is really only N900 and USBTTY now. -obj-$(CONFIG_USB_DEVICE) += core.o ep0.o diff --git a/drivers/usb/gadget/core.c b/drivers/usb/gadget/core.c deleted file mode 100644 index bcb1ad3082c..00000000000 --- a/drivers/usb/gadget/core.c +++ /dev/null @@ -1,621 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2003 - * Gerry Hamel, geh@ti.com, Texas Instruments - * - * Based on - * linux/drivers/usbd/usbd.c.c - USB Device Core Layer - * - * Copyright (c) 2000, 2001, 2002 Lineo - * Copyright (c) 2001 Hewlett Packard - * - * By: - * Stuart Lynne <sl@lineo.com>, - * Tom Rushworth <tbr@lineo.com>, - * Bruce Balden <balden@lineo.com> - */ - -#include <log.h> -#include <malloc.h> -#include <serial.h> -#include <usbdevice.h> - -#define MAX_INTERFACES 2 - -int maxstrings = 20; - -/* Global variables ************************************************************************** */ - -struct usb_string_descriptor **usb_strings; - -int usb_devices; - -extern struct usb_function_driver ep0_driver; - -int registered_functions; -int registered_devices; - -__maybe_unused static char *usbd_device_events[] = { - "DEVICE_UNKNOWN", - "DEVICE_INIT", - "DEVICE_CREATE", - "DEVICE_HUB_CONFIGURED", - "DEVICE_RESET", - "DEVICE_ADDRESS_ASSIGNED", - "DEVICE_CONFIGURED", - "DEVICE_SET_INTERFACE", - "DEVICE_SET_FEATURE", - "DEVICE_CLEAR_FEATURE", - "DEVICE_DE_CONFIGURED", - "DEVICE_BUS_INACTIVE", - "DEVICE_BUS_ACTIVITY", - "DEVICE_POWER_INTERRUPTION", - "DEVICE_HUB_RESET", - "DEVICE_DESTROY", - "DEVICE_FUNCTION_PRIVATE", -}; - -__maybe_unused static char *usbd_device_status[] = { - "USBD_OPENING", - "USBD_OK", - "USBD_SUSPENDED", - "USBD_CLOSING", -}; - -#define USBD_DEVICE_STATUS(x) (((unsigned int)x <= USBD_CLOSING) ? usbd_device_status[x] : "UNKNOWN") - -/* Descriptor support functions ************************************************************** */ - -/** - * usbd_get_string - find and return a string descriptor - * @index: string index to return - * - * Find an indexed string and return a pointer to a it. - */ -struct usb_string_descriptor *usbd_get_string (__u8 index) -{ - if (index >= maxstrings) { - return NULL; - } - return usb_strings[index]; -} - -/* Access to device descriptor functions ***************************************************** */ - -/* * - * usbd_device_configuration_instance - find a configuration instance for this device - * @device: - * @configuration: index to configuration, 0 - N-1 - * - * Get specifed device configuration. Index should be bConfigurationValue-1. - */ -static struct usb_configuration_instance *usbd_device_configuration_instance (struct usb_device_instance *device, - unsigned int port, unsigned int configuration) -{ - if (configuration >= device->configurations) - return NULL; - - return device->configuration_instance_array + configuration; -} - -/* * - * usbd_device_interface_instance - * @device: - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * - * Return the specified interface descriptor for the specified device. - */ -struct usb_interface_instance *usbd_device_interface_instance (struct usb_device_instance *device, int port, int configuration, int interface) -{ - struct usb_configuration_instance *configuration_instance; - - if ((configuration_instance = usbd_device_configuration_instance (device, port, configuration)) == NULL) { - return NULL; - } - if (interface >= configuration_instance->interfaces) { - return NULL; - } - return configuration_instance->interface_instance_array + interface; -} - -/* * - * usbd_device_alternate_descriptor_list - * @device: - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * @alternate: alternate setting - * - * Return the specified alternate descriptor for the specified device. - */ -struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device_instance *device, int port, int configuration, int interface, int alternate) -{ - struct usb_interface_instance *interface_instance; - - if ((interface_instance = usbd_device_interface_instance (device, port, configuration, interface)) == NULL) { - return NULL; - } - - if (alternate >= interface_instance->alternates) { - return NULL; - } - - return interface_instance->alternates_instance_array + alternate; -} - -/* * - * usbd_device_device_descriptor - * @device: which device - * @configuration: index to configuration, 0 - N-1 - * @port: which port - * - * Return the specified configuration descriptor for the specified device. - */ -struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *device, int port) -{ - return (device->device_descriptor); -} - -/** - * usbd_device_configuration_descriptor - * @device: which device - * @port: which port - * @configuration: index to configuration, 0 - N-1 - * - * Return the specified configuration descriptor for the specified device. - */ -struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struct - usb_device_instance - *device, int port, int configuration) -{ - struct usb_configuration_instance *configuration_instance; - if (!(configuration_instance = usbd_device_configuration_instance (device, port, configuration))) { - return NULL; - } - return (configuration_instance->configuration_descriptor); -} - -/** - * usbd_device_interface_descriptor - * @device: which device - * @port: which port - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * @alternate: alternate setting - * - * Return the specified interface descriptor for the specified device. - */ -struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_device_instance - *device, int port, int configuration, int interface, int alternate) -{ - struct usb_interface_instance *interface_instance; - if (!(interface_instance = usbd_device_interface_instance (device, port, configuration, interface))) { - return NULL; - } - if ((alternate < 0) || (alternate >= interface_instance->alternates)) { - return NULL; - } - return (interface_instance->alternates_instance_array[alternate].interface_descriptor); -} - -/** - * usbd_device_endpoint_descriptor_index - * @device: which device - * @port: which port - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * @alternate: index setting - * @index: which index - * - * Return the specified endpoint descriptor for the specified device. - */ -struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor_index (struct usb_device_instance - *device, int port, int configuration, int interface, int alternate, int index) -{ - struct usb_alternate_instance *alternate_instance; - - if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) { - return NULL; - } - if (index >= alternate_instance->endpoints) { - return NULL; - } - return *(alternate_instance->endpoints_descriptor_array + index); -} - -/** - * usbd_device_endpoint_transfersize - * @device: which device - * @port: which port - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * @index: which index - * - * Return the specified endpoint transfer size; - */ -int usbd_device_endpoint_transfersize (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int index) -{ - struct usb_alternate_instance *alternate_instance; - - if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) { - return 0; - } - if (index >= alternate_instance->endpoints) { - return 0; - } - return *(alternate_instance->endpoint_transfersize_array + index); -} - -/** - * usbd_device_endpoint_descriptor - * @device: which device - * @port: which port - * @configuration: index to configuration, 0 - N-1 - * @interface: index to interface - * @alternate: alternate setting - * @endpoint: which endpoint - * - * Return the specified endpoint descriptor for the specified device. - */ -struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int endpoint) -{ - struct usb_endpoint_descriptor *endpoint_descriptor; - int i; - - for (i = 0; !(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, port, configuration, interface, alternate, i)); i++) { - if (endpoint_descriptor->bEndpointAddress == endpoint) { - return endpoint_descriptor; - } - } - return NULL; -} - -/** - * usbd_endpoint_halted - * @device: point to struct usb_device_instance - * @endpoint: endpoint to check - * - * Return non-zero if endpoint is halted. - */ -int usbd_endpoint_halted (struct usb_device_instance *device, int endpoint) -{ - return (device->status == USB_STATUS_HALT); -} - -/** - * usbd_rcv_complete - complete a receive - * @endpoint: - * @len: - * @urb_bad: - * - * Called from rcv interrupt to complete. - */ -void usbd_rcv_complete(struct usb_endpoint_instance *endpoint, int len, int urb_bad) -{ - if (endpoint) { - struct urb *rcv_urb; - - /*usbdbg("len: %d urb: %p\n", len, endpoint->rcv_urb); */ - - /* if we had an urb then update actual_length, dispatch if neccessary */ - if ((rcv_urb = endpoint->rcv_urb)) { - - /*usbdbg("actual: %d buffer: %d\n", */ - /*rcv_urb->actual_length, rcv_urb->buffer_length); */ - - /* check the urb is ok, are we adding data less than the packetsize */ - if (!urb_bad && (len <= endpoint->rcv_packetSize)) { - /*usbdbg("updating actual_length by %d\n",len); */ - - /* increment the received data size */ - rcv_urb->actual_length += len; - - } else { - usberr(" RECV_ERROR actual: %d buffer: %d urb_bad: %d\n", - rcv_urb->actual_length, rcv_urb->buffer_length, urb_bad); - - rcv_urb->actual_length = 0; - rcv_urb->status = RECV_ERROR; - } - } else { - usberr("no rcv_urb!"); - } - } else { - usberr("no endpoint!"); - } - -} - -/** - * usbd_tx_complete - complete a transmit - * @endpoint: - * @resetart: - * - * Called from tx interrupt to complete. - */ -void usbd_tx_complete (struct usb_endpoint_instance *endpoint) -{ - if (endpoint) { - struct urb *tx_urb; - - /* if we have a tx_urb advance or reset, finish if complete */ - if ((tx_urb = endpoint->tx_urb)) { - int sent = endpoint->last; - endpoint->sent += sent; - endpoint->last -= sent; - - if( (endpoint->tx_urb->actual_length - endpoint->sent) <= 0 ) { - tx_urb->actual_length = 0; - endpoint->sent = 0; - endpoint->last = 0; - - /* Remove from active, save for re-use */ - urb_detach(tx_urb); - urb_append(&endpoint->done, tx_urb); - /*usbdbg("done->next %p, tx_urb %p, done %p", */ - /* endpoint->done.next, tx_urb, &endpoint->done); */ - - endpoint->tx_urb = first_urb_detached(&endpoint->tx); - if( endpoint->tx_urb ) { - endpoint->tx_queue--; - usbdbg("got urb from tx list"); - } - if( !endpoint->tx_urb ) { - /*usbdbg("taking urb from done list"); */ - endpoint->tx_urb = first_urb_detached(&endpoint->done); - } - if( !endpoint->tx_urb ) { - usbdbg("allocating new urb for tx_urb"); - endpoint->tx_urb = usbd_alloc_urb(tx_urb->device, endpoint); - } - } - } - } -} - -/* URB linked list functions ***************************************************** */ - -/* - * Initialize an urb_link to be a single element list. - * If the urb_link is being used as a distinguished list head - * the list is empty when the head is the only link in the list. - */ -void urb_link_init (urb_link * ul) -{ - if (ul) { - ul->prev = ul->next = ul; - } -} - -/* - * Detach an urb_link from a list, and set it - * up as a single element list, so no dangling - * pointers can be followed, and so it can be - * joined to another list if so desired. - */ -void urb_detach (struct urb *urb) -{ - if (urb) { - urb_link *ul = &urb->link; - ul->next->prev = ul->prev; - ul->prev->next = ul->next; - urb_link_init (ul); - } -} - -/* - * Return the first urb_link in a list with a distinguished - * head "hd", or NULL if the list is empty. This will also - * work as a predicate, returning NULL if empty, and non-NULL - * otherwise. - */ -urb_link *first_urb_link (urb_link * hd) -{ - urb_link *nx; - if (NULL != hd && NULL != (nx = hd->next) && nx != hd) { - /* There is at least one element in the list */ - /* (besides the distinguished head). */ - return (nx); - } - /* The list is empty */ - return (NULL); -} - -/* - * Return the first urb in a list with a distinguished - * head "hd", or NULL if the list is empty. - */ -struct urb *first_urb (urb_link * hd) -{ - urb_link *nx; - if (NULL == (nx = first_urb_link (hd))) { - /* The list is empty */ - return (NULL); - } - return (p2surround (struct urb, link, nx)); -} - -/* - * Detach and return the first urb in a list with a distinguished - * head "hd", or NULL if the list is empty. - * - */ -struct urb *first_urb_detached (urb_link * hd) -{ - struct urb *urb; - if ((urb = first_urb (hd))) { - urb_detach (urb); - } - return urb; -} - -/* - * Append an urb_link (or a whole list of - * urb_links) to the tail of another list - * of urb_links. - */ -void urb_append (urb_link * hd, struct urb *urb) -{ - if (hd && urb) { - urb_link *new = &urb->link; - - /* This allows the new urb to be a list of urbs, */ - /* with new pointing at the first, but the link */ - /* must be initialized. */ - /* Order is important here... */ - urb_link *pul = hd->prev; - new->prev->next = hd; - hd->prev = new->prev; - new->prev = pul; - pul->next = new; - } -} - -/* URB create/destroy functions ***************************************************** */ - -/** - * usbd_alloc_urb - allocate an URB appropriate for specified endpoint - * @device: device instance - * @endpoint: endpoint - * - * Allocate an urb structure. The usb device urb structure is used to - * contain all data associated with a transfer, including a setup packet for - * control transfers. - * - * NOTE: endpoint_address MUST contain a direction flag. - */ -struct urb *usbd_alloc_urb (struct usb_device_instance *device, - struct usb_endpoint_instance *endpoint) -{ - struct urb *urb; - - if (!(urb = (struct urb *) malloc (sizeof (struct urb)))) { - usberr (" F A T A L: malloc(%zu) FAILED!!!!", - sizeof (struct urb)); - return NULL; - } - - /* Fill in known fields */ - memset (urb, 0, sizeof (struct urb)); - urb->endpoint = endpoint; - urb->device = device; - urb->buffer = (u8 *) urb->buffer_data; - urb->buffer_length = sizeof (urb->buffer_data); - - urb_link_init (&urb->link); - - return urb; -} - -/** - * usbd_dealloc_urb - deallocate an URB and associated buffer - * @urb: pointer to an urb structure - * - * Deallocate an urb structure and associated data. - */ -void usbd_dealloc_urb (struct urb *urb) -{ - if (urb) { - free (urb); - } -} - -/* Event signaling functions ***************************************************** */ - -/** - * usbd_device_event - called to respond to various usb events - * @device: pointer to struct device - * @event: event to respond to - * - * Used by a Bus driver to indicate an event. - */ -void usbd_device_event_irq (struct usb_device_instance *device, usb_device_event_t event, int data) -{ - usb_device_state_t state; - - if (!device || !device->bus) { - usberr("(%p,%d) NULL device or device->bus", device, event); - return; - } - - state = device->device_state; - - usbinfo("%s", usbd_device_events[event]); - - switch (event) { - case DEVICE_UNKNOWN: - break; - case DEVICE_INIT: - device->device_state = STATE_INIT; - break; - - case DEVICE_CREATE: - device->device_state = STATE_ATTACHED; - break; - - case DEVICE_HUB_CONFIGURED: - device->device_state = STATE_POWERED; - break; - - case DEVICE_RESET: - device->device_state = STATE_DEFAULT; - device->address = 0; - break; - - case DEVICE_ADDRESS_ASSIGNED: - device->device_state = STATE_ADDRESSED; - break; - - case DEVICE_CONFIGURED: - device->device_state = STATE_CONFIGURED; - break; - - case DEVICE_DE_CONFIGURED: - device->device_state = STATE_ADDRESSED; - break; - - case DEVICE_BUS_INACTIVE: - if (device->status != USBD_CLOSING) { - device->status = USBD_SUSPENDED; - } - break; - case DEVICE_BUS_ACTIVITY: - if (device->status != USBD_CLOSING) { - device->status = USBD_OK; - } - break; - - case DEVICE_SET_INTERFACE: - break; - case DEVICE_SET_FEATURE: - break; - case DEVICE_CLEAR_FEATURE: - break; - - case DEVICE_POWER_INTERRUPTION: - device->device_state = STATE_POWERED; - break; - case DEVICE_HUB_RESET: - device->device_state = STATE_ATTACHED; - break; - case DEVICE_DESTROY: - device->device_state = STATE_UNKNOWN; - break; - - case DEVICE_FUNCTION_PRIVATE: - break; - - default: - usbdbg("event %d - not handled",event); - break; - } - debug("%s event: %d oldstate: %d newstate: %d status: %d address: %d", - device->name, event, state, - device->device_state, device->status, device->address); - - /* tell the bus interface driver */ - if( device->event ) { - /* usbdbg("calling device->event"); */ - device->event(device, event, data); - } -} diff --git a/drivers/usb/gadget/ep0.c b/drivers/usb/gadget/ep0.c deleted file mode 100644 index 8c7fc17c2ea..00000000000 --- a/drivers/usb/gadget/ep0.c +++ /dev/null @@ -1,619 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * (C) Copyright 2003 - * Gerry Hamel, geh@ti.com, Texas Instruments - * - * (C) Copyright 2006 - * Bryan O'Donoghue, deckard@CodeHermit.ie - * - * Based on - * linux/drivers/usbd/ep0.c - * - * Copyright (c) 2000, 2001, 2002 Lineo - * Copyright (c) 2001 Hewlett Packard - * - * By: - * Stuart Lynne <sl@lineo.com>, - * Tom Rushworth <tbr@lineo.com>, - * Bruce Balden <balden@lineo.com> - */ - -/* - * This is the builtin ep0 control function. It implements all required functionality - * for responding to control requests (SETUP packets). - * - * XXX - * - * Currently we do not pass any SETUP packets (or other) to the configured - * function driver. This may need to change. - * - * XXX - * - * As alluded to above, a simple callback cdc_recv_setup has been implemented - * in the usb_device data structure to facilicate passing - * Common Device Class packets to a function driver. - * - * XXX - */ - -#include <serial.h> -#include <usbdevice.h> - -#if 0 -#define dbg_ep0(lvl,fmt,args...) serial_printf("[%s] %s:%d: "fmt"\n",__FILE__,__FUNCTION__,__LINE__,##args) -#else -#define dbg_ep0(lvl,fmt,args...) -#endif - -__maybe_unused static char *usbd_device_descriptors[] = { - "UNKNOWN", /* 0 */ - "DEVICE", /* 1 */ - "CONFIG", /* 2 */ - "STRING", /* 3 */ - "INTERFACE", /* 4 */ - "ENDPOINT", /* 5 */ - "DEVICE QUALIFIER", /* 6 */ - "OTHER SPEED", /* 7 */ - "INTERFACE POWER", /* 8 */ -}; - -#define USBD_DEVICE_DESCRIPTORS(x) (((unsigned int)x <= USB_DESCRIPTOR_TYPE_INTERFACE_POWER) ? \ - usbd_device_descriptors[x] : "UNKNOWN") - -__maybe_unused static char *usbd_device_states[] = { - "STATE_INIT", - "STATE_CREATED", - "STATE_ATTACHED", - "STATE_POWERED", - "STATE_DEFAULT", - "STATE_ADDRESSED", - "STATE_CONFIGURED", - "STATE_UNKNOWN", -}; - -#define USBD_DEVICE_STATE(x) (((unsigned int)x <= STATE_UNKNOWN) ? usbd_device_states[x] : "UNKNOWN") - -__maybe_unused static char *usbd_device_requests[] = { - "GET STATUS", /* 0 */ - "CLEAR FEATURE", /* 1 */ - "RESERVED", /* 2 */ - "SET FEATURE", /* 3 */ - "RESERVED", /* 4 */ - "SET ADDRESS", /* 5 */ - "GET DESCRIPTOR", /* 6 */ - "SET DESCRIPTOR", /* 7 */ - "GET CONFIGURATION", /* 8 */ - "SET CONFIGURATION", /* 9 */ - "GET INTERFACE", /* 10 */ - "SET INTERFACE", /* 11 */ - "SYNC FRAME", /* 12 */ -}; - -#define USBD_DEVICE_REQUESTS(x) (((unsigned int)x <= USB_REQ_SYNCH_FRAME) ? usbd_device_requests[x] : "UNKNOWN") - -/* EP0 Configuration Set ********************************************************************* */ - -/** - * ep0_get_status - fill in URB data with appropriate status - * @device: - * @urb: - * @index: - * @requesttype: - * - */ -static int ep0_get_status (struct usb_device_instance *device, - struct urb *urb, int index, int requesttype) -{ - char *cp; - - urb->actual_length = 2; - cp = (char*)urb->buffer; - cp[0] = cp[1] = 0; - - switch (requesttype) { - case USB_REQ_RECIPIENT_DEVICE: - cp[0] = USB_STATUS_SELFPOWERED; - break; - case USB_REQ_RECIPIENT_INTERFACE: - break; - case USB_REQ_RECIPIENT_ENDPOINT: - cp[0] = usbd_endpoint_halted (device, index); - break; - case USB_REQ_RECIPIENT_OTHER: - urb->actual_length = 0; - default: - break; - } - dbg_ep0 (2, "%02x %02x", cp[0], cp[1]); - return 0; -} - -/** - * ep0_get_one - * @device: - * @urb: - * @result: - * - * Set a single byte value in the urb send buffer. Return non-zero to signal - * a request error. - */ -static int ep0_get_one (struct usb_device_instance *device, struct urb *urb, - __u8 result) -{ - urb->actual_length = 1; /* XXX 2? */ - ((char *) urb->buffer)[0] = result; - return 0; -} - -/** - * copy_config - * @urb: pointer to urb - * @data: pointer to configuration data - * @length: length of data - * - * Copy configuration data to urb transfer buffer if there is room for it. - */ -void copy_config (struct urb *urb, void *data, int max_length, - int max_buf) -{ - int available; - int length; - - /*dbg_ep0(3, "-> actual: %d buf: %d max_buf: %d max_length: %d data: %p", */ - /* urb->actual_length, urb->buffer_length, max_buf, max_length, data); */ - - if (!data) { - dbg_ep0 (1, "data is NULL"); - return; - } - length = max_length; - - if (length > max_length) { - dbg_ep0 (1, "length: %d >= max_length: %d", length, - max_length); - return; - } - /*dbg_ep0(1, " actual: %d buf: %d max_buf: %d max_length: %d length: %d", */ - /* urb->actual_length, urb->buffer_length, max_buf, max_length, length); */ - - if ((available = - /*urb->buffer_length */ max_buf - urb->actual_length) <= 0) { - return; - } - /*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */ - /* urb->actual_length, urb->buffer_length, max_buf, length, available); */ - - if (length > available) { - length = available; - } - /*dbg_ep0(1, "actual: %d buf: %d max_buf: %d length: %d available: %d", */ - /* urb->actual_length, urb->buffer_length, max_buf, length, available); */ - - memcpy (urb->buffer + urb->actual_length, data, length); - urb->actual_length += length; - - dbg_ep0 (3, - "copy_config: <- actual: %d buf: %d max_buf: %d max_length: %d available: %d", - urb->actual_length, urb->buffer_length, max_buf, max_length, - available); -} - -/** - * ep0_get_descriptor - * @device: - * @urb: - * @max: - * @descriptor_type: - * @index: - * - * Called by ep0_rx_process for a get descriptor device command. Determine what - * descriptor is being requested, copy to send buffer. Return zero if ok to send, - * return non-zero to signal a request error. - */ -static int ep0_get_descriptor (struct usb_device_instance *device, - struct urb *urb, int max, int descriptor_type, - int index) -{ - int port = 0; /* XXX compound device */ - - /*dbg_ep0(3, "max: %x type: %x index: %x", max, descriptor_type, index); */ - - if (!urb || !urb->buffer || !urb->buffer_length - || (urb->buffer_length < 255)) { - dbg_ep0 (2, "invalid urb %p", urb); - return -1L; - } - - /* setup tx urb */ - urb->actual_length = 0; - - dbg_ep0 (2, "%s", USBD_DEVICE_DESCRIPTORS (descriptor_type)); - - switch (descriptor_type) { - case USB_DESCRIPTOR_TYPE_DEVICE: - { - struct usb_device_descriptor *device_descriptor; - if (! - (device_descriptor = - usbd_device_device_descriptor (device, port))) { - return -1; - } - /* copy descriptor for this device */ - copy_config (urb, device_descriptor, - sizeof (struct usb_device_descriptor), - max); - - /* correct the correct control endpoint 0 max packet size into the descriptor */ - device_descriptor = - (struct usb_device_descriptor *) urb->buffer; - - } - dbg_ep0(3, "copied device configuration, actual_length: 0x%x", urb->actual_length); - break; - - case USB_DESCRIPTOR_TYPE_CONFIGURATION: - { - struct usb_configuration_descriptor - *configuration_descriptor; - struct usb_device_descriptor *device_descriptor; - if (! - (device_descriptor = - usbd_device_device_descriptor (device, port))) { - return -1; - } - /*dbg_ep0(2, "%d %d", index, device_descriptor->bNumConfigurations); */ - if (index >= device_descriptor->bNumConfigurations) { - dbg_ep0 (0, "index too large: %d >= %d", index, - device_descriptor-> - bNumConfigurations); - return -1; - } - - if (! - (configuration_descriptor = - usbd_device_configuration_descriptor (device, - port, - index))) { - dbg_ep0 (0, - "usbd_device_configuration_descriptor failed: %d", - index); - return -1; - } - dbg_ep0(0, "attempt to copy %d bytes to urb\n",cpu_to_le16(configuration_descriptor->wTotalLength)); - copy_config (urb, configuration_descriptor, - - cpu_to_le16(configuration_descriptor->wTotalLength), - max); - } - - break; - - case USB_DESCRIPTOR_TYPE_STRING: - { - struct usb_string_descriptor *string_descriptor; - if (!(string_descriptor = usbd_get_string (index))) { - dbg_ep0(0, "Invalid string index %d\n", index); - return -1; - } - dbg_ep0(3, "string_descriptor: %p length %d", string_descriptor, string_descriptor->bLength); - copy_config (urb, string_descriptor, string_descriptor->bLength, max); - } - break; - case USB_DESCRIPTOR_TYPE_INTERFACE: - dbg_ep0(2, "USB_DESCRIPTOR_TYPE_INTERFACE - error not implemented\n"); - return -1; - case USB_DESCRIPTOR_TYPE_ENDPOINT: - dbg_ep0(2, "USB_DESCRIPTOR_TYPE_ENDPOINT - error not implemented\n"); - return -1; - case USB_DESCRIPTOR_TYPE_HID: - { - dbg_ep0(2, "USB_DESCRIPTOR_TYPE_HID - error not implemented\n"); - return -1; /* unsupported at this time */ -#if 0 - int bNumInterface = - le16_to_cpu (urb->device_request.wIndex); - int bAlternateSetting = 0; - int class = 0; - struct usb_class_descriptor *class_descriptor; - - if (!(class_descriptor = - usbd_device_class_descriptor_index (device, - port, 0, - bNumInterface, - bAlternateSetting, - class)) - || class_descriptor->descriptor.hid.bDescriptorType != USB_DT_HID) { - dbg_ep0 (3, "[%d] interface is not HID", - bNumInterface); - return -1; - } - /* copy descriptor for this class */ - copy_config (urb, class_descriptor, - class_descriptor->descriptor.hid.bLength, - max); -#endif - } - break; - case USB_DESCRIPTOR_TYPE_REPORT: - { - dbg_ep0(2, "USB_DESCRIPTOR_TYPE_REPORT - error not implemented\n"); - return -1; /* unsupported at this time */ -#if 0 - int bNumInterface = - le16_to_cpu (urb->device_request.wIndex); - int bAlternateSetting = 0; - int class = 0; - struct usb_class_report_descriptor *report_descriptor; - - if (!(report_descriptor = - usbd_device_class_report_descriptor_index - (device, port, 0, bNumInterface, - bAlternateSetting, class)) - || report_descriptor->bDescriptorType != - USB_DT_REPORT) { - dbg_ep0 (3, "[%d] descriptor is not REPORT", - bNumInterface); - return -1; - } - /* copy report descriptor for this class */ - /*copy_config(urb, &report_descriptor->bData[0], report_descriptor->wLength, max); */ - if (max - urb->actual_length > 0) { - int length = - min(report_descriptor->wLength, - max - urb->actual_length); - memcpy (urb->buffer + urb->actual_length, - &report_descriptor->bData[0], length); - urb->actual_length += length; - } -#endif - } - break; - case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER: - return -1; - - default: - return -1; - } - - dbg_ep0 (1, "urb: buffer: %p buffer_length: %2d actual_length: %2d tx_packetSize: %2d", - urb->buffer, urb->buffer_length, urb->actual_length, - device->bus->endpoint_array[0].tx_packetSize); -/* - if ((urb->actual_length < max) && !(urb->actual_length % device->bus->endpoint_array[0].tx_packetSize)) { - dbg_ep0(0, "adding null byte"); - urb->buffer[urb->actual_length++] = 0; - dbg_ep0(0, "urb: buffer_length: %2d actual_length: %2d packet size: %2d", - urb->buffer_length, urb->actual_length device->bus->endpoint_array[0].tx_packetSize); - } -*/ - return 0; - -} - -/** - * ep0_recv_setup - called to indicate URB has been received - * @urb: pointer to struct urb - * - * Check if this is a setup packet, process the device request, put results - * back into the urb and return zero or non-zero to indicate success (DATA) - * or failure (STALL). - * - */ -int ep0_recv_setup (struct urb *urb) -{ - /*struct usb_device_request *request = urb->buffer; */ - /*struct usb_device_instance *device = urb->device; */ - - struct usb_device_request *request; - struct usb_device_instance *device; - int address; - - dbg_ep0 (0, "entering ep0_recv_setup()"); - if (!urb || !urb->device) { - dbg_ep0 (3, "invalid URB %p", urb); - return -1; - } - - request = &urb->device_request; - device = urb->device; - - dbg_ep0 (3, "urb: %p device: %p", urb, urb->device); - - /*dbg_ep0(2, "- - - - - - - - - -"); */ - - dbg_ep0 (2, - "bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x %s", - request->bmRequestType, request->bRequest, - le16_to_cpu (request->wValue), le16_to_cpu (request->wIndex), - le16_to_cpu (request->wLength), - USBD_DEVICE_REQUESTS (request->bRequest)); - - /* handle USB Standard Request (c.f. USB Spec table 9-2) */ - if ((request->bmRequestType & USB_REQ_TYPE_MASK) != 0) { - if(device->device_state <= STATE_CONFIGURED){ - /* Attempt to handle a CDC specific request if we are - * in the configured state. - */ - return device->cdc_recv_setup(request,urb); - } - dbg_ep0 (1, "non standard request: %x", - request->bmRequestType & USB_REQ_TYPE_MASK); - return -1; /* Stall here */ - } - - switch (device->device_state) { - case STATE_CREATED: - case STATE_ATTACHED: - case STATE_POWERED: - /* It actually is important to allow requests in these states, - * Windows will request descriptors before assigning an - * address to the client. - */ - - /*dbg_ep0 (1, "request %s not allowed in this state: %s", */ - /* USBD_DEVICE_REQUESTS(request->bRequest), */ - /* usbd_device_states[device->device_state]); */ - /*return -1; */ - break; - - case STATE_INIT: - case STATE_DEFAULT: - switch (request->bRequest) { - case USB_REQ_GET_STATUS: - case USB_REQ_GET_INTERFACE: - case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */ - case USB_REQ_CLEAR_FEATURE: - case USB_REQ_SET_FEATURE: - case USB_REQ_SET_DESCRIPTOR: - /* case USB_REQ_SET_CONFIGURATION: */ - case USB_REQ_SET_INTERFACE: - dbg_ep0 (1, - "request %s not allowed in DEFAULT state: %s", - USBD_DEVICE_REQUESTS (request->bRequest), - usbd_device_states[device->device_state]); - return -1; - - case USB_REQ_SET_CONFIGURATION: - case USB_REQ_SET_ADDRESS: - case USB_REQ_GET_DESCRIPTOR: - case USB_REQ_GET_CONFIGURATION: - break; - } - case STATE_ADDRESSED: - case STATE_CONFIGURED: - break; - case STATE_UNKNOWN: - dbg_ep0 (1, "request %s not allowed in UNKNOWN state: %s", - USBD_DEVICE_REQUESTS (request->bRequest), - usbd_device_states[device->device_state]); - return -1; - } - - /* handle all requests that return data (direction bit set on bm RequestType) */ - if ((request->bmRequestType & USB_REQ_DIRECTION_MASK)) { - - dbg_ep0 (3, "Device-to-Host"); - - switch (request->bRequest) { - - case USB_REQ_GET_STATUS: - return ep0_get_status (device, urb, request->wIndex, - request->bmRequestType & - USB_REQ_RECIPIENT_MASK); - - case USB_REQ_GET_DESCRIPTOR: - return ep0_get_descriptor (device, urb, - le16_to_cpu (request->wLength), - le16_to_cpu (request->wValue) >> 8, - le16_to_cpu (request->wValue) & 0xff); - - case USB_REQ_GET_CONFIGURATION: - dbg_ep0(2, "get config %d\n", device->configuration); - return ep0_get_one (device, urb, - device->configuration); - - case USB_REQ_GET_INTERFACE: - return ep0_get_one (device, urb, device->alternate); - - case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */ - return -1; - - case USB_REQ_CLEAR_FEATURE: - case USB_REQ_SET_FEATURE: - case USB_REQ_SET_ADDRESS: - case USB_REQ_SET_DESCRIPTOR: - case USB_REQ_SET_CONFIGURATION: - case USB_REQ_SET_INTERFACE: - return -1; - } - } - /* handle the requests that do not return data */ - else { - - /*dbg_ep0(3, "Host-to-Device"); */ - switch (request->bRequest) { - - case USB_REQ_CLEAR_FEATURE: - case USB_REQ_SET_FEATURE: - dbg_ep0 (0, "Host-to-Device"); - switch (request-> - bmRequestType & USB_REQ_RECIPIENT_MASK) { - case USB_REQ_RECIPIENT_DEVICE: - /* XXX DEVICE_REMOTE_WAKEUP or TEST_MODE would be added here */ - /* XXX fall through for now as we do not support either */ - case USB_REQ_RECIPIENT_INTERFACE: - case USB_REQ_RECIPIENT_OTHER: - dbg_ep0 (0, "request %s not", - USBD_DEVICE_REQUESTS (request->bRequest)); - default: - return -1; - - case USB_REQ_RECIPIENT_ENDPOINT: - dbg_ep0 (0, "ENDPOINT: %x", le16_to_cpu (request->wValue)); - if (le16_to_cpu (request->wValue) == USB_ENDPOINT_HALT) { - /*return usbd_device_feature (device, le16_to_cpu (request->wIndex), */ - /* request->bRequest == USB_REQ_SET_FEATURE); */ - /* NEED TO IMPLEMENT THIS!!! */ - return -1; - } else { - dbg_ep0 (1, "request %s bad wValue: %04x", - USBD_DEVICE_REQUESTS - (request->bRequest), - le16_to_cpu (request->wValue)); - return -1; - } - } - - case USB_REQ_SET_ADDRESS: - /* check if this is a re-address, reset first if it is (this shouldn't be possible) */ - if (device->device_state != STATE_DEFAULT) { - dbg_ep0 (1, "set_address: %02x state: %s", - le16_to_cpu (request->wValue), - usbd_device_states[device->device_state]); - return -1; - } - address = le16_to_cpu (request->wValue); - if ((address & 0x7f) != address) { - dbg_ep0 (1, "invalid address %04x %04x", - address, address & 0x7f); - return -1; - } - device->address = address; - - /*dbg_ep0(2, "address: %d %d %d", */ - /* request->wValue, le16_to_cpu(request->wValue), device->address); */ - - return 0; - - case USB_REQ_SET_DESCRIPTOR: /* XXX should we support this? */ - dbg_ep0 (0, "set descriptor: NOT SUPPORTED"); - return -1; - - case USB_REQ_SET_CONFIGURATION: - /* c.f. 9.4.7 - the top half of wValue is reserved */ - device->configuration = le16_to_cpu(request->wValue) & 0xff; - - /* reset interface and alternate settings */ - device->interface = device->alternate = 0; - - /*dbg_ep0(2, "set configuration: %d", device->configuration); */ - /*dbg_ep0(2, "DEVICE_CONFIGURED.. event?\n"); */ - return 0; - - case USB_REQ_SET_INTERFACE: - device->interface = le16_to_cpu (request->wIndex); - device->alternate = le16_to_cpu (request->wValue); - /*dbg_ep0(2, "set interface: %d alternate: %d", device->interface, device->alternate); */ - dbg_ep0(2, "DEVICE_SET_INTERFACE.. event?\n"); - return 0; - - case USB_REQ_GET_STATUS: - case USB_REQ_GET_DESCRIPTOR: - case USB_REQ_GET_CONFIGURATION: - case USB_REQ_GET_INTERFACE: - case USB_REQ_SYNCH_FRAME: /* XXX should never see this (?) */ - return -1; - } - } - return -1; -} diff --git a/drivers/usb/musb-new/Kconfig b/drivers/usb/musb-new/Kconfig index c52afd41a75..ad9072a5327 100644 --- a/drivers/usb/musb-new/Kconfig +++ b/drivers/usb/musb-new/Kconfig @@ -22,6 +22,13 @@ config USB_MUSB_GADGET Enables the MUSB USB dual-role controller in gadget mode. if USB_MUSB_HOST || USB_MUSB_GADGET +config USB_MUSB_SC5XX + bool "Analog Devices MUSB support" + depends on (SC57X || SC58X) + help + Say y here to enable support for the USB controller on + ADI SC57X/SC58X processors. + config USB_MUSB_DA8XX bool "Enable DA8xx MUSB Controller" depends on ARCH_DAVINCI diff --git a/drivers/usb/musb-new/Makefile b/drivers/usb/musb-new/Makefile index 396ff02654b..6638772daca 100644 --- a/drivers/usb/musb-new/Makefile +++ b/drivers/usb/musb-new/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_USB_MUSB_PIC32) += pic32.o obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o obj-$(CONFIG_USB_MUSB_TI) += ti-musb.o obj-$(CONFIG_USB_MUSB_UX500) += ux500.o +obj-$(CONFIG_USB_MUSB_SC5XX) += sc5xx.o ccflags-y := $(call cc-option,-Wno-unused-variable) \ $(call cc-option,-Wno-unused-but-set-variable) \ diff --git a/drivers/usb/musb-new/sc5xx.c b/drivers/usb/musb-new/sc5xx.c new file mode 100644 index 00000000000..16201480b43 --- /dev/null +++ b/drivers/usb/musb-new/sc5xx.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * ADI SC5XX MUSB "glue layer" + * + * Written and/or maintained by Timesys Corporation + * + * Loosely ported from Linux driver: + * Author: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * + */ + +#include <dm.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <linux/usb/musb.h> +#include "linux-compat.h" +#include "musb_core.h" +#include "musb_uboot.h" + +#define MUSB_SOFTRST 0x7f +#define MUSB_SOFTRST_NRST BIT(0) +#define MUSB_SOFTRST_NRSTX BIT(1) + +#define REG_USB_VBUS_CTL 0x380 +#define REG_USB_ID_CTL 0x382 +#define REG_USB_PHY_CTL 0x394 +#define REG_USB_PLL_OSC 0x398 +#define REG_USB_UTMI_CTL 0x39c + +/* controller data */ +struct sc5xx_musb_data { + struct musb_host_data mdata; + struct device dev; +}; + +#define to_sc5xx_musb_data(d) \ + container_of(d, struct sc5xx_musb_data, dev) + +static void sc5xx_musb_disable(struct musb *musb) +{ + /* no way to shut the controller */ +} + +static int sc5xx_musb_enable(struct musb *musb) +{ + /* soft reset by NRSTx */ + musb_writeb(musb->mregs, MUSB_SOFTRST, MUSB_SOFTRST_NRSTX); + /* set mode */ + musb_platform_set_mode(musb, musb->board_mode); + + return 0; +} + +static irqreturn_t sc5xx_interrupt(int irq, void *hci) +{ + struct musb *musb = hci; + irqreturn_t ret = IRQ_NONE; + u8 devctl; + + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); + + if (musb->int_usb & MUSB_INTR_VBUSERROR) { + musb->int_usb &= ~MUSB_INTR_VBUSERROR; + devctl = musb_readw(musb->mregs, MUSB_DEVCTL); + devctl |= MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + } + if (musb->int_usb || musb->int_tx || musb->int_rx) { + musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); + musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); + musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); + ret = musb_interrupt(musb); + } + + if (musb->int_usb & MUSB_INTR_DISCONNECT && is_host_active(musb)) + musb_writeb(musb->mregs, REG_USB_VBUS_CTL, 0x0); + + return ret; +} + +static int sc5xx_musb_set_mode(struct musb *musb, u8 mode) +{ + struct device *dev = musb->controller; + struct sc5xx_musb_data *pdata = to_sc5xx_musb_data(dev); + + switch (mode) { + case MUSB_HOST: + musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x1); + break; + case MUSB_PERIPHERAL: + musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x3); + break; + case MUSB_OTG: + musb_writeb(musb->mregs, REG_USB_ID_CTL, 0x0); + break; + default: + dev_err(dev, "unsupported mode %d\n", mode); + return -EINVAL; + } + + return 0; +} + +static int sc5xx_musb_init(struct musb *musb) +{ + struct sc5xx_musb_data *pdata = to_sc5xx_musb_data(musb->controller); + + musb->isr = sc5xx_interrupt; + + musb_writel(musb->mregs, REG_USB_PLL_OSC, 20 << 1); + musb_writeb(musb->mregs, REG_USB_VBUS_CTL, 0x0); + musb_writeb(musb->mregs, REG_USB_PHY_CTL, 0x80); + musb_writel(musb->mregs, REG_USB_UTMI_CTL, + 0x40 | musb_readl(musb->mregs, REG_USB_UTMI_CTL)); + + return 0; +} + +const struct musb_platform_ops sc5xx_musb_ops = { + .init = sc5xx_musb_init, + .set_mode = sc5xx_musb_set_mode, + .disable = sc5xx_musb_disable, + .enable = sc5xx_musb_enable, +}; + +static struct musb_hdrc_config sc5xx_musb_config = { + .multipoint = 1, + .dyn_fifo = 1, + .num_eps = 16, + .ram_bits = 12, +}; + +/* has one MUSB controller which can be host or gadget */ +static struct musb_hdrc_platform_data sc5xx_musb_plat = { + .mode = MUSB_HOST, + .config = &sc5xx_musb_config, + .power = 100, + .platform_ops = &sc5xx_musb_ops, +}; + +static int musb_usb_probe(struct udevice *dev) +{ + struct usb_bus_priv *priv = dev_get_uclass_priv(dev); + struct sc5xx_musb_data *pdata = dev_get_priv(dev); + struct musb_host_data *mdata = &pdata->mdata; + void __iomem *mregs; + int ret; + + priv->desc_before_addr = true; + + mregs = dev_remap_addr(dev); + if (!mregs) + return -EINVAL; + + /* init controller */ + if (IS_ENABLED(CONFIG_USB_MUSB_HOST)) { + mdata->host = musb_init_controller(&sc5xx_musb_plat, + &pdata->dev, mregs); + if (!mdata->host) + return -EIO; + + ret = musb_lowlevel_init(mdata); + } else { + sc5xx_musb_plat.mode = MUSB_PERIPHERAL; + mdata->host = musb_register(&sc5xx_musb_plat, &pdata->dev, mregs); + if (!mdata->host) + return -EIO; + } + return ret; +} + +static int musb_usb_remove(struct udevice *dev) +{ + struct sc5xx_musb_data *pdata = dev_get_priv(dev); + + musb_stop(pdata->mdata.host); + + return 0; +} + +static const struct udevice_id sc5xx_musb_ids[] = { + { .compatible = "adi,sc5xx-musb" }, + { } +}; + +U_BOOT_DRIVER(usb_musb) = { + .name = "sc5xx-musb", + .id = UCLASS_USB, + .of_match = sc5xx_musb_ids, + .probe = musb_usb_probe, + .remove = musb_usb_remove, +#ifdef CONFIG_USB_MUSB_HOST + .ops = &musb_usb_ops, +#endif + .plat_auto = sizeof(struct usb_plat), + .priv_auto = sizeof(struct sc5xx_musb_data), +}; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 3c3cebaacd0..df607303616 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -293,27 +293,6 @@ config PANEL_HX8238D source "drivers/video/fonts/Kconfig" -config VIDCONSOLE_AS_LCD - bool "Use 'vidconsole' when CONFIG_VIDCONSOLE_AS_NAME string is seen in stdout" - help - This is a work-around for boards which have 'lcd' or 'vga' in their - stdout environment variable, but have moved to use driver model for - video. In this case the console will no-longer work. While it is - possible to update the environment, the breakage may be confusing for - users. This option will be removed around the end of 2020. - -config VIDCONSOLE_AS_NAME - string "Use 'vidconsole' when string defined here is seen in stdout" - depends on VIDCONSOLE_AS_LCD - default "lcd" if LCD || TEGRA_COMMON - default "vga" if !LCD - help - This is a work-around for boards which have 'lcd' or 'vga' in their - stdout environment variable, but have moved to use driver model for - video. In this case the console will no-longer work. While it is - possible to update the environment, the breakage may be confusing for - users. This option will be removed around the end of 2020. - config VIDEO_BOCHS bool "Enable Bochs video emulation for QEMU" help @@ -599,6 +578,15 @@ config VIDEO_LCD_SAMSUNG_LTL106HL02 LCD module found in Microsoft Surface 2. The panel has a FullHD resolution (1920x1080). +config VIDEO_LCD_SHARP_LQ079L1SX01 + tristate "Sharp LQ079L1SX01 1536x2048 DSI video mode panel" + depends on PANEL && BACKLIGHT + select VIDEO_MIPI_DSI + help + Say Y here if you want to enable support for Sharp LQ079L1SX01 + LCD module found in Xiaomi Mi Pad tablet. The panel has a QXGA + resolution (1536x2048). + config VIDEO_LCD_SHARP_LQ101R1SX01 tristate "Sharp LQ101R1SX01 2560x1600 DSI video mode panel" depends on PANEL && BACKLIGHT @@ -712,6 +700,12 @@ config I2C_EDID help This enables library for accessing EDID data from an LCD panel. +config I2C_EDID_STANDARD + bool "Enable standard timings EDID library expansion" + depends on I2C_EDID + help + This enables standard timings expansion for EDID data from an LCD panel. + config DISPLAY bool "Enable Display support" depends on DM @@ -743,6 +737,16 @@ config BACKLIGHT_LM3533 LM3533 Lighting Power chip. Only Bank A is supported as for now. Supported backlight level range is from 2 to 255 with step of 1. +config BACKLIGHT_LP855x + bool "Backlight Driver for LP855x" + depends on BACKLIGHT + select DM_I2C + help + Say Y to enable the backlight driver for National Semiconductor / TI + LP8550/1/2/3/5/6/7 LED Backlight Driver. Only register driven mode is + supported for now, PWM mode can be added if there will be any need in + it. Supported backlight level range is from 0 to 255 with step of 1. + source "drivers/video/ti/Kconfig" source "drivers/video/exynos/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 5a00438ce06..6073bc5234a 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_$(PHASE_)BMP) += bmp.o endif obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_backlight.o +obj-$(CONFIG_BACKLIGHT_LP855x) += lp855x_backlight.o obj-${CONFIG_EXYNOS_FB} += exynos/ obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/ obj-${CONFIG_VIDEO_STM32} += stm32/ @@ -64,6 +65,7 @@ obj-$(CONFIG_VIDEO_LCD_RAYDIUM_RM68200) += raydium-rm68200.o obj-$(CONFIG_VIDEO_LCD_RENESAS_R61307) += renesas-r61307.o obj-$(CONFIG_VIDEO_LCD_RENESAS_R69328) += renesas-r69328.o obj-$(CONFIG_VIDEO_LCD_SAMSUNG_LTL106HL02) += samsung-ltl106hl02.o +obj-$(CONFIG_VIDEO_LCD_SHARP_LQ079L1SX01) += sharp-lq079l1sx01.o obj-$(CONFIG_VIDEO_LCD_SHARP_LQ101R1SX01) += sharp-lq101r1sx01.o obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o obj-$(CONFIG_VIDEO_LCD_TDO_TL070WSH30) += tdo-tl070wsh30.o diff --git a/drivers/video/bridge/Kconfig b/drivers/video/bridge/Kconfig index ab917273720..be53034bd3d 100644 --- a/drivers/video/bridge/Kconfig +++ b/drivers/video/bridge/Kconfig @@ -9,7 +9,7 @@ config VIDEO_BRIDGE config VIDEO_BRIDGE_PARADE_DP501 bool "Support Parade DP501 DP & DVI/HDMI dual mode transmitter" - depends on PANEL && DM_GPIO + depends on VIDEO_BRIDGE && PANEL && DM_GPIO select DM_I2C help The Parade DP501 is a DP & DVI/HDMI dual-mode transmitter. It @@ -46,16 +46,23 @@ config VIDEO_BRIDGE_ANALOGIX_ANX6345 config VIDEO_BRIDGE_SOLOMON_SSD2825 bool "Solomon SSD2825 bridge driver" - depends on PANEL && DM_GPIO + depends on VIDEO_BRIDGE && PANEL && DM_GPIO select VIDEO_MIPI_DSI help - Solomon SSD2824 SPI RGB-DSI bridge driver wrapped into panel uClass. + Solomon SSD2824 SPI RGB-DSI bridge driver. config VIDEO_BRIDGE_TOSHIBA_TC358768 bool "Support Toshiba TC358768 MIPI DSI bridge" - depends on PANEL && DM_GPIO + depends on VIDEO_BRIDGE && PANEL && DM_GPIO select VIDEO_MIPI_DSI select DM_I2C help Toshiba TC358768AXBG/TC358778XBG DSI bridge chip driver. Found in Asus Transformer Infinity TF700T. + +config VIDEO_BRIDGE_LVDS_CODEC + bool "Transparent LVDS encoders and decoders support" + depends on VIDEO_BRIDGE && PANEL && DM_GPIO + help + Support for transparent LVDS encoders and decoders that don't + require any configuration. diff --git a/drivers/video/bridge/Makefile b/drivers/video/bridge/Makefile index 58697e3cbe9..63dc6e62c49 100644 --- a/drivers/video/bridge/Makefile +++ b/drivers/video/bridge/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_VIDEO_BRIDGE_NXP_PTN3460) += ptn3460.o obj-$(CONFIG_VIDEO_BRIDGE_ANALOGIX_ANX6345) += anx6345.o obj-$(CONFIG_VIDEO_BRIDGE_SOLOMON_SSD2825) += ssd2825.o obj-$(CONFIG_VIDEO_BRIDGE_TOSHIBA_TC358768) += tc358768.o +obj-$(CONFIG_VIDEO_BRIDGE_LVDS_CODEC) += lvds-codec.o diff --git a/drivers/video/bridge/dp501.c b/drivers/video/bridge/dp501.c index 095e3e71fed..9937cfe095b 100644 --- a/drivers/video/bridge/dp501.c +++ b/drivers/video/bridge/dp501.c @@ -9,6 +9,7 @@ #include <log.h> #include <backlight.h> #include <panel.h> +#include <video_bridge.h> #include <linux/delay.h> #include <linux/err.h> #include <power/regulator.h> @@ -206,7 +207,6 @@ struct dp501_priv { struct udevice *chip2; struct udevice *vdd; - struct gpio_desc reset_gpio; struct gpio_desc enable_gpio; }; @@ -484,16 +484,19 @@ static int dp501_panel_timings(struct udevice *dev, return 0; } -static void dp501_hw_init(struct dp501_priv *priv) +static void dp501_hw_init(struct udevice *dev) { - dm_gpio_set_value(&priv->reset_gpio, 1); + struct dp501_priv *priv = dev_get_priv(dev); + struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); + + dm_gpio_set_value(&uc_priv->reset, 1); regulator_set_enable_if_allowed(priv->vdd, 1); dm_gpio_set_value(&priv->enable_gpio, 1); udelay(100); - dm_gpio_set_value(&priv->reset_gpio, 0); + dm_gpio_set_value(&uc_priv->reset, 0); mdelay(80); } @@ -521,14 +524,6 @@ static int dp501_setup(struct udevice *dev) } /* get gpios */ - ret = gpio_request_by_name(dev, "reset-gpios", 0, - &priv->reset_gpio, GPIOD_IS_OUT); - if (ret) { - log_debug("%s: Could not decode reset-gpios (%d)\n", - __func__, ret); - return ret; - } - ret = gpio_request_by_name(dev, "enable-gpios", 0, &priv->enable_gpio, GPIOD_IS_OUT); if (ret) { @@ -544,7 +539,7 @@ static int dp501_setup(struct udevice *dev) return ret; } - dp501_hw_init(priv); + dp501_hw_init(dev); /* get EDID */ return panel_get_display_timing(priv->panel, &priv->timing); @@ -558,8 +553,8 @@ static int dp501_probe(struct udevice *dev) return dp501_setup(dev); } -struct panel_ops dp501_ops = { - .enable_backlight = dp501_attach, +static const struct video_bridge_ops dp501_ops = { + .attach = dp501_attach, .set_backlight = dp501_set_backlight, .get_display_timing = dp501_panel_timings, }; @@ -571,7 +566,7 @@ static const struct udevice_id dp501_ids[] = { U_BOOT_DRIVER(dp501) = { .name = "dp501", - .id = UCLASS_PANEL, + .id = UCLASS_VIDEO_BRIDGE, .of_match = dp501_ids, .ops = &dp501_ops, .probe = dp501_probe, diff --git a/drivers/video/bridge/lvds-codec.c b/drivers/video/bridge/lvds-codec.c new file mode 100644 index 00000000000..6cd8368a39e --- /dev/null +++ b/drivers/video/bridge/lvds-codec.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + * Loosely based on Linux lvds-codec.c driver + */ + +#include <dm.h> +#include <dm/ofnode_graph.h> +#include <log.h> +#include <panel.h> +#include <video_bridge.h> +#include <asm/gpio.h> +#include <power/regulator.h> + +struct lvds_codec_priv { + struct udevice *panel; + struct display_timing timing; + + struct gpio_desc powerdown_gpio; + struct udevice *power; +}; + +static int lvds_codec_attach(struct udevice *dev) +{ + struct lvds_codec_priv *priv = dev_get_priv(dev); + + regulator_set_enable_if_allowed(priv->power, 1); + dm_gpio_set_value(&priv->powerdown_gpio, 0); + + return panel_enable_backlight(priv->panel); +} + +static int lvds_codec_set_panel(struct udevice *dev, int percent) +{ + struct lvds_codec_priv *priv = dev_get_priv(dev); + + return panel_set_backlight(priv->panel, percent); +} + +static int lvds_codec_panel_timings(struct udevice *dev, + struct display_timing *timing) +{ + struct lvds_codec_priv *priv = dev_get_priv(dev); + + memcpy(timing, &priv->timing, sizeof(*timing)); + + return 0; +} + +/* Function is purely for sandbox testing */ +static int lvds_codec_read_edid(struct udevice *dev, u8 *buf, int buf_size) +{ + return 0; +} + +static int lvds_codec_get_panel(struct udevice *dev) +{ + struct lvds_codec_priv *priv = dev_get_priv(dev); + int i, ret; + + u32 num = ofnode_graph_get_port_count(dev_ofnode(dev)); + + for (i = 0; i < num; i++) { + ofnode remote = ofnode_graph_get_remote_node(dev_ofnode(dev), i, -1); + + ret = uclass_get_device_by_of_offset(UCLASS_PANEL, + ofnode_to_offset(remote), + &priv->panel); + if (!ret) + return 0; + } + + /* If this point is reached, no panels were found */ + return -ENODEV; +} + +static int lvds_codec_probe(struct udevice *dev) +{ + struct lvds_codec_priv *priv = dev_get_priv(dev); + int ret; + + ret = lvds_codec_get_panel(dev); + if (ret) { + log_debug("%s: cannot get panel: ret=%d\n", __func__, ret); + return log_ret(ret); + } + + panel_get_display_timing(priv->panel, &priv->timing); + + ret = gpio_request_by_name(dev, "powerdown-gpios", 0, + &priv->powerdown_gpio, GPIOD_IS_OUT); + if (ret) { + log_debug("%s: could not get powerdown-gpios (%d)\n", __func__, ret); + if (ret != -ENOENT) + return log_ret(ret); + } + + ret = device_get_supply_regulator(dev, "power-supply", &priv->power); + if (ret) { + log_debug("%s: power regulator error: %d\n", __func__, ret); + if (ret != -ENOENT) + return log_ret(ret); + } + + return 0; +} + +static const struct video_bridge_ops lvds_codec_ops = { + .attach = lvds_codec_attach, + .set_backlight = lvds_codec_set_panel, + .get_display_timing = lvds_codec_panel_timings, + .read_edid = lvds_codec_read_edid, +}; + +static const struct udevice_id lvds_codec_ids[] = { + { .compatible = "lvds-decoder" }, + { .compatible = "lvds-encoder" }, + { } +}; + +U_BOOT_DRIVER(lvds_codec) = { + .name = "lvds_codec", + .id = UCLASS_VIDEO_BRIDGE, + .of_match = lvds_codec_ids, + .ops = &lvds_codec_ops, + .probe = lvds_codec_probe, + .priv_auto = sizeof(struct lvds_codec_priv), +}; diff --git a/drivers/video/bridge/ssd2825.c b/drivers/video/bridge/ssd2825.c index f978021c860..a307993377c 100644 --- a/drivers/video/bridge/ssd2825.c +++ b/drivers/video/bridge/ssd2825.c @@ -5,12 +5,15 @@ #include <clk.h> #include <dm.h> +#include <dm/ofnode_graph.h> #include <log.h> #include <misc.h> #include <mipi_display.h> #include <mipi_dsi.h> #include <backlight.h> +#include <video_bridge.h> #include <panel.h> +#include <power/regulator.h> #include <spi.h> #include <linux/delay.h> #include <linux/err.h> @@ -106,6 +109,10 @@ #define SSD2825_LP_MIN_CLK 5000 /* KHz */ #define SSD2825_REF_MIN_CLK 2000 /* KHz */ +static const char * const ssd2825_supplies[] = { + "dvdd-supply", "avdd-supply", "vddio-supply" +}; + struct ssd2825_bridge_priv { struct mipi_dsi_host host; struct mipi_dsi_device device; @@ -113,12 +120,16 @@ struct ssd2825_bridge_priv { struct udevice *panel; struct display_timing timing; + struct udevice *supplies[ARRAY_SIZE(ssd2825_supplies)]; + struct gpio_desc power_gpio; - struct gpio_desc reset_gpio; struct clk *tx_clk; u32 pll_freq_kbps; /* PLL in kbps */ + + u32 hzd; /* HS Zero Delay in ns */ + u32 hpd; /* HS Prepare Delay is ns */ }; static int ssd2825_spi_write(struct udevice *dev, int reg, @@ -231,7 +242,6 @@ static ssize_t ssd2825_bridge_transfer(struct mipi_dsi_host *host, const struct mipi_dsi_msg *msg) { struct udevice *dev = (struct udevice *)host->dev; - u8 buf = *(u8 *)msg->tx_buf; u16 config; int ret; @@ -260,15 +270,6 @@ static ssize_t ssd2825_bridge_transfer(struct mipi_dsi_host *host, ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); ssd2825_write_dsi(dev, msg->tx_buf, msg->tx_len); - if (buf == MIPI_DCS_SET_DISPLAY_ON) { - ssd2825_write_register(dev, SSD2825_CONFIGURATION_REG, - SSD2825_CONF_REG_HS | SSD2825_CONF_REG_VEN | - SSD2825_CONF_REG_DCS | SSD2825_CONF_REG_ECD | - SSD2825_CONF_REG_EOT); - ssd2825_write_register(dev, SSD2825_PLL_CTRL_REG, 0x0001); - ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); - } - return 0; } @@ -312,9 +313,14 @@ static void ssd2825_setup_pll(struct udevice *dev) struct mipi_dsi_device *device = &priv->device; struct display_timing *dt = &priv->timing; u16 pll_config, lp_div; + u32 nibble_delay, nibble_freq_khz; u32 pclk_mult, tx_freq_khz, pd_lines; + u8 hzd, hpd; tx_freq_khz = clk_get_rate(priv->tx_clk) / 1000; + if (!tx_freq_khz || tx_freq_khz < 0) + tx_freq_khz = SSD2825_REF_MIN_CLK; + pd_lines = mipi_dsi_pixel_format_to_bpp(device->format); pclk_mult = pd_lines / device->lanes + 1; @@ -324,12 +330,19 @@ static void ssd2825_setup_pll(struct udevice *dev) lp_div = priv->pll_freq_kbps / (SSD2825_LP_MIN_CLK * 8); + /* nibble_delay in nanoseconds */ + nibble_freq_khz = priv->pll_freq_kbps / 4; + nibble_delay = 1000 * 1000 / nibble_freq_khz; + + hzd = priv->hzd / nibble_delay; + hpd = (priv->hpd - 4 * nibble_delay) / nibble_delay; + /* Disable PLL */ ssd2825_write_register(dev, SSD2825_PLL_CTRL_REG, 0x0000); ssd2825_write_register(dev, SSD2825_LINE_CTRL_REG, 0x0001); /* Set delays */ - ssd2825_write_register(dev, SSD2825_DELAY_ADJ_REG_1, 0x2103); + ssd2825_write_register(dev, SSD2825_DELAY_ADJ_REG_1, (hzd << 8) | hpd); /* Set PLL coeficients */ ssd2825_write_register(dev, SSD2825_PLL_CONFIGURATION_REG, pll_config); @@ -343,11 +356,30 @@ static void ssd2825_setup_pll(struct udevice *dev) ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); } -static int ssd2825_bridge_enable_panel(struct udevice *dev) +static int ssd2825_bridge_attach(struct udevice *dev) { struct ssd2825_bridge_priv *priv = dev_get_priv(dev); struct mipi_dsi_device *device = &priv->device; struct display_timing *dt = &priv->timing; + u8 pixel_format; + int ret; + + /* Set pixel format */ + switch (device->format) { + case MIPI_DSI_FMT_RGB565: + pixel_format = 0x00; + break; + case MIPI_DSI_FMT_RGB666_PACKED: + pixel_format = 0x01; + break; + case MIPI_DSI_FMT_RGB666: + pixel_format = 0x02; + break; + case MIPI_DSI_FMT_RGB888: + default: + pixel_format = 0x03; + break; + } /* Perform SW reset */ ssd2825_write_register(dev, SSD2825_OPERATION_CTRL_REG, 0x0100); @@ -367,7 +399,7 @@ static int ssd2825_bridge_enable_panel(struct udevice *dev) ssd2825_write_register(dev, SSD2825_RGB_INTERFACE_CTRL_REG_6, SSD2825_HSYNC_HIGH | SSD2825_VSYNC_HIGH | SSD2825_PCKL_HIGH | SSD2825_NON_BURST | - (3 - device->format)); + pixel_format); ssd2825_write_register(dev, SSD2825_LANE_CONFIGURATION_REG, device->lanes - 1); ssd2825_write_register(dev, SSD2825_TEST_REG, 0x0004); @@ -384,7 +416,18 @@ static int ssd2825_bridge_enable_panel(struct udevice *dev) ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); /* Perform panel setup */ - return panel_enable_backlight(priv->panel); + ret = panel_enable_backlight(priv->panel); + if (ret) + return ret; + + ssd2825_write_register(dev, SSD2825_CONFIGURATION_REG, + SSD2825_CONF_REG_HS | SSD2825_CONF_REG_VEN | + SSD2825_CONF_REG_DCS | SSD2825_CONF_REG_ECD | + SSD2825_CONF_REG_EOT); + ssd2825_write_register(dev, SSD2825_PLL_CTRL_REG, 0x0001); + ssd2825_write_register(dev, SSD2825_VC_CTRL_REG, 0x0000); + + return 0; } static int ssd2825_bridge_set_panel(struct udevice *dev, int percent) @@ -407,7 +450,8 @@ static int ssd2825_bridge_panel_timings(struct udevice *dev, static int ssd2825_bridge_hw_init(struct udevice *dev) { struct ssd2825_bridge_priv *priv = dev_get_priv(dev); - int ret; + struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); + int i, ret; ret = clk_prepare_enable(priv->tx_clk); if (ret) { @@ -416,25 +460,28 @@ static int ssd2825_bridge_hw_init(struct udevice *dev) return ret; } - ret = dm_gpio_set_value(&priv->power_gpio, 1); - if (ret) { - log_debug("%s: error changing power-gpios (%d)\n", - __func__, ret); - return ret; + /* enable supplies */ + for (i = 0; i < ARRAY_SIZE(ssd2825_supplies); i++) { + ret = regulator_set_enable_if_allowed(priv->supplies[i], 1); + if (ret) { + log_debug("%s: cannot enable %s %d\n", __func__, + ssd2825_supplies[i], ret); + return ret; + } } mdelay(10); - ret = dm_gpio_set_value(&priv->reset_gpio, 0); + ret = dm_gpio_set_value(&uc_priv->reset, 1); if (ret) { - log_debug("%s: error changing reset-gpios (%d)\n", + log_debug("%s: error entering reset (%d)\n", __func__, ret); return ret; } mdelay(10); - ret = dm_gpio_set_value(&priv->reset_gpio, 1); + ret = dm_gpio_set_value(&uc_priv->reset, 0); if (ret) { - log_debug("%s: error changing reset-gpios (%d)\n", + log_debug("%s: error exiting reset (%d)\n", __func__, ret); return ret; } @@ -443,13 +490,33 @@ static int ssd2825_bridge_hw_init(struct udevice *dev) return 0; } +static int ssd2825_bridge_get_panel(struct udevice *dev) +{ + struct ssd2825_bridge_priv *priv = dev_get_priv(dev); + int i, ret; + + u32 num = ofnode_graph_get_port_count(dev_ofnode(dev)); + + for (i = 0; i < num; i++) { + ofnode remote = ofnode_graph_get_remote_node(dev_ofnode(dev), i, -1); + + ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, + &priv->panel); + if (!ret) + return 0; + } + + /* If this point is reached, no panels were found */ + return -ENODEV; +} + static int ssd2825_bridge_probe(struct udevice *dev) { struct ssd2825_bridge_priv *priv = dev_get_priv(dev); struct spi_slave *slave = dev_get_parent_priv(dev); struct mipi_dsi_device *device = &priv->device; struct mipi_dsi_panel_plat *mipi_plat; - int ret; + int i, ret; ret = spi_claim_bus(slave); if (ret) { @@ -457,10 +524,9 @@ static int ssd2825_bridge_probe(struct udevice *dev) return ret; } - ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, - "panel", &priv->panel); + ret = ssd2825_bridge_get_panel(dev); if (ret) { - log_err("cannot get panel: ret=%d\n", ret); + log_debug("%s: panel not found, ret %d\n", __func__, ret); return ret; } @@ -477,33 +543,33 @@ static int ssd2825_bridge_probe(struct udevice *dev) device->format = mipi_plat->format; device->mode_flags = mipi_plat->mode_flags; - /* get panel gpios */ - ret = gpio_request_by_name(dev, "power-gpios", 0, - &priv->power_gpio, GPIOD_IS_OUT); - if (ret) { - log_err("could not decode power-gpios (%d)\n", ret); - return ret; - } - - ret = gpio_request_by_name(dev, "reset-gpios", 0, - &priv->reset_gpio, GPIOD_IS_OUT); - if (ret) { - log_err("could not decode reset-gpios (%d)\n", ret); - return ret; + /* get supplies */ + for (i = 0; i < ARRAY_SIZE(ssd2825_supplies); i++) { + ret = device_get_supply_regulator(dev, ssd2825_supplies[i], + &priv->supplies[i]); + if (ret) { + log_debug("%s: cannot get %s %d\n", __func__, + ssd2825_supplies[i], ret); + if (ret != -ENOENT) + return log_ret(ret); + } } /* get clk */ - priv->tx_clk = devm_clk_get(dev, "tx_clk"); + priv->tx_clk = devm_clk_get_optional(dev, NULL); if (IS_ERR(priv->tx_clk)) { log_err("cannot get tx_clk: %ld\n", PTR_ERR(priv->tx_clk)); return PTR_ERR(priv->tx_clk); } + priv->hzd = dev_read_u32_default(dev, "solomon,hs-zero-delay-ns", 133); + priv->hpd = dev_read_u32_default(dev, "solomon,hs-prep-delay-ns", 40); + return ssd2825_bridge_hw_init(dev); } -static const struct panel_ops ssd2825_bridge_ops = { - .enable_backlight = ssd2825_bridge_enable_panel, +static const struct video_bridge_ops ssd2825_bridge_ops = { + .attach = ssd2825_bridge_attach, .set_backlight = ssd2825_bridge_set_panel, .get_display_timing = ssd2825_bridge_panel_timings, }; @@ -515,9 +581,10 @@ static const struct udevice_id ssd2825_bridge_ids[] = { U_BOOT_DRIVER(ssd2825) = { .name = "ssd2825", - .id = UCLASS_PANEL, + .id = UCLASS_VIDEO_BRIDGE, .of_match = ssd2825_bridge_ids, .ops = &ssd2825_bridge_ops, + .bind = dm_scan_fdt_dev, .probe = ssd2825_bridge_probe, .priv_auto = sizeof(struct ssd2825_bridge_priv), }; diff --git a/drivers/video/bridge/tc358768.c b/drivers/video/bridge/tc358768.c index 19b6ca29d3e..358004f30f2 100644 --- a/drivers/video/bridge/tc358768.c +++ b/drivers/video/bridge/tc358768.c @@ -6,12 +6,14 @@ #include <clk.h> #include <dm.h> +#include <dm/ofnode_graph.h> #include <i2c.h> #include <log.h> #include <mipi_display.h> #include <mipi_dsi.h> #include <backlight.h> #include <panel.h> +#include <video_bridge.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/kernel.h> @@ -122,6 +124,10 @@ #define NANO 1000000000UL #define PICO 1000000000000ULL +static const char * const tc358768_supplies[] = { + "vddc-supply", "vddmipi-supply", "vddio-supply" +}; + struct tc358768_priv { struct mipi_dsi_host host; struct mipi_dsi_device device; @@ -129,9 +135,7 @@ struct tc358768_priv { struct udevice *panel; struct display_timing timing; - struct udevice *vddc; - struct udevice *vddmipi; - struct udevice *vddio; + struct udevice *supplies[ARRAY_SIZE(tc358768_supplies)]; struct clk *refclk; @@ -265,25 +269,27 @@ static void tc358768_sw_reset(struct udevice *dev) tc358768_write(dev, TC358768_SYSCTL, 0); } -static void tc358768_hw_enable(struct tc358768_priv *priv) +static void tc358768_hw_enable(struct udevice *dev) { + struct tc358768_priv *priv = dev_get_priv(dev); + struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); int ret; ret = clk_prepare_enable(priv->refclk); if (ret) log_debug("%s: error enabling refclk (%d)\n", __func__, ret); - ret = regulator_set_enable_if_allowed(priv->vddc, true); + ret = regulator_set_enable_if_allowed(priv->supplies[0], true); if (ret) log_debug("%s: error enabling vddc (%d)\n", __func__, ret); - ret = regulator_set_enable_if_allowed(priv->vddmipi, true); + ret = regulator_set_enable_if_allowed(priv->supplies[1], true); if (ret) log_debug("%s: error enabling vddmipi (%d)\n", __func__, ret); mdelay(10); - ret = regulator_set_enable_if_allowed(priv->vddio, true); + ret = regulator_set_enable_if_allowed(priv->supplies[2], true); if (ret) log_debug("%s: error enabling vddio (%d)\n", __func__, ret); @@ -293,7 +299,7 @@ static void tc358768_hw_enable(struct tc358768_priv *priv) * The RESX is active low (GPIO_ACTIVE_LOW). * DEASSERT (value = 0) the reset_gpio to enable the chip */ - ret = dm_gpio_set_value(&priv->reset_gpio, 0); + ret = dm_gpio_set_value(&uc_priv->reset, 0); if (ret) log_debug("%s: error changing reset-gpio (%d)\n", __func__, ret); @@ -477,7 +483,7 @@ static int tc358768_attach(struct udevice *dev) device->mode_flags &= ~MIPI_DSI_CLOCK_NON_CONTINUOUS; } - tc358768_hw_enable(priv); + tc358768_hw_enable(dev); tc358768_sw_reset(dev); tc358768_setup_pll(dev); @@ -874,12 +880,33 @@ static int tc358768_panel_timings(struct udevice *dev, return 0; } +static int tc358768_get_panel(struct udevice *dev) +{ + struct tc358768_priv *priv = dev_get_priv(dev); + int i, ret; + + u32 num = ofnode_graph_get_port_count(dev_ofnode(dev)); + + for (i = 0; i < num; i++) { + ofnode remote = ofnode_graph_get_remote_node(dev_ofnode(dev), i, -1); + + ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, + &priv->panel); + if (!ret) + return 0; + } + + /* If this point is reached, no panels were found */ + return -ENODEV; +} + static int tc358768_setup(struct udevice *dev) { struct tc358768_priv *priv = dev_get_priv(dev); + struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); struct mipi_dsi_device *device = &priv->device; struct mipi_dsi_panel_plat *mipi_plat; - int ret; + int i, ret; /* The bridge uses 16 bit registers */ ret = i2c_set_chip_offset_len(dev, 2); @@ -889,11 +916,10 @@ static int tc358768_setup(struct udevice *dev) return ret; } - ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, - "panel", &priv->panel); + ret = tc358768_get_panel(dev); if (ret) { - log_debug("%s: Cannot get panel: ret=%d\n", __func__, ret); - return log_ret(ret); + log_debug("%s: panel not found, ret %d\n", __func__, ret); + return ret; } panel_get_display_timing(priv->panel, &priv->timing); @@ -913,44 +939,26 @@ static int tc358768_setup(struct udevice *dev) priv->dsi_lanes = device->lanes; /* get regulators */ - ret = device_get_supply_regulator(dev, "vddc-supply", &priv->vddc); - if (ret) { - log_debug("%s: vddc regulator error: %d\n", __func__, ret); - if (ret != -ENOENT) - return log_ret(ret); - } - - ret = device_get_supply_regulator(dev, "vddmipi-supply", &priv->vddmipi); - if (ret) { - log_debug("%s: vddmipi regulator error: %d\n", __func__, ret); - if (ret != -ENOENT) - return log_ret(ret); - } - - ret = device_get_supply_regulator(dev, "vddio-supply", &priv->vddio); - if (ret) { - log_debug("%s: vddio regulator error: %d\n", __func__, ret); - if (ret != -ENOENT) - return log_ret(ret); + for (i = 0; i < ARRAY_SIZE(tc358768_supplies); i++) { + ret = device_get_supply_regulator(dev, tc358768_supplies[i], + &priv->supplies[i]); + if (ret) { + log_debug("%s: cannot get %s %d\n", __func__, + tc358768_supplies[i], ret); + if (ret != -ENOENT) + return log_ret(ret); + } } /* get clk */ - priv->refclk = devm_clk_get(dev, "refclk"); + priv->refclk = devm_clk_get(dev, NULL); if (IS_ERR(priv->refclk)) { log_debug("%s: Could not get refclk: %ld\n", __func__, PTR_ERR(priv->refclk)); return PTR_ERR(priv->refclk); } - /* get gpios */ - ret = gpio_request_by_name(dev, "reset-gpios", 0, - &priv->reset_gpio, GPIOD_IS_OUT); - if (ret) { - log_debug("%s: Could not decode reset-gpios (%d)\n", __func__, ret); - return ret; - } - - dm_gpio_set_value(&priv->reset_gpio, 1); + dm_gpio_set_value(&uc_priv->reset, 1); return 0; } @@ -963,8 +971,8 @@ static int tc358768_probe(struct udevice *dev) return tc358768_setup(dev); } -struct panel_ops tc358768_ops = { - .enable_backlight = tc358768_attach, +static const struct video_bridge_ops tc358768_ops = { + .attach = tc358768_attach, .set_backlight = tc358768_set_backlight, .get_display_timing = tc358768_panel_timings, }; @@ -977,9 +985,10 @@ static const struct udevice_id tc358768_ids[] = { U_BOOT_DRIVER(tc358768) = { .name = "tc358768", - .id = UCLASS_PANEL, + .id = UCLASS_VIDEO_BRIDGE, .of_match = tc358768_ids, .ops = &tc358768_ops, + .bind = dm_scan_fdt_dev, .probe = tc358768_probe, .priv_auto = sizeof(struct tc358768_priv), }; diff --git a/drivers/video/bridge/video-bridge-uclass.c b/drivers/video/bridge/video-bridge-uclass.c index 2084a2e03ee..1b8aa12b9e8 100644 --- a/drivers/video/bridge/video-bridge-uclass.c +++ b/drivers/video/bridge/video-bridge-uclass.c @@ -33,6 +33,17 @@ int video_bridge_attach(struct udevice *dev) return ops->attach(dev); } +int video_bridge_get_display_timing(struct udevice *dev, + struct display_timing *timings) +{ + struct video_bridge_ops *ops = video_bridge_get_ops(dev); + + if (!ops->get_display_timing) + return -ENOSYS; + + return ops->get_display_timing(dev, timings); +} + int video_bridge_check_attached(struct udevice *dev) { struct video_bridge_priv *uc_priv = dev_get_uclass_priv(dev); diff --git a/drivers/video/endeavoru-panel.c b/drivers/video/endeavoru-panel.c index d4ba4d8b6da..9950ff8bb05 100644 --- a/drivers/video/endeavoru-panel.c +++ b/drivers/video/endeavoru-panel.c @@ -117,6 +117,18 @@ static int endeavoru_panel_set_backlight(struct udevice *dev, int percent) struct endeavoru_panel_priv *priv = dev_get_priv(dev); int ret; + /* + * Due to the use of the Tegra DC backlight feature, backlight + * requests MUST NOT be made during probe or earlier. This is + * because it creates a loop, as the backlight is a DC child. + */ + ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, + "backlight", &priv->backlight); + if (ret) { + log_err("cannot get backlight: ret = %d\n", ret); + return ret; + } + ret = backlight_enable(priv->backlight); if (ret) return ret; @@ -136,13 +148,6 @@ static int endeavoru_panel_of_to_plat(struct udevice *dev) struct endeavoru_panel_priv *priv = dev_get_priv(dev); int ret; - ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, - "backlight", &priv->backlight); - if (ret) { - log_err("cannot get backlight: ret = %d\n", ret); - return ret; - } - ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, "vdd-supply", &priv->vdd); if (ret) { @@ -231,7 +236,7 @@ static int endeavoru_panel_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 2; plat->format = MIPI_DSI_FMT_RGB888; - plat->mode_flags = MIPI_DSI_MODE_VIDEO; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; return endeavoru_panel_hw_init(dev); } diff --git a/drivers/video/lg-ld070wx3.c b/drivers/video/lg-ld070wx3.c index 610a06ffe7b..3676e45bc65 100644 --- a/drivers/video/lg-ld070wx3.c +++ b/drivers/video/lg-ld070wx3.c @@ -158,7 +158,7 @@ static int lg_ld070wx3_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 4; plat->format = MIPI_DSI_FMT_RGB888; - plat->mode_flags = MIPI_DSI_MODE_VIDEO; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; return lg_ld070wx3_hw_init(dev); } diff --git a/drivers/video/lm3533_backlight.c b/drivers/video/lm3533_backlight.c index 6b51fa0628e..7b87b6bd40b 100644 --- a/drivers/video/lm3533_backlight.c +++ b/drivers/video/lm3533_backlight.c @@ -7,6 +7,7 @@ #include <backlight.h> #include <dm.h> +#include <dm/ofnode.h> #include <i2c.h> #include <log.h> #include <linux/delay.h> @@ -17,56 +18,79 @@ #define LM3533_BL_MAX_BRIGHTNESS 0xFF #define LM3533_SINK_OUTPUT_CONFIG_1 0x10 -#define LM3533_CONTROL_BANK_A_PWM 0x14 +#define LM3533_CONTROL_PWM_BASE 0x14 +#define PWM_MAX GENMASK(5, 0) #define LM3533_CONTROL_BANK_AB_BRIGHTNESS 0x1A -#define LM3533_CONTROL_BANK_A_FULLSCALE_CURRENT 0x1F +#define LM3533_CONTROL_FULLSCALE_CURRENT_BASE 0x1F +#define MAX_CURRENT_MIN 5000 +#define MAX_CURRENT_MAX 29800 +#define MAX_CURRENT_STEP 800 #define LM3533_CONTROL_BANK_ENABLE 0x27 #define LM3533_OVP_FREQUENCY_PWM_POLARITY 0x2C +#define BOOST_OVP_MASK GENMASK(2, 1) +#define BOOST_OVP_SHIFT 1 +#define BOOST_FREQ_MASK BIT(0) +#define BOOST_FREQ_SHIFT 0 #define LM3533_BRIGHTNESS_REGISTER_A 0x40 +#define LM3533_BOOST_OVP_16V 16000000UL +#define LM3533_BOOST_FREQ_500KHZ 500000UL + struct lm3533_backlight_priv { struct gpio_desc enable_gpio; u32 def_bl_lvl; + + /* Core */ + u32 boost_ovp; + u32 boost_freq; + + /* Backlight */ + u32 reg; + u16 max_current; /* 5000 - 29800 uA (800 uA step) */ + u8 pwm; /* 0 - 0x3f */ + bool linear; + bool hvled; }; static int lm3533_backlight_enable(struct udevice *dev) { struct lm3533_backlight_priv *priv = dev_get_priv(dev); + u8 val, id = priv->reg; int ret; - dm_gpio_set_value(&priv->enable_gpio, 1); - mdelay(5); + if (priv->linear) { + ret = dm_i2c_reg_clrset(dev, LM3533_CONTROL_BANK_AB_BRIGHTNESS, + BIT(2 * id + 1), BIT(2 * id + 1)); + if (ret) + return ret; + } - /* HVLED 1 & 2 are controlled by Bank A */ - ret = dm_i2c_reg_write(dev, LM3533_SINK_OUTPUT_CONFIG_1, 0x00); - if (ret) - return ret; + if (priv->hvled) { + ret = dm_i2c_reg_clrset(dev, LM3533_SINK_OUTPUT_CONFIG_1, + BIT(0) | BIT(1), id | id << 1); + if (ret) + return ret; + } - /* PWM input is disabled for CABC */ - ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_A_PWM, 0x00); - if (ret) - return ret; + /* Set current */ + if (priv->max_current < MAX_CURRENT_MIN || priv->max_current > MAX_CURRENT_MAX) + return -EINVAL; - /* Linear & Control Bank A is configured for register Current control */ - ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_AB_BRIGHTNESS, 0x02); + val = (priv->max_current - MAX_CURRENT_MIN) / MAX_CURRENT_STEP; + ret = dm_i2c_reg_write(dev, LM3533_CONTROL_FULLSCALE_CURRENT_BASE + id, val); if (ret) return ret; - /* Full-Scale Current (20.2mA) */ - ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_A_FULLSCALE_CURRENT, 0x13); - if (ret) - return ret; + /* Set PWM mask */ + if (priv->pwm > PWM_MAX) + return -EINVAL; - /* Control Bank A is enable */ - ret = dm_i2c_reg_write(dev, LM3533_CONTROL_BANK_ENABLE, 0x01); + ret = dm_i2c_reg_write(dev, LM3533_CONTROL_PWM_BASE + id, priv->pwm); if (ret) return ret; - ret = dm_i2c_reg_write(dev, LM3533_OVP_FREQUENCY_PWM_POLARITY, 0x0A); - if (ret) - return ret; - - return 0; + /* Enable Control Bank */ + return dm_i2c_reg_clrset(dev, LM3533_CONTROL_BANK_ENABLE, BIT(id), BIT(id)); } static int lm3533_backlight_set_brightness(struct udevice *dev, int percent) @@ -92,6 +116,56 @@ static int lm3533_backlight_set_brightness(struct udevice *dev, int percent) return 0; } +static int lm3533_backlight_of_to_plat(struct udevice *dev) +{ + struct lm3533_backlight_priv *priv = dev_get_priv(dev); + ofnode child; + int ret; + + ret = gpio_request_by_name(dev, "enable-gpios", 0, + &priv->enable_gpio, GPIOD_IS_OUT); + if (ret) { + log_err("Could not decode enable-gpios (%d)\n", ret); + return ret; + } + + priv->boost_ovp = dev_read_u32_default(dev, "ti,boost-ovp-microvolt", + LM3533_BOOST_OVP_16V); + + /* boost_ovp is defined in microvolts, convert to enum value */ + priv->boost_ovp = priv->boost_ovp / (8 * 1000 * 1000) - 2; + + priv->boost_freq = dev_read_u32_default(dev, "ti,boost-freq-hz", + LM3533_BOOST_FREQ_500KHZ); + + /* boost_freq is defined in Hz, convert to enum value */ + priv->boost_freq = priv->boost_freq / (500 * 1000) - 1; + + /* Backlight is one of children but has no dedicated driver */ + ofnode_for_each_subnode(child, dev_ofnode(dev)) { + if (ofnode_device_is_compatible(child, "ti,lm3533-backlight")) { + const char *node_name = ofnode_get_name(child); + + if (!strcmp(&node_name[10], "1")) + priv->reg = 1; + else + priv->reg = 0; + + priv->max_current = ofnode_read_u32_default(child, "ti,max-current-microamp", + 5000); + priv->pwm = ofnode_read_u32_default(child, "ti,pwm-config-mask", 0); + + priv->def_bl_lvl = ofnode_read_u32_default(child, "default-brightness", + LM3533_BL_MAX_BRIGHTNESS); + + priv->linear = ofnode_read_bool(child, "ti,linear-mapping-mode"); + priv->hvled = ofnode_read_bool(child, "ti,hardware-controlled"); + } + } + + return 0; +} + static int lm3533_backlight_probe(struct udevice *dev) { struct lm3533_backlight_priv *priv = dev_get_priv(dev); @@ -100,15 +174,22 @@ static int lm3533_backlight_probe(struct udevice *dev) if (device_get_uclass_id(dev->parent) != UCLASS_I2C) return -EPROTONOSUPPORT; - ret = gpio_request_by_name(dev, "enable-gpios", 0, - &priv->enable_gpio, GPIOD_IS_OUT); + dm_gpio_set_value(&priv->enable_gpio, 1); + mdelay(5); + + ret = dm_i2c_reg_clrset(dev, LM3533_OVP_FREQUENCY_PWM_POLARITY, + BOOST_FREQ_MASK, priv->boost_freq << BOOST_FREQ_SHIFT); if (ret) { - log_err("Could not decode enable-gpios (%d)\n", ret); + log_debug("%s: freq config failed %d\n", __func__, ret); return ret; } - priv->def_bl_lvl = dev_read_u32_default(dev, "default-brightness-level", - LM3533_BL_MAX_BRIGHTNESS); + ret = dm_i2c_reg_clrset(dev, LM3533_OVP_FREQUENCY_PWM_POLARITY, + BOOST_OVP_MASK, priv->boost_ovp << BOOST_OVP_SHIFT); + if (ret) { + log_debug("%s: ovp config failed %d\n", __func__, ret); + return ret; + } return 0; } @@ -127,6 +208,7 @@ U_BOOT_DRIVER(lm3533_backlight) = { .name = "lm3533_backlight", .id = UCLASS_PANEL_BACKLIGHT, .of_match = lm3533_backlight_ids, + .of_to_plat = lm3533_backlight_of_to_plat, .probe = lm3533_backlight_probe, .ops = &lm3533_backlight_ops, .priv_auto = sizeof(struct lm3533_backlight_priv), diff --git a/drivers/video/lp855x_backlight.c b/drivers/video/lp855x_backlight.c new file mode 100644 index 00000000000..5debc0aa453 --- /dev/null +++ b/drivers/video/lp855x_backlight.c @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2011 Texas Instruments + * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#define LOG_CATEGORY UCLASS_PANEL_BACKLIGHT + +#include <malloc.h> +#include <backlight.h> +#include <dm.h> +#include <dm/devres.h> +#include <i2c.h> +#include <log.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <asm/gpio.h> +#include <power/regulator.h> + +#define LP855x_MIN_BRIGHTNESS 0x00 +#define LP855x_MAX_BRIGHTNESS 0xff + +/* LP8550/1/2/3/6 Registers */ +#define LP855X_BRIGHTNESS_CTRL 0x00 +#define LP855X_DEVICE_CTRL 0x01 +#define LP855X_EEPROM_START 0xa0 +#define LP855X_EEPROM_END 0xa7 +#define LP8556_EPROM_START 0x98 +#define LP8556_EPROM_END 0xaf + +/* LP8555/7 Registers */ +#define LP8557_BL_CMD 0x00 +#define LP8557_BL_MASK 0x01 +#define LP8557_BL_ON 0x01 +#define LP8557_BL_OFF 0x00 +#define LP8557_BRIGHTNESS_CTRL 0x04 +#define LP8557_CONFIG 0x10 +#define LP8555_EPROM_START 0x10 +#define LP8555_EPROM_END 0x7a +#define LP8557_EPROM_START 0x10 +#define LP8557_EPROM_END 0x1e + +struct lp855x_rom_data { + u8 addr; + u8 val; +}; + +struct lp855x_backlight_priv; + +/* + * struct lp855x_device_config + * @pre_init_device: init device function call before updating the brightness + * @reg_brightness: register address for brigthenss control + * @reg_devicectrl: register address for device control + * @post_init_device: late init device function call + */ +struct lp855x_device_config { + int (*pre_init_device)(struct udevice *dev); + u8 reg_brightness; + u8 reg_devicectrl; + u8 reg_eepromstart; + u8 reg_eepromend; + int (*post_init_device)(struct udevice *dev); +}; + +struct lp855x_backlight_priv { + struct udevice *supply; /* regulator for VDD input */ + struct udevice *enable; /* regulator for EN/VDDIO input */ + + u8 device_control; + u8 initial_brightness; + + int size_program; + struct lp855x_rom_data *rom_data; + struct lp855x_device_config *cfg; +}; + +static int lp855x_backlight_enable(struct udevice *dev) +{ + struct lp855x_backlight_priv *priv = dev_get_priv(dev); + int ret; + + ret = regulator_set_enable_if_allowed(priv->supply, 1); + if (ret) { + log_debug("%s: enabling power-supply failed (%d)\n", + __func__, ret); + return ret; + } + + ret = regulator_set_enable_if_allowed(priv->enable, 1); + if (ret) { + log_debug("%s: enabling enable-supply failed (%d)\n", + __func__, ret); + return ret; + } + mdelay(2); + + if (priv->cfg->pre_init_device) { + ret = priv->cfg->pre_init_device(dev); + if (ret) { + log_debug("%s: pre init device err: %d\n", + __func__, ret); + return ret; + } + } + + ret = dm_i2c_reg_write(dev, priv->cfg->reg_brightness, + priv->initial_brightness); + if (ret) + return ret; + + ret = dm_i2c_reg_write(dev, priv->cfg->reg_devicectrl, + priv->device_control); + if (ret) + return ret; + + if (priv->size_program > 0) { + int i; + u8 val, addr; + + for (i = 0; i < priv->size_program; i++) { + addr = priv->rom_data[i].addr; + val = priv->rom_data[i].val; + + if (addr < priv->cfg->reg_eepromstart && + addr > priv->cfg->reg_eepromend) + continue; + + ret = dm_i2c_reg_write(dev, addr, val); + if (ret) + return ret; + } + } + + if (priv->cfg->post_init_device) { + ret = priv->cfg->post_init_device(dev); + if (ret) { + log_debug("%s: post init device err: %d\n", + __func__, ret); + return ret; + } + } + + return 0; +} + +static int lp855x_backlight_set_brightness(struct udevice *dev, int percent) +{ + struct lp855x_backlight_priv *priv = dev_get_priv(dev); + + if (percent == BACKLIGHT_DEFAULT) + percent = priv->initial_brightness; + + if (percent < LP855x_MIN_BRIGHTNESS) + percent = LP855x_MIN_BRIGHTNESS; + + if (percent > LP855x_MAX_BRIGHTNESS) + percent = LP855x_MAX_BRIGHTNESS; + + /* Set brightness level */ + return dm_i2c_reg_write(dev, priv->cfg->reg_brightness, + percent); +} + +static int lp855x_backlight_probe(struct udevice *dev) +{ + struct lp855x_backlight_priv *priv = dev_get_priv(dev); + int rom_length, ret; + + if (device_get_uclass_id(dev->parent) != UCLASS_I2C) + return -EPROTONOSUPPORT; + + priv->cfg = (struct lp855x_device_config *)dev_get_driver_data(dev); + + dev_read_u8(dev, "dev-ctrl", &priv->device_control); + dev_read_u8(dev, "init-brt", &priv->initial_brightness); + + /* Fill ROM platform data if defined */ + rom_length = dev_get_child_count(dev); + if (rom_length > 0) { + struct lp855x_rom_data *rom; + ofnode child; + int i = 0; + + rom = devm_kcalloc(dev, rom_length, sizeof(*rom), GFP_KERNEL); + if (!rom) + return -ENOMEM; + + dev_for_each_subnode(child, dev) { + ofnode_read_u8(child, "rom-addr", &rom[i].addr); + ofnode_read_u8(child, "rom-val", &rom[i].val); + i++; + } + + priv->size_program = rom_length; + priv->rom_data = &rom[0]; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "power-supply", &priv->supply); + if (ret) { + log_err("%s: cannot get power-supply: ret = %d\n", __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "enable-supply", &priv->enable); + if (ret) { + log_err("%s: cannot get enable-supply: ret = %d\n", __func__, ret); + return ret; + } + + return 0; +} + +static const struct backlight_ops lp855x_backlight_ops = { + .enable = lp855x_backlight_enable, + .set_brightness = lp855x_backlight_set_brightness, +}; + +static int lp8556_bl_rst(struct udevice *dev) +{ + int ret; + + /* Reset backlight after updating EPROM settings */ + ret = dm_i2c_reg_clrset(dev, LP855X_DEVICE_CTRL, LP8557_BL_MASK, + LP8557_BL_OFF); + if (ret) + return ret; + + mdelay(10); + + return dm_i2c_reg_clrset(dev, LP855X_DEVICE_CTRL, LP8557_BL_MASK, + LP8557_BL_ON); +} + +static int lp8557_bl_off(struct udevice *dev) +{ + /* BL_ON = 0 before updating EPROM settings */ + return dm_i2c_reg_clrset(dev, LP8557_BL_CMD, LP8557_BL_MASK, + LP8557_BL_OFF); +} + +static int lp8557_bl_on(struct udevice *dev) +{ + /* BL_ON = 1 after updating EPROM settings */ + return dm_i2c_reg_clrset(dev, LP8557_BL_CMD, LP8557_BL_MASK, + LP8557_BL_ON); +} + +static struct lp855x_device_config lp855x_dev_cfg = { + .reg_brightness = LP855X_BRIGHTNESS_CTRL, + .reg_devicectrl = LP855X_DEVICE_CTRL, + .reg_eepromstart = LP855X_EEPROM_START, + .reg_eepromend = LP855X_EEPROM_END, +}; + +static struct lp855x_device_config lp8555_dev_cfg = { + .reg_brightness = LP8557_BRIGHTNESS_CTRL, + .reg_devicectrl = LP8557_CONFIG, + .reg_eepromstart = LP8555_EPROM_START, + .reg_eepromend = LP8555_EPROM_END, + .pre_init_device = lp8557_bl_off, + .post_init_device = lp8557_bl_on, +}; + +static struct lp855x_device_config lp8556_dev_cfg = { + .reg_brightness = LP855X_BRIGHTNESS_CTRL, + .reg_devicectrl = LP855X_DEVICE_CTRL, + .reg_eepromstart = LP8556_EPROM_START, + .reg_eepromend = LP8556_EPROM_END, + .post_init_device = lp8556_bl_rst, +}; + +static struct lp855x_device_config lp8557_dev_cfg = { + .reg_brightness = LP8557_BRIGHTNESS_CTRL, + .reg_devicectrl = LP8557_CONFIG, + .reg_eepromstart = LP8557_EPROM_START, + .reg_eepromend = LP8557_EPROM_END, + .pre_init_device = lp8557_bl_off, + .post_init_device = lp8557_bl_on, +}; + +static const struct udevice_id lp855x_backlight_ids[] = { + { .compatible = "ti,lp8550", .data = (ulong)&lp855x_dev_cfg }, + { .compatible = "ti,lp8551", .data = (ulong)&lp855x_dev_cfg }, + { .compatible = "ti,lp8552", .data = (ulong)&lp855x_dev_cfg }, + { .compatible = "ti,lp8553", .data = (ulong)&lp855x_dev_cfg }, + { .compatible = "ti,lp8555", .data = (ulong)&lp8555_dev_cfg }, + { .compatible = "ti,lp8556", .data = (ulong)&lp8556_dev_cfg }, + { .compatible = "ti,lp8557", .data = (ulong)&lp8557_dev_cfg }, + { } +}; + +U_BOOT_DRIVER(lp855x_backlight) = { + .name = "lp855x_backlight", + .id = UCLASS_PANEL_BACKLIGHT, + .of_match = lp855x_backlight_ids, + .probe = lp855x_backlight_probe, + .ops = &lp855x_backlight_ops, + .priv_auto = sizeof(struct lp855x_backlight_priv), +}; diff --git a/drivers/video/renesas-r61307.c b/drivers/video/renesas-r61307.c index a3697bce5ee..ef6fab1e953 100644 --- a/drivers/video/renesas-r61307.c +++ b/drivers/video/renesas-r61307.c @@ -254,17 +254,17 @@ static int renesas_r61307_hw_init(struct udevice *dev) return ret; } - ret = dm_gpio_set_value(&priv->reset_gpio, 0); + ret = dm_gpio_set_value(&priv->reset_gpio, 1); if (ret) { - log_debug("%s: changing reset-gpio failed (%d)\n", + log_debug("%s: entering reset failed (%d)\n", __func__, ret); return ret; } mdelay(5); - ret = dm_gpio_set_value(&priv->reset_gpio, 1); + ret = dm_gpio_set_value(&priv->reset_gpio, 0); if (ret) { - log_debug("%s: changing reset-gpio failed (%d)\n", + log_debug("%s: exiting reset failed (%d)\n", __func__, ret); return ret; } @@ -281,7 +281,8 @@ static int renesas_r61307_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 4; plat->format = MIPI_DSI_FMT_RGB888; - plat->mode_flags = MIPI_DSI_MODE_VIDEO; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; return renesas_r61307_hw_init(dev); } @@ -294,7 +295,7 @@ static const struct panel_ops renesas_r61307_ops = { static const struct udevice_id renesas_r61307_ids[] = { { .compatible = "koe,tx13d100vm0eaa" }, - { .compatible = "hitachi,tx13d100vm0eaa" }, + { .compatible = "hit,tx13d100vm0eaa" }, { } }; diff --git a/drivers/video/renesas-r69328.c b/drivers/video/renesas-r69328.c index 9861c3fef11..0954b04b62e 100644 --- a/drivers/video/renesas-r69328.c +++ b/drivers/video/renesas-r69328.c @@ -32,9 +32,11 @@ #define R69328_POWER_SET 0xD1 struct renesas_r69328_priv { + struct udevice *vdd; + struct udevice *vddio; + struct udevice *backlight; - struct gpio_desc enable_gpio; struct gpio_desc reset_gpio; }; @@ -159,10 +161,15 @@ static int renesas_r69328_of_to_plat(struct udevice *dev) return ret; } - ret = gpio_request_by_name(dev, "enable-gpios", 0, - &priv->enable_gpio, GPIOD_IS_OUT); + ret = device_get_supply_regulator(dev, "vdd-supply", &priv->vdd); if (ret) { - log_err("could not decode enable-gpios (%d)\n", ret); + log_err("Cannot get vdd-supply: ret = %d\n", ret); + return ret; + } + + ret = device_get_supply_regulator(dev, "vddio-supply", &priv->vddio); + if (ret) { + log_err("Cannot get vddio-supply: ret = %d\n", ret); return ret; } @@ -181,25 +188,32 @@ static int renesas_r69328_hw_init(struct udevice *dev) struct renesas_r69328_priv *priv = dev_get_priv(dev); int ret; - ret = dm_gpio_set_value(&priv->enable_gpio, 1); + ret = regulator_set_enable_if_allowed(priv->vddio, 1); if (ret) { - log_debug("%s: error changing enable-gpios (%d)\n", + log_debug("%s: enabling vddio-supply failed (%d)\n", __func__, ret); return ret; } mdelay(5); - ret = dm_gpio_set_value(&priv->reset_gpio, 0); + ret = regulator_set_enable_if_allowed(priv->vdd, 1); if (ret) { - log_debug("%s: error changing reset-gpios (%d)\n", + log_debug("%s: enabling vdd-supply failed (%d)\n", __func__, ret); return ret; } - mdelay(5); ret = dm_gpio_set_value(&priv->reset_gpio, 1); if (ret) { - log_debug("%s: error changing reset-gpios (%d)\n", + log_debug("%s: error entering reset (%d)\n", + __func__, ret); + return ret; + } + mdelay(5); + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_debug("%s: error exiting reset (%d)\n", __func__, ret); return ret; } @@ -216,7 +230,8 @@ static int renesas_r69328_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 4; plat->format = MIPI_DSI_FMT_RGB888; - plat->mode_flags = MIPI_DSI_MODE_VIDEO; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM; return renesas_r69328_hw_init(dev); } diff --git a/drivers/video/samsung-ltl106hl02.c b/drivers/video/samsung-ltl106hl02.c index 5e6c11c4be3..1efc9fca610 100644 --- a/drivers/video/samsung-ltl106hl02.c +++ b/drivers/video/samsung-ltl106hl02.c @@ -129,7 +129,7 @@ static int samsung_ltl106hl02_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 4; plat->format = MIPI_DSI_FMT_RGB888; - plat->mode_flags = MIPI_DSI_MODE_VIDEO; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; return samsung_ltl106hl02_hw_init(dev); } diff --git a/drivers/video/sharp-lq079l1sx01.c b/drivers/video/sharp-lq079l1sx01.c new file mode 100644 index 00000000000..a8197f40fc7 --- /dev/null +++ b/drivers/video/sharp-lq079l1sx01.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sharp LQ079L1SX01 DSI panel driver + * + * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <backlight.h> +#include <dm.h> +#include <panel.h> +#include <log.h> +#include <mipi_dsi.h> +#include <linux/delay.h> +#include <asm/gpio.h> +#include <power/regulator.h> + +struct sharp_lq079l1sx01_priv { + struct udevice *backlight; + struct udevice *panel_sec; + + struct udevice *avdd; + struct udevice *vddio; + struct udevice *vsp; + struct udevice *vsn; + + struct gpio_desc reset_gpio; +}; + +static struct display_timing default_timing = { + .pixelclock.typ = 215000000, + .hactive.typ = 1536, + .hfront_porch.typ = 136, + .hback_porch.typ = 28, + .hsync_len.typ = 28, + .vactive.typ = 2048, + .vfront_porch.typ = 14, + .vback_porch.typ = 8, + .vsync_len.typ = 2, +}; + +static int dcs_write_one(struct mipi_dsi_device *dsi, u8 cmd, u8 data) +{ + return mipi_dsi_dcs_write(dsi, cmd, &data, 1); +} + +static int sharp_lq079l1sx01_configure_link(struct udevice *dev) +{ + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + struct mipi_dsi_device *dsi = plat->device; + int ret; + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + log_debug("%s: failed to exit sleep mode %s: %d\n", + __func__, dev->parent->name, ret); + } + mdelay(120); + + ret = dcs_write_one(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0xff); + if (ret < 0) { + log_debug("%s: failed to SET_DISPLAY_BRIGHTNESS %s: %d\n", + __func__, dev->parent->name, ret); + } + ret = dcs_write_one(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x01); + if (ret < 0) { + log_debug("%s: failed to WRITE_POWER_SAVE %s: %d\n", + __func__, dev->parent->name, ret); + } + ret = dcs_write_one(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x2c); + if (ret < 0) { + log_debug("%s: failed to WRITE_CONTROL_DISPLAY %s: %d\n", + __func__, dev->parent->name, ret); + } + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) { + log_debug("%s: failed to set panel on %s: %d\n", + __func__, dev->parent->name, ret); + } + + return 0; +} + +static int sharp_lq079l1sx01_enable_backlight(struct udevice *dev) +{ + struct sharp_lq079l1sx01_priv *priv = dev_get_priv(dev); + + if (!priv->panel_sec) + return 0; + + sharp_lq079l1sx01_configure_link(dev); + sharp_lq079l1sx01_configure_link(priv->panel_sec); + + return 0; +} + +static int sharp_lq079l1sx01_set_backlight(struct udevice *dev, int percent) +{ + struct sharp_lq079l1sx01_priv *priv = dev_get_priv(dev); + int ret; + + if (!priv->panel_sec) + return 0; + + ret = backlight_enable(priv->backlight); + if (ret) + return ret; + + return backlight_set_brightness(priv->backlight, percent); +} + +static int sharp_lq079l1sx01_timings(struct udevice *dev, + struct display_timing *timing) +{ + memcpy(timing, &default_timing, sizeof(*timing)); + return 0; +} + +static int sharp_lq079l1sx01_of_to_plat(struct udevice *dev) +{ + struct sharp_lq079l1sx01_priv *priv = dev_get_priv(dev); + int ret; + + /* If node has no link2 it is secondary panel */ + if (!dev_read_bool(dev, "link2")) + return 0; + + ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, + "link2", &priv->panel_sec); + if (ret) { + log_debug("%s: cannot get secondary panel: ret = %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, + "backlight", &priv->backlight); + if (ret) { + log_debug("%s: cannot get backlight: ret = %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "avdd-supply", &priv->avdd); + if (ret) { + log_debug("%s: cannot get avdd-supply: ret = %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "vddio-supply", &priv->vddio); + if (ret) { + log_debug("%s: cannot get vddio-supply: ret = %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "vsp-supply", &priv->vsp); + if (ret) { + log_debug("%s: cannot get vsp-supply: ret = %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, + "vsn-supply", &priv->vsn); + if (ret) { + log_debug("%s: cannot get vsn-supply: ret = %d\n", + __func__, ret); + return ret; + } + + ret = gpio_request_by_name(dev, "reset-gpios", 0, + &priv->reset_gpio, GPIOD_IS_OUT); + if (ret) { + log_debug("%s: cannot get reser-gpios (%d)\n", + __func__, ret); + return ret; + } + + return 0; +} + +static int sharp_lq079l1sx01_hw_init(struct udevice *dev) +{ + struct sharp_lq079l1sx01_priv *priv = dev_get_priv(dev); + int ret; + + if (!priv->panel_sec) + return 0; + + ret = regulator_set_enable_if_allowed(priv->vddio, 1); + if (ret) { + log_debug("%s: enabling vddio-supply failed (%d)\n", + __func__, ret); + return ret; + } + + ret = regulator_set_enable_if_allowed(priv->avdd, 1); + if (ret) { + log_debug("%s: enabling avdd-supply failed (%d)\n", + __func__, ret); + return ret; + } + + mdelay(12); + + ret = regulator_set_enable_if_allowed(priv->vsp, 1); + if (ret) { + log_debug("%s: enabling vsp-supply failed (%d)\n", + __func__, ret); + return ret; + } + + mdelay(12); + + ret = regulator_set_enable_if_allowed(priv->vsn, 1); + if (ret) { + log_debug("%s: enabling vsn-supply failed (%d)\n", + __func__, ret); + return ret; + } + + mdelay(24); + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_debug("%s: error disabling reset-gpios (%d)\n", + __func__, ret); + return ret; + } + udelay(3); + + ret = dm_gpio_set_value(&priv->reset_gpio, 1); + if (ret) { + log_debug("%s: error enabling reset-gpios (%d)\n", + __func__, ret); + return ret; + } + udelay(3); + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_debug("%s: error disabling reset-gpios (%d)\n", + __func__, ret); + return ret; + } + mdelay(32); + + return 0; +} + +static int sharp_lq079l1sx01_probe(struct udevice *dev) +{ + struct mipi_dsi_panel_plat *plat = dev_get_plat(dev); + + /* fill characteristics of DSI data link */ + plat->lanes = 4; + plat->format = MIPI_DSI_FMT_RGB888; + plat->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; + + return sharp_lq079l1sx01_hw_init(dev); +} + +static const struct panel_ops sharp_lq079l1sx01_ops = { + .enable_backlight = sharp_lq079l1sx01_enable_backlight, + .set_backlight = sharp_lq079l1sx01_set_backlight, + .get_display_timing = sharp_lq079l1sx01_timings, +}; + +static const struct udevice_id sharp_lq079l1sx01_ids[] = { + { .compatible = "sharp,lq079l1sx01" }, + { } +}; + +U_BOOT_DRIVER(sharp_lq079l1sx01) = { + .name = "sharp_lq079l1sx01", + .id = UCLASS_PANEL, + .of_match = sharp_lq079l1sx01_ids, + .ops = &sharp_lq079l1sx01_ops, + .of_to_plat = sharp_lq079l1sx01_of_to_plat, + .probe = sharp_lq079l1sx01_probe, + .plat_auto = sizeof(struct mipi_dsi_panel_plat), + .priv_auto = sizeof(struct sharp_lq079l1sx01_priv), +}; diff --git a/drivers/video/sharp-lq101r1sx01.c b/drivers/video/sharp-lq101r1sx01.c index 5d8453fd796..4fdf0da8a94 100644 --- a/drivers/video/sharp-lq101r1sx01.c +++ b/drivers/video/sharp-lq101r1sx01.c @@ -255,6 +255,7 @@ static int sharp_lq101r1sx01_probe(struct udevice *dev) /* fill characteristics of DSI data link */ plat->lanes = 4; plat->format = MIPI_DSI_FMT_RGB888; + plat->mode_flags = MIPI_DSI_MODE_LPM; return sharp_lq101r1sx01_hw_init(dev); } diff --git a/drivers/video/tegra20/Kconfig b/drivers/video/tegra20/Kconfig index f5c4843e119..598f9ea1f21 100644 --- a/drivers/video/tegra20/Kconfig +++ b/drivers/video/tegra20/Kconfig @@ -1,6 +1,11 @@ +config HOST1X_TEGRA + bool "NVIDIA Tegra host1x BUS support" + depends on SIMPLE_BUS + config VIDEO_TEGRA20 bool "Enable Display Controller support on Tegra20 and Tegra 30" depends on OF_CONTROL + select HOST1X_TEGRA help T20/T30 support video output to an attached LCD panel as well as other options such as HDMI. Only the LCD is supported in U-Boot. @@ -9,13 +14,22 @@ config VIDEO_TEGRA20 config VIDEO_DSI_TEGRA30 bool "Enable Tegra 30 DSI support" - depends on PANEL && DM_GPIO + depends on VIDEO_BRIDGE && PANEL && DM_GPIO select VIDEO_TEGRA20 select VIDEO_MIPI_DSI help T30 has native support for DSI panels. This option enables support for such panels which can be used on endeavoru and tf600t. +config VIDEO_HDMI_TEGRA + bool "Enable Tegra HDMI support" + depends on VIDEO_BRIDGE && DM_I2C + select I2C_EDID + select VIDEO_TEGRA20 + help + Tegra has native support for HDMI. This option enables support + for such connection and can be used for any supported device. + config TEGRA_BACKLIGHT_PWM bool "Enable Tegra DC PWM backlight support" depends on BACKLIGHT diff --git a/drivers/video/tegra20/Makefile b/drivers/video/tegra20/Makefile index a75aea2a875..78521405749 100644 --- a/drivers/video/tegra20/Makefile +++ b/drivers/video/tegra20/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ +obj-$(CONFIG_HOST1X_TEGRA) += tegra-host1x.o obj-$(CONFIG_VIDEO_TEGRA20) += tegra-dc.o obj-$(CONFIG_VIDEO_DSI_TEGRA30) += tegra-dsi.o tegra-mipi.o mipi-phy.o +obj-$(CONFIG_VIDEO_HDMI_TEGRA) += tegra-hdmi.o obj-$(CONFIG_TEGRA_BACKLIGHT_PWM) += tegra-pwm-backlight.o diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c index d24aa375b39..1f43153ff27 100644 --- a/drivers/video/tegra20/tegra-dc.c +++ b/drivers/video/tegra20/tegra-dc.c @@ -1,28 +1,23 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2011 The Chromium OS Authors. + * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com> */ #include <backlight.h> #include <cpu_func.h> +#include <clk.h> #include <dm.h> +#include <dm/ofnode_graph.h> #include <fdtdec.h> #include <log.h> #include <panel.h> -#include <part.h> -#include <pwm.h> #include <video.h> -#include <asm/cache.h> -#include <asm/global_data.h> +#include <video_bridge.h> #include <asm/system.h> -#include <asm/gpio.h> #include <asm/io.h> - #include <asm/arch/clock.h> -#include <asm/arch/funcmux.h> -#include <asm/arch/pinmux.h> #include <asm/arch/powergate.h> -#include <asm/arch/pwm.h> #include "tegra-dc.h" @@ -39,12 +34,14 @@ struct tegra_lcd_priv { int height; /* height in pixels */ enum video_log2_bpp log2_bpp; /* colour depth */ struct display_timing timing; - struct udevice *panel; + struct udevice *panel; /* Panels attached to RGB */ + struct udevice *bridge; /* Bridge linked with DC */ struct dc_ctlr *dc; /* Display controller regmap */ const struct tegra_dc_soc_info *soc; fdt_addr_t frame_buffer; /* Address of frame buffer */ unsigned pixel_clock; /* Pixel clock in Hz */ - int dc_clk[2]; /* Contains clk and its parent */ + struct clk *clk; + struct clk *clk_parent; ulong scdiv; /* Clock divider used by disp_clk_ctrl */ bool rotation; /* 180 degree panel turn */ int pipe; /* DC controller: 0 for A, 1 for B */ @@ -144,10 +141,9 @@ static int update_display_mode(struct tegra_lcd_priv *priv) val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT; val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT; writel(val, &disp->disp_interface_ctrl); - } - if (priv->soc->has_rgb) writel(0x00010001, &disp->shift_clk_opt); + } val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT; val |= priv->scdiv << SHIFT_CLK_DIVIDER_SHIFT; @@ -267,7 +263,9 @@ static int setup_window(struct tegra_lcd_priv *priv, win->out_h = priv->height; win->phys_addr = priv->frame_buffer; win->stride = priv->width * (1 << priv->log2_bpp) / 8; - debug("%s: depth = %d\n", __func__, priv->log2_bpp); + + log_debug("%s: depth = %d\n", __func__, priv->log2_bpp); + switch (priv->log2_bpp) { case VIDEO_BPP32: win->fmt = COLOR_DEPTH_R8G8B8A8; @@ -279,7 +277,7 @@ static int setup_window(struct tegra_lcd_priv *priv, break; default: - debug("Unsupported LCD bit depth"); + log_debug("Unsupported LCD bit depth\n"); return -1; } @@ -301,7 +299,8 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, void *default_lcd_base) { struct disp_ctl_win window; - unsigned long rate = clock_get_rate(priv->dc_clk[1]); + unsigned long rate = clk_get_rate(priv->clk_parent); + int ret; priv->frame_buffer = (u32)default_lcd_base; @@ -309,15 +308,10 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, * We halve the rate if DISP1 parent is PLLD, since actual parent * is plld_out0 which is PLLD divided by 2. */ - if (priv->dc_clk[1] == CLOCK_ID_DISPLAY) + if (priv->clk_parent->id == CLOCK_ID_DISPLAY || + priv->clk_parent->id == CLOCK_ID_DISPLAY2) rate /= 2; -#ifndef CONFIG_TEGRA20 - /* PLLD2 obeys same rules as PLLD but it is present only on T30+ */ - if (priv->dc_clk[1] == CLOCK_ID_DISPLAY2) - rate /= 2; -#endif - /* * The pixel clock divider is in 7.1 format (where the bottom bit * represents 0.5). Here we calculate the divider needed to get from @@ -327,14 +321,9 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, if (!priv->scdiv) priv->scdiv = ((rate * 2 + priv->pixel_clock / 2) / priv->pixel_clock) - 2; - debug("Display clock %lu, divider %lu\n", rate, priv->scdiv); + log_debug("Display clock %lu, divider %lu\n", rate, priv->scdiv); - /* - * HOST1X is init by default at 150MHz with PLLC as parent - */ - clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_CGENERAL, - 150 * 1000000); - clock_start_periph_pll(priv->dc_clk[0], priv->dc_clk[1], + clock_start_periph_pll(priv->clk->id, priv->clk_parent->id, rate); basic_init(&priv->dc->cmd); @@ -348,8 +337,9 @@ static int tegra_display_probe(struct tegra_lcd_priv *priv, if (priv->pixel_clock) update_display_mode(priv); - if (setup_window(priv, &window)) - return -1; + ret = setup_window(priv, &window); + if (ret) + return ret; update_window(priv, &window); @@ -364,10 +354,6 @@ static int tegra_lcd_probe(struct udevice *dev) int ret; /* Initialize the Tegra display controller */ -#ifdef CONFIG_TEGRA20 - funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); -#endif - if (priv->soc->has_pgate) { uint powergate; @@ -378,44 +364,51 @@ static int tegra_lcd_probe(struct udevice *dev) ret = tegra_powergate_power_off(powergate); if (ret < 0) { - log_err("failed to power off DISP gate: %d", ret); + log_debug("failed to power off DISP gate: %d", ret); return ret; } ret = tegra_powergate_sequence_power_up(powergate, - priv->dc_clk[0]); + priv->clk->id); if (ret < 0) { - log_err("failed to power up DISP gate: %d", ret); + log_debug("failed to power up DISP gate: %d", ret); return ret; } } /* Get shift clock divider from Tegra DSI if used */ - if (!strcmp(priv->panel->name, TEGRA_DSI_A) || - !strcmp(priv->panel->name, TEGRA_DSI_B)) { - struct tegra_dc_plat *dc_plat = dev_get_plat(priv->panel); + if (priv->bridge) { + if (!strcmp(priv->bridge->driver->name, "tegra_dsi")) { + struct tegra_dc_plat *dc_plat = dev_get_plat(priv->bridge); - priv->scdiv = dc_plat->scdiv; + priv->scdiv = dc_plat->scdiv; + } } /* Clean the framebuffer area */ memset((u8 *)plat->base, 0, plat->size); flush_dcache_all(); - if (tegra_display_probe(priv, (void *)plat->base)) { - debug("%s: Failed to probe display driver\n", __func__); - return -1; + ret = tegra_display_probe(priv, (void *)plat->base); + if (ret) { + log_debug("%s: Failed to probe display driver\n", __func__); + return ret; } -#ifdef CONFIG_TEGRA20 - pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM); - pinmux_tristate_disable(PMUX_PINGRP_GPU); -#endif + if (priv->panel) { + ret = panel_enable_backlight(priv->panel); + if (ret) { + log_debug("%s: Cannot enable backlight, ret=%d\n", __func__, ret); + return ret; + } + } - ret = panel_enable_backlight(priv->panel); - if (ret) { - debug("%s: Cannot enable backlight, ret=%d\n", __func__, ret); - return ret; + if (priv->bridge) { + ret = video_bridge_attach(priv->bridge); + if (ret) { + log_debug("%s: Cannot attach bridge, ret=%d\n", __func__, ret); + return ret; + } } mmu_set_region_dcache_behaviour(priv->frame_buffer, plat->size, @@ -427,81 +420,198 @@ static int tegra_lcd_probe(struct udevice *dev) uc_priv->xsize = priv->width; uc_priv->ysize = priv->height; uc_priv->bpix = priv->log2_bpp; - debug("LCD frame buffer at %08x, size %x\n", priv->frame_buffer, - plat->size); + log_debug("LCD frame buffer at %08x, size %x\n", priv->frame_buffer, + plat->size); - return panel_set_backlight(priv->panel, BACKLIGHT_DEFAULT); + if (priv->panel) { + ret = panel_set_backlight(priv->panel, BACKLIGHT_DEFAULT); + if (ret) + return ret; + } + + if (priv->bridge) { + ret = video_bridge_set_backlight(priv->bridge, BACKLIGHT_DEFAULT); + if (ret) + return ret; + } + + return 0; +} + +static int tegra_lcd_configure_rgb(struct udevice *dev, ofnode rgb) +{ + struct tegra_lcd_priv *priv = dev_get_priv(dev); + ofnode remote; + int ret; + + /* DC can have only 1 port */ + remote = ofnode_graph_get_remote_node(rgb, -1, -1); + + ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, &priv->panel); + if (!ret) + return 0; + + ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE, remote, &priv->bridge); + if (!ret) + return 0; + + /* Try legacy method if graph did not work */ + remote = ofnode_parse_phandle(rgb, "nvidia,panel", 0); + if (!ofnode_valid(remote)) + return -EINVAL; + + ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, &priv->panel); + if (ret) { + log_debug("%s: Cannot find panel for '%s' (ret=%d)\n", + __func__, dev->name, ret); + + ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE, remote, + &priv->bridge); + if (ret) { + log_err("%s: Cannot find panel or bridge for '%s' (ret=%d)\n", + __func__, dev->name, ret); + return ret; + } + } + + return 0; +} + +static int tegra_lcd_configure_internal(struct udevice *dev) +{ + struct tegra_lcd_priv *priv = dev_get_priv(dev); + struct tegra_dc_plat *dc_plat; + ofnode host1x = ofnode_get_parent(dev_ofnode(dev)); + ofnode node; + int ret; + + switch (priv->pipe) { + case 0: /* DC0 is usually used for DSI */ + /* Check for ganged DSI configuration */ + ofnode_for_each_subnode(node, host1x) + if (ofnode_name_eq(node, "dsi") && ofnode_is_enabled(node) && + ofnode_read_bool(node, "nvidia,ganged-mode")) + goto exit; + + /* If no master DSI found loop for any active DSI */ + ofnode_for_each_subnode(node, host1x) + if (ofnode_name_eq(node, "dsi") && ofnode_is_enabled(node)) + goto exit; + + log_err("%s: failed to find DSI device for '%s'\n", + __func__, dev->name); + + return -ENODEV; + case 1: /* DC1 is usually used for HDMI */ + ofnode_for_each_subnode(node, host1x) + if (ofnode_name_eq(node, "hdmi")) + goto exit; + + log_err("%s: failed to find HDMI device for '%s'\n", + __func__, dev->name); + + return -ENODEV; + default: + log_debug("Unsupported DC selection\n"); + return -EINVAL; + } + +exit: + ret = uclass_get_device_by_ofnode(UCLASS_VIDEO_BRIDGE, node, &priv->bridge); + if (ret) { + log_err("%s: failed to get DSI/HDMI device for '%s' (ret %d)\n", + __func__, dev->name, ret); + return ret; + } + + priv->clk_parent = devm_clk_get(priv->bridge, "parent"); + if (IS_ERR(priv->clk_parent)) { + log_debug("%s: Could not get DC clock parent from DSI/HDMI: %ld\n", + __func__, PTR_ERR(priv->clk_parent)); + return PTR_ERR(priv->clk_parent); + } + + dc_plat = dev_get_plat(priv->bridge); + + /* Fill the platform data for internal devices */ + dc_plat->dev = dev; + dc_plat->dc = priv->dc; + dc_plat->pipe = priv->pipe; + + return 0; } static int tegra_lcd_of_to_plat(struct udevice *dev) { struct tegra_lcd_priv *priv = dev_get_priv(dev); - const void *blob = gd->fdt_blob; struct display_timing *timing; - int node = dev_of_offset(dev); - int panel_node; - int rgb; + ofnode rgb; int ret; priv->dc = (struct dc_ctlr *)dev_read_addr_ptr(dev); if (!priv->dc) { - debug("%s: No display controller address\n", __func__); + log_debug("%s: No display controller address\n", __func__); return -EINVAL; } priv->soc = (struct tegra_dc_soc_info *)dev_get_driver_data(dev); - ret = clock_decode_pair(dev, priv->dc_clk); - if (ret < 0) { - debug("%s: Cannot decode clocks for '%s' (ret = %d)\n", - __func__, dev->name, ret); - return -EINVAL; + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + log_debug("%s: Could not get DC clock: %ld\n", + __func__, PTR_ERR(priv->clk)); + return PTR_ERR(priv->clk); + } + + priv->clk_parent = devm_clk_get(dev, "parent"); + if (IS_ERR(priv->clk_parent)) { + log_debug("%s: Could not get DC clock parent: %ld\n", + __func__, PTR_ERR(priv->clk_parent)); + return PTR_ERR(priv->clk_parent); } priv->rotation = dev_read_bool(dev, "nvidia,180-rotation"); priv->pipe = dev_read_u32_default(dev, "nvidia,head", 0); - rgb = fdt_subnode_offset(blob, node, "rgb"); - if (rgb < 0) { - debug("%s: Cannot find rgb subnode for '%s' (ret=%d)\n", - __func__, dev->name, rgb); - return -EINVAL; - } - /* - * Sadly the panel phandle is in an rgb subnode so we cannot use - * uclass_get_device_by_phandle(). + * Usual logic of Tegra video routing should be next: + * 1. Check rgb subnode for RGB/LVDS panels or bridges + * 2. If none found, then iterate through bridges bound, + * looking for DSIA or DSIB for DC0 and HDMI for DC1. + * If none of above is valid, then configuration is not + * valid. */ - panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel"); - if (panel_node < 0) { - debug("%s: Cannot find panel information\n", __func__); - return -EINVAL; - } - ret = uclass_get_device_by_of_offset(UCLASS_PANEL, panel_node, - &priv->panel); - if (ret) { - debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__, - dev->name, ret); - return ret; + rgb = dev_read_subnode(dev, "rgb"); + if (ofnode_valid(rgb) && ofnode_is_enabled(rgb)) { + /* RGB is available, use it */ + ret = tegra_lcd_configure_rgb(dev, rgb); + if (ret) + return ret; + } else { + /* RGB is not available, check for internal devices */ + ret = tegra_lcd_configure_internal(dev); + if (ret) + return ret; } - /* Fill the platform data for internal devices */ - if (!strcmp(priv->panel->name, TEGRA_DSI_A) || - !strcmp(priv->panel->name, TEGRA_DSI_B)) { - struct tegra_dc_plat *dc_plat = dev_get_plat(priv->panel); - - dc_plat->dev = dev; - dc_plat->dc = priv->dc; - dc_plat->pipe = priv->pipe; + if (priv->panel) { + ret = panel_get_display_timing(priv->panel, &priv->timing); + if (ret) { + ret = ofnode_decode_display_timing(rgb, 0, &priv->timing); + if (ret) { + log_debug("%s: Cannot read display timing for '%s' (ret=%d)\n", + __func__, dev->name, ret); + return -EINVAL; + } + } } - ret = panel_get_display_timing(priv->panel, &priv->timing); - if (ret) { - ret = fdtdec_decode_display_timing(blob, rgb, 0, &priv->timing); + if (priv->bridge) { + ret = video_bridge_get_display_timing(priv->bridge, &priv->timing); if (ret) { - debug("%s: Cannot read display timing for '%s' (ret=%d)\n", - __func__, dev->name, ret); + log_debug("%s: Cannot read display timing for '%s' (ret=%d)\n", + __func__, dev->name, ret); return -EINVAL; } } @@ -518,23 +628,13 @@ static int tegra_lcd_of_to_plat(struct udevice *dev) static int tegra_lcd_bind(struct udevice *dev) { struct video_uc_plat *plat = dev_get_uclass_plat(dev); - const void *blob = gd->fdt_blob; - int node = dev_of_offset(dev); - int rgb; - - rgb = fdt_subnode_offset(blob, node, "rgb"); - if ((rgb < 0) || !fdtdec_get_is_enabled(blob, rgb)) - return -ENODEV; plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT * (1 << LCD_MAX_LOG2_BPP) / 8; - return 0; + return dm_scan_fdt_dev(dev); } -static const struct video_ops tegra_lcd_ops = { -}; - static const struct tegra_dc_soc_info tegra20_dc_soc_info = { .has_timer = true, .has_rgb = true, @@ -564,6 +664,9 @@ static const struct udevice_id tegra_lcd_ids[] = { .compatible = "nvidia,tegra114-dc", .data = (ulong)&tegra114_dc_soc_info }, { + .compatible = "nvidia,tegra124-dc", + .data = (ulong)&tegra114_dc_soc_info + }, { /* sentinel */ } }; @@ -572,7 +675,6 @@ U_BOOT_DRIVER(tegra_lcd) = { .name = "tegra_lcd", .id = UCLASS_VIDEO, .of_match = tegra_lcd_ids, - .ops = &tegra_lcd_ops, .bind = tegra_lcd_bind, .probe = tegra_lcd_probe, .of_to_plat = tegra_lcd_of_to_plat, diff --git a/drivers/video/tegra20/tegra-dc.h b/drivers/video/tegra20/tegra-dc.h index 7d0c189ec80..2a4013b3355 100644 --- a/drivers/video/tegra20/tegra-dc.h +++ b/drivers/video/tegra20/tegra-dc.h @@ -14,9 +14,6 @@ /* arch-tegra/dc exists only because T124 uses it */ #include <asm/arch-tegra/dc.h> -#define TEGRA_DSI_A "dsi@54300000" -#define TEGRA_DSI_B "dsi@54400000" - struct tegra_dc_plat { struct udevice *dev; /* Display controller device */ struct dc_ctlr *dc; /* Display controller regmap */ diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c index 6327266dd22..a2a22fa0fe2 100644 --- a/drivers/video/tegra20/tegra-dsi.c +++ b/drivers/video/tegra20/tegra-dsi.c @@ -5,11 +5,13 @@ */ #include <dm.h> +#include <clk.h> #include <log.h> #include <misc.h> #include <mipi_display.h> #include <mipi_dsi.h> #include <backlight.h> +#include <video_bridge.h> #include <panel.h> #include <reset.h> #include <linux/delay.h> @@ -46,10 +48,13 @@ struct tegra_dsi_priv { enum tegra_dsi_format format; - int dsi_clk; + struct clk *clk; + struct clk *clk_parent; + int video_fifo_depth; int host_fifo_depth; + u32 calibration_pads; u32 version; /* for ganged-mode support */ @@ -246,6 +251,9 @@ static ssize_t tegra_dsi_host_transfer(struct mipi_dsi_host *host, value = DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC; + if ((msg->flags & MIPI_DSI_MSG_USE_LPM) == 0) + value |= DSI_HOST_CONTROL_HS; + /* * The host FIFO has a maximum of 64 words, so larger transmissions * need to use the video FIFO. @@ -515,8 +523,9 @@ static void tegra_dsi_pad_calibrate(struct dsi_pad_ctrl_reg *pad) writel(value, TEGRA_VI_BASE + (CSI_CIL_PAD_CONFIG << 2)); } -static void tegra_dsi_mipi_calibrate(struct tegra_dsi_priv *priv) +static void tegra_dsi_mipi_calibrate(struct udevice *dev) { + struct tegra_dsi_priv *priv = dev_get_priv(dev); struct dsi_pad_ctrl_reg *pad = &priv->dsi->pad; u32 value; int ret; @@ -545,15 +554,20 @@ static void tegra_dsi_mipi_calibrate(struct tegra_dsi_priv *priv) DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3); writel(value, &pad->pad_ctrl_3); - ret = misc_write(priv->mipi, 0, NULL, 0); + ret = misc_write(priv->mipi, priv->calibration_pads, NULL, 0); if (ret) log_debug("%s: MIPI calibration failed %d\n", __func__, ret); + + if (priv->slave) + tegra_dsi_mipi_calibrate(priv->slave); } -static void tegra_dsi_set_timeout(struct dsi_timeout_reg *rtimeout, +static void tegra_dsi_set_timeout(struct udevice *dev, unsigned long bclk, unsigned int vrefresh) { + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct dsi_timeout_reg *rtimeout = &priv->dsi->timeout; unsigned int timeout; u32 value; @@ -569,12 +583,17 @@ static void tegra_dsi_set_timeout(struct dsi_timeout_reg *rtimeout, value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0); writel(value, &rtimeout->dsi_to_tally); + + if (priv->slave) + tegra_dsi_set_timeout(priv->slave, bclk, vrefresh); } -static void tegra_dsi_set_phy_timing(struct dsi_timing_reg *ptiming, +static void tegra_dsi_set_phy_timing(struct udevice *dev, unsigned long period, const struct mipi_dphy_timing *dphy_timing) { + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct dsi_timing_reg *ptiming = &priv->dsi->ptiming; u32 value; value = DSI_TIMING_FIELD(dphy_timing->hsexit, period, 1) << 24 | @@ -598,6 +617,31 @@ static void tegra_dsi_set_phy_timing(struct dsi_timing_reg *ptiming, DSI_TIMING_FIELD(dphy_timing->tasure, period, 1) << 8 | DSI_TIMING_FIELD(dphy_timing->tago, period, 1); writel(value, &ptiming->dsi_bta_timing); + + if (priv->slave) + tegra_dsi_set_phy_timing(priv->slave, period, dphy_timing); +} + +static u32 tegra_dsi_get_lanes(struct udevice *dev) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct mipi_dsi_device *device = &priv->device; + + if (priv->master) { + struct tegra_dsi_priv *mpriv = dev_get_priv(priv->master); + struct mipi_dsi_device *mdevice = &mpriv->device; + + return mdevice->lanes + device->lanes; + } + + if (priv->slave) { + struct tegra_dsi_priv *spriv = dev_get_priv(priv->slave); + struct mipi_dsi_device *sdevice = &spriv->device; + + return device->lanes + sdevice->lanes; + } + + return device->lanes; } static void tegra_dsi_ganged_enable(struct udevice *dev, unsigned int start, @@ -611,7 +655,7 @@ static void tegra_dsi_ganged_enable(struct udevice *dev, unsigned int start, writel(DSI_GANGED_MODE_CONTROL_ENABLE, &ganged->ganged_mode_ctrl); } -static void tegra_dsi_configure(struct udevice *dev, +static void tegra_dsi_configure(struct udevice *dev, unsigned int pipe, unsigned long mode_flags) { struct tegra_dsi_priv *priv = dev_get_priv(dev); @@ -642,7 +686,7 @@ static void tegra_dsi_configure(struct udevice *dev, value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(priv->format) | DSI_CONTROL_LANES(device->lanes - 1) | - DSI_CONTROL_SOURCE(0); + DSI_CONTROL_SOURCE(pipe); writel(value, &misc->dsi_ctrl); writel(priv->video_fifo_depth, &misc->dsi_max_threshold); @@ -680,12 +724,19 @@ static void tegra_dsi_configure(struct udevice *dev, /* horizontal back porch */ hbp = timing->hback_porch.typ * mul / div; - if ((mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) == 0) - hbp += hsw; - /* horizontal front porch */ hfp = timing->hfront_porch.typ * mul / div; + if (priv->master || priv->slave) { + hact /= 2; + hsw /= 2; + hbp = hbp / 2 - 1; + hfp /= 2; + } + + if ((mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) == 0) + hbp += hsw; + /* subtract packet overhead */ hsw -= 10; hbp -= 14; @@ -695,9 +746,6 @@ static void tegra_dsi_configure(struct udevice *dev, writel(hact << 16 | hbp, &len->dsi_pkt_len_2_3); writel(hfp, &len->dsi_pkt_len_4_5); writel(0x0f0f << 16, &len->dsi_pkt_len_6_7); - - /* set SOL delay (for non-burst mode only) */ - writel(8 * mul / div, &misc->dsi_sol_delay); } else { if (priv->master || priv->slave) { /* @@ -717,32 +765,33 @@ static void tegra_dsi_configure(struct udevice *dev, value = MIPI_DCS_WRITE_MEMORY_START << 8 | MIPI_DCS_WRITE_MEMORY_CONTINUE; writel(value, &len->dsi_dcs_cmds); + } - /* set SOL delay */ - if (priv->master || priv->slave) { - unsigned long delay, bclk, bclk_ganged; - unsigned int lanes = device->lanes; - unsigned long htotal = timing->hactive.typ + timing->hfront_porch.typ + - timing->hback_porch.typ + timing->hsync_len.typ; - - /* SOL to valid, valid to FIFO and FIFO write delay */ - delay = 4 + 4 + 2; - delay = DIV_ROUND_UP(delay * mul, div * lanes); - /* FIFO read delay */ - delay = delay + 6; - - bclk = DIV_ROUND_UP(htotal * mul, div * lanes); - bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes); - value = bclk - bclk_ganged + delay + 20; - } else { - /* TODO: revisit for non-ganged mode */ - value = 8 * mul / div; - } - - writel(value, &misc->dsi_sol_delay); + /* set SOL delay */ + if (priv->master || priv->slave) { + unsigned long delay, bclk, bclk_ganged; + unsigned int lanes = tegra_dsi_get_lanes(dev); + unsigned long htotal = timing->hactive.typ + timing->hfront_porch.typ + + timing->hback_porch.typ + timing->hsync_len.typ; + + /* SOL to valid, valid to FIFO and FIFO write delay */ + delay = 4 + 4 + 2; + delay = DIV_ROUND_UP(delay * mul, div * lanes); + /* FIFO read delay */ + delay = delay + 6; + + bclk = DIV_ROUND_UP(htotal * mul, div * lanes); + bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes); + value = bclk - bclk_ganged + delay + 20; + } else { + /* set SOL delay (for non-burst mode only) */ + value = 8 * mul / div; } + writel(value, &misc->dsi_sol_delay); + if (priv->slave) { + tegra_dsi_configure(priv->slave, pipe, mode_flags); /* * TODO: Support modes other than symmetrical left-right * split. @@ -753,15 +802,31 @@ static void tegra_dsi_configure(struct udevice *dev, } } +static void tegra_dsi_enable(struct udevice *dev) +{ + struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct dsi_misc_reg *misc = &priv->dsi->misc; + u32 value; + + /* enable DSI controller */ + value = readl(&misc->dsi_pwr_ctrl); + value |= DSI_POWER_CONTROL_ENABLE; + writel(value, &misc->dsi_pwr_ctrl); + + if (priv->slave) + tegra_dsi_enable(priv->slave); +} + static int tegra_dsi_encoder_enable(struct udevice *dev) { struct tegra_dsi_priv *priv = dev_get_priv(dev); + struct tegra_dc_plat *dc_plat = dev_get_plat(dev); struct mipi_dsi_device *device = &priv->device; struct display_timing *timing = &priv->timing; struct dsi_misc_reg *misc = &priv->dsi->misc; unsigned int mul, div; unsigned long bclk, plld, period; - u32 value; + u32 value, lanes; int ret; /* If for some reasone DSI is enabled then it needs to @@ -780,16 +845,17 @@ static int tegra_dsi_encoder_enable(struct udevice *dev) writel(0, &misc->int_enable); if (priv->version) - tegra_dsi_mipi_calibrate(priv); + tegra_dsi_mipi_calibrate(dev); else tegra_dsi_pad_calibrate(&priv->dsi->pad); tegra_dsi_get_muldiv(device->format, &mul, &div); /* compute byte clock */ - bclk = (timing->pixelclock.typ * mul) / (div * device->lanes); + lanes = tegra_dsi_get_lanes(dev); + bclk = (timing->pixelclock.typ * mul) / (div * lanes); - tegra_dsi_set_timeout(&priv->dsi->timeout, bclk, 60); + tegra_dsi_set_timeout(dev, bclk, 60); /* * Compute bit clock and round up to the next MHz. @@ -813,25 +879,18 @@ static int tegra_dsi_encoder_enable(struct udevice *dev) * The D-PHY timing fields are expressed in byte-clock cycles, so * multiply the period by 8. */ - tegra_dsi_set_phy_timing(&priv->dsi->ptiming, - period * 8, &priv->dphy_timing); + tegra_dsi_set_phy_timing(dev, period * 8, &priv->dphy_timing); /* Perform panel HW setup */ ret = panel_enable_backlight(priv->panel); if (ret) return ret; - tegra_dsi_configure(dev, device->mode_flags); + tegra_dsi_configure(dev, dc_plat->pipe, device->mode_flags); tegra_dc_enable_controller(dev); - /* enable DSI controller */ - value = readl(&misc->dsi_pwr_ctrl); - value |= DSI_POWER_CONTROL_ENABLE; - writel(value, &misc->dsi_pwr_ctrl); - - if (priv->slave) - tegra_dsi_encoder_enable(priv->slave); + tegra_dsi_enable(dev); return 0; } @@ -859,21 +918,35 @@ static void tegra_dsi_init_clocks(struct udevice *dev) struct tegra_dsi_priv *priv = dev_get_priv(dev); struct tegra_dc_plat *dc_plat = dev_get_plat(dev); struct mipi_dsi_device *device = &priv->device; - unsigned int mul, div; + unsigned int mul, div, lanes; unsigned long bclk, plld; - if (!priv->slave) { + /* Switch parents of DSI clocks in case of not standard parent */ + if (priv->clk->id == PERIPH_ID_DSI && + priv->clk_parent->id == CLOCK_ID_DISPLAY2) { + /* Change DSIA clock parent to PLLD2 */ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + + /* DSIA_CLK_SRC */ + setbits_le32(&clkrst->crc_pll[CLOCK_ID_DISPLAY].pll_base, + BIT(25)); + } + + if (priv->clk->id == PERIPH_ID_DSIB && + priv->clk_parent->id == CLOCK_ID_DISPLAY) { /* Change DSIB clock parent to match DSIA */ struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; - clrbits_le32(&clkrst->plld2.pll_base, BIT(25)); /* DSIB_CLK_SRC */ + /* DSIB_CLK_SRC */ + clrbits_le32(&clkrst->plld2.pll_base, BIT(25)); } tegra_dsi_get_muldiv(device->format, &mul, &div); - bclk = (priv->timing.pixelclock.typ * mul) / - (div * device->lanes); + lanes = tegra_dsi_get_lanes(dev); + bclk = (priv->timing.pixelclock.typ * mul) / (div * lanes); plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC); @@ -893,16 +966,16 @@ static void tegra_dsi_init_clocks(struct udevice *dev) switch (clock_get_osc_freq()) { case CLOCK_OSC_FREQ_12_0: /* OSC is 12Mhz */ case CLOCK_OSC_FREQ_48_0: /* OSC is 48Mhz */ - clock_set_rate(CLOCK_ID_DISPLAY, plld, 12, 0, 8); + clock_set_rate(priv->clk_parent->id, plld, 12, 0, 8); break; case CLOCK_OSC_FREQ_26_0: /* OSC is 26Mhz */ - clock_set_rate(CLOCK_ID_DISPLAY, plld, 26, 0, 8); + clock_set_rate(priv->clk_parent->id, plld, 26, 0, 8); break; case CLOCK_OSC_FREQ_13_0: /* OSC is 13Mhz */ case CLOCK_OSC_FREQ_16_8: /* OSC is 16.8Mhz */ - clock_set_rate(CLOCK_ID_DISPLAY, plld, 13, 0, 8); + clock_set_rate(priv->clk_parent->id, plld, 13, 0, 8); break; case CLOCK_OSC_FREQ_19_2: @@ -914,11 +987,7 @@ static void tegra_dsi_init_clocks(struct udevice *dev) break; } - priv->dsi_clk = clock_decode_periph_id(dev); - - clock_enable(priv->dsi_clk); - udelay(2); - reset_set_enable(priv->dsi_clk, 0); + clk_enable(priv->clk); } static int tegra_dsi_ganged_probe(struct udevice *dev) @@ -926,7 +995,7 @@ static int tegra_dsi_ganged_probe(struct udevice *dev) struct tegra_dsi_priv *mpriv = dev_get_priv(dev); struct udevice *gangster; - uclass_get_device_by_phandle(UCLASS_PANEL, dev, + uclass_get_device_by_phandle(UCLASS_VIDEO_BRIDGE, dev, "nvidia,ganged-mode", &gangster); if (gangster) { /* Ganged mode is set */ @@ -955,6 +1024,20 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) return -EINVAL; } + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + log_debug("%s: Could not get DSI clock: %ld\n", + __func__, PTR_ERR(priv->clk)); + return PTR_ERR(priv->clk); + } + + priv->clk_parent = devm_clk_get(dev, "parent"); + if (IS_ERR(priv->clk_parent)) { + log_debug("%s: Could not get DSI clock parent: %ld\n", + __func__, PTR_ERR(priv->clk_parent)); + return PTR_ERR(priv->clk_parent); + } + priv->video_fifo_depth = 1920; priv->host_fifo_depth = 64; @@ -973,10 +1056,22 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) debug("%s: Cannot get avdd-dsi-csi-supply: error %d\n", __func__, ret); - ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, - "panel", &priv->panel); + /* Check all DSI children */ + device_foreach_child(priv->panel, dev) { + if (device_get_uclass_id(priv->panel) == UCLASS_PANEL) + break; + } + + /* if loop exits without panel device return error */ + if (device_get_uclass_id(priv->panel) != UCLASS_PANEL) { + log_debug("%s: panel not found, ret %d\n", __func__, ret); + return -EINVAL; + } + + ret = uclass_get_device_by_ofnode(UCLASS_PANEL, dev_ofnode(priv->panel), + &priv->panel); if (ret) { - printf("%s: Cannot get panel: error %d\n", __func__, ret); + log_debug("%s: Cannot get panel: error %d\n", __func__, ret); return log_ret(ret); } @@ -988,6 +1083,14 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) log_debug("%s: cannot get MIPI: error %d\n", __func__, ret); return ret; } + + ret = dev_read_u32_index(dev, "nvidia,mipi-calibrate", 1, + &priv->calibration_pads); + if (ret) { + log_debug("%s: cannot get calibration pads: error %d\n", + __func__, ret); + return ret; + } } panel_get_display_timing(priv->panel, &priv->timing); @@ -1019,8 +1122,8 @@ static int tegra_dsi_bridge_probe(struct udevice *dev) return 0; } -static const struct panel_ops tegra_dsi_bridge_ops = { - .enable_backlight = tegra_dsi_encoder_enable, +static const struct video_bridge_ops tegra_dsi_bridge_ops = { + .attach = tegra_dsi_encoder_enable, .set_backlight = tegra_dsi_bridge_set_panel, .get_display_timing = tegra_dsi_panel_timings, }; @@ -1028,14 +1131,16 @@ static const struct panel_ops tegra_dsi_bridge_ops = { static const struct udevice_id tegra_dsi_bridge_ids[] = { { .compatible = "nvidia,tegra30-dsi", .data = DSI_V0 }, { .compatible = "nvidia,tegra114-dsi", .data = DSI_V1 }, + { .compatible = "nvidia,tegra124-dsi", .data = DSI_V1 }, { } }; U_BOOT_DRIVER(tegra_dsi) = { .name = "tegra_dsi", - .id = UCLASS_PANEL, + .id = UCLASS_VIDEO_BRIDGE, .of_match = tegra_dsi_bridge_ids, .ops = &tegra_dsi_bridge_ops, + .bind = dm_scan_fdt_dev, .probe = tegra_dsi_bridge_probe, .plat_auto = sizeof(struct tegra_dc_plat), .priv_auto = sizeof(struct tegra_dsi_priv), diff --git a/drivers/video/tegra20/tegra-hdmi.c b/drivers/video/tegra20/tegra-hdmi.c new file mode 100644 index 00000000000..bda69919d92 --- /dev/null +++ b/drivers/video/tegra20/tegra-hdmi.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013 NVIDIA Corporation + * Copyright (c) 2023 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <clk.h> +#include <dm.h> +#include <edid.h> +#include <i2c.h> +#include <log.h> +#include <misc.h> +#include <panel.h> +#include <reset.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/time.h> +#include <power/regulator.h> +#include <video_bridge.h> + +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/arch/clock.h> + +#include "tegra-dc.h" +#include "tegra-hdmi.h" + +#define DDCCI_ENTRY_ADDR 0x37 +#define DDCCI_SOURSE_ADDR 0x51 +#define DDCCI_COMMAND_WRITE 0x03 +#define DDCCI_CTRL_BRIGHTNESS 0x10 + +#define HDMI_EDID_I2C_ADDR 0x50 +#define HDMI_REKEY_DEFAULT 56 + +static const char * const hdmi_supplies[] = { + "hdmi-supply", "pll-supply", "vdd-supply" +}; + +struct tmds_config { + unsigned int pclk; + u32 pll0; + u32 pll1; + u32 pe_current; + u32 drive_current; + u32 peak_current; +}; + +struct tegra_hdmi_config { + const struct tmds_config *tmds; + unsigned int num_tmds; + unsigned int max_pclk; + + /* to be filled */ +}; + +struct tegra_hdmi_priv { + struct hdmi_ctlr *hdmi_regmap; + + struct udevice *supplies[ARRAY_SIZE(hdmi_supplies)]; + struct udevice *hdmi_ddc; + + struct gpio_desc hpd; /* hotplug detection gpio */ + struct display_timing timing; + + struct clk *clk; + struct clk *clk_parent; + + int panel_bits_per_colourp; + const struct tegra_hdmi_config *config; +}; + +/* 1280x720p 60hz: EIA/CEA-861-B Format 4 */ +static struct display_timing default_720p_timing = { + .pixelclock.typ = 74250000, + .hactive.typ = 1280, + .hfront_porch.typ = 110, + .hback_porch.typ = 220, + .hsync_len.typ = 40, + .vactive.typ = 720, + .vfront_porch.typ = 5, + .vback_porch.typ = 20, + .vsync_len.typ = 5, + .flags = DISPLAY_FLAGS_HSYNC_HIGH | + DISPLAY_FLAGS_VSYNC_HIGH, +}; + +static const struct tmds_config tegra20_tmds_config[] = { + { /* slow pixel clock modes */ + .pclk = 27000000, + .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | + SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(0) | + SOR_PLL_TX_REG_LOAD(3), + .pll1 = SOR_PLL_TMDS_TERM_ENABLE, + .pe_current = PE_CURRENT0(PE_CURRENT_0_0_mA) | + PE_CURRENT1(PE_CURRENT_0_0_mA) | + PE_CURRENT2(PE_CURRENT_0_0_mA) | + PE_CURRENT3(PE_CURRENT_0_0_mA), + .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE1(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE2(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE3(DRIVE_CURRENT_7_125_mA), + }, + { /* high pixel clock modes */ + .pclk = UINT_MAX, + .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | + SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(1) | + SOR_PLL_TX_REG_LOAD(3), + .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN, + .pe_current = PE_CURRENT0(PE_CURRENT_6_0_mA) | + PE_CURRENT1(PE_CURRENT_6_0_mA) | + PE_CURRENT2(PE_CURRENT_6_0_mA) | + PE_CURRENT3(PE_CURRENT_6_0_mA), + .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE1(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE2(DRIVE_CURRENT_7_125_mA) | + DRIVE_CURRENT_LANE3(DRIVE_CURRENT_7_125_mA), + }, +}; + +static const struct tmds_config tegra30_tmds_config[] = { + { /* 480p modes */ + .pclk = 27000000, + .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | + SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(0) | + SOR_PLL_TX_REG_LOAD(0), + .pll1 = SOR_PLL_TMDS_TERM_ENABLE, + .pe_current = PE_CURRENT0(PE_CURRENT_0_0_mA) | + PE_CURRENT1(PE_CURRENT_0_0_mA) | + PE_CURRENT2(PE_CURRENT_0_0_mA) | + PE_CURRENT3(PE_CURRENT_0_0_mA), + .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA), + }, { /* 720p modes */ + .pclk = 74250000, + .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | + SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(1) | + SOR_PLL_TX_REG_LOAD(0), + .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN, + .pe_current = PE_CURRENT0(PE_CURRENT_5_0_mA) | + PE_CURRENT1(PE_CURRENT_5_0_mA) | + PE_CURRENT2(PE_CURRENT_5_0_mA) | + PE_CURRENT3(PE_CURRENT_5_0_mA), + .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA), + }, { /* 1080p modes */ + .pclk = UINT_MAX, + .pll0 = SOR_PLL_BG_V17_S(3) | SOR_PLL_ICHPMP(1) | + SOR_PLL_RESISTORSEL | SOR_PLL_VCOCAP(3) | + SOR_PLL_TX_REG_LOAD(0), + .pll1 = SOR_PLL_TMDS_TERM_ENABLE | SOR_PLL_PE_EN, + .pe_current = PE_CURRENT0(PE_CURRENT_5_0_mA) | + PE_CURRENT1(PE_CURRENT_5_0_mA) | + PE_CURRENT2(PE_CURRENT_5_0_mA) | + PE_CURRENT3(PE_CURRENT_5_0_mA), + .drive_current = DRIVE_CURRENT_LANE0(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE1(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE2(DRIVE_CURRENT_5_250_mA) | + DRIVE_CURRENT_LANE3(DRIVE_CURRENT_5_250_mA), + }, +}; + +static void tegra_dc_enable_controller(struct udevice *dev) +{ + struct tegra_dc_plat *dc_plat = dev_get_plat(dev); + struct dc_ctlr *dc = dc_plat->dc; + u32 value; + + value = readl(&dc->disp.disp_win_opt); + value |= HDMI_ENABLE; + writel(value, &dc->disp.disp_win_opt); + + writel(GENERAL_UPDATE, &dc->cmd.state_ctrl); + writel(GENERAL_ACT_REQ, &dc->cmd.state_ctrl); +} + +static void tegra_hdmi_setup_tmds(struct tegra_hdmi_priv *priv, + const struct tmds_config *tmds) +{ + struct hdmi_ctlr *hdmi = priv->hdmi_regmap; + u32 value; + + writel(tmds->pll0, &hdmi->nv_pdisp_sor_pll0); + writel(tmds->pll1, &hdmi->nv_pdisp_sor_pll1); + writel(tmds->pe_current, &hdmi->nv_pdisp_pe_current); + + writel(tmds->drive_current, &hdmi->nv_pdisp_sor_lane_drive_current); + + value = readl(&hdmi->nv_pdisp_sor_lane_drive_current); + value |= BIT(31); + writel(value, &hdmi->nv_pdisp_sor_lane_drive_current); +} + +static int tegra_hdmi_encoder_enable(struct udevice *dev) +{ + struct tegra_dc_plat *dc_plat = dev_get_plat(dev); + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + struct dc_ctlr *dc = dc_plat->dc; + struct display_timing *dt = &priv->timing; + struct hdmi_ctlr *hdmi = priv->hdmi_regmap; + unsigned long rate, div82; + unsigned int pulse_start, rekey; + int retries = 1000; + u32 value; + int i; + + /* power up sequence */ + value = readl(&hdmi->nv_pdisp_sor_pll0); + value &= ~SOR_PLL_PDBG; + writel(value, &hdmi->nv_pdisp_sor_pll0); + + udelay(20); + + value = readl(&hdmi->nv_pdisp_sor_pll0); + value &= ~SOR_PLL_PWR; + writel(value, &hdmi->nv_pdisp_sor_pll0); + + writel(VSYNC_H_POSITION(1), &dc->disp.disp_timing_opt); + writel(DITHER_CONTROL_DISABLE | BASE_COLOR_SIZE_888, + &dc->disp.disp_color_ctrl); + + /* video_preamble uses h_pulse2 */ + pulse_start = 1 + dt->hsync_len.typ + dt->hback_porch.typ - 10; + + writel(H_PULSE2_ENABLE, &dc->disp.disp_signal_opt0); + + value = PULSE_MODE_NORMAL | PULSE_POLARITY_HIGH | + PULSE_QUAL_VACTIVE | PULSE_LAST_END_A; + writel(value, &dc->disp.h_pulse[H_PULSE2].h_pulse_ctrl); + + value = PULSE_START(pulse_start) | PULSE_END(pulse_start + 8); + writel(value, &dc->disp.h_pulse[H_PULSE2].h_pulse_pos[H_PULSE0_POSITION_A]); + + value = VSYNC_WINDOW_END(0x210) | VSYNC_WINDOW_START(0x200) | + VSYNC_WINDOW_ENABLE; + writel(value, &hdmi->nv_pdisp_hdmi_vsync_window); + + if (dc_plat->pipe) + value = HDMI_SRC_DISPLAYB; + else + value = HDMI_SRC_DISPLAYA; + + if (dt->hactive.typ == 720 && (dt->vactive.typ == 480 || + dt->vactive.typ == 576)) + writel(value | ARM_VIDEO_RANGE_FULL, + &hdmi->nv_pdisp_input_control); + else + writel(value | ARM_VIDEO_RANGE_LIMITED, + &hdmi->nv_pdisp_input_control); + + rate = clock_get_periph_rate(priv->clk->id, priv->clk_parent->id); + div82 = rate / USEC_PER_SEC * 4; + value = SOR_REFCLK_DIV_INT(div82 >> 2) | SOR_REFCLK_DIV_FRAC(div82); + writel(value, &hdmi->nv_pdisp_sor_refclk); + + rekey = HDMI_REKEY_DEFAULT; + value = HDMI_CTRL_REKEY(rekey); + value |= HDMI_CTRL_MAX_AC_PACKET((dt->hsync_len.typ + dt->hback_porch.typ + + dt->hfront_porch.typ - rekey - 18) / 32); + writel(value, &hdmi->nv_pdisp_hdmi_ctrl); + + /* TMDS CONFIG */ + for (i = 0; i < priv->config->num_tmds; i++) { + if (dt->pixelclock.typ <= priv->config->tmds[i].pclk) { + tegra_hdmi_setup_tmds(priv, &priv->config->tmds[i]); + break; + } + } + + writel(SOR_SEQ_PU_PC(0) | SOR_SEQ_PU_PC_ALT(0) | SOR_SEQ_PD_PC(8) | + SOR_SEQ_PD_PC_ALT(8), &hdmi->nv_pdisp_sor_seq_ctl); + + value = SOR_SEQ_INST_WAIT_TIME(1) | SOR_SEQ_INST_WAIT_UNITS_VSYNC | + SOR_SEQ_INST_HALT | SOR_SEQ_INST_PIN_A_LOW | + SOR_SEQ_INST_PIN_B_LOW | SOR_SEQ_INST_DRIVE_PWM_OUT_LO; + + writel(value, &hdmi->nv_pdisp_sor_seq_inst0); + writel(value, &hdmi->nv_pdisp_sor_seq_inst8); + + value = readl(&hdmi->nv_pdisp_sor_cstm); + + value &= ~SOR_CSTM_ROTCLK(~0); + value |= SOR_CSTM_ROTCLK(2); + value |= SOR_CSTM_PLLDIV; + value &= ~SOR_CSTM_LVDS_ENABLE; + value &= ~SOR_CSTM_MODE_MASK; + value |= SOR_CSTM_MODE_TMDS; + + writel(value, &hdmi->nv_pdisp_sor_cstm); + + /* start SOR */ + writel(SOR_PWR_NORMAL_STATE_PU | SOR_PWR_NORMAL_START_NORMAL | + SOR_PWR_SAFE_STATE_PD | SOR_PWR_SETTING_NEW_TRIGGER, + &hdmi->nv_pdisp_sor_pwr); + writel(SOR_PWR_NORMAL_STATE_PU | SOR_PWR_NORMAL_START_NORMAL | + SOR_PWR_SAFE_STATE_PD | SOR_PWR_SETTING_NEW_DONE, + &hdmi->nv_pdisp_sor_pwr); + + do { + if (--retries < 0) + return -ETIME; + value = readl(&hdmi->nv_pdisp_sor_pwr); + } while (value & SOR_PWR_SETTING_NEW_PENDING); + + value = SOR_STATE_ASY_CRCMODE_COMPLETE | + SOR_STATE_ASY_OWNER_HEAD0 | + SOR_STATE_ASY_SUBOWNER_BOTH | + SOR_STATE_ASY_PROTOCOL_SINGLE_TMDS_A | + SOR_STATE_ASY_DEPOL_POS; + + /* setup sync polarities */ + if (dt->flags & DISPLAY_FLAGS_HSYNC_HIGH) + value |= SOR_STATE_ASY_HSYNCPOL_POS; + + if (dt->flags & DISPLAY_FLAGS_HSYNC_LOW) + value |= SOR_STATE_ASY_HSYNCPOL_NEG; + + if (dt->flags & DISPLAY_FLAGS_VSYNC_HIGH) + value |= SOR_STATE_ASY_VSYNCPOL_POS; + + if (dt->flags & DISPLAY_FLAGS_VSYNC_LOW) + value |= SOR_STATE_ASY_VSYNCPOL_NEG; + + writel(value, &hdmi->nv_pdisp_sor_state2); + + value = SOR_STATE_ASY_HEAD_OPMODE_AWAKE | SOR_STATE_ASY_ORMODE_NORMAL; + writel(value, &hdmi->nv_pdisp_sor_state1); + + writel(0, &hdmi->nv_pdisp_sor_state0); + writel(SOR_STATE_UPDATE, &hdmi->nv_pdisp_sor_state0); + writel(value | SOR_STATE_ATTACHED, + &hdmi->nv_pdisp_sor_state1); + writel(0, &hdmi->nv_pdisp_sor_state0); + + tegra_dc_enable_controller(dev); + + return 0; +} + +/* DDC/CI backlight control */ +static int tegra_hdmi_set_connector(struct udevice *dev, int percent) +{ + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + struct udevice *ddc_entry; + struct i2c_msg msg[1]; + u8 checksum = DDCCI_ENTRY_ADDR << 1; + int i, ret; + + ret = dm_i2c_probe(priv->hdmi_ddc, DDCCI_ENTRY_ADDR, 0, &ddc_entry); + if (ret) { + log_debug("%s: cannot probe DDC/CI entry: error %d\n", + __func__, ret); + return 0; + } + + /* + * payload[1] is length: hithest bit OR last 4 bits indicate + * the number of following bytes (excluding checksum) + */ + u8 payload[7] = { DDCCI_SOURSE_ADDR, BIT(7) | (sizeof(payload) - 3), + DDCCI_COMMAND_WRITE, DDCCI_CTRL_BRIGHTNESS, + (u8)(percent & 0xff), (u8)(percent & 0xff), 0 }; + + /* DDC/CI checksum is a simple XOR of all preceding bytes */ + for (i = 0; i < (sizeof(payload) - 1); i++) + checksum ^= payload[i]; + + payload[6] = checksum; + + msg->addr = DDCCI_ENTRY_ADDR; + msg->flags = 0; + msg->len = sizeof(payload); + msg->buf = payload; + + dm_i2c_xfer(ddc_entry, msg, 1); + + return 0; +} + +static int tegra_hdmi_timings(struct udevice *dev, + struct display_timing *timing) +{ + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + + memcpy(timing, &priv->timing, sizeof(*timing)); + + return 0; +} + +static void tegra_hdmi_init_clocks(struct udevice *dev) +{ + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + u32 n = priv->timing.pixelclock.typ * 2 / USEC_PER_SEC; + + switch (clock_get_osc_freq()) { + case CLOCK_OSC_FREQ_12_0: /* OSC is 12Mhz */ + case CLOCK_OSC_FREQ_48_0: /* OSC is 48Mhz */ + clock_set_rate(priv->clk_parent->id, n, 12, 0, 8); + break; + + case CLOCK_OSC_FREQ_26_0: /* OSC is 26Mhz */ + clock_set_rate(priv->clk_parent->id, n, 26, 0, 8); + break; + + case CLOCK_OSC_FREQ_13_0: /* OSC is 13Mhz */ + case CLOCK_OSC_FREQ_16_8: /* OSC is 16.8Mhz */ + clock_set_rate(priv->clk_parent->id, n, 13, 0, 8); + break; + + case CLOCK_OSC_FREQ_19_2: + case CLOCK_OSC_FREQ_38_4: + default: + /* + * These are not supported. + */ + break; + } + + clock_start_periph_pll(priv->clk->id, priv->clk_parent->id, + priv->timing.pixelclock.typ); +} + +static bool tegra_hdmi_mode_valid(void *hdmi_priv, const struct display_timing *timing) +{ + struct tegra_hdmi_priv *priv = hdmi_priv; + + if (timing->pixelclock.typ > priv->config->max_pclk) + return false; + + return true; +} + +static int tegra_hdmi_decode_edid(struct udevice *dev) +{ + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + struct udevice *hdmi_edid; + uchar edid_buf[EDID_SIZE] = { 0 }; + int i, ret; + + /* Poll for 1 sec in case EDID is not ready right after hpd */ + for (i = 0; i < 10; i++) { + ret = dm_i2c_probe(priv->hdmi_ddc, HDMI_EDID_I2C_ADDR, 0, + &hdmi_edid); + if (!ret) + break; + + mdelay(100); + } + if (ret) { + log_debug("%s: cannot probe EDID: error %d\n", + __func__, ret); + return ret; + } + + ret = dm_i2c_read(hdmi_edid, 0, edid_buf, sizeof(edid_buf)); + if (ret) { + log_debug("%s: cannot dump EDID buffer: error %d\n", + __func__, ret); + return ret; + } + + ret = edid_get_timing_validate(edid_buf, sizeof(edid_buf), &priv->timing, + &priv->panel_bits_per_colourp, + tegra_hdmi_mode_valid, priv); + if (ret) { + log_debug("%s: cannot decode EDID info: error %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +static int tegra_hdmi_wait_hpd(struct tegra_hdmi_priv *priv) +{ + int i; + + /* Poll 1 second for HPD signal */ + for (i = 0; i < 10; i++) { + if (dm_gpio_get_value(&priv->hpd)) + return 0; + + mdelay(100); + } + + return -ETIMEDOUT; +} + +static int tegra_hdmi_probe(struct udevice *dev) +{ + struct tegra_hdmi_priv *priv = dev_get_priv(dev); + struct reset_ctl reset_ctl; + int i, ret; + + priv->hdmi_regmap = (struct hdmi_ctlr *)dev_read_addr_ptr(dev); + if (!priv->hdmi_regmap) { + log_debug("%s: no display controller address\n", __func__); + return -EINVAL; + } + + priv->config = (struct tegra_hdmi_config *)dev_get_driver_data(dev); + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + log_debug("%s: Could not get HDMI clock: %ld\n", + __func__, PTR_ERR(priv->clk)); + return PTR_ERR(priv->clk); + } + + priv->clk_parent = devm_clk_get(dev, "parent"); + if (IS_ERR(priv->clk_parent)) { + log_debug("%s: Could not get HDMI clock parent: %ld\n", + __func__, PTR_ERR(priv->clk_parent)); + return PTR_ERR(priv->clk_parent); + } + + for (i = 0; i < ARRAY_SIZE(hdmi_supplies); i++) { + ret = device_get_supply_regulator(dev, hdmi_supplies[i], + &priv->supplies[i]); + if (ret) { + log_debug("%s: cannot get %s %d\n", __func__, + hdmi_supplies[i], ret); + if (ret != -ENOENT) + return log_ret(ret); + } + + ret = regulator_set_enable_if_allowed(priv->supplies[i], true); + if (ret && ret != -ENOSYS) { + log_debug("%s: cannot enable %s: error %d\n", + __func__, hdmi_supplies[i], ret); + return ret; + } + } + + ret = reset_get_by_name(dev, "hdmi", &reset_ctl); + if (ret) { + log_debug("%s: reset_get_by_name() failed: %d\n", + __func__, ret); + return ret; + } + + ret = uclass_get_device_by_phandle(UCLASS_I2C, dev, + "nvidia,ddc-i2c-bus", + &priv->hdmi_ddc); + if (ret) { + log_debug("%s: cannot get hdmi ddc i2c bus: error %d\n", + __func__, ret); + return ret; + } + + ret = gpio_request_by_name(dev, "nvidia,hpd-gpio", 0, + &priv->hpd, GPIOD_IS_IN); + if (ret) { + log_debug("%s: Could not decode hpd-gpios (%d)\n", + __func__, ret); + return ret; + } + + /* wait for connector */ + ret = tegra_hdmi_wait_hpd(priv); + if (ret) { + /* HPD failed, use default timings */ + memcpy(&priv->timing, &default_720p_timing, + sizeof(default_720p_timing)); + } else { + ret = tegra_hdmi_decode_edid(dev); + if (ret) + memcpy(&priv->timing, &default_720p_timing, + sizeof(default_720p_timing)); + } + + reset_assert(&reset_ctl); + tegra_hdmi_init_clocks(dev); + + mdelay(2); + reset_deassert(&reset_ctl); + + return 0; +} + +static const struct tegra_hdmi_config tegra20_hdmi_config = { + .tmds = tegra20_tmds_config, + .num_tmds = ARRAY_SIZE(tegra20_tmds_config), + .max_pclk = 148500000, /* 1080p */ +}; + +static const struct tegra_hdmi_config tegra30_hdmi_config = { + .tmds = tegra30_tmds_config, + .num_tmds = ARRAY_SIZE(tegra30_tmds_config), + .max_pclk = 148500000, /* 1080p */ +}; + +static const struct video_bridge_ops tegra_hdmi_ops = { + .attach = tegra_hdmi_encoder_enable, + .set_backlight = tegra_hdmi_set_connector, + .get_display_timing = tegra_hdmi_timings, +}; + +static const struct udevice_id tegra_hdmi_ids[] = { + { + .compatible = "nvidia,tegra20-hdmi", + .data = (ulong)&tegra20_hdmi_config + }, { + .compatible = "nvidia,tegra30-hdmi", + .data = (ulong)&tegra30_hdmi_config + }, { + /* sentinel */ + } +}; + +U_BOOT_DRIVER(tegra_hdmi) = { + .name = "tegra_hdmi", + .id = UCLASS_VIDEO_BRIDGE, + .of_match = tegra_hdmi_ids, + .ops = &tegra_hdmi_ops, + .probe = tegra_hdmi_probe, + .plat_auto = sizeof(struct tegra_dc_plat), + .priv_auto = sizeof(struct tegra_hdmi_priv), +}; diff --git a/drivers/video/tegra20/tegra-hdmi.h b/drivers/video/tegra20/tegra-hdmi.h new file mode 100644 index 00000000000..d17655973e3 --- /dev/null +++ b/drivers/video/tegra20/tegra-hdmi.h @@ -0,0 +1,648 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2010 + * NVIDIA Corporation <www.nvidia.com> + */ + +#ifndef _TEGRA_HDMI_H +#define _TEGRA_HDMI_H + +#ifndef __ASSEMBLY__ +#include <linux/bitops.h> +#endif + +/* Register definitions for the Tegra high-definition multimedia interface */ + +/* High-Definition Multimedia Interface (HDMI_) regs */ +struct hdmi_ctlr { + /* Address 0x000 ~ 0x0d2 */ + uint ctxsw; /* _CTXSW */ /* 0x00 */ + + uint nv_pdisp_sor_state0; /* _NV_PDISP_SOR_STATE0 */ + uint nv_pdisp_sor_state1; /* _NV_PDISP_SOR_STATE1 */ + uint nv_pdisp_sor_state2; /* _NV_PDISP_SOR_STATE2 */ + + uint nv_pdisp_rg_hdcp_an_msb; /* _NV_PDISP_RG_HDCP_AN_MSB */ + uint nv_pdisp_rg_hdcp_an_lsb; /* _NV_PDISP_RG_HDCP_AN_LSB */ + uint nv_pdisp_rg_hdcp_cn_msb; /* _NV_PDISP_RG_HDCP_CN_MSB */ + uint nv_pdisp_rg_hdcp_cn_lsb; /* _NV_PDISP_RG_HDCP_CN_LSB */ + uint nv_pdisp_rg_hdcp_aksv_msb; /* _NV_PDISP_RG_HDCP_AKSV_MSB */ + uint nv_pdisp_rg_hdcp_aksv_lsb; /* _NV_PDISP_RG_HDCP_AKSV_LSB */ + uint nv_pdisp_rg_hdcp_bksv_msb; /* _NV_PDISP_RG_HDCP_BKSV_MSB */ + uint nv_pdisp_rg_hdcp_bksv_lsb; /* _NV_PDISP_RG_HDCP_BKSV_LSB */ + uint nv_pdisp_rg_hdcp_cksv_msb; /* _NV_PDISP_RG_HDCP_CKSV_MSB */ + uint nv_pdisp_rg_hdcp_cksv_lsb; /* _NV_PDISP_RG_HDCP_CKSV_LSB */ + uint nv_pdisp_rg_hdcp_dksv_msb; /* _NV_PDISP_RG_HDCP_DKSV_MSB */ + uint nv_pdisp_rg_hdcp_dksv_lsb; /* _NV_PDISP_RG_HDCP_DKSV_LSB */ + uint nv_pdisp_rg_hdcp_ctrl; /* _NV_PDISP_RG_HDCP_CTRL */ /* 0x10 */ + uint nv_pdisp_rg_hdcp_cmode; /* _NV_PDISP_RG_HDCP_CMODE */ + uint nv_pdisp_rg_hdcp_mprime_msb; /* _NV_PDISP_RG_HDCP_MPRIME_MSB */ + uint nv_pdisp_rg_hdcp_mprime_lsb; /* _NV_PDISP_RG_HDCP_MPRIME_LSB */ + uint nv_pdisp_rg_hdcp_sprime_msb; /* _NV_PDISP_RG_HDCP_SPRIME_MSB */ + uint nv_pdisp_rg_hdcp_sprime_lsb2; /* _NV_PDISP_RG_HDCP_SPRIME_LSB2 */ + uint nv_pdisp_rg_hdcp_sprime_lsb1; /* _NV_PDISP_RG_HDCP_SPRIME_LSB1 */ + uint nv_pdisp_rg_hdcp_ri; /* _NV_PDISP_RG_HDCP_RI */ + uint nv_pdisp_rg_hdcp_cs_msb; /* _NV_PDISP_RG_HDCP_CS_MSB */ + uint nv_pdisp_rg_hdcp_cs_lsb; /* _NV_PDISP_RG_HDCP_CS_LSB */ + + uint nv_pdisp_hdmi_audio_emu0; /* _NV_PDISP_HDMI_AUDIO_EMU0 */ + uint nv_pdisp_hdmi_audio_emu_rdata0; /* _NV_PDISP_HDMI_AUDIO_EMU_RDATA0 */ + uint nv_pdisp_hdmi_audio_emu1; /* _NV_PDISP_HDMI_AUDIO_EMU1 */ + uint nv_pdisp_hdmi_audio_emu2; /* _NV_PDISP_HDMI_AUDIO_EMU2 */ + uint nv_pdisp_hdmi_audio_infoframe_ctrl; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL */ + uint nv_pdisp_hdmi_audio_infoframe_status; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_STATUS */ + uint nv_pdisp_hdmi_audio_infoframe_header; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER */ /* 0x20 */ + uint nv_pdisp_hdmi_audio_infoframe_subpack0_low; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW */ + uint nv_pdisp_hdmi_audio_infoframe_subpack0_high; /* _NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH */ + + uint nv_pdisp_hdmi_avi_infoframe_ctrl; /* _NV_PDISP_HDMI_AVI_INFOFRAME_CTRL */ + uint nv_pdisp_hdmi_avi_infoframe_status; /* _NV_PDISP_HDMI_AVI_INFOFRAME_STATUS */ + uint nv_pdisp_hdmi_avi_infoframe_header; /* _NV_PDISP_HDMI_AVI_INFOFRAME_HEADER */ + uint nv_pdisp_hdmi_avi_infoframe_subpack0_low; /* _NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW */ + uint nv_pdisp_hdmi_avi_infoframe_subpack0_high; /* _NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH */ + uint nv_pdisp_hdmi_avi_infoframe_subpack1_low; /* _NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW */ + uint nv_pdisp_hdmi_avi_infoframe_subpack1_high; /* _NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH */ + + uint nv_pdisp_hdmi_generic_ctrl; /* _NV_PDISP_HDMI_GENERIC_CTRL */ + uint nv_pdisp_hdmi_generic_status; /* _NV_PDISP_HDMI_GENERIC_STATUS */ + uint nv_pdisp_hdmi_generic_header; /* _NV_PDISP_HDMI_GENERIC_HEADER */ + uint nv_pdisp_hdmi_generic_subpack0_low; /* _NV_PDISP_HDMI_GENERIC_SUBPACK0_LOW */ + uint nv_pdisp_hdmi_generic_subpack0_high; /* _NV_PDISP_HDMI_GENERIC_SUBPACK0_HIGH */ + uint nv_pdisp_hdmi_generic_subpack1_low; /* _NV_PDISP_HDMI_GENERIC_SUBPACK1_LOW */ + uint nv_pdisp_hdmi_generic_subpack1_high; /* _NV_PDISP_HDMI_GENERIC_SUBPACK1_HIGH */ + uint nv_pdisp_hdmi_generic_subpack2_low; /* _NV_PDISP_HDMI_GENERIC_SUBPACK2_LOW */ + uint nv_pdisp_hdmi_generic_subpack2_high; /* _NV_PDISP_HDMI_GENERIC_SUBPACK2_HIGH */ + uint nv_pdisp_hdmi_generic_subpack3_low; /* _NV_PDISP_HDMI_GENERIC_SUBPACK3_LOW */ + uint nv_pdisp_hdmi_generic_subpack3_high; /* _NV_PDISP_HDMI_GENERIC_SUBPACK3_HIGH */ + + uint nv_pdisp_hdmi_acr_ctrl; /* _NV_PDISP_HDMI_ACR_CTRL */ + uint nv_pdisp_hdmi_acr_0320_subpack_low; /* _NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_0320_subpack_high; /* _NV_PDISP_HDMI_ACR_0320_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_0441_subpack_low; /* _NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_0441_subpack_high; /* _NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_0882_subpack_low; /* _NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_0882_subpack_high; /* _NV_PDISP_HDMI_ACR_0882_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_1764_subpack_low; /* _NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_1764_subpack_high; /* _NV_PDISP_HDMI_ACR_1764_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_0480_subpack_low; /* _NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_0480_subpack_high; /* _NV_PDISP_HDMI_ACR_0480_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_0960_subpack_low; /* _NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_0960_subpack_high; /* _NV_PDISP_HDMI_ACR_0960_SUBPACK_HIGH */ + uint nv_pdisp_hdmi_acr_1920_subpack_low; /* _NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW */ + uint nv_pdisp_hdmi_acr_1920_subpack_high; /* _NV_PDISP_HDMI_ACR_1920_SUBPACK_HIGH */ + + uint nv_pdisp_hdmi_ctrl; /* _NV_PDISP_HDMI_CTRL */ + uint nv_pdisp_hdmi_vsync_keepout; /* _NV_PDISP_HDMI_VSYNC_KEEPOUT */ + uint nv_pdisp_hdmi_vsync_window; /* _NV_PDISP_HDMI_VSYNC_WINDOW */ + uint nv_pdisp_hdmi_gcp_ctrl; /* _NV_PDISP_HDMI_GCP_CTRL */ + uint nv_pdisp_hdmi_gcp_status; /* _NV_PDISP_HDMI_GCP_STATUS */ + uint nv_pdisp_hdmi_gcp_subpack; /* _NV_PDISP_HDMI_GCP_SUBPACK */ + uint nv_pdisp_hdmi_channel_status1; /* _NV_PDISP_HDMI_CHANNEL_STATUS1 */ + uint nv_pdisp_hdmi_channel_status2; /* _NV_PDISP_HDMI_CHANNEL_STATUS2 */ + uint nv_pdisp_hdmi_emu0; /* _NV_PDISP_HDMI_EMU0 */ + uint nv_pdisp_hdmi_emu1; /* _NV_PDISP_HDMI_EMU1 */ + uint nv_pdisp_hdmi_emu1_rdata; /* _NV_PDISP_HDMI_EMU1_RDATA */ + uint nv_pdisp_hdmi_spare; /* _NV_PDISP_HDMI_SPARE */ + uint nv_pdisp_hdmi_spdif_chn_status1; /* _NV_PDISP_HDMI_SPDIF_CHN_STATUS1 */ + uint nv_pdisp_hdmi_spdif_chn_status2; /* _NV_PDISP_HDMI_SPDIF_CHN_STATUS2 */ + + uint nv_pdisp_hdcprif_rom_ctrl; /* _NV_PDISP_HDCPRIF_ROM_CTRL */ + + uint unused; + + uint nv_pdisp_sor_cap; /* _NV_PDISP_SOR_CAP */ + uint nv_pdisp_sor_pwr; /* _NV_PDISP_SOR_PWR */ + uint nv_pdisp_sor_test; /* _NV_PDISP_SOR_TEST */ + uint nv_pdisp_sor_pll0; /* _NV_PDISP_SOR_PLL0 */ + uint nv_pdisp_sor_pll1; /* _NV_PDISP_SOR_PLL1 */ + uint nv_pdisp_sor_pll2; /* _NV_PDISP_SOR_PLL2 */ + uint nv_pdisp_sor_cstm; /* _NV_PDISP_SOR_CSTM */ + uint nv_pdisp_sor_lvds; /* _NV_PDISP_SOR_LVDS */ + uint nv_pdisp_sor_crca; /* _NV_PDISP_SOR_CRCA */ + uint nv_pdisp_sor_crcb; /* _NV_PDISP_SOR_CRCB */ + uint nv_pdisp_sor_blank; /* _NV_PDISP_SOR_BLANK */ + + uint nv_pdisp_sor_seq_ctl; /* _NV_PDISP_SOR_SEQ_CTL */ + uint nv_pdisp_sor_seq_inst0; /* _NV_PDISP_SOR_SEQ_INST0 */ + uint nv_pdisp_sor_seq_inst1; /* _NV_PDISP_SOR_SEQ_INST1 */ + uint nv_pdisp_sor_seq_inst2; /* _NV_PDISP_SOR_SEQ_INST2 */ + uint nv_pdisp_sor_seq_inst3; /* _NV_PDISP_SOR_SEQ_INST3 */ + uint nv_pdisp_sor_seq_inst4; /* _NV_PDISP_SOR_SEQ_INST4 */ + uint nv_pdisp_sor_seq_inst5; /* _NV_PDISP_SOR_SEQ_INST5 */ + uint nv_pdisp_sor_seq_inst6; /* _NV_PDISP_SOR_SEQ_INST6 */ + uint nv_pdisp_sor_seq_inst7; /* _NV_PDISP_SOR_SEQ_INST7 */ + uint nv_pdisp_sor_seq_inst8; /* _NV_PDISP_SOR_SEQ_INST8 */ + uint nv_pdisp_sor_seq_inst9; /* _NV_PDISP_SOR_SEQ_INST9 */ + uint nv_pdisp_sor_seq_insta; /* _NV_PDISP_SOR_SEQ_INSTA */ + uint nv_pdisp_sor_seq_instb; /* _NV_PDISP_SOR_SEQ_INSTB */ + uint nv_pdisp_sor_seq_instc; /* _NV_PDISP_SOR_SEQ_INSTC */ + uint nv_pdisp_sor_seq_instd; /* _NV_PDISP_SOR_SEQ_INSTD */ + uint nv_pdisp_sor_seq_inste; /* _NV_PDISP_SOR_SEQ_INSTE */ + uint nv_pdisp_sor_seq_instf; /* _NV_PDISP_SOR_SEQ_INSTF */ + + uint unused1[2]; + + uint nv_pdisp_sor_vcrca0; /* _NV_PDISP_SOR_VCRCA0 */ + uint nv_pdisp_sor_vcrca1; /* _NV_PDISP_SOR_VCRCA1 */ + uint nv_pdisp_sor_ccrca0; /* _NV_PDISP_SOR_CCRCA0 */ + uint nv_pdisp_sor_ccrca1; /* _NV_PDISP_SOR_CCRCA1 */ + + uint nv_pdisp_sor_edataa0; /* _NV_PDISP_SOR_EDATAA0 */ + uint nv_pdisp_sor_edataa1; /* _NV_PDISP_SOR_EDATAA1 */ + + uint nv_pdisp_sor_counta0; /* _NV_PDISP_SOR_COUNTA0 */ + uint nv_pdisp_sor_counta1; /* _NV_PDISP_SOR_COUNTA1 */ + + uint nv_pdisp_sor_debuga0; /* _NV_PDISP_SOR_DEBUGA0 */ + uint nv_pdisp_sor_debuga1; /* _NV_PDISP_SOR_DEBUGA1 */ + + uint nv_pdisp_sor_trig; /* _NV_PDISP_SOR_TRIG */ + uint nv_pdisp_sor_mscheck; /* _NV_PDISP_SOR_MSCHECK */ + uint nv_pdisp_sor_lane_drive_current; /* _NV_PDISP_SOR_LANE_DRIVE_CURRENT */ + + uint nv_pdisp_audio_debug0; /* _NV_PDISP_AUDIO_DEBUG0 0x7f */ + uint nv_pdisp_audio_debug1; /* _NV_PDISP_AUDIO_DEBUG1 0x80 */ + uint nv_pdisp_audio_debug2; /* _NV_PDISP_AUDIO_DEBUG2 0x81 */ + + uint nv_pdisp_audio_fs1; /* _NV_PDISP_AUDIO_FS1 0x82 */ + uint nv_pdisp_audio_fs2; /* _NV_PDISP_AUDIO_FS2 */ + uint nv_pdisp_audio_fs3; /* _NV_PDISP_AUDIO_FS3 */ + uint nv_pdisp_audio_fs4; /* _NV_PDISP_AUDIO_FS4 */ + uint nv_pdisp_audio_fs5; /* _NV_PDISP_AUDIO_FS5 */ + uint nv_pdisp_audio_fs6; /* _NV_PDISP_AUDIO_FS6 */ + uint nv_pdisp_audio_fs7; /* _NV_PDISP_AUDIO_FS7 0x88 */ + + uint nv_pdisp_audio_pulse_width; /* _NV_PDISP_AUDIO_PULSE_WIDTH */ + uint nv_pdisp_audio_threshold; /* _NV_PDISP_AUDIO_THRESHOLD */ + uint nv_pdisp_audio_cntrl0; /* _NV_PDISP_AUDIO_CNTRL0 */ + uint nv_pdisp_audio_n; /* _NV_PDISP_AUDIO_N */ + uint nv_pdisp_audio_nval[7]; /* _NV_PDISP_AUDIO_NVAL */ + + uint nv_pdisp_hdcprif_rom_timing; /* _NV_PDISP_HDCPRIF_ROM_TIMING */ + uint nv_pdisp_sor_refclk; /* _NV_PDISP_SOR_REFCLK */ + uint nv_pdisp_crc_control; /* _NV_PDISP_CRC_CONTROL */ + uint nv_pdisp_input_control; /* _NV_PDISP_INPUT_CONTROL */ + uint nv_pdisp_scratch; /* _NV_PDISP_SCRATCH */ + uint nv_pdisp_pe_current; /* _NV_PDISP_PE_CURRENT */ + + uint nv_pdisp_key_ctrl; /* _NV_PDISP_KEY_CTRL */ + uint nv_pdisp_key_debug0; /* _NV_PDISP_KEY_DEBUG0 */ + uint nv_pdisp_key_debug1; /* _NV_PDISP_KEY_DEBUG1 */ + uint nv_pdisp_key_debug2; /* _NV_PDISP_KEY_DEBUG2 */ + uint nv_pdisp_key_hdcp_key_0; /* _NV_PDISP_KEY_HDCP_KEY_0 */ + uint nv_pdisp_key_hdcp_key_1; /* _NV_PDISP_KEY_HDCP_KEY_1 */ + uint nv_pdisp_key_hdcp_key_2; /* _NV_PDISP_KEY_HDCP_KEY_2 */ + uint nv_pdisp_key_hdcp_key_3; /* _NV_PDISP_KEY_HDCP_KEY_3 */ + uint nv_pdisp_key_hdcp_key_trig; /* _NV_PDISP_KEY_HDCP_KEY_3 */ + uint nv_pdisp_key_skey_index; /* _NV_PDISP_KEY_HDCP_KEY_3 */ /* 0xa3 */ + + uint unused2[8]; + + uint nv_pdisp_sor_audio_cntrl0; /* _NV_PDISP_SOR_AUDIO_CNTRL0 */ /* 0xac */ + uint nv_pdisp_sor_audio_debug; /* _NV_PDISP_SOR_AUDIO_DEBUG */ + uint nv_pdisp_sor_audio_spare0; /* _NV_PDISP_SOR_AUDIO_SPARE0 */ + uint nv_pdisp_sor_audio_nval[7]; /* _NV_PDISP_SOR_AUDIO_NVAL 0xaf ~ 0xb5 */ + uint nv_pdisp_sor_audio_hda_scratch[4]; /* _NV_PDISP_SOR_AUDIO_HDA_SCRATCH 0xb6 ~ 0xb9 */ + uint nv_pdisp_sor_audio_hda_codec_scratch[2]; /* _NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH 0xba ~ 0xbb */ + + uint nv_pdisp_sor_audio_hda_eld_bufwr; /* _NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR */ + uint nv_pdisp_sor_audio_hda_presense; /* _NV_PDISP_SOR_AUDIO_HDA_PRESENSE */ + uint nv_pdisp_sor_audio_hda_cp; /* _NV_PDISP_SOR_AUDIO_HDA_CP */ + uint nv_pdisp_sor_audio_aval[8]; /* _NV_PDISP_SOR_AUDIO_AVAL */ + uint nv_pdisp_sor_audio_gen_ctrl; /* _NV_PDISP_SOR_AUDIO_GEN_CTRL */ + + uint unused3[4]; + + uint nv_pdisp_int_status; /* _NV_PDISP_INT_STATUS */ + uint nv_pdisp_int_mask; /* _NV_PDISP_INT_MASK */ + uint nv_pdisp_int_enable; /* _NV_PDISP_INT_ENABLE */ + + uint unused4[2]; + + uint nv_pdisp_sor_io_peak_current; /* _NV_PDISP_SOR_IO_PEAK_CURRENT */ + uint nv_pdisp_sor_pad_ctls0; /* _NV_PDISP_SOR_PAD_CTLS0 */ +}; + +/* HDMI_NV_PDISP_SOR_STATE0 0x01 */ +#define SOR_STATE_UPDATE BIT(0) + +/* HDMI_NV_PDISP_SOR_STATE1 0x02 */ +#define SOR_STATE_ASY_HEAD_OPMODE_AWAKE BIT(1) +#define SOR_STATE_ASY_ORMODE_NORMAL BIT(2) +#define SOR_STATE_ATTACHED BIT(3) + +/* HDMI_NV_PDISP_SOR_STATE2 0x03 */ +#define SOR_STATE_ASY_OWNER_NONE (0 << 0) +#define SOR_STATE_ASY_OWNER_HEAD0 (1 << 0) +#define SOR_STATE_ASY_SUBOWNER_NONE (0 << 4) +#define SOR_STATE_ASY_SUBOWNER_SUBHEAD0 (1 << 4) +#define SOR_STATE_ASY_SUBOWNER_SUBHEAD1 (2 << 4) +#define SOR_STATE_ASY_SUBOWNER_BOTH (3 << 4) +#define SOR_STATE_ASY_CRCMODE_ACTIVE (0 << 6) +#define SOR_STATE_ASY_CRCMODE_COMPLETE (1 << 6) +#define SOR_STATE_ASY_CRCMODE_NON_ACTIVE (2 << 6) +#define SOR_STATE_ASY_PROTOCOL_SINGLE_TMDS_A (1 << 8) +#define SOR_STATE_ASY_PROTOCOL_CUSTOM (15 << 8) +#define SOR_STATE_ASY_HSYNCPOL_POS (0 << 12) +#define SOR_STATE_ASY_HSYNCPOL_NEG (1 << 12) +#define SOR_STATE_ASY_VSYNCPOL_POS (0 << 13) +#define SOR_STATE_ASY_VSYNCPOL_NEG (1 << 13) +#define SOR_STATE_ASY_DEPOL_POS (0 << 14) +#define SOR_STATE_ASY_DEPOL_NEG (1 << 14) + +#define INFOFRAME_CTRL_ENABLE BIT(0) +#define INFOFRAME_HEADER_TYPE(x) (((x) & 0xff) << 0) +#define INFOFRAME_HEADER_VERSION(x) (((x) & 0xff) << 8) +#define INFOFRAME_HEADER_LEN(x) (((x) & 0x0f) << 16) + +/* HDMI_NV_PDISP_HDMI_GENERIC_CTRL 0x2a */ +#define GENERIC_CTRL_ENABLE BIT(0) +#define GENERIC_CTRL_OTHER BIT(4) +#define GENERIC_CTRL_SINGLE BIT(8) +#define GENERIC_CTRL_HBLANK BIT(12) +#define GENERIC_CTRL_AUDIO BIT(16) + +/* HDMI_NV_PDISP_HDMI_ACR_* */ +#define ACR_SUBPACK_CTS(x) (((x) & 0xffffff) << 8) +#define ACR_SUBPACK_N(x) (((x) & 0xffffff) << 0) +#define ACR_ENABLE BIT(31) + +/* HDMI_NV_PDISP_HDMI_CTRL 0x44 */ +#define HDMI_CTRL_REKEY(x) (((x) & 0x7f) << 0) +#define HDMI_CTRL_MAX_AC_PACKET(x) (((x) & 0x1f) << 16) +#define HDMI_CTRL_ENABLE BIT(30) + +/* HDMI_NV_PDISP_HDMI_VSYNC_* */ +#define VSYNC_WINDOW_END(x) (((x) & 0x3ff) << 0) +#define VSYNC_WINDOW_START(x) (((x) & 0x3ff) << 16) +#define VSYNC_WINDOW_ENABLE BIT(31) + +/* HDMI_NV_PDISP_HDMI_SPARE 0x4f */ +#define SPARE_HW_CTS BIT(0) +#define SPARE_FORCE_SW_CTS BIT(1) +#define SPARE_CTS_RESET_VAL(x) (((x) & 0x7) << 16) + +/* HDMI_NV_PDISP_SOR_PWR 0x55 */ +#define SOR_PWR_NORMAL_STATE_PD (0 << 0) +#define SOR_PWR_NORMAL_STATE_PU (1 << 0) +#define SOR_PWR_NORMAL_START_NORMAL (0 << 1) +#define SOR_PWR_NORMAL_START_ALT (1 << 1) +#define SOR_PWR_SAFE_STATE_PD (0 << 16) +#define SOR_PWR_SAFE_STATE_PU (1 << 16) +#define SOR_PWR_SETTING_NEW_DONE (0 << 31) +#define SOR_PWR_SETTING_NEW_PENDING (1 << 31) +#define SOR_PWR_SETTING_NEW_TRIGGER (1 << 31) + +/* HDMI_NV_PDISP_SOR_PLL0 0x57 */ +#define SOR_PLL_PWR BIT(0) +#define SOR_PLL_PDBG BIT(1) +#define SOR_PLL_VCAPD BIT(2) +#define SOR_PLL_PDPORT BIT(3) +#define SOR_PLL_RESISTORSEL BIT(4) +#define SOR_PLL_PULLDOWN BIT(5) +#define SOR_PLL_VCOCAP(x) (((x) & 0xf) << 8) +#define SOR_PLL_BG_V17_S(x) (((x) & 0xf) << 12) +#define SOR_PLL_FILTER(x) (((x) & 0xf) << 16) +#define SOR_PLL_ICHPMP(x) (((x) & 0xf) << 24) +#define SOR_PLL_TX_REG_LOAD(x) (((x) & 0xf) << 28) + +/* HDMI_NV_PDISP_SOR_PLL1 0x58 */ +#define SOR_PLL_TMDS_TERM_ENABLE BIT(8) +#define SOR_PLL_TMDS_TERMADJ(x) (((x) & 0xf) << 9) +#define SOR_PLL_LOADADJ(x) (((x) & 0xf) << 20) +#define SOR_PLL_PE_EN BIT(28) +#define SOR_PLL_HALF_FULL_PE BIT(29) +#define SOR_PLL_S_D_PIN_PE BIT(30) + +/* HDMI_NV_PDISP_SOR_CSTM 0x5a */ +#define SOR_CSTM_ROTCLK(x) (((x) & 0xf) << 24) +#define SOR_CSTM_PLLDIV BIT(21) +#define SOR_CSTM_LVDS_ENABLE BIT(16) +#define SOR_CSTM_MODE_LVDS (0 << 12) +#define SOR_CSTM_MODE_TMDS (1 << 12) +#define SOR_CSTM_MODE_MASK (3 << 12) + +/* HDMI_NV_PDISP_SOR_SEQ_CTL 0x5f */ +#define SOR_SEQ_PU_PC(x) (((x) & 0xf) << 0) +#define SOR_SEQ_PU_PC_ALT(x) (((x) & 0xf) << 4) +#define SOR_SEQ_PD_PC(x) (((x) & 0xf) << 8) +#define SOR_SEQ_PD_PC_ALT(x) (((x) & 0xf) << 12) +#define SOR_SEQ_PC(x) (((x) & 0xf) << 16) +#define SOR_SEQ_STATUS BIT(28) +#define SOR_SEQ_SWITCH BIT(30) + +/* HDMI_NV_PDISP_SOR_SEQ_INST(x) (0x60 + (x)) */ +#define SOR_SEQ_INST_WAIT_TIME(x) (((x) & 0x3ff) << 0) +#define SOR_SEQ_INST_WAIT_UNITS_VSYNC (2 << 12) +#define SOR_SEQ_INST_HALT (1 << 15) +#define SOR_SEQ_INST_PIN_A_LOW (0 << 21) +#define SOR_SEQ_INST_PIN_A_HIGH (1 << 21) +#define SOR_SEQ_INST_PIN_B_LOW (0 << 22) +#define SOR_SEQ_INST_PIN_B_HIGH (1 << 22) +#define SOR_SEQ_INST_DRIVE_PWM_OUT_LO (1 << 23) + +/* HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT 0x7e */ +#define DRIVE_CURRENT_LANE0(x) (((x) & 0x3f) << 0) +#define DRIVE_CURRENT_LANE1(x) (((x) & 0x3f) << 8) +#define DRIVE_CURRENT_LANE2(x) (((x) & 0x3f) << 16) +#define DRIVE_CURRENT_LANE3(x) (((x) & 0x3f) << 24) +#define DRIVE_CURRENT_LANE0_T114(x) (((x) & 0x7f) << 0) +#define DRIVE_CURRENT_LANE1_T114(x) (((x) & 0x7f) << 8) +#define DRIVE_CURRENT_LANE2_T114(x) (((x) & 0x7f) << 16) +#define DRIVE_CURRENT_LANE3_T114(x) (((x) & 0x7f) << 24) + +/* Drive current list */ +enum { + DRIVE_CURRENT_1_500_mA, + DRIVE_CURRENT_1_875_mA, + DRIVE_CURRENT_2_250_mA, + DRIVE_CURRENT_2_625_mA, + DRIVE_CURRENT_3_000_mA, + DRIVE_CURRENT_3_375_mA, + DRIVE_CURRENT_3_750_mA, + DRIVE_CURRENT_4_125_mA, + DRIVE_CURRENT_4_500_mA, + DRIVE_CURRENT_4_875_mA, + DRIVE_CURRENT_5_250_mA, + DRIVE_CURRENT_5_625_mA, + DRIVE_CURRENT_6_000_mA, + DRIVE_CURRENT_6_375_mA, + DRIVE_CURRENT_6_750_mA, + DRIVE_CURRENT_7_125_mA, + DRIVE_CURRENT_7_500_mA, + DRIVE_CURRENT_7_875_mA, + DRIVE_CURRENT_8_250_mA, + DRIVE_CURRENT_8_625_mA, + DRIVE_CURRENT_9_000_mA, + DRIVE_CURRENT_9_375_mA, + DRIVE_CURRENT_9_750_mA, + DRIVE_CURRENT_10_125_mA, + DRIVE_CURRENT_10_500_mA, + DRIVE_CURRENT_10_875_mA, + DRIVE_CURRENT_11_250_mA, + DRIVE_CURRENT_11_625_mA, + DRIVE_CURRENT_12_000_mA, + DRIVE_CURRENT_12_375_mA, + DRIVE_CURRENT_12_750_mA, + DRIVE_CURRENT_13_125_mA, + DRIVE_CURRENT_13_500_mA, + DRIVE_CURRENT_13_875_mA, + DRIVE_CURRENT_14_250_mA, + DRIVE_CURRENT_14_625_mA, + DRIVE_CURRENT_15_000_mA, + DRIVE_CURRENT_15_375_mA, + DRIVE_CURRENT_15_750_mA, + DRIVE_CURRENT_16_125_mA, + DRIVE_CURRENT_16_500_mA, + DRIVE_CURRENT_16_875_mA, + DRIVE_CURRENT_17_250_mA, + DRIVE_CURRENT_17_625_mA, + DRIVE_CURRENT_18_000_mA, + DRIVE_CURRENT_18_375_mA, + DRIVE_CURRENT_18_750_mA, + DRIVE_CURRENT_19_125_mA, + DRIVE_CURRENT_19_500_mA, + DRIVE_CURRENT_19_875_mA, + DRIVE_CURRENT_20_250_mA, + DRIVE_CURRENT_20_625_mA, + DRIVE_CURRENT_21_000_mA, + DRIVE_CURRENT_21_375_mA, + DRIVE_CURRENT_21_750_mA, + DRIVE_CURRENT_22_125_mA, + DRIVE_CURRENT_22_500_mA, + DRIVE_CURRENT_22_875_mA, + DRIVE_CURRENT_23_250_mA, + DRIVE_CURRENT_23_625_mA, + DRIVE_CURRENT_24_000_mA, + DRIVE_CURRENT_24_375_mA, + DRIVE_CURRENT_24_750_mA, +}; + +/* Drive current list for T114 */ +enum { + DRIVE_CURRENT_0_000_mA_T114, + DRIVE_CURRENT_0_400_mA_T114, + DRIVE_CURRENT_0_800_mA_T114, + DRIVE_CURRENT_1_200_mA_T114, + DRIVE_CURRENT_1_600_mA_T114, + DRIVE_CURRENT_2_000_mA_T114, + DRIVE_CURRENT_2_400_mA_T114, + DRIVE_CURRENT_2_800_mA_T114, + DRIVE_CURRENT_3_200_mA_T114, + DRIVE_CURRENT_3_600_mA_T114, + DRIVE_CURRENT_4_000_mA_T114, + DRIVE_CURRENT_4_400_mA_T114, + DRIVE_CURRENT_4_800_mA_T114, + DRIVE_CURRENT_5_200_mA_T114, + DRIVE_CURRENT_5_600_mA_T114, + DRIVE_CURRENT_6_000_mA_T114, + DRIVE_CURRENT_6_400_mA_T114, + DRIVE_CURRENT_6_800_mA_T114, + DRIVE_CURRENT_7_200_mA_T114, + DRIVE_CURRENT_7_600_mA_T114, + DRIVE_CURRENT_8_000_mA_T114, + DRIVE_CURRENT_8_400_mA_T114, + DRIVE_CURRENT_8_800_mA_T114, + DRIVE_CURRENT_9_200_mA_T114, + DRIVE_CURRENT_9_600_mA_T114, + DRIVE_CURRENT_10_000_mA_T114, + DRIVE_CURRENT_10_400_mA_T114, + DRIVE_CURRENT_10_800_mA_T114, + DRIVE_CURRENT_11_200_mA_T114, + DRIVE_CURRENT_11_600_mA_T114, + DRIVE_CURRENT_12_000_mA_T114, + DRIVE_CURRENT_12_400_mA_T114, + DRIVE_CURRENT_12_800_mA_T114, + DRIVE_CURRENT_13_200_mA_T114, + DRIVE_CURRENT_13_600_mA_T114, + DRIVE_CURRENT_14_000_mA_T114, + DRIVE_CURRENT_14_400_mA_T114, + DRIVE_CURRENT_14_800_mA_T114, + DRIVE_CURRENT_15_200_mA_T114, + DRIVE_CURRENT_15_600_mA_T114, + DRIVE_CURRENT_16_000_mA_T114, + DRIVE_CURRENT_16_400_mA_T114, + DRIVE_CURRENT_16_800_mA_T114, + DRIVE_CURRENT_17_200_mA_T114, + DRIVE_CURRENT_17_600_mA_T114, + DRIVE_CURRENT_18_000_mA_T114, + DRIVE_CURRENT_18_400_mA_T114, + DRIVE_CURRENT_18_800_mA_T114, + DRIVE_CURRENT_19_200_mA_T114, + DRIVE_CURRENT_19_600_mA_T114, + DRIVE_CURRENT_20_000_mA_T114, + DRIVE_CURRENT_20_400_mA_T114, + DRIVE_CURRENT_20_800_mA_T114, + DRIVE_CURRENT_21_200_mA_T114, + DRIVE_CURRENT_21_600_mA_T114, + DRIVE_CURRENT_22_000_mA_T114, + DRIVE_CURRENT_22_400_mA_T114, + DRIVE_CURRENT_22_800_mA_T114, + DRIVE_CURRENT_23_200_mA_T114, + DRIVE_CURRENT_23_600_mA_T114, + DRIVE_CURRENT_24_000_mA_T114, + DRIVE_CURRENT_24_400_mA_T114, + DRIVE_CURRENT_24_800_mA_T114, + DRIVE_CURRENT_25_200_mA_T114, + DRIVE_CURRENT_25_400_mA_T114, + DRIVE_CURRENT_25_800_mA_T114, + DRIVE_CURRENT_26_200_mA_T114, + DRIVE_CURRENT_26_600_mA_T114, + DRIVE_CURRENT_27_000_mA_T114, + DRIVE_CURRENT_27_400_mA_T114, + DRIVE_CURRENT_27_800_mA_T114, + DRIVE_CURRENT_28_200_mA_T114, +}; + +/* HDMI_NV_PDISP_AUDIO_FS */ +#define AUDIO_FS_LOW(x) (((x) & 0xfff) << 0) +#define AUDIO_FS_HIGH(x) (((x) & 0xfff) << 16) + +/* HDMI_NV_PDISP_AUDIO_CNTRL0 0x8b */ +#define AUDIO_CNTRL0_ERROR_TOLERANCE(x) (((x) & 0xff) << 0) +#define AUDIO_CNTRL0_SOURCE_SELECT_AUTO (0 << 20) +#define AUDIO_CNTRL0_SOURCE_SELECT_SPDIF (1 << 20) +#define AUDIO_CNTRL0_SOURCE_SELECT_HDAL (2 << 20) +#define AUDIO_CNTRL0_FRAMES_PER_BLOCK(x) (((x) & 0xff) << 24) + +/* HDMI_NV_PDISP_AUDIO_N 0x8c */ +#define AUDIO_N_VALUE(x) (((x) & 0xfffff) << 0) +#define AUDIO_N_RESETF (1 << 20) +#define AUDIO_N_GENERATE_NORMAL (0 << 24) +#define AUDIO_N_GENERATE_ALTERNATE (1 << 24) + +/* HDMI_NV_PDISP_SOR_REFCLK 0x95 */ +#define SOR_REFCLK_DIV_INT(x) (((x) & 0xff) << 8) +#define SOR_REFCLK_DIV_FRAC(x) (((x) & 0x03) << 6) + +/* HDMI_NV_PDISP_INPUT_CONTROL 0x97 */ +#define HDMI_SRC_DISPLAYA (0 << 0) +#define HDMI_SRC_DISPLAYB (1 << 0) +#define ARM_VIDEO_RANGE_FULL (0 << 1) +#define ARM_VIDEO_RANGE_LIMITED (1 << 1) + +/* HDMI_NV_PDISP_PE_CURRENT 0x99 */ +#define PE_CURRENT0(x) (((x) & 0xf) << 0) +#define PE_CURRENT1(x) (((x) & 0xf) << 8) +#define PE_CURRENT2(x) (((x) & 0xf) << 16) +#define PE_CURRENT3(x) (((x) & 0xf) << 24) + +enum { + PE_CURRENT_0_0_mA, + PE_CURRENT_0_5_mA, + PE_CURRENT_1_0_mA, + PE_CURRENT_1_5_mA, + PE_CURRENT_2_0_mA, + PE_CURRENT_2_5_mA, + PE_CURRENT_3_0_mA, + PE_CURRENT_3_5_mA, + PE_CURRENT_4_0_mA, + PE_CURRENT_4_5_mA, + PE_CURRENT_5_0_mA, + PE_CURRENT_5_5_mA, + PE_CURRENT_6_0_mA, + PE_CURRENT_6_5_mA, + PE_CURRENT_7_0_mA, + PE_CURRENT_7_5_mA, +}; + +enum { + PE_CURRENT_0_mA_T114, + PE_CURRENT_1_mA_T114, + PE_CURRENT_2_mA_T114, + PE_CURRENT_3_mA_T114, + PE_CURRENT_4_mA_T114, + PE_CURRENT_5_mA_T114, + PE_CURRENT_6_mA_T114, + PE_CURRENT_7_mA_T114, + PE_CURRENT_8_mA_T114, + PE_CURRENT_9_mA_T114, + PE_CURRENT_10_mA_T114, + PE_CURRENT_11_mA_T114, + PE_CURRENT_12_mA_T114, + PE_CURRENT_13_mA_T114, + PE_CURRENT_14_mA_T114, + PE_CURRENT_15_mA_T114, +}; + +/* HDMI_NV_PDISP_SOR_AUDIO_CNTRL0 0xac */ +#define SOR_AUDIO_CNTRL0_SOURCE_SELECT_AUTO (0 << 20) +#define SOR_AUDIO_CNTRL0_SOURCE_SELECT_SPDIF (1 << 20) +#define SOR_AUDIO_CNTRL0_SOURCE_SELECT_HDAL (2 << 20) +#define SOR_AUDIO_CNTRL0_INJECT_NULLSMPL (1 << 29) + +/* HDMI_NV_PDISP_SOR_AUDIO_SPARE0 0xae */ +#define SOR_AUDIO_SPARE0_HBR_ENABLE BIT(27) + +/* HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0 0xba */ +#define SOR_AUDIO_HDA_CODEC_SCRATCH0_VALID BIT(30) +#define SOR_AUDIO_HDA_CODEC_SCRATCH0_FMT_MASK 0xffff + +/* HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE 0xbd */ +#define SOR_AUDIO_HDA_PRESENSE_VALID BIT(1) +#define SOR_AUDIO_HDA_PRESENSE_PRESENT BIT(0) + +/* HDMI_NV_PDISP_INT_STATUS 0xcc */ +#define INT_SCRATCH BIT(3) +#define INT_CP_REQUEST BIT(2) +#define INT_CODEC_SCRATCH1 BIT(1) +#define INT_CODEC_SCRATCH0 BIT(0) + +/* HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT 0xd1 */ +#define PEAK_CURRENT_LANE0(x) (((x) & 0x7f) << 0) +#define PEAK_CURRENT_LANE1(x) (((x) & 0x7f) << 8) +#define PEAK_CURRENT_LANE2(x) (((x) & 0x7f) << 16) +#define PEAK_CURRENT_LANE3(x) (((x) & 0x7f) << 24) + +enum { + PEAK_CURRENT_0_000_mA, + PEAK_CURRENT_0_200_mA, + PEAK_CURRENT_0_400_mA, + PEAK_CURRENT_0_600_mA, + PEAK_CURRENT_0_800_mA, + PEAK_CURRENT_1_000_mA, + PEAK_CURRENT_1_200_mA, + PEAK_CURRENT_1_400_mA, + PEAK_CURRENT_1_600_mA, + PEAK_CURRENT_1_800_mA, + PEAK_CURRENT_2_000_mA, + PEAK_CURRENT_2_200_mA, + PEAK_CURRENT_2_400_mA, + PEAK_CURRENT_2_600_mA, + PEAK_CURRENT_2_800_mA, + PEAK_CURRENT_3_000_mA, + PEAK_CURRENT_3_200_mA, + PEAK_CURRENT_3_400_mA, + PEAK_CURRENT_3_600_mA, + PEAK_CURRENT_3_800_mA, + PEAK_CURRENT_4_000_mA, + PEAK_CURRENT_4_200_mA, + PEAK_CURRENT_4_400_mA, + PEAK_CURRENT_4_600_mA, + PEAK_CURRENT_4_800_mA, + PEAK_CURRENT_5_000_mA, + PEAK_CURRENT_5_200_mA, + PEAK_CURRENT_5_400_mA, + PEAK_CURRENT_5_600_mA, + PEAK_CURRENT_5_800_mA, + PEAK_CURRENT_6_000_mA, + PEAK_CURRENT_6_200_mA, + PEAK_CURRENT_6_400_mA, + PEAK_CURRENT_6_600_mA, + PEAK_CURRENT_6_800_mA, + PEAK_CURRENT_7_000_mA, + PEAK_CURRENT_7_200_mA, + PEAK_CURRENT_7_400_mA, + PEAK_CURRENT_7_600_mA, + PEAK_CURRENT_7_800_mA, + PEAK_CURRENT_8_000_mA, + PEAK_CURRENT_8_200_mA, + PEAK_CURRENT_8_400_mA, + PEAK_CURRENT_8_600_mA, + PEAK_CURRENT_8_800_mA, + PEAK_CURRENT_9_000_mA, + PEAK_CURRENT_9_200_mA, + PEAK_CURRENT_9_400_mA, +}; + +#endif /* _TEGRA_HDMI_H */ diff --git a/drivers/video/tegra20/tegra-host1x.c b/drivers/video/tegra20/tegra-host1x.c new file mode 100644 index 00000000000..58ab871a3b4 --- /dev/null +++ b/drivers/video/tegra20/tegra-host1x.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com> + */ + +#include <dm.h> +#include <clk.h> +#include <log.h> +#include <reset.h> +#include <linux/delay.h> + +#include <asm/arch/clock.h> +#include <asm/arch-tegra/clk_rst.h> + +struct tegra_host1x_info { + u32 clk_parent; + u32 rate; +}; + +static int tegra_host1x_probe(struct udevice *dev) +{ + struct clk *clk; + struct reset_ctl reset_ctl; + const struct tegra_host1x_info *info; + int ret; + + clk = devm_clk_get(dev, NULL); + if (IS_ERR(clk)) { + log_debug("%s: cannot get HOST1X clock: %ld\n", + __func__, PTR_ERR(clk)); + return PTR_ERR(clk); + } + + ret = reset_get_by_name(dev, "host1x", &reset_ctl); + if (ret) { + log_debug("%s: cannot get HOST1X reset: %d\n", + __func__, ret); + return ret; + } + + info = (struct tegra_host1x_info *)dev_get_driver_data(dev); + + reset_assert(&reset_ctl); + clock_start_periph_pll(clk->id, info->clk_parent, info->rate); + + mdelay(2); + reset_deassert(&reset_ctl); + + return 0; +} + +static const struct tegra_host1x_info tegra20_host1x_info = { + .clk_parent = CLOCK_ID_CGENERAL, + .rate = 150000000, /* 150 MHz */ +}; + +static const struct tegra_host1x_info tegra114_host1x_info = { + .clk_parent = CLOCK_ID_PERIPH, + .rate = 136000000, /* 136 MHz */ +}; + +static const struct udevice_id tegra_host1x_ids[] = { + { + .compatible = "nvidia,tegra20-host1x", + .data = (ulong)&tegra20_host1x_info + }, { + .compatible = "nvidia,tegra30-host1x", + .data = (ulong)&tegra20_host1x_info + }, { + .compatible = "nvidia,tegra114-host1x", + .data = (ulong)&tegra114_host1x_info + }, { + .compatible = "nvidia,tegra124-host1x", + .data = (ulong)&tegra114_host1x_info + }, { + /* sentinel */ + } +}; + +U_BOOT_DRIVER(tegra_host1x) = { + .name = "tegra_host1x", + .id = UCLASS_SIMPLE_BUS, + .of_match = tegra_host1x_ids, + .probe = tegra_host1x_probe, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/video/tegra20/tegra-mipi.c b/drivers/video/tegra20/tegra-mipi.c index 2df3c1a9942..a4f4343d008 100644 --- a/drivers/video/tegra20/tegra-mipi.c +++ b/drivers/video/tegra20/tegra-mipi.c @@ -10,9 +10,10 @@ #include <linux/delay.h> #include <linux/iopoll.h> +#include <asm/arch/clock.h> #include <asm/io.h> -/* MIPI control registers 0x00 ~ 0x60 */ +/* MIPI control registers 0x00 ~ 0x74 */ struct mipi_ctlr { uint mipi_cal_ctrl; uint mipi_cal_autocal_ctrl; @@ -38,8 +39,17 @@ struct mipi_ctlr { uint mipi_cal_bias_pad_cfg0; uint mipi_cal_bias_pad_cfg1; uint mipi_cal_bias_pad_cfg2; + + uint mipi_cal_dsia_config_2; + uint mipi_cal_dsib_config_2; + uint mipi_cal_cilc_config_2; + uint mipi_cal_cild_config_2; + uint mipi_cal_csie_config_2; }; +#define MIPI_DSIA_PADS 0x60 +#define MIPI_DSIB_PADS 0x180 + #define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26) #define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24) #define MIPI_CAL_CTRL_CLKEN_OVR BIT(4) @@ -64,26 +74,25 @@ struct mipi_ctlr { #define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4) #define MIPI_CAL_BIAS_PAD_PDVREG BIT(1) +#define MIPI_CAL_HSCLKPDOSDSI(x) (((x) & 0x1f) << 8) +#define MIPI_CAL_HSCLKPUOSDSI(x) (((x) & 0x1f) << 0) + struct tegra_mipi_priv { struct mipi_ctlr *mipi; struct clk *mipi_cal; + u32 version; }; -static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf, - int size) +enum { + T114, + T124, +}; + +static void tegra114_mipi_pads_cal(struct tegra_mipi_priv *priv, + int calibration_pads) { - struct tegra_mipi_priv *priv = dev_get_priv(dev); u32 value; - value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(0x2) | - MIPI_CAL_BIAS_PAD_DRV_UP_REF(0x0); - writel(value, &priv->mipi->mipi_cal_bias_pad_cfg1); - - value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2); - value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7); - value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7); - writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2); - value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) | MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x4) | MIPI_CAL_TERMOS(0x5); @@ -99,6 +108,95 @@ static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf value = readl(&priv->mipi->mipi_cal_config_dsid); value &= ~(MIPI_CAL_SEL(0x1)); writel(value, &priv->mipi->mipi_cal_config_dsid); +} + +static void tegra124_mipi_pads_cal(struct tegra_mipi_priv *priv, + int calibration_pads) +{ + u32 value; + + /* Calibrate DSI-A */ + if (calibration_pads == MIPI_DSIA_PADS) { + printf("Calibrating DSI-A pads\n"); + + value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x0) | + MIPI_CAL_TERMOS(0x0); + writel(value, &priv->mipi->mipi_cal_config_dsia); + writel(value, &priv->mipi->mipi_cal_config_dsib); + + value = MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSCLKPDOSDSI(0x1) | + MIPI_CAL_HSCLKPUOSDSI(0x2); + writel(value, &priv->mipi->mipi_cal_dsia_config_2); + writel(value, &priv->mipi->mipi_cal_dsib_config_2); + + /* Deselect PAD C */ + value = readl(&priv->mipi->mipi_cal_cilc_config_2); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_cilc_config_2); + + /* Deselect PAD D */ + value = readl(&priv->mipi->mipi_cal_cild_config_2); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_cild_config_2); + } + + /* Calibrate DSI-B */ + if (calibration_pads == MIPI_DSIB_PADS) { + printf("Calibrating DSI-B pads\n"); + + value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x0) | + MIPI_CAL_TERMOS(0x0); + writel(value, &priv->mipi->mipi_cal_config_csic); + writel(value, &priv->mipi->mipi_cal_config_csid); + + value = MIPI_CAL_SEL(0x1) | + MIPI_CAL_HSCLKPDOSDSI(0x1) | + MIPI_CAL_HSCLKPUOSDSI(0x2); + writel(value, &priv->mipi->mipi_cal_cilc_config_2); + writel(value, &priv->mipi->mipi_cal_cild_config_2); + + /* Deselect PAD A */ + value = readl(&priv->mipi->mipi_cal_dsia_config_2); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_dsia_config_2); + + /* Deselect PAD B */ + value = readl(&priv->mipi->mipi_cal_dsib_config_2); + value &= ~(MIPI_CAL_SEL(0x1)); + writel(value, &priv->mipi->mipi_cal_dsib_config_2); + } +} + +static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf, + int size) +{ + struct tegra_mipi_priv *priv = dev_get_priv(dev); + u32 value; + + value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(0x2) | + MIPI_CAL_BIAS_PAD_DRV_UP_REF(0x0); + writel(value, &priv->mipi->mipi_cal_bias_pad_cfg1); + + value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2); + value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7); + value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7); + writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2); + + switch (priv->version) { + case T114: + tegra114_mipi_pads_cal(priv, offset); + break; + + case T124: + tegra124_mipi_pads_cal(priv, offset); + break; + + default: + return -EINVAL; + } value = readl(&priv->mipi->mipi_cal_ctrl); value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf); @@ -134,6 +232,11 @@ static int tegra_mipi_enable(struct udevice *dev, bool val) struct tegra_mipi_priv *priv = dev_get_priv(dev); u32 value; + reset_set_enable(priv->mipi_cal->id, 1); + mdelay(100); + reset_set_enable(priv->mipi_cal->id, 0); + mdelay(1); + clk_enable(priv->mipi_cal); value = readl(&priv->mipi->mipi_cal_bias_pad_cfg0); @@ -157,6 +260,8 @@ static int tegra_mipi_probe(struct udevice *dev) { struct tegra_mipi_priv *priv = dev_get_priv(dev); + priv->version = dev_get_driver_data(dev); + priv->mipi = (struct mipi_ctlr *)dev_read_addr_ptr(dev); if (!priv->mipi) { log_debug("%s: no MIPI controller address\n", __func__); @@ -174,7 +279,8 @@ static int tegra_mipi_probe(struct udevice *dev) } static const struct udevice_id tegra_mipi_ids[] = { - { .compatible = "nvidia,tegra114-mipi" }, + { .compatible = "nvidia,tegra114-mipi", .data = T114 }, + { .compatible = "nvidia,tegra124-mipi", .data = T124 }, { } }; diff --git a/drivers/video/tegra20/tegra-pwm-backlight.c b/drivers/video/tegra20/tegra-pwm-backlight.c index 79d8a021a3a..998f0df1991 100644 --- a/drivers/video/tegra20/tegra-pwm-backlight.c +++ b/drivers/video/tegra20/tegra-pwm-backlight.c @@ -17,9 +17,6 @@ #include "tegra-dc.h" -#define TEGRA_DISPLAY_A_BASE 0x54200000 -#define TEGRA_DISPLAY_B_BASE 0x54240000 - #define TEGRA_PWM_BL_MIN_BRIGHTNESS 0x10 #define TEGRA_PWM_BL_MAX_BRIGHTNESS 0xFF @@ -106,14 +103,11 @@ static int tegra_pwm_backlight_enable(struct udevice *dev) static int tegra_pwm_backlight_probe(struct udevice *dev) { struct tegra_pwm_backlight_priv *priv = dev_get_priv(dev); + ofnode dc = ofnode_get_parent(dev_ofnode(dev)); - if (dev_read_bool(dev, "nvidia,display-b-base")) - priv->dc = (struct dc_ctlr *)TEGRA_DISPLAY_B_BASE; - else - priv->dc = (struct dc_ctlr *)TEGRA_DISPLAY_A_BASE; - + priv->dc = (struct dc_ctlr *)ofnode_get_addr(dc); if (!priv->dc) { - log_err("no display controller address\n"); + log_err("%s: failed to get DC controller\n", __func__); return -EINVAL; } diff --git a/drivers/video/ti/tilcdc.c b/drivers/video/ti/tilcdc.c index 493e2f18cd2..340c79299ba 100644 --- a/drivers/video/ti/tilcdc.c +++ b/drivers/video/ti/tilcdc.c @@ -234,7 +234,7 @@ static int tilcdc_probe(struct udevice *dev) return -EINVAL; } - err = uclass_get_device_by_name(UCLASS_CLK, "lcd_gclk@534", &clk_dev); + err = uclass_get_device_by_name(UCLASS_CLK, "clock-lcd-gclk@534", &clk_dev); if (err) { dev_err(dev, "failed to get lcd_gclk device\n"); return err; @@ -252,7 +252,7 @@ static int tilcdc_probe(struct udevice *dev) return rate; } - err = uclass_get_device_by_name(UCLASS_CLK, "dpll_disp_m2_ck@4a4", + err = uclass_get_device_by_name(UCLASS_CLK, "clock-dpll-disp-m2@4a4", &clk_dev); if (err) { dev_err(dev, "failed to get dpll_disp_m2 clock device\n"); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index b39b2546e5c..1bb67f50352 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -95,6 +95,15 @@ config WDT_APPLE The watchdog will perform a full SoC reset resulting in a reboot of the entire system. +config WDT_ADI + bool "Analog Devices watchdog timer support" + select WDT + select SPL_WDT if SPL + depends on ARCH_SC5XX + help + Enable this to support Watchdog Timer on ADI SC57X, SC58X, SC59X, + and SC59X_64 processors + config WDT_ARMADA_37XX bool "Marvell Armada 37xx watchdog timer support" depends on WDT && ARMADA_3700 diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 9b6b1a8e8ad..e6bd4c587af 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -53,3 +53,4 @@ obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o obj-$(CONFIG_WDT_SUNXI) += sunxi_wdt.o obj-$(CONFIG_WDT_TANGIER) += tangier_wdt.o obj-$(CONFIG_WDT_XILINX) += xilinx_wwdt.o +obj-$(CONFIG_WDT_ADI) += adi_wdt.o diff --git a/drivers/watchdog/adi_wdt.c b/drivers/watchdog/adi_wdt.c new file mode 100644 index 00000000000..6f5b3d5d042 --- /dev/null +++ b/drivers/watchdog/adi_wdt.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * (C) Copyright 2022 - Analog Devices, Inc. + * + * Written and/or maintained by Timesys Corporation + * + * Converted to driver model by Nathan Barrett-Morrison + * + * Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com> + * Contact: Greg Malysa <greg.malysa@timesys.com> + * + * adi_wtd.c - driver for ADI on-chip watchdog + * + */ + +#include <clk.h> +#include <dm.h> +#include <wdt.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/io.h> + +#define WDOG_CTL 0x0 +#define WDOG_CNT 0x4 +#define WDOG_STAT 0x8 + +#define RCU_CTL 0x0 +#define RCU_STAT 0x4 + +#define SEC_GCTL 0x0 +#define SEC_FCTL 0x10 +#define SEC_SCTL0 0x800 + +#define WDEN 0x0010 +#define WDDIS 0x0AD0 + +struct adi_wdt_priv { + void __iomem *rcu_base; + void __iomem *sec_base; + void __iomem *wdt_base; + struct clk clock; +}; + +static int adi_wdt_reset(struct udevice *dev) +{ + struct adi_wdt_priv *priv = dev_get_priv(dev); + + iowrite32(0, priv->wdt_base + WDOG_STAT); + + return 0; +} + +static int adi_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags) +{ + struct adi_wdt_priv *priv = dev_get_priv(dev); + + /* Disable SYSCD_RESETb input and clear the RCU0 reset status */ + iowrite32(0xf, priv->rcu_base + RCU_STAT); + iowrite32(0x0, priv->rcu_base + RCU_CTL); + + /* reset the SEC controller */ + iowrite32(0x2, priv->sec_base + SEC_GCTL); + iowrite32(0x2, priv->sec_base + SEC_FCTL); + + udelay(50); + + /* enable SEC fault event */ + iowrite32(0x1, priv->sec_base + SEC_GCTL); + + /* ANOMALY 36100004 Spurious External Fault event occurs when FCTL + * is re-programmed when currently active fault is not cleared + */ + iowrite32(0xc0, priv->sec_base + SEC_FCTL); + iowrite32(0xc1, priv->sec_base + SEC_FCTL); + + /* enable SEC fault source for watchdog0 */ + setbits_32(priv->sec_base + SEC_SCTL0 + (3*8), 0x6); + + /* Enable SYSCD_RESETb input */ + iowrite32(0x100, priv->rcu_base + RCU_CTL); + + /* enable watchdog0 */ + iowrite32(WDDIS, priv->wdt_base + WDOG_CTL); + + iowrite32(timeout_ms / 1000 * + (clk_get_rate(&priv->clock) / (IS_ENABLED(CONFIG_SC58X) ? 2 : 1)), + priv->wdt_base + WDOG_CNT); + + iowrite32(0, priv->wdt_base + WDOG_STAT); + iowrite32(WDEN, priv->wdt_base + WDOG_CTL); + + return 0; +} + +static int adi_wdt_probe(struct udevice *dev) +{ + struct adi_wdt_priv *priv = dev_get_priv(dev); + int ret; + struct resource res; + + ret = dev_read_resource_byname(dev, "rcu", &res); + if (ret) + return ret; + priv->rcu_base = devm_ioremap(dev, res.start, resource_size(&res)); + + ret = dev_read_resource_byname(dev, "sec", &res); + if (ret) + return ret; + priv->sec_base = devm_ioremap(dev, res.start, resource_size(&res)); + + ret = dev_read_resource_byname(dev, "wdt", &res); + if (ret) + return ret; + priv->wdt_base = devm_ioremap(dev, res.start, resource_size(&res)); + + ret = clk_get_by_name(dev, "sclk0", &priv->clock); + if (ret < 0) { + printf("Can't get WDT clk: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct wdt_ops adi_wdt_ops = { + .start = adi_wdt_start, + .reset = adi_wdt_reset, +}; + +static const struct udevice_id adi_wdt_ids[] = { + { .compatible = "adi,wdt" }, + {} +}; + +U_BOOT_DRIVER(adi_wdt) = { + .name = "adi_wdt", + .id = UCLASS_WDT, + .of_match = adi_wdt_ids, + .probe = adi_wdt_probe, + .ops = &adi_wdt_ops, + .priv_auto = sizeof(struct adi_wdt_priv), + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/watchdog/da9063-wdt.c b/drivers/watchdog/da9063-wdt.c index b7216b57863..ec9bc033011 100644 --- a/drivers/watchdog/da9063-wdt.c +++ b/drivers/watchdog/da9063-wdt.c @@ -145,5 +145,4 @@ U_BOOT_DRIVER(da9063_wdt) = { .id = UCLASS_WDT, .of_match = da9063_wdt_ids, .ops = &da9063_wdt_ops, - .flags = DM_FLAG_PROBE_AFTER_BIND, }; |