2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / common / locomo . c
*
* Sharp LoCoMo 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 .
*
* This file contains all generic LoCoMo support .
*
* All initialization functions provided here are intended to be called
* from machine specific code with proper arguments when required .
*
* Based on sa1111 . c
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/ioport.h>
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2005-04-17 02:20:36 +04:00
# include <linux/slab.h>
# include <linux/spinlock.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2005-04-17 02:20:36 +04:00
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2005-04-17 02:20:36 +04:00
# include <asm/irq.h>
# include <asm/mach/irq.h>
# include <asm/hardware/locomo.h>
2010-02-05 05:07:33 +03:00
/* LoCoMo Interrupts */
# define IRQ_LOCOMO_KEY (0)
# define IRQ_LOCOMO_GPIO (1)
# define IRQ_LOCOMO_LT (2)
# define IRQ_LOCOMO_SPI (3)
2005-04-17 02:20:36 +04:00
/* M62332 output channel selection */
# define M62332_EVR_CH 1 /* M62332 volume channel number */
/* 0 : CH.1 , 1 : CH. 2 */
/* DAC send data */
# define M62332_SLAVE_ADDR 0x4e /* Slave address */
# define M62332_W_BIT 0x00 /* W bit (0 only) */
# define M62332_SUB_ADDR 0x00 /* Sub address */
# define M62332_A_BIT 0x00 /* A bit (0 only) */
/* DAC setup and hold times (expressed in us) */
# define DAC_BUS_FREE_TIME 5 /* 4.7 us */
# define DAC_START_SETUP_TIME 5 /* 4.7 us */
# define DAC_STOP_SETUP_TIME 4 /* 4.0 us */
# define DAC_START_HOLD_TIME 5 /* 4.7 us */
# define DAC_SCL_LOW_HOLD_TIME 5 /* 4.7 us */
# define DAC_SCL_HIGH_HOLD_TIME 4 /* 4.0 us */
# define DAC_DATA_SETUP_TIME 1 /* 250 ns */
# define DAC_DATA_HOLD_TIME 1 /* 300 ns */
# define DAC_LOW_SETUP_TIME 1 /* 300 ns */
# define DAC_HIGH_SETUP_TIME 1 /* 1000 ns */
/* the following is the overall data for the locomo chip */
struct locomo {
struct device * dev ;
unsigned long phys ;
unsigned int irq ;
2010-02-05 05:07:33 +03:00
int irq_base ;
2005-04-17 02:20:36 +04:00
spinlock_t lock ;
2006-03-15 18:43:04 +03:00
void __iomem * base ;
2007-07-09 22:39:19 +04:00
# ifdef CONFIG_PM
void * saved_state ;
# endif
2005-04-17 02:20:36 +04:00
} ;
struct locomo_dev_info {
unsigned long offset ;
unsigned long length ;
unsigned int devid ;
unsigned int irq [ 1 ] ;
const char * name ;
} ;
/* All the locomo devices. If offset is non-zero, the mapbase for the
* locomo_dev will be set to the chip base plus offset . If offset is
* zero , then the mapbase for the locomo_dev will be set to zero . An
* offset of zero means the device only uses GPIOs or other helper
* functions inside this file */
static struct locomo_dev_info locomo_devices [ ] = {
{
. devid = LOCOMO_DEVID_KEYBOARD ,
2010-02-05 05:07:33 +03:00
. irq = { IRQ_LOCOMO_KEY } ,
2005-04-17 02:20:36 +04:00
. name = " locomo-keyboard " ,
. offset = LOCOMO_KEYBOARD ,
. length = 16 ,
} ,
{
. devid = LOCOMO_DEVID_FRONTLIGHT ,
. irq = { } ,
. name = " locomo-frontlight " ,
. offset = LOCOMO_FRONTLIGHT ,
. length = 8 ,
} ,
{
. devid = LOCOMO_DEVID_BACKLIGHT ,
. irq = { } ,
. name = " locomo-backlight " ,
. offset = LOCOMO_BACKLIGHT ,
. length = 8 ,
} ,
{
. devid = LOCOMO_DEVID_AUDIO ,
. irq = { } ,
. name = " locomo-audio " ,
. offset = LOCOMO_AUDIO ,
. length = 4 ,
} ,
{
. devid = LOCOMO_DEVID_LED ,
. irq = { } ,
. name = " locomo-led " ,
. offset = LOCOMO_LED ,
. length = 8 ,
} ,
{
. devid = LOCOMO_DEVID_UART ,
. irq = { } ,
. name = " locomo-uart " ,
. offset = 0 ,
. length = 0 ,
} ,
2006-09-26 01:41:47 +04:00
{
. devid = LOCOMO_DEVID_SPI ,
. irq = { } ,
. name = " locomo-spi " ,
. offset = LOCOMO_SPI ,
. length = 0x30 ,
} ,
2005-04-17 02:20:36 +04:00
} ;
2006-11-23 14:41:32 +03:00
static void locomo_handler ( unsigned int irq , struct irq_desc * desc )
2005-04-17 02:20:36 +04:00
{
2011-03-24 15:25:22 +03:00
struct locomo * lchip = irq_get_chip_data ( irq ) ;
2005-04-17 02:20:36 +04:00
int req , i ;
/* Acknowledge the parent IRQ */
2010-11-29 12:19:01 +03:00
desc - > irq_data . chip - > irq_ack ( & desc - > irq_data ) ;
2005-04-17 02:20:36 +04:00
/* check why this interrupt was generated */
2010-02-05 05:07:33 +03:00
req = locomo_readl ( lchip - > base + LOCOMO_ICR ) & 0x0f00 ;
2005-04-17 02:20:36 +04:00
if ( req ) {
/* generate the next interrupt(s) */
2010-02-05 05:07:33 +03:00
irq = lchip - > irq_base ;
2008-10-09 16:36:24 +04:00
for ( i = 0 ; i < = 3 ; i + + , irq + + ) {
2005-04-17 02:20:36 +04:00
if ( req & ( 0x0100 < < i ) ) {
2008-10-09 16:36:24 +04:00
generic_handle_irq ( irq ) ;
2005-04-17 02:20:36 +04:00
}
}
}
}
2010-11-29 12:19:01 +03:00
static void locomo_ack_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
}
2010-11-29 12:19:01 +03:00
static void locomo_mask_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2010-11-29 12:19:01 +03:00
struct locomo * lchip = irq_data_get_irq_chip_data ( d ) ;
2005-04-17 02:20:36 +04:00
unsigned int r ;
2010-02-05 05:07:33 +03:00
r = locomo_readl ( lchip - > base + LOCOMO_ICR ) ;
2010-11-29 12:19:01 +03:00
r & = ~ ( 0x0010 < < ( d - > irq - lchip - > irq_base ) ) ;
2010-02-05 05:07:33 +03:00
locomo_writel ( r , lchip - > base + LOCOMO_ICR ) ;
2005-04-17 02:20:36 +04:00
}
2010-11-29 12:19:01 +03:00
static void locomo_unmask_irq ( struct irq_data * d )
2005-04-17 02:20:36 +04:00
{
2010-11-29 12:19:01 +03:00
struct locomo * lchip = irq_data_get_irq_chip_data ( d ) ;
2005-04-17 02:20:36 +04:00
unsigned int r ;
2010-02-05 05:07:33 +03:00
r = locomo_readl ( lchip - > base + LOCOMO_ICR ) ;
2010-11-29 12:19:01 +03:00
r | = ( 0x0010 < < ( d - > irq - lchip - > irq_base ) ) ;
2010-02-05 05:07:33 +03:00
locomo_writel ( r , lchip - > base + LOCOMO_ICR ) ;
2005-04-17 02:20:36 +04:00
}
2006-08-02 01:26:25 +04:00
static struct irq_chip locomo_chip = {
2010-11-29 12:19:01 +03:00
. name = " LOCOMO " ,
. irq_ack = locomo_ack_irq ,
. irq_mask = locomo_mask_irq ,
. irq_unmask = locomo_unmask_irq ,
2005-04-17 02:20:36 +04:00
} ;
static void locomo_setup_irq ( struct locomo * lchip )
{
2010-02-05 05:07:33 +03:00
int irq = lchip - > irq_base ;
2005-04-17 02:20:36 +04:00
/*
* Install handler for IRQ_LOCOMO_HW .
*/
2011-03-24 15:25:22 +03:00
irq_set_irq_type ( lchip - > irq , IRQ_TYPE_EDGE_FALLING ) ;
irq_set_chip_data ( lchip - > irq , lchip ) ;
irq_set_chained_handler ( lchip - > irq , locomo_handler ) ;
2005-04-17 02:20:36 +04:00
2010-02-05 05:07:33 +03:00
/* Install handlers for IRQ_LOCOMO_* */
for ( ; irq < = lchip - > irq_base + 3 ; irq + + ) {
2011-03-24 15:35:09 +03:00
irq_set_chip_and_handler ( irq , & locomo_chip , handle_level_irq ) ;
2011-03-24 15:29:39 +03:00
irq_set_chip_data ( irq , lchip ) ;
2005-04-17 02:20:36 +04:00
set_irq_flags ( irq , IRQF_VALID | IRQF_PROBE ) ;
}
}
static void locomo_dev_release ( struct device * _dev )
{
struct locomo_dev * dev = LOCOMO_DEV ( _dev ) ;
kfree ( dev ) ;
}
static int
locomo_init_one_child ( struct locomo * lchip , struct locomo_dev_info * info )
{
struct locomo_dev * dev ;
int ret ;
2006-03-20 22:46:41 +03:00
dev = kzalloc ( sizeof ( struct locomo_dev ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! dev ) {
ret = - ENOMEM ;
goto out ;
}
/*
* If the parent device has a DMA mask associated with it ,
* propagate it down to the children .
*/
if ( lchip - > dev - > dma_mask ) {
dev - > dma_mask = * lchip - > dev - > dma_mask ;
dev - > dev . dma_mask = & dev - > dma_mask ;
}
2008-05-30 19:42:11 +04:00
dev_set_name ( & dev - > dev , " %s " , info - > name ) ;
2005-04-17 02:20:36 +04:00
dev - > devid = info - > devid ;
dev - > dev . parent = lchip - > dev ;
dev - > dev . bus = & locomo_bus_type ;
dev - > dev . release = locomo_dev_release ;
dev - > dev . coherent_dma_mask = lchip - > dev - > coherent_dma_mask ;
if ( info - > offset )
dev - > mapbase = lchip - > base + info - > offset ;
else
dev - > mapbase = 0 ;
dev - > length = info - > length ;
2010-02-05 05:07:33 +03:00
dev - > irq [ 0 ] = ( lchip - > irq_base = = NO_IRQ ) ?
NO_IRQ : lchip - > irq_base + info - > irq [ 0 ] ;
2005-04-17 02:20:36 +04:00
ret = device_register ( & dev - > dev ) ;
if ( ret ) {
out :
kfree ( dev ) ;
}
return ret ;
}
2005-09-08 17:41:54 +04:00
# ifdef CONFIG_PM
struct locomo_save_data {
u16 LCM_GPO ;
u16 LCM_SPICT ;
u16 LCM_GPE ;
u16 LCM_ASD ;
u16 LCM_SPIMD ;
} ;
2005-11-10 01:32:44 +03:00
static int locomo_suspend ( struct platform_device * dev , pm_message_t state )
2005-09-08 17:41:54 +04:00
{
2005-11-10 01:32:44 +03:00
struct locomo * lchip = platform_get_drvdata ( dev ) ;
2005-09-08 17:41:54 +04:00
struct locomo_save_data * save ;
unsigned long flags ;
save = kmalloc ( sizeof ( struct locomo_save_data ) , GFP_KERNEL ) ;
if ( ! save )
return - ENOMEM ;
2007-07-09 22:39:19 +04:00
lchip - > saved_state = save ;
2005-09-08 17:41:54 +04:00
spin_lock_irqsave ( & lchip - > lock , flags ) ;
save - > LCM_GPO = locomo_readl ( lchip - > base + LOCOMO_GPO ) ; /* GPIO */
locomo_writel ( 0x00 , lchip - > base + LOCOMO_GPO ) ;
2008-04-29 20:44:54 +04:00
save - > LCM_SPICT = locomo_readl ( lchip - > base + LOCOMO_SPI + LOCOMO_SPICT ) ; /* SPI */
2010-03-22 05:33:58 +03:00
locomo_writel ( 0x40 , lchip - > base + LOCOMO_SPI + LOCOMO_SPICT ) ;
2005-09-08 17:41:54 +04:00
save - > LCM_GPE = locomo_readl ( lchip - > base + LOCOMO_GPE ) ; /* GPIO */
locomo_writel ( 0x00 , lchip - > base + LOCOMO_GPE ) ;
save - > LCM_ASD = locomo_readl ( lchip - > base + LOCOMO_ASD ) ; /* ADSTART */
locomo_writel ( 0x00 , lchip - > base + LOCOMO_ASD ) ;
2008-04-29 20:44:54 +04:00
save - > LCM_SPIMD = locomo_readl ( lchip - > base + LOCOMO_SPI + LOCOMO_SPIMD ) ; /* SPI */
locomo_writel ( 0x3C14 , lchip - > base + LOCOMO_SPI + LOCOMO_SPIMD ) ;
2005-09-08 17:41:54 +04:00
locomo_writel ( 0x00 , lchip - > base + LOCOMO_PAIF ) ;
locomo_writel ( 0x00 , lchip - > base + LOCOMO_DAC ) ;
locomo_writel ( 0x00 , lchip - > base + LOCOMO_BACKLIGHT + LOCOMO_TC ) ;
2008-04-29 20:44:54 +04:00
if ( ( locomo_readl ( lchip - > base + LOCOMO_LED + LOCOMO_LPT0 ) & 0x88 ) & & ( locomo_readl ( lchip - > base + LOCOMO_LED + LOCOMO_LPT1 ) & 0x88 ) )
2005-09-08 17:41:54 +04:00
locomo_writel ( 0x00 , lchip - > base + LOCOMO_C32K ) ; /* CLK32 off */
else
/* 18MHz already enabled, so no wait */
locomo_writel ( 0xc1 , lchip - > base + LOCOMO_C32K ) ; /* CLK32 on */
locomo_writel ( 0x00 , lchip - > base + LOCOMO_TADC ) ; /* 18MHz clock off*/
locomo_writel ( 0x00 , lchip - > base + LOCOMO_AUDIO + LOCOMO_ACC ) ; /* 22MHz/24MHz clock off */
locomo_writel ( 0x00 , lchip - > base + LOCOMO_FRONTLIGHT + LOCOMO_ALS ) ; /* FL */
spin_unlock_irqrestore ( & lchip - > lock , flags ) ;
return 0 ;
}
2005-11-10 01:32:44 +03:00
static int locomo_resume ( struct platform_device * dev )
2005-09-08 17:41:54 +04:00
{
2005-11-10 01:32:44 +03:00
struct locomo * lchip = platform_get_drvdata ( dev ) ;
2005-09-08 17:41:54 +04:00
struct locomo_save_data * save ;
unsigned long r ;
unsigned long flags ;
2007-07-09 22:39:19 +04:00
save = lchip - > saved_state ;
2005-09-08 17:41:54 +04:00
if ( ! save )
return 0 ;
spin_lock_irqsave ( & lchip - > lock , flags ) ;
locomo_writel ( save - > LCM_GPO , lchip - > base + LOCOMO_GPO ) ;
2008-04-29 20:44:54 +04:00
locomo_writel ( save - > LCM_SPICT , lchip - > base + LOCOMO_SPI + LOCOMO_SPICT ) ;
2005-09-08 17:41:54 +04:00
locomo_writel ( save - > LCM_GPE , lchip - > base + LOCOMO_GPE ) ;
locomo_writel ( save - > LCM_ASD , lchip - > base + LOCOMO_ASD ) ;
2008-04-29 20:44:54 +04:00
locomo_writel ( save - > LCM_SPIMD , lchip - > base + LOCOMO_SPI + LOCOMO_SPIMD ) ;
2005-09-08 17:41:54 +04:00
locomo_writel ( 0x00 , lchip - > base + LOCOMO_C32K ) ;
locomo_writel ( 0x90 , lchip - > base + LOCOMO_TADC ) ;
locomo_writel ( 0 , lchip - > base + LOCOMO_KEYBOARD + LOCOMO_KSC ) ;
r = locomo_readl ( lchip - > base + LOCOMO_KEYBOARD + LOCOMO_KIC ) ;
r & = 0xFEFF ;
locomo_writel ( r , lchip - > base + LOCOMO_KEYBOARD + LOCOMO_KIC ) ;
locomo_writel ( 0x1 , lchip - > base + LOCOMO_KEYBOARD + LOCOMO_KCMD ) ;
spin_unlock_irqrestore ( & lchip - > lock , flags ) ;
2007-07-09 22:39:19 +04:00
lchip - > saved_state = NULL ;
2005-09-08 17:41:54 +04:00
kfree ( save ) ;
return 0 ;
}
# endif
2006-03-15 19:03:03 +03:00
2005-04-17 02:20:36 +04:00
/**
* locomo_probe - probe for a single LoCoMo chip .
* @ phys_addr : physical address of device .
*
* Probe for a LoCoMo chip . This must be called
* before any other locomo - specific code .
*
* Returns :
* % - ENODEV device not found .
* % - EBUSY physical address already marked in - use .
* % 0 successful .
*/
static int
__locomo_probe ( struct device * me , struct resource * mem , int irq )
{
2010-02-05 05:07:33 +03:00
struct locomo_platform_data * pdata = me - > platform_data ;
2005-04-17 02:20:36 +04:00
struct locomo * lchip ;
unsigned long r ;
int i , ret = - ENODEV ;
2006-03-20 22:46:41 +03:00
lchip = kzalloc ( sizeof ( struct locomo ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! lchip )
return - ENOMEM ;
spin_lock_init ( & lchip - > lock ) ;
lchip - > dev = me ;
dev_set_drvdata ( lchip - > dev , lchip ) ;
lchip - > phys = mem - > start ;
lchip - > irq = irq ;
2010-02-05 05:07:33 +03:00
lchip - > irq_base = ( pdata ) ? pdata - > irq_base : NO_IRQ ;
2005-04-17 02:20:36 +04:00
/*
* Map the whole region . This also maps the
* registers for our children .
*/
lchip - > base = ioremap ( mem - > start , PAGE_SIZE ) ;
if ( ! lchip - > base ) {
ret = - ENOMEM ;
goto out ;
}
/* locomo initialize */
locomo_writel ( 0 , lchip - > base + LOCOMO_ICR ) ;
/* KEYBOARD */
locomo_writel ( 0 , lchip - > base + LOCOMO_KEYBOARD + LOCOMO_KIC ) ;
/* GPIO */
locomo_writel ( 0 , lchip - > base + LOCOMO_GPO ) ;
2008-04-29 20:44:54 +04:00
locomo_writel ( ( LOCOMO_GPIO ( 1 ) | LOCOMO_GPIO ( 2 ) | LOCOMO_GPIO ( 13 ) | LOCOMO_GPIO ( 14 ) )
2005-04-17 02:20:36 +04:00
, lchip - > base + LOCOMO_GPE ) ;
2008-04-29 20:44:54 +04:00
locomo_writel ( ( LOCOMO_GPIO ( 1 ) | LOCOMO_GPIO ( 2 ) | LOCOMO_GPIO ( 13 ) | LOCOMO_GPIO ( 14 ) )
2005-04-17 02:20:36 +04:00
, lchip - > base + LOCOMO_GPD ) ;
locomo_writel ( 0 , lchip - > base + LOCOMO_GIE ) ;
2006-06-26 11:26:27 +04:00
/* Frontlight */
2005-04-17 02:20:36 +04:00
locomo_writel ( 0 , lchip - > base + LOCOMO_FRONTLIGHT + LOCOMO_ALS ) ;
locomo_writel ( 0 , lchip - > base + LOCOMO_FRONTLIGHT + LOCOMO_ALD ) ;
2006-03-15 19:03:03 +03:00
2005-04-17 02:20:36 +04:00
/* Longtime timer */
locomo_writel ( 0 , lchip - > base + LOCOMO_LTINT ) ;
/* SPI */
2010-03-22 05:33:58 +03:00
locomo_writel ( 0 , lchip - > base + LOCOMO_SPI + LOCOMO_SPIIE ) ;
2005-04-17 02:20:36 +04:00
locomo_writel ( 6 + 8 + 320 + 30 - 10 , lchip - > base + LOCOMO_ASD ) ;
r = locomo_readl ( lchip - > base + LOCOMO_ASD ) ;
r | = 0x8000 ;
locomo_writel ( r , lchip - > base + LOCOMO_ASD ) ;
locomo_writel ( 6 + 8 + 320 + 30 - 10 - 128 + 4 , lchip - > base + LOCOMO_HSD ) ;
r = locomo_readl ( lchip - > base + LOCOMO_HSD ) ;
r | = 0x8000 ;
locomo_writel ( r , lchip - > base + LOCOMO_HSD ) ;
locomo_writel ( 128 / 8 , lchip - > base + LOCOMO_HSC ) ;
/* XON */
locomo_writel ( 0x80 , lchip - > base + LOCOMO_TADC ) ;
udelay ( 1000 ) ;
/* CLK9MEN */
r = locomo_readl ( lchip - > base + LOCOMO_TADC ) ;
r | = 0x10 ;
locomo_writel ( r , lchip - > base + LOCOMO_TADC ) ;
udelay ( 100 ) ;
/* init DAC */
r = locomo_readl ( lchip - > base + LOCOMO_DAC ) ;
r | = LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB ;
locomo_writel ( r , lchip - > base + LOCOMO_DAC ) ;
r = locomo_readl ( lchip - > base + LOCOMO_VER ) ;
printk ( KERN_INFO " LoCoMo Chip: %lu%lu \n " , ( r > > 8 ) , ( r & 0xff ) ) ;
/*
* The interrupt controller must be initialised before any
* other device to ensure that the interrupts are available .
*/
2010-02-05 05:07:33 +03:00
if ( lchip - > irq ! = NO_IRQ & & lchip - > irq_base ! = NO_IRQ )
2005-04-17 02:20:36 +04:00
locomo_setup_irq ( lchip ) ;
for ( i = 0 ; i < ARRAY_SIZE ( locomo_devices ) ; i + + )
locomo_init_one_child ( lchip , & locomo_devices [ i ] ) ;
return 0 ;
out :
kfree ( lchip ) ;
return ret ;
}
2005-09-04 14:33:12 +04:00
static int locomo_remove_child ( struct device * dev , void * data )
2005-04-17 02:20:36 +04:00
{
2005-09-04 14:33:12 +04:00
device_unregister ( dev ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2005-09-04 14:33:12 +04:00
static void __locomo_remove ( struct locomo * lchip )
{
device_for_each_child ( lchip - > dev , NULL , locomo_remove_child ) ;
2005-04-17 02:20:36 +04:00
if ( lchip - > irq ! = NO_IRQ ) {
2011-03-24 15:25:22 +03:00
irq_set_chained_handler ( lchip - > irq , NULL ) ;
irq_set_handler_data ( lchip - > irq , NULL ) ;
2005-04-17 02:20:36 +04:00
}
iounmap ( lchip - > base ) ;
kfree ( lchip ) ;
}
2005-11-10 01:32:44 +03:00
static int locomo_probe ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
struct resource * mem ;
int irq ;
2005-11-10 01:32:44 +03:00
mem = platform_get_resource ( dev , IORESOURCE_MEM , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( ! mem )
return - EINVAL ;
2005-11-10 01:32:44 +03:00
irq = platform_get_irq ( dev , 0 ) ;
2006-01-19 20:56:29 +03:00
if ( irq < 0 )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
2005-11-10 01:32:44 +03:00
return __locomo_probe ( & dev - > dev , mem , irq ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-10 01:32:44 +03:00
static int locomo_remove ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
2005-11-12 23:25:25 +03:00
struct locomo * lchip = platform_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
if ( lchip ) {
__locomo_remove ( lchip ) ;
2005-11-10 01:32:44 +03:00
platform_set_drvdata ( dev , NULL ) ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
/*
* Not sure if this should be on the system bus or not yet .
* We really want some way to register a system device at
* the per - machine level , and then have this driver pick
* up the registered devices .
*/
2005-11-10 01:32:44 +03:00
static struct platform_driver locomo_device_driver = {
2005-04-17 02:20:36 +04:00
. probe = locomo_probe ,
. remove = locomo_remove ,
2005-09-08 17:41:54 +04:00
# ifdef CONFIG_PM
. suspend = locomo_suspend ,
. resume = locomo_resume ,
# endif
2005-11-10 01:32:44 +03:00
. driver = {
. name = " locomo " ,
} ,
2005-04-17 02:20:36 +04:00
} ;
/*
* Get the parent device driver ( us ) structure
* from a child function device
*/
static inline struct locomo * locomo_chip_driver ( struct locomo_dev * ldev )
{
return ( struct locomo * ) dev_get_drvdata ( ldev - > dev . parent ) ;
}
2006-09-25 23:11:48 +04:00
void locomo_gpio_set_dir ( struct device * dev , unsigned int bits , unsigned int dir )
2005-04-17 02:20:36 +04:00
{
2006-09-25 23:11:48 +04:00
struct locomo * lchip = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
unsigned int r ;
2006-09-25 23:11:48 +04:00
if ( ! lchip )
return ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & lchip - > lock , flags ) ;
r = locomo_readl ( lchip - > base + LOCOMO_GPD ) ;
2008-04-29 20:44:54 +04:00
if ( dir )
r | = bits ;
else
r & = ~ bits ;
2005-04-17 02:20:36 +04:00
locomo_writel ( r , lchip - > base + LOCOMO_GPD ) ;
r = locomo_readl ( lchip - > base + LOCOMO_GPE ) ;
if ( dir )
r | = bits ;
else
r & = ~ bits ;
locomo_writel ( r , lchip - > base + LOCOMO_GPE ) ;
spin_unlock_irqrestore ( & lchip - > lock , flags ) ;
}
2009-09-18 23:40:27 +04:00
EXPORT_SYMBOL ( locomo_gpio_set_dir ) ;
2005-04-17 02:20:36 +04:00
2006-09-25 23:11:48 +04:00
int locomo_gpio_read_level ( struct device * dev , unsigned int bits )
2005-04-17 02:20:36 +04:00
{
2006-09-25 23:11:48 +04:00
struct locomo * lchip = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
unsigned int ret ;
2006-09-25 23:11:48 +04:00
if ( ! lchip )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & lchip - > lock , flags ) ;
ret = locomo_readl ( lchip - > base + LOCOMO_GPL ) ;
spin_unlock_irqrestore ( & lchip - > lock , flags ) ;
ret & = bits ;
return ret ;
}
2009-09-18 23:40:27 +04:00
EXPORT_SYMBOL ( locomo_gpio_read_level ) ;
2005-04-17 02:20:36 +04:00
2006-09-25 23:11:48 +04:00
int locomo_gpio_read_output ( struct device * dev , unsigned int bits )
2005-04-17 02:20:36 +04:00
{
2006-09-25 23:11:48 +04:00
struct locomo * lchip = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
unsigned int ret ;
2006-09-25 23:11:48 +04:00
if ( ! lchip )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & lchip - > lock , flags ) ;
ret = locomo_readl ( lchip - > base + LOCOMO_GPO ) ;
spin_unlock_irqrestore ( & lchip - > lock , flags ) ;
ret & = bits ;
return ret ;
}
2009-09-18 23:40:27 +04:00
EXPORT_SYMBOL ( locomo_gpio_read_output ) ;
2005-04-17 02:20:36 +04:00
2006-09-25 23:11:48 +04:00
void locomo_gpio_write ( struct device * dev , unsigned int bits , unsigned int set )
2005-04-17 02:20:36 +04:00
{
2006-09-25 23:11:48 +04:00
struct locomo * lchip = dev_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
unsigned int r ;
2006-09-25 23:11:48 +04:00
if ( ! lchip )
return ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & lchip - > lock , flags ) ;
r = locomo_readl ( lchip - > base + LOCOMO_GPO ) ;
if ( set )
r | = bits ;
else
r & = ~ bits ;
locomo_writel ( r , lchip - > base + LOCOMO_GPO ) ;
spin_unlock_irqrestore ( & lchip - > lock , flags ) ;
}
2009-09-18 23:40:27 +04:00
EXPORT_SYMBOL ( locomo_gpio_write ) ;
2005-04-17 02:20:36 +04:00
static void locomo_m62332_sendbit ( void * mapbase , int bit )
{
unsigned int r ;
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r & = ~ ( LOCOMO_DAC_SCLOEB ) ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_LOW_SETUP_TIME ) ; /* 300 nsec */
udelay ( DAC_DATA_HOLD_TIME ) ; /* 300 nsec */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r & = ~ ( LOCOMO_DAC_SCLOEB ) ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_LOW_SETUP_TIME ) ; /* 300 nsec */
udelay ( DAC_SCL_LOW_HOLD_TIME ) ; /* 4.7 usec */
if ( bit & 1 ) {
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r | = LOCOMO_DAC_SDAOEB ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_HIGH_SETUP_TIME ) ; /* 1000 nsec */
} else {
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r & = ~ ( LOCOMO_DAC_SDAOEB ) ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_LOW_SETUP_TIME ) ; /* 300 nsec */
}
udelay ( DAC_DATA_SETUP_TIME ) ; /* 250 nsec */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r | = LOCOMO_DAC_SCLOEB ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_HIGH_SETUP_TIME ) ; /* 1000 nsec */
udelay ( DAC_SCL_HIGH_HOLD_TIME ) ; /* 4.0 usec */
}
void locomo_m62332_senddata ( struct locomo_dev * ldev , unsigned int dac_data , int channel )
{
struct locomo * lchip = locomo_chip_driver ( ldev ) ;
int i ;
unsigned char data ;
unsigned int r ;
void * mapbase = lchip - > base ;
unsigned long flags ;
spin_lock_irqsave ( & lchip - > lock , flags ) ;
/* Start */
udelay ( DAC_BUS_FREE_TIME ) ; /* 5.0 usec */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r | = LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_HIGH_SETUP_TIME ) ; /* 1000 nsec */
udelay ( DAC_SCL_HIGH_HOLD_TIME ) ; /* 4.0 usec */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r & = ~ ( LOCOMO_DAC_SDAOEB ) ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_START_HOLD_TIME ) ; /* 5.0 usec */
udelay ( DAC_DATA_HOLD_TIME ) ; /* 300 nsec */
/* Send slave address and W bit (LSB is W bit) */
data = ( M62332_SLAVE_ADDR < < 1 ) | M62332_W_BIT ;
for ( i = 1 ; i < = 8 ; i + + ) {
locomo_m62332_sendbit ( mapbase , data > > ( 8 - i ) ) ;
}
/* Check A bit */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r & = ~ ( LOCOMO_DAC_SCLOEB ) ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_LOW_SETUP_TIME ) ; /* 300 nsec */
udelay ( DAC_SCL_LOW_HOLD_TIME ) ; /* 4.7 usec */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r & = ~ ( LOCOMO_DAC_SDAOEB ) ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_LOW_SETUP_TIME ) ; /* 300 nsec */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r | = LOCOMO_DAC_SCLOEB ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_HIGH_SETUP_TIME ) ; /* 1000 nsec */
udelay ( DAC_SCL_HIGH_HOLD_TIME ) ; /* 4.7 usec */
if ( locomo_readl ( mapbase + LOCOMO_DAC ) & LOCOMO_DAC_SDAOEB ) { /* High is error */
printk ( KERN_WARNING " locomo: m62332_senddata Error 1 \n " ) ;
2010-03-22 06:09:20 +03:00
goto out ;
2005-04-17 02:20:36 +04:00
}
/* Send Sub address (LSB is channel select) */
/* channel = 0 : ch1 select */
/* = 1 : ch2 select */
data = M62332_SUB_ADDR + channel ;
for ( i = 1 ; i < = 8 ; i + + ) {
locomo_m62332_sendbit ( mapbase , data > > ( 8 - i ) ) ;
}
/* Check A bit */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r & = ~ ( LOCOMO_DAC_SCLOEB ) ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_LOW_SETUP_TIME ) ; /* 300 nsec */
udelay ( DAC_SCL_LOW_HOLD_TIME ) ; /* 4.7 usec */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r & = ~ ( LOCOMO_DAC_SDAOEB ) ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_LOW_SETUP_TIME ) ; /* 300 nsec */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r | = LOCOMO_DAC_SCLOEB ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_HIGH_SETUP_TIME ) ; /* 1000 nsec */
udelay ( DAC_SCL_HIGH_HOLD_TIME ) ; /* 4.7 usec */
if ( locomo_readl ( mapbase + LOCOMO_DAC ) & LOCOMO_DAC_SDAOEB ) { /* High is error */
printk ( KERN_WARNING " locomo: m62332_senddata Error 2 \n " ) ;
2010-03-22 06:09:20 +03:00
goto out ;
2005-04-17 02:20:36 +04:00
}
/* Send DAC data */
for ( i = 1 ; i < = 8 ; i + + ) {
locomo_m62332_sendbit ( mapbase , dac_data > > ( 8 - i ) ) ;
}
/* Check A bit */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r & = ~ ( LOCOMO_DAC_SCLOEB ) ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_LOW_SETUP_TIME ) ; /* 300 nsec */
udelay ( DAC_SCL_LOW_HOLD_TIME ) ; /* 4.7 usec */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r & = ~ ( LOCOMO_DAC_SDAOEB ) ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_LOW_SETUP_TIME ) ; /* 300 nsec */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r | = LOCOMO_DAC_SCLOEB ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_HIGH_SETUP_TIME ) ; /* 1000 nsec */
udelay ( DAC_SCL_HIGH_HOLD_TIME ) ; /* 4.7 usec */
if ( locomo_readl ( mapbase + LOCOMO_DAC ) & LOCOMO_DAC_SDAOEB ) { /* High is error */
printk ( KERN_WARNING " locomo: m62332_senddata Error 3 \n " ) ;
}
2010-03-22 06:09:20 +03:00
out :
2005-04-17 02:20:36 +04:00
/* stop */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r & = ~ ( LOCOMO_DAC_SCLOEB ) ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_LOW_SETUP_TIME ) ; /* 300 nsec */
udelay ( DAC_SCL_LOW_HOLD_TIME ) ; /* 4.7 usec */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r | = LOCOMO_DAC_SCLOEB ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_HIGH_SETUP_TIME ) ; /* 1000 nsec */
udelay ( DAC_SCL_HIGH_HOLD_TIME ) ; /* 4 usec */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r | = LOCOMO_DAC_SDAOEB ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_HIGH_SETUP_TIME ) ; /* 1000 nsec */
udelay ( DAC_SCL_HIGH_HOLD_TIME ) ; /* 4 usec */
r = locomo_readl ( mapbase + LOCOMO_DAC ) ;
r | = LOCOMO_DAC_SCLOEB | LOCOMO_DAC_SDAOEB ;
locomo_writel ( r , mapbase + LOCOMO_DAC ) ;
udelay ( DAC_LOW_SETUP_TIME ) ; /* 1000 nsec */
udelay ( DAC_SCL_LOW_HOLD_TIME ) ; /* 4.7 usec */
spin_unlock_irqrestore ( & lchip - > lock , flags ) ;
}
2009-09-18 23:40:27 +04:00
EXPORT_SYMBOL ( locomo_m62332_senddata ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:26:27 +04:00
/*
* Frontlight control
*/
void locomo_frontlight_set ( struct locomo_dev * dev , int duty , int vr , int bpwf )
{
unsigned long flags ;
struct locomo * lchip = locomo_chip_driver ( dev ) ;
if ( vr )
2006-09-25 23:11:48 +04:00
locomo_gpio_write ( dev - > dev . parent , LOCOMO_GPIO_FL_VR , 1 ) ;
2006-06-26 11:26:27 +04:00
else
2006-09-25 23:11:48 +04:00
locomo_gpio_write ( dev - > dev . parent , LOCOMO_GPIO_FL_VR , 0 ) ;
2006-06-26 11:26:27 +04:00
spin_lock_irqsave ( & lchip - > lock , flags ) ;
locomo_writel ( bpwf , lchip - > base + LOCOMO_FRONTLIGHT + LOCOMO_ALS ) ;
udelay ( 100 ) ;
locomo_writel ( duty , lchip - > base + LOCOMO_FRONTLIGHT + LOCOMO_ALD ) ;
locomo_writel ( bpwf | LOCOMO_ALC_EN , lchip - > base + LOCOMO_FRONTLIGHT + LOCOMO_ALS ) ;
spin_unlock_irqrestore ( & lchip - > lock , flags ) ;
}
2008-12-01 06:41:19 +03:00
EXPORT_SYMBOL ( locomo_frontlight_set ) ;
2006-06-26 11:26:27 +04:00
2005-04-17 02:20:36 +04:00
/*
* LoCoMo " Register Access Bus. "
*
* We model this as a regular bus type , and hang devices directly
* off this .
*/
static int locomo_match ( struct device * _dev , struct device_driver * _drv )
{
struct locomo_dev * dev = LOCOMO_DEV ( _dev ) ;
struct locomo_driver * drv = LOCOMO_DRV ( _drv ) ;
return dev - > devid = = drv - > devid ;
}
static int locomo_bus_suspend ( struct device * dev , pm_message_t state )
{
struct locomo_dev * ldev = LOCOMO_DEV ( dev ) ;
struct locomo_driver * drv = LOCOMO_DRV ( dev - > driver ) ;
int ret = 0 ;
if ( drv & & drv - > suspend )
ret = drv - > suspend ( ldev , state ) ;
return ret ;
}
static int locomo_bus_resume ( struct device * dev )
{
struct locomo_dev * ldev = LOCOMO_DEV ( dev ) ;
struct locomo_driver * drv = LOCOMO_DRV ( dev - > driver ) ;
int ret = 0 ;
if ( drv & & drv - > resume )
ret = drv - > resume ( ldev ) ;
return ret ;
}
static int locomo_bus_probe ( struct device * dev )
{
struct locomo_dev * ldev = LOCOMO_DEV ( dev ) ;
struct locomo_driver * drv = LOCOMO_DRV ( dev - > driver ) ;
int ret = - ENODEV ;
if ( drv - > probe )
ret = drv - > probe ( ldev ) ;
return ret ;
}
static int locomo_bus_remove ( struct device * dev )
{
struct locomo_dev * ldev = LOCOMO_DEV ( dev ) ;
struct locomo_driver * drv = LOCOMO_DRV ( dev - > driver ) ;
int ret = 0 ;
if ( drv - > remove )
ret = drv - > remove ( ldev ) ;
return ret ;
}
struct bus_type locomo_bus_type = {
. name = " locomo-bus " ,
. match = locomo_match ,
2006-01-05 17:33:04 +03:00
. probe = locomo_bus_probe ,
. remove = locomo_bus_remove ,
2005-04-17 02:20:36 +04:00
. suspend = locomo_bus_suspend ,
. resume = locomo_bus_resume ,
} ;
int locomo_driver_register ( struct locomo_driver * driver )
{
driver - > drv . bus = & locomo_bus_type ;
return driver_register ( & driver - > drv ) ;
}
2009-09-18 23:40:27 +04:00
EXPORT_SYMBOL ( locomo_driver_register ) ;
2005-04-17 02:20:36 +04:00
void locomo_driver_unregister ( struct locomo_driver * driver )
{
driver_unregister ( & driver - > drv ) ;
}
2009-09-18 23:40:27 +04:00
EXPORT_SYMBOL ( locomo_driver_unregister ) ;
2005-04-17 02:20:36 +04:00
static int __init locomo_init ( void )
{
int ret = bus_register ( & locomo_bus_type ) ;
if ( ret = = 0 )
2005-11-10 01:32:44 +03:00
platform_driver_register ( & locomo_device_driver ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
static void __exit locomo_exit ( void )
{
2005-11-10 01:32:44 +03:00
platform_driver_unregister ( & locomo_device_driver ) ;
2005-04-17 02:20:36 +04:00
bus_unregister ( & locomo_bus_type ) ;
}
module_init ( locomo_init ) ;
module_exit ( locomo_exit ) ;
MODULE_DESCRIPTION ( " Sharp LoCoMo core driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " John Lenz <lenz@cs.wisc.edu> " ) ;