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(&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(&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