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>
15e6f6390aSChristophe Leroy #include <linux/of_address.h>
16fb180322SJohn Rigby #include <linux/of_platform.h>
174b5006ecSAnatolij Gustschin #include <linux/fsl-diu-fb.h>
1810239733SAnton Blanchard #include <linux/memblock.h>
194b5006ecSAnatolij Gustschin #include <sysdev/fsl_soc.h>
20fb180322SJohn Rigby 
214b5006ecSAnatolij Gustschin #include <asm/cacheflush.h>
22fb180322SJohn Rigby #include <asm/machdep.h>
23fb180322SJohn Rigby #include <asm/ipic.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 
mpc512x_restart(char * cmd)3295ec77c0SDaniel Axtens void __noreturn mpc512x_restart(char *cmd)
33a8dbceb7SAnatolij Gustschin {
34a8dbceb7SAnatolij Gustschin 	if (reset_module_base) {
35a8dbceb7SAnatolij Gustschin 		/* Enable software reset "RSTE" */
36a8dbceb7SAnatolij Gustschin 		out_be32(&reset_module_base->rpr, 0x52535445);
37a8dbceb7SAnatolij Gustschin 		/* Set software hard reset */
38a8dbceb7SAnatolij Gustschin 		out_be32(&reset_module_base->rcr, 0x2);
39a8dbceb7SAnatolij Gustschin 	} else {
40a8dbceb7SAnatolij Gustschin 		pr_err("Restart module not mapped.\n");
41a8dbceb7SAnatolij Gustschin 	}
42a8dbceb7SAnatolij Gustschin 	for (;;)
43a8dbceb7SAnatolij Gustschin 		;
44a8dbceb7SAnatolij Gustschin }
45a8dbceb7SAnatolij Gustschin 
464b5006ecSAnatolij Gustschin struct fsl_diu_shared_fb {
474b5006ecSAnatolij Gustschin 	u8		gamma[0x300];	/* 32-bit aligned! */
484b5006ecSAnatolij Gustschin 	struct diu_ad	ad0;		/* 32-bit aligned! */
494b5006ecSAnatolij Gustschin 	phys_addr_t	fb_phys;
504b5006ecSAnatolij Gustschin 	size_t		fb_len;
514b5006ecSAnatolij Gustschin 	bool		in_use;
524b5006ecSAnatolij Gustschin };
534b5006ecSAnatolij Gustschin 
54ba218127SGerhard Sittig /* receives a pixel clock spec in pico seconds, adjusts the DIU clock rate */
mpc512x_set_pixel_clock(unsigned int pixclock)557e198197SBrian Norris static void mpc512x_set_pixel_clock(unsigned int pixclock)
564b5006ecSAnatolij Gustschin {
574b5006ecSAnatolij Gustschin 	struct device_node *np;
58ba218127SGerhard Sittig 	struct clk *clk_diu;
59ba218127SGerhard Sittig 	unsigned long epsilon, minpixclock, maxpixclock;
60ba218127SGerhard Sittig 	unsigned long offset, want, got, delta;
614b5006ecSAnatolij Gustschin 
62ba218127SGerhard Sittig 	/* lookup and enable the DIU clock */
63ba218127SGerhard Sittig 	np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu");
644b5006ecSAnatolij Gustschin 	if (!np) {
65ba218127SGerhard Sittig 		pr_err("Could not find DIU device tree node.\n");
664b5006ecSAnatolij Gustschin 		return;
674b5006ecSAnatolij Gustschin 	}
68ba218127SGerhard Sittig 	clk_diu = of_clk_get(np, 0);
69ba218127SGerhard Sittig 	if (IS_ERR(clk_diu)) {
70ba218127SGerhard Sittig 		/* backwards compat with device trees that lack clock specs */
71ba218127SGerhard Sittig 		clk_diu = clk_get_sys(np->name, "ipg");
72ba218127SGerhard Sittig 	}
734b5006ecSAnatolij Gustschin 	of_node_put(np);
74ba218127SGerhard Sittig 	if (IS_ERR(clk_diu)) {
75ba218127SGerhard Sittig 		pr_err("Could not lookup DIU clock.\n");
76ba218127SGerhard Sittig 		return;
77ba218127SGerhard Sittig 	}
78ba218127SGerhard Sittig 	if (clk_prepare_enable(clk_diu)) {
79ba218127SGerhard Sittig 		pr_err("Could not enable DIU clock.\n");
804b5006ecSAnatolij Gustschin 		return;
814b5006ecSAnatolij Gustschin 	}
824b5006ecSAnatolij Gustschin 
83ba218127SGerhard Sittig 	/*
84ba218127SGerhard Sittig 	 * convert the picoseconds spec into the desired clock rate,
85ba218127SGerhard Sittig 	 * determine the acceptable clock range for the monitor (+/- 5%),
86ba218127SGerhard Sittig 	 * do the calculation in steps to avoid integer overflow
87ba218127SGerhard Sittig 	 */
88ba218127SGerhard Sittig 	pr_debug("DIU pixclock in ps - %u\n", pixclock);
89ba218127SGerhard Sittig 	pixclock = (1000000000 / pixclock) * 1000;
904b5006ecSAnatolij Gustschin 	pr_debug("DIU pixclock freq  - %u\n", pixclock);
91ba218127SGerhard Sittig 	epsilon = pixclock / 20; /* pixclock * 0.05 */
92ba218127SGerhard Sittig 	pr_debug("DIU deviation      - %lu\n", epsilon);
93ba218127SGerhard Sittig 	minpixclock = pixclock - epsilon;
94ba218127SGerhard Sittig 	maxpixclock = pixclock + epsilon;
954b5006ecSAnatolij Gustschin 	pr_debug("DIU minpixclock    - %lu\n", minpixclock);
964b5006ecSAnatolij Gustschin 	pr_debug("DIU maxpixclock    - %lu\n", maxpixclock);
974b5006ecSAnatolij Gustschin 
98ba218127SGerhard Sittig 	/*
99ba218127SGerhard Sittig 	 * check whether the DIU supports the desired pixel clock
100ba218127SGerhard Sittig 	 *
101ba218127SGerhard Sittig 	 * - simply request the desired clock and see what the
102ba218127SGerhard Sittig 	 *   platform's clock driver will make of it, assuming that it
103ba218127SGerhard Sittig 	 *   will setup the best approximation of the requested value
104ba218127SGerhard Sittig 	 * - try other candidate frequencies in the order of decreasing
105ba218127SGerhard Sittig 	 *   preference (i.e. with increasing distance from the desired
106ba218127SGerhard Sittig 	 *   pixel clock, and checking the lower frequency before the
107ba218127SGerhard Sittig 	 *   higher frequency to not overload the hardware) until the
108ba218127SGerhard Sittig 	 *   first match is found -- any potential subsequent match
109ba218127SGerhard Sittig 	 *   would only be as good as the former match or typically
110ba218127SGerhard Sittig 	 *   would be less preferrable
111ba218127SGerhard Sittig 	 *
112ba218127SGerhard Sittig 	 * the offset increment of pixelclock divided by 64 is an
113ba218127SGerhard Sittig 	 * arbitrary choice -- it's simple to calculate, in the typical
114ba218127SGerhard Sittig 	 * case we expect the first check to succeed already, in the
115ba218127SGerhard Sittig 	 * worst case seven frequencies get tested (the exact center and
116ba218127SGerhard Sittig 	 * three more values each to the left and to the right) before
117ba218127SGerhard Sittig 	 * the 5% tolerance window is exceeded, resulting in fast enough
118ba218127SGerhard Sittig 	 * execution yet high enough probability of finding a suitable
119ba218127SGerhard Sittig 	 * value, while the error rate will be in the order of single
120ba218127SGerhard Sittig 	 * percents
121ba218127SGerhard Sittig 	 */
122ba218127SGerhard Sittig 	for (offset = 0; offset <= epsilon; offset += pixclock / 64) {
123ba218127SGerhard Sittig 		want = pixclock - offset;
124ba218127SGerhard Sittig 		pr_debug("DIU checking clock - %lu\n", want);
125ba218127SGerhard Sittig 		clk_set_rate(clk_diu, want);
126ba218127SGerhard Sittig 		got = clk_get_rate(clk_diu);
127ba218127SGerhard Sittig 		delta = abs(pixclock - got);
128ba218127SGerhard Sittig 		if (delta < epsilon)
129ba218127SGerhard Sittig 			break;
130ba218127SGerhard Sittig 		if (!offset)
131ba218127SGerhard Sittig 			continue;
132ba218127SGerhard Sittig 		want = pixclock + offset;
133ba218127SGerhard Sittig 		pr_debug("DIU checking clock - %lu\n", want);
134ba218127SGerhard Sittig 		clk_set_rate(clk_diu, want);
135ba218127SGerhard Sittig 		got = clk_get_rate(clk_diu);
136ba218127SGerhard Sittig 		delta = abs(pixclock - got);
137ba218127SGerhard Sittig 		if (delta < epsilon)
138ba218127SGerhard Sittig 			break;
1394b5006ecSAnatolij Gustschin 	}
140ba218127SGerhard Sittig 	if (offset <= epsilon) {
141ba218127SGerhard Sittig 		pr_debug("DIU clock accepted - %lu\n", want);
142ba218127SGerhard Sittig 		pr_debug("DIU pixclock want %u, got %lu, delta %lu, eps %lu\n",
143ba218127SGerhard Sittig 			 pixclock, got, delta, epsilon);
144ba218127SGerhard Sittig 		return;
1454b5006ecSAnatolij Gustschin 	}
146ba218127SGerhard Sittig 	pr_warn("DIU pixclock auto search unsuccessful\n");
1474b5006ecSAnatolij Gustschin 
148ba218127SGerhard Sittig 	/*
149ba218127SGerhard Sittig 	 * what is the most appropriate action to take when the search
150ba218127SGerhard Sittig 	 * for an available pixel clock which is acceptable to the
151ba218127SGerhard Sittig 	 * monitor has failed?  disable the DIU (clock) or just provide
152ba218127SGerhard Sittig 	 * a "best effort"?  we go with the latter
153ba218127SGerhard Sittig 	 */
154ba218127SGerhard Sittig 	pr_warn("DIU pixclock best effort fallback (backend's choice)\n");
155ba218127SGerhard Sittig 	clk_set_rate(clk_diu, pixclock);
156ba218127SGerhard Sittig 	got = clk_get_rate(clk_diu);
157ba218127SGerhard Sittig 	delta = abs(pixclock - got);
158ba218127SGerhard Sittig 	pr_debug("DIU pixclock want %u, got %lu, delta %lu, eps %lu\n",
159ba218127SGerhard Sittig 		 pixclock, got, delta, epsilon);
1604b5006ecSAnatolij Gustschin }
1614b5006ecSAnatolij Gustschin 
1627e198197SBrian Norris static enum fsl_diu_monitor_port
mpc512x_valid_monitor_port(enum fsl_diu_monitor_port port)1637653aaabSTimur Tabi mpc512x_valid_monitor_port(enum fsl_diu_monitor_port port)
1644b5006ecSAnatolij Gustschin {
1657653aaabSTimur Tabi 	return FSL_DIU_PORT_DVI;
1664b5006ecSAnatolij Gustschin }
1674b5006ecSAnatolij Gustschin 
1684b5006ecSAnatolij Gustschin static struct fsl_diu_shared_fb __attribute__ ((__aligned__(8))) diu_shared_fb;
1694b5006ecSAnatolij Gustschin 
mpc512x_free_bootmem(struct page * page)1704b5006ecSAnatolij Gustschin static inline void mpc512x_free_bootmem(struct page *page)
1714b5006ecSAnatolij Gustschin {
1724b5006ecSAnatolij Gustschin 	BUG_ON(PageTail(page));
173fe896d18SJoonsoo Kim 	BUG_ON(page_ref_count(page) > 1);
1745d585e5cSJiang Liu 	free_reserved_page(page);
1754b5006ecSAnatolij Gustschin }
1764b5006ecSAnatolij Gustschin 
mpc512x_release_bootmem(void)1777e198197SBrian Norris static void mpc512x_release_bootmem(void)
1784b5006ecSAnatolij Gustschin {
1794b5006ecSAnatolij Gustschin 	unsigned long addr = diu_shared_fb.fb_phys & PAGE_MASK;
1804b5006ecSAnatolij Gustschin 	unsigned long size = diu_shared_fb.fb_len;
1814b5006ecSAnatolij Gustschin 	unsigned long start, end;
1824b5006ecSAnatolij Gustschin 
1834b5006ecSAnatolij Gustschin 	if (diu_shared_fb.in_use) {
1844b5006ecSAnatolij Gustschin 		start = PFN_UP(addr);
1854b5006ecSAnatolij Gustschin 		end = PFN_DOWN(addr + size);
1864b5006ecSAnatolij Gustschin 
1874b5006ecSAnatolij Gustschin 		for (; start < end; start++)
1884b5006ecSAnatolij Gustschin 			mpc512x_free_bootmem(pfn_to_page(start));
1894b5006ecSAnatolij Gustschin 
1904b5006ecSAnatolij Gustschin 		diu_shared_fb.in_use = false;
1914b5006ecSAnatolij Gustschin 	}
1924b5006ecSAnatolij Gustschin 	diu_ops.release_bootmem	= NULL;
1934b5006ecSAnatolij Gustschin }
1944b5006ecSAnatolij Gustschin 
1954b5006ecSAnatolij Gustschin /*
1964b5006ecSAnatolij Gustschin  * Check if DIU was pre-initialized. If so, perform steps
1974b5006ecSAnatolij Gustschin  * needed to continue displaying through the whole boot process.
1984b5006ecSAnatolij Gustschin  * Move area descriptor and gamma table elsewhere, they are
1994b5006ecSAnatolij Gustschin  * destroyed by bootmem allocator otherwise. The frame buffer
2004b5006ecSAnatolij Gustschin  * address range will be reserved in setup_arch() after bootmem
2014b5006ecSAnatolij Gustschin  * allocator is up.
2024b5006ecSAnatolij Gustschin  */
mpc512x_init_diu(void)2037e198197SBrian Norris static void __init mpc512x_init_diu(void)
2044b5006ecSAnatolij Gustschin {
2054b5006ecSAnatolij Gustschin 	struct device_node *np;
2064b5006ecSAnatolij Gustschin 	struct diu __iomem *diu_reg;
2074b5006ecSAnatolij Gustschin 	phys_addr_t desc;
2084b5006ecSAnatolij Gustschin 	void __iomem *vaddr;
2094b5006ecSAnatolij Gustschin 	unsigned long mode, pix_fmt, res, bpp;
2104b5006ecSAnatolij Gustschin 	unsigned long dst;
2114b5006ecSAnatolij Gustschin 
2124b5006ecSAnatolij Gustschin 	np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu");
2134b5006ecSAnatolij Gustschin 	if (!np) {
2144b5006ecSAnatolij Gustschin 		pr_err("No DIU node\n");
2154b5006ecSAnatolij Gustschin 		return;
2164b5006ecSAnatolij Gustschin 	}
2174b5006ecSAnatolij Gustschin 
2184b5006ecSAnatolij Gustschin 	diu_reg = of_iomap(np, 0);
2194b5006ecSAnatolij Gustschin 	of_node_put(np);
2204b5006ecSAnatolij Gustschin 	if (!diu_reg) {
2214b5006ecSAnatolij Gustschin 		pr_err("Can't map DIU\n");
2224b5006ecSAnatolij Gustschin 		return;
2234b5006ecSAnatolij Gustschin 	}
2244b5006ecSAnatolij Gustschin 
2254b5006ecSAnatolij Gustschin 	mode = in_be32(&diu_reg->diu_mode);
226c4e5a023STimur Tabi 	if (mode == MFB_MODE0) {
2274b5006ecSAnatolij Gustschin 		pr_info("%s: DIU OFF\n", __func__);
2284b5006ecSAnatolij Gustschin 		goto out;
2294b5006ecSAnatolij Gustschin 	}
2304b5006ecSAnatolij Gustschin 
2314b5006ecSAnatolij Gustschin 	desc = in_be32(&diu_reg->desc[0]);
2324b5006ecSAnatolij Gustschin 	vaddr = ioremap(desc, sizeof(struct diu_ad));
2334b5006ecSAnatolij Gustschin 	if (!vaddr) {
2344b5006ecSAnatolij Gustschin 		pr_err("Can't map DIU area desc.\n");
2354b5006ecSAnatolij Gustschin 		goto out;
2364b5006ecSAnatolij Gustschin 	}
2374b5006ecSAnatolij Gustschin 	memcpy(&diu_shared_fb.ad0, vaddr, sizeof(struct diu_ad));
2384b5006ecSAnatolij Gustschin 	/* flush fb area descriptor */
2394b5006ecSAnatolij Gustschin 	dst = (unsigned long)&diu_shared_fb.ad0;
2404b5006ecSAnatolij Gustschin 	flush_dcache_range(dst, dst + sizeof(struct diu_ad) - 1);
2414b5006ecSAnatolij Gustschin 
2424b5006ecSAnatolij Gustschin 	res = in_be32(&diu_reg->disp_size);
2434b5006ecSAnatolij Gustschin 	pix_fmt = in_le32(vaddr);
2444b5006ecSAnatolij Gustschin 	bpp = ((pix_fmt >> 16) & 0x3) + 1;
2454b5006ecSAnatolij Gustschin 	diu_shared_fb.fb_phys = in_le32(vaddr + 4);
2464b5006ecSAnatolij Gustschin 	diu_shared_fb.fb_len = ((res & 0xfff0000) >> 16) * (res & 0xfff) * bpp;
2474b5006ecSAnatolij Gustschin 	diu_shared_fb.in_use = true;
2484b5006ecSAnatolij Gustschin 	iounmap(vaddr);
2494b5006ecSAnatolij Gustschin 
2504b5006ecSAnatolij Gustschin 	desc = in_be32(&diu_reg->gamma);
2514b5006ecSAnatolij Gustschin 	vaddr = ioremap(desc, sizeof(diu_shared_fb.gamma));
2524b5006ecSAnatolij Gustschin 	if (!vaddr) {
2534b5006ecSAnatolij Gustschin 		pr_err("Can't map DIU area desc.\n");
2544b5006ecSAnatolij Gustschin 		diu_shared_fb.in_use = false;
2554b5006ecSAnatolij Gustschin 		goto out;
2564b5006ecSAnatolij Gustschin 	}
2574b5006ecSAnatolij Gustschin 	memcpy(&diu_shared_fb.gamma, vaddr, sizeof(diu_shared_fb.gamma));
2584b5006ecSAnatolij Gustschin 	/* flush gamma table */
2594b5006ecSAnatolij Gustschin 	dst = (unsigned long)&diu_shared_fb.gamma;
2604b5006ecSAnatolij Gustschin 	flush_dcache_range(dst, dst + sizeof(diu_shared_fb.gamma) - 1);
2614b5006ecSAnatolij Gustschin 
2624b5006ecSAnatolij Gustschin 	iounmap(vaddr);
2634b5006ecSAnatolij Gustschin 	out_be32(&diu_reg->gamma, virt_to_phys(&diu_shared_fb.gamma));
2644b5006ecSAnatolij Gustschin 	out_be32(&diu_reg->desc[1], 0);
2654b5006ecSAnatolij Gustschin 	out_be32(&diu_reg->desc[2], 0);
2664b5006ecSAnatolij Gustschin 	out_be32(&diu_reg->desc[0], virt_to_phys(&diu_shared_fb.ad0));
2674b5006ecSAnatolij Gustschin 
2684b5006ecSAnatolij Gustschin out:
2694b5006ecSAnatolij Gustschin 	iounmap(diu_reg);
2704b5006ecSAnatolij Gustschin }
2714b5006ecSAnatolij Gustschin 
mpc512x_setup_diu(void)2727e198197SBrian Norris static void __init mpc512x_setup_diu(void)
2734b5006ecSAnatolij Gustschin {
2744b5006ecSAnatolij Gustschin 	int ret;
2754b5006ecSAnatolij Gustschin 
2764b5006ecSAnatolij Gustschin 	/*
2774b5006ecSAnatolij Gustschin 	 * We do not allocate and configure new area for bitmap buffer
2781fd02f66SJulia Lawall 	 * because it would require copying bitmap data (splash image)
2794b5006ecSAnatolij Gustschin 	 * and so negatively affect boot time. Instead we reserve the
2804b5006ecSAnatolij Gustschin 	 * already configured frame buffer area so that it won't be
2814b5006ecSAnatolij Gustschin 	 * destroyed. The starting address of the area to reserve and
28210239733SAnton Blanchard 	 * also it's length is passed to memblock_reserve(). It will be
2834b5006ecSAnatolij Gustschin 	 * freed later on first open of fbdev, when splash image is not
2844b5006ecSAnatolij Gustschin 	 * needed any more.
2854b5006ecSAnatolij Gustschin 	 */
2864b5006ecSAnatolij Gustschin 	if (diu_shared_fb.in_use) {
28710239733SAnton Blanchard 		ret = memblock_reserve(diu_shared_fb.fb_phys,
28810239733SAnton Blanchard 				       diu_shared_fb.fb_len);
2894b5006ecSAnatolij Gustschin 		if (ret) {
2904b5006ecSAnatolij Gustschin 			pr_err("%s: reserve bootmem failed\n", __func__);
2914b5006ecSAnatolij Gustschin 			diu_shared_fb.in_use = false;
2924b5006ecSAnatolij Gustschin 		}
2934b5006ecSAnatolij Gustschin 	}
2944b5006ecSAnatolij Gustschin 
2954b5006ecSAnatolij Gustschin 	diu_ops.set_pixel_clock		= mpc512x_set_pixel_clock;
2967653aaabSTimur Tabi 	diu_ops.valid_monitor_port	= mpc512x_valid_monitor_port;
2974b5006ecSAnatolij Gustschin 	diu_ops.release_bootmem		= mpc512x_release_bootmem;
2984b5006ecSAnatolij Gustschin }
2994b5006ecSAnatolij Gustschin 
mpc512x_init_IRQ(void)300fb180322SJohn Rigby void __init mpc512x_init_IRQ(void)
301fb180322SJohn Rigby {
302fb180322SJohn Rigby 	struct device_node *np;
303fb180322SJohn Rigby 
304fb180322SJohn Rigby 	np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-ipic");
305fb180322SJohn Rigby 	if (!np)
306fb180322SJohn Rigby 		return;
307fb180322SJohn Rigby 
308fb180322SJohn Rigby 	ipic_init(np, 0);
309fb180322SJohn Rigby 	of_node_put(np);
310fb180322SJohn Rigby 
311fb180322SJohn Rigby 	/*
312fb180322SJohn Rigby 	 * Initialize the default interrupt mapping priorities,
313fb180322SJohn Rigby 	 * in case the boot rom changed something on us.
314fb180322SJohn Rigby 	 */
315fb180322SJohn Rigby 	ipic_set_default_priority();
316fb180322SJohn Rigby }
317fb180322SJohn Rigby 
318fb180322SJohn Rigby /*
319fb180322SJohn Rigby  * Nodes to do bus probe on, soc and localbus
320fb180322SJohn Rigby  */
321ce6d73c9SUwe Kleine-König static const struct of_device_id of_bus_ids[] __initconst = {
322fb180322SJohn Rigby 	{ .compatible = "fsl,mpc5121-immr", },
323fb180322SJohn Rigby 	{ .compatible = "fsl,mpc5121-localbus", },
324534ada5eSAnatolij Gustschin 	{ .compatible = "fsl,mpc5121-mbx", },
325534ada5eSAnatolij Gustschin 	{ .compatible = "fsl,mpc5121-nfc", },
326534ada5eSAnatolij Gustschin 	{ .compatible = "fsl,mpc5121-sram", },
327534ada5eSAnatolij Gustschin 	{ .compatible = "fsl,mpc5121-pci", },
328534ada5eSAnatolij Gustschin 	{ .compatible = "gpio-leds", },
329fb180322SJohn Rigby 	{},
330fb180322SJohn Rigby };
331fb180322SJohn Rigby 
mpc512x_declare_of_platform_devices(void)3327e198197SBrian Norris static void __init mpc512x_declare_of_platform_devices(void)
333fb180322SJohn Rigby {
334fb180322SJohn Rigby 	if (of_platform_bus_probe(NULL, of_bus_ids, NULL))
335fb180322SJohn Rigby 		printk(KERN_ERR __FILE__ ": "
336fb180322SJohn Rigby 			"Error while probing of_platform bus\n");
337fb180322SJohn Rigby }
338fb180322SJohn Rigby 
3392da8cb6aSAnatolij Gustschin #define DEFAULT_FIFO_SIZE 16
3402da8cb6aSAnatolij Gustschin 
mpc512x_select_psc_compat(void)3412493a242SNick Child const char *__init mpc512x_select_psc_compat(void)
342a9b6aae4SMatteo Facchinetti {
343a9b6aae4SMatteo Facchinetti 	if (of_machine_is_compatible("fsl,mpc5121"))
344a9b6aae4SMatteo Facchinetti 		return "fsl,mpc5121-psc";
345a9b6aae4SMatteo Facchinetti 
346a9b6aae4SMatteo Facchinetti 	if (of_machine_is_compatible("fsl,mpc5125"))
347a9b6aae4SMatteo Facchinetti 		return "fsl,mpc5125-psc";
348a9b6aae4SMatteo Facchinetti 
349a9b6aae4SMatteo Facchinetti 	return NULL;
350a9b6aae4SMatteo Facchinetti }
351a9b6aae4SMatteo Facchinetti 
mpc512x_select_reset_compat(void)352*be922070SChristophe Leroy static const char *__init mpc512x_select_reset_compat(void)
3530875a88eSMatteo Facchinetti {
3540875a88eSMatteo Facchinetti 	if (of_machine_is_compatible("fsl,mpc5121"))
3550875a88eSMatteo Facchinetti 		return "fsl,mpc5121-reset";
3560875a88eSMatteo Facchinetti 
3570875a88eSMatteo Facchinetti 	if (of_machine_is_compatible("fsl,mpc5125"))
3580875a88eSMatteo Facchinetti 		return "fsl,mpc5125-reset";
3590875a88eSMatteo Facchinetti 
3600875a88eSMatteo Facchinetti 	return NULL;
3610875a88eSMatteo Facchinetti }
3620875a88eSMatteo Facchinetti 
get_fifo_size(struct device_node * np,char * prop_name)3632da8cb6aSAnatolij Gustschin static unsigned int __init get_fifo_size(struct device_node *np,
3642da8cb6aSAnatolij Gustschin 					 char *prop_name)
3652da8cb6aSAnatolij Gustschin {
3662da8cb6aSAnatolij Gustschin 	const unsigned int *fp;
3672da8cb6aSAnatolij Gustschin 
3682da8cb6aSAnatolij Gustschin 	fp = of_get_property(np, prop_name, NULL);
3692da8cb6aSAnatolij Gustschin 	if (fp)
3702da8cb6aSAnatolij Gustschin 		return *fp;
3712da8cb6aSAnatolij Gustschin 
372f2c2cbccSJoe Perches 	pr_warn("no %s property in %pOF node, defaulting to %d\n",
373b7c670d6SRob Herring 		prop_name, np, DEFAULT_FIFO_SIZE);
3742da8cb6aSAnatolij Gustschin 
3752da8cb6aSAnatolij Gustschin 	return DEFAULT_FIFO_SIZE;
3762da8cb6aSAnatolij Gustschin }
3772da8cb6aSAnatolij Gustschin 
3782da8cb6aSAnatolij Gustschin #define FIFOC(_base) ((struct mpc512x_psc_fifo __iomem *) \
3792da8cb6aSAnatolij Gustschin 		    ((u32)(_base) + sizeof(struct mpc52xx_psc)))
3802da8cb6aSAnatolij Gustschin 
3812da8cb6aSAnatolij Gustschin /* Init PSC FIFO space for TX and RX slices */
mpc512x_psc_fifo_init(void)3827e198197SBrian Norris static void __init mpc512x_psc_fifo_init(void)
3832da8cb6aSAnatolij Gustschin {
3842da8cb6aSAnatolij Gustschin 	struct device_node *np;
3852da8cb6aSAnatolij Gustschin 	void __iomem *psc;
3862da8cb6aSAnatolij Gustschin 	unsigned int tx_fifo_size;
3872da8cb6aSAnatolij Gustschin 	unsigned int rx_fifo_size;
388a9b6aae4SMatteo Facchinetti 	const char *psc_compat;
3892da8cb6aSAnatolij Gustschin 	int fifobase = 0; /* current fifo address in 32 bit words */
3902da8cb6aSAnatolij Gustschin 
391a9b6aae4SMatteo Facchinetti 	psc_compat = mpc512x_select_psc_compat();
392a9b6aae4SMatteo Facchinetti 	if (!psc_compat) {
393a9b6aae4SMatteo Facchinetti 		pr_err("%s: no compatible devices found\n", __func__);
394a9b6aae4SMatteo Facchinetti 		return;
395a9b6aae4SMatteo Facchinetti 	}
396a9b6aae4SMatteo Facchinetti 
397a9b6aae4SMatteo Facchinetti 	for_each_compatible_node(np, NULL, psc_compat) {
3982da8cb6aSAnatolij Gustschin 		tx_fifo_size = get_fifo_size(np, "fsl,tx-fifo-size");
3992da8cb6aSAnatolij Gustschin 		rx_fifo_size = get_fifo_size(np, "fsl,rx-fifo-size");
4002da8cb6aSAnatolij Gustschin 
4012da8cb6aSAnatolij Gustschin 		/* size in register is in 4 byte units */
4022da8cb6aSAnatolij Gustschin 		tx_fifo_size /= 4;
4032da8cb6aSAnatolij Gustschin 		rx_fifo_size /= 4;
4042da8cb6aSAnatolij Gustschin 		if (!tx_fifo_size)
4052da8cb6aSAnatolij Gustschin 			tx_fifo_size = 1;
4062da8cb6aSAnatolij Gustschin 		if (!rx_fifo_size)
4072da8cb6aSAnatolij Gustschin 			rx_fifo_size = 1;
4082da8cb6aSAnatolij Gustschin 
4092da8cb6aSAnatolij Gustschin 		psc = of_iomap(np, 0);
4102da8cb6aSAnatolij Gustschin 		if (!psc) {
411b7c670d6SRob Herring 			pr_err("%s: Can't map %pOF device\n",
412b7c670d6SRob Herring 				__func__, np);
4132da8cb6aSAnatolij Gustschin 			continue;
4142da8cb6aSAnatolij Gustschin 		}
4152da8cb6aSAnatolij Gustschin 
4162da8cb6aSAnatolij Gustschin 		/* FIFO space is 4KiB, check if requested size is available */
4172da8cb6aSAnatolij Gustschin 		if ((fifobase + tx_fifo_size + rx_fifo_size) > 0x1000) {
418b7c670d6SRob Herring 			pr_err("%s: no fifo space available for %pOF\n",
419b7c670d6SRob Herring 				__func__, np);
4202da8cb6aSAnatolij Gustschin 			iounmap(psc);
4212da8cb6aSAnatolij Gustschin 			/*
4222da8cb6aSAnatolij Gustschin 			 * chances are that another device requests less
4232da8cb6aSAnatolij Gustschin 			 * fifo space, so we continue.
4242da8cb6aSAnatolij Gustschin 			 */
4252da8cb6aSAnatolij Gustschin 			continue;
4262da8cb6aSAnatolij Gustschin 		}
4272da8cb6aSAnatolij Gustschin 
4282da8cb6aSAnatolij Gustschin 		/* set tx and rx fifo size registers */
4292da8cb6aSAnatolij Gustschin 		out_be32(&FIFOC(psc)->txsz, (fifobase << 16) | tx_fifo_size);
4302da8cb6aSAnatolij Gustschin 		fifobase += tx_fifo_size;
4312da8cb6aSAnatolij Gustschin 		out_be32(&FIFOC(psc)->rxsz, (fifobase << 16) | rx_fifo_size);
4322da8cb6aSAnatolij Gustschin 		fifobase += rx_fifo_size;
4332da8cb6aSAnatolij Gustschin 
4342da8cb6aSAnatolij Gustschin 		/* reset and enable the slices */
4352da8cb6aSAnatolij Gustschin 		out_be32(&FIFOC(psc)->txcmd, 0x80);
4362da8cb6aSAnatolij Gustschin 		out_be32(&FIFOC(psc)->txcmd, 0x01);
4372da8cb6aSAnatolij Gustschin 		out_be32(&FIFOC(psc)->rxcmd, 0x80);
4382da8cb6aSAnatolij Gustschin 		out_be32(&FIFOC(psc)->rxcmd, 0x01);
4392da8cb6aSAnatolij Gustschin 
4402da8cb6aSAnatolij Gustschin 		iounmap(psc);
4412da8cb6aSAnatolij Gustschin 	}
4422da8cb6aSAnatolij Gustschin }
4432da8cb6aSAnatolij Gustschin 
mpc512x_restart_init(void)444*be922070SChristophe Leroy static void __init mpc512x_restart_init(void)
445*be922070SChristophe Leroy {
446*be922070SChristophe Leroy 	struct device_node *np;
447*be922070SChristophe Leroy 	const char *reset_compat;
448*be922070SChristophe Leroy 
449*be922070SChristophe Leroy 	reset_compat = mpc512x_select_reset_compat();
450*be922070SChristophe Leroy 	np = of_find_compatible_node(NULL, NULL, reset_compat);
451*be922070SChristophe Leroy 	if (!np)
452*be922070SChristophe Leroy 		return;
453*be922070SChristophe Leroy 
454*be922070SChristophe Leroy 	reset_module_base = of_iomap(np, 0);
455*be922070SChristophe Leroy 	of_node_put(np);
456*be922070SChristophe Leroy }
457*be922070SChristophe Leroy 
mpc512x_init_early(void)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 
mpc512x_init(void)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 
mpc512x_setup_arch(void)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  */
mpc512x_cs_config(unsigned int cs,u32 val)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