18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: John Rigby <jrigby@freescale.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Description: 88c2ecf20Sopenharmony_ci * MPC512x Shared code 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/clk.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/irq.h> 158c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 168c2ecf20Sopenharmony_ci#include <linux/fsl-diu-fb.h> 178c2ecf20Sopenharmony_ci#include <linux/memblock.h> 188c2ecf20Sopenharmony_ci#include <sysdev/fsl_soc.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 218c2ecf20Sopenharmony_ci#include <asm/machdep.h> 228c2ecf20Sopenharmony_ci#include <asm/ipic.h> 238c2ecf20Sopenharmony_ci#include <asm/prom.h> 248c2ecf20Sopenharmony_ci#include <asm/time.h> 258c2ecf20Sopenharmony_ci#include <asm/mpc5121.h> 268c2ecf20Sopenharmony_ci#include <asm/mpc52xx_psc.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "mpc512x.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic struct mpc512x_reset_module __iomem *reset_module_base; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic void __init mpc512x_restart_init(void) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct device_node *np; 358c2ecf20Sopenharmony_ci const char *reset_compat; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci reset_compat = mpc512x_select_reset_compat(); 388c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, reset_compat); 398c2ecf20Sopenharmony_ci if (!np) 408c2ecf20Sopenharmony_ci return; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci reset_module_base = of_iomap(np, 0); 438c2ecf20Sopenharmony_ci of_node_put(np); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_civoid __noreturn mpc512x_restart(char *cmd) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci if (reset_module_base) { 498c2ecf20Sopenharmony_ci /* Enable software reset "RSTE" */ 508c2ecf20Sopenharmony_ci out_be32(&reset_module_base->rpr, 0x52535445); 518c2ecf20Sopenharmony_ci /* Set software hard reset */ 528c2ecf20Sopenharmony_ci out_be32(&reset_module_base->rcr, 0x2); 538c2ecf20Sopenharmony_ci } else { 548c2ecf20Sopenharmony_ci pr_err("Restart module not mapped.\n"); 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci for (;;) 578c2ecf20Sopenharmony_ci ; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct fsl_diu_shared_fb { 618c2ecf20Sopenharmony_ci u8 gamma[0x300]; /* 32-bit aligned! */ 628c2ecf20Sopenharmony_ci struct diu_ad ad0; /* 32-bit aligned! */ 638c2ecf20Sopenharmony_ci phys_addr_t fb_phys; 648c2ecf20Sopenharmony_ci size_t fb_len; 658c2ecf20Sopenharmony_ci bool in_use; 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* receives a pixel clock spec in pico seconds, adjusts the DIU clock rate */ 698c2ecf20Sopenharmony_cistatic void mpc512x_set_pixel_clock(unsigned int pixclock) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct device_node *np; 728c2ecf20Sopenharmony_ci struct clk *clk_diu; 738c2ecf20Sopenharmony_ci unsigned long epsilon, minpixclock, maxpixclock; 748c2ecf20Sopenharmony_ci unsigned long offset, want, got, delta; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* lookup and enable the DIU clock */ 778c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu"); 788c2ecf20Sopenharmony_ci if (!np) { 798c2ecf20Sopenharmony_ci pr_err("Could not find DIU device tree node.\n"); 808c2ecf20Sopenharmony_ci return; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci clk_diu = of_clk_get(np, 0); 838c2ecf20Sopenharmony_ci if (IS_ERR(clk_diu)) { 848c2ecf20Sopenharmony_ci /* backwards compat with device trees that lack clock specs */ 858c2ecf20Sopenharmony_ci clk_diu = clk_get_sys(np->name, "ipg"); 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci of_node_put(np); 888c2ecf20Sopenharmony_ci if (IS_ERR(clk_diu)) { 898c2ecf20Sopenharmony_ci pr_err("Could not lookup DIU clock.\n"); 908c2ecf20Sopenharmony_ci return; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci if (clk_prepare_enable(clk_diu)) { 938c2ecf20Sopenharmony_ci pr_err("Could not enable DIU clock.\n"); 948c2ecf20Sopenharmony_ci return; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* 988c2ecf20Sopenharmony_ci * convert the picoseconds spec into the desired clock rate, 998c2ecf20Sopenharmony_ci * determine the acceptable clock range for the monitor (+/- 5%), 1008c2ecf20Sopenharmony_ci * do the calculation in steps to avoid integer overflow 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci pr_debug("DIU pixclock in ps - %u\n", pixclock); 1038c2ecf20Sopenharmony_ci pixclock = (1000000000 / pixclock) * 1000; 1048c2ecf20Sopenharmony_ci pr_debug("DIU pixclock freq - %u\n", pixclock); 1058c2ecf20Sopenharmony_ci epsilon = pixclock / 20; /* pixclock * 0.05 */ 1068c2ecf20Sopenharmony_ci pr_debug("DIU deviation - %lu\n", epsilon); 1078c2ecf20Sopenharmony_ci minpixclock = pixclock - epsilon; 1088c2ecf20Sopenharmony_ci maxpixclock = pixclock + epsilon; 1098c2ecf20Sopenharmony_ci pr_debug("DIU minpixclock - %lu\n", minpixclock); 1108c2ecf20Sopenharmony_ci pr_debug("DIU maxpixclock - %lu\n", maxpixclock); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* 1138c2ecf20Sopenharmony_ci * check whether the DIU supports the desired pixel clock 1148c2ecf20Sopenharmony_ci * 1158c2ecf20Sopenharmony_ci * - simply request the desired clock and see what the 1168c2ecf20Sopenharmony_ci * platform's clock driver will make of it, assuming that it 1178c2ecf20Sopenharmony_ci * will setup the best approximation of the requested value 1188c2ecf20Sopenharmony_ci * - try other candidate frequencies in the order of decreasing 1198c2ecf20Sopenharmony_ci * preference (i.e. with increasing distance from the desired 1208c2ecf20Sopenharmony_ci * pixel clock, and checking the lower frequency before the 1218c2ecf20Sopenharmony_ci * higher frequency to not overload the hardware) until the 1228c2ecf20Sopenharmony_ci * first match is found -- any potential subsequent match 1238c2ecf20Sopenharmony_ci * would only be as good as the former match or typically 1248c2ecf20Sopenharmony_ci * would be less preferrable 1258c2ecf20Sopenharmony_ci * 1268c2ecf20Sopenharmony_ci * the offset increment of pixelclock divided by 64 is an 1278c2ecf20Sopenharmony_ci * arbitrary choice -- it's simple to calculate, in the typical 1288c2ecf20Sopenharmony_ci * case we expect the first check to succeed already, in the 1298c2ecf20Sopenharmony_ci * worst case seven frequencies get tested (the exact center and 1308c2ecf20Sopenharmony_ci * three more values each to the left and to the right) before 1318c2ecf20Sopenharmony_ci * the 5% tolerance window is exceeded, resulting in fast enough 1328c2ecf20Sopenharmony_ci * execution yet high enough probability of finding a suitable 1338c2ecf20Sopenharmony_ci * value, while the error rate will be in the order of single 1348c2ecf20Sopenharmony_ci * percents 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ci for (offset = 0; offset <= epsilon; offset += pixclock / 64) { 1378c2ecf20Sopenharmony_ci want = pixclock - offset; 1388c2ecf20Sopenharmony_ci pr_debug("DIU checking clock - %lu\n", want); 1398c2ecf20Sopenharmony_ci clk_set_rate(clk_diu, want); 1408c2ecf20Sopenharmony_ci got = clk_get_rate(clk_diu); 1418c2ecf20Sopenharmony_ci delta = abs(pixclock - got); 1428c2ecf20Sopenharmony_ci if (delta < epsilon) 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci if (!offset) 1458c2ecf20Sopenharmony_ci continue; 1468c2ecf20Sopenharmony_ci want = pixclock + offset; 1478c2ecf20Sopenharmony_ci pr_debug("DIU checking clock - %lu\n", want); 1488c2ecf20Sopenharmony_ci clk_set_rate(clk_diu, want); 1498c2ecf20Sopenharmony_ci got = clk_get_rate(clk_diu); 1508c2ecf20Sopenharmony_ci delta = abs(pixclock - got); 1518c2ecf20Sopenharmony_ci if (delta < epsilon) 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci if (offset <= epsilon) { 1558c2ecf20Sopenharmony_ci pr_debug("DIU clock accepted - %lu\n", want); 1568c2ecf20Sopenharmony_ci pr_debug("DIU pixclock want %u, got %lu, delta %lu, eps %lu\n", 1578c2ecf20Sopenharmony_ci pixclock, got, delta, epsilon); 1588c2ecf20Sopenharmony_ci return; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci pr_warn("DIU pixclock auto search unsuccessful\n"); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* 1638c2ecf20Sopenharmony_ci * what is the most appropriate action to take when the search 1648c2ecf20Sopenharmony_ci * for an available pixel clock which is acceptable to the 1658c2ecf20Sopenharmony_ci * monitor has failed? disable the DIU (clock) or just provide 1668c2ecf20Sopenharmony_ci * a "best effort"? we go with the latter 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_ci pr_warn("DIU pixclock best effort fallback (backend's choice)\n"); 1698c2ecf20Sopenharmony_ci clk_set_rate(clk_diu, pixclock); 1708c2ecf20Sopenharmony_ci got = clk_get_rate(clk_diu); 1718c2ecf20Sopenharmony_ci delta = abs(pixclock - got); 1728c2ecf20Sopenharmony_ci pr_debug("DIU pixclock want %u, got %lu, delta %lu, eps %lu\n", 1738c2ecf20Sopenharmony_ci pixclock, got, delta, epsilon); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic enum fsl_diu_monitor_port 1778c2ecf20Sopenharmony_cimpc512x_valid_monitor_port(enum fsl_diu_monitor_port port) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci return FSL_DIU_PORT_DVI; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic struct fsl_diu_shared_fb __attribute__ ((__aligned__(8))) diu_shared_fb; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic inline void mpc512x_free_bootmem(struct page *page) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci BUG_ON(PageTail(page)); 1878c2ecf20Sopenharmony_ci BUG_ON(page_ref_count(page) > 1); 1888c2ecf20Sopenharmony_ci free_reserved_page(page); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void mpc512x_release_bootmem(void) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci unsigned long addr = diu_shared_fb.fb_phys & PAGE_MASK; 1948c2ecf20Sopenharmony_ci unsigned long size = diu_shared_fb.fb_len; 1958c2ecf20Sopenharmony_ci unsigned long start, end; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (diu_shared_fb.in_use) { 1988c2ecf20Sopenharmony_ci start = PFN_UP(addr); 1998c2ecf20Sopenharmony_ci end = PFN_DOWN(addr + size); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci for (; start < end; start++) 2028c2ecf20Sopenharmony_ci mpc512x_free_bootmem(pfn_to_page(start)); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci diu_shared_fb.in_use = false; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci diu_ops.release_bootmem = NULL; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/* 2108c2ecf20Sopenharmony_ci * Check if DIU was pre-initialized. If so, perform steps 2118c2ecf20Sopenharmony_ci * needed to continue displaying through the whole boot process. 2128c2ecf20Sopenharmony_ci * Move area descriptor and gamma table elsewhere, they are 2138c2ecf20Sopenharmony_ci * destroyed by bootmem allocator otherwise. The frame buffer 2148c2ecf20Sopenharmony_ci * address range will be reserved in setup_arch() after bootmem 2158c2ecf20Sopenharmony_ci * allocator is up. 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_cistatic void __init mpc512x_init_diu(void) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct device_node *np; 2208c2ecf20Sopenharmony_ci struct diu __iomem *diu_reg; 2218c2ecf20Sopenharmony_ci phys_addr_t desc; 2228c2ecf20Sopenharmony_ci void __iomem *vaddr; 2238c2ecf20Sopenharmony_ci unsigned long mode, pix_fmt, res, bpp; 2248c2ecf20Sopenharmony_ci unsigned long dst; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu"); 2278c2ecf20Sopenharmony_ci if (!np) { 2288c2ecf20Sopenharmony_ci pr_err("No DIU node\n"); 2298c2ecf20Sopenharmony_ci return; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci diu_reg = of_iomap(np, 0); 2338c2ecf20Sopenharmony_ci of_node_put(np); 2348c2ecf20Sopenharmony_ci if (!diu_reg) { 2358c2ecf20Sopenharmony_ci pr_err("Can't map DIU\n"); 2368c2ecf20Sopenharmony_ci return; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci mode = in_be32(&diu_reg->diu_mode); 2408c2ecf20Sopenharmony_ci if (mode == MFB_MODE0) { 2418c2ecf20Sopenharmony_ci pr_info("%s: DIU OFF\n", __func__); 2428c2ecf20Sopenharmony_ci goto out; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci desc = in_be32(&diu_reg->desc[0]); 2468c2ecf20Sopenharmony_ci vaddr = ioremap(desc, sizeof(struct diu_ad)); 2478c2ecf20Sopenharmony_ci if (!vaddr) { 2488c2ecf20Sopenharmony_ci pr_err("Can't map DIU area desc.\n"); 2498c2ecf20Sopenharmony_ci goto out; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci memcpy(&diu_shared_fb.ad0, vaddr, sizeof(struct diu_ad)); 2528c2ecf20Sopenharmony_ci /* flush fb area descriptor */ 2538c2ecf20Sopenharmony_ci dst = (unsigned long)&diu_shared_fb.ad0; 2548c2ecf20Sopenharmony_ci flush_dcache_range(dst, dst + sizeof(struct diu_ad) - 1); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci res = in_be32(&diu_reg->disp_size); 2578c2ecf20Sopenharmony_ci pix_fmt = in_le32(vaddr); 2588c2ecf20Sopenharmony_ci bpp = ((pix_fmt >> 16) & 0x3) + 1; 2598c2ecf20Sopenharmony_ci diu_shared_fb.fb_phys = in_le32(vaddr + 4); 2608c2ecf20Sopenharmony_ci diu_shared_fb.fb_len = ((res & 0xfff0000) >> 16) * (res & 0xfff) * bpp; 2618c2ecf20Sopenharmony_ci diu_shared_fb.in_use = true; 2628c2ecf20Sopenharmony_ci iounmap(vaddr); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci desc = in_be32(&diu_reg->gamma); 2658c2ecf20Sopenharmony_ci vaddr = ioremap(desc, sizeof(diu_shared_fb.gamma)); 2668c2ecf20Sopenharmony_ci if (!vaddr) { 2678c2ecf20Sopenharmony_ci pr_err("Can't map DIU area desc.\n"); 2688c2ecf20Sopenharmony_ci diu_shared_fb.in_use = false; 2698c2ecf20Sopenharmony_ci goto out; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci memcpy(&diu_shared_fb.gamma, vaddr, sizeof(diu_shared_fb.gamma)); 2728c2ecf20Sopenharmony_ci /* flush gamma table */ 2738c2ecf20Sopenharmony_ci dst = (unsigned long)&diu_shared_fb.gamma; 2748c2ecf20Sopenharmony_ci flush_dcache_range(dst, dst + sizeof(diu_shared_fb.gamma) - 1); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci iounmap(vaddr); 2778c2ecf20Sopenharmony_ci out_be32(&diu_reg->gamma, virt_to_phys(&diu_shared_fb.gamma)); 2788c2ecf20Sopenharmony_ci out_be32(&diu_reg->desc[1], 0); 2798c2ecf20Sopenharmony_ci out_be32(&diu_reg->desc[2], 0); 2808c2ecf20Sopenharmony_ci out_be32(&diu_reg->desc[0], virt_to_phys(&diu_shared_fb.ad0)); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ciout: 2838c2ecf20Sopenharmony_ci iounmap(diu_reg); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic void __init mpc512x_setup_diu(void) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci int ret; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* 2918c2ecf20Sopenharmony_ci * We do not allocate and configure new area for bitmap buffer 2928c2ecf20Sopenharmony_ci * because it would requere copying bitmap data (splash image) 2938c2ecf20Sopenharmony_ci * and so negatively affect boot time. Instead we reserve the 2948c2ecf20Sopenharmony_ci * already configured frame buffer area so that it won't be 2958c2ecf20Sopenharmony_ci * destroyed. The starting address of the area to reserve and 2968c2ecf20Sopenharmony_ci * also it's length is passed to memblock_reserve(). It will be 2978c2ecf20Sopenharmony_ci * freed later on first open of fbdev, when splash image is not 2988c2ecf20Sopenharmony_ci * needed any more. 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_ci if (diu_shared_fb.in_use) { 3018c2ecf20Sopenharmony_ci ret = memblock_reserve(diu_shared_fb.fb_phys, 3028c2ecf20Sopenharmony_ci diu_shared_fb.fb_len); 3038c2ecf20Sopenharmony_ci if (ret) { 3048c2ecf20Sopenharmony_ci pr_err("%s: reserve bootmem failed\n", __func__); 3058c2ecf20Sopenharmony_ci diu_shared_fb.in_use = false; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci diu_ops.set_pixel_clock = mpc512x_set_pixel_clock; 3108c2ecf20Sopenharmony_ci diu_ops.valid_monitor_port = mpc512x_valid_monitor_port; 3118c2ecf20Sopenharmony_ci diu_ops.release_bootmem = mpc512x_release_bootmem; 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_civoid __init mpc512x_init_IRQ(void) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct device_node *np; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-ipic"); 3198c2ecf20Sopenharmony_ci if (!np) 3208c2ecf20Sopenharmony_ci return; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci ipic_init(np, 0); 3238c2ecf20Sopenharmony_ci of_node_put(np); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* 3268c2ecf20Sopenharmony_ci * Initialize the default interrupt mapping priorities, 3278c2ecf20Sopenharmony_ci * in case the boot rom changed something on us. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci ipic_set_default_priority(); 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/* 3338c2ecf20Sopenharmony_ci * Nodes to do bus probe on, soc and localbus 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_cistatic const struct of_device_id of_bus_ids[] __initconst = { 3368c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc5121-immr", }, 3378c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc5121-localbus", }, 3388c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc5121-mbx", }, 3398c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc5121-nfc", }, 3408c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc5121-sram", }, 3418c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc5121-pci", }, 3428c2ecf20Sopenharmony_ci { .compatible = "gpio-leds", }, 3438c2ecf20Sopenharmony_ci {}, 3448c2ecf20Sopenharmony_ci}; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic void __init mpc512x_declare_of_platform_devices(void) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci if (of_platform_bus_probe(NULL, of_bus_ids, NULL)) 3498c2ecf20Sopenharmony_ci printk(KERN_ERR __FILE__ ": " 3508c2ecf20Sopenharmony_ci "Error while probing of_platform bus\n"); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci#define DEFAULT_FIFO_SIZE 16 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ciconst char *mpc512x_select_psc_compat(void) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci if (of_machine_is_compatible("fsl,mpc5121")) 3588c2ecf20Sopenharmony_ci return "fsl,mpc5121-psc"; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (of_machine_is_compatible("fsl,mpc5125")) 3618c2ecf20Sopenharmony_ci return "fsl,mpc5125-psc"; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return NULL; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ciconst char *mpc512x_select_reset_compat(void) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci if (of_machine_is_compatible("fsl,mpc5121")) 3698c2ecf20Sopenharmony_ci return "fsl,mpc5121-reset"; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (of_machine_is_compatible("fsl,mpc5125")) 3728c2ecf20Sopenharmony_ci return "fsl,mpc5125-reset"; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return NULL; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic unsigned int __init get_fifo_size(struct device_node *np, 3788c2ecf20Sopenharmony_ci char *prop_name) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci const unsigned int *fp; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci fp = of_get_property(np, prop_name, NULL); 3838c2ecf20Sopenharmony_ci if (fp) 3848c2ecf20Sopenharmony_ci return *fp; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci pr_warn("no %s property in %pOF node, defaulting to %d\n", 3878c2ecf20Sopenharmony_ci prop_name, np, DEFAULT_FIFO_SIZE); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci return DEFAULT_FIFO_SIZE; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci#define FIFOC(_base) ((struct mpc512x_psc_fifo __iomem *) \ 3938c2ecf20Sopenharmony_ci ((u32)(_base) + sizeof(struct mpc52xx_psc))) 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci/* Init PSC FIFO space for TX and RX slices */ 3968c2ecf20Sopenharmony_cistatic void __init mpc512x_psc_fifo_init(void) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci struct device_node *np; 3998c2ecf20Sopenharmony_ci void __iomem *psc; 4008c2ecf20Sopenharmony_ci unsigned int tx_fifo_size; 4018c2ecf20Sopenharmony_ci unsigned int rx_fifo_size; 4028c2ecf20Sopenharmony_ci const char *psc_compat; 4038c2ecf20Sopenharmony_ci int fifobase = 0; /* current fifo address in 32 bit words */ 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci psc_compat = mpc512x_select_psc_compat(); 4068c2ecf20Sopenharmony_ci if (!psc_compat) { 4078c2ecf20Sopenharmony_ci pr_err("%s: no compatible devices found\n", __func__); 4088c2ecf20Sopenharmony_ci return; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci for_each_compatible_node(np, NULL, psc_compat) { 4128c2ecf20Sopenharmony_ci tx_fifo_size = get_fifo_size(np, "fsl,tx-fifo-size"); 4138c2ecf20Sopenharmony_ci rx_fifo_size = get_fifo_size(np, "fsl,rx-fifo-size"); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* size in register is in 4 byte units */ 4168c2ecf20Sopenharmony_ci tx_fifo_size /= 4; 4178c2ecf20Sopenharmony_ci rx_fifo_size /= 4; 4188c2ecf20Sopenharmony_ci if (!tx_fifo_size) 4198c2ecf20Sopenharmony_ci tx_fifo_size = 1; 4208c2ecf20Sopenharmony_ci if (!rx_fifo_size) 4218c2ecf20Sopenharmony_ci rx_fifo_size = 1; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci psc = of_iomap(np, 0); 4248c2ecf20Sopenharmony_ci if (!psc) { 4258c2ecf20Sopenharmony_ci pr_err("%s: Can't map %pOF device\n", 4268c2ecf20Sopenharmony_ci __func__, np); 4278c2ecf20Sopenharmony_ci continue; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* FIFO space is 4KiB, check if requested size is available */ 4318c2ecf20Sopenharmony_ci if ((fifobase + tx_fifo_size + rx_fifo_size) > 0x1000) { 4328c2ecf20Sopenharmony_ci pr_err("%s: no fifo space available for %pOF\n", 4338c2ecf20Sopenharmony_ci __func__, np); 4348c2ecf20Sopenharmony_ci iounmap(psc); 4358c2ecf20Sopenharmony_ci /* 4368c2ecf20Sopenharmony_ci * chances are that another device requests less 4378c2ecf20Sopenharmony_ci * fifo space, so we continue. 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_ci continue; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* set tx and rx fifo size registers */ 4438c2ecf20Sopenharmony_ci out_be32(&FIFOC(psc)->txsz, (fifobase << 16) | tx_fifo_size); 4448c2ecf20Sopenharmony_ci fifobase += tx_fifo_size; 4458c2ecf20Sopenharmony_ci out_be32(&FIFOC(psc)->rxsz, (fifobase << 16) | rx_fifo_size); 4468c2ecf20Sopenharmony_ci fifobase += rx_fifo_size; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* reset and enable the slices */ 4498c2ecf20Sopenharmony_ci out_be32(&FIFOC(psc)->txcmd, 0x80); 4508c2ecf20Sopenharmony_ci out_be32(&FIFOC(psc)->txcmd, 0x01); 4518c2ecf20Sopenharmony_ci out_be32(&FIFOC(psc)->rxcmd, 0x80); 4528c2ecf20Sopenharmony_ci out_be32(&FIFOC(psc)->rxcmd, 0x01); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci iounmap(psc); 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_civoid __init mpc512x_init_early(void) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci mpc512x_restart_init(); 4618c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_FB_FSL_DIU)) 4628c2ecf20Sopenharmony_ci mpc512x_init_diu(); 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_civoid __init mpc512x_init(void) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci mpc5121_clk_init(); 4688c2ecf20Sopenharmony_ci mpc512x_declare_of_platform_devices(); 4698c2ecf20Sopenharmony_ci mpc512x_psc_fifo_init(); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_civoid __init mpc512x_setup_arch(void) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_FB_FSL_DIU)) 4758c2ecf20Sopenharmony_ci mpc512x_setup_diu(); 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci/** 4798c2ecf20Sopenharmony_ci * mpc512x_cs_config - Setup chip select configuration 4808c2ecf20Sopenharmony_ci * @cs: chip select number 4818c2ecf20Sopenharmony_ci * @val: chip select configuration value 4828c2ecf20Sopenharmony_ci * 4838c2ecf20Sopenharmony_ci * Perform chip select configuration for devices on LocalPlus Bus. 4848c2ecf20Sopenharmony_ci * Intended to dynamically reconfigure the chip select parameters 4858c2ecf20Sopenharmony_ci * for configurable devices on the bus. 4868c2ecf20Sopenharmony_ci */ 4878c2ecf20Sopenharmony_ciint mpc512x_cs_config(unsigned int cs, u32 val) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci static struct mpc512x_lpc __iomem *lpc; 4908c2ecf20Sopenharmony_ci struct device_node *np; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (cs > 7) 4938c2ecf20Sopenharmony_ci return -EINVAL; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (!lpc) { 4968c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-lpc"); 4978c2ecf20Sopenharmony_ci lpc = of_iomap(np, 0); 4988c2ecf20Sopenharmony_ci of_node_put(np); 4998c2ecf20Sopenharmony_ci if (!lpc) 5008c2ecf20Sopenharmony_ci return -ENOMEM; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci out_be32(&lpc->cs_cfg[cs], val); 5048c2ecf20Sopenharmony_ci return 0; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mpc512x_cs_config); 507