11.10Snisimura/* $NetBSD: sni_emmc.c,v 1.10 2021/12/21 06:00:45 nisimura Exp $ */ 21.1Snisimura 31.1Snisimura/*- 41.1Snisimura * Copyright (c) 2020 The NetBSD Foundation, Inc. 51.1Snisimura * All rights reserved. 61.1Snisimura * 71.1Snisimura * This code is derived from software contributed to The NetBSD Foundation 81.1Snisimura * by Tohru Nishimura. 91.1Snisimura * 101.1Snisimura * Redistribution and use in source and binary forms, with or without 111.1Snisimura * modification, are permitted provided that the following conditions 121.1Snisimura * are met: 131.1Snisimura * 1. Redistributions of source code must retain the above copyright 141.1Snisimura * notice, this list of conditions and the following disclaimer. 151.1Snisimura * 2. Redistributions in binary form must reproduce the above copyright 161.1Snisimura * notice, this list of conditions and the following disclaimer in the 171.1Snisimura * documentation and/or other materials provided with the distribution. 181.1Snisimura * 191.1Snisimura * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 201.1Snisimura * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 211.1Snisimura * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 221.1Snisimura * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 231.1Snisimura * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 241.1Snisimura * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 251.1Snisimura * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 261.1Snisimura * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 271.1Snisimura * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 281.1Snisimura * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 291.1Snisimura * POSSIBILITY OF SUCH DAMAGE. 301.1Snisimura */ 311.1Snisimura 321.1Snisimura/* 331.1Snisimura * Socionext SC2A11 SynQuacer eMMC driver 341.1Snisimura */ 351.1Snisimura 361.1Snisimura#include <sys/cdefs.h> 371.10Snisimura__KERNEL_RCSID(0, "$NetBSD: sni_emmc.c,v 1.10 2021/12/21 06:00:45 nisimura Exp $"); 381.1Snisimura 391.1Snisimura#include <sys/param.h> 401.1Snisimura#include <sys/bus.h> 411.1Snisimura#include <sys/intr.h> 421.1Snisimura#include <sys/device.h> 431.1Snisimura#include <sys/errno.h> 441.1Snisimura#include <sys/kernel.h> 451.1Snisimura#include <sys/systm.h> 461.1Snisimura 471.1Snisimura#include <machine/endian.h> 481.1Snisimura 491.2Snisimura#include <dev/sdmmc/sdhcreg.h> 501.2Snisimura#include <dev/sdmmc/sdhcvar.h> 511.2Snisimura#include <dev/sdmmc/sdmmcvar.h> 521.2Snisimura 531.1Snisimura#include <dev/fdt/fdtvar.h> 541.1Snisimura#include <dev/acpi/acpireg.h> 551.1Snisimura#include <dev/acpi/acpivar.h> 561.2Snisimura#include <dev/acpi/acpi_intr.h> 571.1Snisimura 581.1Snisimurastatic int sniemmc_fdt_match(device_t, struct cfdata *, void *); 591.1Snisimurastatic void sniemmc_fdt_attach(device_t, device_t, void *); 601.1Snisimurastatic int sniemmc_acpi_match(device_t, struct cfdata *, void *); 611.1Snisimurastatic void sniemmc_acpi_attach(device_t, device_t, void *); 621.1Snisimura 631.1Snisimurastruct sniemmc_softc { 641.2Snisimura struct sdhc_softc sc; 651.2Snisimura bus_space_tag_t sc_iot; 661.2Snisimura bus_space_handle_t sc_ioh; 671.2Snisimura bus_addr_t sc_iob; 681.2Snisimura bus_size_t sc_ios; 691.4Snisimura void *sc_ih; 701.2Snisimura struct sdhc_host *sc_hosts[1]; 711.2Snisimura bus_dmamap_t sc_dmamap; 721.2Snisimura bus_dma_segment_t sc_segs[1]; 731.2Snisimura kcondvar_t sc_cv; 741.4Snisimura int sc_phandle; 751.1Snisimura}; 761.1Snisimura 771.1SnisimuraCFATTACH_DECL_NEW(sniemmc_fdt, sizeof(struct sniemmc_softc), 781.1Snisimura sniemmc_fdt_match, sniemmc_fdt_attach, NULL, NULL); 791.1Snisimura 801.1SnisimuraCFATTACH_DECL_NEW(sniemmc_acpi, sizeof(struct sniemmc_softc), 811.1Snisimura sniemmc_acpi_match, sniemmc_acpi_attach, NULL, NULL); 821.1Snisimura 831.2Snisimurastatic void sniemmc_attach_i(device_t); 841.1Snisimura 851.8Sthorpejstatic const struct device_compatible_entry compat_data[] = { 861.8Sthorpej { .compat = "socionext,synquacer-sdhci" }, 871.8Sthorpej { .compat = "fujitsu,mb86s70-sdhci-3.0" }, 881.8Sthorpej DEVICE_COMPAT_EOL 891.8Sthorpej}; 901.10Snisimurastatic const struct device_compatible_entry compatible[] = { 911.10Snisimura { .compat = "SCX0002" }, 921.10Snisimura DEVICE_COMPAT_EOL 931.10Snisimura}; 941.8Sthorpej 951.1Snisimurastatic int 961.1Snisimurasniemmc_fdt_match(device_t parent, struct cfdata *match, void *aux) 971.1Snisimura{ 981.1Snisimura struct fdt_attach_args * const faa = aux; 991.1Snisimura 1001.8Sthorpej return of_compatible_match(faa->faa_phandle, compat_data); 1011.1Snisimura} 1021.1Snisimura 1031.1Snisimurastatic void 1041.1Snisimurasniemmc_fdt_attach(device_t parent, device_t self, void *aux) 1051.1Snisimura{ 1061.1Snisimura struct sniemmc_softc * const sc = device_private(self); 1071.2Snisimura struct fdt_attach_args * const faa = aux; 1081.2Snisimura const int phandle = faa->faa_phandle; 1091.2Snisimura bus_space_handle_t ioh; 1101.2Snisimura bus_addr_t addr; 1111.2Snisimura bus_size_t size; 1121.2Snisimura char intrstr[128]; 1131.1Snisimura 1141.10Snisimura aprint_naive("\n"); 1151.10Snisimura aprint_normal_dev(self, "Socionext eMMC controller\n"); 1161.10Snisimura 1171.4Snisimura if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0 1181.4Snisimura || bus_space_map(faa->faa_bst, addr, size, 0, &ioh) != 0) { 1191.10Snisimura aprint_error_dev(self, "unable to map device\n"); 1201.2Snisimura return; 1211.2Snisimura } 1221.4Snisimura if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 1231.10Snisimura aprint_error_dev(self, "failed to decode interrupt\n"); 1241.4Snisimura goto fail; 1251.4Snisimura } 1261.4Snisimura sc->sc_ih = fdtbus_intr_establish(phandle, 0, IPL_SDMMC, 0, 1271.4Snisimura sdhc_intr, &sc->sc); 1281.4Snisimura if (sc->sc_ih == NULL) { 1291.4Snisimura aprint_error_dev(self, "couldn't establish interrupt on %s\n", 1301.4Snisimura intrstr); 1311.4Snisimura goto fail; 1321.2Snisimura } 1331.5Snisimura aprint_normal_dev(self, "interrupting on %s\n", intrstr); 1341.2Snisimura 1351.2Snisimura sc->sc.sc_dev = self; 1361.2Snisimura sc->sc.sc_dmat = faa->faa_dmat; 1371.2Snisimura sc->sc.sc_host = sc->sc_hosts; 1381.2Snisimura sc->sc_iot = faa->faa_bst; 1391.2Snisimura sc->sc_ioh = ioh; 1401.2Snisimura sc->sc_iob = addr; 1411.2Snisimura sc->sc_ios = size; 1421.10Snisimura sc->sc_phandle = phandle; 1431.2Snisimura 1441.2Snisimura config_defer(self, sniemmc_attach_i); 1451.2Snisimura return; 1461.2Snisimura fail: 1471.2Snisimura bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); 1481.2Snisimura return; 1491.1Snisimura} 1501.1Snisimura 1511.1Snisimurastatic int 1521.1Snisimurasniemmc_acpi_match(device_t parent, struct cfdata *match, void *aux) 1531.1Snisimura{ 1541.1Snisimura struct acpi_attach_args *aa = aux; 1551.1Snisimura 1561.10Snisimura return acpi_compatible_match(aa, compatible); 1571.1Snisimura} 1581.1Snisimura 1591.1Snisimurastatic void 1601.1Snisimurasniemmc_acpi_attach(device_t parent, device_t self, void *aux) 1611.1Snisimura{ 1621.1Snisimura struct sniemmc_softc * const sc = device_private(self); 1631.2Snisimura struct acpi_attach_args *aa = aux; 1641.10Snisimura ACPI_HANDLE handle = aa->aa_node->ad_handle; 1651.2Snisimura bus_space_handle_t ioh; 1661.2Snisimura struct acpi_resources res; 1671.2Snisimura struct acpi_mem *mem; 1681.2Snisimura struct acpi_irq *irq; 1691.2Snisimura ACPI_STATUS rv; 1701.2Snisimura 1711.10Snisimura aprint_naive("\n"); 1721.10Snisimura aprint_normal(": Socionext eMMC controller\n"); 1731.10Snisimura 1741.2Snisimura rv = acpi_resource_parse(self, aa->aa_node->ad_handle, "_CRS", 1751.2Snisimura &res, &acpi_resource_parse_ops_default); 1761.2Snisimura if (ACPI_FAILURE(rv)) 1771.2Snisimura return; 1781.2Snisimura mem = acpi_res_mem(&res, 0); 1791.2Snisimura irq = acpi_res_irq(&res, 0); 1801.4Snisimura if (mem == NULL || irq == NULL || mem->ar_length == 0) { 1811.10Snisimura aprint_error_dev(self, "incomplete resources\n"); 1821.2Snisimura return; 1831.2Snisimura } 1841.2Snisimura if (bus_space_map(aa->aa_memt, mem->ar_base, mem->ar_length, 0, 1851.2Snisimura &ioh)) { 1861.10Snisimura aprint_error_dev(self, "couldn't map registers\n"); 1871.2Snisimura return; 1881.2Snisimura } 1891.10Snisimura sc->sc_ih = acpi_intr_establish(self, (uint64_t)handle, 1901.4Snisimura IPL_BIO, false, sdhc_intr, &sc->sc, device_xname(self)); 1911.4Snisimura if (sc->sc_ih == NULL) { 1921.4Snisimura aprint_error_dev(self, "couldn't establish interrupt\n"); 1931.4Snisimura goto fail; 1941.4Snisimura } 1951.2Snisimura 1961.2Snisimura sc->sc.sc_dev = self; 1971.2Snisimura sc->sc.sc_dmat = aa->aa_dmat; 1981.2Snisimura sc->sc.sc_host = sc->sc_hosts; 1991.2Snisimura sc->sc_iot = aa->aa_memt; 2001.2Snisimura sc->sc_ioh = ioh; 2011.2Snisimura sc->sc_ios = mem->ar_length; 2021.10Snisimura sc->sc_phandle = 0; 2031.2Snisimura 2041.2Snisimura config_defer(self, sniemmc_attach_i); 2051.2Snisimura 2061.2Snisimura acpi_resource_cleanup(&res); 2071.2Snisimura return; 2081.2Snisimura fail: 2091.2Snisimura bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); 2101.2Snisimura acpi_resource_cleanup(&res); 2111.2Snisimura return; 2121.1Snisimura} 2131.1Snisimura 2141.2Snisimurastatic void 2151.2Snisimurasniemmc_attach_i(device_t self) 2161.1Snisimura{ 2171.2Snisimura struct sniemmc_softc * const sc = device_private(self); 2181.2Snisimura int error; 2191.2Snisimura 2201.2Snisimura sc->sc.sc_flags = 0; 2211.2Snisimura sc->sc.sc_flags |= SDHC_FLAG_32BIT_ACCESS; 2221.2Snisimura sc->sc.sc_clkbase = 50000; /* Default to 50MHz */ 2231.2Snisimura 2241.6Snisimura aprint_normal_dev(sc->sc.sc_dev, "doing sdhc_host() ...\n"); 2251.4Snisimura#if 0 2261.2Snisimura error = sdhc_host_found(&sc->sc, sc->sc_iot, sc->sc_ioh, sc->sc_ios); 2271.4Snisimura#endif 2281.4Snisimura error = 0; 2291.2Snisimura if (error) { 2301.10Snisimura aprint_error_dev(self, "couldn't initialize host, error=%d\n", 2311.10Snisimura error); 2321.2Snisimura goto fail; 2331.2Snisimura } 2341.2Snisimura return; 2351.2Snisimura fail: 2361.3Snisimura if (sc->sc_phandle) 2371.3Snisimura fdtbus_intr_disestablish(sc->sc_phandle, sc->sc_ih); 2381.3Snisimura else 2391.3Snisimura acpi_intr_disestablish(sc->sc_ih); 2401.2Snisimura sc->sc_ih = NULL; 2411.2Snisimura bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); 2421.2Snisimura return; 2431.1Snisimura} 244