11.1Smatt/*-
21.1Smatt * Copyright (c) 2012 The NetBSD Foundation, Inc.
31.1Smatt * All rights reserved.
41.1Smatt *
51.1Smatt * This code is derived from software contributed to The NetBSD Foundation
61.1Smatt * by Matt Thomas of 3am Software Foundry.
71.1Smatt *
81.1Smatt * Redistribution and use in source and binary forms, with or without
91.1Smatt * modification, are permitted provided that the following conditions
101.1Smatt * are met:
111.1Smatt * 1. Redistributions of source code must retain the above copyright
121.1Smatt *    notice, this list of conditions and the following disclaimer.
131.1Smatt * 2. Redistributions in binary form must reproduce the above copyright
141.1Smatt *    notice, this list of conditions and the following disclaimer in the
151.1Smatt *    documentation and/or other materials provided with the distribution.
161.1Smatt *
171.1Smatt * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
181.1Smatt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
191.1Smatt * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
201.1Smatt * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
211.1Smatt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
221.1Smatt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
231.1Smatt * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
241.1Smatt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
251.1Smatt * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
261.1Smatt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
271.1Smatt * POSSIBILITY OF SUCH DAMAGE.
281.1Smatt */
291.1Smatt
301.1Smatt#include "opt_broadcom.h"
311.1Smatt#include "locators.h"
321.1Smatt#include "com.h"
331.1Smatt#include "gpio.h"
341.1Smatt#include "bcmcca.h"
351.1Smatt
361.1Smatt#define	CCA_PRIVATE
371.1Smatt#define CRU_PRIVATE
381.1Smatt#define IDM_PRIVATE
391.1Smatt
401.1Smatt#if NCOM == 0
411.1Smatt#error no console configured
421.1Smatt#endif
431.1Smatt
441.1Smatt#include <sys/cdefs.h>
451.1Smatt
461.6Sskrll__KERNEL_RCSID(1, "$NetBSD: bcm53xx_cca.c,v 1.6 2024/02/16 15:11:17 skrll Exp $");
471.1Smatt
481.1Smatt#include <sys/param.h>
491.1Smatt#include <sys/bus.h>
501.1Smatt#include <sys/device.h>
511.1Smatt#include <sys/intr.h>
521.1Smatt#include <sys/systm.h>
531.1Smatt#include <sys/time.h>
541.1Smatt#include <sys/termios.h>
551.1Smatt
561.1Smatt#include <dev/ic/comreg.h>
571.1Smatt#include <dev/ic/comvar.h>
581.1Smatt
591.1Smatt#include <arm/mainbus/mainbus.h>
601.1Smatt
611.1Smatt#include <arm/broadcom/bcm53xx_reg.h>
621.1Smatt#include <arm/broadcom/bcm53xx_var.h>
631.1Smatt
641.1Smattstatic int bcmcca_mainbus_match(device_t, cfdata_t, void *);
651.1Smattstatic void bcmcca_mainbus_attach(device_t, device_t, void *);
661.1Smatt
671.1Smattstruct bcmcca_softc;
681.1Smattstatic void bcmcca_uart_attach(struct bcmcca_softc *sc);
691.1Smatt#if NGPIO > 0
701.1Smattstatic void bcmcca_gpio_attach(struct bcmcca_softc *sc);
711.1Smatt#endif
721.1Smatt
731.1Smattstruct bcmcca_softc {
741.1Smatt	device_t sc_dev;
751.1Smatt	bus_space_tag_t sc_bst;
761.1Smatt	bus_space_handle_t sc_bsh;
771.1Smatt	struct com_softc *sc_com_softc[2];
781.1Smatt	void *sc_ih;
791.1Smatt	uint32_t sc_gpiopins;
801.1Smatt};
811.1Smatt
821.1Smattstruct bcmcca_attach_args {
831.1Smatt	bus_space_tag_t ccaaa_bst;
841.1Smatt	bus_space_handle_t ccaaa_bsh;
851.1Smatt	bus_size_t ccaaa_offset;
861.1Smatt	bus_size_t ccaaa_size;
871.1Smatt	int ccaaa_channel;
881.1Smatt};
891.1Smatt
901.1Smattstatic struct bcmcca_softc bcmcca_sc = {
911.1Smatt	.sc_gpiopins = 0xffffff,	/* assume all 24 pins are available */
921.1Smatt};
931.1Smatt
941.1SmattCFATTACH_DECL_NEW(bcmcca, 0,
951.1Smatt	bcmcca_mainbus_match, bcmcca_mainbus_attach, NULL, NULL);
961.1Smatt
971.1Smattstatic int
981.1Smattbcmcca_mainbus_match(device_t parent, cfdata_t cf, void *aux)
991.1Smatt{
1001.1Smatt	if (bcmcca_sc.sc_dev != NULL)
1011.1Smatt		return 0;
1021.1Smatt
1031.1Smatt	return 1;
1041.1Smatt}
1051.1Smatt
1061.1Smattstatic int
1071.1Smattbcmcca_print(void *aux, const char *pnp)
1081.1Smatt{
1091.1Smatt	const struct bcmcca_attach_args * const ccaaa = aux;
1101.1Smatt
1111.1Smatt	if (ccaaa->ccaaa_channel != BCMCCACF_CHANNEL_DEFAULT)
1121.1Smatt		aprint_normal(" channel %d", ccaaa->ccaaa_channel);
1131.1Smatt
1141.1Smatt	return QUIET;
1151.1Smatt}
1161.1Smatt
1171.1Smattstatic inline uint32_t
1181.1Smattbcmcca_read_4(struct bcmcca_softc *sc, bus_size_t o)
1191.1Smatt{
1201.1Smatt	return bus_space_read_4(sc->sc_bst, sc->sc_bsh, o);
1211.1Smatt}
1221.1Smatt
1231.1Smattstatic inline void
1241.1Smattbcmcca_write_4(struct bcmcca_softc *sc, bus_size_t o, uint32_t v)
1251.1Smatt{
1261.1Smatt	return bus_space_write_4(sc->sc_bst, sc->sc_bsh, o, v);
1271.1Smatt}
1281.1Smatt
1291.1Smattstatic int
1301.1Smattbcmcca_intr(void *arg)
1311.1Smatt{
1321.1Smatt	struct bcmcca_softc * sc = arg;
1331.1Smatt	int rv = 0;
1341.1Smatt
1351.1Smatt	uint32_t v = bcmcca_read_4(sc, MISC_INTSTATUS);
1361.1Smatt	if (v & INTSTATUS_UARTINT) {
1371.1Smatt		if (sc->sc_com_softc[0] != NULL)
1381.1Smatt			rv = comintr(sc->sc_com_softc[0]);
1391.1Smatt		if (sc->sc_com_softc[1] != NULL) {
1401.1Smatt			int rv0 = comintr(sc->sc_com_softc[1]);
1411.1Smatt			if (rv)
1421.1Smatt				rv = rv0;
1431.1Smatt		}
1441.1Smatt	}
1451.1Smatt	if (v & INTSTATUS_GPIOINT) {
1461.6Sskrll
1471.1Smatt	}
1481.1Smatt	return rv;
1491.1Smatt}
1501.1Smatt
1511.1Smattstatic void
1521.1Smattbcmcca_mainbus_attach(device_t parent, device_t self, void *aux)
1531.1Smatt{
1541.1Smatt	struct bcmcca_softc * const sc = &bcmcca_sc;
1551.1Smatt
1561.1Smatt	sc->sc_dev = self;
1571.5Sriastrad	device_set_private(self, sc);
1581.1Smatt
1591.1Smatt	sc->sc_bst = bcm53xx_ioreg_bst;
1601.1Smatt
1611.1Smatt	bus_space_subregion (sc->sc_bst, bcm53xx_ioreg_bsh,
1621.1Smatt	    CCA_MISC_BASE, CCA_MISC_SIZE, &sc->sc_bsh);
1631.1Smatt
1641.1Smatt	uint32_t chipid = bcmcca_read_4(sc, MISC_CHIPID);
1651.1Smatt
1661.1Smatt	aprint_naive("\n");
1671.1Smatt	aprint_normal(": BCM%u (Rev %c%u)\n",
1681.1Smatt	    (u_int)__SHIFTOUT(chipid, CHIPID_ID),
1691.1Smatt	    (u_int)('A' + (__SHIFTOUT(chipid, CHIPID_REV) >> 2)),
1701.1Smatt	    (u_int)(__SHIFTOUT(chipid, CHIPID_REV) & 3));
1711.1Smatt
1721.1Smatt	sc->sc_ih = intr_establish(IRQ_CCA, IPL_TTY, IST_LEVEL, bcmcca_intr, sc);
1731.1Smatt	if (sc->sc_ih == NULL) {
1741.1Smatt		aprint_error_dev(sc->sc_dev, "failed to establish CCA intr\n");
1751.1Smatt		return;
1761.1Smatt	}
1771.1Smatt	aprint_normal_dev(sc->sc_dev, "interrupting at irq %d\n", IRQ_CCA);
1781.1Smatt
1791.1Smatt	bcmcca_uart_attach(sc);
1801.1Smatt#if NGPIO > 0
1811.1Smatt	bcmcca_gpio_attach(sc);
1821.1Smatt#endif
1831.1Smatt}
1841.1Smatt
1851.1Smattstatic void
1861.1Smattbcmcca_uart_attach(struct bcmcca_softc *sc)
1871.1Smatt{
1881.1Smatt	struct bcmcca_attach_args ccaaa = {
1891.1Smatt		.ccaaa_bst = sc->sc_bst,
1901.1Smatt		.ccaaa_bsh = sc->sc_bsh,
1911.1Smatt		.ccaaa_offset = CCA_UART0_BASE,
1921.1Smatt		.ccaaa_size = COM_NPORTS,
1931.1Smatt		.ccaaa_channel = 0,
1941.1Smatt	};
1951.1Smatt	device_t dv;
1961.1Smatt
1971.1Smatt#if 0
1981.1Smatt	/*
1991.1Smatt	 * Force the UART to use the BCM53xx reference clock.
2001.1Smatt	 */
2011.1Smatt	uint32_t v = bcmcca_read_4(sc, IDM_BASE + APBX_IDM_IO_CONTROL_DIRECT);
2021.1Smatt	if (v & IO_CONTROL_DIRECT_UARTCLKSEL) {
2031.1Smatt		v &= ~IO_CONTROL_DIRECT_UARTCLKSEL;
2041.1Smatt		bcmcca_write_4(sc, IDM_BASE + APBX_IDM_IO_CONTROL_DIRECT, v);
2051.1Smatt	}
2061.1Smatt	v = bcmcca_read_4(sc, MISC_CORECTL);
2071.1Smatt	if (v & CORECTL_UART_CLK_OVERRIDE) {
2081.1Smatt		v &= ~CORECTL_UART_CLK_OVERRIDE;
2091.1Smatt		bcmcca_write_4(sc, MISC_CORECTL, v);
2101.1Smatt	}
2111.1Smatt#endif
2121.1Smatt
2131.1Smatt	bool children = false;
2141.1Smatt
2151.4Sthorpej	dv = config_found(sc->sc_dev, &ccaaa, bcmcca_print, CFARGS_NONE);
2161.1Smatt	if (dv != NULL) {
2171.1Smatt		sc->sc_com_softc[0] = device_private(dv);
2181.1Smatt		children = true;
2191.1Smatt	}
2201.1Smatt
2211.1Smatt	ccaaa.ccaaa_offset = CCA_UART1_BASE;
2221.1Smatt	ccaaa.ccaaa_channel = 1;
2231.1Smatt
2241.4Sthorpej	dv = config_found(sc->sc_dev, &ccaaa, bcmcca_print, CFARGS_NONE);
2251.1Smatt	if (dv != NULL) {
2261.1Smatt		sc->sc_com_softc[1] = device_private(dv);
2271.1Smatt		children = true;
2281.1Smatt		/*
2291.1Smatt		 * UART1 uses the same pins as GPIO pins 15..12
2301.1Smatt		 */
2311.1Smatt		sc->sc_gpiopins &= ~__BITS(15,12);
2321.1Smatt	}
2331.1Smatt
2341.1Smatt	if (children) {
2351.1Smatt		/*
2361.1Smatt		 * If we configured children, enable interrupts for the UART(s).
2371.1Smatt		 */
2381.1Smatt		uint32_t intmask = bcmcca_read_4(sc, MISC_INTMASK);
2391.1Smatt		intmask |= INTMASK_UARTINT;
2401.1Smatt		bcmcca_write_4(sc, MISC_INTMASK, intmask);
2411.1Smatt	}
2421.1Smatt}
2431.1Smatt
2441.1Smattstatic int com_cca_match(device_t, cfdata_t, void *);
2451.1Smattstatic void com_cca_attach(device_t, device_t, void *);
2461.1Smatt
2471.1SmattCFATTACH_DECL_NEW(com_cca, sizeof(struct com_softc),
2481.1Smatt    com_cca_match, com_cca_attach, NULL, NULL);
2491.1Smatt
2501.1Smattstatic int
2511.1Smattcom_cca_match(device_t parent, cfdata_t cf, void *aux)
2521.1Smatt{
2531.1Smatt	struct bcmcca_attach_args * const ccaaa = aux;
2541.1Smatt	const int channel = cf->cf_loc[BCMCCACF_CHANNEL];
2551.1Smatt	const bus_addr_t addr = BCM53XX_IOREG_PBASE + ccaaa->ccaaa_offset;
2561.1Smatt	bus_space_handle_t bsh;
2571.1Smatt
2581.1Smatt	KASSERT(ccaaa->ccaaa_offset == CCA_UART0_BASE || ccaaa->ccaaa_offset == CCA_UART1_BASE);
2591.1Smatt	KASSERT(bcmcca_sc.sc_com_softc[ccaaa->ccaaa_channel] == NULL);
2601.1Smatt
2611.1Smatt	if (channel != BCMCCACF_CHANNEL_DEFAULT && channel != ccaaa->ccaaa_channel)
2621.1Smatt		return 0;
2631.1Smatt
2641.1Smatt	if (com_is_console(ccaaa->ccaaa_bst, addr, NULL))
2651.1Smatt		return 1;
2661.1Smatt
2671.1Smatt	bus_space_subregion(ccaaa->ccaaa_bst, ccaaa->ccaaa_bsh,
2681.1Smatt	    ccaaa->ccaaa_offset, ccaaa->ccaaa_size, &bsh);
2691.1Smatt
2701.1Smatt	return comprobe1(ccaaa->ccaaa_bst, bsh);
2711.1Smatt}
2721.1Smatt
2731.1Smattstatic void
2741.1Smattcom_cca_attach(device_t parent, device_t self, void *aux)
2751.1Smatt{
2761.1Smatt	struct com_softc * const sc = device_private(self);
2771.1Smatt	struct bcmcca_attach_args * const ccaaa = aux;
2781.1Smatt	const bus_addr_t addr = BCM53XX_IOREG_PBASE + ccaaa->ccaaa_offset;
2791.1Smatt	bus_space_handle_t bsh;
2801.1Smatt
2811.1Smatt	sc->sc_dev = self;
2821.1Smatt	sc->sc_frequency = BCM53XX_REF_CLK;
2831.1Smatt	sc->sc_type = COM_TYPE_NORMAL;
2841.1Smatt
2851.1Smatt	if (com_is_console(ccaaa->ccaaa_bst, addr, &bsh) == 0 &&
2861.1Smatt	    bus_space_subregion(ccaaa->ccaaa_bst, ccaaa->ccaaa_bsh,
2871.1Smatt		ccaaa->ccaaa_offset, ccaaa->ccaaa_size, &bsh)) {
2881.1Smatt		panic(": can't map registers\n");
2891.1Smatt		return;
2901.1Smatt	}
2911.2Sthorpej	com_init_regs(&sc->sc_regs, ccaaa->ccaaa_bst, bsh, addr);
2921.1Smatt
2931.1Smatt	com_attach_subr(sc);
2941.1Smatt}
2951.1Smatt
2961.1Smatt#if NGPIO > 0
2971.1Smattstatic void
2981.1Smattbcmcca_gpio_attach(struct bcmcca_softc *sc)
2991.1Smatt{
3001.1Smatt	/*
3011.1Smatt	 * First see if there are any pins being used as GPIO pins...
3021.1Smatt	 */
3031.1Smatt	uint32_t v = bcmcca_read(sc, CRU_BASE + CRU_GPIO_SELECT);
3041.1Smatt	if (v == 0)
3051.1Smatt		return;
3061.1Smatt}
3071.1Smatt#endif
308