xref: /netbsd-src/sys/dev/ipmi.c (revision aa194c9cc143963c2e47fadaadd9ae62e89520c7)
1*aa194c9cSriastradh /*	$NetBSD: ipmi.c,v 1.14 2024/12/04 15:26:07 riastradh Exp $ */
2dc766f2bSmlelstv 
3dc766f2bSmlelstv /*
4b1f23fc9Smlelstv  * Copyright (c) 2019 Michael van Elst
5b1f23fc9Smlelstv  *
6b1f23fc9Smlelstv  * Redistribution and use in source and binary forms, with or without
7b1f23fc9Smlelstv  * modification, are permitted provided that the following conditions
8b1f23fc9Smlelstv  * are met:
9b1f23fc9Smlelstv  * 1. Redistributions of source code must retain the above copyright
10b1f23fc9Smlelstv  *    notice, this list of conditions and the following disclaimer.
11b1f23fc9Smlelstv  * 2. Redistributions in binary form must reproduce the above copyright
12b1f23fc9Smlelstv  *    notice, this list of conditions and the following disclaimer in the
13b1f23fc9Smlelstv  *    documentation and/or other materials provided with the distribution.
14b1f23fc9Smlelstv  *
15b1f23fc9Smlelstv  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16b1f23fc9Smlelstv  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17b1f23fc9Smlelstv  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18b1f23fc9Smlelstv  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19b1f23fc9Smlelstv  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20b1f23fc9Smlelstv  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21b1f23fc9Smlelstv  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22b1f23fc9Smlelstv  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23b1f23fc9Smlelstv  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24b1f23fc9Smlelstv  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25b1f23fc9Smlelstv  *
26b1f23fc9Smlelstv  */
27b1f23fc9Smlelstv /*
28dc766f2bSmlelstv  * Copyright (c) 2006 Manuel Bouyer.
29dc766f2bSmlelstv  *
30dc766f2bSmlelstv  * Redistribution and use in source and binary forms, with or without
31dc766f2bSmlelstv  * modification, are permitted provided that the following conditions
32dc766f2bSmlelstv  * are met:
33dc766f2bSmlelstv  * 1. Redistributions of source code must retain the above copyright
34dc766f2bSmlelstv  *    notice, this list of conditions and the following disclaimer.
35dc766f2bSmlelstv  * 2. Redistributions in binary form must reproduce the above copyright
36dc766f2bSmlelstv  *    notice, this list of conditions and the following disclaimer in the
37dc766f2bSmlelstv  *    documentation and/or other materials provided with the distribution.
38dc766f2bSmlelstv  *
39dc766f2bSmlelstv  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
40dc766f2bSmlelstv  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
41dc766f2bSmlelstv  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
42dc766f2bSmlelstv  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
43dc766f2bSmlelstv  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44dc766f2bSmlelstv  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
45dc766f2bSmlelstv  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
46dc766f2bSmlelstv  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
47dc766f2bSmlelstv  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
48dc766f2bSmlelstv  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49dc766f2bSmlelstv  *
50dc766f2bSmlelstv  */
51dc766f2bSmlelstv 
52dc766f2bSmlelstv /*
53dc766f2bSmlelstv  * Copyright (c) 2005 Jordan Hargrave
54dc766f2bSmlelstv  * All rights reserved.
55dc766f2bSmlelstv  *
56dc766f2bSmlelstv  * Redistribution and use in source and binary forms, with or without
57dc766f2bSmlelstv  * modification, are permitted provided that the following conditions
58dc766f2bSmlelstv  * are met:
59dc766f2bSmlelstv  * 1. Redistributions of source code must retain the above copyright
60dc766f2bSmlelstv  *    notice, this list of conditions and the following disclaimer.
61dc766f2bSmlelstv  * 2. Redistributions in binary form must reproduce the above copyright
62dc766f2bSmlelstv  *    notice, this list of conditions and the following disclaimer in the
63dc766f2bSmlelstv  *    documentation and/or other materials provided with the distribution.
64dc766f2bSmlelstv  *
65dc766f2bSmlelstv  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
66dc766f2bSmlelstv  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
67dc766f2bSmlelstv  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
68dc766f2bSmlelstv  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
69dc766f2bSmlelstv  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
70dc766f2bSmlelstv  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
71dc766f2bSmlelstv  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
72dc766f2bSmlelstv  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
73dc766f2bSmlelstv  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
74dc766f2bSmlelstv  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
75dc766f2bSmlelstv  * SUCH DAMAGE.
76dc766f2bSmlelstv  */
77dc766f2bSmlelstv 
78dc766f2bSmlelstv #include <sys/cdefs.h>
79*aa194c9cSriastradh __KERNEL_RCSID(0, "$NetBSD: ipmi.c,v 1.14 2024/12/04 15:26:07 riastradh Exp $");
80dc766f2bSmlelstv 
81dc766f2bSmlelstv #include <sys/types.h>
82dc766f2bSmlelstv #include <sys/param.h>
83dc766f2bSmlelstv #include <sys/systm.h>
84dc766f2bSmlelstv #include <sys/kernel.h>
85dc766f2bSmlelstv #include <sys/device.h>
86dc766f2bSmlelstv #include <sys/extent.h>
87dc766f2bSmlelstv #include <sys/callout.h>
88dc766f2bSmlelstv #include <sys/envsys.h>
89dc766f2bSmlelstv #include <sys/malloc.h>
90dc766f2bSmlelstv #include <sys/kthread.h>
91dc766f2bSmlelstv #include <sys/bus.h>
92dc766f2bSmlelstv #include <sys/intr.h>
93b1f23fc9Smlelstv #include <sys/ioctl.h>
94b1f23fc9Smlelstv #include <sys/poll.h>
95b1f23fc9Smlelstv #include <sys/conf.h>
96dc766f2bSmlelstv 
97dc766f2bSmlelstv #include <dev/isa/isareg.h>
98dc766f2bSmlelstv #include <dev/isa/isavar.h>
99dc766f2bSmlelstv 
100b1f23fc9Smlelstv #include <sys/ipmi.h>
101dc766f2bSmlelstv #include <dev/ipmivar.h>
102dc766f2bSmlelstv 
103dc766f2bSmlelstv #include <uvm/uvm_extern.h>
104dc766f2bSmlelstv 
105b1f23fc9Smlelstv #include "ioconf.h"
106b1f23fc9Smlelstv 
107b1f23fc9Smlelstv static dev_type_open(ipmi_open);
108b1f23fc9Smlelstv static dev_type_close(ipmi_close);
109b1f23fc9Smlelstv static dev_type_ioctl(ipmi_ioctl);
110b1f23fc9Smlelstv static dev_type_poll(ipmi_poll);
111b1f23fc9Smlelstv 
112b1f23fc9Smlelstv const struct cdevsw ipmi_cdevsw = {
113b1f23fc9Smlelstv 	.d_open = ipmi_open,
114b1f23fc9Smlelstv 	.d_close = ipmi_close,
115b1f23fc9Smlelstv 	.d_read = noread,
116b1f23fc9Smlelstv 	.d_write = nowrite,
117b1f23fc9Smlelstv 	.d_ioctl = ipmi_ioctl,
118b1f23fc9Smlelstv 	.d_stop = nostop,
119b1f23fc9Smlelstv 	.d_tty = notty,
120b1f23fc9Smlelstv 	.d_poll = ipmi_poll,
121b1f23fc9Smlelstv 	.d_mmap = nommap,
122b1f23fc9Smlelstv 	.d_kqfilter = nokqfilter,
123b1f23fc9Smlelstv 	.d_discard = nodiscard,
124b1f23fc9Smlelstv 	.d_flag = D_OTHER
125b1f23fc9Smlelstv };
126b1f23fc9Smlelstv 
127b1f23fc9Smlelstv #define IPMIUNIT(n) (minor(n))
128b1f23fc9Smlelstv 
129dc766f2bSmlelstv struct ipmi_sensor {
130dc766f2bSmlelstv 	uint8_t	*i_sdr;
131dc766f2bSmlelstv 	int		i_num;
132dc766f2bSmlelstv 	int		i_stype;
133dc766f2bSmlelstv 	int		i_etype;
134dc766f2bSmlelstv 	char		i_envdesc[64];
135dc766f2bSmlelstv 	int 		i_envtype; /* envsys compatible type */
136dc766f2bSmlelstv 	int		i_envnum; /* envsys index */
137dc766f2bSmlelstv 	sysmon_envsys_lim_t i_limits, i_deflims;
138dc766f2bSmlelstv 	uint32_t	i_props, i_defprops;
139dc766f2bSmlelstv 	SLIST_ENTRY(ipmi_sensor) i_list;
140dc766f2bSmlelstv 	int32_t		i_prevval;	/* feed rnd source on change */
141dc766f2bSmlelstv };
142dc766f2bSmlelstv 
143dc766f2bSmlelstv #if 0
144dc766f2bSmlelstv static	int ipmi_nintr;
145dc766f2bSmlelstv #endif
146dc766f2bSmlelstv static	int ipmi_dbg = 0;
147dc766f2bSmlelstv static	int ipmi_enabled = 0;
148dc766f2bSmlelstv 
149dc766f2bSmlelstv #define SENSOR_REFRESH_RATE (hz / 2)
150dc766f2bSmlelstv 
151dc766f2bSmlelstv #define IPMI_BTMSG_LEN			0
152dc766f2bSmlelstv #define IPMI_BTMSG_NFLN			1
153dc766f2bSmlelstv #define IPMI_BTMSG_SEQ			2
154dc766f2bSmlelstv #define IPMI_BTMSG_CMD			3
155dc766f2bSmlelstv #define IPMI_BTMSG_CCODE		4
156dc766f2bSmlelstv #define IPMI_BTMSG_DATASND		4
157dc766f2bSmlelstv #define IPMI_BTMSG_DATARCV		5
158dc766f2bSmlelstv 
159dc766f2bSmlelstv #define IPMI_MSG_NFLN			0
160dc766f2bSmlelstv #define IPMI_MSG_CMD			1
161dc766f2bSmlelstv #define IPMI_MSG_CCODE			2
162dc766f2bSmlelstv #define IPMI_MSG_DATASND		2
163dc766f2bSmlelstv #define IPMI_MSG_DATARCV		3
164dc766f2bSmlelstv 
165dc766f2bSmlelstv #define IPMI_SENSOR_TYPE_TEMP		0x0101
166dc766f2bSmlelstv #define IPMI_SENSOR_TYPE_VOLT		0x0102
167dc766f2bSmlelstv #define IPMI_SENSOR_TYPE_FAN		0x0104
168dc766f2bSmlelstv #define IPMI_SENSOR_TYPE_INTRUSION	0x6F05
169dc766f2bSmlelstv #define IPMI_SENSOR_TYPE_PWRSUPPLY	0x6F08
170dc766f2bSmlelstv 
171dc766f2bSmlelstv #define IPMI_NAME_UNICODE		0x00
172dc766f2bSmlelstv #define IPMI_NAME_BCDPLUS		0x01
173dc766f2bSmlelstv #define IPMI_NAME_ASCII6BIT		0x02
174dc766f2bSmlelstv #define IPMI_NAME_ASCII8BIT		0x03
175dc766f2bSmlelstv 
176dc766f2bSmlelstv #define IPMI_ENTITY_PWRSUPPLY		0x0A
177dc766f2bSmlelstv 
178dc766f2bSmlelstv #define IPMI_SENSOR_SCANNING_ENABLED	(1L << 6)
179dc766f2bSmlelstv #define IPMI_SENSOR_UNAVAILABLE		(1L << 5)
180dc766f2bSmlelstv #define IPMI_INVALID_SENSOR_P(x) \
181dc766f2bSmlelstv 	(((x) & (IPMI_SENSOR_SCANNING_ENABLED|IPMI_SENSOR_UNAVAILABLE)) \
182dc766f2bSmlelstv 	!= IPMI_SENSOR_SCANNING_ENABLED)
183dc766f2bSmlelstv 
184dc766f2bSmlelstv #define IPMI_SDR_TYPEFULL		1
185dc766f2bSmlelstv #define IPMI_SDR_TYPECOMPACT		2
186dc766f2bSmlelstv 
187dc766f2bSmlelstv #define byteof(x) ((x) >> 3)
188dc766f2bSmlelstv #define bitof(x)  (1L << ((x) & 0x7))
189dc766f2bSmlelstv #define TB(b,m)	  (data[2+byteof(b)] & bitof(b))
190dc766f2bSmlelstv 
191dc766f2bSmlelstv #define dbg_printf(lvl, fmt...) \
192dc766f2bSmlelstv 	if (ipmi_dbg >= lvl) \
193dc766f2bSmlelstv 		printf(fmt);
194dc766f2bSmlelstv #define dbg_dump(lvl, msg, len, buf) \
195dc766f2bSmlelstv 	if (len && ipmi_dbg >= lvl) \
196dc766f2bSmlelstv 		dumpb(msg, len, (const uint8_t *)(buf));
197dc766f2bSmlelstv 
198dc766f2bSmlelstv static	long signextend(unsigned long, int);
199dc766f2bSmlelstv 
200dc766f2bSmlelstv SLIST_HEAD(ipmi_sensors_head, ipmi_sensor);
201dc766f2bSmlelstv static struct ipmi_sensors_head ipmi_sensor_list =
202dc766f2bSmlelstv     SLIST_HEAD_INITIALIZER(&ipmi_sensor_list);
203dc766f2bSmlelstv 
204dc766f2bSmlelstv static	void dumpb(const char *, int, const uint8_t *);
205dc766f2bSmlelstv 
206dc766f2bSmlelstv static	int read_sensor(struct ipmi_softc *, struct ipmi_sensor *);
207dc766f2bSmlelstv static	int add_sdr_sensor(struct ipmi_softc *, uint8_t *);
208dc766f2bSmlelstv static	int get_sdr_partial(struct ipmi_softc *, uint16_t, uint16_t,
209dc766f2bSmlelstv 	    uint8_t, uint8_t, void *, uint16_t *);
210dc766f2bSmlelstv static	int get_sdr(struct ipmi_softc *, uint16_t, uint16_t *);
211dc766f2bSmlelstv 
212dc766f2bSmlelstv static	char *ipmi_buf_acquire(struct ipmi_softc *, size_t);
213dc766f2bSmlelstv static	void ipmi_buf_release(struct ipmi_softc *, char *);
214dc766f2bSmlelstv static	int ipmi_sendcmd(struct ipmi_softc *, int, int, int, int, int, const void*);
215dc766f2bSmlelstv static	int ipmi_recvcmd(struct ipmi_softc *, int, int *, void *);
216dc766f2bSmlelstv static	void ipmi_delay(struct ipmi_softc *, int);
217dc766f2bSmlelstv 
218f95aa101Smlelstv static	int ipmi_get_device_id(struct ipmi_softc *, struct ipmi_device_id *);
219dc766f2bSmlelstv static	int ipmi_watchdog_setmode(struct sysmon_wdog *);
220dc766f2bSmlelstv static	int ipmi_watchdog_tickle(struct sysmon_wdog *);
221dc766f2bSmlelstv static	void ipmi_dotickle(struct ipmi_softc *);
222dc766f2bSmlelstv 
223dc766f2bSmlelstv #if 0
224dc766f2bSmlelstv static	int ipmi_intr(void *);
225dc766f2bSmlelstv #endif
226dc766f2bSmlelstv 
227dc766f2bSmlelstv static	int ipmi_match(device_t, cfdata_t, void *);
228dc766f2bSmlelstv static	void ipmi_attach(device_t, device_t, void *);
229dc766f2bSmlelstv static	int ipmi_detach(device_t, int);
230dc766f2bSmlelstv 
231dc766f2bSmlelstv static	long	ipmi_convert(uint8_t, struct sdrtype1 *, long);
232dc766f2bSmlelstv static	void	ipmi_sensor_name(char *, int, uint8_t, uint8_t *);
233dc766f2bSmlelstv 
234dc766f2bSmlelstv /* BMC Helper Functions */
235dc766f2bSmlelstv static	uint8_t bmc_read(struct ipmi_softc *, int);
236dc766f2bSmlelstv static	void bmc_write(struct ipmi_softc *, int, uint8_t);
237dc766f2bSmlelstv static	int bmc_io_wait(struct ipmi_softc *, int, uint8_t, uint8_t, const char *);
238dc766f2bSmlelstv static	int bmc_io_wait_spin(struct ipmi_softc *, int, uint8_t, uint8_t);
239dc766f2bSmlelstv static	int bmc_io_wait_sleep(struct ipmi_softc *, int, uint8_t, uint8_t);
240dc766f2bSmlelstv 
241dc766f2bSmlelstv static	void *cmn_buildmsg(struct ipmi_softc *, int, int, int, const void *, int *);
242dc766f2bSmlelstv 
243dc766f2bSmlelstv static	int getbits(uint8_t *, int, int);
244dc766f2bSmlelstv static	int ipmi_sensor_type(int, int, int);
245dc766f2bSmlelstv 
246dc766f2bSmlelstv static	void ipmi_refresh_sensors(struct ipmi_softc *);
247dc766f2bSmlelstv static	int ipmi_map_regs(struct ipmi_softc *, struct ipmi_attach_args *);
248dc766f2bSmlelstv static	void ipmi_unmap_regs(struct ipmi_softc *);
249dc766f2bSmlelstv 
250dc766f2bSmlelstv static	int32_t ipmi_convert_sensor(uint8_t *, struct ipmi_sensor *);
251dc766f2bSmlelstv static	void ipmi_set_limits(struct sysmon_envsys *, envsys_data_t *,
252dc766f2bSmlelstv 		sysmon_envsys_lim_t *, uint32_t *);
253dc766f2bSmlelstv static	void ipmi_get_limits(struct sysmon_envsys *, envsys_data_t *,
254dc766f2bSmlelstv 		sysmon_envsys_lim_t *, uint32_t *);
255dc766f2bSmlelstv static	void ipmi_get_sensor_limits(struct ipmi_softc *, struct ipmi_sensor *,
256dc766f2bSmlelstv 		sysmon_envsys_lim_t *, uint32_t *);
257dc766f2bSmlelstv static	int ipmi_sensor_status(struct ipmi_softc *, struct ipmi_sensor *,
258dc766f2bSmlelstv 		envsys_data_t *, uint8_t *);
259dc766f2bSmlelstv 
260dc766f2bSmlelstv static	int add_child_sensors(struct ipmi_softc *, uint8_t *, int, int, int,
261dc766f2bSmlelstv 		int, int, int, const char *);
262dc766f2bSmlelstv 
263dc766f2bSmlelstv static	bool ipmi_suspend(device_t, const pmf_qual_t *);
264dc766f2bSmlelstv 
265dc766f2bSmlelstv static	int kcs_probe(struct ipmi_softc *);
266dc766f2bSmlelstv static	int kcs_reset(struct ipmi_softc *);
267dc766f2bSmlelstv static	int kcs_sendmsg(struct ipmi_softc *, int, const uint8_t *);
268dc766f2bSmlelstv static	int kcs_recvmsg(struct ipmi_softc *, int, int *len, uint8_t *);
269dc766f2bSmlelstv 
270f219451cSmlelstv static	void *bt_buildmsg(struct ipmi_softc *, int, int, int, const void *, int *);
271dc766f2bSmlelstv static	int bt_probe(struct ipmi_softc *);
272dc766f2bSmlelstv static	int bt_reset(struct ipmi_softc *);
273dc766f2bSmlelstv static	int bt_sendmsg(struct ipmi_softc *, int, const uint8_t *);
274dc766f2bSmlelstv static	int bt_recvmsg(struct ipmi_softc *, int, int *, uint8_t *);
275dc766f2bSmlelstv 
276dc766f2bSmlelstv static	int smic_probe(struct ipmi_softc *);
277dc766f2bSmlelstv static	int smic_reset(struct ipmi_softc *);
278dc766f2bSmlelstv static	int smic_sendmsg(struct ipmi_softc *, int, const uint8_t *);
279dc766f2bSmlelstv static	int smic_recvmsg(struct ipmi_softc *, int, int *, uint8_t *);
280dc766f2bSmlelstv 
281dc766f2bSmlelstv static struct ipmi_if kcs_if = {
282dc766f2bSmlelstv 	"KCS",
283dc766f2bSmlelstv 	IPMI_IF_KCS_NREGS,
284dc766f2bSmlelstv 	cmn_buildmsg,
285dc766f2bSmlelstv 	kcs_sendmsg,
286dc766f2bSmlelstv 	kcs_recvmsg,
287dc766f2bSmlelstv 	kcs_reset,
288dc766f2bSmlelstv 	kcs_probe,
289dc766f2bSmlelstv };
290dc766f2bSmlelstv 
291dc766f2bSmlelstv static struct ipmi_if smic_if = {
292dc766f2bSmlelstv 	"SMIC",
293dc766f2bSmlelstv 	IPMI_IF_SMIC_NREGS,
294dc766f2bSmlelstv 	cmn_buildmsg,
295dc766f2bSmlelstv 	smic_sendmsg,
296dc766f2bSmlelstv 	smic_recvmsg,
297dc766f2bSmlelstv 	smic_reset,
298dc766f2bSmlelstv 	smic_probe,
299dc766f2bSmlelstv };
300dc766f2bSmlelstv 
301dc766f2bSmlelstv static struct ipmi_if bt_if = {
302dc766f2bSmlelstv 	"BT",
303dc766f2bSmlelstv 	IPMI_IF_BT_NREGS,
304dc766f2bSmlelstv 	bt_buildmsg,
305dc766f2bSmlelstv 	bt_sendmsg,
306dc766f2bSmlelstv 	bt_recvmsg,
307dc766f2bSmlelstv 	bt_reset,
308dc766f2bSmlelstv 	bt_probe,
309dc766f2bSmlelstv };
310dc766f2bSmlelstv 
311dc766f2bSmlelstv static	struct ipmi_if *ipmi_get_if(int);
312dc766f2bSmlelstv 
313dc766f2bSmlelstv static struct ipmi_if *
314dc766f2bSmlelstv ipmi_get_if(int iftype)
315dc766f2bSmlelstv {
316dc766f2bSmlelstv 	switch (iftype) {
317dc766f2bSmlelstv 	case IPMI_IF_KCS:
318dc766f2bSmlelstv 		return &kcs_if;
319dc766f2bSmlelstv 	case IPMI_IF_SMIC:
320dc766f2bSmlelstv 		return &smic_if;
321dc766f2bSmlelstv 	case IPMI_IF_BT:
322dc766f2bSmlelstv 		return &bt_if;
323dc766f2bSmlelstv 	default:
324dc766f2bSmlelstv 		return NULL;
325dc766f2bSmlelstv 	}
326dc766f2bSmlelstv }
327dc766f2bSmlelstv 
328dc766f2bSmlelstv /*
329dc766f2bSmlelstv  * BMC Helper Functions
330dc766f2bSmlelstv  */
331dc766f2bSmlelstv static uint8_t
332dc766f2bSmlelstv bmc_read(struct ipmi_softc *sc, int offset)
333dc766f2bSmlelstv {
334dc766f2bSmlelstv 	return bus_space_read_1(sc->sc_iot, sc->sc_ioh,
335dc766f2bSmlelstv 	    offset * sc->sc_if_iospacing);
336dc766f2bSmlelstv }
337dc766f2bSmlelstv 
338dc766f2bSmlelstv static void
339dc766f2bSmlelstv bmc_write(struct ipmi_softc *sc, int offset, uint8_t val)
340dc766f2bSmlelstv {
341dc766f2bSmlelstv 	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
342dc766f2bSmlelstv 	    offset * sc->sc_if_iospacing, val);
343dc766f2bSmlelstv }
344dc766f2bSmlelstv 
345dc766f2bSmlelstv static int
346dc766f2bSmlelstv bmc_io_wait_sleep(struct ipmi_softc *sc, int offset, uint8_t mask,
347dc766f2bSmlelstv     uint8_t value)
348dc766f2bSmlelstv {
349dc766f2bSmlelstv 	int retries;
350dc766f2bSmlelstv 	uint8_t v;
351dc766f2bSmlelstv 
352dc766f2bSmlelstv 	KASSERT(mutex_owned(&sc->sc_cmd_mtx));
353dc766f2bSmlelstv 
354dc766f2bSmlelstv 	for (retries = 0; retries < sc->sc_max_retries; retries++) {
355dc766f2bSmlelstv 		v = bmc_read(sc, offset);
356dc766f2bSmlelstv 		if ((v & mask) == value)
357dc766f2bSmlelstv 			return v;
3582ec61c4fSriastradh 		kpause("ipmicmd", /*intr*/false, /*timo*/1, /*mtx*/NULL);
359dc766f2bSmlelstv 	}
360dc766f2bSmlelstv 	return -1;
361dc766f2bSmlelstv }
362dc766f2bSmlelstv 
363dc766f2bSmlelstv static int
364dc766f2bSmlelstv bmc_io_wait(struct ipmi_softc *sc, int offset, uint8_t mask, uint8_t value,
365dc766f2bSmlelstv     const char *lbl)
366dc766f2bSmlelstv {
367dc766f2bSmlelstv 	int v;
368dc766f2bSmlelstv 
369dc766f2bSmlelstv 	v = bmc_io_wait_spin(sc, offset, mask, value);
370dc766f2bSmlelstv 	if (cold || v != -1)
371dc766f2bSmlelstv 		return v;
372dc766f2bSmlelstv 
373dc766f2bSmlelstv 	return bmc_io_wait_sleep(sc, offset, mask, value);
374dc766f2bSmlelstv }
375dc766f2bSmlelstv 
376dc766f2bSmlelstv static int
377dc766f2bSmlelstv bmc_io_wait_spin(struct ipmi_softc *sc, int offset, uint8_t mask,
378dc766f2bSmlelstv     uint8_t value)
379dc766f2bSmlelstv {
380dc766f2bSmlelstv 	uint8_t	v;
381dc766f2bSmlelstv 	int			count = cold ? 15000 : 500;
382dc766f2bSmlelstv 	/* ~us */
383dc766f2bSmlelstv 
384dc766f2bSmlelstv 	while (count--) {
385dc766f2bSmlelstv 		v = bmc_read(sc, offset);
386dc766f2bSmlelstv 		if ((v & mask) == value)
387dc766f2bSmlelstv 			return v;
388dc766f2bSmlelstv 
389dc766f2bSmlelstv 		delay(1);
390dc766f2bSmlelstv 	}
391dc766f2bSmlelstv 
392dc766f2bSmlelstv 	return -1;
393dc766f2bSmlelstv 
394dc766f2bSmlelstv }
395dc766f2bSmlelstv 
396dc766f2bSmlelstv #define NETFN_LUN(nf,ln) (((nf) << 2) | ((ln) & 0x3))
397b1f23fc9Smlelstv #define GET_NETFN(m) (((m) >> 2)
398b1f23fc9Smlelstv #define GET_LUN(m) ((m) & 0x03)
399dc766f2bSmlelstv 
400dc766f2bSmlelstv /*
401dc766f2bSmlelstv  * BT interface
402dc766f2bSmlelstv  */
403dc766f2bSmlelstv #define _BT_CTRL_REG			0
404dc766f2bSmlelstv #define	  BT_CLR_WR_PTR			(1L << 0)
405dc766f2bSmlelstv #define	  BT_CLR_RD_PTR			(1L << 1)
406dc766f2bSmlelstv #define	  BT_HOST2BMC_ATN		(1L << 2)
407dc766f2bSmlelstv #define	  BT_BMC2HOST_ATN		(1L << 3)
408dc766f2bSmlelstv #define	  BT_EVT_ATN			(1L << 4)
409dc766f2bSmlelstv #define	  BT_HOST_BUSY			(1L << 6)
410dc766f2bSmlelstv #define	  BT_BMC_BUSY			(1L << 7)
411dc766f2bSmlelstv 
412dc766f2bSmlelstv #define	  BT_READY	(BT_HOST_BUSY|BT_HOST2BMC_ATN|BT_BMC2HOST_ATN)
413dc766f2bSmlelstv 
414dc766f2bSmlelstv #define _BT_DATAIN_REG			1
415dc766f2bSmlelstv #define _BT_DATAOUT_REG			1
416dc766f2bSmlelstv 
417dc766f2bSmlelstv #define _BT_INTMASK_REG			2
418dc766f2bSmlelstv #define	 BT_IM_HIRQ_PEND		(1L << 1)
419dc766f2bSmlelstv #define	 BT_IM_SCI_EN			(1L << 2)
420dc766f2bSmlelstv #define	 BT_IM_SMI_EN			(1L << 3)
421dc766f2bSmlelstv #define	 BT_IM_NMI2SMI			(1L << 4)
422dc766f2bSmlelstv 
423dc766f2bSmlelstv static int bt_read(struct ipmi_softc *, int);
424dc766f2bSmlelstv static int bt_write(struct ipmi_softc *, int, uint8_t);
425dc766f2bSmlelstv 
426dc766f2bSmlelstv static int
427dc766f2bSmlelstv bt_read(struct ipmi_softc *sc, int reg)
428dc766f2bSmlelstv {
429dc766f2bSmlelstv 	return bmc_read(sc, reg);
430dc766f2bSmlelstv }
431dc766f2bSmlelstv 
432dc766f2bSmlelstv static int
433dc766f2bSmlelstv bt_write(struct ipmi_softc *sc, int reg, uint8_t data)
434dc766f2bSmlelstv {
435dc766f2bSmlelstv 	if (bmc_io_wait(sc, _BT_CTRL_REG, BT_BMC_BUSY, 0, __func__) < 0)
436dc766f2bSmlelstv 		return -1;
437dc766f2bSmlelstv 
438dc766f2bSmlelstv 	bmc_write(sc, reg, data);
439dc766f2bSmlelstv 	return 0;
440dc766f2bSmlelstv }
441dc766f2bSmlelstv 
442dc766f2bSmlelstv static int
443dc766f2bSmlelstv bt_sendmsg(struct ipmi_softc *sc, int len, const uint8_t *data)
444dc766f2bSmlelstv {
445dc766f2bSmlelstv 	int i;
446dc766f2bSmlelstv 
447dc766f2bSmlelstv 	bt_write(sc, _BT_CTRL_REG, BT_CLR_WR_PTR);
448dc766f2bSmlelstv 	for (i = 0; i < len; i++)
449dc766f2bSmlelstv 		bt_write(sc, _BT_DATAOUT_REG, data[i]);
450dc766f2bSmlelstv 
451dc766f2bSmlelstv 	bt_write(sc, _BT_CTRL_REG, BT_HOST2BMC_ATN);
452dc766f2bSmlelstv 	if (bmc_io_wait(sc, _BT_CTRL_REG, BT_HOST2BMC_ATN | BT_BMC_BUSY, 0,
453dc766f2bSmlelstv 	    __func__) < 0)
454dc766f2bSmlelstv 		return -1;
455dc766f2bSmlelstv 
456dc766f2bSmlelstv 	return 0;
457dc766f2bSmlelstv }
458dc766f2bSmlelstv 
459dc766f2bSmlelstv static int
460dc766f2bSmlelstv bt_recvmsg(struct ipmi_softc *sc, int maxlen, int *rxlen, uint8_t *data)
461dc766f2bSmlelstv {
462dc766f2bSmlelstv 	uint8_t len, v, i;
463dc766f2bSmlelstv 
464dc766f2bSmlelstv 	if (bmc_io_wait(sc, _BT_CTRL_REG, BT_BMC2HOST_ATN, BT_BMC2HOST_ATN,
465dc766f2bSmlelstv 	    __func__) < 0)
466dc766f2bSmlelstv 		return -1;
467dc766f2bSmlelstv 
468dc766f2bSmlelstv 	bt_write(sc, _BT_CTRL_REG, BT_HOST_BUSY);
469dc766f2bSmlelstv 	bt_write(sc, _BT_CTRL_REG, BT_BMC2HOST_ATN);
470dc766f2bSmlelstv 	bt_write(sc, _BT_CTRL_REG, BT_CLR_RD_PTR);
471dc766f2bSmlelstv 	len = bt_read(sc, _BT_DATAIN_REG);
472dc766f2bSmlelstv 	for (i = IPMI_BTMSG_NFLN; i <= len; i++) {
473dc766f2bSmlelstv 		v = bt_read(sc, _BT_DATAIN_REG);
474dc766f2bSmlelstv 		if (i != IPMI_BTMSG_SEQ)
475dc766f2bSmlelstv 			*(data++) = v;
476dc766f2bSmlelstv 	}
477dc766f2bSmlelstv 	bt_write(sc, _BT_CTRL_REG, BT_HOST_BUSY);
478dc766f2bSmlelstv 	*rxlen = len - 1;
479dc766f2bSmlelstv 
480dc766f2bSmlelstv 	return 0;
481dc766f2bSmlelstv }
482dc766f2bSmlelstv 
483dc766f2bSmlelstv static int
484dc766f2bSmlelstv bt_reset(struct ipmi_softc *sc)
485dc766f2bSmlelstv {
486dc766f2bSmlelstv 	return -1;
487dc766f2bSmlelstv }
488dc766f2bSmlelstv 
489dc766f2bSmlelstv static int
490dc766f2bSmlelstv bt_probe(struct ipmi_softc *sc)
491dc766f2bSmlelstv {
492dc766f2bSmlelstv 	uint8_t rv;
493dc766f2bSmlelstv 
494dc766f2bSmlelstv 	rv = bmc_read(sc, _BT_CTRL_REG);
495dc766f2bSmlelstv 	rv &= BT_HOST_BUSY;
496dc766f2bSmlelstv 	rv |= BT_CLR_WR_PTR|BT_CLR_RD_PTR|BT_BMC2HOST_ATN|BT_HOST2BMC_ATN;
497dc766f2bSmlelstv 	bmc_write(sc, _BT_CTRL_REG, rv);
498dc766f2bSmlelstv 
499dc766f2bSmlelstv 	rv = bmc_read(sc, _BT_INTMASK_REG);
500dc766f2bSmlelstv 	rv &= BT_IM_SCI_EN|BT_IM_SMI_EN|BT_IM_NMI2SMI;
501dc766f2bSmlelstv 	rv |= BT_IM_HIRQ_PEND;
502dc766f2bSmlelstv 	bmc_write(sc, _BT_INTMASK_REG, rv);
503dc766f2bSmlelstv 
504dc766f2bSmlelstv #if 0
505dc766f2bSmlelstv 	printf("%s: %2x\n", __func__, v);
506dc766f2bSmlelstv 	printf(" WR    : %2x\n", v & BT_CLR_WR_PTR);
507dc766f2bSmlelstv 	printf(" RD    : %2x\n", v & BT_CLR_RD_PTR);
508dc766f2bSmlelstv 	printf(" H2B   : %2x\n", v & BT_HOST2BMC_ATN);
509dc766f2bSmlelstv 	printf(" B2H   : %2x\n", v & BT_BMC2HOST_ATN);
510dc766f2bSmlelstv 	printf(" EVT   : %2x\n", v & BT_EVT_ATN);
511dc766f2bSmlelstv 	printf(" HBSY  : %2x\n", v & BT_HOST_BUSY);
512dc766f2bSmlelstv 	printf(" BBSY  : %2x\n", v & BT_BMC_BUSY);
513dc766f2bSmlelstv #endif
514dc766f2bSmlelstv 	return 0;
515dc766f2bSmlelstv }
516dc766f2bSmlelstv 
517dc766f2bSmlelstv /*
518dc766f2bSmlelstv  * SMIC interface
519dc766f2bSmlelstv  */
520dc766f2bSmlelstv #define _SMIC_DATAIN_REG		0
521dc766f2bSmlelstv #define _SMIC_DATAOUT_REG		0
522dc766f2bSmlelstv 
523dc766f2bSmlelstv #define _SMIC_CTRL_REG			1
524dc766f2bSmlelstv #define	  SMS_CC_GET_STATUS		 0x40
525dc766f2bSmlelstv #define	  SMS_CC_START_TRANSFER		 0x41
526dc766f2bSmlelstv #define	  SMS_CC_NEXT_TRANSFER		 0x42
527dc766f2bSmlelstv #define	  SMS_CC_END_TRANSFER		 0x43
528dc766f2bSmlelstv #define	  SMS_CC_START_RECEIVE		 0x44
529dc766f2bSmlelstv #define	  SMS_CC_NEXT_RECEIVE		 0x45
530dc766f2bSmlelstv #define	  SMS_CC_END_RECEIVE		 0x46
531dc766f2bSmlelstv #define	  SMS_CC_TRANSFER_ABORT		 0x47
532dc766f2bSmlelstv 
533dc766f2bSmlelstv #define	  SMS_SC_READY			 0xc0
534dc766f2bSmlelstv #define	  SMS_SC_WRITE_START		 0xc1
535dc766f2bSmlelstv #define	  SMS_SC_WRITE_NEXT		 0xc2
536dc766f2bSmlelstv #define	  SMS_SC_WRITE_END		 0xc3
537dc766f2bSmlelstv #define	  SMS_SC_READ_START		 0xc4
538dc766f2bSmlelstv #define	  SMS_SC_READ_NEXT		 0xc5
539dc766f2bSmlelstv #define	  SMS_SC_READ_END		 0xc6
540dc766f2bSmlelstv 
541dc766f2bSmlelstv #define _SMIC_FLAG_REG			2
542dc766f2bSmlelstv #define	  SMIC_BUSY			(1L << 0)
543dc766f2bSmlelstv #define	  SMIC_SMS_ATN			(1L << 2)
544dc766f2bSmlelstv #define	  SMIC_EVT_ATN			(1L << 3)
545dc766f2bSmlelstv #define	  SMIC_SMI			(1L << 4)
546dc766f2bSmlelstv #define	  SMIC_TX_DATA_RDY		(1L << 6)
547dc766f2bSmlelstv #define	  SMIC_RX_DATA_RDY		(1L << 7)
548dc766f2bSmlelstv 
549dc766f2bSmlelstv static int smic_wait(struct ipmi_softc *, uint8_t, uint8_t, const char *);
550dc766f2bSmlelstv static int smic_write_cmd_data(struct ipmi_softc *, uint8_t, const uint8_t *);
551dc766f2bSmlelstv static int smic_read_data(struct ipmi_softc *, uint8_t *);
552dc766f2bSmlelstv 
553dc766f2bSmlelstv static int
554dc766f2bSmlelstv smic_wait(struct ipmi_softc *sc, uint8_t mask, uint8_t val, const char *lbl)
555dc766f2bSmlelstv {
556dc766f2bSmlelstv 	int v;
557dc766f2bSmlelstv 
558dc766f2bSmlelstv 	/* Wait for expected flag bits */
559dc766f2bSmlelstv 	v = bmc_io_wait(sc, _SMIC_FLAG_REG, mask, val, __func__);
560dc766f2bSmlelstv 	if (v < 0)
561dc766f2bSmlelstv 		return -1;
562dc766f2bSmlelstv 
563dc766f2bSmlelstv 	/* Return current status */
564dc766f2bSmlelstv 	v = bmc_read(sc, _SMIC_CTRL_REG);
565dc766f2bSmlelstv 	dbg_printf(99, "%s(%s) = %#.2x\n", __func__, lbl, v);
566dc766f2bSmlelstv 	return v;
567dc766f2bSmlelstv }
568dc766f2bSmlelstv 
569dc766f2bSmlelstv static int
570dc766f2bSmlelstv smic_write_cmd_data(struct ipmi_softc *sc, uint8_t cmd, const uint8_t *data)
571dc766f2bSmlelstv {
572dc766f2bSmlelstv 	int	sts, v;
573dc766f2bSmlelstv 
574dc766f2bSmlelstv 	dbg_printf(50, "%s: %#.2x %#.2x\n", __func__, cmd, data ? *data : -1);
575dc766f2bSmlelstv 	sts = smic_wait(sc, SMIC_TX_DATA_RDY | SMIC_BUSY, SMIC_TX_DATA_RDY,
576dc766f2bSmlelstv 	    "smic_write_cmd_data ready");
577dc766f2bSmlelstv 	if (sts < 0)
578dc766f2bSmlelstv 		return sts;
579dc766f2bSmlelstv 
580dc766f2bSmlelstv 	bmc_write(sc, _SMIC_CTRL_REG, cmd);
581dc766f2bSmlelstv 	if (data)
582dc766f2bSmlelstv 		bmc_write(sc, _SMIC_DATAOUT_REG, *data);
583dc766f2bSmlelstv 
584dc766f2bSmlelstv 	/* Toggle BUSY bit, then wait for busy bit to clear */
585dc766f2bSmlelstv 	v = bmc_read(sc, _SMIC_FLAG_REG);
586dc766f2bSmlelstv 	bmc_write(sc, _SMIC_FLAG_REG, v | SMIC_BUSY);
587dc766f2bSmlelstv 
588dc766f2bSmlelstv 	return smic_wait(sc, SMIC_BUSY, 0, __func__);
589dc766f2bSmlelstv }
590dc766f2bSmlelstv 
591dc766f2bSmlelstv static int
592dc766f2bSmlelstv smic_read_data(struct ipmi_softc *sc, uint8_t *data)
593dc766f2bSmlelstv {
594dc766f2bSmlelstv 	int sts;
595dc766f2bSmlelstv 
596dc766f2bSmlelstv 	sts = smic_wait(sc, SMIC_RX_DATA_RDY | SMIC_BUSY, SMIC_RX_DATA_RDY,
597dc766f2bSmlelstv 	    __func__);
598dc766f2bSmlelstv 	if (sts >= 0) {
599dc766f2bSmlelstv 		*data = bmc_read(sc, _SMIC_DATAIN_REG);
600dc766f2bSmlelstv 		dbg_printf(50, "%s: %#.2x\n", __func__, *data);
601dc766f2bSmlelstv 	}
602dc766f2bSmlelstv 	return sts;
603dc766f2bSmlelstv }
604dc766f2bSmlelstv 
605dc766f2bSmlelstv #define ErrStat(a, ...) if (a) printf(__VA_ARGS__);
606dc766f2bSmlelstv 
607dc766f2bSmlelstv static int
608dc766f2bSmlelstv smic_sendmsg(struct ipmi_softc *sc, int len, const uint8_t *data)
609dc766f2bSmlelstv {
610dc766f2bSmlelstv 	int sts, idx;
611dc766f2bSmlelstv 
612dc766f2bSmlelstv 	sts = smic_write_cmd_data(sc, SMS_CC_START_TRANSFER, &data[0]);
613dc766f2bSmlelstv 	ErrStat(sts != SMS_SC_WRITE_START, "%s: wstart", __func__);
614dc766f2bSmlelstv 	for (idx = 1; idx < len - 1; idx++) {
615dc766f2bSmlelstv 		sts = smic_write_cmd_data(sc, SMS_CC_NEXT_TRANSFER,
616dc766f2bSmlelstv 		    &data[idx]);
617dc766f2bSmlelstv 		ErrStat(sts != SMS_SC_WRITE_NEXT, "%s: write", __func__);
618dc766f2bSmlelstv 	}
619dc766f2bSmlelstv 	sts = smic_write_cmd_data(sc, SMS_CC_END_TRANSFER, &data[idx]);
620dc766f2bSmlelstv 	if (sts != SMS_SC_WRITE_END) {
621dc766f2bSmlelstv 		dbg_printf(50, "%s: %d/%d = %#.2x\n", __func__, idx, len, sts);
622dc766f2bSmlelstv 		return -1;
623dc766f2bSmlelstv 	}
624dc766f2bSmlelstv 
625dc766f2bSmlelstv 	return 0;
626dc766f2bSmlelstv }
627dc766f2bSmlelstv 
628dc766f2bSmlelstv static int
629dc766f2bSmlelstv smic_recvmsg(struct ipmi_softc *sc, int maxlen, int *len, uint8_t *data)
630dc766f2bSmlelstv {
631dc766f2bSmlelstv 	int sts, idx;
632dc766f2bSmlelstv 
633dc766f2bSmlelstv 	*len = 0;
634dc766f2bSmlelstv 	sts = smic_wait(sc, SMIC_RX_DATA_RDY, SMIC_RX_DATA_RDY, __func__);
635dc766f2bSmlelstv 	if (sts < 0)
636dc766f2bSmlelstv 		return -1;
637dc766f2bSmlelstv 
638dc766f2bSmlelstv 	sts = smic_write_cmd_data(sc, SMS_CC_START_RECEIVE, NULL);
639dc766f2bSmlelstv 	ErrStat(sts != SMS_SC_READ_START, "%s: rstart", __func__);
640dc766f2bSmlelstv 	for (idx = 0;; ) {
641dc766f2bSmlelstv 		sts = smic_read_data(sc, &data[idx++]);
642dc766f2bSmlelstv 		if (sts != SMS_SC_READ_START && sts != SMS_SC_READ_NEXT)
643dc766f2bSmlelstv 			break;
644dc766f2bSmlelstv 		smic_write_cmd_data(sc, SMS_CC_NEXT_RECEIVE, NULL);
645dc766f2bSmlelstv 	}
646dc766f2bSmlelstv 	ErrStat(sts != SMS_SC_READ_END, "%s: rend", __func__);
647dc766f2bSmlelstv 
648dc766f2bSmlelstv 	*len = idx;
649dc766f2bSmlelstv 
650dc766f2bSmlelstv 	sts = smic_write_cmd_data(sc, SMS_CC_END_RECEIVE, NULL);
651dc766f2bSmlelstv 	if (sts != SMS_SC_READY) {
652dc766f2bSmlelstv 		dbg_printf(50, "%s: %d/%d = %#.2x\n",
653dc766f2bSmlelstv 		    __func__, idx, maxlen, sts);
654dc766f2bSmlelstv 		return -1;
655dc766f2bSmlelstv 	}
656dc766f2bSmlelstv 
657dc766f2bSmlelstv 	return 0;
658dc766f2bSmlelstv }
659dc766f2bSmlelstv 
660dc766f2bSmlelstv static int
661dc766f2bSmlelstv smic_reset(struct ipmi_softc *sc)
662dc766f2bSmlelstv {
663dc766f2bSmlelstv 	return -1;
664dc766f2bSmlelstv }
665dc766f2bSmlelstv 
666dc766f2bSmlelstv static int
667dc766f2bSmlelstv smic_probe(struct ipmi_softc *sc)
668dc766f2bSmlelstv {
669dc766f2bSmlelstv 	/* Flag register should not be 0xFF on a good system */
670dc766f2bSmlelstv 	if (bmc_read(sc, _SMIC_FLAG_REG) == 0xFF)
671dc766f2bSmlelstv 		return -1;
672dc766f2bSmlelstv 
673dc766f2bSmlelstv 	return 0;
674dc766f2bSmlelstv }
675dc766f2bSmlelstv 
676dc766f2bSmlelstv /*
677dc766f2bSmlelstv  * KCS interface
678dc766f2bSmlelstv  */
679dc766f2bSmlelstv #define _KCS_DATAIN_REGISTER		0
680dc766f2bSmlelstv #define _KCS_DATAOUT_REGISTER		0
681dc766f2bSmlelstv #define	  KCS_READ_NEXT			0x68
682dc766f2bSmlelstv 
683dc766f2bSmlelstv #define _KCS_COMMAND_REGISTER		1
684dc766f2bSmlelstv #define	  KCS_GET_STATUS		0x60
685dc766f2bSmlelstv #define	  KCS_WRITE_START		0x61
686dc766f2bSmlelstv #define	  KCS_WRITE_END			0x62
687dc766f2bSmlelstv 
688dc766f2bSmlelstv #define _KCS_STATUS_REGISTER		1
689dc766f2bSmlelstv #define	  KCS_OBF			(1L << 0)
690dc766f2bSmlelstv #define	  KCS_IBF			(1L << 1)
691dc766f2bSmlelstv #define	  KCS_SMS_ATN			(1L << 2)
692dc766f2bSmlelstv #define	  KCS_CD			(1L << 3)
693dc766f2bSmlelstv #define	  KCS_OEM1			(1L << 4)
694dc766f2bSmlelstv #define	  KCS_OEM2			(1L << 5)
695dc766f2bSmlelstv #define	  KCS_STATE_MASK		0xc0
696dc766f2bSmlelstv #define	    KCS_IDLE_STATE		0x00
697dc766f2bSmlelstv #define	    KCS_READ_STATE		0x40
698dc766f2bSmlelstv #define	    KCS_WRITE_STATE		0x80
699dc766f2bSmlelstv #define	    KCS_ERROR_STATE		0xC0
700dc766f2bSmlelstv 
701dc766f2bSmlelstv static int kcs_wait(struct ipmi_softc *, uint8_t, uint8_t, const char *);
702dc766f2bSmlelstv static int kcs_write_cmd(struct ipmi_softc *, uint8_t);
703dc766f2bSmlelstv static int kcs_write_data(struct ipmi_softc *, uint8_t);
704dc766f2bSmlelstv static int kcs_read_data(struct ipmi_softc *, uint8_t *);
705dc766f2bSmlelstv 
706dc766f2bSmlelstv static int
707dc766f2bSmlelstv kcs_wait(struct ipmi_softc *sc, uint8_t mask, uint8_t value, const char *lbl)
708dc766f2bSmlelstv {
709dc766f2bSmlelstv 	int v;
710dc766f2bSmlelstv 
711dc766f2bSmlelstv 	v = bmc_io_wait(sc, _KCS_STATUS_REGISTER, mask, value, lbl);
712dc766f2bSmlelstv 	if (v < 0)
713dc766f2bSmlelstv 		return v;
714dc766f2bSmlelstv 
715dc766f2bSmlelstv 	/* Check if output buffer full, read dummy byte	 */
716dc766f2bSmlelstv 	if ((v & (KCS_OBF | KCS_STATE_MASK)) == (KCS_OBF | KCS_WRITE_STATE))
717dc766f2bSmlelstv 		bmc_read(sc, _KCS_DATAIN_REGISTER);
718dc766f2bSmlelstv 
719dc766f2bSmlelstv 	/* Check for error state */
720dc766f2bSmlelstv 	if ((v & KCS_STATE_MASK) == KCS_ERROR_STATE) {
721dc766f2bSmlelstv 		bmc_write(sc, _KCS_COMMAND_REGISTER, KCS_GET_STATUS);
722dc766f2bSmlelstv 		while (bmc_read(sc, _KCS_STATUS_REGISTER) & KCS_IBF)
723dc766f2bSmlelstv 			;
724dc766f2bSmlelstv 		aprint_error_dev(sc->sc_dev, "error code: %#x\n",
725dc766f2bSmlelstv 		    bmc_read(sc, _KCS_DATAIN_REGISTER));
726dc766f2bSmlelstv 	}
727dc766f2bSmlelstv 
728dc766f2bSmlelstv 	return v & KCS_STATE_MASK;
729dc766f2bSmlelstv }
730dc766f2bSmlelstv 
731dc766f2bSmlelstv static int
732dc766f2bSmlelstv kcs_write_cmd(struct ipmi_softc *sc, uint8_t cmd)
733dc766f2bSmlelstv {
734dc766f2bSmlelstv 	/* ASSERT: IBF and OBF are clear */
735dc766f2bSmlelstv 	dbg_printf(50, "%s: %#.2x\n", __func__, cmd);
736dc766f2bSmlelstv 	bmc_write(sc, _KCS_COMMAND_REGISTER, cmd);
737dc766f2bSmlelstv 
738dc766f2bSmlelstv 	return kcs_wait(sc, KCS_IBF, 0, "write_cmd");
739dc766f2bSmlelstv }
740dc766f2bSmlelstv 
741dc766f2bSmlelstv static int
742dc766f2bSmlelstv kcs_write_data(struct ipmi_softc *sc, uint8_t data)
743dc766f2bSmlelstv {
744dc766f2bSmlelstv 	/* ASSERT: IBF and OBF are clear */
745dc766f2bSmlelstv 	dbg_printf(50, "%s: %#.2x\n", __func__, data);
746dc766f2bSmlelstv 	bmc_write(sc, _KCS_DATAOUT_REGISTER, data);
747dc766f2bSmlelstv 
748dc766f2bSmlelstv 	return kcs_wait(sc, KCS_IBF, 0, "write_data");
749dc766f2bSmlelstv }
750dc766f2bSmlelstv 
751dc766f2bSmlelstv static int
752dc766f2bSmlelstv kcs_read_data(struct ipmi_softc *sc, uint8_t * data)
753dc766f2bSmlelstv {
754dc766f2bSmlelstv 	int sts;
755dc766f2bSmlelstv 
756dc766f2bSmlelstv 	sts = kcs_wait(sc, KCS_IBF | KCS_OBF, KCS_OBF, __func__);
757dc766f2bSmlelstv 	if (sts != KCS_READ_STATE)
758dc766f2bSmlelstv 		return sts;
759dc766f2bSmlelstv 
760dc766f2bSmlelstv 	/* ASSERT: OBF is set read data, request next byte */
761dc766f2bSmlelstv 	*data = bmc_read(sc, _KCS_DATAIN_REGISTER);
762dc766f2bSmlelstv 	bmc_write(sc, _KCS_DATAOUT_REGISTER, KCS_READ_NEXT);
763dc766f2bSmlelstv 
764dc766f2bSmlelstv 	dbg_printf(50, "%s: %#.2x\n", __func__, *data);
765dc766f2bSmlelstv 
766dc766f2bSmlelstv 	return sts;
767dc766f2bSmlelstv }
768dc766f2bSmlelstv 
769dc766f2bSmlelstv /* Exported KCS functions */
770dc766f2bSmlelstv static int
771dc766f2bSmlelstv kcs_sendmsg(struct ipmi_softc *sc, int len, const uint8_t * data)
772dc766f2bSmlelstv {
773dc766f2bSmlelstv 	int idx, sts;
774dc766f2bSmlelstv 
775dc766f2bSmlelstv 	/* ASSERT: IBF is clear */
776dc766f2bSmlelstv 	dbg_dump(50, __func__, len, data);
777dc766f2bSmlelstv 	sts = kcs_write_cmd(sc, KCS_WRITE_START);
778dc766f2bSmlelstv 	for (idx = 0; idx < len; idx++) {
779dc766f2bSmlelstv 		if (idx == len - 1)
780dc766f2bSmlelstv 			sts = kcs_write_cmd(sc, KCS_WRITE_END);
781dc766f2bSmlelstv 
782dc766f2bSmlelstv 		if (sts != KCS_WRITE_STATE)
783dc766f2bSmlelstv 			break;
784dc766f2bSmlelstv 
785dc766f2bSmlelstv 		sts = kcs_write_data(sc, data[idx]);
786dc766f2bSmlelstv 	}
787dc766f2bSmlelstv 	if (sts != KCS_READ_STATE) {
788dc766f2bSmlelstv 		dbg_printf(1, "%s: %d/%d <%#.2x>\n", __func__, idx, len, sts);
789dc766f2bSmlelstv 		dbg_dump(1, __func__, len, data);
790dc766f2bSmlelstv 		return -1;
791dc766f2bSmlelstv 	}
792dc766f2bSmlelstv 
793dc766f2bSmlelstv 	return 0;
794dc766f2bSmlelstv }
795dc766f2bSmlelstv 
796dc766f2bSmlelstv static int
797dc766f2bSmlelstv kcs_recvmsg(struct ipmi_softc *sc, int maxlen, int *rxlen, uint8_t * data)
798dc766f2bSmlelstv {
799dc766f2bSmlelstv 	int idx, sts;
800dc766f2bSmlelstv 
801dc766f2bSmlelstv 	for (idx = 0; idx < maxlen; idx++) {
802dc766f2bSmlelstv 		sts = kcs_read_data(sc, &data[idx]);
803dc766f2bSmlelstv 		if (sts != KCS_READ_STATE)
804dc766f2bSmlelstv 			break;
805dc766f2bSmlelstv 	}
806dc766f2bSmlelstv 	sts = kcs_wait(sc, KCS_IBF, 0, __func__);
807dc766f2bSmlelstv 	*rxlen = idx;
808dc766f2bSmlelstv 	if (sts != KCS_IDLE_STATE) {
809dc766f2bSmlelstv 		dbg_printf(1, "%s: %d/%d <%#.2x>\n",
810dc766f2bSmlelstv 		    __func__, idx, maxlen, sts);
811dc766f2bSmlelstv 		return -1;
812dc766f2bSmlelstv 	}
813dc766f2bSmlelstv 
814dc766f2bSmlelstv 	dbg_dump(50, __func__, idx, data);
815dc766f2bSmlelstv 
816dc766f2bSmlelstv 	return 0;
817dc766f2bSmlelstv }
818dc766f2bSmlelstv 
819dc766f2bSmlelstv static int
820dc766f2bSmlelstv kcs_reset(struct ipmi_softc *sc)
821dc766f2bSmlelstv {
822dc766f2bSmlelstv 	return -1;
823dc766f2bSmlelstv }
824dc766f2bSmlelstv 
825dc766f2bSmlelstv static int
826dc766f2bSmlelstv kcs_probe(struct ipmi_softc *sc)
827dc766f2bSmlelstv {
828dc766f2bSmlelstv 	uint8_t v;
829dc766f2bSmlelstv 
830dc766f2bSmlelstv 	v = bmc_read(sc, _KCS_STATUS_REGISTER);
831dc766f2bSmlelstv #if 0
832dc766f2bSmlelstv 	printf("%s: %2x\n", __func__, v);
833dc766f2bSmlelstv 	printf(" STS: %2x\n", v & KCS_STATE_MASK);
834dc766f2bSmlelstv 	printf(" ATN: %2x\n", v & KCS_SMS_ATN);
835dc766f2bSmlelstv 	printf(" C/D: %2x\n", v & KCS_CD);
836dc766f2bSmlelstv 	printf(" IBF: %2x\n", v & KCS_IBF);
837dc766f2bSmlelstv 	printf(" OBF: %2x\n", v & KCS_OBF);
838dc766f2bSmlelstv #else
839dc766f2bSmlelstv 	__USE(v);
840dc766f2bSmlelstv #endif
841dc766f2bSmlelstv 	return 0;
842dc766f2bSmlelstv }
843dc766f2bSmlelstv 
844dc766f2bSmlelstv /*
845dc766f2bSmlelstv  * IPMI code
846dc766f2bSmlelstv  */
847dc766f2bSmlelstv #define READ_SMS_BUFFER		0x37
848dc766f2bSmlelstv #define WRITE_I2C		0x50
849dc766f2bSmlelstv 
850dc766f2bSmlelstv #define GET_MESSAGE_CMD		0x33
851dc766f2bSmlelstv #define SEND_MESSAGE_CMD	0x34
852dc766f2bSmlelstv 
853dc766f2bSmlelstv #define IPMB_CHANNEL_NUMBER	0
854dc766f2bSmlelstv 
855dc766f2bSmlelstv #define PUBLIC_BUS		0
856dc766f2bSmlelstv 
857dc766f2bSmlelstv #define MIN_I2C_PACKET_SIZE	3
858dc766f2bSmlelstv #define MIN_IMB_PACKET_SIZE	7	/* one byte for cksum */
859dc766f2bSmlelstv 
860dc766f2bSmlelstv #define MIN_BTBMC_REQ_SIZE	4
861dc766f2bSmlelstv #define MIN_BTBMC_RSP_SIZE	5
862dc766f2bSmlelstv #define MIN_BMC_REQ_SIZE	2
863dc766f2bSmlelstv #define MIN_BMC_RSP_SIZE	3
864dc766f2bSmlelstv 
865dc766f2bSmlelstv #define BMC_SA			0x20	/* BMC/ESM3 */
866dc766f2bSmlelstv #define FPC_SA			0x22	/* front panel */
867dc766f2bSmlelstv #define BP_SA			0xC0	/* Primary Backplane */
868dc766f2bSmlelstv #define BP2_SA			0xC2	/* Secondary Backplane */
869dc766f2bSmlelstv #define PBP_SA			0xC4	/* Peripheral Backplane */
870dc766f2bSmlelstv #define DRAC_SA			0x28	/* DRAC-III */
871dc766f2bSmlelstv #define DRAC3_SA		0x30	/* DRAC-III */
872dc766f2bSmlelstv #define BMC_LUN			0
873dc766f2bSmlelstv #define SMS_LUN			2
874dc766f2bSmlelstv 
875dc766f2bSmlelstv struct ipmi_request {
876dc766f2bSmlelstv 	uint8_t	rsSa;
877dc766f2bSmlelstv 	uint8_t	rsLun;
878dc766f2bSmlelstv 	uint8_t	netFn;
879dc766f2bSmlelstv 	uint8_t	cmd;
880dc766f2bSmlelstv 	uint8_t	data_len;
881dc766f2bSmlelstv 	uint8_t	*data;
882dc766f2bSmlelstv };
883dc766f2bSmlelstv 
884dc766f2bSmlelstv struct ipmi_response {
885dc766f2bSmlelstv 	uint8_t	cCode;
886dc766f2bSmlelstv 	uint8_t	data_len;
887dc766f2bSmlelstv 	uint8_t	*data;
888dc766f2bSmlelstv };
889dc766f2bSmlelstv 
890dc766f2bSmlelstv struct ipmi_bmc_request {
891dc766f2bSmlelstv 	uint8_t	bmc_nfLn;
892dc766f2bSmlelstv 	uint8_t	bmc_cmd;
893dc766f2bSmlelstv 	uint8_t	bmc_data_len;
894dc766f2bSmlelstv 	uint8_t	bmc_data[1];
895dc766f2bSmlelstv };
896dc766f2bSmlelstv 
897dc766f2bSmlelstv struct ipmi_bmc_response {
898dc766f2bSmlelstv 	uint8_t	bmc_nfLn;
899dc766f2bSmlelstv 	uint8_t	bmc_cmd;
900dc766f2bSmlelstv 	uint8_t	bmc_cCode;
901dc766f2bSmlelstv 	uint8_t	bmc_data_len;
902dc766f2bSmlelstv 	uint8_t	bmc_data[1];
903dc766f2bSmlelstv };
904dc766f2bSmlelstv 
905dc766f2bSmlelstv 
906dc766f2bSmlelstv CFATTACH_DECL2_NEW(ipmi, sizeof(struct ipmi_softc),
907dc766f2bSmlelstv     ipmi_match, ipmi_attach, ipmi_detach, NULL, NULL, NULL);
908dc766f2bSmlelstv 
909dc766f2bSmlelstv static void
910dc766f2bSmlelstv dumpb(const char *lbl, int len, const uint8_t *data)
911dc766f2bSmlelstv {
912dc766f2bSmlelstv 	int idx;
913dc766f2bSmlelstv 
914dc766f2bSmlelstv 	printf("%s: ", lbl);
915dc766f2bSmlelstv 	for (idx = 0; idx < len; idx++)
916dc766f2bSmlelstv 		printf("%.2x ", data[idx]);
917dc766f2bSmlelstv 
918dc766f2bSmlelstv 	printf("\n");
919dc766f2bSmlelstv }
920dc766f2bSmlelstv 
921dc766f2bSmlelstv /*
922dc766f2bSmlelstv  * bt_buildmsg builds an IPMI message from a nfLun, cmd, and data
923dc766f2bSmlelstv  * This is used by BT protocol
924dc766f2bSmlelstv  *
925dc766f2bSmlelstv  * Returns a buffer to an allocated message, txlen contains length
926dc766f2bSmlelstv  *   of allocated message
927dc766f2bSmlelstv  */
928dc766f2bSmlelstv static void *
929dc766f2bSmlelstv bt_buildmsg(struct ipmi_softc *sc, int nfLun, int cmd, int len,
930dc766f2bSmlelstv     const void *data, int *txlen)
931dc766f2bSmlelstv {
932dc766f2bSmlelstv 	uint8_t *buf;
933dc766f2bSmlelstv 
934dc766f2bSmlelstv 	/* Block transfer needs 4 extra bytes: length/netfn/seq/cmd + data */
935dc766f2bSmlelstv 	*txlen = len + 4;
936dc766f2bSmlelstv 	buf = ipmi_buf_acquire(sc, *txlen);
937dc766f2bSmlelstv 	if (buf == NULL)
938dc766f2bSmlelstv 		return NULL;
939dc766f2bSmlelstv 
940dc766f2bSmlelstv 	buf[IPMI_BTMSG_LEN] = len + 3;
941dc766f2bSmlelstv 	buf[IPMI_BTMSG_NFLN] = nfLun;
942dc766f2bSmlelstv 	buf[IPMI_BTMSG_SEQ] = sc->sc_btseq++;
943dc766f2bSmlelstv 	buf[IPMI_BTMSG_CMD] = cmd;
944dc766f2bSmlelstv 	if (len && data)
945dc766f2bSmlelstv 		memcpy(buf + IPMI_BTMSG_DATASND, data, len);
946dc766f2bSmlelstv 
947dc766f2bSmlelstv 	return buf;
948dc766f2bSmlelstv }
949dc766f2bSmlelstv 
950dc766f2bSmlelstv /*
951dc766f2bSmlelstv  * cmn_buildmsg builds an IPMI message from a nfLun, cmd, and data
952dc766f2bSmlelstv  * This is used by both SMIC and KCS protocols
953dc766f2bSmlelstv  *
954dc766f2bSmlelstv  * Returns a buffer to an allocated message, txlen contains length
955dc766f2bSmlelstv  *   of allocated message
956dc766f2bSmlelstv  */
957dc766f2bSmlelstv static void *
958dc766f2bSmlelstv cmn_buildmsg(struct ipmi_softc *sc, int nfLun, int cmd, int len,
959dc766f2bSmlelstv     const void *data, int *txlen)
960dc766f2bSmlelstv {
961dc766f2bSmlelstv 	uint8_t *buf;
962dc766f2bSmlelstv 
963dc766f2bSmlelstv 	/* Common needs two extra bytes: nfLun/cmd + data */
964dc766f2bSmlelstv 	*txlen = len + 2;
965dc766f2bSmlelstv 	buf = ipmi_buf_acquire(sc, *txlen);
966dc766f2bSmlelstv 	if (buf == NULL)
967dc766f2bSmlelstv 		return NULL;
968dc766f2bSmlelstv 
969dc766f2bSmlelstv 	buf[IPMI_MSG_NFLN] = nfLun;
970dc766f2bSmlelstv 	buf[IPMI_MSG_CMD] = cmd;
971dc766f2bSmlelstv 	if (len && data)
972dc766f2bSmlelstv 		memcpy(buf + IPMI_MSG_DATASND, data, len);
973dc766f2bSmlelstv 
974dc766f2bSmlelstv 	return buf;
975dc766f2bSmlelstv }
976dc766f2bSmlelstv 
977dc766f2bSmlelstv /*
978dc766f2bSmlelstv  * ipmi_sendcmd: caller must hold sc_cmd_mtx.
979dc766f2bSmlelstv  *
980dc766f2bSmlelstv  * Send an IPMI command
981dc766f2bSmlelstv  */
982dc766f2bSmlelstv static int
983dc766f2bSmlelstv ipmi_sendcmd(struct ipmi_softc *sc, int rssa, int rslun, int netfn, int cmd,
984dc766f2bSmlelstv     int txlen, const void *data)
985dc766f2bSmlelstv {
986dc766f2bSmlelstv 	uint8_t	*buf;
987dc766f2bSmlelstv 	int		rc = -1;
988dc766f2bSmlelstv 
989dc766f2bSmlelstv 	dbg_printf(50, "%s: rssa=%#.2x nfln=%#.2x cmd=%#.2x len=%#.2x\n",
990dc766f2bSmlelstv 	    __func__, rssa, NETFN_LUN(netfn, rslun), cmd, txlen);
991dc766f2bSmlelstv 	dbg_dump(10, __func__, txlen, data);
992dc766f2bSmlelstv 	if (rssa != BMC_SA) {
993dc766f2bSmlelstv #if 0
994dc766f2bSmlelstv 		buf = sc->sc_if->buildmsg(sc, NETFN_LUN(APP_NETFN, BMC_LUN),
995dc766f2bSmlelstv 		    APP_SEND_MESSAGE, 7 + txlen, NULL, &txlen);
996dc766f2bSmlelstv 		pI2C->bus = (sc->if_ver == 0x09) ?
997dc766f2bSmlelstv 		    PUBLIC_BUS :
998dc766f2bSmlelstv 		    IPMB_CHANNEL_NUMBER;
999dc766f2bSmlelstv 
1000dc766f2bSmlelstv 		imbreq->rsSa = rssa;
1001dc766f2bSmlelstv 		imbreq->nfLn = NETFN_LUN(netfn, rslun);
1002dc766f2bSmlelstv 		imbreq->cSum1 = -(imbreq->rsSa + imbreq->nfLn);
1003dc766f2bSmlelstv 		imbreq->rqSa = BMC_SA;
1004dc766f2bSmlelstv 		imbreq->seqLn = NETFN_LUN(sc->imb_seq++, SMS_LUN);
1005dc766f2bSmlelstv 		imbreq->cmd = cmd;
1006dc766f2bSmlelstv 		if (txlen)
1007dc766f2bSmlelstv 			memcpy(imbreq->data, data, txlen);
1008dc766f2bSmlelstv 		/* Set message checksum */
1009dc766f2bSmlelstv 		imbreq->data[txlen] = cksum8(&imbreq->rqSa, txlen + 3);
1010dc766f2bSmlelstv #endif
1011dc766f2bSmlelstv 		goto done;
1012dc766f2bSmlelstv 	} else
1013dc766f2bSmlelstv 		buf = sc->sc_if->buildmsg(sc, NETFN_LUN(netfn, rslun), cmd,
1014dc766f2bSmlelstv 		    txlen, data, &txlen);
1015dc766f2bSmlelstv 
1016dc766f2bSmlelstv 	if (buf == NULL) {
1017dc766f2bSmlelstv 		aprint_error_dev(sc->sc_dev, "sendcmd buffer busy\n");
1018dc766f2bSmlelstv 		goto done;
1019dc766f2bSmlelstv 	}
1020dc766f2bSmlelstv 	rc = sc->sc_if->sendmsg(sc, txlen, buf);
1021dc766f2bSmlelstv 	ipmi_buf_release(sc, buf);
1022dc766f2bSmlelstv 
1023dc766f2bSmlelstv 	ipmi_delay(sc, 50); /* give bmc chance to digest command */
1024dc766f2bSmlelstv 
1025dc766f2bSmlelstv done:
1026dc766f2bSmlelstv 	return rc;
1027dc766f2bSmlelstv }
1028dc766f2bSmlelstv 
1029dc766f2bSmlelstv static void
1030dc766f2bSmlelstv ipmi_buf_release(struct ipmi_softc *sc, char *buf)
1031dc766f2bSmlelstv {
1032dc766f2bSmlelstv 	KASSERT(sc->sc_buf_rsvd);
1033dc766f2bSmlelstv 	KASSERT(sc->sc_buf == buf);
1034dc766f2bSmlelstv 	sc->sc_buf_rsvd = false;
1035dc766f2bSmlelstv }
1036dc766f2bSmlelstv 
1037dc766f2bSmlelstv static char *
1038dc766f2bSmlelstv ipmi_buf_acquire(struct ipmi_softc *sc, size_t len)
1039dc766f2bSmlelstv {
1040dc766f2bSmlelstv 	KASSERT(len <= sizeof(sc->sc_buf));
1041dc766f2bSmlelstv 
1042dc766f2bSmlelstv 	if (sc->sc_buf_rsvd || len > sizeof(sc->sc_buf))
1043dc766f2bSmlelstv 		return NULL;
1044dc766f2bSmlelstv 	sc->sc_buf_rsvd = true;
1045dc766f2bSmlelstv 	return sc->sc_buf;
1046dc766f2bSmlelstv }
1047dc766f2bSmlelstv 
1048dc766f2bSmlelstv /*
1049dc766f2bSmlelstv  * ipmi_recvcmd: caller must hold sc_cmd_mtx.
1050dc766f2bSmlelstv  */
1051dc766f2bSmlelstv static int
1052dc766f2bSmlelstv ipmi_recvcmd(struct ipmi_softc *sc, int maxlen, int *rxlen, void *data)
1053dc766f2bSmlelstv {
1054dc766f2bSmlelstv 	uint8_t	*buf, rc = 0;
1055dc766f2bSmlelstv 	int		rawlen;
1056dc766f2bSmlelstv 
1057dc766f2bSmlelstv 	/* Need three extra bytes: netfn/cmd/ccode + data */
1058dc766f2bSmlelstv 	buf = ipmi_buf_acquire(sc, maxlen + 3);
1059dc766f2bSmlelstv 	if (buf == NULL) {
1060dc766f2bSmlelstv 		aprint_error_dev(sc->sc_dev, "%s: malloc fails\n", __func__);
1061dc766f2bSmlelstv 		return -1;
1062dc766f2bSmlelstv 	}
1063dc766f2bSmlelstv 	/* Receive message from interface, copy out result data */
1064dc766f2bSmlelstv 	if (sc->sc_if->recvmsg(sc, maxlen + 3, &rawlen, buf)) {
1065dc766f2bSmlelstv 		ipmi_buf_release(sc, buf);
1066dc766f2bSmlelstv 		return -1;
1067dc766f2bSmlelstv 	}
1068dc766f2bSmlelstv 
1069b1f23fc9Smlelstv 	*rxlen = rawlen >= IPMI_MSG_DATARCV ? rawlen - IPMI_MSG_DATARCV : 0;
1070dc766f2bSmlelstv 	if (*rxlen > 0 && data)
1071dc766f2bSmlelstv 		memcpy(data, buf + IPMI_MSG_DATARCV, *rxlen);
1072dc766f2bSmlelstv 
1073dc766f2bSmlelstv 	if ((rc = buf[IPMI_MSG_CCODE]) != 0)
1074dc766f2bSmlelstv 		dbg_printf(1, "%s: nfln=%#.2x cmd=%#.2x err=%#.2x\n", __func__,
1075dc766f2bSmlelstv 		    buf[IPMI_MSG_NFLN], buf[IPMI_MSG_CMD], buf[IPMI_MSG_CCODE]);
1076dc766f2bSmlelstv 
1077dc766f2bSmlelstv 	dbg_printf(50, "%s: nfln=%#.2x cmd=%#.2x err=%#.2x len=%#.2x\n",
1078dc766f2bSmlelstv 	    __func__, buf[IPMI_MSG_NFLN], buf[IPMI_MSG_CMD],
1079dc766f2bSmlelstv 	    buf[IPMI_MSG_CCODE], *rxlen);
1080dc766f2bSmlelstv 	dbg_dump(10, __func__, *rxlen, data);
1081dc766f2bSmlelstv 
1082dc766f2bSmlelstv 	ipmi_buf_release(sc, buf);
1083dc766f2bSmlelstv 
1084dc766f2bSmlelstv 	return rc;
1085dc766f2bSmlelstv }
1086dc766f2bSmlelstv 
1087dc766f2bSmlelstv /*
1088dc766f2bSmlelstv  * ipmi_delay: caller must hold sc_cmd_mtx.
1089dc766f2bSmlelstv  */
1090dc766f2bSmlelstv static void
1091dc766f2bSmlelstv ipmi_delay(struct ipmi_softc *sc, int ms)
1092dc766f2bSmlelstv {
1093dc766f2bSmlelstv 	if (cold) {
1094dc766f2bSmlelstv 		delay(ms * 1000);
1095dc766f2bSmlelstv 		return;
1096dc766f2bSmlelstv 	}
10972ec61c4fSriastradh 	kpause("ipmicmd", /*intr*/false, /*timo*/mstohz(ms), /*mtx*/NULL);
1098dc766f2bSmlelstv }
1099dc766f2bSmlelstv 
1100dc766f2bSmlelstv /* Read a partial SDR entry */
1101dc766f2bSmlelstv static int
1102dc766f2bSmlelstv get_sdr_partial(struct ipmi_softc *sc, uint16_t recordId, uint16_t reserveId,
1103dc766f2bSmlelstv     uint8_t offset, uint8_t length, void *buffer, uint16_t *nxtRecordId)
1104dc766f2bSmlelstv {
1105d3d86aceSriastradh 	union {
1106d3d86aceSriastradh 		struct {
1107d3d86aceSriastradh 			uint16_t	reserveId;
1108d3d86aceSriastradh 			uint16_t	recordId;
1109d3d86aceSriastradh 			uint8_t		offset;
1110d3d86aceSriastradh 			uint8_t		length;
1111d3d86aceSriastradh 		} __packed	cmd;
1112d3d86aceSriastradh 		struct {
1113d3d86aceSriastradh 			uint16_t	nxtRecordId;
1114d3d86aceSriastradh 			uint8_t		data[262];
1115d3d86aceSriastradh 		} __packed	msg;
1116d3d86aceSriastradh 	}		u;
1117dc766f2bSmlelstv 	int		len;
1118dc766f2bSmlelstv 
1119d3d86aceSriastradh 	__CTASSERT(sizeof(u) == 256 + 8);
1120d3d86aceSriastradh 	__CTASSERT(sizeof(u.cmd) == 6);
1121d3d86aceSriastradh 	__CTASSERT(offsetof(typeof(u.msg), data) == 2);
1122d3d86aceSriastradh 
1123d3d86aceSriastradh 	u.cmd.reserveId = reserveId;
1124d3d86aceSriastradh 	u.cmd.recordId = recordId;
1125d3d86aceSriastradh 	u.cmd.offset = offset;
1126d3d86aceSriastradh 	u.cmd.length = length;
1127dc766f2bSmlelstv 	mutex_enter(&sc->sc_cmd_mtx);
1128d3d86aceSriastradh 	if (ipmi_sendcmd(sc, BMC_SA, 0, STORAGE_NETFN, STORAGE_GET_SDR,
1129d3d86aceSriastradh 		sizeof(u.cmd), &u.cmd)) {
1130dc766f2bSmlelstv 		mutex_exit(&sc->sc_cmd_mtx);
1131dc766f2bSmlelstv 		aprint_error_dev(sc->sc_dev, "%s: sendcmd fails\n", __func__);
1132dc766f2bSmlelstv 		return -1;
1133dc766f2bSmlelstv 	}
1134d3d86aceSriastradh 	if (ipmi_recvcmd(sc, 8 + length, &len, &u.msg)) {
1135dc766f2bSmlelstv 		mutex_exit(&sc->sc_cmd_mtx);
1136dc766f2bSmlelstv 		aprint_error_dev(sc->sc_dev, "%s: recvcmd fails\n", __func__);
1137dc766f2bSmlelstv 		return -1;
1138dc766f2bSmlelstv 	}
1139dc766f2bSmlelstv 	mutex_exit(&sc->sc_cmd_mtx);
1140dc766f2bSmlelstv 	if (nxtRecordId)
1141d3d86aceSriastradh 		*nxtRecordId = u.msg.nxtRecordId;
1142d3d86aceSriastradh 	memcpy(buffer, u.msg.data, len - offsetof(typeof(u.msg), data));
1143dc766f2bSmlelstv 
1144dc766f2bSmlelstv 	return 0;
1145dc766f2bSmlelstv }
1146dc766f2bSmlelstv 
1147dc766f2bSmlelstv static int maxsdrlen = 0x10;
1148dc766f2bSmlelstv 
1149dc766f2bSmlelstv /* Read an entire SDR; pass to add sensor */
1150dc766f2bSmlelstv static int
1151dc766f2bSmlelstv get_sdr(struct ipmi_softc *sc, uint16_t recid, uint16_t *nxtrec)
1152dc766f2bSmlelstv {
1153dc766f2bSmlelstv 	uint16_t	resid = 0;
1154dc766f2bSmlelstv 	int		len, sdrlen, offset;
1155dc766f2bSmlelstv 	uint8_t	*psdr;
1156dc766f2bSmlelstv 	struct sdrhdr	shdr;
1157dc766f2bSmlelstv 
1158dc766f2bSmlelstv 	mutex_enter(&sc->sc_cmd_mtx);
1159dc766f2bSmlelstv 	/* Reserve SDR */
1160dc766f2bSmlelstv 	if (ipmi_sendcmd(sc, BMC_SA, 0, STORAGE_NETFN, STORAGE_RESERVE_SDR,
1161dc766f2bSmlelstv 	    0, NULL)) {
1162dc766f2bSmlelstv 		mutex_exit(&sc->sc_cmd_mtx);
1163dc766f2bSmlelstv 		aprint_error_dev(sc->sc_dev, "reserve send fails\n");
1164dc766f2bSmlelstv 		return -1;
1165dc766f2bSmlelstv 	}
1166dc766f2bSmlelstv 	if (ipmi_recvcmd(sc, sizeof(resid), &len, &resid)) {
1167dc766f2bSmlelstv 		mutex_exit(&sc->sc_cmd_mtx);
1168dc766f2bSmlelstv 		aprint_error_dev(sc->sc_dev, "reserve recv fails\n");
1169dc766f2bSmlelstv 		return -1;
1170dc766f2bSmlelstv 	}
1171dc766f2bSmlelstv 	mutex_exit(&sc->sc_cmd_mtx);
1172dc766f2bSmlelstv 	/* Get SDR Header */
1173dc766f2bSmlelstv 	if (get_sdr_partial(sc, recid, resid, 0, sizeof shdr, &shdr, nxtrec)) {
1174dc766f2bSmlelstv 		aprint_error_dev(sc->sc_dev, "get header fails\n");
1175dc766f2bSmlelstv 		return -1;
1176dc766f2bSmlelstv 	}
1177dc766f2bSmlelstv 	/* Allocate space for entire SDR Length of SDR in header does not
1178dc766f2bSmlelstv 	 * include header length */
1179dc766f2bSmlelstv 	sdrlen = sizeof(shdr) + shdr.record_length;
1180dc766f2bSmlelstv 	psdr = malloc(sdrlen, M_DEVBUF, M_WAITOK);
1181dc766f2bSmlelstv 	if (psdr == NULL)
1182dc766f2bSmlelstv 		return -1;
1183dc766f2bSmlelstv 
1184dc766f2bSmlelstv 	memcpy(psdr, &shdr, sizeof(shdr));
1185dc766f2bSmlelstv 
1186dc766f2bSmlelstv 	/* Read SDR Data maxsdrlen bytes at a time */
1187dc766f2bSmlelstv 	for (offset = sizeof(shdr); offset < sdrlen; offset += maxsdrlen) {
1188dc766f2bSmlelstv 		len = sdrlen - offset;
1189dc766f2bSmlelstv 		if (len > maxsdrlen)
1190dc766f2bSmlelstv 			len = maxsdrlen;
1191dc766f2bSmlelstv 
1192dc766f2bSmlelstv 		if (get_sdr_partial(sc, recid, resid, offset, len,
1193dc766f2bSmlelstv 		    psdr + offset, NULL)) {
1194dc766f2bSmlelstv 			aprint_error_dev(sc->sc_dev,
1195dc766f2bSmlelstv 			    "get chunk : %d,%d fails\n", offset, len);
1196dc766f2bSmlelstv 			free(psdr, M_DEVBUF);
1197dc766f2bSmlelstv 			return -1;
1198dc766f2bSmlelstv 		}
1199dc766f2bSmlelstv 	}
1200dc766f2bSmlelstv 
1201dc766f2bSmlelstv 	/* Add SDR to sensor list, if not wanted, free buffer */
1202dc766f2bSmlelstv 	if (add_sdr_sensor(sc, psdr) == 0)
1203dc766f2bSmlelstv 		free(psdr, M_DEVBUF);
1204dc766f2bSmlelstv 
1205dc766f2bSmlelstv 	return 0;
1206dc766f2bSmlelstv }
1207dc766f2bSmlelstv 
1208dc766f2bSmlelstv static int
1209dc766f2bSmlelstv getbits(uint8_t *bytes, int bitpos, int bitlen)
1210dc766f2bSmlelstv {
1211dc766f2bSmlelstv 	int	v;
1212dc766f2bSmlelstv 	int	mask;
1213dc766f2bSmlelstv 
1214dc766f2bSmlelstv 	bitpos += bitlen - 1;
1215dc766f2bSmlelstv 	for (v = 0; bitlen--;) {
1216dc766f2bSmlelstv 		v <<= 1;
1217dc766f2bSmlelstv 		mask = 1L << (bitpos & 7);
1218dc766f2bSmlelstv 		if (bytes[bitpos >> 3] & mask)
1219dc766f2bSmlelstv 			v |= 1;
1220dc766f2bSmlelstv 		bitpos--;
1221dc766f2bSmlelstv 	}
1222dc766f2bSmlelstv 
1223dc766f2bSmlelstv 	return v;
1224dc766f2bSmlelstv }
1225dc766f2bSmlelstv 
1226dc766f2bSmlelstv /* Decode IPMI sensor name */
1227dc766f2bSmlelstv static void
1228dc766f2bSmlelstv ipmi_sensor_name(char *name, int len, uint8_t typelen, uint8_t *bits)
1229dc766f2bSmlelstv {
1230dc766f2bSmlelstv 	int	i, slen;
1231dc766f2bSmlelstv 	char	bcdplus[] = "0123456789 -.:,_";
1232dc766f2bSmlelstv 
1233dc766f2bSmlelstv 	slen = typelen & 0x1F;
1234dc766f2bSmlelstv 	switch (typelen >> 6) {
1235dc766f2bSmlelstv 	case IPMI_NAME_UNICODE:
1236dc766f2bSmlelstv 		//unicode
1237dc766f2bSmlelstv 		break;
1238dc766f2bSmlelstv 
1239dc766f2bSmlelstv 	case IPMI_NAME_BCDPLUS:
1240dc766f2bSmlelstv 		/* Characters are encoded in 4-bit BCDPLUS */
1241dc766f2bSmlelstv 		if (len < slen * 2 + 1)
1242dc766f2bSmlelstv 			slen = (len >> 1) - 1;
1243dc766f2bSmlelstv 		for (i = 0; i < slen; i++) {
1244dc766f2bSmlelstv 			*(name++) = bcdplus[bits[i] >> 4];
1245dc766f2bSmlelstv 			*(name++) = bcdplus[bits[i] & 0xF];
1246dc766f2bSmlelstv 		}
1247dc766f2bSmlelstv 		break;
1248dc766f2bSmlelstv 
1249dc766f2bSmlelstv 	case IPMI_NAME_ASCII6BIT:
1250dc766f2bSmlelstv 		/* Characters are encoded in 6-bit ASCII
1251dc766f2bSmlelstv 		 *   0x00 - 0x3F maps to 0x20 - 0x5F */
1252dc766f2bSmlelstv 		/* XXX: need to calculate max len: slen = 3/4 * len */
1253dc766f2bSmlelstv 		if (len < slen + 1)
1254dc766f2bSmlelstv 			slen = len - 1;
1255dc766f2bSmlelstv 		for (i = 0; i < slen * 8; i += 6)
1256dc766f2bSmlelstv 			*(name++) = getbits(bits, i, 6) + ' ';
1257dc766f2bSmlelstv 		break;
1258dc766f2bSmlelstv 
1259dc766f2bSmlelstv 	case IPMI_NAME_ASCII8BIT:
1260dc766f2bSmlelstv 		/* Characters are 8-bit ascii */
1261dc766f2bSmlelstv 		if (len < slen + 1)
1262dc766f2bSmlelstv 			slen = len - 1;
1263dc766f2bSmlelstv 		while (slen--)
1264dc766f2bSmlelstv 			*(name++) = *(bits++);
1265dc766f2bSmlelstv 		break;
1266dc766f2bSmlelstv 	}
1267dc766f2bSmlelstv 	*name = 0;
1268dc766f2bSmlelstv }
1269dc766f2bSmlelstv 
1270dc766f2bSmlelstv /* Sign extend a n-bit value */
1271dc766f2bSmlelstv static long
1272dc766f2bSmlelstv signextend(unsigned long val, int bits)
1273dc766f2bSmlelstv {
1274dc766f2bSmlelstv 	long msk = (1L << (bits-1))-1;
1275dc766f2bSmlelstv 
1276dc766f2bSmlelstv 	return -(val & ~msk) | val;
1277dc766f2bSmlelstv }
1278dc766f2bSmlelstv 
1279dc766f2bSmlelstv 
1280dc766f2bSmlelstv /* fixpoint arithmetic */
1281dc766f2bSmlelstv #define FIX2INT(x)   ((int64_t)((x) >> 32))
1282dc766f2bSmlelstv #define INT2FIX(x)   ((int64_t)((uint64_t)(x) << 32))
1283dc766f2bSmlelstv 
1284dc766f2bSmlelstv #define FIX2            0x0000000200000000ll /* 2.0 */
1285dc766f2bSmlelstv #define FIX3            0x0000000300000000ll /* 3.0 */
1286dc766f2bSmlelstv #define FIXE            0x00000002b7e15163ll /* 2.71828182845904523536 */
1287dc766f2bSmlelstv #define FIX10           0x0000000a00000000ll /* 10.0 */
1288dc766f2bSmlelstv #define FIXMONE         0xffffffff00000000ll /* -1.0 */
1289dc766f2bSmlelstv #define FIXHALF         0x0000000080000000ll /* 0.5 */
1290dc766f2bSmlelstv #define FIXTHIRD        0x0000000055555555ll /* 0.33333333333333333333 */
1291dc766f2bSmlelstv 
1292dc766f2bSmlelstv #define FIX1LOG2        0x0000000171547653ll /* 1.0/log(2) */
1293dc766f2bSmlelstv #define FIX1LOGE        0x0000000100000000ll /* 1.0/log(2.71828182845904523536) */
1294dc766f2bSmlelstv #define FIX1LOG10       0x000000006F2DEC55ll /* 1.0/log(10) */
1295dc766f2bSmlelstv 
1296dc766f2bSmlelstv #define FIX1E           0x000000005E2D58D9ll /* 1.0/2.71828182845904523536 */
1297dc766f2bSmlelstv 
1298dc766f2bSmlelstv static int64_t fixlog_a[] = {
1299dc766f2bSmlelstv 	0x0000000100000000ll /* 1.0/1.0 */,
1300dc766f2bSmlelstv 	0xffffffff80000000ll /* -1.0/2.0 */,
1301dc766f2bSmlelstv 	0x0000000055555555ll /* 1.0/3.0 */,
1302dc766f2bSmlelstv 	0xffffffffc0000000ll /* -1.0/4.0 */,
1303dc766f2bSmlelstv 	0x0000000033333333ll /* 1.0/5.0 */,
1304dc766f2bSmlelstv 	0x000000002aaaaaabll /* -1.0/6.0 */,
1305dc766f2bSmlelstv 	0x0000000024924925ll /* 1.0/7.0 */,
1306dc766f2bSmlelstv 	0x0000000020000000ll /* -1.0/8.0 */,
1307dc766f2bSmlelstv 	0x000000001c71c71cll /* 1.0/9.0 */
1308dc766f2bSmlelstv };
1309dc766f2bSmlelstv 
1310dc766f2bSmlelstv static int64_t fixexp_a[] = {
1311dc766f2bSmlelstv 	0x0000000100000000ll /* 1.0/1.0 */,
1312dc766f2bSmlelstv 	0x0000000100000000ll /* 1.0/1.0 */,
1313dc766f2bSmlelstv 	0x0000000080000000ll /* 1.0/2.0 */,
1314dc766f2bSmlelstv 	0x000000002aaaaaabll /* 1.0/6.0 */,
1315dc766f2bSmlelstv 	0x000000000aaaaaabll /* 1.0/24.0 */,
1316dc766f2bSmlelstv 	0x0000000002222222ll /* 1.0/120.0 */,
1317dc766f2bSmlelstv 	0x00000000005b05b0ll /* 1.0/720.0 */,
1318dc766f2bSmlelstv 	0x00000000000d00d0ll /* 1.0/5040.0 */,
1319dc766f2bSmlelstv 	0x000000000001a01all /* 1.0/40320.0 */
1320dc766f2bSmlelstv };
1321dc766f2bSmlelstv 
1322dc766f2bSmlelstv static int64_t
1323dc766f2bSmlelstv fixmul(int64_t x, int64_t y)
1324dc766f2bSmlelstv {
1325dc766f2bSmlelstv 	int64_t z;
1326dc766f2bSmlelstv 	int64_t a,b,c,d;
1327dc766f2bSmlelstv 	int neg;
1328dc766f2bSmlelstv 
1329dc766f2bSmlelstv 	neg = 0;
1330dc766f2bSmlelstv 	if (x < 0) {
1331dc766f2bSmlelstv 		x = -x;
1332dc766f2bSmlelstv 		neg = !neg;
1333dc766f2bSmlelstv 	}
1334dc766f2bSmlelstv 	if (y < 0) {
1335dc766f2bSmlelstv 		y = -y;
1336dc766f2bSmlelstv 		neg = !neg;
1337dc766f2bSmlelstv 	}
1338dc766f2bSmlelstv 
1339dc766f2bSmlelstv 	a = FIX2INT(x);
1340dc766f2bSmlelstv 	b = x - INT2FIX(a);
1341dc766f2bSmlelstv 	c = FIX2INT(y);
1342dc766f2bSmlelstv 	d = y - INT2FIX(c);
1343dc766f2bSmlelstv 
1344dc766f2bSmlelstv 	z = INT2FIX(a*c) + a * d + b * c + (b/2 * d/2 >> 30);
1345dc766f2bSmlelstv 
1346dc766f2bSmlelstv 	return neg ? -z : z;
1347dc766f2bSmlelstv }
1348dc766f2bSmlelstv 
1349dc766f2bSmlelstv static int64_t
1350dc766f2bSmlelstv poly(int64_t x0, int64_t x, int64_t a[], int n)
1351dc766f2bSmlelstv {
1352dc766f2bSmlelstv 	int64_t z;
1353dc766f2bSmlelstv 	int i;
1354dc766f2bSmlelstv 
1355dc766f2bSmlelstv 	z  = fixmul(x0, a[0]);
1356dc766f2bSmlelstv 	for (i=1; i<n; ++i) {
1357dc766f2bSmlelstv 		x0 = fixmul(x0, x);
1358dc766f2bSmlelstv 		z  = fixmul(x0, a[i]) + z;
1359dc766f2bSmlelstv 	}
1360dc766f2bSmlelstv 	return z;
1361dc766f2bSmlelstv }
1362dc766f2bSmlelstv 
1363dc766f2bSmlelstv static int64_t
1364dc766f2bSmlelstv logx(int64_t x, int64_t y)
1365dc766f2bSmlelstv {
1366dc766f2bSmlelstv 	int64_t z;
1367dc766f2bSmlelstv 
1368dc766f2bSmlelstv 	if (x <= INT2FIX(0)) {
1369dc766f2bSmlelstv 		z = INT2FIX(-99999);
1370dc766f2bSmlelstv 		goto done;
1371dc766f2bSmlelstv 	}
1372dc766f2bSmlelstv 
1373dc766f2bSmlelstv 	z = INT2FIX(0);
1374dc766f2bSmlelstv 	while (x >= FIXE) {
1375dc766f2bSmlelstv 		x = fixmul(x, FIX1E);
1376dc766f2bSmlelstv 		z += INT2FIX(1);
1377dc766f2bSmlelstv 	}
1378dc766f2bSmlelstv 	while (x < INT2FIX(1)) {
1379dc766f2bSmlelstv 		x = fixmul(x, FIXE);
1380dc766f2bSmlelstv 		z -= INT2FIX(1);
1381dc766f2bSmlelstv 	}
1382dc766f2bSmlelstv 
1383dc766f2bSmlelstv 	x -= INT2FIX(1);
1384dc766f2bSmlelstv 	z += poly(x, x, fixlog_a, sizeof(fixlog_a)/sizeof(fixlog_a[0]));
1385dc766f2bSmlelstv 	z  = fixmul(z, y);
1386dc766f2bSmlelstv 
1387dc766f2bSmlelstv done:
1388dc766f2bSmlelstv 	return z;
1389dc766f2bSmlelstv }
1390dc766f2bSmlelstv 
1391dc766f2bSmlelstv static int64_t
1392dc766f2bSmlelstv powx(int64_t x, int64_t y)
1393dc766f2bSmlelstv {
1394dc766f2bSmlelstv 	int64_t k;
1395dc766f2bSmlelstv 
1396dc766f2bSmlelstv 	if (x == INT2FIX(0))
1397dc766f2bSmlelstv 		goto done;
1398dc766f2bSmlelstv 
1399dc766f2bSmlelstv 	x = logx(x,y);
1400dc766f2bSmlelstv 
1401dc766f2bSmlelstv 	if (x < INT2FIX(0)) {
1402dc766f2bSmlelstv 		x = INT2FIX(0) - x;
1403dc766f2bSmlelstv 		k = -FIX2INT(x);
1404dc766f2bSmlelstv 		x = INT2FIX(-k) - x;
1405dc766f2bSmlelstv 	} else {
1406dc766f2bSmlelstv 		k = FIX2INT(x);
1407dc766f2bSmlelstv 		x = x - INT2FIX(k);
1408dc766f2bSmlelstv 	}
1409dc766f2bSmlelstv 
1410dc766f2bSmlelstv 	x = poly(INT2FIX(1), x, fixexp_a, sizeof(fixexp_a)/sizeof(fixexp_a[0]));
1411dc766f2bSmlelstv 
1412dc766f2bSmlelstv 	while (k < 0) {
1413dc766f2bSmlelstv 		x = fixmul(x, FIX1E);
1414dc766f2bSmlelstv 		++k;
1415dc766f2bSmlelstv 	}
1416dc766f2bSmlelstv 	while (k > 0) {
1417dc766f2bSmlelstv 		x = fixmul(x, FIXE);
1418dc766f2bSmlelstv 		--k;
1419dc766f2bSmlelstv 	}
1420dc766f2bSmlelstv 
1421dc766f2bSmlelstv done:
1422dc766f2bSmlelstv 	return x;
1423dc766f2bSmlelstv }
1424dc766f2bSmlelstv 
1425dc766f2bSmlelstv /* Convert IPMI reading from sensor factors */
1426dc766f2bSmlelstv static long
1427dc766f2bSmlelstv ipmi_convert(uint8_t v, struct sdrtype1 *s1, long adj)
1428dc766f2bSmlelstv {
1429dc766f2bSmlelstv 	int64_t	M, B;
1430dc766f2bSmlelstv 	char	K1, K2;
1431dc766f2bSmlelstv 	int64_t	val, v1, v2, vs;
1432dc766f2bSmlelstv 	int sign = (s1->units1 >> 6) & 0x3;
1433dc766f2bSmlelstv 
1434dc766f2bSmlelstv 	vs = (sign == 0x1 || sign == 0x2) ? (int8_t)v : v;
1435dc766f2bSmlelstv 	if ((vs < 0) && (sign == 0x1))
1436dc766f2bSmlelstv 		vs++;
1437dc766f2bSmlelstv 
1438dc766f2bSmlelstv 	/* Calculate linear reading variables */
1439dc766f2bSmlelstv 	M  = signextend((((short)(s1->m_tolerance & 0xC0)) << 2) + s1->m, 10);
1440dc766f2bSmlelstv 	B  = signextend((((short)(s1->b_accuracy & 0xC0)) << 2) + s1->b, 10);
1441dc766f2bSmlelstv 	K1 = signextend(s1->rbexp & 0xF, 4);
1442dc766f2bSmlelstv 	K2 = signextend(s1->rbexp >> 4, 4);
1443dc766f2bSmlelstv 
1444dc766f2bSmlelstv 	/* Calculate sensor reading:
1445dc766f2bSmlelstv 	 *  y = L((M * v + (B * 10^K1)) * 10^(K2+adj)
1446dc766f2bSmlelstv 	 *
1447dc766f2bSmlelstv 	 * This commutes out to:
1448dc766f2bSmlelstv 	 *  y = L(M*v * 10^(K2+adj) + B * 10^(K1+K2+adj)); */
1449dc766f2bSmlelstv 	v1 = powx(FIX10, INT2FIX(K2 + adj));
1450dc766f2bSmlelstv 	v2 = powx(FIX10, INT2FIX(K1 + K2 + adj));
1451dc766f2bSmlelstv 	val = M * vs * v1 + B * v2;
1452dc766f2bSmlelstv 
1453dc766f2bSmlelstv 	/* Linearization function: y = f(x) 0 : y = x 1 : y = ln(x) 2 : y =
1454dc766f2bSmlelstv 	 * log10(x) 3 : y = log2(x) 4 : y = e^x 5 : y = 10^x 6 : y = 2^x 7 : y
1455dc766f2bSmlelstv 	 * = 1/x 8 : y = x^2 9 : y = x^3 10 : y = square root(x) 11 : y = cube
1456dc766f2bSmlelstv 	 * root(x) */
1457dc766f2bSmlelstv 	switch (s1->linear & 0x7f) {
1458dc766f2bSmlelstv 	case 0: break;
1459dc766f2bSmlelstv 	case 1: val = logx(val,FIX1LOGE); break;
1460dc766f2bSmlelstv 	case 2: val = logx(val,FIX1LOG10); break;
1461dc766f2bSmlelstv 	case 3: val = logx(val,FIX1LOG2); break;
1462dc766f2bSmlelstv 	case 4: val = powx(FIXE,val); break;
1463dc766f2bSmlelstv 	case 5: val = powx(FIX10,val); break;
1464dc766f2bSmlelstv 	case 6: val = powx(FIX2,val); break;
1465dc766f2bSmlelstv 	case 7: val = powx(val,FIXMONE); break;
1466dc766f2bSmlelstv 	case 8: val = powx(val,FIX2); break;
1467dc766f2bSmlelstv 	case 9: val = powx(val,FIX3); break;
1468dc766f2bSmlelstv 	case 10: val = powx(val,FIXHALF); break;
1469dc766f2bSmlelstv 	case 11: val = powx(val,FIXTHIRD); break;
1470dc766f2bSmlelstv 	}
1471dc766f2bSmlelstv 
1472dc766f2bSmlelstv 	return FIX2INT(val);
1473dc766f2bSmlelstv }
1474dc766f2bSmlelstv 
1475dc766f2bSmlelstv static int32_t
1476dc766f2bSmlelstv ipmi_convert_sensor(uint8_t *reading, struct ipmi_sensor *psensor)
1477dc766f2bSmlelstv {
1478dc766f2bSmlelstv 	struct sdrtype1	*s1 = (struct sdrtype1 *)psensor->i_sdr;
1479dc766f2bSmlelstv 	int32_t val;
1480dc766f2bSmlelstv 
1481dc766f2bSmlelstv 	switch (psensor->i_envtype) {
1482dc766f2bSmlelstv 	case ENVSYS_STEMP:
1483dc766f2bSmlelstv 		val = ipmi_convert(reading[0], s1, 6) + 273150000;
1484dc766f2bSmlelstv 		break;
1485dc766f2bSmlelstv 
1486dc766f2bSmlelstv 	case ENVSYS_SVOLTS_DC:
1487dc766f2bSmlelstv 		val = ipmi_convert(reading[0], s1, 6);
1488dc766f2bSmlelstv 		break;
1489dc766f2bSmlelstv 
1490dc766f2bSmlelstv 	case ENVSYS_SFANRPM:
1491dc766f2bSmlelstv 		val = ipmi_convert(reading[0], s1, 0);
1492dc766f2bSmlelstv 		if (((s1->units1>>3)&0x7) == 0x3)
1493dc766f2bSmlelstv 			val *= 60; /* RPS -> RPM */
1494dc766f2bSmlelstv 		break;
1495dc766f2bSmlelstv 	default:
1496dc766f2bSmlelstv 		val = 0;
1497dc766f2bSmlelstv 		break;
1498dc766f2bSmlelstv 	}
1499dc766f2bSmlelstv 	return val;
1500dc766f2bSmlelstv }
1501dc766f2bSmlelstv 
1502dc766f2bSmlelstv static void
1503dc766f2bSmlelstv ipmi_set_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
1504dc766f2bSmlelstv 		sysmon_envsys_lim_t *limits, uint32_t *props)
1505dc766f2bSmlelstv {
1506dc766f2bSmlelstv 	struct ipmi_sensor *ipmi_s;
1507dc766f2bSmlelstv 
1508dc766f2bSmlelstv 	/* Find the ipmi_sensor corresponding to this edata */
1509dc766f2bSmlelstv 	SLIST_FOREACH(ipmi_s, &ipmi_sensor_list, i_list) {
1510dc766f2bSmlelstv 		if (ipmi_s->i_envnum == edata->sensor) {
1511dc766f2bSmlelstv 			if (limits == NULL) {
1512dc766f2bSmlelstv 				limits = &ipmi_s->i_deflims;
1513dc766f2bSmlelstv 				props  = &ipmi_s->i_defprops;
1514dc766f2bSmlelstv 			}
1515dc766f2bSmlelstv 			*props |= PROP_DRIVER_LIMITS;
1516dc766f2bSmlelstv 			ipmi_s->i_limits = *limits;
1517dc766f2bSmlelstv 			ipmi_s->i_props  = *props;
1518dc766f2bSmlelstv 			return;
1519dc766f2bSmlelstv 		}
1520dc766f2bSmlelstv 	}
1521dc766f2bSmlelstv 	return;
1522dc766f2bSmlelstv }
1523dc766f2bSmlelstv 
1524dc766f2bSmlelstv static void
1525dc766f2bSmlelstv ipmi_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
1526dc766f2bSmlelstv 		sysmon_envsys_lim_t *limits, uint32_t *props)
1527dc766f2bSmlelstv {
1528dc766f2bSmlelstv 	struct ipmi_sensor *ipmi_s;
1529dc766f2bSmlelstv 	struct ipmi_softc *sc = sme->sme_cookie;
1530dc766f2bSmlelstv 
1531dc766f2bSmlelstv 	/* Find the ipmi_sensor corresponding to this edata */
1532dc766f2bSmlelstv 	SLIST_FOREACH(ipmi_s, &ipmi_sensor_list, i_list) {
1533dc766f2bSmlelstv 		if (ipmi_s->i_envnum == edata->sensor) {
1534dc766f2bSmlelstv 			ipmi_get_sensor_limits(sc, ipmi_s, limits, props);
1535dc766f2bSmlelstv 			ipmi_s->i_limits = *limits;
1536dc766f2bSmlelstv 			ipmi_s->i_props  = *props;
1537dc766f2bSmlelstv 			if (ipmi_s->i_defprops == 0) {
1538dc766f2bSmlelstv 				ipmi_s->i_defprops = *props;
1539dc766f2bSmlelstv 				ipmi_s->i_deflims  = *limits;
1540dc766f2bSmlelstv 			}
1541dc766f2bSmlelstv 			return;
1542dc766f2bSmlelstv 		}
1543dc766f2bSmlelstv 	}
1544dc766f2bSmlelstv 	return;
1545dc766f2bSmlelstv }
1546dc766f2bSmlelstv 
1547404b3312Smlelstv /* valid bits for (upper,lower) x (non-recoverable, critical, warn) */
1548404b3312Smlelstv #define UN	0x20
1549404b3312Smlelstv #define UC	0x10
1550404b3312Smlelstv #define UW	0x08
1551404b3312Smlelstv #define LN	0x04
1552404b3312Smlelstv #define LC	0x02
1553404b3312Smlelstv #define LW	0x01
1554404b3312Smlelstv 
1555dc766f2bSmlelstv static void
1556dc766f2bSmlelstv ipmi_get_sensor_limits(struct ipmi_softc *sc, struct ipmi_sensor *psensor,
1557dc766f2bSmlelstv 		       sysmon_envsys_lim_t *limits, uint32_t *props)
1558dc766f2bSmlelstv {
1559dc766f2bSmlelstv 	struct sdrtype1	*s1 = (struct sdrtype1 *)psensor->i_sdr;
1560dc766f2bSmlelstv 	bool failure;
1561dc766f2bSmlelstv 	int	rxlen;
1562404b3312Smlelstv 	uint8_t	data[32], valid;
1563dc766f2bSmlelstv 	uint32_t prop_critmax, prop_warnmax, prop_critmin, prop_warnmin;
1564dc766f2bSmlelstv 	int32_t *pcritmax, *pwarnmax, *pcritmin, *pwarnmin;
1565dc766f2bSmlelstv 
1566dc766f2bSmlelstv 	*props &= ~(PROP_CRITMIN | PROP_CRITMAX | PROP_WARNMIN | PROP_WARNMAX);
1567dc766f2bSmlelstv 	data[0] = psensor->i_num;
1568dc766f2bSmlelstv 	mutex_enter(&sc->sc_cmd_mtx);
1569dc766f2bSmlelstv 	failure =
1570dc766f2bSmlelstv 	    ipmi_sendcmd(sc, s1->owner_id, s1->owner_lun,
1571dc766f2bSmlelstv 			 SE_NETFN, SE_GET_SENSOR_THRESHOLD, 1, data) ||
1572dc766f2bSmlelstv 	    ipmi_recvcmd(sc, sizeof(data), &rxlen, data);
1573dc766f2bSmlelstv 	mutex_exit(&sc->sc_cmd_mtx);
1574dc766f2bSmlelstv 	if (failure)
1575dc766f2bSmlelstv 		return;
1576dc766f2bSmlelstv 
1577dc766f2bSmlelstv 	dbg_printf(25, "%s: %#.2x %#.2x %#.2x %#.2x %#.2x %#.2x %#.2x\n",
1578dc766f2bSmlelstv 	    __func__, data[0], data[1], data[2], data[3], data[4], data[5],
1579dc766f2bSmlelstv 	    data[6]);
1580dc766f2bSmlelstv 
1581dc766f2bSmlelstv 	switch (s1->linear & 0x7f) {
1582dc766f2bSmlelstv 	case 7: /* 1/x sensor, exchange upper and lower limits */
1583dc766f2bSmlelstv 		prop_critmax = PROP_CRITMIN;
1584dc766f2bSmlelstv 		prop_warnmax = PROP_WARNMIN;
1585dc766f2bSmlelstv 		prop_critmin = PROP_CRITMAX;
1586dc766f2bSmlelstv 		prop_warnmin = PROP_WARNMAX;
1587dc766f2bSmlelstv 		pcritmax = &limits->sel_critmin;
1588dc766f2bSmlelstv 		pwarnmax = &limits->sel_warnmin;
1589dc766f2bSmlelstv 		pcritmin = &limits->sel_critmax;
1590dc766f2bSmlelstv 		pwarnmin = &limits->sel_warnmax;
1591dc766f2bSmlelstv 		break;
1592dc766f2bSmlelstv 	default:
1593dc766f2bSmlelstv 		prop_critmax = PROP_CRITMAX;
1594dc766f2bSmlelstv 		prop_warnmax = PROP_WARNMAX;
1595dc766f2bSmlelstv 		prop_critmin = PROP_CRITMIN;
1596dc766f2bSmlelstv 		prop_warnmin = PROP_WARNMIN;
1597dc766f2bSmlelstv 		pcritmax = &limits->sel_critmax;
1598dc766f2bSmlelstv 		pwarnmax = &limits->sel_warnmax;
1599dc766f2bSmlelstv 		pcritmin = &limits->sel_critmin;
1600dc766f2bSmlelstv 		pwarnmin = &limits->sel_warnmin;
1601dc766f2bSmlelstv 		break;
1602dc766f2bSmlelstv 	}
1603dc766f2bSmlelstv 
1604404b3312Smlelstv 	valid = data[0];
1605404b3312Smlelstv 
1606404b3312Smlelstv 	/* if upper non-recoverable < warning, ignore it */
1607404b3312Smlelstv 	if ((valid & (UN|UW)) == (UN|UW) && data[6] < data[4])
1608404b3312Smlelstv 		valid ^= UN;
1609404b3312Smlelstv 	/* if upper critical < warning, ignore it */
1610404b3312Smlelstv 	if ((valid & (UC|UW)) == (UC|UW) && data[5] < data[4])
1611404b3312Smlelstv 		valid ^= UC;
1612404b3312Smlelstv 
1613404b3312Smlelstv 	/* if lower non-recoverable > warning, ignore it */
1614404b3312Smlelstv 	if ((data[0] & (LN|LW)) == (LN|LW) && data[3] > data[1])
1615404b3312Smlelstv 		valid ^= LN;
1616404b3312Smlelstv 	/* if lower critical > warning, ignore it */
1617404b3312Smlelstv 	if ((data[0] & (LC|LW)) == (LC|LW) && data[2] > data[1])
1618404b3312Smlelstv 		valid ^= LC;
1619404b3312Smlelstv 
1620404b3312Smlelstv 	if (valid & UN && data[6] != 0xff) {
1621dc766f2bSmlelstv 		*pcritmax = ipmi_convert_sensor(&data[6], psensor);
1622dc766f2bSmlelstv 		*props |= prop_critmax;
1623dc766f2bSmlelstv 	}
1624404b3312Smlelstv 	if (valid & UC && data[5] != 0xff) {
1625dc766f2bSmlelstv 		*pcritmax = ipmi_convert_sensor(&data[5], psensor);
1626dc766f2bSmlelstv 		*props |= prop_critmax;
1627dc766f2bSmlelstv 	}
1628404b3312Smlelstv 	if (valid & UW && data[4] != 0xff) {
1629dc766f2bSmlelstv 		*pwarnmax = ipmi_convert_sensor(&data[4], psensor);
1630dc766f2bSmlelstv 		*props |= prop_warnmax;
1631dc766f2bSmlelstv 	}
1632404b3312Smlelstv 	if (valid & LN && data[3] != 0x00) {
1633dc766f2bSmlelstv 		*pcritmin = ipmi_convert_sensor(&data[3], psensor);
1634dc766f2bSmlelstv 		*props |= prop_critmin;
1635dc766f2bSmlelstv 	}
1636404b3312Smlelstv 	if (valid & LC && data[2] != 0x00) {
1637dc766f2bSmlelstv 		*pcritmin = ipmi_convert_sensor(&data[2], psensor);
1638dc766f2bSmlelstv 		*props |= prop_critmin;
1639dc766f2bSmlelstv 	}
1640404b3312Smlelstv 	if (valid & LW && data[1] != 0x00) {
1641dc766f2bSmlelstv 		*pwarnmin = ipmi_convert_sensor(&data[1], psensor);
1642dc766f2bSmlelstv 		*props |= prop_warnmin;
1643dc766f2bSmlelstv 	}
1644dc766f2bSmlelstv 	return;
1645dc766f2bSmlelstv }
1646dc766f2bSmlelstv 
1647dc766f2bSmlelstv static int
1648dc766f2bSmlelstv ipmi_sensor_status(struct ipmi_softc *sc, struct ipmi_sensor *psensor,
1649dc766f2bSmlelstv     envsys_data_t *edata, uint8_t *reading)
1650dc766f2bSmlelstv {
1651dc766f2bSmlelstv 	int	etype;
1652dc766f2bSmlelstv 
1653dc766f2bSmlelstv 	/* Get reading of sensor */
1654dc766f2bSmlelstv 	edata->value_cur = ipmi_convert_sensor(reading, psensor);
1655dc766f2bSmlelstv 
1656dc766f2bSmlelstv 	/* Return Sensor Status */
1657dc766f2bSmlelstv 	etype = (psensor->i_etype << 8) + psensor->i_stype;
1658dc766f2bSmlelstv 	switch (etype) {
1659dc766f2bSmlelstv 	case IPMI_SENSOR_TYPE_TEMP:
1660dc766f2bSmlelstv 	case IPMI_SENSOR_TYPE_VOLT:
1661dc766f2bSmlelstv 	case IPMI_SENSOR_TYPE_FAN:
1662dc766f2bSmlelstv 		if (psensor->i_props & PROP_CRITMAX &&
1663dc766f2bSmlelstv 		    edata->value_cur > psensor->i_limits.sel_critmax)
1664dc766f2bSmlelstv 			return ENVSYS_SCRITOVER;
1665dc766f2bSmlelstv 
1666dc766f2bSmlelstv 		if (psensor->i_props & PROP_WARNMAX &&
1667dc766f2bSmlelstv 		    edata->value_cur > psensor->i_limits.sel_warnmax)
1668dc766f2bSmlelstv 			return ENVSYS_SWARNOVER;
1669dc766f2bSmlelstv 
1670dc766f2bSmlelstv 		if (psensor->i_props & PROP_CRITMIN &&
1671dc766f2bSmlelstv 		    edata->value_cur < psensor->i_limits.sel_critmin)
1672dc766f2bSmlelstv 			return ENVSYS_SCRITUNDER;
1673dc766f2bSmlelstv 
16747e1880b3Snonaka 		if (psensor->i_props & PROP_WARNMIN &&
16757e1880b3Snonaka 		    edata->value_cur < psensor->i_limits.sel_warnmin)
16767e1880b3Snonaka 			return ENVSYS_SWARNUNDER;
16777e1880b3Snonaka 
1678dc766f2bSmlelstv 		break;
1679dc766f2bSmlelstv 
1680dc766f2bSmlelstv 	case IPMI_SENSOR_TYPE_INTRUSION:
1681dc766f2bSmlelstv 		edata->value_cur = (reading[2] & 1) ? 0 : 1;
1682dc766f2bSmlelstv 		if (reading[2] & 0x1)
1683dc766f2bSmlelstv 			return ENVSYS_SCRITICAL;
1684dc766f2bSmlelstv 		break;
1685dc766f2bSmlelstv 
1686dc766f2bSmlelstv 	case IPMI_SENSOR_TYPE_PWRSUPPLY:
1687dc766f2bSmlelstv 		/* Reading: 1 = present+powered, 0 = otherwise */
1688dc766f2bSmlelstv 		edata->value_cur = (reading[2] & 1) ? 0 : 1;
1689dc766f2bSmlelstv 		if (reading[2] & 0x10) {
1690dc766f2bSmlelstv 			/* XXX: Need envsys type for Power Supply types
1691dc766f2bSmlelstv 			 *   ok: power supply installed && powered
1692dc766f2bSmlelstv 			 * warn: power supply installed && !powered
1693dc766f2bSmlelstv 			 * crit: power supply !installed
1694dc766f2bSmlelstv 			 */
1695dc766f2bSmlelstv 			return ENVSYS_SCRITICAL;
1696dc766f2bSmlelstv 		}
1697dc766f2bSmlelstv 		if (reading[2] & 0x08) {
1698dc766f2bSmlelstv 			/* Power supply AC lost */
1699dc766f2bSmlelstv 			return ENVSYS_SWARNOVER;
1700dc766f2bSmlelstv 		}
1701dc766f2bSmlelstv 		break;
1702dc766f2bSmlelstv 	}
1703dc766f2bSmlelstv 
1704dc766f2bSmlelstv 	return ENVSYS_SVALID;
1705dc766f2bSmlelstv }
1706dc766f2bSmlelstv 
1707dc766f2bSmlelstv static int
1708dc766f2bSmlelstv read_sensor(struct ipmi_softc *sc, struct ipmi_sensor *psensor)
1709dc766f2bSmlelstv {
1710dc766f2bSmlelstv 	struct sdrtype1	*s1 = (struct sdrtype1 *) psensor->i_sdr;
1711dc766f2bSmlelstv 	uint8_t	data[8];
1712dc766f2bSmlelstv 	int		rxlen;
1713dc766f2bSmlelstv 	envsys_data_t *edata = &sc->sc_sensor[psensor->i_envnum];
1714dc766f2bSmlelstv 
1715dc766f2bSmlelstv 	memset(data, 0, sizeof(data));
1716dc766f2bSmlelstv 	data[0] = psensor->i_num;
1717dc766f2bSmlelstv 
1718dc766f2bSmlelstv 	mutex_enter(&sc->sc_cmd_mtx);
1719dc766f2bSmlelstv 	if (ipmi_sendcmd(sc, s1->owner_id, s1->owner_lun, SE_NETFN,
1720dc766f2bSmlelstv 	    SE_GET_SENSOR_READING, 1, data))
1721dc766f2bSmlelstv 		goto err;
1722dc766f2bSmlelstv 
1723dc766f2bSmlelstv 	if (ipmi_recvcmd(sc, sizeof(data), &rxlen, data))
1724dc766f2bSmlelstv 		goto err;
1725dc766f2bSmlelstv 	mutex_exit(&sc->sc_cmd_mtx);
1726dc766f2bSmlelstv 
1727dc766f2bSmlelstv 	dbg_printf(10, "m=%u, m_tolerance=%u, b=%u, b_accuracy=%u, "
1728dc766f2bSmlelstv 	    "rbexp=%u, linear=%d\n", s1->m, s1->m_tolerance, s1->b,
1729dc766f2bSmlelstv 	    s1->b_accuracy, s1->rbexp, s1->linear);
1730dc766f2bSmlelstv 	dbg_printf(10, "values=%#.2x %#.2x %#.2x %#.2x %s\n",
1731dc766f2bSmlelstv 	    data[0],data[1],data[2],data[3], edata->desc);
1732dc766f2bSmlelstv 	if (IPMI_INVALID_SENSOR_P(data[1])) {
1733dc766f2bSmlelstv 		/* Check if sensor is valid */
1734dc766f2bSmlelstv 		edata->state = ENVSYS_SINVALID;
1735dc766f2bSmlelstv 	} else {
1736dc766f2bSmlelstv 		edata->state = ipmi_sensor_status(sc, psensor, edata, data);
1737dc766f2bSmlelstv 	}
1738dc766f2bSmlelstv 	return 0;
1739dc766f2bSmlelstv err:
1740dc766f2bSmlelstv 	mutex_exit(&sc->sc_cmd_mtx);
1741dc766f2bSmlelstv 	return -1;
1742dc766f2bSmlelstv }
1743dc766f2bSmlelstv 
1744dc766f2bSmlelstv static int
1745dc766f2bSmlelstv ipmi_sensor_type(int type, int ext_type, int entity)
1746dc766f2bSmlelstv {
1747dc766f2bSmlelstv 	switch (ext_type << 8L | type) {
1748dc766f2bSmlelstv 	case IPMI_SENSOR_TYPE_TEMP:
1749dc766f2bSmlelstv 		return ENVSYS_STEMP;
1750dc766f2bSmlelstv 
1751dc766f2bSmlelstv 	case IPMI_SENSOR_TYPE_VOLT:
1752dc766f2bSmlelstv 		return ENVSYS_SVOLTS_DC;
1753dc766f2bSmlelstv 
1754dc766f2bSmlelstv 	case IPMI_SENSOR_TYPE_FAN:
1755dc766f2bSmlelstv 		return ENVSYS_SFANRPM;
1756dc766f2bSmlelstv 
1757dc766f2bSmlelstv 	case IPMI_SENSOR_TYPE_PWRSUPPLY:
1758dc766f2bSmlelstv 		if (entity == IPMI_ENTITY_PWRSUPPLY)
1759dc766f2bSmlelstv 			return ENVSYS_INDICATOR;
1760dc766f2bSmlelstv 		break;
1761dc766f2bSmlelstv 
1762dc766f2bSmlelstv 	case IPMI_SENSOR_TYPE_INTRUSION:
1763dc766f2bSmlelstv 		return ENVSYS_INDICATOR;
1764dc766f2bSmlelstv 	}
1765dc766f2bSmlelstv 
1766dc766f2bSmlelstv 	return -1;
1767dc766f2bSmlelstv }
1768dc766f2bSmlelstv 
1769dc766f2bSmlelstv /* Add Sensor to BSD Sysctl interface */
1770dc766f2bSmlelstv static int
1771dc766f2bSmlelstv add_sdr_sensor(struct ipmi_softc *sc, uint8_t *psdr)
1772dc766f2bSmlelstv {
1773dc766f2bSmlelstv 	int			rc;
1774dc766f2bSmlelstv 	struct sdrtype1		*s1 = (struct sdrtype1 *)psdr;
1775dc766f2bSmlelstv 	struct sdrtype2		*s2 = (struct sdrtype2 *)psdr;
1776dc766f2bSmlelstv 	char			name[64];
1777dc766f2bSmlelstv 
1778dc766f2bSmlelstv 	switch (s1->sdrhdr.record_type) {
1779dc766f2bSmlelstv 	case IPMI_SDR_TYPEFULL:
1780dc766f2bSmlelstv 		ipmi_sensor_name(name, sizeof(name), s1->typelen, s1->name);
1781dc766f2bSmlelstv 		rc = add_child_sensors(sc, psdr, 1, s1->sensor_num,
1782dc766f2bSmlelstv 		    s1->sensor_type, s1->event_code, 0, s1->entity_id, name);
1783dc766f2bSmlelstv 		break;
1784dc766f2bSmlelstv 
1785dc766f2bSmlelstv 	case IPMI_SDR_TYPECOMPACT:
1786dc766f2bSmlelstv 		ipmi_sensor_name(name, sizeof(name), s2->typelen, s2->name);
1787dc766f2bSmlelstv 		rc = add_child_sensors(sc, psdr, s2->share1 & 0xF,
1788dc766f2bSmlelstv 		    s2->sensor_num, s2->sensor_type, s2->event_code,
1789dc766f2bSmlelstv 		    s2->share2 & 0x7F, s2->entity_id, name);
1790dc766f2bSmlelstv 		break;
1791dc766f2bSmlelstv 
1792dc766f2bSmlelstv 	default:
1793dc766f2bSmlelstv 		return 0;
1794dc766f2bSmlelstv 	}
1795dc766f2bSmlelstv 
1796dc766f2bSmlelstv 	return rc;
1797dc766f2bSmlelstv }
1798dc766f2bSmlelstv 
1799dc766f2bSmlelstv static int
1800dc766f2bSmlelstv ipmi_is_dupname(char *name)
1801dc766f2bSmlelstv {
1802dc766f2bSmlelstv 	struct ipmi_sensor *ipmi_s;
1803dc766f2bSmlelstv 
1804dc766f2bSmlelstv 	SLIST_FOREACH(ipmi_s, &ipmi_sensor_list, i_list) {
1805dc766f2bSmlelstv 		if (strcmp(ipmi_s->i_envdesc, name) == 0) {
1806dc766f2bSmlelstv 			return 1;
1807dc766f2bSmlelstv 		}
1808dc766f2bSmlelstv 	}
1809dc766f2bSmlelstv 	return 0;
1810dc766f2bSmlelstv }
1811dc766f2bSmlelstv 
1812dc766f2bSmlelstv static int
1813dc766f2bSmlelstv add_child_sensors(struct ipmi_softc *sc, uint8_t *psdr, int count,
1814dc766f2bSmlelstv     int sensor_num, int sensor_type, int ext_type, int sensor_base,
1815dc766f2bSmlelstv     int entity, const char *name)
1816dc766f2bSmlelstv {
1817dc766f2bSmlelstv 	int			typ, idx, dupcnt, c;
1818dc766f2bSmlelstv 	char			*e;
1819dc766f2bSmlelstv 	struct ipmi_sensor	*psensor;
1820dc766f2bSmlelstv 	struct sdrtype1		*s1 = (struct sdrtype1 *)psdr;
1821dc766f2bSmlelstv 
1822dc766f2bSmlelstv 	typ = ipmi_sensor_type(sensor_type, ext_type, entity);
1823dc766f2bSmlelstv 	if (typ == -1) {
1824dc766f2bSmlelstv 		dbg_printf(5, "Unknown sensor type:%#.2x et:%#.2x sn:%#.2x "
1825dc766f2bSmlelstv 		    "name:%s\n", sensor_type, ext_type, sensor_num, name);
1826dc766f2bSmlelstv 		return 0;
1827dc766f2bSmlelstv 	}
1828dc766f2bSmlelstv 	dupcnt = 0;
1829dc766f2bSmlelstv 	sc->sc_nsensors += count;
1830dc766f2bSmlelstv 	for (idx = 0; idx < count; idx++) {
1831dc766f2bSmlelstv 		psensor = malloc(sizeof(struct ipmi_sensor), M_DEVBUF,
1832dc766f2bSmlelstv 		    M_WAITOK);
1833dc766f2bSmlelstv 		if (psensor == NULL)
1834dc766f2bSmlelstv 			break;
1835dc766f2bSmlelstv 
1836dc766f2bSmlelstv 		memset(psensor, 0, sizeof(struct ipmi_sensor));
1837dc766f2bSmlelstv 
1838dc766f2bSmlelstv 		/* Initialize BSD Sensor info */
1839dc766f2bSmlelstv 		psensor->i_sdr = psdr;
1840dc766f2bSmlelstv 		psensor->i_num = sensor_num + idx;
1841dc766f2bSmlelstv 		psensor->i_stype = sensor_type;
1842dc766f2bSmlelstv 		psensor->i_etype = ext_type;
1843dc766f2bSmlelstv 		psensor->i_envtype = typ;
1844dc766f2bSmlelstv 		if (count > 1)
1845dc766f2bSmlelstv 			snprintf(psensor->i_envdesc,
1846dc766f2bSmlelstv 			    sizeof(psensor->i_envdesc),
1847dc766f2bSmlelstv 			    "%s - %d", name, sensor_base + idx);
1848dc766f2bSmlelstv 		else
1849dc766f2bSmlelstv 			strlcpy(psensor->i_envdesc, name,
1850dc766f2bSmlelstv 			    sizeof(psensor->i_envdesc));
1851dc766f2bSmlelstv 
1852dc766f2bSmlelstv 		/*
1853dc766f2bSmlelstv 		 * Check for duplicates.  If there are duplicates,
1854dc766f2bSmlelstv 		 * make sure there is space in the name (if not,
1855dc766f2bSmlelstv 		 * truncate to make space) for a count (1-99) to
1856dc766f2bSmlelstv 		 * add to make the name unique.  If we run the
1857dc766f2bSmlelstv 		 * counter out, just accept the duplicate (@name99)
1858dc766f2bSmlelstv 		 * for now.
1859dc766f2bSmlelstv 		 */
1860dc766f2bSmlelstv 		if (ipmi_is_dupname(psensor->i_envdesc)) {
1861dc766f2bSmlelstv 			if (strlen(psensor->i_envdesc) >=
1862dc766f2bSmlelstv 			    sizeof(psensor->i_envdesc) - 3) {
1863dc766f2bSmlelstv 				e = psensor->i_envdesc +
1864dc766f2bSmlelstv 				    sizeof(psensor->i_envdesc) - 3;
1865dc766f2bSmlelstv 			} else {
1866dc766f2bSmlelstv 				e = psensor->i_envdesc +
1867dc766f2bSmlelstv 				    strlen(psensor->i_envdesc);
1868dc766f2bSmlelstv 			}
1869dc766f2bSmlelstv 			c = psensor->i_envdesc +
1870dc766f2bSmlelstv 			    sizeof(psensor->i_envdesc) - e;
1871dc766f2bSmlelstv 			do {
1872dc766f2bSmlelstv 				dupcnt++;
1873dc766f2bSmlelstv 				snprintf(e, c, "%d", dupcnt);
1874dc766f2bSmlelstv 			} while (dupcnt < 100 &&
1875dc766f2bSmlelstv 			         ipmi_is_dupname(psensor->i_envdesc));
1876dc766f2bSmlelstv 		}
1877dc766f2bSmlelstv 
1878dc766f2bSmlelstv 		dbg_printf(5, "%s: %#.4x %#.2x:%d ent:%#.2x:%#.2x %s\n",
1879dc766f2bSmlelstv 		    __func__,
1880dc766f2bSmlelstv 		    s1->sdrhdr.record_id, s1->sensor_type,
1881dc766f2bSmlelstv 		    typ, s1->entity_id, s1->entity_instance,
1882dc766f2bSmlelstv 		    psensor->i_envdesc);
1883dc766f2bSmlelstv 		SLIST_INSERT_HEAD(&ipmi_sensor_list, psensor, i_list);
1884dc766f2bSmlelstv 	}
1885dc766f2bSmlelstv 
1886dc766f2bSmlelstv 	return 1;
1887dc766f2bSmlelstv }
1888dc766f2bSmlelstv 
1889dc766f2bSmlelstv #if 0
1890dc766f2bSmlelstv /* Interrupt handler */
1891dc766f2bSmlelstv static int
1892dc766f2bSmlelstv ipmi_intr(void *arg)
1893dc766f2bSmlelstv {
1894dc766f2bSmlelstv 	struct ipmi_softc	*sc = (struct ipmi_softc *)arg;
1895dc766f2bSmlelstv 	int			v;
1896dc766f2bSmlelstv 
1897dc766f2bSmlelstv 	v = bmc_read(sc, _KCS_STATUS_REGISTER);
1898dc766f2bSmlelstv 	if (v & KCS_OBF)
1899dc766f2bSmlelstv 		++ipmi_nintr;
1900dc766f2bSmlelstv 
1901dc766f2bSmlelstv 	return 0;
1902dc766f2bSmlelstv }
1903dc766f2bSmlelstv #endif
1904dc766f2bSmlelstv 
1905dc766f2bSmlelstv /* Handle IPMI Timer - reread sensor values */
1906dc766f2bSmlelstv static void
1907dc766f2bSmlelstv ipmi_refresh_sensors(struct ipmi_softc *sc)
1908dc766f2bSmlelstv {
1909dc766f2bSmlelstv 
1910dc766f2bSmlelstv 	if (SLIST_EMPTY(&ipmi_sensor_list))
1911dc766f2bSmlelstv 		return;
1912dc766f2bSmlelstv 
1913dc766f2bSmlelstv 	sc->current_sensor = SLIST_NEXT(sc->current_sensor, i_list);
1914dc766f2bSmlelstv 	if (sc->current_sensor == NULL)
1915dc766f2bSmlelstv 		sc->current_sensor = SLIST_FIRST(&ipmi_sensor_list);
1916dc766f2bSmlelstv 
1917dc766f2bSmlelstv 	if (read_sensor(sc, sc->current_sensor)) {
1918dc766f2bSmlelstv 		dbg_printf(1, "%s: error reading\n", __func__);
1919dc766f2bSmlelstv 	}
1920dc766f2bSmlelstv }
1921dc766f2bSmlelstv 
1922dc766f2bSmlelstv static int
1923dc766f2bSmlelstv ipmi_map_regs(struct ipmi_softc *sc, struct ipmi_attach_args *ia)
1924dc766f2bSmlelstv {
1925dc766f2bSmlelstv 	int error;
1926dc766f2bSmlelstv 
1927dc766f2bSmlelstv 	sc->sc_if = ipmi_get_if(ia->iaa_if_type);
1928dc766f2bSmlelstv 	if (sc->sc_if == NULL)
1929dc766f2bSmlelstv 		return -1;
1930dc766f2bSmlelstv 
1931dc766f2bSmlelstv 	if (ia->iaa_if_iotype == 'i')
1932dc766f2bSmlelstv 		sc->sc_iot = ia->iaa_iot;
1933dc766f2bSmlelstv 	else
1934dc766f2bSmlelstv 		sc->sc_iot = ia->iaa_memt;
1935dc766f2bSmlelstv 
1936dc766f2bSmlelstv 	sc->sc_if_rev = ia->iaa_if_rev;
1937dc766f2bSmlelstv 	sc->sc_if_iospacing = ia->iaa_if_iospacing;
1938dc766f2bSmlelstv 	if ((error = bus_space_map(sc->sc_iot, ia->iaa_if_iobase,
1939dc766f2bSmlelstv 	    sc->sc_if->nregs * sc->sc_if_iospacing, 0, &sc->sc_ioh)) != 0) {
1940dc766f2bSmlelstv 		const char *xname = sc->sc_dev ? device_xname(sc->sc_dev) :
1941dc766f2bSmlelstv 		    "ipmi0";
1942dc766f2bSmlelstv 		aprint_error("%s: %s:bus_space_map(..., %" PRIx64 ", %x"
1943dc766f2bSmlelstv 		    ", 0, %p) type %c failed %d\n",
1944e47e31c9Smlelstv 		    xname, __func__, (uint64_t)ia->iaa_if_iobase,
1945dc766f2bSmlelstv 		    sc->sc_if->nregs * sc->sc_if_iospacing, &sc->sc_ioh,
1946dc766f2bSmlelstv 		    ia->iaa_if_iotype, error);
1947dc766f2bSmlelstv 		return -1;
1948dc766f2bSmlelstv 	}
1949dc766f2bSmlelstv #if 0
1950dc766f2bSmlelstv 	if (iaa->if_if_irq != -1)
1951dc766f2bSmlelstv 		sc->ih = isa_intr_establish(-1, iaa->if_if_irq,
1952dc766f2bSmlelstv 		    iaa->if_irqlvl, IPL_BIO, ipmi_intr, sc,
1953dc766f2bSmlelstv 		    device_xname(sc->sc_dev);
1954dc766f2bSmlelstv #endif
1955dc766f2bSmlelstv 	return 0;
1956dc766f2bSmlelstv }
1957dc766f2bSmlelstv 
1958dc766f2bSmlelstv static void
1959dc766f2bSmlelstv ipmi_unmap_regs(struct ipmi_softc *sc)
1960dc766f2bSmlelstv {
1961dc766f2bSmlelstv 	bus_space_unmap(sc->sc_iot, sc->sc_ioh,
1962dc766f2bSmlelstv 	    sc->sc_if->nregs * sc->sc_if_iospacing);
1963dc766f2bSmlelstv }
1964dc766f2bSmlelstv 
1965dc766f2bSmlelstv static int
1966dc766f2bSmlelstv ipmi_match(device_t parent, cfdata_t cf, void *aux)
1967dc766f2bSmlelstv {
1968dc766f2bSmlelstv 	struct ipmi_softc sc;
1969dc766f2bSmlelstv 	struct ipmi_attach_args *ia = aux;
1970dc766f2bSmlelstv 	int			rv = 0;
1971dc766f2bSmlelstv 
1972dc766f2bSmlelstv 	memset(&sc, 0, sizeof(sc));
1973dc766f2bSmlelstv 
1974dc766f2bSmlelstv 	/* Map registers */
1975dc766f2bSmlelstv 	if (ipmi_map_regs(&sc, ia) != 0)
1976dc766f2bSmlelstv 		return 0;
1977dc766f2bSmlelstv 
1978dc766f2bSmlelstv 	sc.sc_if->probe(&sc);
1979dc766f2bSmlelstv 
1980dc766f2bSmlelstv 	mutex_init(&sc.sc_cmd_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
1981dc766f2bSmlelstv 
1982f95aa101Smlelstv 	if (ipmi_get_device_id(&sc, NULL) == 0)
1983f95aa101Smlelstv 		rv = 1;
1984f95aa101Smlelstv 
1985dc766f2bSmlelstv 	mutex_destroy(&sc.sc_cmd_mtx);
1986dc766f2bSmlelstv 	ipmi_unmap_regs(&sc);
1987dc766f2bSmlelstv 
1988dc766f2bSmlelstv 	return rv;
1989dc766f2bSmlelstv }
1990dc766f2bSmlelstv 
1991dc766f2bSmlelstv static void
1992dc766f2bSmlelstv ipmi_thread(void *cookie)
1993dc766f2bSmlelstv {
1994dc766f2bSmlelstv 	device_t		self = cookie;
1995dc766f2bSmlelstv 	struct ipmi_softc	*sc = device_private(self);
1996dc766f2bSmlelstv 	struct ipmi_attach_args *ia = &sc->sc_ia;
1997dc766f2bSmlelstv 	uint16_t		rec;
1998dc766f2bSmlelstv 	struct ipmi_sensor *ipmi_s;
1999f95aa101Smlelstv 	struct ipmi_device_id	id;
2000dc766f2bSmlelstv 	int i;
2001dc766f2bSmlelstv 
2002dc766f2bSmlelstv 	sc->sc_thread_running = true;
2003dc766f2bSmlelstv 
2004dc766f2bSmlelstv 	/* setup ticker */
2005dc766f2bSmlelstv 	sc->sc_max_retries = hz * 90; /* 90 seconds max */
2006dc766f2bSmlelstv 
2007dc766f2bSmlelstv 	/* Map registers */
2008dc766f2bSmlelstv 	ipmi_map_regs(sc, ia);
2009dc766f2bSmlelstv 
2010b851421cSriastradh 	/* Setup Watchdog timer */
2011b851421cSriastradh 	sc->sc_wdog.smw_name = device_xname(sc->sc_dev);
2012b851421cSriastradh 	sc->sc_wdog.smw_cookie = sc;
2013b851421cSriastradh 	sc->sc_wdog.smw_setmode = ipmi_watchdog_setmode;
2014b851421cSriastradh 	sc->sc_wdog.smw_tickle = ipmi_watchdog_tickle;
2015b851421cSriastradh 	sysmon_wdog_register(&sc->sc_wdog);
2016b851421cSriastradh 
2017b851421cSriastradh 	/* Set up a power handler so we can possibly sleep */
2018b851421cSriastradh 	if (!pmf_device_register(self, ipmi_suspend, NULL))
2019b851421cSriastradh                 aprint_error_dev(self, "couldn't establish a power handler\n");
2020b851421cSriastradh 
2021b851421cSriastradh 	/*
2022b851421cSriastradh 	 * Allow boot to proceed -- we'll do the rest asynchronously
2023b851421cSriastradh 	 * since it requires talking to the device.
2024b851421cSriastradh 	 */
2025b851421cSriastradh 	config_pending_decr(self);
2026b851421cSriastradh 
2027f95aa101Smlelstv 	memset(&id, 0, sizeof(id));
2028f95aa101Smlelstv 	if (ipmi_get_device_id(sc, &id))
2029f95aa101Smlelstv 		aprint_error_dev(self, "Failed to re-query device ID\n");
2030f95aa101Smlelstv 
2031dc766f2bSmlelstv 	/* Scan SDRs, add sensors to list */
2032dc766f2bSmlelstv 	for (rec = 0; rec != 0xFFFF;)
2033dc766f2bSmlelstv 		if (get_sdr(sc, rec, &rec))
2034dc766f2bSmlelstv 			break;
2035dc766f2bSmlelstv 
2036dc766f2bSmlelstv 	/* allocate and fill sensor arrays */
203776313cf4Sriastradh 	sc->sc_sensor = malloc(sizeof(sc->sc_sensor[0]) * sc->sc_nsensors,
2038dc766f2bSmlelstv 	    M_DEVBUF, M_WAITOK | M_ZERO);
2039dc766f2bSmlelstv 
2040dc766f2bSmlelstv 	sc->sc_envsys = sysmon_envsys_create();
2041dc766f2bSmlelstv 	sc->sc_envsys->sme_cookie = sc;
2042dc766f2bSmlelstv 	sc->sc_envsys->sme_get_limits = ipmi_get_limits;
2043dc766f2bSmlelstv 	sc->sc_envsys->sme_set_limits = ipmi_set_limits;
2044dc766f2bSmlelstv 
2045dc766f2bSmlelstv 	i = 0;
2046dc766f2bSmlelstv 	SLIST_FOREACH(ipmi_s, &ipmi_sensor_list, i_list) {
2047dc766f2bSmlelstv 		ipmi_s->i_props = 0;
2048dc766f2bSmlelstv 		ipmi_s->i_envnum = -1;
2049dc766f2bSmlelstv 		sc->sc_sensor[i].units = ipmi_s->i_envtype;
2050dc766f2bSmlelstv 		sc->sc_sensor[i].state = ENVSYS_SINVALID;
2051dc766f2bSmlelstv 		sc->sc_sensor[i].flags |= ENVSYS_FHAS_ENTROPY;
2052dc766f2bSmlelstv 		/*
2053dc766f2bSmlelstv 		 * Monitor threshold limits in the sensors.
2054dc766f2bSmlelstv 		 */
2055dc766f2bSmlelstv 		switch (sc->sc_sensor[i].units) {
2056dc766f2bSmlelstv 		case ENVSYS_STEMP:
2057dc766f2bSmlelstv 		case ENVSYS_SVOLTS_DC:
2058dc766f2bSmlelstv 		case ENVSYS_SFANRPM:
2059dc766f2bSmlelstv 			sc->sc_sensor[i].flags |= ENVSYS_FMONLIMITS;
2060dc766f2bSmlelstv 			break;
2061dc766f2bSmlelstv 		default:
2062dc766f2bSmlelstv 			sc->sc_sensor[i].flags |= ENVSYS_FMONCRITICAL;
2063dc766f2bSmlelstv 		}
2064dc766f2bSmlelstv 		(void)strlcpy(sc->sc_sensor[i].desc, ipmi_s->i_envdesc,
2065dc766f2bSmlelstv 		    sizeof(sc->sc_sensor[i].desc));
2066dc766f2bSmlelstv 		++i;
2067dc766f2bSmlelstv 
2068dc766f2bSmlelstv 		if (sysmon_envsys_sensor_attach(sc->sc_envsys,
2069dc766f2bSmlelstv 						&sc->sc_sensor[i-1]))
2070dc766f2bSmlelstv 			continue;
2071dc766f2bSmlelstv 
2072dc766f2bSmlelstv 		/* get reference number from envsys */
2073dc766f2bSmlelstv 		ipmi_s->i_envnum = sc->sc_sensor[i-1].sensor;
2074dc766f2bSmlelstv 	}
2075dc766f2bSmlelstv 
2076dc766f2bSmlelstv 	sc->sc_envsys->sme_name = device_xname(sc->sc_dev);
2077dc766f2bSmlelstv 	sc->sc_envsys->sme_flags = SME_DISABLE_REFRESH;
2078dc766f2bSmlelstv 
2079dc766f2bSmlelstv 	if (sysmon_envsys_register(sc->sc_envsys)) {
2080dc766f2bSmlelstv 		aprint_error_dev(self, "unable to register with sysmon\n");
2081dc766f2bSmlelstv 		sysmon_envsys_destroy(sc->sc_envsys);
2082f219451cSmlelstv 		sc->sc_envsys = NULL;
2083dc766f2bSmlelstv 	}
2084dc766f2bSmlelstv 
2085dc766f2bSmlelstv 	/* initialize sensor list for thread */
2086dc766f2bSmlelstv 	if (!SLIST_EMPTY(&ipmi_sensor_list))
2087dc766f2bSmlelstv 		sc->current_sensor = SLIST_FIRST(&ipmi_sensor_list);
2088dc766f2bSmlelstv 
2089dc766f2bSmlelstv 	aprint_verbose_dev(self, "version %d.%d interface %s %sbase "
2090dc766f2bSmlelstv 	    "0x%" PRIx64 "/%#x spacing %d\n",
2091dc766f2bSmlelstv 	    ia->iaa_if_rev >> 4, ia->iaa_if_rev & 0xF, sc->sc_if->name,
2092e47e31c9Smlelstv 	    ia->iaa_if_iotype == 'i' ? "io" : "mem",
2093e47e31c9Smlelstv 	    (uint64_t)ia->iaa_if_iobase,
2094dc766f2bSmlelstv 	    ia->iaa_if_iospacing * sc->sc_if->nregs, ia->iaa_if_iospacing);
2095dc766f2bSmlelstv 	if (ia->iaa_if_irq != -1)
2096dc766f2bSmlelstv 		aprint_verbose_dev(self, " irq %d\n", ia->iaa_if_irq);
2097dc766f2bSmlelstv 
2098f95aa101Smlelstv 	if (id.deviceid != 0) {
2099f95aa101Smlelstv 		aprint_normal_dev(self, "ID %u.%u IPMI %x.%x%s%s\n",
2100f95aa101Smlelstv 			id.deviceid, (id.revision & 0xf),
2101f95aa101Smlelstv 			(id.version & 0xf), (id.version >> 4) & 0xf,
2102f95aa101Smlelstv 			(id.fwrev1 & 0x80) ? " Initializing" : " Available",
2103f95aa101Smlelstv 			(id.revision & 0x80) ? " +SDRs" : "");
2104f95aa101Smlelstv 		if (id.additional != 0)
2105f95aa101Smlelstv 			aprint_verbose_dev(self, "Additional%s%s%s%s%s%s%s%s\n",
2106f95aa101Smlelstv 				(id.additional & 0x80) ? " Chassis" : "",
2107f95aa101Smlelstv 				(id.additional & 0x40) ? " Bridge" : "",
2108f95aa101Smlelstv 				(id.additional & 0x20) ? " IPMBGen" : "",
2109f95aa101Smlelstv 				(id.additional & 0x10) ? " IPMBRcv" : "",
2110f95aa101Smlelstv 				(id.additional & 0x08) ? " FRU" : "",
2111f95aa101Smlelstv 				(id.additional & 0x04) ? " SEL" : "",
2112f95aa101Smlelstv 				(id.additional & 0x02) ? " SDR" : "",
2113f95aa101Smlelstv 				(id.additional & 0x01) ? " Sensor" : "");
2114f95aa101Smlelstv 		aprint_verbose_dev(self, "Manufacturer %05x Product %04x\n",
2115f95aa101Smlelstv 			(id.manufacturer[2] & 0xf) << 16
2116f95aa101Smlelstv 			    | id.manufacturer[1] << 8
2117f95aa101Smlelstv 			    | id.manufacturer[0],
2118f95aa101Smlelstv 			id.product[1] << 8
2119f95aa101Smlelstv 			    | id.manufacturer[0]);
2120f95aa101Smlelstv 		aprint_verbose_dev(self, "Firmware %u.%x\n",
2121f95aa101Smlelstv 			(id.fwrev1 & 0x7f), id.fwrev2);
2122f95aa101Smlelstv 	}
2123f95aa101Smlelstv 
2124dc766f2bSmlelstv 	/* setup flag to exclude iic */
2125dc766f2bSmlelstv 	ipmi_enabled = 1;
2126dc766f2bSmlelstv 
2127dc766f2bSmlelstv 	mutex_enter(&sc->sc_poll_mtx);
2128b851421cSriastradh 	sc->sc_thread_ready = true;
2129b851421cSriastradh 	cv_broadcast(&sc->sc_mode_cv);
2130dc766f2bSmlelstv 	while (sc->sc_thread_running) {
2131b1f23fc9Smlelstv 		while (sc->sc_mode == IPMI_MODE_COMMAND)
2132b1f23fc9Smlelstv 			cv_wait(&sc->sc_mode_cv, &sc->sc_poll_mtx);
2133b1f23fc9Smlelstv 		sc->sc_mode = IPMI_MODE_ENVSYS;
2134b1f23fc9Smlelstv 
2135dc766f2bSmlelstv 		if (sc->sc_tickle_due) {
2136dc766f2bSmlelstv 			ipmi_dotickle(sc);
2137dc766f2bSmlelstv 			sc->sc_tickle_due = false;
2138dc766f2bSmlelstv 		}
2139b1f23fc9Smlelstv 		ipmi_refresh_sensors(sc);
2140b1f23fc9Smlelstv 
2141b1f23fc9Smlelstv 		sc->sc_mode = IPMI_MODE_IDLE;
2142b1f23fc9Smlelstv 		cv_broadcast(&sc->sc_mode_cv);
2143b1f23fc9Smlelstv 		cv_timedwait(&sc->sc_poll_cv, &sc->sc_poll_mtx,
2144b1f23fc9Smlelstv 		    SENSOR_REFRESH_RATE);
2145dc766f2bSmlelstv 	}
2146dc766f2bSmlelstv 	mutex_exit(&sc->sc_poll_mtx);
2147dc766f2bSmlelstv 	kthread_exit(0);
2148dc766f2bSmlelstv }
2149dc766f2bSmlelstv 
2150dc766f2bSmlelstv static void
2151dc766f2bSmlelstv ipmi_attach(device_t parent, device_t self, void *aux)
2152dc766f2bSmlelstv {
2153dc766f2bSmlelstv 	struct ipmi_softc	*sc = device_private(self);
2154dc766f2bSmlelstv 
2155dc766f2bSmlelstv 	sc->sc_ia = *(struct ipmi_attach_args *)aux;
2156dc766f2bSmlelstv 	sc->sc_dev = self;
2157dc766f2bSmlelstv 	aprint_naive("\n");
2158dc766f2bSmlelstv 	aprint_normal("\n");
2159dc766f2bSmlelstv 
2160dc766f2bSmlelstv 	/* lock around read_sensor so that no one messes with the bmc regs */
2161dc766f2bSmlelstv 	mutex_init(&sc->sc_cmd_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
2162dc766f2bSmlelstv 
2163dc766f2bSmlelstv 	mutex_init(&sc->sc_poll_mtx, MUTEX_DEFAULT, IPL_SOFTCLOCK);
2164dc766f2bSmlelstv 	cv_init(&sc->sc_poll_cv, "ipmipoll");
2165b1f23fc9Smlelstv 	cv_init(&sc->sc_mode_cv, "ipmimode");
2166dc766f2bSmlelstv 
216776313cf4Sriastradh 	if (kthread_create(PRI_NONE, KTHREAD_MUSTJOIN, NULL, ipmi_thread, self,
2168dc766f2bSmlelstv 	    &sc->sc_kthread, "%s", device_xname(self)) != 0) {
2169dc766f2bSmlelstv 		aprint_error_dev(self, "unable to create thread, disabled\n");
2170dc766f2bSmlelstv 	} else
21710052d108Sriastradh 		config_pending_incr(self);
2172dc766f2bSmlelstv }
2173dc766f2bSmlelstv 
2174dc766f2bSmlelstv static int
2175dc766f2bSmlelstv ipmi_detach(device_t self, int flags)
2176dc766f2bSmlelstv {
2177dc766f2bSmlelstv 	struct ipmi_sensor *i;
2178dc766f2bSmlelstv 	int rc;
2179dc766f2bSmlelstv 	struct ipmi_softc *sc = device_private(self);
2180dc766f2bSmlelstv 
2181dc766f2bSmlelstv 	mutex_enter(&sc->sc_poll_mtx);
2182dc766f2bSmlelstv 	sc->sc_thread_running = false;
2183dc766f2bSmlelstv 	cv_signal(&sc->sc_poll_cv);
2184dc766f2bSmlelstv 	mutex_exit(&sc->sc_poll_mtx);
218576313cf4Sriastradh 	if (sc->sc_kthread)
218676313cf4Sriastradh 		(void)kthread_join(sc->sc_kthread);
2187dc766f2bSmlelstv 
2188dc766f2bSmlelstv 	if ((rc = sysmon_wdog_unregister(&sc->sc_wdog)) != 0) {
2189dc766f2bSmlelstv 		if (rc == ERESTART)
2190dc766f2bSmlelstv 			rc = EINTR;
2191dc766f2bSmlelstv 		return rc;
2192dc766f2bSmlelstv 	}
2193dc766f2bSmlelstv 
2194dc766f2bSmlelstv 	/* cancel any pending countdown */
2195dc766f2bSmlelstv 	sc->sc_wdog.smw_mode &= ~WDOG_MODE_MASK;
2196dc766f2bSmlelstv 	sc->sc_wdog.smw_mode |= WDOG_MODE_DISARMED;
2197dc766f2bSmlelstv 	sc->sc_wdog.smw_period = WDOG_PERIOD_DEFAULT;
2198dc766f2bSmlelstv 
2199dc766f2bSmlelstv 	if ((rc = ipmi_watchdog_setmode(&sc->sc_wdog)) != 0)
2200dc766f2bSmlelstv 		return rc;
2201dc766f2bSmlelstv 
2202dc766f2bSmlelstv 	ipmi_enabled = 0;
2203dc766f2bSmlelstv 
2204dc766f2bSmlelstv 	if (sc->sc_envsys != NULL) {
2205dc766f2bSmlelstv 		/* _unregister also destroys */
2206dc766f2bSmlelstv 		sysmon_envsys_unregister(sc->sc_envsys);
2207dc766f2bSmlelstv 		sc->sc_envsys = NULL;
2208dc766f2bSmlelstv 	}
2209dc766f2bSmlelstv 
2210dc766f2bSmlelstv 	while ((i = SLIST_FIRST(&ipmi_sensor_list)) != NULL) {
2211dc766f2bSmlelstv 		SLIST_REMOVE_HEAD(&ipmi_sensor_list, i_list);
2212dc766f2bSmlelstv 		free(i, M_DEVBUF);
2213dc766f2bSmlelstv 	}
2214dc766f2bSmlelstv 
2215dc766f2bSmlelstv 	if (sc->sc_sensor != NULL) {
2216dc766f2bSmlelstv 		free(sc->sc_sensor, M_DEVBUF);
2217dc766f2bSmlelstv 		sc->sc_sensor = NULL;
2218dc766f2bSmlelstv 	}
2219dc766f2bSmlelstv 
2220dc766f2bSmlelstv 	ipmi_unmap_regs(sc);
2221dc766f2bSmlelstv 
2222b1f23fc9Smlelstv 	cv_destroy(&sc->sc_mode_cv);
2223dc766f2bSmlelstv 	cv_destroy(&sc->sc_poll_cv);
2224dc766f2bSmlelstv 	mutex_destroy(&sc->sc_poll_mtx);
2225dc766f2bSmlelstv 	mutex_destroy(&sc->sc_cmd_mtx);
2226dc766f2bSmlelstv 
2227dc766f2bSmlelstv 	return 0;
2228dc766f2bSmlelstv }
2229dc766f2bSmlelstv 
2230dc766f2bSmlelstv static int
2231f95aa101Smlelstv ipmi_get_device_id(struct ipmi_softc *sc, struct ipmi_device_id *res)
2232f95aa101Smlelstv {
2233f95aa101Smlelstv 	uint8_t		buf[32];
2234f95aa101Smlelstv 	int		len;
2235f95aa101Smlelstv 	int		rc;
2236f95aa101Smlelstv 
2237f95aa101Smlelstv 	mutex_enter(&sc->sc_cmd_mtx);
2238f95aa101Smlelstv 	/* Identify BMC device early to detect lying bios */
2239f95aa101Smlelstv 	rc = ipmi_sendcmd(sc, BMC_SA, 0, APP_NETFN, APP_GET_DEVICE_ID, 0, NULL);
2240f95aa101Smlelstv 	if (rc) {
2241f95aa101Smlelstv 		dbg_printf(1, ": unable to send get device id "
2242f95aa101Smlelstv 		    "command\n");
2243f95aa101Smlelstv 		goto done;
2244f95aa101Smlelstv 	}
2245f95aa101Smlelstv 	rc = ipmi_recvcmd(sc, sizeof(buf), &len, buf);
2246f95aa101Smlelstv 	if (rc) {
2247f95aa101Smlelstv 		dbg_printf(1, ": unable to retrieve device id\n");
2248f95aa101Smlelstv 	}
2249f95aa101Smlelstv done:
2250f95aa101Smlelstv 	mutex_exit(&sc->sc_cmd_mtx);
2251f95aa101Smlelstv 
2252f95aa101Smlelstv 	if (rc == 0 && res != NULL)
2253f95aa101Smlelstv 		memcpy(res, buf, MIN(sizeof(*res), len));
2254f95aa101Smlelstv 
2255f95aa101Smlelstv 	return rc;
2256f95aa101Smlelstv }
2257f95aa101Smlelstv 
2258f95aa101Smlelstv static int
2259dc766f2bSmlelstv ipmi_watchdog_setmode(struct sysmon_wdog *smwdog)
2260dc766f2bSmlelstv {
2261dc766f2bSmlelstv 	struct ipmi_softc	*sc = smwdog->smw_cookie;
2262dc766f2bSmlelstv 	struct ipmi_get_watchdog gwdog;
2263dc766f2bSmlelstv 	struct ipmi_set_watchdog swdog;
2264dc766f2bSmlelstv 	int			rc, len;
2265dc766f2bSmlelstv 
2266dc766f2bSmlelstv 	if (smwdog->smw_period < 10)
2267dc766f2bSmlelstv 		return EINVAL;
2268dc766f2bSmlelstv 	if (smwdog->smw_period == WDOG_PERIOD_DEFAULT)
2269dc766f2bSmlelstv 		sc->sc_wdog.smw_period = 10;
2270dc766f2bSmlelstv 	else
2271dc766f2bSmlelstv 		sc->sc_wdog.smw_period = smwdog->smw_period;
2272dc766f2bSmlelstv 
2273b851421cSriastradh 	/* Wait until the device is initialized */
2274b851421cSriastradh 	rc = 0;
2275b851421cSriastradh 	mutex_enter(&sc->sc_poll_mtx);
2276b851421cSriastradh 	while (sc->sc_thread_ready)
2277b851421cSriastradh 		rc = cv_wait_sig(&sc->sc_mode_cv, &sc->sc_poll_mtx);
2278b851421cSriastradh 	mutex_exit(&sc->sc_poll_mtx);
2279b851421cSriastradh 	if (rc)
2280b851421cSriastradh 		return rc;
2281b851421cSriastradh 
2282dc766f2bSmlelstv 	mutex_enter(&sc->sc_cmd_mtx);
2283dc766f2bSmlelstv 	/* see if we can properly task to the watchdog */
2284dc766f2bSmlelstv 	rc = ipmi_sendcmd(sc, BMC_SA, BMC_LUN, APP_NETFN,
2285dc766f2bSmlelstv 	    APP_GET_WATCHDOG_TIMER, 0, NULL);
2286dc766f2bSmlelstv 	rc = ipmi_recvcmd(sc, sizeof(gwdog), &len, &gwdog);
2287dc766f2bSmlelstv 	mutex_exit(&sc->sc_cmd_mtx);
2288dc766f2bSmlelstv 	if (rc) {
2289dc766f2bSmlelstv 		aprint_error_dev(sc->sc_dev,
2290dc766f2bSmlelstv 		    "APP_GET_WATCHDOG_TIMER returned %#x\n", rc);
2291dc766f2bSmlelstv 		return EIO;
2292dc766f2bSmlelstv 	}
2293dc766f2bSmlelstv 
2294dc766f2bSmlelstv 	memset(&swdog, 0, sizeof(swdog));
2295dc766f2bSmlelstv 	/* Period is 10ths/sec */
2296dc766f2bSmlelstv 	swdog.wdog_timeout = htole16(sc->sc_wdog.smw_period * 10);
2297dc766f2bSmlelstv 	if ((smwdog->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED)
2298dc766f2bSmlelstv 		swdog.wdog_action = IPMI_WDOG_ACT_DISABLED;
2299dc766f2bSmlelstv 	else
2300dc766f2bSmlelstv 		swdog.wdog_action = IPMI_WDOG_ACT_RESET;
2301dc766f2bSmlelstv 	swdog.wdog_use = IPMI_WDOG_USE_USE_OS;
2302dc766f2bSmlelstv 
2303dc766f2bSmlelstv 	mutex_enter(&sc->sc_cmd_mtx);
2304dc766f2bSmlelstv 	if ((rc = ipmi_sendcmd(sc, BMC_SA, BMC_LUN, APP_NETFN,
2305dc766f2bSmlelstv 	    APP_SET_WATCHDOG_TIMER, sizeof(swdog), &swdog)) == 0)
2306dc766f2bSmlelstv 		rc = ipmi_recvcmd(sc, 0, &len, NULL);
2307dc766f2bSmlelstv 	mutex_exit(&sc->sc_cmd_mtx);
2308dc766f2bSmlelstv 	if (rc) {
2309dc766f2bSmlelstv 		aprint_error_dev(sc->sc_dev,
2310dc766f2bSmlelstv 		    "APP_SET_WATCHDOG_TIMER returned %#x\n", rc);
2311dc766f2bSmlelstv 		return EIO;
2312dc766f2bSmlelstv 	}
2313dc766f2bSmlelstv 
2314dc766f2bSmlelstv 	return 0;
2315dc766f2bSmlelstv }
2316dc766f2bSmlelstv 
2317dc766f2bSmlelstv static int
2318dc766f2bSmlelstv ipmi_watchdog_tickle(struct sysmon_wdog *smwdog)
2319dc766f2bSmlelstv {
2320dc766f2bSmlelstv 	struct ipmi_softc	*sc = smwdog->smw_cookie;
2321dc766f2bSmlelstv 
2322dc766f2bSmlelstv 	mutex_enter(&sc->sc_poll_mtx);
2323dc766f2bSmlelstv 	sc->sc_tickle_due = true;
2324dc766f2bSmlelstv 	cv_signal(&sc->sc_poll_cv);
2325dc766f2bSmlelstv 	mutex_exit(&sc->sc_poll_mtx);
2326dc766f2bSmlelstv 	return 0;
2327dc766f2bSmlelstv }
2328dc766f2bSmlelstv 
2329dc766f2bSmlelstv static void
2330dc766f2bSmlelstv ipmi_dotickle(struct ipmi_softc *sc)
2331dc766f2bSmlelstv {
2332dc766f2bSmlelstv 	int			rc, len;
2333dc766f2bSmlelstv 
2334dc766f2bSmlelstv 	mutex_enter(&sc->sc_cmd_mtx);
2335dc766f2bSmlelstv 	/* tickle the watchdog */
2336dc766f2bSmlelstv 	if ((rc = ipmi_sendcmd(sc, BMC_SA, BMC_LUN, APP_NETFN,
2337dc766f2bSmlelstv 	    APP_RESET_WATCHDOG, 0, NULL)) == 0)
2338dc766f2bSmlelstv 		rc = ipmi_recvcmd(sc, 0, &len, NULL);
2339dc766f2bSmlelstv 	mutex_exit(&sc->sc_cmd_mtx);
2340dc766f2bSmlelstv 	if (rc != 0) {
2341dc766f2bSmlelstv 		aprint_error_dev(sc->sc_dev, "watchdog tickle returned %#x\n",
2342dc766f2bSmlelstv 		    rc);
2343dc766f2bSmlelstv 	}
2344dc766f2bSmlelstv }
2345dc766f2bSmlelstv 
2346dc766f2bSmlelstv static bool
2347dc766f2bSmlelstv ipmi_suspend(device_t dev, const pmf_qual_t *qual)
2348dc766f2bSmlelstv {
2349dc766f2bSmlelstv 	struct ipmi_softc *sc = device_private(dev);
2350dc766f2bSmlelstv 
2351dc766f2bSmlelstv 	/* Don't allow suspend if watchdog is armed */
2352dc766f2bSmlelstv 	if ((sc->sc_wdog.smw_mode & WDOG_MODE_MASK) != WDOG_MODE_DISARMED)
2353dc766f2bSmlelstv 		return false;
2354dc766f2bSmlelstv 	return true;
2355dc766f2bSmlelstv }
2356b1f23fc9Smlelstv 
2357b1f23fc9Smlelstv static int
2358b1f23fc9Smlelstv ipmi_open(dev_t dev, int flag, int fmt, lwp_t *l)
2359b1f23fc9Smlelstv {
2360f219451cSmlelstv 	struct ipmi_softc *sc;
2361f219451cSmlelstv 	int unit;
2362f219451cSmlelstv 
2363f219451cSmlelstv 	unit = IPMIUNIT(dev);
2364f219451cSmlelstv 	if ((sc = device_lookup_private(&ipmi_cd, unit)) == NULL)
2365f219451cSmlelstv 		return (ENXIO);
2366f219451cSmlelstv 
2367b1f23fc9Smlelstv 	return 0;
2368b1f23fc9Smlelstv }
2369b1f23fc9Smlelstv 
2370b1f23fc9Smlelstv static int
2371b1f23fc9Smlelstv ipmi_close(dev_t dev, int flag, int fmt, lwp_t *l)
2372b1f23fc9Smlelstv {
2373b1f23fc9Smlelstv 	struct ipmi_softc *sc;
2374b1f23fc9Smlelstv 	int unit;
2375b1f23fc9Smlelstv 
2376b1f23fc9Smlelstv 	unit = IPMIUNIT(dev);
2377b1f23fc9Smlelstv 	if ((sc = device_lookup_private(&ipmi_cd, unit)) == NULL)
2378b1f23fc9Smlelstv 		return (ENXIO);
2379b1f23fc9Smlelstv 
2380b1f23fc9Smlelstv 	mutex_enter(&sc->sc_poll_mtx);
2381b1f23fc9Smlelstv 	if (sc->sc_mode == IPMI_MODE_COMMAND) {
2382b1f23fc9Smlelstv 		sc->sc_mode = IPMI_MODE_IDLE;
2383b1f23fc9Smlelstv 		cv_broadcast(&sc->sc_mode_cv);
2384b1f23fc9Smlelstv 	}
2385b1f23fc9Smlelstv 	mutex_exit(&sc->sc_poll_mtx);
2386b1f23fc9Smlelstv 	return 0;
2387b1f23fc9Smlelstv }
2388b1f23fc9Smlelstv 
2389b1f23fc9Smlelstv static int
2390b1f23fc9Smlelstv ipmi_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l)
2391b1f23fc9Smlelstv {
2392b1f23fc9Smlelstv 	struct ipmi_softc *sc;
2393b1f23fc9Smlelstv 	int unit, error = 0, len;
2394b1f23fc9Smlelstv 	struct ipmi_req *req;
2395b1f23fc9Smlelstv 	struct ipmi_recv *recv;
2396b1f23fc9Smlelstv 	struct ipmi_addr addr;
2397b1f23fc9Smlelstv 	unsigned char ccode, *buf = NULL;
2398b1f23fc9Smlelstv 
2399b1f23fc9Smlelstv 	unit = IPMIUNIT(dev);
2400b1f23fc9Smlelstv 	if ((sc = device_lookup_private(&ipmi_cd, unit)) == NULL)
2401b1f23fc9Smlelstv 		return (ENXIO);
2402b1f23fc9Smlelstv 
2403b1f23fc9Smlelstv 	switch (cmd) {
2404b1f23fc9Smlelstv 	case IPMICTL_SEND_COMMAND:
2405b1f23fc9Smlelstv 		mutex_enter(&sc->sc_poll_mtx);
2406b1f23fc9Smlelstv 		while (sc->sc_mode == IPMI_MODE_ENVSYS) {
2407b1f23fc9Smlelstv 			error = cv_wait_sig(&sc->sc_mode_cv, &sc->sc_poll_mtx);
2408b1f23fc9Smlelstv 			if (error == EINTR) {
2409b1f23fc9Smlelstv 				mutex_exit(&sc->sc_poll_mtx);
2410b1f23fc9Smlelstv 				return error;
2411b1f23fc9Smlelstv 			}
2412b1f23fc9Smlelstv 		}
2413b1f23fc9Smlelstv 		sc->sc_mode = IPMI_MODE_COMMAND;
2414b1f23fc9Smlelstv 		mutex_exit(&sc->sc_poll_mtx);
2415b1f23fc9Smlelstv 		break;
2416b1f23fc9Smlelstv 	}
2417b1f23fc9Smlelstv 
2418b1f23fc9Smlelstv 	mutex_enter(&sc->sc_cmd_mtx);
2419b1f23fc9Smlelstv 
2420b1f23fc9Smlelstv 	switch (cmd) {
2421b1f23fc9Smlelstv 	case IPMICTL_SEND_COMMAND:
2422b1f23fc9Smlelstv 		req = data;
2423b1f23fc9Smlelstv 		buf = malloc(IPMI_MAX_RX, M_DEVBUF, M_WAITOK);
2424b1f23fc9Smlelstv 
2425b1f23fc9Smlelstv 		len = req->msg.data_len;
2426b1f23fc9Smlelstv 		if (len < 0 || len > IPMI_MAX_RX) {
2427b1f23fc9Smlelstv 			error = EINVAL;
2428b1f23fc9Smlelstv 			break;
2429b1f23fc9Smlelstv 		}
2430b1f23fc9Smlelstv 
2431b1f23fc9Smlelstv 		/* clear pending result */
2432b1f23fc9Smlelstv 		if (sc->sc_sent)
2433b1f23fc9Smlelstv 			(void)ipmi_recvcmd(sc, IPMI_MAX_RX, &len, buf);
2434b1f23fc9Smlelstv 
2435b1f23fc9Smlelstv 		/* XXX */
2436b1f23fc9Smlelstv 		error = copyin(req->addr, &addr, sizeof(addr));
2437b1f23fc9Smlelstv 		if (error)
2438b1f23fc9Smlelstv 			break;
2439b1f23fc9Smlelstv 
2440b1f23fc9Smlelstv 		error = copyin(req->msg.data, buf, len);
2441b1f23fc9Smlelstv 		if (error)
2442b1f23fc9Smlelstv 			break;
2443b1f23fc9Smlelstv 
2444b1f23fc9Smlelstv 		/* save for receive */
2445b1f23fc9Smlelstv 		sc->sc_msgid = req->msgid;
2446b1f23fc9Smlelstv 		sc->sc_netfn = req->msg.netfn;
2447b1f23fc9Smlelstv 		sc->sc_cmd = req->msg.cmd;
2448b1f23fc9Smlelstv 
2449b1f23fc9Smlelstv 		if (ipmi_sendcmd(sc, BMC_SA, 0, req->msg.netfn,
2450b1f23fc9Smlelstv 		    req->msg.cmd, len, buf)) {
2451b1f23fc9Smlelstv 			error = EIO;
2452b1f23fc9Smlelstv 			break;
2453b1f23fc9Smlelstv 		}
2454b1f23fc9Smlelstv 		sc->sc_sent = true;
2455b1f23fc9Smlelstv 		break;
2456b1f23fc9Smlelstv 	case IPMICTL_RECEIVE_MSG_TRUNC:
2457b1f23fc9Smlelstv 	case IPMICTL_RECEIVE_MSG:
2458b1f23fc9Smlelstv 		recv = data;
2459b1f23fc9Smlelstv 		buf = malloc(IPMI_MAX_RX, M_DEVBUF, M_WAITOK);
2460b1f23fc9Smlelstv 
2461b1f23fc9Smlelstv 		if (recv->msg.data_len < 1) {
2462b1f23fc9Smlelstv 			error = EINVAL;
2463b1f23fc9Smlelstv 			break;
2464b1f23fc9Smlelstv 		}
2465b1f23fc9Smlelstv 
2466b1f23fc9Smlelstv 		/* XXX */
2467b1f23fc9Smlelstv 		error = copyin(recv->addr, &addr, sizeof(addr));
2468b1f23fc9Smlelstv 		if (error)
2469b1f23fc9Smlelstv 			break;
2470b1f23fc9Smlelstv 
2471b1f23fc9Smlelstv 
2472b1f23fc9Smlelstv 		if (!sc->sc_sent) {
2473b1f23fc9Smlelstv 			error = EIO;
2474b1f23fc9Smlelstv 			break;
2475b1f23fc9Smlelstv 		}
2476b1f23fc9Smlelstv 
2477b1f23fc9Smlelstv 		len = 0;
2478b1f23fc9Smlelstv 		error = ipmi_recvcmd(sc, IPMI_MAX_RX, &len, buf);
2479b1f23fc9Smlelstv 		if (error < 0) {
2480b1f23fc9Smlelstv 			error = EIO;
2481b1f23fc9Smlelstv 			break;
2482b1f23fc9Smlelstv 		}
2483b1f23fc9Smlelstv 		ccode = (unsigned char)error;
2484b1f23fc9Smlelstv 		sc->sc_sent = false;
2485b1f23fc9Smlelstv 
2486b1f23fc9Smlelstv 		if (len > recv->msg.data_len - 1) {
2487b1f23fc9Smlelstv 			if (cmd == IPMICTL_RECEIVE_MSG) {
2488b1f23fc9Smlelstv 				error = EMSGSIZE;
2489b1f23fc9Smlelstv 				break;
2490b1f23fc9Smlelstv 			}
2491b1f23fc9Smlelstv 			len = recv->msg.data_len - 1;
2492b1f23fc9Smlelstv 		}
2493b1f23fc9Smlelstv 
2494b1f23fc9Smlelstv 		addr.channel = IPMI_BMC_CHANNEL;
2495b1f23fc9Smlelstv 
2496b1f23fc9Smlelstv 		recv->recv_type = IPMI_RESPONSE_RECV_TYPE;
2497b1f23fc9Smlelstv 		recv->msgid = sc->sc_msgid;
2498b1f23fc9Smlelstv 		recv->msg.netfn = sc->sc_netfn;
2499b1f23fc9Smlelstv 		recv->msg.cmd = sc->sc_cmd;
2500b1f23fc9Smlelstv 		recv->msg.data_len = len+1;
2501b1f23fc9Smlelstv 
2502b1f23fc9Smlelstv 		error = copyout(&addr, recv->addr, sizeof(addr));
2503b1f23fc9Smlelstv 		if (error == 0)
2504b1f23fc9Smlelstv 			error = copyout(&ccode, recv->msg.data, 1);
2505b1f23fc9Smlelstv 		if (error == 0)
2506b1f23fc9Smlelstv 			error = copyout(buf, recv->msg.data+1, len);
2507b1f23fc9Smlelstv 		break;
2508b1f23fc9Smlelstv 	case IPMICTL_SET_MY_ADDRESS_CMD:
2509b1f23fc9Smlelstv 		sc->sc_address = *(int *)data;
2510b1f23fc9Smlelstv 		break;
2511b1f23fc9Smlelstv 	case IPMICTL_GET_MY_ADDRESS_CMD:
2512b1f23fc9Smlelstv 		*(int *)data = sc->sc_address;
2513b1f23fc9Smlelstv 		break;
2514b1f23fc9Smlelstv 	case IPMICTL_SET_MY_LUN_CMD:
2515b1f23fc9Smlelstv 		sc->sc_lun = *(int *)data & 0x3;
2516b1f23fc9Smlelstv 		break;
2517b1f23fc9Smlelstv 	case IPMICTL_GET_MY_LUN_CMD:
2518b1f23fc9Smlelstv 		*(int *)data = sc->sc_lun;
2519b1f23fc9Smlelstv 		break;
2520b1f23fc9Smlelstv 	case IPMICTL_SET_GETS_EVENTS_CMD:
2521b1f23fc9Smlelstv 		break;
2522b1f23fc9Smlelstv 	case IPMICTL_REGISTER_FOR_CMD:
2523b1f23fc9Smlelstv 	case IPMICTL_UNREGISTER_FOR_CMD:
2524b1f23fc9Smlelstv 		error = EOPNOTSUPP;
2525b1f23fc9Smlelstv 		break;
2526b1f23fc9Smlelstv 	default:
2527b1f23fc9Smlelstv 		error = ENODEV;
2528b1f23fc9Smlelstv 		break;
2529b1f23fc9Smlelstv 	}
2530b1f23fc9Smlelstv 
2531b1f23fc9Smlelstv 	if (buf)
2532b1f23fc9Smlelstv 		free(buf, M_DEVBUF);
2533b1f23fc9Smlelstv 
2534b1f23fc9Smlelstv 	mutex_exit(&sc->sc_cmd_mtx);
2535b1f23fc9Smlelstv 
2536b1f23fc9Smlelstv 	switch (cmd) {
2537b1f23fc9Smlelstv 	case IPMICTL_RECEIVE_MSG:
2538b1f23fc9Smlelstv 	case IPMICTL_RECEIVE_MSG_TRUNC:
2539b1f23fc9Smlelstv 		mutex_enter(&sc->sc_poll_mtx);
2540b1f23fc9Smlelstv 		sc->sc_mode = IPMI_MODE_IDLE;
2541b1f23fc9Smlelstv 		cv_broadcast(&sc->sc_mode_cv);
2542b1f23fc9Smlelstv 		mutex_exit(&sc->sc_poll_mtx);
2543b1f23fc9Smlelstv 		break;
2544b1f23fc9Smlelstv 	}
2545b1f23fc9Smlelstv 
2546b1f23fc9Smlelstv 	return error;
2547b1f23fc9Smlelstv }
2548b1f23fc9Smlelstv 
2549b1f23fc9Smlelstv static int
2550b1f23fc9Smlelstv ipmi_poll(dev_t dev, int events, lwp_t *l)
2551b1f23fc9Smlelstv {
2552b1f23fc9Smlelstv 	struct ipmi_softc *sc;
2553b1f23fc9Smlelstv 	int unit, revents = 0;
2554b1f23fc9Smlelstv 
2555b1f23fc9Smlelstv 	unit = IPMIUNIT(dev);
2556b1f23fc9Smlelstv 	if ((sc = device_lookup_private(&ipmi_cd, unit)) == NULL)
2557b1f23fc9Smlelstv 		return (ENXIO);
2558b1f23fc9Smlelstv 
2559b1f23fc9Smlelstv 	mutex_enter(&sc->sc_cmd_mtx);
2560b1f23fc9Smlelstv 	if (events & (POLLIN | POLLRDNORM)) {
2561b1f23fc9Smlelstv 		if (sc->sc_sent)
2562b1f23fc9Smlelstv 			revents |= events & (POLLIN | POLLRDNORM);
2563b1f23fc9Smlelstv 	}
2564b1f23fc9Smlelstv 	mutex_exit(&sc->sc_cmd_mtx);
2565b1f23fc9Smlelstv 
2566b1f23fc9Smlelstv 	return revents;
2567b1f23fc9Smlelstv }
2568