1*da79f4d7Sskrll /* $NetBSD: sdhc_acpi.c,v 1.22 2024/08/17 07:00:35 skrll Exp $ */ 2038e2d2dSnonaka 3038e2d2dSnonaka /* 4038e2d2dSnonaka * Copyright (c) 2016 Kimihiro Nonaka <nonaka@NetBSD.org> 5038e2d2dSnonaka * All rights reserved. 6038e2d2dSnonaka * 7038e2d2dSnonaka * Redistribution and use in source and binary forms, with or without 8038e2d2dSnonaka * modification, are permitted provided that the following conditions 9038e2d2dSnonaka * are met: 10038e2d2dSnonaka * 1. Redistributions of source code must retain the above copyright 11038e2d2dSnonaka * notice, this list of conditions and the following disclaimer. 12038e2d2dSnonaka * 2. The name of the author may not be used to endorse or promote products 13038e2d2dSnonaka * derived from this software without specific prior written permission. 14038e2d2dSnonaka * 15038e2d2dSnonaka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16038e2d2dSnonaka * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17038e2d2dSnonaka * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18038e2d2dSnonaka * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19038e2d2dSnonaka * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20038e2d2dSnonaka * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21038e2d2dSnonaka * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22038e2d2dSnonaka * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23038e2d2dSnonaka * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24038e2d2dSnonaka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25038e2d2dSnonaka * SUCH DAMAGE. 26038e2d2dSnonaka */ 27038e2d2dSnonaka 28038e2d2dSnonaka #include <sys/cdefs.h> 29*da79f4d7Sskrll __KERNEL_RCSID(0, "$NetBSD: sdhc_acpi.c,v 1.22 2024/08/17 07:00:35 skrll Exp $"); 30038e2d2dSnonaka 31038e2d2dSnonaka #include <sys/param.h> 32038e2d2dSnonaka #include <sys/device.h> 33038e2d2dSnonaka #include <sys/systm.h> 34038e2d2dSnonaka #include <sys/kmem.h> 35038e2d2dSnonaka 36038e2d2dSnonaka #include <dev/acpi/acpireg.h> 37038e2d2dSnonaka #include <dev/acpi/acpivar.h> 38f62244a8Sjmcneill #include <dev/acpi/acpi_intr.h> 39038e2d2dSnonaka 40038e2d2dSnonaka #include <dev/sdmmc/sdhcreg.h> 41038e2d2dSnonaka #include <dev/sdmmc/sdhcvar.h> 42038e2d2dSnonaka #include <dev/sdmmc/sdmmcvar.h> 43038e2d2dSnonaka 448507cd68Sjmcneill /* Freescale ESDHC */ 458507cd68Sjmcneill #define SDHC_ESDHC_FLAGS \ 468507cd68Sjmcneill (SDHC_FLAG_HAVE_DVS|SDHC_FLAG_NO_PWR0|SDHC_FLAG_32BIT_ACCESS|SDHC_FLAG_ENHANCED) 478507cd68Sjmcneill 48f30b89bbSjmcneill /* Rockchip eMMC device-specific method (_DSM) - 434addb0-8ff3-49d5-a724-95844b79ad1f */ 49f30b89bbSjmcneill static UINT8 sdhc_acpi_rockchip_dsm_uuid[ACPI_UUID_LENGTH] = { 50f30b89bbSjmcneill 0xb0, 0xdd, 0x4a, 0x43, 0xf3, 0x8f, 0xd5, 0x49, 51f30b89bbSjmcneill 0xa7, 0x24, 0x95, 0x84, 0x4b, 0x79, 0xad, 0x1f 52f30b89bbSjmcneill }; 53f30b89bbSjmcneill #define ROCKCHIP_DSM_REV 0 54f30b89bbSjmcneill #define ROCKCHIP_DSM_FUNC_SET_CARD_CLOCK 1 55f30b89bbSjmcneill 56038e2d2dSnonaka #define _COMPONENT ACPI_RESOURCE_COMPONENT 57038e2d2dSnonaka ACPI_MODULE_NAME ("sdhc_acpi") 58038e2d2dSnonaka 59038e2d2dSnonaka static int sdhc_acpi_match(device_t, cfdata_t, void *); 60038e2d2dSnonaka static void sdhc_acpi_attach(device_t, device_t, void *); 61038e2d2dSnonaka static int sdhc_acpi_detach(device_t, int); 62038e2d2dSnonaka static bool sdhc_acpi_resume(device_t, const pmf_qual_t *); 63038e2d2dSnonaka 64038e2d2dSnonaka struct sdhc_acpi_softc { 65038e2d2dSnonaka struct sdhc_softc sc; 667003a765Snonaka bus_space_tag_t sc_memt; 677003a765Snonaka bus_space_handle_t sc_memh; 687003a765Snonaka bus_size_t sc_memsize; 69f62244a8Sjmcneill void *sc_ih; 70f30b89bbSjmcneill ACPI_HANDLE sc_handle; 71038e2d2dSnonaka 72038e2d2dSnonaka ACPI_HANDLE sc_crs, sc_srs; 73038e2d2dSnonaka ACPI_BUFFER sc_crs_buffer; 74038e2d2dSnonaka }; 75038e2d2dSnonaka 76038e2d2dSnonaka CFATTACH_DECL_NEW(sdhc_acpi, sizeof(struct sdhc_acpi_softc), 77038e2d2dSnonaka sdhc_acpi_match, sdhc_acpi_attach, sdhc_acpi_detach, NULL); 78038e2d2dSnonaka 797003a765Snonaka static void sdhc_acpi_intel_emmc_hw_reset(struct sdhc_softc *, 807003a765Snonaka struct sdhc_host *); 81038e2d2dSnonaka 82f30b89bbSjmcneill static int sdhc_acpi_rockchip_bus_clock(struct sdhc_softc *, 83f30b89bbSjmcneill int); 84f30b89bbSjmcneill 857003a765Snonaka static const struct sdhc_acpi_slot { 867003a765Snonaka const char *hid; 877003a765Snonaka const char *uid; 887003a765Snonaka int type; 897003a765Snonaka #define SLOT_TYPE_SD 0 /* SD or SDIO */ 907003a765Snonaka #define SLOT_TYPE_EMMC 1 /* eMMC */ 918507cd68Sjmcneill uint32_t flags; 927003a765Snonaka } sdhc_acpi_slot_map[] = { 93d26e0438Schristos { .hid = "80865ACA", .type = SLOT_TYPE_SD }, 94d26e0438Schristos { .hid = "80865ACC", .type = SLOT_TYPE_EMMC }, 95d26e0438Schristos { .hid = "80865AD0", .type = SLOT_TYPE_SD }, 96d26e0438Schristos { .hid = "80860F14", .uid = "1", .type = SLOT_TYPE_EMMC }, 97d26e0438Schristos { .hid = "80860F14", .uid = "3", .type = SLOT_TYPE_SD }, 98d26e0438Schristos { .hid = "80860F16", .type = SLOT_TYPE_SD }, 99d26e0438Schristos { .hid = "INT33BB", .uid = "2", .type = SLOT_TYPE_SD }, 100d26e0438Schristos { .hid = "INT33BB", .uid = "3", .type = SLOT_TYPE_SD }, 101d26e0438Schristos { .hid = "INT33C6", .type = SLOT_TYPE_SD }, 102d26e0438Schristos { .hid = "INT3436", .type = SLOT_TYPE_SD }, 103d26e0438Schristos { .hid = "INT344D", .type = SLOT_TYPE_SD }, 104d26e0438Schristos { .hid = "NXP0003", .uid = "0", .type = SLOT_TYPE_SD, 105d26e0438Schristos .flags = SDHC_ESDHC_FLAGS }, 1068fd56fc1Stnn { .hid = "NXP0003", .uid = "1", .type = SLOT_TYPE_EMMC, 107d26e0438Schristos .flags = SDHC_ESDHC_FLAGS }, 108f30b89bbSjmcneill { .hid = "RKCP0D40", .type = SLOT_TYPE_SD, 109f30b89bbSjmcneill .flags = SDHC_FLAG_32BIT_ACCESS | 110f30b89bbSjmcneill SDHC_FLAG_8BIT_MODE | 111f30b89bbSjmcneill SDHC_FLAG_SINGLE_POWER_WRITE }, 112d26e0438Schristos 1138507cd68Sjmcneill /* Generic IDs last */ 114d26e0438Schristos { .hid = "PNP0D40", .type = SLOT_TYPE_SD }, 115d26e0438Schristos { .hid = "PNP0FFF", .uid = "3", .type = SLOT_TYPE_SD }, 116038e2d2dSnonaka }; 117038e2d2dSnonaka 1187003a765Snonaka static const struct sdhc_acpi_slot * 1197003a765Snonaka sdhc_acpi_find_slot(ACPI_DEVICE_INFO *ad) 1207003a765Snonaka { 1217003a765Snonaka const struct sdhc_acpi_slot *slot; 1227003a765Snonaka const char *hid, *uid; 1237003a765Snonaka size_t i; 1247003a765Snonaka 1257003a765Snonaka hid = ad->HardwareId.String; 1267003a765Snonaka uid = ad->UniqueId.String; 1277003a765Snonaka 1287003a765Snonaka if (!(ad->Valid & ACPI_VALID_HID) || hid == NULL) 1297003a765Snonaka return NULL; 1307003a765Snonaka 1317003a765Snonaka for (i = 0; i < __arraycount(sdhc_acpi_slot_map); i++) { 1327003a765Snonaka slot = &sdhc_acpi_slot_map[i]; 133996c868aSjmcneill const char * const slot_id[] = { slot->hid, NULL }; 134996c868aSjmcneill if (acpi_match_hid(ad, slot_id)) { 1357003a765Snonaka if (slot->uid == NULL || 1367003a765Snonaka ((ad->Valid & ACPI_VALID_UID) != 0 && 1377003a765Snonaka uid != NULL && 1387003a765Snonaka strcmp(uid, slot->uid) == 0)) 1397003a765Snonaka return slot; 1407003a765Snonaka } 1417003a765Snonaka } 1427003a765Snonaka return NULL; 1437003a765Snonaka } 1447003a765Snonaka 145038e2d2dSnonaka static int 146038e2d2dSnonaka sdhc_acpi_match(device_t parent, cfdata_t match, void *opaque) 147038e2d2dSnonaka { 148038e2d2dSnonaka struct acpi_attach_args *aa = opaque; 149038e2d2dSnonaka 150038e2d2dSnonaka if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 151038e2d2dSnonaka return 0; 152038e2d2dSnonaka 1537003a765Snonaka return sdhc_acpi_find_slot(aa->aa_node->ad_devinfo) != NULL; 154038e2d2dSnonaka } 155038e2d2dSnonaka 156038e2d2dSnonaka static void 157038e2d2dSnonaka sdhc_acpi_attach(device_t parent, device_t self, void *opaque) 158038e2d2dSnonaka { 159038e2d2dSnonaka struct sdhc_acpi_softc *sc = device_private(self); 160038e2d2dSnonaka struct acpi_attach_args *aa = opaque; 1617003a765Snonaka const struct sdhc_acpi_slot *slot; 162038e2d2dSnonaka struct acpi_resources res; 163038e2d2dSnonaka struct acpi_mem *mem; 164038e2d2dSnonaka struct acpi_irq *irq; 165038e2d2dSnonaka ACPI_STATUS rv; 1663d5564abSjmcneill ACPI_INTEGER clock_freq; 167d76ef063Sjmcneill ACPI_INTEGER caps, caps_mask; 168f30b89bbSjmcneill ACPI_INTEGER funcs; 1690d209957Sdyoung bool non_removable; 170038e2d2dSnonaka 171038e2d2dSnonaka sc->sc.sc_dev = self; 172038e2d2dSnonaka sc->sc.sc_dmat = aa->aa_dmat; 173038e2d2dSnonaka sc->sc.sc_host = NULL; 1747003a765Snonaka sc->sc_memt = aa->aa_memt; 175f30b89bbSjmcneill sc->sc_handle = aa->aa_node->ad_handle; 176038e2d2dSnonaka 1777003a765Snonaka slot = sdhc_acpi_find_slot(aa->aa_node->ad_devinfo); 1787003a765Snonaka if (slot->type == SLOT_TYPE_EMMC) 1797003a765Snonaka sc->sc.sc_vendor_hw_reset = sdhc_acpi_intel_emmc_hw_reset; 1807003a765Snonaka 181f30b89bbSjmcneill rv = acpi_dsm_query(sc->sc_handle, sdhc_acpi_rockchip_dsm_uuid, 182f30b89bbSjmcneill ROCKCHIP_DSM_REV, &funcs); 183f30b89bbSjmcneill if (ACPI_SUCCESS(rv) && 184f30b89bbSjmcneill ISSET(funcs, __BIT(ROCKCHIP_DSM_FUNC_SET_CARD_CLOCK))) { 185f30b89bbSjmcneill sc->sc.sc_vendor_bus_clock = sdhc_acpi_rockchip_bus_clock; 186f30b89bbSjmcneill } 187f30b89bbSjmcneill 188038e2d2dSnonaka rv = acpi_resource_parse(self, aa->aa_node->ad_handle, "_CRS", 189038e2d2dSnonaka &res, &acpi_resource_parse_ops_default); 190038e2d2dSnonaka if (ACPI_FAILURE(rv)) 191038e2d2dSnonaka return; 192038e2d2dSnonaka 193038e2d2dSnonaka AcpiGetHandle(aa->aa_node->ad_handle, "_CRS", &sc->sc_crs); 194038e2d2dSnonaka AcpiGetHandle(aa->aa_node->ad_handle, "_SRS", &sc->sc_srs); 195038e2d2dSnonaka if (sc->sc_crs && sc->sc_srs) { 196038e2d2dSnonaka /* XXX Why need this? */ 197038e2d2dSnonaka sc->sc_crs_buffer.Pointer = NULL; 198038e2d2dSnonaka sc->sc_crs_buffer.Length = ACPI_ALLOCATE_LOCAL_BUFFER; 199038e2d2dSnonaka rv = AcpiGetCurrentResources(sc->sc_crs, &sc->sc_crs_buffer); 200038e2d2dSnonaka if (ACPI_FAILURE(rv)) 201038e2d2dSnonaka sc->sc_crs = sc->sc_srs = NULL; 202038e2d2dSnonaka } 203038e2d2dSnonaka 204038e2d2dSnonaka mem = acpi_res_mem(&res, 0); 205038e2d2dSnonaka irq = acpi_res_irq(&res, 0); 206038e2d2dSnonaka if (mem == NULL || irq == NULL) { 207038e2d2dSnonaka aprint_error_dev(self, "incomplete resources\n"); 208038e2d2dSnonaka goto cleanup; 209038e2d2dSnonaka } 210b23590a5Smlelstv if (mem->ar_length == 0) { 211b23590a5Smlelstv aprint_error_dev(self, "zero length memory resource\n"); 212b23590a5Smlelstv goto cleanup; 213b23590a5Smlelstv } 2147003a765Snonaka sc->sc_memsize = mem->ar_length; 215038e2d2dSnonaka 2167003a765Snonaka if (bus_space_map(sc->sc_memt, mem->ar_base, sc->sc_memsize, 0, 2177003a765Snonaka &sc->sc_memh)) { 218038e2d2dSnonaka aprint_error_dev(self, "couldn't map registers\n"); 219038e2d2dSnonaka goto cleanup; 220038e2d2dSnonaka } 221038e2d2dSnonaka 2221a05c81cSkre sc->sc_ih = acpi_intr_establish(self, 2231a05c81cSkre (uint64_t)(uintptr_t)aa->aa_node->ad_handle, 224f62244a8Sjmcneill IPL_BIO, false, sdhc_intr, &sc->sc, device_xname(self)); 225f62244a8Sjmcneill if (sc->sc_ih == NULL) { 226038e2d2dSnonaka aprint_error_dev(self, 227038e2d2dSnonaka "couldn't establish interrupt handler\n"); 2287003a765Snonaka goto unmap; 229038e2d2dSnonaka } 230038e2d2dSnonaka 2310a827a3fSchs sc->sc.sc_host = kmem_zalloc(sizeof(struct sdhc_host *), KM_SLEEP); 232038e2d2dSnonaka 2338507cd68Sjmcneill sc->sc.sc_flags |= slot->flags; 2348507cd68Sjmcneill 23521c1cf7dSnonaka /* Enable DMA transfer */ 23621c1cf7dSnonaka sc->sc.sc_flags |= SDHC_FLAG_USE_DMA; 23721c1cf7dSnonaka 2380d209957Sdyoung rv = acpi_dsd_bool(aa->aa_node->ad_handle, "non-removable", 2390d209957Sdyoung &non_removable); 2400d209957Sdyoung if (ACPI_SUCCESS(rv) && non_removable) 2410d209957Sdyoung sc->sc.sc_flags |= SDHC_FLAG_NON_REMOVABLE; 2420d209957Sdyoung 2433d5564abSjmcneill /* Read clock frequency from device properties */ 2443d5564abSjmcneill rv = acpi_dsd_integer(aa->aa_node->ad_handle, "clock-frequency", 2453d5564abSjmcneill &clock_freq); 2466225e78eSjmcneill if (ACPI_SUCCESS(rv)) { 2473d5564abSjmcneill sc->sc.sc_clkbase = clock_freq / 1000; 2486225e78eSjmcneill sc->sc.sc_flags |= SDHC_FLAG_NO_CLKBASE; 2496225e78eSjmcneill } 2503d5564abSjmcneill 251d76ef063Sjmcneill /* Capability overrides */ 252d76ef063Sjmcneill caps = caps_mask = 0; 253d76ef063Sjmcneill acpi_dsd_integer(aa->aa_node->ad_handle, "sdhci-caps-mask", &caps_mask); 254d76ef063Sjmcneill acpi_dsd_integer(aa->aa_node->ad_handle, "sdhci-caps", &caps); 255d76ef063Sjmcneill if (caps || caps_mask) { 256d76ef063Sjmcneill sc->sc.sc_caps = bus_space_read_4(sc->sc_memt, sc->sc_memh, 257d76ef063Sjmcneill SDHC_CAPABILITIES); 258d76ef063Sjmcneill sc->sc.sc_caps &= ~(caps_mask & 0xffffffff); 259d76ef063Sjmcneill sc->sc.sc_caps |= (caps & 0xffffffff); 260d76ef063Sjmcneill sc->sc.sc_caps2 = bus_space_read_4(sc->sc_memt, 261d76ef063Sjmcneill sc->sc_memh, SDHC_CAPABILITIES2); 262d76ef063Sjmcneill sc->sc.sc_caps2 &= ~(caps_mask >> 32); 263d76ef063Sjmcneill sc->sc.sc_caps2 |= (caps >> 32); 264d76ef063Sjmcneill sc->sc.sc_flags |= SDHC_FLAG_HOSTCAPS; 265d76ef063Sjmcneill } 266d76ef063Sjmcneill 2677003a765Snonaka if (sdhc_host_found(&sc->sc, sc->sc_memt, sc->sc_memh, 2687003a765Snonaka sc->sc_memsize) != 0) { 269038e2d2dSnonaka aprint_error_dev(self, "couldn't initialize host\n"); 2707003a765Snonaka goto fail; 271038e2d2dSnonaka } 272038e2d2dSnonaka 273038e2d2dSnonaka if (!pmf_device_register1(self, sdhc_suspend, sdhc_acpi_resume, 274038e2d2dSnonaka sdhc_shutdown)) { 275038e2d2dSnonaka aprint_error_dev(self, "couldn't establish powerhook\n"); 276038e2d2dSnonaka } 277038e2d2dSnonaka 278038e2d2dSnonaka acpi_resource_cleanup(&res); 279038e2d2dSnonaka return; 280038e2d2dSnonaka 2817003a765Snonaka fail: 2827003a765Snonaka if (sc->sc.sc_host != NULL) 2837003a765Snonaka kmem_free(sc->sc.sc_host, sizeof(struct sdhc_host *)); 2847003a765Snonaka sc->sc.sc_host = NULL; 285f62244a8Sjmcneill if (sc->sc_ih != NULL) 286f62244a8Sjmcneill acpi_intr_disestablish(sc->sc_ih); 287f62244a8Sjmcneill sc->sc_ih = NULL; 2887003a765Snonaka unmap: 2897003a765Snonaka bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_memsize); 2907003a765Snonaka sc->sc_memsize = 0; 2917003a765Snonaka cleanup: 2927003a765Snonaka if (sc->sc_crs_buffer.Pointer) 2937003a765Snonaka ACPI_FREE(sc->sc_crs_buffer.Pointer); 2947003a765Snonaka sc->sc_crs_buffer.Pointer = NULL; 2957003a765Snonaka acpi_resource_cleanup(&res); 296038e2d2dSnonaka } 297038e2d2dSnonaka 298038e2d2dSnonaka static int 299038e2d2dSnonaka sdhc_acpi_detach(device_t self, int flags) 300038e2d2dSnonaka { 301038e2d2dSnonaka struct sdhc_acpi_softc *sc = device_private(self); 302038e2d2dSnonaka int rv; 303038e2d2dSnonaka 304038e2d2dSnonaka pmf_device_deregister(self); 305038e2d2dSnonaka 306038e2d2dSnonaka rv = sdhc_detach(&sc->sc, flags); 307038e2d2dSnonaka if (rv) 308038e2d2dSnonaka return rv; 309038e2d2dSnonaka 310f62244a8Sjmcneill if (sc->sc_ih != NULL) 311f62244a8Sjmcneill acpi_intr_disestablish(sc->sc_ih); 312038e2d2dSnonaka 313038e2d2dSnonaka if (sc->sc.sc_host != NULL) 314038e2d2dSnonaka kmem_free(sc->sc.sc_host, sizeof(struct sdhc_host *)); 315038e2d2dSnonaka 3167003a765Snonaka if (sc->sc_memsize > 0) 3177003a765Snonaka bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_memsize); 3187003a765Snonaka 3197003a765Snonaka if (sc->sc_crs_buffer.Pointer) 3207003a765Snonaka ACPI_FREE(sc->sc_crs_buffer.Pointer); 3217003a765Snonaka 322038e2d2dSnonaka return 0; 323038e2d2dSnonaka } 324038e2d2dSnonaka 325038e2d2dSnonaka static bool 326038e2d2dSnonaka sdhc_acpi_resume(device_t self, const pmf_qual_t *qual) 327038e2d2dSnonaka { 328038e2d2dSnonaka struct sdhc_acpi_softc *sc = device_private(self); 329038e2d2dSnonaka ACPI_STATUS rv; 330038e2d2dSnonaka 331038e2d2dSnonaka if (sc->sc_crs && sc->sc_srs) { 332038e2d2dSnonaka rv = AcpiSetCurrentResources(sc->sc_srs, &sc->sc_crs_buffer); 333038e2d2dSnonaka if (ACPI_FAILURE(rv)) 334038e2d2dSnonaka printf("%s: _SRS failed: %s\n", 335038e2d2dSnonaka device_xname(self), AcpiFormatException(rv)); 336038e2d2dSnonaka } 337038e2d2dSnonaka 338038e2d2dSnonaka return sdhc_resume(self, qual); 339038e2d2dSnonaka } 340038e2d2dSnonaka 3417003a765Snonaka static void 3427003a765Snonaka sdhc_acpi_intel_emmc_hw_reset(struct sdhc_softc *sc, struct sdhc_host *hp) 3437003a765Snonaka { 3447003a765Snonaka kmutex_t *plock = sdhc_host_lock(hp); 3457003a765Snonaka uint8_t reg; 3467003a765Snonaka 3477003a765Snonaka mutex_enter(plock); 3487003a765Snonaka 3497003a765Snonaka reg = sdhc_host_read_1(hp, SDHC_POWER_CTL); 3507003a765Snonaka reg |= 0x10; 3517003a765Snonaka sdhc_host_write_1(hp, SDHC_POWER_CTL, reg); 3527003a765Snonaka 3537003a765Snonaka sdmmc_delay(10); 3547003a765Snonaka 3557003a765Snonaka reg &= ~0x10; 3567003a765Snonaka sdhc_host_write_1(hp, SDHC_POWER_CTL, reg); 3577003a765Snonaka 3587003a765Snonaka sdmmc_delay(1000); 3597003a765Snonaka 3607003a765Snonaka mutex_exit(plock); 3617003a765Snonaka } 362f30b89bbSjmcneill 363f30b89bbSjmcneill static int 364f30b89bbSjmcneill sdhc_acpi_rockchip_bus_clock(struct sdhc_softc *sc, int freq) 365f30b89bbSjmcneill { 366f30b89bbSjmcneill struct sdhc_acpi_softc *asc = (struct sdhc_acpi_softc *)sc; 367f30b89bbSjmcneill ACPI_STATUS rv; 368f30b89bbSjmcneill ACPI_OBJECT targetfreq; 369f30b89bbSjmcneill ACPI_OBJECT arg3; 370f30b89bbSjmcneill ACPI_INTEGER actfreq; 371f30b89bbSjmcneill 372f30b89bbSjmcneill targetfreq.Integer.Type = ACPI_TYPE_INTEGER; 373f30b89bbSjmcneill targetfreq.Integer.Value = freq * 1000; 374f30b89bbSjmcneill arg3.Package.Type = ACPI_TYPE_PACKAGE; 375f30b89bbSjmcneill arg3.Package.Count = 1; 376f30b89bbSjmcneill arg3.Package.Elements = &targetfreq; 377f30b89bbSjmcneill 378f30b89bbSjmcneill rv = acpi_dsm_integer(asc->sc_handle, sdhc_acpi_rockchip_dsm_uuid, 379f30b89bbSjmcneill ROCKCHIP_DSM_REV, ROCKCHIP_DSM_FUNC_SET_CARD_CLOCK, &arg3, 380f30b89bbSjmcneill &actfreq); 381f30b89bbSjmcneill if (ACPI_FAILURE(rv)) { 382f30b89bbSjmcneill aprint_error_dev(sc->sc_dev, 383f30b89bbSjmcneill "eMMC Set Card Clock DSM failed: %s\n", 384f30b89bbSjmcneill AcpiFormatException(rv)); 385f30b89bbSjmcneill return ENXIO; 386f30b89bbSjmcneill } 387f30b89bbSjmcneill 388f30b89bbSjmcneill aprint_debug_dev(sc->sc_dev, 38903f9182eSjmcneill "eMMC Set Card Clock DSM returned %" PRIu64 " Hz\n", actfreq); 39003f9182eSjmcneill if (actfreq == 0 && freq != 0) { 391f30b89bbSjmcneill return EINVAL; 392f30b89bbSjmcneill } 393f30b89bbSjmcneill 394f30b89bbSjmcneill return 0; 395f30b89bbSjmcneill } 396