1/* $NetBSD: bcm2835_com_acpi.c,v 1.2 2021/08/08 18:55:12 jmcneill Exp $ */ 2 3/* 4 * Copyright (c) 2021 Jared 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. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__KERNEL_RCSID(0, "$NetBSD: bcm2835_com_acpi.c,v 1.2 2021/08/08 18:55:12 jmcneill Exp $"); 30 31#include <sys/param.h> 32#include <sys/device.h> 33#include <sys/systm.h> 34#include <sys/termios.h> 35 36#include <dev/acpi/acpivar.h> 37#include <dev/acpi/acpi_intr.h> 38 39#include <dev/ic/comvar.h> 40 41#include <arm/broadcom/bcm2835_mbox.h> 42#include <evbarm/rpi/vcio.h> 43#include <evbarm/rpi/vcprop.h> 44 45static int bcmcom_acpi_match(device_t, cfdata_t , void *); 46static void bcmcom_acpi_attach(device_t, device_t, void *); 47 48static u_int bcmcom_acpi_get_clockrate(device_t); 49 50struct vcmbox_clockrate_request { 51 struct vcprop_buffer_hdr vb_hdr; 52 struct vcprop_tag_clockrate vbt_clockrate; 53 struct vcprop_tag end; 54} __packed; 55 56CFATTACH_DECL_NEW(bcmcom_acpi, sizeof(struct com_softc), bcmcom_acpi_match, 57 bcmcom_acpi_attach, NULL, NULL); 58 59static const struct device_compatible_entry compat_data[] = { 60 { .compat = "BCM2836", .value = COM_TYPE_BCMAUXUART }, 61 DEVICE_COMPAT_EOL 62}; 63 64static int 65bcmcom_acpi_match(device_t parent, cfdata_t match, void *aux) 66{ 67 struct acpi_attach_args *aa = aux; 68 69 return acpi_compatible_match(aa, compat_data); 70} 71 72static void 73bcmcom_acpi_attach(device_t parent, device_t self, void *aux) 74{ 75 struct com_softc *sc = device_private(self); 76 struct acpi_attach_args *aa = aux; 77 const struct device_compatible_entry *dce; 78 struct acpi_resources res; 79 struct acpi_mem *mem; 80 struct acpi_irq *irq; 81 bus_space_tag_t iot; 82 bus_space_handle_t ioh; 83 bus_addr_t base; 84 bus_size_t size; 85 ACPI_STATUS rv; 86 void *ih; 87 88 sc->sc_dev = self; 89 90 rv = acpi_resource_parse(sc->sc_dev, aa->aa_node->ad_handle, "_CRS", 91 &res, &acpi_resource_parse_ops_default); 92 if (ACPI_FAILURE(rv)) { 93 return; 94 } 95 96 mem = acpi_res_mem(&res, 0); 97 if (mem == NULL) { 98 aprint_error_dev(self, "couldn't find mem resource\n"); 99 goto cleanup; 100 } 101 102 iot = aa->aa_memt; 103 base = mem->ar_base + 0x40; 104 size = mem->ar_length - 0x40; 105 106 irq = acpi_res_irq(&res, 0); 107 if (irq == NULL) { 108 aprint_error_dev(self, "couldn't find irq resource\n"); 109 goto cleanup; 110 } 111 112 dce = acpi_compatible_lookup(aa, compat_data); 113 KASSERT(dce != NULL); 114 115 sc->sc_type = dce->value; 116 117 if (!com_is_console(iot, base, &ioh)) { 118 if (bus_space_map(iot, base, size, 0, &ioh)) { 119 aprint_error_dev(self, "can't map mem space\n"); 120 goto cleanup; 121 } 122 } 123 124 com_init_regs_stride(&sc->sc_regs, iot, ioh, base, 2); 125 126 aprint_normal("%s", device_xname(self)); 127 128 sc->sc_frequency = bcmcom_acpi_get_clockrate(self); 129 130 com_attach_subr(sc); 131 132 ih = acpi_intr_establish(self, 133 (uint64_t)(uintptr_t)aa->aa_node->ad_handle, 134 IPL_SERIAL, true, comintr, sc, device_xname(self)); 135 if (ih == NULL) { 136 aprint_error_dev(self, "couldn't establish interrupt\n"); 137 goto cleanup; 138 } 139 140 if (!pmf_device_register(self, NULL, com_resume)) 141 aprint_error_dev(self, "couldn't establish a power handler\n"); 142 143cleanup: 144 acpi_resource_cleanup(&res); 145} 146 147static u_int 148bcmcom_acpi_get_clockrate(device_t dev) 149{ 150 struct vcmbox_clockrate_request vb; 151 uint32_t res; 152 int error; 153 154 VCPROP_INIT_REQUEST(vb); 155 VCPROP_INIT_TAG(vb.vbt_clockrate, VCPROPTAG_GET_CLOCKRATE); 156 vb.vbt_clockrate.id = htole32(VCPROP_CLK_CORE); 157 error = bcmmbox_request(BCMMBOX_CHANARM2VC, &vb, sizeof(vb), &res); 158 if (error != 0) { 159 aprint_error_dev(dev, "MBOX request failed: %d\n", error); 160 return 0; 161 } 162 if (!vcprop_buffer_success_p(&vb.vb_hdr) || 163 !vcprop_tag_success_p(&vb.vbt_clockrate.tag)) { 164 return 0; 165 } 166 167 return le32toh(vb.vbt_clockrate.rate) * 2; 168} 169