1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 2004-2009 Cavium Networks
7  * Copyright (C) 2008 Wind River Systems
8  */
9 
10 #include <linux/init.h>
11 #include <linux/irq.h>
12 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 
15 #include <asm/octeon/octeon.h>
16 #include <asm/octeon/cvmx-rnm-defs.h>
17 
18 static struct octeon_cf_data octeon_cf_data;
19 
20 static int __init octeon_cf_device_init(void)
21 {
22 	union cvmx_mio_boot_reg_cfgx mio_boot_reg_cfg;
23 	unsigned long base_ptr, region_base, region_size;
24 	struct platform_device *pd;
25 	struct resource cf_resources[3];
26 	unsigned int num_resources;
27 	int i;
28 	int ret = 0;
29 
30 	/* Setup octeon-cf platform device if present. */
31 	base_ptr = 0;
32 	if (octeon_bootinfo->major_version == 1
33 		&& octeon_bootinfo->minor_version >= 1) {
34 		if (octeon_bootinfo->compact_flash_common_base_addr)
35 			base_ptr =
36 				octeon_bootinfo->compact_flash_common_base_addr;
37 	} else {
38 		base_ptr = 0x1d000800;
39 	}
40 
41 	if (!base_ptr)
42 		return ret;
43 
44 	/* Find CS0 region. */
45 	for (i = 0; i < 8; i++) {
46 		mio_boot_reg_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(i));
47 		region_base = mio_boot_reg_cfg.s.base << 16;
48 		region_size = (mio_boot_reg_cfg.s.size + 1) << 16;
49 		if (mio_boot_reg_cfg.s.en && base_ptr >= region_base
50 		    && base_ptr < region_base + region_size)
51 			break;
52 	}
53 	if (i >= 7) {
54 		/* i and i + 1 are CS0 and CS1, both must be less than 8. */
55 		goto out;
56 	}
57 	octeon_cf_data.base_region = i;
58 	octeon_cf_data.is16bit = mio_boot_reg_cfg.s.width;
59 	octeon_cf_data.base_region_bias = base_ptr - region_base;
60 	memset(cf_resources, 0, sizeof(cf_resources));
61 	num_resources = 0;
62 	cf_resources[num_resources].flags	= IORESOURCE_MEM;
63 	cf_resources[num_resources].start	= region_base;
64 	cf_resources[num_resources].end	= region_base + region_size - 1;
65 	num_resources++;
66 
67 
68 	if (!(base_ptr & 0xfffful)) {
69 		/*
70 		 * Boot loader signals availability of DMA (true_ide
71 		 * mode) by setting low order bits of base_ptr to
72 		 * zero.
73 		 */
74 
75 		/* Asume that CS1 immediately follows. */
76 		mio_boot_reg_cfg.u64 =
77 			cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(i + 1));
78 		region_base = mio_boot_reg_cfg.s.base << 16;
79 		region_size = (mio_boot_reg_cfg.s.size + 1) << 16;
80 		if (!mio_boot_reg_cfg.s.en)
81 			goto out;
82 
83 		cf_resources[num_resources].flags	= IORESOURCE_MEM;
84 		cf_resources[num_resources].start	= region_base;
85 		cf_resources[num_resources].end	= region_base + region_size - 1;
86 		num_resources++;
87 
88 		octeon_cf_data.dma_engine = 0;
89 		cf_resources[num_resources].flags	= IORESOURCE_IRQ;
90 		cf_resources[num_resources].start	= OCTEON_IRQ_BOOTDMA;
91 		cf_resources[num_resources].end	= OCTEON_IRQ_BOOTDMA;
92 		num_resources++;
93 	} else {
94 		octeon_cf_data.dma_engine = -1;
95 	}
96 
97 	pd = platform_device_alloc("pata_octeon_cf", -1);
98 	if (!pd) {
99 		ret = -ENOMEM;
100 		goto out;
101 	}
102 	pd->dev.platform_data = &octeon_cf_data;
103 
104 	ret = platform_device_add_resources(pd, cf_resources, num_resources);
105 	if (ret)
106 		goto fail;
107 
108 	ret = platform_device_add(pd);
109 	if (ret)
110 		goto fail;
111 
112 	return ret;
113 fail:
114 	platform_device_put(pd);
115 out:
116 	return ret;
117 }
118 device_initcall(octeon_cf_device_init);
119 
120 /* Octeon Random Number Generator.  */
121 static int __init octeon_rng_device_init(void)
122 {
123 	struct platform_device *pd;
124 	int ret = 0;
125 
126 	struct resource rng_resources[] = {
127 		{
128 			.flags	= IORESOURCE_MEM,
129 			.start	= XKPHYS_TO_PHYS(CVMX_RNM_CTL_STATUS),
130 			.end	= XKPHYS_TO_PHYS(CVMX_RNM_CTL_STATUS) + 0xf
131 		}, {
132 			.flags	= IORESOURCE_MEM,
133 			.start	= cvmx_build_io_address(8, 0),
134 			.end	= cvmx_build_io_address(8, 0) + 0x7
135 		}
136 	};
137 
138 	pd = platform_device_alloc("octeon_rng", -1);
139 	if (!pd) {
140 		ret = -ENOMEM;
141 		goto out;
142 	}
143 
144 	ret = platform_device_add_resources(pd, rng_resources,
145 					    ARRAY_SIZE(rng_resources));
146 	if (ret)
147 		goto fail;
148 
149 	ret = platform_device_add(pd);
150 	if (ret)
151 		goto fail;
152 
153 	return ret;
154 fail:
155 	platform_device_put(pd);
156 
157 out:
158 	return ret;
159 }
160 device_initcall(octeon_rng_device_init);
161 
162 /* Octeon SMI/MDIO interface.  */
163 static int __init octeon_mdiobus_device_init(void)
164 {
165 	struct platform_device *pd;
166 	int ret = 0;
167 
168 	if (octeon_is_simulation())
169 		return 0; /* No mdio in the simulator. */
170 
171 	/* The bus number is the platform_device id.  */
172 	pd = platform_device_alloc("mdio-octeon", 0);
173 	if (!pd) {
174 		ret = -ENOMEM;
175 		goto out;
176 	}
177 
178 	ret = platform_device_add(pd);
179 	if (ret)
180 		goto fail;
181 
182 	return ret;
183 fail:
184 	platform_device_put(pd);
185 
186 out:
187 	return ret;
188 
189 }
190 device_initcall(octeon_mdiobus_device_init);
191 
192 /* Octeon mgmt port Ethernet interface.  */
193 static int __init octeon_mgmt_device_init(void)
194 {
195 	struct platform_device *pd;
196 	int ret = 0;
197 	int port, num_ports;
198 
199 	struct resource mgmt_port_resource = {
200 		.flags	= IORESOURCE_IRQ,
201 		.start	= -1,
202 		.end	= -1
203 	};
204 
205 	if (!OCTEON_IS_MODEL(OCTEON_CN56XX) && !OCTEON_IS_MODEL(OCTEON_CN52XX))
206 		return 0;
207 
208 	if (OCTEON_IS_MODEL(OCTEON_CN56XX))
209 		num_ports = 1;
210 	else
211 		num_ports = 2;
212 
213 	for (port = 0; port < num_ports; port++) {
214 		pd = platform_device_alloc("octeon_mgmt", port);
215 		if (!pd) {
216 			ret = -ENOMEM;
217 			goto out;
218 		}
219 		switch (port) {
220 		case 0:
221 			mgmt_port_resource.start = OCTEON_IRQ_MII0;
222 			break;
223 		case 1:
224 			mgmt_port_resource.start = OCTEON_IRQ_MII1;
225 			break;
226 		default:
227 			BUG();
228 		}
229 		mgmt_port_resource.end = mgmt_port_resource.start;
230 
231 		ret = platform_device_add_resources(pd, &mgmt_port_resource, 1);
232 
233 		if (ret)
234 			goto fail;
235 
236 		ret = platform_device_add(pd);
237 		if (ret)
238 			goto fail;
239 	}
240 	return ret;
241 fail:
242 	platform_device_put(pd);
243 
244 out:
245 	return ret;
246 
247 }
248 device_initcall(octeon_mgmt_device_init);
249 
250 MODULE_AUTHOR("David Daney <ddaney@caviumnetworks.com>");
251 MODULE_LICENSE("GPL");
252 MODULE_DESCRIPTION("Platform driver for Octeon SOC");
253