1 1.2 jmcneill /* $NetBSD: qcompep.c,v 1.2 2025/01/08 22:58:05 jmcneill Exp $ */ 2 1.1 jmcneill /* $OpenBSD: qcaoss.c,v 1.1 2023/05/23 14:10:27 patrick Exp $ */ 3 1.2 jmcneill /* $OpenBSD: qccpucp.c,v 1.1 2024/11/16 21:17:54 tobhe Exp $ */ 4 1.1 jmcneill /* 5 1.1 jmcneill * Copyright (c) 2023 Patrick Wildt <patrick (at) blueri.se> 6 1.2 jmcneill * Copyright (c) 2024 Tobias Heider <tobhe (at) openbsd.org> 7 1.1 jmcneill * 8 1.1 jmcneill * Permission to use, copy, modify, and distribute this software for any 9 1.1 jmcneill * purpose with or without fee is hereby granted, provided that the above 10 1.1 jmcneill * copyright notice and this permission notice appear in all copies. 11 1.1 jmcneill * 12 1.1 jmcneill * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 1.1 jmcneill * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 1.1 jmcneill * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 1.1 jmcneill * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 1.1 jmcneill * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 1.1 jmcneill * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 1.1 jmcneill * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 1.1 jmcneill */ 20 1.1 jmcneill 21 1.1 jmcneill #include <sys/param.h> 22 1.1 jmcneill #include <sys/systm.h> 23 1.1 jmcneill #include <sys/device.h> 24 1.1 jmcneill #include <sys/kmem.h> 25 1.1 jmcneill 26 1.1 jmcneill #include <dev/acpi/acpivar.h> 27 1.1 jmcneill #include <dev/acpi/qcompep.h> 28 1.1 jmcneill #include <dev/acpi/qcomipcc.h> 29 1.1 jmcneill 30 1.2 jmcneill #include <dev/ic/scmi.h> 31 1.2 jmcneill 32 1.1 jmcneill #define AOSS_DESC_MAGIC 0x0 33 1.1 jmcneill #define AOSS_DESC_VERSION 0x4 34 1.1 jmcneill #define AOSS_DESC_FEATURES 0x8 35 1.1 jmcneill #define AOSS_DESC_UCORE_LINK_STATE 0xc 36 1.1 jmcneill #define AOSS_DESC_UCORE_LINK_STATE_ACK 0x10 37 1.1 jmcneill #define AOSS_DESC_UCORE_CH_STATE 0x14 38 1.1 jmcneill #define AOSS_DESC_UCORE_CH_STATE_ACK 0x18 39 1.1 jmcneill #define AOSS_DESC_UCORE_MBOX_SIZE 0x1c 40 1.1 jmcneill #define AOSS_DESC_UCORE_MBOX_OFFSET 0x20 41 1.1 jmcneill #define AOSS_DESC_MCORE_LINK_STATE 0x24 42 1.1 jmcneill #define AOSS_DESC_MCORE_LINK_STATE_ACK 0x28 43 1.1 jmcneill #define AOSS_DESC_MCORE_CH_STATE 0x2c 44 1.1 jmcneill #define AOSS_DESC_MCORE_CH_STATE_ACK 0x30 45 1.1 jmcneill #define AOSS_DESC_MCORE_MBOX_SIZE 0x34 46 1.1 jmcneill #define AOSS_DESC_MCORE_MBOX_OFFSET 0x38 47 1.1 jmcneill 48 1.1 jmcneill #define AOSS_MAGIC 0x4d41494c 49 1.1 jmcneill #define AOSS_VERSION 1 50 1.1 jmcneill 51 1.1 jmcneill #define AOSS_STATE_UP (0xffffU << 0) 52 1.1 jmcneill #define AOSS_STATE_DOWN (0xffffU << 16) 53 1.1 jmcneill 54 1.1 jmcneill #define AOSSREAD4(sc, reg) \ 55 1.1 jmcneill bus_space_read_4((sc)->sc_iot, (sc)->sc_aoss_ioh, (reg)) 56 1.1 jmcneill #define AOSSWRITE4(sc, reg, val) \ 57 1.1 jmcneill bus_space_write_4((sc)->sc_iot, (sc)->sc_aoss_ioh, (reg), (val)) 58 1.1 jmcneill 59 1.2 jmcneill #define CPUCP_REG_CMD(i) (0x104 + ((i) * 8)) 60 1.2 jmcneill #define CPUCP_MASK_CMD 0xffffffffffffffffULL 61 1.2 jmcneill #define CPUCP_REG_RX_MAP 0x4000 62 1.2 jmcneill #define CPUCP_REG_RX_STAT 0x4400 63 1.2 jmcneill #define CPUCP_REG_RX_CLEAR 0x4800 64 1.2 jmcneill #define CPUCP_REG_RX_EN 0x4C00 65 1.2 jmcneill 66 1.2 jmcneill #define RXREAD8(sc, reg) \ 67 1.2 jmcneill (bus_space_read_8((sc)->sc_iot, (sc)->sc_cpucp_rx_ioh, (reg))) 68 1.2 jmcneill #define RXWRITE8(sc, reg, val) \ 69 1.2 jmcneill bus_space_write_8((sc)->sc_iot, (sc)->sc_cpucp_rx_ioh, (reg), (val)) 70 1.2 jmcneill 71 1.2 jmcneill #define TXWRITE4(sc, reg, val) \ 72 1.2 jmcneill bus_space_write_4((sc)->sc_iot, (sc)->sc_cpucp_tx_ioh, (reg), (val)) 73 1.2 jmcneill 74 1.2 jmcneill 75 1.1 jmcneill struct qcpep_data { 76 1.1 jmcneill bus_addr_t aoss_base; 77 1.1 jmcneill bus_size_t aoss_size; 78 1.1 jmcneill uint32_t aoss_client_id; 79 1.1 jmcneill uint32_t aoss_signal_id; 80 1.2 jmcneill bus_addr_t cpucp_rx_base; 81 1.2 jmcneill bus_size_t cpucp_rx_size; 82 1.2 jmcneill bus_addr_t cpucp_tx_base; 83 1.2 jmcneill bus_size_t cpucp_tx_size; 84 1.2 jmcneill bus_addr_t cpucp_shmem_base; 85 1.2 jmcneill bus_size_t cpucp_shmem_size; 86 1.1 jmcneill }; 87 1.1 jmcneill 88 1.1 jmcneill struct qcpep_softc { 89 1.1 jmcneill device_t sc_dev; 90 1.1 jmcneill bus_space_tag_t sc_iot; 91 1.1 jmcneill 92 1.1 jmcneill const struct qcpep_data *sc_data; 93 1.1 jmcneill 94 1.1 jmcneill bus_space_handle_t sc_aoss_ioh; 95 1.1 jmcneill size_t sc_aoss_offset; 96 1.1 jmcneill size_t sc_aoss_size; 97 1.1 jmcneill void * sc_aoss_ipcc; 98 1.2 jmcneill 99 1.2 jmcneill bus_space_handle_t sc_cpucp_rx_ioh; 100 1.2 jmcneill bus_space_handle_t sc_cpucp_tx_ioh; 101 1.2 jmcneill 102 1.2 jmcneill struct scmi_softc sc_scmi; 103 1.1 jmcneill }; 104 1.1 jmcneill 105 1.1 jmcneill struct qcpep_softc *qcpep_sc; 106 1.1 jmcneill 107 1.1 jmcneill static const struct qcpep_data qcpep_x1e_data = { 108 1.1 jmcneill .aoss_base = 0x0c300000, 109 1.1 jmcneill .aoss_size = 0x400, 110 1.1 jmcneill .aoss_client_id = 0, /* IPCC_CLIENT_AOP */ 111 1.1 jmcneill .aoss_signal_id = 0, /* IPCC_MPROC_SIGNAL_GLINK_QMP */ 112 1.2 jmcneill .cpucp_rx_base = 0x17430000, 113 1.2 jmcneill .cpucp_rx_size = 0x10000, 114 1.2 jmcneill .cpucp_tx_base = 0x18830000, 115 1.2 jmcneill .cpucp_tx_size = 0x10000, 116 1.2 jmcneill .cpucp_shmem_base = 0x18b4e000, 117 1.2 jmcneill .cpucp_shmem_size = 0x400, 118 1.1 jmcneill }; 119 1.1 jmcneill 120 1.1 jmcneill static const struct device_compatible_entry compat_data[] = { 121 1.1 jmcneill { .compat = "QCOM0C17", .data = &qcpep_x1e_data }, 122 1.1 jmcneill DEVICE_COMPAT_EOL 123 1.1 jmcneill }; 124 1.1 jmcneill 125 1.1 jmcneill static int qcpep_match(device_t, cfdata_t, void *); 126 1.1 jmcneill static void qcpep_attach(device_t, device_t, void *); 127 1.1 jmcneill 128 1.1 jmcneill CFATTACH_DECL_NEW(qcompep, sizeof(struct qcpep_softc), 129 1.1 jmcneill qcpep_match, qcpep_attach, NULL, NULL); 130 1.1 jmcneill 131 1.1 jmcneill static int 132 1.1 jmcneill qcpep_match(device_t parent, cfdata_t match, void *aux) 133 1.1 jmcneill { 134 1.1 jmcneill struct acpi_attach_args *aa = aux; 135 1.1 jmcneill 136 1.1 jmcneill return acpi_compatible_match(aa, compat_data); 137 1.1 jmcneill } 138 1.1 jmcneill 139 1.1 jmcneill static void 140 1.1 jmcneill qcpep_attach(device_t parent, device_t self, void *aux) 141 1.1 jmcneill { 142 1.1 jmcneill struct qcpep_softc *sc = device_private(self); 143 1.1 jmcneill struct acpi_attach_args *aa = aux; 144 1.2 jmcneill CPU_INFO_ITERATOR cii; 145 1.2 jmcneill struct cpu_info *ci; 146 1.1 jmcneill struct acpi_resources res; 147 1.2 jmcneill uint8_t *scmi_shmem; 148 1.1 jmcneill ACPI_STATUS rv; 149 1.2 jmcneill int i, last_pkg;; 150 1.1 jmcneill 151 1.1 jmcneill rv = acpi_resource_parse(self, aa->aa_node->ad_handle, 152 1.1 jmcneill "_CRS", &res, &acpi_resource_parse_ops_default); 153 1.1 jmcneill if (ACPI_FAILURE(rv)) { 154 1.1 jmcneill return; 155 1.1 jmcneill } 156 1.1 jmcneill acpi_resource_cleanup(&res); 157 1.1 jmcneill 158 1.1 jmcneill sc->sc_dev = self; 159 1.1 jmcneill sc->sc_iot = aa->aa_memt; 160 1.1 jmcneill sc->sc_data = acpi_compatible_lookup(aa, compat_data)->data; 161 1.1 jmcneill 162 1.1 jmcneill if (bus_space_map(sc->sc_iot, sc->sc_data->aoss_base, 163 1.1 jmcneill sc->sc_data->aoss_size, BUS_SPACE_MAP_NONPOSTED, &sc->sc_aoss_ioh)) { 164 1.2 jmcneill aprint_error_dev(self, "couldn't map aoss registers\n"); 165 1.2 jmcneill return; 166 1.2 jmcneill } 167 1.2 jmcneill if (bus_space_map(sc->sc_iot, sc->sc_data->cpucp_rx_base, 168 1.2 jmcneill sc->sc_data->cpucp_rx_size, BUS_SPACE_MAP_NONPOSTED, 169 1.2 jmcneill &sc->sc_cpucp_rx_ioh)) { 170 1.2 jmcneill aprint_error_dev(self, "couldn't map cpucp rx registers\n"); 171 1.2 jmcneill return; 172 1.2 jmcneill } 173 1.2 jmcneill if (bus_space_map(sc->sc_iot, sc->sc_data->cpucp_tx_base, 174 1.2 jmcneill sc->sc_data->cpucp_tx_size, BUS_SPACE_MAP_NONPOSTED, 175 1.2 jmcneill &sc->sc_cpucp_tx_ioh)) { 176 1.2 jmcneill aprint_error_dev(self, "couldn't map cpucp tx registers\n"); 177 1.1 jmcneill return; 178 1.1 jmcneill } 179 1.1 jmcneill 180 1.1 jmcneill sc->sc_aoss_ipcc = qcipcc_channel(sc->sc_data->aoss_client_id, 181 1.1 jmcneill sc->sc_data->aoss_signal_id); 182 1.1 jmcneill if (sc->sc_aoss_ipcc == NULL) { 183 1.1 jmcneill aprint_error_dev(self, "couldn't find ipcc mailbox\n"); 184 1.1 jmcneill return; 185 1.1 jmcneill } 186 1.1 jmcneill 187 1.1 jmcneill if (AOSSREAD4(sc, AOSS_DESC_MAGIC) != AOSS_MAGIC || 188 1.1 jmcneill AOSSREAD4(sc, AOSS_DESC_VERSION) != AOSS_VERSION) { 189 1.1 jmcneill aprint_error_dev(self, "invalid QMP info\n"); 190 1.1 jmcneill return; 191 1.1 jmcneill } 192 1.1 jmcneill 193 1.1 jmcneill sc->sc_aoss_offset = AOSSREAD4(sc, AOSS_DESC_MCORE_MBOX_OFFSET); 194 1.1 jmcneill sc->sc_aoss_size = AOSSREAD4(sc, AOSS_DESC_MCORE_MBOX_SIZE); 195 1.1 jmcneill if (sc->sc_aoss_size == 0) { 196 1.1 jmcneill aprint_error_dev(self, "invalid AOSS mailbox size\n"); 197 1.1 jmcneill return; 198 1.1 jmcneill } 199 1.1 jmcneill 200 1.1 jmcneill AOSSWRITE4(sc, AOSS_DESC_UCORE_LINK_STATE_ACK, 201 1.1 jmcneill AOSSREAD4(sc, AOSS_DESC_UCORE_LINK_STATE)); 202 1.1 jmcneill 203 1.1 jmcneill AOSSWRITE4(sc, AOSS_DESC_MCORE_LINK_STATE, AOSS_STATE_UP); 204 1.1 jmcneill qcipcc_send(sc->sc_aoss_ipcc); 205 1.1 jmcneill 206 1.1 jmcneill for (i = 1000; i > 0; i--) { 207 1.1 jmcneill if (AOSSREAD4(sc, AOSS_DESC_MCORE_LINK_STATE_ACK) == AOSS_STATE_UP) 208 1.1 jmcneill break; 209 1.1 jmcneill delay(1000); 210 1.1 jmcneill } 211 1.1 jmcneill if (i == 0) { 212 1.1 jmcneill aprint_error_dev(self, "didn't get link state ack\n"); 213 1.1 jmcneill return; 214 1.1 jmcneill } 215 1.1 jmcneill 216 1.1 jmcneill AOSSWRITE4(sc, AOSS_DESC_MCORE_CH_STATE, AOSS_STATE_UP); 217 1.1 jmcneill qcipcc_send(sc->sc_aoss_ipcc); 218 1.1 jmcneill 219 1.1 jmcneill for (i = 1000; i > 0; i--) { 220 1.1 jmcneill if (AOSSREAD4(sc, AOSS_DESC_UCORE_CH_STATE) == AOSS_STATE_UP) 221 1.1 jmcneill break; 222 1.1 jmcneill delay(1000); 223 1.1 jmcneill } 224 1.1 jmcneill if (i == 0) { 225 1.1 jmcneill aprint_error_dev(self, "didn't get open channel\n"); 226 1.1 jmcneill return; 227 1.1 jmcneill } 228 1.1 jmcneill 229 1.1 jmcneill AOSSWRITE4(sc, AOSS_DESC_UCORE_CH_STATE_ACK, AOSS_STATE_UP); 230 1.1 jmcneill qcipcc_send(sc->sc_aoss_ipcc); 231 1.1 jmcneill 232 1.1 jmcneill for (i = 1000; i > 0; i--) { 233 1.1 jmcneill if (AOSSREAD4(sc, AOSS_DESC_MCORE_CH_STATE_ACK) == AOSS_STATE_UP) 234 1.1 jmcneill break; 235 1.1 jmcneill delay(1000); 236 1.1 jmcneill } 237 1.1 jmcneill if (i == 0) { 238 1.1 jmcneill aprint_error_dev(self, "didn't get channel ack\n"); 239 1.1 jmcneill return; 240 1.1 jmcneill } 241 1.1 jmcneill 242 1.2 jmcneill RXWRITE8(sc, CPUCP_REG_RX_EN, 0); 243 1.2 jmcneill RXWRITE8(sc, CPUCP_REG_RX_CLEAR, 0); 244 1.2 jmcneill RXWRITE8(sc, CPUCP_REG_RX_MAP, 0); 245 1.2 jmcneill RXWRITE8(sc, CPUCP_REG_RX_MAP, CPUCP_MASK_CMD); 246 1.2 jmcneill 247 1.1 jmcneill qcpep_sc = sc; 248 1.2 jmcneill 249 1.2 jmcneill /* SCMI setup */ 250 1.2 jmcneill scmi_shmem = AcpiOsMapMemory(sc->sc_data->cpucp_shmem_base, 251 1.2 jmcneill sc->sc_data->cpucp_shmem_size); 252 1.2 jmcneill if (scmi_shmem == NULL) { 253 1.2 jmcneill aprint_error_dev(self, "couldn't map SCMI shared memory\n"); 254 1.2 jmcneill return; 255 1.2 jmcneill } 256 1.2 jmcneill 257 1.2 jmcneill sc->sc_scmi.sc_dev = self; 258 1.2 jmcneill sc->sc_scmi.sc_iot = sc->sc_iot; 259 1.2 jmcneill sc->sc_scmi.sc_shmem_tx = (struct scmi_shmem *)(scmi_shmem + 0x000); 260 1.2 jmcneill sc->sc_scmi.sc_shmem_rx = (struct scmi_shmem *)(scmi_shmem + 0x200); 261 1.2 jmcneill sc->sc_scmi.sc_mbox_tx = qccpucp_channel(0); 262 1.2 jmcneill sc->sc_scmi.sc_mbox_tx_send = qccpucp_send; 263 1.2 jmcneill sc->sc_scmi.sc_mbox_rx = qccpucp_channel(2); 264 1.2 jmcneill sc->sc_scmi.sc_mbox_rx_send = qccpucp_send; 265 1.2 jmcneill /* Build performance domain to CPU map. */ 266 1.2 jmcneill sc->sc_scmi.sc_perf_ndmap = 0; 267 1.2 jmcneill last_pkg = -1; 268 1.2 jmcneill for (CPU_INFO_FOREACH(cii, ci)) { 269 1.2 jmcneill if (ci->ci_package_id != last_pkg) { 270 1.2 jmcneill sc->sc_scmi.sc_perf_ndmap++; 271 1.2 jmcneill last_pkg = ci->ci_package_id; 272 1.2 jmcneill } 273 1.2 jmcneill } 274 1.2 jmcneill sc->sc_scmi.sc_perf_dmap = kmem_zalloc( 275 1.2 jmcneill sizeof(*sc->sc_scmi.sc_perf_dmap) * sc->sc_scmi.sc_perf_ndmap, 276 1.2 jmcneill KM_SLEEP); 277 1.2 jmcneill last_pkg = -1; 278 1.2 jmcneill i = 0; 279 1.2 jmcneill for (CPU_INFO_FOREACH(cii, ci)) { 280 1.2 jmcneill if (ci->ci_package_id != last_pkg) { 281 1.2 jmcneill sc->sc_scmi.sc_perf_dmap[i].pm_domain = i; 282 1.2 jmcneill sc->sc_scmi.sc_perf_dmap[i].pm_ci = ci; 283 1.2 jmcneill last_pkg = ci->ci_package_id; 284 1.2 jmcneill i++; 285 1.2 jmcneill } 286 1.2 jmcneill } 287 1.2 jmcneill if (scmi_init_mbox(&sc->sc_scmi) != 0) { 288 1.2 jmcneill aprint_error_dev(self, "couldn't setup SCMI\n"); 289 1.2 jmcneill return; 290 1.2 jmcneill } 291 1.2 jmcneill scmi_attach_perf(&sc->sc_scmi); 292 1.1 jmcneill } 293 1.1 jmcneill 294 1.1 jmcneill int 295 1.1 jmcneill qcaoss_send(char *data, size_t len) 296 1.1 jmcneill { 297 1.1 jmcneill struct qcpep_softc *sc = qcpep_sc; 298 1.1 jmcneill uint32_t reg; 299 1.1 jmcneill int i; 300 1.1 jmcneill 301 1.1 jmcneill if (sc == NULL) 302 1.1 jmcneill return ENXIO; 303 1.1 jmcneill 304 1.1 jmcneill if (data == NULL || sizeof(uint32_t) + len > sc->sc_aoss_size || 305 1.1 jmcneill (len % sizeof(uint32_t)) != 0) 306 1.1 jmcneill return EINVAL; 307 1.1 jmcneill 308 1.1 jmcneill /* Write data first, needs to be 32-bit access. */ 309 1.1 jmcneill for (i = 0; i < len; i += 4) { 310 1.1 jmcneill memcpy(®, data + i, sizeof(reg)); 311 1.1 jmcneill AOSSWRITE4(sc, sc->sc_aoss_offset + sizeof(uint32_t) + i, reg); 312 1.1 jmcneill } 313 1.1 jmcneill 314 1.1 jmcneill /* Commit transaction by writing length. */ 315 1.1 jmcneill AOSSWRITE4(sc, sc->sc_aoss_offset, len); 316 1.1 jmcneill 317 1.1 jmcneill /* Assert it's stored and inform peer. */ 318 1.1 jmcneill if (AOSSREAD4(sc, sc->sc_aoss_offset) != len) { 319 1.1 jmcneill device_printf(sc->sc_dev, 320 1.1 jmcneill "aoss message readback failed\n"); 321 1.1 jmcneill } 322 1.1 jmcneill qcipcc_send(sc->sc_aoss_ipcc); 323 1.1 jmcneill 324 1.1 jmcneill for (i = 1000; i > 0; i--) { 325 1.1 jmcneill if (AOSSREAD4(sc, sc->sc_aoss_offset) == 0) 326 1.1 jmcneill break; 327 1.1 jmcneill delay(1000); 328 1.1 jmcneill } 329 1.1 jmcneill if (i == 0) { 330 1.1 jmcneill device_printf(sc->sc_dev, "timeout sending message\n"); 331 1.1 jmcneill AOSSWRITE4(sc, sc->sc_aoss_offset, 0); 332 1.1 jmcneill return ETIMEDOUT; 333 1.1 jmcneill } 334 1.1 jmcneill 335 1.1 jmcneill return 0; 336 1.1 jmcneill } 337 1.2 jmcneill 338 1.2 jmcneill void * 339 1.2 jmcneill qccpucp_channel(u_int id) 340 1.2 jmcneill { 341 1.2 jmcneill struct qcpep_softc *sc = qcpep_sc; 342 1.2 jmcneill uint64_t val; 343 1.2 jmcneill 344 1.2 jmcneill if (sc == NULL || id > 2) { 345 1.2 jmcneill return NULL; 346 1.2 jmcneill } 347 1.2 jmcneill 348 1.2 jmcneill val = RXREAD8(sc, CPUCP_REG_RX_EN); 349 1.2 jmcneill val |= (1 << id); 350 1.2 jmcneill RXWRITE8(sc, CPUCP_REG_RX_EN, val); 351 1.2 jmcneill 352 1.2 jmcneill return (void *)(uintptr_t)(id + 1); 353 1.2 jmcneill } 354 1.2 jmcneill 355 1.2 jmcneill int 356 1.2 jmcneill qccpucp_send(void *cookie) 357 1.2 jmcneill { 358 1.2 jmcneill struct qcpep_softc *sc = qcpep_sc; 359 1.2 jmcneill uintptr_t id = (uintptr_t)cookie - 1; 360 1.2 jmcneill 361 1.2 jmcneill TXWRITE4(sc, CPUCP_REG_CMD(id), 0); 362 1.2 jmcneill 363 1.2 jmcneill return 0; 364 1.2 jmcneill } 365