#define CLK_CORE_PHASE_MASK GENMASK(9, 8)
#define CLK_TX_PHASE_MASK GENMASK(11, 10)
#define CLK_RX_PHASE_MASK GENMASK(13, 12)
+#define CLK_PHASE_0 0
+#define CLK_PHASE_180 2
#define CLK_V2_TX_DELAY_MASK GENMASK(19, 16)
#define CLK_V2_RX_DELAY_MASK GENMASK(23, 20)
#define CLK_V2_ALWAYS_ON BIT(24)
#define CLK_V3_RX_DELAY_MASK GENMASK(27, 22)
#define CLK_V3_ALWAYS_ON BIT(28)
-#define CLK_DELAY_STEP_PS 200
-#define CLK_PHASE_STEP 30
-#define CLK_PHASE_POINT_NUM (360 / CLK_PHASE_STEP)
-
#define CLK_TX_DELAY_MASK(h) (h->data->tx_delay_mask)
#define CLK_RX_DELAY_MASK(h) (h->data->rx_delay_mask)
#define CLK_ALWAYS_ON(h) (h->data->always_on)
void __iomem *regs;
struct clk *core_clk;
+ struct clk *mux_clk;
struct clk *mmc_clk;
- struct clk *rx_clk;
- struct clk *tx_clk;
unsigned long req_rate;
bool ddr;
#define CMD_RESP_MASK GENMASK(31, 1)
#define CMD_RESP_SRAM BIT(0)
-struct meson_mmc_phase {
- struct clk_hw hw;
- void __iomem *reg;
- unsigned long phase_mask;
- unsigned long delay_mask;
- unsigned int delay_step_ps;
-};
-
-#define to_meson_mmc_phase(_hw) container_of(_hw, struct meson_mmc_phase, hw)
-
-static int meson_mmc_clk_get_phase(struct clk_hw *hw)
-{
- struct meson_mmc_phase *mmc = to_meson_mmc_phase(hw);
- unsigned int phase_num = 1 << hweight_long(mmc->phase_mask);
- unsigned long period_ps, p, d;
- int degrees;
- u32 val;
-
- val = readl(mmc->reg);
- p = (val & mmc->phase_mask) >> __ffs(mmc->phase_mask);
- degrees = p * 360 / phase_num;
-
- if (mmc->delay_mask) {
- period_ps = DIV_ROUND_UP((unsigned long)NSEC_PER_SEC * 1000,
- clk_get_rate(hw->clk));
- d = (val & mmc->delay_mask) >> __ffs(mmc->delay_mask);
- degrees += d * mmc->delay_step_ps * 360 / period_ps;
- degrees %= 360;
- }
-
- return degrees;
-}
-
-static void meson_mmc_apply_phase_delay(struct meson_mmc_phase *mmc,
- unsigned int phase,
- unsigned int delay)
-{
- u32 val;
-
- val = readl(mmc->reg);
- val &= ~mmc->phase_mask;
- val |= phase << __ffs(mmc->phase_mask);
-
- if (mmc->delay_mask) {
- val &= ~mmc->delay_mask;
- val |= delay << __ffs(mmc->delay_mask);
- }
-
- writel(val, mmc->reg);
-}
-
-static int meson_mmc_clk_set_phase(struct clk_hw *hw, int degrees)
-{
- struct meson_mmc_phase *mmc = to_meson_mmc_phase(hw);
- unsigned int phase_num = 1 << hweight_long(mmc->phase_mask);
- unsigned long period_ps, d = 0, r;
- uint64_t p;
-
- p = degrees % 360;
-
- if (!mmc->delay_mask) {
- p = DIV_ROUND_CLOSEST_ULL(p, 360 / phase_num);
- } else {
- period_ps = DIV_ROUND_UP((unsigned long)NSEC_PER_SEC * 1000,
- clk_get_rate(hw->clk));
-
- /* First compute the phase index (p), the remainder (r) is the
- * part we'll try to acheive using the delays (d).
- */
- r = do_div(p, 360 / phase_num);
- d = DIV_ROUND_CLOSEST(r * period_ps,
- 360 * mmc->delay_step_ps);
- d = min(d, mmc->delay_mask >> __ffs(mmc->delay_mask));
- }
-
- meson_mmc_apply_phase_delay(mmc, p, d);
- return 0;
-}
-
-static const struct clk_ops meson_mmc_clk_phase_ops = {
- .get_phase = meson_mmc_clk_get_phase,
- .set_phase = meson_mmc_clk_set_phase,
-};
-
static unsigned int meson_mmc_get_timeout_msecs(struct mmc_data *data)
{
unsigned int timeout = data->timeout_ns / NSEC_PER_MSEC;
struct clk_init_data init;
struct clk_mux *mux;
struct clk_divider *div;
- struct meson_mmc_phase *core, *tx, *rx;
- struct clk *clk;
char clk_name[32];
int i, ret = 0;
const char *mux_parent_names[MUX_CLK_NUM_PARENTS];
u32 clk_reg;
/* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
- clk_reg = 0;
- clk_reg |= CLK_ALWAYS_ON(host);
+ clk_reg = CLK_ALWAYS_ON(host);
clk_reg |= CLK_DIV_MASK;
+ clk_reg |= FIELD_PREP(CLK_CORE_PHASE_MASK, CLK_PHASE_180);
+ clk_reg |= FIELD_PREP(CLK_TX_PHASE_MASK, CLK_PHASE_0);
+ clk_reg |= FIELD_PREP(CLK_RX_PHASE_MASK, CLK_PHASE_0);
writel(clk_reg, host->regs + SD_EMMC_CLOCK);
/* get the mux parents */
mux->mask = CLK_SRC_MASK >> mux->shift;
mux->hw.init = &init;
- clk = devm_clk_register(host->dev, &mux->hw);
- if (WARN_ON(IS_ERR(clk)))
- return PTR_ERR(clk);
+ host->mux_clk = devm_clk_register(host->dev, &mux->hw);
+ if (WARN_ON(IS_ERR(host->mux_clk)))
+ return PTR_ERR(host->mux_clk);
/* create the divider */
div = devm_kzalloc(host->dev, sizeof(*div), GFP_KERNEL);
init.name = clk_name;
init.ops = &clk_divider_ops;
init.flags = CLK_SET_RATE_PARENT;
- clk_parent[0] = __clk_get_name(clk);
+ clk_parent[0] = __clk_get_name(host->mux_clk);
init.parent_names = clk_parent;
init.num_parents = 1;
div->hw.init = &init;
div->flags = CLK_DIVIDER_ONE_BASED;
- clk = devm_clk_register(host->dev, &div->hw);
- if (WARN_ON(IS_ERR(clk)))
- return PTR_ERR(clk);
-
- /* create the mmc core clock */
- core = devm_kzalloc(host->dev, sizeof(*core), GFP_KERNEL);
- if (!core)
- return -ENOMEM;
-
- snprintf(clk_name, sizeof(clk_name), "%s#core", dev_name(host->dev));
- init.name = clk_name;
- init.ops = &meson_mmc_clk_phase_ops;
- init.flags = CLK_SET_RATE_PARENT;
- clk_parent[0] = __clk_get_name(clk);
- init.parent_names = clk_parent;
- init.num_parents = 1;
-
- core->reg = host->regs + SD_EMMC_CLOCK;
- core->phase_mask = CLK_CORE_PHASE_MASK;
- core->hw.init = &init;
-
- host->mmc_clk = devm_clk_register(host->dev, &core->hw);
- if (WARN_ON(PTR_ERR_OR_ZERO(host->mmc_clk)))
+ host->mmc_clk = devm_clk_register(host->dev, &div->hw);
+ if (WARN_ON(IS_ERR(host->mmc_clk)))
return PTR_ERR(host->mmc_clk);
- /* create the mmc tx clock */
- tx = devm_kzalloc(host->dev, sizeof(*tx), GFP_KERNEL);
- if (!tx)
- return -ENOMEM;
-
- snprintf(clk_name, sizeof(clk_name), "%s#tx", dev_name(host->dev));
- init.name = clk_name;
- init.ops = &meson_mmc_clk_phase_ops;
- init.flags = 0;
- clk_parent[0] = __clk_get_name(host->mmc_clk);
- init.parent_names = clk_parent;
- init.num_parents = 1;
-
- tx->reg = host->regs + SD_EMMC_CLOCK;
- tx->phase_mask = CLK_TX_PHASE_MASK;
- tx->delay_mask = CLK_TX_DELAY_MASK(host);
- tx->delay_step_ps = CLK_DELAY_STEP_PS;
- tx->hw.init = &init;
-
- host->tx_clk = devm_clk_register(host->dev, &tx->hw);
- if (WARN_ON(PTR_ERR_OR_ZERO(host->tx_clk)))
- return PTR_ERR(host->tx_clk);
-
- /* create the mmc rx clock */
- rx = devm_kzalloc(host->dev, sizeof(*rx), GFP_KERNEL);
- if (!rx)
- return -ENOMEM;
-
- snprintf(clk_name, sizeof(clk_name), "%s#rx", dev_name(host->dev));
- init.name = clk_name;
- init.ops = &meson_mmc_clk_phase_ops;
- init.flags = 0;
- clk_parent[0] = __clk_get_name(host->mmc_clk);
- init.parent_names = clk_parent;
- init.num_parents = 1;
-
- rx->reg = host->regs + SD_EMMC_CLOCK;
- rx->phase_mask = CLK_RX_PHASE_MASK;
- rx->delay_mask = CLK_RX_DELAY_MASK(host);
- rx->delay_step_ps = CLK_DELAY_STEP_PS;
- rx->hw.init = &init;
-
- host->rx_clk = devm_clk_register(host->dev, &rx->hw);
- if (WARN_ON(PTR_ERR_OR_ZERO(host->rx_clk)))
- return PTR_ERR(host->rx_clk);
-
/* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
host->mmc->f_min = clk_round_rate(host->mmc_clk, 400000);
ret = clk_set_rate(host->mmc_clk, host->mmc->f_min);
if (ret)
return ret;
- clk_set_phase(host->mmc_clk, 180);
- clk_set_phase(host->tx_clk, 0);
- clk_set_phase(host->rx_clk, 0);
-
return clk_prepare_enable(host->mmc_clk);
}
-static void meson_mmc_shift_map(unsigned long *map, unsigned long shift)
-{
- DECLARE_BITMAP(left, CLK_PHASE_POINT_NUM);
- DECLARE_BITMAP(right, CLK_PHASE_POINT_NUM);
-
- /*
- * shift the bitmap right and reintroduce the dropped bits on the left
- * of the bitmap
- */
- bitmap_shift_right(right, map, shift, CLK_PHASE_POINT_NUM);
- bitmap_shift_left(left, map, CLK_PHASE_POINT_NUM - shift,
- CLK_PHASE_POINT_NUM);
- bitmap_or(map, left, right, CLK_PHASE_POINT_NUM);
-}
-
-static void meson_mmc_find_next_region(unsigned long *map,
- unsigned long *start,
- unsigned long *stop)
-{
- *start = find_next_bit(map, CLK_PHASE_POINT_NUM, *start);
- *stop = find_next_zero_bit(map, CLK_PHASE_POINT_NUM, *start);
-}
-
-static int meson_mmc_find_tuning_point(unsigned long *test)
-{
- unsigned long shift, stop, offset = 0, start = 0, size = 0;
-
- /* Get the all good/all bad situation out the way */
- if (bitmap_full(test, CLK_PHASE_POINT_NUM))
- return 0; /* All points are good so point 0 will do */
- else if (bitmap_empty(test, CLK_PHASE_POINT_NUM))
- return -EIO; /* No successful tuning point */
-
- /*
- * Now we know there is a least one region find. Make sure it does
- * not wrap by the shifting the bitmap if necessary
- */
- shift = find_first_zero_bit(test, CLK_PHASE_POINT_NUM);
- if (shift != 0)
- meson_mmc_shift_map(test, shift);
-
- while (start < CLK_PHASE_POINT_NUM) {
- meson_mmc_find_next_region(test, &start, &stop);
-
- if ((stop - start) > size) {
- offset = start;
- size = stop - start;
- }
-
- start = stop;
- }
-
- /* Get the center point of the region */
- offset += (size / 2);
-
- /* Shift the result back */
- offset = (offset + shift) % CLK_PHASE_POINT_NUM;
-
- return offset;
-}
-
-static int meson_mmc_clk_phase_tuning(struct mmc_host *mmc, u32 opcode,
- struct clk *clk)
-{
- int point, ret;
- DECLARE_BITMAP(test, CLK_PHASE_POINT_NUM);
-
- dev_dbg(mmc_dev(mmc), "%s phase/delay tunning...\n",
- __clk_get_name(clk));
- bitmap_zero(test, CLK_PHASE_POINT_NUM);
-
- /* Explore tuning points */
- for (point = 0; point < CLK_PHASE_POINT_NUM; point++) {
- clk_set_phase(clk, point * CLK_PHASE_STEP);
- ret = mmc_send_tuning(mmc, opcode, NULL);
- if (!ret)
- set_bit(point, test);
- }
-
- /* Find the optimal tuning point and apply it */
- point = meson_mmc_find_tuning_point(test);
- if (point < 0)
- return point; /* tuning failed */
-
- clk_set_phase(clk, point * CLK_PHASE_STEP);
- dev_dbg(mmc_dev(mmc), "success with phase: %d\n",
- clk_get_phase(clk));
- return 0;
-}
-
-static int meson_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
-{
- struct meson_host *host = mmc_priv(mmc);
- int adj = 0;
-
- /* enable signal resampling w/o delay */
- adj = ADJUST_ADJ_EN;
- writel(adj, host->regs + host->data->adjust);
-
- return meson_mmc_clk_phase_tuning(mmc, opcode, host->rx_clk);
-}
-
static int meson_mmc_prepare_ios_clock(struct meson_host *host,
struct mmc_ios *ios)
{
/* disable signal resampling */
writel(0, host->regs + host->data->adjust);
- /* Reset rx phase */
- clk_set_phase(host->rx_clk, 0);
-
break;
case MMC_POWER_ON:
.get_cd = meson_mmc_get_cd,
.pre_req = meson_mmc_pre_req,
.post_req = meson_mmc_post_req,
- .execute_tuning = meson_mmc_execute_tuning,
.card_busy = meson_mmc_card_busy,
.start_signal_voltage_switch = meson_mmc_voltage_switch,
};