Quoting Sven Peter (2021-05-24 11:27:44) > Add a simple driver for gate clocks found on Apple SoCs. These don't > have any frequency associated with them and are only used to enable > access to MMIO regions of various peripherals. Can we figure out what frequency they are clocking at? > > Signed-off-by: Sven Peter <sven@xxxxxxxxxxxxx> > diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig > index e80918be8e9c..ac987a8cf318 100644 > --- a/drivers/clk/Kconfig > +++ b/drivers/clk/Kconfig > @@ -245,6 +245,18 @@ config CLK_TWL6040 > McPDM. McPDM module is using the external bit clock on the McPDM bus > as functional clock. > > +config COMMON_CLK_APPLE > + tristate "Clock driver for Apple platforms" > + depends on ARCH_APPLE && COMMON_CLK The COMMON_CLK depend is redundant. Please remove. > + default ARCH_APPLE > + help > + Support for clock gates on Apple SoCs such as the M1. > + > + These clock gates do not have a frequency associated with them and > + are only used to power on/off various peripherals. Generally, a clock > + gate needs to be enabled before the respective MMIO region can be > + accessed. > + > config COMMON_CLK_AXI_CLKGEN > tristate "AXI clkgen driver" > depends on HAS_IOMEM || COMPILE_TEST > diff --git a/drivers/clk/clk-apple-gate.c b/drivers/clk/clk-apple-gate.c > new file mode 100644 > index 000000000000..799e9269758f > --- /dev/null > +++ b/drivers/clk/clk-apple-gate.c > @@ -0,0 +1,152 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Apple SoC clock/power gating driver > + * > + * Copyright The Asahi Linux Contributors > + */ > + > +#include <linux/clk.h> Hopefully this can be droped. > +#include <linux/clkdev.h> And this one too. clkdev is not modern. > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/iopoll.h> > +#include <linux/clk-provider.h> > +#include <linux/of.h> > +#include <linux/of_address.h> > +#include <linux/platform_device.h> > +#include <linux/module.h> > + > +#define CLOCK_TARGET_MODE_MASK 0x0f APPLE_ prefix on all these? > +#define CLOCK_TARGET_MODE(m) (((m)&0xf)) > +#define CLOCK_ACTUAL_MODE_MASK 0xf0 > +#define CLOCK_ACTUAL_MODE(m) (((m)&0xf) << 4) > + > +#define CLOCK_MODE_ENABLE 0xf > +#define CLOCK_MODE_DISABLE 0 > + > +#define CLOCK_ENDISABLE_TIMEOUT 100 > + > +struct apple_clk_gate { > + struct clk_hw hw; > + void __iomem *reg; > +}; > + > +#define to_apple_clk_gate(_hw) container_of(_hw, struct apple_clk_gate, hw) > + > +static int apple_clk_gate_endisable(struct clk_hw *hw, int enable) > +{ > + struct apple_clk_gate *gate = to_apple_clk_gate(hw); > + u32 reg; > + u32 mode; > + > + if (enable) > + mode = CLOCK_MODE_ENABLE; > + else > + mode = CLOCK_MODE_DISABLE; > + > + reg = readl(gate->reg); > + reg &= ~CLOCK_TARGET_MODE_MASK; > + reg |= CLOCK_TARGET_MODE(mode); > + writel(reg, gate->reg); > + > + return readl_poll_timeout_atomic(gate->reg, reg, > + (reg & CLOCK_ACTUAL_MODE_MASK) == > + CLOCK_ACTUAL_MODE(mode), > + 1, CLOCK_ENDISABLE_TIMEOUT); Maybe this return readl_poll_timeout_atomic(gate->reg, reg, (reg & CLOCK_ACTUAL_MODE_MASK) == CLOCK_ACTUAL_MODE(mode), 1, CLOCK_ENDISABLE_TIMEOUT); at the least please don't break the == across two lines. > +} > + > +static int apple_clk_gate_enable(struct clk_hw *hw) > +{ > + return apple_clk_gate_endisable(hw, 1); > +} > + > +static void apple_clk_gate_disable(struct clk_hw *hw) > +{ > + apple_clk_gate_endisable(hw, 0); > +} > + > +static int apple_clk_gate_is_enabled(struct clk_hw *hw) > +{ > + struct apple_clk_gate *gate = to_apple_clk_gate(hw); > + u32 reg; > + > + reg = readl(gate->reg); > + > + if ((reg & CLOCK_ACTUAL_MODE_MASK) == CLOCK_ACTUAL_MODE(CLOCK_MODE_ENABLE)) Any chance we can use FIELD_GET() and friends? Please don't do bit shifting stuff inside conditionals as it just makes life more complicated than it needs to be. > + return 1; > + return 0; How about just return <logic>? > +} > + > +static const struct clk_ops apple_clk_gate_ops = { > + .enable = apple_clk_gate_enable, > + .disable = apple_clk_gate_disable, > + .is_enabled = apple_clk_gate_is_enabled, > +}; > + > +static int apple_gate_clk_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct device_node *node = dev->of_node; > + const struct clk_parent_data parent_data[] = { > + { .index = 0 }, > + }; Yay thanks for not doing it the old way! > + struct apple_clk_gate *data; > + struct clk_hw *hw; > + struct clk_init_data init; > + struct resource *res; > + int num_parents; > + int ret; > + > + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); > + if (!data) > + return -ENOMEM; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + data->reg = devm_ioremap_resource(dev, res); > + if (IS_ERR(data->reg)) > + return PTR_ERR(data->reg); > + > + num_parents = of_clk_get_parent_count(node); Oh no I spoke too soon. > + if (num_parents > 1) { > + dev_err(dev, "clock supports at most one parent\n"); > + return -EINVAL; > + } > + > + init.name = dev->of_node->name; > + init.ops = &apple_clk_gate_ops; > + init.flags = 0; > + init.parent_names = NULL; > + init.parent_data = parent_data; > + init.num_parents = num_parents; > + > + data->hw.init = &init; > + hw = &data->hw; > + > + ret = devm_clk_hw_register(dev, hw); > + if (ret) > + return ret; > + > + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); It looks like a one clk per DT node binding which is not how we do it. I see Rob has started that discussion on the binding so we can keep that there. > +} > +