1 /* $NetBSD: tegra_hdaudio.c,v 1.9 2017/09/27 10:50:06 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following 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 ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * 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 __KERNEL_RCSID(0, "$NetBSD: tegra_hdaudio.c,v 1.9 2017/09/27 10:50:06 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/device.h> 35 #include <sys/intr.h> 36 #include <sys/systm.h> 37 #include <sys/kernel.h> 38 39 #include <dev/hdaudio/hdaudioreg.h> 40 #include <dev/hdaudio/hdaudiovar.h> 41 42 #include <arm/nvidia/tegra_var.h> 43 #include <arm/nvidia/tegra_pmcreg.h> 44 #include <arm/nvidia/tegra_hdaudioreg.h> 45 46 #include <dev/fdt/fdtvar.h> 47 48 #define TEGRA_HDAUDIO_OFFSET 0x8000 49 50 #define TEGRA_HDA_IFPS_BAR0_REG 0x0080 51 #define TEGRA_HDA_IFPS_CONFIG_REG 0x0180 52 #define TEGRA_HDA_IFPS_INTR_REG 0x0188 53 #define TEGRA_HDA_CFG_CMD_REG 0x1004 54 #define TEGRA_HDA_CFG_BAR0_REG 0x1010 55 56 static int tegra_hdaudio_match(device_t, cfdata_t, void *); 57 static void tegra_hdaudio_attach(device_t, device_t, void *); 58 static int tegra_hdaudio_detach(device_t, int); 59 static int tegra_hdaudio_rescan(device_t, const char *, const int *); 60 static void tegra_hdaudio_childdet(device_t, device_t); 61 62 static int tegra_hdaudio_intr(void *); 63 64 struct tegra_hdaudio_softc { 65 struct hdaudio_softc sc; 66 bus_space_tag_t sc_bst; 67 bus_space_handle_t sc_bsh; 68 void *sc_ih; 69 int sc_phandle; 70 struct clk *sc_clk_hda; 71 struct clk *sc_clk_hda2hdmi; 72 struct clk *sc_clk_hda2codec_2x; 73 struct fdtbus_reset *sc_rst_hda; 74 struct fdtbus_reset *sc_rst_hda2hdmi; 75 struct fdtbus_reset *sc_rst_hda2codec_2x; 76 }; 77 78 static int tegra_hdaudio_init_clocks(struct tegra_hdaudio_softc *); 79 static void tegra_hdaudio_init(struct tegra_hdaudio_softc *); 80 81 CFATTACH_DECL2_NEW(tegra_hdaudio, sizeof(struct tegra_hdaudio_softc), 82 tegra_hdaudio_match, tegra_hdaudio_attach, tegra_hdaudio_detach, NULL, 83 tegra_hdaudio_rescan, tegra_hdaudio_childdet); 84 85 static int 86 tegra_hdaudio_match(device_t parent, cfdata_t cf, void *aux) 87 { 88 const char * const compatible[] = { 89 "nvidia,tegra210-hda", 90 "nvidia,tegra124-hda", 91 NULL 92 }; 93 struct fdt_attach_args * const faa = aux; 94 95 return of_match_compatible(faa->faa_phandle, compatible); 96 } 97 98 static void 99 tegra_hdaudio_attach(device_t parent, device_t self, void *aux) 100 { 101 struct tegra_hdaudio_softc * const sc = device_private(self); 102 struct fdt_attach_args * const faa = aux; 103 const int phandle = faa->faa_phandle; 104 char intrstr[128]; 105 bus_addr_t addr; 106 bus_size_t size; 107 int error; 108 109 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 110 aprint_error(": couldn't get registers\n"); 111 return; 112 } 113 sc->sc_clk_hda = fdtbus_clock_get(phandle, "hda"); 114 if (sc->sc_clk_hda == NULL) { 115 aprint_error(": couldn't get clock hda\n"); 116 return; 117 } 118 sc->sc_clk_hda2hdmi = fdtbus_clock_get(phandle, "hda2hdmi"); 119 if (sc->sc_clk_hda2hdmi == NULL) { 120 aprint_error(": couldn't get clock hda2hdmi\n"); 121 return; 122 } 123 sc->sc_clk_hda2codec_2x = fdtbus_clock_get(phandle, "hda2codec_2x"); 124 if (sc->sc_clk_hda2codec_2x == NULL) { 125 aprint_error(": couldn't get clock hda2codec_2x\n"); 126 return; 127 } 128 sc->sc_rst_hda = fdtbus_reset_get(phandle, "hda"); 129 if (sc->sc_rst_hda == NULL) { 130 aprint_error(": couldn't get reset hda\n"); 131 return; 132 } 133 sc->sc_rst_hda2hdmi = fdtbus_reset_get(phandle, "hda2hdmi"); 134 if (sc->sc_rst_hda2hdmi == NULL) { 135 aprint_error(": couldn't get reset hda2hdmi\n"); 136 return; 137 } 138 sc->sc_rst_hda2codec_2x = fdtbus_reset_get(phandle, "hda2codec_2x"); 139 if (sc->sc_rst_hda2codec_2x == NULL) { 140 aprint_error(": couldn't get reset hda2codec_2x\n"); 141 return; 142 } 143 144 sc->sc_phandle = phandle; 145 sc->sc_bst = faa->faa_bst; 146 error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh); 147 if (error) { 148 aprint_error(": couldn't map %#llx: %d", (uint64_t)addr, error); 149 return; 150 } 151 152 sc->sc.sc_dev = self; 153 sc->sc.sc_memt = faa->faa_bst; 154 bus_space_subregion(sc->sc.sc_memt, sc->sc_bsh, TEGRA_HDAUDIO_OFFSET, 155 size - TEGRA_HDAUDIO_OFFSET, &sc->sc.sc_memh); 156 sc->sc.sc_memvalid = true; 157 sc->sc.sc_dmat = faa->faa_dmat; 158 159 aprint_naive("\n"); 160 aprint_normal(": HDA\n"); 161 162 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 163 aprint_error_dev(self, "failed to decode interrupt\n"); 164 return; 165 } 166 167 sc->sc_ih = fdtbus_intr_establish(phandle, 0, IPL_AUDIO, 0, 168 tegra_hdaudio_intr, sc); 169 if (sc->sc_ih == NULL) { 170 aprint_error_dev(self, "couldn't establish interrupt on %s\n", 171 intrstr); 172 return; 173 } 174 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 175 176 tegra_pmc_power(PMC_PARTID_DISB, true); 177 178 if (tegra_hdaudio_init_clocks(sc) != 0) 179 return; 180 181 tegra_hdaudio_init(sc); 182 183 hdaudio_attach(self, &sc->sc); 184 } 185 186 static int 187 tegra_hdaudio_init_clocks(struct tegra_hdaudio_softc *sc) 188 { 189 device_t self = sc->sc.sc_dev; 190 int error; 191 192 /* Assert resets */ 193 fdtbus_reset_assert(sc->sc_rst_hda); 194 fdtbus_reset_assert(sc->sc_rst_hda2hdmi); 195 fdtbus_reset_assert(sc->sc_rst_hda2codec_2x); 196 197 /* Set hda to 48MHz and enable it */ 198 error = clk_set_rate(sc->sc_clk_hda, 48000000); 199 if (error) { 200 aprint_error_dev(self, "couldn't set hda frequency: %d\n", 201 error); 202 return error; 203 } 204 error = clk_enable(sc->sc_clk_hda); 205 if (error) { 206 aprint_error_dev(self, "couldn't enable clock hda: %d\n", 207 error); 208 return error; 209 } 210 211 /* Enable hda2hdmi clock */ 212 error = clk_enable(sc->sc_clk_hda2hdmi); 213 if (error) { 214 aprint_error_dev(self, "couldn't enable clock hda2hdmi: %d\n", 215 error); 216 return error; 217 } 218 219 /* Set hda2codec_2x to 48MHz and enable it */ 220 error = clk_set_rate(sc->sc_clk_hda2codec_2x, 48000000); 221 if (error) { 222 aprint_error_dev(self, 223 "couldn't set clock hda2codec_2x frequency: %d\n", error); 224 return error; 225 } 226 error = clk_enable(sc->sc_clk_hda2codec_2x); 227 if (error) { 228 aprint_error_dev(self, 229 "couldn't enable clock hda2codec_2x: %d\n", error); 230 return error; 231 } 232 233 /* De-assert resets */ 234 fdtbus_reset_deassert(sc->sc_rst_hda); 235 fdtbus_reset_deassert(sc->sc_rst_hda2hdmi); 236 fdtbus_reset_deassert(sc->sc_rst_hda2codec_2x); 237 238 return 0; 239 } 240 241 static void 242 tegra_hdaudio_init(struct tegra_hdaudio_softc *sc) 243 { 244 tegra_reg_set_clear(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_IFPS_CONFIG_REG, 245 TEGRA_HDA_IFPS_CONFIG_FPCI_EN, 0); 246 tegra_reg_set_clear(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_CFG_CMD_REG, 247 TEGRA_HDA_CFG_CMD_ENABLE_SERR | 248 TEGRA_HDA_CFG_CMD_BUS_MASTER | 249 TEGRA_HDA_CFG_CMD_MEM_SPACE | 250 TEGRA_HDA_CFG_CMD_IO_SPACE, 251 TEGRA_HDA_CFG_CMD_DISABLE_INTR); 252 bus_space_write_4(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_CFG_BAR0_REG, 253 0xffffffff); 254 bus_space_write_4(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_CFG_BAR0_REG, 255 0x00004000); 256 bus_space_write_4(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_IFPS_BAR0_REG, 257 TEGRA_HDA_CFG_BAR0_START); 258 tegra_reg_set_clear(sc->sc_bst, sc->sc_bsh, TEGRA_HDA_IFPS_INTR_REG, 259 TEGRA_HDA_IFPS_INTR_EN, 0); 260 } 261 262 static int 263 tegra_hdaudio_detach(device_t self, int flags) 264 { 265 struct tegra_hdaudio_softc * const sc = device_private(self); 266 267 hdaudio_detach(&sc->sc, flags); 268 269 if (sc->sc_ih) { 270 fdtbus_intr_disestablish(sc->sc_phandle, sc->sc_ih); 271 sc->sc_ih = NULL; 272 } 273 274 sc->sc.sc_memvalid = false; 275 276 return 0; 277 } 278 279 static int 280 tegra_hdaudio_rescan(device_t self, const char *ifattr, const int *locs) 281 { 282 struct tegra_hdaudio_softc * const sc = device_private(self); 283 284 return hdaudio_rescan(&sc->sc, ifattr, locs); 285 } 286 287 static void 288 tegra_hdaudio_childdet(device_t self, device_t child) 289 { 290 struct tegra_hdaudio_softc * const sc = device_private(self); 291 292 hdaudio_childdet(&sc->sc, child); 293 } 294 295 static int 296 tegra_hdaudio_intr(void *priv) 297 { 298 struct tegra_hdaudio_softc * const sc = priv; 299 300 return hdaudio_intr(&sc->sc); 301 } 302