2007-10-04 00:09:33 +04:00
/*
* MPC8610 HPCD board specific routines
*
* Initial author : Xianghua Xiao < x . xiao @ freescale . com >
* Recode : Jason Jin < jason . jin @ freescale . com >
2008-04-28 13:15:36 +04:00
* York Sun < yorksun @ freescale . com >
2007-10-04 00:09:33 +04:00
*
* Rewrite the interrupt routing . remove the 8259 PIC support ,
* All the integrated device in ULI use sideband interrupt .
*
2008-04-28 13:15:36 +04:00
* Copyright 2008 Freescale Semiconductor Inc .
2007-10-04 00:09:33 +04:00
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*/
# include <linux/stddef.h>
# include <linux/kernel.h>
# include <linux/pci.h>
2009-09-16 01:44:00 +04:00
# include <linux/interrupt.h>
2007-10-04 00:09:33 +04:00
# include <linux/kdev_t.h>
# include <linux/delay.h>
# include <linux/seq_file.h>
# include <linux/of.h>
# include <asm/time.h>
# include <asm/machdep.h>
# include <asm/pci-bridge.h>
# include <asm/prom.h>
# include <mm/mmu_decl.h>
# include <asm/udbg.h>
# include <asm/mpic.h>
2008-01-18 18:24:53 +03:00
# include <linux/of_platform.h>
2007-10-04 00:09:33 +04:00
# include <sysdev/fsl_pci.h>
# include <sysdev/fsl_soc.h>
2009-06-19 03:49:02 +04:00
# include <sysdev/simple_gpio.h>
2011-06-23 10:16:48 +04:00
# include <asm/fsl_guts.h>
2007-10-04 00:09:33 +04:00
2008-07-02 20:46:20 +04:00
# include "mpc86xx.h"
2009-09-16 01:44:00 +04:00
static struct device_node * pixis_node ;
2008-04-28 13:15:36 +04:00
static unsigned char * pixis_bdcfg0 , * pixis_arch ;
2011-06-23 10:16:48 +04:00
/* DIU Pixel Clock bits of the CLKDVDR Global Utilities register */
# define CLKDVDR_PXCKEN 0x80000000
# define CLKDVDR_PXCKINV 0x10000000
# define CLKDVDR_PXCKDLY 0x06000000
# define CLKDVDR_PXCLK_MASK 0x001F0000
2009-09-16 01:44:00 +04:00
# ifdef CONFIG_SUSPEND
static irqreturn_t mpc8610_sw9_irq ( int irq , void * data )
{
pr_debug ( " %s: PIXIS' event (sw9/wakeup) IRQ handled \n " , __func__ ) ;
return IRQ_HANDLED ;
}
static void __init mpc8610_suspend_init ( void )
{
int irq ;
int ret ;
if ( ! pixis_node )
return ;
irq = irq_of_parse_and_map ( pixis_node , 0 ) ;
if ( ! irq ) {
pr_err ( " %s: can't map pixis event IRQ. \n " , __func__ ) ;
return ;
}
2011-05-04 18:29:31 +04:00
ret = request_irq ( irq , mpc8610_sw9_irq , 0 , " sw9:wakeup " , NULL ) ;
2009-09-16 01:44:00 +04:00
if ( ret ) {
pr_err ( " %s: can't request pixis event IRQ: %d \n " ,
__func__ , ret ) ;
irq_dispose_mapping ( irq ) ;
}
enable_irq_wake ( irq ) ;
}
# else
static inline void mpc8610_suspend_init ( void ) { }
# endif /* CONFIG_SUSPEND */
2008-01-18 18:24:53 +03:00
static struct of_device_id __initdata mpc8610_ids [ ] = {
{ . compatible = " fsl,mpc8610-immr " , } ,
2009-09-16 01:44:00 +04:00
{ . compatible = " fsl,mpc8610-guts " , } ,
2008-05-04 22:46:27 +04:00
{ . compatible = " simple-bus " , } ,
2010-05-04 01:54:15 +04:00
/* So that the DMA channel nodes can be probed individually: */
{ . compatible = " fsl,eloplus-dma " , } ,
2012-08-28 11:44:08 +04:00
/* PCI controllers */
{ . compatible = " fsl,mpc8610-pci " , } ,
{ . compatible = " fsl,mpc8641-pcie " , } ,
2008-01-18 18:24:53 +03:00
{ }
} ;
static int __init mpc8610_declare_of_platform_devices ( void )
{
2009-06-19 03:49:02 +04:00
/* Firstly, register PIXIS GPIOs. */
simple_gpiochip_init ( " fsl,fpga-pixis-gpio-bank " ) ;
2009-09-16 01:44:00 +04:00
/* Enable wakeup on PIXIS' event IRQ. */
mpc8610_suspend_init ( ) ;
2008-01-18 18:24:53 +03:00
/* Without this call, the SSI device driver won't get probed. */
of_platform_bus_probe ( NULL , mpc8610_ids , NULL ) ;
return 0 ;
}
2012-08-28 11:44:08 +04:00
machine_arch_initcall ( mpc86xx_hpcd , mpc8610_declare_of_platform_devices ) ;
2008-01-18 18:24:53 +03:00
2008-04-28 13:15:36 +04:00
# if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE)
2011-05-09 23:29:40 +04:00
/*
* DIU Area Descriptor
*
* The MPC8610 reference manual shows the bits of the AD register in
* little - endian order , which causes the BLUE_C field to be split into two
* parts . To simplify the definition of the MAKE_AD ( ) macro , we define the
* fields in big - endian order and byte - swap the result .
*
* So even though the registers don ' t look like they ' re in the
* same bit positions as they are on the P1022 , the same value is written to
* the AD register on the MPC8610 and on the P1022 .
*/
# define AD_BYTE_F 0x10000000
# define AD_ALPHA_C_MASK 0x0E000000
# define AD_ALPHA_C_SHIFT 25
# define AD_BLUE_C_MASK 0x01800000
# define AD_BLUE_C_SHIFT 23
# define AD_GREEN_C_MASK 0x00600000
# define AD_GREEN_C_SHIFT 21
# define AD_RED_C_MASK 0x00180000
# define AD_RED_C_SHIFT 19
# define AD_PALETTE 0x00040000
# define AD_PIXEL_S_MASK 0x00030000
# define AD_PIXEL_S_SHIFT 16
# define AD_COMP_3_MASK 0x0000F000
# define AD_COMP_3_SHIFT 12
# define AD_COMP_2_MASK 0x00000F00
# define AD_COMP_2_SHIFT 8
# define AD_COMP_1_MASK 0x000000F0
# define AD_COMP_1_SHIFT 4
# define AD_COMP_0_MASK 0x0000000F
# define AD_COMP_0_SHIFT 0
# define MAKE_AD(alpha, red, blue, green, size, c0, c1, c2, c3) \
cpu_to_le32 ( AD_BYTE_F | ( alpha < < AD_ALPHA_C_SHIFT ) | \
( blue < < AD_BLUE_C_SHIFT ) | ( green < < AD_GREEN_C_SHIFT ) | \
( red < < AD_RED_C_SHIFT ) | ( c3 < < AD_COMP_3_SHIFT ) | \
( c2 < < AD_COMP_2_SHIFT ) | ( c1 < < AD_COMP_1_SHIFT ) | \
( c0 < < AD_COMP_0_SHIFT ) | ( size < < AD_PIXEL_S_SHIFT ) )
2008-04-28 13:15:36 +04:00
2011-07-10 00:38:14 +04:00
u32 mpc8610hpcd_get_pixel_format ( enum fsl_diu_monitor_port port ,
unsigned int bits_per_pixel )
2008-04-28 13:15:36 +04:00
{
2011-07-10 00:38:14 +04:00
static const u32 pixelformat [ ] [ 3 ] = {
2011-05-09 23:29:40 +04:00
{
MAKE_AD ( 3 , 0 , 2 , 1 , 3 , 8 , 8 , 8 , 8 ) ,
MAKE_AD ( 4 , 2 , 0 , 1 , 2 , 8 , 8 , 8 , 0 ) ,
MAKE_AD ( 4 , 0 , 2 , 1 , 1 , 5 , 6 , 5 , 0 )
} ,
{
MAKE_AD ( 3 , 2 , 0 , 1 , 3 , 8 , 8 , 8 , 8 ) ,
MAKE_AD ( 4 , 0 , 2 , 1 , 2 , 8 , 8 , 8 , 0 ) ,
MAKE_AD ( 4 , 2 , 0 , 1 , 1 , 5 , 6 , 5 , 0 )
} ,
2008-04-28 13:15:36 +04:00
} ;
2011-05-09 23:29:40 +04:00
unsigned int arch_monitor ;
2008-04-28 13:15:36 +04:00
2011-05-09 23:29:40 +04:00
/* The DVI port is mis-wired on revision 1 of this board. */
2011-07-10 00:38:14 +04:00
arch_monitor =
( ( * pixis_arch = = 0x01 ) & & ( port = = FSL_DIU_PORT_DVI ) ) ? 0 : 1 ;
2011-05-09 23:29:40 +04:00
switch ( bits_per_pixel ) {
case 32 :
return pixelformat [ arch_monitor ] [ 0 ] ;
case 24 :
return pixelformat [ arch_monitor ] [ 1 ] ;
case 16 :
return pixelformat [ arch_monitor ] [ 2 ] ;
default :
pr_err ( " fsl-diu: unsupported pixel depth %u \n " , bits_per_pixel ) ;
return 0 ;
}
2008-04-28 13:15:36 +04:00
}
2011-07-10 00:38:14 +04:00
void mpc8610hpcd_set_gamma_table ( enum fsl_diu_monitor_port port ,
char * gamma_table_base )
2008-04-28 13:15:36 +04:00
{
int i ;
2011-07-10 00:38:14 +04:00
if ( port = = FSL_DIU_PORT_DLVDS ) {
2008-04-28 13:15:36 +04:00
for ( i = 0 ; i < 256 * 3 ; i + + )
gamma_table_base [ i ] = ( gamma_table_base [ i ] < < 2 ) |
( ( gamma_table_base [ i ] > > 6 ) & 0x03 ) ;
}
}
2008-05-12 16:35:33 +04:00
# define PX_BRDCFG0_DVISEL (1 << 3)
# define PX_BRDCFG0_DLINK (1 << 4)
# define PX_BRDCFG0_DIU_MASK (PX_BRDCFG0_DVISEL | PX_BRDCFG0_DLINK)
2011-07-10 00:38:14 +04:00
void mpc8610hpcd_set_monitor_port ( enum fsl_diu_monitor_port port )
2008-04-28 13:15:36 +04:00
{
2011-07-10 00:38:14 +04:00
switch ( port ) {
case FSL_DIU_PORT_DVI :
2008-05-12 16:35:33 +04:00
clrsetbits_8 ( pixis_bdcfg0 , PX_BRDCFG0_DIU_MASK ,
2011-07-10 00:38:14 +04:00
PX_BRDCFG0_DVISEL | PX_BRDCFG0_DLINK ) ;
break ;
case FSL_DIU_PORT_LVDS :
clrsetbits_8 ( pixis_bdcfg0 , PX_BRDCFG0_DIU_MASK ,
PX_BRDCFG0_DLINK ) ;
break ;
case FSL_DIU_PORT_DLVDS :
clrbits8 ( pixis_bdcfg0 , PX_BRDCFG0_DIU_MASK ) ;
break ;
}
2008-04-28 13:15:36 +04:00
}
2011-06-23 10:16:48 +04:00
/**
* mpc8610hpcd_set_pixel_clock : program the DIU ' s clock
*
* @ pixclock : the wavelength , in picoseconds , of the clock
*/
2008-04-28 13:15:36 +04:00
void mpc8610hpcd_set_pixel_clock ( unsigned int pixclock )
{
2011-06-23 10:16:48 +04:00
struct device_node * guts_np = NULL ;
2012-03-19 20:06:39 +04:00
struct ccsr_guts __iomem * guts ;
2011-06-23 10:16:48 +04:00
unsigned long freq ;
u64 temp ;
u32 pxclk ;
/* Map the global utilities registers. */
guts_np = of_find_compatible_node ( NULL , NULL , " fsl,mpc8610-guts " ) ;
if ( ! guts_np ) {
2012-12-15 00:06:18 +04:00
pr_err ( " mpc8610hpcd: missing global utilities device node \n " ) ;
2008-04-28 13:15:36 +04:00
return ;
}
2011-06-23 10:16:48 +04:00
guts = of_iomap ( guts_np , 0 ) ;
of_node_put ( guts_np ) ;
if ( ! guts ) {
2012-12-15 00:06:18 +04:00
pr_err ( " mpc8610hpcd: could not map global utilities device \n " ) ;
2011-06-23 10:16:48 +04:00
return ;
2008-04-28 13:15:36 +04:00
}
2011-06-23 10:16:48 +04:00
/* Convert pixclock from a wavelength to a frequency */
temp = 1000000000000ULL ;
do_div ( temp , pixclock ) ;
freq = temp ;
/*
* ' pxclk ' is the ratio of the platform clock to the pixel clock .
* On the MPC8610 , the value programmed into CLKDVDR is the ratio
* minus one . The valid range of values is 2 - 31.
*/
pxclk = DIV_ROUND_CLOSEST ( fsl_get_sys_freq ( ) , freq ) - 1 ;
pxclk = clamp_t ( u32 , pxclk , 2 , 31 ) ;
/* Disable the pixel clock, and set it to non-inverted and no delay */
clrbits32 ( & guts - > clkdvdr ,
CLKDVDR_PXCKEN | CLKDVDR_PXCKDLY | CLKDVDR_PXCLK_MASK ) ;
/* Enable the clock and set the pxclk */
setbits32 ( & guts - > clkdvdr , CLKDVDR_PXCKEN | ( pxclk < < 16 ) ) ;
iounmap ( guts ) ;
2008-04-28 13:15:36 +04:00
}
2011-07-10 00:38:14 +04:00
enum fsl_diu_monitor_port
mpc8610hpcd_valid_monitor_port ( enum fsl_diu_monitor_port port )
2008-04-28 13:15:36 +04:00
{
2011-07-10 00:38:14 +04:00
return port ;
2008-04-28 13:15:36 +04:00
}
2007-10-04 00:09:33 +04:00
# endif
2008-04-28 13:15:36 +04:00
static void __init mpc86xx_hpcd_setup_arch ( void )
{
struct resource r ;
unsigned char * pixis ;
2007-10-04 00:09:33 +04:00
if ( ppc_md . progress )
ppc_md . progress ( " mpc86xx_hpcd_setup_arch() " , 0 ) ;
2012-08-28 11:44:08 +04:00
fsl_pci_assign_primary ( ) ;
2008-04-28 13:15:36 +04:00
# if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE)
diu_ops . get_pixel_format = mpc8610hpcd_get_pixel_format ;
diu_ops . set_gamma_table = mpc8610hpcd_set_gamma_table ;
diu_ops . set_monitor_port = mpc8610hpcd_set_monitor_port ;
diu_ops . set_pixel_clock = mpc8610hpcd_set_pixel_clock ;
2011-07-10 00:38:14 +04:00
diu_ops . valid_monitor_port = mpc8610hpcd_valid_monitor_port ;
2008-04-28 13:15:36 +04:00
# endif
2009-09-16 01:44:00 +04:00
pixis_node = of_find_compatible_node ( NULL , NULL , " fsl,fpga-pixis " ) ;
if ( pixis_node ) {
of_address_to_resource ( pixis_node , 0 , & r ) ;
of_node_put ( pixis_node ) ;
2008-04-28 13:15:36 +04:00
pixis = ioremap ( r . start , 32 ) ;
if ( ! pixis ) {
printk ( KERN_ERR " Err: can't map FPGA cfg register! \n " ) ;
return ;
}
pixis_bdcfg0 = pixis + 8 ;
pixis_arch = pixis + 1 ;
} else
printk ( KERN_ERR " Err: "
" can't find device node 'fsl,fpga-pixis' \n " ) ;
2007-10-04 00:09:33 +04:00
printk ( " MPC86xx HPCD board from Freescale Semiconductor \n " ) ;
}
/*
* Called very early , device - tree isn ' t unflattened
*/
static int __init mpc86xx_hpcd_probe ( void )
{
unsigned long root = of_get_flat_dt_root ( ) ;
if ( of_flat_dt_is_compatible ( root , " fsl,MPC8610HPCD " ) )
return 1 ; /* Looks good */
return 0 ;
}
2008-04-28 13:15:36 +04:00
static long __init mpc86xx_time_init ( void )
2007-10-04 00:09:33 +04:00
{
unsigned int temp ;
/* Set the time base to zero */
mtspr ( SPRN_TBWL , 0 ) ;
mtspr ( SPRN_TBWU , 0 ) ;
temp = mfspr ( SPRN_HID0 ) ;
temp | = HID0_TBEN ;
mtspr ( SPRN_HID0 , temp ) ;
asm volatile ( " isync " ) ;
return 0 ;
}
define_machine ( mpc86xx_hpcd ) {
. name = " MPC86xx HPCD " ,
. probe = mpc86xx_hpcd_probe ,
. setup_arch = mpc86xx_hpcd_setup_arch ,
2008-07-02 20:46:20 +04:00
. init_IRQ = mpc86xx_init_irq ,
2007-10-04 00:09:33 +04:00
. get_irq = mpic_get_irq ,
2007-10-04 10:04:57 +04:00
. restart = fsl_rstcr_restart ,
2007-10-04 00:09:33 +04:00
. time_init = mpc86xx_time_init ,
. calibrate_decr = generic_calibrate_decr ,
. progress = udbg_progress ,
2012-10-15 23:52:21 +04:00
# ifdef CONFIG_PCI
2007-10-04 00:09:33 +04:00
. pcibios_fixup_bus = fsl_pcibios_fixup_bus ,
2012-10-15 23:52:21 +04:00
# endif
2007-10-04 00:09:33 +04:00
} ;