| /* | 
 |  * Copyright (C) 2014 Google, Inc. | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or modify it | 
 |  * under the terms and conditions of the GNU General Public License, | 
 |  * version 2, as published by the Free Software Foundation. | 
 |  */ | 
 |  | 
 | #include <linux/clk.h> | 
 | #include <linux/clk-provider.h> | 
 | #include <linux/kernel.h> | 
 | #include <linux/of.h> | 
 | #include <linux/of_address.h> | 
 | #include <linux/slab.h> | 
 |  | 
 | #include "clk.h" | 
 |  | 
 | struct pistachio_clk_provider * | 
 | pistachio_clk_alloc_provider(struct device_node *node, unsigned int num_clks) | 
 | { | 
 | 	struct pistachio_clk_provider *p; | 
 |  | 
 | 	p = kzalloc(sizeof(*p), GFP_KERNEL); | 
 | 	if (!p) | 
 | 		return p; | 
 |  | 
 | 	p->clk_data.clks = kcalloc(num_clks, sizeof(struct clk *), GFP_KERNEL); | 
 | 	if (!p->clk_data.clks) | 
 | 		goto free_provider; | 
 | 	p->clk_data.clk_num = num_clks; | 
 | 	p->node = node; | 
 | 	p->base = of_iomap(node, 0); | 
 | 	if (!p->base) { | 
 | 		pr_err("Failed to map clock provider registers\n"); | 
 | 		goto free_clks; | 
 | 	} | 
 |  | 
 | 	return p; | 
 |  | 
 | free_clks: | 
 | 	kfree(p->clk_data.clks); | 
 | free_provider: | 
 | 	kfree(p); | 
 | 	return NULL; | 
 | } | 
 |  | 
 | void pistachio_clk_register_provider(struct pistachio_clk_provider *p) | 
 | { | 
 | 	unsigned int i; | 
 |  | 
 | 	for (i = 0; i < p->clk_data.clk_num; i++) { | 
 | 		if (IS_ERR(p->clk_data.clks[i])) | 
 | 			pr_warn("Failed to register clock %d: %ld\n", i, | 
 | 				PTR_ERR(p->clk_data.clks[i])); | 
 | 	} | 
 |  | 
 | 	of_clk_add_provider(p->node, of_clk_src_onecell_get, &p->clk_data); | 
 | } | 
 |  | 
 | void pistachio_clk_register_gate(struct pistachio_clk_provider *p, | 
 | 				 struct pistachio_gate *gate, | 
 | 				 unsigned int num) | 
 | { | 
 | 	struct clk *clk; | 
 | 	unsigned int i; | 
 |  | 
 | 	for (i = 0; i < num; i++) { | 
 | 		clk = clk_register_gate(NULL, gate[i].name, gate[i].parent, | 
 | 					CLK_SET_RATE_PARENT, | 
 | 					p->base + gate[i].reg, gate[i].shift, | 
 | 					0, NULL); | 
 | 		p->clk_data.clks[gate[i].id] = clk; | 
 | 	} | 
 | } | 
 |  | 
 | void pistachio_clk_register_mux(struct pistachio_clk_provider *p, | 
 | 				struct pistachio_mux *mux, | 
 | 				unsigned int num) | 
 | { | 
 | 	struct clk *clk; | 
 | 	unsigned int i; | 
 |  | 
 | 	for (i = 0; i < num; i++) { | 
 | 		clk = clk_register_mux(NULL, mux[i].name, mux[i].parents, | 
 | 				       mux[i].num_parents, | 
 | 				       CLK_SET_RATE_NO_REPARENT, | 
 | 				       p->base + mux[i].reg, mux[i].shift, | 
 | 				       get_count_order(mux[i].num_parents), | 
 | 				       0, NULL); | 
 | 		p->clk_data.clks[mux[i].id] = clk; | 
 | 	} | 
 | } | 
 |  | 
 | void pistachio_clk_register_div(struct pistachio_clk_provider *p, | 
 | 				struct pistachio_div *div, | 
 | 				unsigned int num) | 
 | { | 
 | 	struct clk *clk; | 
 | 	unsigned int i; | 
 |  | 
 | 	for (i = 0; i < num; i++) { | 
 | 		clk = clk_register_divider(NULL, div[i].name, div[i].parent, | 
 | 					   0, p->base + div[i].reg, 0, | 
 | 					   div[i].width, div[i].div_flags, | 
 | 					   NULL); | 
 | 		p->clk_data.clks[div[i].id] = clk; | 
 | 	} | 
 | } | 
 |  | 
 | void pistachio_clk_register_fixed_factor(struct pistachio_clk_provider *p, | 
 | 					 struct pistachio_fixed_factor *ff, | 
 | 					 unsigned int num) | 
 | { | 
 | 	struct clk *clk; | 
 | 	unsigned int i; | 
 |  | 
 | 	for (i = 0; i < num; i++) { | 
 | 		clk = clk_register_fixed_factor(NULL, ff[i].name, ff[i].parent, | 
 | 						0, 1, ff[i].div); | 
 | 		p->clk_data.clks[ff[i].id] = clk; | 
 | 	} | 
 | } | 
 |  | 
 | void pistachio_clk_force_enable(struct pistachio_clk_provider *p, | 
 | 				unsigned int *clk_ids, unsigned int num) | 
 | { | 
 | 	unsigned int i; | 
 | 	int err; | 
 |  | 
 | 	for (i = 0; i < num; i++) { | 
 | 		struct clk *clk = p->clk_data.clks[clk_ids[i]]; | 
 |  | 
 | 		if (IS_ERR(clk)) | 
 | 			continue; | 
 |  | 
 | 		err = clk_prepare_enable(clk); | 
 | 		if (err) | 
 | 			pr_err("Failed to enable clock %s: %d\n", | 
 | 			       __clk_get_name(clk), err); | 
 | 	} | 
 | } |