1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2021, Adrian Chadd <adrian@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice unmodified, this list of conditions, and the following 11 * disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 33 #include <sys/bus.h> 34 #include <sys/interrupt.h> 35 #include <sys/malloc.h> 36 #include <sys/lock.h> 37 #include <sys/mutex.h> 38 #include <sys/kernel.h> 39 #include <sys/module.h> 40 #include <sys/rman.h> 41 #include <sys/gpio.h> 42 43 #include <vm/vm.h> 44 #include <vm/pmap.h> 45 #include <vm/vm_extern.h> 46 47 #include <machine/bus.h> 48 #include <machine/cpu.h> 49 50 #include <dev/fdt/fdt_common.h> 51 #include <dev/fdt/fdt_pinctrl.h> 52 53 #include <dev/gpio/gpiobusvar.h> 54 #include <dev/ofw/ofw_bus.h> 55 #include <dev/ofw/ofw_bus_subr.h> 56 57 #include <dev/qcom_tcsr/qcom_tcsr_var.h> 58 #include <dev/qcom_tcsr/qcom_tcsr_reg.h> 59 60 /* 61 * The linux-msm branches that support IPQ4018 use "ipq,tcsr". 62 * The openwrt addons use qcom,tcsr. So for now support both. 63 * 64 * Also, it's not quite clear yet (since this is the first port!) 65 * whether these options and registers are specific to the QCA IPQ401x 66 * part or show up in different linux branches as different registers 67 * but with the same driver/naming here. Let's hope that doesn't 68 * happen. 69 */ 70 static struct ofw_compat_data compat_data[] = { 71 { "qcom,tcsr", 1 }, 72 { "ipq,tcsr", 1 }, 73 { NULL, 0 } 74 }; 75 76 static int 77 qcom_tcsr_probe(device_t dev) 78 { 79 80 if (!ofw_bus_status_okay(dev)) 81 return (ENXIO); 82 83 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 84 return (ENXIO); 85 86 device_set_desc(dev, "Qualcomm Core Top Control and Status Driver"); 87 return (BUS_PROBE_DEFAULT); 88 } 89 90 static int 91 qcom_tcsr_attach(device_t dev) 92 { 93 struct qcom_tcsr_softc *sc = device_get_softc(dev); 94 int rid, ret; 95 uint32_t val; 96 97 sc->sc_dev = dev; 98 99 /* 100 * Hardware version is stored in the ofw_compat_data table. 101 */ 102 sc->hw_version = 103 ofw_bus_search_compatible(dev, compat_data)->ocd_data; 104 105 mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); 106 107 rid = 0; 108 sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 109 RF_ACTIVE); 110 if (!sc->sc_mem_res) { 111 device_printf(dev, "ERROR: Could not map memory\n"); 112 ret = ENXIO; 113 goto error; 114 } 115 116 /* 117 * Parse out the open firmware entries to see which particular 118 * configurations we need to set here. 119 */ 120 121 /* 122 * USB control select. 123 * 124 * For linux-msm on the IPQ401x, it actually calls into the SCM 125 * to make the change. OpenWRT just does a register write. 126 * We'll do the register write for now. 127 */ 128 if (OF_getencprop(ofw_bus_get_node(dev), "qcom,usb-ctrl-select", 129 &val, sizeof(val)) > 0) { 130 if (bootverbose) 131 device_printf(sc->sc_dev, 132 "USB control select (val 0x%x)\n", 133 val); 134 QCOM_TCSR_WRITE_4(sc, QCOM_TCSR_USB_PORT_SEL, val); 135 } 136 137 /* 138 * USB high speed phy mode select. 139 */ 140 if (OF_getencprop(ofw_bus_get_node(dev), "qcom,usb-hsphy-mode-select", 141 &val, sizeof(val)) > 0) { 142 if (bootverbose) 143 device_printf(sc->sc_dev, 144 "USB high speed PHY mode select (val 0x%x)\n", 145 val); 146 QCOM_TCSR_WRITE_4(sc, QCOM_TCSR_USB_HSPHY_CONFIG, val); 147 } 148 149 /* 150 * Ethernet switch subsystem interface type select. 151 */ 152 if (OF_getencprop(ofw_bus_get_node(dev), "qcom,ess-interface-select", 153 &val, sizeof(val)) > 0) { 154 uint32_t reg; 155 156 if (bootverbose) 157 device_printf(sc->sc_dev, 158 "ESS external interface select (val 0x%x)\n", 159 val); 160 reg = QCOM_TCSR_READ_4(sc, QCOM_TCSR_ESS_INTERFACE_SEL_OFFSET); 161 reg &= ~QCOM_TCSR_ESS_INTERFACE_SEL_MASK; 162 reg |= (val & QCOM_TCSR_ESS_INTERFACE_SEL_MASK); 163 QCOM_TCSR_WRITE_4(sc, QCOM_TCSR_ESS_INTERFACE_SEL_OFFSET, reg); 164 } 165 166 /* 167 * WiFi GLB select. 168 */ 169 if (OF_getencprop(ofw_bus_get_node(dev), "qcom,wifi_glb_cfg", 170 &val, sizeof(val)) > 0) { 171 if (bootverbose) 172 device_printf(sc->sc_dev, 173 "WIFI GLB select (val 0x%x)\n", 174 val); 175 QCOM_TCSR_WRITE_4(sc, QCOM_TCSR_WIFI0_GLB_CFG_OFFSET, val); 176 QCOM_TCSR_WRITE_4(sc, QCOM_TCSR_WIFI1_GLB_CFG_OFFSET, val); 177 } 178 179 /* 180 * WiFi NOC interconnect memory type. 181 */ 182 if (OF_getencprop(ofw_bus_get_node(dev), 183 "qcom,wifi_noc_memtype_m0_m2", 184 &val, sizeof(val)) > 0) { 185 if (bootverbose) 186 device_printf(sc->sc_dev, 187 "WiFi NOC memory type (val 0x%x)\n", 188 val); 189 QCOM_TCSR_WRITE_4(sc, QCOM_TCSR_PNOC_SNOC_MEMTYPE_M0_M2, val); 190 } 191 192 return (0); 193 194 error: 195 if (sc->sc_mem_res) 196 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 197 mtx_destroy(&sc->sc_mtx); 198 return (ret); 199 } 200 201 static int 202 qcom_tcsr_detach(device_t dev) 203 { 204 struct qcom_tcsr_softc *sc = device_get_softc(dev); 205 206 if (sc->sc_mem_res) 207 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 208 209 mtx_destroy(&sc->sc_mtx); 210 211 return (0); 212 } 213 214 static device_method_t qcom_tcsr_methods[] = { 215 /* Device interface */ 216 DEVMETHOD(device_probe, qcom_tcsr_probe), 217 DEVMETHOD(device_attach, qcom_tcsr_attach), 218 DEVMETHOD(device_detach, qcom_tcsr_detach), 219 220 DEVMETHOD_END 221 }; 222 223 static driver_t qcom_tcsr_driver = { 224 "qcom_tcsr", 225 qcom_tcsr_methods, 226 sizeof(struct qcom_tcsr_softc), 227 }; 228 229 /* 230 * This has to be run early, before the rest of the hardware is potentially 231 * probed/attached. 232 */ 233 EARLY_DRIVER_MODULE(qcom_tcsr, simplebus, qcom_tcsr_driver, 0, 0, 234 BUS_PASS_CPU + BUS_PASS_ORDER_EARLY); 235 SIMPLEBUS_PNP_INFO(compat_data); 236