Commit | Line | Data |
---|---|---|
d979f179 | 1 | /* central.c: Central FHC driver for Sunfire/Starfire/Wildfire. |
1da177e4 | 2 | * |
b69416b5 | 3 | * Copyright (C) 1997, 1999, 2008 David S. Miller (davem@davemloft.net) |
1da177e4 LT |
4 | */ |
5 | ||
6 | #include <linux/kernel.h> | |
7 | #include <linux/types.h> | |
8 | #include <linux/string.h> | |
1da177e4 | 9 | #include <linux/init.h> |
b69416b5 DM |
10 | #include <linux/of_device.h> |
11 | #include <linux/platform_device.h> | |
1da177e4 | 12 | |
1da177e4 | 13 | #include <asm/fhc.h> |
b69416b5 DM |
14 | #include <asm/upa.h> |
15 | ||
16 | struct clock_board { | |
17 | void __iomem *clock_freq_regs; | |
18 | void __iomem *clock_regs; | |
19 | void __iomem *clock_ver_reg; | |
20 | int num_slots; | |
21 | struct resource leds_resource; | |
22 | struct platform_device leds_pdev; | |
23 | }; | |
24 | ||
25 | struct fhc { | |
26 | void __iomem *pregs; | |
27 | bool central; | |
28 | bool jtag_master; | |
29 | int board_num; | |
30 | struct resource leds_resource; | |
31 | struct platform_device leds_pdev; | |
32 | }; | |
33 | ||
34 | static int __devinit clock_board_calc_nslots(struct clock_board *p) | |
35 | { | |
36 | u8 reg = upa_readb(p->clock_regs + CLOCK_STAT1) & 0xc0; | |
1da177e4 | 37 | |
b69416b5 DM |
38 | switch (reg) { |
39 | case 0x40: | |
40 | return 16; | |
1da177e4 | 41 | |
b69416b5 DM |
42 | case 0xc0: |
43 | return 8; | |
1da177e4 | 44 | |
b69416b5 DM |
45 | case 0x80: |
46 | reg = 0; | |
47 | if (p->clock_ver_reg) | |
48 | reg = upa_readb(p->clock_ver_reg); | |
49 | if (reg) { | |
50 | if (reg & 0x80) | |
51 | return 4; | |
52 | else | |
53 | return 5; | |
54 | } | |
55 | /* Fallthrough */ | |
56 | default: | |
57 | return 4; | |
cecc4e92 | 58 | } |
1da177e4 LT |
59 | } |
60 | ||
b69416b5 DM |
61 | static int __devinit clock_board_probe(struct of_device *op, |
62 | const struct of_device_id *match) | |
1da177e4 | 63 | { |
b69416b5 DM |
64 | struct clock_board *p = kzalloc(sizeof(*p), GFP_KERNEL); |
65 | int err = -ENOMEM; | |
1da177e4 | 66 | |
b69416b5 DM |
67 | if (!p) { |
68 | printk(KERN_ERR "clock_board: Cannot allocate struct clock_board\n"); | |
69 | goto out; | |
1da177e4 | 70 | } |
1da177e4 | 71 | |
b69416b5 DM |
72 | p->clock_freq_regs = of_ioremap(&op->resource[0], 0, |
73 | resource_size(&op->resource[0]), | |
74 | "clock_board_freq"); | |
75 | if (!p->clock_freq_regs) { | |
76 | printk(KERN_ERR "clock_board: Cannot map clock_freq_regs\n"); | |
77 | goto out_free; | |
78 | } | |
1da177e4 | 79 | |
b69416b5 DM |
80 | p->clock_regs = of_ioremap(&op->resource[1], 0, |
81 | resource_size(&op->resource[1]), | |
82 | "clock_board_regs"); | |
83 | if (!p->clock_regs) { | |
84 | printk(KERN_ERR "clock_board: Cannot map clock_regs\n"); | |
85 | goto out_unmap_clock_freq_regs; | |
86 | } | |
1da177e4 | 87 | |
b69416b5 DM |
88 | if (op->resource[2].flags) { |
89 | p->clock_ver_reg = of_ioremap(&op->resource[2], 0, | |
90 | resource_size(&op->resource[2]), | |
91 | "clock_ver_reg"); | |
92 | if (!p->clock_ver_reg) { | |
93 | printk(KERN_ERR "clock_board: Cannot map clock_ver_reg\n"); | |
94 | goto out_unmap_clock_regs; | |
95 | } | |
96 | } | |
1da177e4 | 97 | |
b69416b5 | 98 | p->num_slots = clock_board_calc_nslots(p); |
1da177e4 | 99 | |
b69416b5 DM |
100 | p->leds_resource.start = (unsigned long) |
101 | (p->clock_regs + CLOCK_CTRL); | |
09317146 | 102 | p->leds_resource.end = p->leds_resource.start; |
b69416b5 | 103 | p->leds_resource.name = "leds"; |
1da177e4 | 104 | |
b69416b5 | 105 | p->leds_pdev.name = "sunfire-clockboard-leds"; |
b7c18c1b | 106 | p->leds_pdev.id = -1; |
b69416b5 DM |
107 | p->leds_pdev.resource = &p->leds_resource; |
108 | p->leds_pdev.num_resources = 1; | |
109 | p->leds_pdev.dev.parent = &op->dev; | |
1da177e4 | 110 | |
b69416b5 DM |
111 | err = platform_device_register(&p->leds_pdev); |
112 | if (err) { | |
113 | printk(KERN_ERR "clock_board: Could not register LEDS " | |
114 | "platform device\n"); | |
115 | goto out_unmap_clock_ver_reg; | |
1da177e4 | 116 | } |
1da177e4 | 117 | |
b69416b5 DM |
118 | printk(KERN_INFO "clock_board: Detected %d slot Enterprise system.\n", |
119 | p->num_slots); | |
1da177e4 | 120 | |
b69416b5 DM |
121 | err = 0; |
122 | out: | |
123 | return err; | |
1da177e4 | 124 | |
b69416b5 DM |
125 | out_unmap_clock_ver_reg: |
126 | if (p->clock_ver_reg) | |
127 | of_iounmap(&op->resource[2], p->clock_ver_reg, | |
128 | resource_size(&op->resource[2])); | |
cecc4e92 | 129 | |
b69416b5 DM |
130 | out_unmap_clock_regs: |
131 | of_iounmap(&op->resource[1], p->clock_regs, | |
132 | resource_size(&op->resource[1])); | |
1da177e4 | 133 | |
b69416b5 DM |
134 | out_unmap_clock_freq_regs: |
135 | of_iounmap(&op->resource[0], p->clock_freq_regs, | |
136 | resource_size(&op->resource[0])); | |
1da177e4 | 137 | |
b69416b5 DM |
138 | out_free: |
139 | kfree(p); | |
140 | goto out; | |
1da177e4 LT |
141 | } |
142 | ||
b69416b5 DM |
143 | static struct of_device_id __initdata clock_board_match[] = { |
144 | { | |
145 | .name = "clock-board", | |
146 | }, | |
147 | {}, | |
148 | }; | |
149 | ||
150 | static struct of_platform_driver clock_board_driver = { | |
151 | .match_table = clock_board_match, | |
152 | .probe = clock_board_probe, | |
153 | .driver = { | |
154 | .name = "clock_board", | |
155 | }, | |
156 | }; | |
157 | ||
158 | static int __devinit fhc_probe(struct of_device *op, | |
159 | const struct of_device_id *match) | |
1da177e4 | 160 | { |
b69416b5 DM |
161 | struct fhc *p = kzalloc(sizeof(*p), GFP_KERNEL); |
162 | int err = -ENOMEM; | |
163 | u32 reg; | |
1da177e4 | 164 | |
b69416b5 DM |
165 | if (!p) { |
166 | printk(KERN_ERR "fhc: Cannot allocate struct fhc\n"); | |
167 | goto out; | |
1da177e4 LT |
168 | } |
169 | ||
b69416b5 DM |
170 | if (!strcmp(op->node->parent->name, "central")) |
171 | p->central = true; | |
1da177e4 | 172 | |
b69416b5 DM |
173 | p->pregs = of_ioremap(&op->resource[0], 0, |
174 | resource_size(&op->resource[0]), | |
175 | "fhc_pregs"); | |
176 | if (!p->pregs) { | |
177 | printk(KERN_ERR "fhc: Cannot map pregs\n"); | |
178 | goto out_free; | |
cecc4e92 | 179 | } |
1da177e4 | 180 | |
b69416b5 DM |
181 | if (p->central) { |
182 | reg = upa_readl(p->pregs + FHC_PREGS_BSR); | |
183 | p->board_num = ((reg >> 16) & 1) | ((reg >> 12) & 0x0e); | |
184 | } else { | |
185 | p->board_num = of_getintprop_default(op->node, "board#", -1); | |
186 | if (p->board_num == -1) { | |
187 | printk(KERN_ERR "fhc: No board# property\n"); | |
188 | goto out_unmap_pregs; | |
189 | } | |
190 | if (upa_readl(p->pregs + FHC_PREGS_JCTRL) & FHC_JTAG_CTRL_MENAB) | |
191 | p->jtag_master = true; | |
192 | } | |
1da177e4 | 193 | |
b69416b5 DM |
194 | if (!p->central) { |
195 | p->leds_resource.start = (unsigned long) | |
196 | (p->pregs + FHC_PREGS_CTRL); | |
09317146 | 197 | p->leds_resource.end = p->leds_resource.start; |
b69416b5 DM |
198 | p->leds_resource.name = "leds"; |
199 | ||
200 | p->leds_pdev.name = "sunfire-fhc-leds"; | |
b7c18c1b | 201 | p->leds_pdev.id = p->board_num; |
b69416b5 DM |
202 | p->leds_pdev.resource = &p->leds_resource; |
203 | p->leds_pdev.num_resources = 1; | |
204 | p->leds_pdev.dev.parent = &op->dev; | |
205 | ||
206 | err = platform_device_register(&p->leds_pdev); | |
207 | if (err) { | |
208 | printk(KERN_ERR "fhc: Could not register LEDS " | |
209 | "platform device\n"); | |
210 | goto out_unmap_pregs; | |
211 | } | |
212 | } | |
213 | reg = upa_readl(p->pregs + FHC_PREGS_CTRL); | |
1da177e4 | 214 | |
b69416b5 DM |
215 | if (!p->central) |
216 | reg |= FHC_CONTROL_IXIST; | |
1da177e4 | 217 | |
b69416b5 DM |
218 | reg &= ~(FHC_CONTROL_AOFF | |
219 | FHC_CONTROL_BOFF | | |
220 | FHC_CONTROL_SLINE); | |
1da177e4 | 221 | |
b69416b5 DM |
222 | upa_writel(reg, p->pregs + FHC_PREGS_CTRL); |
223 | upa_readl(p->pregs + FHC_PREGS_CTRL); | |
1da177e4 | 224 | |
b69416b5 DM |
225 | reg = upa_readl(p->pregs + FHC_PREGS_ID); |
226 | printk(KERN_INFO "fhc: Board #%d, Version[%x] PartID[%x] Manuf[%x] %s\n", | |
227 | p->board_num, | |
228 | (reg & FHC_ID_VERS) >> 28, | |
229 | (reg & FHC_ID_PARTID) >> 12, | |
230 | (reg & FHC_ID_MANUF) >> 1, | |
231 | (p->jtag_master ? | |
232 | "(JTAG Master)" : | |
233 | (p->central ? "(Central)" : ""))); | |
1da177e4 | 234 | |
b69416b5 | 235 | err = 0; |
1da177e4 | 236 | |
b69416b5 DM |
237 | out: |
238 | return err; | |
1da177e4 | 239 | |
b69416b5 DM |
240 | out_unmap_pregs: |
241 | of_iounmap(&op->resource[0], p->pregs, resource_size(&op->resource[0])); | |
1da177e4 | 242 | |
b69416b5 DM |
243 | out_free: |
244 | kfree(p); | |
245 | goto out; | |
1da177e4 LT |
246 | } |
247 | ||
b69416b5 DM |
248 | static struct of_device_id __initdata fhc_match[] = { |
249 | { | |
250 | .name = "fhc", | |
251 | }, | |
252 | {}, | |
253 | }; | |
254 | ||
255 | static struct of_platform_driver fhc_driver = { | |
256 | .match_table = fhc_match, | |
257 | .probe = fhc_probe, | |
258 | .driver = { | |
259 | .name = "fhc", | |
260 | }, | |
261 | }; | |
262 | ||
263 | static int __init sunfire_init(void) | |
1da177e4 | 264 | { |
b69416b5 DM |
265 | (void) of_register_driver(&fhc_driver, &of_platform_bus_type); |
266 | (void) of_register_driver(&clock_board_driver, &of_platform_bus_type); | |
267 | return 0; | |
1da177e4 | 268 | } |
b69416b5 DM |
269 | |
270 | subsys_initcall(sunfire_init); |