2011-05-03 20:14:48 +04:00
/*
* Copyright ( C ) ST - Ericsson SA 2010
*
* License Terms : GNU General Public License v2
* Author : Mattias Nilsson < mattias . i . nilsson @ stericsson . com >
*
* U5500 PRCM Unit interface driver
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/err.h>
# include <linux/spinlock.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <linux/mutex.h>
# include <linux/completion.h>
# include <linux/irq.h>
# include <linux/jiffies.h>
# include <linux/bitops.h>
# include <linux/interrupt.h>
2011-08-12 12:28:10 +04:00
# include <linux/mfd/dbx500-prcmu.h>
2011-05-03 20:14:48 +04:00
# include <mach/hardware.h>
# include <mach/irqs.h>
# include <mach/db5500-regs.h>
2011-08-12 12:28:10 +04:00
# include "dbx500-prcmu-regs.h"
2011-05-03 20:14:48 +04:00
# define _PRCM_MB_HEADER (tcdm_base + 0xFE8)
# define PRCM_REQ_MB0_HEADER (_PRCM_MB_HEADER + 0x0)
# define PRCM_REQ_MB1_HEADER (_PRCM_MB_HEADER + 0x1)
# define PRCM_REQ_MB2_HEADER (_PRCM_MB_HEADER + 0x2)
# define PRCM_REQ_MB3_HEADER (_PRCM_MB_HEADER + 0x3)
# define PRCM_REQ_MB4_HEADER (_PRCM_MB_HEADER + 0x4)
# define PRCM_REQ_MB5_HEADER (_PRCM_MB_HEADER + 0x5)
# define PRCM_REQ_MB6_HEADER (_PRCM_MB_HEADER + 0x6)
# define PRCM_REQ_MB7_HEADER (_PRCM_MB_HEADER + 0x7)
# define PRCM_ACK_MB0_HEADER (_PRCM_MB_HEADER + 0x8)
# define PRCM_ACK_MB1_HEADER (_PRCM_MB_HEADER + 0x9)
# define PRCM_ACK_MB2_HEADER (_PRCM_MB_HEADER + 0xa)
# define PRCM_ACK_MB3_HEADER (_PRCM_MB_HEADER + 0xb)
# define PRCM_ACK_MB4_HEADER (_PRCM_MB_HEADER + 0xc)
# define PRCM_ACK_MB5_HEADER (_PRCM_MB_HEADER + 0xd)
# define PRCM_ACK_MB6_HEADER (_PRCM_MB_HEADER + 0xe)
# define PRCM_ACK_MB7_HEADER (_PRCM_MB_HEADER + 0xf)
/* Req Mailboxes */
# define PRCM_REQ_MB0 (tcdm_base + 0xFD8)
# define PRCM_REQ_MB1 (tcdm_base + 0xFCC)
# define PRCM_REQ_MB2 (tcdm_base + 0xFC4)
# define PRCM_REQ_MB3 (tcdm_base + 0xFC0)
# define PRCM_REQ_MB4 (tcdm_base + 0xF98)
# define PRCM_REQ_MB5 (tcdm_base + 0xF90)
# define PRCM_REQ_MB6 (tcdm_base + 0xF8C)
# define PRCM_REQ_MB7 (tcdm_base + 0xF84)
/* Ack Mailboxes */
# define PRCM_ACK_MB0 (tcdm_base + 0xF38)
# define PRCM_ACK_MB1 (tcdm_base + 0xF30)
# define PRCM_ACK_MB2 (tcdm_base + 0xF24)
# define PRCM_ACK_MB3 (tcdm_base + 0xF20)
# define PRCM_ACK_MB4 (tcdm_base + 0xF1C)
# define PRCM_ACK_MB5 (tcdm_base + 0xF14)
# define PRCM_ACK_MB6 (tcdm_base + 0xF0C)
# define PRCM_ACK_MB7 (tcdm_base + 0xF08)
enum mb_return_code {
RC_SUCCESS ,
RC_FAIL ,
} ;
/* Mailbox 0 headers. */
enum mb0_header {
/* request */
RMB0H_PWR_STATE_TRANS = 1 ,
RMB0H_WAKE_UP_CFG ,
RMB0H_RD_WAKE_UP_ACK ,
/* acknowledge */
AMB0H_WAKE_UP = 1 ,
} ;
/* Mailbox 5 headers. */
enum mb5_header {
MB5H_I2C_WRITE = 1 ,
MB5H_I2C_READ ,
} ;
/* Request mailbox 5 fields. */
# define PRCM_REQ_MB5_I2C_SLAVE (PRCM_REQ_MB5 + 0)
# define PRCM_REQ_MB5_I2C_REG (PRCM_REQ_MB5 + 1)
# define PRCM_REQ_MB5_I2C_SIZE (PRCM_REQ_MB5 + 2)
# define PRCM_REQ_MB5_I2C_DATA (PRCM_REQ_MB5 + 4)
/* Acknowledge mailbox 5 fields. */
# define PRCM_ACK_MB5_RETURN_CODE (PRCM_ACK_MB5 + 0)
# define PRCM_ACK_MB5_I2C_DATA (PRCM_ACK_MB5 + 4)
# define NUM_MB 8
# define MBOX_BIT BIT
# define ALL_MBOX_BITS (MBOX_BIT(NUM_MB) - 1)
/*
* Used by MCDE to setup all necessary PRCMU registers
*/
# define PRCMU_RESET_DSIPLL 0x00004000
# define PRCMU_UNCLAMP_DSIPLL 0x00400800
/* HDMI CLK MGT PLLSW=001 (PLLSOC0), PLLDIV=0x8, = 50 Mhz*/
# define PRCMU_DSI_CLOCK_SETTING 0x00000128
/* TVCLK_MGT PLLSW=001 (PLLSOC0) PLLDIV=0x13, = 19.05 MHZ */
# define PRCMU_DSI_LP_CLOCK_SETTING 0x00000135
2011-08-12 12:29:10 +04:00
# define PRCMU_PLLDSI_FREQ_SETTING 0x00020121
2011-05-03 20:14:48 +04:00
# define PRCMU_DSI_PLLOUT_SEL_SETTING 0x00000002
2011-08-12 12:29:10 +04:00
# define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x03000201
2011-05-03 20:14:48 +04:00
# define PRCMU_DISABLE_ESCAPE_CLOCK_DIV 0x00000101
# define PRCMU_ENABLE_PLLDSI 0x00000001
# define PRCMU_DISABLE_PLLDSI 0x00000000
# define PRCMU_DSI_RESET_SW 0x00000003
2011-08-12 12:29:10 +04:00
# define PRCMU_RESOUTN0_PIN 0x00000001
# define PRCMU_RESOUTN1_PIN 0x00000002
# define PRCMU_RESOUTN2_PIN 0x00000004
2011-05-03 20:14:48 +04:00
# define PRCMU_PLLDSI_LOCKP_LOCKED 0x3
/*
* mb0_transfer - state needed for mailbox 0 communication .
* @ lock : The transaction lock .
*/
static struct {
spinlock_t lock ;
} mb0_transfer ;
/*
* mb5_transfer - state needed for mailbox 5 communication .
* @ lock : The transaction lock .
* @ work : The transaction completion structure .
* @ ack : Reply ( " acknowledge " ) data .
*/
static struct {
struct mutex lock ;
struct completion work ;
struct {
u8 header ;
u8 status ;
u8 value [ 4 ] ;
} ack ;
} mb5_transfer ;
/* PRCMU TCDM base IO address. */
static __iomem void * tcdm_base ;
/**
* db5500_prcmu_abb_read ( ) - Read register value ( s ) from the ABB .
* @ slave : The I2C slave address .
* @ reg : The ( start ) register address .
* @ value : The read out value ( s ) .
* @ size : The number of registers to read .
*
* Reads register value ( s ) from the ABB .
* @ size has to be < = 4.
*/
int db5500_prcmu_abb_read ( u8 slave , u8 reg , u8 * value , u8 size )
{
int r ;
if ( ( size < 1 ) | | ( 4 < size ) )
return - EINVAL ;
mutex_lock ( & mb5_transfer . lock ) ;
while ( readl ( PRCM_MBOX_CPU_VAL ) & MBOX_BIT ( 5 ) )
cpu_relax ( ) ;
writeb ( slave , PRCM_REQ_MB5_I2C_SLAVE ) ;
writeb ( reg , PRCM_REQ_MB5_I2C_REG ) ;
writeb ( size , PRCM_REQ_MB5_I2C_SIZE ) ;
writeb ( MB5H_I2C_READ , PRCM_REQ_MB5_HEADER ) ;
writel ( MBOX_BIT ( 5 ) , PRCM_MBOX_CPU_SET ) ;
wait_for_completion ( & mb5_transfer . work ) ;
r = 0 ;
if ( ( mb5_transfer . ack . header = = MB5H_I2C_READ ) & &
( mb5_transfer . ack . status = = RC_SUCCESS ) )
memcpy ( value , mb5_transfer . ack . value , ( size_t ) size ) ;
else
r = - EIO ;
mutex_unlock ( & mb5_transfer . lock ) ;
return r ;
}
/**
* db5500_prcmu_abb_write ( ) - Write register value ( s ) to the ABB .
* @ slave : The I2C slave address .
* @ reg : The ( start ) register address .
* @ value : The value ( s ) to write .
* @ size : The number of registers to write .
*
* Writes register value ( s ) to the ABB .
* @ size has to be < = 4.
*/
int db5500_prcmu_abb_write ( u8 slave , u8 reg , u8 * value , u8 size )
{
int r ;
if ( ( size < 1 ) | | ( 4 < size ) )
return - EINVAL ;
mutex_lock ( & mb5_transfer . lock ) ;
while ( readl ( PRCM_MBOX_CPU_VAL ) & MBOX_BIT ( 5 ) )
cpu_relax ( ) ;
writeb ( slave , PRCM_REQ_MB5_I2C_SLAVE ) ;
writeb ( reg , PRCM_REQ_MB5_I2C_REG ) ;
writeb ( size , PRCM_REQ_MB5_I2C_SIZE ) ;
memcpy_toio ( PRCM_REQ_MB5_I2C_DATA , value , size ) ;
writeb ( MB5H_I2C_WRITE , PRCM_REQ_MB5_HEADER ) ;
writel ( MBOX_BIT ( 5 ) , PRCM_MBOX_CPU_SET ) ;
wait_for_completion ( & mb5_transfer . work ) ;
if ( ( mb5_transfer . ack . header = = MB5H_I2C_WRITE ) & &
( mb5_transfer . ack . status = = RC_SUCCESS ) )
r = 0 ;
else
r = - EIO ;
mutex_unlock ( & mb5_transfer . lock ) ;
return r ;
}
int db5500_prcmu_enable_dsipll ( void )
{
int i ;
/* Enable DSIPLL_RESETN resets */
writel ( PRCMU_RESET_DSIPLL , PRCM_APE_RESETN_CLR ) ;
/* Unclamp DSIPLL in/out */
writel ( PRCMU_UNCLAMP_DSIPLL , PRCM_MMIP_LS_CLAMP_CLR ) ;
/* Set DSI PLL FREQ */
writel ( PRCMU_PLLDSI_FREQ_SETTING , PRCM_PLLDSI_FREQ ) ;
writel ( PRCMU_DSI_PLLOUT_SEL_SETTING ,
PRCM_DSI_PLLOUT_SEL ) ;
/* Enable Escape clocks */
writel ( PRCMU_ENABLE_ESCAPE_CLOCK_DIV , PRCM_DSITVCLK_DIV ) ;
/* Start DSI PLL */
writel ( PRCMU_ENABLE_PLLDSI , PRCM_PLLDSI_ENABLE ) ;
/* Reset DSI PLL */
writel ( PRCMU_DSI_RESET_SW , PRCM_DSI_SW_RESET ) ;
for ( i = 0 ; i < 10 ; i + + ) {
if ( ( readl ( PRCM_PLLDSI_LOCKP ) &
PRCMU_PLLDSI_LOCKP_LOCKED ) = = PRCMU_PLLDSI_LOCKP_LOCKED )
break ;
udelay ( 100 ) ;
}
/* Release DSIPLL_RESETN */
writel ( PRCMU_RESET_DSIPLL , PRCM_APE_RESETN_SET ) ;
return 0 ;
}
int db5500_prcmu_disable_dsipll ( void )
{
/* Disable dsi pll */
writel ( PRCMU_DISABLE_PLLDSI , PRCM_PLLDSI_ENABLE ) ;
/* Disable escapeclock */
writel ( PRCMU_DISABLE_ESCAPE_CLOCK_DIV , PRCM_DSITVCLK_DIV ) ;
return 0 ;
}
int db5500_prcmu_set_display_clocks ( void )
{
/* HDMI and TVCLK Should be handled somewhere else */
/* PLLDIV=8, PLLSW=2, CLKEN=1 */
writel ( PRCMU_DSI_CLOCK_SETTING , PRCM_HDMICLK_MGT ) ;
/* PLLDIV=14, PLLSW=2, CLKEN=1 */
writel ( PRCMU_DSI_LP_CLOCK_SETTING , PRCM_TVCLK_MGT ) ;
return 0 ;
}
static void ack_dbb_wakeup ( void )
{
unsigned long flags ;
spin_lock_irqsave ( & mb0_transfer . lock , flags ) ;
while ( readl ( PRCM_MBOX_CPU_VAL ) & MBOX_BIT ( 0 ) )
cpu_relax ( ) ;
writeb ( RMB0H_RD_WAKE_UP_ACK , PRCM_REQ_MB0_HEADER ) ;
writel ( MBOX_BIT ( 0 ) , PRCM_MBOX_CPU_SET ) ;
spin_unlock_irqrestore ( & mb0_transfer . lock , flags ) ;
}
static inline void print_unknown_header_warning ( u8 n , u8 header )
{
pr_warning ( " prcmu: Unknown message header (%d) in mailbox %d. \n " ,
header , n ) ;
}
static bool read_mailbox_0 ( void )
{
bool r ;
u8 header ;
header = readb ( PRCM_ACK_MB0_HEADER ) ;
switch ( header ) {
case AMB0H_WAKE_UP :
r = true ;
break ;
default :
print_unknown_header_warning ( 0 , header ) ;
r = false ;
break ;
}
2011-08-12 12:28:10 +04:00
writel ( MBOX_BIT ( 0 ) , PRCM_ARM_IT1_CLR ) ;
2011-05-03 20:14:48 +04:00
return r ;
}
static bool read_mailbox_1 ( void )
{
2011-08-12 12:28:10 +04:00
writel ( MBOX_BIT ( 1 ) , PRCM_ARM_IT1_CLR ) ;
2011-05-03 20:14:48 +04:00
return false ;
}
static bool read_mailbox_2 ( void )
{
2011-08-12 12:28:10 +04:00
writel ( MBOX_BIT ( 2 ) , PRCM_ARM_IT1_CLR ) ;
2011-05-03 20:14:48 +04:00
return false ;
}
static bool read_mailbox_3 ( void )
{
2011-08-12 12:28:10 +04:00
writel ( MBOX_BIT ( 3 ) , PRCM_ARM_IT1_CLR ) ;
2011-05-03 20:14:48 +04:00
return false ;
}
static bool read_mailbox_4 ( void )
{
2011-08-12 12:28:10 +04:00
writel ( MBOX_BIT ( 4 ) , PRCM_ARM_IT1_CLR ) ;
2011-05-03 20:14:48 +04:00
return false ;
}
static bool read_mailbox_5 ( void )
{
u8 header ;
header = readb ( PRCM_ACK_MB5_HEADER ) ;
switch ( header ) {
case MB5H_I2C_READ :
memcpy_fromio ( mb5_transfer . ack . value , PRCM_ACK_MB5_I2C_DATA , 4 ) ;
case MB5H_I2C_WRITE :
mb5_transfer . ack . header = header ;
mb5_transfer . ack . status = readb ( PRCM_ACK_MB5_RETURN_CODE ) ;
complete ( & mb5_transfer . work ) ;
break ;
default :
print_unknown_header_warning ( 5 , header ) ;
break ;
}
2011-08-12 12:28:10 +04:00
writel ( MBOX_BIT ( 5 ) , PRCM_ARM_IT1_CLR ) ;
2011-05-03 20:14:48 +04:00
return false ;
}
static bool read_mailbox_6 ( void )
{
2011-08-12 12:28:10 +04:00
writel ( MBOX_BIT ( 6 ) , PRCM_ARM_IT1_CLR ) ;
2011-05-03 20:14:48 +04:00
return false ;
}
static bool read_mailbox_7 ( void )
{
2011-08-12 12:28:10 +04:00
writel ( MBOX_BIT ( 7 ) , PRCM_ARM_IT1_CLR ) ;
2011-05-03 20:14:48 +04:00
return false ;
}
static bool ( * const read_mailbox [ NUM_MB ] ) ( void ) = {
read_mailbox_0 ,
read_mailbox_1 ,
read_mailbox_2 ,
read_mailbox_3 ,
read_mailbox_4 ,
read_mailbox_5 ,
read_mailbox_6 ,
read_mailbox_7
} ;
static irqreturn_t prcmu_irq_handler ( int irq , void * data )
{
u32 bits ;
u8 n ;
irqreturn_t r ;
bits = ( readl ( PRCM_ARM_IT1_VAL ) & ALL_MBOX_BITS ) ;
if ( unlikely ( ! bits ) )
return IRQ_NONE ;
r = IRQ_HANDLED ;
for ( n = 0 ; bits ; n + + ) {
if ( bits & MBOX_BIT ( n ) ) {
bits - = MBOX_BIT ( n ) ;
if ( read_mailbox [ n ] ( ) )
r = IRQ_WAKE_THREAD ;
}
}
return r ;
}
static irqreturn_t prcmu_irq_thread_fn ( int irq , void * data )
{
ack_dbb_wakeup ( ) ;
return IRQ_HANDLED ;
}
void __init db5500_prcmu_early_init ( void )
{
tcdm_base = __io_address ( U5500_PRCMU_TCDM_BASE ) ;
spin_lock_init ( & mb0_transfer . lock ) ;
mutex_init ( & mb5_transfer . lock ) ;
init_completion ( & mb5_transfer . work ) ;
}
/**
* prcmu_fw_init - arch init call for the Linux PRCMU fw init logic
*
*/
int __init db5500_prcmu_init ( void )
{
int r = 0 ;
if ( ux500_is_svp ( ) | | ! cpu_is_u5500 ( ) )
return - ENODEV ;
/* Clean up the mailbox interrupts after pre-kernel code. */
2011-08-12 12:28:10 +04:00
writel ( ALL_MBOX_BITS , PRCM_ARM_IT1_CLR ) ;
2011-05-03 20:14:48 +04:00
r = request_threaded_irq ( IRQ_DB5500_PRCMU1 , prcmu_irq_handler ,
prcmu_irq_thread_fn , 0 , " prcmu " , NULL ) ;
if ( r < 0 ) {
pr_err ( " prcmu: Failed to allocate IRQ_DB5500_PRCMU1. \n " ) ;
return - EBUSY ;
}
return 0 ;
}
arch_initcall ( db5500_prcmu_init ) ;