xref: /openbsd/sys/dev/ic/iosf.c (revision da5607f6)
1 /*	$OpenBSD: iosf.c,v 1.2 2024/06/26 01:40:49 jsg Exp $ */
2 
3 /*
4  * Copyright (c) 2023 David Gwynne <dlg@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/mutex.h>
23 #include <sys/rwlock.h>
24 
25 #include <machine/bus.h>
26 
27 #include <dev/ic/iosfvar.h>
28 
29 #define IOSF_MBI_MASK_HI		0xffffff00
30 #define IOSF_MBI_MASK_LO		0x000000ff
31 #define IOSF_MBI_ENABLE			0x000000f0
32 
33 #define IOSF_MBI_MCR_OP_SHIFT		24
34 #define IOSF_MBI_MCR_PORT_SHIFT		16
35 #define IOSF_MBI_MCR_OFFSET_SHIFT	8
36 
37 /* IOSF sideband read/write opcodes */
38 #define IOSF_MBI_OP_MMIO_READ		0x00
39 #define IOSF_MBI_OP_MMIO_WRITE		0x01
40 #define IOSF_MBI_OP_CFG_READ		0x04
41 #define IOSF_MBI_OP_CFG_WRITE		0x05
42 #define IOSF_MBI_OP_CR_READ		0x06
43 #define IOSF_MBI_OP_CR_WRITE		0x07
44 #define IOSF_MBI_OP_REG_READ		0x10
45 #define IOSF_MBI_OP_REG_WRITE		0x11
46 #define IOSF_MBI_OP_ESRAM_READ		0x12
47 #define IOSF_MBI_OP_ESRAM_WRITE		0x13
48 
49 /* Baytrail */
50 #define IOSF_BT_MBI_UNIT_AUNIT		0x00
51 #define IOSF_BT_MBI_UNIT_SMC		0x01
52 #define IOSF_BT_MBI_UNIT_CPU		0x02
53 #define IOSF_BT_MBI_UNIT_BUNIT		0x03
54 #define IOSF_BT_MBI_UNIT_PMC		0x04
55 #define IOSF_BT_MBI_UNIT_GFX		0x06
56 #define IOSF_BT_MBI_UNIT_SMI		0x0C
57 #define IOSF_BT_MBI_UNIT_CCK		0x14
58 #define IOSF_BT_MBI_UNIT_USB		0x43
59 #define IOSF_BT_MBI_UNIT_SATA		0xA3
60 #define IOSF_BT_MBI_UNIT_PCIE		0xA6
61 
62 /* semaphore bits */
63 #define IOSF_PUNIT_SEM_BIT		(1 << 0)
64 #define IOSF_PUNIT_SEM_ACQUIRE		(1 << 1)
65 
66 struct cfdriver iosf_cd = {
67 	NULL, "iosf", DV_DULL
68 };
69 
70 /*
71  * serialise register ops
72  */
73 static struct mutex iosf_mbi_mtx = MUTEX_INITIALIZER(IPL_HIGH);
74 
75 /*
76  * rwlock for kernel to coordinate access to the mbi with
77  */
78 static struct rwlock iosf_lock = RWLOCK_INITIALIZER("iosf");
79 
80 /*
81  * drivers provide an iosf_mbi that acts as a backend for the code below.
82  */
83 static struct iosf_mbi *iosf_mbi;
84 
85 void
iosf_mbi_attach(struct iosf_mbi * mbi)86 iosf_mbi_attach(struct iosf_mbi *mbi)
87 {
88 	/*
89 	 * assume this is serialised by autoconf being run sequentially
90 	 * during boot.
91 	 */
92 
93 	if (iosf_mbi == NULL || iosf_mbi->mbi_prio < mbi->mbi_prio)
94 		iosf_mbi = mbi;
95 }
96 
97 static inline uint32_t
iosf_mbi_mcr(uint8_t op,uint8_t port,uint32_t offset)98 iosf_mbi_mcr(uint8_t op, uint8_t port, uint32_t offset)
99 {
100 	uint32_t rv = IOSF_MBI_ENABLE;
101 	rv |= op << IOSF_MBI_MCR_OP_SHIFT;
102 	rv |= port << IOSF_MBI_MCR_PORT_SHIFT;
103 	rv |= (offset & IOSF_MBI_MASK_LO) << IOSF_MBI_MCR_OFFSET_SHIFT;
104 	return (rv);
105 }
106 
107 static inline uint32_t
iosf_mbi_mcrx(uint32_t offset)108 iosf_mbi_mcrx(uint32_t offset)
109 {
110 	return (offset & IOSF_MBI_MASK_HI);
111 }
112 
113 /*
114  * serialised mbi mdr operations
115  */
116 
117 static uint32_t
iosf_mbi_mdr_read(struct iosf_mbi * mbi,uint8_t port,uint8_t op,uint32_t offset)118 iosf_mbi_mdr_read(struct iosf_mbi *mbi, uint8_t port, uint8_t op,
119     uint32_t offset)
120 {
121 	uint32_t mcr, mcrx, mdr;
122 
123 	mcr = iosf_mbi_mcr(op, port, offset);
124 	mcrx = iosf_mbi_mcrx(offset);
125 
126 	mtx_enter(&iosf_mbi_mtx);
127 	mdr = (*mbi->mbi_mdr_rd)(mbi, mcr, mcrx);
128 	mtx_leave(&iosf_mbi_mtx);
129 
130 	return (mdr);
131 }
132 
133 static void
iosf_mbi_mdr_write(struct iosf_mbi * mbi,uint8_t port,uint8_t op,uint32_t offset,uint32_t mdr)134 iosf_mbi_mdr_write(struct iosf_mbi *mbi, uint8_t port, uint8_t op,
135     uint32_t offset, uint32_t mdr)
136 {
137 	uint32_t mcr, mcrx;
138 
139 	mcr = iosf_mbi_mcr(op, port, offset);
140 	mcrx = iosf_mbi_mcrx(offset);
141 
142 	mtx_enter(&iosf_mbi_mtx);
143 	(*mbi->mbi_mdr_wr)(mbi, mcr, mcrx, mdr);
144 	mtx_leave(&iosf_mbi_mtx);
145 }
146 
147 static void
iosf_mbi_mdr_modify(struct iosf_mbi * mbi,uint8_t port,uint8_t op,uint32_t offset,uint32_t bits,uint32_t mask)148 iosf_mbi_mdr_modify(struct iosf_mbi *mbi, uint8_t port, uint8_t op,
149     uint32_t offset, uint32_t bits, uint32_t mask)
150 {
151 	uint32_t mcr, mcrx, mdr;
152 
153 	mcr = iosf_mbi_mcr(op, port, offset);
154 	mcrx = iosf_mbi_mcrx(offset);
155 
156 	mtx_enter(&iosf_mbi_mtx);
157 	mdr = (*mbi->mbi_mdr_rd)(mbi, mcr, mcrx);
158 
159 	CLR(mdr, mask);
160 	SET(mdr, bits & mask);
161 
162 	(*mbi->mbi_mdr_wr)(mbi, mcr, mcrx, mdr);
163 	mtx_leave(&iosf_mbi_mtx);
164 }
165 
166 /*
167  * linux compat api
168  */
169 
170 int
iosf_mbi_read(uint8_t port,uint8_t opcode,uint32_t offset,uint32_t * mdrp)171 iosf_mbi_read(uint8_t port, uint8_t opcode, uint32_t offset, uint32_t *mdrp)
172 {
173 	struct iosf_mbi *mbi;
174 
175 	mbi = iosf_mbi;
176 	if (mbi == NULL)
177 		return (ENODEV);
178 
179 	/* check port != BT_MBI_UNIT_GFX? */
180 
181 	*mdrp = iosf_mbi_mdr_read(mbi, port, opcode, offset);
182 
183 	return (0);
184 }
185 
186 int
iosf_mbi_write(uint8_t port,uint8_t opcode,uint32_t offset,uint32_t mdr)187 iosf_mbi_write(uint8_t port, uint8_t opcode, uint32_t offset, uint32_t mdr)
188 {
189 	struct iosf_mbi *mbi;
190 
191 	mbi = iosf_mbi;
192 	if (mbi == NULL)
193 		return (ENODEV);
194 
195 	/* check port != BT_MBI_UNIT_GFX? */
196 
197 	iosf_mbi_mdr_write(mbi, port, opcode, offset, mdr);
198 
199 	return (0);
200 }
201 
202 int
iosf_mbi_modify(uint8_t port,uint8_t opcode,uint32_t offset,uint32_t bits,uint32_t mask)203 iosf_mbi_modify(uint8_t port, uint8_t opcode, uint32_t offset,
204     uint32_t bits, uint32_t mask)
205 {
206 	struct iosf_mbi *mbi;
207 
208 	mbi = iosf_mbi;
209 	if (mbi == NULL)
210 		return (ENODEV);
211 
212 	/* check port != BT_MBI_UNIT_GFX? */
213 
214 	iosf_mbi_mdr_modify(mbi, port, opcode, offset, bits, mask);
215 
216 	return (0);
217 }
218 
219 int
iosf_mbi_available(void)220 iosf_mbi_available(void)
221 {
222 	return (iosf_mbi != NULL);
223 }
224 
225 static uint32_t
iosf_mbi_sem_get(struct iosf_mbi * mbi)226 iosf_mbi_sem_get(struct iosf_mbi *mbi)
227 {
228 	uint32_t sem;
229 
230 	sem = iosf_mbi_mdr_read(mbi,
231 	    IOSF_BT_MBI_UNIT_PMC, IOSF_MBI_OP_REG_READ, mbi->mbi_semaddr);
232 
233 	return (ISSET(sem, IOSF_PUNIT_SEM_BIT));
234 }
235 
236 static void
iosf_mbi_sem_reset(struct iosf_mbi * mbi)237 iosf_mbi_sem_reset(struct iosf_mbi *mbi)
238 {
239 	iosf_mbi_mdr_modify(mbi,
240 	    IOSF_BT_MBI_UNIT_PMC, IOSF_MBI_OP_REG_READ, mbi->mbi_semaddr,
241 	    0, IOSF_PUNIT_SEM_BIT);
242 }
243 
244 void
iosf_mbi_punit_acquire(void)245 iosf_mbi_punit_acquire(void)
246 {
247 	rw_enter_write(&iosf_lock);
248 }
249 
250 void
iosf_mbi_punit_release(void)251 iosf_mbi_punit_release(void)
252 {
253 	rw_exit_write(&iosf_lock);
254 }
255 
256 void
iosf_mbi_assert_punit_acquired(void)257 iosf_mbi_assert_punit_acquired(void)
258 {
259 	int s;
260 
261 	if (splassert_ctl == 0)
262 		return;
263 
264 	s = rw_status(&iosf_lock);
265 	if (s != RW_WRITE)
266 		splassert_fail(RW_WRITE, s, __func__);
267 }
268 
269 static void
iosf_sem_wait(uint64_t usec,int waitok)270 iosf_sem_wait(uint64_t usec, int waitok)
271 {
272 	if (waitok)
273 		tsleep_nsec(&nowake, PRIBIO, "iosfsem", USEC_TO_NSEC(usec));
274 	else
275 		delay(usec);
276 }
277 
278 #include <dev/i2c/i2cvar.h>
279 
280 int
iosf_i2c_acquire(int flags)281 iosf_i2c_acquire(int flags)
282 {
283 	struct iosf_mbi *mbi;
284 	int waitok = !cold && !ISSET(flags, I2C_F_POLL);
285 	unsigned int i;
286 
287 	mbi = iosf_mbi;
288 	if (mbi == NULL)
289 		return (0);
290 
291 	if (waitok)
292 		rw_enter_write(&iosf_lock);
293 	else if (iosf_lock.rwl_owner != 0)
294 		panic("%s", __func__);
295 
296 	/* XXX disable C6 and C7 states */
297 
298 	iosf_mbi_mdr_write(mbi, IOSF_BT_MBI_UNIT_PMC, IOSF_MBI_OP_REG_WRITE,
299 	    mbi->mbi_semaddr, IOSF_PUNIT_SEM_ACQUIRE);
300 
301 	for (i = 0; i < 50; i++) {
302 		if (iosf_mbi_sem_get(mbi)) {
303 			/* success! */
304 			return (0);
305 		}
306 
307 		iosf_sem_wait(10000, waitok);
308 	}
309 
310 	iosf_mbi_sem_reset(mbi);
311 
312 	if (waitok)
313 		rw_exit_write(&iosf_lock);
314 	else if (iosf_lock.rwl_owner != 0)
315 		panic("%s", __func__);
316 
317 	return (EWOULDBLOCK);
318 }
319 
320 void
iosf_i2c_release(int flags)321 iosf_i2c_release(int flags)
322 {
323 	struct iosf_mbi *mbi;
324 	int waitok = !cold && !ISSET(flags, I2C_F_POLL);
325 
326 	mbi = iosf_mbi;
327 	if (mbi == NULL)
328 		return;
329 
330 	iosf_mbi_sem_reset(mbi);
331 
332 	if (waitok)
333 		rw_exit_write(&iosf_lock);
334 	else if (iosf_lock.rwl_owner != 0)
335 		panic("%s", __func__);
336 }
337