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