2008-02-07 11:14:49 +03:00
/*
* driver / mfd / asic3 . c
*
* Compaq ASIC3 support .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* Copyright 2001 Compaq Computer Corporation .
* Copyright 2004 - 2005 Phil Blundell
2008-06-20 13:02:19 +04:00
* Copyright 2007 - 2008 OpenedHand Ltd .
2008-02-07 11:14:49 +03:00
*
* Authors : Phil Blundell < pb @ handhelds . org > ,
* Samuel Ortiz < sameo @ openedhand . com >
*
*/
# include <linux/kernel.h>
2009-06-15 14:10:24 +04:00
# include <linux/delay.h>
2008-02-07 11:14:49 +03:00
# include <linux/irq.h>
2008-06-20 13:02:19 +04:00
# include <linux/gpio.h>
2008-02-07 11:14:49 +03:00
# include <linux/io.h>
# include <linux/spinlock.h>
# include <linux/platform_device.h>
# include <linux/mfd/asic3.h>
2009-06-15 14:10:24 +04:00
# include <linux/mfd/core.h>
# include <linux/mfd/ds1wm.h>
2009-06-15 14:10:25 +04:00
# include <linux/mfd/tmio.h>
2008-02-07 11:14:49 +03:00
2009-06-05 20:31:02 +04:00
enum {
ASIC3_CLOCK_SPI ,
ASIC3_CLOCK_OWM ,
ASIC3_CLOCK_PWM0 ,
ASIC3_CLOCK_PWM1 ,
ASIC3_CLOCK_LED0 ,
ASIC3_CLOCK_LED1 ,
ASIC3_CLOCK_LED2 ,
ASIC3_CLOCK_SD_HOST ,
ASIC3_CLOCK_SD_BUS ,
ASIC3_CLOCK_SMBUS ,
ASIC3_CLOCK_EX0 ,
ASIC3_CLOCK_EX1 ,
} ;
struct asic3_clk {
int enabled ;
unsigned int cdex ;
unsigned long rate ;
} ;
# define INIT_CDEX(_name, _rate) \
[ ASIC3_CLOCK_ # # _name ] = { \
. cdex = CLOCK_CDEX_ # # _name , \
. rate = _rate , \
}
struct asic3_clk asic3_clk_init [ ] __initdata = {
INIT_CDEX ( SPI , 0 ) ,
INIT_CDEX ( OWM , 5000000 ) ,
INIT_CDEX ( PWM0 , 0 ) ,
INIT_CDEX ( PWM1 , 0 ) ,
INIT_CDEX ( LED0 , 0 ) ,
INIT_CDEX ( LED1 , 0 ) ,
INIT_CDEX ( LED2 , 0 ) ,
INIT_CDEX ( SD_HOST , 24576000 ) ,
INIT_CDEX ( SD_BUS , 12288000 ) ,
INIT_CDEX ( SMBUS , 0 ) ,
INIT_CDEX ( EX0 , 32768 ) ,
INIT_CDEX ( EX1 , 24576000 ) ,
} ;
2008-06-20 13:02:19 +04:00
struct asic3 {
void __iomem * mapping ;
unsigned int bus_shift ;
unsigned int irq_nr ;
unsigned int irq_base ;
spinlock_t lock ;
u16 irq_bothedge [ 4 ] ;
struct gpio_chip gpio ;
struct device * dev ;
2009-06-05 20:31:02 +04:00
struct asic3_clk clocks [ ARRAY_SIZE ( asic3_clk_init ) ] ;
2008-06-20 13:02:19 +04:00
} ;
static int asic3_gpio_get ( struct gpio_chip * chip , unsigned offset ) ;
2008-02-07 11:14:49 +03:00
static inline void asic3_write_register ( struct asic3 * asic ,
unsigned int reg , u32 value )
{
2008-03-29 06:10:58 +03:00
iowrite16 ( value , asic - > mapping +
2008-02-07 11:14:49 +03:00
( reg > > asic - > bus_shift ) ) ;
}
static inline u32 asic3_read_register ( struct asic3 * asic ,
unsigned int reg )
{
2008-03-29 06:10:58 +03:00
return ioread16 ( asic - > mapping +
2008-02-07 11:14:49 +03:00
( reg > > asic - > bus_shift ) ) ;
}
2009-06-05 20:31:01 +04:00
void asic3_set_register ( struct asic3 * asic , u32 reg , u32 bits , bool set )
{
unsigned long flags ;
u32 val ;
spin_lock_irqsave ( & asic - > lock , flags ) ;
val = asic3_read_register ( asic , reg ) ;
if ( set )
val | = bits ;
else
val & = ~ bits ;
asic3_write_register ( asic , reg , val ) ;
spin_unlock_irqrestore ( & asic - > lock , flags ) ;
}
2008-02-07 11:14:49 +03:00
/* IRQs */
# define MAX_ASIC_ISR_LOOPS 20
2008-06-20 13:12:21 +04:00
# define ASIC3_GPIO_BASE_INCR \
( ASIC3_GPIO_B_BASE - ASIC3_GPIO_A_BASE )
2008-02-07 11:14:49 +03:00
static void asic3_irq_flip_edge ( struct asic3 * asic ,
u32 base , int bit )
{
u16 edge ;
unsigned long flags ;
spin_lock_irqsave ( & asic - > lock , flags ) ;
edge = asic3_read_register ( asic ,
2008-06-20 13:12:21 +04:00
base + ASIC3_GPIO_EDGE_TRIGGER ) ;
2008-02-07 11:14:49 +03:00
edge ^ = bit ;
asic3_write_register ( asic ,
2008-06-20 13:12:21 +04:00
base + ASIC3_GPIO_EDGE_TRIGGER , edge ) ;
2008-02-07 11:14:49 +03:00
spin_unlock_irqrestore ( & asic - > lock , flags ) ;
}
static void asic3_irq_demux ( unsigned int irq , struct irq_desc * desc )
{
int iter , i ;
unsigned long flags ;
struct asic3 * asic ;
desc - > chip - > ack ( irq ) ;
asic = desc - > handler_data ;
for ( iter = 0 ; iter < MAX_ASIC_ISR_LOOPS ; iter + + ) {
u32 status ;
int bank ;
spin_lock_irqsave ( & asic - > lock , flags ) ;
status = asic3_read_register ( asic ,
2008-06-20 13:12:21 +04:00
ASIC3_OFFSET ( INTR , P_INT_STAT ) ) ;
2008-02-07 11:14:49 +03:00
spin_unlock_irqrestore ( & asic - > lock , flags ) ;
/* Check all ten register bits */
if ( ( status & 0x3ff ) = = 0 )
break ;
/* Handle GPIO IRQs */
for ( bank = 0 ; bank < ASIC3_NUM_GPIO_BANKS ; bank + + ) {
if ( status & ( 1 < < bank ) ) {
unsigned long base , istat ;
2008-06-20 13:12:21 +04:00
base = ASIC3_GPIO_A_BASE
+ bank * ASIC3_GPIO_BASE_INCR ;
2008-02-07 11:14:49 +03:00
spin_lock_irqsave ( & asic - > lock , flags ) ;
istat = asic3_read_register ( asic ,
base +
2008-06-20 13:12:21 +04:00
ASIC3_GPIO_INT_STATUS ) ;
2008-02-07 11:14:49 +03:00
/* Clearing IntStatus */
asic3_write_register ( asic ,
base +
2008-06-20 13:12:21 +04:00
ASIC3_GPIO_INT_STATUS , 0 ) ;
2008-02-07 11:14:49 +03:00
spin_unlock_irqrestore ( & asic - > lock , flags ) ;
for ( i = 0 ; i < ASIC3_GPIOS_PER_BANK ; i + + ) {
int bit = ( 1 < < i ) ;
unsigned int irqnr ;
if ( ! ( istat & bit ) )
continue ;
irqnr = asic - > irq_base +
( ASIC3_GPIOS_PER_BANK * bank )
+ i ;
2008-08-20 07:50:05 +04:00
desc = irq_to_desc ( irqnr ) ;
2008-02-07 11:14:49 +03:00
desc - > handle_irq ( irqnr , desc ) ;
if ( asic - > irq_bothedge [ bank ] & bit )
asic3_irq_flip_edge ( asic , base ,
bit ) ;
}
}
}
/* Handle remaining IRQs in the status register */
for ( i = ASIC3_NUM_GPIOS ; i < ASIC3_NR_IRQS ; i + + ) {
/* They start at bit 4 and go up */
if ( status & ( 1 < < ( i - ASIC3_NUM_GPIOS + 4 ) ) ) {
2008-08-20 07:50:05 +04:00
desc = irq_to_desc ( asic - > irq_base + i ) ;
2008-02-07 11:14:49 +03:00
desc - > handle_irq ( asic - > irq_base + i ,
desc ) ;
}
}
}
if ( iter > = MAX_ASIC_ISR_LOOPS )
2008-06-20 13:11:19 +04:00
dev_err ( asic - > dev , " interrupt processing overrun \n " ) ;
2008-02-07 11:14:49 +03:00
}
static inline int asic3_irq_to_bank ( struct asic3 * asic , int irq )
{
int n ;
n = ( irq - asic - > irq_base ) > > 4 ;
2008-06-20 13:12:21 +04:00
return ( n * ( ASIC3_GPIO_B_BASE - ASIC3_GPIO_A_BASE ) ) ;
2008-02-07 11:14:49 +03:00
}
static inline int asic3_irq_to_index ( struct asic3 * asic , int irq )
{
return ( irq - asic - > irq_base ) & 0xf ;
}
static void asic3_mask_gpio_irq ( unsigned int irq )
{
struct asic3 * asic = get_irq_chip_data ( irq ) ;
u32 val , bank , index ;
unsigned long flags ;
bank = asic3_irq_to_bank ( asic , irq ) ;
index = asic3_irq_to_index ( asic , irq ) ;
spin_lock_irqsave ( & asic - > lock , flags ) ;
2008-06-20 13:12:21 +04:00
val = asic3_read_register ( asic , bank + ASIC3_GPIO_MASK ) ;
2008-02-07 11:14:49 +03:00
val | = 1 < < index ;
2008-06-20 13:12:21 +04:00
asic3_write_register ( asic , bank + ASIC3_GPIO_MASK , val ) ;
2008-02-07 11:14:49 +03:00
spin_unlock_irqrestore ( & asic - > lock , flags ) ;
}
static void asic3_mask_irq ( unsigned int irq )
{
struct asic3 * asic = get_irq_chip_data ( irq ) ;
int regval ;
unsigned long flags ;
spin_lock_irqsave ( & asic - > lock , flags ) ;
regval = asic3_read_register ( asic ,
2008-06-20 13:12:21 +04:00
ASIC3_INTR_BASE +
ASIC3_INTR_INT_MASK ) ;
2008-02-07 11:14:49 +03:00
regval & = ~ ( ASIC3_INTMASK_MASK0 < <
( irq - ( asic - > irq_base + ASIC3_NUM_GPIOS ) ) ) ;
asic3_write_register ( asic ,
2008-06-20 13:12:21 +04:00
ASIC3_INTR_BASE +
ASIC3_INTR_INT_MASK ,
2008-02-07 11:14:49 +03:00
regval ) ;
spin_unlock_irqrestore ( & asic - > lock , flags ) ;
}
static void asic3_unmask_gpio_irq ( unsigned int irq )
{
struct asic3 * asic = get_irq_chip_data ( irq ) ;
u32 val , bank , index ;
unsigned long flags ;
bank = asic3_irq_to_bank ( asic , irq ) ;
index = asic3_irq_to_index ( asic , irq ) ;
spin_lock_irqsave ( & asic - > lock , flags ) ;
2008-06-20 13:12:21 +04:00
val = asic3_read_register ( asic , bank + ASIC3_GPIO_MASK ) ;
2008-02-07 11:14:49 +03:00
val & = ~ ( 1 < < index ) ;
2008-06-20 13:12:21 +04:00
asic3_write_register ( asic , bank + ASIC3_GPIO_MASK , val ) ;
2008-02-07 11:14:49 +03:00
spin_unlock_irqrestore ( & asic - > lock , flags ) ;
}
static void asic3_unmask_irq ( unsigned int irq )
{
struct asic3 * asic = get_irq_chip_data ( irq ) ;
int regval ;
unsigned long flags ;
spin_lock_irqsave ( & asic - > lock , flags ) ;
regval = asic3_read_register ( asic ,
2008-06-20 13:12:21 +04:00
ASIC3_INTR_BASE +
ASIC3_INTR_INT_MASK ) ;
2008-02-07 11:14:49 +03:00
regval | = ( ASIC3_INTMASK_MASK0 < <
( irq - ( asic - > irq_base + ASIC3_NUM_GPIOS ) ) ) ;
asic3_write_register ( asic ,
2008-06-20 13:12:21 +04:00
ASIC3_INTR_BASE +
ASIC3_INTR_INT_MASK ,
2008-02-07 11:14:49 +03:00
regval ) ;
spin_unlock_irqrestore ( & asic - > lock , flags ) ;
}
static int asic3_gpio_irq_type ( unsigned int irq , unsigned int type )
{
struct asic3 * asic = get_irq_chip_data ( irq ) ;
u32 bank , index ;
u16 trigger , level , edge , bit ;
unsigned long flags ;
bank = asic3_irq_to_bank ( asic , irq ) ;
index = asic3_irq_to_index ( asic , irq ) ;
bit = 1 < < index ;
spin_lock_irqsave ( & asic - > lock , flags ) ;
level = asic3_read_register ( asic ,
2008-06-20 13:12:21 +04:00
bank + ASIC3_GPIO_LEVEL_TRIGGER ) ;
2008-02-07 11:14:49 +03:00
edge = asic3_read_register ( asic ,
2008-06-20 13:12:21 +04:00
bank + ASIC3_GPIO_EDGE_TRIGGER ) ;
2008-02-07 11:14:49 +03:00
trigger = asic3_read_register ( asic ,
2008-06-20 13:12:21 +04:00
bank + ASIC3_GPIO_TRIGGER_TYPE ) ;
2008-02-07 11:14:49 +03:00
asic - > irq_bothedge [ ( irq - asic - > irq_base ) > > 4 ] & = ~ bit ;
2008-07-27 07:23:31 +04:00
if ( type = = IRQ_TYPE_EDGE_RISING ) {
2008-02-07 11:14:49 +03:00
trigger | = bit ;
edge | = bit ;
2008-07-27 07:23:31 +04:00
} else if ( type = = IRQ_TYPE_EDGE_FALLING ) {
2008-02-07 11:14:49 +03:00
trigger | = bit ;
edge & = ~ bit ;
2008-07-27 07:23:31 +04:00
} else if ( type = = IRQ_TYPE_EDGE_BOTH ) {
2008-02-07 11:14:49 +03:00
trigger | = bit ;
2008-06-20 13:02:19 +04:00
if ( asic3_gpio_get ( & asic - > gpio , irq - asic - > irq_base ) )
2008-02-07 11:14:49 +03:00
edge & = ~ bit ;
else
edge | = bit ;
asic - > irq_bothedge [ ( irq - asic - > irq_base ) > > 4 ] | = bit ;
2008-07-27 07:23:31 +04:00
} else if ( type = = IRQ_TYPE_LEVEL_LOW ) {
2008-02-07 11:14:49 +03:00
trigger & = ~ bit ;
level & = ~ bit ;
2008-07-27 07:23:31 +04:00
} else if ( type = = IRQ_TYPE_LEVEL_HIGH ) {
2008-02-07 11:14:49 +03:00
trigger & = ~ bit ;
level | = bit ;
} else {
/*
2008-07-27 07:23:31 +04:00
* if type = = IRQ_TYPE_NONE , we should mask interrupts , but
2008-02-07 11:14:49 +03:00
* be careful to not unmask them if mask was also called .
* Probably need internal state for mask .
*/
2008-06-20 13:11:19 +04:00
dev_notice ( asic - > dev , " irq type not changed \n " ) ;
2008-02-07 11:14:49 +03:00
}
2008-06-20 13:12:21 +04:00
asic3_write_register ( asic , bank + ASIC3_GPIO_LEVEL_TRIGGER ,
2008-02-07 11:14:49 +03:00
level ) ;
2008-06-20 13:12:21 +04:00
asic3_write_register ( asic , bank + ASIC3_GPIO_EDGE_TRIGGER ,
2008-02-07 11:14:49 +03:00
edge ) ;
2008-06-20 13:12:21 +04:00
asic3_write_register ( asic , bank + ASIC3_GPIO_TRIGGER_TYPE ,
2008-02-07 11:14:49 +03:00
trigger ) ;
spin_unlock_irqrestore ( & asic - > lock , flags ) ;
return 0 ;
}
static struct irq_chip asic3_gpio_irq_chip = {
. name = " ASIC3-GPIO " ,
. ack = asic3_mask_gpio_irq ,
. mask = asic3_mask_gpio_irq ,
. unmask = asic3_unmask_gpio_irq ,
. set_type = asic3_gpio_irq_type ,
} ;
static struct irq_chip asic3_irq_chip = {
. name = " ASIC3 " ,
. ack = asic3_mask_irq ,
. mask = asic3_mask_irq ,
. unmask = asic3_unmask_irq ,
} ;
2008-06-21 02:51:38 +04:00
static int __init asic3_irq_probe ( struct platform_device * pdev )
2008-02-07 11:14:49 +03:00
{
struct asic3 * asic = platform_get_drvdata ( pdev ) ;
unsigned long clksel = 0 ;
unsigned int irq , irq_base ;
2008-07-26 06:44:41 +04:00
int ret ;
2008-02-07 11:14:49 +03:00
2008-07-26 06:44:41 +04:00
ret = platform_get_irq ( pdev , 0 ) ;
if ( ret < 0 )
return ret ;
asic - > irq_nr = ret ;
2008-02-07 11:14:49 +03:00
/* turn on clock to IRQ controller */
clksel | = CLOCK_SEL_CX ;
asic3_write_register ( asic , ASIC3_OFFSET ( CLOCK , SEL ) ,
clksel ) ;
irq_base = asic - > irq_base ;
for ( irq = irq_base ; irq < irq_base + ASIC3_NR_IRQS ; irq + + ) {
if ( irq < asic - > irq_base + ASIC3_NUM_GPIOS )
set_irq_chip ( irq , & asic3_gpio_irq_chip ) ;
else
set_irq_chip ( irq , & asic3_irq_chip ) ;
set_irq_chip_data ( irq , asic ) ;
set_irq_handler ( irq , handle_level_irq ) ;
set_irq_flags ( irq , IRQF_VALID | IRQF_PROBE ) ;
}
2008-06-20 13:12:21 +04:00
asic3_write_register ( asic , ASIC3_OFFSET ( INTR , INT_MASK ) ,
2008-02-07 11:14:49 +03:00
ASIC3_INTMASK_GINTMASK ) ;
set_irq_chained_handler ( asic - > irq_nr , asic3_irq_demux ) ;
2008-07-27 07:23:31 +04:00
set_irq_type ( asic - > irq_nr , IRQ_TYPE_EDGE_RISING ) ;
2008-02-07 11:14:49 +03:00
set_irq_data ( asic - > irq_nr , asic ) ;
return 0 ;
}
static void asic3_irq_remove ( struct platform_device * pdev )
{
struct asic3 * asic = platform_get_drvdata ( pdev ) ;
unsigned int irq , irq_base ;
irq_base = asic - > irq_base ;
for ( irq = irq_base ; irq < irq_base + ASIC3_NR_IRQS ; irq + + ) {
set_irq_flags ( irq , 0 ) ;
set_irq_handler ( irq , NULL ) ;
set_irq_chip ( irq , NULL ) ;
set_irq_chip_data ( irq , NULL ) ;
}
set_irq_chained_handler ( asic - > irq_nr , NULL ) ;
}
/* GPIOs */
2008-06-20 13:02:19 +04:00
static int asic3_gpio_direction ( struct gpio_chip * chip ,
unsigned offset , int out )
{
u32 mask = ASIC3_GPIO_TO_MASK ( offset ) , out_reg ;
unsigned int gpio_base ;
unsigned long flags ;
struct asic3 * asic ;
asic = container_of ( chip , struct asic3 , gpio ) ;
gpio_base = ASIC3_GPIO_TO_BASE ( offset ) ;
2008-06-20 13:12:21 +04:00
if ( gpio_base > ASIC3_GPIO_D_BASE ) {
2008-06-20 13:11:19 +04:00
dev_err ( asic - > dev , " Invalid base (0x%x) for gpio %d \n " ,
gpio_base , offset ) ;
2008-06-20 13:02:19 +04:00
return - EINVAL ;
}
spin_lock_irqsave ( & asic - > lock , flags ) ;
2008-06-20 13:12:21 +04:00
out_reg = asic3_read_register ( asic , gpio_base + ASIC3_GPIO_DIRECTION ) ;
2008-06-20 13:02:19 +04:00
/* Input is 0, Output is 1 */
if ( out )
out_reg | = mask ;
else
out_reg & = ~ mask ;
2008-06-20 13:12:21 +04:00
asic3_write_register ( asic , gpio_base + ASIC3_GPIO_DIRECTION , out_reg ) ;
2008-06-20 13:02:19 +04:00
spin_unlock_irqrestore ( & asic - > lock , flags ) ;
return 0 ;
}
static int asic3_gpio_direction_input ( struct gpio_chip * chip ,
unsigned offset )
{
return asic3_gpio_direction ( chip , offset , 0 ) ;
}
static int asic3_gpio_direction_output ( struct gpio_chip * chip ,
unsigned offset , int value )
{
return asic3_gpio_direction ( chip , offset , 1 ) ;
}
static int asic3_gpio_get ( struct gpio_chip * chip ,
unsigned offset )
{
unsigned int gpio_base ;
u32 mask = ASIC3_GPIO_TO_MASK ( offset ) ;
struct asic3 * asic ;
asic = container_of ( chip , struct asic3 , gpio ) ;
gpio_base = ASIC3_GPIO_TO_BASE ( offset ) ;
2008-06-20 13:12:21 +04:00
if ( gpio_base > ASIC3_GPIO_D_BASE ) {
2008-06-20 13:11:19 +04:00
dev_err ( asic - > dev , " Invalid base (0x%x) for gpio %d \n " ,
gpio_base , offset ) ;
2008-06-20 13:02:19 +04:00
return - EINVAL ;
}
2008-06-20 13:12:21 +04:00
return asic3_read_register ( asic , gpio_base + ASIC3_GPIO_STATUS ) & mask ;
2008-06-20 13:02:19 +04:00
}
static void asic3_gpio_set ( struct gpio_chip * chip ,
unsigned offset , int value )
{
u32 mask , out_reg ;
unsigned int gpio_base ;
unsigned long flags ;
struct asic3 * asic ;
asic = container_of ( chip , struct asic3 , gpio ) ;
gpio_base = ASIC3_GPIO_TO_BASE ( offset ) ;
2008-06-20 13:12:21 +04:00
if ( gpio_base > ASIC3_GPIO_D_BASE ) {
2008-06-20 13:11:19 +04:00
dev_err ( asic - > dev , " Invalid base (0x%x) for gpio %d \n " ,
gpio_base , offset ) ;
2008-06-20 13:02:19 +04:00
return ;
}
mask = ASIC3_GPIO_TO_MASK ( offset ) ;
spin_lock_irqsave ( & asic - > lock , flags ) ;
2008-06-20 13:12:21 +04:00
out_reg = asic3_read_register ( asic , gpio_base + ASIC3_GPIO_OUT ) ;
2008-06-20 13:02:19 +04:00
if ( value )
out_reg | = mask ;
else
out_reg & = ~ mask ;
2008-06-20 13:12:21 +04:00
asic3_write_register ( asic , gpio_base + ASIC3_GPIO_OUT , out_reg ) ;
2008-06-20 13:02:19 +04:00
spin_unlock_irqrestore ( & asic - > lock , flags ) ;
return ;
}
2008-06-21 02:51:38 +04:00
static __init int asic3_gpio_probe ( struct platform_device * pdev ,
u16 * gpio_config , int num )
2008-02-07 11:14:49 +03:00
{
struct asic3 * asic = platform_get_drvdata ( pdev ) ;
2008-06-20 13:09:51 +04:00
u16 alt_reg [ ASIC3_NUM_GPIO_BANKS ] ;
u16 out_reg [ ASIC3_NUM_GPIO_BANKS ] ;
u16 dir_reg [ ASIC3_NUM_GPIO_BANKS ] ;
int i ;
2008-02-07 11:14:49 +03:00
2008-10-27 14:24:09 +03:00
memset ( alt_reg , 0 , ASIC3_NUM_GPIO_BANKS * sizeof ( u16 ) ) ;
memset ( out_reg , 0 , ASIC3_NUM_GPIO_BANKS * sizeof ( u16 ) ) ;
memset ( dir_reg , 0 , ASIC3_NUM_GPIO_BANKS * sizeof ( u16 ) ) ;
2008-06-20 13:09:51 +04:00
/* Enable all GPIOs */
2008-06-20 13:12:21 +04:00
asic3_write_register ( asic , ASIC3_GPIO_OFFSET ( A , MASK ) , 0xffff ) ;
asic3_write_register ( asic , ASIC3_GPIO_OFFSET ( B , MASK ) , 0xffff ) ;
asic3_write_register ( asic , ASIC3_GPIO_OFFSET ( C , MASK ) , 0xffff ) ;
asic3_write_register ( asic , ASIC3_GPIO_OFFSET ( D , MASK ) , 0xffff ) ;
2008-02-07 11:14:49 +03:00
2008-06-20 13:09:51 +04:00
for ( i = 0 ; i < num ; i + + ) {
u8 alt , pin , dir , init , bank_num , bit_num ;
u16 config = gpio_config [ i ] ;
pin = ASIC3_CONFIG_GPIO_PIN ( config ) ;
alt = ASIC3_CONFIG_GPIO_ALT ( config ) ;
dir = ASIC3_CONFIG_GPIO_DIR ( config ) ;
init = ASIC3_CONFIG_GPIO_INIT ( config ) ;
bank_num = ASIC3_GPIO_TO_BANK ( pin ) ;
bit_num = ASIC3_GPIO_TO_BIT ( pin ) ;
alt_reg [ bank_num ] | = ( alt < < bit_num ) ;
out_reg [ bank_num ] | = ( init < < bit_num ) ;
dir_reg [ bank_num ] | = ( dir < < bit_num ) ;
}
for ( i = 0 ; i < ASIC3_NUM_GPIO_BANKS ; i + + ) {
asic3_write_register ( asic ,
ASIC3_BANK_TO_BASE ( i ) +
2008-06-20 13:12:21 +04:00
ASIC3_GPIO_DIRECTION ,
2008-06-20 13:09:51 +04:00
dir_reg [ i ] ) ;
asic3_write_register ( asic ,
2008-06-20 13:12:21 +04:00
ASIC3_BANK_TO_BASE ( i ) + ASIC3_GPIO_OUT ,
2008-06-20 13:09:51 +04:00
out_reg [ i ] ) ;
asic3_write_register ( asic ,
ASIC3_BANK_TO_BASE ( i ) +
2008-06-20 13:12:21 +04:00
ASIC3_GPIO_ALT_FUNCTION ,
2008-06-20 13:09:51 +04:00
alt_reg [ i ] ) ;
2008-02-07 11:14:49 +03:00
}
2008-06-20 13:02:19 +04:00
return gpiochip_add ( & asic - > gpio ) ;
2008-02-07 11:14:49 +03:00
}
2008-06-20 13:02:19 +04:00
static int asic3_gpio_remove ( struct platform_device * pdev )
2008-02-07 11:14:49 +03:00
{
2008-06-20 13:02:19 +04:00
struct asic3 * asic = platform_get_drvdata ( pdev ) ;
return gpiochip_remove ( & asic - > gpio ) ;
2008-02-07 11:14:49 +03:00
}
2009-06-05 20:31:02 +04:00
static int asic3_clk_enable ( struct asic3 * asic , struct asic3_clk * clk )
{
unsigned long flags ;
u32 cdex ;
spin_lock_irqsave ( & asic - > lock , flags ) ;
if ( clk - > enabled + + = = 0 ) {
cdex = asic3_read_register ( asic , ASIC3_OFFSET ( CLOCK , CDEX ) ) ;
cdex | = clk - > cdex ;
asic3_write_register ( asic , ASIC3_OFFSET ( CLOCK , CDEX ) , cdex ) ;
}
spin_unlock_irqrestore ( & asic - > lock , flags ) ;
return 0 ;
}
static void asic3_clk_disable ( struct asic3 * asic , struct asic3_clk * clk )
{
unsigned long flags ;
u32 cdex ;
WARN_ON ( clk - > enabled = = 0 ) ;
spin_lock_irqsave ( & asic - > lock , flags ) ;
if ( - - clk - > enabled = = 0 ) {
cdex = asic3_read_register ( asic , ASIC3_OFFSET ( CLOCK , CDEX ) ) ;
cdex & = ~ clk - > cdex ;
asic3_write_register ( asic , ASIC3_OFFSET ( CLOCK , CDEX ) , cdex ) ;
}
spin_unlock_irqrestore ( & asic - > lock , flags ) ;
}
2008-02-07 11:14:49 +03:00
2009-06-15 14:10:24 +04:00
/* MFD cells (SPI, PWM, LED, DS1WM, MMC) */
static struct ds1wm_driver_data ds1wm_pdata = {
. active_high = 1 ,
} ;
static struct resource ds1wm_resources [ ] = {
{
. start = ASIC3_OWM_BASE ,
. end = ASIC3_OWM_BASE + 0x13 ,
. flags = IORESOURCE_MEM ,
} ,
{
. start = ASIC3_IRQ_OWM ,
. start = ASIC3_IRQ_OWM ,
. flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE ,
} ,
} ;
static int ds1wm_enable ( struct platform_device * pdev )
{
struct asic3 * asic = dev_get_drvdata ( pdev - > dev . parent ) ;
/* Turn on external clocks and the OWM clock */
asic3_clk_enable ( asic , & asic - > clocks [ ASIC3_CLOCK_EX0 ] ) ;
asic3_clk_enable ( asic , & asic - > clocks [ ASIC3_CLOCK_EX1 ] ) ;
asic3_clk_enable ( asic , & asic - > clocks [ ASIC3_CLOCK_OWM ] ) ;
msleep ( 1 ) ;
/* Reset and enable DS1WM */
asic3_set_register ( asic , ASIC3_OFFSET ( EXTCF , RESET ) ,
ASIC3_EXTCF_OWM_RESET , 1 ) ;
msleep ( 1 ) ;
asic3_set_register ( asic , ASIC3_OFFSET ( EXTCF , RESET ) ,
ASIC3_EXTCF_OWM_RESET , 0 ) ;
msleep ( 1 ) ;
asic3_set_register ( asic , ASIC3_OFFSET ( EXTCF , SELECT ) ,
ASIC3_EXTCF_OWM_EN , 1 ) ;
msleep ( 1 ) ;
return 0 ;
}
static int ds1wm_disable ( struct platform_device * pdev )
{
struct asic3 * asic = dev_get_drvdata ( pdev - > dev . parent ) ;
asic3_set_register ( asic , ASIC3_OFFSET ( EXTCF , SELECT ) ,
ASIC3_EXTCF_OWM_EN , 0 ) ;
asic3_clk_disable ( asic , & asic - > clocks [ ASIC3_CLOCK_OWM ] ) ;
asic3_clk_disable ( asic , & asic - > clocks [ ASIC3_CLOCK_EX0 ] ) ;
asic3_clk_disable ( asic , & asic - > clocks [ ASIC3_CLOCK_EX1 ] ) ;
return 0 ;
}
static struct mfd_cell asic3_cell_ds1wm = {
. name = " ds1wm " ,
. enable = ds1wm_enable ,
. disable = ds1wm_disable ,
. driver_data = & ds1wm_pdata ,
. num_resources = ARRAY_SIZE ( ds1wm_resources ) ,
. resources = ds1wm_resources ,
} ;
2009-06-15 14:10:25 +04:00
static struct tmio_mmc_data asic3_mmc_data = {
. hclk = 24576000 ,
} ;
static struct resource asic3_mmc_resources [ ] = {
{
. start = ASIC3_SD_CTRL_BASE ,
. end = ASIC3_SD_CTRL_BASE + 0x3ff ,
. flags = IORESOURCE_MEM ,
} ,
{
. start = ASIC3_SD_CONFIG_BASE ,
. end = ASIC3_SD_CONFIG_BASE + 0x1ff ,
. flags = IORESOURCE_MEM ,
} ,
{
. start = 0 ,
. end = 0 ,
. flags = IORESOURCE_IRQ ,
} ,
} ;
static int asic3_mmc_enable ( struct platform_device * pdev )
{
struct asic3 * asic = dev_get_drvdata ( pdev - > dev . parent ) ;
/* Not sure if it must be done bit by bit, but leaving as-is */
asic3_set_register ( asic , ASIC3_OFFSET ( SDHWCTRL , SDCONF ) ,
ASIC3_SDHWCTRL_LEVCD , 1 ) ;
asic3_set_register ( asic , ASIC3_OFFSET ( SDHWCTRL , SDCONF ) ,
ASIC3_SDHWCTRL_LEVWP , 1 ) ;
asic3_set_register ( asic , ASIC3_OFFSET ( SDHWCTRL , SDCONF ) ,
ASIC3_SDHWCTRL_SUSPEND , 0 ) ;
asic3_set_register ( asic , ASIC3_OFFSET ( SDHWCTRL , SDCONF ) ,
ASIC3_SDHWCTRL_PCLR , 0 ) ;
asic3_clk_enable ( asic , & asic - > clocks [ ASIC3_CLOCK_EX0 ] ) ;
/* CLK32 used for card detection and for interruption detection
* when HCLK is stopped .
*/
asic3_clk_enable ( asic , & asic - > clocks [ ASIC3_CLOCK_EX1 ] ) ;
msleep ( 1 ) ;
/* HCLK 24.576 MHz, BCLK 12.288 MHz: */
asic3_write_register ( asic , ASIC3_OFFSET ( CLOCK , SEL ) ,
CLOCK_SEL_CX | CLOCK_SEL_SD_HCLK_SEL ) ;
asic3_clk_enable ( asic , & asic - > clocks [ ASIC3_CLOCK_SD_HOST ] ) ;
asic3_clk_enable ( asic , & asic - > clocks [ ASIC3_CLOCK_SD_BUS ] ) ;
msleep ( 1 ) ;
asic3_set_register ( asic , ASIC3_OFFSET ( EXTCF , SELECT ) ,
ASIC3_EXTCF_SD_MEM_ENABLE , 1 ) ;
/* Enable SD card slot 3.3V power supply */
asic3_set_register ( asic , ASIC3_OFFSET ( SDHWCTRL , SDCONF ) ,
ASIC3_SDHWCTRL_SDPWR , 1 ) ;
return 0 ;
}
static int asic3_mmc_disable ( struct platform_device * pdev )
{
struct asic3 * asic = dev_get_drvdata ( pdev - > dev . parent ) ;
/* Put in suspend mode */
asic3_set_register ( asic , ASIC3_OFFSET ( SDHWCTRL , SDCONF ) ,
ASIC3_SDHWCTRL_SUSPEND , 1 ) ;
/* Disable clocks */
asic3_clk_disable ( asic , & asic - > clocks [ ASIC3_CLOCK_SD_HOST ] ) ;
asic3_clk_disable ( asic , & asic - > clocks [ ASIC3_CLOCK_SD_BUS ] ) ;
asic3_clk_disable ( asic , & asic - > clocks [ ASIC3_CLOCK_EX0 ] ) ;
asic3_clk_disable ( asic , & asic - > clocks [ ASIC3_CLOCK_EX1 ] ) ;
return 0 ;
}
static struct mfd_cell asic3_cell_mmc = {
. name = " tmio-mmc " ,
. enable = asic3_mmc_enable ,
. disable = asic3_mmc_disable ,
. driver_data = & asic3_mmc_data ,
. num_resources = ARRAY_SIZE ( asic3_mmc_resources ) ,
. resources = asic3_mmc_resources ,
} ;
2009-06-15 14:10:24 +04:00
static int __init asic3_mfd_probe ( struct platform_device * pdev ,
struct resource * mem )
{
struct asic3 * asic = platform_get_drvdata ( pdev ) ;
2009-06-15 14:10:25 +04:00
struct resource * mem_sdio ;
int irq , ret ;
mem_sdio = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
if ( ! mem_sdio )
dev_dbg ( asic - > dev , " no SDIO MEM resource \n " ) ;
irq = platform_get_irq ( pdev , 1 ) ;
if ( irq < 0 )
dev_dbg ( asic - > dev , " no SDIO IRQ resource \n " ) ;
2009-06-15 14:10:24 +04:00
/* DS1WM */
asic3_set_register ( asic , ASIC3_OFFSET ( EXTCF , SELECT ) ,
ASIC3_EXTCF_OWM_SMB , 0 ) ;
ds1wm_resources [ 0 ] . start > > = asic - > bus_shift ;
ds1wm_resources [ 0 ] . end > > = asic - > bus_shift ;
asic3_cell_ds1wm . platform_data = & asic3_cell_ds1wm ;
asic3_cell_ds1wm . data_size = sizeof ( asic3_cell_ds1wm ) ;
2009-06-15 14:10:25 +04:00
/* MMC */
asic3_mmc_resources [ 0 ] . start > > = asic - > bus_shift ;
asic3_mmc_resources [ 0 ] . end > > = asic - > bus_shift ;
asic3_mmc_resources [ 1 ] . start > > = asic - > bus_shift ;
asic3_mmc_resources [ 1 ] . end > > = asic - > bus_shift ;
asic3_cell_mmc . platform_data = & asic3_cell_mmc ;
asic3_cell_mmc . data_size = sizeof ( asic3_cell_mmc ) ;
2009-06-15 14:10:24 +04:00
ret = mfd_add_devices ( & pdev - > dev , pdev - > id ,
& asic3_cell_ds1wm , 1 , mem , asic - > irq_base ) ;
2009-06-15 14:10:25 +04:00
if ( ret < 0 )
goto out ;
if ( mem_sdio & & ( irq > = 0 ) )
ret = mfd_add_devices ( & pdev - > dev , pdev - > id ,
& asic3_cell_mmc , 1 , mem_sdio , irq ) ;
2009-06-15 14:10:24 +04:00
2009-06-15 14:10:25 +04:00
out :
2009-06-15 14:10:24 +04:00
return ret ;
}
static void asic3_mfd_remove ( struct platform_device * pdev )
{
mfd_remove_devices ( & pdev - > dev ) ;
}
2008-02-07 11:14:49 +03:00
/* Core */
2008-06-21 02:51:38 +04:00
static int __init asic3_probe ( struct platform_device * pdev )
2008-02-07 11:14:49 +03:00
{
struct asic3_platform_data * pdata = pdev - > dev . platform_data ;
struct asic3 * asic ;
struct resource * mem ;
unsigned long clksel ;
2008-06-20 13:02:19 +04:00
int ret = 0 ;
2008-02-07 11:14:49 +03:00
asic = kzalloc ( sizeof ( struct asic3 ) , GFP_KERNEL ) ;
2008-06-20 13:02:19 +04:00
if ( asic = = NULL ) {
printk ( KERN_ERR " kzalloc failed \n " ) ;
2008-02-07 11:14:49 +03:00
return - ENOMEM ;
2008-06-20 13:02:19 +04:00
}
2008-02-07 11:14:49 +03:00
spin_lock_init ( & asic - > lock ) ;
platform_set_drvdata ( pdev , asic ) ;
asic - > dev = & pdev - > dev ;
mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! mem ) {
ret = - ENOMEM ;
2008-06-20 13:11:19 +04:00
dev_err ( asic - > dev , " no MEM resource \n " ) ;
2008-06-20 13:02:19 +04:00
goto out_free ;
2008-02-07 11:14:49 +03:00
}
2009-06-05 20:31:04 +04:00
asic - > mapping = ioremap ( mem - > start , resource_size ( mem ) ) ;
2008-02-07 11:14:49 +03:00
if ( ! asic - > mapping ) {
ret = - ENOMEM ;
2008-06-20 13:11:19 +04:00
dev_err ( asic - > dev , " Couldn't ioremap \n " ) ;
2008-06-20 13:02:19 +04:00
goto out_free ;
2008-02-07 11:14:49 +03:00
}
asic - > irq_base = pdata - > irq_base ;
2008-07-10 04:17:02 +04:00
/* calculate bus shift from mem resource */
2009-06-05 20:31:04 +04:00
asic - > bus_shift = 2 - ( resource_size ( mem ) > > 12 ) ;
2008-02-07 11:14:49 +03:00
clksel = 0 ;
asic3_write_register ( asic , ASIC3_OFFSET ( CLOCK , SEL ) , clksel ) ;
ret = asic3_irq_probe ( pdev ) ;
if ( ret < 0 ) {
2008-06-20 13:11:19 +04:00
dev_err ( asic - > dev , " Couldn't probe IRQs \n " ) ;
2008-06-20 13:02:19 +04:00
goto out_unmap ;
}
asic - > gpio . base = pdata - > gpio_base ;
asic - > gpio . ngpio = ASIC3_NUM_GPIOS ;
asic - > gpio . get = asic3_gpio_get ;
asic - > gpio . set = asic3_gpio_set ;
asic - > gpio . direction_input = asic3_gpio_direction_input ;
asic - > gpio . direction_output = asic3_gpio_direction_output ;
2008-06-20 13:09:51 +04:00
ret = asic3_gpio_probe ( pdev ,
pdata - > gpio_config ,
pdata - > gpio_config_num ) ;
2008-06-20 13:02:19 +04:00
if ( ret < 0 ) {
2008-06-20 13:11:19 +04:00
dev_err ( asic - > dev , " GPIO probe failed \n " ) ;
2008-06-20 13:02:19 +04:00
goto out_irq ;
2008-02-07 11:14:49 +03:00
}
2009-06-05 20:31:02 +04:00
/* Making a per-device copy is only needed for the
* theoretical case of multiple ASIC3s on one board :
*/
memcpy ( asic - > clocks , asic3_clk_init , sizeof ( asic3_clk_init ) ) ;
2009-06-15 14:10:24 +04:00
asic3_mfd_probe ( pdev , mem ) ;
2008-06-20 13:11:19 +04:00
dev_info ( asic - > dev , " ASIC3 Core driver \n " ) ;
2008-02-07 11:14:49 +03:00
return 0 ;
2008-06-20 13:02:19 +04:00
out_irq :
asic3_irq_remove ( pdev ) ;
out_unmap :
2008-02-07 11:14:49 +03:00
iounmap ( asic - > mapping ) ;
2008-06-20 13:02:19 +04:00
out_free :
2008-02-07 11:14:49 +03:00
kfree ( asic ) ;
return ret ;
}
static int asic3_remove ( struct platform_device * pdev )
{
2008-06-20 13:02:19 +04:00
int ret ;
2008-02-07 11:14:49 +03:00
struct asic3 * asic = platform_get_drvdata ( pdev ) ;
2009-06-15 14:10:24 +04:00
asic3_mfd_remove ( pdev ) ;
2008-06-20 13:02:19 +04:00
ret = asic3_gpio_remove ( pdev ) ;
if ( ret < 0 )
return ret ;
2008-02-07 11:14:49 +03:00
asic3_irq_remove ( pdev ) ;
asic3_write_register ( asic , ASIC3_OFFSET ( CLOCK , SEL ) , 0 ) ;
iounmap ( asic - > mapping ) ;
kfree ( asic ) ;
return 0 ;
}
static void asic3_shutdown ( struct platform_device * pdev )
{
}
static struct platform_driver asic3_device_driver = {
. driver = {
. name = " asic3 " ,
} ,
. remove = __devexit_p ( asic3_remove ) ,
. shutdown = asic3_shutdown ,
} ;
static int __init asic3_init ( void )
{
int retval = 0 ;
2008-06-21 02:51:38 +04:00
retval = platform_driver_probe ( & asic3_device_driver , asic3_probe ) ;
2008-02-07 11:14:49 +03:00
return retval ;
}
subsys_initcall ( asic3_init ) ;