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