1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #include <mram_latency.h>
7 #include <zephyr/kernel.h>
8 #include <nrfs_mram.h>
9 #include <nrfs_backend_ipc_service.h>
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(mram_latency, CONFIG_MRAM_LATENCY_LOG_LEVEL);
12 
13 enum mram_latency_state {
14 	MRAM_LATENCY_OFF = 0,
15 	MRAM_LATENCY_OFF_PENDING,
16 	MRAM_LATENCY_ON,
17 };
18 
19 static struct k_work work;
20 static bool no_latency;
21 static enum mram_latency_state state;
22 
23 static onoff_notify_fn onoff_notify;
24 struct onoff_manager mram_latency_mgr;
25 
26 struct sync_latency_req {
27 	struct onoff_client cli;
28 	struct k_sem sem;
29 	int res;
30 };
31 
latency_change_req(bool latency_not_allowed)32 static void latency_change_req(bool latency_not_allowed)
33 {
34 	nrfs_err_t err;
35 
36 	if (latency_not_allowed) {
37 		err = nrfs_mram_set_latency(MRAM_LATENCY_NOT_ALLOWED, NULL);
38 		if (err != NRFS_SUCCESS) {
39 			onoff_notify(&mram_latency_mgr, -EIO);
40 		}
41 	} else {
42 		/* There is no event for that setting so we can notify onoff manager
43 		 * immediately.
44 		 */
45 		err = nrfs_mram_set_latency(MRAM_LATENCY_ALLOWED, NULL);
46 		onoff_notify(&mram_latency_mgr, err != NRFS_SUCCESS ? -EIO : 0);
47 	}
48 }
49 
latency_change(bool latency_not_allowed)50 static void latency_change(bool latency_not_allowed)
51 {
52 	LOG_DBG("Request: latency %s allowed", latency_not_allowed ? "not " : "");
53 	if (state == MRAM_LATENCY_OFF) {
54 		state = MRAM_LATENCY_OFF_PENDING;
55 	} else if (k_is_in_isr()) {
56 		/* nrfs cannot be called from interrupt context so defer to the work
57 		 * queue context and execute from there.
58 		 */
59 		no_latency = latency_not_allowed;
60 		k_work_submit(&work);
61 	} else {
62 		latency_change_req(latency_not_allowed);
63 	}
64 }
65 
no_latency_start(struct onoff_manager * mgr,onoff_notify_fn notify)66 static void no_latency_start(struct onoff_manager *mgr, onoff_notify_fn notify)
67 {
68 	onoff_notify = notify;
69 	latency_change(true);
70 }
71 
no_latency_stop(struct onoff_manager * mgr,onoff_notify_fn notify)72 static void no_latency_stop(struct onoff_manager *mgr, onoff_notify_fn notify)
73 {
74 	latency_change(false);
75 }
76 
evt_handler(nrfs_mram_latency_evt_t const * p_evt,void * context)77 static void evt_handler(nrfs_mram_latency_evt_t const *p_evt, void *context)
78 {
79 	int res = p_evt->type == NRFS_MRAM_LATENCY_REQ_APPLIED ? 0 : -EIO;
80 
81 	LOG_DBG("Latency not allowed - applied");
82 	onoff_notify(&mram_latency_mgr, res);
83 }
84 
work_handler(struct k_work * work)85 static void work_handler(struct k_work *work)
86 {
87 	latency_change_req(no_latency);
88 }
89 
mram_no_latency_cancel_or_release(struct onoff_client * cli)90 int mram_no_latency_cancel_or_release(struct onoff_client *cli)
91 {
92 	return onoff_cancel_or_release(&mram_latency_mgr, cli);
93 }
94 
mram_no_latency_request(struct onoff_client * cli)95 int mram_no_latency_request(struct onoff_client *cli)
96 {
97 	return onoff_request(&mram_latency_mgr, cli);
98 }
99 
sync_req_cb(struct onoff_manager * mgr,struct onoff_client * cli,uint32_t state,int res)100 static void sync_req_cb(struct onoff_manager *mgr, struct onoff_client *cli, uint32_t state,
101 			int res)
102 {
103 	struct sync_latency_req *req = CONTAINER_OF(cli, struct sync_latency_req, cli);
104 
105 	req->res = res;
106 	k_sem_give(&req->sem);
107 }
108 
mram_no_latency_sync_request(void)109 int mram_no_latency_sync_request(void)
110 {
111 	struct sync_latency_req req;
112 	int rv;
113 
114 	if (k_is_in_isr() || (state != MRAM_LATENCY_ON)) {
115 		return -ENOTSUP;
116 	}
117 
118 	k_sem_init(&req.sem, 0, 1);
119 	sys_notify_init_callback(&req.cli.notify, sync_req_cb);
120 	rv = onoff_request(&mram_latency_mgr, &req.cli);
121 	if (rv < 0) {
122 		return rv;
123 	}
124 
125 	rv = k_sem_take(&req.sem, K_MSEC(CONFIG_MRAM_LATENCY_SYNC_TIMEOUT));
126 	if (rv < 0) {
127 		return rv;
128 	}
129 
130 	return req.res;
131 }
132 
mram_no_latency_sync_release(void)133 int mram_no_latency_sync_release(void)
134 {
135 	return onoff_release(&mram_latency_mgr) >= 0 ? 0 : -EIO;
136 }
137 
138 /* First initialize onoff manager to be able to accept requests. */
init_manager(void)139 static int init_manager(void)
140 {
141 	static const struct onoff_transitions transitions =
142 		ONOFF_TRANSITIONS_INITIALIZER(no_latency_start, no_latency_stop, NULL);
143 
144 	return onoff_manager_init(&mram_latency_mgr, &transitions);
145 }
146 
147 /* When kernel and IPC is running initialize nrfs. Optionally, execute pending request. */
init_nrfs(void)148 static int init_nrfs(void)
149 {
150 	nrfs_err_t err;
151 	int rv;
152 
153 	err = nrfs_backend_wait_for_connection(K_FOREVER);
154 	if (err != NRFS_SUCCESS) {
155 		return -EIO;
156 	}
157 
158 	err = nrfs_mram_init(evt_handler);
159 	if (err != NRFS_SUCCESS) {
160 		return -EIO;
161 	}
162 
163 	k_work_init(&work, work_handler);
164 
165 	if (state == MRAM_LATENCY_OFF_PENDING) {
166 		latency_change(true);
167 	}
168 
169 	state = MRAM_LATENCY_ON;
170 
171 	if (IS_ENABLED(CONFIG_MRAM_LATENCY_AUTO_REQ)) {
172 		mram_no_latency_sync_request();
173 	}
174 
175 	return rv;
176 }
177 
178 SYS_INIT(init_manager, PRE_KERNEL_1, 0);
179 SYS_INIT(init_nrfs, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
180