1 /*
2  * Clock driver for the ARM Integrator/IM-PD1 board
3  * Copyright (C) 2012-2013 Linus Walleij
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  */
9 #include <linux/clk-provider.h>
10 #include <linux/clk.h>
11 #include <linux/clkdev.h>
12 #include <linux/err.h>
13 #include <linux/io.h>
14 #include <linux/platform_data/clk-integrator.h>
15 
16 #include <mach/impd1.h>
17 
18 #include "clk-icst.h"
19 
20 struct impd1_clk {
21 	char *vco1name;
22 	struct clk *vco1clk;
23 	char *vco2name;
24 	struct clk *vco2clk;
25 	struct clk *mmciclk;
26 	char *uartname;
27 	struct clk *uartclk;
28 	char *spiname;
29 	struct clk *spiclk;
30 	char *scname;
31 	struct clk *scclk;
32 	struct clk_lookup *clks[6];
33 };
34 
35 /* One entry for each connected IM-PD1 LM */
36 static struct impd1_clk impd1_clks[4];
37 
38 /*
39  * There are two VCO's on the IM-PD1
40  */
41 
42 static const struct icst_params impd1_vco1_params = {
43 	.ref		= 24000000,	/* 24 MHz */
44 	.vco_max	= ICST525_VCO_MAX_3V,
45 	.vco_min	= ICST525_VCO_MIN,
46 	.vd_min		= 12,
47 	.vd_max		= 519,
48 	.rd_min		= 3,
49 	.rd_max		= 120,
50 	.s2div		= icst525_s2div,
51 	.idx2s		= icst525_idx2s,
52 };
53 
54 static const struct clk_icst_desc impd1_icst1_desc = {
55 	.params = &impd1_vco1_params,
56 	.vco_offset = IMPD1_OSC1,
57 	.lock_offset = IMPD1_LOCK,
58 };
59 
60 static const struct icst_params impd1_vco2_params = {
61 	.ref		= 24000000,	/* 24 MHz */
62 	.vco_max	= ICST525_VCO_MAX_3V,
63 	.vco_min	= ICST525_VCO_MIN,
64 	.vd_min		= 12,
65 	.vd_max		= 519,
66 	.rd_min		= 3,
67 	.rd_max		= 120,
68 	.s2div		= icst525_s2div,
69 	.idx2s		= icst525_idx2s,
70 };
71 
72 static const struct clk_icst_desc impd1_icst2_desc = {
73 	.params = &impd1_vco2_params,
74 	.vco_offset = IMPD1_OSC2,
75 	.lock_offset = IMPD1_LOCK,
76 };
77 
78 /**
79  * integrator_impd1_clk_init() - set up the integrator clock tree
80  * @base: base address of the logic module (LM)
81  * @id: the ID of this LM
82  */
83 void integrator_impd1_clk_init(void __iomem *base, unsigned int id)
84 {
85 	struct impd1_clk *imc;
86 	struct clk *clk;
87 	int i;
88 
89 	if (id > 3) {
90 		pr_crit("no more than 4 LMs can be attached\n");
91 		return;
92 	}
93 	imc = &impd1_clks[id];
94 
95 	imc->vco1name = kasprintf(GFP_KERNEL, "lm%x-vco1", id);
96 	clk = icst_clk_register(NULL, &impd1_icst1_desc, imc->vco1name, base);
97 	imc->vco1clk = clk;
98 	imc->clks[0] = clkdev_alloc(clk, NULL, "lm%x:01000", id);
99 
100 	/* VCO2 is also called "CLK2" */
101 	imc->vco2name = kasprintf(GFP_KERNEL, "lm%x-vco2", id);
102 	clk = icst_clk_register(NULL, &impd1_icst2_desc, imc->vco2name, base);
103 	imc->vco2clk = clk;
104 
105 	/* MMCI uses CLK2 right off */
106 	imc->clks[1] = clkdev_alloc(clk, NULL, "lm%x:00700", id);
107 
108 	/* UART reference clock divides CLK2 by a fixed factor 4 */
109 	imc->uartname = kasprintf(GFP_KERNEL, "lm%x-uartclk", id);
110 	clk = clk_register_fixed_factor(NULL, imc->uartname, imc->vco2name,
111 				   CLK_IGNORE_UNUSED, 1, 4);
112 	imc->uartclk = clk;
113 	imc->clks[2] = clkdev_alloc(clk, NULL, "lm%x:00100", id);
114 	imc->clks[3] = clkdev_alloc(clk, NULL, "lm%x:00200", id);
115 
116 	/* SPI PL022 clock divides CLK2 by a fixed factor 64 */
117 	imc->spiname = kasprintf(GFP_KERNEL, "lm%x-spiclk", id);
118 	clk = clk_register_fixed_factor(NULL, imc->spiname, imc->vco2name,
119 				   CLK_IGNORE_UNUSED, 1, 64);
120 	imc->clks[4] = clkdev_alloc(clk, NULL, "lm%x:00300", id);
121 
122 	/* Smart Card clock divides CLK2 by a fixed factor 4 */
123 	imc->scname = kasprintf(GFP_KERNEL, "lm%x-scclk", id);
124 	clk = clk_register_fixed_factor(NULL, imc->scname, imc->vco2name,
125 				   CLK_IGNORE_UNUSED, 1, 4);
126 	imc->scclk = clk;
127 	imc->clks[5] = clkdev_alloc(clk, NULL, "lm%x:00600", id);
128 
129 	for (i = 0; i < ARRAY_SIZE(imc->clks); i++)
130 		clkdev_add(imc->clks[i]);
131 }
132 
133 void integrator_impd1_clk_exit(unsigned int id)
134 {
135 	int i;
136 	struct impd1_clk *imc;
137 
138 	if (id > 3)
139 		return;
140 	imc = &impd1_clks[id];
141 
142 	for (i = 0; i < ARRAY_SIZE(imc->clks); i++)
143 		clkdev_drop(imc->clks[i]);
144 	clk_unregister(imc->spiclk);
145 	clk_unregister(imc->uartclk);
146 	clk_unregister(imc->vco2clk);
147 	clk_unregister(imc->vco1clk);
148 	kfree(imc->scname);
149 	kfree(imc->spiname);
150 	kfree(imc->uartname);
151 	kfree(imc->vco2name);
152 	kfree(imc->vco1name);
153 }
154