174e0613eSEmmanuel Vadot /*
274e0613eSEmmanuel Vadot * Copyright 2017 Emmanuel Vadot <manu@freebsd.org>
374e0613eSEmmanuel Vadot *
474e0613eSEmmanuel Vadot * Redistribution and use in source and binary forms, with or without
574e0613eSEmmanuel Vadot * modification, are permitted provided that the following conditions are
674e0613eSEmmanuel Vadot * met:
774e0613eSEmmanuel Vadot *
874e0613eSEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright
974e0613eSEmmanuel Vadot * notice, this list of conditions and the following disclaimer.
1074e0613eSEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright
1174e0613eSEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the
1274e0613eSEmmanuel Vadot * documentation and/or other materials provided with the distribution.
1374e0613eSEmmanuel Vadot *
1474e0613eSEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1574e0613eSEmmanuel Vadot * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1674e0613eSEmmanuel Vadot * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1774e0613eSEmmanuel Vadot * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
1874e0613eSEmmanuel Vadot * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1974e0613eSEmmanuel Vadot * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2074e0613eSEmmanuel Vadot * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
2174e0613eSEmmanuel Vadot * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2274e0613eSEmmanuel Vadot * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
2374e0613eSEmmanuel Vadot * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
2474e0613eSEmmanuel Vadot * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2574e0613eSEmmanuel Vadot */
2674e0613eSEmmanuel Vadot
2774e0613eSEmmanuel Vadot #include <sys/param.h>
2874e0613eSEmmanuel Vadot #include <sys/kernel.h>
2974e0613eSEmmanuel Vadot #include <sys/bus.h>
3074e0613eSEmmanuel Vadot #include <sys/module.h>
3187d4212bSEmmanuel Vadot #include <sys/queue.h>
3287d4212bSEmmanuel Vadot #include <sys/taskqueue.h>
3374e0613eSEmmanuel Vadot
3474e0613eSEmmanuel Vadot #include <machine/bus.h>
3574e0613eSEmmanuel Vadot
3674e0613eSEmmanuel Vadot #include <dev/mmc/bridge.h>
37a1af70e5SEmmanuel Vadot #include <dev/mmc/mmc_fdt_helpers.h>
3874e0613eSEmmanuel Vadot
3974e0613eSEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
4074e0613eSEmmanuel Vadot
41be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
422f882399SEmmanuel Vadot
4374e0613eSEmmanuel Vadot #include <dev/mmc/host/dwmmc_var.h>
4474e0613eSEmmanuel Vadot
45d3a60796SIlya Bakulin #include "opt_mmccam.h"
46d3a60796SIlya Bakulin
47a5091e03SEmmanuel Vadot enum RKTYPE {
48a5091e03SEmmanuel Vadot RK2928 = 1,
49ca28db3cSEmmanuel Vadot RK3288,
50a5091e03SEmmanuel Vadot };
51a5091e03SEmmanuel Vadot
5274e0613eSEmmanuel Vadot static struct ofw_compat_data compat_data[] = {
53a5091e03SEmmanuel Vadot {"rockchip,rk2928-dw-mshc", RK2928},
54ca28db3cSEmmanuel Vadot {"rockchip,rk3288-dw-mshc", RK3288},
5574e0613eSEmmanuel Vadot {NULL, 0},
5674e0613eSEmmanuel Vadot };
5774e0613eSEmmanuel Vadot
58a5091e03SEmmanuel Vadot static int dwmmc_rockchip_update_ios(struct dwmmc_softc *sc, struct mmc_ios *ios);
59a5091e03SEmmanuel Vadot
6074e0613eSEmmanuel Vadot static int
rockchip_dwmmc_probe(device_t dev)6174e0613eSEmmanuel Vadot rockchip_dwmmc_probe(device_t dev)
6274e0613eSEmmanuel Vadot {
6374e0613eSEmmanuel Vadot
6474e0613eSEmmanuel Vadot if (!ofw_bus_status_okay(dev))
6574e0613eSEmmanuel Vadot return (ENXIO);
6674e0613eSEmmanuel Vadot
6774e0613eSEmmanuel Vadot if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
6874e0613eSEmmanuel Vadot return (ENXIO);
6974e0613eSEmmanuel Vadot
7074e0613eSEmmanuel Vadot device_set_desc(dev, "Synopsys DesignWare Mobile "
7174e0613eSEmmanuel Vadot "Storage Host Controller (RockChip)");
7274e0613eSEmmanuel Vadot
7374e0613eSEmmanuel Vadot return (BUS_PROBE_VENDOR);
7474e0613eSEmmanuel Vadot }
7574e0613eSEmmanuel Vadot
7674e0613eSEmmanuel Vadot static int
rockchip_dwmmc_attach(device_t dev)7774e0613eSEmmanuel Vadot rockchip_dwmmc_attach(device_t dev)
7874e0613eSEmmanuel Vadot {
7974e0613eSEmmanuel Vadot struct dwmmc_softc *sc;
80a5091e03SEmmanuel Vadot int type;
81*0f684b5aSStephen Hurd int rc;
8274e0613eSEmmanuel Vadot
8374e0613eSEmmanuel Vadot sc = device_get_softc(dev);
8474e0613eSEmmanuel Vadot sc->hwtype = HWTYPE_ROCKCHIP;
85a5091e03SEmmanuel Vadot type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
8674e0613eSEmmanuel Vadot
87a5091e03SEmmanuel Vadot switch (type) {
88a5091e03SEmmanuel Vadot case RK2928:
8974e0613eSEmmanuel Vadot sc->use_pio = 1;
90a5091e03SEmmanuel Vadot break;
91a5091e03SEmmanuel Vadot }
92a5091e03SEmmanuel Vadot
93a5091e03SEmmanuel Vadot sc->update_ios = &dwmmc_rockchip_update_ios;
94*0f684b5aSStephen Hurd rc = dwmmc_attach(dev);
95a5091e03SEmmanuel Vadot
96*0f684b5aSStephen Hurd /*
97*0f684b5aSStephen Hurd * Note: It's not that the controller doesn't support HS200,
98*0f684b5aSStephen Hurd * it's that FreeBSD doesn't support tuning.
99*0f684b5aSStephen Hurd * If someone implemented tuning, this could work.
100*0f684b5aSStephen Hurd */
101*0f684b5aSStephen Hurd device_printf(dev, "Disabling HS200+ (tuning code not written)\n");
102*0f684b5aSStephen Hurd sc->host.caps &= ~(MMC_CAP_MMC_HS200 | MMC_CAP_MMC_HS400);
103*0f684b5aSStephen Hurd
104*0f684b5aSStephen Hurd return (rc);
10574e0613eSEmmanuel Vadot }
10674e0613eSEmmanuel Vadot
107a5091e03SEmmanuel Vadot static int
dwmmc_rockchip_update_ios(struct dwmmc_softc * sc,struct mmc_ios * ios)108a5091e03SEmmanuel Vadot dwmmc_rockchip_update_ios(struct dwmmc_softc *sc, struct mmc_ios *ios)
109a5091e03SEmmanuel Vadot {
110a5091e03SEmmanuel Vadot unsigned int clock;
111a5091e03SEmmanuel Vadot int error;
112a5091e03SEmmanuel Vadot
113a5091e03SEmmanuel Vadot if (ios->clock && ios->clock != sc->bus_hz) {
114a5091e03SEmmanuel Vadot sc->bus_hz = clock = ios->clock;
115a5091e03SEmmanuel Vadot /* Set the MMC clock. */
116a5091e03SEmmanuel Vadot if (sc->ciu) {
117a5091e03SEmmanuel Vadot /*
118a5091e03SEmmanuel Vadot * Apparently you need to set the ciu clock to
119a5091e03SEmmanuel Vadot * the double of bus_hz
120a5091e03SEmmanuel Vadot */
121a5091e03SEmmanuel Vadot error = clk_set_freq(sc->ciu, clock * 2,
122a5091e03SEmmanuel Vadot CLK_SET_ROUND_DOWN);
123a5091e03SEmmanuel Vadot if (error != 0) {
124a5091e03SEmmanuel Vadot device_printf(sc->dev,
125a5091e03SEmmanuel Vadot "failed to set frequency to %u Hz: %d\n",
126a5091e03SEmmanuel Vadot clock, error);
127a5091e03SEmmanuel Vadot return (error);
128a5091e03SEmmanuel Vadot }
129a5091e03SEmmanuel Vadot }
130a5091e03SEmmanuel Vadot }
131a5091e03SEmmanuel Vadot return (0);
132a5091e03SEmmanuel Vadot }
133a5091e03SEmmanuel Vadot
13474e0613eSEmmanuel Vadot static device_method_t rockchip_dwmmc_methods[] = {
13574e0613eSEmmanuel Vadot /* bus interface */
13674e0613eSEmmanuel Vadot DEVMETHOD(device_probe, rockchip_dwmmc_probe),
13774e0613eSEmmanuel Vadot DEVMETHOD(device_attach, rockchip_dwmmc_attach),
13887dc015dSEmmanuel Vadot DEVMETHOD(device_detach, dwmmc_detach),
13974e0613eSEmmanuel Vadot
14074e0613eSEmmanuel Vadot DEVMETHOD_END
14174e0613eSEmmanuel Vadot };
14274e0613eSEmmanuel Vadot
14374e0613eSEmmanuel Vadot DEFINE_CLASS_1(rockchip_dwmmc, rockchip_dwmmc_driver, rockchip_dwmmc_methods,
14474e0613eSEmmanuel Vadot sizeof(struct dwmmc_softc), dwmmc_driver);
14574e0613eSEmmanuel Vadot
146b58c5abfSJohn Baldwin DRIVER_MODULE(rockchip_dwmmc, simplebus, rockchip_dwmmc_driver, 0, 0);
147b58c5abfSJohn Baldwin DRIVER_MODULE(rockchip_dwmmc, ofwbus, rockchip_dwmmc_driver, NULL, NULL);
14874e0613eSEmmanuel Vadot #ifndef MMCCAM
14974e0613eSEmmanuel Vadot MMC_DECLARE_BRIDGE(rockchip_dwmmc);
15074e0613eSEmmanuel Vadot #endif
151