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