Home | History | Annotate | Download | only in mvebu
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Marvell Armada 37xx SoC Peripheral clocks
      4  *
      5  * Marek Behun <marek.behun (at) nic.cz>
      6  *
      7  * Based on Linux driver by:
      8  *   Gregory CLEMENT <gregory.clement (at) free-electrons.com>
      9  */
     10 
     11 #include <common.h>
     12 #include <malloc.h>
     13 #include <clk-uclass.h>
     14 #include <clk.h>
     15 #include <dm.h>
     16 #include <asm/io.h>
     17 #include <asm/arch/cpu.h>
     18 
     19 #define TBG_SEL		0x0
     20 #define DIV_SEL0	0x4
     21 #define DIV_SEL1	0x8
     22 #define DIV_SEL2	0xC
     23 #define CLK_SEL		0x10
     24 #define CLK_DIS		0x14
     25 
     26 enum a37xx_periph_parent {
     27 	TBG_A_P		= 0,
     28 	TBG_B_P		= 1,
     29 	TBG_A_S		= 2,
     30 	TBG_B_S		= 3,
     31 	MAX_TBG_PARENTS	= 4,
     32 	XTAL		= 4,
     33 	MAX_PARENTS	= 5,
     34 };
     35 
     36 static const struct {
     37 	const char *name;
     38 	enum a37xx_periph_parent parent;
     39 } a37xx_periph_parent_names[] = {
     40 	{ "TBG-A-P", TBG_A_P },
     41 	{ "TBG-B-P", TBG_B_P },
     42 	{ "TBG-A-S", TBG_A_S },
     43 	{ "TBG-B-S", TBG_B_S },
     44 	{ "xtal",    XTAL    },
     45 };
     46 
     47 struct clk_periph;
     48 
     49 struct a37xx_periphclk {
     50 	void __iomem *reg;
     51 
     52 	ulong parents[MAX_PARENTS];
     53 
     54 	const struct clk_periph *clks;
     55 	bool clk_has_periph_parent[16];
     56 	int clk_parent[16];
     57 
     58 	int count;
     59 };
     60 
     61 struct clk_div_table {
     62 	u32 div;
     63 	u32 val;
     64 };
     65 
     66 struct clk_periph {
     67 	const char *name;
     68 
     69 	const char *parent_name;
     70 
     71 	u32 disable_bit;
     72 	int mux_shift;
     73 
     74 	const struct clk_div_table *div_table[2];
     75 	s32 div_reg_off[2];
     76 	u32 div_mask[2];
     77 	int div_shift[2];
     78 
     79 	unsigned can_gate : 1;
     80 	unsigned can_mux : 1;
     81 	unsigned dividers : 2;
     82 };
     83 
     84 static const struct clk_div_table div_table1[] = {
     85 	{ 1, 1 },
     86 	{ 2, 2 },
     87 	{ 0, 0 },
     88 };
     89 
     90 static const struct clk_div_table div_table2[] = {
     91 	{ 2, 1 },
     92 	{ 4, 2 },
     93 	{ 0, 0 },
     94 };
     95 
     96 static const struct clk_div_table div_table6[] = {
     97 	{ 1, 1 },
     98 	{ 2, 2 },
     99 	{ 3, 3 },
    100 	{ 4, 4 },
    101 	{ 5, 5 },
    102 	{ 6, 6 },
    103 	{ 0, 0 },
    104 };
    105 
    106 #define CLK_FULL_DD(_n, _d, _mux, _r0, _r1, _s0, _s1)	\
    107 	{						\
    108 		.name = #_n,				\
    109 		.disable_bit = BIT(_d),			\
    110 		.mux_shift = _mux,			\
    111 		.div_table[0] = div_table6,		\
    112 		.div_table[1] = div_table6,		\
    113 		.div_reg_off[0] = _r0,			\
    114 		.div_reg_off[1] = _r1,			\
    115 		.div_shift[0] = _s0,			\
    116 		.div_shift[1] = _s1,			\
    117 		.div_mask[0] = 7,			\
    118 		.div_mask[1] = 7,			\
    119 		.can_gate = 1,				\
    120 		.can_mux = 1,				\
    121 		.dividers = 2,				\
    122 	}
    123 
    124 #define CLK_FULL(_n, _d, _mux, _r, _s, _m, _t)	\
    125 	{					\
    126 		.name = #_n,			\
    127 		.disable_bit = BIT(_d),		\
    128 		.mux_shift = _mux,		\
    129 		.div_table[0] = _t,		\
    130 		.div_reg_off[0] = _r,		\
    131 		.div_shift[0] = _s,		\
    132 		.div_mask[0] = _m,		\
    133 		.can_gate = 1,			\
    134 		.can_mux = 1,			\
    135 		.dividers = 1,			\
    136 	}
    137 
    138 #define CLK_GATE_DIV(_n, _d, _r, _s, _m, _t, _p)	\
    139 	{						\
    140 		.name = #_n,				\
    141 		.parent_name = _p,			\
    142 		.disable_bit = BIT(_d),			\
    143 		.div_table[0] = _t,			\
    144 		.div_reg_off[0] = _r,			\
    145 		.div_shift[0] = _s,			\
    146 		.div_mask[0] = _m,			\
    147 		.can_gate = 1,				\
    148 		.dividers = 1,				\
    149 	}
    150 
    151 #define CLK_GATE(_n, _d, _p)		\
    152 	{				\
    153 		.name = #_n,		\
    154 		.parent_name = _p,	\
    155 		.disable_bit = BIT(_d),	\
    156 		.can_gate = 1,		\
    157 	}
    158 
    159 #define CLK_MUX_DIV(_n, _mux, _r, _s, _m, _t)	\
    160 	{					\
    161 		.name = #_n,			\
    162 		.mux_shift = _mux,		\
    163 		.div_table[0] = _t,		\
    164 		.div_reg_off[0] = _r,		\
    165 		.div_shift[0] = _s,		\
    166 		.div_mask[0] = _m,		\
    167 		.can_mux = 1,			\
    168 		.dividers = 1,			\
    169 	}
    170 
    171 #define CLK_MUX_DD(_n, _mux, _r0, _r1, _s0, _s1)	\
    172 	{						\
    173 		.name = #_n,				\
    174 		.mux_shift = _mux,			\
    175 		.div_table[0] = div_table6,		\
    176 		.div_table[1] = div_table6,		\
    177 		.div_reg_off[0] = _r0,			\
    178 		.div_reg_off[1] = _r1,			\
    179 		.div_shift[0] = _s0,			\
    180 		.div_shift[1] = _s1,			\
    181 		.div_mask[0] = 7,			\
    182 		.div_mask[1] = 7,			\
    183 		.can_mux = 1,				\
    184 		.dividers = 2,				\
    185 	}
    186 
    187 /* NB periph clocks */
    188 static const struct clk_periph clks_nb[] = {
    189 	CLK_FULL_DD(mmc, 2, 0, DIV_SEL2, DIV_SEL2, 16, 13),
    190 	CLK_FULL_DD(sata_host, 3, 2, DIV_SEL2, DIV_SEL2, 10, 7),
    191 	CLK_FULL_DD(sec_at, 6, 4, DIV_SEL1, DIV_SEL1, 3, 0),
    192 	CLK_FULL_DD(sec_dap, 7, 6, DIV_SEL1, DIV_SEL1, 9, 6),
    193 	CLK_FULL_DD(tscem, 8, 8, DIV_SEL1, DIV_SEL1, 15, 12),
    194 	CLK_FULL(tscem_tmx, 10, 10, DIV_SEL1, 18, 7, div_table6),
    195 	CLK_GATE(avs, 11, "xtal"),
    196 	CLK_FULL_DD(sqf, 12, 12, DIV_SEL1, DIV_SEL1, 27, 24),
    197 	CLK_FULL_DD(pwm, 13, 14, DIV_SEL0, DIV_SEL0, 3, 0),
    198 	CLK_GATE(i2c_2, 16, "xtal"),
    199 	CLK_GATE(i2c_1, 17, "xtal"),
    200 	CLK_GATE_DIV(ddr_phy, 19, DIV_SEL0, 18, 1, div_table2, "TBG-A-S"),
    201 	CLK_FULL_DD(ddr_fclk, 21, 16, DIV_SEL0, DIV_SEL0, 15, 12),
    202 	CLK_FULL(trace, 22, 18, DIV_SEL0, 20, 7, div_table6),
    203 	CLK_FULL(counter, 23, 20, DIV_SEL0, 23, 7, div_table6),
    204 	CLK_FULL_DD(eip97, 24, 24, DIV_SEL2, DIV_SEL2, 22, 19),
    205 	CLK_MUX_DIV(cpu, 22, DIV_SEL0, 28, 7, div_table6),
    206 	{ },
    207 };
    208 
    209 /* SB periph clocks */
    210 static const struct clk_periph clks_sb[] = {
    211 	CLK_MUX_DD(gbe_50, 6, DIV_SEL2, DIV_SEL2, 6, 9),
    212 	CLK_MUX_DD(gbe_core, 8, DIV_SEL1, DIV_SEL1, 18, 21),
    213 	CLK_MUX_DD(gbe_125, 10, DIV_SEL1, DIV_SEL1, 6, 9),
    214 	CLK_GATE(gbe1_50, 0, "gbe_50"),
    215 	CLK_GATE(gbe0_50, 1, "gbe_50"),
    216 	CLK_GATE(gbe1_125, 2, "gbe_125"),
    217 	CLK_GATE(gbe0_125, 3, "gbe_125"),
    218 	CLK_GATE_DIV(gbe1_core, 4, DIV_SEL1, 13, 1, div_table1, "gbe_core"),
    219 	CLK_GATE_DIV(gbe0_core, 5, DIV_SEL1, 14, 1, div_table1, "gbe_core"),
    220 	CLK_GATE_DIV(gbe_bm, 12, DIV_SEL1, 0, 1, div_table1, "gbe_core"),
    221 	CLK_FULL_DD(sdio, 11, 14, DIV_SEL0, DIV_SEL0, 3, 6),
    222 	CLK_FULL_DD(usb32_usb2_sys, 16, 16, DIV_SEL0, DIV_SEL0, 9, 12),
    223 	CLK_FULL_DD(usb32_ss_sys, 17, 18, DIV_SEL0, DIV_SEL0, 15, 18),
    224 	{ },
    225 };
    226 
    227 static inline int get_mux(struct a37xx_periphclk *priv, int shift)
    228 {
    229 	return (readl(priv->reg + TBG_SEL) >> shift) & 3;
    230 }
    231 
    232 static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id);
    233 
    234 static ulong get_parent_rate(struct a37xx_periphclk *priv, int id)
    235 {
    236 	const struct clk_periph *clk = &priv->clks[id];
    237 	ulong res;
    238 
    239 	if (clk->can_mux) {
    240 		/* parent is one of TBG clocks */
    241 		int tbg = get_mux(priv, clk->mux_shift);
    242 
    243 		res = priv->parents[tbg];
    244 	} else if (priv->clk_has_periph_parent[id]) {
    245 		/* parent is one of other periph clocks */
    246 
    247 		if (priv->clk_parent[id] >= priv->count)
    248 			return -EINVAL;
    249 
    250 		res = periph_clk_get_rate(priv, priv->clk_parent[id]);
    251 	} else {
    252 		/* otherwise parent is one of TBGs or XTAL */
    253 
    254 		if (priv->clk_parent[id] >= MAX_PARENTS)
    255 			return -EINVAL;
    256 
    257 		res = priv->parents[priv->clk_parent[id]];
    258 	}
    259 
    260 	return res;
    261 }
    262 
    263 static ulong get_div(struct a37xx_periphclk *priv,
    264 		     const struct clk_periph *clk, int idx)
    265 {
    266 	const struct clk_div_table *i;
    267 	u32 reg;
    268 
    269 	reg = readl(priv->reg + clk->div_reg_off[idx]);
    270 	reg = (reg >> clk->div_shift[idx]) & clk->div_mask[idx];
    271 
    272 	/* find divisor for register value val */
    273 	for (i = clk->div_table[idx]; i && i->div != 0; ++i)
    274 		if (i->val == reg)
    275 			return i->div;
    276 
    277 	return 0;
    278 }
    279 
    280 static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id)
    281 {
    282 	const struct clk_periph *clk = &priv->clks[id];
    283 	ulong rate, div;
    284 	int i;
    285 
    286 	rate = get_parent_rate(priv, id);
    287 	if (rate == -EINVAL)
    288 		return -EINVAL;
    289 
    290 	/* divide the parent rate by dividers */
    291 	div = 1;
    292 	for (i = 0; i < clk->dividers; ++i)
    293 		div *= get_div(priv, clk, i);
    294 
    295 	if (!div)
    296 		return 0;
    297 
    298 	return DIV_ROUND_UP(rate, div);
    299 }
    300 
    301 static ulong armada_37xx_periph_clk_get_rate(struct clk *clk)
    302 {
    303 	struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
    304 
    305 	if (clk->id >= priv->count)
    306 		return -EINVAL;
    307 
    308 	return periph_clk_get_rate(priv, clk->id);
    309 }
    310 
    311 static int periph_clk_enable(struct clk *clk, int enable)
    312 {
    313 	struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
    314 	const struct clk_periph *periph_clk = &priv->clks[clk->id];
    315 
    316 	if (clk->id >= priv->count)
    317 		return -EINVAL;
    318 
    319 	if (!periph_clk->can_gate)
    320 		return -ENOTSUPP;
    321 
    322 	if (enable)
    323 		clrbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
    324 	else
    325 		setbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
    326 
    327 	return 0;
    328 }
    329 
    330 static int armada_37xx_periph_clk_enable(struct clk *clk)
    331 {
    332 	return periph_clk_enable(clk, 1);
    333 }
    334 
    335 static int armada_37xx_periph_clk_disable(struct clk *clk)
    336 {
    337 	return periph_clk_enable(clk, 0);
    338 }
    339 
    340 #if defined(CONFIG_CMD_CLK) && defined(CONFIG_CLK_ARMADA_3720)
    341 static int armada_37xx_periph_clk_dump(struct udevice *dev)
    342 {
    343 	struct a37xx_periphclk *priv = dev_get_priv(dev);
    344 	const struct clk_periph *clks;
    345 	int i;
    346 
    347 	if (!priv)
    348 		return -ENODEV;
    349 
    350 	clks = priv->clks;
    351 
    352 	for (i = 0; i < priv->count; ++i)
    353 		printf("  %s at %lu Hz\n", clks[i].name,
    354 		       periph_clk_get_rate(priv, i));
    355 	printf("\n");
    356 
    357 	return 0;
    358 }
    359 
    360 static int clk_dump(const char *name, int (*func)(struct udevice *))
    361 {
    362 	struct udevice *dev;
    363 
    364 	if (uclass_get_device_by_name(UCLASS_CLK, name, &dev)) {
    365 		printf("Cannot find device %s\n", name);
    366 		return -ENODEV;
    367 	}
    368 
    369 	return func(dev);
    370 }
    371 
    372 int armada_37xx_tbg_clk_dump(struct udevice *);
    373 
    374 int soc_clk_dump(void)
    375 {
    376 	printf("  xtal at %u000000 Hz\n\n", get_ref_clk());
    377 
    378 	if (clk_dump("tbg@13200", armada_37xx_tbg_clk_dump))
    379 		return 1;
    380 
    381 	if (clk_dump("nb-periph-clk@13000",
    382 		     armada_37xx_periph_clk_dump))
    383 		return 1;
    384 
    385 	if (clk_dump("sb-periph-clk@18000",
    386 		     armada_37xx_periph_clk_dump))
    387 		return 1;
    388 
    389 	return 0;
    390 }
    391 #endif
    392 
    393 static int armada_37xx_periph_clk_probe(struct udevice *dev)
    394 {
    395 	struct a37xx_periphclk *priv = dev_get_priv(dev);
    396 	const struct clk_periph *clks;
    397 	int ret, i;
    398 
    399 	clks = (const struct clk_periph *)dev_get_driver_data(dev);
    400 	if (!clks)
    401 		return -ENODEV;
    402 
    403 	priv->reg = dev_read_addr_ptr(dev);
    404 	if (!priv->reg) {
    405 		dev_err(dev, "no io address\n");
    406 		return -ENODEV;
    407 	}
    408 
    409 	/* count clk_periph nodes */
    410 	priv->count = 0;
    411 	while (clks[priv->count].name)
    412 		priv->count++;
    413 
    414 	priv->clks = clks;
    415 
    416 	/* assign parent IDs to nodes which have non-NULL parent_name */
    417 	for (i = 0; i < priv->count; ++i) {
    418 		int j;
    419 
    420 		if (!clks[i].parent_name)
    421 			continue;
    422 
    423 		/* first try if parent_name is one of TBGs or XTAL */
    424 		for (j = 0; j < MAX_PARENTS; ++j)
    425 			if (!strcmp(clks[i].parent_name,
    426 				    a37xx_periph_parent_names[j].name))
    427 				break;
    428 
    429 		if (j < MAX_PARENTS) {
    430 			priv->clk_has_periph_parent[i] = false;
    431 			priv->clk_parent[i] =
    432 				a37xx_periph_parent_names[j].parent;
    433 			continue;
    434 		}
    435 
    436 		/* else parent_name should be one of other periph clocks */
    437 		for (j = 0; j < priv->count; ++j) {
    438 			if (!strcmp(clks[i].parent_name, clks[j].name))
    439 				break;
    440 		}
    441 
    442 		if (j < priv->count) {
    443 			priv->clk_has_periph_parent[i] = true;
    444 			priv->clk_parent[i] = j;
    445 			continue;
    446 		}
    447 
    448 		dev_err(dev, "undefined parent %s\n", clks[i].parent_name);
    449 		return -EINVAL;
    450 	}
    451 
    452 	for (i = 0; i < MAX_PARENTS; ++i) {
    453 		struct clk clk;
    454 
    455 		if (i == XTAL) {
    456 			priv->parents[i] = get_ref_clk() * 1000000;
    457 			continue;
    458 		}
    459 
    460 		ret = clk_get_by_index(dev, i, &clk);
    461 		if (ret) {
    462 			dev_err(dev, "one of parent clocks (%i) missing: %i\n",
    463 				i, ret);
    464 			return -ENODEV;
    465 		}
    466 
    467 		priv->parents[i] = clk_get_rate(&clk);
    468 		clk_free(&clk);
    469 	}
    470 
    471 	return 0;
    472 }
    473 
    474 static const struct clk_ops armada_37xx_periph_clk_ops = {
    475 	.get_rate = armada_37xx_periph_clk_get_rate,
    476 	.enable = armada_37xx_periph_clk_enable,
    477 	.disable = armada_37xx_periph_clk_disable,
    478 };
    479 
    480 static const struct udevice_id armada_37xx_periph_clk_ids[] = {
    481 	{
    482 		.compatible = "marvell,armada-3700-periph-clock-nb",
    483 		.data = (ulong)clks_nb,
    484 	},
    485 	{
    486 		.compatible = "marvell,armada-3700-periph-clock-sb",
    487 		.data = (ulong)clks_sb,
    488 	},
    489 	{}
    490 };
    491 
    492 U_BOOT_DRIVER(armada_37xx_periph_clk) = {
    493 	.name		= "armada_37xx_periph_clk",
    494 	.id		= UCLASS_CLK,
    495 	.of_match	= armada_37xx_periph_clk_ids,
    496 	.ops		= &armada_37xx_periph_clk_ops,
    497 	.priv_auto_alloc_size = sizeof(struct a37xx_periphclk),
    498 	.probe		= armada_37xx_periph_clk_probe,
    499 };
    500