From patchwork Tue Oct 31 19:54:22 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Manivannan Sadhasivam X-Patchwork-Id: 117662 Delivered-To: patch@linaro.org Received: by 10.140.22.164 with SMTP id 33csp12151qgn; Tue, 31 Oct 2017 12:55:11 -0700 (PDT) X-Google-Smtp-Source: ABhQp+Tl3Ixfuv/5O+DHb778oK1wBGjVFU3ZYLrywyx6mG03d4wPf2NN4dzJp0UOcL6YNSpwiM1A X-Received: by 10.98.36.135 with SMTP id k7mr3222246pfk.63.1509479711218; Tue, 31 Oct 2017 12:55:11 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1509479711; cv=none; d=google.com; s=arc-20160816; b=PnFLvLt/xUvmW+R5uFo/WSsCHDtoPXAIU8KdyqAwitpE2UEzLrn2C//vxFsvRhCyru Vju1rR3JQEb20Q2TynUVZKuqQTEtbKg58BnPn+fTe7hZn3w+B6fs/tASqjwRVL/jgE1Q 1Qj5se+vve1i21ZrSeuGP8tNKY2o2oKV8SYJxswlhJsHgcWAVoq+ysPko12OIEDCLEIt CUlGIW2rpohZbIaAuLAOmc9sZLqndbg4y3ybJPvZX6hod52d44HS5C4G8APaGOwaPPlY CJtNGSG3pUFVvfqLtp2mBEpLMI4cT5oJ4tB6cCM4VFn56V59Hl9TbuU+VcTGY98M6Avd CAdA== 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:message-id:date:subject:cc:to:from :dkim-signature:arc-authentication-results; bh=gOO+YLKv5J3Hyjccfw3bLuSXVjvclfFd5vqh4h0cSKk=; b=hfBqLdjK/KIpdVoteXOUSN9hi02zbRMWpXWgaGAIC1OjjhQBf1P/nvUen3pUp7MkqR MAjLIyViKfZKD1Eb33p5rSSL2l5mQwt8O50SiKJuwhOUJlfwPhby8Ol2GN9vt8aH6FwL XCY5ryzD8NF46CNMQmwspk38cIetFnxojMX27/pUl0kcXUZXVDhZyUSZUQUZbF5Gki+e qxvR30lNctqwV5iub+JS/rSld6zRrqPGjvMsZ7nVU6DIC5i2FG7Qkp718lNY/Vk7+58W Nzox9QImKuHnKbw+FpK2v8+8tbAHHVgayCbBklKvWVkKt379lpmwJZmpmjJXBXe1oCxS yaJQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=UM3z+XfQ; spf=pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=devicetree-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 205si2245728pgd.240.2017.10.31.12.55.10; Tue, 31 Oct 2017 12:55:11 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=UM3z+XfQ; spf=pass (google.com: best guess record for domain of devicetree-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=devicetree-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932871AbdJaTzI (ORCPT + 6 others); Tue, 31 Oct 2017 15:55:08 -0400 Received: from mail-pg0-f66.google.com ([74.125.83.66]:45047 "EHLO mail-pg0-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932501AbdJaTzE (ORCPT ); Tue, 31 Oct 2017 15:55:04 -0400 Received: by mail-pg0-f66.google.com with SMTP id j3so132055pga.1 for ; Tue, 31 Oct 2017 12:55:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=aGEb/PWIHUCp2uaY7mJX+BWerEVkreaAFZcCFw0WN8w=; b=UM3z+XfQM0nodr+srBlYnFiwgRjKz6fOiQnMk0gJ8AriyR20uFFScsR+hm7oSEfrUd pRlsKAbQwpXgbIuOgk9wbYi0kQ+7/p2z/PjjL7Sd/fqiG1UBYYpD7uNYOSqV/CVoINb6 QfLTGa9iP4aJkhN/6SRctp8APMi1KRGaJ7Mi4= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=aGEb/PWIHUCp2uaY7mJX+BWerEVkreaAFZcCFw0WN8w=; b=m/BrL9KIor8WcAZjz7jtzIKLq97I/A52ZybcSeggEjl8rbmCCXtkqVX8WZq+i90b7L Ukd9cgbZSzEyqWSguVPPwyFfQHV5IN9egDPfYfIZ+A9EYE846uDk2Hc9w34GiW9Pxjtt 1mq1PGRgi+aTdDuwtvUcMY7YVpR2GhT1YT0SId+NZNTGs7RgUyG1FVwv7NlxTotEvH3x Wmc3i2bTK4wASYxW+7ZzFdT4up3KQI2qVE/4wwtMmtPzUNr/CJy/SCL6FQPArbCbF4Zu B5t0UJ2UQ1FLCUMey6yWo7IdCxbjqO/IMlyE834htBzxlUoYyLiCDkuqSeC3dGc17C9f bvBQ== X-Gm-Message-State: AMCzsaWgfHmbqYEGIHt8VFd7tIXX53BkEl15gdj4YE1PJH4yYpUTNzHH SbimA5IJBEQxphGT9WREcxo3 X-Received: by 10.98.217.138 with SMTP id b10mr3332662pfl.39.1509479702677; Tue, 31 Oct 2017 12:55:02 -0700 (PDT) Received: from localhost.localdomain ([2405:204:7307:c0df:a955:b01b:cf95:dedf]) by smtp.gmail.com with ESMTPSA id r18sm4254289pfe.99.2017.10.31.12.54.55 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 31 Oct 2017 12:55:02 -0700 (PDT) From: Manivannan Sadhasivam To: mturquette@baylibre.com, sboyd@codeaurora.org, afaerber@suse.de, robh+dt@kernel.org, mark.rutland@arm.com Cc: linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, davem@davemloft.net, mchehab@kernel.org, rdunlap@infradead.org, daniel.thompson@linaro.org, amit.kucheria@linaro.org, liuwei@actions-semi.com, Manivannan Sadhasivam Subject: [PATCH 2/3] clk: owl: add clock driver for Actions S900 SoC Date: Wed, 1 Nov 2017 01:24:22 +0530 Message-Id: <1509479663-8985-3-git-send-email-manivannan.sadhasivam@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1509479663-8985-1-git-send-email-manivannan.sadhasivam@linaro.org> References: <1509479663-8985-1-git-send-email-manivannan.sadhasivam@linaro.org> MIME-Version: 1.0 Sender: devicetree-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: devicetree@vger.kernel.org This patch adds clock driver for Actions Semi OWL series S900 SoC. Signed-off-by: Manivannan Sadhasivam --- MAINTAINERS | 5 + drivers/clk/Makefile | 1 + drivers/clk/owl/Makefile | 2 + drivers/clk/owl/clk-factor.c | 270 ++++++++++++ drivers/clk/owl/clk-pll.c | 346 +++++++++++++++ drivers/clk/owl/clk-s900.c | 587 +++++++++++++++++++++++++ drivers/clk/owl/clk.c | 318 ++++++++++++++ drivers/clk/owl/clk.h | 301 +++++++++++++ include/dt-bindings/clock/actions,s900-clock.h | 141 ++++++ 9 files changed, 1971 insertions(+) create mode 100644 drivers/clk/owl/Makefile create mode 100644 drivers/clk/owl/clk-factor.c create mode 100644 drivers/clk/owl/clk-pll.c create mode 100644 drivers/clk/owl/clk-s900.c create mode 100644 drivers/clk/owl/clk.c create mode 100644 drivers/clk/owl/clk.h create mode 100644 include/dt-bindings/clock/actions,s900-clock.h -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/MAINTAINERS b/MAINTAINERS index 2d3d750..beae8aa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1098,6 +1098,11 @@ F: arch/arm/mach-*/ F: arch/arm/plat-*/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc.git +ARM/ACTIONS SEMI SoC CLOCK SUPPORT +L: Manivannan Sadhasivam +S: Maintained +F: drivers/clk/owl/ + ARM/ACTIONS SEMI ARCHITECTURE M: Andreas Färber L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index c99f363..821c1e1 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -75,6 +75,7 @@ endif obj-y += mvebu/ obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_COMMON_CLK_NXP) += nxp/ +obj-$(CONFIG_ARCH_ACTIONS) += owl/ obj-$(CONFIG_MACH_PISTACHIO) += pistachio/ obj-$(CONFIG_COMMON_CLK_PXA) += pxa/ obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/ diff --git a/drivers/clk/owl/Makefile b/drivers/clk/owl/Makefile new file mode 100644 index 0000000..dbba0af --- /dev/null +++ b/drivers/clk/owl/Makefile @@ -0,0 +1,2 @@ +obj-y += clk.o clk-pll.o clk-factor.o +obj-$(CONFIG_ARCH_ACTIONS) += clk-s900.o diff --git a/drivers/clk/owl/clk-factor.c b/drivers/clk/owl/clk-factor.c new file mode 100644 index 0000000..6429acd --- /dev/null +++ b/drivers/clk/owl/clk-factor.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2014 Actions Semi Inc. + * David Liu + * + * Copyright (c) 2017 Linaro Ltd. + * Author: Manivannan Sadhasivam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "clk.h" + +#define to_owl_factor(_hw) container_of(_hw, struct owl_factor, hw) +#define div_mask(d) ((1 << ((d)->width)) - 1) + +static unsigned int _get_table_maxval(const struct clk_factor_table *table) +{ + unsigned int maxval = 0; + const struct clk_factor_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->val > maxval) + maxval = clkt->val; + return maxval; +} + +static int _get_table_div_mul(const struct clk_factor_table *table, + unsigned int val, unsigned int *mul, unsigned int *div) +{ + const struct clk_factor_table *clkt; + + for (clkt = table; clkt->div; clkt++) { + if (clkt->val == val) { + *mul = clkt->mul; + *div = clkt->div; + return 1; + } + } + return 0; +} + +static unsigned int _get_table_val(const struct clk_factor_table *table, + unsigned long rate, unsigned long parent_rate) +{ + const struct clk_factor_table *clkt; + int val = -1; + u64 calc_rate; + + for (clkt = table; clkt->div; clkt++) { + calc_rate = parent_rate * clkt->mul; + do_div(calc_rate, clkt->div); + + if ((unsigned long)calc_rate <= rate) { + val = clkt->val; + break; + } + } + + if (val == -1) + val = _get_table_maxval(table); + + return val; +} + +static int clk_val_best(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate) +{ + struct owl_factor *factor = to_owl_factor(hw); + const struct clk_factor_table *clkt = factor->table; + unsigned long parent_rate, try_parent_rate, best = 0, now; + unsigned long parent_rate_saved = *best_parent_rate; + int bestval = 0; + + if (!rate) + rate = 1; + + if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) { + parent_rate = *best_parent_rate; + bestval = _get_table_val(clkt, rate, parent_rate); + return bestval; + } + + for (clkt = factor->table; clkt->div; clkt++) { + try_parent_rate = rate * clkt->div / clkt->mul; + + if (try_parent_rate == parent_rate_saved) { + pr_debug("%s: [%d %d %d] found try_parent_rate %ld\n", + __func__, clkt->val, clkt->mul, clkt->div, + try_parent_rate); + /* + * It's the most ideal case if the requested rate can be + * divided from parent clock without any need to change + * parent rate, so return the divider immediately. + */ + *best_parent_rate = parent_rate_saved; + return clkt->val; + } + + parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), + try_parent_rate); + now = DIV_ROUND_UP(parent_rate, clkt->div) * clkt->mul; + if (now <= rate && now > best) { + bestval = clkt->val; + best = now; + *best_parent_rate = parent_rate; + } + } + + if (!bestval) { + bestval = _get_table_maxval(clkt); + *best_parent_rate = clk_hw_round_rate( + clk_hw_get_parent(hw), 1); + } + + pr_debug("%s: return bestval %d\n", __func__, bestval); + + return bestval; +} + +/** + * owl_factor_round_rate() - round a clock frequency + * @hw: handle between common and hardware-specific interfaces + * @rate: desired clock frequency + * @prate: clock frequency of parent clock + */ +static long owl_factor_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct owl_factor *factor = to_owl_factor(hw); + const struct clk_factor_table *clkt = factor->table; + unsigned int val, mul = 0, div = 1; + + val = clk_val_best(hw, rate, parent_rate); + _get_table_div_mul(clkt, val, &mul, &div); + + return *parent_rate * mul / div; +} + +/** + * owl_factor_recalc_rate() - recalculate clock frequency + * @hw: handle between common and hardware-specific interfaces + * @parent_rate: clock frequency of parent clock + */ +static unsigned long owl_factor_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct owl_factor *factor = to_owl_factor(hw); + const struct clk_factor_table *clkt = factor->table; + u64 rate; + u32 val, mul, div; + + div = 0; + mul = 0; + + val = readl(factor->reg) >> factor->shift; + val &= div_mask(factor); + + _get_table_div_mul(clkt, val, &mul, &div); + if (!div) { + WARN(!(factor->flags & CLK_DIVIDER_ALLOW_ZERO), + "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n", + __clk_get_name(hw->clk)); + return parent_rate; + } + + rate = (u64)parent_rate * mul; + do_div(rate, div); + + return rate; +} + +/** + * owl_factor_set_rate() - set clock frequency + * @hw: handle between common and hardware-specific interfaces + * @parent_rate: clock frequency of parent clock + */ +static int owl_factor_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct owl_factor *factor = to_owl_factor(hw); + unsigned long flags = 0; + u32 val, v; + + val = _get_table_val(factor->table, rate, parent_rate); + + pr_debug("%s: get_table_val %d\n", __func__, val); + + if (val > div_mask(factor)) + val = div_mask(factor); + + if (factor->lock) + spin_lock_irqsave(factor->lock, flags); + + v = readl(factor->reg); + v &= ~(div_mask(factor) << factor->shift); + v |= val << factor->shift; + writel(v, factor->reg); + + if (factor->lock) + spin_unlock_irqrestore(factor->lock, flags); + + return 0; +} + +const struct clk_ops owl_factor_ops = { + .round_rate = owl_factor_round_rate, + .recalc_rate = owl_factor_recalc_rate, + .set_rate = owl_factor_set_rate, +}; + +/** + * owl_factor_clk_register() - register pll with the clock framework + * @name: pll name + * @parent: parent clock name + * @reg: pointer to pll control register + * @pll_status: pointer to pll status register + * @lock_index: bit index to this pll's lock status bit in pll_status + * @lock: register lock + */ +struct clk_hw *owl_factor_clk_register(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_factor_flags, const struct clk_factor_table *table, + spinlock_t *lock) + +{ + struct owl_factor *factor; + struct clk_init_data initd; + struct clk_hw *clk_hw; + int ret; + + factor = kzalloc(sizeof(*factor), GFP_KERNEL); + if (!factor) + return ERR_PTR(-ENOMEM); + + initd.name = name; + initd.ops = &owl_factor_ops; + initd.flags = flags; + initd.parent_names = (parent_name ? &parent_name : NULL); + initd.num_parents = (parent_name ? 1 : 0); + + factor->reg = reg; + factor->shift = shift; + factor->width = width; + factor->flags = clk_factor_flags; + factor->lock = lock; + factor->hw.init = &initd; + factor->table = table; + + clk_hw = &factor->hw; + ret = clk_hw_register(dev, clk_hw); + if (ret) { + kfree(factor); + clk_hw = ERR_PTR(ret); + } + + return clk_hw; +} diff --git a/drivers/clk/owl/clk-pll.c b/drivers/clk/owl/clk-pll.c new file mode 100644 index 0000000..ef98e90 --- /dev/null +++ b/drivers/clk/owl/clk-pll.c @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2014 Actions Semi Inc. + * David Liu + * + * Copyright (c) 2017 Linaro Ltd. + * Author: Manivannan Sadhasivam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include "clk.h" + +/** + * struct owl_pll + * @hw: handle between common and hardware-specific interfaces + * @reg: pll control register + * @lock: register lock + * @bfreq: base frequency of the pll. pll frequency = bfreq * mul + * @enable_bit: enable bit for pll + * @shift: shift to the multiplier bit field + * @width: width of the multiplier bit field + * @min_mul: minimum multiple for the pll + * @max_mul: maximum multiple for the pll + * @pll_flags: flags for the pll + * @table: pll table + */ +struct owl_pll { + struct clk_hw hw; + void __iomem *reg; + spinlock_t *lock; + unsigned long bfreq; + u8 enable_bit; + u8 shift; + u8 width; + u8 min_mul; + u8 max_mul; + u8 pll_flags; + const struct clk_pll_table *table; +}; + +#define to_owl_pll(_hw) container_of(_hw, struct owl_pll, hw) +#define mul_mask(m) ((1 << ((m)->width)) - 1) +#define PLL_STABILITY_WAIT_US (50) + +/** + * owl_pll_calculate_mul() - calculate multiple for specific rate + * @pll: owl pll + * @rate: desired clock frequency + */ +static u32 owl_pll_calculate_mul(struct owl_pll *pll, unsigned long rate) +{ + u32 mul; + + mul = DIV_ROUND_CLOSEST(rate, pll->bfreq); + if (mul < pll->min_mul) + mul = pll->min_mul; + else if (mul > pll->max_mul) + mul = pll->max_mul; + + return mul &= mul_mask(pll); +} + +static unsigned int _get_table_rate(const struct clk_pll_table *table, + unsigned int val) +{ + const struct clk_pll_table *clkt; + + for (clkt = table; clkt->rate; clkt++) + if (clkt->val == val) + return clkt->rate; + + return 0; +} + +static const struct clk_pll_table *_get_pll_table( + const struct clk_pll_table *table, unsigned long rate) +{ + const struct clk_pll_table *clkt; + + for (clkt = table; clkt->rate; clkt++) { + if (clkt->rate == rate) { + table = clkt; + break; + } else if (clkt->rate < rate) + table = clkt; + } + + return table; +} + +/** + * owl_pll_round_rate() - round a clock frequency + * @hw: handle between common and hardware-specific interfaces + * @rate: desired clock frequency + * @parent_rate: clock frequency of parent clock + */ +static long owl_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct owl_pll *pll = to_owl_pll(hw); + const struct clk_pll_table *clkt; + u32 mul; + + if (pll->table) { + clkt = _get_pll_table(pll->table, rate); + return clkt->rate; + } + + /* fixed frequency */ + if (pll->width == 0) + return pll->bfreq; + + mul = owl_pll_calculate_mul(pll, rate); + + return pll->bfreq * mul; +} + +/** + * owl_pll_recalc_rate() - recalculate pll clock frequency + * @hw: handle between common and hardware-specific interfaces + * @parent_rate: clock frequency of parent clock + */ +static unsigned long owl_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct owl_pll *pll = to_owl_pll(hw); + unsigned long rate; + u32 val, mul; + + if (pll->table) { + val = readl(pll->reg) >> pll->shift; + val &= mul_mask(pll); + + rate = _get_table_rate(pll->table, val); + + return rate; + } + + /* fixed frequency */ + if (pll->width == 0) + return pll->bfreq; + + mul = (readl(pll->reg) >> pll->shift) & mul_mask(pll); + + return pll->bfreq * mul; +} + +/** + * owl_pll_is_enabled - check if pll is enabled + * @hw: handle between common and hardware-specific interfaces + * + * Not sure this is a good idea, but since disabled means bypassed for + * this clock implementation we say we are always enabled. + */ +static int owl_pll_is_enabled(struct clk_hw *hw) +{ + struct owl_pll *pll = to_owl_pll(hw); + unsigned long flags = 0; + u32 v; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + v = readl(pll->reg); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + return !!(v & BIT(pll->enable_bit)); +} + +/** + * owl_pll_enable - enable pll clock + * @hw: handle between common and hardware-specific interfaces + */ +static int owl_pll_enable(struct clk_hw *hw) +{ + struct owl_pll *pll = to_owl_pll(hw); + unsigned long flags = 0; + u32 v; + + /* exit if pll is enabled */ + if (owl_pll_is_enabled(hw)) + return 0; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + v = readl(pll->reg); + v |= BIT(pll->enable_bit); + writel(v, pll->reg); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + udelay(PLL_STABILITY_WAIT_US); + + return 0; +} + +/** + * owl_pll_disable - disable pll clock + * @hw: handle between common and hardware-specific interfaces + */ +static void owl_pll_disable(struct clk_hw *hw) +{ + struct owl_pll *pll = to_owl_pll(hw); + unsigned long flags = 0; + u32 v; + + /* exit if pll is disabled */ + if (!owl_pll_is_enabled(hw)) + return; + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + v = readl(pll->reg); + v &= ~BIT(pll->enable_bit); + writel(v, pll->reg); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); +} + +/** + * owl_pll_set_rate - set pll rate + * @hw: handle between common and hardware-specific interfaces + * @rate: desired clock frequency + * @parent_rate: clock frequency of parent + */ +static int owl_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct owl_pll *pll = to_owl_pll(hw); + const struct clk_pll_table *clkt; + unsigned long flags = 0; + u32 val, v; + + pr_debug("%s: rate %ld, parent_rate %ld, before set rate: reg 0x%x\n", + __func__, rate, parent_rate, readl(pll->reg)); + + /* fixed frequency */ + if (pll->width == 0) + return 0; + + if (pll->table) { + clkt = _get_pll_table(pll->table, rate); + val = clkt->val; + } else { + val = owl_pll_calculate_mul(pll, rate); + } + + if (pll->lock) + spin_lock_irqsave(pll->lock, flags); + + v = readl(pll->reg); + v &= ~mul_mask(pll); + v |= val << pll->shift; + writel(v, pll->reg); + + udelay(PLL_STABILITY_WAIT_US); + + if (pll->lock) + spin_unlock_irqrestore(pll->lock, flags); + + pr_debug("%s: after set rate: reg 0x%x\n", __func__, + readl(pll->reg)); + + return 0; +} + +static const struct clk_ops owl_pll_ops = { + .enable = owl_pll_enable, + .disable = owl_pll_disable, + .is_enabled = owl_pll_is_enabled, + .round_rate = owl_pll_round_rate, + .recalc_rate = owl_pll_recalc_rate, + .set_rate = owl_pll_set_rate, +}; + +/** + * owl_clk_register_pll() - register pll with the clock framework + * @name: pll name + * @parent: parent clock name + * @reg: pointer to pll control register + * @pll_status: pointer to pll status register + * @lock_index: bit index to this pll's lock status bit in @pll_status + * @lock: register lock + */ +struct clk_hw *owl_pll_clk_register(const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, unsigned long bfreq, + u8 enable_bit, u8 shift, u8 width, u8 min_mul, u8 max_mul, + u8 pll_flags, const struct clk_pll_table *table, + spinlock_t *lock) +{ + struct owl_pll *pll; + struct clk_hw *clk_hw; + struct clk_init_data initd; + int ret; + + pll = kmalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + initd.name = name; + initd.parent_names = (parent_name ? &parent_name : NULL); + initd.num_parents = (parent_name ? 1 : 0); + initd.ops = &owl_pll_ops; + initd.flags = flags; + + pll->hw.init = &initd; + pll->bfreq = bfreq; + pll->enable_bit = enable_bit; + pll->shift = shift; + pll->width = width; + pll->min_mul = min_mul; + pll->max_mul = max_mul; + pll->pll_flags = pll_flags; + pll->table = table; + pll->reg = reg; + pll->lock = lock; + + clk_hw = &pll->hw; + ret = clk_hw_register(NULL, clk_hw); + if (ret) { + kfree(pll); + clk_hw = ERR_PTR(ret); + } + + return clk_hw; +} diff --git a/drivers/clk/owl/clk-s900.c b/drivers/clk/owl/clk-s900.c new file mode 100644 index 0000000..f8d3e33 --- /dev/null +++ b/drivers/clk/owl/clk-s900.c @@ -0,0 +1,587 @@ +/* + * Copyright (c) 2014 Actions Semi Inc. + * David Liu + * + * Copyright (c) 2017 Linaro Ltd. + * Author: Manivannan Sadhasivam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include +#include "clk.h" + +#define CMU_COREPLL (0x0000) +#define CMU_DEVPLL (0x0004) +#define CMU_DDRPLL (0x0008) +#define CMU_NANDPLL (0x000C) +#define CMU_DISPLAYPLL (0x0010) +#define CMU_AUDIOPLL (0x0014) +#define CMU_TVOUTPLL (0x0018) +#define CMU_BUSCLK (0x001C) +#define CMU_SENSORCLK (0x0020) +#define CMU_LCDCLK (0x0024) +#define CMU_DSICLK (0x0028) +#define CMU_CSICLK (0x002C) +#define CMU_DECLK (0x0030) +#define CMU_BISPCLK (0x0034) +#define CMU_IMXCLK (0x0038) +#define CMU_HDECLK (0x003C) +#define CMU_VDECLK (0x0040) +#define CMU_VCECLK (0x0044) +#define CMU_NANDCCLK (0x004C) +#define CMU_SD0CLK (0x0050) +#define CMU_SD1CLK (0x0054) +#define CMU_SD2CLK (0x0058) +#define CMU_UART0CLK (0x005C) +#define CMU_UART1CLK (0x0060) +#define CMU_UART2CLK (0x0064) +#define CMU_PWM0CLK (0x0070) +#define CMU_PWM1CLK (0x0074) +#define CMU_PWM2CLK (0x0078) +#define CMU_PWM3CLK (0x007C) +#define CMU_USBPLL (0x0080) +#define CMU_ASSISTPLL (0x0084) +#define CMU_EDPCLK (0x0088) +#define CMU_GPU3DCLK (0x0090) +#define CMU_CORECTL (0x009C) +#define CMU_DEVCLKEN0 (0x00A0) +#define CMU_DEVCLKEN1 (0x00A4) +#define CMU_DEVRST0 (0x00A8) +#define CMU_DEVRST1 (0x00AC) +#define CMU_UART3CLK (0x00B0) +#define CMU_UART4CLK (0x00B4) +#define CMU_UART5CLK (0x00B8) +#define CMU_UART6CLK (0x00BC) +#define CMU_TLSCLK (0x00C0) +#define CMU_SD3CLK (0x00C4) +#define CMU_PWM4CLK (0x00C8) +#define CMU_PWM5CLK (0x00CC) + +static struct clk_pll_table clk_audio_pll_table[] = { + {0, 45158400}, {1, 49152000}, + {0, 0}, +}; + +static struct clk_pll_table clk_edp_pll_table[] = { + {0, 810000000}, {1, 1350000000}, {2, 2700000000}, + {0, 0}, +}; + +/* pll clocks */ +static struct owl_pll_clock s900_pll_clks[] = { + { CLK_CORE_PLL, "core_pll", NULL, 0, CMU_COREPLL, 24000000, 9, 0, 8, 5, 107, 0, NULL, }, + { CLK_DEV_PLL, "dev_pll", NULL, 0, CMU_DEVPLL, 6000000, 8, 0, 8, 20, 180, 0, NULL, }, + { CLK_DDR_PLL, "ddr_pll", NULL, 0, CMU_DDRPLL, 24000000, 8, 0, 8, 5, 45, 0, NULL, }, + { CLK_NAND_PLL, "nand_pll", NULL, 0, CMU_NANDPLL, 6000000, 8, 0, 8, 4, 100, 0, NULL, }, + { CLK_DISPLAY_PLL, "display_pll", NULL, 0, CMU_DISPLAYPLL, 6000000, 8, 0, 8, 20, 180, 0, NULL, }, + { CLK_ASSIST_PLL, "assist_pll", NULL, 0, CMU_ASSISTPLL, 500000000, 0, 0, 0, 0, 0, CLK_OWL_PLL_FIXED_FREQ, NULL, }, + { CLK_AUDIO_PLL, "audio_pll", NULL, 0, CMU_AUDIOPLL, 0, 4, 0, 1, 0, 0, 0, clk_audio_pll_table, }, + + { CLK_EDP_PLL, "edp_pll", "24M_edp", 0, CMU_EDPCLK, 0, 9, 0, 2, 0, 0, 0, clk_edp_pll_table, }, +}; + +static const char *cpu_clk_mux_p[] = { "losc", "hosc", "core_pll", }; +static const char *dev_clk_p[] = { "hosc", "dev_pll", }; +static const char *noc_clk_mux_p[] = { "dev_clk", "assist_pll", }; +static const char *dmm_clk_mux_p[] = { "dev_clk", "nand_pll", "assist_pll", "ddr_clk_src", }; + +static const char *bisp_clk_mux_p[] = { "assist_pll", "dev_clk", }; +static const char *csi_clk_mux_p[] = { "display_pll", "dev_clk", }; +static const char *de_clk_mux_p[] = { "assist_pll", "dev_clk", }; +static const char *eth_mac_clk_mux_p[] = { "assist_pll", }; +static const char *gpu_clk_mux_p[] = { "dev_clk", "display_pll", "", "ddr_clk_src", }; +static const char *hde_clk_mux_p[] = { "dev_clk", "display_pll", "", "ddr_clk_src", }; +static const char *i2c_clk_mux_p[] = { "assist_pll", }; +static const char *imx_clk_mux_p[] = { "assist_pll", "dev_clk", }; +static const char *lcd_clk_mux_p[] = { "display_pll", "nand_pll", }; +static const char *nand_clk_mux_p[] = { "dev_clk", "nand_pll", }; +static const char *pwm_clk_mux_p[] = { "hosc" }; +static const char *sd_clk_mux_p[] = { "dev_clk", "nand_pll", }; +static const char *sensor_clk_mux_p[] = { "hosc", "bisp", }; +static const char *speed_sensor_clk_mux_p[] = { "hosc", }; +static const char *spi_clk_mux_p[] = { "ahb_clk", }; +static const char *thermal_sensor_clk_mux_p[] = { "hosc", }; +static const char *uart_clk_mux_p[] = { "hosc", "dev_pll", }; +static const char *vce_clk_mux_p[] = { "dev_clk", "display_pll", "assist_pll", "ddr_clk_src", }; +static const char *i2s_clk_mux_p[] = { "audio_pll", }; + +static const char *edp_clk_mux_p[] = { "assist_pll", "display_pll", }; + +/* mux clocks */ +static struct owl_mux_clock s900_mux_clks[] = { + { CLK_CPU, "cpu_clk", cpu_clk_mux_p, ARRAY_SIZE(cpu_clk_mux_p), CLK_SET_RATE_PARENT, CMU_BUSCLK, 0, 2, 0, "cpu_clk", }, + { CLK_DEV, "dev_clk", dev_clk_p, ARRAY_SIZE(dev_clk_p), CLK_SET_RATE_PARENT, CMU_DEVPLL, 12, 1, 0, "dev_clk", }, + { CLK_NOC_CLK_MUX, "noc_clk_mux", noc_clk_mux_p, ARRAY_SIZE(noc_clk_mux_p), CLK_SET_RATE_PARENT, CMU_BUSCLK, 7, 1, 0, }, +}; + +static struct clk_div_table nand_div_table[] = { + {0, 1}, {1, 2}, {2, 4}, {3, 6}, + {4, 8}, {5, 10}, {6, 12}, {7, 14}, + {8, 16}, {9, 18}, {10, 20}, {11, 22}, + {12, 24}, {13, 26}, {14, 28}, {15, 30}, + {0, 0}, +}; + +static struct clk_factor_table sd_factor_table[] = { + /* bit0 ~ 4 */ + {0, 1, 1}, {1, 1, 2}, {2, 1, 3}, {3, 1, 4}, + {4, 1, 5}, {5, 1, 6}, {6, 1, 7}, {7, 1, 8}, + {8, 1, 9}, {9, 1, 10}, {10, 1, 11}, {11, 1, 12}, + {12, 1, 13}, {13, 1, 14}, {14, 1, 15}, {15, 1, 16}, + {16, 1, 17}, {17, 1, 18}, {18, 1, 19}, {19, 1, 20}, + {20, 1, 21}, {21, 1, 22}, {22, 1, 23}, {23, 1, 24}, + {24, 1, 25}, {25, 1, 26}, {26, 1, 27}, {27, 1, 28}, + {28, 1, 29}, {29, 1, 30}, {30, 1, 31}, {31, 1, 32}, + + /* bit8: /128 */ + {256, 1, 1 * 128}, {257, 1, 2 * 128}, {258, 1, 3 * 128}, {259, 1, 4 * 128}, + {260, 1, 5 * 128}, {261, 1, 6 * 128}, {262, 1, 7 * 128}, {263, 1, 8 * 128}, + {264, 1, 9 * 128}, {265, 1, 10 * 128}, {266, 1, 11 * 128}, {267, 1, 12 * 128}, + {268, 1, 13 * 128}, {269, 1, 14 * 128}, {270, 1, 15 * 128}, {271, 1, 16 * 128}, + {272, 1, 17 * 128}, {273, 1, 18 * 128}, {274, 1, 19 * 128}, {275, 1, 20 * 128}, + {276, 1, 21 * 128}, {277, 1, 22 * 128}, {278, 1, 23 * 128}, {279, 1, 24 * 128}, + {280, 1, 25 * 128}, {281, 1, 26 * 128}, {282, 1, 27 * 128}, {283, 1, 28 * 128}, + {284, 1, 29 * 128}, {285, 1, 30 * 128}, {286, 1, 31 * 128}, {287, 1, 32 * 128}, + + {0, 0}, +}; + +static struct clk_div_table apb_div_table[] = { + {1, 2}, {2, 3}, {3, 4}, + {0, 0}, +}; + +static struct clk_div_table eth_mac_div_table[] = { + {0, 2}, {1, 4}, + {0, 0}, +}; + +static struct clk_div_table rmii_ref_div_table[] = { + {0, 4}, {1, 10}, + {0, 0}, +}; + +static struct clk_div_table usb3_mac_div_table[] = { + {1, 2}, {2, 3}, {3, 4}, + {0, 8}, +}; + +static struct clk_div_table i2s_div_table[] = { + {0, 1}, {1, 2}, {2, 3}, {3, 4}, + {4, 6}, {5, 8}, {6, 12}, {7, 16}, + {8, 24}, + {0, 0}, +}; + +static struct clk_div_table hdmia_div_table[] = { + {0, 1}, {1, 2}, {2, 3}, {3, 4}, + {4, 6}, {5, 8}, {6, 12}, {7, 16}, + {8, 24}, + {0, 0}, +}; + + +/* divider clocks */ +static struct owl_divider_clock s900_div_clks[] = { + { CLK_NOC_CLK_DIV, "noc_clk_div", "noc_clk", 0, CMU_BUSCLK, 19, 1, 0, NULL, }, + { CLK_AHB, "ahb_clk", "noc_clk_div", 0, CMU_BUSCLK, 4, 1, 0, NULL, "ahb_clk", }, + { CLK_APB, "apb_clk", "ahb_clk", 0, CMU_BUSCLK, 8, 2, 0, apb_div_table, "apb_clk", }, + { CLK_USB3_MAC, "usb3_mac", "assist_pll", 0, CMU_ASSISTPLL, 12, 2, 0, usb3_mac_div_table, "usb3_mac", }, + { CLK_RMII_REF, "rmii_ref", "assist_pll", 0, CMU_ASSISTPLL, 8, 1, 0, rmii_ref_div_table, "rmii_ref", }, +}; + +static struct clk_factor_table dmm_factor_table[] = { + {0, 1, 1}, {1, 2, 3}, {2, 1, 2}, {3, 1, 3}, + {4, 1, 4}, + {0, 0, 0}, +}; + +static struct clk_factor_table noc_factor_table[] = { + {0, 1, 1}, {1, 2, 3}, {2, 1, 2}, {3, 1, 3}, {4, 1, 4}, + {0, 0, 0}, +}; + +static struct clk_factor_table bisp_factor_table[] = { + {0, 1, 1}, {1, 2, 3}, {2, 1, 2}, {3, 2, 5}, + {4, 1, 3}, {5, 1, 4}, {6, 1, 6}, {7, 1, 8}, + {0, 0, 0}, +}; + +/* divider clocks */ +static struct owl_factor_clock s900_factor_clks[] = { + { CLK_NOC, "noc_clk", "noc_clk_mux", 0, CMU_BUSCLK, 16, 3, 0, noc_factor_table, "noc_clk", }, + { CLK_DE1, "de_clk1", "de_clk", 0, CMU_DECLK, 0, 3, 0, bisp_factor_table, "de_clk1", }, + { CLK_DE2, "de_clk2", "de_clk", 0, CMU_DECLK, 4, 3, 0, bisp_factor_table, "de_clk2", }, + { CLK_DE3, "de_clk3", "de_clk", 0, CMU_DECLK, 8, 3, 0, bisp_factor_table, "de_clk3", }, +}; + +/* gate clocks */ +static struct owl_gate_clock s900_gate_clks[] = { + { CLK_GPIO, "gpio", "apb_clk", 0, CMU_DEVCLKEN0, 18, 0, "gpio", }, + { CLK_GPU, "gpu", NULL, 0, CMU_DEVCLKEN0, 30, 0, "gpu", }, + { CLK_DMAC, "dmac", "noc_clk_div", 0, CMU_DEVCLKEN0, 1, 0, "dmac", }, + { CLK_TIMER, "timer", "hosc", 0, CMU_DEVCLKEN1, 27, 0, "timer", }, + { CLK_DSI, "dsi_clk", NULL, 0, CMU_DEVCLKEN0, 12, 0, "dsi", }, + + { CLK_DDR0, "ddr0_clk", "ddr_pll", CLK_IGNORE_UNUSED, CMU_DEVCLKEN0, 31, 0, "ddr0", }, + { CLK_DDR1, "ddr1_clk", "ddr_pll", CLK_IGNORE_UNUSED, CMU_DEVCLKEN0, 29, 0, "ddr1", }, + + { CLK_USB3_480MPLL0, "usb3_480mpll0", NULL, 0, CMU_USBPLL, 3, 0, "usb3_480mpll0", }, + { CLK_USB3_480MPHY0, "usb3_480mphy0", NULL, 0, CMU_USBPLL, 2, 0, "usb3_480mphy0", }, + { CLK_USB3_5GPHY, "usb3_5gphy", NULL, 0, CMU_USBPLL, 1, 0, "usb3_5gphy", }, + { CLK_USB3_CCE, "usb3_cce", NULL, 0, CMU_USBPLL, 0, 0, "usb3_cce", }, + + { CLK_24M_EDP, "24M_edp", "diff_24M", 0, CMU_EDPCLK, 8, 0, "24M_edp", }, + { CLK_EDP_LINK, "edp_link", "edp_pll", 0, CMU_DEVCLKEN0, 10, 0, "edp_link", }, + + { CLK_USB2H0_PLLEN, "usbh0_pllen", NULL, 0, CMU_USBPLL, 12, 0, "usbh0_pllen", }, + { CLK_USB2H0_PHY, "usbh0_phy", NULL, 0, CMU_USBPLL, 10, 0, "usbh0_phy", }, + { CLK_USB2H0_CCE, "usbh0_cce", NULL, 0, CMU_USBPLL, 8, 0, "usbh0_cce", }, + + { CLK_USB2H1_PLLEN, "usbh1_pllen", NULL, 0, CMU_USBPLL, 13, 0, "usbh1_pllen", }, + { CLK_USB2H1_PHY, "usbh1_phy", NULL, 0, CMU_USBPLL, 11, 0, "usbh1_phy", }, + { CLK_USB2H1_CCE, "usbh1_cce", NULL, 0, CMU_USBPLL, 9, 0, "usbh1_cce", }, +}; + +static struct owl_composite_clock s900_composite_clks[] = { + COMP_FACTOR_CLK(CLK_BISP, "bisp", 0, + C_MUX(bisp_clk_mux_p, CMU_BISPCLK, 4, 1, 0), + C_GATE(CMU_DEVCLKEN0, 14, 0), + C_FACTOR(CMU_BISPCLK, 0, 3, bisp_factor_table, 0)), + + COMP_DIV_CLK(CLK_CSI0, "csi0", 0, + C_MUX(csi_clk_mux_p, CMU_CSICLK, 4, 1, 0), + C_GATE(CMU_DEVCLKEN0, 13, 0), + C_DIVIDER(CMU_CSICLK, 0, 4, NULL, 0)), + + COMP_DIV_CLK(CLK_CSI1, "csi1", 0, + C_MUX(csi_clk_mux_p, CMU_CSICLK, 20, 1, 0), + C_GATE(CMU_DEVCLKEN0, 15, 0), + C_DIVIDER(CMU_CSICLK, 16, 4, NULL, 0)), + + COMP_PASS_CLK(CLK_DE, "de_clk", 0, + C_MUX(de_clk_mux_p, CMU_DECLK, 12, 1, 0), + C_GATE(CMU_DEVCLKEN0, 8, 0)), + + COMP_FACTOR_CLK(CLK_DMM, "dmm", CLK_IGNORE_UNUSED, + C_MUX(dmm_clk_mux_p, CMU_BUSCLK, 10, 2, 0), + C_GATE(CMU_DEVCLKEN0, 19, 0), + C_FACTOR(CMU_BUSCLK, 12, 3, dmm_factor_table, 0)), + + COMP_FACTOR_CLK(CLK_EDP, "edp_clk", 0, + C_MUX(edp_clk_mux_p, CMU_EDPCLK, 19, 1, 0), + C_GATE(CMU_DEVCLKEN0, 10, 0), + C_FACTOR(CMU_EDPCLK, 16, 3, bisp_factor_table, 0)), + + COMP_DIV_CLK(CLK_ETH_MAC, "eth_mac", 0, + C_MUX_F(eth_mac_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 22, 0), + C_DIVIDER(CMU_ASSISTPLL, 10, 1, eth_mac_div_table, 0)), + + COMP_FACTOR_CLK(CLK_GPU_CORE, "gpu_core", 0, + C_MUX(gpu_clk_mux_p, CMU_GPU3DCLK, 4, 2, 0), + C_GATE(CMU_GPU3DCLK, 15, 0), + C_FACTOR(CMU_GPU3DCLK, 0, 3, bisp_factor_table, 0)), + + COMP_FACTOR_CLK(CLK_GPU_MEM, "gpu_mem", 0, + C_MUX(gpu_clk_mux_p, CMU_GPU3DCLK, 20, 2, 0), + C_GATE(CMU_GPU3DCLK, 14, 0), + C_FACTOR(CMU_GPU3DCLK, 16, 3, bisp_factor_table, 0)), + + COMP_FACTOR_CLK(CLK_GPU_SYS, "gpu_sys", 0, + C_MUX(gpu_clk_mux_p, CMU_GPU3DCLK, 28, 2, 0), + C_GATE(CMU_GPU3DCLK, 13, 0), + C_FACTOR(CMU_GPU3DCLK, 24, 3, bisp_factor_table, 0)), + + COMP_FACTOR_CLK(CLK_HDE, "hde", 0, + C_MUX(hde_clk_mux_p, CMU_HDECLK, 4, 2, 0), + C_GATE(CMU_DEVCLKEN0, 27, 0), + C_FACTOR(CMU_HDECLK, 0, 3, bisp_factor_table, 0)), + + COMP_DIV_CLK(CLK_HDMI_AUDIO, "hdmia", 0, + C_MUX(i2s_clk_mux_p, CMU_AUDIOPLL, 24, 1, 0), + C_GATE(CMU_DEVCLKEN0, 22, 0), + C_DIVIDER(CMU_AUDIOPLL, 24, 4, hdmia_div_table, 0)), + + COMP_FIXED_FACTOR_CLK(CLK_I2C0, "i2c0", 0, + C_MUX_F(i2c_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 14, 0), + C_FIXED_FACTOR(1, 5)), + + COMP_FIXED_FACTOR_CLK(CLK_I2C1, "i2c1", 0, + C_MUX_F(i2c_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 15, 0), + C_FIXED_FACTOR(1, 5)), + + COMP_FIXED_FACTOR_CLK(CLK_I2C2, "i2c2", 0, + C_MUX_F(i2c_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 30, 0), + C_FIXED_FACTOR(1, 5)), + + COMP_FIXED_FACTOR_CLK(CLK_I2C3, "i2c3", 0, + C_MUX_F(i2c_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 31, 0), + C_FIXED_FACTOR(1, 5)), + + COMP_FIXED_FACTOR_CLK(CLK_I2C4, "i2c4", 0, + C_MUX_F(i2c_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN0, 17, 0), + C_FIXED_FACTOR(1, 5)), + + COMP_FIXED_FACTOR_CLK(CLK_I2C5, "i2c5", 0, + C_MUX_F(i2c_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 1, 0), + C_FIXED_FACTOR(1, 5)), + + COMP_DIV_CLK(CLK_I2SRX, "i2srx", 0, + C_MUX(i2s_clk_mux_p, CMU_AUDIOPLL, 24, 1, 0), + C_GATE(CMU_DEVCLKEN0, 21, 0), + C_DIVIDER(CMU_AUDIOPLL, 20, 4, i2s_div_table, 0)), + + COMP_DIV_CLK(CLK_I2STX, "i2stx", 0, + C_MUX(i2s_clk_mux_p, CMU_AUDIOPLL, 24, 1, 0), + C_GATE(CMU_DEVCLKEN0, 20, 0), + C_DIVIDER(CMU_AUDIOPLL, 16, 4, i2s_div_table, 0)), + + COMP_FACTOR_CLK(CLK_IMX, "imx", 0, + C_MUX(imx_clk_mux_p, CMU_IMXCLK, 4, 1, 0), + C_GATE(CMU_DEVCLKEN1, 17, 0), + C_FACTOR(CMU_IMXCLK, 0, 3, bisp_factor_table, 0)), + + COMP_DIV_CLK(CLK_LCD, "lcd", 0, + C_MUX(lcd_clk_mux_p, CMU_LCDCLK, 12, 2, 0), + C_GATE(CMU_DEVCLKEN0, 9, 0), + C_DIVIDER(CMU_LCDCLK, 0, 5, NULL, 0)), + + COMP_DIV_CLK(CLK_NAND0, "nand0", CLK_SET_RATE_PARENT, + C_MUX(nand_clk_mux_p, CMU_NANDCCLK, 8, 1, 0), + C_GATE(CMU_DEVCLKEN0, 4, 0), + C_DIVIDER(CMU_NANDCCLK, 0, 4, nand_div_table, 0)), + + COMP_DIV_CLK(CLK_NAND1, "nand1", CLK_SET_RATE_PARENT, + C_MUX(nand_clk_mux_p, CMU_NANDCCLK, 24, 1, 0), + C_GATE(CMU_DEVCLKEN0, 11, 0), + C_DIVIDER(CMU_NANDCCLK, 16, 4, nand_div_table, 0)), + + COMP_DIV_CLK(CLK_PWM0, "pwm0", 0, + C_MUX_F(pwm_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 23, 0), + C_DIVIDER(CMU_PWM0CLK, 0, 6, NULL, 0)), + + COMP_DIV_CLK(CLK_PWM0, "pwm1", 0, + C_MUX_F(pwm_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 24, 0), + C_DIVIDER(CMU_PWM1CLK, 0, 6, NULL, 0)), + /* + * pwm2 may be for backlight, do not gate it + * even it is "unused", because it may be + * enabled at boot stage, and in kernel, driver + * has no effective method to know the real status, + * so, the best way is keeping it as what it was. + */ + COMP_DIV_CLK(CLK_PWM0, "pwm2", CLK_IGNORE_UNUSED, + C_MUX_F(pwm_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 25, 0), + C_DIVIDER(CMU_PWM2CLK, 0, 6, NULL, 0)), + + COMP_DIV_CLK(CLK_PWM0, "pwm3", 0, + C_MUX_F(pwm_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 26, 0), + C_DIVIDER(CMU_PWM3CLK, 0, 6, NULL, 0)), + + COMP_DIV_CLK(CLK_PWM0, "pwm4", 0, + C_MUX_F(pwm_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 4, 0), + C_DIVIDER(CMU_PWM4CLK, 0, 6, NULL, 0)), + + COMP_DIV_CLK(CLK_PWM5, "pwm5", 0, + C_MUX_F(pwm_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 5, 0), + C_DIVIDER(CMU_PWM5CLK, 0, 6, NULL, 0)), + + COMP_FACTOR_CLK(CLK_SD0, "sd0", 0, + C_MUX(sd_clk_mux_p, CMU_SD0CLK, 9, 1, 0), + C_GATE(CMU_DEVCLKEN0, 5, 0), + C_FACTOR(CMU_SD0CLK, 0, 9, sd_factor_table, 0)), + + COMP_FACTOR_CLK(CLK_SD1, "sd1", 0, + C_MUX(sd_clk_mux_p, CMU_SD1CLK, 9, 1, 0), + C_GATE(CMU_DEVCLKEN0, 6, 0), + C_FACTOR(CMU_SD1CLK, 0, 9, sd_factor_table, 0)), + + COMP_FACTOR_CLK(CLK_SD2, "sd2", 0, + C_MUX(sd_clk_mux_p, CMU_SD2CLK, 9, 1, 0), + C_GATE(CMU_DEVCLKEN0, 7, 0), + C_FACTOR(CMU_SD2CLK, 0, 9, sd_factor_table, 0)), + + COMP_FACTOR_CLK(CLK_SD3, "sd3", 0, + C_MUX(sd_clk_mux_p, CMU_SD3CLK, 9, 1, 0), + C_GATE(CMU_DEVCLKEN0, 16, 0), + C_FACTOR(CMU_SD3CLK, 0, 9, sd_factor_table, 0)), + + COMP_DIV_CLK(CLK_SENSOR, "sensor", 0, + C_MUX(sensor_clk_mux_p, CMU_SENSORCLK, 4, 1, 0), + C_NULL, + C_DIVIDER(CMU_SENSORCLK, 0, 4, NULL, 0)), + + COMP_DIV_CLK(CLK_SPEED_SENSOR, "speed_sensor", 0, + C_MUX_F(speed_sensor_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 0, 0), + C_DIVIDER(CMU_TLSCLK, 0, 4, NULL, CLK_DIVIDER_POWER_OF_TWO)), + + COMP_PASS_CLK(CLK_SPI0, "spi0", 0, + C_MUX_F(spi_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 10, 0)), + + COMP_PASS_CLK(CLK_SPI1, "spi1", 0, + C_MUX_F(spi_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 11, 0)), + + COMP_PASS_CLK(CLK_SPI2, "spi2", 0, + C_MUX_F(spi_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 12, 0)), + + COMP_PASS_CLK(CLK_SPI3, "spi3", 0, + C_MUX_F(spi_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 13, 0)), + + COMP_DIV_CLK(CLK_THERMAL_SENSOR, "thermal_sensor", 0, + C_MUX_F(thermal_sensor_clk_mux_p, 0), + C_GATE(CMU_DEVCLKEN1, 2, 0), + C_DIVIDER(CMU_TLSCLK, 8, 4, NULL, CLK_DIVIDER_POWER_OF_TWO)), + + COMP_DIV_CLK(CLK_UART0, "uart0", 0, + C_MUX(uart_clk_mux_p, CMU_UART0CLK, 16, 1, 0), + C_GATE(CMU_DEVCLKEN1, 6, 0), + C_DIVIDER(CMU_UART0CLK, 0, 8, NULL, CLK_DIVIDER_ROUND_CLOSEST)), + + COMP_DIV_CLK(CLK_UART1, "uart1", 0, + C_MUX(uart_clk_mux_p, CMU_UART1CLK, 16, 1, 0), + C_GATE(CMU_DEVCLKEN1, 7, 0), + C_DIVIDER(CMU_UART1CLK, 1, 8, NULL, CLK_DIVIDER_ROUND_CLOSEST)), + + COMP_DIV_CLK(CLK_UART2, "uart2", 0, + C_MUX(uart_clk_mux_p, CMU_UART2CLK, 16, 1, 0), + C_GATE(CMU_DEVCLKEN1, 8, 0), + C_DIVIDER(CMU_UART2CLK, 0, 8, NULL, CLK_DIVIDER_ROUND_CLOSEST)), + + COMP_DIV_CLK(CLK_UART3, "uart3", 0, + C_MUX(uart_clk_mux_p, CMU_UART3CLK, 16, 1, 0), + C_GATE(CMU_DEVCLKEN1, 19, 0), + C_DIVIDER(CMU_UART3CLK, 0, 8, NULL, CLK_DIVIDER_ROUND_CLOSEST)), + + COMP_DIV_CLK(CLK_UART4, "uart4", 0, + C_MUX(uart_clk_mux_p, CMU_UART4CLK, 16, 1, 0), + C_GATE(CMU_DEVCLKEN1, 20, 0), + C_DIVIDER(CMU_UART4CLK, 0, 8, NULL, CLK_DIVIDER_ROUND_CLOSEST)), + + COMP_DIV_CLK(CLK_UART5, "uart5", 0, + C_MUX(uart_clk_mux_p, CMU_UART5CLK, 16, 1, 0), + C_GATE(CMU_DEVCLKEN1, 21, 0), + C_DIVIDER(CMU_UART5CLK, 0, 8, NULL, CLK_DIVIDER_ROUND_CLOSEST)), + + COMP_DIV_CLK(CLK_UART6, "uart6", 0, + C_MUX(uart_clk_mux_p, CMU_UART6CLK, 16, 1, 0), + C_GATE(CMU_DEVCLKEN1, 18, 0), + C_DIVIDER(CMU_UART6CLK, 0, 8, NULL, CLK_DIVIDER_ROUND_CLOSEST)), + + COMP_FACTOR_CLK(CLK_VCE, "vce", 0, + C_MUX(vce_clk_mux_p, CMU_VCECLK, 4, 2, 0), + C_GATE(CMU_DEVCLKEN0, 26, 0), + C_FACTOR(CMU_VCECLK, 0, 3, bisp_factor_table, 0)), + + COMP_FACTOR_CLK(CLK_VDE, "vde", 0, + C_MUX(hde_clk_mux_p, CMU_VDECLK, 4, 2, 0), + C_GATE(CMU_DEVCLKEN0, 25, 0), + C_FACTOR(CMU_VDECLK, 0, 3, bisp_factor_table, 0)), +}; + +static int s900_clk_probe(struct platform_device *pdev) +{ + struct owl_clk_provider *ctx; + struct device_node *np = pdev->dev.of_node; + struct resource *res; + void __iomem *base; + int i; + + ctx = kzalloc(sizeof(struct owl_clk_provider) + + sizeof(*ctx->clk_data.hws) * CLK_NR_CLKS, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + for (i = 0; i < CLK_NR_CLKS; ++i) + ctx->clk_data.hws[i] = ERR_PTR(-ENOENT); + + ctx->reg_base = base; + ctx->clk_data.num = CLK_NR_CLKS; + spin_lock_init(&ctx->lock); + + /* register pll clocks */ + owl_clk_register_pll(ctx, s900_pll_clks, + ARRAY_SIZE(s900_pll_clks)); + + /* register divider clocks */ + owl_clk_register_divider(ctx, s900_div_clks, + ARRAY_SIZE(s900_div_clks)); + + /* register factor divider clocks */ + owl_clk_register_factor(ctx, s900_factor_clks, + ARRAY_SIZE(s900_factor_clks)); + + /* register mux clocks */ + owl_clk_register_mux(ctx, s900_mux_clks, + ARRAY_SIZE(s900_mux_clks)); + + /* register gate clocks */ + owl_clk_register_gate(ctx, s900_gate_clks, + ARRAY_SIZE(s900_gate_clks)); + + /* register composite clocks */ + owl_clk_register_composite(ctx, s900_composite_clks, + ARRAY_SIZE(s900_composite_clks)); + + return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, + &ctx->clk_data); + +} + +static const struct of_device_id s900_clk_of_match[] = { + { .compatible = "actions,s900-clock", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, s900_clk_of_match); + +static struct platform_driver s900_clk_driver = { + .probe = s900_clk_probe, + .driver = { + .name = "s900-clk", + .of_match_table = s900_clk_of_match, + }, +}; + +static int __init s900_clk_init(void) +{ + return platform_driver_register(&s900_clk_driver); +} +core_initcall(s900_clk_init); diff --git a/drivers/clk/owl/clk.c b/drivers/clk/owl/clk.c new file mode 100644 index 0000000..bf31b31 --- /dev/null +++ b/drivers/clk/owl/clk.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2014 Actions Semi Inc. + * David Liu + * + * Copyright (c) 2017 Linaro Ltd. + * Author: Manivannan Sadhasivam + * + * based on + * + * samsung/clk.h + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2013 Linaro Ltd. + * Author: Thomas Abraham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "clk.h" + +void owl_clk_add_hw_data(struct owl_clk_provider *ctx, struct clk_hw *clk_hw, + unsigned int id) +{ + if (id) + ctx->clk_data.hws[id] = clk_hw; +} + +/* register a list of fixed factor clocks */ +void owl_clk_register_fixed_factor(struct owl_clk_provider *ctx, + struct owl_fixed_factor_clock *clks, int nums) +{ + struct clk_hw *clk_hw; + int i; + + for (i = 0; i < nums; i++) { + clk_hw = clk_hw_register_fixed_factor(NULL, clks[i].name, + clks[i].parent_name, clks[i].flags, + clks[i].mult, clks[i].div); + if (IS_ERR(clk_hw)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + + owl_clk_add_hw_data(ctx, clk_hw, clks[i].id); + } +} + +/* register a list of pll clocks */ +void owl_clk_register_pll(struct owl_clk_provider *ctx, + struct owl_pll_clock *clks, int nums) +{ + struct clk_hw *clk_hw; + int i; + + for (i = 0; i < nums; i++) { + clk_hw = owl_pll_clk_register(clks[i].name, clks[i].parent_name, + clks[i].flags, ctx->reg_base + clks[i].offset, + clks[i].bfreq, clks[i].enable_bit, + clks[i].shift, clks[i].width, + clks[i].min_mul, clks[i].max_mul, + clks[i].pll_flags, clks[i].table, + &ctx->lock); + if (IS_ERR(clk_hw)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + + } + + owl_clk_add_hw_data(ctx, clk_hw, clks[i].id); + } +} + +/* register a list of divider clocks */ +void owl_clk_register_divider(struct owl_clk_provider *ctx, + struct owl_divider_clock *clks, int nums) +{ + struct clk_hw *clk_hw; + int i; + + for (i = 0; i < nums; i++) { + clk_hw = clk_hw_register_divider_table(NULL, clks[i].name, + clks[i].parent_name, clks[i].flags, + ctx->reg_base + clks[i].offset, clks[i].shift, + clks[i].width, clks[i].div_flags, + clks[i].table, &ctx->lock); + if (IS_ERR(clk_hw)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + + owl_clk_add_hw_data(ctx, clk_hw, clks[i].id); + } +} + +/* register a list of factor divider clocks */ +void owl_clk_register_factor(struct owl_clk_provider *ctx, + struct owl_factor_clock *clks, int nums) +{ + struct clk_hw *clk_hw; + int i; + + for (i = 0; i < nums; i++) { + clk_hw = owl_factor_clk_register(NULL, clks[i].name, + clks[i].parent_name, clks[i].flags, + ctx->reg_base + clks[i].offset, clks[i].shift, + clks[i].width, clks[i].div_flags, + clks[i].table, &ctx->lock); + if (IS_ERR(clk_hw)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + + owl_clk_add_hw_data(ctx, clk_hw, clks[i].id); + } +} + +/* register a list of mux clocks */ +void owl_clk_register_mux(struct owl_clk_provider *ctx, + struct owl_mux_clock *clks, int nums) +{ + struct clk_hw *clk_hw; + int i; + + for (i = 0; i < nums; i++) { + clk_hw = clk_hw_register_mux(NULL, clks[i].name, + clks[i].parent_names, clks[i].num_parents, + clks[i].flags, ctx->reg_base + clks[i].offset, + clks[i].shift, clks[i].width, + clks[i].mux_flags, &ctx->lock); + if (IS_ERR(clk_hw)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + + owl_clk_add_hw_data(ctx, clk_hw, clks[i].id); + } +} + +/* register a list of gate clocks */ +void owl_clk_register_gate(struct owl_clk_provider *ctx, + struct owl_gate_clock *clks, int nums) +{ + struct clk_hw *clk_hw; + int i; + + for (i = 0; i < nums; i++) { + clk_hw = clk_hw_register_gate(NULL, clks[i].name, + clks[i].parent_name, clks[i].flags, + ctx->reg_base + clks[i].offset, + clks[i].bit_idx, clks[i].gate_flags, + &ctx->lock); + if (IS_ERR(clk_hw)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + + owl_clk_add_hw_data(ctx, clk_hw, clks[i].id); + } +} + +static struct clk_hw *_register_composite(struct owl_clk_provider *ctx, + struct owl_composite_clock *cclk) +{ + struct clk_hw *clk_hw; + struct owl_mux_clock *amux; + struct owl_gate_clock *agate; + union rate_clock *arate; + struct clk_gate *gate = NULL; + struct clk_mux *mux = NULL; + struct clk_fixed_factor *fixed_factor = NULL; + struct clk_divider *div = NULL; + struct owl_factor *factor = NULL; + struct clk_hw *mux_hw = NULL; + struct clk_hw *gate_hw = NULL; + struct clk_hw *rate_hw = NULL; + const struct clk_ops *rate_ops = NULL; + const char *clk_name = cclk->name; + const char **parent_names; + int i, num_parents; + + amux = &cclk->mux; + agate = &cclk->gate; + arate = &cclk->rate; + + parent_names = NULL; + num_parents = 0; + + if (amux->id) { + num_parents = amux->num_parents; + if (num_parents > 0) { + parent_names = kzalloc((sizeof(char *) * num_parents), + GFP_KERNEL); + if (!parent_names) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < num_parents; i++) + parent_names[i] = kstrdup(amux->parent_names[i], + GFP_KERNEL); + } + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return NULL; + + /* set up gate properties */ + mux->reg = ctx->reg_base + amux->offset; + mux->shift = amux->shift; + mux->mask = BIT(amux->width) - 1; + mux->flags = amux->mux_flags; + mux->lock = &ctx->lock; + mux_hw = &mux->hw; + } + + if (arate->fixed_factor.id) { + switch (cclk->type) { + case OWL_COMPOSITE_TYPE_FIXED_FACTOR: + fixed_factor = kzalloc(sizeof(*fixed_factor), + GFP_KERNEL); + if (!fixed_factor) + return NULL; + fixed_factor->mult = arate->fixed_factor.mult; + fixed_factor->div = arate->fixed_factor.div; + + rate_ops = &clk_fixed_factor_ops; + rate_hw = &fixed_factor->hw; + break; + + case OWL_COMPOSITE_TYPE_DIVIDER: + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return NULL; + div->reg = ctx->reg_base + arate->div.offset; + div->shift = arate->div.shift; + div->width = arate->div.width; + div->flags = arate->div.div_flags; + div->table = arate->div.table; + div->lock = &ctx->lock; + + rate_ops = &clk_divider_ops; + rate_hw = &div->hw; + break; + + case OWL_COMPOSITE_TYPE_FACTOR: + factor = kzalloc(sizeof(*factor), GFP_KERNEL); + if (!factor) + return NULL; + factor->reg = ctx->reg_base + arate->factor.offset; + factor->shift = arate->factor.shift; + factor->width = arate->factor.width; + factor->flags = arate->factor.div_flags; + factor->table = arate->factor.table; + factor->lock = &ctx->lock; + + rate_ops = &owl_factor_ops; + rate_hw = &factor->hw; + break; + + default: + break; + } + } + + if (agate->id) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + /* set up gate properties */ + gate->reg = ctx->reg_base + agate->offset; + gate->bit_idx = agate->bit_idx; + gate->lock = &ctx->lock; + gate_hw = &gate->hw; + } + + clk_hw = clk_hw_register_composite(NULL, clk_name, + parent_names, num_parents, + mux_hw, &clk_mux_ops, + rate_hw, rate_ops, + gate_hw, &clk_gate_ops, cclk->flags); + + return clk_hw; +} + +/* register a list of composite clocks */ +void owl_clk_register_composite(struct owl_clk_provider *ctx, + struct owl_composite_clock *clks, int nums) +{ + struct clk_hw *clk_hw; + int i; + + for (i = 0; i < nums; i++) { + clk_hw = _register_composite(ctx, &clks[i]); + if (IS_ERR(clk_hw)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + + owl_clk_add_hw_data(ctx, clk_hw, clks[i].id); + } +} diff --git a/drivers/clk/owl/clk.h b/drivers/clk/owl/clk.h new file mode 100644 index 0000000..f3d852b --- /dev/null +++ b/drivers/clk/owl/clk.h @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2014 Actions Semi Inc. + * David Liu + * + * Copyright (c) 2017 Linaro Ltd. + * Author: Manivannan Sadhasivam + * + * based on + * + * samsung/clk.h + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * Copyright (c) 2013 Linaro Ltd. + * Author: Thomas Abraham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __OWL_CLK_H +#define __OWL_CLK_H + +#include + +struct owl_clk_provider { + void __iomem *reg_base; + struct clk_hw_onecell_data clk_data; + spinlock_t lock; +}; + +struct owl_fixed_factor_clock { + unsigned int id; + char *name; + const char *parent_name; + unsigned long flags; + unsigned int mult; + unsigned int div; +}; + +/* last entry should have rate = 0 */ +struct clk_pll_table { + unsigned int val; + unsigned long rate; +}; + +struct owl_pll_clock { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + unsigned long bfreq; + u8 enable_bit; + u8 shift; + u8 width; + u8 min_mul; + u8 max_mul; + u8 pll_flags; + const struct clk_pll_table *table; +}; + +#define CLK_OWL_PLL_FIXED_FREQ BIT(0) + +struct owl_divider_clock { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u8 div_flags; + struct clk_div_table *table; + const char *alias; +}; + +struct clk_factor_table { + unsigned int val; + unsigned int mul; + unsigned int div; +}; + +struct owl_factor_clock { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u8 div_flags; + struct clk_factor_table *table; + const char *alias; +}; + +struct owl_factor { + struct clk_hw hw; + void __iomem *reg; + u8 shift; + u8 width; + u8 flags; + const struct clk_factor_table *table; + spinlock_t *lock; +}; + +extern const struct clk_ops owl_factor_ops; + +struct owl_mux_clock { + unsigned int id; + const char *name; + const char **parent_names; + u8 num_parents; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u8 mux_flags; + const char *alias; +}; + +struct owl_gate_clock { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 bit_idx; + u8 gate_flags; + const char *alias; +}; + +union rate_clock { + struct owl_fixed_factor_clock fixed_factor; + struct owl_divider_clock div; + struct owl_factor_clock factor; +}; + +struct owl_composite_clock { + unsigned int id; + const char *name; + unsigned int type; + unsigned long flags; + + struct owl_mux_clock mux; + struct owl_gate_clock gate; + union rate_clock rate; +}; + +#define OWL_COMPOSITE_TYPE_DIVIDER 1 +#define OWL_COMPOSITE_TYPE_FACTOR 2 +#define OWL_COMPOSITE_TYPE_FIXED_FACTOR 3 +#define OWL_COMPOSITE_TYPE_PASS 10 + +#define COMP_FIXED_FACTOR_CLK(_id, _name, _flags, _mux, _gate, _fixed_factor) \ + { \ + .id = _id, \ + .name = _name, \ + .type = OWL_COMPOSITE_TYPE_FIXED_FACTOR, \ + .flags = _flags, \ + .mux = _mux, \ + .gate = _gate, \ + .rate.fixed_factor = _fixed_factor, \ + } + +#define COMP_DIV_CLK(_id, _name, _flags, _mux, _gate, _div) \ + { \ + .id = _id, \ + .name = _name, \ + .type = OWL_COMPOSITE_TYPE_DIVIDER, \ + .flags = _flags, \ + .mux = _mux, \ + .gate = _gate, \ + .rate.div = _div, \ + } + +#define COMP_FACTOR_CLK(_id, _name, _flags, _mux, _gate, _factor) \ + { \ + .id = _id, \ + .name = _name, \ + .type = OWL_COMPOSITE_TYPE_FACTOR, \ + .flags = _flags, \ + .mux = _mux, \ + .gate = _gate, \ + .rate.factor = _factor, \ + } + +#define COMP_PASS_CLK(_id, _name, _flags, _mux, _gate) \ + { \ + .id = _id, \ + .name = _name, \ + .type = OWL_COMPOSITE_TYPE_PASS, \ + .flags = _flags, \ + .mux = _mux, \ + .gate = _gate, \ + } + + +#define C_MUX(p, o, s, w, mf) \ + { \ + .id = -1, \ + .parent_names = p, \ + .num_parents = ARRAY_SIZE(p), \ + .offset = o, \ + .shift = s, \ + .width = w, \ + .mux_flags = mf, \ + } + +/* fixed mux, only one parent */ +#define C_MUX_F(p, mf) \ + { \ + .id = -1, \ + .parent_names = p, \ + .num_parents = 1, \ + .mux_flags = mf, \ + } + +#define C_GATE(o, b, gf) \ + { \ + .id = -1, \ + .offset = o, \ + .bit_idx = b, \ + .gate_flags = gf, \ + } + +#define C_NULL \ + { \ + .id = 0, \ + } + +#define C_FIXED_FACTOR(m, d) \ + { \ + .id = -1, \ + .mult = m, \ + .div = d, \ + } + +#define C_DIVIDER(o, s, w, t, df) \ + { \ + .id = -1, \ + .offset = o, \ + .shift = s, \ + .width = w, \ + .table = t, \ + .div_flags = df, \ + } + +#define C_FACTOR(o, s, w, t, df) \ + { \ + .id = -1, \ + .offset = o, \ + .shift = s, \ + .width = w, \ + .table = t, \ + .div_flags = df, \ + } + +extern void owl_clk_register_pll(struct owl_clk_provider *ctx, + struct owl_pll_clock *clks, int nums); + +extern void owl_clk_register_fixed_factor( + struct owl_clk_provider *ctx, + struct owl_fixed_factor_clock *clks, + int nums); + +extern void owl_clk_register_divider(struct owl_clk_provider *ctx, + struct owl_divider_clock *clks, int nums); + +extern void owl_clk_register_factor(struct owl_clk_provider *ctx, + struct owl_factor_clock *clks, int nums); + +extern void owl_clk_register_mux(struct owl_clk_provider *ctx, + struct owl_mux_clock *clks, int nums); + +extern void owl_clk_register_gate(struct owl_clk_provider *ctx, + struct owl_gate_clock *clks, int nums); + +extern void owl_clk_register_composite(struct owl_clk_provider *ctx, + struct owl_composite_clock *clks, int nums); + +extern struct clk_hw *owl_pll_clk_register(const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, unsigned long bfreq, u8 enable_bit, + u8 shift, u8 width, u8 min_mul, u8 max_mul, u8 pll_flags, + const struct clk_pll_table *table, spinlock_t *lock); + +extern struct clk_hw *owl_factor_clk_register(struct device *dev, + const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, u8 shift, + u8 width, u8 clk_factor_flags, + const struct clk_factor_table *table, spinlock_t *lock); + +#endif /* __OWL_CLK_H */ diff --git a/include/dt-bindings/clock/actions,s900-clock.h b/include/dt-bindings/clock/actions,s900-clock.h new file mode 100644 index 0000000..8056c27 --- /dev/null +++ b/include/dt-bindings/clock/actions,s900-clock.h @@ -0,0 +1,141 @@ +/* + * Device Tree binding constants for Actions S900 SoC clock controller + * + * Copyright (c) 2014 Actions Semi Inc. + * Copyright (c) 2017 Linaro Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __DT_BINDINGS_CLOCK_S900_H +#define __DT_BINDINGS_CLOCK_S900_H + +#define CLK_NONE 0 + +/* fixed rate clocks */ +#define CLK_LOSC 1 +#define CLK_HOSC 2 + +/* pll clocks */ +#define CLK_CORE_PLL 3 +#define CLK_DEV_PLL 4 +#define CLK_DDR_PLL 5 +#define CLK_NAND_PLL 6 +#define CLK_DISPLAY_PLL 7 +#define CLK_DSI_PLL 8 +#define CLK_ASSIST_PLL 9 +#define CLK_AUDIO_PLL 10 + +/* system clock */ +#define CLK_CPU 15 +#define CLK_DEV 16 +#define CLK_NOC 17 +#define CLK_NOC_CLK_MUX 18 +#define CLK_NOC_CLK_DIV 19 +#define CLK_AHB 20 +#define CLK_APB 21 +#define CLK_DMAC 22 + +/* peripheral device clock */ +#define CLK_GPIO 23 + +#define CLK_BISP 24 +#define CLK_CSI0 25 +#define CLK_CSI1 26 + +#define CLK_DE 27 +#define CLK_DE1 28 +#define CLK_DE2 29 +#define CLK_DE3 30 +#define CLK_DSI 32 + +#define CLK_GPU 33 +#define CLK_GPU_CORE 34 +#define CLK_GPU_MEM 35 +#define CLK_GPU_SYS 36 + +#define CLK_HDE 37 +#define CLK_I2C0 38 +#define CLK_I2C1 39 +#define CLK_I2C2 40 +#define CLK_I2C3 41 +#define CLK_I2C4 42 +#define CLK_I2C5 43 +#define CLK_I2SRX 44 +#define CLK_I2STX 45 +#define CLK_IMX 46 +#define CLK_LCD 47 +#define CLK_NAND0 48 +#define CLK_NAND1 49 +#define CLK_PWM0 50 +#define CLK_PWM1 51 +#define CLK_PWM2 52 +#define CLK_PWM3 53 +#define CLK_PWM4 54 +#define CLK_PWM5 55 +#define CLK_SD0 56 +#define CLK_SD1 57 +#define CLK_SD2 58 +#define CLK_SD3 59 +#define CLK_SENSOR 60 +#define CLK_SPEED_SENSOR 61 +#define CLK_SPI0 62 +#define CLK_SPI1 63 +#define CLK_SPI2 64 +#define CLK_SPI3 65 +#define CLK_THERMAL_SENSOR 66 +#define CLK_UART0 67 +#define CLK_UART1 68 +#define CLK_UART2 69 +#define CLK_UART3 70 +#define CLK_UART4 71 +#define CLK_UART5 72 +#define CLK_UART6 73 +#define CLK_VCE 74 +#define CLK_VDE 75 + +#define CLK_USB3_480MPLL0 76 +#define CLK_USB3_480MPHY0 77 +#define CLK_USB3_5GPHY 78 +#define CLK_USB3_CCE 79 +#define CLK_USB3_MAC 80 + +#define CLK_TIMER 83 + +#define CLK_HDMI_AUDIO 84 + +#define CLK_24M 85 + +#define CLK_EDP 86 + +#define CLK_24M_EDP 87 +#define CLK_EDP_PLL 88 +#define CLK_EDP_LINK 89 + +#define CLK_USB2H0_PLLEN 90 +#define CLK_USB2H0_PHY 91 +#define CLK_USB2H0_CCE 92 +#define CLK_USB2H1_PLLEN 93 +#define CLK_USB2H1_PHY 94 +#define CLK_USB2H1_CCE 95 + +#define CLK_DDR0 96 +#define CLK_DDR1 97 +#define CLK_DMM 98 + +#define CLK_ETH_MAC 99 +#define CLK_RMII_REF 100 + +#define CLK_NR_CLKS 110 + +#endif /* __DT_BINDINGS_CLOCK_S900_H */