1*a48b9003SAbdelkader Boudih /*
2*a48b9003SAbdelkader Boudih * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
3*a48b9003SAbdelkader Boudih *
4*a48b9003SAbdelkader Boudih * SPDX-License-Identifier: BSD-2-Clause
5*a48b9003SAbdelkader Boudih */
6*a48b9003SAbdelkader Boudih
7*a48b9003SAbdelkader Boudih /*
8*a48b9003SAbdelkader Boudih * MMIO backend for Apple SMC (T2 and later Macs).
9*a48b9003SAbdelkader Boudih *
10*a48b9003SAbdelkader Boudih * T2 Macs expose the SMC via memory-mapped registers instead of I/O ports.
11*a48b9003SAbdelkader Boudih * Protocol: clear status, write key/cmd, poll for ready, read result.
12*a48b9003SAbdelkader Boudih */
13*a48b9003SAbdelkader Boudih
14*a48b9003SAbdelkader Boudih #include <sys/param.h>
15*a48b9003SAbdelkader Boudih #include <sys/bus.h>
16*a48b9003SAbdelkader Boudih #include <sys/endian.h>
17*a48b9003SAbdelkader Boudih #include <sys/kernel.h>
18*a48b9003SAbdelkader Boudih #include <sys/lock.h>
19*a48b9003SAbdelkader Boudih #include <sys/mutex.h>
20*a48b9003SAbdelkader Boudih #include <sys/module.h>
21*a48b9003SAbdelkader Boudih #include <sys/rman.h>
22*a48b9003SAbdelkader Boudih #include <sys/sysctl.h>
23*a48b9003SAbdelkader Boudih #include <sys/systm.h>
24*a48b9003SAbdelkader Boudih #include <sys/taskqueue.h>
25*a48b9003SAbdelkader Boudih
26*a48b9003SAbdelkader Boudih #include <machine/bus.h>
27*a48b9003SAbdelkader Boudih
28*a48b9003SAbdelkader Boudih #include <dev/asmc/asmcvar.h>
29*a48b9003SAbdelkader Boudih #include <dev/asmc/asmcmmio.h>
30*a48b9003SAbdelkader Boudih
31*a48b9003SAbdelkader Boudih /*
32*a48b9003SAbdelkader Boudih * Wait for MMIO status register bit 5 (ready) with exponential backoff.
33*a48b9003SAbdelkader Boudih * Caller must hold sc_mtx.
34*a48b9003SAbdelkader Boudih */
35*a48b9003SAbdelkader Boudih static int
asmc_mmio_wait(device_t dev)36*a48b9003SAbdelkader Boudih asmc_mmio_wait(device_t dev)
37*a48b9003SAbdelkader Boudih {
38*a48b9003SAbdelkader Boudih struct asmc_softc *sc = device_get_softc(dev);
39*a48b9003SAbdelkader Boudih int i;
40*a48b9003SAbdelkader Boudih uint8_t status;
41*a48b9003SAbdelkader Boudih int delay_us = 10;
42*a48b9003SAbdelkader Boudih
43*a48b9003SAbdelkader Boudih for (i = 0; i < ASMC_MMIO_MAX_WAIT; i++) {
44*a48b9003SAbdelkader Boudih status = bus_read_1(sc->sc_iomem, ASMC_MMIO_STATUS);
45*a48b9003SAbdelkader Boudih if (status & ASMC_MMIO_STATUS_READY)
46*a48b9003SAbdelkader Boudih return (0);
47*a48b9003SAbdelkader Boudih DELAY(delay_us);
48*a48b9003SAbdelkader Boudih if (delay_us < 3200)
49*a48b9003SAbdelkader Boudih delay_us *= 2;
50*a48b9003SAbdelkader Boudih }
51*a48b9003SAbdelkader Boudih
52*a48b9003SAbdelkader Boudih return (ETIMEDOUT);
53*a48b9003SAbdelkader Boudih }
54*a48b9003SAbdelkader Boudih
55*a48b9003SAbdelkader Boudih int
asmc_mmio_key_read(device_t dev,const char * key,uint8_t * buf,uint8_t len)56*a48b9003SAbdelkader Boudih asmc_mmio_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len)
57*a48b9003SAbdelkader Boudih {
58*a48b9003SAbdelkader Boudih struct asmc_softc *sc = device_get_softc(dev);
59*a48b9003SAbdelkader Boudih uint32_t key_int;
60*a48b9003SAbdelkader Boudih int error, i;
61*a48b9003SAbdelkader Boudih uint8_t cmd_result, rlen;
62*a48b9003SAbdelkader Boudih
63*a48b9003SAbdelkader Boudih if (len > ASMC_MAXVAL)
64*a48b9003SAbdelkader Boudih return (EINVAL);
65*a48b9003SAbdelkader Boudih
66*a48b9003SAbdelkader Boudih mtx_lock_spin(&sc->sc_mtx);
67*a48b9003SAbdelkader Boudih
68*a48b9003SAbdelkader Boudih /* Clear status if non-zero */
69*a48b9003SAbdelkader Boudih if (bus_read_1(sc->sc_iomem, ASMC_MMIO_STATUS))
70*a48b9003SAbdelkader Boudih bus_write_1(sc->sc_iomem, ASMC_MMIO_STATUS, 0);
71*a48b9003SAbdelkader Boudih
72*a48b9003SAbdelkader Boudih /* Write key name as raw 4 bytes */
73*a48b9003SAbdelkader Boudih memcpy(&key_int, key, 4);
74*a48b9003SAbdelkader Boudih bus_write_4(sc->sc_iomem, ASMC_MMIO_KEY_NAME, key_int);
75*a48b9003SAbdelkader Boudih
76*a48b9003SAbdelkader Boudih /* Write SMC ID (always 0) and command */
77*a48b9003SAbdelkader Boudih bus_write_1(sc->sc_iomem, ASMC_MMIO_SMC_ID, 0);
78*a48b9003SAbdelkader Boudih bus_write_1(sc->sc_iomem, ASMC_MMIO_CMD, ASMC_CMDREAD);
79*a48b9003SAbdelkader Boudih
80*a48b9003SAbdelkader Boudih /* Wait for ready */
81*a48b9003SAbdelkader Boudih error = asmc_mmio_wait(dev);
82*a48b9003SAbdelkader Boudih if (error != 0) {
83*a48b9003SAbdelkader Boudih uint8_t st = bus_read_1(sc->sc_iomem, ASMC_MMIO_STATUS);
84*a48b9003SAbdelkader Boudih uint8_t cm = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD);
85*a48b9003SAbdelkader Boudih mtx_unlock_spin(&sc->sc_mtx);
86*a48b9003SAbdelkader Boudih device_printf(dev,
87*a48b9003SAbdelkader Boudih "%s: timeout key %.4s status=0x%02x cmd=0x%02x\n",
88*a48b9003SAbdelkader Boudih __func__, key, st, cm);
89*a48b9003SAbdelkader Boudih return (error);
90*a48b9003SAbdelkader Boudih }
91*a48b9003SAbdelkader Boudih
92*a48b9003SAbdelkader Boudih /* Check command result (0 = success, 0x84 = key not found) */
93*a48b9003SAbdelkader Boudih cmd_result = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD);
94*a48b9003SAbdelkader Boudih if (cmd_result != 0) {
95*a48b9003SAbdelkader Boudih mtx_unlock_spin(&sc->sc_mtx);
96*a48b9003SAbdelkader Boudih device_printf(dev,
97*a48b9003SAbdelkader Boudih "%s: key %.4s cmd error 0x%02x\n",
98*a48b9003SAbdelkader Boudih __func__, key, cmd_result);
99*a48b9003SAbdelkader Boudih return (EIO);
100*a48b9003SAbdelkader Boudih }
101*a48b9003SAbdelkader Boudih
102*a48b9003SAbdelkader Boudih /* Read data length and data bytes; zero-fill remainder */
103*a48b9003SAbdelkader Boudih rlen = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA_LEN);
104*a48b9003SAbdelkader Boudih rlen = MIN(rlen, len);
105*a48b9003SAbdelkader Boudih for (i = rlen; i < len; i++)
106*a48b9003SAbdelkader Boudih buf[i] = 0;
107*a48b9003SAbdelkader Boudih for (i = 0; i < rlen; i++)
108*a48b9003SAbdelkader Boudih buf[i] = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA + i);
109*a48b9003SAbdelkader Boudih
110*a48b9003SAbdelkader Boudih mtx_unlock_spin(&sc->sc_mtx);
111*a48b9003SAbdelkader Boudih return (0);
112*a48b9003SAbdelkader Boudih }
113*a48b9003SAbdelkader Boudih
114*a48b9003SAbdelkader Boudih int
asmc_mmio_key_write(device_t dev,const char * key,uint8_t * buf,uint8_t len)115*a48b9003SAbdelkader Boudih asmc_mmio_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len)
116*a48b9003SAbdelkader Boudih {
117*a48b9003SAbdelkader Boudih struct asmc_softc *sc = device_get_softc(dev);
118*a48b9003SAbdelkader Boudih uint32_t key_int;
119*a48b9003SAbdelkader Boudih int error, i;
120*a48b9003SAbdelkader Boudih uint8_t cmd_result;
121*a48b9003SAbdelkader Boudih
122*a48b9003SAbdelkader Boudih if (len > ASMC_MAXVAL)
123*a48b9003SAbdelkader Boudih return (EINVAL);
124*a48b9003SAbdelkader Boudih
125*a48b9003SAbdelkader Boudih mtx_lock_spin(&sc->sc_mtx);
126*a48b9003SAbdelkader Boudih
127*a48b9003SAbdelkader Boudih /* Clear status */
128*a48b9003SAbdelkader Boudih bus_write_1(sc->sc_iomem, ASMC_MMIO_STATUS, 0);
129*a48b9003SAbdelkader Boudih
130*a48b9003SAbdelkader Boudih /* Write data bytes first */
131*a48b9003SAbdelkader Boudih for (i = 0; i < len; i++)
132*a48b9003SAbdelkader Boudih bus_write_1(sc->sc_iomem, ASMC_MMIO_DATA + i, buf[i]);
133*a48b9003SAbdelkader Boudih
134*a48b9003SAbdelkader Boudih /* Write key name as raw 4 bytes */
135*a48b9003SAbdelkader Boudih memcpy(&key_int, key, 4);
136*a48b9003SAbdelkader Boudih bus_write_4(sc->sc_iomem, ASMC_MMIO_KEY_NAME, key_int);
137*a48b9003SAbdelkader Boudih
138*a48b9003SAbdelkader Boudih /* Write length, SMC ID, command */
139*a48b9003SAbdelkader Boudih bus_write_1(sc->sc_iomem, ASMC_MMIO_DATA_LEN, len);
140*a48b9003SAbdelkader Boudih bus_write_1(sc->sc_iomem, ASMC_MMIO_SMC_ID, 0);
141*a48b9003SAbdelkader Boudih bus_write_1(sc->sc_iomem, ASMC_MMIO_CMD, ASMC_CMDWRITE);
142*a48b9003SAbdelkader Boudih
143*a48b9003SAbdelkader Boudih /* Wait for ready */
144*a48b9003SAbdelkader Boudih error = asmc_mmio_wait(dev);
145*a48b9003SAbdelkader Boudih if (error != 0) {
146*a48b9003SAbdelkader Boudih mtx_unlock_spin(&sc->sc_mtx);
147*a48b9003SAbdelkader Boudih device_printf(dev, "%s: timeout writing key %.4s\n",
148*a48b9003SAbdelkader Boudih __func__, key);
149*a48b9003SAbdelkader Boudih return (error);
150*a48b9003SAbdelkader Boudih }
151*a48b9003SAbdelkader Boudih
152*a48b9003SAbdelkader Boudih cmd_result = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD);
153*a48b9003SAbdelkader Boudih mtx_unlock_spin(&sc->sc_mtx);
154*a48b9003SAbdelkader Boudih
155*a48b9003SAbdelkader Boudih return (cmd_result == 0 ? 0 : EIO);
156*a48b9003SAbdelkader Boudih }
157*a48b9003SAbdelkader Boudih
158*a48b9003SAbdelkader Boudih int
asmc_mmio_key_getinfo(device_t dev,const char * key,uint8_t * len,char * type)159*a48b9003SAbdelkader Boudih asmc_mmio_key_getinfo(device_t dev, const char *key, uint8_t *len, char *type)
160*a48b9003SAbdelkader Boudih {
161*a48b9003SAbdelkader Boudih struct asmc_softc *sc = device_get_softc(dev);
162*a48b9003SAbdelkader Boudih uint32_t key_int;
163*a48b9003SAbdelkader Boudih int error, i;
164*a48b9003SAbdelkader Boudih uint8_t cmd_result;
165*a48b9003SAbdelkader Boudih
166*a48b9003SAbdelkader Boudih mtx_lock_spin(&sc->sc_mtx);
167*a48b9003SAbdelkader Boudih
168*a48b9003SAbdelkader Boudih /* Clear status */
169*a48b9003SAbdelkader Boudih bus_write_1(sc->sc_iomem, ASMC_MMIO_STATUS, 0);
170*a48b9003SAbdelkader Boudih
171*a48b9003SAbdelkader Boudih /* Write key name as raw 4 bytes */
172*a48b9003SAbdelkader Boudih memcpy(&key_int, key, 4);
173*a48b9003SAbdelkader Boudih bus_write_4(sc->sc_iomem, ASMC_MMIO_KEY_NAME, key_int);
174*a48b9003SAbdelkader Boudih
175*a48b9003SAbdelkader Boudih bus_write_1(sc->sc_iomem, ASMC_MMIO_SMC_ID, 0);
176*a48b9003SAbdelkader Boudih bus_write_1(sc->sc_iomem, ASMC_MMIO_CMD, ASMC_CMDGETINFO);
177*a48b9003SAbdelkader Boudih
178*a48b9003SAbdelkader Boudih error = asmc_mmio_wait(dev);
179*a48b9003SAbdelkader Boudih if (error != 0) {
180*a48b9003SAbdelkader Boudih mtx_unlock_spin(&sc->sc_mtx);
181*a48b9003SAbdelkader Boudih return (error);
182*a48b9003SAbdelkader Boudih }
183*a48b9003SAbdelkader Boudih
184*a48b9003SAbdelkader Boudih cmd_result = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD);
185*a48b9003SAbdelkader Boudih if (cmd_result != 0) {
186*a48b9003SAbdelkader Boudih mtx_unlock_spin(&sc->sc_mtx);
187*a48b9003SAbdelkader Boudih return (EIO);
188*a48b9003SAbdelkader Boudih }
189*a48b9003SAbdelkader Boudih
190*a48b9003SAbdelkader Boudih /*
191*a48b9003SAbdelkader Boudih * GETINFO response layout (MMIO):
192*a48b9003SAbdelkader Boudih * data[0..3] = type code (4 chars)
193*a48b9003SAbdelkader Boudih * data[4] = reserved
194*a48b9003SAbdelkader Boudih * data[5] = data length
195*a48b9003SAbdelkader Boudih * data[6] = flags/attributes
196*a48b9003SAbdelkader Boudih */
197*a48b9003SAbdelkader Boudih if (type != NULL) {
198*a48b9003SAbdelkader Boudih for (i = 0; i < ASMC_TYPELEN; i++)
199*a48b9003SAbdelkader Boudih type[i] = bus_read_1(sc->sc_iomem,
200*a48b9003SAbdelkader Boudih ASMC_MMIO_DATA + i);
201*a48b9003SAbdelkader Boudih type[ASMC_TYPELEN] = '\0';
202*a48b9003SAbdelkader Boudih }
203*a48b9003SAbdelkader Boudih if (len != NULL)
204*a48b9003SAbdelkader Boudih *len = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA + 5);
205*a48b9003SAbdelkader Boudih
206*a48b9003SAbdelkader Boudih mtx_unlock_spin(&sc->sc_mtx);
207*a48b9003SAbdelkader Boudih return (0);
208*a48b9003SAbdelkader Boudih }
209*a48b9003SAbdelkader Boudih
210*a48b9003SAbdelkader Boudih int
asmc_mmio_key_getbyindex(device_t dev,int index,char * key)211*a48b9003SAbdelkader Boudih asmc_mmio_key_getbyindex(device_t dev, int index, char *key)
212*a48b9003SAbdelkader Boudih {
213*a48b9003SAbdelkader Boudih struct asmc_softc *sc = device_get_softc(dev);
214*a48b9003SAbdelkader Boudih uint32_t idx_val;
215*a48b9003SAbdelkader Boudih int error, i;
216*a48b9003SAbdelkader Boudih uint8_t cmd_result;
217*a48b9003SAbdelkader Boudih
218*a48b9003SAbdelkader Boudih mtx_lock_spin(&sc->sc_mtx);
219*a48b9003SAbdelkader Boudih
220*a48b9003SAbdelkader Boudih bus_write_1(sc->sc_iomem, ASMC_MMIO_STATUS, 0);
221*a48b9003SAbdelkader Boudih
222*a48b9003SAbdelkader Boudih /* Write index as big-endian 4 bytes to key name register */
223*a48b9003SAbdelkader Boudih idx_val = htobe32(index);
224*a48b9003SAbdelkader Boudih bus_write_4(sc->sc_iomem, ASMC_MMIO_KEY_NAME, idx_val);
225*a48b9003SAbdelkader Boudih
226*a48b9003SAbdelkader Boudih bus_write_1(sc->sc_iomem, ASMC_MMIO_SMC_ID, 0);
227*a48b9003SAbdelkader Boudih bus_write_1(sc->sc_iomem, ASMC_MMIO_CMD, ASMC_CMDGETBYINDEX);
228*a48b9003SAbdelkader Boudih
229*a48b9003SAbdelkader Boudih error = asmc_mmio_wait(dev);
230*a48b9003SAbdelkader Boudih if (error != 0) {
231*a48b9003SAbdelkader Boudih mtx_unlock_spin(&sc->sc_mtx);
232*a48b9003SAbdelkader Boudih return (error);
233*a48b9003SAbdelkader Boudih }
234*a48b9003SAbdelkader Boudih
235*a48b9003SAbdelkader Boudih cmd_result = bus_read_1(sc->sc_iomem, ASMC_MMIO_CMD);
236*a48b9003SAbdelkader Boudih if (cmd_result != 0) {
237*a48b9003SAbdelkader Boudih mtx_unlock_spin(&sc->sc_mtx);
238*a48b9003SAbdelkader Boudih return (EIO);
239*a48b9003SAbdelkader Boudih }
240*a48b9003SAbdelkader Boudih
241*a48b9003SAbdelkader Boudih /* Result: 4-byte key name in DATA */
242*a48b9003SAbdelkader Boudih for (i = 0; i < ASMC_KEYLEN; i++)
243*a48b9003SAbdelkader Boudih key[i] = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA + i);
244*a48b9003SAbdelkader Boudih key[ASMC_KEYLEN] = '\0';
245*a48b9003SAbdelkader Boudih
246*a48b9003SAbdelkader Boudih mtx_unlock_spin(&sc->sc_mtx);
247*a48b9003SAbdelkader Boudih return (0);
248*a48b9003SAbdelkader Boudih }
249*a48b9003SAbdelkader Boudih
250*a48b9003SAbdelkader Boudih /*
251*a48b9003SAbdelkader Boudih * Validate MMIO and detect T2.
252*a48b9003SAbdelkader Boudih * Check that status register is accessible and LDKN firmware version >= 2.
253*a48b9003SAbdelkader Boudih */
254*a48b9003SAbdelkader Boudih int
asmc_mmio_probe(device_t dev)255*a48b9003SAbdelkader Boudih asmc_mmio_probe(device_t dev)
256*a48b9003SAbdelkader Boudih {
257*a48b9003SAbdelkader Boudih struct asmc_softc *sc = device_get_softc(dev);
258*a48b9003SAbdelkader Boudih rman_res_t size;
259*a48b9003SAbdelkader Boudih uint8_t status, ldkn;
260*a48b9003SAbdelkader Boudih int error;
261*a48b9003SAbdelkader Boudih
262*a48b9003SAbdelkader Boudih size = rman_get_size(sc->sc_iomem);
263*a48b9003SAbdelkader Boudih if (size < ASMC_MMIO_MIN_SIZE) {
264*a48b9003SAbdelkader Boudih device_printf(dev, "MMIO region too small (%jd < %d)\n",
265*a48b9003SAbdelkader Boudih (intmax_t)size, ASMC_MMIO_MIN_SIZE);
266*a48b9003SAbdelkader Boudih return (ENXIO);
267*a48b9003SAbdelkader Boudih }
268*a48b9003SAbdelkader Boudih
269*a48b9003SAbdelkader Boudih /* Check status register isn't stuck at 0xFF */
270*a48b9003SAbdelkader Boudih status = bus_read_1(sc->sc_iomem, ASMC_MMIO_STATUS);
271*a48b9003SAbdelkader Boudih if (status == 0xFF) {
272*a48b9003SAbdelkader Boudih device_printf(dev, "MMIO status register reads 0xFF\n");
273*a48b9003SAbdelkader Boudih return (ENXIO);
274*a48b9003SAbdelkader Boudih }
275*a48b9003SAbdelkader Boudih
276*a48b9003SAbdelkader Boudih /*
277*a48b9003SAbdelkader Boudih * We need the mutex initialized before calling mmio_key_read,
278*a48b9003SAbdelkader Boudih * but attach hasn't done it yet. Initialize early.
279*a48b9003SAbdelkader Boudih */
280*a48b9003SAbdelkader Boudih if (!mtx_initialized(&sc->sc_mtx))
281*a48b9003SAbdelkader Boudih mtx_init(&sc->sc_mtx, "asmc", NULL, MTX_SPIN);
282*a48b9003SAbdelkader Boudih
283*a48b9003SAbdelkader Boudih /* Read LDKN (firmware version) -- must be >= 2 for MMIO */
284*a48b9003SAbdelkader Boudih error = asmc_mmio_key_read(dev, ASMC_KEY_LDKN, &ldkn, 1);
285*a48b9003SAbdelkader Boudih if (error != 0) {
286*a48b9003SAbdelkader Boudih device_printf(dev, "MMIO: failed to read LDKN key\n");
287*a48b9003SAbdelkader Boudih return (ENXIO);
288*a48b9003SAbdelkader Boudih }
289*a48b9003SAbdelkader Boudih
290*a48b9003SAbdelkader Boudih if (ldkn < 2) {
291*a48b9003SAbdelkader Boudih device_printf(dev, "MMIO: LDKN=%d (need >= 2)\n", ldkn);
292*a48b9003SAbdelkader Boudih return (ENXIO);
293*a48b9003SAbdelkader Boudih }
294*a48b9003SAbdelkader Boudih
295*a48b9003SAbdelkader Boudih device_printf(dev, "MMIO: LDKN=%d, T2 SMC detected\n", ldkn);
296*a48b9003SAbdelkader Boudih sc->sc_is_t2 = 1;
297*a48b9003SAbdelkader Boudih
298*a48b9003SAbdelkader Boudih return (0);
299*a48b9003SAbdelkader Boudih }
300*a48b9003SAbdelkader Boudih
301*a48b9003SAbdelkader Boudih void
asmc_mmio_detach(device_t dev,struct asmc_softc * sc)302*a48b9003SAbdelkader Boudih asmc_mmio_detach(device_t dev, struct asmc_softc *sc)
303*a48b9003SAbdelkader Boudih {
304*a48b9003SAbdelkader Boudih
305*a48b9003SAbdelkader Boudih if (sc->sc_iomem != NULL) {
306*a48b9003SAbdelkader Boudih bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid_mem,
307*a48b9003SAbdelkader Boudih sc->sc_iomem);
308*a48b9003SAbdelkader Boudih sc->sc_iomem = NULL;
309*a48b9003SAbdelkader Boudih }
310*a48b9003SAbdelkader Boudih sc->sc_is_mmio = 0;
311*a48b9003SAbdelkader Boudih sc->sc_is_t2 = 0;
312*a48b9003SAbdelkader Boudih }
313*a48b9003SAbdelkader Boudih
314*a48b9003SAbdelkader Boudih /*
315*a48b9003SAbdelkader Boudih * Convert IEEE 754 float (as u32) to unsigned integer.
316*a48b9003SAbdelkader Boudih * Kernel soft-float: extract integer part only.
317*a48b9003SAbdelkader Boudih * Used for T2 fan RPM values (always positive, reasonable range).
318*a48b9003SAbdelkader Boudih */
319*a48b9003SAbdelkader Boudih uint32_t
asmc_float_to_u32(uint32_t d)320*a48b9003SAbdelkader Boudih asmc_float_to_u32(uint32_t d)
321*a48b9003SAbdelkader Boudih {
322*a48b9003SAbdelkader Boudih int32_t exp;
323*a48b9003SAbdelkader Boudih uint32_t fr;
324*a48b9003SAbdelkader Boudih
325*a48b9003SAbdelkader Boudih /* Negative or zero */
326*a48b9003SAbdelkader Boudih if (d == 0 || (d >> 31) != 0)
327*a48b9003SAbdelkader Boudih return (0);
328*a48b9003SAbdelkader Boudih
329*a48b9003SAbdelkader Boudih exp = (int32_t)((d >> 23) & 0xff) - 0x7f;
330*a48b9003SAbdelkader Boudih fr = d & 0x7fffff; /* 23-bit mantissa */
331*a48b9003SAbdelkader Boudih
332*a48b9003SAbdelkader Boudih if (exp < 0)
333*a48b9003SAbdelkader Boudih return (0);
334*a48b9003SAbdelkader Boudih if (exp > 23) {
335*a48b9003SAbdelkader Boudih if (exp > 30)
336*a48b9003SAbdelkader Boudih return (0xffffffffu);
337*a48b9003SAbdelkader Boudih return ((1u << exp) | (fr << (exp - 23)));
338*a48b9003SAbdelkader Boudih }
339*a48b9003SAbdelkader Boudih /* Normal case: 0 <= exp <= 23 */
340*a48b9003SAbdelkader Boudih return ((1u << exp) + (fr >> (23 - exp)));
341*a48b9003SAbdelkader Boudih }
342*a48b9003SAbdelkader Boudih
343*a48b9003SAbdelkader Boudih /*
344*a48b9003SAbdelkader Boudih * Convert unsigned integer to IEEE 754 float (as u32).
345*a48b9003SAbdelkader Boudih * Only handles values in fan RPM range (0-65535).
346*a48b9003SAbdelkader Boudih */
347*a48b9003SAbdelkader Boudih uint32_t
asmc_u32_to_float(uint32_t d)348*a48b9003SAbdelkader Boudih asmc_u32_to_float(uint32_t d)
349*a48b9003SAbdelkader Boudih {
350*a48b9003SAbdelkader Boudih uint32_t dc, bc, exp;
351*a48b9003SAbdelkader Boudih
352*a48b9003SAbdelkader Boudih if (d == 0)
353*a48b9003SAbdelkader Boudih return (0);
354*a48b9003SAbdelkader Boudih
355*a48b9003SAbdelkader Boudih /* Find highest set bit position */
356*a48b9003SAbdelkader Boudih dc = d;
357*a48b9003SAbdelkader Boudih bc = 0;
358*a48b9003SAbdelkader Boudih while (dc >>= 1)
359*a48b9003SAbdelkader Boudih ++bc;
360*a48b9003SAbdelkader Boudih
361*a48b9003SAbdelkader Boudih bc = MIN(bc, 30);
362*a48b9003SAbdelkader Boudih
363*a48b9003SAbdelkader Boudih exp = 0x7f + bc;
364*a48b9003SAbdelkader Boudih
365*a48b9003SAbdelkader Boudih /*
366*a48b9003SAbdelkader Boudih * Mantissa: strip the implicit leading 1-bit and place
367*a48b9003SAbdelkader Boudih * remaining bits into the 23-bit mantissa field.
368*a48b9003SAbdelkader Boudih */
369*a48b9003SAbdelkader Boudih if (bc >= 23)
370*a48b9003SAbdelkader Boudih return ((exp << 23) | ((d >> (bc - 23)) & 0x7fffff));
371*a48b9003SAbdelkader Boudih else
372*a48b9003SAbdelkader Boudih return ((exp << 23) | ((d << (23 - bc)) & 0x7fffff));
373*a48b9003SAbdelkader Boudih }
374*a48b9003SAbdelkader Boudih
375*a48b9003SAbdelkader Boudih /*
376*a48b9003SAbdelkader Boudih * Battery charge limit sysctl (T2 Macs).
377*a48b9003SAbdelkader Boudih * BCLM key: 1 byte, 0-100 (percentage).
378*a48b9003SAbdelkader Boudih */
379*a48b9003SAbdelkader Boudih int
asmc_bclm_sysctl(SYSCTL_HANDLER_ARGS)380*a48b9003SAbdelkader Boudih asmc_bclm_sysctl(SYSCTL_HANDLER_ARGS)
381*a48b9003SAbdelkader Boudih {
382*a48b9003SAbdelkader Boudih device_t dev = (device_t)arg1;
383*a48b9003SAbdelkader Boudih uint8_t bclm;
384*a48b9003SAbdelkader Boudih int val, error;
385*a48b9003SAbdelkader Boudih
386*a48b9003SAbdelkader Boudih error = asmc_mmio_key_read(dev, ASMC_KEY_BCLM, &bclm, 1);
387*a48b9003SAbdelkader Boudih if (error != 0)
388*a48b9003SAbdelkader Boudih return (EIO);
389*a48b9003SAbdelkader Boudih
390*a48b9003SAbdelkader Boudih val = (int)bclm;
391*a48b9003SAbdelkader Boudih error = sysctl_handle_int(oidp, &val, 0, req);
392*a48b9003SAbdelkader Boudih if (error != 0 || req->newptr == NULL)
393*a48b9003SAbdelkader Boudih return (error);
394*a48b9003SAbdelkader Boudih
395*a48b9003SAbdelkader Boudih if (val < 0 || val > 100)
396*a48b9003SAbdelkader Boudih return (EINVAL);
397*a48b9003SAbdelkader Boudih
398*a48b9003SAbdelkader Boudih bclm = (uint8_t)val;
399*a48b9003SAbdelkader Boudih error = asmc_mmio_key_write(dev, ASMC_KEY_BCLM, &bclm, 1);
400*a48b9003SAbdelkader Boudih
401*a48b9003SAbdelkader Boudih return (error != 0 ? EIO : 0);
402*a48b9003SAbdelkader Boudih }
403