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