bcm53xx_cca.c revision 1.1
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#include "com.h" 33#include "gpio.h" 34#include "bcmcca.h" 35 36#define CCA_PRIVATE 37#define CRU_PRIVATE 38#define IDM_PRIVATE 39 40#if NCOM == 0 41#error no console configured 42#endif 43 44#include <sys/cdefs.h> 45 46__KERNEL_RCSID(1, "$NetBSD: bcm53xx_cca.c,v 1.1 2012/09/01 00:04:44 matt Exp $"); 47 48#include <sys/param.h> 49#include <sys/bus.h> 50#include <sys/device.h> 51#include <sys/intr.h> 52#include <sys/systm.h> 53#include <sys/time.h> 54#include <sys/termios.h> 55 56#include <dev/ic/comreg.h> 57#include <dev/ic/comvar.h> 58 59#include <arm/mainbus/mainbus.h> 60 61#include <arm/broadcom/bcm53xx_reg.h> 62#include <arm/broadcom/bcm53xx_var.h> 63 64static int bcmcca_mainbus_match(device_t, cfdata_t, void *); 65static void bcmcca_mainbus_attach(device_t, device_t, void *); 66 67struct bcmcca_softc; 68static void bcmcca_uart_attach(struct bcmcca_softc *sc); 69#if NGPIO > 0 70static void bcmcca_gpio_attach(struct bcmcca_softc *sc); 71#endif 72 73struct bcmcca_softc { 74 device_t sc_dev; 75 bus_space_tag_t sc_bst; 76 bus_space_handle_t sc_bsh; 77 struct com_softc *sc_com_softc[2]; 78 void *sc_ih; 79 uint32_t sc_gpiopins; 80}; 81 82struct bcmcca_attach_args { 83 bus_space_tag_t ccaaa_bst; 84 bus_space_handle_t ccaaa_bsh; 85 bus_size_t ccaaa_offset; 86 bus_size_t ccaaa_size; 87 int ccaaa_channel; 88}; 89 90static struct bcmcca_softc bcmcca_sc = { 91 .sc_gpiopins = 0xffffff, /* assume all 24 pins are available */ 92}; 93 94CFATTACH_DECL_NEW(bcmcca, 0, 95 bcmcca_mainbus_match, bcmcca_mainbus_attach, NULL, NULL); 96 97static int 98bcmcca_mainbus_match(device_t parent, cfdata_t cf, void *aux) 99{ 100 if (bcmcca_sc.sc_dev != NULL) 101 return 0; 102 103 return 1; 104} 105 106static int 107bcmcca_print(void *aux, const char *pnp) 108{ 109 const struct bcmcca_attach_args * const ccaaa = aux; 110 111 if (ccaaa->ccaaa_channel != BCMCCACF_CHANNEL_DEFAULT) 112 aprint_normal(" channel %d", ccaaa->ccaaa_channel); 113 114 return QUIET; 115} 116 117static inline uint32_t 118bcmcca_read_4(struct bcmcca_softc *sc, bus_size_t o) 119{ 120 return bus_space_read_4(sc->sc_bst, sc->sc_bsh, o); 121} 122 123static inline void 124bcmcca_write_4(struct bcmcca_softc *sc, bus_size_t o, uint32_t v) 125{ 126 return bus_space_write_4(sc->sc_bst, sc->sc_bsh, o, v); 127} 128 129static int 130bcmcca_intr(void *arg) 131{ 132 struct bcmcca_softc * sc = arg; 133 int rv = 0; 134 135 uint32_t v = bcmcca_read_4(sc, MISC_INTSTATUS); 136 if (v & INTSTATUS_UARTINT) { 137 if (sc->sc_com_softc[0] != NULL) 138 rv = comintr(sc->sc_com_softc[0]); 139 if (sc->sc_com_softc[1] != NULL) { 140 int rv0 = comintr(sc->sc_com_softc[1]); 141 if (rv) 142 rv = rv0; 143 } 144 } 145 if (v & INTSTATUS_GPIOINT) { 146 147 } 148 return rv; 149} 150 151static void 152bcmcca_mainbus_attach(device_t parent, device_t self, void *aux) 153{ 154 struct bcmcca_softc * const sc = &bcmcca_sc; 155 156 sc->sc_dev = self; 157 self->dv_private = sc; 158 159 sc->sc_bst = bcm53xx_ioreg_bst; 160 161 bus_space_subregion (sc->sc_bst, bcm53xx_ioreg_bsh, 162 CCA_MISC_BASE, CCA_MISC_SIZE, &sc->sc_bsh); 163 164 uint32_t chipid = bcmcca_read_4(sc, MISC_CHIPID); 165 166 aprint_naive("\n"); 167 aprint_normal(": BCM%u (Rev %c%u)\n", 168 (u_int)__SHIFTOUT(chipid, CHIPID_ID), 169 (u_int)('A' + (__SHIFTOUT(chipid, CHIPID_REV) >> 2)), 170 (u_int)(__SHIFTOUT(chipid, CHIPID_REV) & 3)); 171 172 sc->sc_ih = intr_establish(IRQ_CCA, IPL_TTY, IST_LEVEL, bcmcca_intr, sc); 173 if (sc->sc_ih == NULL) { 174 aprint_error_dev(sc->sc_dev, "failed to establish CCA intr\n"); 175 return; 176 } 177 aprint_normal_dev(sc->sc_dev, "interrupting at irq %d\n", IRQ_CCA); 178 179 bcmcca_uart_attach(sc); 180#if NGPIO > 0 181 bcmcca_gpio_attach(sc); 182#endif 183} 184 185static void 186bcmcca_uart_attach(struct bcmcca_softc *sc) 187{ 188 struct bcmcca_attach_args ccaaa = { 189 .ccaaa_bst = sc->sc_bst, 190 .ccaaa_bsh = sc->sc_bsh, 191 .ccaaa_offset = CCA_UART0_BASE, 192 .ccaaa_size = COM_NPORTS, 193 .ccaaa_channel = 0, 194 }; 195 device_t dv; 196 197#if 0 198 /* 199 * Force the UART to use the BCM53xx reference clock. 200 */ 201 uint32_t v = bcmcca_read_4(sc, IDM_BASE + APBX_IDM_IO_CONTROL_DIRECT); 202 if (v & IO_CONTROL_DIRECT_UARTCLKSEL) { 203 v &= ~IO_CONTROL_DIRECT_UARTCLKSEL; 204 bcmcca_write_4(sc, IDM_BASE + APBX_IDM_IO_CONTROL_DIRECT, v); 205 } 206 v = bcmcca_read_4(sc, MISC_CORECTL); 207 if (v & CORECTL_UART_CLK_OVERRIDE) { 208 v &= ~CORECTL_UART_CLK_OVERRIDE; 209 bcmcca_write_4(sc, MISC_CORECTL, v); 210 } 211#endif 212 213 bool children = false; 214 215 dv = config_found(sc->sc_dev, &ccaaa, bcmcca_print); 216 if (dv != NULL) { 217 sc->sc_com_softc[0] = device_private(dv); 218 children = true; 219 } 220 221 ccaaa.ccaaa_offset = CCA_UART1_BASE; 222 ccaaa.ccaaa_channel = 1; 223 224 dv = config_found(sc->sc_dev, &ccaaa, bcmcca_print); 225 if (dv != NULL) { 226 sc->sc_com_softc[1] = device_private(dv); 227 children = true; 228 /* 229 * UART1 uses the same pins as GPIO pins 15..12 230 */ 231 sc->sc_gpiopins &= ~__BITS(15,12); 232 } 233 234 if (children) { 235 /* 236 * If we configured children, enable interrupts for the UART(s). 237 */ 238 uint32_t intmask = bcmcca_read_4(sc, MISC_INTMASK); 239 intmask |= INTMASK_UARTINT; 240 bcmcca_write_4(sc, MISC_INTMASK, intmask); 241 } 242} 243 244static int com_cca_match(device_t, cfdata_t, void *); 245static void com_cca_attach(device_t, device_t, void *); 246 247CFATTACH_DECL_NEW(com_cca, sizeof(struct com_softc), 248 com_cca_match, com_cca_attach, NULL, NULL); 249 250static int 251com_cca_match(device_t parent, cfdata_t cf, void *aux) 252{ 253 struct bcmcca_attach_args * const ccaaa = aux; 254 const int channel = cf->cf_loc[BCMCCACF_CHANNEL]; 255 const bus_addr_t addr = BCM53XX_IOREG_PBASE + ccaaa->ccaaa_offset; 256 bus_space_handle_t bsh; 257 258 KASSERT(ccaaa->ccaaa_offset == CCA_UART0_BASE || ccaaa->ccaaa_offset == CCA_UART1_BASE); 259 KASSERT(bcmcca_sc.sc_com_softc[ccaaa->ccaaa_channel] == NULL); 260 261 if (channel != BCMCCACF_CHANNEL_DEFAULT && channel != ccaaa->ccaaa_channel) 262 return 0; 263 264 if (com_is_console(ccaaa->ccaaa_bst, addr, NULL)) 265 return 1; 266 267 bus_space_subregion(ccaaa->ccaaa_bst, ccaaa->ccaaa_bsh, 268 ccaaa->ccaaa_offset, ccaaa->ccaaa_size, &bsh); 269 270 return comprobe1(ccaaa->ccaaa_bst, bsh); 271} 272 273static void 274com_cca_attach(device_t parent, device_t self, void *aux) 275{ 276 struct com_softc * const sc = device_private(self); 277 struct bcmcca_attach_args * const ccaaa = aux; 278 const bus_addr_t addr = BCM53XX_IOREG_PBASE + ccaaa->ccaaa_offset; 279 bus_space_handle_t bsh; 280 281 sc->sc_dev = self; 282 sc->sc_frequency = BCM53XX_REF_CLK; 283 sc->sc_type = COM_TYPE_NORMAL; 284 285 if (com_is_console(ccaaa->ccaaa_bst, addr, &bsh) == 0 && 286 bus_space_subregion(ccaaa->ccaaa_bst, ccaaa->ccaaa_bsh, 287 ccaaa->ccaaa_offset, ccaaa->ccaaa_size, &bsh)) { 288 panic(": can't map registers\n"); 289 return; 290 } 291 COM_INIT_REGS(sc->sc_regs, ccaaa->ccaaa_bst, bsh, addr); 292 293 com_attach_subr(sc); 294} 295 296#if NGPIO > 0 297static void 298bcmcca_gpio_attach(struct bcmcca_softc *sc) 299{ 300 /* 301 * First see if there are any pins being used as GPIO pins... 302 */ 303 uint32_t v = bcmcca_read(sc, CRU_BASE + CRU_GPIO_SELECT); 304 if (v == 0) 305 return; 306} 307#endif 308