/* * libubb/mmcclk.c - Calculate MMC bus clock speed * * Written 2013 by Werner Almesberger * Copyright 2013 Werner Almesberger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include #define MSCCDR_MAX 32 #define CLKRT_MAX 8 /* * Nominally, the limit is 20 MHz. A clock can be obtained until 84 MHz, * possibly even higher. Beyond 56 MHz, the MMC controller in some Bens * has been observed to fail to complete the operation when attempting * data transmission. * * Some Bens still work fine at 84 MHz. */ #define BUS_SAFE_MHZ 56 /* always works */ #define BUS_UNSAFE_MHZ 84 /* some devices don't like this */ static int calculate_clock(struct mmcclk *dsc) { dsc->bus_clk_hz = dsc->sys_clk_hz/(dsc->clkdiv+1.0)/(1 << dsc->clkrt); if (dsc->flags & MMCCLK_FLAG_ALL) return 1; if ((dsc->flags & MMCCLK_FLAG_UNSAFE) && dsc->bus_clk_hz <= BUS_UNSAFE_MHZ*1000000) return 1; return dsc->bus_clk_hz <= BUS_SAFE_MHZ*1000000; } void mmcclk_first(struct mmcclk *dsc, int sys_clk_hz, unsigned flags) { if (sys_clk_hz) dsc->sys_clk_hz = sys_clk_hz; else dsc->sys_clk_hz = (CPCCR >> 21) & 1 ? BEN_PLL_CLK_HZ : BEN_PLL_CLK_HZ/2; dsc->flags = flags; dsc->clkdiv = dsc->clkrt = 0; if (calculate_clock(dsc)) return; mmcclk_next(dsc); } int mmcclk_next(struct mmcclk *dsc) { while (1) { if (++dsc->clkdiv == MSCCDR_MAX) { dsc->clkdiv = 0; if (++dsc->clkrt == CLKRT_MAX) return 0; } if (calculate_clock(dsc)) return 1; } } void mmcclk_start(struct mmcclk *dsc) { MSCCDR = dsc->clkdiv; /* set controller clock */ CLKGR &= ~(1 << 7); /* enable MSC clock */ MSC_STRPCL = MSC_STRPCRL_RESET; /* reset the MSC */ while (MSC_STAT & MSC_STAT_IS_RESETTING); /* wait until reset finishes */ MSC_CLKRT = dsc->clkrt; /* set bus clock */ MSC_STRPCL = MSC_STRPCRL_START_CLOCK; } void mmcclk_stop(void) { MSC_STRPCL = MSC_STRPCRL_STOP_CLOCK; }