1 /*
2  * Copyright (c) 2023, Meta
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT zephyr_devmux
8 
9 #include <limits.h>
10 
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/misc/devmux/devmux.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/sys/util.h>
15 
16 struct devmux_config {
17 	const struct device **devs;
18 	const size_t n_devs;
19 };
20 
21 struct devmux_data {
22 	struct k_spinlock lock;
23 	size_t selected;
24 };
25 
26 /* The number of devmux devices */
27 #define N DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT)
28 
29 static const struct device *devmux_devices[N];
30 static const struct devmux_config *devmux_configs[N];
31 static struct devmux_data *devmux_datas[N];
32 
devmux_device_is_valid(const struct device * dev)33 static bool devmux_device_is_valid(const struct device *dev)
34 {
35 	for (size_t i = 0; i < N; ++i) {
36 		if (dev == devmux_devices[i]) {
37 			return true;
38 		}
39 	}
40 
41 	return false;
42 }
43 
devmux_inst_get(const struct device * dev)44 static size_t devmux_inst_get(const struct device *dev)
45 {
46 	for (size_t i = 0; i < N; i++) {
47 		if (dev == devmux_devices[i]) {
48 			return i;
49 		}
50 	}
51 
52 	return SIZE_MAX;
53 }
54 
devmux_config_get(const struct device * dev)55 const struct devmux_config *devmux_config_get(const struct device *dev)
56 {
57 	for (size_t i = 0; i < N; i++) {
58 		if (dev == devmux_devices[i]) {
59 			return devmux_configs[i];
60 		}
61 	}
62 
63 	return NULL;
64 }
65 
devmux_data_get(const struct device * dev)66 struct devmux_data *devmux_data_get(const struct device *dev)
67 {
68 	for (size_t i = 0; i < N; i++) {
69 		if (dev == devmux_devices[i]) {
70 			return devmux_datas[i];
71 		}
72 	}
73 
74 	return NULL;
75 }
76 
z_impl_devmux_select_get(const struct device * dev)77 int z_impl_devmux_select_get(const struct device *dev)
78 {
79 	int index;
80 	struct devmux_data *const data = devmux_data_get(dev);
81 
82 	if (!devmux_device_is_valid(dev)) {
83 		return -EINVAL;
84 	}
85 
86 	K_SPINLOCK(&data->lock)
87 	{
88 		index = data->selected;
89 	}
90 
91 	return index;
92 }
93 
94 #ifdef CONFIG_USERSPACE
z_vrfy_devmux_select_get(const struct device * dev)95 int z_vrfy_devmux_select_get(const struct device *dev)
96 {
97 	return z_impl_devmux_select_get(dev);
98 }
99 #include <zephyr/syscalls/devmux_select_get_mrsh.c>
100 #endif
101 
z_impl_devmux_select_set(struct device * dev,size_t index)102 int z_impl_devmux_select_set(struct device *dev, size_t index)
103 {
104 	struct devmux_data *const data = devmux_data_get(dev);
105 	const struct devmux_config *config = devmux_config_get(dev);
106 
107 	if (!devmux_device_is_valid(dev) || index >= config->n_devs) {
108 		return -EINVAL;
109 	}
110 
111 	if (!device_is_ready(config->devs[index])) {
112 		return -ENODEV;
113 	}
114 
115 	K_SPINLOCK(&data->lock)
116 	{
117 		*dev = *config->devs[index];
118 		data->selected = index;
119 	}
120 
121 	return 0;
122 }
123 
124 #ifdef CONFIG_USERSPACE
z_vrfy_devmux_select_set(struct device * dev,size_t index)125 int z_vrfy_devmux_select_set(struct device *dev, size_t index)
126 {
127 	return z_impl_devmux_select_set(dev, index);
128 }
129 #include <zephyr/syscalls/devmux_select_set_mrsh.c>
130 #endif
131 
devmux_init(struct device * const dev)132 static int devmux_init(struct device *const dev)
133 {
134 	size_t inst = devmux_inst_get(dev);
135 	struct devmux_data *const data = dev->data;
136 	const struct devmux_config *config = dev->config;
137 	size_t sel = data->selected;
138 
139 	devmux_configs[inst] = config;
140 	devmux_datas[inst] = data;
141 
142 	if (!device_is_ready(config->devs[sel])) {
143 		return -ENODEV;
144 	}
145 
146 	*dev = *config->devs[sel];
147 
148 	return 0;
149 }
150 
151 #define DEVMUX_PHANDLE_TO_DEVICE(node_id, prop, idx)                                               \
152 	DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, idx))
153 
154 #define DEVMUX_PHANDLE_DEVICES(_n)                                                                 \
155 	DT_INST_FOREACH_PROP_ELEM_SEP(_n, devices, DEVMUX_PHANDLE_TO_DEVICE, (,))
156 
157 #define DEVMUX_SELECTED(_n) DT_INST_PROP(_n, selected)
158 
159 #define DEVMUX_DEFINE(_n)                                                                          \
160 	BUILD_ASSERT(DT_INST_PROP_OR(_n, zephyr_mutable, 0),                                       \
161 		     "devmux nodes must contain the 'zephyr,mutable' property");                   \
162 	BUILD_ASSERT(DT_INST_PROP_LEN(_n, devices) > 0, "devices array must have non-zero size");  \
163 	BUILD_ASSERT(DT_INST_PROP_LEN(_n, devices) <= INT_MAX,                                     \
164 		     "devices array must be less than INT_MAX");                                   \
165 	BUILD_ASSERT(DEVMUX_SELECTED(_n) >= 0, "selected must be > 0");                            \
166 	BUILD_ASSERT(DEVMUX_SELECTED(_n) < DT_INST_PROP_LEN(_n, devices),                          \
167 		     "selected must be within bounds of devices phandle array");                   \
168 	static const struct device *demux_devs_##_n[] = {DEVMUX_PHANDLE_DEVICES(_n)};              \
169 	static const struct devmux_config devmux_config_##_n = {                                   \
170 		.devs = demux_devs_##_n,                                                           \
171 		.n_devs = DT_INST_PROP_LEN(_n, devices),                                           \
172 	};                                                                                         \
173 	static struct devmux_data devmux_data_##_n = {                                             \
174 		.selected = DEVMUX_SELECTED(_n),                                                   \
175 	};                                                                                         \
176                                                                                                    \
177 	DEVICE_DT_INST_DEFINE(_n, devmux_init, NULL, &devmux_data_##_n, &devmux_config_##_n,       \
178 			      PRE_KERNEL_1, CONFIG_DEVMUX_INIT_PRIORITY, NULL);
179 
180 DT_INST_FOREACH_STATUS_OKAY(DEVMUX_DEFINE)
181 
182 #define DEVMUX_DEVICE_GET(_n) DEVICE_DT_INST_GET(_n),
183 static const struct device *devmux_devices[] = {DT_INST_FOREACH_STATUS_OKAY(DEVMUX_DEVICE_GET)};
184