Path: blob/master/arch/powerpc/platforms/512x/mpc512x_shared.c
10820 views
/*1* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. All rights reserved.2*3* Author: John Rigby <[email protected]>4*5* Description:6* MPC512x Shared code7*8* This is free software; you can redistribute it and/or modify it9* under the terms of the GNU General Public License as published by10* the Free Software Foundation; either version 2 of the License, or11* (at your option) any later version.12*/1314#include <linux/kernel.h>15#include <linux/io.h>16#include <linux/irq.h>17#include <linux/of_platform.h>18#include <linux/fsl-diu-fb.h>19#include <linux/bootmem.h>20#include <sysdev/fsl_soc.h>2122#include <asm/cacheflush.h>23#include <asm/machdep.h>24#include <asm/ipic.h>25#include <asm/prom.h>26#include <asm/time.h>27#include <asm/mpc5121.h>28#include <asm/mpc52xx_psc.h>2930#include "mpc512x.h"3132static struct mpc512x_reset_module __iomem *reset_module_base;3334static void __init mpc512x_restart_init(void)35{36struct device_node *np;3738np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-reset");39if (!np)40return;4142reset_module_base = of_iomap(np, 0);43of_node_put(np);44}4546void mpc512x_restart(char *cmd)47{48if (reset_module_base) {49/* Enable software reset "RSTE" */50out_be32(&reset_module_base->rpr, 0x52535445);51/* Set software hard reset */52out_be32(&reset_module_base->rcr, 0x2);53} else {54pr_err("Restart module not mapped.\n");55}56for (;;)57;58}5960struct fsl_diu_shared_fb {61u8 gamma[0x300]; /* 32-bit aligned! */62struct diu_ad ad0; /* 32-bit aligned! */63phys_addr_t fb_phys;64size_t fb_len;65bool in_use;66};6768unsigned int mpc512x_get_pixel_format(unsigned int bits_per_pixel,69int monitor_port)70{71switch (bits_per_pixel) {72case 32:73return 0x88883316;74case 24:75return 0x88082219;76case 16:77return 0x65053118;78}79return 0x00000400;80}8182void mpc512x_set_gamma_table(int monitor_port, char *gamma_table_base)83{84}8586void mpc512x_set_monitor_port(int monitor_port)87{88}8990#define DIU_DIV_MASK 0x000000ff91void mpc512x_set_pixel_clock(unsigned int pixclock)92{93unsigned long bestval, bestfreq, speed, busfreq;94unsigned long minpixclock, maxpixclock, pixval;95struct mpc512x_ccm __iomem *ccm;96struct device_node *np;97u32 temp;98long err;99int i;100101np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-clock");102if (!np) {103pr_err("Can't find clock control module.\n");104return;105}106107ccm = of_iomap(np, 0);108of_node_put(np);109if (!ccm) {110pr_err("Can't map clock control module reg.\n");111return;112}113114np = of_find_node_by_type(NULL, "cpu");115if (np) {116const unsigned int *prop =117of_get_property(np, "bus-frequency", NULL);118119of_node_put(np);120if (prop) {121busfreq = *prop;122} else {123pr_err("Can't get bus-frequency property\n");124return;125}126} else {127pr_err("Can't find 'cpu' node.\n");128return;129}130131/* Pixel Clock configuration */132pr_debug("DIU: Bus Frequency = %lu\n", busfreq);133speed = busfreq * 4; /* DIU_DIV ratio is 4 * CSB_CLK / DIU_CLK */134135/* Calculate the pixel clock with the smallest error */136/* calculate the following in steps to avoid overflow */137pr_debug("DIU pixclock in ps - %d\n", pixclock);138temp = (1000000000 / pixclock) * 1000;139pixclock = temp;140pr_debug("DIU pixclock freq - %u\n", pixclock);141142temp = temp / 20; /* pixclock * 0.05 */143pr_debug("deviation = %d\n", temp);144minpixclock = pixclock - temp;145maxpixclock = pixclock + temp;146pr_debug("DIU minpixclock - %lu\n", minpixclock);147pr_debug("DIU maxpixclock - %lu\n", maxpixclock);148pixval = speed/pixclock;149pr_debug("DIU pixval = %lu\n", pixval);150151err = LONG_MAX;152bestval = pixval;153pr_debug("DIU bestval = %lu\n", bestval);154155bestfreq = 0;156for (i = -1; i <= 1; i++) {157temp = speed / (pixval+i);158pr_debug("DIU test pixval i=%d, pixval=%lu, temp freq. = %u\n",159i, pixval, temp);160if ((temp < minpixclock) || (temp > maxpixclock))161pr_debug("DIU exceeds monitor range (%lu to %lu)\n",162minpixclock, maxpixclock);163else if (abs(temp - pixclock) < err) {164pr_debug("Entered the else if block %d\n", i);165err = abs(temp - pixclock);166bestval = pixval + i;167bestfreq = temp;168}169}170171pr_debug("DIU chose = %lx\n", bestval);172pr_debug("DIU error = %ld\n NomPixClk ", err);173pr_debug("DIU: Best Freq = %lx\n", bestfreq);174/* Modify DIU_DIV in CCM SCFR1 */175temp = in_be32(&ccm->scfr1);176pr_debug("DIU: Current value of SCFR1: 0x%08x\n", temp);177temp &= ~DIU_DIV_MASK;178temp |= (bestval & DIU_DIV_MASK);179out_be32(&ccm->scfr1, temp);180pr_debug("DIU: Modified value of SCFR1: 0x%08x\n", temp);181iounmap(ccm);182}183184ssize_t mpc512x_show_monitor_port(int monitor_port, char *buf)185{186return sprintf(buf, "0 - 5121 LCD\n");187}188189int mpc512x_set_sysfs_monitor_port(int val)190{191return 0;192}193194static struct fsl_diu_shared_fb __attribute__ ((__aligned__(8))) diu_shared_fb;195196#if defined(CONFIG_FB_FSL_DIU) || \197defined(CONFIG_FB_FSL_DIU_MODULE)198static inline void mpc512x_free_bootmem(struct page *page)199{200__ClearPageReserved(page);201BUG_ON(PageTail(page));202BUG_ON(atomic_read(&page->_count) > 1);203atomic_set(&page->_count, 1);204__free_page(page);205totalram_pages++;206}207208void mpc512x_release_bootmem(void)209{210unsigned long addr = diu_shared_fb.fb_phys & PAGE_MASK;211unsigned long size = diu_shared_fb.fb_len;212unsigned long start, end;213214if (diu_shared_fb.in_use) {215start = PFN_UP(addr);216end = PFN_DOWN(addr + size);217218for (; start < end; start++)219mpc512x_free_bootmem(pfn_to_page(start));220221diu_shared_fb.in_use = false;222}223diu_ops.release_bootmem = NULL;224}225#endif226227/*228* Check if DIU was pre-initialized. If so, perform steps229* needed to continue displaying through the whole boot process.230* Move area descriptor and gamma table elsewhere, they are231* destroyed by bootmem allocator otherwise. The frame buffer232* address range will be reserved in setup_arch() after bootmem233* allocator is up.234*/235void __init mpc512x_init_diu(void)236{237struct device_node *np;238struct diu __iomem *diu_reg;239phys_addr_t desc;240void __iomem *vaddr;241unsigned long mode, pix_fmt, res, bpp;242unsigned long dst;243244np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu");245if (!np) {246pr_err("No DIU node\n");247return;248}249250diu_reg = of_iomap(np, 0);251of_node_put(np);252if (!diu_reg) {253pr_err("Can't map DIU\n");254return;255}256257mode = in_be32(&diu_reg->diu_mode);258if (mode != MFB_MODE1) {259pr_info("%s: DIU OFF\n", __func__);260goto out;261}262263desc = in_be32(&diu_reg->desc[0]);264vaddr = ioremap(desc, sizeof(struct diu_ad));265if (!vaddr) {266pr_err("Can't map DIU area desc.\n");267goto out;268}269memcpy(&diu_shared_fb.ad0, vaddr, sizeof(struct diu_ad));270/* flush fb area descriptor */271dst = (unsigned long)&diu_shared_fb.ad0;272flush_dcache_range(dst, dst + sizeof(struct diu_ad) - 1);273274res = in_be32(&diu_reg->disp_size);275pix_fmt = in_le32(vaddr);276bpp = ((pix_fmt >> 16) & 0x3) + 1;277diu_shared_fb.fb_phys = in_le32(vaddr + 4);278diu_shared_fb.fb_len = ((res & 0xfff0000) >> 16) * (res & 0xfff) * bpp;279diu_shared_fb.in_use = true;280iounmap(vaddr);281282desc = in_be32(&diu_reg->gamma);283vaddr = ioremap(desc, sizeof(diu_shared_fb.gamma));284if (!vaddr) {285pr_err("Can't map DIU area desc.\n");286diu_shared_fb.in_use = false;287goto out;288}289memcpy(&diu_shared_fb.gamma, vaddr, sizeof(diu_shared_fb.gamma));290/* flush gamma table */291dst = (unsigned long)&diu_shared_fb.gamma;292flush_dcache_range(dst, dst + sizeof(diu_shared_fb.gamma) - 1);293294iounmap(vaddr);295out_be32(&diu_reg->gamma, virt_to_phys(&diu_shared_fb.gamma));296out_be32(&diu_reg->desc[1], 0);297out_be32(&diu_reg->desc[2], 0);298out_be32(&diu_reg->desc[0], virt_to_phys(&diu_shared_fb.ad0));299300out:301iounmap(diu_reg);302}303304void __init mpc512x_setup_diu(void)305{306int ret;307308/*309* We do not allocate and configure new area for bitmap buffer310* because it would requere copying bitmap data (splash image)311* and so negatively affect boot time. Instead we reserve the312* already configured frame buffer area so that it won't be313* destroyed. The starting address of the area to reserve and314* also it's length is passed to reserve_bootmem(). It will be315* freed later on first open of fbdev, when splash image is not316* needed any more.317*/318if (diu_shared_fb.in_use) {319ret = reserve_bootmem(diu_shared_fb.fb_phys,320diu_shared_fb.fb_len,321BOOTMEM_EXCLUSIVE);322if (ret) {323pr_err("%s: reserve bootmem failed\n", __func__);324diu_shared_fb.in_use = false;325}326}327328#if defined(CONFIG_FB_FSL_DIU) || \329defined(CONFIG_FB_FSL_DIU_MODULE)330diu_ops.get_pixel_format = mpc512x_get_pixel_format;331diu_ops.set_gamma_table = mpc512x_set_gamma_table;332diu_ops.set_monitor_port = mpc512x_set_monitor_port;333diu_ops.set_pixel_clock = mpc512x_set_pixel_clock;334diu_ops.show_monitor_port = mpc512x_show_monitor_port;335diu_ops.set_sysfs_monitor_port = mpc512x_set_sysfs_monitor_port;336diu_ops.release_bootmem = mpc512x_release_bootmem;337#endif338}339340void __init mpc512x_init_IRQ(void)341{342struct device_node *np;343344np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-ipic");345if (!np)346return;347348ipic_init(np, 0);349of_node_put(np);350351/*352* Initialize the default interrupt mapping priorities,353* in case the boot rom changed something on us.354*/355ipic_set_default_priority();356}357358/*359* Nodes to do bus probe on, soc and localbus360*/361static struct of_device_id __initdata of_bus_ids[] = {362{ .compatible = "fsl,mpc5121-immr", },363{ .compatible = "fsl,mpc5121-localbus", },364{},365};366367void __init mpc512x_declare_of_platform_devices(void)368{369struct device_node *np;370371if (of_platform_bus_probe(NULL, of_bus_ids, NULL))372printk(KERN_ERR __FILE__ ": "373"Error while probing of_platform bus\n");374375np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-nfc");376if (np) {377of_platform_device_create(np, NULL, NULL);378of_node_put(np);379}380}381382#define DEFAULT_FIFO_SIZE 16383384static unsigned int __init get_fifo_size(struct device_node *np,385char *prop_name)386{387const unsigned int *fp;388389fp = of_get_property(np, prop_name, NULL);390if (fp)391return *fp;392393pr_warning("no %s property in %s node, defaulting to %d\n",394prop_name, np->full_name, DEFAULT_FIFO_SIZE);395396return DEFAULT_FIFO_SIZE;397}398399#define FIFOC(_base) ((struct mpc512x_psc_fifo __iomem *) \400((u32)(_base) + sizeof(struct mpc52xx_psc)))401402/* Init PSC FIFO space for TX and RX slices */403void __init mpc512x_psc_fifo_init(void)404{405struct device_node *np;406void __iomem *psc;407unsigned int tx_fifo_size;408unsigned int rx_fifo_size;409int fifobase = 0; /* current fifo address in 32 bit words */410411for_each_compatible_node(np, NULL, "fsl,mpc5121-psc") {412tx_fifo_size = get_fifo_size(np, "fsl,tx-fifo-size");413rx_fifo_size = get_fifo_size(np, "fsl,rx-fifo-size");414415/* size in register is in 4 byte units */416tx_fifo_size /= 4;417rx_fifo_size /= 4;418if (!tx_fifo_size)419tx_fifo_size = 1;420if (!rx_fifo_size)421rx_fifo_size = 1;422423psc = of_iomap(np, 0);424if (!psc) {425pr_err("%s: Can't map %s device\n",426__func__, np->full_name);427continue;428}429430/* FIFO space is 4KiB, check if requested size is available */431if ((fifobase + tx_fifo_size + rx_fifo_size) > 0x1000) {432pr_err("%s: no fifo space available for %s\n",433__func__, np->full_name);434iounmap(psc);435/*436* chances are that another device requests less437* fifo space, so we continue.438*/439continue;440}441442/* set tx and rx fifo size registers */443out_be32(&FIFOC(psc)->txsz, (fifobase << 16) | tx_fifo_size);444fifobase += tx_fifo_size;445out_be32(&FIFOC(psc)->rxsz, (fifobase << 16) | rx_fifo_size);446fifobase += rx_fifo_size;447448/* reset and enable the slices */449out_be32(&FIFOC(psc)->txcmd, 0x80);450out_be32(&FIFOC(psc)->txcmd, 0x01);451out_be32(&FIFOC(psc)->rxcmd, 0x80);452out_be32(&FIFOC(psc)->rxcmd, 0x01);453454iounmap(psc);455}456}457458void __init mpc512x_init(void)459{460mpc512x_declare_of_platform_devices();461mpc5121_clk_init();462mpc512x_restart_init();463mpc512x_psc_fifo_init();464}465466467