Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add clock driver for meson #278

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions LICENSES/GPL-2.0-only.txt

Large diffs are not rendered by default.

117 changes: 117 additions & 0 deletions LICENSES/GPL-2.0-or-later.txt

Large diffs are not rendered by default.

78 changes: 78 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ const DriverClass = struct {
virtio
};

const Clock = enum {
meson,
imx,
};

const I2cHost = enum {
meson,
};
Expand Down Expand Up @@ -202,6 +207,60 @@ fn addBlockDriver(
return driver;
}

fn addClockDriver(
b: *std.Build,
clk_config_include: LazyPath,
util: *std.Build.Step.Compile,
class: DriverClass.Clock,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
) *std.Build.Step.Compile {
const driver = addPd(b, .{
.name = b.fmt("driver_clk_{s}.elf", .{@tagName(class)}),
.target = target,
.optimize = optimize,
.strip = false,
});

switch (class) {
.meson => {
const files: []const []const u8 = &.{
"drivers/clk/meson/clk-meson.c",
"drivers/clk/meson/clk-measure.c",
"drivers/clk/meson/sm1-clk.c",
};
driver.addCSourceFiles(.{ .files = files });
},
.imx => {
const files: []const []const u8 = &.{
"drivers/clk/imx/clk-imx.c",
"drivers/clk/imx/clk-imx8mq.c",
};
driver.addCSourceFiles(.{ .files = files });
},
}

const common_src_files = .{
"clk-operations.c",
"clk.c",
};

inline for (common_src_files) |f| {
driver.addCSourceFile(.{
.file = b.path(b.fmt("drivers/clk/{s}", .{ f }))
});
}

driver.defineCMacro(b.fmt("BOARD_CLASS_{s}", .{ @tagName(class) }), "1");
driver.addIncludePath(clk_config_include);
driver.addIncludePath(b.path("include"));
driver.addIncludePath(b.path("drivers/clk"));
driver.addIncludePath(b.path(b.fmt("drivers/clk/{s}/include", .{@tagName(class)})));
driver.linkLibrary(util);

return driver;
}

fn addMmcDriver(
b: *std.Build,
blk_config_include: LazyPath,
Expand Down Expand Up @@ -301,6 +360,8 @@ pub fn build(b: *std.Build) void {
const net_config_include_option = b.option([]const u8, "net_config_include", "Include path to network config header") orelse "";
const i2c_client_include_option = b.option([]const u8, "i2c_client_include", "Include path to client config header") orelse "";
const gpu_config_include_option = b.option([]const u8, "gpu_config_include", "Include path to gpu config header") orelse "";
const clk_conf_include_option = b.option([]const u8, "clk_conf_include", "Include path to client config header") orelse "";
const dtb_path = b.option([]const u8, "dtb_path", "Path to the DTB file") orelse "";

// TODO: Right now this is not super ideal. What's happening is that we do not
// always need a serial config include, but we must always specify it
Expand All @@ -312,6 +373,7 @@ pub fn build(b: *std.Build) void {
const net_config_include = LazyPath{ .cwd_relative = net_config_include_option };
const i2c_client_include = LazyPath{ .cwd_relative = i2c_client_include_option };
const gpu_config_include = LazyPath{ .cwd_relative = gpu_config_include_option };
const clk_client_include = LazyPath{ .cwd_relative = clk_conf_include_option };
// libmicrokit
// We're declaring explicitly here instead of with anonymous structs due to a bug. See https://github.com/ziglang/zig/issues/19832
libmicrokit = LazyPath{ .cwd_relative = libmicrokit_opt.? };
Expand Down Expand Up @@ -454,6 +516,22 @@ pub fn build(b: *std.Build) void {
b.installArtifact(driver);
}

// Clock drivers
inline for (std.meta.fields(DriverClass.Clock)) |class| {
const driver = addClockDriver(b, clk_client_include, util, @enumFromInt(class.value), target, optimize);
driver.linkLibrary(util_putchar_debug);

const clk_config = b.addSystemCommand(&.{
"python",
"drivers/clk/create_clk_config.py",
dtb_path,
}); // Creates a system command which runs the python interpreter
const clk_config_include = clk_config.addOutputDirectoryArg("test");
driver.addIncludePath(clk_config_include);

b.installArtifact(driver);
}

// I2C components
const i2c_virt = addPd(b, .{
.name = "i2c_virt.elf",
Expand Down
262 changes: 262 additions & 0 deletions drivers/clk/clk-operations.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/*
* Operations for MPLL clocks are derived from:
* https://github.com/torvalds/linux/blob/
* befe87380e21f0d37633273e1068c9318f8135ff/drivers/clk/meson/clk-mpll.c
*
* Copyright (c) 2016 AmLogic, Inc.
* Author: Michael Turquette <[email protected]>
*/

// SPDX-License-Identifier: GPL-2.0-only
/*
* Operations for PLL clocks are derived from:
* https://github.com/torvalds/linux/blob/
* befe87380e21f0d37633273e1068c9318f8135ff/drivers/clk/meson/clk-pll.c
*
* Copyright (c) 2015 Endless Mobile, Inc.
* Author: Carlo Caione <[email protected]>
*
* Copyright (c) 2018 Baylibre, SAS.
* Author: Jerome Brunet <[email protected]>
*/

// SPDX-License-Identifier: GPL-2.0-only
/*
* Operations for gate clocks are derived from:
* https://github.com/torvalds/linux/blob/
* befe87380e21f0d37633273e1068c9318f8135ff/drivers/clk/meson/clk-regmap.c
*
* Copyright (c) 2018 BayLibre, SAS.
* Author: Jerome Brunet <[email protected]>
*/

// SPDX-License-Identifier: GPL-2.0-only
/*
* Operations for fixed factor clocks are derived from:
* https://github.com/torvalds/linux/blob/
* befe87380e21f0d37633273e1068c9318f8135ff/drivers/clk/clk-fixed-factor.c
*
* Copyright (C) 2011 Sascha Hauer, Pengutronix <[email protected]>
*/

// SPDX-License-Identifier: GPL-2.0-only
/*
* Operations for divider clocks are derived from:
* https://github.com/torvalds/linux/blob/
* befe87380e21f0d37633273e1068c9318f8135ff/drivers/clk/clk-divider.c
*
* Copyright (C) 2011 Sascha Hauer, Pengutronix <[email protected]>
* Copyright (C) 2011 Richard Zhao, Linaro <[email protected]>
* Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <[email protected]>
*
* Adjustable divider clock implementation
*/

// SPDX-License-Identifier: GPL-2.0-only
/*
* Operations for multiplexer clocks are derived from:
* https://github.com/torvalds/linux/blob/
* befe87380e21f0d37633273e1068c9318f8135ff/drivers/clk/clk-mux.c
*
* Copyright (C) 2011 Sascha Hauer, Pengutronix <[email protected]>
* Copyright (C) 2011 Richard Zhao, Linaro <[email protected]>
* Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <[email protected]>
*
* Simple multiplexer clock implementation
*/

// SPDX-License-Identifier: GPL-2.0-only
/*
* Operations for meson_vid_pll_div are derived from:
* https://github.com/torvalds/linux/blob/
* 7ec462100ef9142344ddbf86f2c3008b97acddbe/drivers/clk/meson/vid-pll-div.c
* https://github.com/torvalds/linux/blob/
* 7ec462100ef9142344ddbf86f2c3008b97acddbe/drivers/clk/meson/vclk.c
*
* Copyright (c) 2018 BayLibre, SAS.
* Author: Neil Armstrong <[email protected]>
*/

#include <clk.h>
#include <clk-operations.h>
#include <sddf/timer/client.h>
#include <sddf/util/printf.h>
#include <clk_utils.h>

static inline int clk_gate_enable(struct clk *clk)
{
struct clk_gate_data *data = (struct clk_gate_data *)(clk->data);

return regmap_update_bits(clk->base, data->offset, data->bit_idx, MASK(1), 1);
}

static inline int clk_gate_disable(struct clk *clk)
{
struct clk_gate_data *data = (struct clk_gate_data *)(clk->data);

regmap_update_bits(clk->base, data->offset, data->bit_idx, MASK(1), 0);
return 0;
}

static inline int clk_gate_is_enabled(struct clk *clk)
{
struct clk_gate_data *data = (struct clk_gate_data *)(clk->data);

/* TODO: to be checked */
/* if (gate->flags & CLK_GATE_SET_TO_DISABLE) */
/* val ^= BIT(gate->bit_idx); */

/* val &= BIT(gate->bit_idx); */
/* return val ? 1 : 0; */

return regmap_read_bits(clk->base, data->offset, data->bit_idx, MASK(1));
}

const struct clk_ops clk_gate_ops = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need to provide the vector of functions; our systems will have only a single clock driver in them. Kill the 'static' and just link directly against the symbols instead of via a function pointer

.enable = clk_gate_enable,
.disable = clk_gate_disable,
.is_enabled = clk_gate_is_enabled,
};

const struct clk_ops clk_gate_ro_ops = {
.is_enabled = clk_gate_is_enabled,
};

static inline uint64_t clk_div_recalc_rate(const struct clk *clk, uint64_t prate)
{

struct clk_div_data *data = (struct clk_div_data *)(clk->data);
uint32_t div = regmap_read_bits(clk->base, data->offset, data->shift, MASK(data->width));

/* TODO: Need to verify the following cases */
if (data->flags & CLK_DIVIDER_ONE_BASED) {
;
} else if (data->flags & CLK_DIVIDER_POWER_OF_TWO) {
div = 1 << div;
} else if (data->flags & CLK_DIVIDER_MAX_AT_ZERO) {
div = div ? div : MASK(data->width) + 1;
} else {
div += 1;
}

return DIV_ROUND_UP_ULL((uint64_t)prate, div);
}

static inline int clk_div_set_rate(const struct clk *clk, uint64_t rate, uint64_t parent_rate)
{
struct clk_div_data *data = (struct clk_div_data *)(clk->data);
uint32_t div = DIV_ROUND_UP(parent_rate, rate);

if (data->flags & CLK_DIVIDER_ONE_BASED) {
/* TODO: to be implemented */
;
} else if (data->flags & CLK_DIVIDER_POWER_OF_TWO) {
/* div = __ffs(div); */
} else if (data->flags & CLK_DIVIDER_MAX_AT_ZERO) {
div = (div == MASK(data->width) + 1) ? 0 : div;
} else {
div -= 1;
}
return regmap_update_bits(clk->base, data->offset, data->shift, MASK(data->width), div);
}

const struct clk_ops clk_divider_ops = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be needed in our LionsOS systems

.enable = NULL,
.recalc_rate = clk_div_recalc_rate,
/* .determine_rate = clk_div_determine_rate, */
.set_rate = clk_div_set_rate,
};

const struct clk_ops clk_divider_ro_ops = {
.recalc_rate = clk_div_recalc_rate,
/* .determine_rate = clk_div_determine_rate, */
};

static inline int clk_mux_get_parent(const struct clk *clk, uint8_t *index)
{
struct clk_mux_data *data = (struct clk_mux_data *)(clk->data);
uint32_t num_parents = clk->hw.init->num_parents;
uint32_t val = regmap_read_bits(clk->base, data->offset, data->shift, data->mask);

if (data->table) {
int i;
for (i = 0; i < num_parents; i++) {
if (data->table[i] == val) {
*index = i;
return 0;
}
}
return CLK_UNKNOWN_TARGET;
}

/* if (val && (flags & CLK_MUX_INDEX_BIT)) */
/* val = ffs(val) - 1; */

/* if (val && (flags & CLK_MUX_INDEX_ONE)) */
/* val--; */

if (val >= num_parents)
return CLK_UNKNOWN_TARGET;

*index = val;
return 0;
}

static inline int clk_mux_set_parent(struct clk *clk, uint8_t index)
{
struct clk_mux_data *data = (struct clk_mux_data *)(clk->data);

if (data->table) {
uint32_t val = data->table[index];
return regmap_update_bits(clk->base, data->offset, data->shift, data->mask, val);
}

return regmap_update_bits(clk->base, data->offset, data->shift, data->mask, index);
}

const struct clk_ops clk_mux_ops = {
.get_parent = clk_mux_get_parent, .set_parent = clk_mux_set_parent,
/* .determine_rate = clk_mux_determine_rate, */
};

const struct clk_ops clk_mux_ro_ops = {
.get_parent = clk_mux_get_parent,
};

static inline uint64_t clk_factor_recalc_rate(const struct clk *clk, uint64_t parent_rate)
{
struct clk_fixed_factor_data *data = (struct clk_fixed_factor_data *)(clk->data);
uint64_t rate;

rate = (uint64_t)parent_rate * data->mult;
do_div(rate, data->div);
return (uint64_t)rate;
}

const struct clk_ops clk_fixed_factor_ops = {
/* .round_rate = clk_factor_round_rate, */
/* .set_rate = clk_factor_set_rate, */
.recalc_rate = clk_factor_recalc_rate,
/* .recalc_accuracy = clk_factor_recalc_accuracy, */
};

static inline int clk_source_set_rate(const struct clk *clk, uint64_t rate, uint64_t parent_rate)
{
struct clk_source_data *data = (struct clk_source_data *)(clk->data);
data->rate = rate;

return 0;
}

static inline uint64_t clk_source_get_rate(const struct clk *clk, uint64_t prate)
{
struct clk_source_data *data = (struct clk_source_data *)(clk->data);

return data->rate;
}

const struct clk_ops clk_source_ops = {
.recalc_rate = clk_source_get_rate,
.set_rate = clk_source_set_rate,
};
Loading