| /* |
| * Copyright (c) 2022 Andrei-Edward Popa |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT raspberrypi_pico_reset |
| |
| #include <limits.h> |
| |
| #include <zephyr/arch/cpu.h> |
| #include <zephyr/device.h> |
| #include <zephyr/drivers/reset.h> |
| |
| struct reset_rpi_config { |
| DEVICE_MMIO_ROM; |
| uint8_t reg_width; |
| uint8_t active_low; |
| uintptr_t base_address; |
| }; |
| |
| static int reset_rpi_read_register(const struct device *dev, uint16_t offset, uint32_t *value) |
| { |
| const struct reset_rpi_config *config = dev->config; |
| uint32_t base_address = config->base_address; |
| |
| switch (config->reg_width) { |
| case 1: |
| *value = sys_read8(base_address + offset); |
| break; |
| case 2: |
| *value = sys_read16(base_address + offset); |
| break; |
| case 4: |
| *value = sys_read32(base_address + offset); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int reset_rpi_write_register(const struct device *dev, uint16_t offset, uint32_t value) |
| { |
| const struct reset_rpi_config *config = dev->config; |
| uint32_t base_address = config->base_address; |
| |
| switch (config->reg_width) { |
| case 1: |
| sys_write8(value, base_address + offset); |
| break; |
| case 2: |
| sys_write16(value, base_address + offset); |
| break; |
| case 4: |
| sys_write32(value, base_address + offset); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int reset_rpi_status(const struct device *dev, uint32_t id, uint8_t *status) |
| { |
| const struct reset_rpi_config *config = dev->config; |
| uint16_t offset; |
| uint32_t value; |
| uint8_t regbit; |
| int ret; |
| |
| offset = id / (config->reg_width * CHAR_BIT); |
| regbit = id % (config->reg_width * CHAR_BIT); |
| |
| ret = reset_rpi_read_register(dev, offset, &value); |
| if (ret) { |
| return ret; |
| } |
| |
| *status = !(value & BIT(regbit)) ^ !config->active_low; |
| |
| return ret; |
| } |
| |
| static int reset_rpi_update(const struct device *dev, uint32_t id, uint8_t assert) |
| { |
| const struct reset_rpi_config *config = dev->config; |
| uint16_t offset; |
| uint32_t value; |
| uint8_t regbit; |
| int ret; |
| |
| offset = id / (config->reg_width * CHAR_BIT); |
| regbit = id % (config->reg_width * CHAR_BIT); |
| |
| ret = reset_rpi_read_register(dev, offset, &value); |
| if (ret) { |
| return ret; |
| } |
| |
| if (assert ^ config->active_low) { |
| value |= BIT(regbit); |
| } else { |
| value &= ~BIT(regbit); |
| } |
| |
| return reset_rpi_write_register(dev, offset, value); |
| } |
| |
| static int reset_rpi_line_assert(const struct device *dev, uint32_t id) |
| { |
| return reset_rpi_update(dev, id, 1); |
| } |
| |
| static int reset_rpi_line_deassert(const struct device *dev, uint32_t id) |
| { |
| return reset_rpi_update(dev, id, 0); |
| } |
| |
| static int reset_rpi_line_toggle(const struct device *dev, uint32_t id) |
| { |
| int ret; |
| |
| ret = reset_rpi_line_assert(dev, id); |
| if (ret) { |
| return ret; |
| } |
| |
| return reset_rpi_line_deassert(dev, id); |
| } |
| |
| static int reset_rpi_init(const struct device *dev) |
| { |
| DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); |
| |
| return 0; |
| } |
| |
| static const struct reset_driver_api reset_rpi_driver_api = { |
| .status = reset_rpi_status, |
| .line_assert = reset_rpi_line_assert, |
| .line_deassert = reset_rpi_line_deassert, |
| .line_toggle = reset_rpi_line_toggle, |
| }; |
| |
| #define RPI_RESET_INIT(idx) \ |
| static const struct reset_rpi_config reset_rpi_config_##idx = { \ |
| DEVICE_MMIO_ROM_INIT(DT_DRV_INST(idx)), \ |
| .reg_width = DT_INST_PROP_OR(idx, reg_width, 4), \ |
| .active_low = DT_INST_PROP_OR(idx, active_low, 0), \ |
| .base_address = DT_INST_REG_ADDR(idx), \ |
| }; \ |
| \ |
| DEVICE_DT_INST_DEFINE(idx, reset_rpi_init, \ |
| NULL, NULL, \ |
| &reset_rpi_config_##idx, PRE_KERNEL_1, \ |
| CONFIG_RESET_INIT_PRIORITY, \ |
| &reset_rpi_driver_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(RPI_RESET_INIT); |