| /* |
| * Copyright (c) Carlo Caione <ccaione@baylibre.com> |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT syscon |
| |
| #include <sys/util.h> |
| #include <device.h> |
| #include <init.h> |
| |
| #include <drivers/syscon.h> |
| |
| #include "syscon_common.h" |
| |
| struct syscon_generic_config { |
| DEVICE_MMIO_ROM; |
| uint8_t reg_width; |
| }; |
| |
| struct syscon_generic_data { |
| DEVICE_MMIO_RAM; |
| size_t size; |
| }; |
| |
| static int syscon_generic_get_base(const struct device *dev, uintptr_t *addr) |
| { |
| if (!dev) { |
| return -ENODEV; |
| } |
| |
| *addr = DEVICE_MMIO_GET(dev); |
| return 0; |
| } |
| |
| static int syscon_generic_read_reg(const struct device *dev, uint16_t reg, uint32_t *val) |
| { |
| const struct syscon_generic_config *config; |
| struct syscon_generic_data *data; |
| uintptr_t base_address; |
| |
| if (!dev) { |
| return -ENODEV; |
| } |
| |
| data = dev->data; |
| config = dev->config; |
| |
| if (!val) { |
| return -EINVAL; |
| } |
| |
| if (syscon_sanitize_reg(®, data->size, config->reg_width)) { |
| return -EINVAL; |
| } |
| |
| base_address = DEVICE_MMIO_GET(dev); |
| |
| switch (config->reg_width) { |
| case 1: |
| *val = sys_read8(base_address + reg); |
| break; |
| case 2: |
| *val = sys_read16(base_address + reg); |
| break; |
| case 4: |
| *val = sys_read32(base_address + reg); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int syscon_generic_write_reg(const struct device *dev, uint16_t reg, uint32_t val) |
| { |
| const struct syscon_generic_config *config; |
| struct syscon_generic_data *data; |
| uintptr_t base_address; |
| |
| if (!dev) { |
| return -ENODEV; |
| } |
| |
| data = dev->data; |
| config = dev->config; |
| |
| if (syscon_sanitize_reg(®, data->size, config->reg_width)) { |
| return -EINVAL; |
| } |
| |
| base_address = DEVICE_MMIO_GET(dev); |
| |
| switch (config->reg_width) { |
| case 1: |
| sys_write8(val, (base_address + reg)); |
| break; |
| case 2: |
| sys_write16(val, (base_address + reg)); |
| break; |
| case 4: |
| sys_write32(val, (base_address + reg)); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int syscon_generic_get_size(const struct device *dev, size_t *size) |
| { |
| struct syscon_generic_data *data = dev->data; |
| |
| *size = data->size; |
| return 0; |
| } |
| |
| static const struct syscon_driver_api syscon_generic_driver_api = { |
| .read = syscon_generic_read_reg, |
| .write = syscon_generic_write_reg, |
| .get_base = syscon_generic_get_base, |
| .get_size = syscon_generic_get_size, |
| }; |
| |
| static int syscon_generic_init(const struct device *dev) |
| { |
| DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); |
| |
| return 0; |
| } |
| |
| #define SYSCON_INIT(inst) \ |
| static const struct syscon_generic_config syscon_generic_config_##inst = { \ |
| DEVICE_MMIO_ROM_INIT(DT_DRV_INST(inst)), \ |
| .reg_width = DT_INST_PROP_OR(inst, reg_io_width, 4), \ |
| }; \ |
| static struct syscon_generic_data syscon_generic_data_##inst = { \ |
| .size = DT_REG_SIZE(DT_DRV_INST(inst)), \ |
| }; \ |
| DEVICE_DT_INST_DEFINE(inst, syscon_generic_init, NULL, &syscon_generic_data_##inst, \ |
| &syscon_generic_config_##inst, PRE_KERNEL_1, \ |
| CONFIG_SYSCON_INIT_PRIORITY, &syscon_generic_driver_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(SYSCON_INIT); |