aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/clk-allo-dac.c
blob: a9844cb9454bd86981fb9276bdbf7b2d00fa22f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/*
 * Clock Driver for Allo DAC
 *
 * Author:	Baswaraj K <jaikumar@cem-solutions.net>
 *		Copyright 2016
 *		based on code by Stuart MacLean
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 */

#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/platform_device.h>

/* Clock rate of CLK44EN attached to GPIO6 pin */
#define CLK_44EN_RATE 45158400UL
/* Clock rate of CLK48EN attached to GPIO3 pin */
#define CLK_48EN_RATE 49152000UL

/**
 * struct allo_dac_clk - Common struct to the Allo DAC
 * @hw: clk_hw for the common clk framework
 * @mode: 0 => CLK44EN, 1 => CLK48EN
 */
struct clk_allo_hw {
	struct clk_hw hw;
	uint8_t mode;
};

#define to_allo_clk(_hw) container_of(_hw, struct clk_allo_hw, hw)

static const struct of_device_id clk_allo_dac_dt_ids[] = {
	{ .compatible = "allo,dac-clk",},
	{ }
};
MODULE_DEVICE_TABLE(of, clk_allo_dac_dt_ids);

static unsigned long clk_allo_dac_recalc_rate(struct clk_hw *hw,
	unsigned long parent_rate)
{
	return (to_allo_clk(hw)->mode == 0) ? CLK_44EN_RATE :
		CLK_48EN_RATE;
}

static long clk_allo_dac_round_rate(struct clk_hw *hw,
	unsigned long rate, unsigned long *parent_rate)
{
	long actual_rate;

	if (rate <= CLK_44EN_RATE) {
		actual_rate = (long)CLK_44EN_RATE;
	} else if (rate >= CLK_48EN_RATE) {
		actual_rate = (long)CLK_48EN_RATE;
	} else {
		long diff44Rate = (long)(rate - CLK_44EN_RATE);
		long diff48Rate = (long)(CLK_48EN_RATE - rate);

		if (diff44Rate < diff48Rate)
			actual_rate = (long)CLK_44EN_RATE;
		else
			actual_rate = (long)CLK_48EN_RATE;
	}
	return actual_rate;
}


static int clk_allo_dac_set_rate(struct clk_hw *hw,
	unsigned long rate, unsigned long parent_rate)
{
	unsigned long actual_rate;
	struct clk_allo_hw *clk = to_allo_clk(hw);

	actual_rate = (unsigned long)clk_allo_dac_round_rate(hw, rate,
		&parent_rate);
	clk->mode = (actual_rate == CLK_44EN_RATE) ? 0 : 1;
	return 0;
}


const struct clk_ops clk_allo_dac_rate_ops = {
	.recalc_rate = clk_allo_dac_recalc_rate,
	.round_rate = clk_allo_dac_round_rate,
	.set_rate = clk_allo_dac_set_rate,
};

static int clk_allo_dac_probe(struct platform_device *pdev)
{
	int ret;
	struct clk_allo_hw *proclk;
	struct clk *clk;
	struct device *dev;
	struct clk_init_data init;

	dev = &pdev->dev;

	proclk = kzalloc(sizeof(struct clk_allo_hw), GFP_KERNEL);
	if (!proclk)
		return -ENOMEM;

	init.name = "clk-allo-dac";
	init.ops = &clk_allo_dac_rate_ops;
	init.flags = 0;
	init.parent_names = NULL;
	init.num_parents = 0;

	proclk->mode = 0;
	proclk->hw.init = &init;

	clk = devm_clk_register(dev, &proclk->hw);
	if (!IS_ERR(clk)) {
		ret = of_clk_add_provider(dev->of_node, of_clk_src_simple_get,
			clk);
	} else {
		dev_err(dev, "Fail to register clock driver\n");
		kfree(proclk);
		ret = PTR_ERR(clk);
	}
	return ret;
}

static int clk_allo_dac_remove(struct platform_device *pdev)
{
	of_clk_del_provider(pdev->dev.of_node);
	return 0;
}

static struct platform_driver clk_allo_dac_driver = {
	.probe = clk_allo_dac_probe,
	.remove = clk_allo_dac_remove,
	.driver = {
		.name = "clk-allo-dac",
		.of_match_table = clk_allo_dac_dt_ids,
	},
};

static int __init clk_allo_dac_init(void)
{
	return platform_driver_register(&clk_allo_dac_driver);
}
core_initcall(clk_allo_dac_init);

static void __exit clk_allo_dac_exit(void)
{
	platform_driver_unregister(&clk_allo_dac_driver);
}
module_exit(clk_allo_dac_exit);

MODULE_DESCRIPTION("Allo DAC clock driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:clk-allo-dac");