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