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