diff options
Diffstat (limited to 'drivers')
25 files changed, 1107 insertions, 237 deletions
diff --git a/drivers/fastboot/fb_mmc.c b/drivers/fastboot/fb_mmc.c index 8e74e50e918..2f3837e5591 100644 --- a/drivers/fastboot/fb_mmc.c +++ b/drivers/fastboot/fb_mmc.c @@ -40,7 +40,7 @@ static int raw_part_get_info_by_name(struct blk_desc *dev_desc, /* check for raw partition descriptor */ strcpy(env_desc_name, "fastboot_raw_partition_"); - strncat(env_desc_name, name, PART_NAME_LEN); + strlcat(env_desc_name, name, PART_NAME_LEN); raw_part_desc = strdup(env_get(env_desc_name)); if (raw_part_desc == NULL) return -ENODEV; @@ -61,7 +61,7 @@ static int raw_part_get_info_by_name(struct blk_desc *dev_desc, info->start = simple_strtoul(argv[0], NULL, 0); info->size = simple_strtoul(argv[1], NULL, 0); info->blksz = dev_desc->blksz; - strncpy((char *)info->name, name, PART_NAME_LEN); + strlcpy((char *)info->name, name, PART_NAME_LEN); if (raw_part_desc) { if (strcmp(strsep(&raw_part_desc, " "), "mmcpart") == 0) { @@ -114,7 +114,7 @@ static int part_get_info_by_name_or_alias(struct blk_desc **dev_desc, /* check for alias */ strcpy(env_alias_name, "fastboot_partition_alias_"); - strncat(env_alias_name, name, PART_NAME_LEN); + strlcat(env_alias_name, name, PART_NAME_LEN); aliased_part_name = env_get(env_alias_name); if (aliased_part_name != NULL) ret = do_get_part_info(dev_desc, aliased_part_name, diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c index 97a5dace15f..4b968205c2f 100644 --- a/drivers/firmware/scmi/sandbox-scmi_agent.c +++ b/drivers/firmware/scmi/sandbox-scmi_agent.c @@ -20,17 +20,18 @@ * processing. It simulates few of the SCMI services for some of the * SCMI protocols embedded in U-Boot. Currently: * - SCMI clock protocol: emulate 2 agents each exposing few clocks - * - SCMI reset protocol: emulate 1 agents each exposing a reset + * - SCMI reset protocol: emulate 1 agent exposing a reset controller + * - SCMI voltage domain protocol: emulate 1 agent exposing 2 regulators * - * Agent #0 simulates 2 clocks and 1 reset domain. + * Agent #0 simulates 2 clocks, 1 reset domain and 1 voltage domain. * See IDs in scmi0_clk[]/scmi0_reset[] and "sandbox-scmi-agent@0" in test.dts. * * Agent #1 simulates 1 clock. * See IDs in scmi1_clk[] and "sandbox-scmi-agent@1" in test.dts. * - * All clocks are default disabled and reset levels down. + * All clocks and regulators are default disabled and reset controller down. * - * This Driver exports sandbox_scmi_service_ct() for the test sequence to + * This Driver exports sandbox_scmi_service_ctx() for the test sequence to * get the state of the simulated services (clock state, rate, ...) and * check back-end device state reflects the request send through the * various uclass devices, as clocks and reset controllers. @@ -47,6 +48,11 @@ static struct sandbox_scmi_reset scmi0_reset[] = { { .id = 3 }, }; +static struct sandbox_scmi_voltd scmi0_voltd[] = { + { .id = 0, .voltage_uv = 3300000 }, + { .id = 1, .voltage_uv = 1800000 }, +}; + static struct sandbox_scmi_clk scmi1_clk[] = { { .id = 1, .rate = 44 }, }; @@ -83,6 +89,13 @@ static void debug_print_agent_state(struct udevice *dev, char *str) agent->reset_count, agent->reset_count ? agent->reset[0].asserted : -1, agent->reset_count > 1 ? agent->reset[1].asserted : -1); + dev_dbg(dev, " scmi%u_voltd (%zu): %u/%d, %u/%d, ...\n", + agent->idx, + agent->voltd_count, + agent->voltd_count ? agent->voltd[0].enabled : -1, + agent->voltd_count ? agent->voltd[0].voltage_uv : -1, + agent->voltd_count ? agent->voltd[1].enabled : -1, + agent->voltd_count ? agent->voltd[1].voltage_uv : -1); }; static struct sandbox_scmi_clk *get_scmi_clk_state(uint agent_id, uint clock_id) @@ -125,6 +138,20 @@ static struct sandbox_scmi_reset *get_scmi_reset_state(uint agent_id, return NULL; } +static struct sandbox_scmi_voltd *get_scmi_voltd_state(uint agent_id, + uint domain_id) +{ + size_t n; + + if (agent_id == 0) { + for (n = 0; n < ARRAY_SIZE(scmi0_voltd); n++) + if (scmi0_voltd[n].id == domain_id) + return scmi0_voltd + n; + } + + return NULL; +} + /* * Sandbox SCMI agent ops */ @@ -292,6 +319,160 @@ static int sandbox_scmi_rd_reset(struct udevice *dev, struct scmi_msg *msg) return 0; } +static int sandbox_scmi_voltd_attribs(struct udevice *dev, struct scmi_msg *msg) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + struct scmi_voltd_attr_in *in = NULL; + struct scmi_voltd_attr_out *out = NULL; + struct sandbox_scmi_voltd *voltd_state = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + in = (struct scmi_voltd_attr_in *)msg->in_msg; + out = (struct scmi_voltd_attr_out *)msg->out_msg; + + voltd_state = get_scmi_voltd_state(agent->idx, in->domain_id); + if (!voltd_state) { + dev_err(dev, "Unexpected domain ID %u\n", in->domain_id); + + out->status = SCMI_NOT_FOUND; + } else { + memset(out, 0, sizeof(*out)); + snprintf(out->name, sizeof(out->name), "regu%u", in->domain_id); + + out->status = SCMI_SUCCESS; + } + + return 0; +} + +static int sandbox_scmi_voltd_config_set(struct udevice *dev, + struct scmi_msg *msg) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + struct scmi_voltd_config_set_in *in = NULL; + struct scmi_voltd_config_set_out *out = NULL; + struct sandbox_scmi_voltd *voltd_state = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + in = (struct scmi_voltd_config_set_in *)msg->in_msg; + out = (struct scmi_voltd_config_set_out *)msg->out_msg; + + voltd_state = get_scmi_voltd_state(agent->idx, in->domain_id); + if (!voltd_state) { + dev_err(dev, "Unexpected domain ID %u\n", in->domain_id); + + out->status = SCMI_NOT_FOUND; + } else if (in->config & ~SCMI_VOLTD_CONFIG_MASK) { + dev_err(dev, "Invalid config value 0x%x\n", in->config); + + out->status = SCMI_INVALID_PARAMETERS; + } else if (in->config != SCMI_VOLTD_CONFIG_ON && + in->config != SCMI_VOLTD_CONFIG_OFF) { + dev_err(dev, "Unexpected custom value 0x%x\n", in->config); + + out->status = SCMI_INVALID_PARAMETERS; + } else { + voltd_state->enabled = in->config == SCMI_VOLTD_CONFIG_ON; + out->status = SCMI_SUCCESS; + } + + return 0; +} + +static int sandbox_scmi_voltd_config_get(struct udevice *dev, + struct scmi_msg *msg) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + struct scmi_voltd_config_get_in *in = NULL; + struct scmi_voltd_config_get_out *out = NULL; + struct sandbox_scmi_voltd *voltd_state = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + in = (struct scmi_voltd_config_get_in *)msg->in_msg; + out = (struct scmi_voltd_config_get_out *)msg->out_msg; + + voltd_state = get_scmi_voltd_state(agent->idx, in->domain_id); + if (!voltd_state) { + dev_err(dev, "Unexpected domain ID %u\n", in->domain_id); + + out->status = SCMI_NOT_FOUND; + } else { + if (voltd_state->enabled) + out->config = SCMI_VOLTD_CONFIG_ON; + else + out->config = SCMI_VOLTD_CONFIG_OFF; + + out->status = SCMI_SUCCESS; + } + + return 0; +} + +static int sandbox_scmi_voltd_level_set(struct udevice *dev, + struct scmi_msg *msg) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + struct scmi_voltd_level_set_in *in = NULL; + struct scmi_voltd_level_set_out *out = NULL; + struct sandbox_scmi_voltd *voltd_state = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + in = (struct scmi_voltd_level_set_in *)msg->in_msg; + out = (struct scmi_voltd_level_set_out *)msg->out_msg; + + voltd_state = get_scmi_voltd_state(agent->idx, in->domain_id); + if (!voltd_state) { + dev_err(dev, "Unexpected domain ID %u\n", in->domain_id); + + out->status = SCMI_NOT_FOUND; + } else { + voltd_state->voltage_uv = in->voltage_level; + out->status = SCMI_SUCCESS; + } + + return 0; +} + +static int sandbox_scmi_voltd_level_get(struct udevice *dev, + struct scmi_msg *msg) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + struct scmi_voltd_level_get_in *in = NULL; + struct scmi_voltd_level_get_out *out = NULL; + struct sandbox_scmi_voltd *voltd_state = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + in = (struct scmi_voltd_level_get_in *)msg->in_msg; + out = (struct scmi_voltd_level_get_out *)msg->out_msg; + + voltd_state = get_scmi_voltd_state(agent->idx, in->domain_id); + if (!voltd_state) { + dev_err(dev, "Unexpected domain ID %u\n", in->domain_id); + + out->status = SCMI_NOT_FOUND; + } else { + out->voltage_level = voltd_state->voltage_uv; + out->status = SCMI_SUCCESS; + } + + return 0; +} + static int sandbox_scmi_test_process_msg(struct udevice *dev, struct scmi_msg *msg) { @@ -318,6 +499,22 @@ static int sandbox_scmi_test_process_msg(struct udevice *dev, break; } break; + case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN: + switch (msg->message_id) { + case SCMI_VOLTAGE_DOMAIN_ATTRIBUTES: + return sandbox_scmi_voltd_attribs(dev, msg); + case SCMI_VOLTAGE_DOMAIN_CONFIG_SET: + return sandbox_scmi_voltd_config_set(dev, msg); + case SCMI_VOLTAGE_DOMAIN_CONFIG_GET: + return sandbox_scmi_voltd_config_get(dev, msg); + case SCMI_VOLTAGE_DOMAIN_LEVEL_SET: + return sandbox_scmi_voltd_level_set(dev, msg); + case SCMI_VOLTAGE_DOMAIN_LEVEL_GET: + return sandbox_scmi_voltd_level_get(dev, msg); + default: + break; + } + break; case SCMI_PROTOCOL_ID_BASE: case SCMI_PROTOCOL_ID_POWER_DOMAIN: case SCMI_PROTOCOL_ID_SYSTEM: @@ -369,6 +566,8 @@ static int sandbox_scmi_test_probe(struct udevice *dev) .clk_count = ARRAY_SIZE(scmi0_clk), .reset = scmi0_reset, .reset_count = ARRAY_SIZE(scmi0_reset), + .voltd = scmi0_voltd, + .voltd_count = ARRAY_SIZE(scmi0_voltd), }; break; case '1': diff --git a/drivers/firmware/scmi/sandbox-scmi_devices.c b/drivers/firmware/scmi/sandbox-scmi_devices.c index 69239a198f0..66a67928817 100644 --- a/drivers/firmware/scmi/sandbox-scmi_devices.c +++ b/drivers/firmware/scmi/sandbox-scmi_devices.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2020, Linaro Limited + * Copyright (C) 2020-2021, Linaro Limited */ #define LOG_CATEGORY UCLASS_MISC @@ -8,11 +8,13 @@ #include <common.h> #include <clk.h> #include <dm.h> +#include <log.h> #include <malloc.h> #include <reset.h> #include <asm/io.h> #include <asm/scmi_test.h> #include <dm/device_compat.h> +#include <power/regulator.h> /* * Simulate to some extent a SCMI exchange. @@ -23,16 +25,19 @@ #define SCMI_TEST_DEVICES_CLK_COUNT 3 #define SCMI_TEST_DEVICES_RD_COUNT 1 +#define SCMI_TEST_DEVICES_VOLTD_COUNT 2 /* * struct sandbox_scmi_device_priv - Storage for device handles used by test * @clk: Array of clock instances used by tests * @reset_clt: Array of the reset controller instances used by tests + * @regulators: Array of regulator device references used by the tests * @devices: Resources exposed by sandbox_scmi_devices_ctx() */ struct sandbox_scmi_device_priv { struct clk clk[SCMI_TEST_DEVICES_CLK_COUNT]; struct reset_ctl reset_ctl[SCMI_TEST_DEVICES_RD_COUNT]; + struct udevice *regulators[SCMI_TEST_DEVICES_VOLTD_COUNT]; struct sandbox_scmi_devices devices; }; @@ -76,6 +81,8 @@ static int sandbox_scmi_devices_probe(struct udevice *dev) .clk_count = SCMI_TEST_DEVICES_CLK_COUNT, .reset = priv->reset_ctl, .reset_count = SCMI_TEST_DEVICES_RD_COUNT, + .regul = priv->regulators, + .regul_count = SCMI_TEST_DEVICES_VOLTD_COUNT, }; for (n = 0; n < SCMI_TEST_DEVICES_CLK_COUNT; n++) { @@ -94,8 +101,24 @@ static int sandbox_scmi_devices_probe(struct udevice *dev) } } + for (n = 0; n < SCMI_TEST_DEVICES_VOLTD_COUNT; n++) { + char name[32]; + + ret = snprintf(name, sizeof(name), "regul%zu-supply", n); + assert(ret >= 0 && ret < sizeof(name)); + + ret = device_get_supply_regulator(dev, name, + priv->devices.regul + n); + if (ret) { + dev_err(dev, "%s: Failed on voltd %zu\n", __func__, n); + goto err_regul; + } + } + return 0; +err_regul: + n = SCMI_TEST_DEVICES_RD_COUNT; err_reset: for (; n > 0; n--) reset_free(priv->devices.reset + n - 1); diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index 527163b5ce3..4f5870b4838 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -80,6 +80,16 @@ static int scmi_bind_protocols(struct udevice *dev) if (IS_ENABLED(CONFIG_RESET_SCMI)) drv = DM_DRIVER_GET(scmi_reset_domain); break; + case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN: + if (IS_ENABLED(CONFIG_DM_REGULATOR_SCMI)) { + node = ofnode_find_subnode(node, "regulators"); + if (!ofnode_valid(node)) { + dev_err(dev, "no regulators node\n"); + return -ENXIO; + } + drv = DM_DRIVER_GET(scmi_voltage_domain); + } + break; default: break; } diff --git a/drivers/firmware/scmi/smt.c b/drivers/firmware/scmi/smt.c index 60b9d499b79..f1915c00741 100644 --- a/drivers/firmware/scmi/smt.c +++ b/drivers/firmware/scmi/smt.c @@ -41,8 +41,13 @@ int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt) if (ret) return ret; - faddr = cpu_to_fdt32(resource.start); - paddr = ofnode_translate_address(args.node, &faddr); + /* TEMP workaround for ofnode_read_resource translation issue */ + if (of_live_active()) { + paddr = resource.start; + } else { + faddr = cpu_to_fdt32(resource.start); + paddr = ofnode_translate_address(args.node, &faddr); + } smt->size = resource_size(&resource); if (smt->size < sizeof(struct scmi_smt_header)) { @@ -56,8 +61,10 @@ int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt) #ifdef CONFIG_ARM if (dcache_status()) - mmu_set_region_dcache_behaviour((uintptr_t)smt->buf, - smt->size, DCACHE_OFF); + mmu_set_region_dcache_behaviour(ALIGN_DOWN((uintptr_t)smt->buf, MMU_SECTION_SIZE), + ALIGN(smt->size, MMU_SECTION_SIZE), + DCACHE_OFF); + #endif return 0; diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index f24db87ef06..e4e7f58c39a 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -713,17 +713,6 @@ int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags) return dm_gpio_clrset_flags(desc, GPIOD_MASK_DIR, flags); } -int dm_gpio_set_dir(struct gpio_desc *desc) -{ - int ret; - - ret = check_reserved(desc, "set_dir"); - if (ret) - return ret; - - return _dm_gpio_set_flags(desc, desc->flags); -} - int dm_gpios_clrset_flags(struct gpio_desc *desc, int count, ulong clr, ulong set) { diff --git a/drivers/i2c/i2c-gpio.c b/drivers/i2c/i2c-gpio.c index a301a4460b3..cf8f8f40359 100644 --- a/drivers/i2c/i2c-gpio.c +++ b/drivers/i2c/i2c-gpio.c @@ -48,12 +48,13 @@ static int i2c_gpio_sda_get(struct i2c_gpio_bus *bus) static void i2c_gpio_sda_set(struct i2c_gpio_bus *bus, int bit) { struct gpio_desc *sda = &bus->gpios[PIN_SDA]; + ulong flags; if (bit) - sda->flags = (sda->flags & ~GPIOD_IS_OUT) | GPIOD_IS_IN; + flags = GPIOD_IS_IN; else - sda->flags = (sda->flags & (~GPIOD_IS_IN & ~GPIOD_IS_OUT_ACTIVE)) | GPIOD_IS_OUT; - dm_gpio_set_dir(sda); + flags = GPIOD_IS_OUT; + dm_gpio_clrset_flags(sda, GPIOD_MASK_DIR, flags); } static void i2c_gpio_scl_set(struct i2c_gpio_bus *bus, int bit) @@ -62,16 +63,14 @@ static void i2c_gpio_scl_set(struct i2c_gpio_bus *bus, int bit) int count = 0; if (bit) { - scl->flags = (scl->flags & ~GPIOD_IS_OUT) | GPIOD_IS_IN; - dm_gpio_set_dir(scl); + dm_gpio_clrset_flags(scl, GPIOD_MASK_DIR, GPIOD_IS_IN); while (!dm_gpio_get_value(scl) && count++ < 100000) udelay(1); if (!dm_gpio_get_value(scl)) pr_err("timeout waiting on slave to release scl\n"); } else { - scl->flags = (scl->flags & (~GPIOD_IS_IN & ~GPIOD_IS_OUT_ACTIVE)) | GPIOD_IS_OUT; - dm_gpio_set_dir(scl); + dm_gpio_clrset_flags(scl, GPIOD_MASK_DIR, GPIOD_IS_OUT); } } @@ -79,11 +78,11 @@ static void i2c_gpio_scl_set(struct i2c_gpio_bus *bus, int bit) static void i2c_gpio_scl_set_output_only(struct i2c_gpio_bus *bus, int bit) { struct gpio_desc *scl = &bus->gpios[PIN_SCL]; - scl->flags = (scl->flags & (~GPIOD_IS_IN & ~GPIOD_IS_OUT_ACTIVE)) | GPIOD_IS_OUT; + ulong flags = GPIOD_IS_OUT; if (bit) - scl->flags |= GPIOD_IS_OUT_ACTIVE; - dm_gpio_set_dir(scl); + flags |= GPIOD_IS_OUT_ACTIVE; + dm_gpio_clrset_flags(scl, GPIOD_MASK_DIR, flags); } static void i2c_gpio_write_bit(struct i2c_gpio_bus *bus, int delay, uchar bit) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 7d2a2997797..c650471ff7c 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -368,8 +368,22 @@ config WINBOND_W83627 config QFW bool help - Hidden option to enable QEMU fw_cfg interface. This will be selected by - either CONFIG_CMD_QFW or CONFIG_GENERATE_ACPI_TABLE. + Hidden option to enable QEMU fw_cfg interface and uclass. This will + be selected by either CONFIG_CMD_QFW or CONFIG_GENERATE_ACPI_TABLE. + +config QFW_PIO + bool + depends on QFW + help + Hidden option to enable PIO QEMU fw_cfg interface. This will be + selected by the appropriate QEMU board. + +config QFW_MMIO + bool + depends on QFW + help + Hidden option to enable MMIO QEMU fw_cfg interface. This will be + selected by the appropriate QEMU board. config I2C_EEPROM bool "Enable driver for generic I2C-attached EEPROMs" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 1a493960074..0c67d43a5d4 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -55,7 +55,12 @@ obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o obj-$(CONFIG_P2SB) += p2sb-uclass.o obj-$(CONFIG_PCA9551_LED) += pca9551_led.o obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o -obj-$(CONFIG_QFW) += qfw.o +ifdef CONFIG_QFW +obj-y += qfw.o +obj-$(CONFIG_QFW_PIO) += qfw_pio.o +obj-$(CONFIG_QFW_MMIO) += qfw_mmio.o +obj-$(CONFIG_SANDBOX) += qfw_sandbox.o +endif obj-$(CONFIG_ROCKCHIP_EFUSE) += rockchip-efuse.o obj-$(CONFIG_ROCKCHIP_OTP) += rockchip-otp.o obj-$(CONFIG_SANDBOX) += syscon_sandbox.o misc_sandbox.o diff --git a/drivers/misc/qfw.c b/drivers/misc/qfw.c index f6eb6583ed0..ea00be88a8d 100644 --- a/drivers/misc/qfw.c +++ b/drivers/misc/qfw.c @@ -1,25 +1,22 @@ // SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com> + * (C) Copyright 2021 Asherah Connor <ashe@kivikakk.ee> */ +#define LOG_CATEGORY UCLASS_QFW + #include <common.h> #include <command.h> #include <errno.h> #include <log.h> #include <malloc.h> #include <qfw.h> -#include <asm/io.h> +#include <dm.h> +#include <misc.h> #ifdef CONFIG_GENERATE_ACPI_TABLE #include <asm/tables.h> #endif -#include <linux/list.h> - -static bool fwcfg_present; -static bool fwcfg_dma_present; -static struct fw_cfg_arch_ops *fwcfg_arch_ops; - -static LIST_HEAD(fw_list); #ifdef CONFIG_GENERATE_ACPI_TABLE /* @@ -32,7 +29,8 @@ static LIST_HEAD(fw_list); * be ignored. * @return: 0 on success, or negative value on failure */ -static int bios_linker_allocate(struct bios_linker_entry *entry, ulong *addr) +static int bios_linker_allocate(struct udevice *dev, + struct bios_linker_entry *entry, ulong *addr) { uint32_t size, align; struct fw_file *file; @@ -45,7 +43,7 @@ static int bios_linker_allocate(struct bios_linker_entry *entry, ulong *addr) return -EINVAL; } - file = qemu_fwcfg_find_file(entry->alloc.file); + file = qfw_find_file(dev, entry->alloc.file); if (!file) { printf("error: can't find file %s\n", entry->alloc.file); return -ENOENT; @@ -75,8 +73,8 @@ static int bios_linker_allocate(struct bios_linker_entry *entry, ulong *addr) debug("bios_linker_allocate: allocate file %s, size %u, zone %d, align %u, addr 0x%lx\n", file->cfg.name, size, entry->alloc.zone, align, aligned_addr); - qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select), - size, (void *)aligned_addr); + qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, + (void *)aligned_addr); file->addr = aligned_addr; /* adjust address for low memory allocation */ @@ -94,16 +92,17 @@ static int bios_linker_allocate(struct bios_linker_entry *entry, ulong *addr) * ACPI tables * @return: 0 on success, or negative value on failure */ -static int bios_linker_add_pointer(struct bios_linker_entry *entry) +static int bios_linker_add_pointer(struct udevice *dev, + struct bios_linker_entry *entry) { struct fw_file *dest, *src; uint32_t offset = le32_to_cpu(entry->pointer.offset); uint64_t pointer = 0; - dest = qemu_fwcfg_find_file(entry->pointer.dest_file); + dest = qfw_find_file(dev, entry->pointer.dest_file); if (!dest || !dest->addr) return -ENOENT; - src = qemu_fwcfg_find_file(entry->pointer.src_file); + src = qfw_find_file(dev, entry->pointer.src_file); if (!src || !src->addr) return -ENOENT; @@ -127,13 +126,14 @@ static int bios_linker_add_pointer(struct bios_linker_entry *entry) * checksums * @return: 0 on success, or negative value on failure */ -static int bios_linker_add_checksum(struct bios_linker_entry *entry) +static int bios_linker_add_checksum(struct udevice *dev, + struct bios_linker_entry *entry) { struct fw_file *file; uint8_t *data, cksum = 0; uint8_t *cksum_start; - file = qemu_fwcfg_find_file(entry->cksum.file); + file = qfw_find_file(dev, entry->cksum.file); if (!file || !file->addr) return -ENOENT; @@ -149,20 +149,27 @@ static int bios_linker_add_checksum(struct bios_linker_entry *entry) /* This function loads and patches ACPI tables provided by QEMU */ ulong write_acpi_tables(ulong addr) { - int i, ret = 0; + int i, ret; struct fw_file *file; struct bios_linker_entry *table_loader; struct bios_linker_entry *entry; uint32_t size; + struct udevice *dev; + + ret = qfw_get_dev(&dev); + if (ret) { + printf("error: no qfw\n"); + return addr; + } /* make sure fw_list is loaded */ - ret = qemu_fwcfg_read_firmware_list(); + ret = qfw_read_firmware_list(dev); if (ret) { printf("error: can't read firmware file list\n"); return addr; } - file = qemu_fwcfg_find_file("etc/table-loader"); + file = qfw_find_file(dev, "etc/table-loader"); if (!file) { printf("error: can't find etc/table-loader\n"); return addr; @@ -180,24 +187,23 @@ ulong write_acpi_tables(ulong addr) return addr; } - qemu_fwcfg_read_entry(be16_to_cpu(file->cfg.select), - size, table_loader); + qfw_read_entry(dev, be16_to_cpu(file->cfg.select), size, table_loader); for (i = 0; i < (size / sizeof(*entry)); i++) { entry = table_loader + i; switch (le32_to_cpu(entry->command)) { case BIOS_LINKER_LOADER_COMMAND_ALLOCATE: - ret = bios_linker_allocate(entry, &addr); + ret = bios_linker_allocate(dev, entry, &addr); if (ret) goto out; break; case BIOS_LINKER_LOADER_COMMAND_ADD_POINTER: - ret = bios_linker_add_pointer(entry); + ret = bios_linker_add_pointer(dev, entry); if (ret) goto out; break; case BIOS_LINKER_LOADER_COMMAND_ADD_CHECKSUM: - ret = bios_linker_add_checksum(entry); + ret = bios_linker_add_checksum(dev, entry); if (ret) goto out; break; @@ -209,9 +215,9 @@ ulong write_acpi_tables(ulong addr) out: if (ret) { struct fw_cfg_file_iter iter; - for (file = qemu_fwcfg_file_iter_init(&iter); - !qemu_fwcfg_file_iter_end(&iter); - file = qemu_fwcfg_file_iter_next(&iter)) { + for (file = qfw_file_iter_init(dev, &iter); + !qfw_file_iter_end(&iter); + file = qfw_file_iter_next(&iter)) { if (file->addr) { free((void *)file->addr); file->addr = 0; @@ -225,170 +231,89 @@ out: ulong acpi_get_rsdp_addr(void) { + int ret; struct fw_file *file; + struct udevice *dev; - file = qemu_fwcfg_find_file("etc/acpi/rsdp"); + ret = qfw_get_dev(&dev); + if (ret) { + printf("error: no qfw\n"); + return 0; + } + + file = qfw_find_file(dev, "etc/acpi/rsdp"); return file->addr; } #endif -/* Read configuration item using fw_cfg PIO interface */ -static void qemu_fwcfg_read_entry_pio(uint16_t entry, - uint32_t size, void *address) +static void qfw_read_entry_io(struct qfw_dev *qdev, u16 entry, u32 size, + void *address) { - debug("qemu_fwcfg_read_entry_pio: entry 0x%x, size %u address %p\n", - entry, size, address); + struct dm_qfw_ops *ops = dm_qfw_get_ops(qdev->dev); + + debug("%s: entry 0x%x, size %u address %p\n", __func__, entry, size, + address); - return fwcfg_arch_ops->arch_read_pio(entry, size, address); + ops->read_entry_io(qdev->dev, entry, size, address); } -/* Read configuration item using fw_cfg DMA interface */ -static void qemu_fwcfg_read_entry_dma(uint16_t entry, - uint32_t size, void *address) +static void qfw_read_entry_dma(struct qfw_dev *qdev, u16 entry, u32 size, + void *address) { - struct fw_cfg_dma_access dma; + struct dm_qfw_ops *ops = dm_qfw_get_ops(qdev->dev); - dma.length = cpu_to_be32(size); - dma.address = cpu_to_be64((uintptr_t)address); - dma.control = cpu_to_be32(FW_CFG_DMA_READ); + struct qfw_dma dma = { + .length = cpu_to_be32(size), + .address = cpu_to_be64((uintptr_t)address), + .control = cpu_to_be32(FW_CFG_DMA_READ), + }; /* - * writting FW_CFG_INVALID will cause read operation to resume at - * last offset, otherwise read will start at offset 0 + * writing FW_CFG_INVALID will cause read operation to resume at last + * offset, otherwise read will start at offset 0 */ if (entry != FW_CFG_INVALID) dma.control |= cpu_to_be32(FW_CFG_DMA_SELECT | (entry << 16)); - barrier(); - - debug("qemu_fwcfg_read_entry_dma: entry 0x%x, size %u address %p, control 0x%x\n", + debug("%s: entry 0x%x, size %u address %p, control 0x%x\n", __func__, entry, size, address, be32_to_cpu(dma.control)); - fwcfg_arch_ops->arch_read_dma(&dma); -} + barrier(); -bool qemu_fwcfg_present(void) -{ - return fwcfg_present; + ops->read_entry_dma(qdev->dev, &dma); } -bool qemu_fwcfg_dma_present(void) +void qfw_read_entry(struct udevice *dev, u16 entry, u32 size, void *address) { - return fwcfg_dma_present; -} + struct qfw_dev *qdev = dev_get_uclass_priv(dev); -void qemu_fwcfg_read_entry(uint16_t entry, uint32_t length, void *address) -{ - if (fwcfg_dma_present) - qemu_fwcfg_read_entry_dma(entry, length, address); + if (qdev->dma_present) + qfw_read_entry_dma(qdev, entry, size, address); else - qemu_fwcfg_read_entry_pio(entry, length, address); + qfw_read_entry_io(qdev, entry, size, address); } -int qemu_fwcfg_online_cpus(void) +int qfw_register(struct udevice *dev) { - uint16_t nb_cpus; + struct qfw_dev *qdev = dev_get_uclass_priv(dev); + u32 qemu, dma_enabled; - if (!fwcfg_present) - return -ENODEV; - - qemu_fwcfg_read_entry(FW_CFG_NB_CPUS, 2, &nb_cpus); - - return le16_to_cpu(nb_cpus); -} + qdev->dev = dev; + INIT_LIST_HEAD(&qdev->fw_list); -int qemu_fwcfg_read_firmware_list(void) -{ - int i; - uint32_t count; - struct fw_file *file; - struct list_head *entry; - - /* don't read it twice */ - if (!list_empty(&fw_list)) - return 0; + qfw_read_entry_io(qdev, FW_CFG_SIGNATURE, 4, &qemu); + if (be32_to_cpu(qemu) != QEMU_FW_CFG_SIGNATURE) + return -ENODEV; - qemu_fwcfg_read_entry(FW_CFG_FILE_DIR, 4, &count); - if (!count) - return 0; - - count = be32_to_cpu(count); - for (i = 0; i < count; i++) { - file = malloc(sizeof(*file)); - if (!file) { - printf("error: allocating resource\n"); - goto err; - } - qemu_fwcfg_read_entry(FW_CFG_INVALID, - sizeof(struct fw_cfg_file), &file->cfg); - file->addr = 0; - list_add_tail(&file->list, &fw_list); - } + qfw_read_entry_io(qdev, FW_CFG_ID, 1, &dma_enabled); + if (dma_enabled & FW_CFG_DMA_ENABLED) + qdev->dma_present = true; return 0; - -err: - list_for_each(entry, &fw_list) { - file = list_entry(entry, struct fw_file, list); - free(file); - } - - return -ENOMEM; } -struct fw_file *qemu_fwcfg_find_file(const char *name) -{ - struct list_head *entry; - struct fw_file *file; - - list_for_each(entry, &fw_list) { - file = list_entry(entry, struct fw_file, list); - if (!strcmp(file->cfg.name, name)) - return file; - } - - return NULL; -} - -struct fw_file *qemu_fwcfg_file_iter_init(struct fw_cfg_file_iter *iter) -{ - iter->entry = fw_list.next; - return list_entry((struct list_head *)iter->entry, - struct fw_file, list); -} - -struct fw_file *qemu_fwcfg_file_iter_next(struct fw_cfg_file_iter *iter) -{ - iter->entry = ((struct list_head *)iter->entry)->next; - return list_entry((struct list_head *)iter->entry, - struct fw_file, list); -} - -bool qemu_fwcfg_file_iter_end(struct fw_cfg_file_iter *iter) -{ - return iter->entry == &fw_list; -} - -void qemu_fwcfg_init(struct fw_cfg_arch_ops *ops) -{ - uint32_t qemu; - uint32_t dma_enabled; - - fwcfg_present = false; - fwcfg_dma_present = false; - fwcfg_arch_ops = NULL; - - if (!ops || !ops->arch_read_pio || !ops->arch_read_dma) - return; - fwcfg_arch_ops = ops; - - qemu_fwcfg_read_entry_pio(FW_CFG_SIGNATURE, 4, &qemu); - if (be32_to_cpu(qemu) == QEMU_FW_CFG_SIGNATURE) - fwcfg_present = true; - - if (fwcfg_present) { - qemu_fwcfg_read_entry_pio(FW_CFG_ID, 1, &dma_enabled); - if (dma_enabled & FW_CFG_DMA_ENABLED) - fwcfg_dma_present = true; - } -} +UCLASS_DRIVER(qfw) = { + .id = UCLASS_QFW, + .name = "qfw", + .per_device_auto = sizeof(struct qfw_dev), +}; diff --git a/drivers/misc/qfw_mmio.c b/drivers/misc/qfw_mmio.c new file mode 100644 index 00000000000..f397384054a --- /dev/null +++ b/drivers/misc/qfw_mmio.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * MMIO interface for QFW + * + * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com> + * (C) Copyright 2021 Asherah Connor <ashe@kivikakk.ee> + */ + +#define LOG_CATEGORY UCLASS_QFW + +#include <asm/types.h> +#include <asm/io.h> +#include <dm.h> +#include <dm/device.h> +#include <qfw.h> + +struct qfw_mmio { + /* + * Each access to the 64-bit data register can be 8/16/32/64 bits wide. + */ + union { + u8 data8; + u16 data16; + u32 data32; + u64 data64; + }; + u16 selector; + u8 padding[6]; + u64 dma; +}; + +struct qfw_mmio_plat { + volatile struct qfw_mmio *mmio; +}; + +static void qfw_mmio_read_entry_io(struct udevice *dev, u16 entry, u32 size, + void *address) +{ + struct qfw_mmio_plat *plat = dev_get_plat(dev); + + /* + * writing FW_CFG_INVALID will cause read operation to resume at last + * offset, otherwise read will start at offset 0 + * + * Note: on platform where the control register is MMIO, the register + * is big endian. + */ + if (entry != FW_CFG_INVALID) + plat->mmio->selector = cpu_to_be16(entry); + + /* the endianness of data register is string-preserving */ + while (size >= 8) { + *(u64 *)address = plat->mmio->data64; + address += 8; + size -= 8; + } + while (size >= 4) { + *(u32 *)address = plat->mmio->data32; + address += 4; + size -= 4; + } + while (size >= 2) { + *(u16 *)address = plat->mmio->data16; + address += 2; + size -= 2; + } + while (size >= 1) { + *(u8 *)address = plat->mmio->data8; + address += 1; + size -= 1; + } +} + +/* Read configuration item using fw_cfg DMA interface */ +static void qfw_mmio_read_entry_dma(struct udevice *dev, struct qfw_dma *dma) +{ + struct qfw_mmio_plat *plat = dev_get_plat(dev); + + /* the DMA address register is big-endian */ + plat->mmio->dma = cpu_to_be64((uintptr_t)dma); + + while (be32_to_cpu(dma->control) & ~FW_CFG_DMA_ERROR); +} + +static int qfw_mmio_of_to_plat(struct udevice *dev) +{ + struct qfw_mmio_plat *plat = dev_get_plat(dev); + + plat->mmio = map_physmem(dev_read_addr(dev), + sizeof(struct qfw_mmio), + MAP_NOCACHE); + + return 0; +} + +static int qfw_mmio_probe(struct udevice *dev) +{ + return qfw_register(dev); +} + +static struct dm_qfw_ops qfw_mmio_ops = { + .read_entry_io = qfw_mmio_read_entry_io, + .read_entry_dma = qfw_mmio_read_entry_dma, +}; + +static const struct udevice_id qfw_mmio_ids[] = { + { .compatible = "qemu,fw-cfg-mmio" }, + {} +}; + +U_BOOT_DRIVER(qfw_mmio) = { + .name = "qfw_mmio", + .id = UCLASS_QFW, + .of_match = qfw_mmio_ids, + .plat_auto = sizeof(struct qfw_mmio_plat), + .of_to_plat = qfw_mmio_of_to_plat, + .probe = qfw_mmio_probe, + .ops = &qfw_mmio_ops, +}; diff --git a/drivers/misc/qfw_pio.c b/drivers/misc/qfw_pio.c new file mode 100644 index 00000000000..e2f628d3383 --- /dev/null +++ b/drivers/misc/qfw_pio.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PIO interface for QFW + * + * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com> + * (C) Copyright 2021 Asherah Connor <ashe@kivikakk.ee> + */ + +#define LOG_CATEGORY UCLASS_QFW + +#include <asm/io.h> +#include <dm/device.h> +#include <qfw.h> + +/* + * PIO ports are correct for x86, which appears to be the only arch that uses + * PIO. + */ +#define FW_CONTROL_PORT 0x510 +#define FW_DATA_PORT 0x511 +#define FW_DMA_PORT_LOW 0x514 +#define FW_DMA_PORT_HIGH 0x518 + +static void qfw_pio_read_entry_io(struct udevice *dev, u16 entry, u32 size, + void *address) +{ + /* + * writing FW_CFG_INVALID will cause read operation to resume at last + * offset, otherwise read will start at offset 0 + * + * Note: on platform where the control register is IO port, the + * endianness is little endian. + */ + if (entry != FW_CFG_INVALID) + outw(cpu_to_le16(entry), FW_CONTROL_PORT); + + /* the endianness of data register is string-preserving */ + u32 i = 0; + u8 *data = address; + + while (size--) + data[i++] = inb(FW_DATA_PORT); +} + +/* Read configuration item using fw_cfg DMA interface */ +static void qfw_pio_read_entry_dma(struct udevice *dev, struct qfw_dma *dma) +{ + /* the DMA address register is big-endian */ + outl(cpu_to_be32((uintptr_t)dma), FW_DMA_PORT_HIGH); + + while (be32_to_cpu(dma->control) & ~FW_CFG_DMA_ERROR); +} + +static int qfw_pio_probe(struct udevice *dev) +{ + return qfw_register(dev); +} + +static struct dm_qfw_ops qfw_pio_ops = { + .read_entry_io = qfw_pio_read_entry_io, + .read_entry_dma = qfw_pio_read_entry_dma, +}; + +U_BOOT_DRIVER(qfw_pio) = { + .name = "qfw_pio", + .id = UCLASS_QFW, + .probe = qfw_pio_probe, + .ops = &qfw_pio_ops, +}; diff --git a/drivers/misc/qfw_sandbox.c b/drivers/misc/qfw_sandbox.c new file mode 100644 index 00000000000..b09974d33bd --- /dev/null +++ b/drivers/misc/qfw_sandbox.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sandbox interface for QFW + * + * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com> + * (C) Copyright 2021 Asherah Connor <ashe@kivikakk.ee> + */ + +#define LOG_CATEGORY UCLASS_QFW + +#include <asm/types.h> +#include <asm/io.h> +#include <dm.h> +#include <dm/device.h> +#include <qfw.h> + +struct qfw_sandbox_plat { + u8 file_dir_offset; +}; + +static void qfw_sandbox_read_entry_io(struct udevice *dev, u16 entry, u32 size, + void *address) +{ + debug("%s: entry 0x%x size %u address %p\n", __func__, entry, size, + address); + + switch (entry) { + case FW_CFG_SIGNATURE: + if (size == 4) + *((u32 *)address) = cpu_to_be32(QEMU_FW_CFG_SIGNATURE); + break; + case FW_CFG_ID: + /* Advertise DMA support */ + if (size == 1) + *((u8 *)address) = FW_CFG_DMA_ENABLED; + break; + default: + debug("%s got unsupported entry 0x%x\n", __func__, entry); + /* + * Sandbox driver doesn't support other entries here, assume we use DMA + * to read them -- the uclass driver will exclusively use it when + * advertised. + */ + } +} + +static void qfw_sandbox_read_entry_dma(struct udevice *dev, struct qfw_dma *dma) +{ + u16 entry; + u32 control = be32_to_cpu(dma->control); + void *address = (void *)be64_to_cpu(dma->address); + u32 length = be32_to_cpu(dma->length); + struct qfw_sandbox_plat *plat = dev_get_plat(dev); + struct fw_cfg_file *file; + + debug("%s\n", __func__); + + if (!(control & FW_CFG_DMA_READ)) + return; + + if (control & FW_CFG_DMA_SELECT) { + /* Start new read. */ + entry = control >> 16; + + /* Arbitrary values to be used by tests. */ + switch (entry) { + case FW_CFG_NB_CPUS: + if (length == 2) + *((u16 *)address) = cpu_to_le16(5); + break; + case FW_CFG_FILE_DIR: + if (length == 4) { + *((u32 *)address) = cpu_to_be32(2); + plat->file_dir_offset = 1; + } + break; + default: + debug("%s got unsupported entry 0x%x\n", __func__, + entry); + } + } else if (plat->file_dir_offset && length == 64) { + file = address; + switch (plat->file_dir_offset) { + case 1: + file->size = cpu_to_be32(8); + file->select = cpu_to_be16(FW_CFG_FILE_FIRST); + strcpy(file->name, "test-one"); + plat->file_dir_offset++; + break; + case 2: + file->size = cpu_to_be32(8); + file->select = cpu_to_be16(FW_CFG_FILE_FIRST + 1); + strcpy(file->name, "test-two"); + plat->file_dir_offset++; + break; + } + } + + /* + * Signal that we are finished. No-one checks this in sandbox -- + * normally the platform-specific driver looks for it -- but let's + * replicate the behaviour in case someone relies on it later. + */ + dma->control = 0; +} + +static int qfw_sandbox_probe(struct udevice *dev) +{ + return qfw_register(dev); +} + +static struct dm_qfw_ops qfw_sandbox_ops = { + .read_entry_io = qfw_sandbox_read_entry_io, + .read_entry_dma = qfw_sandbox_read_entry_dma, +}; + +U_BOOT_DRIVER(qfw_sandbox) = { + .name = "qfw_sandbox", + .id = UCLASS_QFW, + .plat_auto = sizeof(struct qfw_sandbox_plat), + .probe = qfw_sandbox_probe, + .ops = &qfw_sandbox_ops, +}; + +U_BOOT_DRVINFO(qfw_sandbox) = { + .name = "qfw_sandbox", +}; diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 6557faddf94..3679ee727e9 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -3559,6 +3559,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", __func__, page); instr->state = MTD_ERASE_FAILED; + instr->fail_addr = + ((loff_t)page << chip->page_shift); goto erase_exit; } diff --git a/drivers/pci/pci_auto.c b/drivers/pci/pci_auto.c index da76148c583..05663c72b4b 100644 --- a/drivers/pci/pci_auto.c +++ b/drivers/pci/pci_auto.c @@ -88,8 +88,9 @@ static void dm_pciauto_setup_device(struct udevice *dev, int bars_num, else bar_res = mem; - debug("PCI Autoconfig: BAR %d, %s, size=0x%llx, ", + debug("PCI Autoconfig: BAR %d, %s%s, size=0x%llx, ", bar_nr, bar_res == prefetch ? "Prf" : "Mem", + found_mem64 ? "64" : "", (unsigned long long)bar_size); } diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 7b4c0f02c60..89099fde573 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_$(SPL_)PMIC_LP87565) += lp87565.o obj-$(CONFIG_PMIC_STPMIC1) += stpmic1.o obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o -obj-$(CONFIG_POWER_MAX77696) += pmic_max77696.o obj-$(CONFIG_POWER_MUIC_MAX8997) += muic_max8997.o obj-$(CONFIG_POWER_PCA9450) += pmic_pca9450.o obj-$(CONFIG_POWER_PFUZE100) += pmic_pfuze100.o diff --git a/drivers/power/pmic/pmic_max77696.c b/drivers/power/pmic/pmic_max77696.c deleted file mode 100644 index f3a73d6405f..00000000000 --- a/drivers/power/pmic/pmic_max77696.c +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2015 Freescale Semiconductor, Inc. - * Fabio Estevam <fabio.estevam@freescale.com> - */ - -#include <common.h> -#include <errno.h> -#include <i2c.h> -#include <power/pmic.h> -#include <power/max77696_pmic.h> - -int power_max77696_init(unsigned char bus) -{ - static const char name[] = "MAX77696"; - struct pmic *p = pmic_alloc(); - - if (!p) { - printf("%s: POWER allocation error!\n", __func__); - return -ENOMEM; - } - - p->name = name; - p->interface = PMIC_I2C; - p->number_of_regs = PMIC_NUM_OF_REGS; - p->hw.i2c.addr = CONFIG_POWER_MAX77696_I2C_ADDR; - p->hw.i2c.tx_num = 1; - p->bus = bus; - - return 0; -} diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index fbbea18c7d1..5d6180229e9 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -353,3 +353,11 @@ config DM_REGULATOR_TPS65941 TPS65941 series of PMICs have 5 single phase BUCKs that can also be configured in multi phase modes & 4 LDOs. The driver implements get/set api for value and enable. + +config DM_REGULATOR_SCMI + bool "Enable driver for SCMI voltage domain regulators" + depends on DM_REGULATOR + select SCMI_AGENT + help + Enable this option if you want to support regulators exposed through + the SCMI voltage domain protocol by a SCMI server. diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index 9d58112dcb1..b2f5972ea79 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -30,3 +30,4 @@ obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS62360) += tps62360_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STPMIC1) += stpmic1.o obj-$(CONFIG_DM_REGULATOR_TPS65941) += tps65941_regulator.o +obj-$(CONFIG_DM_REGULATOR_SCMI) += scmi_regulator.o diff --git a/drivers/power/regulator/scmi_regulator.c b/drivers/power/regulator/scmi_regulator.c new file mode 100644 index 00000000000..b3142bf4e1f --- /dev/null +++ b/drivers/power/regulator/scmi_regulator.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020-2021 Linaro Limited + */ +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> +#include <asm/types.h> +#include <dm/device.h> +#include <dm/device_compat.h> +#include <dm/device-internal.h> +#include <linux/kernel.h> +#include <power/regulator.h> + +/** + * struct scmi_regulator_platdata - Platform data for a scmi voltage domain regulator + * @domain_id: ID representing the regulator for the related SCMI agent + */ +struct scmi_regulator_platdata { + u32 domain_id; +}; + +static int scmi_voltd_set_enable(struct udevice *dev, bool enable) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_config_set_in in = { + .domain_id = pdata->domain_id, + .config = enable ? SCMI_VOLTD_CONFIG_ON : SCMI_VOLTD_CONFIG_OFF, + }; + struct scmi_voltd_config_set_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + SCMI_VOLTAGE_DOMAIN_CONFIG_SET, + in, out); + int ret; + + ret = devm_scmi_process_msg(dev->parent->parent, &msg); + if (ret) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret) + return ret; + + return ret; +} + +static int scmi_voltd_get_enable(struct udevice *dev) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_config_get_in in = { + .domain_id = pdata->domain_id, + }; + struct scmi_voltd_config_get_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + SCMI_VOLTAGE_DOMAIN_CONFIG_GET, + in, out); + int ret; + + ret = devm_scmi_process_msg(dev->parent->parent, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) + return ret; + + return out.config == SCMI_VOLTD_CONFIG_ON; +} + +static int scmi_voltd_set_voltage_level(struct udevice *dev, int uV) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_level_set_in in = { + .domain_id = pdata->domain_id, + .voltage_level = uV, + }; + struct scmi_voltd_level_set_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + SCMI_VOLTAGE_DOMAIN_LEVEL_SET, + in, out); + int ret; + + ret = devm_scmi_process_msg(dev->parent->parent, &msg); + if (ret < 0) + return ret; + + return scmi_to_linux_errno(out.status); +} + +static int scmi_voltd_get_voltage_level(struct udevice *dev) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_level_get_in in = { + .domain_id = pdata->domain_id, + }; + struct scmi_voltd_level_get_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + SCMI_VOLTAGE_DOMAIN_LEVEL_GET, + in, out); + int ret; + + ret = devm_scmi_process_msg(dev->parent->parent, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) + return ret; + + return out.voltage_level; +} + +static int scmi_regulator_of_to_plat(struct udevice *dev) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + fdt_addr_t reg; + + reg = dev_read_addr(dev); + if (reg == FDT_ADDR_T_NONE) + return -EINVAL; + + pdata->domain_id = (u32)reg; + + return 0; +} + +static int scmi_regulator_probe(struct udevice *dev) +{ + struct scmi_regulator_platdata *pdata = dev_get_plat(dev); + struct scmi_voltd_attr_in in = { 0 }; + struct scmi_voltd_attr_out out = { 0 }; + struct scmi_msg scmi_msg = { + .protocol_id = SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, + .message_id = SCMI_VOLTAGE_DOMAIN_ATTRIBUTES, + .in_msg = (u8 *)&in, + .in_msg_sz = sizeof(in), + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + /* Check voltage domain is known from SCMI server */ + in.domain_id = pdata->domain_id; + + ret = devm_scmi_process_msg(dev->parent->parent, &scmi_msg); + if (ret) { + dev_err(dev, "Failed to query voltage domain %u: %d\n", + pdata->domain_id, ret); + return -ENXIO; + } + + return 0; +} + +static const struct dm_regulator_ops scmi_voltd_ops = { + .get_value = scmi_voltd_get_voltage_level, + .set_value = scmi_voltd_set_voltage_level, + .get_enable = scmi_voltd_get_enable, + .set_enable = scmi_voltd_set_enable, +}; + +U_BOOT_DRIVER(scmi_regulator) = { + .name = "scmi_regulator", + .id = UCLASS_REGULATOR, + .ops = &scmi_voltd_ops, + .probe = scmi_regulator_probe, + .of_to_plat = scmi_regulator_of_to_plat, + .plat_auto = sizeof(struct scmi_regulator_platdata), +}; + +static int scmi_regulator_bind(struct udevice *dev) +{ + struct driver *drv; + ofnode node; + int ret; + + drv = DM_DRIVER_GET(scmi_regulator); + + ofnode_for_each_subnode(node, dev_ofnode(dev)) { + ret = device_bind(dev, drv, ofnode_get_name(node), + NULL, node, NULL); + if (ret) + return ret; + } + + return 0; +} + +U_BOOT_DRIVER(scmi_voltage_domain) = { + .name = "scmi_voltage_domain", + .id = UCLASS_NOP, + .bind = scmi_regulator_bind, +}; diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index dafba35279b..c84a9d2b27b 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -102,6 +102,12 @@ config RTC_PCF8563 If you say yes here you get support for the Philips PCF8563 RTC and compatible chips. +config RTC_RV3028 + bool "Enable RV3028 driver" + depends on DM_RTC + help + The MicroCrystal RV3028 is a I2C Real Time Clock (RTC) + config RTC_RV3029 bool "Enable RV3029 driver" depends on DM_RTC diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 15609e7b182..f668cf9050c 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_RTC_PCF2127) += pcf2127.o obj-$(CONFIG_RTC_PL031) += pl031.o obj-$(CONFIG_RTC_PT7C4338) += pt7c4338.o obj-$(CONFIG_RTC_RS5C372A) += rs5c372.o +obj-$(CONFIG_RTC_RV3028) += rv3028.o obj-$(CONFIG_RTC_RV3029) += rv3029.o obj-$(CONFIG_RTC_RV8803) += rv8803.o obj-$(CONFIG_RTC_RX8025) += rx8025.o diff --git a/drivers/rtc/rv3028.c b/drivers/rtc/rv3028.c new file mode 100644 index 00000000000..9f63afc14a8 --- /dev/null +++ b/drivers/rtc/rv3028.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * RTC driver for the Micro Crystal RV3028 + * + * based on linux driver from + * Copyright (C) 2019 Micro Crystal SA + * + * Alexandre Belloni <alexandre.belloni@bootlin.com> + * + */ + +#include <dm.h> +#include <i2c.h> +#include <rtc.h> + +#define RV3028_SEC 0x00 +#define RV3028_MIN 0x01 +#define RV3028_HOUR 0x02 +#define RV3028_WDAY 0x03 +#define RV3028_DAY 0x04 +#define RV3028_MONTH 0x05 +#define RV3028_YEAR 0x06 +#define RV3028_ALARM_MIN 0x07 +#define RV3028_ALARM_HOUR 0x08 +#define RV3028_ALARM_DAY 0x09 +#define RV3028_STATUS 0x0E +#define RV3028_CTRL1 0x0F +#define RV3028_CTRL2 0x10 +#define RV3028_EVT_CTRL 0x13 +#define RV3028_TS_COUNT 0x14 +#define RV3028_TS_SEC 0x15 +#define RV3028_RAM1 0x1F +#define RV3028_EEPROM_ADDR 0x25 +#define RV3028_EEPROM_DATA 0x26 +#define RV3028_EEPROM_CMD 0x27 +#define RV3028_CLKOUT 0x35 +#define RV3028_OFFSET 0x36 +#define RV3028_BACKUP 0x37 + +#define RV3028_STATUS_PORF BIT(0) +#define RV3028_STATUS_EVF BIT(1) +#define RV3028_STATUS_AF BIT(2) +#define RV3028_STATUS_TF BIT(3) +#define RV3028_STATUS_UF BIT(4) +#define RV3028_STATUS_BSF BIT(5) +#define RV3028_STATUS_CLKF BIT(6) +#define RV3028_STATUS_EEBUSY BIT(7) + +#define RV3028_CLKOUT_FD_MASK GENMASK(2, 0) +#define RV3028_CLKOUT_PORIE BIT(3) +#define RV3028_CLKOUT_CLKSY BIT(6) +#define RV3028_CLKOUT_CLKOE BIT(7) + +#define RV3028_CTRL1_EERD BIT(3) +#define RV3028_CTRL1_WADA BIT(5) + +#define RV3028_CTRL2_RESET BIT(0) +#define RV3028_CTRL2_12_24 BIT(1) +#define RV3028_CTRL2_EIE BIT(2) +#define RV3028_CTRL2_AIE BIT(3) +#define RV3028_CTRL2_TIE BIT(4) +#define RV3028_CTRL2_UIE BIT(5) +#define RV3028_CTRL2_TSE BIT(7) + +#define RV3028_EVT_CTRL_TSR BIT(2) + +#define RV3028_EEPROM_CMD_UPDATE 0x11 +#define RV3028_EEPROM_CMD_WRITE 0x21 +#define RV3028_EEPROM_CMD_READ 0x22 + +#define RV3028_EEBUSY_POLL 10000 +#define RV3028_EEBUSY_TIMEOUT 100000 + +#define RV3028_BACKUP_TCE BIT(5) +#define RV3028_BACKUP_TCR_MASK GENMASK(1, 0) + +#define OFFSET_STEP_PPT 953674 + +#define RTC_RV3028_LEN 7 + +static int rv3028_rtc_get(struct udevice *dev, struct rtc_time *tm) +{ + u8 regs[RTC_RV3028_LEN]; + u8 status; + int ret; + + ret = dm_i2c_read(dev, RV3028_STATUS, &status, 1); + if (ret < 0) { + printf("%s: error reading RTC status: %x\n", __func__, ret); + return -EIO; + } + + if (status & RV3028_STATUS_PORF) { + printf("Voltage low, data is invalid.\n"); + return -EINVAL; + } + + ret = dm_i2c_read(dev, RV3028_SEC, regs, sizeof(regs)); + if (ret < 0) { + printf("%s: error reading RTC: %x\n", __func__, ret); + return -EIO; + } + + tm->tm_sec = bcd2bin(regs[RV3028_SEC] & 0x7f); + tm->tm_min = bcd2bin(regs[RV3028_MIN] & 0x7f); + tm->tm_hour = bcd2bin(regs[RV3028_HOUR] & 0x3f); + tm->tm_wday = regs[RV3028_WDAY] & 0x7; + tm->tm_mday = bcd2bin(regs[RV3028_DAY] & 0x3f); + tm->tm_mon = bcd2bin(regs[RV3028_MONTH] & 0x1f); + tm->tm_year = bcd2bin(regs[RV3028_YEAR]) + 2000; + tm->tm_yday = 0; + tm->tm_isdst = 0; + + debug("%s: %4d-%02d-%02d (wday=%d) %2d:%02d:%02d\n", + __func__, tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + return 0; +} + +static int rv3028_rtc_set(struct udevice *dev, const struct rtc_time *tm) +{ + u8 regs[RTC_RV3028_LEN]; + u8 status; + int ret; + + debug("%s: %4d-%02d-%02d (wday=%d( %2d:%02d:%02d\n", + __func__, tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + if (tm->tm_year < 2000) { + printf("%s: year %d (before 2000) not supported\n", + __func__, tm->tm_year); + return -EINVAL; + } + + regs[RV3028_SEC] = bin2bcd(tm->tm_sec); + regs[RV3028_MIN] = bin2bcd(tm->tm_min); + regs[RV3028_HOUR] = bin2bcd(tm->tm_hour); + regs[RV3028_WDAY] = tm->tm_wday; + regs[RV3028_DAY] = bin2bcd(tm->tm_mday); + regs[RV3028_MONTH] = bin2bcd(tm->tm_mon); + regs[RV3028_YEAR] = bin2bcd(tm->tm_year - 2000); + + ret = dm_i2c_write(dev, RV3028_SEC, regs, sizeof(regs)); + if (ret) { + printf("%s: set rtc error: %d\n", __func__, ret); + return ret; + } + + ret = dm_i2c_read(dev, RV3028_STATUS, &status, 1); + if (ret < 0) { + printf("%s: error reading RTC status: %x\n", __func__, ret); + return -EIO; + } + status |= RV3028_STATUS_PORF; + return dm_i2c_write(dev, RV3028_STATUS, &status, 1); +} + +static int rv3028_rtc_reset(struct udevice *dev) +{ + return 0; +} + +static int rv3028_rtc_read8(struct udevice *dev, unsigned int reg) +{ + u8 data; + int ret; + + ret = dm_i2c_read(dev, reg, &data, sizeof(data)); + return ret < 0 ? ret : data; +} + +static int rv3028_rtc_write8(struct udevice *dev, unsigned int reg, int val) +{ + u8 data = val; + + return dm_i2c_write(dev, reg, &data, 1); +} + +static int rv3028_probe(struct udevice *dev) +{ + i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS | + DM_I2C_CHIP_WR_ADDRESS); + + return 0; +} + +static const struct rtc_ops rv3028_rtc_ops = { + .get = rv3028_rtc_get, + .set = rv3028_rtc_set, + .read8 = rv3028_rtc_read8, + .write8 = rv3028_rtc_write8, + .reset = rv3028_rtc_reset, +}; + +static const struct udevice_id rv3028_rtc_ids[] = { + { .compatible = "microcrystal,rv3028" }, + { } +}; + +U_BOOT_DRIVER(rtc_rv3028) = { + .name = "rtc-rv3028", + .id = UCLASS_RTC, + .probe = rv3028_probe, + .of_match = rv3028_rtc_ids, + .ops = &rv3028_rtc_ops, +}; diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index b898c32edc0..73dbb22ba09 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -624,14 +624,14 @@ static int optee_probe(struct udevice *dev) u32 sec_caps; if (!is_optee_api(pdata->invoke_fn)) { - debug("%s: OP-TEE api uid mismatch\n", __func__); + dev_err(dev, "OP-TEE api uid mismatch\n"); return -ENOENT; } print_os_revision(dev, pdata->invoke_fn); if (!api_revision_is_compatible(pdata->invoke_fn)) { - debug("%s: OP-TEE api revision mismatch\n", __func__); + dev_err(dev, "OP-TEE api revision mismatch\n"); return -ENOENT; } @@ -642,7 +642,7 @@ static int optee_probe(struct udevice *dev) */ if (!exchange_capabilities(pdata->invoke_fn, &sec_caps) || !(sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)) { - debug("%s: OP-TEE capabilities mismatch\n", __func__); + dev_err(dev, "OP-TEE capabilities mismatch\n"); return -ENOENT; } diff --git a/drivers/usb/dwc3/linux-compat.h b/drivers/usb/dwc3/linux-compat.h index 82793765bea..3bb0bda5a6b 100644 --- a/drivers/usb/dwc3/linux-compat.h +++ b/drivers/usb/dwc3/linux-compat.h @@ -13,10 +13,4 @@ #define dev_WARN(dev, format, arg...) debug(format, ##arg) -static inline size_t strlcat(char *dest, const char *src, size_t n) -{ - strcat(dest, src); - return strlen(dest) + strlen(src); -} - #endif |