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