Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eeca99e7 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: VPU: Support new clocks for HQV, FRC and QDSS"

parents fae1faee 985d533d
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@ Required properties:
- interrupt-names : Names corresponding to the defined interrupts list.
    - "vpu_wdog" : Watchdog interrupt
    - "vpu_hfi" : Firmware to Host interrupt
- clock-names: Array of clocks that the driver requires for the device.
 The names here correspond to the clock names used in clk_get(<name>).

- qcom,maple-clk-load-freq-tbl: Table of <load, freq> entries. An entry
  specifies a given VPU processing load (in bits per second), and a
@@ -41,6 +43,7 @@ Example:
		reg-names = "vpu_csr", "vpu_smem";
		interrupts = <0 44 0>, <0 45 0>;
		interrupt-names = "vpu_wdog", "vpu_hfi";
		clock-names = "core_clk", "bus_clock", "iface_clk";
		qcom,maple-clk-load-freq-tbl = <100000 50000000>,
			<500000 400000000>;
		qcom,vdp-clk-load-freq-tbl = <200000 100000000>,
+91 −81
Original line number Diff line number Diff line
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -156,33 +156,15 @@ int vpu_bus_scale(u32 load)
 * vpu_vdp_clk between 200MHz and 400MHz during runtime to optimize for
 * power consumption
 */
#define	VPU_CLK_GATE_LEVEL VPU_VDP_CLK

static const char *clock_names[VPU_MAX_CLKS] = {
	[VPU_BUS_CLK] = "vdp_bus_clk",
	[VPU_MAPLE_CLK] = "core_clk",
	[VPU_VDP_CLK] = "vdp_clk",
	[VPU_AHB_CLK] = "iface_clk",
	[VPU_AXI_CLK] = "bus_clk",
	[VPU_SLEEP_CLK] = "sleep_clk",
	[VPU_CXO_CLK] = "cxo_clk",
	[VPU_MAPLE_AXI_CLK] = "maple_bus_clk",
	[VPU_PRNG_CLK] = "prng_clk",
};

struct vpu_core_clock {
	struct clk *clk;
	u32 status;
	u32 current_freq;
	struct load_freq_table *load_freq_tbl;
	const char *name;
};
#define NOT_USED(clk_ctrl, i) (!( \
			((clk_ctrl)->clock[i]->flag & CLOCK_PRESENT) && \
			((clk_ctrl)->clock[i]->flag & (clk_ctrl)->mask)))
#define NOT_IN_GROUP(clk_ctrl, i, clk_group) (!( \
			((clk_ctrl)->clock[i]->flag & (clk_group))))

static const u32 clock_freqs[VPU_MAX_CLKS][VPU_POWER_MAX] = {
	[VPU_BUS_CLK]	= { 40000000,  80000000,  80000000},
	[VPU_MAPLE_CLK]	= {200000000, 400000000, 400000000},
	[VPU_VDP_CLK]	= {200000000, 200000000, 400000000},
};
#define CLOCK_IS_SCALABLE(clk_ctrl, i) \
			((clk_ctrl)->clock[i]->flag & CLOCK_SCALABLE)

/*
 * Note: there is no lock in this block
@@ -194,14 +176,15 @@ struct vpu_clk_control {
	/* svs, nominal, turbo, dynamic(default) */
	u32 mode;

	struct vpu_core_clock clock[VPU_MAX_CLKS];
	struct vpu_clock *clock[VPU_MAX_CLKS];

	u32 mask;
};

void *vpu_clock_init(struct vpu_platform_resources *res)
{
	int i;
	int rc = -1;
	struct vpu_core_clock *cl;
	struct vpu_clock *cl;
	struct vpu_clk_control *clk_ctrl;

	if (!res)
@@ -214,14 +197,21 @@ void *vpu_clock_init(struct vpu_platform_resources *res)
		return NULL;
	}

	for (i = 0; i < VPU_MAX_CLKS; i++)
		clk_ctrl->clock[i] = &res->clock[i];

	/* mask allowing to only enable clocks from certain groups of clocks */
	clk_ctrl->mask = CLOCK_CORE;

	/* setup the clock handles */
	for (i = 0; i < VPU_MAX_CLKS; i++) {
		cl = &clk_ctrl->clock[i];
		if (NOT_USED(clk_ctrl, i))
			continue;

		cl->load_freq_tbl = &res->clock_tables[i];
		cl->name = clock_names[i];
		cl = clk_ctrl->clock[i];

		if (i <= VPU_CLK_GATE_LEVEL && cl->load_freq_tbl->count == 0) {
		if (CLOCK_IS_SCALABLE(clk_ctrl, i) &&
				!cl->load_freq_tbl.count) {
			pr_err("%s freq table size is 0\n", cl->name);
			goto fail_init_clocks;
		}
@@ -229,27 +219,16 @@ void *vpu_clock_init(struct vpu_platform_resources *res)
		cl->clk = devm_clk_get(&res->pdev->dev, cl->name);
		if (IS_ERR_OR_NULL(cl->clk)) {
			pr_err("Failed to get clock: %s\n", cl->name);
			rc = PTR_ERR(cl->clk);
			break;
		}
		cl->status = 0;
	}

	/* free the clock if not all successful */
	if (i < VPU_MAX_CLKS) {
		for (i = 0; i < VPU_MAX_CLKS; i++) {
			cl = &clk_ctrl->clock[i];
			if (cl->clk) {
				clk_put(cl->clk);
			cl->clk = NULL;
			}
		}
			goto fail_init_clocks;
		}
		cl->status = 0;
	}

	return clk_ctrl;

fail_init_clocks:
	vpu_clock_deinit((void *)clk_ctrl);
	kfree(clk_ctrl);
	return NULL;
}
@@ -257,7 +236,7 @@ fail_init_clocks:
void vpu_clock_deinit(void *clkh)
{
	int i;
	struct vpu_core_clock *cl;
	struct vpu_clock *cl;
	struct vpu_clk_control *clk_ctr = (struct vpu_clk_control *)clkh;

	if (!clk_ctr) {
@@ -266,21 +245,32 @@ void vpu_clock_deinit(void *clkh)
	}

	for (i = 0; i < VPU_MAX_CLKS; i++) {
		cl = &clk_ctr->clock[i];
		if (NOT_USED(clk_ctr, i))
			continue;
		cl = clk_ctr->clock[i];
		if (cl->status) {
			clk_disable_unprepare(cl->clk);
			cl->status = 0;
		}
		if (cl->clk) {
			clk_put(cl->clk);
			cl->clk = NULL;
		}
	}

	kfree(clk_ctr);
}

int vpu_clock_enable(void *clkh)
/*
 * vpu_clock_enable() - enable a group of clocks
 *
 * @clkh:		clock handler
 * @clk_group:	see vpu_clock_flag (group section)
 *
 */
int vpu_clock_enable(void *clkh, u32 clk_group)
{
	struct vpu_core_clock *cl;
	struct vpu_clock *cl;
	struct vpu_clk_control *clk_ctr = (struct vpu_clk_control *)clkh;
	int i = 0;
	int rc = 0;
@@ -293,14 +283,18 @@ int vpu_clock_enable(void *clkh)
	clk_ctr->mode = VPU_POWER_DYNAMIC;

	for (i = 0; i < VPU_MAX_CLKS; i++) {
		cl = &clk_ctr->clock[i];
		if (NOT_USED(clk_ctr, i) ||
				NOT_IN_GROUP(clk_ctr, i, clk_group))
			continue;

		cl = clk_ctr->clock[i];

		if (cl->status == 0) {
			/* set rate if it's a gated clock */
			if (i <= VPU_CLK_GATE_LEVEL &&
				cl->load_freq_tbl->entry) {
			if (CLOCK_IS_SCALABLE(clk_ctr, i) &&
				cl->load_freq_tbl.entry) {
				cl->current_freq =
					cl->load_freq_tbl->entry[0].freq;
					cl->load_freq_tbl.entry[0].freq;

				rc = clk_set_rate(cl->clk, cl->current_freq);
				if (rc) {
@@ -325,21 +319,21 @@ int vpu_clock_enable(void *clkh)
	return rc;

fail_clk_enable:
	for (i = 0; i < VPU_MAX_CLKS; i++) {
		cl = &clk_ctr->clock[i];
		if (cl->status) {
			clk_disable_unprepare(cl->clk);
			cl->status = 0;
		}
	}

	vpu_clock_disable(clkh, clk_group);
	return rc;
}

void vpu_clock_disable(void *clkh)
/*
 * vpu_clock_disable() - disable a group of clocks
 *
 * @clkh:		clock handler
 * @clk_group:	see vpu_clock_flag (group section)
 *
 */
void vpu_clock_disable(void *clkh, u32 clk_group)
{
	int i;
	struct vpu_core_clock *cl;
	struct vpu_clock *cl;
	struct vpu_clk_control *clk_ctr = (struct vpu_clk_control *)clkh;

	if (!clk_ctr) {
@@ -348,7 +342,10 @@ void vpu_clock_disable(void *clkh)
	}

	for (i = 0; i < VPU_MAX_CLKS; i++) {
		cl = &clk_ctr->clock[i];
		if (NOT_USED(clk_ctr, i) ||
				NOT_IN_GROUP(clk_ctr, i, clk_group))
			continue;
		cl = clk_ctr->clock[i];
		if (cl->status) {
			clk_disable_unprepare(cl->clk);
			cl->status = 0;
@@ -356,10 +353,10 @@ void vpu_clock_disable(void *clkh)
	}
}

static unsigned long __clock_get_rate(struct vpu_core_clock *clock,
static unsigned long __clock_get_rate(struct vpu_clock *clock,
	u32 num_bits_per_sec)
{
	struct load_freq_table *table = clock->load_freq_tbl;
	struct load_freq_table *table = &clock->load_freq_tbl;
	unsigned long ret = 0;
	int i;

@@ -385,10 +382,13 @@ int vpu_clock_scale(void *clkh, u32 load)

	clk_ctr->load = load;

	for (i = 0; i <= VPU_CLK_GATE_LEVEL; i++) {
		struct vpu_core_clock *cl = &clk_ctr->clock[i];
	for (i = 0; i < VPU_MAX_CLKS; i++) {
		struct vpu_clock *cl = clk_ctr->clock[i];
		unsigned long freq;

		if (NOT_USED(clk_ctr, i) || !CLOCK_IS_SCALABLE(clk_ctr, i))
			continue;

		freq = __clock_get_rate(cl, load);
		if (clk_ctr->mode == VPU_POWER_DYNAMIC) {
			rc = clk_set_rate(cl->clk, freq);
@@ -407,7 +407,7 @@ int vpu_clock_scale(void *clkh, u32 load)
int vpu_clock_gating_off(void *clkh)
{
	int i;
	struct vpu_core_clock *cl;
	struct vpu_clock *cl;
	struct vpu_clk_control *clk_ctr = (struct vpu_clk_control *)clkh;
	int rc = 0;

@@ -420,8 +420,11 @@ int vpu_clock_gating_off(void *clkh)
	if (clk_ctr->mode != VPU_POWER_DYNAMIC)
		return 0;

	for (i = 0; i <= VPU_CLK_GATE_LEVEL; i++) {
		cl = &clk_ctr->clock[i];
	for (i = 0; i < VPU_MAX_CLKS; i++) {
		if (NOT_USED(clk_ctr, i) || !CLOCK_IS_SCALABLE(clk_ctr, i))
			continue;

		cl = clk_ctr->clock[i];
		if (cl->status == 0) {
			rc = clk_enable(cl->clk);
			if (rc) {
@@ -440,7 +443,7 @@ int vpu_clock_gating_off(void *clkh)
void vpu_clock_gating_on(void *clkh)
{
	int i;
	struct vpu_core_clock *cl;
	struct vpu_clock *cl;
	struct vpu_clk_control *clk_ctr = (struct vpu_clk_control *)clkh;

	if (!clk_ctr) {
@@ -452,8 +455,11 @@ void vpu_clock_gating_on(void *clkh)
	if (clk_ctr->mode != VPU_POWER_DYNAMIC)
		return;

	for (i = 0; i <= VPU_CLK_GATE_LEVEL; i++) {
		cl = &clk_ctr->clock[i];
	for (i = 0; i < VPU_MAX_CLKS; i++) {
		if (NOT_USED(clk_ctr, i) || !CLOCK_IS_SCALABLE(clk_ctr, i))
			continue;

		cl = clk_ctr->clock[i];
		if (cl->status) {
			clk_disable(cl->clk);
			cl->status = 0;
@@ -477,12 +483,16 @@ void vpu_clock_mode_set(void *clkh, enum vpu_power_mode mode)

	if (mode <= VPU_POWER_DYNAMIC) {
		clk_ctr->mode = mode;
		for (i = 0; i <= VPU_CLK_GATE_LEVEL; i++) {
			struct vpu_core_clock *cl = &clk_ctr->clock[i];
		for (i = 0; i < VPU_MAX_CLKS; i++) {
			struct vpu_clock *cl = clk_ctr->clock[i];
			unsigned long freq;

			if (NOT_USED(clk_ctr, i) ||
					!CLOCK_IS_SCALABLE(clk_ctr, i))
				continue;

			if (mode < VPU_POWER_DYNAMIC)
				freq = clock_freqs[i][mode];
				freq = cl->pwr_frequencies[mode];
			else
				freq = cl->current_freq;

+3 −11
Original line number Diff line number Diff line
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -26,21 +26,13 @@ int vpu_bus_scale(u32 load);
void *vpu_clock_init(struct vpu_platform_resources *res);
void vpu_clock_deinit(void *clk_handle);

int  vpu_clock_enable(void *clk_handle);
void vpu_clock_disable(void *clk_handle);
int  vpu_clock_enable(void *clk_handle, u32 clk_group);
void vpu_clock_disable(void *clk_handle, u32 clk_group);
int vpu_clock_scale(void *clk_handle, u32 load);

int vpu_clock_gating_off(void *clkh);
void vpu_clock_gating_on(void *clkh);

enum vpu_power_mode {
	VPU_POWER_SVS,
	VPU_POWER_NOMINAL,
	VPU_POWER_TURBO,
	VPU_POWER_DYNAMIC,
	VPU_POWER_MAX = VPU_POWER_DYNAMIC
};

void vpu_clock_mode_set(void *clkh, enum vpu_power_mode mode);
enum vpu_power_mode vpu_clock_mode_get(void *clkh);
#endif /* __H_VPU_BUS_CLOCK_H__ */
+4 −4
Original line number Diff line number Diff line
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -1947,7 +1947,7 @@ static int vpu_hw_power_on(struct vpu_channel_hal *hal)
	}

	/* enable the VPU clocks */
	rc = vpu_clock_enable(hal->clk_handle);
	rc = vpu_clock_enable(hal->clk_handle, CLOCK_BOOT);
	if (unlikely(rc)) {
		pr_err("failed to enable clock\n");
		goto err_clock;
@@ -1970,7 +1970,7 @@ err_power:
 */
static void vpu_hw_power_off(struct vpu_channel_hal *hal)
{
	vpu_clock_disable(hal->clk_handle);
	vpu_clock_disable(hal->clk_handle, CLOCK_ALL_GROUPS);
	vpu_bus_unvote();
	if (hal->vdd_enabled) {
		regulator_disable(hal->vdd);
@@ -1993,7 +1993,7 @@ int vpu_hw_sys_suspend(void)
	/* TODO: Send suspend IPC command once it is defined	*/

	/* make sure clock are on */
	rc = vpu_clock_enable(ch_hal->clk_handle);
	rc = vpu_clock_enable(ch_hal->clk_handle, CLOCK_BOOT);
	if (rc) {
		pr_err("clock off when trying to suspend\n");
		goto suspend_exit;
+165 −11
Original line number Diff line number Diff line
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -78,10 +78,110 @@ static struct vpu_iommu_map iommus_array[VPU_MAX_IOMMU_DOMAIN] = {
	},
};

static const char * const clk_table_dt_entries[] = {
	[VPU_BUS_CLK] = "qcom,bus-clk-load-freq-tbl",
	[VPU_MAPLE_CLK] = "qcom,maple-clk-load-freq-tbl",
	[VPU_VDP_CLK] = "qcom,vdp-clk-load-freq-tbl",
struct vpu_clock_descr {
	char *name;
	u32 flag;	/* enum vpu_clock_flag */
	u32 pwr_frequencies[VPU_POWER_MAX];
	char *load_freq_dt_entry;
};

const struct vpu_clock_descr vpu_clock_set[VPU_MAX_CLKS] = {
		[VPU_BUS_CLK] = {
			.name = "vdp_bus_clk",
			.flag = CLOCK_CORE | CLOCK_BOOT | CLOCK_SCALABLE,
			.pwr_frequencies = { 40000000,  80000000,  80000000},
			.load_freq_dt_entry = "qcom,bus-clk-load-freq-tbl",
		},
		[VPU_MAPLE_CLK] = {
			.name = "core_clk",
			.flag = CLOCK_CORE | CLOCK_BOOT | CLOCK_SCALABLE,
			.pwr_frequencies = {200000000, 400000000, 400000000},
			.load_freq_dt_entry = "qcom,maple-clk-load-freq-tbl",
		},
		[VPU_VDP_CLK] = {
			.name = "vdp_clk",
			.flag = CLOCK_CORE | CLOCK_BOOT | CLOCK_SCALABLE,
			.pwr_frequencies = {200000000, 200000000, 400000000},
			.load_freq_dt_entry = "qcom,vdp-clk-load-freq-tbl",
		},
		[VPU_VDP_XIN] = {
			.name = "vdp_xin_clk",
			.flag = CLOCK_CORE | CLOCK_BOOT | CLOCK_SCALABLE,
			.pwr_frequencies = {200000000, 467000000, 467000000},
			.load_freq_dt_entry = "qcom,vdp-xin-clk-load-freq-tbl",
		},
		[VPU_AHB_CLK] = {
			.name = "iface_clk",
			.flag = CLOCK_CORE | CLOCK_BOOT,
		},
		[VPU_AXI_CLK] = {
			.name = "bus_clk",
			.flag = CLOCK_CORE | CLOCK_BOOT,
		},
		[VPU_SLEEP_CLK] = {
			.name = "sleep_clk",
			.flag = CLOCK_CORE | CLOCK_BOOT,
		},
		[VPU_CXO_CLK] = {
			.name = "cxo_clk",
			.flag = CLOCK_CORE | CLOCK_BOOT,
		},
		[VPU_MAPLE_AXI_CLK] = {
			.name = "maple_bus_clk",
			.flag = CLOCK_CORE | CLOCK_BOOT,
		},
		[VPU_PRNG_CLK] = {
			.name = "prng_clk",
			.flag = CLOCK_CORE | CLOCK_BOOT,
		},
		[VPU_FRC_GPROC] {
			.name = "gproc_clk",
			.flag = CLOCK_FRC | CLOCK_BOOT,
		},
		[VPU_FRC_KPROC] {
			.name = "kproc_clk",
			.flag = CLOCK_FRC | CLOCK_BOOT,
		},
		[VPU_FRC_SDMC_FRCS] {
			.name = "sdmc_frcs_clk",
			.flag = CLOCK_FRC,
		},
		[VPU_FRC_SDME_FRCF] {
			.name = "sdme_frcf_clk",
			.flag = CLOCK_FRC,
		},
		[VPU_FRC_SDME_FRCS] {
			.name = "sdme_frcs_clk",
			.flag = CLOCK_FRC,
		},
		[VPU_FRC_SDME_VPRO] {
			.name = "sdme_vproc_clk",
			.flag = CLOCK_FRC,
		},
		[VPU_FRC_HDMC_FRCF] {
			.name = "hdmc_frcf_clk",
			.flag = CLOCK_FRC,
		},
		[VPU_FRC_PREPROC] {
			.name = "preproc_clk",
			.flag = CLOCK_FRC,
		},
		[VPU_FRC_FRC_XIN] {
			.name = "frc_xin_clk",
			.flag = CLOCK_FRC,
		},
		[VPU_FRC_MAPLE_AXI] {
			.name = "maple_axi_clk",
			.flag = CLOCK_FRC,
		},
		[VPU_QDSS_AT] {
			.name = "qdss_at_clk",
			.flag = CLOCK_QDSS,
		},
		[VPU_QDSS_TSCTR_DIV8] {
			.name = "qdss_tsctr_div8_clk",
			.flag = CLOCK_QDSS,
		},
};

struct bus_pdata_config {
@@ -125,7 +225,7 @@ static int __vpu_load_freq_table(struct vpu_platform_resources *res,
	clk_table->entry = devm_kzalloc(&pdev->dev,
		num_elements * sizeof(*clk_table->entry), GFP_KERNEL);
	if (!clk_table->entry) {
		pr_err("Failed alloc load_freq_tabll\n");
		pr_err("Failed alloc load_freq_table\n");
		return -ENOMEM;
	}

@@ -148,12 +248,17 @@ static int __vpu_load_freq_tables(struct vpu_platform_resources *res)
{
	int ret = 0, i;

	for (i = 0; i < ARRAY_SIZE(clk_table_dt_entries); i++) {
		ret = __vpu_load_freq_table(res, clk_table_dt_entries[i],
				&res->clock_tables[i]);
	for (i = 0; i < VPU_MAX_CLKS; i++) {
		struct vpu_clock *cl = &res->clock[i];

		if ((cl->flag & CLOCK_PRESENT) && (cl->flag & CLOCK_SCALABLE)) {
			ret = __vpu_load_freq_table(res,
					vpu_clock_set[i].load_freq_dt_entry,
					&cl->load_freq_tbl);
			if (ret)
				return ret;
		}
	}

	return ret;
}
@@ -259,6 +364,49 @@ static int __vpu_load_bus_vectors(struct vpu_platform_resources *res)
	return 0;
}

static int __vpu_load_clk_names(struct vpu_platform_resources *res)
{
	int ret = 0, i, j, num_elements;
	struct platform_device *pdev = res->pdev;
	const char *name;

	num_elements = of_property_count_strings(pdev->dev.of_node,
				"qcom,clock-names");
	if (num_elements <= 0) {
		pr_err("No valid clock list in device tree.\n");
		return -EINVAL;
	} else if (num_elements > VPU_MAX_CLKS) {
		pr_err("List of clocks to enable is too large\n");
		return -EINVAL;
	}

	for (i = 0; i < num_elements; i++) {
		bool found = false;
		ret = of_property_read_string_index(pdev->dev.of_node,
				"qcom,clock-names", i, &name);
		if (ret)
			return ret;

		for (j = 0; j < VPU_MAX_CLKS; j++) {
			if (strcmp(name, vpu_clock_set[j].name) == 0) {
				res->clock[j].name = vpu_clock_set[j].name;
				res->clock[j].flag = vpu_clock_set[j].flag |
								CLOCK_PRESENT;
				res->clock[j].pwr_frequencies =
					vpu_clock_set[j].pwr_frequencies;
				found = true;
				break;
			}
		}
		if (!found) {
			pr_err("clock %s not found\n", name);
			return -EINVAL;
		}
	}

	return 0;
}

static int __vpu_load_iommu_maps(struct vpu_platform_resources *res)
{
	int ret = 0, i, j, num_elements;
@@ -351,6 +499,12 @@ int read_vpu_platform_resources(struct vpu_platform_resources *res,
			GFP_KERNEL))
		return -ENOMEM;

	ret = __vpu_load_clk_names(res);
	if (ret) {
		pr_err("Failed to load clock names: %d\n", ret);
		goto err_read_dt_resources;
	}

	ret = __vpu_load_freq_tables(res);
	if (ret) {
		pr_err("Failed to load freq tables: %d\n", ret);
Loading