1 /*
2 * Copyright (c) 2022 Andrei-Edward Popa
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT raspberrypi_pico_reset
8
9 #include <limits.h>
10
11 #include <zephyr/arch/cpu.h>
12 #include <zephyr/device.h>
13 #include <zephyr/drivers/reset.h>
14
15 struct reset_rpi_config {
16 DEVICE_MMIO_ROM;
17 uint8_t reg_width;
18 uint8_t active_low;
19 uintptr_t base_address;
20 };
21
reset_rpi_read_register(const struct device * dev,uint16_t offset,uint32_t * value)22 static int reset_rpi_read_register(const struct device *dev, uint16_t offset, uint32_t *value)
23 {
24 const struct reset_rpi_config *config = dev->config;
25 uint32_t base_address = config->base_address;
26
27 switch (config->reg_width) {
28 case 1:
29 *value = sys_read8(base_address + offset);
30 break;
31 case 2:
32 *value = sys_read16(base_address + offset);
33 break;
34 case 4:
35 *value = sys_read32(base_address + offset);
36 break;
37 default:
38 return -EINVAL;
39 }
40
41 return 0;
42 }
43
reset_rpi_write_register(const struct device * dev,uint16_t offset,uint32_t value)44 static int reset_rpi_write_register(const struct device *dev, uint16_t offset, uint32_t value)
45 {
46 const struct reset_rpi_config *config = dev->config;
47 uint32_t base_address = config->base_address;
48
49 switch (config->reg_width) {
50 case 1:
51 sys_write8(value, base_address + offset);
52 break;
53 case 2:
54 sys_write16(value, base_address + offset);
55 break;
56 case 4:
57 sys_write32(value, base_address + offset);
58 break;
59 default:
60 return -EINVAL;
61 }
62
63 return 0;
64 }
65
reset_rpi_status(const struct device * dev,uint32_t id,uint8_t * status)66 static int reset_rpi_status(const struct device *dev, uint32_t id, uint8_t *status)
67 {
68 const struct reset_rpi_config *config = dev->config;
69 uint16_t offset;
70 uint32_t value;
71 uint8_t regbit;
72 int ret;
73
74 offset = id / (config->reg_width * CHAR_BIT);
75 regbit = id % (config->reg_width * CHAR_BIT);
76
77 ret = reset_rpi_read_register(dev, offset, &value);
78 if (ret) {
79 return ret;
80 }
81
82 *status = !(value & BIT(regbit)) ^ !config->active_low;
83
84 return ret;
85 }
86
reset_rpi_update(const struct device * dev,uint32_t id,uint8_t assert)87 static int reset_rpi_update(const struct device *dev, uint32_t id, uint8_t assert)
88 {
89 const struct reset_rpi_config *config = dev->config;
90 uint16_t offset;
91 uint32_t value;
92 uint8_t regbit;
93 int ret;
94
95 offset = id / (config->reg_width * CHAR_BIT);
96 regbit = id % (config->reg_width * CHAR_BIT);
97
98 ret = reset_rpi_read_register(dev, offset, &value);
99 if (ret) {
100 return ret;
101 }
102
103 if (assert ^ config->active_low) {
104 value |= BIT(regbit);
105 } else {
106 value &= ~BIT(regbit);
107 }
108
109 return reset_rpi_write_register(dev, offset, value);
110 }
111
reset_rpi_line_assert(const struct device * dev,uint32_t id)112 static int reset_rpi_line_assert(const struct device *dev, uint32_t id)
113 {
114 return reset_rpi_update(dev, id, 1);
115 }
116
reset_rpi_line_deassert(const struct device * dev,uint32_t id)117 static int reset_rpi_line_deassert(const struct device *dev, uint32_t id)
118 {
119 return reset_rpi_update(dev, id, 0);
120 }
121
reset_rpi_line_toggle(const struct device * dev,uint32_t id)122 static int reset_rpi_line_toggle(const struct device *dev, uint32_t id)
123 {
124 int ret;
125
126 ret = reset_rpi_line_assert(dev, id);
127 if (ret) {
128 return ret;
129 }
130
131 return reset_rpi_line_deassert(dev, id);
132 }
133
reset_rpi_init(const struct device * dev)134 static int reset_rpi_init(const struct device *dev)
135 {
136 DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
137
138 return 0;
139 }
140
141 static const struct reset_driver_api reset_rpi_driver_api = {
142 .status = reset_rpi_status,
143 .line_assert = reset_rpi_line_assert,
144 .line_deassert = reset_rpi_line_deassert,
145 .line_toggle = reset_rpi_line_toggle,
146 };
147
148 #define RPI_RESET_INIT(idx) \
149 static const struct reset_rpi_config reset_rpi_config_##idx = { \
150 DEVICE_MMIO_ROM_INIT(DT_DRV_INST(idx)), \
151 .reg_width = DT_INST_PROP_OR(idx, reg_width, 4), \
152 .active_low = DT_INST_PROP_OR(idx, active_low, 0), \
153 .base_address = DT_INST_REG_ADDR(idx), \
154 }; \
155 \
156 DEVICE_DT_INST_DEFINE(idx, reset_rpi_init, \
157 NULL, NULL, \
158 &reset_rpi_config_##idx, PRE_KERNEL_1, \
159 CONFIG_RESET_INIT_PRIORITY, \
160 &reset_rpi_driver_api);
161
162 DT_INST_FOREACH_STATUS_OKAY(RPI_RESET_INIT);
163