1 /*
2 * Copyright (c) Carlo Caione <ccaione@baylibre.com>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT syscon
8
9 #include <errno.h>
10
11 #include <zephyr/arch/cpu.h>
12 #include <zephyr/sys/util.h>
13 #include <zephyr/device.h>
14 #include <zephyr/init.h>
15
16 #include <zephyr/drivers/syscon.h>
17
18 #include "syscon_common.h"
19
20 struct syscon_generic_config {
21 DEVICE_MMIO_ROM;
22 uint8_t reg_width;
23 };
24
25 struct syscon_generic_data {
26 DEVICE_MMIO_RAM;
27 size_t size;
28 };
29
syscon_generic_get_base(const struct device * dev,uintptr_t * addr)30 static int syscon_generic_get_base(const struct device *dev, uintptr_t *addr)
31 {
32 if (!dev) {
33 return -ENODEV;
34 }
35
36 *addr = DEVICE_MMIO_GET(dev);
37 return 0;
38 }
39
syscon_generic_read_reg(const struct device * dev,uint16_t reg,uint32_t * val)40 static int syscon_generic_read_reg(const struct device *dev, uint16_t reg, uint32_t *val)
41 {
42 const struct syscon_generic_config *config;
43 struct syscon_generic_data *data;
44 uintptr_t base_address;
45
46 if (!dev) {
47 return -ENODEV;
48 }
49
50 data = dev->data;
51 config = dev->config;
52
53 if (!val) {
54 return -EINVAL;
55 }
56
57 if (syscon_sanitize_reg(®, data->size, config->reg_width)) {
58 return -EINVAL;
59 }
60
61 base_address = DEVICE_MMIO_GET(dev);
62
63 switch (config->reg_width) {
64 case 1:
65 *val = sys_read8(base_address + reg);
66 break;
67 case 2:
68 *val = sys_read16(base_address + reg);
69 break;
70 case 4:
71 *val = sys_read32(base_address + reg);
72 break;
73 default:
74 return -EINVAL;
75 }
76
77 return 0;
78 }
79
syscon_generic_write_reg(const struct device * dev,uint16_t reg,uint32_t val)80 static int syscon_generic_write_reg(const struct device *dev, uint16_t reg, uint32_t val)
81 {
82 const struct syscon_generic_config *config;
83 struct syscon_generic_data *data;
84 uintptr_t base_address;
85
86 if (!dev) {
87 return -ENODEV;
88 }
89
90 data = dev->data;
91 config = dev->config;
92
93 if (syscon_sanitize_reg(®, data->size, config->reg_width)) {
94 return -EINVAL;
95 }
96
97 base_address = DEVICE_MMIO_GET(dev);
98
99 switch (config->reg_width) {
100 case 1:
101 sys_write8(val, (base_address + reg));
102 break;
103 case 2:
104 sys_write16(val, (base_address + reg));
105 break;
106 case 4:
107 sys_write32(val, (base_address + reg));
108 break;
109 default:
110 return -EINVAL;
111 }
112
113 return 0;
114 }
115
syscon_generic_get_size(const struct device * dev,size_t * size)116 static int syscon_generic_get_size(const struct device *dev, size_t *size)
117 {
118 struct syscon_generic_data *data = dev->data;
119
120 *size = data->size;
121 return 0;
122 }
123
124 static const struct syscon_driver_api syscon_generic_driver_api = {
125 .read = syscon_generic_read_reg,
126 .write = syscon_generic_write_reg,
127 .get_base = syscon_generic_get_base,
128 .get_size = syscon_generic_get_size,
129 };
130
syscon_generic_init(const struct device * dev)131 static int syscon_generic_init(const struct device *dev)
132 {
133 DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
134
135 return 0;
136 }
137
138 #define SYSCON_INIT(inst) \
139 static const struct syscon_generic_config syscon_generic_config_##inst = { \
140 DEVICE_MMIO_ROM_INIT(DT_DRV_INST(inst)), \
141 .reg_width = DT_INST_PROP_OR(inst, reg_io_width, 4), \
142 }; \
143 static struct syscon_generic_data syscon_generic_data_##inst = { \
144 .size = DT_INST_REG_SIZE(inst), \
145 }; \
146 DEVICE_DT_INST_DEFINE(inst, syscon_generic_init, NULL, &syscon_generic_data_##inst, \
147 &syscon_generic_config_##inst, PRE_KERNEL_1, \
148 CONFIG_SYSCON_INIT_PRIORITY, &syscon_generic_driver_api);
149
150 DT_INST_FOREACH_STATUS_OKAY(SYSCON_INIT);
151