diff options
Diffstat (limited to 'arch/avr32/mach-at32ap/hsmc.c')
-rw-r--r-- | arch/avr32/mach-at32ap/hsmc.c | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/arch/avr32/mach-at32ap/hsmc.c b/arch/avr32/mach-at32ap/hsmc.c new file mode 100644 index 000000000000..7691721928a7 --- /dev/null +++ b/arch/avr32/mach-at32ap/hsmc.c | |||
@@ -0,0 +1,164 @@ | |||
1 | /* | ||
2 | * Static Memory Controller for AT32 chips | ||
3 | * | ||
4 | * Copyright (C) 2006 Atmel Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | #define DEBUG | ||
11 | #include <linux/clk.h> | ||
12 | #include <linux/err.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | |||
17 | #include <asm/io.h> | ||
18 | #include <asm/arch/smc.h> | ||
19 | |||
20 | #include "hsmc.h" | ||
21 | |||
22 | #define NR_CHIP_SELECTS 6 | ||
23 | |||
24 | struct hsmc { | ||
25 | void __iomem *regs; | ||
26 | struct clk *pclk; | ||
27 | struct clk *mck; | ||
28 | }; | ||
29 | |||
30 | static struct hsmc *hsmc; | ||
31 | |||
32 | int smc_set_configuration(int cs, const struct smc_config *config) | ||
33 | { | ||
34 | unsigned long mul; | ||
35 | unsigned long offset; | ||
36 | u32 setup, pulse, cycle, mode; | ||
37 | |||
38 | if (!hsmc) | ||
39 | return -ENODEV; | ||
40 | if (cs >= NR_CHIP_SELECTS) | ||
41 | return -EINVAL; | ||
42 | |||
43 | /* | ||
44 | * cycles = x / T = x * f | ||
45 | * = ((x * 1000000000) * ((f * 65536) / 1000000000)) / 65536 | ||
46 | * = ((x * 1000000000) * (((f / 10000) * 65536) / 100000)) / 65536 | ||
47 | */ | ||
48 | mul = (clk_get_rate(hsmc->mck) / 10000) << 16; | ||
49 | mul /= 100000; | ||
50 | |||
51 | #define ns2cyc(x) ((((x) * mul) + 65535) >> 16) | ||
52 | |||
53 | setup = (HSMC_BF(NWE_SETUP, ns2cyc(config->nwe_setup)) | ||
54 | | HSMC_BF(NCS_WR_SETUP, ns2cyc(config->ncs_write_setup)) | ||
55 | | HSMC_BF(NRD_SETUP, ns2cyc(config->nrd_setup)) | ||
56 | | HSMC_BF(NCS_RD_SETUP, ns2cyc(config->ncs_read_setup))); | ||
57 | pulse = (HSMC_BF(NWE_PULSE, ns2cyc(config->nwe_pulse)) | ||
58 | | HSMC_BF(NCS_WR_PULSE, ns2cyc(config->ncs_write_pulse)) | ||
59 | | HSMC_BF(NRD_PULSE, ns2cyc(config->nrd_pulse)) | ||
60 | | HSMC_BF(NCS_RD_PULSE, ns2cyc(config->ncs_read_pulse))); | ||
61 | cycle = (HSMC_BF(NWE_CYCLE, ns2cyc(config->write_cycle)) | ||
62 | | HSMC_BF(NRD_CYCLE, ns2cyc(config->read_cycle))); | ||
63 | |||
64 | switch (config->bus_width) { | ||
65 | case 1: | ||
66 | mode = HSMC_BF(DBW, HSMC_DBW_8_BITS); | ||
67 | break; | ||
68 | case 2: | ||
69 | mode = HSMC_BF(DBW, HSMC_DBW_16_BITS); | ||
70 | break; | ||
71 | case 4: | ||
72 | mode = HSMC_BF(DBW, HSMC_DBW_32_BITS); | ||
73 | break; | ||
74 | default: | ||
75 | return -EINVAL; | ||
76 | } | ||
77 | |||
78 | if (config->nrd_controlled) | ||
79 | mode |= HSMC_BIT(READ_MODE); | ||
80 | if (config->nwe_controlled) | ||
81 | mode |= HSMC_BIT(WRITE_MODE); | ||
82 | if (config->byte_write) | ||
83 | mode |= HSMC_BIT(BAT); | ||
84 | |||
85 | pr_debug("smc cs%d: setup/%08x pulse/%08x cycle/%08x mode/%08x\n", | ||
86 | cs, setup, pulse, cycle, mode); | ||
87 | |||
88 | offset = cs * 0x10; | ||
89 | hsmc_writel(hsmc, SETUP0 + offset, setup); | ||
90 | hsmc_writel(hsmc, PULSE0 + offset, pulse); | ||
91 | hsmc_writel(hsmc, CYCLE0 + offset, cycle); | ||
92 | hsmc_writel(hsmc, MODE0 + offset, mode); | ||
93 | hsmc_readl(hsmc, MODE0); /* I/O barrier */ | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | EXPORT_SYMBOL(smc_set_configuration); | ||
98 | |||
99 | static int hsmc_probe(struct platform_device *pdev) | ||
100 | { | ||
101 | struct resource *regs; | ||
102 | struct clk *pclk, *mck; | ||
103 | int ret; | ||
104 | |||
105 | if (hsmc) | ||
106 | return -EBUSY; | ||
107 | |||
108 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
109 | if (!regs) | ||
110 | return -ENXIO; | ||
111 | pclk = clk_get(&pdev->dev, "pclk"); | ||
112 | if (IS_ERR(pclk)) | ||
113 | return PTR_ERR(pclk); | ||
114 | mck = clk_get(&pdev->dev, "mck"); | ||
115 | if (IS_ERR(mck)) { | ||
116 | ret = PTR_ERR(mck); | ||
117 | goto out_put_pclk; | ||
118 | } | ||
119 | |||
120 | ret = -ENOMEM; | ||
121 | hsmc = kzalloc(sizeof(struct hsmc), GFP_KERNEL); | ||
122 | if (!hsmc) | ||
123 | goto out_put_clocks; | ||
124 | |||
125 | clk_enable(pclk); | ||
126 | clk_enable(mck); | ||
127 | |||
128 | hsmc->pclk = pclk; | ||
129 | hsmc->mck = mck; | ||
130 | hsmc->regs = ioremap(regs->start, regs->end - regs->start + 1); | ||
131 | if (!hsmc->regs) | ||
132 | goto out_disable_clocks; | ||
133 | |||
134 | dev_info(&pdev->dev, "Atmel Static Memory Controller at 0x%08lx\n", | ||
135 | (unsigned long)regs->start); | ||
136 | |||
137 | platform_set_drvdata(pdev, hsmc); | ||
138 | |||
139 | return 0; | ||
140 | |||
141 | out_disable_clocks: | ||
142 | clk_disable(mck); | ||
143 | clk_disable(pclk); | ||
144 | kfree(hsmc); | ||
145 | out_put_clocks: | ||
146 | clk_put(mck); | ||
147 | out_put_pclk: | ||
148 | clk_put(pclk); | ||
149 | hsmc = NULL; | ||
150 | return ret; | ||
151 | } | ||
152 | |||
153 | static struct platform_driver hsmc_driver = { | ||
154 | .probe = hsmc_probe, | ||
155 | .driver = { | ||
156 | .name = "smc", | ||
157 | }, | ||
158 | }; | ||
159 | |||
160 | static int __init hsmc_init(void) | ||
161 | { | ||
162 | return platform_driver_register(&hsmc_driver); | ||
163 | } | ||
164 | arch_initcall(hsmc_init); | ||