| 
									
										
										
										
											2008-07-09 14:54:03 -06:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. All rights reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Author: John Rigby <jrigby@freescale.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Description: | 
					
						
							|  |  |  |  * MPC512x Shared code | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This is free software; you can redistribute it and/or modify it | 
					
						
							|  |  |  |  * under the terms of the GNU General Public License as published by | 
					
						
							|  |  |  |  * the Free Software Foundation; either version 2 of the License, or | 
					
						
							|  |  |  |  * (at your option) any later version. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-30 23:51:36 +01:00
										 |  |  | #include <linux/clk.h>
 | 
					
						
							| 
									
										
										
										
											2008-07-09 14:54:03 -06:00
										 |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/io.h>
 | 
					
						
							|  |  |  | #include <linux/irq.h>
 | 
					
						
							|  |  |  | #include <linux/of_platform.h>
 | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | #include <linux/fsl-diu-fb.h>
 | 
					
						
							|  |  |  | #include <linux/bootmem.h>
 | 
					
						
							|  |  |  | #include <sysdev/fsl_soc.h>
 | 
					
						
							| 
									
										
										
										
											2008-07-09 14:54:03 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | #include <asm/cacheflush.h>
 | 
					
						
							| 
									
										
										
										
											2008-07-09 14:54:03 -06:00
										 |  |  | #include <asm/machdep.h>
 | 
					
						
							|  |  |  | #include <asm/ipic.h>
 | 
					
						
							|  |  |  | #include <asm/prom.h>
 | 
					
						
							|  |  |  | #include <asm/time.h>
 | 
					
						
							| 
									
										
										
										
											2010-02-16 10:36:26 -07:00
										 |  |  | #include <asm/mpc5121.h>
 | 
					
						
							| 
									
										
										
										
											2010-04-30 13:21:26 +00:00
										 |  |  | #include <asm/mpc52xx_psc.h>
 | 
					
						
							| 
									
										
										
										
											2008-07-09 14:54:03 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "mpc512x.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-16 10:36:26 -07:00
										 |  |  | static struct mpc512x_reset_module __iomem *reset_module_base; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __init mpc512x_restart_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct device_node *np; | 
					
						
							| 
									
										
										
										
											2013-06-10 10:13:52 +02:00
										 |  |  | 	const char *reset_compat; | 
					
						
							| 
									
										
										
										
											2010-02-16 10:36:26 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-10 10:13:52 +02:00
										 |  |  | 	reset_compat = mpc512x_select_reset_compat(); | 
					
						
							|  |  |  | 	np = of_find_compatible_node(NULL, NULL, reset_compat); | 
					
						
							| 
									
										
										
										
											2010-02-16 10:36:26 -07:00
										 |  |  | 	if (!np) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	reset_module_base = of_iomap(np, 0); | 
					
						
							|  |  |  | 	of_node_put(np); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void mpc512x_restart(char *cmd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (reset_module_base) { | 
					
						
							|  |  |  | 		/* Enable software reset "RSTE" */ | 
					
						
							|  |  |  | 		out_be32(&reset_module_base->rpr, 0x52535445); | 
					
						
							|  |  |  | 		/* Set software hard reset */ | 
					
						
							|  |  |  | 		out_be32(&reset_module_base->rcr, 0x2); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		pr_err("Restart module not mapped.\n"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for (;;) | 
					
						
							|  |  |  | 		; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | struct fsl_diu_shared_fb { | 
					
						
							|  |  |  | 	u8		gamma[0x300];	/* 32-bit aligned! */ | 
					
						
							|  |  |  | 	struct diu_ad	ad0;		/* 32-bit aligned! */ | 
					
						
							|  |  |  | 	phys_addr_t	fb_phys; | 
					
						
							|  |  |  | 	size_t		fb_len; | 
					
						
							|  |  |  | 	bool		in_use; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-30 23:51:36 +01:00
										 |  |  | /* receives a pixel clock spec in pico seconds, adjusts the DIU clock rate */ | 
					
						
							| 
									
										
										
										
											2013-10-11 10:37:38 -07:00
										 |  |  | static void mpc512x_set_pixel_clock(unsigned int pixclock) | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct device_node *np; | 
					
						
							| 
									
										
										
										
											2013-11-30 23:51:36 +01:00
										 |  |  | 	struct clk *clk_diu; | 
					
						
							|  |  |  | 	unsigned long epsilon, minpixclock, maxpixclock; | 
					
						
							|  |  |  | 	unsigned long offset, want, got, delta; | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-30 23:51:36 +01:00
										 |  |  | 	/* lookup and enable the DIU clock */ | 
					
						
							|  |  |  | 	np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu"); | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | 	if (!np) { | 
					
						
							| 
									
										
										
										
											2013-11-30 23:51:36 +01:00
										 |  |  | 		pr_err("Could not find DIU device tree node.\n"); | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-11-30 23:51:36 +01:00
										 |  |  | 	clk_diu = of_clk_get(np, 0); | 
					
						
							|  |  |  | 	if (IS_ERR(clk_diu)) { | 
					
						
							|  |  |  | 		/* backwards compat with device trees that lack clock specs */ | 
					
						
							|  |  |  | 		clk_diu = clk_get_sys(np->name, "ipg"); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | 	of_node_put(np); | 
					
						
							| 
									
										
										
										
											2013-11-30 23:51:36 +01:00
										 |  |  | 	if (IS_ERR(clk_diu)) { | 
					
						
							|  |  |  | 		pr_err("Could not lookup DIU clock.\n"); | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-11-30 23:51:36 +01:00
										 |  |  | 	if (clk_prepare_enable(clk_diu)) { | 
					
						
							|  |  |  | 		pr_err("Could not enable DIU clock.\n"); | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-30 23:51:36 +01:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * convert the picoseconds spec into the desired clock rate, | 
					
						
							|  |  |  | 	 * determine the acceptable clock range for the monitor (+/- 5%), | 
					
						
							|  |  |  | 	 * do the calculation in steps to avoid integer overflow | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	pr_debug("DIU pixclock in ps - %u\n", pixclock); | 
					
						
							|  |  |  | 	pixclock = (1000000000 / pixclock) * 1000; | 
					
						
							|  |  |  | 	pr_debug("DIU pixclock freq  - %u\n", pixclock); | 
					
						
							|  |  |  | 	epsilon = pixclock / 20; /* pixclock * 0.05 */ | 
					
						
							|  |  |  | 	pr_debug("DIU deviation      - %lu\n", epsilon); | 
					
						
							|  |  |  | 	minpixclock = pixclock - epsilon; | 
					
						
							|  |  |  | 	maxpixclock = pixclock + epsilon; | 
					
						
							|  |  |  | 	pr_debug("DIU minpixclock    - %lu\n", minpixclock); | 
					
						
							|  |  |  | 	pr_debug("DIU maxpixclock    - %lu\n", maxpixclock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * check whether the DIU supports the desired pixel clock | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * - simply request the desired clock and see what the | 
					
						
							|  |  |  | 	 *   platform's clock driver will make of it, assuming that it | 
					
						
							|  |  |  | 	 *   will setup the best approximation of the requested value | 
					
						
							|  |  |  | 	 * - try other candidate frequencies in the order of decreasing | 
					
						
							|  |  |  | 	 *   preference (i.e. with increasing distance from the desired | 
					
						
							|  |  |  | 	 *   pixel clock, and checking the lower frequency before the | 
					
						
							|  |  |  | 	 *   higher frequency to not overload the hardware) until the | 
					
						
							|  |  |  | 	 *   first match is found -- any potential subsequent match | 
					
						
							|  |  |  | 	 *   would only be as good as the former match or typically | 
					
						
							|  |  |  | 	 *   would be less preferrable | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * the offset increment of pixelclock divided by 64 is an | 
					
						
							|  |  |  | 	 * arbitrary choice -- it's simple to calculate, in the typical | 
					
						
							|  |  |  | 	 * case we expect the first check to succeed already, in the | 
					
						
							|  |  |  | 	 * worst case seven frequencies get tested (the exact center and | 
					
						
							|  |  |  | 	 * three more values each to the left and to the right) before | 
					
						
							|  |  |  | 	 * the 5% tolerance window is exceeded, resulting in fast enough | 
					
						
							|  |  |  | 	 * execution yet high enough probability of finding a suitable | 
					
						
							|  |  |  | 	 * value, while the error rate will be in the order of single | 
					
						
							|  |  |  | 	 * percents | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	for (offset = 0; offset <= epsilon; offset += pixclock / 64) { | 
					
						
							|  |  |  | 		want = pixclock - offset; | 
					
						
							|  |  |  | 		pr_debug("DIU checking clock - %lu\n", want); | 
					
						
							|  |  |  | 		clk_set_rate(clk_diu, want); | 
					
						
							|  |  |  | 		got = clk_get_rate(clk_diu); | 
					
						
							|  |  |  | 		delta = abs(pixclock - got); | 
					
						
							|  |  |  | 		if (delta < epsilon) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		if (!offset) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		want = pixclock + offset; | 
					
						
							|  |  |  | 		pr_debug("DIU checking clock - %lu\n", want); | 
					
						
							|  |  |  | 		clk_set_rate(clk_diu, want); | 
					
						
							|  |  |  | 		got = clk_get_rate(clk_diu); | 
					
						
							|  |  |  | 		delta = abs(pixclock - got); | 
					
						
							|  |  |  | 		if (delta < epsilon) | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-11-30 23:51:36 +01:00
										 |  |  | 	if (offset <= epsilon) { | 
					
						
							|  |  |  | 		pr_debug("DIU clock accepted - %lu\n", want); | 
					
						
							|  |  |  | 		pr_debug("DIU pixclock want %u, got %lu, delta %lu, eps %lu\n", | 
					
						
							|  |  |  | 			 pixclock, got, delta, epsilon); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	pr_warn("DIU pixclock auto search unsuccessful\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * what is the most appropriate action to take when the search | 
					
						
							|  |  |  | 	 * for an available pixel clock which is acceptable to the | 
					
						
							|  |  |  | 	 * monitor has failed?  disable the DIU (clock) or just provide | 
					
						
							|  |  |  | 	 * a "best effort"?  we go with the latter | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	pr_warn("DIU pixclock best effort fallback (backend's choice)\n"); | 
					
						
							|  |  |  | 	clk_set_rate(clk_diu, pixclock); | 
					
						
							|  |  |  | 	got = clk_get_rate(clk_diu); | 
					
						
							|  |  |  | 	delta = abs(pixclock - got); | 
					
						
							|  |  |  | 	pr_debug("DIU pixclock want %u, got %lu, delta %lu, eps %lu\n", | 
					
						
							|  |  |  | 		 pixclock, got, delta, epsilon); | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-11 10:37:38 -07:00
										 |  |  | static enum fsl_diu_monitor_port | 
					
						
							| 
									
										
										
										
											2011-07-09 15:38:14 -05:00
										 |  |  | mpc512x_valid_monitor_port(enum fsl_diu_monitor_port port) | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-07-09 15:38:14 -05:00
										 |  |  | 	return FSL_DIU_PORT_DVI; | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct fsl_diu_shared_fb __attribute__ ((__aligned__(8))) diu_shared_fb; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline void mpc512x_free_bootmem(struct page *page) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BUG_ON(PageTail(page)); | 
					
						
							|  |  |  | 	BUG_ON(atomic_read(&page->_count) > 1); | 
					
						
							| 
									
										
										
										
											2013-04-29 15:06:47 -07:00
										 |  |  | 	free_reserved_page(page); | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-11 10:37:38 -07:00
										 |  |  | static void mpc512x_release_bootmem(void) | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	unsigned long addr = diu_shared_fb.fb_phys & PAGE_MASK; | 
					
						
							|  |  |  | 	unsigned long size = diu_shared_fb.fb_len; | 
					
						
							|  |  |  | 	unsigned long start, end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (diu_shared_fb.in_use) { | 
					
						
							|  |  |  | 		start = PFN_UP(addr); | 
					
						
							|  |  |  | 		end = PFN_DOWN(addr + size); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for (; start < end; start++) | 
					
						
							|  |  |  | 			mpc512x_free_bootmem(pfn_to_page(start)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		diu_shared_fb.in_use = false; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	diu_ops.release_bootmem	= NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Check if DIU was pre-initialized. If so, perform steps | 
					
						
							|  |  |  |  * needed to continue displaying through the whole boot process. | 
					
						
							|  |  |  |  * Move area descriptor and gamma table elsewhere, they are | 
					
						
							|  |  |  |  * destroyed by bootmem allocator otherwise. The frame buffer | 
					
						
							|  |  |  |  * address range will be reserved in setup_arch() after bootmem | 
					
						
							|  |  |  |  * allocator is up. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-10-11 10:37:38 -07:00
										 |  |  | static void __init mpc512x_init_diu(void) | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct device_node *np; | 
					
						
							|  |  |  | 	struct diu __iomem *diu_reg; | 
					
						
							|  |  |  | 	phys_addr_t desc; | 
					
						
							|  |  |  | 	void __iomem *vaddr; | 
					
						
							|  |  |  | 	unsigned long mode, pix_fmt, res, bpp; | 
					
						
							|  |  |  | 	unsigned long dst; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu"); | 
					
						
							|  |  |  | 	if (!np) { | 
					
						
							|  |  |  | 		pr_err("No DIU node\n"); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	diu_reg = of_iomap(np, 0); | 
					
						
							|  |  |  | 	of_node_put(np); | 
					
						
							|  |  |  | 	if (!diu_reg) { | 
					
						
							|  |  |  | 		pr_err("Can't map DIU\n"); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mode = in_be32(&diu_reg->diu_mode); | 
					
						
							| 
									
										
										
										
											2011-09-28 16:19:53 -05:00
										 |  |  | 	if (mode == MFB_MODE0) { | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | 		pr_info("%s: DIU OFF\n", __func__); | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	desc = in_be32(&diu_reg->desc[0]); | 
					
						
							|  |  |  | 	vaddr = ioremap(desc, sizeof(struct diu_ad)); | 
					
						
							|  |  |  | 	if (!vaddr) { | 
					
						
							|  |  |  | 		pr_err("Can't map DIU area desc.\n"); | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	memcpy(&diu_shared_fb.ad0, vaddr, sizeof(struct diu_ad)); | 
					
						
							|  |  |  | 	/* flush fb area descriptor */ | 
					
						
							|  |  |  | 	dst = (unsigned long)&diu_shared_fb.ad0; | 
					
						
							|  |  |  | 	flush_dcache_range(dst, dst + sizeof(struct diu_ad) - 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	res = in_be32(&diu_reg->disp_size); | 
					
						
							|  |  |  | 	pix_fmt = in_le32(vaddr); | 
					
						
							|  |  |  | 	bpp = ((pix_fmt >> 16) & 0x3) + 1; | 
					
						
							|  |  |  | 	diu_shared_fb.fb_phys = in_le32(vaddr + 4); | 
					
						
							|  |  |  | 	diu_shared_fb.fb_len = ((res & 0xfff0000) >> 16) * (res & 0xfff) * bpp; | 
					
						
							|  |  |  | 	diu_shared_fb.in_use = true; | 
					
						
							|  |  |  | 	iounmap(vaddr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	desc = in_be32(&diu_reg->gamma); | 
					
						
							|  |  |  | 	vaddr = ioremap(desc, sizeof(diu_shared_fb.gamma)); | 
					
						
							|  |  |  | 	if (!vaddr) { | 
					
						
							|  |  |  | 		pr_err("Can't map DIU area desc.\n"); | 
					
						
							|  |  |  | 		diu_shared_fb.in_use = false; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	memcpy(&diu_shared_fb.gamma, vaddr, sizeof(diu_shared_fb.gamma)); | 
					
						
							|  |  |  | 	/* flush gamma table */ | 
					
						
							|  |  |  | 	dst = (unsigned long)&diu_shared_fb.gamma; | 
					
						
							|  |  |  | 	flush_dcache_range(dst, dst + sizeof(diu_shared_fb.gamma) - 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	iounmap(vaddr); | 
					
						
							|  |  |  | 	out_be32(&diu_reg->gamma, virt_to_phys(&diu_shared_fb.gamma)); | 
					
						
							|  |  |  | 	out_be32(&diu_reg->desc[1], 0); | 
					
						
							|  |  |  | 	out_be32(&diu_reg->desc[2], 0); | 
					
						
							|  |  |  | 	out_be32(&diu_reg->desc[0], virt_to_phys(&diu_shared_fb.ad0)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	iounmap(diu_reg); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-11 10:37:38 -07:00
										 |  |  | static void __init mpc512x_setup_diu(void) | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * We do not allocate and configure new area for bitmap buffer | 
					
						
							|  |  |  | 	 * because it would requere copying bitmap data (splash image) | 
					
						
							|  |  |  | 	 * and so negatively affect boot time. Instead we reserve the | 
					
						
							|  |  |  | 	 * already configured frame buffer area so that it won't be | 
					
						
							|  |  |  | 	 * destroyed. The starting address of the area to reserve and | 
					
						
							|  |  |  | 	 * also it's length is passed to reserve_bootmem(). It will be | 
					
						
							|  |  |  | 	 * freed later on first open of fbdev, when splash image is not | 
					
						
							|  |  |  | 	 * needed any more. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (diu_shared_fb.in_use) { | 
					
						
							|  |  |  | 		ret = reserve_bootmem(diu_shared_fb.fb_phys, | 
					
						
							|  |  |  | 				      diu_shared_fb.fb_len, | 
					
						
							|  |  |  | 				      BOOTMEM_EXCLUSIVE); | 
					
						
							|  |  |  | 		if (ret) { | 
					
						
							|  |  |  | 			pr_err("%s: reserve bootmem failed\n", __func__); | 
					
						
							|  |  |  | 			diu_shared_fb.in_use = false; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	diu_ops.set_pixel_clock		= mpc512x_set_pixel_clock; | 
					
						
							| 
									
										
										
										
											2011-07-09 15:38:14 -05:00
										 |  |  | 	diu_ops.valid_monitor_port	= mpc512x_valid_monitor_port; | 
					
						
							| 
									
										
										
										
											2010-07-23 04:00:37 +00:00
										 |  |  | 	diu_ops.release_bootmem		= mpc512x_release_bootmem; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-07-09 14:54:03 -06:00
										 |  |  | void __init mpc512x_init_IRQ(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct device_node *np; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-ipic"); | 
					
						
							|  |  |  | 	if (!np) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ipic_init(np, 0); | 
					
						
							|  |  |  | 	of_node_put(np); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Initialize the default interrupt mapping priorities, | 
					
						
							|  |  |  | 	 * in case the boot rom changed something on us. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	ipic_set_default_priority(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Nodes to do bus probe on, soc and localbus | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-09-10 21:56:38 +02:00
										 |  |  | static const struct of_device_id of_bus_ids[] __initconst = { | 
					
						
							| 
									
										
										
										
											2008-07-09 14:54:03 -06:00
										 |  |  | 	{ .compatible = "fsl,mpc5121-immr", }, | 
					
						
							|  |  |  | 	{ .compatible = "fsl,mpc5121-localbus", }, | 
					
						
							| 
									
										
										
										
											2013-01-24 10:51:18 +01:00
										 |  |  | 	{ .compatible = "fsl,mpc5121-mbx", }, | 
					
						
							|  |  |  | 	{ .compatible = "fsl,mpc5121-nfc", }, | 
					
						
							|  |  |  | 	{ .compatible = "fsl,mpc5121-sram", }, | 
					
						
							|  |  |  | 	{ .compatible = "fsl,mpc5121-pci", }, | 
					
						
							|  |  |  | 	{ .compatible = "gpio-leds", }, | 
					
						
							| 
									
										
										
										
											2008-07-09 14:54:03 -06:00
										 |  |  | 	{}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-11 10:37:38 -07:00
										 |  |  | static void __init mpc512x_declare_of_platform_devices(void) | 
					
						
							| 
									
										
										
										
											2008-07-09 14:54:03 -06:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (of_platform_bus_probe(NULL, of_bus_ids, NULL)) | 
					
						
							|  |  |  | 		printk(KERN_ERR __FILE__ ": " | 
					
						
							|  |  |  | 			"Error while probing of_platform bus\n"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-30 13:21:26 +00:00
										 |  |  | #define DEFAULT_FIFO_SIZE 16
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-04 03:57:30 +00:00
										 |  |  | const char *mpc512x_select_psc_compat(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (of_machine_is_compatible("fsl,mpc5121")) | 
					
						
							|  |  |  | 		return "fsl,mpc5121-psc"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (of_machine_is_compatible("fsl,mpc5125")) | 
					
						
							|  |  |  | 		return "fsl,mpc5125-psc"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-10 10:13:52 +02:00
										 |  |  | const char *mpc512x_select_reset_compat(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (of_machine_is_compatible("fsl,mpc5121")) | 
					
						
							|  |  |  | 		return "fsl,mpc5121-reset"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (of_machine_is_compatible("fsl,mpc5125")) | 
					
						
							|  |  |  | 		return "fsl,mpc5125-reset"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-04-30 13:21:26 +00:00
										 |  |  | static unsigned int __init get_fifo_size(struct device_node *np, | 
					
						
							|  |  |  | 					 char *prop_name) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	const unsigned int *fp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fp = of_get_property(np, prop_name, NULL); | 
					
						
							|  |  |  | 	if (fp) | 
					
						
							|  |  |  | 		return *fp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pr_warning("no %s property in %s node, defaulting to %d\n", | 
					
						
							|  |  |  | 		   prop_name, np->full_name, DEFAULT_FIFO_SIZE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return DEFAULT_FIFO_SIZE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define FIFOC(_base) ((struct mpc512x_psc_fifo __iomem *) \
 | 
					
						
							|  |  |  | 		    ((u32)(_base) + sizeof(struct mpc52xx_psc))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Init PSC FIFO space for TX and RX slices */ | 
					
						
							| 
									
										
										
										
											2013-10-11 10:37:38 -07:00
										 |  |  | static void __init mpc512x_psc_fifo_init(void) | 
					
						
							| 
									
										
										
										
											2010-04-30 13:21:26 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct device_node *np; | 
					
						
							|  |  |  | 	void __iomem *psc; | 
					
						
							|  |  |  | 	unsigned int tx_fifo_size; | 
					
						
							|  |  |  | 	unsigned int rx_fifo_size; | 
					
						
							| 
									
										
										
										
											2013-04-04 03:57:30 +00:00
										 |  |  | 	const char *psc_compat; | 
					
						
							| 
									
										
										
										
											2010-04-30 13:21:26 +00:00
										 |  |  | 	int fifobase = 0; /* current fifo address in 32 bit words */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-04 03:57:30 +00:00
										 |  |  | 	psc_compat = mpc512x_select_psc_compat(); | 
					
						
							|  |  |  | 	if (!psc_compat) { | 
					
						
							|  |  |  | 		pr_err("%s: no compatible devices found\n", __func__); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for_each_compatible_node(np, NULL, psc_compat) { | 
					
						
							| 
									
										
										
										
											2010-04-30 13:21:26 +00:00
										 |  |  | 		tx_fifo_size = get_fifo_size(np, "fsl,tx-fifo-size"); | 
					
						
							|  |  |  | 		rx_fifo_size = get_fifo_size(np, "fsl,rx-fifo-size"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* size in register is in 4 byte units */ | 
					
						
							|  |  |  | 		tx_fifo_size /= 4; | 
					
						
							|  |  |  | 		rx_fifo_size /= 4; | 
					
						
							|  |  |  | 		if (!tx_fifo_size) | 
					
						
							|  |  |  | 			tx_fifo_size = 1; | 
					
						
							|  |  |  | 		if (!rx_fifo_size) | 
					
						
							|  |  |  | 			rx_fifo_size = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		psc = of_iomap(np, 0); | 
					
						
							|  |  |  | 		if (!psc) { | 
					
						
							|  |  |  | 			pr_err("%s: Can't map %s device\n", | 
					
						
							|  |  |  | 				__func__, np->full_name); | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* FIFO space is 4KiB, check if requested size is available */ | 
					
						
							|  |  |  | 		if ((fifobase + tx_fifo_size + rx_fifo_size) > 0x1000) { | 
					
						
							|  |  |  | 			pr_err("%s: no fifo space available for %s\n", | 
					
						
							|  |  |  | 				__func__, np->full_name); | 
					
						
							|  |  |  | 			iounmap(psc); | 
					
						
							|  |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * chances are that another device requests less | 
					
						
							|  |  |  | 			 * fifo space, so we continue. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* set tx and rx fifo size registers */ | 
					
						
							|  |  |  | 		out_be32(&FIFOC(psc)->txsz, (fifobase << 16) | tx_fifo_size); | 
					
						
							|  |  |  | 		fifobase += tx_fifo_size; | 
					
						
							|  |  |  | 		out_be32(&FIFOC(psc)->rxsz, (fifobase << 16) | rx_fifo_size); | 
					
						
							|  |  |  | 		fifobase += rx_fifo_size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* reset and enable the slices */ | 
					
						
							|  |  |  | 		out_be32(&FIFOC(psc)->txcmd, 0x80); | 
					
						
							|  |  |  | 		out_be32(&FIFOC(psc)->txcmd, 0x01); | 
					
						
							|  |  |  | 		out_be32(&FIFOC(psc)->rxcmd, 0x80); | 
					
						
							|  |  |  | 		out_be32(&FIFOC(psc)->rxcmd, 0x01); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		iounmap(psc); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-14 04:40:53 +00:00
										 |  |  | void __init mpc512x_init_early(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-05-14 04:40:54 +00:00
										 |  |  | 	mpc512x_restart_init(); | 
					
						
							| 
									
										
										
										
											2013-05-14 04:40:53 +00:00
										 |  |  | 	if (IS_ENABLED(CONFIG_FB_FSL_DIU)) | 
					
						
							|  |  |  | 		mpc512x_init_diu(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-02-16 10:35:13 -07:00
										 |  |  | void __init mpc512x_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	mpc5121_clk_init(); | 
					
						
							| 
									
										
										
										
											2013-01-25 17:29:21 +01:00
										 |  |  | 	mpc512x_declare_of_platform_devices(); | 
					
						
							| 
									
										
										
										
											2010-04-30 13:21:26 +00:00
										 |  |  | 	mpc512x_psc_fifo_init(); | 
					
						
							| 
									
										
										
										
											2010-02-16 10:35:13 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2013-02-04 11:16:02 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-14 04:40:53 +00:00
										 |  |  | void __init mpc512x_setup_arch(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (IS_ENABLED(CONFIG_FB_FSL_DIU)) | 
					
						
							|  |  |  | 		mpc512x_setup_diu(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-04 11:16:02 +01:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * mpc512x_cs_config - Setup chip select configuration | 
					
						
							|  |  |  |  * @cs: chip select number | 
					
						
							|  |  |  |  * @val: chip select configuration value | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Perform chip select configuration for devices on LocalPlus Bus. | 
					
						
							|  |  |  |  * Intended to dynamically reconfigure the chip select parameters | 
					
						
							|  |  |  |  * for configurable devices on the bus. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int mpc512x_cs_config(unsigned int cs, u32 val) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	static struct mpc512x_lpc __iomem *lpc; | 
					
						
							|  |  |  | 	struct device_node *np; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (cs > 7) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!lpc) { | 
					
						
							|  |  |  | 		np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-lpc"); | 
					
						
							|  |  |  | 		lpc = of_iomap(np, 0); | 
					
						
							|  |  |  | 		of_node_put(np); | 
					
						
							|  |  |  | 		if (!lpc) | 
					
						
							|  |  |  | 			return -ENOMEM; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	out_be32(&lpc->cs_cfg[cs], val); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(mpc512x_cs_config); |