1 /*- 2 * Copyright (c) 2016 Rubicon Communications, LLC (Netgate) 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/bus.h> 33 #include <sys/kernel.h> 34 #include <sys/module.h> 35 #include <sys/sysctl.h> 36 37 #include <machine/bus.h> 38 39 #include <arm/ti/am335x/am335x_scm.h> 40 #include <arm/ti/ti_cpuid.h> 41 #include <arm/ti/ti_scm.h> 42 43 #include <dev/extres/syscon/syscon.h> 44 #include "syscon_if.h" 45 46 #define TZ_ZEROC 2731 47 48 struct am335x_scm_softc { 49 int sc_last_temp; 50 struct sysctl_oid *sc_temp_oid; 51 struct syscon *syscon; 52 }; 53 54 static int 55 am335x_scm_temp_sysctl(SYSCTL_HANDLER_ARGS) 56 { 57 device_t dev; 58 int i, temp; 59 struct am335x_scm_softc *sc; 60 uint32_t reg; 61 62 dev = (device_t)arg1; 63 sc = device_get_softc(dev); 64 65 /* Read the temperature and convert to Kelvin. */ 66 for(i = 50; i > 0; i--) { 67 reg = SYSCON_READ_4(sc->syscon, SCM_BGAP_CTRL); 68 if ((reg & SCM_BGAP_EOCZ) == 0) 69 break; 70 DELAY(50); 71 } 72 if ((reg & SCM_BGAP_EOCZ) == 0) { 73 sc->sc_last_temp = 74 (reg >> SCM_BGAP_TEMP_SHIFT) & SCM_BGAP_TEMP_MASK; 75 sc->sc_last_temp *= 10; 76 } 77 temp = sc->sc_last_temp + TZ_ZEROC; 78 79 return (sysctl_handle_int(oidp, &temp, 0, req)); 80 } 81 82 static void 83 am335x_scm_identify(driver_t *driver, device_t parent) 84 { 85 device_t child; 86 87 /* AM335x only. */ 88 if (ti_chip() != CHIP_AM335X) 89 return; 90 91 /* Make sure we attach only once. */ 92 if (device_find_child(parent, "am335x_scm", -1) != NULL) 93 return; 94 95 child = device_add_child(parent, "am335x_scm", -1); 96 if (child == NULL) 97 device_printf(parent, "cannot add ti_scm child\n"); 98 } 99 100 static int 101 am335x_scm_probe(device_t dev) 102 { 103 /* Just allow the first one */ 104 if (strcmp(device_get_nameunit(dev), "am335x_scm0") != 0) 105 return (ENXIO); 106 107 device_set_desc(dev, "AM335x Control Module Extension"); 108 109 return (BUS_PROBE_DEFAULT); 110 } 111 112 static int 113 am335x_scm_attach(device_t dev) 114 { 115 struct am335x_scm_softc *sc; 116 struct sysctl_ctx_list *ctx; 117 struct sysctl_oid_list *tree; 118 uint32_t reg; 119 phandle_t opp_table; 120 int err; 121 122 sc = device_get_softc(dev); 123 124 /* FIXME: For now; Go and kidnap syscon from opp-table */ 125 opp_table = OF_finddevice("/opp-table"); 126 if (opp_table == -1) { 127 device_printf(dev, "Cant find /opp-table\n"); 128 return (ENXIO); 129 } 130 if (!OF_hasprop(opp_table, "syscon")) { 131 device_printf(dev, "/opp-table missing syscon property\n"); 132 return (ENXIO); 133 } 134 err = syscon_get_by_ofw_property(dev, opp_table, "syscon", &sc->syscon); 135 if (err) { 136 device_printf(dev, "Failed to get syscon\n"); 137 return (ENXIO); 138 } 139 140 /* Reset the digital outputs. */ 141 SYSCON_WRITE_4(sc->syscon, SCM_BGAP_CTRL, 0); 142 reg = SYSCON_READ_4(sc->syscon, SCM_BGAP_CTRL); 143 DELAY(500); 144 /* Set continuous mode. */ 145 SYSCON_WRITE_4(sc->syscon, SCM_BGAP_CTRL, SCM_BGAP_CONTCONV); 146 reg = SYSCON_READ_4(sc->syscon, SCM_BGAP_CTRL); 147 DELAY(500); 148 /* Start the ADC conversion. */ 149 reg = SCM_BGAP_CLRZ | SCM_BGAP_CONTCONV | SCM_BGAP_SOC; 150 SYSCON_WRITE_4(sc->syscon, SCM_BGAP_CTRL, reg); 151 152 /* Temperature sysctl. */ 153 ctx = device_get_sysctl_ctx(dev); 154 tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); 155 sc->sc_temp_oid = SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, 156 "temperature", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 157 dev, 0, am335x_scm_temp_sysctl, "IK", "Current temperature"); 158 159 return (0); 160 } 161 162 static int 163 am335x_scm_detach(device_t dev) 164 { 165 struct am335x_scm_softc *sc; 166 167 sc = device_get_softc(dev); 168 169 /* Remove temperature sysctl. */ 170 if (sc->sc_temp_oid != NULL) 171 sysctl_remove_oid(sc->sc_temp_oid, 1, 0); 172 173 /* Stop the bandgap ADC. */ 174 SYSCON_WRITE_4(sc->syscon, SCM_BGAP_CTRL, SCM_BGAP_BGOFF); 175 176 return (0); 177 } 178 179 static device_method_t am335x_scm_methods[] = { 180 DEVMETHOD(device_identify, am335x_scm_identify), 181 DEVMETHOD(device_probe, am335x_scm_probe), 182 DEVMETHOD(device_attach, am335x_scm_attach), 183 DEVMETHOD(device_detach, am335x_scm_detach), 184 185 DEVMETHOD_END 186 }; 187 188 static driver_t am335x_scm_driver = { 189 "am335x_scm", 190 am335x_scm_methods, 191 sizeof(struct am335x_scm_softc), 192 }; 193 194 DRIVER_MODULE(am335x_scm, ti_scm, am335x_scm_driver, 0, 0); 195 MODULE_VERSION(am335x_scm, 1); 196 MODULE_DEPEND(am335x_scm, ti_scm_syscon, 1, 1, 1); 197