xref: /openbmc/linux/drivers/clk/zynqmp/clk-mux-zynqmp.c (revision 1188f7f111c61394ec56beb8e30322305a8220b6)
13fde0e16SJolly Shah // SPDX-License-Identifier: GPL-2.0
23fde0e16SJolly Shah /*
33fde0e16SJolly Shah  * Zynq UltraScale+ MPSoC mux
43fde0e16SJolly Shah  *
53fde0e16SJolly Shah  *  Copyright (C) 2016-2018 Xilinx
63fde0e16SJolly Shah  */
73fde0e16SJolly Shah 
83fde0e16SJolly Shah #include <linux/clk-provider.h>
93fde0e16SJolly Shah #include <linux/slab.h>
103fde0e16SJolly Shah #include "clk-zynqmp.h"
113fde0e16SJolly Shah 
123fde0e16SJolly Shah /*
133fde0e16SJolly Shah  * DOC: basic adjustable multiplexer clock that cannot gate
143fde0e16SJolly Shah  *
153fde0e16SJolly Shah  * Traits of this clock:
163fde0e16SJolly Shah  * prepare - clk_prepare only ensures that parents are prepared
173fde0e16SJolly Shah  * enable - clk_enable only ensures that parents are enabled
183fde0e16SJolly Shah  * rate - rate is only affected by parent switching.  No clk_set_rate support
193fde0e16SJolly Shah  * parent - parent is adjustable through clk_set_parent
203fde0e16SJolly Shah  */
213fde0e16SJolly Shah 
223fde0e16SJolly Shah /**
233fde0e16SJolly Shah  * struct zynqmp_clk_mux - multiplexer clock
243fde0e16SJolly Shah  *
253fde0e16SJolly Shah  * @hw:		handle between common and hardware-specific interfaces
263fde0e16SJolly Shah  * @flags:	hardware-specific flags
273fde0e16SJolly Shah  * @clk_id:	Id of clock
283fde0e16SJolly Shah  */
293fde0e16SJolly Shah struct zynqmp_clk_mux {
303fde0e16SJolly Shah 	struct clk_hw hw;
313fde0e16SJolly Shah 	u8 flags;
323fde0e16SJolly Shah 	u32 clk_id;
333fde0e16SJolly Shah };
343fde0e16SJolly Shah 
353fde0e16SJolly Shah #define to_zynqmp_clk_mux(_hw) container_of(_hw, struct zynqmp_clk_mux, hw)
363fde0e16SJolly Shah 
373fde0e16SJolly Shah /**
383fde0e16SJolly Shah  * zynqmp_clk_mux_get_parent() - Get parent of clock
393fde0e16SJolly Shah  * @hw:		handle between common and hardware-specific interfaces
403fde0e16SJolly Shah  *
416c9feabcSMichal Simek  * Return: Parent index on success or number of parents in case of error
423fde0e16SJolly Shah  */
zynqmp_clk_mux_get_parent(struct clk_hw * hw)433fde0e16SJolly Shah static u8 zynqmp_clk_mux_get_parent(struct clk_hw *hw)
443fde0e16SJolly Shah {
453fde0e16SJolly Shah 	struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw);
463fde0e16SJolly Shah 	const char *clk_name = clk_hw_get_name(hw);
473fde0e16SJolly Shah 	u32 clk_id = mux->clk_id;
483fde0e16SJolly Shah 	u32 val;
493fde0e16SJolly Shah 	int ret;
503fde0e16SJolly Shah 
5170c0d364SRajan Vaja 	ret = zynqmp_pm_clock_getparent(clk_id, &val);
523fde0e16SJolly Shah 
536c9feabcSMichal Simek 	if (ret) {
544917394eSMichael Tretter 		pr_debug("%s() getparent failed for clock: %s, ret = %d\n",
553fde0e16SJolly Shah 			 __func__, clk_name, ret);
566c9feabcSMichal Simek 		/*
576c9feabcSMichal Simek 		 * clk_core_get_parent_by_index() takes num_parents as incorrect
586c9feabcSMichal Simek 		 * index which is exactly what I want to return here
596c9feabcSMichal Simek 		 */
606c9feabcSMichal Simek 		return clk_hw_get_num_parents(hw);
616c9feabcSMichal Simek 	}
623fde0e16SJolly Shah 
633fde0e16SJolly Shah 	return val;
643fde0e16SJolly Shah }
653fde0e16SJolly Shah 
663fde0e16SJolly Shah /**
673fde0e16SJolly Shah  * zynqmp_clk_mux_set_parent() - Set parent of clock
683fde0e16SJolly Shah  * @hw:		handle between common and hardware-specific interfaces
693fde0e16SJolly Shah  * @index:	Parent index
703fde0e16SJolly Shah  *
713fde0e16SJolly Shah  * Return: 0 on success else error+reason
723fde0e16SJolly Shah  */
zynqmp_clk_mux_set_parent(struct clk_hw * hw,u8 index)733fde0e16SJolly Shah static int zynqmp_clk_mux_set_parent(struct clk_hw *hw, u8 index)
743fde0e16SJolly Shah {
753fde0e16SJolly Shah 	struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw);
763fde0e16SJolly Shah 	const char *clk_name = clk_hw_get_name(hw);
773fde0e16SJolly Shah 	u32 clk_id = mux->clk_id;
783fde0e16SJolly Shah 	int ret;
793fde0e16SJolly Shah 
8070c0d364SRajan Vaja 	ret = zynqmp_pm_clock_setparent(clk_id, index);
813fde0e16SJolly Shah 
823fde0e16SJolly Shah 	if (ret)
834917394eSMichael Tretter 		pr_debug("%s() set parent failed for clock: %s, ret = %d\n",
843fde0e16SJolly Shah 			 __func__, clk_name, ret);
853fde0e16SJolly Shah 
863fde0e16SJolly Shah 	return ret;
873fde0e16SJolly Shah }
883fde0e16SJolly Shah 
893fde0e16SJolly Shah static const struct clk_ops zynqmp_clk_mux_ops = {
903fde0e16SJolly Shah 	.get_parent = zynqmp_clk_mux_get_parent,
913fde0e16SJolly Shah 	.set_parent = zynqmp_clk_mux_set_parent,
92*9b2dcd1bSJay Buddhabhatti 	.determine_rate = __clk_mux_determine_rate_closest,
933fde0e16SJolly Shah };
943fde0e16SJolly Shah 
953fde0e16SJolly Shah static const struct clk_ops zynqmp_clk_mux_ro_ops = {
963fde0e16SJolly Shah 	.get_parent = zynqmp_clk_mux_get_parent,
973fde0e16SJolly Shah };
983fde0e16SJolly Shah 
zynqmp_clk_map_mux_ccf_flags(const u32 zynqmp_type_flag)9954530ed1SRajan Vaja static inline unsigned long zynqmp_clk_map_mux_ccf_flags(
10054530ed1SRajan Vaja 				       const u32 zynqmp_type_flag)
10154530ed1SRajan Vaja {
10254530ed1SRajan Vaja 	unsigned long ccf_flag = 0;
10354530ed1SRajan Vaja 
10454530ed1SRajan Vaja 	if (zynqmp_type_flag & ZYNQMP_CLK_MUX_INDEX_ONE)
10554530ed1SRajan Vaja 		ccf_flag |= CLK_MUX_INDEX_ONE;
10654530ed1SRajan Vaja 	if (zynqmp_type_flag & ZYNQMP_CLK_MUX_INDEX_BIT)
10754530ed1SRajan Vaja 		ccf_flag |= CLK_MUX_INDEX_BIT;
10854530ed1SRajan Vaja 	if (zynqmp_type_flag & ZYNQMP_CLK_MUX_HIWORD_MASK)
10954530ed1SRajan Vaja 		ccf_flag |= CLK_MUX_HIWORD_MASK;
11054530ed1SRajan Vaja 	if (zynqmp_type_flag & ZYNQMP_CLK_MUX_READ_ONLY)
11154530ed1SRajan Vaja 		ccf_flag |= CLK_MUX_READ_ONLY;
11254530ed1SRajan Vaja 	if (zynqmp_type_flag & ZYNQMP_CLK_MUX_ROUND_CLOSEST)
11354530ed1SRajan Vaja 		ccf_flag |= CLK_MUX_ROUND_CLOSEST;
11454530ed1SRajan Vaja 	if (zynqmp_type_flag & ZYNQMP_CLK_MUX_BIG_ENDIAN)
11554530ed1SRajan Vaja 		ccf_flag |= CLK_MUX_BIG_ENDIAN;
11654530ed1SRajan Vaja 
11754530ed1SRajan Vaja 	return ccf_flag;
11854530ed1SRajan Vaja }
11954530ed1SRajan Vaja 
1203fde0e16SJolly Shah /**
1213fde0e16SJolly Shah  * zynqmp_clk_register_mux() - Register a mux table with the clock
1223fde0e16SJolly Shah  *			       framework
1233fde0e16SJolly Shah  * @name:		Name of this clock
1243fde0e16SJolly Shah  * @clk_id:		Id of this clock
1253fde0e16SJolly Shah  * @parents:		Name of this clock's parents
1263fde0e16SJolly Shah  * @num_parents:	Number of parents
1273fde0e16SJolly Shah  * @nodes:		Clock topology node
1283fde0e16SJolly Shah  *
1293fde0e16SJolly Shah  * Return: clock hardware of the registered clock mux
1303fde0e16SJolly Shah  */
zynqmp_clk_register_mux(const char * name,u32 clk_id,const char * const * parents,u8 num_parents,const struct clock_topology * nodes)1313fde0e16SJolly Shah struct clk_hw *zynqmp_clk_register_mux(const char *name, u32 clk_id,
1323fde0e16SJolly Shah 				       const char * const *parents,
1333fde0e16SJolly Shah 				       u8 num_parents,
1343fde0e16SJolly Shah 				       const struct clock_topology *nodes)
1353fde0e16SJolly Shah {
1363fde0e16SJolly Shah 	struct zynqmp_clk_mux *mux;
1373fde0e16SJolly Shah 	struct clk_hw *hw;
1383fde0e16SJolly Shah 	struct clk_init_data init;
1393fde0e16SJolly Shah 	int ret;
1403fde0e16SJolly Shah 
1413fde0e16SJolly Shah 	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
1423fde0e16SJolly Shah 	if (!mux)
1433fde0e16SJolly Shah 		return ERR_PTR(-ENOMEM);
1443fde0e16SJolly Shah 
1453fde0e16SJolly Shah 	init.name = name;
1463fde0e16SJolly Shah 	if (nodes->type_flag & CLK_MUX_READ_ONLY)
1473fde0e16SJolly Shah 		init.ops = &zynqmp_clk_mux_ro_ops;
1483fde0e16SJolly Shah 	else
1493fde0e16SJolly Shah 		init.ops = &zynqmp_clk_mux_ops;
150610a5d83SRajan Vaja 
151610a5d83SRajan Vaja 	init.flags = zynqmp_clk_map_common_ccf_flags(nodes->flag);
152610a5d83SRajan Vaja 
1533fde0e16SJolly Shah 	init.parent_names = parents;
1543fde0e16SJolly Shah 	init.num_parents = num_parents;
15554530ed1SRajan Vaja 	mux->flags = zynqmp_clk_map_mux_ccf_flags(nodes->type_flag);
1563fde0e16SJolly Shah 	mux->hw.init = &init;
1573fde0e16SJolly Shah 	mux->clk_id = clk_id;
1583fde0e16SJolly Shah 
1593fde0e16SJolly Shah 	hw = &mux->hw;
1603fde0e16SJolly Shah 	ret = clk_hw_register(NULL, hw);
1613fde0e16SJolly Shah 	if (ret) {
162e7296d16SShubhrajyoti Datta 		kfree(mux);
1633fde0e16SJolly Shah 		hw = ERR_PTR(ret);
1643fde0e16SJolly Shah 	}
1653fde0e16SJolly Shah 
1663fde0e16SJolly Shah 	return hw;
1673fde0e16SJolly Shah }
168