1 1.1 matt /*- 2 1.1 matt * Copyright (c) 2012 The NetBSD Foundation, Inc. 3 1.1 matt * All rights reserved. 4 1.1 matt * 5 1.1 matt * This code is derived from software contributed to The NetBSD Foundation 6 1.1 matt * by Matt Thomas of 3am Software Foundry. 7 1.1 matt * 8 1.1 matt * Redistribution and use in source and binary forms, with or without 9 1.1 matt * modification, are permitted provided that the following conditions 10 1.1 matt * are met: 11 1.1 matt * 1. Redistributions of source code must retain the above copyright 12 1.1 matt * notice, this list of conditions and the following disclaimer. 13 1.1 matt * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 matt * notice, this list of conditions and the following disclaimer in the 15 1.1 matt * documentation and/or other materials provided with the distribution. 16 1.1 matt * 17 1.1 matt * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 1.1 matt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 1.1 matt * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 1.1 matt * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 1.1 matt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 1.1 matt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 1.1 matt * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 1.1 matt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 1.1 matt * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 1.1 matt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 1.1 matt * POSSIBILITY OF SUCH DAMAGE. 28 1.1 matt */ 29 1.1 matt 30 1.1 matt #include "opt_broadcom.h" 31 1.1 matt #include "locators.h" 32 1.1 matt 33 1.1 matt #define CRU_PRIVATE 34 1.1 matt #define IDM_PRIVATE 35 1.1 matt 36 1.1 matt #include <sys/cdefs.h> 37 1.1 matt 38 1.4 skrll __KERNEL_RCSID(1, "$NetBSD: bcm53xx_idm.c,v 1.4 2024/02/16 15:11:17 skrll Exp $"); 39 1.1 matt 40 1.1 matt #include <sys/param.h> 41 1.1 matt #include <sys/bus.h> 42 1.1 matt #include <sys/device.h> 43 1.1 matt #include <sys/intr.h> 44 1.1 matt #include <sys/systm.h> 45 1.1 matt 46 1.1 matt #include <arm/mainbus/mainbus.h> 47 1.1 matt 48 1.1 matt #include <arm/broadcom/bcm53xx_reg.h> 49 1.1 matt #include <arm/broadcom/bcm53xx_var.h> 50 1.1 matt 51 1.1 matt struct idm_info { 52 1.1 matt bus_size_t idm_offset; 53 1.1 matt const char *idm_name; 54 1.1 matt int idm_port; 55 1.1 matt bool (*idm_unreset)(bus_space_tag_t, bus_space_handle_t, 56 1.1 matt const struct idm_info *); 57 1.1 matt }; 58 1.1 matt 59 1.1 matt static bool 60 1.1 matt bcmeth_unreset(bus_space_tag_t bst, bus_space_handle_t bsh, 61 1.1 matt const struct idm_info *idm) 62 1.4 skrll { 63 1.1 matt /* 64 1.1 matt * To enable any GMAC, we must enable all off them. 65 1.1 matt */ 66 1.1 matt static const bus_size_t regoff[] = { 67 1.2 matt IDM_BASE + IDM_AMAC0_BASE, 68 1.2 matt IDM_BASE + IDM_AMAC1_BASE, 69 1.2 matt IDM_BASE + IDM_AMAC2_BASE, 70 1.2 matt IDM_BASE + IDM_AMAC3_BASE, 71 1.1 matt }; 72 1.1 matt static bool bcmeth_init_done; 73 1.1 matt if (!bcmeth_init_done) { 74 1.1 matt for (size_t idx = 0; idx < __arraycount(regoff); idx++) { 75 1.2 matt const bus_size_t off = regoff[idx]; 76 1.2 matt bus_space_write_4(bst, bsh, off + IDM_RESET_CONTROL, 0); 77 1.2 matt uint32_t v = bus_space_read_4(bst, bsh, 78 1.2 matt off + IDM_IO_CONTROL_DIRECT); 79 1.2 matt /* 80 1.2 matt * Clear read-allocate and write-allocate bits from 81 1.3 matt * ACP cache access so we don't pollute the caches with 82 1.3 matt * DMA traffic. 83 1.2 matt */ 84 1.2 matt v &= ~IO_CONTROL_DIRECT_ARCACHE; 85 1.2 matt v &= ~IO_CONTROL_DIRECT_AWCACHE; 86 1.3 matt #if 0 87 1.3 matt v |= __SHIFTIN(AXCACHE_WA, IO_CONTROL_DIRECT_ARCACHE); 88 1.3 matt v |= __SHIFTIN(AXCACHE_RA, IO_CONTROL_DIRECT_AWCACHE); 89 1.3 matt #endif 90 1.2 matt v |= __SHIFTIN(AXCACHE_C|AXCACHE_B, IO_CONTROL_DIRECT_ARCACHE); 91 1.2 matt v |= __SHIFTIN(AXCACHE_C|AXCACHE_B, IO_CONTROL_DIRECT_AWCACHE); 92 1.3 matt /* 93 1.3 matt * These are the default but make sure they are 94 1.3 matt * properly set. 95 1.3 matt */ 96 1.3 matt v |= __SHIFTIN(0x1F, IO_CONTROL_DIRECT_ARUSER); 97 1.3 matt v |= __SHIFTIN(0x1F, IO_CONTROL_DIRECT_AWUSER); 98 1.3 matt v |= IO_CONTROL_DIRECT_CLK_250_SEL; 99 1.3 matt v |= IO_CONTROL_DIRECT_DIRECT_GMII_MODE; 100 1.3 matt v |= IO_CONTROL_DIRECT_SOURCE_SYNC_MODE_EN; 101 1.3 matt v |= IO_CONTROL_DIRECT_CLK_GATING_EN; 102 1.3 matt 103 1.2 matt bus_space_write_4(bst, bsh, off + IDM_IO_CONTROL_DIRECT, 104 1.2 matt v); 105 1.1 matt } 106 1.1 matt bcmeth_init_done = true; 107 1.1 matt } 108 1.1 matt return true; 109 1.1 matt } 110 1.1 matt 111 1.1 matt static bool 112 1.1 matt bcmccb_idm_unreset(bus_space_tag_t bst, bus_space_handle_t bsh, 113 1.1 matt const struct idm_info *idm) 114 1.1 matt { 115 1.1 matt if (idm->idm_offset == 0) 116 1.1 matt return true; 117 1.1 matt 118 1.1 matt /* 119 1.1 matt * If the device might be in reset, let's try to take it out of it. 120 1.1 matt */ 121 1.1 matt bus_size_t o = IDM_BASE + idm->idm_offset + IDM_RESET_CONTROL; 122 1.1 matt uint32_t v = bus_space_read_4(bst, bsh, o); 123 1.1 matt if (v & 1) { 124 1.1 matt v &= ~1; 125 1.1 matt bus_space_write_4(bst, bsh, o, v); 126 1.1 matt } 127 1.1 matt return true; 128 1.1 matt } 129 1.1 matt 130 1.1 matt static bool 131 1.1 matt bcmpax2_idm_unreset(bus_space_tag_t bst, bus_space_handle_t bsh, 132 1.1 matt const struct idm_info *idm) 133 1.1 matt { 134 1.1 matt uint32_t v = bus_space_read_4(bst, bsh, CRU_BASE + CRU_STRAPS_CONTROL); 135 1.1 matt 136 1.1 matt if (v & STRAP_USB3_SEL) 137 1.1 matt return false; 138 1.1 matt 139 1.1 matt return bcmccb_idm_unreset(bst, bsh, idm); 140 1.1 matt } 141 1.1 matt 142 1.1 matt static bool 143 1.1 matt bcmxhci_idm_unreset(bus_space_tag_t bst, bus_space_handle_t bsh, 144 1.1 matt const struct idm_info *idm) 145 1.1 matt { 146 1.1 matt uint32_t v = bus_space_read_4(bst, bsh, CRU_BASE + CRU_STRAPS_CONTROL); 147 1.1 matt 148 1.1 matt if ((v & STRAP_USB3_SEL) == 0) 149 1.1 matt return false; 150 1.1 matt 151 1.1 matt return bcmccb_idm_unreset(bst, bsh, idm); 152 1.1 matt } 153 1.1 matt 154 1.1 matt static const struct idm_info bcm53xx_idm_info[] = { 155 1.1 matt { 0, "bcmi2c", BCMCCBCF_PORT_DEFAULT, bcmccb_idm_unreset }, 156 1.1 matt { 0, "bcmmdio", BCMCCBCF_PORT_DEFAULT, bcmccb_idm_unreset }, 157 1.1 matt { 0, "bcmrng", BCMCCBCF_PORT_DEFAULT, bcmccb_idm_unreset }, 158 1.1 matt { IDM_PCIE_M0_BASE, "bcmpax", 0, bcmccb_idm_unreset }, 159 1.1 matt { IDM_PCIE_M1_BASE, "bcmpax", 1, bcmccb_idm_unreset }, 160 1.1 matt { IDM_PCIE_M2_BASE, "bcmpax", 2, bcmpax2_idm_unreset }, 161 1.1 matt { IDM_AMAC0_BASE, "bcmeth", 0, bcmeth_unreset }, 162 1.1 matt { IDM_AMAC1_BASE, "bcmeth", 1, bcmeth_unreset }, 163 1.1 matt { IDM_AMAC2_BASE, "bcmeth", 2, bcmeth_unreset }, 164 1.1 matt { IDM_AMAC3_BASE, "bcmeth", 3, bcmeth_unreset }, 165 1.1 matt { IDM_USB3_BASE, "xhci", BCMCCBCF_PORT_DEFAULT, bcmxhci_idm_unreset }, 166 1.1 matt { IDM_SDIO_BASE, "sdhc", BCMCCBCF_PORT_DEFAULT, bcmccb_idm_unreset }, 167 1.1 matt { IDM_USB2_BASE, "bcmusb", BCMCCBCF_PORT_DEFAULT, bcmccb_idm_unreset }, 168 1.1 matt }; 169 1.1 matt 170 1.1 matt static const struct idm_info * 171 1.1 matt bcmccb_idm_lookup(const struct bcm_locators * const loc) 172 1.1 matt { 173 1.1 matt const struct idm_info *idm = bcm53xx_idm_info; 174 1.1 matt for (size_t i = 0; i < __arraycount(bcm53xx_idm_info); i++, idm++) { 175 1.1 matt if (strcmp(idm->idm_name, loc->loc_name) == 0 176 1.1 matt && idm->idm_port == loc->loc_port) { 177 1.1 matt return idm; 178 1.1 matt } 179 1.1 matt } 180 1.1 matt return NULL; 181 1.1 matt } 182 1.1 matt 183 1.1 matt bool 184 1.1 matt bcm53xx_idm_device_init(const struct bcm_locators *loc, bus_space_tag_t bst, 185 1.1 matt bus_space_handle_t bsh) 186 1.1 matt { 187 1.1 matt const struct idm_info * const idm = bcmccb_idm_lookup(loc); 188 1.1 matt if (idm == NULL) 189 1.1 matt return false; 190 1.1 matt 191 1.1 matt /* 192 1.1 matt * If the device might be in reset, let's try to take it out of it. 193 1.1 matt */ 194 1.1 matt return (*idm->idm_unreset)(bst, bsh, idm); 195 1.1 matt } 196