diff options
Diffstat (limited to 'drivers')
48 files changed, 3781 insertions, 83 deletions
diff --git a/drivers/ata/ahci-pci.c b/drivers/ata/ahci-pci.c index 1ca439d3fa4..11ec98b56f9 100644 --- a/drivers/ata/ahci-pci.c +++ b/drivers/ata/ahci-pci.c @@ -35,6 +35,7 @@ U_BOOT_DRIVER(ahci_pci) = { static struct pci_device_id ahci_pci_supported[] = { { PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_SATA_AHCI, ~0) }, + { PCI_DEVICE(0x1b21, 0x0611) }, {}, }; diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 9a08575053d..21a89eba5af 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -548,6 +548,7 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port) { struct ahci_ioports *pp = &(uc_priv->port[port]); void __iomem *port_mmio = pp->port_mmio; + u64 dma_addr; u32 port_status; void __iomem *mem; @@ -593,10 +594,12 @@ static int ahci_port_start(struct ahci_uc_priv *uc_priv, u8 port) pp->cmd_tbl_sg = (struct ahci_sg *)(uintptr_t)virt_to_phys((void *)mem); - writel_with_flush((unsigned long)pp->cmd_slot, - port_mmio + PORT_LST_ADDR); - - writel_with_flush(pp->rx_fis, port_mmio + PORT_FIS_ADDR); + dma_addr = (ulong)pp->cmd_slot; + writel_with_flush(dma_addr, port_mmio + PORT_LST_ADDR); + writel_with_flush(dma_addr >> 32, port_mmio + PORT_LST_ADDR_HI); + dma_addr = (ulong)pp->rx_fis; + writel_with_flush(dma_addr, port_mmio + PORT_FIS_ADDR); + writel_with_flush(dma_addr >> 32, port_mmio + PORT_FIS_ADDR_HI); #ifdef CONFIG_SUNXI_AHCI sunxi_dma_init(port_mmio); diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 95fe0aea2ce..16d4237f891 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -95,6 +95,14 @@ config CLK_HSDK help Enable this to support the cgu clocks on Synopsys ARC HSDK +config CLK_VERSAL + bool "Enable clock driver support for Versal" + depends on ARCH_VERSAL + select ZYNQMP_FIRMWARE + help + This clock driver adds support for clock realted settings for + Versal platform. + config CLK_VEXPRESS_OSC bool "Enable driver for Arm Versatile Express OSC clock generators" depends on CLK && VEXPRESS_CONFIG @@ -113,6 +121,7 @@ config CLK_ZYNQ config CLK_ZYNQMP bool "Enable clock driver support for ZynqMP" depends on ARCH_ZYNQMP + select ZYNQMP_FIRMWARE help This clock driver adds support for clock realted settings for ZynqMP platform. @@ -125,6 +134,13 @@ config CLK_STM32MP1 Enable the STM32 clock (RCC) driver. Enable support for manipulating STM32MP1's on-SoC clocks. +config CLK_CDCE9XX + bool "Enable CDCD9XX clock driver" + depends on CLK + help + Enable the clock synthesizer driver for CDCE913/925/937/949 + series of chips. + source "drivers/clk/analogbits/Kconfig" source "drivers/clk/at91/Kconfig" source "drivers/clk/exynos/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 68aabe1ca99..8de67774680 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -43,3 +43,5 @@ obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o obj-$(CONFIG_SANDBOX_CLK_CCF) += clk_sandbox_ccf.o obj-$(CONFIG_STM32H7) += clk_stm32h7.o obj-$(CONFIG_CLK_TI_SCI) += clk-ti-sci.o +obj-$(CONFIG_CLK_VERSAL) += clk_versal.o +obj-$(CONFIG_CLK_CDCE9XX) += clk-cdce9xx.o diff --git a/drivers/clk/clk-cdce9xx.c b/drivers/clk/clk-cdce9xx.c new file mode 100644 index 00000000000..5d1489ab0ec --- /dev/null +++ b/drivers/clk/clk-cdce9xx.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments CDCE913/925/937/949 clock synthesizer driver + * + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Tero Kristo <t-kristo@ti.com> + * + * Based on Linux kernel clk-cdce925.c. + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <clk-uclass.h> +#include <i2c.h> + +#define MAX_NUMBER_OF_PLLS 4 +#define MAX_NUMER_OF_OUTPUTS 9 + +#define CDCE9XX_REG_GLOBAL1 0x01 +#define CDCE9XX_REG_Y1SPIPDIVH 0x02 +#define CDCE9XX_REG_PDIV1L 0x03 +#define CDCE9XX_REG_XCSEL 0x05 + +#define CDCE9XX_PDIV1_H_MASK 0x3 + +#define CDCE9XX_REG_PDIV(clk) (0x16 + (((clk) - 1) & 1) + \ + ((clk) - 1) / 2 * 0x10) + +#define CDCE9XX_PDIV_MASK 0x7f + +#define CDCE9XX_BYTE_TRANSFER BIT(7) + +struct cdce9xx_chip_info { + int num_plls; + int num_outputs; +}; + +struct cdce9xx_clk_data { + struct udevice *i2c; + struct cdce9xx_chip_info *chip; + u32 xtal_rate; +}; + +static const struct cdce9xx_chip_info cdce913_chip_info = { + .num_plls = 1, .num_outputs = 3, +}; + +static const struct cdce9xx_chip_info cdce925_chip_info = { + .num_plls = 2, .num_outputs = 5, +}; + +static const struct cdce9xx_chip_info cdce937_chip_info = { + .num_plls = 3, .num_outputs = 7, +}; + +static const struct cdce9xx_chip_info cdce949_chip_info = { + .num_plls = 4, .num_outputs = 9, +}; + +static int cdce9xx_reg_read(struct udevice *dev, u8 addr, u8 *buf) +{ + struct cdce9xx_clk_data *data = dev_get_priv(dev); + int ret; + + ret = dm_i2c_read(data->i2c, addr | CDCE9XX_BYTE_TRANSFER, buf, 1); + if (ret) + dev_err(dev, "%s: failed for addr:%x, ret:%d\n", __func__, + addr, ret); + + return ret; +} + +static int cdce9xx_reg_write(struct udevice *dev, u8 addr, u8 val) +{ + struct cdce9xx_clk_data *data = dev_get_priv(dev); + int ret; + + ret = dm_i2c_write(data->i2c, addr | CDCE9XX_BYTE_TRANSFER, &val, 1); + if (ret) + dev_err(dev, "%s: failed for addr:%x, ret:%d\n", __func__, + addr, ret); + + return ret; +} + +static int cdce9xx_clk_of_xlate(struct clk *clk, + struct ofnode_phandle_args *args) +{ + struct cdce9xx_clk_data *data = dev_get_priv(clk->dev); + + if (args->args_count != 1) + return -EINVAL; + + if (args->args[0] > data->chip->num_outputs) + return -EINVAL; + + clk->id = args->args[0]; + + return 0; +} + +static int cdce9xx_clk_probe(struct udevice *dev) +{ + struct cdce9xx_clk_data *data = dev_get_priv(dev); + struct cdce9xx_chip_info *chip = (void *)dev_get_driver_data(dev); + int ret; + u32 val; + struct clk clk; + + val = (u32)dev_read_addr_ptr(dev); + + ret = i2c_get_chip(dev->parent, val, 1, &data->i2c); + if (ret) { + dev_err(dev, "I2C probe failed.\n"); + return ret; + } + + data->chip = chip; + + ret = clk_get_by_index(dev, 0, &clk); + data->xtal_rate = clk_get_rate(&clk); + + val = dev_read_u32_default(dev, "xtal-load-pf", -1); + if (val >= 0) + cdce9xx_reg_write(dev, CDCE9XX_REG_XCSEL, val << 3); + + return 0; +} + +static u16 cdce9xx_clk_get_pdiv(struct clk *clk) +{ + u8 val; + u16 pdiv; + int ret; + + if (clk->id == 0) { + ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, &val); + if (ret) + return 0; + + pdiv = (val & CDCE9XX_PDIV1_H_MASK) << 8; + + ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV1L, &val); + if (ret) + return 0; + + pdiv |= val; + } else { + ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV(clk->id), + &val); + if (ret) + return 0; + + pdiv = val & CDCE9XX_PDIV_MASK; + } + + return pdiv; +} + +static u32 cdce9xx_clk_get_parent_rate(struct clk *clk) +{ + struct cdce9xx_clk_data *data = dev_get_priv(clk->dev); + + return data->xtal_rate; +} + +static ulong cdce9xx_clk_get_rate(struct clk *clk) +{ + u32 parent_rate; + u16 pdiv; + + parent_rate = cdce9xx_clk_get_parent_rate(clk); + + pdiv = cdce9xx_clk_get_pdiv(clk); + + return parent_rate / pdiv; +} + +static ulong cdce9xx_clk_set_rate(struct clk *clk, ulong rate) +{ + u32 parent_rate; + int pdiv; + u32 diff; + u8 val; + int ret; + + parent_rate = cdce9xx_clk_get_parent_rate(clk); + + pdiv = parent_rate / rate; + + diff = rate - parent_rate / pdiv; + + if (rate - parent_rate / (pdiv + 1) < diff) + pdiv++; + + if (clk->id == 0) { + ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, &val); + if (ret) + return ret; + + val &= ~CDCE9XX_PDIV1_H_MASK; + + val |= (pdiv >> 8); + + ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, val); + if (ret) + return ret; + + ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_PDIV1L, + (pdiv & 0xff)); + if (ret) + return ret; + } else { + ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV(clk->id), + &val); + if (ret) + return ret; + + val &= ~CDCE9XX_PDIV_MASK; + + val |= pdiv; + + ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_PDIV(clk->id), + val); + if (ret) + return ret; + } + + return 0; +} + +static const struct udevice_id cdce9xx_clk_of_match[] = { + { .compatible = "ti,cdce913", .data = (u32)&cdce913_chip_info }, + { .compatible = "ti,cdce925", .data = (u32)&cdce925_chip_info }, + { .compatible = "ti,cdce937", .data = (u32)&cdce937_chip_info }, + { .compatible = "ti,cdce949", .data = (u32)&cdce949_chip_info }, + { /* sentinel */ }, +}; + +static const struct clk_ops cdce9xx_clk_ops = { + .of_xlate = cdce9xx_clk_of_xlate, + .get_rate = cdce9xx_clk_get_rate, + .set_rate = cdce9xx_clk_set_rate, +}; + +U_BOOT_DRIVER(cdce9xx_clk) = { + .name = "cdce9xx-clk", + .id = UCLASS_CLK, + .of_match = cdce9xx_clk_of_match, + .probe = cdce9xx_clk_probe, + .priv_auto_alloc_size = sizeof(struct cdce9xx_clk_data), + .ops = &cdce9xx_clk_ops, +}; diff --git a/drivers/clk/clk_versal.c b/drivers/clk/clk_versal.c new file mode 100644 index 00000000000..df87645774c --- /dev/null +++ b/drivers/clk/clk_versal.c @@ -0,0 +1,746 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 Xilinx, Inc. + * Siva Durga Prasad Paladugu <siva.durga.paladugu@xilinx.com> + */ + +#include <common.h> +#include <linux/bitops.h> +#include <linux/bitfield.h> +#include <malloc.h> +#include <clk-uclass.h> +#include <clk.h> +#include <dm.h> +#include <asm/arch/sys_proto.h> + +#define MAX_PARENT 100 +#define MAX_NODES 6 +#define MAX_NAME_LEN 50 + +#define CLK_TYPE_SHIFT 2 + +#define PM_API_PAYLOAD_LEN 3 + +#define NA_PARENT 0xFFFFFFFF +#define DUMMY_PARENT 0xFFFFFFFE + +#define CLK_TYPE_FIELD_LEN 4 +#define CLK_TOPOLOGY_NODE_OFFSET 16 +#define NODES_PER_RESP 3 + +#define CLK_TYPE_FIELD_MASK 0xF +#define CLK_FLAG_FIELD_MASK GENMASK(21, 8) +#define CLK_TYPE_FLAG_FIELD_MASK GENMASK(31, 24) +#define CLK_TYPE_FLAG2_FIELD_MASK GENMASK(7, 4) +#define CLK_TYPE_FLAG_BITS 8 + +#define CLK_PARENTS_ID_LEN 16 +#define CLK_PARENTS_ID_MASK 0xFFFF + +#define END_OF_TOPOLOGY_NODE 1 +#define END_OF_PARENTS 1 + +#define CLK_VALID_MASK 0x1 +#define NODE_CLASS_SHIFT 26U +#define NODE_SUBCLASS_SHIFT 20U +#define NODE_TYPE_SHIFT 14U +#define NODE_INDEX_SHIFT 0U + +#define CLK_GET_NAME_RESP_LEN 16 +#define CLK_GET_TOPOLOGY_RESP_WORDS 3 +#define CLK_GET_PARENTS_RESP_WORDS 3 +#define CLK_GET_ATTR_RESP_WORDS 1 + +#define NODE_SUBCLASS_CLOCK_PLL 1 +#define NODE_SUBCLASS_CLOCK_OUT 2 +#define NODE_SUBCLASS_CLOCK_REF 3 + +#define NODE_CLASS_CLOCK 2 +#define NODE_CLASS_MASK 0x3F + +#define CLOCK_NODE_TYPE_MUX 1 +#define CLOCK_NODE_TYPE_DIV 4 +#define CLOCK_NODE_TYPE_GATE 6 + +enum pm_query_id { + PM_QID_INVALID, + PM_QID_CLOCK_GET_NAME, + PM_QID_CLOCK_GET_TOPOLOGY, + PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS, + PM_QID_CLOCK_GET_PARENTS, + PM_QID_CLOCK_GET_ATTRIBUTES, + PM_QID_PINCTRL_GET_NUM_PINS, + PM_QID_PINCTRL_GET_NUM_FUNCTIONS, + PM_QID_PINCTRL_GET_NUM_FUNCTION_GROUPS, + PM_QID_PINCTRL_GET_FUNCTION_NAME, + PM_QID_PINCTRL_GET_FUNCTION_GROUPS, + PM_QID_PINCTRL_GET_PIN_GROUPS, + PM_QID_CLOCK_GET_NUM_CLOCKS, + PM_QID_CLOCK_GET_MAX_DIVISOR, +}; + +enum clk_type { + CLK_TYPE_OUTPUT, + CLK_TYPE_EXTERNAL, +}; + +struct clock_parent { + char name[MAX_NAME_LEN]; + int id; + u32 flag; +}; + +struct clock_topology { + u32 type; + u32 flag; + u32 type_flag; +}; + +struct versal_clock { + char clk_name[MAX_NAME_LEN]; + u32 valid; + enum clk_type type; + struct clock_topology node[MAX_NODES]; + u32 num_nodes; + struct clock_parent parent[MAX_PARENT]; + u32 num_parents; + u32 clk_id; +}; + +struct versal_clk_priv { + struct versal_clock *clk; +}; + +static ulong alt_ref_clk; +static ulong pl_alt_ref_clk; +static ulong ref_clk; + +struct versal_pm_query_data { + u32 qid; + u32 arg1; + u32 arg2; + u32 arg3; +}; + +static struct versal_clock *clock; +static unsigned int clock_max_idx; + +#define PM_QUERY_DATA 35 + +static int versal_pm_query(struct versal_pm_query_data qdata, u32 *ret_payload) +{ + struct pt_regs regs; + + regs.regs[0] = PM_SIP_SVC | PM_QUERY_DATA; + regs.regs[1] = ((u64)qdata.arg1 << 32) | qdata.qid; + regs.regs[2] = ((u64)qdata.arg3 << 32) | qdata.arg2; + + smc_call(®s); + + if (ret_payload) { + ret_payload[0] = (u32)regs.regs[0]; + ret_payload[1] = upper_32_bits(regs.regs[0]); + ret_payload[2] = (u32)regs.regs[1]; + ret_payload[3] = upper_32_bits(regs.regs[1]); + ret_payload[4] = (u32)regs.regs[2]; + } + + return qdata.qid == PM_QID_CLOCK_GET_NAME ? 0 : regs.regs[0]; +} + +static inline int versal_is_valid_clock(u32 clk_id) +{ + if (clk_id >= clock_max_idx) + return -ENODEV; + + return clock[clk_id].valid; +} + +static int versal_get_clock_name(u32 clk_id, char *clk_name) +{ + int ret; + + ret = versal_is_valid_clock(clk_id); + if (ret == 1) { + strncpy(clk_name, clock[clk_id].clk_name, MAX_NAME_LEN); + return 0; + } + + return ret == 0 ? -EINVAL : ret; +} + +static int versal_get_clock_type(u32 clk_id, u32 *type) +{ + int ret; + + ret = versal_is_valid_clock(clk_id); + if (ret == 1) { + *type = clock[clk_id].type; + return 0; + } + + return ret == 0 ? -EINVAL : ret; +} + +static int versal_pm_clock_get_num_clocks(u32 *nclocks) +{ + struct versal_pm_query_data qdata = {0}; + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + qdata.qid = PM_QID_CLOCK_GET_NUM_CLOCKS; + + ret = versal_pm_query(qdata, ret_payload); + *nclocks = ret_payload[1]; + + return ret; +} + +static int versal_pm_clock_get_name(u32 clock_id, char *name) +{ + struct versal_pm_query_data qdata = {0}; + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + qdata.qid = PM_QID_CLOCK_GET_NAME; + qdata.arg1 = clock_id; + + ret = versal_pm_query(qdata, ret_payload); + if (ret) + return ret; + memcpy(name, ret_payload, CLK_GET_NAME_RESP_LEN); + + return 0; +} + +static int versal_pm_clock_get_topology(u32 clock_id, u32 index, u32 *topology) +{ + struct versal_pm_query_data qdata = {0}; + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + qdata.qid = PM_QID_CLOCK_GET_TOPOLOGY; + qdata.arg1 = clock_id; + qdata.arg2 = index; + + ret = versal_pm_query(qdata, ret_payload); + memcpy(topology, &ret_payload[1], CLK_GET_TOPOLOGY_RESP_WORDS * 4); + + return ret; +} + +static int versal_pm_clock_get_parents(u32 clock_id, u32 index, u32 *parents) +{ + struct versal_pm_query_data qdata = {0}; + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + qdata.qid = PM_QID_CLOCK_GET_PARENTS; + qdata.arg1 = clock_id; + qdata.arg2 = index; + + ret = versal_pm_query(qdata, ret_payload); + memcpy(parents, &ret_payload[1], CLK_GET_PARENTS_RESP_WORDS * 4); + + return ret; +} + +static int versal_pm_clock_get_attributes(u32 clock_id, u32 *attr) +{ + struct versal_pm_query_data qdata = {0}; + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + qdata.qid = PM_QID_CLOCK_GET_ATTRIBUTES; + qdata.arg1 = clock_id; + + ret = versal_pm_query(qdata, ret_payload); + memcpy(attr, &ret_payload[1], CLK_GET_ATTR_RESP_WORDS * 4); + + return ret; +} + +static int __versal_clock_get_topology(struct clock_topology *topology, + u32 *data, u32 *nnodes) +{ + int i; + + for (i = 0; i < PM_API_PAYLOAD_LEN; i++) { + if (!(data[i] & CLK_TYPE_FIELD_MASK)) + return END_OF_TOPOLOGY_NODE; + topology[*nnodes].type = data[i] & CLK_TYPE_FIELD_MASK; + topology[*nnodes].flag = FIELD_GET(CLK_FLAG_FIELD_MASK, + data[i]); + topology[*nnodes].type_flag = + FIELD_GET(CLK_TYPE_FLAG_FIELD_MASK, data[i]); + topology[*nnodes].type_flag |= + FIELD_GET(CLK_TYPE_FLAG2_FIELD_MASK, data[i]) << + CLK_TYPE_FLAG_BITS; + debug("topology type:0x%x, flag:0x%x, type_flag:0x%x\n", + topology[*nnodes].type, topology[*nnodes].flag, + topology[*nnodes].type_flag); + (*nnodes)++; + } + + return 0; +} + +static int versal_clock_get_topology(u32 clk_id, + struct clock_topology *topology, + u32 *num_nodes) +{ + int j, ret; + u32 pm_resp[PM_API_PAYLOAD_LEN] = {0}; + + *num_nodes = 0; + for (j = 0; j <= MAX_NODES; j += 3) { + ret = versal_pm_clock_get_topology(clock[clk_id].clk_id, j, + pm_resp); + if (ret) + return ret; + ret = __versal_clock_get_topology(topology, pm_resp, num_nodes); + if (ret == END_OF_TOPOLOGY_NODE) + return 0; + } + + return 0; +} + +static int __versal_clock_get_parents(struct clock_parent *parents, u32 *data, + u32 *nparent) +{ + int i; + struct clock_parent *parent; + + for (i = 0; i < PM_API_PAYLOAD_LEN; i++) { + if (data[i] == NA_PARENT) + return END_OF_PARENTS; + + parent = &parents[i]; + parent->id = data[i] & CLK_PARENTS_ID_MASK; + if (data[i] == DUMMY_PARENT) { + strcpy(parent->name, "dummy_name"); + parent->flag = 0; + } else { + parent->flag = data[i] >> CLK_PARENTS_ID_LEN; + if (versal_get_clock_name(parent->id, parent->name)) + continue; + } + debug("parent name:%s\n", parent->name); + *nparent += 1; + } + + return 0; +} + +static int versal_clock_get_parents(u32 clk_id, struct clock_parent *parents, + u32 *num_parents) +{ + int j = 0, ret; + u32 pm_resp[PM_API_PAYLOAD_LEN] = {0}; + + *num_parents = 0; + do { + /* Get parents from firmware */ + ret = versal_pm_clock_get_parents(clock[clk_id].clk_id, j, + pm_resp); + if (ret) + return ret; + + ret = __versal_clock_get_parents(&parents[j], pm_resp, + num_parents); + if (ret == END_OF_PARENTS) + return 0; + j += PM_API_PAYLOAD_LEN; + } while (*num_parents <= MAX_PARENT); + + return 0; +} + +static u32 versal_clock_get_div(u32 clk_id) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + u32 div; + + versal_pm_request(PM_CLOCK_GETDIVIDER, clk_id, 0, 0, 0, ret_payload); + div = ret_payload[1]; + + return div; +} + +static u32 versal_clock_set_div(u32 clk_id, u32 div) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + + versal_pm_request(PM_CLOCK_SETDIVIDER, clk_id, div, 0, 0, ret_payload); + + return div; +} + +static u64 versal_clock_ref(u32 clk_id) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ref; + + versal_pm_request(PM_CLOCK_GETPARENT, clk_id, 0, 0, 0, ret_payload); + ref = ret_payload[0]; + if (!(ref & 1)) + return ref_clk; + if (ref & 2) + return pl_alt_ref_clk; + return 0; +} + +static u64 versal_clock_get_pll_rate(u32 clk_id) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + u32 fbdiv; + u32 res; + u32 frac; + u64 freq; + u32 parent_rate, parent_id; + u32 id = clk_id & 0xFFF; + + versal_pm_request(PM_CLOCK_GETSTATE, clk_id, 0, 0, 0, ret_payload); + res = ret_payload[1]; + if (!res) { + printf("0%x PLL not enabled\n", clk_id); + return 0; + } + + parent_id = clock[clock[id].parent[0].id].clk_id; + parent_rate = versal_clock_ref(parent_id); + + versal_pm_request(PM_CLOCK_GETDIVIDER, clk_id, 0, 0, 0, ret_payload); + fbdiv = ret_payload[1]; + versal_pm_request(PM_CLOCK_PLL_GETPARAM, clk_id, 2, 0, 0, ret_payload); + frac = ret_payload[1]; + + freq = (fbdiv * parent_rate) >> (1 << frac); + + return freq; +} + +static u32 versal_clock_mux(u32 clk_id) +{ + int i; + u32 id = clk_id & 0xFFF; + + for (i = 0; i < clock[id].num_nodes; i++) + if (clock[id].node[i].type == CLOCK_NODE_TYPE_MUX) + return 1; + + return 0; +} + +static u32 versal_clock_get_parentid(u32 clk_id) +{ + u32 parent_id = 0; + u32 ret_payload[PAYLOAD_ARG_CNT]; + u32 id = clk_id & 0xFFF; + + if (versal_clock_mux(clk_id)) { + versal_pm_request(PM_CLOCK_GETPARENT, clk_id, 0, 0, 0, + ret_payload); + parent_id = ret_payload[1]; + } + + debug("parent_id:0x%x\n", clock[clock[id].parent[parent_id].id].clk_id); + return clock[clock[id].parent[parent_id].id].clk_id; +} + +static u32 versal_clock_gate(u32 clk_id) +{ + u32 id = clk_id & 0xFFF; + int i; + + for (i = 0; i < clock[id].num_nodes; i++) + if (clock[id].node[i].type == CLOCK_NODE_TYPE_GATE) + return 1; + + return 0; +} + +static u32 versal_clock_div(u32 clk_id) +{ + int i; + u32 id = clk_id & 0xFFF; + + for (i = 0; i < clock[id].num_nodes; i++) + if (clock[id].node[i].type == CLOCK_NODE_TYPE_DIV) + return 1; + + return 0; +} + +static u32 versal_clock_pll(u32 clk_id, u64 *clk_rate) +{ + if (((clk_id >> NODE_SUBCLASS_SHIFT) & NODE_CLASS_MASK) == + NODE_SUBCLASS_CLOCK_PLL && + ((clk_id >> NODE_CLASS_SHIFT) & NODE_CLASS_MASK) == + NODE_CLASS_CLOCK) { + *clk_rate = versal_clock_get_pll_rate(clk_id); + return 1; + } + + return 0; +} + +static u64 versal_clock_calc(u32 clk_id) +{ + u32 parent_id; + u64 clk_rate; + u32 div; + + if (versal_clock_pll(clk_id, &clk_rate)) + return clk_rate; + + parent_id = versal_clock_get_parentid(clk_id); + if (((parent_id >> NODE_SUBCLASS_SHIFT) & + NODE_CLASS_MASK) == NODE_SUBCLASS_CLOCK_REF) + return versal_clock_ref(clk_id); + + clk_rate = versal_clock_calc(parent_id); + + if (versal_clock_div(clk_id)) { + div = versal_clock_get_div(clk_id); + clk_rate = DIV_ROUND_CLOSEST(clk_rate, div); + } + + return clk_rate; +} + +static int versal_clock_get_rate(u32 clk_id, u64 *clk_rate) +{ + if (((clk_id >> NODE_SUBCLASS_SHIFT) & + NODE_CLASS_MASK) == NODE_SUBCLASS_CLOCK_REF) + *clk_rate = versal_clock_ref(clk_id); + + if (versal_clock_pll(clk_id, clk_rate)) + return 0; + + if (((clk_id >> NODE_SUBCLASS_SHIFT) & + NODE_CLASS_MASK) == NODE_SUBCLASS_CLOCK_OUT && + ((clk_id >> NODE_CLASS_SHIFT) & + NODE_CLASS_MASK) == NODE_CLASS_CLOCK) { + if (!versal_clock_gate(clk_id)) + return -EINVAL; + *clk_rate = versal_clock_calc(clk_id); + return 0; + } + + return 0; +} + +int soc_clk_dump(void) +{ + u64 clk_rate = 0; + u32 type, ret, i = 0; + + printf("\n ****** VERSAL CLOCKS *****\n"); + + printf("alt_ref_clk:%ld pl_alt_ref_clk:%ld ref_clk:%ld\n", + alt_ref_clk, pl_alt_ref_clk, ref_clk); + for (i = 0; i < clock_max_idx; i++) { + debug("%s\n", clock[i].clk_name); + ret = versal_get_clock_type(i, &type); + if (ret || type != CLK_TYPE_OUTPUT) + continue; + + ret = versal_clock_get_rate(clock[i].clk_id, &clk_rate); + + if (ret != -EINVAL) + printf("clk: %s freq:%lld\n", + clock[i].clk_name, clk_rate); + } + + return 0; +} + +static void versal_get_clock_info(void) +{ + int i, ret; + u32 attr, type = 0, nodetype, subclass, class; + + for (i = 0; i < clock_max_idx; i++) { + ret = versal_pm_clock_get_attributes(i, &attr); + if (ret) + continue; + + clock[i].valid = attr & CLK_VALID_MASK; + clock[i].type = ((attr >> CLK_TYPE_SHIFT) & 0x1) ? + CLK_TYPE_EXTERNAL : CLK_TYPE_OUTPUT; + nodetype = (attr >> NODE_TYPE_SHIFT) & NODE_CLASS_MASK; + subclass = (attr >> NODE_SUBCLASS_SHIFT) & NODE_CLASS_MASK; + class = (attr >> NODE_CLASS_SHIFT) & NODE_CLASS_MASK; + + clock[i].clk_id = (class << NODE_CLASS_SHIFT) | + (subclass << NODE_SUBCLASS_SHIFT) | + (nodetype << NODE_TYPE_SHIFT) | + (i << NODE_INDEX_SHIFT); + + ret = versal_pm_clock_get_name(clock[i].clk_id, + clock[i].clk_name); + if (ret) + continue; + debug("clk name:%s, Valid:%d, type:%d, clk_id:0x%x\n", + clock[i].clk_name, clock[i].valid, + clock[i].type, clock[i].clk_id); + } + + /* Get topology of all clock */ + for (i = 0; i < clock_max_idx; i++) { + ret = versal_get_clock_type(i, &type); + if (ret || type != CLK_TYPE_OUTPUT) + continue; + debug("clk name:%s\n", clock[i].clk_name); + ret = versal_clock_get_topology(i, clock[i].node, + &clock[i].num_nodes); + if (ret) + continue; + + ret = versal_clock_get_parents(i, clock[i].parent, + &clock[i].num_parents); + if (ret) + continue; + } +} + +int versal_clock_setup(void) +{ + int ret; + + ret = versal_pm_clock_get_num_clocks(&clock_max_idx); + if (ret) + return ret; + + debug("%s, clock_max_idx:0x%x\n", __func__, clock_max_idx); + clock = calloc(clock_max_idx, sizeof(*clock)); + if (!clock) + return -ENOMEM; + + versal_get_clock_info(); + + return 0; +} + +static int versal_clock_get_freq_by_name(char *name, struct udevice *dev, + ulong *freq) +{ + struct clk clk; + int ret; + + ret = clk_get_by_name(dev, name, &clk); + if (ret < 0) { + dev_err(dev, "failed to get %s\n", name); + return ret; + } + + *freq = clk_get_rate(&clk); + if (IS_ERR_VALUE(*freq)) { + dev_err(dev, "failed to get rate %s\n", name); + return -EINVAL; + } + + return 0; +} + +static int versal_clk_probe(struct udevice *dev) +{ + int ret; + struct versal_clk_priv *priv = dev_get_priv(dev); + + debug("%s\n", __func__); + + ret = versal_clock_get_freq_by_name("alt_ref_clk", dev, &alt_ref_clk); + if (ret < 0) + return -EINVAL; + + ret = versal_clock_get_freq_by_name("pl_alt_ref_clk", + dev, &pl_alt_ref_clk); + if (ret < 0) + return -EINVAL; + + ret = versal_clock_get_freq_by_name("ref_clk", dev, &ref_clk); + if (ret < 0) + return -EINVAL; + + versal_clock_setup(); + + priv->clk = clock; + + return ret; +} + +static ulong versal_clk_get_rate(struct clk *clk) +{ + struct versal_clk_priv *priv = dev_get_priv(clk->dev); + u32 id = clk->id; + u32 clk_id; + u64 clk_rate = 0; + + debug("%s\n", __func__); + + clk_id = priv->clk[id].clk_id; + + versal_clock_get_rate(clk_id, &clk_rate); + + return clk_rate; +} + +static ulong versal_clk_set_rate(struct clk *clk, ulong rate) +{ + struct versal_clk_priv *priv = dev_get_priv(clk->dev); + u32 id = clk->id; + u32 clk_id; + u64 clk_rate = 0; + u32 div; + int ret; + + debug("%s\n", __func__); + + clk_id = priv->clk[id].clk_id; + + ret = versal_clock_get_rate(clk_id, &clk_rate); + if (ret) { + printf("Clock is not a Gate:0x%x\n", clk_id); + return 0; + } + + do { + if (versal_clock_div(clk_id)) { + div = versal_clock_get_div(clk_id); + clk_rate *= div; + div = DIV_ROUND_CLOSEST(clk_rate, rate); + versal_clock_set_div(clk_id, div); + debug("%s, div:%d, newrate:%lld\n", __func__, + div, DIV_ROUND_CLOSEST(clk_rate, div)); + return DIV_ROUND_CLOSEST(clk_rate, div); + } + clk_id = versal_clock_get_parentid(clk_id); + } while (((clk_id >> NODE_SUBCLASS_SHIFT) & + NODE_CLASS_MASK) != NODE_SUBCLASS_CLOCK_REF); + + printf("Clock didn't has Divisors:0x%x\n", priv->clk[id].clk_id); + + return clk_rate; +} + +static struct clk_ops versal_clk_ops = { + .set_rate = versal_clk_set_rate, + .get_rate = versal_clk_get_rate, +}; + +static const struct udevice_id versal_clk_ids[] = { + { .compatible = "xlnx,versal-clk" }, + { } +}; + +U_BOOT_DRIVER(versal_clk) = { + .name = "versal-clk", + .id = UCLASS_CLK, + .of_match = versal_clk_ids, + .probe = versal_clk_probe, + .ops = &versal_clk_ops, + .priv_auto_alloc_size = sizeof(struct versal_clk_priv), +}; diff --git a/drivers/core/device.c b/drivers/core/device.c index 84f0f0fbf0e..ce66c72e5ec 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -568,6 +568,17 @@ int device_get_child(struct udevice *parent, int index, struct udevice **devp) return -ENODEV; } +int device_get_child_count(struct udevice *parent) +{ + struct udevice *dev; + int count = 0; + + list_for_each_entry(dev, &parent->child_head, sibling_node) + count++; + + return count; +} + int device_find_child_by_seq(struct udevice *parent, int seq_or_req_seq, bool find_req_seq, struct udevice **devp) { diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 873bc8c7967..b70a2063551 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -26,3 +26,13 @@ config TI_SCI_PROTOCOL This protocol library is used by client drivers to use the features provided by the system controller. + +config ZYNQMP_FIRMWARE + bool "ZynqMP Firmware interface" + select FIRMWARE + help + Firmware interface driver is used by different + drivers to communicate with the firmware for + various platform management services. + Say yes to enable ZynqMP firmware interface driver. + If in doubt, say N. diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 6c3e1296852..a0c250a473e 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_FIRMWARE) += firmware-uclass.o obj-$(CONFIG_$(SPL_)ARM_PSCI_FW) += psci.o obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o obj-$(CONFIG_SANDBOX) += firmware-sandbox.o +obj-$(CONFIG_ZYNQMP_FIRMWARE) += firmware-zynqmp.o diff --git a/drivers/firmware/firmware-zynqmp.c b/drivers/firmware/firmware-zynqmp.c new file mode 100644 index 00000000000..15e82ac3b31 --- /dev/null +++ b/drivers/firmware/firmware-zynqmp.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx Zynq MPSoC Firmware driver + * + * Copyright (C) 2018-2019 Xilinx, Inc. + */ + +#include <common.h> +#include <dm.h> + +#if defined(CONFIG_ZYNQMP_IPI) +#include <mailbox.h> +#include <zynqmp_firmware.h> +#include <asm/arch/sys_proto.h> + +#define PMUFW_PAYLOAD_ARG_CNT 8 + +struct zynqmp_power { + struct mbox_chan tx_chan; + struct mbox_chan rx_chan; +} zynqmp_power; + +static int ipi_req(const u32 *req, size_t req_len, u32 *res, size_t res_maxlen) +{ + struct zynqmp_ipi_msg msg; + int ret; + + if (req_len > PMUFW_PAYLOAD_ARG_CNT || + res_maxlen > PMUFW_PAYLOAD_ARG_CNT) + return -EINVAL; + + if (!(zynqmp_power.tx_chan.dev) || !(&zynqmp_power.rx_chan.dev)) + return -EINVAL; + + msg.buf = (u32 *)req; + msg.len = req_len; + ret = mbox_send(&zynqmp_power.tx_chan, &msg); + if (ret) { + debug("%s: Sending message failed\n", __func__); + return ret; + } + + msg.buf = res; + msg.len = res_maxlen; + ret = mbox_recv(&zynqmp_power.rx_chan, &msg, 100); + if (ret) + debug("%s: Receiving message failed\n", __func__); + + return ret; +} + +static int send_req(const u32 *req, size_t req_len, u32 *res, size_t res_maxlen) +{ + if (IS_ENABLED(CONFIG_SPL_BUILD)) + return ipi_req(req, req_len, res, res_maxlen); + + return invoke_smc(req[0] + PM_SIP_SVC, 0, 0, 0, 0, res); +} + +unsigned int zynqmp_firmware_version(void) +{ + int ret; + u32 ret_payload[PAYLOAD_ARG_CNT]; + static u32 pm_api_version = ZYNQMP_PM_VERSION_INVALID; + + /* + * Get PMU version only once and later + * just return stored values instead of + * asking PMUFW again. + **/ + if (pm_api_version == ZYNQMP_PM_VERSION_INVALID) { + const u32 request[] = { PM_GET_API_VERSION }; + + ret = send_req(request, ARRAY_SIZE(request), ret_payload, 2); + if (ret) + panic("PMUFW is not found - Please load it!\n"); + + pm_api_version = ret_payload[1]; + if (pm_api_version < ZYNQMP_PM_VERSION) + panic("PMUFW version error. Expected: v%d.%d\n", + ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR); + } + + return pm_api_version; +}; + +/** + * Send a configuration object to the PMU firmware. + * + * @cfg_obj: Pointer to the configuration object + * @size: Size of @cfg_obj in bytes + */ +void zynqmp_pmufw_load_config_object(const void *cfg_obj, size_t size) +{ + const u32 request[] = { + PM_SET_CONFIGURATION, + (u32)((u64)cfg_obj) + }; + u32 response; + int err; + + printf("Loading new PMUFW cfg obj (%ld bytes)\n", size); + + err = send_req(request, ARRAY_SIZE(request), &response, 1); + if (err) + panic("Cannot load PMUFW configuration object (%d)\n", err); + if (response != 0) + panic("PMUFW returned 0x%08x status!\n", response); +} + +static int zynqmp_power_probe(struct udevice *dev) +{ + int ret = 0; + + debug("%s, (dev=%p)\n", __func__, dev); + + ret = mbox_get_by_name(dev, "tx", &zynqmp_power.tx_chan); + if (ret) { + debug("%s, cannot tx mailbox\n", __func__); + return ret; + } + + ret = mbox_get_by_name(dev, "rx", &zynqmp_power.rx_chan); + if (ret) { + debug("%s, cannot rx mailbox\n", __func__); + return ret; + } + + ret = zynqmp_firmware_version(); + printf("PMUFW:\tv%d.%d\n", + ret >> ZYNQMP_PM_VERSION_MAJOR_SHIFT, + ret & ZYNQMP_PM_VERSION_MINOR_MASK); + + return 0; +}; + +static const struct udevice_id zynqmp_power_ids[] = { + { .compatible = "xlnx,zynqmp-power" }, + { } +}; + +U_BOOT_DRIVER(zynqmp_power) = { + .name = "zynqmp_power", + .id = UCLASS_FIRMWARE, + .of_match = zynqmp_power_ids, + .probe = zynqmp_power_probe, +}; +#endif + +static const struct udevice_id zynqmp_firmware_ids[] = { + { .compatible = "xlnx,zynqmp-firmware" }, + { .compatible = "xlnx,versal-firmware"}, + { } +}; + +U_BOOT_DRIVER(zynqmp_firmware) = { + .id = UCLASS_FIRMWARE, + .name = "zynqmp-firmware", + .probe = dm_scan_fdt_dev, + .of_match = zynqmp_firmware_ids, +}; diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index 105a299812f..fe398a1d496 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -56,6 +56,15 @@ config FPGA_ZYNQMPPL Enable FPGA driver for loading bitstream in BIT and BIN format on Xilinx Zynq UltraScale+ (ZynqMP) device. +config FPGA_VERSALPL + bool "Enable Xilinx FPGA driver for Versal" + depends on FPGA_XILINX + help + Enable FPGA driver for loading bitstream in PDI format on Xilinx + Versal device. PDI is a new programmable device image format for + Versal. The bitstream will only be generated as PDI for Versal + platform. + config FPGA_SPARTAN3 bool "Enable Spartan3 FPGA driver" depends on FPGA_XILINX diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index 5a778c10e80..04e6480f202 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -6,6 +6,7 @@ obj-y += fpga.o obj-$(CONFIG_FPGA_SPARTAN2) += spartan2.o obj-$(CONFIG_FPGA_SPARTAN3) += spartan3.o +obj-$(CONFIG_FPGA_VERSALPL) += versalpl.o obj-$(CONFIG_FPGA_VIRTEX2) += virtex2.o obj-$(CONFIG_FPGA_ZYNQPL) += zynqpl.o obj-$(CONFIG_FPGA_ZYNQMPPL) += zynqmppl.o diff --git a/drivers/fpga/versalpl.c b/drivers/fpga/versalpl.c new file mode 100644 index 00000000000..69617a9b1d7 --- /dev/null +++ b/drivers/fpga/versalpl.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2019, Xilinx, Inc, + * Siva Durga Prasad Paladugu <siva.durga.paladugu@xilinx.com> + */ + +#include <common.h> +#include <asm/arch/sys_proto.h> +#include <memalign.h> +#include <versalpl.h> + +static ulong versal_align_dma_buffer(ulong *buf, u32 len) +{ + ulong *new_buf; + + if ((ulong)buf != ALIGN((ulong)buf, ARCH_DMA_MINALIGN)) { + new_buf = (ulong *)ALIGN((ulong)buf, ARCH_DMA_MINALIGN); + memcpy(new_buf, buf, len); + buf = new_buf; + } + + return (ulong)buf; +} + +static int versal_load(xilinx_desc *desc, const void *buf, size_t bsize, + bitstream_type bstype) +{ + ulong bin_buf; + int ret; + u32 buf_lo, buf_hi; + u32 ret_payload[5]; + + bin_buf = versal_align_dma_buffer((ulong *)buf, bsize); + + debug("%s called!\n", __func__); + flush_dcache_range(bin_buf, bin_buf + bsize); + + buf_lo = lower_32_bits(bin_buf); + buf_hi = upper_32_bits(bin_buf); + + ret = versal_pm_request(VERSAL_PM_LOAD_PDI, VERSAL_PM_PDI_TYPE, buf_lo, + buf_hi, 0, ret_payload); + if (ret) + puts("PL FPGA LOAD fail\n"); + + return ret; +} + +struct xilinx_fpga_op versal_op = { + .load = versal_load, +}; diff --git a/drivers/fpga/xilinx.c b/drivers/fpga/xilinx.c index f5135504eeb..4b0334b6beb 100644 --- a/drivers/fpga/xilinx.c +++ b/drivers/fpga/xilinx.c @@ -226,7 +226,10 @@ int xilinx_info(xilinx_desc *desc) case xilinx_zynqmp: printf("ZynqMP PL\n"); break; - /* Add new family types here */ + case xilinx_versal: + printf("Versal PL\n"); + break; + /* Add new family types here */ default: printf ("Unknown family type, %d\n", desc->family); } @@ -257,6 +260,9 @@ int xilinx_info(xilinx_desc *desc) case csu_dma: printf("csu_dma configuration interface (ZynqMP)\n"); break; + case cfi: + printf("CFI configuration interface (Versal)\n"); + break; /* Add new interface types here */ default: printf ("Unsupported interface type, %d\n", desc->iface); diff --git a/drivers/fpga/zynqmppl.c b/drivers/fpga/zynqmppl.c index 22bfdd8dce6..c2670271c8e 100644 --- a/drivers/fpga/zynqmppl.c +++ b/drivers/fpga/zynqmppl.c @@ -8,6 +8,7 @@ #include <console.h> #include <common.h> #include <zynqmppl.h> +#include <zynqmp_firmware.h> #include <linux/sizes.h> #include <asm/arch/sys_proto.h> #include <memalign.h> @@ -151,9 +152,9 @@ static ulong zynqmp_align_dma_buffer(u32 *buf, u32 len, u32 swap) buf = new_buf; } else if ((swap != SWAP_DONE) && - (zynqmp_pmufw_version() <= PMUFW_V1_0)) { + (zynqmp_firmware_version() <= PMUFW_V1_0)) { /* For bitstream which are aligned */ - u32 *new_buf = (u32 *)buf; + new_buf = buf; printf("%s: Bitstream is not swapped(%d) - swap it\n", __func__, swap); @@ -204,7 +205,7 @@ static int zynqmp_load(xilinx_desc *desc, const void *buf, size_t bsize, u32 ret_payload[PAYLOAD_ARG_CNT]; bool xilfpga_old = false; - if (zynqmp_pmufw_version() <= PMUFW_V1_0) { + if (zynqmp_firmware_version() <= PMUFW_V1_0) { puts("WARN: PMUFW v1.0 or less is detected\n"); puts("WARN: Not all bitstream formats are supported\n"); puts("WARN: Please upgrade PMUFW\n"); diff --git a/drivers/gpio/zynq_gpio.c b/drivers/gpio/zynq_gpio.c index 55a5cba0688..a760c5bdda6 100644 --- a/drivers/gpio/zynq_gpio.c +++ b/drivers/gpio/zynq_gpio.c @@ -292,7 +292,7 @@ static int zynq_gpio_direction_output(struct udevice *dev, unsigned gpio, writel(reg, platdata->base + ZYNQ_GPIO_OUTEN_OFFSET(bank_num)); /* set the state of the pin */ - gpio_set_value(gpio, value); + zynq_gpio_set_value(dev, gpio, value); return 0; } diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 11bf5522db5..85c2a829aed 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -41,4 +41,10 @@ config K3_SEC_PROXY Select this driver if your platform has support for this hardware block. +config ZYNQMP_IPI + bool "Xilinx ZynqMP IPI controller support" + depends on DM_MAILBOX && ARCH_ZYNQMP + help + This enables support for the Xilinx ZynqMP Inter Processor Interrupt + communication controller. endmenu diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index a753cc4e680..d2ace8cd212 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox-test.o obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o obj-$(CONFIG_TEGRA_HSP) += tegra-hsp.o obj-$(CONFIG_K3_SEC_PROXY) += k3-sec-proxy.o +obj-$(CONFIG_ZYNQMP_IPI) += zynqmp-ipi.o diff --git a/drivers/mailbox/mailbox-uclass.c b/drivers/mailbox/mailbox-uclass.c index 1b4a5863c9e..9fdb6279e4f 100644 --- a/drivers/mailbox/mailbox-uclass.c +++ b/drivers/mailbox/mailbox-uclass.c @@ -49,7 +49,16 @@ int mbox_get_by_index(struct udevice *dev, int index, struct mbox_chan *chan) if (ret) { debug("%s: uclass_get_device_by_of_offset failed: %d\n", __func__, ret); - return ret; + + /* Test with parent node */ + ret = uclass_get_device_by_ofnode(UCLASS_MAILBOX, + ofnode_get_parent(args.node), + &dev_mbox); + if (ret) { + debug("%s: mbox node from parent failed: %d\n", + __func__, ret); + return ret; + }; } ops = mbox_dev_ops(dev_mbox); @@ -63,7 +72,8 @@ int mbox_get_by_index(struct udevice *dev, int index, struct mbox_chan *chan) return ret; } - ret = ops->request(chan); + if (ops->request) + ret = ops->request(chan); if (ret) { debug("ops->request() failed: %d\n", ret); return ret; @@ -94,7 +104,10 @@ int mbox_free(struct mbox_chan *chan) debug("%s(chan=%p)\n", __func__, chan); - return ops->free(chan); + if (ops->free) + return ops->free(chan); + + return 0; } int mbox_send(struct mbox_chan *chan, const void *data) diff --git a/drivers/mailbox/zynqmp-ipi.c b/drivers/mailbox/zynqmp-ipi.c new file mode 100644 index 00000000000..c181a7b8176 --- /dev/null +++ b/drivers/mailbox/zynqmp-ipi.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Xilinx Zynq MPSoC Mailbox driver + * + * Copyright (C) 2018-2019 Xilinx, Inc. + */ + +#include <common.h> +#include <asm/io.h> +#include <dm.h> +#include <mailbox-uclass.h> +#include <mach/sys_proto.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <wait_bit.h> + +/* IPI bitmasks, register base */ +/* TODO: move reg base to DT */ +#define IPI_BIT_MASK_PMU0 0x10000 +#define IPI_INT_REG_BASE_APU 0xFF300000 + +struct ipi_int_regs { + u32 trig; /* 0x0 */ + u32 obs; /* 0x4 */ + u32 ist; /* 0x8 */ + u32 imr; /* 0xC */ + u32 ier; /* 0x10 */ + u32 idr; /* 0x14 */ +}; + +#define ipi_int_apu ((struct ipi_int_regs *)IPI_INT_REG_BASE_APU) + +struct zynqmp_ipi { + void __iomem *local_req_regs; + void __iomem *local_res_regs; + void __iomem *remote_req_regs; + void __iomem *remote_res_regs; +}; + +static int zynqmp_ipi_send(struct mbox_chan *chan, const void *data) +{ + const struct zynqmp_ipi_msg *msg = (struct zynqmp_ipi_msg *)data; + struct zynqmp_ipi *zynqmp = dev_get_priv(chan->dev); + u32 ret; + u32 *mbx = (u32 *)zynqmp->local_req_regs; + + for (size_t i = 0; i < msg->len; i++) + writel(msg->buf[i], &mbx[i]); + + /* Write trigger interrupt */ + writel(IPI_BIT_MASK_PMU0, &ipi_int_apu->trig); + + /* Wait until observation bit is cleared */ + ret = wait_for_bit_le32(&ipi_int_apu->obs, IPI_BIT_MASK_PMU0, false, + 100, false); + + debug("%s, send %ld bytes\n", __func__, msg->len); + return ret; +}; + +static int zynqmp_ipi_recv(struct mbox_chan *chan, void *data) +{ + struct zynqmp_ipi_msg *msg = (struct zynqmp_ipi_msg *)data; + struct zynqmp_ipi *zynqmp = dev_get_priv(chan->dev); + u32 *mbx = (u32 *)zynqmp->local_res_regs; + + for (size_t i = 0; i < msg->len; i++) + msg->buf[i] = readl(&mbx[i]); + + debug("%s, recv %ld bytes\n", __func__, msg->len); + return 0; +}; + +static int zynqmp_ipi_probe(struct udevice *dev) +{ + struct zynqmp_ipi *zynqmp = dev_get_priv(dev); + struct resource res; + ofnode node; + + debug("%s(dev=%p)\n", __func__, dev); + + /* Get subnode where the regs are defined */ + /* Note IPI mailbox node needs to be the first one in DT */ + node = ofnode_first_subnode(dev_ofnode(dev)); + + if (ofnode_read_resource_byname(node, "local_request_region", &res)) { + dev_err(dev, "No reg property for local_request_region\n"); + return -EINVAL; + }; + zynqmp->local_req_regs = devm_ioremap(dev, res.start, + (res.start - res.end)); + + if (ofnode_read_resource_byname(node, "local_response_region", &res)) { + dev_err(dev, "No reg property for local_response_region\n"); + return -EINVAL; + }; + zynqmp->local_res_regs = devm_ioremap(dev, res.start, + (res.start - res.end)); + + if (ofnode_read_resource_byname(node, "remote_request_region", &res)) { + dev_err(dev, "No reg property for remote_request_region\n"); + return -EINVAL; + }; + zynqmp->remote_req_regs = devm_ioremap(dev, res.start, + (res.start - res.end)); + + if (ofnode_read_resource_byname(node, "remote_response_region", &res)) { + dev_err(dev, "No reg property for remote_response_region\n"); + return -EINVAL; + }; + zynqmp->remote_res_regs = devm_ioremap(dev, res.start, + (res.start - res.end)); + + return 0; +}; + +static const struct udevice_id zynqmp_ipi_ids[] = { + { .compatible = "xlnx,zynqmp-ipi-mailbox" }, + { } +}; + +struct mbox_ops zynqmp_ipi_mbox_ops = { + .send = zynqmp_ipi_send, + .recv = zynqmp_ipi_recv, +}; + +U_BOOT_DRIVER(zynqmp_ipi) = { + .name = "zynqmp-ipi", + .id = UCLASS_MAILBOX, + .of_match = zynqmp_ipi_ids, + .probe = zynqmp_ipi_probe, + .priv_auto_alloc_size = sizeof(struct zynqmp_ipi), + .ops = &zynqmp_ipi_mbox_ops, +}; diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index d7f8427b184..18af0762a88 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -645,6 +645,12 @@ config ZYNQ_SDHCI_MIN_FREQ help Set the minimum frequency of the controller. +config ZYNQ_HISPD_BROKEN + bool "High speed broken for Zynq SDHCI controller" + depends on MMC_SDHCI_ZYNQ + help + Set if high speed mode is broken. + config MMC_SUNXI bool "Allwinner sunxi SD/MMC Host Controller support" depends on ARCH_SUNXI && !UART0_PORT_F diff --git a/drivers/mmc/am654_sdhci.c b/drivers/mmc/am654_sdhci.c index 1793a3f99a5..7cd5516197c 100644 --- a/drivers/mmc/am654_sdhci.c +++ b/drivers/mmc/am654_sdhci.c @@ -219,23 +219,10 @@ static int am654_sdhci_probe(struct udevice *dev) struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct sdhci_host *host = dev_get_priv(dev); struct mmc_config *cfg = &plat->cfg; - struct power_domain sdhci_pwrdmn; struct clk clk; unsigned long clock; int ret; - ret = power_domain_get_by_index(dev, &sdhci_pwrdmn, 0); - if (!ret) { - ret = power_domain_on(&sdhci_pwrdmn); - if (ret) { - dev_err(dev, "Power domain on failed (%d)\n", ret); - return ret; - } - } else if (ret != -ENOENT && ret != -ENODEV && ret != -ENOSYS) { - dev_err(dev, "failed to get power domain (%d)\n", ret); - return ret; - } - ret = clk_get_by_index(dev, 0, &clk); if (ret) { dev_err(dev, "failed to get clock\n"); diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c index 3225a7ac938..529eec9c45c 100644 --- a/drivers/mmc/zynq_sdhci.c +++ b/drivers/mmc/zynq_sdhci.c @@ -190,7 +190,7 @@ static void arasan_sdhci_set_control_reg(struct sdhci_host *host) } #endif -#if defined(CONFIG_DM_MMC) && defined(CONFIG_ARCH_ZYNQMP) +#if defined(CONFIG_ARCH_ZYNQMP) const struct sdhci_ops arasan_ops = { .platform_execute_tuning = &arasan_sdhci_execute_tuning, .set_delay = &arasan_sdhci_set_tapdelay, @@ -266,7 +266,7 @@ static int arasan_sdhci_ofdata_to_platdata(struct udevice *dev) priv->host->name = dev->name; -#if defined(CONFIG_DM_MMC) && defined(CONFIG_ARCH_ZYNQMP) +#if defined(CONFIG_ARCH_ZYNQMP) priv->host->ops = &arasan_ops; #endif diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 2a3da068c90..30bd8e76530 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -228,6 +228,13 @@ config PHY_VITESSE config PHY_XILINX bool "Xilinx Ethernet PHYs support" +config PHY_XILINX_GMII2RGMII + bool "Xilinx GMII to RGMII Ethernet PHYs support" + help + This adds support for Xilinx GMII to RGMII IP core. This IP acts + as bridge between MAC connected over GMII and external phy that + is connected over RGMII interface. + config PHY_FIXED bool "Fixed-Link PHY" depends on DM_ETH diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 555da83630f..76b6197009b 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_PHY_SMSC) += smsc.o obj-$(CONFIG_PHY_TERANETICS) += teranetics.o obj-$(CONFIG_PHY_TI) += ti.o obj-$(CONFIG_PHY_XILINX) += xilinx_phy.o +obj-$(CONFIG_PHY_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o obj-$(CONFIG_PHY_VITESSE) += vitesse.o obj-$(CONFIG_PHY_MSCC) += mscc.o obj-$(CONFIG_PHY_FIXED) += fixed.o diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index ae37dd6c1e3..f2d17aa91a0 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -458,6 +458,11 @@ static struct phy_driver genphy_driver = { .shutdown = genphy_shutdown, }; +int genphy_init(void) +{ + return phy_register(&genphy_driver); +} + static LIST_HEAD(phy_drivers); int phy_init(void) @@ -540,6 +545,11 @@ int phy_init(void) #ifdef CONFIG_PHY_FIXED phy_fixed_init(); #endif +#ifdef CONFIG_PHY_XILINX_GMII2RGMII + phy_xilinx_gmii2rgmii_init(); +#endif + genphy_init(); + return 0; } @@ -911,6 +921,41 @@ void phy_connect_dev(struct phy_device *phydev, struct eth_device *dev) debug("%s connected to %s\n", dev->name, phydev->drv->name); } +#ifdef CONFIG_PHY_XILINX_GMII2RGMII +#ifdef CONFIG_DM_ETH +static struct phy_device *phy_connect_gmii2rgmii(struct mii_dev *bus, + struct udevice *dev, + phy_interface_t interface) +#else +static struct phy_device *phy_connect_gmii2rgmii(struct mii_dev *bus, + struct eth_device *dev, + phy_interface_t interface) +#endif +{ + struct phy_device *phydev = NULL; + int sn = dev_of_offset(dev); + int off; + + while (sn > 0) { + off = fdt_node_offset_by_compatible(gd->fdt_blob, sn, + "xlnx,gmii-to-rgmii-1.0"); + if (off > 0) { + phydev = phy_device_create(bus, off, + PHY_GMII2RGMII_ID, false, + interface); + break; + } + if (off == -FDT_ERR_NOTFOUND) + sn = fdt_first_subnode(gd->fdt_blob, sn); + else + printf("%s: Error finding compat string:%d\n", + __func__, off); + } + + return phydev; +} +#endif + #ifdef CONFIG_PHY_FIXED #ifdef CONFIG_DM_ETH static struct phy_device *phy_connect_fixed(struct mii_dev *bus, @@ -957,6 +1002,10 @@ struct phy_device *phy_connect(struct mii_dev *bus, int addr, #ifdef CONFIG_PHY_FIXED phydev = phy_connect_fixed(bus, dev, interface); #endif +#ifdef CONFIG_PHY_XILINX_GMII2RGMII + if (!phydev) + phydev = phy_connect_gmii2rgmii(bus, dev, interface); +#endif if (!phydev) phydev = phy_find_by_mask(bus, mask, interface); diff --git a/drivers/net/phy/xilinx_gmii2rgmii.c b/drivers/net/phy/xilinx_gmii2rgmii.c new file mode 100644 index 00000000000..8c20da26820 --- /dev/null +++ b/drivers/net/phy/xilinx_gmii2rgmii.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx GMII2RGMII phy driver + * + * Copyright (C) 2018 Xilinx, Inc. + */ + +#include <dm.h> +#include <phy.h> +#include <config.h> +#include <common.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define ZYNQ_GMII2RGMII_REG 0x10 +#define ZYNQ_GMII2RGMII_SPEED_MASK (BMCR_SPEED1000 | BMCR_SPEED100) + +static int xilinxgmiitorgmii_config(struct phy_device *phydev) +{ + struct phy_device *ext_phydev = phydev->priv; + + debug("%s\n", __func__); + if (ext_phydev->drv->config) + ext_phydev->drv->config(ext_phydev); + + return 0; +} + +static int xilinxgmiitorgmii_extread(struct phy_device *phydev, int addr, + int devaddr, int regnum) +{ + struct phy_device *ext_phydev = phydev->priv; + + debug("%s\n", __func__); + if (ext_phydev->drv->readext) + ext_phydev->drv->readext(ext_phydev, addr, devaddr, regnum); + + return 0; +} + +static int xilinxgmiitorgmii_extwrite(struct phy_device *phydev, int addr, + int devaddr, int regnum, u16 val) + +{ + struct phy_device *ext_phydev = phydev->priv; + + debug("%s\n", __func__); + if (ext_phydev->drv->writeext) + ext_phydev->drv->writeext(ext_phydev, addr, devaddr, regnum, + val); + + return 0; +} + +static int xilinxgmiitorgmii_startup(struct phy_device *phydev) +{ + u16 val = 0; + struct phy_device *ext_phydev = phydev->priv; + + debug("%s\n", __func__); + ext_phydev->dev = phydev->dev; + if (ext_phydev->drv->startup) + ext_phydev->drv->startup(ext_phydev); + + val = phy_read(phydev, phydev->addr, ZYNQ_GMII2RGMII_REG); + val &= ~ZYNQ_GMII2RGMII_SPEED_MASK; + + if (ext_phydev->speed == SPEED_1000) + val |= BMCR_SPEED1000; + else if (ext_phydev->speed == SPEED_100) + val |= BMCR_SPEED100; + + phy_write(phydev, phydev->addr, ZYNQ_GMII2RGMII_REG, val | + BMCR_FULLDPLX); + + phydev->duplex = ext_phydev->duplex; + phydev->speed = ext_phydev->speed; + phydev->link = ext_phydev->link; + + return 0; +} + +static int xilinxgmiitorgmii_probe(struct phy_device *phydev) +{ + int ofnode = phydev->addr; + u32 phy_of_handle; + int ext_phyaddr = -1; + struct phy_device *ext_phydev; + + debug("%s\n", __func__); + + if (phydev->interface != PHY_INTERFACE_MODE_GMII) { + printf("Incorrect interface type\n"); + return -EINVAL; + } + + /* + * Read the phy address again as the one we read in ethernet driver + * was overwritten for the purpose of storing the ofnode + */ + phydev->addr = fdtdec_get_int(gd->fdt_blob, ofnode, "reg", -1); + phy_of_handle = fdtdec_lookup_phandle(gd->fdt_blob, ofnode, + "phy-handle"); + if (phy_of_handle > 0) + ext_phyaddr = fdtdec_get_int(gd->fdt_blob, + phy_of_handle, + "reg", -1); + ext_phydev = phy_find_by_mask(phydev->bus, + 1 << ext_phyaddr, + PHY_INTERFACE_MODE_RGMII); + if (!ext_phydev) { + printf("%s, No external phy device found\n", __func__); + return -EINVAL; + } + + ext_phydev->node = offset_to_ofnode(phy_of_handle); + phydev->priv = ext_phydev; + + debug("%s, gmii2rgmmi:0x%x, extphy:0x%x\n", __func__, phydev->addr, + ext_phyaddr); + + phydev->flags |= PHY_FLAG_BROKEN_RESET; + + return 0; +} + +static struct phy_driver gmii2rgmii_driver = { + .name = "XILINX GMII2RGMII", + .uid = PHY_GMII2RGMII_ID, + .mask = 0xffffffff, + .features = PHY_GBIT_FEATURES, + .probe = xilinxgmiitorgmii_probe, + .config = xilinxgmiitorgmii_config, + .startup = xilinxgmiitorgmii_startup, + .writeext = xilinxgmiitorgmii_extwrite, + .readext = xilinxgmiitorgmii_extread, +}; + +int phy_xilinx_gmii2rgmii_init(void) +{ + phy_register(&gmii2rgmii_driver); + + return 0; +} diff --git a/drivers/net/xilinx_axi_emac.c b/drivers/net/xilinx_axi_emac.c index 26c21c6d70f..36d651109cb 100644 --- a/drivers/net/xilinx_axi_emac.c +++ b/drivers/net/xilinx_axi_emac.c @@ -93,6 +93,7 @@ struct axidma_priv { struct phy_device *phydev; struct mii_dev *bus; u8 eth_hasnobuf; + int phy_of_handle; }; /* BD descriptors */ @@ -276,6 +277,8 @@ static int axiemac_phy_init(struct udevice *dev) phydev->supported &= supported; phydev->advertising = phydev->supported; priv->phydev = phydev; + if (priv->phy_of_handle) + priv->phydev->node = offset_to_ofnode(priv->phy_of_handle); phy_config(phydev); return 0; @@ -736,8 +739,10 @@ static int axi_emac_ofdata_to_platdata(struct udevice *dev) priv->phyaddr = -1; offset = fdtdec_lookup_phandle(gd->fdt_blob, node, "phy-handle"); - if (offset > 0) + if (offset > 0) { priv->phyaddr = fdtdec_get_int(gd->fdt_blob, offset, "reg", -1); + priv->phy_of_handle = offset; + } phy_mode = fdt_getprop(gd->fdt_blob, node, "phy-mode", NULL); if (phy_mode) diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index 033efb81956..a7a6ce987f0 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -26,8 +26,6 @@ #include <asm/arch/sys_proto.h> #include <linux/errno.h> -DECLARE_GLOBAL_DATA_PTR; - /* Bit/mask specification */ #define ZYNQ_GEM_PHYMNTNC_OP_MASK 0x40020000 /* operation mask bits */ #define ZYNQ_GEM_PHYMNTNC_OP_R_MASK 0x20000000 /* read operation */ @@ -465,7 +463,6 @@ static int zynq_gem_init(struct udevice *dev) break; } -#if !defined(CONFIG_ARCH_VERSAL) ret = clk_set_rate(&priv->clk, clk_rate); if (IS_ERR_VALUE(ret) && ret != (unsigned long)-ENOSYS) { dev_err(dev, "failed to set tx clock rate\n"); @@ -477,9 +474,6 @@ static int zynq_gem_init(struct udevice *dev) dev_err(dev, "failed to enable tx clock\n"); return ret; } -#else - debug("requested clk_rate %ld\n", clk_rate); -#endif setbits_le32(®s->nwctrl, ZYNQ_GEM_NWCTRL_RXEN_MASK | ZYNQ_GEM_NWCTRL_TXEN_MASK); @@ -753,6 +747,7 @@ static int zynq_gem_ofdata_to_platdata(struct udevice *dev) } static const struct udevice_id zynq_gem_ids[] = { + { .compatible = "cdns,versal-gem" }, { .compatible = "cdns,zynqmp-gem" }, { .compatible = "cdns,zynq-gem" }, { .compatible = "cdns,gem" }, diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index bdfc0c17963..19e7b50046a 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -151,4 +151,12 @@ config PCI_KEYSTONE help Say Y here if you want to enable PCI controller support on AM654 SoC. +config PCIE_MEDIATEK + bool "MediaTek PCIe Gen2 controller" + depends on DM_PCI + depends on ARCH_MEDIATEK + help + Say Y here if you want to enable Gen2 PCIe controller, + which could be found on MT7623 SoC family. + endif diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index e54a98b8c9c..b1d3dc8610b 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -39,3 +39,4 @@ obj-$(CONFIG_PCIE_LAYERSCAPE_GEN4) += pcie_layerscape_gen4.o \ obj-$(CONFIG_PCI_XILINX) += pcie_xilinx.o obj-$(CONFIG_PCIE_INTEL_FPGA) += pcie_intel_fpga.o obj-$(CONFIG_PCI_KEYSTONE) += pcie_dw_ti.o +obj-$(CONFIG_PCIE_MEDIATEK) += pcie_mediatek.o diff --git a/drivers/pci/pci-emul-uclass.c b/drivers/pci/pci-emul-uclass.c index 0dcf937d9a6..0f63e491a79 100644 --- a/drivers/pci/pci-emul-uclass.c +++ b/drivers/pci/pci-emul-uclass.c @@ -18,6 +18,7 @@ struct sandbox_pci_emul_priv { int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn, struct udevice **containerp, struct udevice **emulp) { + struct pci_emul_uc_priv *upriv; struct udevice *dev; int ret; @@ -30,16 +31,32 @@ int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn, } *containerp = dev; - /* - * See commit 4345998ae9df, - * "pci: sandbox: Support dynamically binding device driver" - */ ret = uclass_get_device_by_phandle(UCLASS_PCI_EMUL, dev, "sandbox,emul", emulp); - if (ret && device_get_uclass_id(dev) != UCLASS_PCI_GENERIC) + if (!ret) { + upriv = dev_get_uclass_priv(*emulp); + + upriv->client = dev; + } else if (device_get_uclass_id(dev) != UCLASS_PCI_GENERIC) { + /* + * See commit 4345998ae9df, + * "pci: sandbox: Support dynamically binding device driver" + */ *emulp = dev; + } + + return 0; +} - return *emulp ? 0 : -ENODEV; +int sandbox_pci_get_client(struct udevice *emul, struct udevice **devp) +{ + struct pci_emul_uc_priv *upriv = dev_get_uclass_priv(emul); + + if (!upriv->client) + return -ENOENT; + *devp = upriv->client; + + return 0; } uint sandbox_pci_read_bar(u32 barval, int type, uint size) @@ -88,6 +105,7 @@ UCLASS_DRIVER(pci_emul) = { .post_probe = sandbox_pci_emul_post_probe, .pre_remove = sandbox_pci_emul_pre_remove, .priv_auto_alloc_size = sizeof(struct sandbox_pci_emul_priv), + .per_device_auto_alloc_size = sizeof(struct pci_emul_uc_priv), }; /* diff --git a/drivers/pci/pcie_mediatek.c b/drivers/pci/pcie_mediatek.c new file mode 100644 index 00000000000..a0dcb258b02 --- /dev/null +++ b/drivers/pci/pcie_mediatek.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MediaTek PCIe host controller driver. + * + * Copyright (c) 2017-2019 MediaTek Inc. + * Author: Ryder Lee <ryder.lee@mediatek.com> + * Honghui Zhang <honghui.zhang@mediatek.com> + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <generic-phy.h> +#include <pci.h> +#include <reset.h> +#include <asm/io.h> +#include <linux/iopoll.h> +#include <linux/list.h> + +/* PCIe shared registers */ +#define PCIE_SYS_CFG 0x00 +#define PCIE_INT_ENABLE 0x0c +#define PCIE_CFG_ADDR 0x20 +#define PCIE_CFG_DATA 0x24 + +/* PCIe per port registers */ +#define PCIE_BAR0_SETUP 0x10 +#define PCIE_CLASS 0x34 +#define PCIE_LINK_STATUS 0x50 + +#define PCIE_PORT_INT_EN(x) BIT(20 + (x)) +#define PCIE_PORT_PERST(x) BIT(1 + (x)) +#define PCIE_PORT_LINKUP BIT(0) +#define PCIE_BAR_MAP_MAX GENMASK(31, 16) + +#define PCIE_BAR_ENABLE BIT(0) +#define PCIE_REVISION_ID BIT(0) +#define PCIE_CLASS_CODE (0x60400 << 8) +#define PCIE_CONF_REG(regn) (((regn) & GENMASK(7, 2)) | \ + ((((regn) >> 8) & GENMASK(3, 0)) << 24)) +#define PCIE_CONF_ADDR(regn, bdf) \ + (PCIE_CONF_REG(regn) | (bdf)) + +/* MediaTek specific configuration registers */ +#define PCIE_FTS_NUM 0x70c +#define PCIE_FTS_NUM_MASK GENMASK(15, 8) +#define PCIE_FTS_NUM_L0(x) ((x) & 0xff << 8) + +#define PCIE_FC_CREDIT 0x73c +#define PCIE_FC_CREDIT_MASK (GENMASK(31, 31) | GENMASK(28, 16)) +#define PCIE_FC_CREDIT_VAL(x) ((x) << 16) + +struct mtk_pcie_port { + void __iomem *base; + struct list_head list; + struct mtk_pcie *pcie; + struct reset_ctl reset; + struct clk sys_ck; + struct phy phy; + u32 slot; +}; + +struct mtk_pcie { + void __iomem *base; + struct clk free_ck; + struct list_head ports; +}; + +static int mtk_pcie_config_address(struct udevice *udev, pci_dev_t bdf, + uint offset, void **paddress) +{ + struct mtk_pcie *pcie = dev_get_priv(udev); + + writel(PCIE_CONF_ADDR(offset, bdf), pcie->base + PCIE_CFG_ADDR); + *paddress = pcie->base + PCIE_CFG_DATA + (offset & 3); + + return 0; +} + +static int mtk_pcie_read_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong *valuep, + enum pci_size_t size) +{ + return pci_generic_mmap_read_config(bus, mtk_pcie_config_address, + bdf, offset, valuep, size); +} + +static int mtk_pcie_write_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong value, + enum pci_size_t size) +{ + return pci_generic_mmap_write_config(bus, mtk_pcie_config_address, + bdf, offset, value, size); +} + +static const struct dm_pci_ops mtk_pcie_ops = { + .read_config = mtk_pcie_read_config, + .write_config = mtk_pcie_write_config, +}; + +static void mtk_pcie_port_free(struct mtk_pcie_port *port) +{ + list_del(&port->list); + free(port); +} + +static int mtk_pcie_startup_port(struct mtk_pcie_port *port) +{ + struct mtk_pcie *pcie = port->pcie; + u32 slot = PCI_DEV(port->slot << 11); + u32 val; + int err; + + /* assert port PERST_N */ + setbits_le32(pcie->base + PCIE_SYS_CFG, PCIE_PORT_PERST(port->slot)); + /* de-assert port PERST_N */ + clrbits_le32(pcie->base + PCIE_SYS_CFG, PCIE_PORT_PERST(port->slot)); + + /* 100ms timeout value should be enough for Gen1/2 training */ + err = readl_poll_timeout(port->base + PCIE_LINK_STATUS, val, + !!(val & PCIE_PORT_LINKUP), 100000); + if (err) + return -ETIMEDOUT; + + /* disable interrupt */ + clrbits_le32(pcie->base + PCIE_INT_ENABLE, + PCIE_PORT_INT_EN(port->slot)); + + /* map to all DDR region. We need to set it before cfg operation. */ + writel(PCIE_BAR_MAP_MAX | PCIE_BAR_ENABLE, + port->base + PCIE_BAR0_SETUP); + + /* configure class code and revision ID */ + writel(PCIE_CLASS_CODE | PCIE_REVISION_ID, port->base + PCIE_CLASS); + + /* configure FC credit */ + writel(PCIE_CONF_ADDR(PCIE_FC_CREDIT, slot), + pcie->base + PCIE_CFG_ADDR); + clrsetbits_le32(pcie->base + PCIE_CFG_DATA, PCIE_FC_CREDIT_MASK, + PCIE_FC_CREDIT_VAL(0x806c)); + + /* configure RC FTS number to 250 when it leaves L0s */ + writel(PCIE_CONF_ADDR(PCIE_FTS_NUM, slot), pcie->base + PCIE_CFG_ADDR); + clrsetbits_le32(pcie->base + PCIE_CFG_DATA, PCIE_FTS_NUM_MASK, + PCIE_FTS_NUM_L0(0x50)); + + return 0; +} + +static void mtk_pcie_enable_port(struct mtk_pcie_port *port) +{ + int err; + + err = clk_enable(&port->sys_ck); + if (err) + goto exit; + + err = reset_assert(&port->reset); + if (err) + goto exit; + + err = reset_deassert(&port->reset); + if (err) + goto exit; + + err = generic_phy_init(&port->phy); + if (err) + goto exit; + + err = generic_phy_power_on(&port->phy); + if (err) + goto exit; + + if (!mtk_pcie_startup_port(port)) + return; + + pr_err("Port%d link down\n", port->slot); +exit: + mtk_pcie_port_free(port); +} + +static int mtk_pcie_parse_port(struct udevice *dev, u32 slot) +{ + struct mtk_pcie *pcie = dev_get_priv(dev); + struct mtk_pcie_port *port; + char name[10]; + int err; + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + snprintf(name, sizeof(name), "port%d", slot); + port->base = dev_remap_addr_name(dev, name); + if (!port->base) + return -ENOENT; + + snprintf(name, sizeof(name), "sys_ck%d", slot); + err = clk_get_by_name(dev, name, &port->sys_ck); + if (err) + return err; + + err = reset_get_by_index(dev, slot, &port->reset); + if (err) + return err; + + err = generic_phy_get_by_index(dev, slot, &port->phy); + if (err) + return err; + + port->slot = slot; + port->pcie = pcie; + + INIT_LIST_HEAD(&port->list); + list_add_tail(&port->list, &pcie->ports); + + return 0; +} + +static int mtk_pcie_probe(struct udevice *dev) +{ + struct mtk_pcie *pcie = dev_get_priv(dev); + struct mtk_pcie_port *port, *tmp; + ofnode subnode; + int err; + + INIT_LIST_HEAD(&pcie->ports); + + pcie->base = dev_remap_addr_name(dev, "subsys"); + if (!pcie->base) + return -ENOENT; + + err = clk_get_by_name(dev, "free_ck", &pcie->free_ck); + if (err) + return err; + + /* enable top level clock */ + err = clk_enable(&pcie->free_ck); + if (err) + return err; + + dev_for_each_subnode(subnode, dev) { + struct fdt_pci_addr addr; + u32 slot = 0; + + if (!ofnode_is_available(subnode)) + continue; + + err = ofnode_read_pci_addr(subnode, 0, "reg", &addr); + if (err) + return err; + + slot = PCI_DEV(addr.phys_hi); + + err = mtk_pcie_parse_port(dev, slot); + if (err) + return err; + } + + /* enable each port, and then check link status */ + list_for_each_entry_safe(port, tmp, &pcie->ports, list) + mtk_pcie_enable_port(port); + + return 0; +} + +static const struct udevice_id mtk_pcie_ids[] = { + { .compatible = "mediatek,mt7623-pcie", }, + { } +}; + +U_BOOT_DRIVER(pcie_mediatek) = { + .name = "pcie_mediatek", + .id = UCLASS_PCI, + .of_match = mtk_pcie_ids, + .ops = &mtk_pcie_ops, + .probe = mtk_pcie_probe, + .priv_auto_alloc_size = sizeof(struct mtk_pcie), +}; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 02312273e27..e317373a5c2 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -205,4 +205,15 @@ config MT76X8_USB_PHY This PHY is found on MT76x8 devices supporting USB. +config PHY_MTK_TPHY + bool "MediaTek T-PHY Driver" + depends on PHY + depends on ARCH_MEDIATEK + help + MediaTek T-PHY driver supports usb2.0, usb3.0 ports, PCIe and + SATA, and meanwhile supports two version T-PHY which have + different banks layout, the T-PHY with shared banks between + multi-ports is first version, otherwise is second veriosn, + so you can easily distinguish them by banks layout. + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 3157f1b7ee5..43ce62e08cd 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -23,3 +23,4 @@ obj-$(CONFIG_OMAP_USB2_PHY) += omap-usb2-phy.o obj-$(CONFIG_KEYSTONE_USB_PHY) += keystone-usb-phy.o obj-$(CONFIG_MT76X8_USB_PHY) += mt76x8-usb-phy.o obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o +obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o diff --git a/drivers/phy/phy-mtk-tphy.c b/drivers/phy/phy-mtk-tphy.c new file mode 100644 index 00000000000..37014812567 --- /dev/null +++ b/drivers/phy/phy-mtk-tphy.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2015 - 2019 MediaTek Inc. + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> + * Ryder Lee <ryder.lee@mediatek.com> + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <generic-phy.h> +#include <mapmem.h> +#include <asm/io.h> + +#include <dt-bindings/phy/phy.h> + +/* version V1 sub-banks offset base address */ +/* banks shared by multiple phys */ +#define SSUSB_SIFSLV_V1_SPLLC 0x000 /* shared by u3 phys */ +#define SSUSB_SIFSLV_V1_CHIP 0x300 /* shared by u3 phys */ +/* u3/pcie/sata phy banks */ +#define SSUSB_SIFSLV_V1_U3PHYD 0x000 +#define SSUSB_SIFSLV_V1_U3PHYA 0x200 + +#define U3P_U3_CHIP_GPIO_CTLD 0x0c +#define P3C_REG_IP_SW_RST BIT(31) +#define P3C_MCU_BUS_CK_GATE_EN BIT(30) +#define P3C_FORCE_IP_SW_RST BIT(29) + +#define U3P_U3_CHIP_GPIO_CTLE 0x10 +#define P3C_RG_SWRST_U3_PHYD BIT(25) +#define P3C_RG_SWRST_U3_PHYD_FORCE_EN BIT(24) + +#define U3P_U3_PHYA_REG0 0x000 +#define P3A_RG_CLKDRV_OFF GENMASK(3, 2) +#define P3A_RG_CLKDRV_OFF_VAL(x) ((0x3 & (x)) << 2) + +#define U3P_U3_PHYA_REG1 0x004 +#define P3A_RG_CLKDRV_AMP GENMASK(31, 29) +#define P3A_RG_CLKDRV_AMP_VAL(x) ((0x7 & (x)) << 29) + +#define U3P_U3_PHYA_DA_REG0 0x100 +#define P3A_RG_XTAL_EXT_PE2H GENMASK(17, 16) +#define P3A_RG_XTAL_EXT_PE2H_VAL(x) ((0x3 & (x)) << 16) +#define P3A_RG_XTAL_EXT_PE1H GENMASK(13, 12) +#define P3A_RG_XTAL_EXT_PE1H_VAL(x) ((0x3 & (x)) << 12) +#define P3A_RG_XTAL_EXT_EN_U3 GENMASK(11, 10) +#define P3A_RG_XTAL_EXT_EN_U3_VAL(x) ((0x3 & (x)) << 10) + +#define U3P_U3_PHYA_DA_REG4 0x108 +#define P3A_RG_PLL_DIVEN_PE2H GENMASK(21, 19) +#define P3A_RG_PLL_BC_PE2H GENMASK(7, 6) +#define P3A_RG_PLL_BC_PE2H_VAL(x) ((0x3 & (x)) << 6) + +#define U3P_U3_PHYA_DA_REG5 0x10c +#define P3A_RG_PLL_BR_PE2H GENMASK(29, 28) +#define P3A_RG_PLL_BR_PE2H_VAL(x) ((0x3 & (x)) << 28) +#define P3A_RG_PLL_IC_PE2H GENMASK(15, 12) +#define P3A_RG_PLL_IC_PE2H_VAL(x) ((0xf & (x)) << 12) + +#define U3P_U3_PHYA_DA_REG6 0x110 +#define P3A_RG_PLL_IR_PE2H GENMASK(19, 16) +#define P3A_RG_PLL_IR_PE2H_VAL(x) ((0xf & (x)) << 16) + +#define U3P_U3_PHYA_DA_REG7 0x114 +#define P3A_RG_PLL_BP_PE2H GENMASK(19, 16) +#define P3A_RG_PLL_BP_PE2H_VAL(x) ((0xf & (x)) << 16) + +#define U3P_U3_PHYA_DA_REG20 0x13c +#define P3A_RG_PLL_DELTA1_PE2H GENMASK(31, 16) +#define P3A_RG_PLL_DELTA1_PE2H_VAL(x) ((0xffff & (x)) << 16) + +#define U3P_U3_PHYA_DA_REG25 0x148 +#define P3A_RG_PLL_DELTA_PE2H GENMASK(15, 0) +#define P3A_RG_PLL_DELTA_PE2H_VAL(x) (0xffff & (x)) + +#define U3P_U3_PHYD_RXDET1 0x128 +#define P3D_RG_RXDET_STB2_SET GENMASK(17, 9) +#define P3D_RG_RXDET_STB2_SET_VAL(x) ((0x1ff & (x)) << 9) + +#define U3P_U3_PHYD_RXDET2 0x12c +#define P3D_RG_RXDET_STB2_SET_P3 GENMASK(8, 0) +#define P3D_RG_RXDET_STB2_SET_P3_VAL(x) (0x1ff & (x)) + +struct u3phy_banks { + void __iomem *spllc; + void __iomem *chip; + void __iomem *phyd; /* include u3phyd_bank2 */ + void __iomem *phya; /* include u3phya_da */ +}; + +struct mtk_phy_instance { + void __iomem *port_base; + const struct device_node *np; + + struct u3phy_banks u3_banks; + + /* reference clock of anolog phy */ + struct clk ref_clk; + u32 index; + u8 type; +}; + +struct mtk_tphy { + void __iomem *sif_base; + struct mtk_phy_instance **phys; + int nphys; +}; + +static void pcie_phy_instance_init(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u3phy_banks *u3_banks = &instance->u3_banks; + + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_DA_REG0, + P3A_RG_XTAL_EXT_PE1H | P3A_RG_XTAL_EXT_PE2H, + P3A_RG_XTAL_EXT_PE1H_VAL(0x2) | + P3A_RG_XTAL_EXT_PE2H_VAL(0x2)); + + /* ref clk drive */ + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_REG1, P3A_RG_CLKDRV_AMP, + P3A_RG_CLKDRV_AMP_VAL(0x4)); + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_REG0, P3A_RG_CLKDRV_OFF, + P3A_RG_CLKDRV_OFF_VAL(0x1)); + + /* SSC delta -5000ppm */ + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_DA_REG20, + P3A_RG_PLL_DELTA1_PE2H, + P3A_RG_PLL_DELTA1_PE2H_VAL(0x3c)); + + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_DA_REG25, + P3A_RG_PLL_DELTA_PE2H, + P3A_RG_PLL_DELTA_PE2H_VAL(0x36)); + + /* change pll BW 0.6M */ + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_DA_REG5, + P3A_RG_PLL_BR_PE2H | P3A_RG_PLL_IC_PE2H, + P3A_RG_PLL_BR_PE2H_VAL(0x1) | + P3A_RG_PLL_IC_PE2H_VAL(0x1)); + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_DA_REG4, + P3A_RG_PLL_DIVEN_PE2H | P3A_RG_PLL_BC_PE2H, + P3A_RG_PLL_BC_PE2H_VAL(0x3)); + + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_DA_REG6, + P3A_RG_PLL_IR_PE2H, P3A_RG_PLL_IR_PE2H_VAL(0x2)); + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_DA_REG7, + P3A_RG_PLL_BP_PE2H, P3A_RG_PLL_BP_PE2H_VAL(0xa)); + + /* Tx Detect Rx Timing: 10us -> 5us */ + clrsetbits_le32(u3_banks->phyd + U3P_U3_PHYD_RXDET1, + P3D_RG_RXDET_STB2_SET, + P3D_RG_RXDET_STB2_SET_VAL(0x10)); + clrsetbits_le32(u3_banks->phyd + U3P_U3_PHYD_RXDET2, + P3D_RG_RXDET_STB2_SET_P3, + P3D_RG_RXDET_STB2_SET_P3_VAL(0x10)); + + /* wait for PCIe subsys register to active */ + udelay(3000); +} + +static void pcie_phy_instance_power_on(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u3phy_banks *bank = &instance->u3_banks; + + clrbits_le32(bank->chip + U3P_U3_CHIP_GPIO_CTLD, + P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST); + clrbits_le32(bank->chip + U3P_U3_CHIP_GPIO_CTLE, + P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD); +} + +static void pcie_phy_instance_power_off(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) + +{ + struct u3phy_banks *bank = &instance->u3_banks; + + setbits_le32(bank->chip + U3P_U3_CHIP_GPIO_CTLD, + P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST); + setbits_le32(bank->chip + U3P_U3_CHIP_GPIO_CTLE, + P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD); +} + +static void phy_v1_banks_init(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u3phy_banks *u3_banks = &instance->u3_banks; + + switch (instance->type) { + case PHY_TYPE_PCIE: + u3_banks->spllc = tphy->sif_base + SSUSB_SIFSLV_V1_SPLLC; + u3_banks->chip = tphy->sif_base + SSUSB_SIFSLV_V1_CHIP; + u3_banks->phyd = instance->port_base + SSUSB_SIFSLV_V1_U3PHYD; + u3_banks->phya = instance->port_base + SSUSB_SIFSLV_V1_U3PHYA; + break; + default: + return; + } +} + +static int mtk_phy_init(struct phy *phy) +{ + struct mtk_tphy *tphy = dev_get_priv(phy->dev); + struct mtk_phy_instance *instance = tphy->phys[phy->id]; + int ret; + + /* we may use a fixed-clock here */ + ret = clk_enable(&instance->ref_clk); + if (ret && ret != -ENOSYS) + return ret; + + switch (instance->type) { + case PHY_TYPE_PCIE: + pcie_phy_instance_init(tphy, instance); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mtk_phy_power_on(struct phy *phy) +{ + struct mtk_tphy *tphy = dev_get_priv(phy->dev); + struct mtk_phy_instance *instance = tphy->phys[phy->id]; + + pcie_phy_instance_power_on(tphy, instance); + + return 0; +} + +static int mtk_phy_power_off(struct phy *phy) +{ + struct mtk_tphy *tphy = dev_get_priv(phy->dev); + struct mtk_phy_instance *instance = tphy->phys[phy->id]; + + pcie_phy_instance_power_off(tphy, instance); + + return 0; +} + +static int mtk_phy_exit(struct phy *phy) +{ + struct mtk_tphy *tphy = dev_get_priv(phy->dev); + struct mtk_phy_instance *instance = tphy->phys[phy->id]; + + clk_disable(&instance->ref_clk); + + return 0; +} + +static int mtk_phy_xlate(struct phy *phy, + struct ofnode_phandle_args *args) +{ + struct mtk_tphy *tphy = dev_get_priv(phy->dev); + struct mtk_phy_instance *instance = NULL; + const struct device_node *phy_np = ofnode_to_np(args->node); + u32 index; + + if (!phy_np) { + dev_err(phy->dev, "null pointer phy node\n"); + return -EINVAL; + } + + if (args->args_count < 1) { + dev_err(phy->dev, "invalid number of cells in 'phy' property\n"); + return -EINVAL; + } + + for (index = 0; index < tphy->nphys; index++) + if (phy_np == tphy->phys[index]->np) { + instance = tphy->phys[index]; + break; + } + + if (!instance) { + dev_err(phy->dev, "failed to find appropriate phy\n"); + return -EINVAL; + } + + phy->id = index; + instance->type = args->args[1]; + if (!(instance->type == PHY_TYPE_USB2 || + instance->type == PHY_TYPE_USB3 || + instance->type == PHY_TYPE_PCIE || + instance->type == PHY_TYPE_SATA)) { + dev_err(phy->dev, "unsupported device type\n"); + return -EINVAL; + } + + phy_v1_banks_init(tphy, instance); + + return 0; +} + +static const struct phy_ops mtk_tphy_ops = { + .init = mtk_phy_init, + .exit = mtk_phy_exit, + .power_on = mtk_phy_power_on, + .power_off = mtk_phy_power_off, + .of_xlate = mtk_phy_xlate, +}; + +static int mtk_tphy_probe(struct udevice *dev) +{ + struct mtk_tphy *tphy = dev_get_priv(dev); + ofnode subnode; + int index = 0; + + dev_for_each_subnode(subnode, dev) + tphy->nphys++; + + tphy->phys = devm_kcalloc(dev, tphy->nphys, sizeof(*tphy->phys), + GFP_KERNEL); + if (!tphy->phys) + return -ENOMEM; + + tphy->sif_base = dev_read_addr_ptr(dev); + if (!tphy->sif_base) + return -ENOENT; + + dev_for_each_subnode(subnode, dev) { + struct mtk_phy_instance *instance; + fdt_addr_t addr; + int err; + + instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL); + if (!instance) + return -ENOMEM; + + addr = ofnode_get_addr(subnode); + if (addr == FDT_ADDR_T_NONE) + return -ENOMEM; + + instance->port_base = map_sysmem(addr, 0); + instance->index = index; + instance->np = ofnode_to_np(subnode); + tphy->phys[index] = instance; + index++; + + err = clk_get_by_index_nodev(subnode, 0, &instance->ref_clk); + if (err) + return err; + } + + return 0; +} + +static const struct udevice_id mtk_tphy_id_table[] = { + { .compatible = "mediatek,generic-tphy-v1", }, + { } +}; + +U_BOOT_DRIVER(mtk_tphy) = { + .name = "mtk-tphy", + .id = UCLASS_PHY, + .of_match = mtk_tphy_id_table, + .ops = &mtk_tphy_ops, + .probe = mtk_tphy_probe, + .priv_auto_alloc_size = sizeof(struct mtk_tphy), +}; diff --git a/drivers/power/regulator/regulator_common.c b/drivers/power/regulator/regulator_common.c index 3dabbe2a85a..20410865672 100644 --- a/drivers/power/regulator/regulator_common.c +++ b/drivers/power/regulator/regulator_common.c @@ -12,10 +12,15 @@ int regulator_common_ofdata_to_platdata(struct udevice *dev, struct regulator_common_platdata *dev_pdata, const char *enable_gpio_name) { struct gpio_desc *gpio; + struct dm_regulator_uclass_platdata *uc_pdata; int flags = GPIOD_IS_OUT; int ret; - if (dev_read_bool(dev, "enable-active-high")) + uc_pdata = dev_get_uclass_platdata(dev); + + if (!dev_read_bool(dev, "enable-active-high")) + flags |= GPIOD_ACTIVE_LOW; + if (uc_pdata->boot_on) flags |= GPIOD_IS_OUT_ACTIVE; /* Get optional enable GPIO desc */ diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index f54a245424a..7c2e4804b5d 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -52,6 +52,26 @@ config REMOTEPROC_TI_K3_ARM64 on various TI K3 family of SoCs through the remote processor framework. +config REMOTEPROC_TI_K3_DSP + bool "TI K3 C66 and C71 remoteproc support" + select REMOTEPROC + depends on ARCH_K3 + depends on TI_SCI_PROTOCOL + help + Say y here to support TI's C66/C71 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 + depends on ARCH_K3 + depends on TI_SCI_PROTOCOL + help + Say y here to support TI's R5F remote processor subsystems + on various TI K3 family of SoCs through the remote processor + framework. + config REMOTEPROC_TI_POWER bool "Support for TI Power processor" select REMOTEPROC diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 271ba55b093..69ae7bd1e89 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -11,4 +11,6 @@ obj-$(CONFIG_K3_SYSTEM_CONTROLLER) += k3_system_controller.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_R5F) += ti_k3_r5f_rproc.o obj-$(CONFIG_REMOTEPROC_TI_POWER) += ti_power_proc.o diff --git a/drivers/remoteproc/rproc-elf-loader.c b/drivers/remoteproc/rproc-elf-loader.c index 67937a75957..b38a226065d 100644 --- a/drivers/remoteproc/rproc-elf-loader.c +++ b/drivers/remoteproc/rproc-elf-loader.c @@ -64,13 +64,90 @@ int rproc_elf32_sanity_check(ulong addr, ulong size) return 0; } -/* A very simple elf loader, assumes the image is valid */ -int rproc_elf32_load_image(struct udevice *dev, unsigned long addr) +/* Basic function to verify ELF64 image format */ +int rproc_elf64_sanity_check(ulong addr, ulong size) +{ + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)addr; + char class; + + if (!addr) { + pr_debug("Invalid fw address?\n"); + return -EFAULT; + } + + if (size < sizeof(Elf64_Ehdr)) { + pr_debug("Image is too small\n"); + return -ENOSPC; + } + + class = ehdr->e_ident[EI_CLASS]; + + if (!IS_ELF(*ehdr) || ehdr->e_type != ET_EXEC || class != ELFCLASS64) { + pr_debug("Not an executable ELF64 image\n"); + return -EPROTONOSUPPORT; + } + + /* We assume the firmware has the same endianness as the host */ +# ifdef __LITTLE_ENDIAN + if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { +# else /* BIG ENDIAN */ + if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) { +# endif + pr_debug("Unsupported firmware endianness\n"); + return -EILSEQ; + } + + if (size < ehdr->e_shoff + sizeof(Elf64_Shdr)) { + pr_debug("Image is too small\n"); + return -ENOSPC; + } + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { + pr_debug("Image is corrupted (bad magic)\n"); + return -EBADF; + } + + if (ehdr->e_phnum == 0) { + pr_debug("No loadable segments\n"); + return -ENOEXEC; + } + + if (ehdr->e_phoff > size) { + pr_debug("Firmware size is too small\n"); + return -ENOSPC; + } + + return 0; +} + +/* Basic function to verify ELF image format */ +int rproc_elf_sanity_check(ulong addr, ulong size) +{ + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)addr; + + if (!addr) { + dev_err(dev, "Invalid firmware address\n"); + return -EFAULT; + } + + if (ehdr->e_ident[EI_CLASS] == ELFCLASS64) + return rproc_elf64_sanity_check(addr, size); + else + return rproc_elf32_sanity_check(addr, size); +} + +int rproc_elf32_load_image(struct udevice *dev, unsigned long addr, ulong size) { Elf32_Ehdr *ehdr; /* Elf header structure pointer */ Elf32_Phdr *phdr; /* Program header structure pointer */ const struct dm_rproc_ops *ops; - unsigned int i; + unsigned int i, ret; + + ret = rproc_elf32_sanity_check(addr, size); + if (ret) { + dev_err(dev, "Invalid ELF32 Image %d\n", ret); + return ret; + } ehdr = (Elf32_Ehdr *)addr; phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff); @@ -86,7 +163,8 @@ int rproc_elf32_load_image(struct udevice *dev, unsigned long addr) continue; if (ops->device_to_virt) - dst = ops->device_to_virt(dev, (ulong)dst); + dst = ops->device_to_virt(dev, (ulong)dst, + phdr->p_memsz); dev_dbg(dev, "Loading phdr %i to 0x%p (%i bytes)\n", i, dst, phdr->p_filesz); @@ -104,3 +182,96 @@ int rproc_elf32_load_image(struct udevice *dev, unsigned long addr) return 0; } + +int rproc_elf64_load_image(struct udevice *dev, ulong addr, ulong size) +{ + const struct dm_rproc_ops *ops = rproc_get_ops(dev); + u64 da, memsz, filesz, offset; + Elf64_Ehdr *ehdr; + Elf64_Phdr *phdr; + int i, ret = 0; + void *ptr; + + dev_dbg(dev, "%s: addr = 0x%lx size = 0x%lx\n", __func__, addr, size); + + if (rproc_elf64_sanity_check(addr, size)) + return -EINVAL; + + ehdr = (Elf64_Ehdr *)addr; + phdr = (Elf64_Phdr *)(addr + (ulong)ehdr->e_phoff); + + /* go through the available ELF segments */ + for (i = 0; i < ehdr->e_phnum; i++, phdr++) { + da = phdr->p_paddr; + memsz = phdr->p_memsz; + filesz = phdr->p_filesz; + offset = phdr->p_offset; + + if (phdr->p_type != PT_LOAD) + continue; + + dev_dbg(dev, "%s:phdr: type %d da 0x%llx memsz 0x%llx filesz 0x%llx\n", + __func__, phdr->p_type, da, memsz, filesz); + + ptr = (void *)(uintptr_t)da; + if (ops->device_to_virt) { + ptr = ops->device_to_virt(dev, da, phdr->p_memsz); + if (!ptr) { + dev_err(dev, "bad da 0x%llx mem 0x%llx\n", da, + memsz); + ret = -EINVAL; + break; + } + } + + if (filesz) + memcpy(ptr, (void *)addr + offset, filesz); + if (filesz != memsz) + memset(ptr + filesz, 0x00, memsz - filesz); + + flush_cache(rounddown((ulong)ptr, ARCH_DMA_MINALIGN), + roundup((ulong)ptr + filesz, ARCH_DMA_MINALIGN) - + rounddown((ulong)ptr, ARCH_DMA_MINALIGN)); + } + + return ret; +} + +int rproc_elf_load_image(struct udevice *dev, ulong addr, ulong size) +{ + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)addr; + + if (!addr) { + dev_err(dev, "Invalid firmware address\n"); + return -EFAULT; + } + + if (ehdr->e_ident[EI_CLASS] == ELFCLASS64) + return rproc_elf64_load_image(dev, addr, size); + else + return rproc_elf32_load_image(dev, addr, size); +} + +static ulong rproc_elf32_get_boot_addr(ulong addr) +{ + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)addr; + + return ehdr->e_entry; +} + +static ulong rproc_elf64_get_boot_addr(ulong addr) +{ + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)addr; + + return ehdr->e_entry; +} + +ulong rproc_elf_get_boot_addr(struct udevice *dev, ulong addr) +{ + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)addr; + + if (ehdr->e_ident[EI_CLASS] == ELFCLASS64) + return rproc_elf64_get_boot_addr(addr); + else + return rproc_elf32_get_boot_addr(addr); +} diff --git a/drivers/remoteproc/sandbox_testproc.c b/drivers/remoteproc/sandbox_testproc.c index 5f35119ab71..eeee49c4ddc 100644 --- a/drivers/remoteproc/sandbox_testproc.c +++ b/drivers/remoteproc/sandbox_testproc.c @@ -306,9 +306,11 @@ static int sandbox_testproc_ping(struct udevice *dev) * sandbox_testproc_device_to_virt() - Convert device address to virtual address * @dev: device to operate upon * @da: device address + * @size: Size of the memory region @da is pointing to * @return converted virtual address */ -static void *sandbox_testproc_device_to_virt(struct udevice *dev, ulong da) +static void *sandbox_testproc_device_to_virt(struct udevice *dev, ulong da, + ulong size) { u64 paddr; diff --git a/drivers/remoteproc/stm32_copro.c b/drivers/remoteproc/stm32_copro.c index ad941f67e86..40bba372119 100644 --- a/drivers/remoteproc/stm32_copro.c +++ b/drivers/remoteproc/stm32_copro.c @@ -107,11 +107,13 @@ static int stm32_copro_set_hold_boot(struct udevice *dev, bool hold) * stm32_copro_device_to_virt() - Convert device address to virtual address * @dev: corresponding STM32 remote processor device * @da: device address + * @size: Size of the memory region @da is pointing to * @return converted virtual address */ -static void *stm32_copro_device_to_virt(struct udevice *dev, ulong da) +static void *stm32_copro_device_to_virt(struct udevice *dev, ulong da, + ulong size) { - fdt32_t in_addr = cpu_to_be32(da); + fdt32_t in_addr = cpu_to_be32(da), end_addr; u64 paddr; paddr = dev_translate_dma_address(dev, &in_addr); @@ -120,6 +122,12 @@ static void *stm32_copro_device_to_virt(struct udevice *dev, ulong da) return NULL; } + end_addr = cpu_to_be32(da + size - 1); + if (dev_translate_dma_address(dev, &end_addr) == OF_BAD_ADDR) { + dev_err(dev, "Unable to convert address %ld\n", da + size - 1); + return NULL; + } + return phys_to_virt(paddr); } @@ -147,14 +155,7 @@ static int stm32_copro_load(struct udevice *dev, ulong addr, ulong size) return ret; } - /* Support only ELF32 image */ - ret = rproc_elf32_sanity_check(addr, size); - if (ret) { - dev_err(dev, "Invalid ELF32 image (%d)\n", ret); - return ret; - } - - return rproc_elf32_load_image(dev, addr); + return rproc_elf32_load_image(dev, addr, size); } /** diff --git a/drivers/remoteproc/ti_k3_dsp_rproc.c b/drivers/remoteproc/ti_k3_dsp_rproc.c new file mode 100644 index 00000000000..c5dc6b25da8 --- /dev/null +++ b/drivers/remoteproc/ti_k3_dsp_rproc.c @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments' K3 DSP Remoteproc driver + * + * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ + * Lokesh Vutla <lokeshvutla@ti.com> + * + */ + +#include <common.h> +#include <dm.h> +#include <remoteproc.h> +#include <errno.h> +#include <clk.h> +#include <reset.h> +#include <asm/io.h> +#include <power-domain.h> +#include <linux/soc/ti/ti_sci_protocol.h> +#include "ti_sci_proc.h" + +#define KEYSTONE_RPROC_LOCAL_ADDRESS_MASK (SZ_16M - 1) + +/** + * struct k3_dsp_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_dsp_mem { + void __iomem *cpu_addr; + phys_addr_t bus_addr; + phys_addr_t dev_addr; + size_t size; +}; + +/** + * struct k3_dsp_privdata - Structure representing Remote processor data. + * @rproc_rst: rproc reset control data + * @tsp: Pointer to TISCI proc contrl handle + * @mem: Array of available memories + * @num_mem: Number of available memories + */ +struct k3_dsp_privdata { + struct reset_ctl dsp_rst; + struct ti_sci_proc tsp; + struct k3_dsp_mem *mem; + int num_mems; +}; + +/** + * k3_dsp_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_dsp_load(struct udevice *dev, ulong addr, ulong size) +{ + struct k3_dsp_privdata *dsp = dev_get_priv(dev); + u32 boot_vector; + int ret; + + dev_dbg(dev, "%s addr = 0x%lx, size = 0x%lx\n", __func__, addr, size); + ret = ti_sci_proc_request(&dsp->tsp); + if (ret) + return ret; + + ret = rproc_elf_load_image(dev, addr, size); + if (ret < 0) { + dev_err(dev, "Loading elf failed %d\n", ret); + goto proc_release; + } + + boot_vector = rproc_elf_get_boot_addr(dev, addr); + + dev_dbg(dev, "%s: Boot vector = 0x%x\n", __func__, boot_vector); + + ret = ti_sci_proc_set_config(&dsp->tsp, boot_vector, 0, 0); +proc_release: + ti_sci_proc_release(&dsp->tsp); + return ret; +} + +/** + * k3_dsp_start() - Start the remote processor + * @dev: rproc device pointer + * + * Return: 0 if all went ok, else return appropriate error + */ +static int k3_dsp_start(struct udevice *dev) +{ + struct k3_dsp_privdata *dsp = dev_get_priv(dev); + int ret; + + dev_dbg(dev, "%s\n", __func__); + + ret = ti_sci_proc_request(&dsp->tsp); + if (ret) + return ret; + /* + * Setting the right clock frequency would have taken care by + * assigned-clock-rates during the device probe. So no need to + * set the frequency again here. + */ + ret = ti_sci_proc_power_domain_on(&dsp->tsp); + if (ret) + goto proc_release; + + ret = reset_deassert(&dsp->dsp_rst); + +proc_release: + ti_sci_proc_release(&dsp->tsp); + + return ret; +} + +static int k3_dsp_stop(struct udevice *dev) +{ + struct k3_dsp_privdata *dsp = dev_get_priv(dev); + + dev_dbg(dev, "%s\n", __func__); + + ti_sci_proc_request(&dsp->tsp); + reset_assert(&dsp->dsp_rst); + ti_sci_proc_power_domain_off(&dsp->tsp); + ti_sci_proc_release(&dsp->tsp); + + return 0; +} + +/** + * k3_dsp_init() - Initialize the remote processor + * @dev: rproc device pointer + * + * Return: 0 if all went ok, else return appropriate error + */ +static int k3_dsp_init(struct udevice *dev) +{ + dev_dbg(dev, "%s\n", __func__); + + return 0; +} + +static int k3_dsp_reset(struct udevice *dev) +{ + dev_dbg(dev, "%s\n", __func__); + + return 0; +} + +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; + + dev_dbg(dev, "%s\n", __func__); + + if (len <= 0) + return NULL; + + for (i = 0; i < dsp->num_mems; i++) { + bus_addr = dsp->mem[i].bus_addr; + dev_addr = dsp->mem[i].dev_addr; + size = dsp->mem[i].size; + + if (da >= dev_addr && ((da + len) <= (dev_addr + size))) { + offset = da - dev_addr; + va = dsp->mem[i].cpu_addr + offset; + return (__force void *)va; + } + + if (da >= bus_addr && (da + len) <= (bus_addr + size)) { + offset = da - bus_addr; + va = dsp->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_dsp_ops = { + .init = k3_dsp_init, + .load = k3_dsp_load, + .start = k3_dsp_start, + .stop = k3_dsp_stop, + .reset = k3_dsp_reset, + .device_to_virt = k3_dsp_da_to_va, +}; + +static int ti_sci_proc_of_to_priv(struct udevice *dev, struct ti_sci_proc *tsp) +{ + u32 ids[2]; + int ret; + + dev_dbg(dev, "%s\n", __func__); + + 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 int k3_dsp_of_get_memories(struct udevice *dev) +{ + static const char * const mem_names[] = {"l2sram", "l1pram", "l1dram"}; + struct k3_dsp_privdata *dsp = dev_get_priv(dev); + int i; + + dev_dbg(dev, "%s\n", __func__); + + dsp->num_mems = ARRAY_SIZE(mem_names); + dsp->mem = calloc(dsp->num_mems, sizeof(*dsp->mem)); + if (!dsp->mem) + return -ENOMEM; + + for (i = 0; i < dsp->num_mems; i++) { + /* C71 cores only have a L1P Cache, there are no L1P SRAMs */ + if (device_is_compatible(dev, "ti,j721e-c71-dsp") && + !strcmp(mem_names[i], "l1pram")) { + dsp->mem[i].bus_addr = FDT_ADDR_T_NONE; + dsp->mem[i].dev_addr = FDT_ADDR_T_NONE; + dsp->mem[i].cpu_addr = NULL; + dsp->mem[i].size = 0; + continue; + } + + dsp->mem[i].bus_addr = dev_read_addr_size_name(dev, mem_names[i], + (fdt_addr_t *)&dsp->mem[i].size); + if (dsp->mem[i].bus_addr == FDT_ADDR_T_NONE) { + dev_err(dev, "%s bus address not found\n", mem_names[i]); + return -EINVAL; + } + dsp->mem[i].cpu_addr = map_physmem(dsp->mem[i].bus_addr, + dsp->mem[i].size, + MAP_NOCACHE); + dsp->mem[i].dev_addr = dsp->mem[i].bus_addr & + KEYSTONE_RPROC_LOCAL_ADDRESS_MASK; + + dev_dbg(dev, "memory %8s: bus addr %pa size 0x%zx va %p da %pa\n", + mem_names[i], &dsp->mem[i].bus_addr, + dsp->mem[i].size, dsp->mem[i].cpu_addr, + &dsp->mem[i].dev_addr); + } + + return 0; +} + +/** + * k3_of_to_priv() - generate private data from device tree + * @dev: corresponding k3 dsp processor device + * @dsp: pointer to driver specific private data + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_dsp_of_to_priv(struct udevice *dev, struct k3_dsp_privdata *dsp) +{ + int ret; + + dev_dbg(dev, "%s\n", __func__); + + ret = reset_get_by_index(dev, 0, &dsp->dsp_rst); + if (ret) { + dev_err(dev, "reset_get() failed: %d\n", ret); + return ret; + } + + ret = ti_sci_proc_of_to_priv(dev, &dsp->tsp); + if (ret) + return ret; + + ret = k3_dsp_of_get_memories(dev); + if (ret) + return ret; + + return 0; +} + +/** + * k3_dsp_probe() - Basic probe + * @dev: corresponding k3 remote processor device + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_dsp_probe(struct udevice *dev) +{ + struct k3_dsp_privdata *dsp; + int ret; + + dev_dbg(dev, "%s\n", __func__); + + dsp = dev_get_priv(dev); + + ret = k3_dsp_of_to_priv(dev, dsp); + if (ret) { + dev_dbg(dev, "%s: Probe failed with error %d\n", __func__, ret); + return ret; + } + + dev_dbg(dev, "Remoteproc successfully probed\n"); + + return 0; +} + +static int k3_dsp_remove(struct udevice *dev) +{ + struct k3_dsp_privdata *dsp = dev_get_priv(dev); + + free(dsp->mem); + + return 0; +} + +static const struct udevice_id k3_dsp_ids[] = { + { .compatible = "ti,j721e-c66-dsp"}, + { .compatible = "ti,j721e-c71-dsp"}, + {} +}; + +U_BOOT_DRIVER(k3_dsp) = { + .name = "k3_dsp", + .of_match = k3_dsp_ids, + .id = UCLASS_REMOTEPROC, + .ops = &k3_dsp_ops, + .probe = k3_dsp_probe, + .remove = k3_dsp_remove, + .priv_auto_alloc_size = sizeof(struct k3_dsp_privdata), +}; diff --git a/drivers/remoteproc/ti_k3_r5f_rproc.c b/drivers/remoteproc/ti_k3_r5f_rproc.c new file mode 100644 index 00000000000..ae1e4b9e04f --- /dev/null +++ b/drivers/remoteproc/ti_k3_r5f_rproc.c @@ -0,0 +1,816 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments' K3 R5 Remoteproc driver + * + * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ + * Lokesh Vutla <lokeshvutla@ti.com> + */ + +#include <common.h> +#include <dm.h> +#include <remoteproc.h> +#include <errno.h> +#include <clk.h> +#include <reset.h> +#include <asm/io.h> +#include <linux/kernel.h> +#include <linux/soc/ti/ti_sci_protocol.h> +#include "ti_sci_proc.h" + +/* + * R5F's view of this address can either be for ATCM or BTCM with the other + * at address 0x0 based on loczrama signal. + */ +#define K3_R5_TCM_DEV_ADDR 0x41010000 + +/* R5 TI-SCI Processor Configuration Flags */ +#define PROC_BOOT_CFG_FLAG_R5_DBG_EN 0x00000001 +#define PROC_BOOT_CFG_FLAG_R5_DBG_NIDEN 0x00000002 +#define PROC_BOOT_CFG_FLAG_R5_LOCKSTEP 0x00000100 +#define PROC_BOOT_CFG_FLAG_R5_TEINIT 0x00000200 +#define PROC_BOOT_CFG_FLAG_R5_NMFI_EN 0x00000400 +#define PROC_BOOT_CFG_FLAG_R5_TCM_RSTBASE 0x00000800 +#define PROC_BOOT_CFG_FLAG_R5_BTCM_EN 0x00001000 +#define PROC_BOOT_CFG_FLAG_R5_ATCM_EN 0x00002000 +#define PROC_BOOT_CFG_FLAG_GEN_IGN_BOOTVECTOR 0x10000000 + +/* R5 TI-SCI Processor Control Flags */ +#define PROC_BOOT_CTRL_FLAG_R5_CORE_HALT 0x00000001 + +/* R5 TI-SCI Processor Status Flags */ +#define PROC_BOOT_STATUS_FLAG_R5_WFE 0x00000001 +#define PROC_BOOT_STATUS_FLAG_R5_WFI 0x00000002 +#define PROC_BOOT_STATUS_FLAG_R5_CLK_GATED 0x00000004 +#define PROC_BOOT_STATUS_FLAG_R5_LOCKSTEP_PERMITTED 0x00000100 + +#define NR_CORES 2 + +enum cluster_mode { + CLUSTER_MODE_SPLIT = 0, + CLUSTER_MODE_LOCKSTEP, +}; + +/** + * struct k3_r5_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_r5f_mem { + void __iomem *cpu_addr; + phys_addr_t bus_addr; + u32 dev_addr; + size_t size; +}; + +/** + * struct k3_r5f_core - K3 R5 core structure + * @dev: cached device pointer + * @cluster: pointer to the parent cluster. + * @reset: reset control handle + * @tsp: TI-SCI processor control handle + * @mem: Array of available internal memories + * @num_mem: Number of available memories + * @atcm_enable: flag to control ATCM enablement + * @btcm_enable: flag to control BTCM enablement + * @loczrama: flag to dictate which TCM is at device address 0x0 + * @in_use: flag to tell if the core is already in use. + */ +struct k3_r5f_core { + struct udevice *dev; + struct k3_r5f_cluster *cluster; + struct reset_ctl reset; + struct ti_sci_proc tsp; + struct k3_r5f_mem *mem; + int num_mems; + u32 atcm_enable; + u32 btcm_enable; + u32 loczrama; + bool in_use; +}; + +/** + * struct k3_r5f_cluster - K3 R5F Cluster structure + * @mode: Mode to configure the Cluster - Split or LockStep + * @cores: Array of pointers to R5 cores within the cluster + */ +struct k3_r5f_cluster { + enum cluster_mode mode; + struct k3_r5f_core *cores[NR_CORES]; +}; + +static bool is_primary_core(struct k3_r5f_core *core) +{ + return core == core->cluster->cores[0]; +} + +static int k3_r5f_proc_request(struct k3_r5f_core *core) +{ + struct k3_r5f_cluster *cluster = core->cluster; + int i, ret; + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP) { + for (i = 0; i < NR_CORES; i++) { + ret = ti_sci_proc_request(&cluster->cores[i]->tsp); + if (ret) + goto proc_release; + } + } else { + ret = ti_sci_proc_request(&core->tsp); + } + + return 0; + +proc_release: + while (i >= 0) { + ti_sci_proc_release(&cluster->cores[i]->tsp); + i--; + } + return ret; +} + +static void k3_r5f_proc_release(struct k3_r5f_core *core) +{ + struct k3_r5f_cluster *cluster = core->cluster; + int i; + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP) + for (i = 0; i < NR_CORES; i++) + ti_sci_proc_release(&cluster->cores[i]->tsp); + else + ti_sci_proc_release(&core->tsp); +} + +static int k3_r5f_lockstep_release(struct k3_r5f_cluster *cluster) +{ + int ret, c; + + dev_dbg(dev, "%s\n", __func__); + + for (c = NR_CORES - 1; c >= 0; c--) { + ret = ti_sci_proc_power_domain_on(&cluster->cores[c]->tsp); + if (ret) + goto unroll_module_reset; + } + + /* deassert local reset on all applicable cores */ + for (c = NR_CORES - 1; c >= 0; c--) { + ret = reset_deassert(&cluster->cores[c]->reset); + if (ret) + goto unroll_local_reset; + } + + return 0; + +unroll_local_reset: + while (c < NR_CORES) { + reset_assert(&cluster->cores[c]->reset); + c++; + } + c = 0; +unroll_module_reset: + while (c < NR_CORES) { + ti_sci_proc_power_domain_off(&cluster->cores[c]->tsp); + c++; + } + + return ret; +} + +static int k3_r5f_split_release(struct k3_r5f_core *core) +{ + int ret; + + dev_dbg(dev, "%s\n", __func__); + + ret = ti_sci_proc_power_domain_on(&core->tsp); + if (ret) { + dev_err(core->dev, "module-reset deassert failed, ret = %d\n", + ret); + return ret; + } + + ret = reset_deassert(&core->reset); + if (ret) { + dev_err(core->dev, "local-reset deassert failed, ret = %d\n", + ret); + if (ti_sci_proc_power_domain_off(&core->tsp)) + dev_warn(core->dev, "module-reset assert back failed\n"); + } + + return ret; +} + +static int k3_r5f_prepare(struct udevice *dev) +{ + struct k3_r5f_core *core = dev_get_priv(dev); + struct k3_r5f_cluster *cluster = core->cluster; + int ret = 0; + + dev_dbg(dev, "%s\n", __func__); + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP) + ret = k3_r5f_lockstep_release(cluster); + else + ret = k3_r5f_split_release(core); + + if (ret) + dev_err(dev, "Unable to enable cores for TCM loading %d\n", + ret); + + return ret; +} + +static int k3_r5f_core_sanity_check(struct k3_r5f_core *core) +{ + struct k3_r5f_cluster *cluster = core->cluster; + + if (core->in_use) { + dev_err(dev, "Invalid op: Trying to load/start on already running core %d\n", + core->tsp.proc_id); + return -EINVAL; + } + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP && !cluster->cores[1]) { + printf("Secondary core is not probed in this cluster\n"); + return -EAGAIN; + } + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP && !is_primary_core(core)) { + dev_err(dev, "Invalid op: Trying to start secondary core %d in lockstep mode\n", + core->tsp.proc_id); + return -EINVAL; + } + + if (cluster->mode == CLUSTER_MODE_SPLIT && !is_primary_core(core)) { + if (!core->cluster->cores[0]->in_use) { + dev_err(dev, "Invalid seq: Enable primary core before loading secondary core\n"); + return -EINVAL; + } + } + + return 0; +} + +/** + * k3_r5f_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_r5f_load(struct udevice *dev, ulong addr, ulong size) +{ + struct k3_r5f_core *core = dev_get_priv(dev); + u32 boot_vector; + int ret; + + dev_dbg(dev, "%s addr = 0x%lx, size = 0x%lx\n", __func__, addr, size); + + ret = k3_r5f_core_sanity_check(core); + if (ret) + return ret; + + ret = k3_r5f_proc_request(core); + if (ret) + return ret; + + ret = k3_r5f_prepare(dev); + if (ret) { + dev_err(dev, "R5f prepare failed for core %d\n", + core->tsp.proc_id); + goto proc_release; + } + + /* Zero out TCMs so that ECC can be effective on all TCM addresses */ + if (core->atcm_enable) + memset(core->mem[0].cpu_addr, 0x00, core->mem[0].size); + if (core->btcm_enable) + memset(core->mem[1].cpu_addr, 0x00, core->mem[1].size); + + ret = rproc_elf_load_image(dev, addr, size); + if (ret < 0) { + dev_err(dev, "Loading elf failedi %d\n", ret); + goto proc_release; + } + + boot_vector = rproc_elf_get_boot_addr(dev, addr); + + dev_dbg(dev, "%s: Boot vector = 0x%x\n", __func__, boot_vector); + + ret = ti_sci_proc_set_config(&core->tsp, boot_vector, 0, 0); + +proc_release: + k3_r5f_proc_release(core); + + return ret; +} + +static int k3_r5f_core_halt(struct k3_r5f_core *core) +{ + int ret; + + ret = ti_sci_proc_set_control(&core->tsp, + PROC_BOOT_CTRL_FLAG_R5_CORE_HALT, 0); + if (ret) + dev_err(core->dev, "Core %d failed to stop\n", + core->tsp.proc_id); + + return ret; +} + +static int k3_r5f_core_run(struct k3_r5f_core *core) +{ + int ret; + + ret = ti_sci_proc_set_control(&core->tsp, + 0, PROC_BOOT_CTRL_FLAG_R5_CORE_HALT); + if (ret) { + dev_err(core->dev, "Core %d failed to start\n", + core->tsp.proc_id); + return ret; + } + + return 0; +} + +/** + * k3_r5f_start() - Start the remote processor + * @dev: rproc device pointer + * + * Return: 0 if all went ok, else return appropriate error + */ +static int k3_r5f_start(struct udevice *dev) +{ + struct k3_r5f_core *core = dev_get_priv(dev); + struct k3_r5f_cluster *cluster = core->cluster; + int ret, c; + + dev_dbg(dev, "%s\n", __func__); + + ret = k3_r5f_core_sanity_check(core); + if (ret) + return ret; + + ret = k3_r5f_proc_request(core); + if (ret) + return ret; + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP) { + if (is_primary_core(core)) { + for (c = NR_CORES - 1; c >= 0; c--) { + ret = k3_r5f_core_run(cluster->cores[c]); + if (ret) + goto unroll_core_run; + } + } else { + dev_err(dev, "Invalid op: Trying to start secondary core %d in lockstep mode\n", + core->tsp.proc_id); + ret = -EINVAL; + goto proc_release; + } + } else { + ret = k3_r5f_core_run(core); + if (ret) + goto proc_release; + } + + core->in_use = true; + + k3_r5f_proc_release(core); + return 0; + +unroll_core_run: + while (c < NR_CORES) { + k3_r5f_core_halt(cluster->cores[c]); + c++; + } +proc_release: + k3_r5f_proc_release(core); + + return ret; +} + +static int k3_r5f_split_reset(struct k3_r5f_core *core) +{ + int ret; + + dev_dbg(dev, "%s\n", __func__); + + if (reset_assert(&core->reset)) + ret = -EINVAL; + + if (ti_sci_proc_power_domain_off(&core->tsp)) + ret = -EINVAL; + + return ret; +} + +static int k3_r5f_lockstep_reset(struct k3_r5f_cluster *cluster) +{ + int ret = 0, c; + + dev_dbg(dev, "%s\n", __func__); + + for (c = 0; c < NR_CORES; c++) + if (reset_assert(&cluster->cores[c]->reset)) + ret = -EINVAL; + + /* disable PSC modules on all applicable cores */ + for (c = 0; c < NR_CORES; c++) + if (ti_sci_proc_power_domain_off(&cluster->cores[c]->tsp)) + ret = -EINVAL; + + return ret; +} + +static int k3_r5f_unprepare(struct udevice *dev) +{ + struct k3_r5f_core *core = dev_get_priv(dev); + struct k3_r5f_cluster *cluster = core->cluster; + int ret; + + dev_dbg(dev, "%s\n", __func__); + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP) { + if (is_primary_core(core)) + ret = k3_r5f_lockstep_reset(cluster); + } else { + ret = k3_r5f_split_reset(core); + } + + if (ret) + dev_warn(dev, "Unable to enable cores for TCM loading %d\n", + ret); + + return 0; +} + +static int k3_r5f_stop(struct udevice *dev) +{ + struct k3_r5f_core *core = dev_get_priv(dev); + struct k3_r5f_cluster *cluster = core->cluster; + int c, ret; + + dev_dbg(dev, "%s\n", __func__); + + ret = k3_r5f_proc_request(core); + if (ret) + return ret; + + core->in_use = false; + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP) { + if (is_primary_core(core)) { + for (c = 0; c < NR_CORES; c++) + k3_r5f_core_halt(cluster->cores[c]); + } else { + dev_err(dev, "Invalid op: Trying to stop secondary core in lockstep mode\n"); + ret = -EINVAL; + goto proc_release; + } + } else { + k3_r5f_core_halt(core); + } + + ret = k3_r5f_unprepare(dev); +proc_release: + k3_r5f_proc_release(core); + return ret; +} + +static void *k3_r5f_da_to_va(struct udevice *dev, ulong da, ulong size) +{ + struct k3_r5f_core *core = dev_get_priv(dev); + void __iomem *va = NULL; + phys_addr_t bus_addr; + u32 dev_addr, offset; + ulong mem_size; + int i; + + dev_dbg(dev, "%s\n", __func__); + + if (size <= 0) + return NULL; + + for (i = 0; i < core->num_mems; i++) { + bus_addr = core->mem[i].bus_addr; + dev_addr = core->mem[i].dev_addr; + mem_size = core->mem[i].size; + + if (da >= bus_addr && (da + size) <= (bus_addr + mem_size)) { + offset = da - bus_addr; + va = core->mem[i].cpu_addr + offset; + return (__force void *)va; + } + + if (da >= dev_addr && (da + size) <= (dev_addr + mem_size)) { + offset = da - dev_addr; + va = core->mem[i].cpu_addr + offset; + return (__force void *)va; + } + } + + /* Assume it is DDR region and return da */ + return map_physmem(da, size, MAP_NOCACHE); +} + +static int k3_r5f_init(struct udevice *dev) +{ + return 0; +} + +static int k3_r5f_reset(struct udevice *dev) +{ + return 0; +} + +static const struct dm_rproc_ops k3_r5f_rproc_ops = { + .init = k3_r5f_init, + .reset = k3_r5f_reset, + .start = k3_r5f_start, + .stop = k3_r5f_stop, + .load = k3_r5f_load, + .device_to_virt = k3_r5f_da_to_va, +}; + +static int k3_r5f_rproc_configure(struct k3_r5f_core *core) +{ + struct k3_r5f_cluster *cluster = core->cluster; + u32 set_cfg = 0, clr_cfg = 0, cfg, ctrl, sts; + u64 boot_vec = 0; + int ret; + + dev_dbg(dev, "%s\n", __func__); + + ret = ti_sci_proc_request(&core->tsp); + if (ret < 0) + return ret; + + /* Do not touch boot vector now. Load will take care of it. */ + clr_cfg |= PROC_BOOT_CFG_FLAG_GEN_IGN_BOOTVECTOR; + + ret = ti_sci_proc_get_status(&core->tsp, &boot_vec, &cfg, &ctrl, &sts); + if (ret) + goto out; + + /* Sanity check for Lockstep mode */ + if (cluster->mode && is_primary_core(core) && + !(sts & PROC_BOOT_STATUS_FLAG_R5_LOCKSTEP_PERMITTED)) { + dev_err(core->dev, "LockStep mode not permitted on this device\n"); + ret = -EINVAL; + goto out; + } + + /* Primary core only configuration */ + if (is_primary_core(core)) { + /* always enable ARM mode */ + clr_cfg |= PROC_BOOT_CFG_FLAG_R5_TEINIT; + if (cluster->mode == CLUSTER_MODE_LOCKSTEP) + set_cfg |= PROC_BOOT_CFG_FLAG_R5_LOCKSTEP; + else + clr_cfg |= PROC_BOOT_CFG_FLAG_R5_LOCKSTEP; + } + + if (core->atcm_enable) + set_cfg |= PROC_BOOT_CFG_FLAG_R5_ATCM_EN; + else + clr_cfg |= PROC_BOOT_CFG_FLAG_R5_ATCM_EN; + + if (core->btcm_enable) + set_cfg |= PROC_BOOT_CFG_FLAG_R5_BTCM_EN; + else + clr_cfg |= PROC_BOOT_CFG_FLAG_R5_BTCM_EN; + + if (core->loczrama) + set_cfg |= PROC_BOOT_CFG_FLAG_R5_TCM_RSTBASE; + else + clr_cfg |= PROC_BOOT_CFG_FLAG_R5_TCM_RSTBASE; + + ret = k3_r5f_core_halt(core); + if (ret) + goto out; + + ret = ti_sci_proc_set_config(&core->tsp, boot_vec, set_cfg, clr_cfg); +out: + ti_sci_proc_release(&core->tsp); + return ret; +} + +static int ti_sci_proc_of_to_priv(struct udevice *dev, struct ti_sci_proc *tsp) +{ + u32 ids[2]; + int ret; + + dev_dbg(dev, "%s\n", __func__); + + 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 int k3_r5f_of_to_priv(struct k3_r5f_core *core) +{ + int ret; + + dev_dbg(dev, "%s\n", __func__); + + core->atcm_enable = dev_read_u32_default(core->dev, "atcm-enable", 0); + core->btcm_enable = dev_read_u32_default(core->dev, "btcm-enable", 1); + core->loczrama = dev_read_u32_default(core->dev, "loczrama", 1); + + ret = ti_sci_proc_of_to_priv(core->dev, &core->tsp); + if (ret) + return ret; + + ret = reset_get_by_index(core->dev, 0, &core->reset); + if (ret) { + dev_err(core->dev, "Reset lines not available: %d\n", ret); + return ret; + } + + return 0; +} + +static int k3_r5f_core_of_get_memories(struct k3_r5f_core *core) +{ + static const char * const mem_names[] = {"atcm", "btcm"}; + struct udevice *dev = core->dev; + int i; + + dev_dbg(dev, "%s\n", __func__); + + core->num_mems = ARRAY_SIZE(mem_names); + core->mem = calloc(core->num_mems, sizeof(*core->mem)); + if (!core->mem) + return -ENOMEM; + + for (i = 0; i < core->num_mems; i++) { + core->mem[i].bus_addr = dev_read_addr_size_name(dev, + mem_names[i], + (fdt_addr_t *)&core->mem[i].size); + if (core->mem[i].bus_addr == FDT_ADDR_T_NONE) { + dev_err(dev, "%s bus address not found\n", + mem_names[i]); + return -EINVAL; + } + core->mem[i].cpu_addr = map_physmem(core->mem[i].bus_addr, + core->mem[i].size, + MAP_NOCACHE); + if (!strcmp(mem_names[i], "atcm")) { + core->mem[i].dev_addr = core->loczrama ? + 0 : K3_R5_TCM_DEV_ADDR; + } else { + core->mem[i].dev_addr = core->loczrama ? + K3_R5_TCM_DEV_ADDR : 0; + } + + dev_dbg(dev, "memory %8s: bus addr %pa size 0x%zx va %p da 0x%x\n", + mem_names[i], &core->mem[i].bus_addr, + core->mem[i].size, core->mem[i].cpu_addr, + core->mem[i].dev_addr); + } + + return 0; +} + +/** + * k3_r5f_probe() - Basic probe + * @dev: corresponding k3 remote processor device + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_r5f_probe(struct udevice *dev) +{ + struct k3_r5f_cluster *cluster = dev_get_priv(dev->parent); + struct k3_r5f_core *core = dev_get_priv(dev); + bool r_state; + int ret; + + dev_dbg(dev, "%s\n", __func__); + + core->dev = dev; + ret = k3_r5f_of_to_priv(core); + if (ret) + return ret; + + core->cluster = cluster; + /* Assume Primary core gets probed first */ + if (!cluster->cores[0]) + cluster->cores[0] = core; + else + cluster->cores[1] = core; + + ret = k3_r5f_core_of_get_memories(core); + if (ret) { + dev_err(dev, "Rproc getting internal memories failed\n"); + return ret; + } + + ret = core->tsp.sci->ops.dev_ops.is_on(core->tsp.sci, core->tsp.dev_id, + &r_state, &core->in_use); + if (ret) + return ret; + + if (core->in_use) { + dev_info(dev, "Core %d is already in use. No rproc commands work\n", + core->tsp.proc_id); + return 0; + } + + /* Make sure Local reset is asserted. Redundant? */ + reset_assert(&core->reset); + + ret = k3_r5f_rproc_configure(core); + if (ret) { + dev_err(dev, "rproc configure failed %d\n", ret); + return ret; + } + + dev_dbg(dev, "Remoteproc successfully probed\n"); + + return 0; +} + +static int k3_r5f_remove(struct udevice *dev) +{ + struct k3_r5f_core *core = dev_get_priv(dev); + + free(core->mem); + + ti_sci_proc_release(&core->tsp); + + return 0; +} + +static const struct udevice_id k3_r5f_rproc_ids[] = { + { .compatible = "ti,am654-r5f"}, + { .compatible = "ti,j721e-r5f"}, + {} +}; + +U_BOOT_DRIVER(k3_r5f_rproc) = { + .name = "k3_r5f_rproc", + .of_match = k3_r5f_rproc_ids, + .id = UCLASS_REMOTEPROC, + .ops = &k3_r5f_rproc_ops, + .probe = k3_r5f_probe, + .remove = k3_r5f_remove, + .priv_auto_alloc_size = sizeof(struct k3_r5f_core), +}; + +static int k3_r5f_cluster_probe(struct udevice *dev) +{ + struct k3_r5f_cluster *cluster = dev_get_priv(dev); + + dev_dbg(dev, "%s\n", __func__); + + cluster->mode = dev_read_u32_default(dev, "lockstep-mode", + CLUSTER_MODE_LOCKSTEP); + + if (device_get_child_count(dev) != 2) { + dev_err(dev, "Invalid number of R5 cores"); + return -EINVAL; + } + + dev_dbg(dev, "%s: Cluster successfully probed in %s mode\n", + __func__, cluster->mode ? "lockstep" : "split"); + + return 0; +} + +static const struct udevice_id k3_r5fss_ids[] = { + { .compatible = "ti,am654-r5fss"}, + { .compatible = "ti,j721e-r5fss"}, + {} +}; + +U_BOOT_DRIVER(k3_r5fss) = { + .name = "k3_r5fss", + .of_match = k3_r5fss_ids, + .id = UCLASS_MISC, + .probe = k3_r5f_cluster_probe, + .priv_auto_alloc_size = sizeof(struct k3_r5f_cluster), +}; diff --git a/drivers/remoteproc/ti_sci_proc.h b/drivers/remoteproc/ti_sci_proc.h index ccfc39ec887..f8299d1aff9 100644 --- a/drivers/remoteproc/ti_sci_proc.h +++ b/drivers/remoteproc/ti_sci_proc.h @@ -19,12 +19,14 @@ * @proc_id: processor id for the consumer remoteproc device * @host_id: host id to pass the control over for this consumer remoteproc * device + * @dev_id: Device ID as identified by system controller. */ struct ti_sci_proc { const struct ti_sci_handle *sci; const struct ti_sci_proc_ops *ops; u8 proc_id; u8 host_id; + u16 dev_id; }; static inline int ti_sci_proc_request(struct ti_sci_proc *tsp) @@ -118,4 +120,29 @@ static inline int ti_sci_proc_set_control(struct ti_sci_proc *tsp, return ret; } +static inline int ti_sci_proc_power_domain_on(struct ti_sci_proc *tsp) +{ + int ret; + + debug("%s: dev_id = %d\n", __func__, tsp->dev_id); + + ret = tsp->sci->ops.dev_ops.get_device_exclusive(tsp->sci, tsp->dev_id); + if (ret) + pr_err("Power-domain on failed for dev = %d\n", tsp->dev_id); + + return ret; +} + +static inline int ti_sci_proc_power_domain_off(struct ti_sci_proc *tsp) +{ + int ret; + + debug("%s: dev_id = %d\n", __func__, tsp->dev_id); + + ret = tsp->sci->ops.dev_ops.put_device(tsp->sci, tsp->dev_id); + if (ret) + pr_err("Power-domain off failed for dev = %d\n", tsp->dev_id); + + return ret; +} #endif /* REMOTEPROC_TI_SCI_PROC_H */ diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8dd3213d485..b8ca2bdedd5 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -67,7 +67,7 @@ config ATMEL_SPI config BCM63XX_HSSPI bool "BCM63XX HSSPI driver" - depends on ARCH_BMIPS + depends on (ARCH_BMIPS || ARCH_BCM6858 || ARCH_BCM63158) help Enable the BCM6328 HSSPI driver. This driver can be used to access the SPI NOR flash on platforms embedding this Broadcom diff --git a/drivers/spi/bcm63xx_hsspi.c b/drivers/spi/bcm63xx_hsspi.c index 4f527fa74aa..e82b80c107c 100644 --- a/drivers/spi/bcm63xx_hsspi.c +++ b/drivers/spi/bcm63xx_hsspi.c @@ -120,9 +120,9 @@ static int bcm63xx_hsspi_set_mode(struct udevice *bus, uint mode) /* clock polarity */ if (mode & SPI_CPOL) - setbits_be32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_POL_MASK); + setbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_POL_MASK); else - clrbits_be32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_POL_MASK); + clrbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_POL_MASK); return 0; } @@ -146,7 +146,7 @@ static void bcm63xx_hsspi_activate_cs(struct bcm63xx_hsspi_priv *priv, set = DIV_ROUND_UP(2048, set); set &= SPI_PFL_CLK_FREQ_MASK; set |= SPI_PFL_CLK_RSTLOOP_MASK; - writel_be(set, priv->regs + SPI_PFL_CLK_REG(plat->cs)); + writel(set, priv->regs + SPI_PFL_CLK_REG(plat->cs)); /* profile signal */ set = 0; @@ -164,7 +164,7 @@ static void bcm63xx_hsspi_activate_cs(struct bcm63xx_hsspi_priv *priv, if (priv->speed > SPI_MAX_SYNC_CLOCK) set |= SPI_PFL_SIG_ASYNCIN_MASK; - clrsetbits_be32(priv->regs + SPI_PFL_SIG_REG(plat->cs), clr, set); + clrsetbits_32(priv->regs + SPI_PFL_SIG_REG(plat->cs), clr, set); /* global control */ set = 0; @@ -182,13 +182,13 @@ static void bcm63xx_hsspi_activate_cs(struct bcm63xx_hsspi_priv *priv, else set |= BIT(!plat->cs); - clrsetbits_be32(priv->regs + SPI_CTL_REG, clr, set); + clrsetbits_32(priv->regs + SPI_CTL_REG, clr, set); } static void bcm63xx_hsspi_deactivate_cs(struct bcm63xx_hsspi_priv *priv) { /* restore cs polarities */ - clrsetbits_be32(priv->regs + SPI_CTL_REG, SPI_CTL_CS_POL_MASK, + clrsetbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CS_POL_MASK, priv->cs_pols); } @@ -247,7 +247,7 @@ static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen, SPI_PFL_MODE_MDWRSZ_MASK; if (plat->mode & SPI_3WIRE) val |= SPI_PFL_MODE_3WIRE_MASK; - writel_be(val, priv->regs + SPI_PFL_MODE_REG(plat->cs)); + writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs)); /* transfer loop */ while (data_bytes > 0) { @@ -262,7 +262,7 @@ static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen, } /* set fifo operation */ - writew_be(opcode | (curr_step & HSSPI_FIFO_OP_BYTES_MASK), + writew(cpu_to_be16(opcode | (curr_step & HSSPI_FIFO_OP_BYTES_MASK)), priv->regs + HSSPI_FIFO_OP_REG); /* issue the transfer */ @@ -271,10 +271,10 @@ static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen, SPI_CMD_PFL_MASK; val |= (!plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK; - writel_be(val, priv->regs + SPI_CMD_REG); + writel(val, priv->regs + SPI_CMD_REG); /* wait for completion */ - ret = wait_for_bit_be32(priv->regs + SPI_STAT_REG, + ret = wait_for_bit_32(priv->regs + SPI_STAT_REG, SPI_STAT_SRCBUSY_MASK, false, 1000, false); if (ret) { @@ -349,48 +349,47 @@ static int bcm63xx_hsspi_probe(struct udevice *dev) return ret; ret = clk_enable(&clk); - if (ret < 0) + if (ret < 0 && ret != -ENOSYS) return ret; ret = clk_free(&clk); - if (ret < 0) + if (ret < 0 && ret != -ENOSYS) return ret; /* get clock rate */ ret = clk_get_by_name(dev, "pll", &clk); - if (ret < 0) + if (ret < 0 && ret != -ENOSYS) return ret; priv->clk_rate = clk_get_rate(&clk); ret = clk_free(&clk); - if (ret < 0) + if (ret < 0 && ret != -ENOSYS) return ret; /* perform reset */ ret = reset_get_by_index(dev, 0, &rst_ctl); - if (ret < 0) - return ret; - - ret = reset_deassert(&rst_ctl); - if (ret < 0) - return ret; + if (ret >= 0) { + ret = reset_deassert(&rst_ctl); + if (ret < 0) + return ret; + } ret = reset_free(&rst_ctl); if (ret < 0) return ret; /* initialize hardware */ - writel_be(0, priv->regs + SPI_IR_MASK_REG); + writel(0, priv->regs + SPI_IR_MASK_REG); /* clear pending interrupts */ - writel_be(SPI_IR_CLEAR_ALL, priv->regs + SPI_IR_STAT_REG); + writel(SPI_IR_CLEAR_ALL, priv->regs + SPI_IR_STAT_REG); /* enable clk gate */ - setbits_be32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_GATE_MASK); + setbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_GATE_MASK); /* read default cs polarities */ - priv->cs_pols = readl_be(priv->regs + SPI_CTL_REG) & + priv->cs_pols = readl(priv->regs + SPI_CTL_REG) & SPI_CTL_CS_POL_MASK; return 0; diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 56e2a046bf0..2498f0efb1a 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -440,6 +440,8 @@ static int dwc3_core_init(struct dwc3 *dwc) goto err0; } + dwc3_phy_setup(dwc); + ret = dwc3_core_soft_reset(dwc); if (ret) goto err0; @@ -514,8 +516,6 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GCTL, reg); - dwc3_phy_setup(dwc); - ret = dwc3_alloc_scratch_buffers(dwc); if (ret) goto err0; |