Received: by 2002:ac0:a5a7:0:0:0:0:0 with SMTP id m36-v6csp3215654imm; Fri, 10 Aug 2018 05:50:18 -0700 (PDT) X-Google-Smtp-Source: AA+uWPzW0swSY9UUiI0jDgEqsvxFXLWIODTAFZUZvz9z84J4vaC4r2YR2mC0sC59h6sqYHi7zrWc X-Received: by 2002:a62:f587:: with SMTP id b7-v6mr6955222pfm.158.1533905418103; Fri, 10 Aug 2018 05:50:18 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1533905418; cv=none; d=google.com; s=arc-20160816; b=CJCrrD2anvtpUZYD612QuMEYRtX77/MsEumi3dzntco3rWVMtn4aaTbKMxDryk/FNT eaqbzIpSpdkibe4bhJ1WOLj+1WZN7ocyni2al5Tda9auadPVLe335oHCwdw/OH67khrb DXZD/lm5ASUNMMiYsjY97C7H1Q1E/nbBXIbbFLnULDh1gDL/GbmbijdUbSsXrAMde72v t3ewHVQ3wq8HEgLAgKZyOR0itwTKrBxja/RidzBsKAiYfFW1oAhYQdW1CgN7PKVXFAws wMtVSCC4xJNlIXVBYih3LS6MX25u08zwMDQTBqYn9WYp6Bi5wsrOVYiwLB3EMn8EOXl4 Jw1A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:date:cc:to:from:subject:message-id :dkim-signature:arc-authentication-results; bh=C0up0PDc7SV3gxBfNhXR6Y2sfCGipUJS/fpAjhKN2ow=; b=ONl06dp/9/gxQIi+zPG64Yr4L9skWly/48zCgAbW85t49aGSZQOzh3LCudu6ceyEan bw3YelmPeg7sT9l0ARW6Dth8p6jwtEijPEGgazlskgzSATwLQkYOmg+MQgXsfAcS17yA xYjTVaM5lyhfRFVyb2FnolJ+TuhppV/11tZlhhHIQsE+vgRVuFDs33WND/LB/WrDO6q3 6b3lochCZc9dNM/3BK2PyKydNS8JiZKn98Y4UGBxlrOzM/SY5/vQP+LATtcFGC/rNGir 9PDOujFDC0TX6MtSjWMPJfDnPmpsDE+FXPNya7FU8EhrnJ35DRLR6YiQEYjqhgRYFpIx jGUA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@baylibre-com.20150623.gappssmtp.com header.s=20150623 header.b=um8QGU7d; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id c18-v6si8870639pgh.530.2018.08.10.05.50.03; Fri, 10 Aug 2018 05:50:18 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@baylibre-com.20150623.gappssmtp.com header.s=20150623 header.b=um8QGU7d; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728274AbeHJPRv (ORCPT + 99 others); Fri, 10 Aug 2018 11:17:51 -0400 Received: from mail-wr1-f67.google.com ([209.85.221.67]:46914 "EHLO mail-wr1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727639AbeHJPRv (ORCPT ); Fri, 10 Aug 2018 11:17:51 -0400 Received: by mail-wr1-f67.google.com with SMTP id h14-v6so8168597wrw.13 for ; Fri, 10 Aug 2018 05:48:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20150623.gappssmtp.com; s=20150623; h=message-id:subject:from:to:cc:date:in-reply-to:references :mime-version:content-transfer-encoding; bh=C0up0PDc7SV3gxBfNhXR6Y2sfCGipUJS/fpAjhKN2ow=; b=um8QGU7dEaAxCkeDW7Q7HRb6ujrB31A4ObWXRxHLQQFODmnRDQi967riThVvfXxqtG VMqpo64SeBHbUaDc9IKnSoE/rcpatfci2EDl2WcxRKSu9VwswFP6T/8aIF76bxy59wXN wxv22a6z8gmH8XVkzwC8Wb4TQgHZ/6JpXX3Jav9g8+47fGNkSj0mPZxYMbc547fXtMzg F3s0h13fh6TrOMqoddP+NzKodVUgXi40ZjD4vg0XLYxEyXrytGcE333i1lpiOVwPk5Ev cxzWykGpW6E3oFkDbTW148vKW0uRdeBlSnPwMd0OQTa+1icEZTyDo1RtPHGyy8QB/ZQL bXsg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:subject:from:to:cc:date:in-reply-to :references:mime-version:content-transfer-encoding; bh=C0up0PDc7SV3gxBfNhXR6Y2sfCGipUJS/fpAjhKN2ow=; b=f8NhwnsR9WGL9lutnFT+VP+nsa1KmF3NBhxbxqbenpfOPO5IzgivyyQTTanCifNl60 2cyUfc+J5dqql+jtOKUQ6owPl3YFzCMGZu45Jmu/RarPrxzlOfEt6N7Ez6CYhXyEQw7j 3lrI22xdHAxuE9mSfdvj3dg+UxCSlf3OsBVnNYeRN6xOAuhq5JV7/INAfDCFMPlyOH+8 nFK9lENPGgtgufihrgxJsszgQNiFuLKmLNRqYBuFwlfmXVMTBoj8kV6bwaJqTfuvqxPm q9WCc7GGK10nxRGa/i5DqriWcFqTq6JI6/Nhi15+6Ug/IIlXhA+GembZ5Zv9LCm08quJ GI+Q== X-Gm-Message-State: AOUpUlEsUa+W9UPjEsKWTWsn7/g7aIQ/eJhk7g8jO0AiebBqm3nIhzO2 fPbQEQ5aC39vV5XYLHh4CBK9gA== X-Received: by 2002:adf:b642:: with SMTP id i2-v6mr3960400wre.54.1533905282478; Fri, 10 Aug 2018 05:48:02 -0700 (PDT) Received: from boomer (cag06-3-82-243-161-21.fbx.proxad.net. [82.243.161.21]) by smtp.gmail.com with ESMTPSA id i205-v6sm1962138wmf.30.2018.08.10.05.48.00 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Fri, 10 Aug 2018 05:48:01 -0700 (PDT) Message-ID: Subject: Re: [PATCH v4 3/3] clk: meson: add sub MMC clock controller driver From: Jerome Brunet To: Yixun Lan , Neil Armstrong Cc: Kevin Hilman , Carlo Caione , Rob Herring , Michael Turquette , Stephen Boyd , Martin Blumenstingl , Liang Yang , Jianxin Pan , Qiufang Dai , Jian Hu , linux-clk@vger.kernel.org, linux-amlogic@lists.infradead.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Date: Fri, 10 Aug 2018 14:47:59 +0200 In-Reply-To: <20180809070724.11935-4-yixun.lan@amlogic.com> References: <20180809070724.11935-1-yixun.lan@amlogic.com> <20180809070724.11935-4-yixun.lan@amlogic.com> Content-Type: text/plain; charset="UTF-8" X-Mailer: Evolution 3.28.5 (3.28.5-1.fc28) Mime-Version: 1.0 Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Thu, 2018-08-09 at 15:07 +0800, Yixun Lan wrote: > The patch will add a MMC clock controller driver which used by MMC or NAND, > It provide a mux and divider clock, and three phase clocks - core, tx, tx. > > Two clocks are provided as the parent of MMC clock controller from > upper layer clock controller - eg "amlogic,axg-clkc" in AXG platform. > > To specify which clock the MMC or NAND driver may consume, > the preprocessor macros in the dt-bindings/clock/amlogic,mmc-clkc.h header > can be used in the device tree sources. > > Signed-off-by: Yixun Lan > --- > drivers/clk/meson/Kconfig | 10 ++ > drivers/clk/meson/Makefile | 1 + > drivers/clk/meson/mmc-clkc.c | 275 +++++++++++++++++++++++++++++++++++ > 3 files changed, 286 insertions(+) > create mode 100644 drivers/clk/meson/mmc-clkc.c > > diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig > index efaa70f682b4..8b8ccbcfed1d 100644 > --- a/drivers/clk/meson/Kconfig > +++ b/drivers/clk/meson/Kconfig > @@ -15,6 +15,16 @@ config COMMON_CLK_MESON_AO > select COMMON_CLK_REGMAP_MESON > select RESET_CONTROLLER > > +config COMMON_CLK_MMC_MESON > + tristate "Meson MMC Sub Clock Controller Driver" > + depends on COMMON_CLK_AMLOGIC > + select MFD_SYSCON > + select REGMAP > + help > + Support for the MMC sub clock controller on Amlogic Meson Platform, > + which include S905 (GXBB, GXL), A113D/X (AXG) devices. > + Say Y if you want this clock enabled. > + > config COMMON_CLK_REGMAP_MESON > bool > select REGMAP > diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile > index 39ce5661b654..31c16d524a4b 100644 > --- a/drivers/clk/meson/Makefile > +++ b/drivers/clk/meson/Makefile > @@ -9,4 +9,5 @@ obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o > obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o > obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o > obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o > +obj-$(CONFIG_COMMON_CLK_MMC_MESON) += mmc-clkc.o > obj-$(CONFIG_COMMON_CLK_REGMAP_MESON) += clk-regmap.o > diff --git a/drivers/clk/meson/mmc-clkc.c b/drivers/clk/meson/mmc-clkc.c > new file mode 100644 > index 000000000000..6aa055f7e62c > --- /dev/null > +++ b/drivers/clk/meson/mmc-clkc.c > @@ -0,0 +1,275 @@ > +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) > +/* > + * Amlogic Meson MMC Sub Clock Controller Driver > + * > + * Copyright (c) 2017 Baylibre SAS. > + * Author: Jerome Brunet > + * > + * Copyright (c) 2018 Amlogic, inc. > + * Author: Yixun Lan > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "clkc.h" > + > +/* clock ID used by internal driver */ > +#define CLKID_MMC_MUX 0 > + > +#define SD_EMMC_CLOCK 0 > +#define CLK_DIV_MASK GENMASK(5, 0) > +#define CLK_SRC_MASK GENMASK(7, 6) > +#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_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_TX_DELAY_MASK GENMASK(21, 16) > +#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 MUX_CLK_NUM_PARENTS 2 > +#define MMC_MAX_CLKS 5 > + > +struct mmc_clkc_data { > + struct meson_clk_phase_delay_data tx; > + struct meson_clk_phase_delay_data rx; > +}; > + > +static struct clk_regmap_mux_data mmc_clkc_mux_data = { > + .offset = SD_EMMC_CLOCK, > + .mask = 0x3, > + .shift = 6, > + .flags = CLK_DIVIDER_ROUND_CLOSEST, > +}; > + > +static struct clk_regmap_div_data mmc_clkc_div_data = { > + .offset = SD_EMMC_CLOCK, > + .shift = 0, > + .width = 6, > + .flags = CLK_DIVIDER_ROUND_CLOSEST | CLK_DIVIDER_ONE_BASED, > +}; > + > +static struct meson_clk_phase_delay_data mmc_clkc_core_phase_delay = { > + .phase_mask = CLK_CORE_PHASE_MASK, > +}; > + > +static const struct mmc_clkc_data mmc_clkc_gx_data = { > + { > + .phase_mask = CLK_TX_PHASE_MASK, > + .delay_mask = CLK_V2_TX_DELAY_MASK, > + .delay_step_ps = CLK_DELAY_STEP_PS, > + }, > + { > + .phase_mask = CLK_RX_PHASE_MASK, > + .delay_mask = CLK_V2_RX_DELAY_MASK, > + .delay_step_ps = CLK_DELAY_STEP_PS, > + }, > +}; > + > +static const struct mmc_clkc_data mmc_clkc_axg_data = { > + { > + .phase_mask = CLK_TX_PHASE_MASK, > + .delay_mask = CLK_V3_TX_DELAY_MASK, > + .delay_step_ps = CLK_DELAY_STEP_PS, > + }, > + { > + .phase_mask = CLK_RX_PHASE_MASK, > + .delay_mask = CLK_V3_RX_DELAY_MASK, > + .delay_step_ps = CLK_DELAY_STEP_PS, > + }, > +}; > + > +static const struct of_device_id mmc_clkc_match_table[] = { > + { > + .compatible = "amlogic,gx-mmc-clkc", > + .data = &mmc_clkc_gx_data > + }, > + { > + .compatible = "amlogic,axg-mmc-clkc", > + .data = &mmc_clkc_axg_data > + }, > + {} > +}; > +MODULE_DEVICE_TABLE(of, mmc_clkc_match_table); > + > +static struct clk_regmap * > +mmc_clkc_register_clk(struct device *dev, struct regmap *map, > + struct clk_init_data *init, > + const char *suffix, void *data) > +{ > + struct clk_regmap *clk; > + char *name; > + int ret; > + > + clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL); > + if (!clk) > + return ERR_PTR(-ENOMEM); > + > + name = kasprintf(GFP_KERNEL, "%s#%s", dev_name(dev), suffix); > + if (!name) > + return ERR_PTR(-ENOMEM); > + > + init->name = name; > + > + clk->map = map; > + clk->data = data; > + clk->hw.init = init; > + > + ret = devm_clk_hw_register(dev, &clk->hw); > + if (ret) > + clk = ERR_PTR(ret); > + > + kfree(name); > + return clk; > +} > + > +static struct clk_regmap *mmc_clkc_register_mux(struct device *dev, > + struct regmap *map) > +{ > + const char *parent_names[MUX_CLK_NUM_PARENTS]; > + struct clk_init_data init; > + struct clk_regmap *mux; > + struct clk *clk; > + int i; > + > + for (i = 0; i < MUX_CLK_NUM_PARENTS; i++) { > + char name[8]; > + > + snprintf(name, sizeof(name), "clkin%d", i); > + clk = devm_clk_get(dev, name); > + if (IS_ERR(clk)) { > + if (clk != ERR_PTR(-EPROBE_DEFER)) > + dev_err(dev, "Missing clock %s\n", name); > + return ERR_PTR((long)clk); > + } > + > + parent_names[i] = __clk_get_name(clk); > + } > + > + init.ops = &clk_regmap_mux_ops; > + init.flags = CLK_SET_RATE_PARENT; > + init.parent_names = parent_names; > + init.num_parents = MUX_CLK_NUM_PARENTS; > + > + mux = mmc_clkc_register_clk(dev, map, &init, "mux", &mmc_clkc_mux_data); > + if (IS_ERR(mux)) > + dev_err(dev, "Mux clock registration failed\n"); > + > + return mux; > +} > + > +static struct clk_regmap * > +mmc_clkc_register_other_clk(struct device *dev, struct regmap *map, This is a poor name. It does not help understand what the function does. mmc_clkc_register_clk_with_parent or whatever helps understand what's going on > + char *suffix, char *parent_suffix, You should not have to rebuild the parent name once again. Pass the clk_hw* of the parent and get the name from it. > + unsigned long flags, > + const struct clk_ops *ops, void *data) > +{ > + struct clk_init_data init; > + struct clk_regmap *clk; > + char *parent; > + > + parent = kasprintf(GFP_KERNEL, "%s#%s", dev_name(dev), parent_suffix); > + if (!parent) > + return ERR_PTR(-ENOMEM); > + > + init.ops = ops; > + init.flags = flags; > + init.parent_names = (const char* const []){ parent, }; > + init.num_parents = 1; > + > + clk = mmc_clkc_register_clk(dev, map, &init, suffix, data); > + if (IS_ERR(clk)) > + dev_err(dev, "Core %s clock registration failed\n", suffix); > + > + kfree(parent); > + return clk; > +} > + > +static int mmc_clkc_probe(struct platform_device *pdev) > +{ > + struct clk_hw_onecell_data *onecell_data; > + struct device *dev = &pdev->dev; > + struct mmc_clkc_data *data; > + struct regmap *map; > + struct clk_regmap *mux, *div, *core, *rx, *tx; Do you really need all this ? or could store them in your onecell_data->hws table as you go ? > + > + data = (struct mmc_clkc_data *)of_device_get_match_data(dev); > + if (!data) > + return -ENODEV; > + > + map = syscon_node_to_regmap(dev->of_node); > + if (IS_ERR(map)) { > + dev_err(dev, "could not find mmc clock controller\n"); > + return PTR_ERR(map); > + } > + > + onecell_data = devm_kzalloc(dev, sizeof(*onecell_data) + > + sizeof(*onecell_data->hws) * MMC_MAX_CLKS, > + GFP_KERNEL); > + if (!onecell_data) > + return -ENOMEM; > + > + mux = mmc_clkc_register_mux(dev, map); > + if (IS_ERR(mux)) > + return PTR_ERR(mux); > + > + div = mmc_clkc_register_other_clk(dev, map, "div", "mux", > + CLK_SET_RATE_PARENT, > + &clk_regmap_divider_ops, > + &mmc_clkc_div_data); > + if (IS_ERR(div)) > + return PTR_ERR(div); > + > + core = mmc_clkc_register_other_clk(dev, map, "core", "div", > + CLK_SET_RATE_PARENT, > + &meson_clk_phase_delay_ops, > + &mmc_clkc_core_phase_delay); > + if (IS_ERR(core)) > + return PTR_ERR(core); > + > + rx = mmc_clkc_register_other_clk(dev, map, "rx", "core", 0, > + &meson_clk_phase_delay_ops, > + &data->rx); > + if (IS_ERR(rx)) > + return PTR_ERR(rx); > + > + tx = mmc_clkc_register_other_clk(dev, map, "tx", "core", 0, > + &meson_clk_phase_delay_ops, > + &data->tx); > + if (IS_ERR(tx)) > + return PTR_ERR(tx); > + > + onecell_data->hws[CLKID_MMC_MUX] = &mux->hw, > + onecell_data->hws[CLKID_MMC_DIV] = &div->hw, > + onecell_data->hws[CLKID_MMC_PHASE_CORE] = &core->hw, > + onecell_data->hws[CLKID_MMC_PHASE_RX] = &rx->hw, > + onecell_data->hws[CLKID_MMC_PHASE_TX] = &tx->hw, > + onecell_data->num = MMC_MAX_CLKS; > + > + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, > + onecell_data); > +} > + > +static struct platform_driver mmc_clkc_driver = { > + .probe = mmc_clkc_probe, > + .driver = { > + .name = "meson-mmc-clkc", > + .of_match_table = of_match_ptr(mmc_clkc_match_table), > + }, > +}; > + > +module_platform_driver(mmc_clkc_driver);