xref: /netbsd/sys/arch/arm/samsung/exynos_dwcmmc.c (revision c0583cc2)
1*c0583cc2Sjmcneill /* $NetBSD: exynos_dwcmmc.c,v 1.2 2015/12/27 20:49:01 jmcneill Exp $ */
21f005c6fSjmcneill 
31f005c6fSjmcneill /*-
41f005c6fSjmcneill  * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
51f005c6fSjmcneill  * All rights reserved.
61f005c6fSjmcneill  *
71f005c6fSjmcneill  * Redistribution and use in source and binary forms, with or without
81f005c6fSjmcneill  * modification, are permitted provided that the following conditions
91f005c6fSjmcneill  * are met:
101f005c6fSjmcneill  * 1. Redistributions of source code must retain the above copyright
111f005c6fSjmcneill  *    notice, this list of conditions and the following disclaimer.
121f005c6fSjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
131f005c6fSjmcneill  *    notice, this list of conditions and the following disclaimer in the
141f005c6fSjmcneill  *    documentation and/or other materials provided with the distribution.
151f005c6fSjmcneill  *
161f005c6fSjmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
171f005c6fSjmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
181f005c6fSjmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
191f005c6fSjmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
201f005c6fSjmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
211f005c6fSjmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
221f005c6fSjmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
231f005c6fSjmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
241f005c6fSjmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
251f005c6fSjmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
261f005c6fSjmcneill  * SUCH DAMAGE.
271f005c6fSjmcneill  */
281f005c6fSjmcneill 
291f005c6fSjmcneill #include <sys/cdefs.h>
30*c0583cc2Sjmcneill __KERNEL_RCSID(0, "$NetBSD: exynos_dwcmmc.c,v 1.2 2015/12/27 20:49:01 jmcneill Exp $");
311f005c6fSjmcneill 
321f005c6fSjmcneill #include <sys/param.h>
331f005c6fSjmcneill #include <sys/bus.h>
341f005c6fSjmcneill #include <sys/device.h>
351f005c6fSjmcneill #include <sys/intr.h>
361f005c6fSjmcneill #include <sys/systm.h>
371f005c6fSjmcneill #include <sys/kernel.h>
381f005c6fSjmcneill #include <sys/mutex.h>
391f005c6fSjmcneill #include <sys/condvar.h>
401f005c6fSjmcneill 
411f005c6fSjmcneill #include <arm/samsung/exynos_var.h>
421f005c6fSjmcneill 
431f005c6fSjmcneill #include <dev/ic/dwc_mmc_var.h>
441f005c6fSjmcneill #include <dev/fdt/fdtvar.h>
451f005c6fSjmcneill 
461f005c6fSjmcneill static int	exynos_dwcmmc_match(device_t, cfdata_t, void *);
471f005c6fSjmcneill static void	exynos_dwcmmc_attach(device_t, device_t, void *);
481f005c6fSjmcneill 
491f005c6fSjmcneill static void	exynos_dwcmmc_attach_i(device_t);
501f005c6fSjmcneill 
511f005c6fSjmcneill static int	exynos_dwcmmc_card_detect(struct dwc_mmc_softc *);
521f005c6fSjmcneill 
531f005c6fSjmcneill struct exynos_dwcmmc_softc {
541f005c6fSjmcneill 	struct dwc_mmc_softc	sc;
551f005c6fSjmcneill 	struct clk		*sc_clk_biu;
561f005c6fSjmcneill 	struct clk		*sc_clk_ciu;
571f005c6fSjmcneill 	struct fdtbus_gpio_pin	*sc_pin_cd;
581f005c6fSjmcneill };
591f005c6fSjmcneill 
601f005c6fSjmcneill CFATTACH_DECL_NEW(exynos_dwcmmc, sizeof(struct dwc_mmc_softc),
611f005c6fSjmcneill 	exynos_dwcmmc_match, exynos_dwcmmc_attach, NULL, NULL);
621f005c6fSjmcneill 
631f005c6fSjmcneill static const char * const exynos_dwcmmc_compat[] = {
641f005c6fSjmcneill 	"samsung,exynos5420-dw-mshc-smu",
651f005c6fSjmcneill 	"samsung,exynos5420-dw-mshc",
661f005c6fSjmcneill 	NULL
671f005c6fSjmcneill };
681f005c6fSjmcneill 
691f005c6fSjmcneill static int
701f005c6fSjmcneill exynos_dwcmmc_match(device_t parent, cfdata_t cf, void *aux)
711f005c6fSjmcneill {
721f005c6fSjmcneill 	struct fdt_attach_args * const faa = aux;
731f005c6fSjmcneill 
741f005c6fSjmcneill 	return of_match_compatible(faa->faa_phandle, exynos_dwcmmc_compat);
751f005c6fSjmcneill }
761f005c6fSjmcneill 
771f005c6fSjmcneill static void
781f005c6fSjmcneill exynos_dwcmmc_attach(device_t parent, device_t self, void *aux)
791f005c6fSjmcneill {
801f005c6fSjmcneill 	struct exynos_dwcmmc_softc *esc = device_private(self);
811f005c6fSjmcneill 	struct dwc_mmc_softc *sc = &esc->sc;
821f005c6fSjmcneill 	struct fdt_attach_args * const faa = aux;
831f005c6fSjmcneill 	const int phandle = faa->faa_phandle;
841f005c6fSjmcneill 	char intrstr[128];
851f005c6fSjmcneill 	bus_addr_t addr;
861f005c6fSjmcneill 	bus_size_t size;
87*c0583cc2Sjmcneill 	u_int bus_width, ciu_div, fifo_depth;
881f005c6fSjmcneill 	int error;
891f005c6fSjmcneill 
901f005c6fSjmcneill 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
911f005c6fSjmcneill 		aprint_error(": couldn't get registers\n");
921f005c6fSjmcneill 		return;
931f005c6fSjmcneill 	}
941f005c6fSjmcneill 
951f005c6fSjmcneill 	if (of_getprop_uint32(phandle, "bus-width", &bus_width)) {
961f005c6fSjmcneill 		bus_width = 4;
971f005c6fSjmcneill 	}
98*c0583cc2Sjmcneill 	if (of_getprop_uint32(phandle, "fifo-depth", &fifo_depth)) {
99*c0583cc2Sjmcneill 		fifo_depth = 64;
100*c0583cc2Sjmcneill 	}
1011f005c6fSjmcneill 	if (of_getprop_uint32(phandle, "samsung,dw-mshc-ciu-div", &ciu_div)) {
1021f005c6fSjmcneill 		aprint_error(": missing samsung,dw-mshc-ciu-div property\n");
1031f005c6fSjmcneill 		return;
1041f005c6fSjmcneill 	}
1051f005c6fSjmcneill 
1061f005c6fSjmcneill 	esc->sc_clk_biu = fdtbus_clock_get(phandle, "biu");
1071f005c6fSjmcneill 	if (esc->sc_clk_biu == NULL) {
1081f005c6fSjmcneill 		aprint_error(": couldn't get clock biu\n");
1091f005c6fSjmcneill 		return;
1101f005c6fSjmcneill 	}
1111f005c6fSjmcneill 	esc->sc_clk_ciu = fdtbus_clock_get(phandle, "ciu");
1121f005c6fSjmcneill 	if (esc->sc_clk_ciu == NULL) {
1131f005c6fSjmcneill 		aprint_error(": couldn't get clock ciu\n");
1141f005c6fSjmcneill 		return;
1151f005c6fSjmcneill 	}
116*c0583cc2Sjmcneill 
117*c0583cc2Sjmcneill 	error = clk_enable(esc->sc_clk_biu);
1181f005c6fSjmcneill 	if (error) {
119*c0583cc2Sjmcneill 		aprint_error(": couldn't enable clock biu: %d\n", error);
1201f005c6fSjmcneill 		return;
1211f005c6fSjmcneill 	}
1221f005c6fSjmcneill 	error = clk_enable(esc->sc_clk_ciu);
1231f005c6fSjmcneill 	if (error) {
1241f005c6fSjmcneill 		aprint_error(": couldn't enable clock ciu: %d\n", error);
1251f005c6fSjmcneill 		return;
1261f005c6fSjmcneill 	}
1271f005c6fSjmcneill 
1281f005c6fSjmcneill 	sc->sc_dev = self;
1291f005c6fSjmcneill 	sc->sc_bst = faa->faa_bst;
1301f005c6fSjmcneill 	sc->sc_dmat = faa->faa_dmat;
1311f005c6fSjmcneill 	error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
1321f005c6fSjmcneill 	if (error) {
1331f005c6fSjmcneill 		aprint_error(": couldn't map %#llx: %d\n",
1341f005c6fSjmcneill 		    (uint64_t)addr, error);
1351f005c6fSjmcneill 		return;
1361f005c6fSjmcneill 	}
1371f005c6fSjmcneill 
1381f005c6fSjmcneill 	sc->sc_clock_freq = clk_get_rate(esc->sc_clk_ciu) / (ciu_div + 1);
139*c0583cc2Sjmcneill 	sc->sc_fifo_depth = fifo_depth;
1401f005c6fSjmcneill 
1411f005c6fSjmcneill 	esc->sc_pin_cd = fdtbus_gpio_acquire(phandle, "cd-gpios",
1421f005c6fSjmcneill 	    GPIO_PIN_INPUT);
1431f005c6fSjmcneill 	if (esc->sc_pin_cd) {
1441f005c6fSjmcneill 		sc->sc_card_detect = exynos_dwcmmc_card_detect;
1451f005c6fSjmcneill 	}
1461f005c6fSjmcneill 
1471f005c6fSjmcneill 	aprint_naive("\n");
1481f005c6fSjmcneill 	aprint_normal(": MHS (%u Hz)\n", sc->sc_clock_freq);
1491f005c6fSjmcneill 
1501f005c6fSjmcneill 	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
1511f005c6fSjmcneill 		aprint_error_dev(self, "failed to decode interrupt\n");
1521f005c6fSjmcneill 		return;
1531f005c6fSjmcneill 	}
1541f005c6fSjmcneill 
1551f005c6fSjmcneill 	sc->sc_ih = fdtbus_intr_establish(phandle, 0, IPL_BIO, 0,
1561f005c6fSjmcneill 	    dwc_mmc_intr, sc);
1571f005c6fSjmcneill 	if (sc->sc_ih == NULL) {
1581f005c6fSjmcneill 		aprint_error_dev(self, "couldn't establish interrupt on %s\n",
1591f005c6fSjmcneill 		    intrstr);
1601f005c6fSjmcneill 		return;
1611f005c6fSjmcneill 	}
1621f005c6fSjmcneill 	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
1631f005c6fSjmcneill 
1641f005c6fSjmcneill 	config_interrupts(self, exynos_dwcmmc_attach_i);
1651f005c6fSjmcneill }
1661f005c6fSjmcneill 
1671f005c6fSjmcneill static void
1681f005c6fSjmcneill exynos_dwcmmc_attach_i(device_t self)
1691f005c6fSjmcneill {
1701f005c6fSjmcneill 	struct exynos_dwcmmc_softc *esc = device_private(self);
1711f005c6fSjmcneill 	struct dwc_mmc_softc *sc = &esc->sc;
1721f005c6fSjmcneill 
1731f005c6fSjmcneill 	dwc_mmc_init(sc);
1741f005c6fSjmcneill }
1751f005c6fSjmcneill 
1761f005c6fSjmcneill static int
1771f005c6fSjmcneill exynos_dwcmmc_card_detect(struct dwc_mmc_softc *sc)
1781f005c6fSjmcneill {
1791f005c6fSjmcneill 	struct exynos_dwcmmc_softc *esc = device_private(sc->sc_dev);
1801f005c6fSjmcneill 
1811f005c6fSjmcneill 	KASSERT(esc->sc_pin_cd != NULL);
1821f005c6fSjmcneill 
1831f005c6fSjmcneill 	return fdtbus_gpio_read(esc->sc_pin_cd);
1841f005c6fSjmcneill }
185