1ea2305f6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2fb180322SJohn Rigby /*
3fb180322SJohn Rigby  * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. All rights reserved.
4fb180322SJohn Rigby  *
5fb180322SJohn Rigby  * Author: John Rigby <jrigby@freescale.com>
6fb180322SJohn Rigby  *
7fb180322SJohn Rigby  * Description:
8fb180322SJohn Rigby  * MPC512x Shared code
9fb180322SJohn Rigby  */
10fb180322SJohn Rigby 
11ba218127SGerhard Sittig #include <linux/clk.h>
12fb180322SJohn Rigby #include <linux/kernel.h>
13fb180322SJohn Rigby #include <linux/io.h>
14fb180322SJohn Rigby #include <linux/irq.h>
15fb180322SJohn Rigby #include <linux/of_platform.h>
164b5006ecSAnatolij Gustschin #include <linux/fsl-diu-fb.h>
1710239733SAnton Blanchard #include <linux/memblock.h>
184b5006ecSAnatolij Gustschin #include <sysdev/fsl_soc.h>
19fb180322SJohn Rigby 
204b5006ecSAnatolij Gustschin #include <asm/cacheflush.h>
21fb180322SJohn Rigby #include <asm/machdep.h>
22fb180322SJohn Rigby #include <asm/ipic.h>
23fb180322SJohn Rigby #include <asm/prom.h>
24fb180322SJohn Rigby #include <asm/time.h>
25a8dbceb7SAnatolij Gustschin #include <asm/mpc5121.h>
262da8cb6aSAnatolij Gustschin #include <asm/mpc52xx_psc.h>
27fb180322SJohn Rigby 
28fb180322SJohn Rigby #include "mpc512x.h"
29fb180322SJohn Rigby 
30a8dbceb7SAnatolij Gustschin static struct mpc512x_reset_module __iomem *reset_module_base;
31a8dbceb7SAnatolij Gustschin 
32a8dbceb7SAnatolij Gustschin static void __init mpc512x_restart_init(void)
33a8dbceb7SAnatolij Gustschin {
34a8dbceb7SAnatolij Gustschin 	struct device_node *np;
350875a88eSMatteo Facchinetti 	const char *reset_compat;
36a8dbceb7SAnatolij Gustschin 
370875a88eSMatteo Facchinetti 	reset_compat = mpc512x_select_reset_compat();
380875a88eSMatteo Facchinetti 	np = of_find_compatible_node(NULL, NULL, reset_compat);
39a8dbceb7SAnatolij Gustschin 	if (!np)
40a8dbceb7SAnatolij Gustschin 		return;
41a8dbceb7SAnatolij Gustschin 
42a8dbceb7SAnatolij Gustschin 	reset_module_base = of_iomap(np, 0);
43a8dbceb7SAnatolij Gustschin 	of_node_put(np);
44a8dbceb7SAnatolij Gustschin }
45a8dbceb7SAnatolij Gustschin 
4695ec77c0SDaniel Axtens void __noreturn mpc512x_restart(char *cmd)
47a8dbceb7SAnatolij Gustschin {
48a8dbceb7SAnatolij Gustschin 	if (reset_module_base) {
49a8dbceb7SAnatolij Gustschin 		/* Enable software reset "RSTE" */
50a8dbceb7SAnatolij Gustschin 		out_be32(&reset_module_base->rpr, 0x52535445);
51a8dbceb7SAnatolij Gustschin 		/* Set software hard reset */
52a8dbceb7SAnatolij Gustschin 		out_be32(&reset_module_base->rcr, 0x2);
53a8dbceb7SAnatolij Gustschin 	} else {
54a8dbceb7SAnatolij Gustschin 		pr_err("Restart module not mapped.\n");
55a8dbceb7SAnatolij Gustschin 	}
56a8dbceb7SAnatolij Gustschin 	for (;;)
57a8dbceb7SAnatolij Gustschin 		;
58a8dbceb7SAnatolij Gustschin }
59a8dbceb7SAnatolij Gustschin 
604b5006ecSAnatolij Gustschin struct fsl_diu_shared_fb {
614b5006ecSAnatolij Gustschin 	u8		gamma[0x300];	/* 32-bit aligned! */
624b5006ecSAnatolij Gustschin 	struct diu_ad	ad0;		/* 32-bit aligned! */
634b5006ecSAnatolij Gustschin 	phys_addr_t	fb_phys;
644b5006ecSAnatolij Gustschin 	size_t		fb_len;
654b5006ecSAnatolij Gustschin 	bool		in_use;
664b5006ecSAnatolij Gustschin };
674b5006ecSAnatolij Gustschin 
68ba218127SGerhard Sittig /* receives a pixel clock spec in pico seconds, adjusts the DIU clock rate */
697e198197SBrian Norris static void mpc512x_set_pixel_clock(unsigned int pixclock)
704b5006ecSAnatolij Gustschin {
714b5006ecSAnatolij Gustschin 	struct device_node *np;
72ba218127SGerhard Sittig 	struct clk *clk_diu;
73ba218127SGerhard Sittig 	unsigned long epsilon, minpixclock, maxpixclock;
74ba218127SGerhard Sittig 	unsigned long offset, want, got, delta;
754b5006ecSAnatolij Gustschin 
76ba218127SGerhard Sittig 	/* lookup and enable the DIU clock */
77ba218127SGerhard Sittig 	np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu");
784b5006ecSAnatolij Gustschin 	if (!np) {
79ba218127SGerhard Sittig 		pr_err("Could not find DIU device tree node.\n");
804b5006ecSAnatolij Gustschin 		return;
814b5006ecSAnatolij Gustschin 	}
82ba218127SGerhard Sittig 	clk_diu = of_clk_get(np, 0);
83ba218127SGerhard Sittig 	if (IS_ERR(clk_diu)) {
84ba218127SGerhard Sittig 		/* backwards compat with device trees that lack clock specs */
85ba218127SGerhard Sittig 		clk_diu = clk_get_sys(np->name, "ipg");
86ba218127SGerhard Sittig 	}
874b5006ecSAnatolij Gustschin 	of_node_put(np);
88ba218127SGerhard Sittig 	if (IS_ERR(clk_diu)) {
89ba218127SGerhard Sittig 		pr_err("Could not lookup DIU clock.\n");
90ba218127SGerhard Sittig 		return;
91ba218127SGerhard Sittig 	}
92ba218127SGerhard Sittig 	if (clk_prepare_enable(clk_diu)) {
93ba218127SGerhard Sittig 		pr_err("Could not enable DIU clock.\n");
944b5006ecSAnatolij Gustschin 		return;
954b5006ecSAnatolij Gustschin 	}
964b5006ecSAnatolij Gustschin 
97ba218127SGerhard Sittig 	/*
98ba218127SGerhard Sittig 	 * convert the picoseconds spec into the desired clock rate,
99ba218127SGerhard Sittig 	 * determine the acceptable clock range for the monitor (+/- 5%),
100ba218127SGerhard Sittig 	 * do the calculation in steps to avoid integer overflow
101ba218127SGerhard Sittig 	 */
102ba218127SGerhard Sittig 	pr_debug("DIU pixclock in ps - %u\n", pixclock);
103ba218127SGerhard Sittig 	pixclock = (1000000000 / pixclock) * 1000;
1044b5006ecSAnatolij Gustschin 	pr_debug("DIU pixclock freq  - %u\n", pixclock);
105ba218127SGerhard Sittig 	epsilon = pixclock / 20; /* pixclock * 0.05 */
106ba218127SGerhard Sittig 	pr_debug("DIU deviation      - %lu\n", epsilon);
107ba218127SGerhard Sittig 	minpixclock = pixclock - epsilon;
108ba218127SGerhard Sittig 	maxpixclock = pixclock + epsilon;
1094b5006ecSAnatolij Gustschin 	pr_debug("DIU minpixclock    - %lu\n", minpixclock);
1104b5006ecSAnatolij Gustschin 	pr_debug("DIU maxpixclock    - %lu\n", maxpixclock);
1114b5006ecSAnatolij Gustschin 
112ba218127SGerhard Sittig 	/*
113ba218127SGerhard Sittig 	 * check whether the DIU supports the desired pixel clock
114ba218127SGerhard Sittig 	 *
115ba218127SGerhard Sittig 	 * - simply request the desired clock and see what the
116ba218127SGerhard Sittig 	 *   platform's clock driver will make of it, assuming that it
117ba218127SGerhard Sittig 	 *   will setup the best approximation of the requested value
118ba218127SGerhard Sittig 	 * - try other candidate frequencies in the order of decreasing
119ba218127SGerhard Sittig 	 *   preference (i.e. with increasing distance from the desired
120ba218127SGerhard Sittig 	 *   pixel clock, and checking the lower frequency before the
121ba218127SGerhard Sittig 	 *   higher frequency to not overload the hardware) until the
122ba218127SGerhard Sittig 	 *   first match is found -- any potential subsequent match
123ba218127SGerhard Sittig 	 *   would only be as good as the former match or typically
124ba218127SGerhard Sittig 	 *   would be less preferrable
125ba218127SGerhard Sittig 	 *
126ba218127SGerhard Sittig 	 * the offset increment of pixelclock divided by 64 is an
127ba218127SGerhard Sittig 	 * arbitrary choice -- it's simple to calculate, in the typical
128ba218127SGerhard Sittig 	 * case we expect the first check to succeed already, in the
129ba218127SGerhard Sittig 	 * worst case seven frequencies get tested (the exact center and
130ba218127SGerhard Sittig 	 * three more values each to the left and to the right) before
131ba218127SGerhard Sittig 	 * the 5% tolerance window is exceeded, resulting in fast enough
132ba218127SGerhard Sittig 	 * execution yet high enough probability of finding a suitable
133ba218127SGerhard Sittig 	 * value, while the error rate will be in the order of single
134ba218127SGerhard Sittig 	 * percents
135ba218127SGerhard Sittig 	 */
136ba218127SGerhard Sittig 	for (offset = 0; offset <= epsilon; offset += pixclock / 64) {
137ba218127SGerhard Sittig 		want = pixclock - offset;
138ba218127SGerhard Sittig 		pr_debug("DIU checking clock - %lu\n", want);
139ba218127SGerhard Sittig 		clk_set_rate(clk_diu, want);
140ba218127SGerhard Sittig 		got = clk_get_rate(clk_diu);
141ba218127SGerhard Sittig 		delta = abs(pixclock - got);
142ba218127SGerhard Sittig 		if (delta < epsilon)
143ba218127SGerhard Sittig 			break;
144ba218127SGerhard Sittig 		if (!offset)
145ba218127SGerhard Sittig 			continue;
146ba218127SGerhard Sittig 		want = pixclock + offset;
147ba218127SGerhard Sittig 		pr_debug("DIU checking clock - %lu\n", want);
148ba218127SGerhard Sittig 		clk_set_rate(clk_diu, want);
149ba218127SGerhard Sittig 		got = clk_get_rate(clk_diu);
150ba218127SGerhard Sittig 		delta = abs(pixclock - got);
151ba218127SGerhard Sittig 		if (delta < epsilon)
152ba218127SGerhard Sittig 			break;
1534b5006ecSAnatolij Gustschin 	}
154ba218127SGerhard Sittig 	if (offset <= epsilon) {
155ba218127SGerhard Sittig 		pr_debug("DIU clock accepted - %lu\n", want);
156ba218127SGerhard Sittig 		pr_debug("DIU pixclock want %u, got %lu, delta %lu, eps %lu\n",
157ba218127SGerhard Sittig 			 pixclock, got, delta, epsilon);
158ba218127SGerhard Sittig 		return;
1594b5006ecSAnatolij Gustschin 	}
160ba218127SGerhard Sittig 	pr_warn("DIU pixclock auto search unsuccessful\n");
1614b5006ecSAnatolij Gustschin 
162ba218127SGerhard Sittig 	/*
163ba218127SGerhard Sittig 	 * what is the most appropriate action to take when the search
164ba218127SGerhard Sittig 	 * for an available pixel clock which is acceptable to the
165ba218127SGerhard Sittig 	 * monitor has failed?  disable the DIU (clock) or just provide
166ba218127SGerhard Sittig 	 * a "best effort"?  we go with the latter
167ba218127SGerhard Sittig 	 */
168ba218127SGerhard Sittig 	pr_warn("DIU pixclock best effort fallback (backend's choice)\n");
169ba218127SGerhard Sittig 	clk_set_rate(clk_diu, pixclock);
170ba218127SGerhard Sittig 	got = clk_get_rate(clk_diu);
171ba218127SGerhard Sittig 	delta = abs(pixclock - got);
172ba218127SGerhard Sittig 	pr_debug("DIU pixclock want %u, got %lu, delta %lu, eps %lu\n",
173ba218127SGerhard Sittig 		 pixclock, got, delta, epsilon);
1744b5006ecSAnatolij Gustschin }
1754b5006ecSAnatolij Gustschin 
1767e198197SBrian Norris static enum fsl_diu_monitor_port
1777653aaabSTimur Tabi mpc512x_valid_monitor_port(enum fsl_diu_monitor_port port)
1784b5006ecSAnatolij Gustschin {
1797653aaabSTimur Tabi 	return FSL_DIU_PORT_DVI;
1804b5006ecSAnatolij Gustschin }
1814b5006ecSAnatolij Gustschin 
1824b5006ecSAnatolij Gustschin static struct fsl_diu_shared_fb __attribute__ ((__aligned__(8))) diu_shared_fb;
1834b5006ecSAnatolij Gustschin 
1844b5006ecSAnatolij Gustschin static inline void mpc512x_free_bootmem(struct page *page)
1854b5006ecSAnatolij Gustschin {
1864b5006ecSAnatolij Gustschin 	BUG_ON(PageTail(page));
187fe896d18SJoonsoo Kim 	BUG_ON(page_ref_count(page) > 1);
1885d585e5cSJiang Liu 	free_reserved_page(page);
1894b5006ecSAnatolij Gustschin }
1904b5006ecSAnatolij Gustschin 
1917e198197SBrian Norris static void mpc512x_release_bootmem(void)
1924b5006ecSAnatolij Gustschin {
1934b5006ecSAnatolij Gustschin 	unsigned long addr = diu_shared_fb.fb_phys & PAGE_MASK;
1944b5006ecSAnatolij Gustschin 	unsigned long size = diu_shared_fb.fb_len;
1954b5006ecSAnatolij Gustschin 	unsigned long start, end;
1964b5006ecSAnatolij Gustschin 
1974b5006ecSAnatolij Gustschin 	if (diu_shared_fb.in_use) {
1984b5006ecSAnatolij Gustschin 		start = PFN_UP(addr);
1994b5006ecSAnatolij Gustschin 		end = PFN_DOWN(addr + size);
2004b5006ecSAnatolij Gustschin 
2014b5006ecSAnatolij Gustschin 		for (; start < end; start++)
2024b5006ecSAnatolij Gustschin 			mpc512x_free_bootmem(pfn_to_page(start));
2034b5006ecSAnatolij Gustschin 
2044b5006ecSAnatolij Gustschin 		diu_shared_fb.in_use = false;
2054b5006ecSAnatolij Gustschin 	}
2064b5006ecSAnatolij Gustschin 	diu_ops.release_bootmem	= NULL;
2074b5006ecSAnatolij Gustschin }
2084b5006ecSAnatolij Gustschin 
2094b5006ecSAnatolij Gustschin /*
2104b5006ecSAnatolij Gustschin  * Check if DIU was pre-initialized. If so, perform steps
2114b5006ecSAnatolij Gustschin  * needed to continue displaying through the whole boot process.
2124b5006ecSAnatolij Gustschin  * Move area descriptor and gamma table elsewhere, they are
2134b5006ecSAnatolij Gustschin  * destroyed by bootmem allocator otherwise. The frame buffer
2144b5006ecSAnatolij Gustschin  * address range will be reserved in setup_arch() after bootmem
2154b5006ecSAnatolij Gustschin  * allocator is up.
2164b5006ecSAnatolij Gustschin  */
2177e198197SBrian Norris static void __init mpc512x_init_diu(void)
2184b5006ecSAnatolij Gustschin {
2194b5006ecSAnatolij Gustschin 	struct device_node *np;
2204b5006ecSAnatolij Gustschin 	struct diu __iomem *diu_reg;
2214b5006ecSAnatolij Gustschin 	phys_addr_t desc;
2224b5006ecSAnatolij Gustschin 	void __iomem *vaddr;
2234b5006ecSAnatolij Gustschin 	unsigned long mode, pix_fmt, res, bpp;
2244b5006ecSAnatolij Gustschin 	unsigned long dst;
2254b5006ecSAnatolij Gustschin 
2264b5006ecSAnatolij Gustschin 	np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu");
2274b5006ecSAnatolij Gustschin 	if (!np) {
2284b5006ecSAnatolij Gustschin 		pr_err("No DIU node\n");
2294b5006ecSAnatolij Gustschin 		return;
2304b5006ecSAnatolij Gustschin 	}
2314b5006ecSAnatolij Gustschin 
2324b5006ecSAnatolij Gustschin 	diu_reg = of_iomap(np, 0);
2334b5006ecSAnatolij Gustschin 	of_node_put(np);
2344b5006ecSAnatolij Gustschin 	if (!diu_reg) {
2354b5006ecSAnatolij Gustschin 		pr_err("Can't map DIU\n");
2364b5006ecSAnatolij Gustschin 		return;
2374b5006ecSAnatolij Gustschin 	}
2384b5006ecSAnatolij Gustschin 
2394b5006ecSAnatolij Gustschin 	mode = in_be32(&diu_reg->diu_mode);
240c4e5a023STimur Tabi 	if (mode == MFB_MODE0) {
2414b5006ecSAnatolij Gustschin 		pr_info("%s: DIU OFF\n", __func__);
2424b5006ecSAnatolij Gustschin 		goto out;
2434b5006ecSAnatolij Gustschin 	}
2444b5006ecSAnatolij Gustschin 
2454b5006ecSAnatolij Gustschin 	desc = in_be32(&diu_reg->desc[0]);
2464b5006ecSAnatolij Gustschin 	vaddr = ioremap(desc, sizeof(struct diu_ad));
2474b5006ecSAnatolij Gustschin 	if (!vaddr) {
2484b5006ecSAnatolij Gustschin 		pr_err("Can't map DIU area desc.\n");
2494b5006ecSAnatolij Gustschin 		goto out;
2504b5006ecSAnatolij Gustschin 	}
2514b5006ecSAnatolij Gustschin 	memcpy(&diu_shared_fb.ad0, vaddr, sizeof(struct diu_ad));
2524b5006ecSAnatolij Gustschin 	/* flush fb area descriptor */
2534b5006ecSAnatolij Gustschin 	dst = (unsigned long)&diu_shared_fb.ad0;
2544b5006ecSAnatolij Gustschin 	flush_dcache_range(dst, dst + sizeof(struct diu_ad) - 1);
2554b5006ecSAnatolij Gustschin 
2564b5006ecSAnatolij Gustschin 	res = in_be32(&diu_reg->disp_size);
2574b5006ecSAnatolij Gustschin 	pix_fmt = in_le32(vaddr);
2584b5006ecSAnatolij Gustschin 	bpp = ((pix_fmt >> 16) & 0x3) + 1;
2594b5006ecSAnatolij Gustschin 	diu_shared_fb.fb_phys = in_le32(vaddr + 4);
2604b5006ecSAnatolij Gustschin 	diu_shared_fb.fb_len = ((res & 0xfff0000) >> 16) * (res & 0xfff) * bpp;
2614b5006ecSAnatolij Gustschin 	diu_shared_fb.in_use = true;
2624b5006ecSAnatolij Gustschin 	iounmap(vaddr);
2634b5006ecSAnatolij Gustschin 
2644b5006ecSAnatolij Gustschin 	desc = in_be32(&diu_reg->gamma);
2654b5006ecSAnatolij Gustschin 	vaddr = ioremap(desc, sizeof(diu_shared_fb.gamma));
2664b5006ecSAnatolij Gustschin 	if (!vaddr) {
2674b5006ecSAnatolij Gustschin 		pr_err("Can't map DIU area desc.\n");
2684b5006ecSAnatolij Gustschin 		diu_shared_fb.in_use = false;
2694b5006ecSAnatolij Gustschin 		goto out;
2704b5006ecSAnatolij Gustschin 	}
2714b5006ecSAnatolij Gustschin 	memcpy(&diu_shared_fb.gamma, vaddr, sizeof(diu_shared_fb.gamma));
2724b5006ecSAnatolij Gustschin 	/* flush gamma table */
2734b5006ecSAnatolij Gustschin 	dst = (unsigned long)&diu_shared_fb.gamma;
2744b5006ecSAnatolij Gustschin 	flush_dcache_range(dst, dst + sizeof(diu_shared_fb.gamma) - 1);
2754b5006ecSAnatolij Gustschin 
2764b5006ecSAnatolij Gustschin 	iounmap(vaddr);
2774b5006ecSAnatolij Gustschin 	out_be32(&diu_reg->gamma, virt_to_phys(&diu_shared_fb.gamma));
2784b5006ecSAnatolij Gustschin 	out_be32(&diu_reg->desc[1], 0);
2794b5006ecSAnatolij Gustschin 	out_be32(&diu_reg->desc[2], 0);
2804b5006ecSAnatolij Gustschin 	out_be32(&diu_reg->desc[0], virt_to_phys(&diu_shared_fb.ad0));
2814b5006ecSAnatolij Gustschin 
2824b5006ecSAnatolij Gustschin out:
2834b5006ecSAnatolij Gustschin 	iounmap(diu_reg);
2844b5006ecSAnatolij Gustschin }
2854b5006ecSAnatolij Gustschin 
2867e198197SBrian Norris static void __init mpc512x_setup_diu(void)
2874b5006ecSAnatolij Gustschin {
2884b5006ecSAnatolij Gustschin 	int ret;
2894b5006ecSAnatolij Gustschin 
2904b5006ecSAnatolij Gustschin 	/*
2914b5006ecSAnatolij Gustschin 	 * We do not allocate and configure new area for bitmap buffer
2924b5006ecSAnatolij Gustschin 	 * because it would requere copying bitmap data (splash image)
2934b5006ecSAnatolij Gustschin 	 * and so negatively affect boot time. Instead we reserve the
2944b5006ecSAnatolij Gustschin 	 * already configured frame buffer area so that it won't be
2954b5006ecSAnatolij Gustschin 	 * destroyed. The starting address of the area to reserve and
29610239733SAnton Blanchard 	 * also it's length is passed to memblock_reserve(). It will be
2974b5006ecSAnatolij Gustschin 	 * freed later on first open of fbdev, when splash image is not
2984b5006ecSAnatolij Gustschin 	 * needed any more.
2994b5006ecSAnatolij Gustschin 	 */
3004b5006ecSAnatolij Gustschin 	if (diu_shared_fb.in_use) {
30110239733SAnton Blanchard 		ret = memblock_reserve(diu_shared_fb.fb_phys,
30210239733SAnton Blanchard 				       diu_shared_fb.fb_len);
3034b5006ecSAnatolij Gustschin 		if (ret) {
3044b5006ecSAnatolij Gustschin 			pr_err("%s: reserve bootmem failed\n", __func__);
3054b5006ecSAnatolij Gustschin 			diu_shared_fb.in_use = false;
3064b5006ecSAnatolij Gustschin 		}
3074b5006ecSAnatolij Gustschin 	}
3084b5006ecSAnatolij Gustschin 
3094b5006ecSAnatolij Gustschin 	diu_ops.set_pixel_clock		= mpc512x_set_pixel_clock;
3107653aaabSTimur Tabi 	diu_ops.valid_monitor_port	= mpc512x_valid_monitor_port;
3114b5006ecSAnatolij Gustschin 	diu_ops.release_bootmem		= mpc512x_release_bootmem;
3124b5006ecSAnatolij Gustschin }
3134b5006ecSAnatolij Gustschin 
314fb180322SJohn Rigby void __init mpc512x_init_IRQ(void)
315fb180322SJohn Rigby {
316fb180322SJohn Rigby 	struct device_node *np;
317fb180322SJohn Rigby 
318fb180322SJohn Rigby 	np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-ipic");
319fb180322SJohn Rigby 	if (!np)
320fb180322SJohn Rigby 		return;
321fb180322SJohn Rigby 
322fb180322SJohn Rigby 	ipic_init(np, 0);
323fb180322SJohn Rigby 	of_node_put(np);
324fb180322SJohn Rigby 
325fb180322SJohn Rigby 	/*
326fb180322SJohn Rigby 	 * Initialize the default interrupt mapping priorities,
327fb180322SJohn Rigby 	 * in case the boot rom changed something on us.
328fb180322SJohn Rigby 	 */
329fb180322SJohn Rigby 	ipic_set_default_priority();
330fb180322SJohn Rigby }
331fb180322SJohn Rigby 
332fb180322SJohn Rigby /*
333fb180322SJohn Rigby  * Nodes to do bus probe on, soc and localbus
334fb180322SJohn Rigby  */
335ce6d73c9SUwe Kleine-König static const struct of_device_id of_bus_ids[] __initconst = {
336fb180322SJohn Rigby 	{ .compatible = "fsl,mpc5121-immr", },
337fb180322SJohn Rigby 	{ .compatible = "fsl,mpc5121-localbus", },
338534ada5eSAnatolij Gustschin 	{ .compatible = "fsl,mpc5121-mbx", },
339534ada5eSAnatolij Gustschin 	{ .compatible = "fsl,mpc5121-nfc", },
340534ada5eSAnatolij Gustschin 	{ .compatible = "fsl,mpc5121-sram", },
341534ada5eSAnatolij Gustschin 	{ .compatible = "fsl,mpc5121-pci", },
342534ada5eSAnatolij Gustschin 	{ .compatible = "gpio-leds", },
343fb180322SJohn Rigby 	{},
344fb180322SJohn Rigby };
345fb180322SJohn Rigby 
3467e198197SBrian Norris static void __init mpc512x_declare_of_platform_devices(void)
347fb180322SJohn Rigby {
348fb180322SJohn Rigby 	if (of_platform_bus_probe(NULL, of_bus_ids, NULL))
349fb180322SJohn Rigby 		printk(KERN_ERR __FILE__ ": "
350fb180322SJohn Rigby 			"Error while probing of_platform bus\n");
351fb180322SJohn Rigby }
352fb180322SJohn Rigby 
3532da8cb6aSAnatolij Gustschin #define DEFAULT_FIFO_SIZE 16
3542da8cb6aSAnatolij Gustschin 
355*2493a242SNick Child const char *__init mpc512x_select_psc_compat(void)
356a9b6aae4SMatteo Facchinetti {
357a9b6aae4SMatteo Facchinetti 	if (of_machine_is_compatible("fsl,mpc5121"))
358a9b6aae4SMatteo Facchinetti 		return "fsl,mpc5121-psc";
359a9b6aae4SMatteo Facchinetti 
360a9b6aae4SMatteo Facchinetti 	if (of_machine_is_compatible("fsl,mpc5125"))
361a9b6aae4SMatteo Facchinetti 		return "fsl,mpc5125-psc";
362a9b6aae4SMatteo Facchinetti 
363a9b6aae4SMatteo Facchinetti 	return NULL;
364a9b6aae4SMatteo Facchinetti }
365a9b6aae4SMatteo Facchinetti 
366*2493a242SNick Child const char *__init mpc512x_select_reset_compat(void)
3670875a88eSMatteo Facchinetti {
3680875a88eSMatteo Facchinetti 	if (of_machine_is_compatible("fsl,mpc5121"))
3690875a88eSMatteo Facchinetti 		return "fsl,mpc5121-reset";
3700875a88eSMatteo Facchinetti 
3710875a88eSMatteo Facchinetti 	if (of_machine_is_compatible("fsl,mpc5125"))
3720875a88eSMatteo Facchinetti 		return "fsl,mpc5125-reset";
3730875a88eSMatteo Facchinetti 
3740875a88eSMatteo Facchinetti 	return NULL;
3750875a88eSMatteo Facchinetti }
3760875a88eSMatteo Facchinetti 
3772da8cb6aSAnatolij Gustschin static unsigned int __init get_fifo_size(struct device_node *np,
3782da8cb6aSAnatolij Gustschin 					 char *prop_name)
3792da8cb6aSAnatolij Gustschin {
3802da8cb6aSAnatolij Gustschin 	const unsigned int *fp;
3812da8cb6aSAnatolij Gustschin 
3822da8cb6aSAnatolij Gustschin 	fp = of_get_property(np, prop_name, NULL);
3832da8cb6aSAnatolij Gustschin 	if (fp)
3842da8cb6aSAnatolij Gustschin 		return *fp;
3852da8cb6aSAnatolij Gustschin 
386f2c2cbccSJoe Perches 	pr_warn("no %s property in %pOF node, defaulting to %d\n",
387b7c670d6SRob Herring 		prop_name, np, DEFAULT_FIFO_SIZE);
3882da8cb6aSAnatolij Gustschin 
3892da8cb6aSAnatolij Gustschin 	return DEFAULT_FIFO_SIZE;
3902da8cb6aSAnatolij Gustschin }
3912da8cb6aSAnatolij Gustschin 
3922da8cb6aSAnatolij Gustschin #define FIFOC(_base) ((struct mpc512x_psc_fifo __iomem *) \
3932da8cb6aSAnatolij Gustschin 		    ((u32)(_base) + sizeof(struct mpc52xx_psc)))
3942da8cb6aSAnatolij Gustschin 
3952da8cb6aSAnatolij Gustschin /* Init PSC FIFO space for TX and RX slices */
3967e198197SBrian Norris static void __init mpc512x_psc_fifo_init(void)
3972da8cb6aSAnatolij Gustschin {
3982da8cb6aSAnatolij Gustschin 	struct device_node *np;
3992da8cb6aSAnatolij Gustschin 	void __iomem *psc;
4002da8cb6aSAnatolij Gustschin 	unsigned int tx_fifo_size;
4012da8cb6aSAnatolij Gustschin 	unsigned int rx_fifo_size;
402a9b6aae4SMatteo Facchinetti 	const char *psc_compat;
4032da8cb6aSAnatolij Gustschin 	int fifobase = 0; /* current fifo address in 32 bit words */
4042da8cb6aSAnatolij Gustschin 
405a9b6aae4SMatteo Facchinetti 	psc_compat = mpc512x_select_psc_compat();
406a9b6aae4SMatteo Facchinetti 	if (!psc_compat) {
407a9b6aae4SMatteo Facchinetti 		pr_err("%s: no compatible devices found\n", __func__);
408a9b6aae4SMatteo Facchinetti 		return;
409a9b6aae4SMatteo Facchinetti 	}
410a9b6aae4SMatteo Facchinetti 
411a9b6aae4SMatteo Facchinetti 	for_each_compatible_node(np, NULL, psc_compat) {
4122da8cb6aSAnatolij Gustschin 		tx_fifo_size = get_fifo_size(np, "fsl,tx-fifo-size");
4132da8cb6aSAnatolij Gustschin 		rx_fifo_size = get_fifo_size(np, "fsl,rx-fifo-size");
4142da8cb6aSAnatolij Gustschin 
4152da8cb6aSAnatolij Gustschin 		/* size in register is in 4 byte units */
4162da8cb6aSAnatolij Gustschin 		tx_fifo_size /= 4;
4172da8cb6aSAnatolij Gustschin 		rx_fifo_size /= 4;
4182da8cb6aSAnatolij Gustschin 		if (!tx_fifo_size)
4192da8cb6aSAnatolij Gustschin 			tx_fifo_size = 1;
4202da8cb6aSAnatolij Gustschin 		if (!rx_fifo_size)
4212da8cb6aSAnatolij Gustschin 			rx_fifo_size = 1;
4222da8cb6aSAnatolij Gustschin 
4232da8cb6aSAnatolij Gustschin 		psc = of_iomap(np, 0);
4242da8cb6aSAnatolij Gustschin 		if (!psc) {
425b7c670d6SRob Herring 			pr_err("%s: Can't map %pOF device\n",
426b7c670d6SRob Herring 				__func__, np);
4272da8cb6aSAnatolij Gustschin 			continue;
4282da8cb6aSAnatolij Gustschin 		}
4292da8cb6aSAnatolij Gustschin 
4302da8cb6aSAnatolij Gustschin 		/* FIFO space is 4KiB, check if requested size is available */
4312da8cb6aSAnatolij Gustschin 		if ((fifobase + tx_fifo_size + rx_fifo_size) > 0x1000) {
432b7c670d6SRob Herring 			pr_err("%s: no fifo space available for %pOF\n",
433b7c670d6SRob Herring 				__func__, np);
4342da8cb6aSAnatolij Gustschin 			iounmap(psc);
4352da8cb6aSAnatolij Gustschin 			/*
4362da8cb6aSAnatolij Gustschin 			 * chances are that another device requests less
4372da8cb6aSAnatolij Gustschin 			 * fifo space, so we continue.
4382da8cb6aSAnatolij Gustschin 			 */
4392da8cb6aSAnatolij Gustschin 			continue;
4402da8cb6aSAnatolij Gustschin 		}
4412da8cb6aSAnatolij Gustschin 
4422da8cb6aSAnatolij Gustschin 		/* set tx and rx fifo size registers */
4432da8cb6aSAnatolij Gustschin 		out_be32(&FIFOC(psc)->txsz, (fifobase << 16) | tx_fifo_size);
4442da8cb6aSAnatolij Gustschin 		fifobase += tx_fifo_size;
4452da8cb6aSAnatolij Gustschin 		out_be32(&FIFOC(psc)->rxsz, (fifobase << 16) | rx_fifo_size);
4462da8cb6aSAnatolij Gustschin 		fifobase += rx_fifo_size;
4472da8cb6aSAnatolij Gustschin 
4482da8cb6aSAnatolij Gustschin 		/* reset and enable the slices */
4492da8cb6aSAnatolij Gustschin 		out_be32(&FIFOC(psc)->txcmd, 0x80);
4502da8cb6aSAnatolij Gustschin 		out_be32(&FIFOC(psc)->txcmd, 0x01);
4512da8cb6aSAnatolij Gustschin 		out_be32(&FIFOC(psc)->rxcmd, 0x80);
4522da8cb6aSAnatolij Gustschin 		out_be32(&FIFOC(psc)->rxcmd, 0x01);
4532da8cb6aSAnatolij Gustschin 
4542da8cb6aSAnatolij Gustschin 		iounmap(psc);
4552da8cb6aSAnatolij Gustschin 	}
4562da8cb6aSAnatolij Gustschin }
4572da8cb6aSAnatolij Gustschin 
4582abbbb63SGerhard Sittig void __init mpc512x_init_early(void)
4592abbbb63SGerhard Sittig {
460a4f4124cSGerhard Sittig 	mpc512x_restart_init();
4612abbbb63SGerhard Sittig 	if (IS_ENABLED(CONFIG_FB_FSL_DIU))
4622abbbb63SGerhard Sittig 		mpc512x_init_diu();
4632abbbb63SGerhard Sittig }
4642abbbb63SGerhard Sittig 
465284ed66fSAnatolij Gustschin void __init mpc512x_init(void)
466284ed66fSAnatolij Gustschin {
467284ed66fSAnatolij Gustschin 	mpc5121_clk_init();
468f29bc0a4SAnatolij Gustschin 	mpc512x_declare_of_platform_devices();
4692da8cb6aSAnatolij Gustschin 	mpc512x_psc_fifo_init();
470284ed66fSAnatolij Gustschin }
471edfcf33cSAnatolij Gustschin 
4722abbbb63SGerhard Sittig void __init mpc512x_setup_arch(void)
4732abbbb63SGerhard Sittig {
4742abbbb63SGerhard Sittig 	if (IS_ENABLED(CONFIG_FB_FSL_DIU))
4752abbbb63SGerhard Sittig 		mpc512x_setup_diu();
4762abbbb63SGerhard Sittig }
4772abbbb63SGerhard Sittig 
478edfcf33cSAnatolij Gustschin /**
479edfcf33cSAnatolij Gustschin  * mpc512x_cs_config - Setup chip select configuration
480edfcf33cSAnatolij Gustschin  * @cs: chip select number
481edfcf33cSAnatolij Gustschin  * @val: chip select configuration value
482edfcf33cSAnatolij Gustschin  *
483edfcf33cSAnatolij Gustschin  * Perform chip select configuration for devices on LocalPlus Bus.
484edfcf33cSAnatolij Gustschin  * Intended to dynamically reconfigure the chip select parameters
485edfcf33cSAnatolij Gustschin  * for configurable devices on the bus.
486edfcf33cSAnatolij Gustschin  */
487edfcf33cSAnatolij Gustschin int mpc512x_cs_config(unsigned int cs, u32 val)
488edfcf33cSAnatolij Gustschin {
489edfcf33cSAnatolij Gustschin 	static struct mpc512x_lpc __iomem *lpc;
490edfcf33cSAnatolij Gustschin 	struct device_node *np;
491edfcf33cSAnatolij Gustschin 
492edfcf33cSAnatolij Gustschin 	if (cs > 7)
493edfcf33cSAnatolij Gustschin 		return -EINVAL;
494edfcf33cSAnatolij Gustschin 
495edfcf33cSAnatolij Gustschin 	if (!lpc) {
496edfcf33cSAnatolij Gustschin 		np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-lpc");
497edfcf33cSAnatolij Gustschin 		lpc = of_iomap(np, 0);
498edfcf33cSAnatolij Gustschin 		of_node_put(np);
499edfcf33cSAnatolij Gustschin 		if (!lpc)
500edfcf33cSAnatolij Gustschin 			return -ENOMEM;
501edfcf33cSAnatolij Gustschin 	}
502edfcf33cSAnatolij Gustschin 
503edfcf33cSAnatolij Gustschin 	out_be32(&lpc->cs_cfg[cs], val);
504edfcf33cSAnatolij Gustschin 	return 0;
505edfcf33cSAnatolij Gustschin }
506edfcf33cSAnatolij Gustschin EXPORT_SYMBOL(mpc512x_cs_config);
507