1 /*
2  * Copyright (c) 2011 Aeroflex Gaisler
3  *
4  * BSD license:
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 
25 
26 #include <asm-leon/amba.h>
27 #undef AMBA_TYPE_AHBIO_ADDR
28 #include <asm-leon/lambapp.h>
29 #include <string.h>
30 
31 #define AMBA_CONF_AREA 0xff000
32 #define AMBA_AHB_SLAVE_CONF_AREA (1 << 11)
33 #define AMBA_APB_SLAVES 16
34 
35 #ifdef PDEBUG
36 #define DPRINTF(p)  printf p
37 #else
38 #define DPRINTF(p)
39 #endif
40 
41 unsigned int
ambapp_addr_from(struct ambapp_mmap * mmaps,unsigned int address)42 ambapp_addr_from (struct ambapp_mmap *mmaps, unsigned int address)
43 {
44   /* no translation? */
45   if (!mmaps)
46     return address;
47 
48   while (mmaps->size)
49     {
50       if ((address >= mmaps->remote_adr)
51 	  && (address <= (mmaps->remote_adr + (mmaps->size - 1))))
52 	{
53 	  return (address - mmaps->remote_adr) + mmaps->local_adr;
54 	}
55       mmaps++;
56     }
57   return 1;
58 }
59 
60 
61 static void
ambapp_ahb_dev_init(unsigned int ioarea,struct ambapp_mmap * mmaps,struct ambapp_pnp_ahb * ahb,struct ambapp_dev_hdr * dev)62 ambapp_ahb_dev_init (unsigned int ioarea,
63 		     struct ambapp_mmap *mmaps,
64 		     struct ambapp_pnp_ahb *ahb, struct ambapp_dev_hdr *dev)
65 {
66   int bar;
67   struct ambapp_ahb_info *ahb_info;
68   unsigned int addr, mask, mbar;
69 
70   /* Setup device struct */
71   dev->vendor = ambapp_pnp_vendor (ahb->id);
72   dev->device = ambapp_pnp_device (ahb->id);
73   ahb_info = dev->devinfo;
74   ahb_info->ver = ambapp_pnp_ver (ahb->id);
75   ahb_info->irq = ambapp_pnp_irq (ahb->id);
76   ahb_info->custom[0] = (unsigned int) ahb->custom[0];
77   ahb_info->custom[1] = (unsigned int) ahb->custom[1];
78   ahb_info->custom[2] = (unsigned int) ahb->custom[2];
79 
80   DPRINTF (("+AHB device %d:%d\n", dev->device, dev->vendor));
81 
82   /* Memory BARs */
83   for (bar = 0; bar < 4; bar++)
84     {
85       mbar = ahb->mbar[bar];
86       if (mbar == 0)
87 	{
88 	  addr = 0;
89 	  mask = 0;
90 	}
91       else
92 	{
93 	  addr = ambapp_pnp_start (mbar);
94 	  if (ambapp_pnp_mbar_type (mbar) == AMBA_TYPE_AHBIO)
95 	    {
96 	      /* AHB I/O area is releative IO_AREA */
97 	      addr = AMBA_TYPE_AHBIO_ADDR (addr, ioarea);
98 	      mask =
99 		(((unsigned int) (ambapp_pnp_mbar_mask ((~mbar)) << 8) |
100 		  0xff)) + 1;
101 	    }
102 	  else
103 	    {
104 	      /* AHB memory area, absolute address */
105 	      addr = ambapp_addr_from (mmaps, addr);
106 	      mask =
107 		(~((unsigned int) (ambapp_pnp_mbar_mask (mbar) << 20))) + 1;
108 	    }
109 	}
110       ahb_info->start[bar] = addr;
111       ahb_info->mask[bar] = mask;
112     }
113 }
114 
115 static void
ambapp_apb_dev_init(unsigned int base,struct ambapp_mmap * mmaps,struct ambapp_pnp_apb * apb,struct ambapp_dev_hdr * dev)116 ambapp_apb_dev_init (unsigned int base,
117 		     struct ambapp_mmap *mmaps,
118 		     struct ambapp_pnp_apb *apb, struct ambapp_dev_hdr *dev)
119 {
120   struct ambapp_apb_info *apb_info;
121 
122   /* Setup device struct */
123   dev->vendor = ambapp_pnp_vendor (apb->id);
124   dev->device = ambapp_pnp_device (apb->id);
125   apb_info = dev->devinfo;
126   apb_info->ver = ambapp_pnp_ver (apb->id);
127   apb_info->irq = ambapp_pnp_irq (apb->id);
128   apb_info->start = ambapp_pnp_apb_start (apb->iobar, base);
129   apb_info->mask = ambapp_pnp_apb_mask (apb->iobar);
130 
131   DPRINTF (("+APB device %d:%d\n", dev->device, dev->vendor));
132 
133 
134 }
135 
136 #define MAX_NUM_BUSES 16
137 static void
ambapp_add_scanned_bus(unsigned int * ioareas,unsigned int ioarea)138 ambapp_add_scanned_bus (unsigned int *ioareas, unsigned int ioarea)
139 {
140   int i;
141   for (i = 0; i < MAX_NUM_BUSES; i++)
142     {
143       if (ioareas[i] == 0)
144 	{
145 	  ioareas[i] = ioarea;
146 	  return;
147 	}
148     }
149 }
150 
151 static int
ambapp_has_been_scanned(unsigned int * ioareas,unsigned int ioarea)152 ambapp_has_been_scanned (unsigned int *ioareas, unsigned int ioarea)
153 {
154   int i;
155   if (!ioareas)
156     return 0;
157 
158   for (i = 0; i < MAX_NUM_BUSES; i++)
159     {
160       if (ioareas[i] == 0)
161 	{
162 	  break;
163 	}
164       else if (ioareas[i] == ioarea)
165 	{
166 	  return 1;
167 	}
168     }
169   return 0;
170 }
171 
172 static int
ambapp_find(unsigned int ioarea,struct ambapp_dev_hdr * parent,struct ambapp_mmap * mmaps,void * internal,int (* find_match)(struct ambapp_dev_hdr * dev,void * arg),void * arg,int vendor,int device)173 ambapp_find (unsigned int ioarea,
174 	     struct ambapp_dev_hdr *parent,
175 	     struct ambapp_mmap *mmaps,
176 	     void *internal,
177 	     int (*find_match) (struct ambapp_dev_hdr * dev, void *arg),
178 	     void *arg, int vendor, int device)
179 {
180   struct ambapp_pnp_ahb *ahb, ahb_buf;
181   struct ambapp_pnp_apb *apb, apb_buf;
182   struct ambapp_dev_hdr *dev, *prev, *prevapb, *apbdev;
183   struct ambapp_ahb_info *ahb_info;
184   int maxloops = 64;
185   unsigned int apbbase, bridge_address;
186   int i, j;
187 
188   DPRINTF (("Scan at 0x%08x\n", ioarea));
189 
190   if (parent)
191     {
192       /* scan first bus for 64 devices, rest for 16 devices */
193       maxloops = 16;
194     }
195   else
196     {
197       if (internal)
198 	{
199 	  ambapp_add_scanned_bus (internal, ioarea);
200 	}
201     }
202 
203   prev = parent;
204 
205   /* AHB MASTERS */
206   ahb = (struct ambapp_pnp_ahb *) (ioarea | AMBA_CONF_AREA);
207   for (i = 0; i < maxloops; i++)
208     {
209       memcpy (&ahb_buf, ahb, sizeof (struct ambapp_pnp_ahb));
210       if (ahb_buf.id != 0)
211 	{
212 	  struct ambapp_dev_hdr _dev;
213 	  struct ambapp_ahb_info _ahb;
214 	  memset (&_dev, 0, sizeof (_dev));
215 	  memset (&_ahb, 0, sizeof (_ahb));
216 	  _dev.devinfo = &_ahb;
217 	  _dev.dev_type = DEV_AHB_MST;
218 	  dev = &_dev;
219 
220 	  ambapp_ahb_dev_init (ioarea, mmaps, &ahb_buf, dev);
221 
222 	  DPRINTF ((" = test %d:%d == %d:%d\n", vendor, device, dev->vendor,
223 		    dev->device));
224 
225 	  if (vendor == dev->vendor &&
226 	      device == dev->device && find_match (dev, arg))
227 	    {
228 	      return 1;
229 	    }
230 	}
231       ahb++;
232     }
233 
234 
235   /* AHB SLAVES */
236   ahb =
237     (struct ambapp_pnp_ahb *) (ioarea | AMBA_CONF_AREA |
238 			       AMBA_AHB_SLAVE_CONF_AREA);
239   for (i = 0; i < maxloops; i++)
240     {
241       memcpy (&ahb_buf, ahb, sizeof (struct ambapp_pnp_ahb));
242       if (ahb_buf.id != 0)
243 	{
244 	  struct ambapp_dev_hdr _dev;
245 	  struct ambapp_ahb_info _ahb;
246 	  memset (&_dev, 0, sizeof (_dev));
247 	  memset (&_ahb, 0, sizeof (_ahb));
248 	  _dev.devinfo = &_ahb;
249 	  _dev.dev_type = DEV_AHB_MST;
250 	  dev = &_dev;
251 
252 	  ambapp_ahb_dev_init (ioarea, mmaps, &ahb_buf, dev);
253 
254 	  DPRINTF ((" = test %d:%d == %d:%d\n", vendor, device, dev->vendor,
255 		    dev->device));
256 
257 	  if (vendor == dev->vendor &&
258 	      device == dev->device && find_match (dev, arg))
259 	    {
260 	      return 1;
261 	    }
262 
263 	  /* Is it a AHB/AHB Bridge ? */
264 	  if ((dev->device == GAISLER_AHB2AHB)
265 	      && (dev->vendor == VENDOR_GAISLER))
266 	    {
267 	      /* AHB/AHB Bridge Found, recurse down the Bridge */
268 	      ahb_info = dev->devinfo;
269 	      if (ahb_info->ver)
270 		{
271 		  bridge_address =
272 		    ambapp_addr_from (mmaps, ahb_info->custom[1]);
273 
274 		  DPRINTF (("+(AHBAHB:0x%x)\n", bridge_address));
275 
276 		  /* Makes sure bus only scanned once */
277 		  if (internal == 0
278 		      || ambapp_has_been_scanned (internal,
279 						  bridge_address) == NULL)
280 		    {
281 		      if (internal)
282 			ambapp_add_scanned_bus (internal, bridge_address);
283 
284 		      if (ambapp_find (bridge_address, dev, mmaps, internal,
285 				       find_match, arg, vendor, device))
286 			return 1;
287 		    }
288 		}
289 	    }
290 	  else if ((dev->device == GAISLER_APBMST)
291 		   && (dev->vendor == VENDOR_GAISLER))
292 	    {
293 	      /* AHB/APB Bridge Found, add the APB devices to this AHB Slave's children */
294 	      prevapb = dev;
295 	      ahb_info = dev->devinfo;
296 	      apbbase = ahb_info->start[0];
297 	      apb = (struct ambapp_pnp_apb *) (apbbase | AMBA_CONF_AREA);
298 	      for (j = 0; j < AMBA_APB_SLAVES; j++)
299 		{
300 		  memcpy (&apb_buf, apb, sizeof (struct ambapp_pnp_apb));
301 		  if (apb_buf.id)
302 		    {
303 		      struct ambapp_dev_hdr _apbdev;
304 		      struct ambapp_apb_info _apb;
305 		      memset (&_apbdev, 0, sizeof (_apbdev));
306 		      memset (&_apb, 0, sizeof (_apb));
307 		      _apbdev.devinfo = &_apb;
308 		      _apbdev.dev_type = DEV_APB_SLV;
309 		      apbdev = &_apbdev;
310 
311 		      ambapp_apb_dev_init (apbbase, mmaps, &apb_buf, apbdev);
312 
313 		      DPRINTF ((" = test %d:%d == %d:%d\n", vendor, device,
314 				apbdev->vendor, apbdev->device));
315 
316 		      if (vendor == apbdev->vendor &&
317 			  device == apbdev->device &&
318 			  find_match (apbdev, arg))
319 			{
320 
321 			  return 1;
322 			}
323 		    }
324 		  apb++;
325 		}
326 	    }
327 	}
328       ahb++;
329     }
330 
331   if (parent == NULL)
332     {
333       /*free(internal); */
334     }
335 
336   return 0;
337 }
338 
339 struct ambapp_dev_find_match_arg
340 {
341   int index;
342   int count;
343   int type;
344   void *dev;
345 };
346 
347 /* AMBA PP find routines */
348 static int
ambapp_dev_find_match(struct ambapp_dev_hdr * dev,void * arg)349 ambapp_dev_find_match (struct ambapp_dev_hdr *dev, void *arg)
350 {
351   struct ambapp_dev_find_match_arg *p = arg;
352 
353   if (p->index == 0)
354     {
355       /* Found controller, stop */
356       if (p->type == DEV_APB_SLV)
357 	{
358 	  *(struct ambapp_apb_info *) p->dev =
359 	    *(struct ambapp_apb_info *) dev->devinfo;
360 	  p->dev = ((struct ambapp_apb_info *) p->dev) + 1;
361 	}
362       else
363 	{
364 	  *(struct ambapp_ahb_info *) p->dev =
365 	    *(struct ambapp_ahb_info *) dev->devinfo;
366 	  p->dev = ((struct ambapp_ahb_info *) p->dev) + 1;
367 	}
368       p->count--;
369       if (p->count < 1)
370 	return 1;
371     }
372   else
373     {
374       p->index--;
375     }
376   return 0;
377 }
378 
379 static int
find_apbslvs_next(int vendor,int device,struct ambapp_apb_info * dev,int index,int maxno)380 find_apbslvs_next (int vendor, int device, struct ambapp_apb_info *dev,
381 		   int index, int maxno)
382 {
383   struct ambapp_dev_find_match_arg arg;
384   unsigned int busses[MAX_NUM_BUSES];
385   memset (busses, 0, sizeof (busses));
386 
387   arg.index = index;
388   arg.count = maxno;
389   arg.type = DEV_APB_SLV;	/* APB */
390   arg.dev = dev;
391 
392   ambapp_find (LEON3_IO_AREA, NULL, NULL, &busses,
393 	       ambapp_dev_find_match, &arg, vendor, device);
394 
395   return maxno - arg.count;
396 }
397 
398 int
find_apbslv(int vendor,int device,struct ambapp_apb_info * dev)399 find_apbslv (int vendor, int device, struct ambapp_apb_info *dev)
400 {
401   return find_apbslvs_next (vendor, device, dev, 0, 1);
402 }
403 
404 struct ambapp_dev_hdr *ambapp_root = NULL;
405 unsigned int busses[MAX_NUM_BUSES];
406 extern unsigned int console;
407 extern unsigned int rtc;
408 extern unsigned int irqmp;
409 
410 void
pnpinit(void)411 pnpinit (void)
412 {
413   struct ambapp_apb_info dev;
414   int n;
415   if ((n = find_apbslv (VENDOR_GAISLER, GAISLER_APBUART, &dev)) == 1)
416     {
417       console = dev.start;
418       DPRINTF (("Found abuart at 0x%x\n", console));
419     }
420   if ((n = find_apbslv (VENDOR_GAISLER, GAISLER_GPTIMER, &dev)) == 1)
421     {
422       rtc = dev.start + 0x10;
423       DPRINTF (("Found rtc at 0x%x\n", rtc));
424     }
425   if ((n = find_apbslv (VENDOR_GAISLER, GAISLER_IRQMP, &dev)) == 1)
426     {
427       irqmp = dev.start;
428       DPRINTF (("Found irqmp at 0x%x\n", rtc));
429     }
430 }
431