~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/misc/atmel_tclib.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include <linux/atmel_tc.h>
 
2
#include <linux/clk.h>
 
3
#include <linux/err.h>
 
4
#include <linux/init.h>
 
5
#include <linux/io.h>
 
6
#include <linux/ioport.h>
 
7
#include <linux/kernel.h>
 
8
#include <linux/platform_device.h>
 
9
#include <linux/slab.h>
 
10
#include <linux/export.h>
 
11
 
 
12
/* Number of bytes to reserve for the iomem resource */
 
13
#define ATMEL_TC_IOMEM_SIZE     256
 
14
 
 
15
 
 
16
/*
 
17
 * This is a thin library to solve the problem of how to portably allocate
 
18
 * one of the TC blocks.  For simplicity, it doesn't currently expect to
 
19
 * share individual timers between different drivers.
 
20
 */
 
21
 
 
22
#if defined(CONFIG_AVR32)
 
23
/* AVR32 has these divide PBB */
 
24
const u8 atmel_tc_divisors[5] = { 0, 4, 8, 16, 32, };
 
25
EXPORT_SYMBOL(atmel_tc_divisors);
 
26
 
 
27
#elif defined(CONFIG_ARCH_AT91)
 
28
/* AT91 has these divide MCK */
 
29
const u8 atmel_tc_divisors[5] = { 2, 8, 32, 128, 0, };
 
30
EXPORT_SYMBOL(atmel_tc_divisors);
 
31
 
 
32
#endif
 
33
 
 
34
static DEFINE_SPINLOCK(tc_list_lock);
 
35
static LIST_HEAD(tc_list);
 
36
 
 
37
/**
 
38
 * atmel_tc_alloc - allocate a specified TC block
 
39
 * @block: which block to allocate
 
40
 * @name: name to be associated with the iomem resource
 
41
 *
 
42
 * Caller allocates a block.  If it is available, a pointer to a
 
43
 * pre-initialized struct atmel_tc is returned. The caller can access
 
44
 * the registers directly through the "regs" field.
 
45
 */
 
46
struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name)
 
47
{
 
48
        struct atmel_tc         *tc;
 
49
        struct platform_device  *pdev = NULL;
 
50
        struct resource         *r;
 
51
 
 
52
        spin_lock(&tc_list_lock);
 
53
        list_for_each_entry(tc, &tc_list, node) {
 
54
                if (tc->pdev->id == block) {
 
55
                        pdev = tc->pdev;
 
56
                        break;
 
57
                }
 
58
        }
 
59
 
 
60
        if (!pdev || tc->iomem)
 
61
                goto fail;
 
62
 
 
63
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
64
        r = request_mem_region(r->start, ATMEL_TC_IOMEM_SIZE, name);
 
65
        if (!r)
 
66
                goto fail;
 
67
 
 
68
        tc->regs = ioremap(r->start, ATMEL_TC_IOMEM_SIZE);
 
69
        if (!tc->regs)
 
70
                goto fail_ioremap;
 
71
 
 
72
        tc->iomem = r;
 
73
 
 
74
out:
 
75
        spin_unlock(&tc_list_lock);
 
76
        return tc;
 
77
 
 
78
fail_ioremap:
 
79
        release_mem_region(r->start, ATMEL_TC_IOMEM_SIZE);
 
80
fail:
 
81
        tc = NULL;
 
82
        goto out;
 
83
}
 
84
EXPORT_SYMBOL_GPL(atmel_tc_alloc);
 
85
 
 
86
/**
 
87
 * atmel_tc_free - release a specified TC block
 
88
 * @tc: Timer/counter block that was returned by atmel_tc_alloc()
 
89
 *
 
90
 * This reverses the effect of atmel_tc_alloc(), unmapping the I/O
 
91
 * registers, invalidating the resource returned by that routine and
 
92
 * making the TC available to other drivers.
 
93
 */
 
94
void atmel_tc_free(struct atmel_tc *tc)
 
95
{
 
96
        spin_lock(&tc_list_lock);
 
97
        if (tc->regs) {
 
98
                iounmap(tc->regs);
 
99
                release_mem_region(tc->iomem->start, ATMEL_TC_IOMEM_SIZE);
 
100
                tc->regs = NULL;
 
101
                tc->iomem = NULL;
 
102
        }
 
103
        spin_unlock(&tc_list_lock);
 
104
}
 
105
EXPORT_SYMBOL_GPL(atmel_tc_free);
 
106
 
 
107
static int __init tc_probe(struct platform_device *pdev)
 
108
{
 
109
        struct atmel_tc *tc;
 
110
        struct clk      *clk;
 
111
        int             irq;
 
112
 
 
113
        if (!platform_get_resource(pdev, IORESOURCE_MEM, 0))
 
114
                return -EINVAL;
 
115
 
 
116
        irq = platform_get_irq(pdev, 0);
 
117
        if (irq < 0)
 
118
                return -EINVAL;
 
119
 
 
120
        tc = kzalloc(sizeof(struct atmel_tc), GFP_KERNEL);
 
121
        if (!tc)
 
122
                return -ENOMEM;
 
123
 
 
124
        tc->pdev = pdev;
 
125
 
 
126
        clk = clk_get(&pdev->dev, "t0_clk");
 
127
        if (IS_ERR(clk)) {
 
128
                kfree(tc);
 
129
                return -EINVAL;
 
130
        }
 
131
 
 
132
        tc->clk[0] = clk;
 
133
        tc->clk[1] = clk_get(&pdev->dev, "t1_clk");
 
134
        if (IS_ERR(tc->clk[1]))
 
135
                tc->clk[1] = clk;
 
136
        tc->clk[2] = clk_get(&pdev->dev, "t2_clk");
 
137
        if (IS_ERR(tc->clk[2]))
 
138
                tc->clk[2] = clk;
 
139
 
 
140
        tc->irq[0] = irq;
 
141
        tc->irq[1] = platform_get_irq(pdev, 1);
 
142
        if (tc->irq[1] < 0)
 
143
                tc->irq[1] = irq;
 
144
        tc->irq[2] = platform_get_irq(pdev, 2);
 
145
        if (tc->irq[2] < 0)
 
146
                tc->irq[2] = irq;
 
147
 
 
148
        spin_lock(&tc_list_lock);
 
149
        list_add_tail(&tc->node, &tc_list);
 
150
        spin_unlock(&tc_list_lock);
 
151
 
 
152
        return 0;
 
153
}
 
154
 
 
155
static struct platform_driver tc_driver = {
 
156
        .driver.name    = "atmel_tcb",
 
157
};
 
158
 
 
159
static int __init tc_init(void)
 
160
{
 
161
        return platform_driver_probe(&tc_driver, tc_probe);
 
162
}
 
163
arch_initcall(tc_init);