162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. All rights reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: John Rigby <jrigby@freescale.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Description: 862306a36Sopenharmony_ci * MPC512x Shared code 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/clk.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/irq.h> 1562306a36Sopenharmony_ci#include <linux/of_address.h> 1662306a36Sopenharmony_ci#include <linux/of_platform.h> 1762306a36Sopenharmony_ci#include <linux/fsl-diu-fb.h> 1862306a36Sopenharmony_ci#include <linux/memblock.h> 1962306a36Sopenharmony_ci#include <sysdev/fsl_soc.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <asm/cacheflush.h> 2262306a36Sopenharmony_ci#include <asm/machdep.h> 2362306a36Sopenharmony_ci#include <asm/ipic.h> 2462306a36Sopenharmony_ci#include <asm/time.h> 2562306a36Sopenharmony_ci#include <asm/mpc5121.h> 2662306a36Sopenharmony_ci#include <asm/mpc52xx_psc.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "mpc512x.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic struct mpc512x_reset_module __iomem *reset_module_base; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_civoid __noreturn mpc512x_restart(char *cmd) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci if (reset_module_base) { 3562306a36Sopenharmony_ci /* Enable software reset "RSTE" */ 3662306a36Sopenharmony_ci out_be32(&reset_module_base->rpr, 0x52535445); 3762306a36Sopenharmony_ci /* Set software hard reset */ 3862306a36Sopenharmony_ci out_be32(&reset_module_base->rcr, 0x2); 3962306a36Sopenharmony_ci } else { 4062306a36Sopenharmony_ci pr_err("Restart module not mapped.\n"); 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci for (;;) 4362306a36Sopenharmony_ci ; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistruct fsl_diu_shared_fb { 4762306a36Sopenharmony_ci u8 gamma[0x300]; /* 32-bit aligned! */ 4862306a36Sopenharmony_ci struct diu_ad ad0; /* 32-bit aligned! */ 4962306a36Sopenharmony_ci phys_addr_t fb_phys; 5062306a36Sopenharmony_ci size_t fb_len; 5162306a36Sopenharmony_ci bool in_use; 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* receives a pixel clock spec in pico seconds, adjusts the DIU clock rate */ 5562306a36Sopenharmony_cistatic void mpc512x_set_pixel_clock(unsigned int pixclock) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct device_node *np; 5862306a36Sopenharmony_ci struct clk *clk_diu; 5962306a36Sopenharmony_ci unsigned long epsilon, minpixclock, maxpixclock; 6062306a36Sopenharmony_ci unsigned long offset, want, got, delta; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* lookup and enable the DIU clock */ 6362306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu"); 6462306a36Sopenharmony_ci if (!np) { 6562306a36Sopenharmony_ci pr_err("Could not find DIU device tree node.\n"); 6662306a36Sopenharmony_ci return; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci clk_diu = of_clk_get(np, 0); 6962306a36Sopenharmony_ci if (IS_ERR(clk_diu)) { 7062306a36Sopenharmony_ci /* backwards compat with device trees that lack clock specs */ 7162306a36Sopenharmony_ci clk_diu = clk_get_sys(np->name, "ipg"); 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci of_node_put(np); 7462306a36Sopenharmony_ci if (IS_ERR(clk_diu)) { 7562306a36Sopenharmony_ci pr_err("Could not lookup DIU clock.\n"); 7662306a36Sopenharmony_ci return; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci if (clk_prepare_enable(clk_diu)) { 7962306a36Sopenharmony_ci pr_err("Could not enable DIU clock.\n"); 8062306a36Sopenharmony_ci return; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* 8462306a36Sopenharmony_ci * convert the picoseconds spec into the desired clock rate, 8562306a36Sopenharmony_ci * determine the acceptable clock range for the monitor (+/- 5%), 8662306a36Sopenharmony_ci * do the calculation in steps to avoid integer overflow 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci pr_debug("DIU pixclock in ps - %u\n", pixclock); 8962306a36Sopenharmony_ci pixclock = (1000000000 / pixclock) * 1000; 9062306a36Sopenharmony_ci pr_debug("DIU pixclock freq - %u\n", pixclock); 9162306a36Sopenharmony_ci epsilon = pixclock / 20; /* pixclock * 0.05 */ 9262306a36Sopenharmony_ci pr_debug("DIU deviation - %lu\n", epsilon); 9362306a36Sopenharmony_ci minpixclock = pixclock - epsilon; 9462306a36Sopenharmony_ci maxpixclock = pixclock + epsilon; 9562306a36Sopenharmony_ci pr_debug("DIU minpixclock - %lu\n", minpixclock); 9662306a36Sopenharmony_ci pr_debug("DIU maxpixclock - %lu\n", maxpixclock); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* 9962306a36Sopenharmony_ci * check whether the DIU supports the desired pixel clock 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * - simply request the desired clock and see what the 10262306a36Sopenharmony_ci * platform's clock driver will make of it, assuming that it 10362306a36Sopenharmony_ci * will setup the best approximation of the requested value 10462306a36Sopenharmony_ci * - try other candidate frequencies in the order of decreasing 10562306a36Sopenharmony_ci * preference (i.e. with increasing distance from the desired 10662306a36Sopenharmony_ci * pixel clock, and checking the lower frequency before the 10762306a36Sopenharmony_ci * higher frequency to not overload the hardware) until the 10862306a36Sopenharmony_ci * first match is found -- any potential subsequent match 10962306a36Sopenharmony_ci * would only be as good as the former match or typically 11062306a36Sopenharmony_ci * would be less preferrable 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * the offset increment of pixelclock divided by 64 is an 11362306a36Sopenharmony_ci * arbitrary choice -- it's simple to calculate, in the typical 11462306a36Sopenharmony_ci * case we expect the first check to succeed already, in the 11562306a36Sopenharmony_ci * worst case seven frequencies get tested (the exact center and 11662306a36Sopenharmony_ci * three more values each to the left and to the right) before 11762306a36Sopenharmony_ci * the 5% tolerance window is exceeded, resulting in fast enough 11862306a36Sopenharmony_ci * execution yet high enough probability of finding a suitable 11962306a36Sopenharmony_ci * value, while the error rate will be in the order of single 12062306a36Sopenharmony_ci * percents 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_ci for (offset = 0; offset <= epsilon; offset += pixclock / 64) { 12362306a36Sopenharmony_ci want = pixclock - offset; 12462306a36Sopenharmony_ci pr_debug("DIU checking clock - %lu\n", want); 12562306a36Sopenharmony_ci clk_set_rate(clk_diu, want); 12662306a36Sopenharmony_ci got = clk_get_rate(clk_diu); 12762306a36Sopenharmony_ci delta = abs(pixclock - got); 12862306a36Sopenharmony_ci if (delta < epsilon) 12962306a36Sopenharmony_ci break; 13062306a36Sopenharmony_ci if (!offset) 13162306a36Sopenharmony_ci continue; 13262306a36Sopenharmony_ci want = pixclock + offset; 13362306a36Sopenharmony_ci pr_debug("DIU checking clock - %lu\n", want); 13462306a36Sopenharmony_ci clk_set_rate(clk_diu, want); 13562306a36Sopenharmony_ci got = clk_get_rate(clk_diu); 13662306a36Sopenharmony_ci delta = abs(pixclock - got); 13762306a36Sopenharmony_ci if (delta < epsilon) 13862306a36Sopenharmony_ci break; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci if (offset <= epsilon) { 14162306a36Sopenharmony_ci pr_debug("DIU clock accepted - %lu\n", want); 14262306a36Sopenharmony_ci pr_debug("DIU pixclock want %u, got %lu, delta %lu, eps %lu\n", 14362306a36Sopenharmony_ci pixclock, got, delta, epsilon); 14462306a36Sopenharmony_ci return; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci pr_warn("DIU pixclock auto search unsuccessful\n"); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* 14962306a36Sopenharmony_ci * what is the most appropriate action to take when the search 15062306a36Sopenharmony_ci * for an available pixel clock which is acceptable to the 15162306a36Sopenharmony_ci * monitor has failed? disable the DIU (clock) or just provide 15262306a36Sopenharmony_ci * a "best effort"? we go with the latter 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ci pr_warn("DIU pixclock best effort fallback (backend's choice)\n"); 15562306a36Sopenharmony_ci clk_set_rate(clk_diu, pixclock); 15662306a36Sopenharmony_ci got = clk_get_rate(clk_diu); 15762306a36Sopenharmony_ci delta = abs(pixclock - got); 15862306a36Sopenharmony_ci pr_debug("DIU pixclock want %u, got %lu, delta %lu, eps %lu\n", 15962306a36Sopenharmony_ci pixclock, got, delta, epsilon); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic enum fsl_diu_monitor_port 16362306a36Sopenharmony_cimpc512x_valid_monitor_port(enum fsl_diu_monitor_port port) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci return FSL_DIU_PORT_DVI; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic struct fsl_diu_shared_fb __attribute__ ((__aligned__(8))) diu_shared_fb; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic inline void mpc512x_free_bootmem(struct page *page) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci BUG_ON(PageTail(page)); 17362306a36Sopenharmony_ci BUG_ON(page_ref_count(page) > 1); 17462306a36Sopenharmony_ci free_reserved_page(page); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void mpc512x_release_bootmem(void) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci unsigned long addr = diu_shared_fb.fb_phys & PAGE_MASK; 18062306a36Sopenharmony_ci unsigned long size = diu_shared_fb.fb_len; 18162306a36Sopenharmony_ci unsigned long start, end; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (diu_shared_fb.in_use) { 18462306a36Sopenharmony_ci start = PFN_UP(addr); 18562306a36Sopenharmony_ci end = PFN_DOWN(addr + size); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci for (; start < end; start++) 18862306a36Sopenharmony_ci mpc512x_free_bootmem(pfn_to_page(start)); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci diu_shared_fb.in_use = false; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci diu_ops.release_bootmem = NULL; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/* 19662306a36Sopenharmony_ci * Check if DIU was pre-initialized. If so, perform steps 19762306a36Sopenharmony_ci * needed to continue displaying through the whole boot process. 19862306a36Sopenharmony_ci * Move area descriptor and gamma table elsewhere, they are 19962306a36Sopenharmony_ci * destroyed by bootmem allocator otherwise. The frame buffer 20062306a36Sopenharmony_ci * address range will be reserved in setup_arch() after bootmem 20162306a36Sopenharmony_ci * allocator is up. 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_cistatic void __init mpc512x_init_diu(void) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct device_node *np; 20662306a36Sopenharmony_ci struct diu __iomem *diu_reg; 20762306a36Sopenharmony_ci phys_addr_t desc; 20862306a36Sopenharmony_ci void __iomem *vaddr; 20962306a36Sopenharmony_ci unsigned long mode, pix_fmt, res, bpp; 21062306a36Sopenharmony_ci unsigned long dst; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu"); 21362306a36Sopenharmony_ci if (!np) { 21462306a36Sopenharmony_ci pr_err("No DIU node\n"); 21562306a36Sopenharmony_ci return; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci diu_reg = of_iomap(np, 0); 21962306a36Sopenharmony_ci of_node_put(np); 22062306a36Sopenharmony_ci if (!diu_reg) { 22162306a36Sopenharmony_ci pr_err("Can't map DIU\n"); 22262306a36Sopenharmony_ci return; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci mode = in_be32(&diu_reg->diu_mode); 22662306a36Sopenharmony_ci if (mode == MFB_MODE0) { 22762306a36Sopenharmony_ci pr_info("%s: DIU OFF\n", __func__); 22862306a36Sopenharmony_ci goto out; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci desc = in_be32(&diu_reg->desc[0]); 23262306a36Sopenharmony_ci vaddr = ioremap(desc, sizeof(struct diu_ad)); 23362306a36Sopenharmony_ci if (!vaddr) { 23462306a36Sopenharmony_ci pr_err("Can't map DIU area desc.\n"); 23562306a36Sopenharmony_ci goto out; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci memcpy(&diu_shared_fb.ad0, vaddr, sizeof(struct diu_ad)); 23862306a36Sopenharmony_ci /* flush fb area descriptor */ 23962306a36Sopenharmony_ci dst = (unsigned long)&diu_shared_fb.ad0; 24062306a36Sopenharmony_ci flush_dcache_range(dst, dst + sizeof(struct diu_ad) - 1); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci res = in_be32(&diu_reg->disp_size); 24362306a36Sopenharmony_ci pix_fmt = in_le32(vaddr); 24462306a36Sopenharmony_ci bpp = ((pix_fmt >> 16) & 0x3) + 1; 24562306a36Sopenharmony_ci diu_shared_fb.fb_phys = in_le32(vaddr + 4); 24662306a36Sopenharmony_ci diu_shared_fb.fb_len = ((res & 0xfff0000) >> 16) * (res & 0xfff) * bpp; 24762306a36Sopenharmony_ci diu_shared_fb.in_use = true; 24862306a36Sopenharmony_ci iounmap(vaddr); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci desc = in_be32(&diu_reg->gamma); 25162306a36Sopenharmony_ci vaddr = ioremap(desc, sizeof(diu_shared_fb.gamma)); 25262306a36Sopenharmony_ci if (!vaddr) { 25362306a36Sopenharmony_ci pr_err("Can't map DIU area desc.\n"); 25462306a36Sopenharmony_ci diu_shared_fb.in_use = false; 25562306a36Sopenharmony_ci goto out; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci memcpy(&diu_shared_fb.gamma, vaddr, sizeof(diu_shared_fb.gamma)); 25862306a36Sopenharmony_ci /* flush gamma table */ 25962306a36Sopenharmony_ci dst = (unsigned long)&diu_shared_fb.gamma; 26062306a36Sopenharmony_ci flush_dcache_range(dst, dst + sizeof(diu_shared_fb.gamma) - 1); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci iounmap(vaddr); 26362306a36Sopenharmony_ci out_be32(&diu_reg->gamma, virt_to_phys(&diu_shared_fb.gamma)); 26462306a36Sopenharmony_ci out_be32(&diu_reg->desc[1], 0); 26562306a36Sopenharmony_ci out_be32(&diu_reg->desc[2], 0); 26662306a36Sopenharmony_ci out_be32(&diu_reg->desc[0], virt_to_phys(&diu_shared_fb.ad0)); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ciout: 26962306a36Sopenharmony_ci iounmap(diu_reg); 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void __init mpc512x_setup_diu(void) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci int ret; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* 27762306a36Sopenharmony_ci * We do not allocate and configure new area for bitmap buffer 27862306a36Sopenharmony_ci * because it would require copying bitmap data (splash image) 27962306a36Sopenharmony_ci * and so negatively affect boot time. Instead we reserve the 28062306a36Sopenharmony_ci * already configured frame buffer area so that it won't be 28162306a36Sopenharmony_ci * destroyed. The starting address of the area to reserve and 28262306a36Sopenharmony_ci * also it's length is passed to memblock_reserve(). It will be 28362306a36Sopenharmony_ci * freed later on first open of fbdev, when splash image is not 28462306a36Sopenharmony_ci * needed any more. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci if (diu_shared_fb.in_use) { 28762306a36Sopenharmony_ci ret = memblock_reserve(diu_shared_fb.fb_phys, 28862306a36Sopenharmony_ci diu_shared_fb.fb_len); 28962306a36Sopenharmony_ci if (ret) { 29062306a36Sopenharmony_ci pr_err("%s: reserve bootmem failed\n", __func__); 29162306a36Sopenharmony_ci diu_shared_fb.in_use = false; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci diu_ops.set_pixel_clock = mpc512x_set_pixel_clock; 29662306a36Sopenharmony_ci diu_ops.valid_monitor_port = mpc512x_valid_monitor_port; 29762306a36Sopenharmony_ci diu_ops.release_bootmem = mpc512x_release_bootmem; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_civoid __init mpc512x_init_IRQ(void) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct device_node *np; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-ipic"); 30562306a36Sopenharmony_ci if (!np) 30662306a36Sopenharmony_ci return; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci ipic_init(np, 0); 30962306a36Sopenharmony_ci of_node_put(np); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* 31262306a36Sopenharmony_ci * Initialize the default interrupt mapping priorities, 31362306a36Sopenharmony_ci * in case the boot rom changed something on us. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci ipic_set_default_priority(); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/* 31962306a36Sopenharmony_ci * Nodes to do bus probe on, soc and localbus 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_cistatic const struct of_device_id of_bus_ids[] __initconst = { 32262306a36Sopenharmony_ci { .compatible = "fsl,mpc5121-immr", }, 32362306a36Sopenharmony_ci { .compatible = "fsl,mpc5121-localbus", }, 32462306a36Sopenharmony_ci { .compatible = "fsl,mpc5121-mbx", }, 32562306a36Sopenharmony_ci { .compatible = "fsl,mpc5121-nfc", }, 32662306a36Sopenharmony_ci { .compatible = "fsl,mpc5121-sram", }, 32762306a36Sopenharmony_ci { .compatible = "fsl,mpc5121-pci", }, 32862306a36Sopenharmony_ci { .compatible = "gpio-leds", }, 32962306a36Sopenharmony_ci {}, 33062306a36Sopenharmony_ci}; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic void __init mpc512x_declare_of_platform_devices(void) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci if (of_platform_bus_probe(NULL, of_bus_ids, NULL)) 33562306a36Sopenharmony_ci printk(KERN_ERR __FILE__ ": " 33662306a36Sopenharmony_ci "Error while probing of_platform bus\n"); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci#define DEFAULT_FIFO_SIZE 16 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ciconst char *__init mpc512x_select_psc_compat(void) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci if (of_machine_is_compatible("fsl,mpc5121")) 34462306a36Sopenharmony_ci return "fsl,mpc5121-psc"; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (of_machine_is_compatible("fsl,mpc5125")) 34762306a36Sopenharmony_ci return "fsl,mpc5125-psc"; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return NULL; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic const char *__init mpc512x_select_reset_compat(void) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci if (of_machine_is_compatible("fsl,mpc5121")) 35562306a36Sopenharmony_ci return "fsl,mpc5121-reset"; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (of_machine_is_compatible("fsl,mpc5125")) 35862306a36Sopenharmony_ci return "fsl,mpc5125-reset"; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return NULL; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic unsigned int __init get_fifo_size(struct device_node *np, 36462306a36Sopenharmony_ci char *prop_name) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci const unsigned int *fp; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci fp = of_get_property(np, prop_name, NULL); 36962306a36Sopenharmony_ci if (fp) 37062306a36Sopenharmony_ci return *fp; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci pr_warn("no %s property in %pOF node, defaulting to %d\n", 37362306a36Sopenharmony_ci prop_name, np, DEFAULT_FIFO_SIZE); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return DEFAULT_FIFO_SIZE; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci#define FIFOC(_base) ((struct mpc512x_psc_fifo __iomem *) \ 37962306a36Sopenharmony_ci ((u32)(_base) + sizeof(struct mpc52xx_psc))) 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci/* Init PSC FIFO space for TX and RX slices */ 38262306a36Sopenharmony_cistatic void __init mpc512x_psc_fifo_init(void) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct device_node *np; 38562306a36Sopenharmony_ci void __iomem *psc; 38662306a36Sopenharmony_ci unsigned int tx_fifo_size; 38762306a36Sopenharmony_ci unsigned int rx_fifo_size; 38862306a36Sopenharmony_ci const char *psc_compat; 38962306a36Sopenharmony_ci int fifobase = 0; /* current fifo address in 32 bit words */ 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci psc_compat = mpc512x_select_psc_compat(); 39262306a36Sopenharmony_ci if (!psc_compat) { 39362306a36Sopenharmony_ci pr_err("%s: no compatible devices found\n", __func__); 39462306a36Sopenharmony_ci return; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci for_each_compatible_node(np, NULL, psc_compat) { 39862306a36Sopenharmony_ci tx_fifo_size = get_fifo_size(np, "fsl,tx-fifo-size"); 39962306a36Sopenharmony_ci rx_fifo_size = get_fifo_size(np, "fsl,rx-fifo-size"); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* size in register is in 4 byte units */ 40262306a36Sopenharmony_ci tx_fifo_size /= 4; 40362306a36Sopenharmony_ci rx_fifo_size /= 4; 40462306a36Sopenharmony_ci if (!tx_fifo_size) 40562306a36Sopenharmony_ci tx_fifo_size = 1; 40662306a36Sopenharmony_ci if (!rx_fifo_size) 40762306a36Sopenharmony_ci rx_fifo_size = 1; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci psc = of_iomap(np, 0); 41062306a36Sopenharmony_ci if (!psc) { 41162306a36Sopenharmony_ci pr_err("%s: Can't map %pOF device\n", 41262306a36Sopenharmony_ci __func__, np); 41362306a36Sopenharmony_ci continue; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* FIFO space is 4KiB, check if requested size is available */ 41762306a36Sopenharmony_ci if ((fifobase + tx_fifo_size + rx_fifo_size) > 0x1000) { 41862306a36Sopenharmony_ci pr_err("%s: no fifo space available for %pOF\n", 41962306a36Sopenharmony_ci __func__, np); 42062306a36Sopenharmony_ci iounmap(psc); 42162306a36Sopenharmony_ci /* 42262306a36Sopenharmony_ci * chances are that another device requests less 42362306a36Sopenharmony_ci * fifo space, so we continue. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_ci continue; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* set tx and rx fifo size registers */ 42962306a36Sopenharmony_ci out_be32(&FIFOC(psc)->txsz, (fifobase << 16) | tx_fifo_size); 43062306a36Sopenharmony_ci fifobase += tx_fifo_size; 43162306a36Sopenharmony_ci out_be32(&FIFOC(psc)->rxsz, (fifobase << 16) | rx_fifo_size); 43262306a36Sopenharmony_ci fifobase += rx_fifo_size; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* reset and enable the slices */ 43562306a36Sopenharmony_ci out_be32(&FIFOC(psc)->txcmd, 0x80); 43662306a36Sopenharmony_ci out_be32(&FIFOC(psc)->txcmd, 0x01); 43762306a36Sopenharmony_ci out_be32(&FIFOC(psc)->rxcmd, 0x80); 43862306a36Sopenharmony_ci out_be32(&FIFOC(psc)->rxcmd, 0x01); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci iounmap(psc); 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic void __init mpc512x_restart_init(void) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct device_node *np; 44762306a36Sopenharmony_ci const char *reset_compat; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci reset_compat = mpc512x_select_reset_compat(); 45062306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, reset_compat); 45162306a36Sopenharmony_ci if (!np) 45262306a36Sopenharmony_ci return; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci reset_module_base = of_iomap(np, 0); 45562306a36Sopenharmony_ci of_node_put(np); 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_civoid __init mpc512x_init_early(void) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci mpc512x_restart_init(); 46162306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_FB_FSL_DIU)) 46262306a36Sopenharmony_ci mpc512x_init_diu(); 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_civoid __init mpc512x_init(void) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci mpc5121_clk_init(); 46862306a36Sopenharmony_ci mpc512x_declare_of_platform_devices(); 46962306a36Sopenharmony_ci mpc512x_psc_fifo_init(); 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_civoid __init mpc512x_setup_arch(void) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_FB_FSL_DIU)) 47562306a36Sopenharmony_ci mpc512x_setup_diu(); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci/** 47962306a36Sopenharmony_ci * mpc512x_cs_config - Setup chip select configuration 48062306a36Sopenharmony_ci * @cs: chip select number 48162306a36Sopenharmony_ci * @val: chip select configuration value 48262306a36Sopenharmony_ci * 48362306a36Sopenharmony_ci * Perform chip select configuration for devices on LocalPlus Bus. 48462306a36Sopenharmony_ci * Intended to dynamically reconfigure the chip select parameters 48562306a36Sopenharmony_ci * for configurable devices on the bus. 48662306a36Sopenharmony_ci */ 48762306a36Sopenharmony_ciint mpc512x_cs_config(unsigned int cs, u32 val) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci static struct mpc512x_lpc __iomem *lpc; 49062306a36Sopenharmony_ci struct device_node *np; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (cs > 7) 49362306a36Sopenharmony_ci return -EINVAL; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (!lpc) { 49662306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-lpc"); 49762306a36Sopenharmony_ci lpc = of_iomap(np, 0); 49862306a36Sopenharmony_ci of_node_put(np); 49962306a36Sopenharmony_ci if (!lpc) 50062306a36Sopenharmony_ci return -ENOMEM; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci out_be32(&lpc->cs_cfg[cs], val); 50462306a36Sopenharmony_ci return 0; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ciEXPORT_SYMBOL(mpc512x_cs_config); 507