linux-stable/drivers/clk/mvebu/armada-370.c
Simon Guinot 1022c75f5a clk: armada-370: fix tclk frequencies
This patch fixes the tclk frequency array for the Armada-370 SoC.
This bug has been introduced by commit 6b72333d
("clk: mvebu: add Armada 370 SoC-centric clock init").

A wrong tclk frequency affects the following drivers: mvsdio, mvneta,
i2c-mv64xxx and mvebu-devbus. This list may be incomplete.

About the mvneta Ethernet driver, note that the tclk frequency is used
to compute the Rx time coalescence. Then, this bug harms the coalescence
configuration and also degrades the networking performances with the
default values.

Signed-off-by: Simon Guinot <simon.guinot@sequanux.org>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Gregory CLEMENT <gregory.clement@free-electrons.com>
Cc: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Acked-by: Jason Cooper <jason@lakedaemon.net>
Cc: stable@vger.kernel.org
Signed-off-by: Michael Turquette <mturquette@deferred.io>
2013-10-06 17:39:46 -07:00

176 lines
4.5 KiB
C

/*
* Marvell Armada 370 SoC clocks
*
* Copyright (C) 2012 Marvell
*
* Gregory CLEMENT <gregory.clement@free-electrons.com>
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
* Andrew Lunn <andrew@lunn.ch>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
#include "common.h"
/*
* Core Clocks
*/
#define SARL 0 /* Low part [0:31] */
#define SARL_A370_PCLK_FREQ_OPT 11
#define SARL_A370_PCLK_FREQ_OPT_MASK 0xF
#define SARL_A370_FAB_FREQ_OPT 15
#define SARL_A370_FAB_FREQ_OPT_MASK 0x1F
#define SARL_A370_TCLK_FREQ_OPT 20
#define SARL_A370_TCLK_FREQ_OPT_MASK 0x1
enum { A370_CPU_TO_NBCLK, A370_CPU_TO_HCLK, A370_CPU_TO_DRAMCLK };
static const struct coreclk_ratio a370_coreclk_ratios[] __initconst = {
{ .id = A370_CPU_TO_NBCLK, .name = "nbclk" },
{ .id = A370_CPU_TO_HCLK, .name = "hclk" },
{ .id = A370_CPU_TO_DRAMCLK, .name = "dramclk" },
};
static const u32 a370_tclk_freqs[] __initconst = {
166000000,
200000000,
};
static u32 __init a370_get_tclk_freq(void __iomem *sar)
{
u8 tclk_freq_select = 0;
tclk_freq_select = ((readl(sar) >> SARL_A370_TCLK_FREQ_OPT) &
SARL_A370_TCLK_FREQ_OPT_MASK);
return a370_tclk_freqs[tclk_freq_select];
}
static const u32 a370_cpu_freqs[] __initconst = {
400000000,
533000000,
667000000,
800000000,
1000000000,
1067000000,
1200000000,
};
static u32 __init a370_get_cpu_freq(void __iomem *sar)
{
u32 cpu_freq;
u8 cpu_freq_select = 0;
cpu_freq_select = ((readl(sar) >> SARL_A370_PCLK_FREQ_OPT) &
SARL_A370_PCLK_FREQ_OPT_MASK);
if (cpu_freq_select >= ARRAY_SIZE(a370_cpu_freqs)) {
pr_err("CPU freq select unsupported %d\n", cpu_freq_select);
cpu_freq = 0;
} else
cpu_freq = a370_cpu_freqs[cpu_freq_select];
return cpu_freq;
}
static const int a370_nbclk_ratios[32][2] __initconst = {
{0, 1}, {1, 2}, {2, 2}, {2, 2},
{1, 2}, {1, 2}, {1, 1}, {2, 3},
{0, 1}, {1, 2}, {2, 4}, {0, 1},
{1, 2}, {0, 1}, {0, 1}, {2, 2},
{0, 1}, {0, 1}, {0, 1}, {1, 1},
{2, 3}, {0, 1}, {0, 1}, {0, 1},
{0, 1}, {0, 1}, {0, 1}, {1, 1},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
};
static const int a370_hclk_ratios[32][2] __initconst = {
{0, 1}, {1, 2}, {2, 6}, {2, 3},
{1, 3}, {1, 4}, {1, 2}, {2, 6},
{0, 1}, {1, 6}, {2, 10}, {0, 1},
{1, 4}, {0, 1}, {0, 1}, {2, 5},
{0, 1}, {0, 1}, {0, 1}, {1, 2},
{2, 6}, {0, 1}, {0, 1}, {0, 1},
{0, 1}, {0, 1}, {0, 1}, {1, 1},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
};
static const int a370_dramclk_ratios[32][2] __initconst = {
{0, 1}, {1, 2}, {2, 3}, {2, 3},
{1, 3}, {1, 2}, {1, 2}, {2, 6},
{0, 1}, {1, 3}, {2, 5}, {0, 1},
{1, 4}, {0, 1}, {0, 1}, {2, 5},
{0, 1}, {0, 1}, {0, 1}, {1, 1},
{2, 3}, {0, 1}, {0, 1}, {0, 1},
{0, 1}, {0, 1}, {0, 1}, {1, 1},
{0, 1}, {0, 1}, {0, 1}, {0, 1},
};
static void __init a370_get_clk_ratio(
void __iomem *sar, int id, int *mult, int *div)
{
u32 opt = ((readl(sar) >> SARL_A370_FAB_FREQ_OPT) &
SARL_A370_FAB_FREQ_OPT_MASK);
switch (id) {
case A370_CPU_TO_NBCLK:
*mult = a370_nbclk_ratios[opt][0];
*div = a370_nbclk_ratios[opt][1];
break;
case A370_CPU_TO_HCLK:
*mult = a370_hclk_ratios[opt][0];
*div = a370_hclk_ratios[opt][1];
break;
case A370_CPU_TO_DRAMCLK:
*mult = a370_dramclk_ratios[opt][0];
*div = a370_dramclk_ratios[opt][1];
break;
}
}
static const struct coreclk_soc_desc a370_coreclks = {
.get_tclk_freq = a370_get_tclk_freq,
.get_cpu_freq = a370_get_cpu_freq,
.get_clk_ratio = a370_get_clk_ratio,
.ratios = a370_coreclk_ratios,
.num_ratios = ARRAY_SIZE(a370_coreclk_ratios),
};
static void __init a370_coreclk_init(struct device_node *np)
{
mvebu_coreclk_setup(np, &a370_coreclks);
}
CLK_OF_DECLARE(a370_core_clk, "marvell,armada-370-core-clock",
a370_coreclk_init);
/*
* Clock Gating Control
*/
static const struct clk_gating_soc_desc a370_gating_desc[] __initconst = {
{ "audio", NULL, 0, 0 },
{ "pex0_en", NULL, 1, 0 },
{ "pex1_en", NULL, 2, 0 },
{ "ge1", NULL, 3, 0 },
{ "ge0", NULL, 4, 0 },
{ "pex0", "pex0_en", 5, 0 },
{ "pex1", "pex1_en", 9, 0 },
{ "sata0", NULL, 15, 0 },
{ "sdio", NULL, 17, 0 },
{ "tdm", NULL, 25, 0 },
{ "ddr", NULL, 28, CLK_IGNORE_UNUSED },
{ "sata1", NULL, 30, 0 },
{ }
};
static void __init a370_clk_gating_init(struct device_node *np)
{
mvebu_clk_gating_setup(np, a370_gating_desc);
}
CLK_OF_DECLARE(a370_clk_gating, "marvell,armada-370-gating-clock",
a370_clk_gating_init);