lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <42102b7d-4e3c-d5fc-b0db-48a1203757ba@lechnology.com>
Date:   Tue, 2 Jan 2018 15:31:10 -0600
From:   David Lechner <david@...hnology.com>
To:     linux-arm-kernel@...ts.infradead.org
Cc:     Sekhar Nori <nsekhar@...com>, Kevin Hilman <khilman@...nel.org>,
        Adam Ford <aford173@...il.com>, linux-kernel@...r.kernel.org,
        linux-clk@...r.kernel.org,
        Michael Turquette <mturquette@...libre.com>,
        Stephen Boyd <sboyd@...eaurora.org>
Subject: Re: [PATCH v4 5/7] clk: Introduce davinci clocks

Forgot to cc linux-clk, so doing that now...


On 12/31/2017 05:39 PM, David Lechner wrote:
> This introduces new drivers for arch/arm/mach-davinci. The code is based
> on the clock drivers from there and adapted to use the common clock
> framework.
> 
> Signed-off-by: David Lechner <david@...hnology.com>
> ---
>   drivers/clk/Makefile                      |   1 +
>   drivers/clk/davinci/Makefile              |   3 +
>   drivers/clk/davinci/da8xx-cfgchip-clk.c   | 380 ++++++++++++++++++++++++++++++
>   drivers/clk/davinci/pll.c                 | 333 ++++++++++++++++++++++++++
>   drivers/clk/davinci/psc.c                 | 217 +++++++++++++++++
>   include/linux/clk/davinci.h               |  46 ++++
>   include/linux/platform_data/davinci_clk.h |  25 ++
>   7 files changed, 1005 insertions(+)
>   create mode 100644 drivers/clk/davinci/Makefile
>   create mode 100644 drivers/clk/davinci/da8xx-cfgchip-clk.c
>   create mode 100644 drivers/clk/davinci/pll.c
>   create mode 100644 drivers/clk/davinci/psc.c
>   create mode 100644 include/linux/clk/davinci.h
>   create mode 100644 include/linux/platform_data/davinci_clk.h
> 
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index f7f761b..c865fd0 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -60,6 +60,7 @@ obj-$(CONFIG_ARCH_ARTPEC)		+= axis/
>   obj-$(CONFIG_ARC_PLAT_AXS10X)		+= axs10x/
>   obj-y					+= bcm/
>   obj-$(CONFIG_ARCH_BERLIN)		+= berlin/
> +obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
>   obj-$(CONFIG_H8300)			+= h8300/
>   obj-$(CONFIG_ARCH_HISI)			+= hisilicon/
>   obj-y					+= imgtec/
> diff --git a/drivers/clk/davinci/Makefile b/drivers/clk/davinci/Makefile
> new file mode 100644
> index 0000000..5efbdcd
> --- /dev/null
> +++ b/drivers/clk/davinci/Makefile
> @@ -0,0 +1,3 @@
> +obj-$(CONFIG_PHY_DA8XX_USB) += da8xx-cfgchip-clk.o
> +obj-y += pll.o
> +obj-y += psc.o
> \ No newline at end of file
> diff --git a/drivers/clk/davinci/da8xx-cfgchip-clk.c b/drivers/clk/davinci/da8xx-cfgchip-clk.c
> new file mode 100644
> index 0000000..780bb25
> --- /dev/null
> +++ b/drivers/clk/davinci/da8xx-cfgchip-clk.c
> @@ -0,0 +1,380 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * da8xx-cfgchip-clk - TI DaVinci DA8xx CFGCHIP clocks driver
> + *
> + * Copyright (C) 2017 David Lechner <david@...hnology.com>
> + *
> + * This driver exposes the USB PHY clocks on DA8xx/AM18xx/OMAP-L13x SoCs.
> + * The clocks consist of two muxes and a PLL. The USB 2.0 PHY mux and PLL are
> + * combined into a single clock in Linux. The USB 1.0 PHY clock just consists
> + * of a mux. These clocks are controlled through CFGCHIP2, which is accessed
> + * as a syscon regmap since it is shared with other devices.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk-provider.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/mfd/da8xx-cfgchip.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/platform_data/davinci_clk.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +/**
> + * da8xx_cfgchip_clk
> + * @usb0_hw: The USB 2.0 PHY clock (mux + PLL)
> + * @usb1_hw: The USB 1.1 PHY clock (mux)
> + * @usb0_clk: The USB 2.0 subsystem PSC clock
> + * @regmap: The CFGCHIP syscon regmap
> + */
> +struct da8xx_cfgchip_clk {
> +	struct clk_hw usb0_hw;
> +	struct clk_hw usb1_hw;
> +	struct clk *usb0_clk;
> +	struct regmap *regmap;
> +};
> +
> +/* The USB 2.0 PHY can use either USB_REFCLKIN or AUXCLK */
> +enum usb0_phy_clk_parent {
> +	USB20_PHY_CLK_PARENT_USB_REFCLKIN,
> +	USB20_PHY_CLK_PARENT_PLL0_AUX,
> +};
> +
> +/* The USB 1.1 PHY can use either the PLL output from the USB 2.0 PHY or
> + * USB_REFCLKIN
> + */
> +enum usb1_phy_clk_parent {
> +	USB1_PHY_CLK_PARENT_USB_REFCLKIN,
> +	USB1_PHY_CLK_PARENT_USB0_PHY_PLL,
> +};
> +
> +/* --- USB 2.0 PHY clock --- */
> +
> +static int usb0_phy_clk_prepare(struct clk_hw *hw)
> +{
> +	struct da8xx_cfgchip_clk *clk =
> +			container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
> +
> +	/* The USB 2.0 PSC clock is only needed temporarily during the USB 2.0
> +	 * PHY clock enable, but since clk_prepare() can't be called in an
> +	 * atomic context (i.e. in clk_enable()), we have to prepare it here.
> +	 */
> +	return clk_prepare(clk->usb0_clk);
> +}
> +
> +static void usb0_phy_clk_unprepare(struct clk_hw *hw)
> +{
> +	struct da8xx_cfgchip_clk *clk =
> +			container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
> +
> +	clk_unprepare(clk->usb0_clk);
> +}
> +
> +static int usb0_phy_clk_enable(struct clk_hw *hw)
> +{
> +	struct da8xx_cfgchip_clk *clk =
> +			container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
> +	unsigned int mask, val;
> +	int ret;
> +
> +	/* Locking the USB 2.O PLL requires that the USB 2.O PSC is enabled
> +	 * temporaily. It can be turned back off once the PLL is locked.
> +	 */
> +	clk_enable(clk->usb0_clk);
> +
> +	/* Turn on the USB 2.0 PHY, but just the PLL, and not OTG. The USB 1.1
> +	 * PHY may use the USB 2.0 PLL clock without USB 2.0 OTG being used.
> +	 */
> +	mask = CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_PHY_PLLON;
> +	val = CFGCHIP2_PHY_PLLON;
> +
> +	regmap_write_bits(clk->regmap, CFGCHIP(2), mask, val);
> +	ret = regmap_read_poll_timeout(clk->regmap, CFGCHIP(2), val,
> +				       val & CFGCHIP2_PHYCLKGD, 0, 500000);
> +
> +	clk_disable(clk->usb0_clk);
> +
> +	return ret;
> +}
> +
> +static void usb0_phy_clk_disable(struct clk_hw *hw)
> +{
> +	struct da8xx_cfgchip_clk *clk =
> +			container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
> +	unsigned int val;
> +
> +	val = CFGCHIP2_PHYPWRDN;
> +	regmap_write_bits(clk->regmap, CFGCHIP(2), val, val);
> +}
> +
> +static unsigned long usb0_phy_clk_recalc_rate(struct clk_hw *hw,
> +					      unsigned long parent_rate)
> +{
> +	struct da8xx_cfgchip_clk *clk =
> +			container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
> +	unsigned int mask, val;
> +
> +	/* The parent clock rate must be one of the following */
> +	mask = CFGCHIP2_REFFREQ_MASK;
> +	switch (parent_rate) {
> +	case 12000000:
> +		val = CFGCHIP2_REFFREQ_12MHZ;
> +		break;
> +	case 13000000:
> +		val = CFGCHIP2_REFFREQ_13MHZ;
> +		break;
> +	case 19200000:
> +		val = CFGCHIP2_REFFREQ_19_2MHZ;
> +		break;
> +	case 20000000:
> +		val = CFGCHIP2_REFFREQ_20MHZ;
> +		break;
> +	case 24000000:
> +		val = CFGCHIP2_REFFREQ_24MHZ;
> +		break;
> +	case 26000000:
> +		val = CFGCHIP2_REFFREQ_26MHZ;
> +		break;
> +	case 38400000:
> +		val = CFGCHIP2_REFFREQ_38_4MHZ;
> +		break;
> +	case 40000000:
> +		val = CFGCHIP2_REFFREQ_40MHZ;
> +		break;
> +	case 48000000:
> +		val = CFGCHIP2_REFFREQ_48MHZ;
> +		break;
> +	default:
> +		return 0;
> +	}
> +
> +	regmap_write_bits(clk->regmap, CFGCHIP(2), mask, val);
> +
> +	/* USB 2.0 PLL always supplies 48MHz */
> +	return 48000000;
> +}
> +
> +static long usb0_phy_clk_round_rate(struct clk_hw *hw, unsigned long rate,
> +				    unsigned long *parent_rate)
> +{
> +	return 48000000;
> +}
> +
> +static int usb0_phy_clk_set_parent(struct clk_hw *hw, u8 index)
> +{
> +	struct da8xx_cfgchip_clk *clk =
> +			container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
> +	unsigned int mask, val;
> +
> +	/* Set the mux depending on the parent clock. */
> +	mask = CFGCHIP2_USB2PHYCLKMUX;
> +	switch (index) {
> +	case USB20_PHY_CLK_PARENT_USB_REFCLKIN:
> +		val = 0;
> +		break;
> +	case USB20_PHY_CLK_PARENT_PLL0_AUX:
> +		val = CFGCHIP2_USB2PHYCLKMUX;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	regmap_write_bits(clk->regmap, CFGCHIP(2), mask, val);
> +
> +	return 0;
> +}
> +
> +static u8 usb0_phy_clk_get_parent(struct clk_hw *hw)
> +{
> +	struct da8xx_cfgchip_clk *clk =
> +			container_of(hw, struct da8xx_cfgchip_clk, usb0_hw);
> +	unsigned int val;
> +
> +	regmap_read(clk->regmap, CFGCHIP(2), &val);
> +
> +	if (val & CFGCHIP2_USB2PHYCLKMUX)
> +		return USB20_PHY_CLK_PARENT_PLL0_AUX;
> +
> +	return USB20_PHY_CLK_PARENT_USB_REFCLKIN;
> +}
> +
> +static const struct clk_ops usb0_phy_clk_ops = {
> +	.prepare	= usb0_phy_clk_prepare,
> +	.unprepare	= usb0_phy_clk_unprepare,
> +	.enable		= usb0_phy_clk_enable,
> +	.disable	= usb0_phy_clk_disable,
> +	.recalc_rate	= usb0_phy_clk_recalc_rate,
> +	.round_rate	= usb0_phy_clk_round_rate,
> +	.set_parent	= usb0_phy_clk_set_parent,
> +	.get_parent	= usb0_phy_clk_get_parent,
> +};
> +
> +static const char * const usb0_phy_clk_parent_names[] = {
> +	[USB20_PHY_CLK_PARENT_USB_REFCLKIN]	= "usb_refclkin",
> +	[USB20_PHY_CLK_PARENT_PLL0_AUX]		= "pll0_aux_clk",
> +};
> +
> +static const struct clk_init_data usb0_phy_clk_init_data = {
> +	.name		= "usb0_phy_clk",
> +	.ops		= &usb0_phy_clk_ops,
> +	.parent_names	= usb0_phy_clk_parent_names,
> +	.num_parents	= ARRAY_SIZE(usb0_phy_clk_parent_names),
> +};
> +
> +/* --- USB 1.1 PHY clock --- */
> +
> +static int usb1_phy_clk_set_parent(struct clk_hw *hw, u8 index)
> +{
> +	struct da8xx_cfgchip_clk *clk =
> +			container_of(hw, struct da8xx_cfgchip_clk, usb1_hw);
> +	unsigned int mask, val;
> +
> +	/* Set the USB 1.1 PHY clock mux based on the parent clock. */
> +	mask = CFGCHIP2_USB1PHYCLKMUX;
> +	switch (index) {
> +	case USB1_PHY_CLK_PARENT_USB_REFCLKIN:
> +		val = CFGCHIP2_USB1PHYCLKMUX;
> +		break;
> +	case USB1_PHY_CLK_PARENT_USB0_PHY_PLL:
> +		val = 0;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	regmap_write_bits(clk->regmap, CFGCHIP(2), mask, val);
> +
> +	return 0;
> +}
> +
> +static u8 usb1_phy_clk_get_parent(struct clk_hw *hw)
> +{
> +	struct da8xx_cfgchip_clk *clk =
> +			container_of(hw, struct da8xx_cfgchip_clk, usb1_hw);
> +	unsigned int val;
> +
> +	regmap_read(clk->regmap, CFGCHIP(2), &val);
> +
> +	if (val & CFGCHIP2_USB1PHYCLKMUX)
> +		return USB1_PHY_CLK_PARENT_USB_REFCLKIN;
> +
> +	return USB1_PHY_CLK_PARENT_USB0_PHY_PLL;
> +}
> +
> +static const struct clk_ops usb1_phy_clk_ops = {
> +	.set_parent	= usb1_phy_clk_set_parent,
> +	.get_parent	= usb1_phy_clk_get_parent,
> +};
> +
> +static const char * const usb1_phy_clk_parent_names[] = {
> +	[USB1_PHY_CLK_PARENT_USB_REFCLKIN]	= "usb_refclkin",
> +	[USB1_PHY_CLK_PARENT_USB0_PHY_PLL]	= "usb0_phy_clk",
> +};
> +
> +static struct clk_init_data usb1_phy_clk_init_data = {
> +	.name		= "usb1_phy_clk",
> +	.ops		= &usb1_phy_clk_ops,
> +	.parent_names	= usb1_phy_clk_parent_names,
> +	.num_parents	= ARRAY_SIZE(usb1_phy_clk_parent_names),
> +};
> +
> +/* --- platform driver --- */
> +
> +static int da8xx_cfgchip_clk_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct da8xx_cfgchip_clk_data *pdata = dev->platform_data;
> +	struct da8xx_cfgchip_clk *phy_clk;
> +	const char *parent_name;
> +	struct clk *parent;
> +	int ret;
> +
> +	if (!pdata)
> +		return -EINVAL;
> +
> +	phy_clk = devm_kzalloc(dev, sizeof(*phy_clk), GFP_KERNEL);
> +	if (!phy_clk)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, phy_clk);
> +
> +	phy_clk->regmap = syscon_regmap_lookup_by_pdevname("syscon");
> +	if (IS_ERR(phy_clk->regmap)) {
> +		dev_err(dev, "Failed to get syscon\n");
> +		return PTR_ERR(phy_clk->regmap);
> +	}
> +
> +	/* USB 2.0 subsystem PSC clock - needed to lock PLL */
> +	phy_clk->usb0_clk = clk_get(dev, "usb20");
> +	if (IS_ERR(phy_clk->usb0_clk)) {
> +		dev_err(dev, "Failed to get usb20 clock\n");
> +		return PTR_ERR(phy_clk->usb0_clk);
> +	}
> +
> +	phy_clk->usb0_hw.init = &usb0_phy_clk_init_data;
> +	ret = devm_clk_hw_register(dev, &phy_clk->usb0_hw);
> +	if (ret) {
> +		dev_err(dev, "Failed to register usb0_phy_clk\n");
> +		return ret;
> +	}
> +
> +	phy_clk->usb1_hw.init = &usb1_phy_clk_init_data;
> +	ret = devm_clk_hw_register(dev, &phy_clk->usb1_hw);
> +	if (ret) {
> +		dev_err(dev, "Failed to register usb1_phy_clk\n");
> +		return ret;
> +	}
> +
> +	parent_name = pdata->usb0_use_refclkin ? "usb_refclkin" : "pll0_aux";
> +	parent = devm_clk_get(dev, parent_name);
> +	if (IS_ERR(parent)) {
> +		dev_err(dev, "Failed to get usb0 parent clock %s\n",
> +			parent_name);
> +		return PTR_ERR(parent);
> +	}
> +
> +	ret = clk_set_parent(phy_clk->usb0_hw.clk, parent);
> +	if (ret) {
> +		dev_err(dev, "Failed to set usb0 parent clock to %s\n",
> +			parent_name);
> +		return ret;
> +	}
> +
> +	clk_hw_register_clkdev(&phy_clk->usb0_hw, NULL, "da8xx-cfgchip-clk");
> +
> +	parent_name = pdata->usb1_use_refclkin ? "usb_refclkin" : "usb0_phy_clk";
> +	parent = devm_clk_get(dev, parent_name);
> +	if (IS_ERR(parent)) {
> +		dev_err(dev, "Failed to get usb1 parent clock %s\n",
> +			parent_name);
> +		return PTR_ERR(parent);
> +	}
> +
> +	ret = clk_set_parent(phy_clk->usb1_hw.clk, parent);
> +	if (ret) {
> +		dev_err(dev, "Failed to set usb1 parent clock to %s\n",
> +			parent_name);
> +		return ret;
> +	}
> +
> +	clk_hw_register_clkdev(&phy_clk->usb0_hw, "usb20_phy", "da8xx-usb-phy");
> +	clk_hw_register_clkdev(&phy_clk->usb1_hw, "usb11_phy", "da8xx-usb-phy");
> +
> +	return 0;
> +}
> +
> +static struct platform_driver da8xx_cfgchip_clk_driver = {
> +	.probe = da8xx_cfgchip_clk_probe,
> +	.driver = {
> +		.name = "da8xx-cfgchip-clk",
> +	},
> +};
> +module_platform_driver(da8xx_cfgchip_clk_driver);
> +
> +MODULE_ALIAS("platform:da8xx-cfgchip-clk");
> +MODULE_AUTHOR("David Lechner <david@...hnology.com>");
> +MODULE_DESCRIPTION("TI DA8xx CFGCHIP clock driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/clk/davinci/pll.c b/drivers/clk/davinci/pll.c
> new file mode 100644
> index 0000000..035cd91
> --- /dev/null
> +++ b/drivers/clk/davinci/pll.c
> @@ -0,0 +1,333 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * PLL clock driver for Davinci devices
> + *
> + * Copyright (C) 2017 David Lechner <david@...hnology.com>
> + *
> + * Based on drivers/clk/keystone/pll.c
> + * Copyright (C) 2013 Texas Instruments Inc.
> + *	Murali Karicheri <m-karicheri2@...com>
> + *	Santosh Shilimkar <santosh.shilimkar@...com>
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +
> +#define REVID		0x000
> +#define PLLCTL		0x100
> +#define OCSEL		0x104
> +#define PLLSECCTL	0x108
> +#define PLLM		0x110
> +#define PREDIV		0x114
> +#define PLLDIV1		0x118
> +#define PLLDIV2		0x11c
> +#define PLLDIV3		0x120
> +#define OSCDIV		0x124
> +#define POSTDIV		0x128
> +#define BPDIV		0x12c
> +#define PLLCMD		0x138
> +#define PLLSTAT		0x13c
> +#define ALNCTL		0x140
> +#define DCHANGE		0x144
> +#define CKEN		0x148
> +#define CKSTAT		0x14c
> +#define SYSTAT		0x150
> +#define PLLDIV4		0x160
> +#define PLLDIV5		0x164
> +#define PLLDIV6		0x168
> +#define PLLDIV7		0x16c
> +#define PLLDIV8		0x170
> +#define PLLDIV9		0x174
> +
> +#define PLLM_MASK		0x1f
> +#define PREDIV_RATIO_MASK	0x1f
> +#define PLLDIV_RATIO_WIDTH	5
> +#define PLLDIV_ENABLE_SHIFT	15
> +#define OSCDIV_RATIO_WIDTH	5
> +#define POSTDIV_RATIO_MASK	0x1f
> +#define BPDIV_RATIO_SHIFT	0
> +#define BPDIV_RATIO_WIDTH	5
> +#define CKEN_OBSCLK_SHIFT	1
> +#define CKEN_AUXEN_SHIFT	0
> +
> +/**
> + * struct davinci_pll_clk - Main PLL clock
> + * @hw: clk_hw for the pll
> + * @base: Base memory address
> + * @parent_rate: Saved parent rate used by some child clocks
> + */
> +struct davinci_pll_clk {
> +	struct clk_hw hw;
> +	void __iomem *base;
> +};
> +
> +#define to_davinci_pll_clk(_hw) container_of((_hw), struct davinci_pll_clk, hw)
> +
> +static unsigned long davinci_pll_clk_recalc(struct clk_hw *hw,
> +					    unsigned long parent_rate)
> +{
> +	struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
> +	unsigned long rate = parent_rate;
> +	u32 prediv, mult, postdiv;
> +
> +	prediv = readl(pll->base + PREDIV) & PREDIV_RATIO_MASK;
> +	mult = readl(pll->base + PLLM) & PLLM_MASK;
> +	postdiv = readl(pll->base + POSTDIV) & POSTDIV_RATIO_MASK;
> +
> +	rate /= prediv + 1;
> +	rate *= mult + 1;
> +	rate /= postdiv + 1;
> +
> +	return rate;
> +}
> +
> +#ifdef CONFIG_DEBUG_FS
> +#include <linux/debugfs.h>
> +
> +#define DEBUG_REG(n)	\
> +{			\
> +	.name	= #n,	\
> +	.offset	= n,	\
> +}
> +
> +static const struct debugfs_reg32 davinci_pll_regs[] = {
> +	DEBUG_REG(REVID),
> +	DEBUG_REG(PLLCTL),
> +	DEBUG_REG(OCSEL),
> +	DEBUG_REG(PLLSECCTL),
> +	DEBUG_REG(PLLM),
> +	DEBUG_REG(PREDIV),
> +	DEBUG_REG(PLLDIV1),
> +	DEBUG_REG(PLLDIV2),
> +	DEBUG_REG(PLLDIV3),
> +	DEBUG_REG(OSCDIV),
> +	DEBUG_REG(POSTDIV),
> +	DEBUG_REG(BPDIV),
> +	DEBUG_REG(PLLCMD),
> +	DEBUG_REG(PLLSTAT),
> +	DEBUG_REG(ALNCTL),
> +	DEBUG_REG(DCHANGE),
> +	DEBUG_REG(CKEN),
> +	DEBUG_REG(CKSTAT),
> +	DEBUG_REG(SYSTAT),
> +	DEBUG_REG(PLLDIV4),
> +	DEBUG_REG(PLLDIV5),
> +	DEBUG_REG(PLLDIV6),
> +	DEBUG_REG(PLLDIV7),
> +	DEBUG_REG(PLLDIV8),
> +	DEBUG_REG(PLLDIV9),
> +};
> +
> +static int davinci_pll_debug_init(struct clk_hw *hw, struct dentry *dentry)
> +{
> +	struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
> +	struct debugfs_regset32 *regset;
> +	struct dentry *d;
> +
> +	regset = kzalloc(sizeof(regset), GFP_KERNEL);
> +	if (!regset)
> +		return -ENOMEM;
> +
> +	regset->regs = davinci_pll_regs;
> +	regset->nregs = ARRAY_SIZE(davinci_pll_regs);
> +	regset->base = pll->base;
> +
> +	d = debugfs_create_regset32("registers", 0400, dentry, regset);
> +	if (IS_ERR(d)) {
> +		kfree(regset);
> +		return PTR_ERR(d);
> +	}
> +
> +	return 0;
> +}
> +#else
> +#define davinci_pll_debug_init NULL
> +#endif
> +
> +static const struct clk_ops davinci_pll_clk_ops = {
> +	.recalc_rate	= davinci_pll_clk_recalc,
> +	.debug_init	= davinci_pll_debug_init,
> +};
> +
> +/**
> + * davinci_pll_clk_register - Register a PLL clock
> + * @name: The clock name
> + * @parent_name: The parent clock name
> + * @base: The PLL's memory region
> + */
> +struct clk *davinci_pll_clk_register(const char *name,
> +				     const char *parent_name,
> +				     void __iomem *base)
> +{
> +	struct clk_init_data init;
> +	struct davinci_pll_clk *pll;
> +	struct clk *clk;
> +
> +	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
> +	if (!pll)
> +		return ERR_PTR(-ENOMEM);
> +
> +	init.name = name;
> +	init.ops = &davinci_pll_clk_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);
> +	init.num_parents = (parent_name ? 1 : 0);
> +
> +	pll->base = base;
> +	pll->hw.init = &init;
> +
> +	clk = clk_register(NULL, &pll->hw);
> +	if (IS_ERR(clk))
> +		kfree(pll);
> +
> +	return clk;
> +}
> +
> +struct davinci_pll_aux_clk {
> +	struct clk_hw hw;
> +	struct davinci_pll_clk *pll;
> +};
> +
> +/**
> + * davinci_pll_aux_clk_register - Register bypass clock (AUXCLK)
> + * @name: The clock name
> + * @parent_name: The parent clock name (usually "ref_clk" since this bypasses
> + *               the PLL)
> + * @base: The PLL memory region
> + */
> +struct clk *davinci_pll_aux_clk_register(const char *name,
> +					 const char *parent_name,
> +					 void __iomem *base)
> +{
> +	return clk_register_gate(NULL, name, parent_name, 0, base + CKEN,
> +				 CKEN_AUXEN_SHIFT, 0, NULL);
> +}
> +
> +/**
> + * davinci_pll_bpdiv_clk_register - Register bypass divider clock (SYSCLKBP)
> + * @name: The clock name
> + * @parent_name: The parent clock name (usually "ref_clk" since this bypasses
> + *               the PLL)
> + * @base: The PLL memory region
> + */
> +struct clk *davinci_pll_bpdiv_clk_register(const char *name,
> +					   const char *parent_name,
> +					   void __iomem *base)
> +{
> +	return clk_register_divider(NULL, name, parent_name, 0, base + BPDIV,
> +				    BPDIV_RATIO_SHIFT, BPDIV_RATIO_WIDTH,
> +				    CLK_DIVIDER_READ_ONLY, NULL);
> +}
> +
> +/**
> + * davinci_pll_obs_clk_register - Register oscillator divider clock (OBSCLK)
> + * @name: The clock name
> + * @parent_names: The parent clock names
> + * @num_parents: The number of paren clocks
> + * @base: The PLL memory region
> + * @table: A table of values cooresponding to the parent clocks (see OCSEL
> + *         register in SRM for values)
> + */
> +struct clk *davinci_pll_obs_clk_register(const char *name,
> +					 const char * const *parent_names,
> +					 u8 num_parents,
> +					 void __iomem *base,
> +					 u32 *table)
> +{
> +	struct clk_mux *mux;
> +	struct clk_gate *gate;
> +	struct clk_divider *divider;
> +	struct clk *clk;
> +
> +	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
> +	if (!mux)
> +		return ERR_PTR(-ENOMEM);
> +
> +	mux->reg = base + OCSEL;
> +	mux->table = table;
> +
> +	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> +	if (!gate) {
> +		kfree(mux);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	gate->reg = base + CKEN;
> +	gate->bit_idx = CKEN_OBSCLK_SHIFT;
> +
> +	divider = kzalloc(sizeof(*divider), GFP_KERNEL);
> +	if (!divider) {
> +		kfree(gate);
> +		kfree(mux);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	divider->reg = base + OSCDIV;
> +	divider->width = OSCDIV_RATIO_WIDTH;
> +
> +	clk = clk_register_composite(NULL, name, parent_names, num_parents,
> +				     &mux->hw, &clk_mux_ops,
> +				     &divider->hw, &clk_divider_ops,
> +				     &gate->hw, &clk_gate_ops, 0);
> +	if (IS_ERR(clk)) {
> +		kfree(divider);
> +		kfree(gate);
> +		kfree(mux);
> +	}
> +
> +	return clk;
> +}
> +
> +/**
> + * davinci_pll_div_clk_register - Register a PLLDIV (SYSCLK) clock
> + * @name: The clock name
> + * @parent_name: The parent clock name
> + * @base: The PLL memory region
> + * @id: The id of the divider (n in PLLDIVn)
> + */
> +struct clk *davinci_pll_div_clk_register(const char *name,
> +					 const char *parent_name,
> +					 void __iomem *base,
> +					 u32 id)
> +{
> +	const char * const *parent_names = (parent_name ? &parent_name : NULL);
> +	int num_parents = (parent_name ? 1 : 0);
> +	struct clk_gate *gate;
> +	struct clk_divider *divider;
> +	struct clk *clk;
> +	u32 reg;
> +
> +	/* PLLDIVn registers are not entirely consecutive */
> +	if (id < 4)
> +		reg = PLLDIV1 + 4 * (id - 1);
> +	else
> +		reg = PLLDIV4 + 4 * (id - 4);
> +
> +	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> +	if (!gate)
> +		return ERR_PTR(-ENOMEM);
> +
> +	gate->reg = base + reg;
> +	gate->bit_idx = PLLDIV_ENABLE_SHIFT;
> +
> +	divider = kzalloc(sizeof(*divider), GFP_KERNEL);
> +	if (!divider) {
> +		kfree(gate);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	divider->reg = base + reg;
> +	divider->width = PLLDIV_RATIO_WIDTH;
> +	divider->flags = CLK_DIVIDER_READ_ONLY;
> +
> +	clk = clk_register_composite(NULL, name, parent_names, num_parents,
> +				     NULL, NULL, &divider->hw, &clk_divider_ops,
> +				     &gate->hw, &clk_gate_ops, 0);
> +	if (IS_ERR(clk)) {
> +		kfree(divider);
> +		kfree(gate);
> +	}
> +
> +	return clk;
> +}
> diff --git a/drivers/clk/davinci/psc.c b/drivers/clk/davinci/psc.c
> new file mode 100644
> index 0000000..8ae85ee
> --- /dev/null
> +++ b/drivers/clk/davinci/psc.c
> @@ -0,0 +1,217 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Clock driver for DA8xx/AM17xx/AM18xx/OMAP-L13x PSC controllers
> + *
> + * Copyright (C) 2017 David Lechner <david@...hnology.com>
> + *
> + * Based on: drivers/clk/keystone/gate.c
> + * Copyright (C) 2013 Texas Instruments.
> + *	Murali Karicheri <m-karicheri2@...com>
> + *	Santosh Shilimkar <santosh.shilimkar@...com>
> + *
> + * And: arch/arm/mach-davinci/psc.c
> + * Copyright (C) 2006 Texas Instruments.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +
> +/* PSC register offsets */
> +#define EPCPR			0x070
> +#define PTCMD			0x120
> +#define PTSTAT			0x128
> +#define PDSTAT			0x200
> +#define PDCTL			0x300
> +#define MDSTAT			0x800
> +#define MDCTL			0xa00
> +
> +/* PSC module states */
> +enum davinci_psc_state {
> +	PSC_STATE_SWRSTDISABLE	= 0,
> +	PSC_STATE_SYNCRST	= 1,
> +	PSC_STATE_DISABLE	= 2,
> +	PSC_STATE_ENABLE	= 3,
> +};
> +
> +#define MDSTAT_STATE_MASK	0x3f
> +#define MDSTAT_MCKOUT		BIT(12)
> +#define PDSTAT_STATE_MASK	0x1f
> +#define MDCTL_FORCE		BIT(31)
> +#define MDCTL_LRESET		BIT(8)
> +#define PDCTL_EPCGOOD		BIT(8)
> +#define PDCTL_NEXT		BIT(0)
> +
> +/**
> + * struct davinci_psc_clk - PSC clock structure
> + * @hw: clk_hw for the psc
> + * @psc_data: PSC driver specific data
> + * @lpsc: Local PSC number (module id)
> + * @pd: Power domain
> + */
> +struct davinci_psc_clk {
> +	struct clk_hw hw;
> +	void __iomem *base;
> +	u32 lpsc;
> +	u32 pd;
> +};
> +
> +#define to_davinci_psc_clk(_hw) container_of(_hw, struct davinci_psc_clk, hw)
> +
> +static void psc_config(struct davinci_psc_clk *psc,
> +		       enum davinci_psc_state next_state)
> +{
> +	u32 epcpr, ptcmd, pdstat, pdctl, mdstat, mdctl, ptstat;
> +
> +	mdctl = readl(psc->base + MDCTL + 4 * psc->lpsc);
> +	mdctl &= ~MDSTAT_STATE_MASK;
> +	mdctl |= next_state;
> +	/* TODO: old davinci clocks for da850 set MDCTL_FORCE bit for sata and
> +	 * dsp here. Is this really needed?
> +	 */
> +	writel(mdctl, psc->base + MDCTL + 4 * psc->lpsc);
> +
> +	pdstat = readl(psc->base + PDSTAT + 4 * psc->pd);
> +	if ((pdstat & PDSTAT_STATE_MASK) == 0) {
> +		pdctl = readl(psc->base + PDSTAT + 4 * psc->pd);
> +		pdctl |= PDCTL_NEXT;
> +		writel(pdctl, psc->base + PDSTAT + 4 * psc->pd);
> +
> +		ptcmd = BIT(psc->pd);
> +		writel(ptcmd, psc->base + PTCMD);
> +
> +		do {
> +			epcpr = __raw_readl(psc->base + EPCPR);
> +		} while (!(epcpr & BIT(psc->pd)));
> +
> +		pdctl = __raw_readl(psc->base + PDCTL + 4 * psc->pd);
> +		pdctl |= PDCTL_EPCGOOD;
> +		__raw_writel(pdctl, psc->base + PDCTL + 4 * psc->pd);
> +	} else {
> +		ptcmd = BIT(psc->pd);
> +		writel(ptcmd, psc->base + PTCMD);
> +	}
> +
> +	do {
> +		ptstat = readl(psc->base + PTSTAT);
> +	} while (ptstat & BIT(psc->pd));
> +
> +	do {
> +		mdstat = readl(psc->base + MDSTAT + 4 * psc->lpsc);
> +	} while (!((mdstat & MDSTAT_STATE_MASK) == next_state));
> +}
> +
> +static int davinci_psc_clk_enable(struct clk_hw *hw)
> +{
> +	struct davinci_psc_clk *psc = to_davinci_psc_clk(hw);
> +
> +	psc_config(psc, PSC_STATE_ENABLE);
> +
> +	return 0;
> +}
> +
> +static void davinci_psc_clk_disable(struct clk_hw *hw)
> +{
> +	struct davinci_psc_clk *psc = to_davinci_psc_clk(hw);
> +
> +	psc_config(psc, PSC_STATE_DISABLE);
> +}
> +
> +static int davinci_psc_clk_is_enabled(struct clk_hw *hw)
> +{
> +	struct davinci_psc_clk *psc = to_davinci_psc_clk(hw);
> +	u32 mdstat;
> +
> +	mdstat = readl(psc->base + MDSTAT + 4 * psc->lpsc);
> +
> +	return (mdstat & MDSTAT_MCKOUT) ? 1 : 0;
> +}
> +
> +static const struct clk_ops davinci_psc_clk_ops = {
> +	.enable		= davinci_psc_clk_enable,
> +	.disable	= davinci_psc_clk_disable,
> +	.is_enabled	= davinci_psc_clk_is_enabled,
> +};
> +
> +/**
> + * davinci_psc_clk_register - register psc clock
> + * @dev: device that is registering this clock
> + * @name: name of this clock
> + * @parent_name: name of clock's parent
> + * @base: memory mapped register for the PSC
> + * @lpsc: local PSC number
> + * @pd: power domain
> + */
> +struct clk *davinci_psc_clk_register(const char *name,
> +				     const char *parent_name,
> +				     void __iomem *base,
> +				     u32 lpsc, u32 pd)
> +{
> +	struct clk_init_data init;
> +	struct davinci_psc_clk *psc;
> +	struct clk *clk;
> +
> +	psc = kzalloc(sizeof(*psc), GFP_KERNEL);
> +	if (!psc)
> +		return ERR_PTR(-ENOMEM);
> +
> +	init.name = name;
> +	init.ops = &davinci_psc_clk_ops;
> +	init.flags = 0;
> +	init.parent_names = (parent_name ? &parent_name : NULL);
> +	init.num_parents = (parent_name ? 1 : 0);
> +
> +	psc->base = base;
> +	psc->hw.init = &init;
> +	psc->lpsc = lpsc;
> +	psc->pd = pd;
> +
> +	clk = clk_register(NULL, &psc->hw);
> +	if (IS_ERR(clk))
> +		kfree(psc);
> +
> +	return clk;
> +}
> +
> +/* FIXME: This needs to be converted to a reset controller. But, the reset
> + * framework is currently device tree only.
> + */
> +
> +DEFINE_SPINLOCK(davinci_psc_reset_lock);
> +
> +static int davinci_psc_clk_reset(struct davinci_psc_clk *psc, bool reset)
> +{
> +	unsigned long flags;
> +	u32 mdctl;
> +
> +	if (IS_ERR_OR_NULL(psc))
> +		return -EINVAL;
> +
> +	spin_lock_irqsave(&davinci_psc_reset_lock, flags);
> +	mdctl = readl(psc->base + MDCTL + 4 * psc->lpsc);
> +	if (reset)
> +		mdctl &= ~MDCTL_LRESET;
> +	else
> +		mdctl |= MDCTL_LRESET;
> +	writel(mdctl, psc->base + MDCTL + 4 * psc->lpsc);
> +	spin_unlock_irqrestore(&davinci_psc_reset_lock, flags);
> +
> +	return 0;
> +}
> +
> +int davinci_clk_reset_assert(struct clk *clk)
> +{
> +	struct davinci_psc_clk *psc = to_davinci_psc_clk(__clk_get_hw(clk));
> +
> +	return davinci_psc_clk_reset(psc, true);
> +}
> +EXPORT_SYMBOL(davinci_clk_reset_assert);
> +
> +int davinci_clk_reset_deassert(struct clk *clk)
> +{
> +	struct davinci_psc_clk *psc = to_davinci_psc_clk(__clk_get_hw(clk));
> +
> +	return davinci_psc_clk_reset(psc, false);
> +}
> +EXPORT_SYMBOL(davinci_clk_reset_deassert);
> diff --git a/include/linux/clk/davinci.h b/include/linux/clk/davinci.h
> new file mode 100644
> index 0000000..c5d2181
> --- /dev/null
> +++ b/include/linux/clk/davinci.h
> @@ -0,0 +1,46 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * TI Davinci clocks
> + *
> + * Copyright (C) 2017 David Lechner <david@...hnology.com>
> + */
> +#ifndef __LINUX_CLK_DAVINCI_H__
> +#define __LINUX_CLK_DAVINCI_H__
> +
> +#include <linux/clk-provider.h>
> +#include <linux/types.h>
> +
> +struct clk *davinci_pll_clk_register(const char *name,
> +				     const char *parent_name,
> +				     void __iomem *base);
> +struct clk *davinci_pll_aux_clk_register(const char *name,
> +					 const char *parent_name,
> +					 void __iomem *base);
> +struct clk *davinci_pll_bpdiv_clk_register(const char *name,
> +					   const char *parent_name,
> +					   void __iomem *base);
> +struct clk *davinci_pll_obs_clk_register(const char *name,
> +					 const char * const *parent_names,
> +					 u8 num_parents,
> +					 void __iomem *base,
> +					 u32 *table);
> +struct clk *davinci_pll_div_clk_register(const char *name,
> +					 const char *parent_name,
> +					 void __iomem *base,
> +					 u32 id);
> +struct clk *davinci_psc_clk_register(const char *name,
> +				     const char *parent_name,
> +				     void __iomem *base,
> +				     u32 lpsc, u32 pd);
> +
> +/* convience macros for board declaration files */
> +#define EXT_CLK(n, r) clk_register_fixed_rate(NULL, (n), NULL, 0, (r))
> +#define FIX_CLK(n, p) clk_register_fixed_factor(NULL, (n), (p), 0, 1, 1)
> +#define PLL_CLK davinci_pll_clk_register
> +#define PLL_DIV_CLK davinci_pll_div_clk_register
> +#define PLL_AUX_CLK davinci_pll_aux_clk_register
> +#define PLL_BP_CLK davinci_pll_bpdiv_clk_register
> +#define PLL_OBS_CLK davinci_pll_obs_clk_register
> +#define PSC_CLK davinci_psc_clk_register
> +
> +#endif /* __LINUX_CLK_DAVINCI_H__ */
> diff --git a/include/linux/platform_data/davinci_clk.h b/include/linux/platform_data/davinci_clk.h
> new file mode 100644
> index 0000000..7576ace
> --- /dev/null
> +++ b/include/linux/platform_data/davinci_clk.h
> @@ -0,0 +1,25 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * TI DaVinci Clock support
> + *
> + * Copyright (C) 2017 David Lechner <david@...hnology.com>
> + */
> +
> +#ifndef __PLATFORM_DATA_DAVINCI_CLK_H
> +#define __PLATFORM_DATA_DAVINCI_CLK_H
> +
> +#include <linux/types.h>
> +
> +/**
> + * da8xx_cfgchip_clk_data - DA8xx CFGCHIP clock platform data
> + * @usb0_use_refclkin: when true, use USB_REFCLKIN, otherwise use AUXCLK for
> + *                     USB 2.0 PHY clock
> + * @usb1_use_refclkin: when true, use USB_REFCLKIN, otherwise use USB 2.0 PHY
> + *                     PLL for USB 1.1 PHY clock
> + */
> +struct da8xx_cfgchip_clk_data {
> +	bool usb0_use_refclkin;
> +	bool usb1_use_refclkin;
> +};
> +
> +#endif /* __PLATFORM_DATA_DAVINCI_CLK_H */
> 

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ