xref: /netbsd/sys/arch/arm/altera/cycv_dwcmmc.c (revision e46279f7)
1*e46279f7Sskrll /* $NetBSD: cycv_dwcmmc.c,v 1.7 2021/01/29 14:12:01 skrll Exp $ */
2e24a29a8Saymeric 
3e24a29a8Saymeric /*-
4e24a29a8Saymeric  * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
5e24a29a8Saymeric  * All rights reserved.
6e24a29a8Saymeric  *
7e24a29a8Saymeric  * Redistribution and use in source and binary forms, with or without
8e24a29a8Saymeric  * modification, are permitted provided that the following conditions
9e24a29a8Saymeric  * are met:
10e24a29a8Saymeric  * 1. Redistributions of source code must retain the above copyright
11e24a29a8Saymeric  *    notice, this list of conditions and the following disclaimer.
12e24a29a8Saymeric  * 2. Redistributions in binary form must reproduce the above copyright
13e24a29a8Saymeric  *    notice, this list of conditions and the following disclaimer in the
14e24a29a8Saymeric  *    documentation and/or other materials provided with the distribution.
15e24a29a8Saymeric  *
16e24a29a8Saymeric  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17e24a29a8Saymeric  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18e24a29a8Saymeric  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19e24a29a8Saymeric  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20e24a29a8Saymeric  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21e24a29a8Saymeric  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22e24a29a8Saymeric  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23e24a29a8Saymeric  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24e24a29a8Saymeric  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25e24a29a8Saymeric  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26e24a29a8Saymeric  * SUCH DAMAGE.
27e24a29a8Saymeric  */
28e24a29a8Saymeric 
29e24a29a8Saymeric #include <sys/cdefs.h>
30*e46279f7Sskrll __KERNEL_RCSID(0, "$NetBSD: cycv_dwcmmc.c,v 1.7 2021/01/29 14:12:01 skrll Exp $");
31e24a29a8Saymeric 
32e24a29a8Saymeric #include <sys/param.h>
33e24a29a8Saymeric #include <sys/bus.h>
34e24a29a8Saymeric #include <sys/device.h>
35e24a29a8Saymeric #include <sys/intr.h>
36e24a29a8Saymeric #include <sys/systm.h>
37e24a29a8Saymeric #include <sys/kernel.h>
38e24a29a8Saymeric #include <sys/mutex.h>
39e24a29a8Saymeric #include <sys/condvar.h>
40e24a29a8Saymeric 
41e24a29a8Saymeric #include <dev/ic/dwc_mmc_reg.h>
42e24a29a8Saymeric #include <dev/ic/dwc_mmc_var.h>
43e24a29a8Saymeric #include <dev/fdt/fdtvar.h>
44e24a29a8Saymeric 
45e24a29a8Saymeric #define	FIFO_REG	0x200
46e24a29a8Saymeric 
47e24a29a8Saymeric static int	cycv_dwcmmc_match(device_t, cfdata_t, void *);
48e24a29a8Saymeric static void	cycv_dwcmmc_attach(device_t, device_t, void *);
49e24a29a8Saymeric 
50802c98c0Sjmcneill static int	cycv_dwcmmc_card_detect(struct dwc_mmc_softc *);
51802c98c0Sjmcneill 
52e24a29a8Saymeric struct cycv_dwcmmc_softc {
53e24a29a8Saymeric 	struct dwc_mmc_softc	sc;
54802c98c0Sjmcneill 	int			sc_phandle;
55802c98c0Sjmcneill 	bool			sc_non_removable;
56802c98c0Sjmcneill 	bool			sc_broken_cd;
57802c98c0Sjmcneill 	struct fdtbus_gpio_pin	*sc_gpio_cd;
58802c98c0Sjmcneill 	bool			sc_gpio_cd_inverted;
59e24a29a8Saymeric 	struct clk		*sc_clk_biu;
60e24a29a8Saymeric 	struct clk		*sc_clk_ciu;
61e24a29a8Saymeric };
62e24a29a8Saymeric 
63e24a29a8Saymeric CFATTACH_DECL_NEW(cycv_dwcmmc, sizeof(struct dwc_mmc_softc),
64e24a29a8Saymeric 	cycv_dwcmmc_match, cycv_dwcmmc_attach, NULL, NULL);
65e24a29a8Saymeric 
668e90f9edSthorpej static const struct device_compatible_entry compat_data[] = {
678e90f9edSthorpej 	{ .compat = "altr,socfpga-dw-mshc" },
688e90f9edSthorpej 	DEVICE_COMPAT_EOL
69e24a29a8Saymeric };
70e24a29a8Saymeric 
71e24a29a8Saymeric static int
cycv_dwcmmc_match(device_t parent,cfdata_t cf,void * aux)72e24a29a8Saymeric cycv_dwcmmc_match(device_t parent, cfdata_t cf, void *aux)
73e24a29a8Saymeric {
74e24a29a8Saymeric 	struct fdt_attach_args * const faa = aux;
75e24a29a8Saymeric 
768e90f9edSthorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
77e24a29a8Saymeric }
78e24a29a8Saymeric 
79e24a29a8Saymeric static void
cycv_dwcmmc_attach(device_t parent,device_t self,void * aux)80e24a29a8Saymeric cycv_dwcmmc_attach(device_t parent, device_t self, void *aux)
81e24a29a8Saymeric {
82e24a29a8Saymeric 	struct cycv_dwcmmc_softc *esc = device_private(self);
83e24a29a8Saymeric 	struct dwc_mmc_softc *sc = &esc->sc;
84e24a29a8Saymeric 	struct fdt_attach_args * const faa = aux;
85e24a29a8Saymeric 	const int phandle = faa->faa_phandle;
86e24a29a8Saymeric 	char intrstr[128];
87e24a29a8Saymeric 	bus_addr_t addr;
88e24a29a8Saymeric 	bus_size_t size;
89e24a29a8Saymeric 	u_int fifo_depth;
90e24a29a8Saymeric 	int error;
91e24a29a8Saymeric 
92e24a29a8Saymeric 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
93e24a29a8Saymeric 		aprint_error(": couldn't get registers\n");
94e24a29a8Saymeric 		return;
95e24a29a8Saymeric 	}
96e24a29a8Saymeric 
97e24a29a8Saymeric 	if (of_getprop_uint32(phandle, "fifo-depth", &fifo_depth)) {
98e24a29a8Saymeric 		fifo_depth = 64;
99e24a29a8Saymeric 	}
100e24a29a8Saymeric 
101e24a29a8Saymeric 	esc->sc_clk_biu = fdtbus_clock_get(phandle, "biu");
102e24a29a8Saymeric 	if (esc->sc_clk_biu == NULL) {
103e24a29a8Saymeric 		aprint_error(": couldn't get clock biu\n");
104e24a29a8Saymeric 		return;
105e24a29a8Saymeric 	}
106e24a29a8Saymeric 	esc->sc_clk_ciu = fdtbus_clock_get(phandle, "ciu");
107e24a29a8Saymeric 	if (esc->sc_clk_ciu == NULL) {
108e24a29a8Saymeric 		aprint_error(": couldn't get clock ciu\n");
109e24a29a8Saymeric 		return;
110e24a29a8Saymeric 	}
111e24a29a8Saymeric 
112e24a29a8Saymeric 	error = clk_enable(esc->sc_clk_biu);
113e24a29a8Saymeric 	if (error) {
114e24a29a8Saymeric 		aprint_error(": couldn't enable clock biu: %d\n", error);
115e24a29a8Saymeric 		return;
116e24a29a8Saymeric 	}
117e24a29a8Saymeric 	error = clk_enable(esc->sc_clk_ciu);
118e24a29a8Saymeric 	if (error) {
119e24a29a8Saymeric 		aprint_error(": couldn't enable clock ciu: %d\n", error);
120e24a29a8Saymeric 		return;
121e24a29a8Saymeric 	}
122e24a29a8Saymeric 
123e24a29a8Saymeric 	sc->sc_dev = self;
124e24a29a8Saymeric 	sc->sc_bst = faa->faa_bst;
125e24a29a8Saymeric 	sc->sc_dmat = faa->faa_dmat;
126e24a29a8Saymeric 	error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
127e24a29a8Saymeric 	if (error) {
12863f179c9Sskrll 		aprint_error(": couldn't map %#" PRIxBUSADDR ": %d\n",
12963f179c9Sskrll 		    addr, error);
130e24a29a8Saymeric 		return;
131e24a29a8Saymeric 	}
132e24a29a8Saymeric 
133e24a29a8Saymeric 	sc->sc_clock_freq = clk_get_rate(esc->sc_clk_ciu);
134e24a29a8Saymeric 	sc->sc_fifo_depth = fifo_depth;
135e24a29a8Saymeric 	sc->sc_fifo_reg = FIFO_REG;
136e24a29a8Saymeric 	sc->sc_flags = DWC_MMC_F_USE_HOLD_REG | DWC_MMC_F_DMA;
137bd8beb09Sskrll 	sc->sc_intr_cardmask = DWC_MMC_INT_SDIO_INT(8);
138e24a29a8Saymeric 
139802c98c0Sjmcneill 	sc->sc_card_detect = cycv_dwcmmc_card_detect;
140e24a29a8Saymeric 	sc->sc_write_protect = NULL;
141e24a29a8Saymeric 
142802c98c0Sjmcneill 	esc->sc_phandle = phandle;
143802c98c0Sjmcneill 	esc->sc_gpio_cd = fdtbus_gpio_acquire(phandle, "cd-gpios", GPIO_PIN_INPUT);
144802c98c0Sjmcneill 	esc->sc_gpio_cd_inverted = of_hasprop(phandle, "cd-inverted") ? 0 : 1;
145802c98c0Sjmcneill 
146802c98c0Sjmcneill 	esc->sc_non_removable = of_hasprop(phandle, "non-removable");
147802c98c0Sjmcneill 	esc->sc_broken_cd = of_hasprop(phandle, "broken-cd");
148802c98c0Sjmcneill 
149e24a29a8Saymeric 	aprint_naive("\n");
150e24a29a8Saymeric 	aprint_normal(": MHS (%u Hz)\n", sc->sc_clock_freq);
151e24a29a8Saymeric 
152e24a29a8Saymeric 	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
153e24a29a8Saymeric 		aprint_error_dev(self, "failed to decode interrupt\n");
154e24a29a8Saymeric 		return;
155e24a29a8Saymeric 	}
156e24a29a8Saymeric 
157e24a29a8Saymeric 	if (dwc_mmc_init(sc) != 0)
158e24a29a8Saymeric 		return;
159e24a29a8Saymeric 
160*e46279f7Sskrll 	sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_BIO, 0,
161*e46279f7Sskrll 	    dwc_mmc_intr, sc, device_xname(sc->sc_dev));
162e24a29a8Saymeric 	if (sc->sc_ih == NULL) {
163e24a29a8Saymeric 		aprint_error_dev(self, "couldn't establish interrupt on %s\n",
164e24a29a8Saymeric 		    intrstr);
165e24a29a8Saymeric 		return;
166e24a29a8Saymeric 	}
167e24a29a8Saymeric 	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
168e24a29a8Saymeric }
169802c98c0Sjmcneill 
170802c98c0Sjmcneill static int
cycv_dwcmmc_card_detect(struct dwc_mmc_softc * sc)171802c98c0Sjmcneill cycv_dwcmmc_card_detect(struct dwc_mmc_softc *sc)
172802c98c0Sjmcneill {
173802c98c0Sjmcneill 	struct cycv_dwcmmc_softc *esc = device_private(sc->sc_dev);
174802c98c0Sjmcneill 	int val;
175802c98c0Sjmcneill 
176802c98c0Sjmcneill 	if (esc->sc_non_removable || esc->sc_broken_cd) {
177802c98c0Sjmcneill 		return 1;
178802c98c0Sjmcneill 	} else if (esc->sc_gpio_cd != NULL) {
179802c98c0Sjmcneill 		val = fdtbus_gpio_read(esc->sc_gpio_cd);
180802c98c0Sjmcneill 		if (esc->sc_gpio_cd_inverted)
181802c98c0Sjmcneill 			val = !val;
182802c98c0Sjmcneill 		return val;
183802c98c0Sjmcneill 	} else {
184802c98c0Sjmcneill 		return 1;
185802c98c0Sjmcneill 	}
186802c98c0Sjmcneill }
187