Skip to content

Commit

Permalink
Clock driver for maaxboard
Browse files Browse the repository at this point in the history
Add clock driver for maaxboard. This commit includes all clock component
definitions, basic operations, and a simple test on maaxboard. Scripts are
added in build systems. A few files are reorganised for reuse
  • Loading branch information
terryzbai committed Nov 3, 2024
1 parent d53cb39 commit bb3e31c
Show file tree
Hide file tree
Showing 15 changed files with 4,875 additions and 22 deletions.
11 changes: 10 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const DriverClass = struct {

const Clock = enum {
meson,
imx,
};

const I2cHost = enum {
Expand Down Expand Up @@ -227,6 +228,14 @@ fn addClockDriver(
};
driver.addCSourceFiles(.{ .files = files });
},
.imx => {
const files: []const []const u8 = &.{
"drivers/clk/imx/clk.c",
"drivers/clk/imx/clk-imx.c",
"drivers/clk/imx/clk-imx8mq.c",
};
driver.addCSourceFiles(.{ .files = files });
},
}

const common_src_files = .{ "clk-operations.c" };
Expand Down Expand Up @@ -457,7 +466,7 @@ pub fn build(b: *std.Build) void {

const clk_config = b.addSystemCommand(&.{
"python",
b.fmt("drivers/clk/{s}/create_clk_config.py", .{ class.name }),
"drivers/clk/create_clk_config.py",
dtb_path,
clk_conf_include_option,
}); // Creates a system command which runs the python interpreter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

# Copyright 2024, UNSW
# SPDX-License-Identifier: BSD-2-Clause
#
# The template is stolen from Bill's pinmux driver

import os
import sys
from typing import List
from devicetree import edtlib, dtlib

supported_compat_str_board = { "hardkernel,odroid-c4" }
supported_compat_str_board = { "hardkernel,odroid-c4", "fsl,imx8mm-evk", "fsl,imx8mq" }

debug_parser = True
clock_list = {}
Expand Down Expand Up @@ -67,7 +65,8 @@ def extract_clocks(dt: dtlib.DT, node: dtlib.Node) -> List:
for clk_id in clock_ids:
add_clock(clk_id, 0, 0)
for pnode in pnodes:
extract_clocks(dt, pnode)
if pnode != node:
extract_clocks(dt, pnode)

if "max-frequency" in props:
max_frequency = node.props["max-frequency"].to_nums()
Expand All @@ -79,7 +78,8 @@ def extract_clocks(dt: dtlib.DT, node: dtlib.Node) -> List:
if (clk_rate):
add_clock(clk_id, clk_rate, 0)
for pnode in pnodes:
extract_clocks(dt, pnode)
if pnode != node:
extract_clocks(dt, pnode)

if "assigned-clocks" in props and "assigned-clock-parents" in props:
assigned_clocks, pnodes = parse_clock_list(devicetree, node.props["assigned-clocks"].to_nums())
Expand All @@ -88,11 +88,11 @@ def extract_clocks(dt: dtlib.DT, node: dtlib.Node) -> List:
if (pclk_id):
add_clock(clk_id, 0, pclk_id)
for pnode in pnodes:
extract_clocks(dt, pnode)
if pnode != node:
extract_clocks(dt, pnode)
for pnode in ppnodes:
extract_clocks(dt, pnode)

return enabled_clks
if pnode != node:
extract_clocks(dt, pnode)

def write_configs_to_headerfile(path: str) -> None:
with open(path + '/clk_config.h', "w") as f:
Expand All @@ -106,6 +106,7 @@ def write_configs_to_headerfile(path: str) -> None:
f.write("static struct clk_cfg clk_configs[] = {{\n{}\n}};".format(",\n".join(clk_cfg_strs)))

if __name__ == "__main__":
supported = False
devicetree = dtlib.DT(sys.argv[1], force=True)
for compat_str in devicetree.root.props["compatible"].to_strings():
if compat_str in supported_compat_str_board:
Expand All @@ -116,7 +117,6 @@ def write_configs_to_headerfile(path: str) -> None:
log_error_parser("this board is not supported.")
exit(1)

enabled_clks = []

for node in devicetree.node_iter():
props = list(node.props.keys())
Expand Down
283 changes: 283 additions & 0 deletions drivers/clk/imx/clk-imx.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
#include <utils.h>
#include <clk-operations.h>
#include <clk-imx.h>

#define PLL_FRAC_DENOM 0x1000000

static int clk_gate2_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, 2, 0x3);
}

static int clk_gate2_disable(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, 2, 0);
}

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

if (regmap_read_bits(clk->base, data->offset, data->bit_idx, 2) == 0x3)
return 1;

return 0;
}

const struct clk_ops clk_gate2_ops = {
.enable = clk_gate2_enable,
.disable = clk_gate2_disable,
/* .disable_unused = clk_gate2_disable_unused, */
.is_enabled = clk_gate2_is_enabled,
};

static unsigned long clk_pll_recalc_rate(const struct clk *clk,
unsigned long prate)
{
/* TODO: This function is derived from Linux codebase, but seems wrong
* according to the datasheet as PLL_REFCLK_DIV_VAL[5:10] is never used. */
struct clk_frac_pll_data *data = (struct clk_frac_pll_data *)(clk->data);
uint64_t temp_rate = prate;
uint64_t rate;

/* Output Divider value is (n + 1) * 2 */
uint32_t output_div_val = regmap_read_bits(clk->base, data->offset, 0, 5);
output_div_val = (output_div_val + 1 ) * 2;

/* Valid Frac Divider value is 1 to 2^24 */
uint32_t frac_div_val = regmap_read_bits(clk->base, data->offset + 0x4, 7, 24);

/* Valid Int Divider value is 1 to 32 */
uint32_t int_div_val = regmap_read_bits(clk->base, data->offset + 0x4, 0, 7);

temp_rate *= prate;
temp_rate *= frac_div_val;
do_div(temp_rate, PLL_FRAC_DENOM);
do_div(temp_rate, output_div_val);

/* Frac Divider value is (n) */
rate = prate * 8 * (int_div_val + 1);
do_div(rate, output_div_val);
rate += temp_rate;

return rate;
}

const struct clk_ops clk_frac_pll_ops = {
/* .prepare = clk_pll_prepare, */
/* .unprepare = clk_pll_unprepare, */
/* .is_prepared = clk_pll_is_prepared, */
.recalc_rate = clk_pll_recalc_rate,
/* .round_rate = clk_pll_round_rate, */
/* .set_rate = clk_pll_set_rate, */
};

static unsigned long clk_sscg_pll_recalc_rate(const struct clk *clk,
unsigned long prate)
{
struct clk_sscg_pll_data *data = (struct clk_sscg_pll_data *)(clk->data);
uint64_t temp_rate = prate;

uint32_t divr1 = regmap_read_bits(clk->base, data->offset + 0x8, 25, 3);
uint32_t divr2 = regmap_read_bits(clk->base, data->offset + 0x8, 19, 6);
uint32_t divf1 = regmap_read_bits(clk->base, data->offset + 0x8, 13, 6);
uint32_t divf2 = regmap_read_bits(clk->base, data->offset + 0x8, 7, 6);
uint32_t divq = regmap_read_bits(clk->base, data->offset + 0x8, 1, 6);

if (regmap_read_bits(clk->base, data->offset, 4, 1)) {
temp_rate = prate;
} else if (regmap_read_bits(clk->base, data->offset, 5, 1)) {
temp_rate *= divf2;
do_div(temp_rate, (divr2 + 1) * (divq + 1));
} else {
temp_rate *= 2;
temp_rate *= (divf1 + 1) * (divf2 + 1);
do_div(temp_rate, (divr1 + 1) * (divr2 + 1) * (divq + 1));
}

return 0;
}

static uint8_t clk_sscg_pll_get_parent(const struct clk *clk)
{
struct clk_sscg_pll_data *data = (struct clk_sscg_pll_data *)(clk->data);
uint8_t ret = 0;

if (regmap_read_bits(clk->base, data->offset, 4, 1)) {
ret = data->bypass2;
} else if (regmap_read_bits(clk->base, data->offset, 5, 1)) {
ret = data->bypass1;
}

return ret;
}

static int clk_sscg_pll_set_parent(struct clk *clk, uint8_t index)
{
/* struct clk_sscg_pll_data *data = (struct clk_sscg_pll_data *)(clk->data); */

/* TODO: This operation is based on `setup.bypass` instead of index passed from callee */

return 0;
}

const struct clk_ops clk_sscg_pll_ops = {
/* .prepare = clk_sscg_pll_prepare, */
/* .unprepare = clk_sscg_pll_unprepare, */
/* .is_prepared = clk_sscg_pll_is_prepared, */
.recalc_rate = clk_sscg_pll_recalc_rate,
/* .set_rate = clk_sscg_pll_set_rate, */
.set_parent = clk_sscg_pll_set_parent,
.get_parent = clk_sscg_pll_get_parent,
/* .determine_rate = clk_sscg_pll_determine_rate, */
};

static unsigned long imx8m_clk_core_slice_recalc_rate(const struct clk *clk,
unsigned long prate)
{
struct clk_core_slice_data *data = (struct clk_core_slice_data *)(clk->data);

uint32_t div_val = regmap_read_bits(clk->base, data->offset, data->div_shift, data->div_width);
/* Divider value is n+1 */
return DIV_ROUND_UP_ULL((uint64_t)prate, div_val + 1);
}

static uint8_t imx8m_clk_core_slice_get_parent(const struct clk *clk)
{
struct clk_core_slice_data *data = (struct clk_core_slice_data *)(clk->data);

uint32_t num_parents = clk->hw.init->num_parents;
uint32_t val = regmap_mux_read_bits(clk->base, data->offset, data->mux_shift, data->mux_mask);

if (val >= num_parents)
return -1;

return val;
}

static int imx8m_clk_core_slice_set_parent(struct clk *clk, uint8_t index)
{
struct clk_core_slice_data *data = (struct clk_core_slice_data *)(clk->data);

/*
* write twice to make sure non-target interface
* SEL_A/B point the same clk input.
*/
regmap_mux_update_bits(clk->base, data->offset, data->mux_shift, data->mux_mask, index);
regmap_mux_update_bits(clk->base, data->offset, data->mux_shift, data->mux_mask, index);

return 0;
}

const struct clk_ops clk_core_slice_ops = {
.recalc_rate = imx8m_clk_core_slice_recalc_rate,
/* .round_rate = imx8m_clk_core_slice_round_rate, */
/* .set_rate = imx8m_clk_core_slice_set_rate, */
/* .determine_rate = imx8m_clk_core_slice_determine_rate, */
.get_parent = imx8m_clk_core_slice_get_parent,
.set_parent = imx8m_clk_core_slice_set_parent,
};

static unsigned long imx8m_clk_common_slice_recalc_rate(const struct clk *clk,
unsigned long prate)
{
struct clk_common_slice_data *data = (struct clk_common_slice_data *)(clk->data);

uint32_t prediv_val = regmap_read_bits(clk->base, data->offset, data->prevdiv_shift, data->prevdiv_width);
/* Divider value is n+1 */
unsigned long prediv_rate = DIV_ROUND_UP_ULL((uint64_t)prate, prediv_val + 1);

uint32_t postdiv_val = regmap_read_bits(clk->base, data->offset, data->postdiv_shift, data->postdiv_width);
/* Divider value is n+1 */
return DIV_ROUND_UP_ULL((uint64_t)prediv_rate, postdiv_val + 1);
}

static uint8_t imx8m_clk_common_slice_get_parent(const struct clk *clk)
{
struct clk_common_slice_data *data = (struct clk_common_slice_data *)(clk->data);

uint32_t num_parents = clk->hw.init->num_parents;
uint32_t val = regmap_mux_read_bits(clk->base, data->offset, data->mux_shift, data->mux_mask);

if (val >= num_parents)
return -1;

return val;
}

static int imx8m_clk_common_slice_set_parent(struct clk *clk, uint8_t index)
{
struct clk_common_slice_data *data = (struct clk_common_slice_data *)(clk->data);

/*
* write twice to make sure non-target interface
* SEL_A/B point the same clk input.
*/
regmap_mux_update_bits(clk->base, data->offset, data->mux_shift, data->mux_mask, index);
regmap_mux_update_bits(clk->base, data->offset, data->mux_shift, data->mux_mask, index);

return 0;
}

const struct clk_ops clk_common_slice_ops = {
.recalc_rate = imx8m_clk_common_slice_recalc_rate,
/* .round_rate = imx8m_clk_common_slice_round_rate, */
/* .set_rate = imx8m_clk_common_slice_set_rate, */
/* .determine_rate = imx8m_clk_common_slice_determine_rate, */
.get_parent = imx8m_clk_common_slice_get_parent,
.set_parent = imx8m_clk_common_slice_set_parent,
};

static unsigned long imx8m_clk_bus_slice_recalc_rate(const struct clk *clk,
unsigned long prate)
{
struct clk_bus_slice_data *data = (struct clk_bus_slice_data *)(clk->data);

uint32_t prediv_val = regmap_read_bits(clk->base, data->offset, data->prevdiv_shift, data->prevdiv_width);
/* Divider value is n+1 */
unsigned long prediv_rate = DIV_ROUND_UP_ULL((uint64_t)prate, prediv_val + 1);

uint32_t postdiv_val = regmap_read_bits(clk->base, data->offset, data->postdiv_shift, data->postdiv_width);
/* Divider value is n+1 */
return DIV_ROUND_UP_ULL((uint64_t)prediv_rate, postdiv_val + 1);
}

static uint8_t imx8m_clk_bus_slice_get_parent(const struct clk *clk)
{
struct clk_bus_slice_data *data = (struct clk_bus_slice_data *)(clk->data);

uint32_t num_parents = clk->hw.init->num_parents;
uint32_t val = regmap_mux_read_bits(clk->base, data->offset, data->mux_shift, data->mux_mask);

if (val >= num_parents)
return -1;

return val;
}

static int imx8m_clk_bus_slice_set_parent(struct clk *clk, uint8_t index)
{
struct clk_bus_slice_data *data = (struct clk_bus_slice_data *)(clk->data);

/*
* write twice to make sure non-target interface
* SEL_A/B point the same clk input.
*/
regmap_mux_update_bits(clk->base, data->offset, data->mux_shift, data->mux_mask, index);
regmap_mux_update_bits(clk->base, data->offset, data->mux_shift, data->mux_mask, index);

return 0;
}

const struct clk_ops clk_bus_slice_ops = {
.recalc_rate = imx8m_clk_bus_slice_recalc_rate,
/* .round_rate = imx8m_clk_composite_divider_round_rate, */
/* .set_rate = imx8m_clk_composite_divider_set_rate, */
/* .determine_rate = imx8m_divider_determine_rate, */
.get_parent = imx8m_clk_bus_slice_get_parent,
.set_parent = imx8m_clk_bus_slice_set_parent,
};
Loading

0 comments on commit bb3e31c

Please sign in to comment.