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