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