1 /* $OpenBSD: iosf.c,v 1.1 2023/04/23 00:20:26 dlg 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 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 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 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 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 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 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 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 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 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 220 iosf_mbi_available(void) 221 { 222 return (iosf_mbi != NULL); 223 } 224 225 static uint32_t 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 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 245 iosf_mbi_punit_acquire(void) 246 { 247 rw_enter_write(&iosf_lock); 248 } 249 250 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 { 258 int s; 259 260 if (splassert_ctl == 0) 261 return; 262 263 s = rw_status(&iosf_lock); 264 if (s != RW_WRITE) 265 splassert_fail(RW_WRITE, s, __func__); 266 } 267 268 static void 269 iosf_sem_wait(uint64_t usec, int waitok) 270 { 271 if (waitok) 272 tsleep_nsec(&nowake, PRIBIO, "iosfsem", USEC_TO_NSEC(usec)); 273 else 274 delay(usec); 275 } 276 277 #include <dev/i2c/i2cvar.h> 278 279 int 280 iosf_i2c_acquire(int flags) 281 { 282 struct iosf_mbi *mbi; 283 int waitok = !cold && !ISSET(flags, I2C_F_POLL); 284 unsigned int i; 285 286 mbi = iosf_mbi; 287 if (mbi == NULL) 288 return (0); 289 290 if (waitok) 291 rw_enter_write(&iosf_lock); 292 else if (iosf_lock.rwl_owner != 0) 293 panic("%s", __func__); 294 295 /* XXX disable C6 and C7 states */ 296 297 iosf_mbi_mdr_write(mbi, IOSF_BT_MBI_UNIT_PMC, IOSF_MBI_OP_REG_WRITE, 298 mbi->mbi_semaddr, IOSF_PUNIT_SEM_ACQUIRE); 299 300 for (i = 0; i < 50; i++) { 301 if (iosf_mbi_sem_get(mbi)) { 302 /* success! */ 303 return (0); 304 } 305 306 iosf_sem_wait(10000, waitok); 307 } 308 309 iosf_mbi_sem_reset(mbi); 310 311 if (waitok) 312 rw_exit_write(&iosf_lock); 313 else if (iosf_lock.rwl_owner != 0) 314 panic("%s", __func__); 315 316 return (EWOULDBLOCK); 317 } 318 319 void 320 iosf_i2c_release(int flags) 321 { 322 struct iosf_mbi *mbi; 323 int waitok = !cold && !ISSET(flags, I2C_F_POLL); 324 325 mbi = iosf_mbi; 326 if (mbi == NULL) 327 return; 328 329 iosf_mbi_sem_reset(mbi); 330 331 if (waitok) 332 rw_exit_write(&iosf_lock); 333 else if (iosf_lock.rwl_owner != 0) 334 panic("%s", __func__); 335 } 336