From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Date: Thu, 7 Apr 2016 04:55:42 -0700 (PDT) From: Yassin Jaffer To: linux-sunxi Cc: dev@linux-sunxi.org, robh+dt@kernel.org, pawel.moll@arm.com, mark.rutland@arm.com, ijc+devicetree@hellion.org.uk, galak@codeaurora.org, wens@csie.org, emilio@elopez.com.ar, mturquette@baylibre.com, sboyd@codeaurora.org, hdegoede@redhat.com, patchesrdh@mveas.com, jenskuske@gmail.com, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org Message-Id: In-Reply-To: <20160318192535.GR30977@lukather> References: <1458204222-31149-1-git-send-email-yassinjaffer@gmail.com> <1458204222-31149-2-git-send-email-yassinjaffer@gmail.com> <20160318192535.GR30977@lukather> Subject: Re: [PATCH] clk: sunxi: Add CSI (camera's Sensors Interface) module clock driver for sun[457]i MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_1562_1348376410.1460030142312" List-ID: ------=_Part_1562_1348376410.1460030142312 Content-Type: multipart/alternative; boundary="----=_Part_1563_2068990815.1460030142313" ------=_Part_1563_2068990815.1460030142313 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit Hi Maxime I had a look at your display clock driver.The only issue I have is setting the clock parents. A simple mask wont do it. I guess using table is the only way. The CSI is only clock which is using irregular parents mux. The remaining clocks including VE, can be based on your display clock driver. Thanx. On Monday, March 21, 2016 at 7:42:48 PM UTC+11, Maxime Ripard wrote: > > Hi Yassin, > > On Thu, Mar 17, 2016 at 07:43:42PM +1100, yassin...@gmail.com > wrote: > > From: Yassin Jaffer > > > > > This patch adds a composite clock type consisting of > > a clock gate, mux, configurable dividers, and a reset control. > > > > Signed-off-by: Yassin Jaffer > > > --- > > Documentation/devicetree/bindings/clock/sunxi.txt | 1 + > > drivers/clk/sunxi/Makefile | 1 + > > drivers/clk/sunxi/clk-a10-csi.c | 188 > ++++++++++++++++++++++ > > 3 files changed, 190 insertions(+) > > create mode 100644 drivers/clk/sunxi/clk-a10-csi.c > > > > diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt > b/Documentation/devicetree/bindings/clock/sunxi.txt > > index e59f57b..c3826f7 100644 > > --- a/Documentation/devicetree/bindings/clock/sunxi.txt > > +++ b/Documentation/devicetree/bindings/clock/sunxi.txt > > @@ -77,6 +77,7 @@ Required properties: > > "allwinner,sun9i-a80-usb-mod-clk" - for usb gates + resets on > A80 > > "allwinner,sun9i-a80-usb-phy-clk" - for usb phy gates + resets > on A80 > > "allwinner,sun4i-a10-ve-clk" - for the Video Engine clock > > + "allwinner,sun4i-a10-csi-clk" - for the CSI module > > > > Required properties for all clocks: > > - reg : shall be the control register address for the clock. > > diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile > > index 3fd7901..42ce752 100644 > > --- a/drivers/clk/sunxi/Makefile > > +++ b/drivers/clk/sunxi/Makefile > > @@ -7,6 +7,7 @@ obj-y += clk-a10-codec.o > > obj-y += clk-a10-hosc.o > > obj-y += clk-a10-mod1.o > > obj-y += clk-a10-pll2.o > > +obj-y += clk-a10-csi.o > > obj-y += clk-a10-ve.o > > obj-y += clk-a20-gmac.o > > obj-y += clk-mod0.o > > diff --git a/drivers/clk/sunxi/clk-a10-csi.c > b/drivers/clk/sunxi/clk-a10-csi.c > > new file mode 100644 > > index 0000000..f17d206 > > --- /dev/null > > +++ b/drivers/clk/sunxi/clk-a10-csi.c > > @@ -0,0 +1,188 @@ > > +/* > > + * Copyright 2016 Yassin Jaffer > > + * > > + * Yassin Jaffer > > > + * > > + * This program is free software; you can redistribute it and/or modify > > + * it under the terms of the GNU General Public License as published by > > + * the Free Software Foundation; either version 2 of the License, or > > + * (at your option) any later version. > > + * > > + * 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. > > + */ > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +static DEFINE_SPINLOCK(sun4i_csi_lock); > > + > > +#define SUN4I_CSI_PARENTS 5 > > +#define SUN4I_CSI_GATE_BIT 31 > > +#define SUN4I_CSI_RESET_BIT 30 > > +#define SUN4I_CSI_MUX_SHIFT 24 > > +#define SUN4I_CSI_DIV_WIDTH 5 > > +#define SUN4I_CSI_DIV_SHIFT 0 > > + > > +static u32 sun4i_csi_mux_table[SUN4I_CSI_PARENTS] = { > > + 0x0, > > + 0x1, > > + 0x2, > > + 0x5, > > + 0x6, > > +}; > > + > > +struct csi_reset_data { > > + void __iomem *reg; > > + spinlock_t *lock; /* lock for reset > handling */ > > + struct reset_controller_dev rcdev; > > +}; > > + > > +static int sun4i_csi_assert(struct reset_controller_dev *rcdev, > > + unsigned long id) > > +{ > > + struct csi_reset_data *data = container_of(rcdev, > > + struct > csi_reset_data, > > + rcdev); > > + unsigned long flags; > > + u32 reg; > > + > > + spin_lock_irqsave(data->lock, flags); > > + > > + reg = readl(data->reg); > > + writel(reg & ~BIT(SUN4I_CSI_RESET_BIT), data->reg); > > + > > + spin_unlock_irqrestore(data->lock, flags); > > + > > + return 0; > > +} > > + > > +static int sun4i_csi_deassert(struct reset_controller_dev *rcdev, > > + unsigned long id) > > +{ > > + struct csi_reset_data *data = container_of(rcdev, > > + struct > csi_reset_data, > > + rcdev); > > + unsigned long flags; > > + u32 reg; > > + > > + spin_lock_irqsave(data->lock, flags); > > + > > + reg = readl(data->reg); > > + writel(reg | BIT(SUN4I_CSI_RESET_BIT), data->reg); > > + > > + spin_unlock_irqrestore(data->lock, flags); > > + > > + return 0; > > +} > > + > > +static int sun4i_csi_of_xlate(struct reset_controller_dev *rcdev, > > + const struct of_phandle_args *reset_spec) > > +{ > > + if (WARN_ON(reset_spec->args_count != 0)) > > + return -EINVAL; > > + > > + return 0; > > +} > > + > > +static struct reset_control_ops sun4i_csi_reset_ops = { > > + .assert = sun4i_csi_assert, > > + .deassert = sun4i_csi_deassert, > > +}; > > + > > +static void __init sun4i_csi_clk_setup(struct device_node *node) > > +{ > > + const char *parents[SUN4I_CSI_PARENTS]; > > + const char *clk_name = node->name; > > + struct csi_reset_data *reset_data; > > + struct clk_divider *div; > > + struct clk_gate *gate; > > + struct clk_mux *mux; > > + void __iomem *reg; > > + struct clk *clk; > > + int i = 0; > > + > > + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); > > + if (IS_ERR(reg)) > > + return; > > + > > + of_property_read_string(node, "clock-output-names", &clk_name); > > + > > + i = of_clk_parent_fill(node, parents, SUN4I_CSI_PARENTS); > > + > > + mux = kzalloc(sizeof(*mux), GFP_KERNEL); > > + if (!mux) > > + goto err_unmap; > > + > > + mux->reg = reg; > > + mux->shift = SUN4I_CSI_MUX_SHIFT; > > + mux->table = sun4i_csi_mux_table; > > + mux->lock = &sun4i_csi_lock; > > + > > + gate = kzalloc(sizeof(*gate), GFP_KERNEL); > > + if (!gate) > > + goto err_free_mux; > > + > > + gate->reg = reg; > > + gate->bit_idx = SUN4I_CSI_GATE_BIT; > > + gate->lock = &sun4i_csi_lock; > > + > > + div = kzalloc(sizeof(*div), GFP_KERNEL); > > + if (!div) > > + goto err_free_gate; > > + > > + div->reg = reg; > > + div->shift = SUN4I_CSI_DIV_SHIFT; > > + div->width = SUN4I_CSI_DIV_WIDTH; > > + div->lock = &sun4i_csi_lock; > > + > > + clk = clk_register_composite(NULL, clk_name, > > + parents, i, > > + &mux->hw, &clk_mux_ops, > > + &div->hw, &clk_divider_ops, > > + &gate->hw, &clk_gate_ops, > > + CLK_SET_RATE_PARENT); > > + if (IS_ERR(clk)) > > + goto err_free_div; > > + > > + of_clk_add_provider(node, of_clk_src_simple_get, clk); > > + > > + reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); > > + if (!reset_data) > > + goto err_free_clk; > > + > > + reset_data->reg = reg; > > + reset_data->lock = &sun4i_csi_lock; > > + reset_data->rcdev.nr_resets = 1; > > + reset_data->rcdev.ops = &sun4i_csi_reset_ops; > > + reset_data->rcdev.of_node = node; > > + reset_data->rcdev.of_xlate = sun4i_csi_of_xlate; > > + reset_data->rcdev.of_reset_n_cells = 0; > > + > > + if (reset_controller_register(&reset_data->rcdev)) > > + goto err_free_reset; > > + > > + return; > > + > > +err_free_reset: > > + kfree(reset_data); > > +err_free_clk: > > + clk_unregister(clk); > > +err_free_div: > > + kfree(div); > > +err_free_gate: > > + kfree(gate); > > +err_free_mux: > > + kfree(mux); > > +err_unmap: > > + iounmap(reg); > > +} > > + > > +CLK_OF_DECLARE(sun4i_csi, "allwinner,sun4i-a10-csi-clk", > > + sun4i_csi_clk_setup); > > + > > That's great, but it shares a lot of infrastructure of boilerplate > code with the display clocks that are part of my DRM serie. > > I plan to post them early next week, I'll cc you so that you can base > your clock on top of it. > > Thanks! > Maxime > > -- > Maxime Ripard, Free Electrons > Embedded Linux, Kernel and Android engineering > http://free-electrons.com > ------=_Part_1563_2068990815.1460030142313 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable
Hi Maxime

I had a look at your display clock driver= .The only issue I have is setting the clock parents. A simple mask wont do = it.
I guess using table is the only way. The CSI is only clock which is= using irregular parents mux. The remaining clocks
including VE, can be = based on your display clock driver.
Thanx.

On Monday, March 21, = 2016 at 7:42:48 PM UTC+11, Maxime Ripard wrote:
Hi Yassin,

On Thu, Mar 17, 2016 at 07:43:42PM +1100, yassin...@gmail.com wrote:
> From: Yassin Jaffer <yassin...@gmail.com>
>=20
> This patch adds a composite clock type consisting of
> a clock gate, mux, configurable dividers, and a reset control.
>=20
> Signed-off-by: Yassin Jaffer <yassin...@gmail.com>
> ---
> =C2=A0Documentation/devicetree/bindings/clock/sunxi.txt | =C2= =A0 1 +
> =C2=A0drivers/clk/sunxi/Makefile =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| =C2=A0 1 +
> =C2=A0drivers/clk/sunxi/clk-a10-csi.c =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | 188 ++++++++++++++++++++++
> =C2=A03 files changed, 190 insertions(+)
> =C2=A0create mode 100644 drivers/clk/sunxi/clk-a10-csi.c
>=20
> diff --git a/Documentation/devicetree/bindings/clock/sunxi.tx= t b/Documentation/devicetree/bindings/clock/sunxi.txt
> index e59f57b..c3826f7 100644
> --- a/Documentation/devicetree/bindings/clock/sunxi.txt
> +++ b/Documentation/devicetree/bindings/clock/sunxi.txt
> @@ -77,6 +77,7 @@ Required properties:
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0"allwin= ner,sun9i-a80-usb-mod-clk" - for usb gates + resets on A80
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0"allwin= ner,sun9i-a80-usb-phy-clk" - for usb phy gates + resets on A80
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0"allwin= ner,sun4i-a10-ve-clk" - for the Video Engine clock
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0"allwinner,s= un4i-a10-csi-clk" - for the CSI module
> =C2=A0
> =C2=A0Required properties for all clocks:
> =C2=A0- reg : shall be the control register address for the clock.
> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefi= le
> index 3fd7901..42ce752 100644
> --- a/drivers/clk/sunxi/Makefile
> +++ b/drivers/clk/sunxi/Makefile
> @@ -7,6 +7,7 @@ obj-y +=3D clk-a10-codec.o
> =C2=A0obj-y +=3D clk-a10-hosc.o
> =C2=A0obj-y +=3D clk-a10-mod1.o
> =C2=A0obj-y +=3D clk-a10-pll2.o
> +obj-y +=3D clk-a10-csi.o
> =C2=A0obj-y +=3D clk-a10-ve.o
> =C2=A0obj-y +=3D clk-a20-gmac.o
> =C2=A0obj-y +=3D clk-mod0.o
> diff --git a/drivers/clk/sunxi/clk-a10-csi.c b/drivers/clk/su= nxi/clk-a10-csi.c
> new file mode 100644
> index 0000000..f17d206
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-a10-csi.c
> @@ -0,0 +1,188 @@
> +/*
> + * Copyright 2016 Yassin Jaffer
> + *
> + * Yassin Jaffer <yassin...@gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or = modify
> + * it under the terms of the GNU General Public License as publis= hed by
> + * the Free Software Foundation; either version 2 of the License,= or
> + * (at your option) any later version.
> + *
> + * 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. =C2=A0See= the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/of_address.h>
> +#include <linux/reset-controller.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +
> +static DEFINE_SPINLOCK(sun4i_csi_lock);
> +
> +#define SUN4I_CSI_PARENTS =C2=A0 =C2=A0 =C2=A0 5
> +#define SUN4I_CSI_GATE_BIT =C2=A0 =C2=A0 =C2=A031
> +#define SUN4I_CSI_RESET_BIT =C2=A0 =C2=A0 30
> +#define SUN4I_CSI_MUX_SHIFT =C2=A0 =C2=A0 24
> +#define SUN4I_CSI_DIV_WIDTH =C2=A0 =C2=A0 5
> +#define SUN4I_CSI_DIV_SHIFT =C2=A0 =C2=A0 0
> +
> +static u32 sun4i_csi_mux_table[SUN4I_CSI_PARENTS] =3D {
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A00x0,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A00x1,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A00x2,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A00x5,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A00x6,
> +};
> +
> +struct csi_reset_data {
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0void __iomem=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0*reg= ;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0spinlock_t=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0*lock;= /* lock for reset handling */
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0struct reset_cont= roller_dev=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0rcdev;
> +};
> +
> +static int sun4i_csi_assert(struct reset_controller_dev *rcdev,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0 =C2=A0 =C2=A0unsigned long id)
> +{
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0struct csi_reset_= data *data =3D container_of(rcdev,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0 =C2=A0struct csi_reset_data,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0 =C2=A0rcdev);
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0unsigned long fla= gs;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0u32 reg;
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0spin_lock_irqsave= (data->lock, flags);
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0reg =3D readl(dat= a->reg);
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0writel(reg & = ~BIT(SUN4I_CSI_RESET_BIT), data->reg);
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0spin_unlock_= irqrestore(data->lock, flags);
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return 0;
> +}
> +
> +static int sun4i_csi_deassert(struct reset_controller_dev *rcdev,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned long id)
> +{
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0struct csi_reset_= data *data =3D container_of(rcdev,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0 =C2=A0struct csi_reset_data,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0 =C2=A0rcdev);
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0unsigned long fla= gs;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0u32 reg;
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0spin_lock_irqsave= (data->lock, flags);
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0reg =3D readl(dat= a->reg);
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0writel(reg | BIT(= SUN4I_CSI_RESET_BIT), data->reg);
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0spin_unlock_= irqrestore(data->lock, flags);
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return 0;
> +}
> +
> +static int sun4i_csi_of_xlate(struct reset_controller_dev *rcdev,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0const struct of_phandle_args *reset_spec)
> +{
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (WARN_ON(reset= _spec->args_count !=3D 0))
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return -EINVAL;
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return 0;
> +}
> +
> +static struct reset_control_ops sun4i_csi_reset_ops =3D {
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0.assert=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=3D sun4i_csi_assert,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0.deassert=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=3D sun4i_csi_deassert,
> +};
> +
> +static void __init sun4i_csi_clk_setup(struct device_node *node)
> +{
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0const char *paren= ts[SUN4I_CSI_PARENTS];
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0const char *clk_n= ame =3D node->name;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0struct csi_reset_= data *reset_data;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0struct clk_divide= r *div;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0struct clk_gate *= gate;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0struct clk_mux *m= ux;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0void __iomem *reg= ;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0struct clk *clk;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0int i =3D 0;
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0reg =3D of_io_req= uest_and_map(node, 0, of_node_full_name(node));
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (IS_ERR(reg))
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return;
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0of_property_read_= string(node, "clock-output-names", &clk_name);
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0i =3D of_clk_pare= nt_fill(node, parents, SUN4I_CSI_PARENTS);
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0mux =3D kzalloc(s= izeof(*mux), GFP_KERNEL);
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (!mux)
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0goto err_unmap;
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0mux->reg =3D r= eg;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0mux->shift =3D= SUN4I_CSI_MUX_SHIFT;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0mux->table =3D= sun4i_csi_mux_table;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0mux->lock =3D = &sun4i_csi_lock;
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0gate =3D kzalloc(= sizeof(*gate), GFP_KERNEL);
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (!gate)
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0goto err_free_mux;
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0gate->reg =3D = reg;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0gate->bit_idx = =3D SUN4I_CSI_GATE_BIT;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0gate->lock =3D= &sun4i_csi_lock;
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0div =3D kzalloc(s= izeof(*div), GFP_KERNEL);
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (!div)
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0goto err_free_gate;
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0div->reg =3D r= eg;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0div->shift =3D= SUN4I_CSI_DIV_SHIFT;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0div->width =3D= SUN4I_CSI_DIV_WIDTH;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0div->lock =3D = &sun4i_csi_lock;
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0clk =3D clk_regis= ter_composite(NULL, clk_name,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0 =C2=A0 p= arents, i,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0 =C2=A0 &= amp;mux->hw, &clk_mux_ops,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0 =C2=A0 &= amp;div->hw, &clk_divider_ops,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0 =C2=A0 &= amp;gate->hw, &clk_gate_ops,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0 =C2=A0 C= LK_SET_RATE_PARENT);
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (IS_ERR(clk))
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0goto err_free_div;
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0of_clk_add_provid= er(node, of_clk_src_simple_get, clk);
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0reset_data =3D kz= alloc(sizeof(*reset_data), GFP_KERNEL);
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (!reset_data)
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0goto err_free_clk;
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0reset_data->re= g =3D reg;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0reset_data->lo= ck =3D &sun4i_csi_lock;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0reset_data->rc= dev.nr_resets =3D 1;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0reset_data->rc= dev.ops =3D &sun4i_csi_reset_ops;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0reset_data->rc= dev.of_node =3D node;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0reset_data->rc= dev.of_xlate =3D sun4i_csi_of_xlate;
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0reset_data->rc= dev.of_reset_n_cells =3D 0;
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (reset_control= ler_register(&reset_data->rcdev))
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0goto err_free_reset;
> +
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return;
> +
> +err_free_reset:
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0kfree(reset_data)= ;
> +err_free_clk:
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0clk_unregister(cl= k);
> +err_free_div:
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0kfree(div);
> +err_free_gate:
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0kfree(gate);
> +err_free_mux:
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0kfree(mux);
> +err_unmap:
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0iounmap(reg);
> +}
> +
> +CLK_OF_DECLARE(sun4i_csi, "allwinner,sun4i-a10-csi-clk"= ,
> +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 =C2=A0 =C2=A0 = =C2=A0 sun4i_csi_clk_setup);
> +

That's great, but it shares a lot of infrastructure of boilerplate
code with the display clocks that are part of my DRM serie.

I plan to post them early next week, I'll cc you so that you can ba= se
your clock on top of it.

Thanks!
Maxime

--=20
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.c= om
------=_Part_1563_2068990815.1460030142313-- ------=_Part_1562_1348376410.1460030142312--