1/* $NetBSD: bcm2835_emmc_acpi.c,v 1.1 2019/12/30 18:53:34 jmcneill Exp $ */ 2 3/* 4 * Copyright (c) 2016 Kimihiro Nonaka <nonaka@NetBSD.org> 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_emmc_acpi.c,v 1.1 2019/12/30 18:53:34 jmcneill Exp $"); 30 31#include <sys/param.h> 32#include <sys/device.h> 33#include <sys/systm.h> 34#include <sys/kmem.h> 35 36#include <dev/acpi/acpireg.h> 37#include <dev/acpi/acpivar.h> 38#include <dev/acpi/acpi_intr.h> 39 40#include <dev/sdmmc/sdhcreg.h> 41#include <dev/sdmmc/sdhcvar.h> 42#include <dev/sdmmc/sdmmcvar.h> 43 44#include <arm/broadcom/bcm2835var.h> 45#include <arm/broadcom/bcm2835_mbox.h> 46 47#include <evbarm/rpi/vcio.h> 48#include <evbarm/rpi/vcpm.h> 49#include <evbarm/rpi/vcprop.h> 50 51#define _COMPONENT ACPI_RESOURCE_COMPONENT 52ACPI_MODULE_NAME ("bcmemmc_acpi") 53 54static int bcmemmc_acpi_match(device_t, cfdata_t, void *); 55static void bcmemmc_acpi_attach(device_t, device_t, void *); 56static void bcmemmc_acpi_attach1(device_t); 57 58static u_int bcmemmc_acpi_emmc_clock_rate(void); 59 60static const char * const compatible[] = { 61 "BCM2847", 62 NULL 63}; 64 65static struct { 66 struct vcprop_buffer_hdr vb_hdr; 67 struct vcprop_tag_clockrate vbt_emmcclockrate; 68 struct vcprop_tag end; 69} vb_emmc __cacheline_aligned = { 70 .vb_hdr = { 71 .vpb_len = sizeof(vb_emmc), 72 .vpb_rcode = VCPROP_PROCESS_REQUEST, 73 }, 74 .vbt_emmcclockrate = { 75 .tag = { 76 .vpt_tag = VCPROPTAG_GET_CLOCKRATE, 77 .vpt_len = VCPROPTAG_LEN(vb_emmc.vbt_emmcclockrate), 78 .vpt_rcode = VCPROPTAG_REQUEST 79 }, 80 .id = VCPROP_CLK_EMMC 81 }, 82 .end = { 83 .vpt_tag = VCPROPTAG_NULL 84 } 85}; 86 87struct bcmemmc_acpi_softc { 88 struct sdhc_softc sc; 89 bus_space_tag_t sc_memt; 90 bus_space_handle_t sc_memh; 91 bus_size_t sc_memsize; 92 void *sc_ih; 93 struct sdhc_host *sc_hosts[1]; 94}; 95 96CFATTACH_DECL_NEW(bcmemmc_acpi, sizeof(struct bcmemmc_acpi_softc), 97 bcmemmc_acpi_match, bcmemmc_acpi_attach, NULL, NULL); 98 99static int 100bcmemmc_acpi_match(device_t parent, cfdata_t match, void *opaque) 101{ 102 struct acpi_attach_args *aa = opaque; 103 104 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 105 return 0; 106 107 return acpi_match_hid(aa->aa_node->ad_devinfo, compatible); 108} 109 110static void 111bcmemmc_acpi_attach(device_t parent, device_t self, void *opaque) 112{ 113 struct bcmemmc_acpi_softc *sc = device_private(self); 114 struct acpi_attach_args *aa = opaque; 115 struct acpi_resources res; 116 struct acpi_mem *mem; 117 struct acpi_irq *irq; 118 ACPI_STATUS rv; 119 120 sc->sc.sc_dev = self; 121 sc->sc.sc_dmat = aa->aa_dmat; 122 sc->sc.sc_host = sc->sc_hosts; 123 sc->sc_memt = aa->aa_memt; 124 125 rv = acpi_resource_parse(self, aa->aa_node->ad_handle, "_CRS", 126 &res, &acpi_resource_parse_ops_default); 127 if (ACPI_FAILURE(rv)) 128 return; 129 130 mem = acpi_res_mem(&res, 0); 131 irq = acpi_res_irq(&res, 0); 132 if (mem == NULL || irq == NULL) { 133 aprint_error_dev(self, "incomplete resources\n"); 134 goto cleanup; 135 } 136 if (mem->ar_length == 0) { 137 aprint_error_dev(self, "zero length memory resource\n"); 138 goto cleanup; 139 } 140 sc->sc_memsize = mem->ar_length; 141 142 if (bus_space_map(sc->sc_memt, mem->ar_base, sc->sc_memsize, 0, 143 &sc->sc_memh)) { 144 aprint_error_dev(self, "couldn't map registers\n"); 145 goto cleanup; 146 } 147 148 sc->sc.sc_flags = SDHC_FLAG_32BIT_ACCESS | 149 SDHC_FLAG_HOSTCAPS | 150 SDHC_FLAG_NO_HS_BIT; 151 sc->sc.sc_caps = SDHC_VOLTAGE_SUPP_3_3V | SDHC_HIGH_SPEED_SUPP | 152 (SDHC_MAX_BLK_LEN_1024 << SDHC_MAX_BLK_LEN_SHIFT); 153 154 sc->sc_ih = acpi_intr_establish(self, 155 (uint64_t)(uintptr_t)aa->aa_node->ad_handle, 156 IPL_BIO, true, sdhc_intr, &sc->sc, device_xname(self)); 157 if (sc->sc_ih == NULL) { 158 aprint_error_dev(self, 159 "couldn't establish interrupt handler\n"); 160 goto unmap; 161 } 162 163 config_defer(self, bcmemmc_acpi_attach1); 164 165 acpi_resource_cleanup(&res); 166 return; 167 168unmap: 169 bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_memsize); 170 sc->sc_memsize = 0; 171cleanup: 172 acpi_resource_cleanup(&res); 173} 174 175static void 176bcmemmc_acpi_attach1(device_t self) 177{ 178 struct bcmemmc_acpi_softc *sc = device_private(self); 179 180 sc->sc.sc_clkbase = bcmemmc_acpi_emmc_clock_rate() / 1000; 181 182 if (sdhc_host_found(&sc->sc, sc->sc_memt, sc->sc_memh, 183 sc->sc_memsize) != 0) { 184 aprint_error_dev(self, "couldn't initialize host\n"); 185 goto fail; 186 } 187 188 return; 189 190fail: 191 if (sc->sc_ih != NULL) 192 acpi_intr_disestablish(sc->sc_ih); 193 sc->sc_ih = NULL; 194} 195 196static u_int 197bcmemmc_acpi_emmc_clock_rate(void) 198{ 199 uint32_t res; 200 int error; 201 202 error = bcmmbox_request(BCMMBOX_CHANARM2VC, &vb_emmc, 203 sizeof(vb_emmc), &res); 204 if (error) { 205 printf("%s: mbox request failed (%d)\n", __func__, error); 206 return 0; 207 } 208 209 if (!vcprop_buffer_success_p(&vb_emmc.vb_hdr) || 210 !vcprop_tag_success_p(&vb_emmc.vbt_emmcclockrate.tag) || 211 vb_emmc.vbt_emmcclockrate.rate < 0) 212 return 0; 213 214 return vb_emmc.vbt_emmcclockrate.rate; 215} 216