summaryrefslogtreecommitdiff
path: root/arch/arm/mach-sa1100/clock.c
blob: 03c75a811cb025b8401961986c92ee66bcedd5b2 (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
/*
 *  linux/arch/arm/mach-sa1100/clock.c
 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/string.h>
#include <linux/clk.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/io.h>
#include <linux/clkdev.h>

#include <mach/hardware.h>
#include <mach/generic.h>

struct clkops {
	void			(*enable)(struct clk *);
	void			(*disable)(struct clk *);
	unsigned long		(*get_rate)(struct clk *);
};

struct clk {
	const struct clkops	*ops;
	unsigned int		enabled;
};

#define DEFINE_CLK(_name, _ops)				\
struct clk clk_##_name = {				\
		.ops	= _ops,				\
	}

static DEFINE_SPINLOCK(clocks_lock);

static void clk_gpio27_enable(struct clk *clk)
{
	/*
	 * First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111:
	 * (SA-1110 Developer's Manual, section 9.1.2.1)
	 */
	GAFR |= GPIO_32_768kHz;
	GPDR |= GPIO_32_768kHz;
	TUCR = TUCR_3_6864MHz;
}

static void clk_gpio27_disable(struct clk *clk)
{
	TUCR = 0;
	GPDR &= ~GPIO_32_768kHz;
	GAFR &= ~GPIO_32_768kHz;
}

static void clk_cpu_enable(struct clk *clk)
{
}

static void clk_cpu_disable(struct clk *clk)
{
}

static unsigned long clk_cpu_get_rate(struct clk *clk)
{
	return sa11x0_getspeed(0) * 1000;
}

int clk_enable(struct clk *clk)
{
	unsigned long flags;

	if (clk) {
		spin_lock_irqsave(&clocks_lock, flags);
		if (clk->enabled++ == 0)
			clk->ops->enable(clk);
		spin_unlock_irqrestore(&clocks_lock, flags);
	}

	return 0;
}
EXPORT_SYMBOL(clk_enable);

void clk_disable(struct clk *clk)
{
	unsigned long flags;

	if (clk) {
		WARN_ON(clk->enabled == 0);
		spin_lock_irqsave(&clocks_lock, flags);
		if (--clk->enabled == 0)
			clk->ops->disable(clk);
		spin_unlock_irqrestore(&clocks_lock, flags);
	}
}
EXPORT_SYMBOL(clk_disable);

unsigned long clk_get_rate(struct clk *clk)
{
	if (clk && clk->ops && clk->ops->get_rate)
		return clk->ops->get_rate(clk);

	return 0;
}
EXPORT_SYMBOL(clk_get_rate);

const struct clkops clk_gpio27_ops = {
	.enable		= clk_gpio27_enable,
	.disable	= clk_gpio27_disable,
};

const struct clkops clk_cpu_ops = {
	.enable		= clk_cpu_enable,
	.disable	= clk_cpu_disable,
	.get_rate	= clk_cpu_get_rate,
};

static DEFINE_CLK(gpio27, &clk_gpio27_ops);

static DEFINE_CLK(cpu, &clk_cpu_ops);

static struct clk_lookup sa11xx_clkregs[] = {
	CLKDEV_INIT("sa1111.0", NULL, &clk_gpio27),
	CLKDEV_INIT("sa1100-rtc", NULL, NULL),
	CLKDEV_INIT("sa11x0-fb", NULL, &clk_cpu),
	CLKDEV_INIT("sa11x0-pcmcia", NULL, &clk_cpu),
	/* sa1111 names devices using internal offsets, PCMCIA is at 0x1800 */
	CLKDEV_INIT("1800", NULL, &clk_cpu),
};

static int __init sa11xx_clk_init(void)
{
	clkdev_add_table(sa11xx_clkregs, ARRAY_SIZE(sa11xx_clkregs));
	return 0;
}
core_initcall(sa11xx_clk_init);