xref: /freebsd/sys/dev/asmc/asmcmmio.c (revision a48b900300ebdbd5c47e664b4cc06e705da91bd8)
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