EfikaMX Smartbook Kernel support, initial

This commit is contained in:
Matt Sealey
2010-09-03 14:36:46 -05:00
parent 8674ba3c7f
commit b26a7674e0
36 changed files with 9550 additions and 106 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -47,6 +47,13 @@ config MACH_MX51_EFIKAMX
help
Include support for Genesi Efika MX platforms.
config MACH_MX51_EFIKASB
bool "Support Genesi Efika MX Smartbook"
select ARCH_MX51
help
Include support for Genesi Efika MX Smartbook. This includes specific
configurations for the board and its peripherals.
config MACH_MX53_EVK
bool "Support MX53 EVK platform"
select ARCH_MX53

View File

@@ -22,3 +22,6 @@ obj-$(CONFIG_MACH_MX51_EFIKAMX) += mx51_efikamx.o mx51_efikamx_io.o mx51_efikamx
mx51_efikamx_spi.o mx51_efikamx_nor.o mx51_efikamx_pmic.o \
mx51_efikamx_mmc.o mx51_efikamx_ata.o mx51_efikamx_powerkey.o \
mx51_efikamx_audio.o mx51_efikamx_display.o early_setup.o
obj-$(CONFIG_MACH_MX51_EFIKASB) += mx51_efikasb.o mx51_efikasb_gpio.o mx51_efikasb_pmic.o \
mx51_efikasb_idle_timer.o mx51_efikasb_input.o \
mx51_efikasb_lid.o mx51_efikasb_powerkey.o

View File

@@ -0,0 +1,833 @@
/*
* Copyright 2009-2010 Pegatron Corporation. All Rights Reserved.
* Copyright 2009-2010 Genesi USA, Inc. All Rights Reserved.
*
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>
#include <linux/spi/spi.h>
#include <linux/i2c.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/spi/flash.h>
#include <linux/regulator/consumer.h>
#include <linux/pmic_external.h>
#include <linux/pmic_status.h>
#include <linux/ipu.h>
#include <linux/mxcfb.h>
#include <linux/pwm_backlight.h>
#include <linux/pci_ids.h>
#include <linux/suspend.h>
#include <linux/ata.h>
#include <mach/common.h>
#include <mach/hardware.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/time.h>
#include <asm/mach/keypad.h>
#include <mach/gpio.h>
#include <mach/mmc.h>
#include <mach/mxc_dvfs.h>
#include <mach/mxc_edid.h>
#include "devices.h"
#include "mx51_efikasb.h"
#include "iomux.h"
#include "mx51_pins.h"
#include "crm_regs.h"
#include "usb.h"
#include <mach/clock.h>
/*!
* @file mach-mx51/mx51_efikasb.c
*
* @brief This file contains the board specific initialization routines.
*
* @ingroup MSL_MX51
*/
extern void __init mx51_efikasb_io_init(void);
extern struct cpu_wp *(*get_cpu_wp)(int *wp);
extern void (*set_num_cpu_wp)(int num);
static int num_cpu_wp = 3;
static int lvds_en_dir = 0;
extern int mxc_get_battery_insertion_status(void);
extern int mxc_get_ac_adapter_insertion_status(void);
extern int mxc_get_batt_low_status(void);
/* extern void mxc_turn_on_batt_low_led(int); */
extern int mxc_get_memory_id(void);
extern unsigned int mxc_get_pcb_id(void);
/* working point(wp): 0 - 800MHz; 1 - 166.25MHz; */
static struct cpu_wp cpu_wp_auto[] = {
{
.pll_rate = 1000000000,
.cpu_rate = 1000000000,
.pdf = 0,
.mfi = 10,
.mfd = 11,
.mfn = 5,
.cpu_podf = 0,
.cpu_voltage = 1175000,},
{
.pll_rate = 800000000,
.cpu_rate = 800000000,
.pdf = 0,
.mfi = 8,
.mfd = 2,
.mfn = 1,
.cpu_podf = 0,
.cpu_voltage = 1100000,},
{
.pll_rate = 800000000,
.cpu_rate = 166250000,
.pdf = 4,
.mfi = 8,
.mfd = 2,
.mfn = 1,
.cpu_podf = 4,
.cpu_voltage = 850000,},
};
struct cpu_wp *mx51_efikasb_get_cpu_wp(int *wp)
{
*wp = num_cpu_wp;
return cpu_wp_auto;
}
void mx51_efikasb_set_num_cpu_wp(int num)
{
num_cpu_wp = num;
return;
}
static struct mxc_w1_config mxc_w1_data = {
.search_rom_accelerator = 1,
};
static struct platform_pwm_backlight_data mxc_pwm_backlight_data = {
.pwm_id = 0,
.max_brightness = 255,
.dft_brightness = 128,
.pwm_period_ns = 78770,
};
extern void mx5_ipu_reset(void); /* eric 20100521 */
static struct mxc_ipu_config mxc_ipu_data = {
.rev = 2,
.reset = mx5_ipu_reset, /* eric 20100521*/
};
extern void mx5_vpu_reset(void);
static struct mxc_vpu_platform_data mxc_vpu_data = {
.reset = mx5_vpu_reset,
};
extern void mx51_efikasb_gpio_spi_chipselect_active(int cspi_mode, int status,
int chipselect);
extern void mx51_efikasb_gpio_spi_chipselect_inactive(int cspi_mode, int status,
int chipselect);
static struct mxc_spi_master mxcspi1_data = {
.maxchipselect = 4,
.spi_version = 23,
.chipselect_active = mx51_efikasb_gpio_spi_chipselect_active,
.chipselect_inactive = mx51_efikasb_gpio_spi_chipselect_inactive,
};
static struct mxc_i2c_platform_data mxci2c_data = {
.i2c_clk = 100000,
};
static struct mxc_srtc_platform_data srtc_data = {
.srtc_sec_mode_addr = 0x83F98840,
};
static struct tve_platform_data tve_data = {
.dac_reg = "VVIDEO",
};
static struct mxc_dvfs_platform_data dvfs_core_data = {
.reg_id = "SW1",
.clk1_id = "cpu_clk",
.clk2_id = "gpc_dvfs_clk",
.gpc_cntr_reg_addr = MXC_GPC_CNTR,
.gpc_vcr_reg_addr = MXC_GPC_VCR,
.ccm_cdcr_reg_addr = MXC_CCM_CDCR,
.ccm_cacrr_reg_addr = MXC_CCM_CACRR,
.ccm_cdhipr_reg_addr = MXC_CCM_CDHIPR,
// .dvfs_thrs_reg_addr = MXC_DVFSTHRS,
//.dvfs_coun_reg_addr = MXC_DVFSCOUN,
//.dvfs_emac_reg_addr = MXC_DVFSEMAC,
//.dvfs_cntr_reg_addr = MXC_DVFSCNTR,
.prediv_mask = 0x1F800,
.prediv_offset = 11,
.prediv_val = 3,
.div3ck_mask = 0xE0000000,
.div3ck_offset = 29,
.div3ck_val = 2,
.emac_val = 0x08,
.upthr_val = 25,
.dnthr_val = 9,
.pncthr_val = 33,
.upcnt_val = 10,
.dncnt_val = 10,
.delay_time = 30,
.num_wp = 3,
};
static struct mxc_dvfsper_data dvfs_per_data = {
.reg_id = "SW2",
.clk_id = "gpc_dvfs_clk",
.gpc_cntr_reg_addr = MXC_GPC_CNTR,
.gpc_vcr_reg_addr = MXC_GPC_VCR,
.gpc_adu = 0x0,
.vai_mask = MXC_DVFSPMCR0_FSVAI_MASK,
.vai_offset = MXC_DVFSPMCR0_FSVAI_OFFSET,
.dvfs_enable_bit = MXC_DVFSPMCR0_DVFEN,
.irq_mask = MXC_DVFSPMCR0_FSVAIM,
.div3_offset = 0,
.div3_mask = 0x7,
.div3_div = 2,
.lp_high = 1200000,
.lp_low = 1200000,
};
static struct mxc_spdif_platform_data mxc_spdif_data = {
.spdif_tx = 1,
.spdif_rx = 0,
.spdif_clk_44100 = 0, /* spdif_ext_clk source for 44.1KHz */
.spdif_clk_48000 = 7, /* audio osc source */
.spdif_clkid = 0,
.spdif_clk = NULL, /* spdif bus clk */
};
#if defined(CONFIG_FB_MXC_SYNC_PANEL) || \
defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE)
static struct resource mxcfb_resources[] = {
[0] = {
.flags = IORESOURCE_MEM,
},
};
static struct mxc_fb_platform_data fb_data[] = {
{
.interface_pix_fmt = IPU_PIX_FMT_RGB24,
.mode_str = "1280x720MR-16@60", /* ron: TBD */
},
{
.interface_pix_fmt = IPU_PIX_FMT_RGB565,
.mode_str = "1024x768M-16@60", /* ron: TBD */
},
};
static void __init mxc_init_fb(void)
{
mxc_fb_devices[1].num_resources = ARRAY_SIZE(mxcfb_resources);
mxc_fb_devices[1].resource = mxcfb_resources;
mxc_register_device(&mxc_fb_devices[1], &fb_data[1]); /* ron: LVDS LCD */
mxc_register_device(&mxc_fb_devices[2], NULL); // Overlay for VPU
}
#endif
static int __initdata enable_ddr2max = { 0 };
static void mxc_power_on_lcd(int on)
{
if(on) {
gpio_set_value(IOMUX_TO_GPIO(LCD_PWRON_PIN), 1); /* LCD Power On */
} else {
gpio_set_value(IOMUX_TO_GPIO(LCD_PWRON_PIN), 0); /* LCD Power Off */
}
}
static void mxc_lvds_enable(int on)
{
if(on == -1) {
gpio_direction_input(IOMUX_TO_GPIO(LCD_LVDS_EN_PIN));
lvds_en_dir = 1;
return;
}
lvds_en_dir = 0;
if(on) {
gpio_set_value(IOMUX_TO_GPIO(LCD_LVDS_EN_PIN), 1); /* LVDS_EN On */
} else {
gpio_set_value(IOMUX_TO_GPIO(LCD_LVDS_EN_PIN), 0); /* LVDS_EN Off */
}
}
static void mxc_turn_on_lcd_backlight(int on)
{
if(on) {
mxc_free_iomux(LCD_BL_PWM_PIN, IOMUX_CONFIG_GPIO);
mxc_request_iomux(LCD_BL_PWM_PIN, IOMUX_CONFIG_ALT1);
msleep(10);
gpio_set_value(IOMUX_TO_GPIO(LCD_BL_PWRON_PIN), 0); /* Backlight Power On */
} else {
gpio_set_value(IOMUX_TO_GPIO(LCD_BL_PWRON_PIN), 1); /* Backlight Power Off */
msleep(10);
mxc_free_iomux(LCD_BL_PWM_PIN, IOMUX_CONFIG_ALT1);
mxc_request_iomux(LCD_BL_PWM_PIN, IOMUX_CONFIG_GPIO);
gpio_direction_output(IOMUX_TO_GPIO(LCD_BL_PWM_PIN), 0);
}
}
static void mxc_reset_lvds(void)
{
gpio_set_value(IOMUX_TO_GPIO(LVDS_RESET_PIN), 0);
msleep(50);
gpio_set_value(IOMUX_TO_GPIO(LVDS_RESET_PIN), 1);
msleep(10);
gpio_set_value(IOMUX_TO_GPIO(LVDS_RESET_PIN), 0);
}
static void mxc_power_on_lvds(int on)
{
if(on)
gpio_set_value(IOMUX_TO_GPIO(LVDS_PWRCTL_PIN), 1);
else
gpio_set_value(IOMUX_TO_GPIO(LVDS_PWRCTL_PIN), 0);
}
static struct mxc_lcd_platform_data lvds_data = {
.core_reg = "VCAM",
.io_reg = "VGEN3",
.analog_reg = "VAUDIO",
.reset = mxc_reset_lvds,
.power_on_lcd = mxc_power_on_lcd,
.turn_on_backlight = mxc_turn_on_lcd_backlight,
.power_on_lvds = mxc_power_on_lvds,
.lvds_enable = mxc_lvds_enable,
};
static struct platform_device mxcbl_device = {
.name = "mxc_efikasb_bl",
};
static struct platform_device mxc_led_device = {
.name = "efikasb_leds",
.id = 1,
};
static struct mxc_battery_platform_data efikasb_batt_data = {
.batt_in_irq = IOMUX_TO_IRQ(BATT_INS_PIN),
.ac_in_irq = IOMUX_TO_IRQ(AC_ADAP_INS_PIN),
.batt_low_irq = IOMUX_TO_IRQ(BATT_LOW_PIN),
.get_batt_in_status = mxc_get_battery_insertion_status,
.get_ac_in_status = mxc_get_ac_adapter_insertion_status,
.get_batt_low_status = mxc_get_batt_low_status,
.set_batt_low_led = NULL/* mxc_turn_on_batt_low_led */,
};
#if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE)
static struct i2c_board_info mxc_i2c1_board_info[] __initdata = {
{
.type = "sgtl5000-i2c",
.addr = 0x0a,
},
{ /* ron: LVDS Controller */
.type = "mtl017",
.addr = 0x3a,
.platform_data = &lvds_data,
},
{
.type = "efikasb-battery",
.addr = 0x0b,
.platform_data = &efikasb_batt_data,
},
};
#endif
#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
static struct mtd_partition mxc_spi_flash_partitions[] = {
{
.name = "redboot",
.offset = 0,
.size = 0x00040000,
.mask_flags = MTD_CAP_ROM,
},
{
.name = "fis-dir",
.offset = MTDPART_OFS_APPEND,
.size = 0x0000F000,
.mask_flags = MTD_CAP_ROM,
},
{
.name = "rb-config",
.offset = MTDPART_OFS_APPEND,
.size = 0x00001000,
.mask_flags = MTD_CAP_ROM,
},
{
.name = "kernel",
.offset = 0x00100000,
.size = MTDPART_SIZ_FULL,
},
};
static struct flash_platform_data mxc_spi_flash_data = {
.name = "mxc_spi_nor",
.parts = mxc_spi_flash_partitions,
.nr_parts = ARRAY_SIZE(mxc_spi_flash_partitions),
/* .type = "sst25vf032b", */
/* ron: also support MXIC MX25L3205D, use jedec_probe to detect */
.type = NULL,
};
static struct spi_board_info mxc_spi_board_info[] __initdata = {
{
.modalias = "sst25vf",
.max_speed_hz = 25000000, /* max spi clock (SCK) speed in HZ */
.bus_num = 1,
.chip_select = 1,
.platform_data = &mxc_spi_flash_data,
},
};
#endif
#if defined(CONFIG_MMC_IMX_ESDHCI) || defined(CONFIG_MMC_IMX_ESDHCI_MODULE)
static int sdhc_write_protect(struct device *dev)
{
unsigned short rc = 0;
if (to_platform_device(dev)->id == 0)
rc = gpio_get_value(IOMUX_TO_GPIO(SDHC1_WP_PIN));
else
rc = gpio_get_value(IOMUX_TO_GPIO(SDHC2_WP_PIN));
return rc;
}
static unsigned int sdhc_get_card_det_status(struct device *dev)
{
int ret;
if (to_platform_device(dev)->id == 0) {
ret = gpio_get_value(IOMUX_TO_GPIO(SDHC1_CD_PIN));
return ret;
} else { /* config the det pin for SDHC2 */
//ron: SDHC2 CD gpio
ret = gpio_get_value(IOMUX_TO_GPIO(SDHC2_CD_PIN));
return ret;
}
}
static struct mxc_mmc_platform_data mmc1_data = {
.ocr_mask = MMC_VDD_31_32,
.caps = MMC_CAP_4_BIT_DATA,
.min_clk = 400000,
.max_clk = 52000000,
.card_inserted_state = 1,
.status = sdhc_get_card_det_status,
.wp_status = sdhc_write_protect,
.clock_mmc = "esdhc_clk",
.power_mmc = NULL,
};
static struct mxc_mmc_platform_data mmc2_data = {
.ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 |
MMC_VDD_31_32,
.caps = MMC_CAP_4_BIT_DATA,
.min_clk = 150000,
.max_clk = 50000000,
.card_inserted_state = 0,
.status = sdhc_get_card_det_status,
.wp_status = sdhc_write_protect,
.clock_mmc = "esdhc_clk",
};
#endif
#if defined(CONFIG_SND_SOC_IMX_3STACK_SGTL5000) \
|| defined(CONFIG_SND_SOC_IMX_3STACK_SGTL5000_MODULE)
static int mxc_sgtl5000_amp_enable(int enable)
{
gpio_set_value(IOMUX_TO_GPIO(AUD_MUTE_PIN), enable ? 1 : 0);
return 0;
}
static int headphone_det_status(void)
{
return gpio_get_value(IOMUX_TO_GPIO(HPJACK_INS_PIN));
}
static struct mxc_audio_platform_data sgtl5000_data = {
.ssi_num = 1,
.src_port = 2,
.ext_port = 3,
.hp_irq = IOMUX_TO_IRQ(HPJACK_INS_PIN),
.hp_status = headphone_det_status,
.amp_enable = mxc_sgtl5000_amp_enable,
.sysclk = 12288000,
};
static struct platform_device mxc_sgtl5000_device = {
.name = "imx-3stack-sgtl5000",
};
#endif
/*!
* Board specific fixup function. It is called by \b setup_arch() in
* setup.c file very early on during kernel starts. It allows the user to
* statically fill in the proper values for the passed-in parameters. None of
* the parameters is used currently.
*
* @param desc pointer to \b struct \b machine_desc
* @param tags pointer to \b struct \b tag
* @param cmdline pointer to the command line
* @param mi pointer to \b struct \b meminfo
*/
static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags,
char **cmdline, struct meminfo *mi)
{
char *str;
int size = SZ_512M - SZ_32M;
struct tag *t;
mxc_set_cpu_type(MXC_CPU_MX51);
get_cpu_wp = mx51_efikasb_get_cpu_wp;
set_num_cpu_wp = mx51_efikasb_set_num_cpu_wp;
for_each_tag(t, tags) {
if (t->hdr.tag != ATAG_CMDLINE)
continue;
str = t->u.cmdline.cmdline;
str = strstr(str, "mem=");
if (str != NULL) {
str += 4;
size = memparse(str, &str);
if (size == 0 || size == SZ_512M)
return;
}
}
for_each_tag(t, tags) {
if (t->hdr.tag != ATAG_MEM)
continue;
t->u.mem.size = size;
#if defined(CONFIG_FB_MXC_SYNC_PANEL) || \
defined(CONFIG_FB_MXC_SYNC_PANEL_MODULE)
mxcfb_resources[0].start = t->u.mem.start + size;
mxcfb_resources[0].end = t->u.mem.start + SZ_512M - 1;
#endif
}
}
#define PWGT1SPIEN (1<<15)
#define PWGT2SPIEN (1<<16)
#define USEROFFSPI (1<<3)
static void mxc_power_off(void)
{
mxc_turn_on_lcd_backlight(0);
msleep(200);
if(lvds_en_dir == 0) {
mxc_lvds_enable(0);
}
mxc_power_on_lvds(0);
msleep(5);
mxc_power_on_lcd(0);
/* We can do power down one of two ways:
Set the power gating
Set USEROFFSPI */
gpio_set_value(IOMUX_TO_GPIO(USB_PHY_RESET_PIN), 0);
msleep(10);
/* Set the power gate bits to power down */
pmic_write_reg(REG_POWER_MISC, (PWGT1SPIEN|PWGT2SPIEN),
(PWGT1SPIEN|PWGT2SPIEN));
mxc_wd_reset();
//robin: CLR_DFF
mxc_request_iomux(SYS_PWROFF_PIN, IOMUX_CONFIG_GPIO);
gpio_direction_output(IOMUX_TO_GPIO(SYS_PWROFF_PIN), 0);
gpio_set_value(IOMUX_TO_GPIO(SYS_PWROFF_PIN), 0);
msleep(10);
gpio_set_value(IOMUX_TO_GPIO(SYS_PWROFF_PIN), 1);
}
static irqreturn_t wwan_wakeup_int(int irq, void *dev_id)
{
if(gpio_get_value(IOMUX_TO_GPIO(WWAN_WAKEUP_PIN)))
set_irq_type(irq, IRQF_TRIGGER_FALLING);
else
set_irq_type(irq, IRQF_TRIGGER_RISING);
pr_info("WWAN wakeup event\n");
return IRQ_HANDLED;
}
static int __init mxc_init_wwan_wakeup(void)
{
int irq, ret;
irq = IOMUX_TO_IRQ(WWAN_WAKEUP_PIN);
if(gpio_get_value(IOMUX_TO_GPIO(WWAN_WAKEUP_PIN)))
set_irq_type(irq, IRQF_TRIGGER_FALLING);
else
set_irq_type(irq, IRQF_TRIGGER_RISING);
ret = request_irq(irq, wwan_wakeup_int, 0, "wwan-wakeup", 0);
if(ret)
pr_info("register WWAN wakeup interrupt failed\n");
else
enable_irq_wake(irq);
return ret;
}
late_initcall(mxc_init_wwan_wakeup);
extern void gpio_ata_active(void);
extern void gpio_ata_inactive(void);
static int ata_init(struct platform_device *pdev)
{
/* Configure the pins */
gpio_ata_active();
return 0;
}
static void ata_exit(void)
{
/* Free the pins */
gpio_ata_inactive();
}
static struct fsl_ata_platform_data ata_data = {
.udma_mask = ATA_UDMA3,
.mwdma_mask = ATA_MWDMA2,
.pio_mask = ATA_PIO4,
.fifo_alarm = MXC_IDE_DMA_WATERMARK / 2,
.max_sg = MXC_IDE_DMA_BD_NR,
.init = ata_init,
.exit = ata_exit,
.core_reg = NULL,
.io_reg = NULL,
};
#if defined(CONFIG_GPS_IOCTRL) || defined(CONFIG_GPS_IOCTRL_MODULE)
static struct mxc_gps_platform_data gps_data = {
.core_reg = "VIOHI",
.analog_reg = "SW4",
};
static struct platform_device mxc_gps_device = {
.name = "gps_ioctrl",
.id = -1,
.dev = {
.platform_data = &gps_data,
},
};
int gpio_gps_access(int para)
{
iomux_pin_name_t pin;
pin = (para & 0x1) ? AGPS_PWRON_PIN : AGPS_RESET_PIN;
if (para & 0x4) /* Read GPIO */
return gpio_get_value(IOMUX_TO_GPIO(pin));
else if (para & 0x2) /* Write GPIO */
gpio_set_value(IOMUX_TO_GPIO(pin), 1);
else
gpio_set_value(IOMUX_TO_GPIO(pin), 0);
return 0;
}
EXPORT_SYMBOL(gpio_gps_access);
#endif
static void __init mxc_board_init(void)
{
struct clk *clk;
// printk(" [VV] mxc_board_init.\n");
mxc_ipu_data.di_clk[0] = clk_get(NULL, "ipu_di0_clk");
mxc_ipu_data.di_clk[1] = clk_get(NULL, "ipu_di1_clk");
#if defined(CONFIG_MMC_IMX_ESDHCI) || defined(CONFIG_MMC_IMX_ESDHCI_MODULE)
/* SD card detect irqs */
mxcsdhc2_device.resource[2].start = IOMUX_TO_IRQ(SDHC2_CD_PIN);
mxcsdhc2_device.resource[2].end = IOMUX_TO_IRQ(SDHC2_CD_PIN);
mxcsdhc1_device.resource[2].start = IOMUX_TO_IRQ(SDHC1_CD_PIN);
mxcsdhc1_device.resource[2].end = IOMUX_TO_IRQ(SDHC1_CD_PIN);
#endif
mxc_cpu_common_init();
mxc_register_gpios();
mx51_efikasb_io_init();
clk = clk_get(&(mxc_fb_devices[0].dev), "axi_b_clk");
clk_set_rate(clk, 133000000);
mxc_register_device(&mxc_dma_device, NULL);
mxc_register_device(&mxc_wdt_device, NULL);
mxc_register_device(&mxcspi1_device, &mxcspi1_data);
#if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE)
mxc_register_device(&mxci2c_devices[1], &mxci2c_data);
#endif
#if defined(CONFIG_MMC_IMX_ESDHCI) || defined(CONFIG_MMC_IMX_ESDHCI_MODULE)
mxc_register_device(&mxcsdhc1_device, &mmc1_data);
mxc_register_device(&mxcsdhc2_device, &mmc2_data);
#endif
mxc_register_device(&mxc_rtc_device, &srtc_data);
mxc_register_device(&mxc_w1_master_device, &mxc_w1_data);
mxc_register_device(&mxc_ipu_device, &mxc_ipu_data);
mxc_register_device(&mxc_tve_device, &tve_data);
mxc_register_device(&mxcvpu_device, &mxc_vpu_data);
mxc_register_device(&gpu_device, NULL);
mxc_register_device(&mxcscc_device, NULL); /* eric 20100521: SCC support */
mxc_register_device(&mx51_lpmode_device, NULL);
mxc_register_device(&busfreq_device, NULL);
mxc_register_device(&sdram_autogating_device, NULL);
mxc_register_device(&mxc_dvfs_core_device, &dvfs_core_data);
mxc_register_device(&mxc_dvfs_per_device, &dvfs_per_data);
mxc_register_device(&mxc_iim_device, NULL);
mxc_register_device(&mxc_pwm1_device, NULL);
mxc_register_device(&mxc_pwm_backlight_device, &mxc_pwm_backlight_data);
mxc_register_device(&mxc_ssi1_device, NULL);
mxc_register_device(&mxc_ssi2_device, NULL);
mxc_register_device(&pata_fsl_device, &ata_data);
mxc_register_device(&mxc_alsa_spdif_device, &mxc_spdif_data);
mxc_register_device(&mxc_fec_device, NULL);
mxc_init_fb();
mxc_register_device(&mxcbl_device, NULL);
mxc_register_device(&mxc_led_device, NULL);
// printk(" [VV] invoking mx51_efikasb_init_mc13892.\n");
mx51_efikasb_init_mc13892();
#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
spi_register_board_info(mxc_spi_board_info,
ARRAY_SIZE(mxc_spi_board_info));
#endif
#if defined(CONFIG_I2C_MXC) || defined(CONFIG_I2C_MXC_MODULE)
i2c_register_board_info(1, mxc_i2c1_board_info,
ARRAY_SIZE(mxc_i2c1_board_info));
#endif
pm_power_off = mxc_power_off;
#if defined(CONFIG_SND_SOC_IMX_3STACK_SGTL5000) \
|| defined(CONFIG_SND_SOC_IMX_3STACK_SGTL5000_MODULE)
if (cpu_is_mx51_rev(CHIP_REV_1_1) == 2) {
sgtl5000_data.sysclk = 26000000;
}
gpio_direction_output(IOMUX_TO_GPIO(AUD_MUTE_PIN), 0);
mxc_register_device(&mxc_sgtl5000_device, &sgtl5000_data);
#endif
mx5_usb_dr_init();
mx5_usbh1_init();
mx51_usbh2_init();
#if defined(CONFIG_GPS_IOCTRL) || defined(CONFIG_GPS_IOCTRL_MODULE)
mxc_register_device(&mxc_gps_device, NULL);
#endif
}
static void __init mx51_efikasb_timer_init(void)
{
struct clk *uart_clk;
/* Change the CPU voltages for TO2*/
if (cpu_is_mx51_rev(CHIP_REV_2_0) <= 1) {
cpu_wp_auto[0].cpu_voltage = 1175000;
cpu_wp_auto[1].cpu_voltage = 1100000;
cpu_wp_auto[2].cpu_voltage = 1000000;
}
mx51_clocks_init(32768, 24000000, 22579200, 24576000);
uart_clk = clk_get(NULL, "uart_clk.0");
early_console_setup(UART1_BASE_ADDR, uart_clk);
}
static struct sys_timer mxc_timer = {
.init = mx51_efikasb_timer_init,
};
/*
* The following uses standard kernel macros define in arch.h in order to
* initialize __mach_desc_MX51_EFIKASB data structure.
*/
/* *INDENT-OFF* */
MACHINE_START(MX51_EFIKASB, "Freescale MX51 Efikasb Board")
/* Maintainer: Genesi, Inc. */
.fixup = fixup_mxc_board,
.map_io = mx5_map_io,
.init_irq = mx5_init_irq,
.init_machine = mxc_board_init,
.timer = &mxc_timer,
MACHINE_END
static int __init ddr2max_setup(char *__unused)
{
enable_ddr2max = 1;
return 1;
}
__setup("ddr2max", ddr2max_setup);
/*
* Workaround to solve the black screen on warm reboot aging test
* This requires to set DDR2 rate to 160MHz before booting kernel
*/
static int __init ddr2_set_max_rate(void)
{
struct clk *tclk;
unsigned long rate;
/*Change the DDR freq to 200MHz*/
if (enable_ddr2max) {
tclk = clk_get(NULL, "ddr_hf_clk");
rate = clk_round_rate(tclk, 200000000);
if (clk_get_rate(tclk) != rate)
clk_set_rate(tclk, rate);
}
return 0;
}
late_initcall(ddr2_set_max_rate);

View File

@@ -0,0 +1,153 @@
/*
* Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#ifndef __ASM_ARCH_MXC_BOARD_MX51_EFIKASB_H__
#define __ASM_ARCH_MXC_BOARD_MX51_EFIKASB_H__
/*!
* @defgroup BRDCFG_MX51 Board Configuration Options
* @ingroup MSL_MX51
*/
/*!
* @file mach-mx51/board-mx51_efikasb.h
*
* @brief This file contains all the board level configuration options.
*
* It currently hold the options defined for Genesi MX51 Efikasb Platform.
*
* @ingroup BRDCFG_MX51
*/
/*
* Include Files
*/
#include <mach/mxc_uart.h>
/*!
* @name MXC UART board level configurations
*/
/*! @{ */
/*!
* Specifies if the Irda transmit path is inverting
*/
#define MXC_IRDA_TX_INV 0
/*!
* Specifies if the Irda receive path is inverting
*/
#define MXC_IRDA_RX_INV 0
/* UART 1 configuration */
/*!
* This define specifies if the UART port is configured to be in DTE or
* DCE mode. There exists a define like this for each UART port. Valid
* values that can be used are \b MODE_DTE or \b MODE_DCE.
*/
#define UART1_MODE MODE_DCE
/*!
* This define specifies if the UART is to be used for IRDA. There exists a
* define like this for each UART port. Valid values that can be used are
* \b IRDA or \b NO_IRDA.
*/
#define UART1_IR NO_IRDA
/*!
* This define is used to enable or disable a particular UART port. If
* disabled, the UART will not be registered in the file system and the user
* will not be able to access it. There exists a define like this for each UART
* port. Specify a value of 1 to enable the UART and 0 to disable it.
*/
#define UART1_ENABLED 1
/*! @} */
/* UART 2 configuration */
#define UART2_MODE MODE_DCE
#define UART2_IR NO_IRDA
#define UART2_ENABLED 1
/* UART 3 configuration */
#define UART3_MODE MODE_DTE
#define UART3_IR NO_IRDA
#define UART3_ENABLED 1
/* UART 4 configuration */
#define UART4_MODE MODE_DCE
#define UART4_IR NO_IRDA
#define UART4_ENABLED 0
/* UART 5 configuration */
#define UART5_MODE MODE_DCE
#define UART5_IR NO_IRDA
#define UART5_ENABLED 0
#define MXC_LL_UART_PADDR UART1_BASE_ADDR
#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
/* ron: Efikasb GPIO Pin Definition */
#define SDHC1_CD_PIN MX51_PIN_EIM_CS2 /* MX51_PIN_GPIO1_0 */
#define SDHC1_WP_PIN MX51_PIN_GPIO1_1
#define SDHC2_CD_PIN MX51_PIN_GPIO1_8
#define SDHC2_WP_PIN MX51_PIN_GPIO1_7
#define HUB_RESET_PIN MX51_PIN_GPIO1_5
#define PMIC_INT_PIN MX51_PIN_GPIO1_6
#define USB_PHY_RESET_PIN MX51_PIN_EIM_D27
#define WIRELESS_SW_PIN MX51_PIN_DI1_PIN12
#define WLAN_PWRON_PIN MX51_PIN_EIM_A22
#define WLAN_RESET_PIN MX51_PIN_EIM_A16
#define BT_PWRON_PIN MX51_PIN_EIM_A17
#define WWAN_PWRON_PIN MX51_PIN_CSI2_D13
#define LCD_LVDS_EN_PIN MX51_PIN_CSI1_D8
#define LCD_PWRON_PIN MX51_PIN_CSI1_D9
#define LCD_BL_PWM_PIN MX51_PIN_GPIO1_2
#define LCD_BL_PWRON_PIN MX51_PIN_CSI2_D19
#define LVDS_RESET_PIN MX51_PIN_DISPB2_SER_DIN
#define LVDS_PWRCTL_PIN MX51_PIN_DISPB2_SER_CLK
#define CAM_PWRON_PIN MX51_PIN_NANDF_CS0
#define BATT_LOW_PIN MX51_PIN_DI1_PIN11
#define BATT_INS_PIN MX51_PIN_DISPB2_SER_DIO
#define AC_ADAP_INS_PIN MX51_PIN_DI1_D0_CS /* MX51_PIN_CSI1_D8 */
#define AUD_MUTE_PIN MX51_PIN_EIM_A23
#define HPJACK_INS_PIN MX51_PIN_DISPB2_SER_RS
#define LID_SW_PIN MX51_PIN_CSI1_VSYNC /* Low: close, High: open */
#define POWER_BTN_PIN MX51_PIN_EIM_DTACK
#define SYS_PWROFF_PIN MX51_PIN_CSI2_VSYNC
#define SYS_PWRGD_PIN MX51_PIN_CSI2_PIXCLK
/* ron: Efikasb LED Pin Definition */
#define CAPS_LED_PIN MX51_PIN_EIM_CS0
#define ALARM_LED_PIN MX51_PIN_GPIO1_3
/* ron: R1.2 borad GPIO definition */
#define MEM_ID0_PIN MX51_PIN_EIM_LBA /* MX51_PIN_GPIO1_4 */
#define MEM_ID1_PIN MX51_PIN_EIM_CRE
#define SIM_CD_PIN MX51_PIN_EIM_CS1
#define WWAN_WAKEUP_PIN MX51_PIN_CSI1_HSYNC
#define AGPS_PWRON_PIN MX51_PIN_CSI2_D12
#define AGPS_RESET_PIN MX51_PIN_CSI2_D18
#define AGPS_PWRSW_PIN MX51_PIN_NANDF_CS1 /* ron: R1.3 add AGPS_PWRSW */
/* ron: R1.3 board GPIO definition */
#define PCB_ID0_PIN MX51_PIN_EIM_CS3
#define PCB_ID1_PIN MX51_PIN_EIM_CS4
#define WDOG_PIN MX51_PIN_GPIO1_4
/* #define BATT_LOW_LED_PIN MX51_PIN_DI1_PIN13 */
extern int __init mx51_efikasb_init_mc13892(void);
#endif /* __ASM_ARCH_MXC_BOARD_MX51_LANGE51_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,251 @@
/*
* Copyright 2009-2010 Pegatron Corporation. All Rights Reserved.
* Copyright 2009-2010 Genesi USA, Inc. All Rights Reserved.
*/
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <linux/keyboard.h>
#include <mach/hardware.h>
#include <mach/gpio.h>
#include <asm/io.h>
#include "mx51_efikasb.h"
#include "iomux.h"
#define DEFAULT_PERIOD 60
#define IDLE_EVENT 1
void mxc_reset_idle_timer(void);
struct efikasb_idle_timer {
int enable;
int timeout;
spinlock_t lock;
struct timer_list timer;
unsigned long period; /* in second */
};
static struct efikasb_idle_timer *idle_timer = NULL;
static BLOCKING_NOTIFIER_HEAD(idle_notifier_list);
int register_idle_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&idle_notifier_list, nb);
}
EXPORT_SYMBOL_GPL(register_idle_notifier);
int unregister_idle_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&idle_notifier_list, nb);
}
EXPORT_SYMBOL_GPL(unregister_idle_notifier);
static int key_event_call(struct notifier_block *blk, unsigned long code, void *_param)
{
switch (code) {
case KBD_KEYCODE:
mxc_reset_idle_timer();
return NOTIFY_OK;
}
return NOTIFY_DONE;
}
static struct notifier_block key_nb = {
.notifier_call = key_event_call,
};
static void idle_timeout_fn(unsigned long data)
{
struct efikasb_idle_timer *efikasb_timer = (struct efikasb_idle_timer *)data;
unsigned long flags;
spin_lock_irqsave(&efikasb_timer->lock, flags);
efikasb_timer->timeout = 1;
efikasb_timer->enable = 0;
spin_unlock_irqrestore(&efikasb_timer->lock, flags);
blocking_notifier_call_chain(&idle_notifier_list, IDLE_EVENT, 1);
}
static void idle_timer_enable(void)
{
unsigned long flags;
if(idle_timer->enable && timer_pending(&idle_timer->timer))
return;
spin_lock_irqsave(&idle_timer->lock, flags);
idle_timer->timer.data = (unsigned long)idle_timer;
idle_timer->timer.function = idle_timeout_fn;
if(idle_timer->period == 0)
idle_timer->period = DEFAULT_PERIOD;
idle_timer->timer.expires = jiffies + idle_timer->period * HZ;
add_timer(&idle_timer->timer);
idle_timer->enable = 1;
idle_timer->timeout = 0;
spin_unlock_irqrestore(&idle_timer->lock, flags);
}
static void idle_timer_disable(void)
{
unsigned long flags;
if(!idle_timer->enable && !timer_pending(&idle_timer->timer))
return;
spin_lock_irqsave(&idle_timer->lock, flags);
del_timer(&idle_timer->timer);
idle_timer->enable = 0;
idle_timer->timeout = 0;
spin_unlock_irqrestore(&idle_timer->lock, flags);
}
static void idle_timer_reset(void)
{
unsigned long flags;
if(!idle_timer->enable && !timer_pending(&idle_timer->timer))
return;
spin_lock_irqsave(&idle_timer->lock, flags);
mod_timer(&idle_timer->timer, jiffies + idle_timer->period * HZ);
idle_timer->timeout = 0;
spin_unlock_irqrestore(&idle_timer->lock, flags);
}
static ssize_t timer_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", idle_timer->enable);
}
static ssize_t timer_enable_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
if(strncmp(buf, "1", 1) == 0)
idle_timer_enable();
else if(strncmp(buf, "0", 1) == 0)
idle_timer_disable();
return count;
}
static ssize_t timer_period_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", (unsigned int) idle_timer->period);
}
static ssize_t timer_period_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *end;
int arg;
arg = simple_strtoul(buf, &end, 10);
if(arg < 0) {
return -EINVAL;
}
idle_timer->period = arg;
idle_timer_reset();
return count;
}
static ssize_t timer_timeout_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
if (!idle_timer->enable)
return sprintf(buf, "0\n");
else
return sprintf(buf, "%d\n",
jiffies_to_msecs(idle_timer->timer.expires - jiffies) / 1000);
}
static struct kobj_attribute timer_enable_attribute =
__ATTR(enable, 0666, timer_enable_show, timer_enable_store);
static struct kobj_attribute timer_period_attribute =
__ATTR(period, 0666, timer_period_show, timer_period_store);
static struct kobj_attribute timer_timeout_attribute =
__ATTR(timeout, S_IFREG | S_IRUGO, timer_timeout_show, NULL);
static struct attribute *idle_attrs[] = {
&timer_enable_attribute.attr,
&timer_period_attribute.attr,
&timer_timeout_attribute.attr,
NULL,
};
static struct attribute_group idle_attr_group = {
.attrs = idle_attrs,
};
static struct platform_device mxc_idle_timer_dev = {
.name = "mxc_idle_timer",
};
static int __init mxc_init_idle_timer(void)
{
int retval;
static struct kobject *idle_timer_kobj;
platform_device_register(&mxc_idle_timer_dev);
idle_timer_kobj = kobject_create_and_add("setting", &mxc_idle_timer_dev.dev.kobj);
if(!idle_timer_kobj)
return -ENOMEM;
retval = sysfs_create_group(idle_timer_kobj, &idle_attr_group);
if(retval) {
kobject_put(idle_timer_kobj);
return retval;
}
idle_timer = kzalloc(sizeof(*idle_timer), GFP_KERNEL);
if(!idle_timer) {
sysfs_remove_group(idle_timer_kobj, &idle_attr_group);
kobject_put(idle_timer_kobj);
return -ENOMEM;
}
init_timer(&idle_timer->timer);
idle_timer->lock = SPIN_LOCK_UNLOCKED;
register_keyboard_notifier(&key_nb);
return 0;
}
late_initcall(mxc_init_idle_timer);
void mxc_reset_idle_timer(void)
{
if(idle_timer->timeout) {
blocking_notifier_call_chain(&idle_notifier_list, IDLE_EVENT, 0);
} else {
idle_timer_reset();
}
}
EXPORT_SYMBOL(mxc_reset_idle_timer);

View File

@@ -0,0 +1,471 @@
/*
* Copyright 2009-2010 Pegatron Corporation. All Rights Reserved.
* Copyright 2009-2010 Genesi USA, Inc. All Rights Reserved.
*/
#include <linux/init.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/suspend.h>
#include <linux/syscalls.h>
#include <linux/pci_ids.h>
#include <linux/platform_device.h>
#include <mach/gpio.h>
#include "mx51_efikasb.h"
#include "mx51_pins.h"
#include "iomux.h"
int wireless_sw_state;
extern int mxc_get_battery_insertion_status(void);
extern int mxc_get_batt_low_status(void);
extern int mxc_get_ac_adapter_insertion_status(void);
extern int mxc_get_wireless_sw_status(void);
extern int mxc_get_sim_card_status(void);
extern void mxc_power_on_wlan(int on);
extern void efikasb_power_on_wwan(int val);
extern void efikasb_power_on_bt(int val);
extern int register_idle_notifier(struct notifier_block *);
extern int unregister_idle_notifier(struct notifier_block *);
extern void mxc_reset_idle_timer(void);
extern int lid_wake_enable;
extern int mxc_get_lid_sw_status(void);
extern void kernel_power_off(void);
static struct input_dev *efikasb_inputdev;
static int system_is_resuming = 0;
#ifdef CONFIG_EFIKASB_EXPERIMENTAL_OS
static void power_off_worker(struct work_struct *work);
static DECLARE_DELAYED_WORK(power_off_work, power_off_worker);
#endif
/*!
* Power Key interrupt handler.
*/
static irqreturn_t power_key_int(int irq, void *dev_id)
{
static int key_pressed = 0;
int pk_pressed;
/* ron: Don't report power key event if system is resuming */
if(system_is_resuming)
return IRQ_HANDLED;
pk_pressed = !gpio_get_value(IOMUX_TO_GPIO(POWER_BTN_PIN));
if (pk_pressed) {
if (key_pressed)
return 0;
key_pressed = 1;
pr_info("PWR key pressed\n");
/* ron: input_report_key(pk_dev, KEY_POWER, pk_pressed); */
set_irq_type(irq, IRQF_TRIGGER_RISING); /* ron: detect falling edge */
if(lid_wake_enable)
enable_irq_wake(IOMUX_TO_IRQ(LID_SW_PIN));
input_report_key(efikasb_inputdev, KEY_POWER, 1);
input_sync(efikasb_inputdev);
} else {
if (!key_pressed)
return 0;
key_pressed = 0;
pr_info("PWR Key released\n");
/* ron: input_report_key(pk_dev, KEY_POWER, pk_released); */
set_irq_type(irq, IRQF_TRIGGER_FALLING); /* ron: detect falling edge */
input_report_key(efikasb_inputdev, KEY_POWER, 0);
input_sync(efikasb_inputdev);
}
return IRQ_HANDLED;
}
static int mxc_init_power_key(void)
{
int ret;
int irq;
/* ron: set power key as wakeup source */
gpio_request(IOMUX_TO_GPIO(POWER_BTN_PIN), "power_btn");
gpio_direction_input(IOMUX_TO_GPIO(POWER_BTN_PIN));
irq = IOMUX_TO_IRQ(POWER_BTN_PIN);
set_irq_type(irq, IRQF_TRIGGER_FALLING); /* ron: detect rising & falling edge */
ret = request_irq(irq, power_key_int, 0, "power_key", 0);
if (ret)
pr_info("register on-off key interrupt failed\n");
else
enable_irq_wake(irq);
return ret;
}
#define KEY_RESUME KEY_WAKEUP
static int pm_notifier_call(struct notifier_block *nb, unsigned long event, void *dummy)
{
switch (event) {
case PM_SUSPEND_PREPARE:
printk("System Suspending .....\n");
system_is_resuming = 1;
return NOTIFY_OK;
case PM_POST_SUSPEND:
printk("System Resumed\n");
system_is_resuming = 0;
input_event(efikasb_inputdev, EV_PWR, KEY_RESUME, 1);
input_sync(efikasb_inputdev);
return NOTIFY_OK;
}
return NOTIFY_DONE;
}
static struct notifier_block pm_nb = {
.notifier_call = pm_notifier_call,
};
#define EVENT_IDLE 1
static int idle_notifier_call(struct notifier_block *nb, unsigned long event, void *v)
{
int value = (int) v;
switch(event) {
case EVENT_IDLE:
input_event(efikasb_inputdev, EV_PWR, KEY_SUSPEND, value);
input_sync(efikasb_inputdev);
return NOTIFY_OK;
}
return NOTIFY_DONE;
}
static struct notifier_block idle_nb = {
.notifier_call = idle_notifier_call,
};
static irqreturn_t wireless_sw_int(int irq, void *dev_id)
{
wireless_sw_state = gpio_get_value(IOMUX_TO_GPIO(WIRELESS_SW_PIN));
if (wireless_sw_state) {
pr_info("Wireless SW Off\n");
set_irq_type(irq, IRQF_TRIGGER_FALLING);
input_report_switch(efikasb_inputdev, SW_RFKILL_ALL, 0);
input_sync(efikasb_inputdev);
// power off WLAN, WWAN and BT by H/W
} else {
pr_info("Wireless SW On\n");
set_irq_type(irq, IRQF_TRIGGER_RISING);
input_report_switch(efikasb_inputdev, SW_RFKILL_ALL, 1);
input_sync(efikasb_inputdev);
}
return IRQ_HANDLED;
}
static int mxc_init_wireless_sw(void)
{
int irq, ret;
gpio_request(IOMUX_TO_GPIO(WIRELESS_SW_PIN), "wireless_sw");
gpio_direction_input(IOMUX_TO_GPIO(WIRELESS_SW_PIN));
irq = IOMUX_TO_IRQ(WIRELESS_SW_PIN);
if (gpio_get_value(IOMUX_TO_GPIO(WIRELESS_SW_PIN)))
set_irq_type(irq, IRQF_TRIGGER_FALLING);
else
set_irq_type(irq, IRQF_TRIGGER_RISING);
ret = request_irq(irq, wireless_sw_int, 0, "wireless-sw", 0);
if (ret)
pr_info("register wireless switch interrupt failed\n");
wireless_sw_state = gpio_get_value(IOMUX_TO_GPIO(WIRELESS_SW_PIN));
return ret;
}
static irqreturn_t sim_detect_int(int irq, void *dev_id)
{
if(mxc_get_sim_card_status()) { /* ron: low active */
set_irq_type(irq, IRQF_TRIGGER_RISING);
pr_info("SIM card inserted\n");
} else {
set_irq_type(irq, IRQF_TRIGGER_FALLING);
pr_info("SIM card removed\n");
}
return IRQ_HANDLED;
}
static int mxc_init_sim_detect(void)
{
int irq, ret;
irq = IOMUX_TO_IRQ(SIM_CD_PIN);
if(mxc_get_sim_card_status()) /* ron: low active */
set_irq_type(irq, IRQF_TRIGGER_RISING);
else
set_irq_type(irq, IRQF_TRIGGER_FALLING);
ret = request_irq(irq, sim_detect_int, 0, "sim-detect", 0);
if(ret)
pr_info("register SIM card detect interrupt failed\n");
return ret;
}
/* late_initcall(mxc_init_sim_detect); */
#ifdef CONFIG_EFIKASB_EXPERIMENTAL_OS
static irqreturn_t efikasb_battery_detect_handler(int irq, void *data)
{
int batt_in;
batt_in = mxc_get_battery_insertion_status();
printk("efikasb_battery: Battery %s\n",
batt_in ? "Inserted" : "Removed");
input_report_switch(efikasb_inputdev, SW_BATT_IN, batt_in);
input_sync(efikasb_inputdev);
if (batt_in)
set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
else
set_irq_type(irq, IRQ_TYPE_EDGE_FALLING);
return IRQ_HANDLED;
}
static irqreturn_t efikasb_ac_charger_detect_handler(int irq, void *data)
{
int ac_in;
ac_in = mxc_get_ac_adapter_insertion_status();
printk("efikasb_ac_charger: AC %s\n",
ac_in ? "Inserted" : "Removed");
input_report_switch(efikasb_inputdev, SW_AC_INSERT, ac_in);
input_sync(efikasb_inputdev);
if (ac_in)
set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
else
set_irq_type(irq, IRQ_TYPE_EDGE_FALLING);
return IRQ_HANDLED;
}
static void power_off_worker(struct work_struct *work)
{
if(!mxc_get_ac_adapter_insertion_status() &&
mxc_get_batt_low_status()) {
sys_sync();
kernel_power_off();
}
}
static irqreturn_t efikasb_batt_low_handler(int irq, void *data)
{
int batt_low;
batt_low = mxc_get_batt_low_status();
printk("efikasb_batter: Battery %s\n",
batt_low ? "Low" : "Normal");
input_event(efikasb_inputdev, EV_PWR, KEY_BATTERY, batt_low);
input_sync(efikasb_inputdev);
if (batt_low) {
set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH/* IRQ_TYPE_EDGE_RISING */);
printk("Battery critical low, shutdown now....\n");
schedule_delayed_work(&power_off_work, msecs_to_jiffies(1000));
} else {
set_irq_type(irq, IRQ_TYPE_LEVEL_LOW/* IRQ_TYPE_EDGE_FALLING */);
}
return IRQ_HANDLED;
}
static int mxc_init_battery(void)
{
int ret;
int batt_low;
int batt_in, ac_in;
batt_in = mxc_get_battery_insertion_status();
if(batt_in) {
ret = request_irq(IOMUX_TO_IRQ(BATT_INS_PIN),
efikasb_battery_detect_handler,
IRQ_TYPE_EDGE_RISING, "efikasb_battery", NULL);
} else {
ret = request_irq(IOMUX_TO_IRQ(BATT_INS_PIN),
efikasb_battery_detect_handler,
IRQ_TYPE_EDGE_FALLING, "efikasb_battery", NULL);
}
ac_in = mxc_get_ac_adapter_insertion_status();
if(ac_in) {
ret = request_irq(IOMUX_TO_IRQ(AC_ADAP_INS_PIN),
efikasb_ac_charger_detect_handler,
IRQ_TYPE_EDGE_RISING, "efikasb_ac_charger", NULL);
} else {
ret = request_irq(IOMUX_TO_IRQ(AC_ADAP_INS_PIN),
efikasb_ac_charger_detect_handler,
IRQ_TYPE_EDGE_FALLING, "efikasb_ac_charger", NULL);
}
batt_low = mxc_get_batt_low_status();
if(batt_low) {
ret = request_irq(IOMUX_TO_IRQ(BATT_LOW_PIN),
efikasb_batt_low_handler,
IRQ_TYPE_LEVEL_HIGH, "efikasb_batt_low", NULL);
} else {
ret = request_irq(IOMUX_TO_IRQ(BATT_LOW_PIN),
efikasb_batt_low_handler,
IRQ_TYPE_LEVEL_LOW, "efikasb_batt_low", NULL);
}
enable_irq_wake(IOMUX_TO_IRQ(BATT_LOW_PIN));
/* ron: if battery critical low, shutdown immediately */
if(!mxc_get_ac_adapter_insertion_status() && batt_low) {
printk("Battery critical low, shutdown now....\n");
schedule_delayed_work(&power_off_work, msecs_to_jiffies(1000));
}
return ret;
}
#endif /* CONFIG_EFIKASB_EXPERIMENTAL_OS */
static ssize_t sim_status_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", mxc_get_sim_card_status());
}
static ssize_t wireless_sw_status_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", mxc_get_wireless_sw_status());
}
static ssize_t batt_low_status_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", mxc_get_batt_low_status());
}
static struct kobj_attribute sim_status_attribute =
__ATTR(sim, S_IFREG | S_IRUGO, sim_status_show, NULL);
static struct kobj_attribute wireless_sw_status_attribute =
__ATTR(wireless_sw, S_IFREG | S_IRUGO, wireless_sw_status_show, NULL);
static struct kobj_attribute batt_low_status_attribute =
__ATTR(battery_low, S_IFREG | S_IRUGO, batt_low_status_show, NULL);
static struct attribute *status_attrs[] = {
&sim_status_attribute.attr,
&wireless_sw_status_attribute.attr,
&batt_low_status_attribute.attr,
NULL,
};
static struct attribute_group status_attr_group = {
.attrs = status_attrs,
};
static struct platform_device mxc_efikasb_input_dev = {
.name = "efikasb_input",
};
static int __init mxc_init_efikasb_inputdev(void)
{
int ret;
struct kobject *efikasb_input_kobj;
platform_device_register(&mxc_efikasb_input_dev);
efikasb_input_kobj = kobject_create_and_add("status", &mxc_efikasb_input_dev.dev.kobj);
if(!efikasb_input_kobj)
return -ENOMEM;
ret = sysfs_create_group(efikasb_input_kobj, &status_attr_group);
if(ret) {
kobject_put(efikasb_input_kobj);
return ret;
}
efikasb_inputdev = input_allocate_device();
if (!efikasb_inputdev) {
pr_err("Failed to allocate hotkey input device\n");
return -ENOMEM;
}
efikasb_inputdev->name = "Genesi Efikasb Extra Buttons";
efikasb_inputdev->phys = "genesi-efikasb/input0";
efikasb_inputdev->uniq = "genesi-efikasb";
efikasb_inputdev->id.bustype = BUS_HOST;
efikasb_inputdev->id.vendor = PCI_VENDOR_ID_FREESCALE;
set_bit(KEY_POWER, efikasb_inputdev->keybit);
set_bit(KEY_SLEEP, efikasb_inputdev->keybit);
set_bit(KEY_WAKEUP, efikasb_inputdev->keybit);
set_bit(EV_KEY, efikasb_inputdev->evbit);
set_bit(EV_PWR, efikasb_inputdev->evbit);
set_bit(SW_RFKILL_ALL, efikasb_inputdev->swbit);
if(mxc_get_wireless_sw_status())
set_bit(SW_RFKILL_ALL, efikasb_inputdev->sw);
/* ron: I known this is not a regular approach,
but Lenovo requests, I can't resist*/
#ifdef CONFIG_EFIKASB_EXPERIMENTAL_OS
set_bit(SW_AC_INSERT, efikasb_inputdev->swbit);
if(mxc_get_ac_adapter_insertion_status())
set_bit(SW_AC_INSERT, efikasb_inputdev->sw);
set_bit(SW_BATT_IN, efikasb_inputdev->swbit);
if(mxc_get_battery_insertion_status())
set_bit(SW_BATT_IN, efikasb_inputdev->sw);
#endif
ret = input_register_device(efikasb_inputdev);
if (ret) {
input_free_device(efikasb_inputdev);
pr_err("Failed to register hotkey input device\n");
sysfs_remove_group(efikasb_input_kobj, &status_attr_group);
kobject_put(efikasb_input_kobj);
return -ENODEV;
}
mxc_init_power_key();
#ifdef CONFIG_EFIKASB_EXPERIMENTAL_OS
mxc_init_battery();
#endif
mxc_init_sim_detect();
mxc_init_wireless_sw();
register_pm_notifier(&pm_nb);
register_idle_notifier(&idle_nb);
return ret;
}
late_initcall(mxc_init_efikasb_inputdev);

View File

@@ -0,0 +1,195 @@
/*
* Copyright 2009-2010 Pegatron Corporation. All Rights Reserved.
* Copyright 2009-2010 Genesi USA, Inc. All Rights Reserved.
*/
#include <linux/init.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/suspend.h>
#include <linux/pci_ids.h>
#include <linux/platform_device.h>
#include <mach/gpio.h>
#include <asm/setup.h>
#include "mx51_efikasb.h"
#include "mx51_pins.h"
#include "iomux.h"
int lid_wake_enable = 0;
extern int mxc_get_lid_sw_status(void);
extern void mxc_reset_idle_timer(void);
static struct input_dev *efikasb_lid_inputdev;
static struct platform_device efikasb_lid_dev = {
.name = "efikasb_lid",
};
static irqreturn_t lid_sw_int(int irq, void *dev_id)
{
int lid_close;
mxc_reset_idle_timer();
lid_close = mxc_get_lid_sw_status();
if(lid_close) {
pr_info("Lid Switch Close\n");
set_irq_type(irq, IRQF_TRIGGER_RISING);
if(lid_wake_enable)
enable_irq_wake(irq);
input_report_switch(efikasb_lid_inputdev, SW_LID, lid_close);
input_sync(efikasb_lid_inputdev);
} else {
pr_info("Lid Switch Open\n");
set_irq_type(irq, IRQF_TRIGGER_FALLING);
input_report_switch(efikasb_lid_inputdev, SW_LID, lid_close);
input_sync(efikasb_lid_inputdev);
if(lid_wake_enable)
disable_irq_wake(irq);
}
return IRQ_HANDLED;
}
static int mxc_init_lid_sw(void)
{
int irq, ret;
gpio_request(IOMUX_TO_GPIO(LID_SW_PIN), "lid_sw");
gpio_direction_input(IOMUX_TO_GPIO(LID_SW_PIN));
irq = IOMUX_TO_IRQ(LID_SW_PIN);
if(mxc_get_lid_sw_status()) {
pr_info("Lid Switch Close\n");
set_irq_type(irq, IRQF_TRIGGER_RISING);
} else {
pr_info("Lid Switch Open\n");
set_irq_type(irq, IRQF_TRIGGER_FALLING);
}
ret = request_irq(irq, lid_sw_int, 0, "lid-sw", 0);
if(ret)
pr_info("register lid switch interrupt failed\n");
return ret;
}
static ssize_t lid_status_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", mxc_get_lid_sw_status());
}
static ssize_t lid_wake_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", lid_wake_enable ? "on": "off");
}
static ssize_t lid_wake_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
if(strncmp(buf, "on", 2) == 0)
lid_wake_enable = 1;
else if (strncmp(buf, "off", 3) == 0)
lid_wake_enable = 0;
else
return -EINVAL;
return count;
}
static struct kobj_attribute lid_status_attribute =
__ATTR(lid, S_IFREG | S_IRUGO, lid_status_show, NULL);
static struct kobj_attribute lid_wake_attribute =
__ATTR(lid_wake, 0666, lid_wake_show, lid_wake_store);
static struct attribute *status_attrs[] = {
&lid_status_attribute.attr,
&lid_wake_attribute.attr,
NULL,
};
static struct attribute_group status_attr_group = {
.attrs = status_attrs,
};
static void __init lid_wake_setup(char **p)
{
if(memcmp(*p, "on", 2) == 0) {
lid_wake_enable = 1;
*p += 2;
} else if(memcmp(*p, "off", 3) == 0) {
lid_wake_enable = 0;
*p += 3;
}
}
__early_param("lid_wake=", lid_wake_setup);
static int __init mxc_init_efikasb_lid(void)
{
int ret ;
struct kobject *lid_kobj;
platform_device_register(&efikasb_lid_dev);
lid_kobj = kobject_create_and_add("status", &efikasb_lid_dev.dev.kobj);
if(!lid_kobj) {
ret = -ENOMEM;
goto err3;
}
ret = sysfs_create_group(lid_kobj, &status_attr_group);
if(ret) {
goto err2;
}
efikasb_lid_inputdev = input_allocate_device();
if(!efikasb_lid_inputdev) {
pr_err("Failed to allocate lid input device\n");
ret = -ENOMEM;
goto err2;
}
efikasb_lid_inputdev->name = "Efikasb Lid Switch";
efikasb_lid_inputdev->phys = "Efikasb/input1";
efikasb_lid_inputdev->uniq = "Efikasb";
efikasb_lid_inputdev->id.bustype = BUS_HOST;
efikasb_lid_inputdev->id.vendor = PCI_VENDOR_ID_FREESCALE;
set_bit(EV_SW, efikasb_lid_inputdev->evbit);
set_bit(SW_LID, efikasb_lid_inputdev->swbit);
/* ron: 0:open 1:close */
if(mxc_get_lid_sw_status())
set_bit(SW_LID, efikasb_lid_inputdev->sw);
ret = input_register_device(efikasb_lid_inputdev);
if(ret) {
pr_err("Failed to register Efikasb lid input device\n");
ret = -ENODEV;
goto err1;
}
mxc_init_lid_sw();
return ret;
err1:
input_free_device(efikasb_lid_inputdev);
err2:
kobject_put(lid_kobj);
err3:
platform_device_unregister(&efikasb_lid_dev);
return ret;
}
late_initcall(mxc_init_efikasb_lid);

View File

@@ -0,0 +1,419 @@
/*
* mx51-efikasb-pmic-mc13892.c -- i.MX51 Efikasb Driver for Atlas MC13892 PMIC
*/
/*
* Copyright 2009-2010 Pegatron Corporation. All Rights Reserved.
* Copyright 2009-2010 Genesi USA, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/err.h>
#include <linux/pmic_external.h>
#include <linux/regulator/machine.h>
#include <linux/mfd/mc13892/core.h>
#include <mach/irqs.h>
#include "iomux.h"
#include "mx51_pins.h"
/*
* Convenience conversion.
* Here atm, maybe there is somewhere better for this.
*/
#define mV_to_uV(mV) (mV * 1000)
#define uV_to_mV(uV) (uV / 1000)
#define V_to_uV(V) (mV_to_uV(V * 1000))
#define uV_to_V(uV) (uV_to_mV(uV) / 1000)
/* Coin cell charger enable */
#define COINCHEN_LSH 23
#define COINCHEN_WID 1
/* Coin cell charger voltage setting */
#define VCOIN_LSH 20
#define VCOIN_WID 3
/* Coin Charger voltage */
#define VCOIN_2_5V 0x0
#define VCOIN_2_7V 0x1
#define VCOIN_2_8V 0x2
#define VCOIN_2_9V 0x3
#define VCOIN_3_0V 0x4
#define VCOIN_3_1V 0x5
#define VCOIN_3_2V 0x6
#define VCOIN_3_3V 0x7
/* regulator standby mask */
#define GEN1_STBY_MASK (1 << 1)
#define IOHI_STBY_MASK (1 << 4)
#define DIG_STBY_MASK (1 << 10)
#define GEN2_STBY_MASK (1 << 13)
#define PLL_STBY_MASK (1 << 16)
#define USB2_STBY_MASK (1 << 19)
#define GEN3_STBY_MASK (1 << 1)
#define CAM_STBY_MASK (1 << 7)
#define VIDEO_STBY_MASK (1 << 13)
#define AUDIO_STBY_MASK (1 << 16)
#define SD_STBY_MASK (1 << 19)
/* 0x92412 */
#define REG_MODE_0_ALL_MASK (GEN1_STBY_MASK |\
DIG_STBY_MASK | GEN2_STBY_MASK |\
PLL_STBY_MASK)
/* 0x92082 */
#define REG_MODE_1_ALL_MASK (CAM_STBY_MASK | VIDEO_STBY_MASK |\
AUDIO_STBY_MASK | SD_STBY_MASK)
/* switch mode setting */
#define SW1MODE_LSB 0
#define SW2MODE_LSB 10
#define SW3MODE_LSB 0
#define SW4MODE_LSB 8
#define SWMODE_MASK 0xF
#define SWMODE_AUTO 0x8
#define PWGT1SPIEN (1 << 15)
#define PWGT2SPIEN (1 << 16)
/* CPU */
static struct regulator_consumer_supply sw1_consumers[] = {
{
.supply = "cpu_vcc",
}
};
static struct regulator_consumer_supply vdig_consumers[] = {
{
/* sgtl5000 */
.supply = "VDDA",
.dev_name = "1-000a",
},
};
static struct regulator_consumer_supply vvideo_consumers[] = {
{
/* sgtl5000 */
.supply = "VDDIO",
.dev_name = "1-000a",
},
};
struct mc13892;
static struct regulator_init_data sw1_init = {
.constraints = {
.name = "SW1",
.min_uV = mV_to_uV(600),
.max_uV = mV_to_uV(1375),
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
.valid_modes_mask = 0,
.always_on = 1,
.boot_on = 1,
.initial_state = PM_SUSPEND_MEM,
.state_mem = {
.uV = 850000,
.mode = REGULATOR_MODE_NORMAL,
.enabled = 1,
},
},
.num_consumer_supplies = ARRAY_SIZE(sw1_consumers),
.consumer_supplies = sw1_consumers,
};
static struct regulator_init_data sw2_init = {
.constraints = {
.name = "SW2",
.min_uV = mV_to_uV(900),
.max_uV = mV_to_uV(1850),
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
.always_on = 1,
.boot_on = 1,
.initial_state = PM_SUSPEND_MEM,
.state_mem = {
.uV = 950000,
.mode = REGULATOR_MODE_NORMAL,
.enabled = 1,
},
}
};
static struct regulator_init_data sw3_init = {
.constraints = {
.name = "SW3",
.min_uV = mV_to_uV(1100),
.max_uV = mV_to_uV(1850),
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
.always_on = 1,
.boot_on = 1,
}
};
static struct regulator_init_data sw4_init = {
.constraints = {
.name = "SW4",
.min_uV = mV_to_uV(1100),
.max_uV = mV_to_uV(1850),
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
.always_on = 1,
.boot_on = 1,
}
};
static struct regulator_init_data viohi_init = {
.constraints = {
.name = "VIOHI",
.always_on = 1,
.boot_on = 1,
}
};
static struct regulator_init_data vusb_init = {
.constraints = {
.name = "VUSB",
.boot_on = 1,
.always_on = 1,
}
};
static struct regulator_init_data swbst_init = {
.constraints = {
.name = "SWBST",
}
};
static struct regulator_init_data vdig_init = {
.constraints = {
.name = "VDIG",
.min_uV = mV_to_uV(1650),
.max_uV = mV_to_uV(1650),
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
.boot_on = 1,
},
.num_consumer_supplies = ARRAY_SIZE(vdig_consumers),
.consumer_supplies = vdig_consumers,
};
static struct regulator_init_data vpll_init = {
.constraints = {
.name = "VPLL",
.min_uV = mV_to_uV(1050),
.max_uV = mV_to_uV(1800),
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
.boot_on = 1,
.always_on = 1,
}
};
static struct regulator_init_data vusb2_init = {
.constraints = {
.name = "VUSB2",
.min_uV = mV_to_uV(2400),
.max_uV = mV_to_uV(2775),
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
.boot_on = 1,
.always_on = 1,
}
};
static struct regulator_init_data vvideo_init = {
.constraints = {
.name = "VVIDEO",
.min_uV = mV_to_uV(2775),
.max_uV = mV_to_uV(2775),
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
.apply_uV =1,
},
.num_consumer_supplies = ARRAY_SIZE(vvideo_consumers),
.consumer_supplies = vvideo_consumers,
};
static struct regulator_init_data vaudio_init = {
.constraints = {
.name = "VAUDIO",
.min_uV = mV_to_uV(2300),
.max_uV = mV_to_uV(3000),
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
}
};
static struct regulator_init_data vsd_init = {
.constraints = {
.name = "VSD",
.min_uV = mV_to_uV(1800),
.max_uV = mV_to_uV(3150),
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
}
};
static struct regulator_init_data vcam_init = {
.constraints = {
.name = "VCAM",
.min_uV = mV_to_uV(2500),
.max_uV = mV_to_uV(3000),
.valid_ops_mask =
REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE,
.valid_modes_mask = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL,
}
};
static struct regulator_init_data vgen1_init = {
.constraints = {
.name = "VGEN1",
.min_uV = mV_to_uV(1200),
.max_uV = mV_to_uV(1200),
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
}
};
static struct regulator_init_data vgen2_init = {
.constraints = {
.name = "VGEN2",
.min_uV = mV_to_uV(1200),
.max_uV = mV_to_uV(3150),
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
.always_on = 1,
}
};
static struct regulator_init_data vgen3_init = {
.constraints = {
.name = "VGEN3",
.min_uV = mV_to_uV(1800),
.max_uV = mV_to_uV(2900),
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
.always_on = 1,
}
};
static struct regulator_init_data gpo1_init = {
.constraints = {
.name = "GPO1",
}
};
static struct regulator_init_data gpo2_init = {
.constraints = {
.name = "GPO2",
}
};
static struct regulator_init_data gpo3_init = {
.constraints = {
.name = "GPO3",
}
};
static struct regulator_init_data gpo4_init = {
.constraints = {
.name = "GPO4",
}
};
static int mc13892_regulator_init(struct mc13892 *mc13892)
{
unsigned int value, register_mask;
printk("Initializing regulators for Efikasb 5.1.\n");
if (mxc_cpu_is_rev(CHIP_REV_2_0) < 0)
sw2_init.constraints.state_mem.uV = 1100000;
else if (mxc_cpu_is_rev(CHIP_REV_2_0) == 1) {
sw2_init.constraints.state_mem.uV = 1250000;
sw1_init.constraints.state_mem.uV = 1000000;
}
/* enable standby controll for all regulators */
pmic_read_reg(REG_MODE_0, &value, 0xffffff);
value |= REG_MODE_0_ALL_MASK;
pmic_write_reg(REG_MODE_0, value, 0xffffff);
pmic_read_reg(REG_MODE_1, &value, 0xffffff);
value |= REG_MODE_1_ALL_MASK;
pmic_write_reg(REG_MODE_1, value, 0xffffff);
/* enable switch audo mode */
pmic_read_reg(REG_IDENTIFICATION, &value, 0xffffff);
/* only for mc13892 2.0A */
if ((value & 0x0000FFFF) == 0x45d0) {
pmic_read_reg(REG_SW_4, &value, 0xffffff);
register_mask = (SWMODE_MASK << SW1MODE_LSB) |
(SWMODE_MASK << SW2MODE_LSB);
value &= ~register_mask;
value |= (SWMODE_AUTO << SW1MODE_LSB) |
(SWMODE_AUTO << SW2MODE_LSB);
pmic_write_reg(REG_SW_4, value, 0xffffff);
pmic_read_reg(REG_SW_5, &value, 0xffffff);
register_mask = (SWMODE_MASK << SW3MODE_LSB) |
(SWMODE_MASK << SW4MODE_LSB);
value &= ~register_mask;
value |= (SWMODE_AUTO << SW3MODE_LSB) |
(SWMODE_AUTO << SW4MODE_LSB);
pmic_write_reg(REG_SW_5, value, 0xffffff);
}
mc13892_register_regulator(mc13892, MC13892_SW1, &sw1_init);
mc13892_register_regulator(mc13892, MC13892_SW2, &sw2_init);
mc13892_register_regulator(mc13892, MC13892_SW3, &sw3_init);
mc13892_register_regulator(mc13892, MC13892_SW4, &sw4_init);
mc13892_register_regulator(mc13892, MC13892_SWBST, &swbst_init);
mc13892_register_regulator(mc13892, MC13892_VIOHI, &viohi_init);
mc13892_register_regulator(mc13892, MC13892_VPLL, &vpll_init);
mc13892_register_regulator(mc13892, MC13892_VDIG, &vdig_init);
mc13892_register_regulator(mc13892, MC13892_VSD, &vsd_init);
mc13892_register_regulator(mc13892, MC13892_VUSB2, &vusb2_init);
mc13892_register_regulator(mc13892, MC13892_VVIDEO, &vvideo_init);
mc13892_register_regulator(mc13892, MC13892_VAUDIO, &vaudio_init);
mc13892_register_regulator(mc13892, MC13892_VCAM, &vcam_init);
mc13892_register_regulator(mc13892, MC13892_VGEN1, &vgen1_init);
mc13892_register_regulator(mc13892, MC13892_VGEN2, &vgen2_init);
mc13892_register_regulator(mc13892, MC13892_VGEN3, &vgen3_init);
mc13892_register_regulator(mc13892, MC13892_VUSB, &vusb_init);
mc13892_register_regulator(mc13892, MC13892_GPO1, &gpo1_init);
mc13892_register_regulator(mc13892, MC13892_GPO2, &gpo2_init);
mc13892_register_regulator(mc13892, MC13892_GPO3, &gpo3_init);
mc13892_register_regulator(mc13892, MC13892_GPO4, &gpo4_init);
regulator_has_full_constraints();
return 0;
}
static struct mc13892_platform_data mc13892_plat = {
.init = mc13892_regulator_init,
};
static struct spi_board_info __initdata mc13892_spi_device = {
.modalias = "pmic_spi",
/*
.irq = IOMUX_TO_IRQ(MX51_PIN_GPIO1_8),
*/
.irq = IOMUX_TO_IRQ(MX51_PIN_GPIO1_6),
.max_speed_hz = 1000000, /* max spi SCK clock speed in HZ */
.bus_num = 1,
.chip_select = 0,
.platform_data = &mc13892_plat,
};
int __init mx51_efikasb_init_mc13892(void)
{
/* ron: disable power gate */
printk(" [VV] mx51_efikasb_init_mc13892.\n");
pmic_write_reg(REG_POWER_MISC, ~(PWGT1SPIEN|PWGT2SPIEN),
(PWGT1SPIEN|PWGT2SPIEN));
return spi_register_board_info(&mc13892_spi_device, 1);
}

View File

@@ -0,0 +1,352 @@
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <mach/hardware.h>
#include <mach/gpio.h>
#include <asm/io.h>
#include "mx51_efikasb.h"
#include "mx51_pins.h"
#include "iomux.h"
extern int mxc_get_power_status(iomux_pin_name_t pin);
extern void mxc_power_on_wlan(int on);
extern void mxc_power_on_wwan(int on);
extern void mxc_power_on_bt(int on);
extern void mxc_power_on_camera(int on);
extern void mxc_power_on_agps(int on);
extern void mxc_reset_agps(void);
extern int suspend_device_by_name(char *dev_name, pm_message_t state);
extern int resume_device_by_name(char *dev_name, pm_message_t state);
static void suspend_usbh2_wq_handler(struct work_struct *work);
static DECLARE_MUTEX(usbh2_mx);
static DECLARE_DELAYED_WORK(suspend_usbh2_work, suspend_usbh2_wq_handler);
#define BIT_BT_PWRON 1
#define BIT_WWAN_PWRON 2
#define BIT_CAM_PWRON 3
extern int wireless_sw_state;
static int usbh2_dev_power = 0;
static int usbh2_is_suspend = 0;
static void suspend_usbh2_wq_handler(struct work_struct *work)
{
int error;
down(&usbh2_mx);
if (usbh2_dev_power == 0) {
error = suspend_device_by_name("fsl-ehci.1", PMSG_SUSPEND);
if (error == 0)
usbh2_is_suspend = 1;
};
up(&usbh2_mx);
}
static ssize_t wlan_pwr_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
int val;
val = mxc_get_power_status(WLAN_PWRON_PIN);
return sprintf(buf, "%s\n", val ? "on" : "off");
}
static ssize_t wlan_pwr_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
int val;
if (strncmp(buf, "on", 2) == 0) {
val = 1;
} else if (strncmp(buf, "off", 3) == 0)
val = 0;
else
return -EINVAL;
mxc_power_on_wlan(val);
return count;
}
static ssize_t wwan_pwr_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
int val;
val = mxc_get_power_status(WWAN_PWRON_PIN);
return sprintf(buf, "%s\n", val ? "on" : "off");
}
static ssize_t wwan_pwr_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
int old_usbh2_dev_power = usbh2_dev_power;
int error;
int val;
if (strncmp(buf, "on", 2) == 0) {
val = 1;
} else if (strncmp(buf, "off", 3) == 0)
val = 0;
else
return -EINVAL;
down(&usbh2_mx);
if (usbh2_dev_power == 0 && val == 1 && usbh2_is_suspend == 1) {
error = resume_device_by_name("fsl-ehci.1", PMSG_RESUME);
if (error == 0)
usbh2_is_suspend = 0;
}
mxc_power_on_wwan(val);
if (val)
usbh2_dev_power |= (1 << BIT_WWAN_PWRON);
else
usbh2_dev_power &= ~(1 << BIT_WWAN_PWRON);
up(&usbh2_mx);
printk("usbh2_dev_power=%x\n", usbh2_dev_power);
if (old_usbh2_dev_power != 0 && usbh2_dev_power == 0)
schedule_delayed_work(&suspend_usbh2_work, msecs_to_jiffies(4000));
return count;
}
static ssize_t bt_pwr_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
int val;
val = mxc_get_power_status(BT_PWRON_PIN);
return sprintf(buf, "%s\n", val ? "on" : "off");
}
static ssize_t bt_pwr_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
int old_usbh2_dev_power = usbh2_dev_power;
int error;
int val;
if (strncmp(buf, "on", 2) == 0) {
val = 1;
} else if (strncmp(buf, "off", 3) == 0)
val = 0;
else
return -EINVAL;
down(&usbh2_mx);
if (usbh2_dev_power == 0 && val == 1 && usbh2_is_suspend == 1) {
error = resume_device_by_name("fsl-ehci.1", PMSG_RESUME);
if (error == 0)
usbh2_is_suspend = 0;
}
mxc_power_on_bt(val);
if (val)
usbh2_dev_power |= (1 << BIT_BT_PWRON);
else
usbh2_dev_power &= ~(1 << BIT_BT_PWRON);
up(&usbh2_mx);
printk("usbh2_dev_power=%x\n", usbh2_dev_power);
if (old_usbh2_dev_power != 0 && usbh2_dev_power == 0)
schedule_delayed_work(&suspend_usbh2_work, msecs_to_jiffies(4000));
return count;
}
static ssize_t camera_pwr_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
int val;
val = mxc_get_power_status(CAM_PWRON_PIN);
return sprintf(buf, "%s\n", val ? "on" : "off");
}
static ssize_t camera_pwr_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
int val;
int old_usbh2_dev_power = usbh2_dev_power;
int error;
if (strncmp(buf, "on", 2) == 0)
val = 1;
else if (strncmp(buf, "off", 3) == 0)
val = 0;
else
return -EINVAL;
down(&usbh2_mx);
if (usbh2_dev_power == 0 && val == 1 && usbh2_is_suspend == 1) {
error = resume_device_by_name("fsl-ehci.1", PMSG_RESUME);
if (error == 0)
usbh2_is_suspend = 0;
}
mxc_power_on_camera(val);
if (val)
usbh2_dev_power |= (1 << BIT_CAM_PWRON);
else
usbh2_dev_power &= ~(1 << BIT_CAM_PWRON);
up(&usbh2_mx);
printk("usbh2_dev_power=%x\n", usbh2_dev_power);
if (old_usbh2_dev_power != 0 && usbh2_dev_power == 0)
schedule_delayed_work(&suspend_usbh2_work, msecs_to_jiffies(4000));
return count;
}
static ssize_t agps_pwr_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
int val;
val = mxc_get_power_status(AGPS_PWRON_PIN);
return sprintf(buf, "%s\n", val ? "on" : "off");
}
static ssize_t agps_pwr_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
int val;
if (strncmp(buf, "on", 2) == 0)
val = 1;
else if (strncmp(buf, "off", 3) == 0)
val = 0;
else
return -EINVAL;
mxc_power_on_agps(val);
if (val == 1)
mxc_reset_agps();
return count;
}
#define DISABLE_POWER_DOWN 0
#define PRECHARGE_POWER_DOWN 1
#define ACTIVE_POWER_DOWN_64 2
#define ACTIVE_POWER_DOWN_128 3
static void mxc_set_ddr2_power_down(int arg)
{
u32 value;
value = __raw_readl(IO_ADDRESS(ESDCTL_BASE_ADDR + 0x08));
value |= (arg & 0x03) << 12;
__raw_writel(value, IO_ADDRESS(ESDCTL_BASE_ADDR + 0x08));
value = __raw_readl(IO_ADDRESS(ESDCTL_BASE_ADDR + 0x00));
value |= (arg & 0x03) << 12;
__raw_writel(value, IO_ADDRESS(ESDCTL_BASE_ADDR + 0x00));
}
static ssize_t ddr2_pd_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
u32 value;
value = __raw_readl(IO_ADDRESS(ESDCTL_BASE_ADDR + 0x08));
value = (value & 0x3000) >> 12;
return sprintf(buf, "%d\n", value);
}
static ssize_t ddr2_pd_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *end;
int arg;
arg = simple_strtoul(buf, &end, 10);
if (arg >= 4 || arg < 0) {
return -EINVAL;
}
mxc_set_ddr2_power_down(arg);
return count;
}
static struct kobj_attribute wlan_pwr_attribute =
__ATTR(wlan_power, 0666, wlan_pwr_show, wlan_pwr_store);
static struct kobj_attribute wwan_pwr_attribute =
__ATTR(wwan_power, 0666, wwan_pwr_show, wwan_pwr_store);
static struct kobj_attribute bt_pwr_attribute =
__ATTR(bt_power, 0666, bt_pwr_show, bt_pwr_store);
static struct kobj_attribute camera_pwr_attribute =
__ATTR(camera_power, 0666, camera_pwr_show, camera_pwr_store);
static struct kobj_attribute agps_pwr_attribute =
__ATTR(agps_power, 0666, agps_pwr_show, agps_pwr_store);
static struct kobj_attribute ddr2_pd_attribute =
__ATTR(ddr2_pd, 0666, ddr2_pd_show, ddr2_pd_store);
static struct attribute *pwr_attrs[] = {
&wlan_pwr_attribute.attr,
&wwan_pwr_attribute.attr,
&bt_pwr_attribute.attr,
&camera_pwr_attribute.attr,
&agps_pwr_attribute.attr,
&ddr2_pd_attribute.attr,
NULL,
};
static struct attribute_group pwr_attr_group = {
.attrs = pwr_attrs,
};
static struct platform_device mxc_pwr_sw_device = {
.name = "mxc_pwr_sw",
};
static int __init mxc_init_pwr_sw(void)
{
int retval;
static struct kobject *pwr_sw_kobj;
init_MUTEX(&usbh2_mx);
usbh2_dev_power |= mxc_get_power_status(BT_PWRON_PIN) << BIT_BT_PWRON;
usbh2_dev_power |= mxc_get_power_status(WWAN_PWRON_PIN) << BIT_WWAN_PWRON;
usbh2_dev_power |= mxc_get_power_status(CAM_PWRON_PIN) << BIT_CAM_PWRON;
if (usbh2_dev_power == 0)
suspend_device_by_name("fsl-ehci.1", PMSG_SUSPEND);
mxc_set_ddr2_power_down(ACTIVE_POWER_DOWN_64); /* ron: set DDR2 power down */
platform_device_register(&mxc_pwr_sw_device);
pwr_sw_kobj = kobject_create_and_add("power_control",
&mxc_pwr_sw_device.dev.kobj);
if (!pwr_sw_kobj)
return -ENOMEM;
retval = sysfs_create_group(pwr_sw_kobj, &pwr_attr_group);
if (retval) {
kobject_put(pwr_sw_kobj);
return retval;
}
return 0;
}
late_initcall(mxc_init_pwr_sw);

View File

@@ -23,6 +23,7 @@
#include <mach/hardware.h>
#include <mach/mxc_uart.h>
#include "serial.h"
#include "mx51_efikasb.h"
#if defined(CONFIG_SERIAL_MXC) || defined(CONFIG_SERIAL_MXC_MODULE)
@@ -36,16 +37,22 @@
static uart_mxc_port mxc_ports[] = {
[0] = {
.port = {
.membase = (void *)IO_ADDRESS(UART1_BASE_ADDR),
.mapbase = UART1_BASE_ADDR,
.iotype = SERIAL_IO_MEM,
.irq = UART1_INT1,
.fifosize = 32,
.flags = ASYNC_BOOT_AUTOCONF,
.line = 0,
},
.ints_muxed = 1,
.mode = MODE_DCE,
.ir_mode = NO_IRDA,
.enabled = 1,
.ints_muxed = UART1_MUX_INTS,
.irqs = {UART1_INT2, UART1_INT3},
.mode = UART1_MODE,
.ir_mode = UART1_IR,
.enabled = UART1_ENABLED,
.hardware_flow = UART1_HW_FLOW,
.cts_threshold = UART1_UCR4_CTSTL,
.dma_enabled = UART1_DMA_ENABLE,
.dma_rxbuf_size = UART1_DMA_RXBUFSIZE,
.rx_threshold = UART1_UFCR_RXTL,
.tx_threshold = UART1_UFCR_TXTL,
@@ -55,16 +62,22 @@ static uart_mxc_port mxc_ports[] = {
},
[1] = {
.port = {
.membase = (void *)IO_ADDRESS(UART2_BASE_ADDR),
.mapbase = UART2_BASE_ADDR,
.iotype = SERIAL_IO_MEM,
.irq = UART2_INT1,
.fifosize = 32,
.flags = ASYNC_BOOT_AUTOCONF,
.line = 1,
},
.ints_muxed = 1,
.mode = MODE_DCE,
.ir_mode = NO_IRDA,
.enabled = 1,
.ints_muxed = UART2_MUX_INTS,
.irqs = {UART2_INT2, UART2_INT3},
.mode = UART2_MODE,
.ir_mode = UART2_IR,
.enabled = UART2_ENABLED,
.hardware_flow = UART2_HW_FLOW,
.cts_threshold = UART2_UCR4_CTSTL,
.dma_enabled = UART2_DMA_ENABLE,
.dma_rxbuf_size = UART2_DMA_RXBUFSIZE,
.rx_threshold = UART2_UFCR_RXTL,
.tx_threshold = UART2_UFCR_TXTL,
@@ -74,17 +87,22 @@ static uart_mxc_port mxc_ports[] = {
},
[2] = {
.port = {
.membase = (void *)IO_ADDRESS(UART3_BASE_ADDR),
.mapbase = UART3_BASE_ADDR,
.iotype = SERIAL_IO_MEM,
.irq = UART3_INT1,
.fifosize = 32,
.flags = ASYNC_BOOT_AUTOCONF,
.line = 2,
},
.ints_muxed = 1,
.mode = MODE_DCE,
.ir_mode = NO_IRDA,
.enabled = 1,
.ints_muxed = UART3_MUX_INTS,
.irqs = {UART3_INT2, UART3_INT3},
.mode = UART3_MODE,
.ir_mode = UART3_IR,
.enabled = UART3_ENABLED,
.hardware_flow = UART3_HW_FLOW,
.cts_threshold = UART3_UCR4_CTSTL,
.dma_enabled = 1,
.dma_enabled = UART3_DMA_ENABLE,
.dma_rxbuf_size = UART3_DMA_RXBUFSIZE,
.rx_threshold = UART3_UFCR_RXTL,
.tx_threshold = UART3_UFCR_TXTL,
@@ -94,16 +112,22 @@ static uart_mxc_port mxc_ports[] = {
},
[3] = {
.port = {
.membase = (void *)IO_ADDRESS(UART4_BASE_ADDR),
.mapbase = UART4_BASE_ADDR,
.iotype = SERIAL_IO_MEM,
.irq = UART4_INT1,
.fifosize = 32,
.flags = ASYNC_BOOT_AUTOCONF,
.line = 3,
},
.ints_muxed = 1,
.mode = MODE_DCE,
.ir_mode = NO_IRDA,
.enabled = 1,
.ints_muxed = UART4_MUX_INTS,
.irqs = {UART4_INT2, UART4_INT3},
.mode = UART4_MODE,
.ir_mode = UART4_IR,
.enabled = UART4_ENABLED,
.hardware_flow = UART4_HW_FLOW,
.cts_threshold = UART4_UCR4_CTSTL,
.dma_enabled = UART4_DMA_ENABLE,
.dma_rxbuf_size = UART4_DMA_RXBUFSIZE,
.rx_threshold = UART4_UFCR_RXTL,
.tx_threshold = UART4_UFCR_TXTL,
@@ -113,16 +137,22 @@ static uart_mxc_port mxc_ports[] = {
},
[4] = {
.port = {
.membase = (void *)IO_ADDRESS(UART5_BASE_ADDR),
.mapbase = UART5_BASE_ADDR,
.iotype = SERIAL_IO_MEM,
.irq = UART5_INT1,
.fifosize = 32,
.flags = ASYNC_BOOT_AUTOCONF,
.line = 4,
},
.ints_muxed = 1,
.mode = MODE_DCE,
.ir_mode = NO_IRDA,
.enabled = 1,
.ints_muxed = UART5_MUX_INTS,
.irqs = {UART5_INT2, UART5_INT3},
.mode = UART5_MODE,
.ir_mode = UART5_IR,
.enabled = UART5_ENABLED,
.hardware_flow = UART5_HW_FLOW,
.cts_threshold = UART5_UCR4_CTSTL,
.dma_enabled = UART5_DMA_ENABLE,
.dma_rxbuf_size = UART5_DMA_RXBUFSIZE,
.rx_threshold = UART5_UFCR_RXTL,
.tx_threshold = UART5_UFCR_TXTL,
@@ -132,111 +162,41 @@ static uart_mxc_port mxc_ports[] = {
},
};
static struct resource mxc_uart_resources1[] = {
{
.start = UART1_BASE_ADDR,
.end = UART1_BASE_ADDR + 0x0B5,
.flags = IORESOURCE_MEM,
},
{
.start = MXC_INT_UART1,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device mxc_uart_device1 = {
.name = "mxcintuart",
.id = 0,
.num_resources = ARRAY_SIZE(mxc_uart_resources1),
.resource = mxc_uart_resources1,
.dev = {
.platform_data = &mxc_ports[0],
},
};
static struct resource mxc_uart_resources2[] = {
{
.start = UART2_BASE_ADDR,
.end = UART2_BASE_ADDR + 0x0B5,
.flags = IORESOURCE_MEM,
},
{
.start = MXC_INT_UART2,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device mxc_uart_device2 = {
.name = "mxcintuart",
.id = 1,
.num_resources = ARRAY_SIZE(mxc_uart_resources2),
.resource = mxc_uart_resources2,
.dev = {
.platform_data = &mxc_ports[1],
},
};
static struct resource mxc_uart_resources3[] = {
{
.start = UART3_BASE_ADDR,
.end = UART3_BASE_ADDR + 0x0B5,
.flags = IORESOURCE_MEM,
},
{
.start = MXC_INT_UART3,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device mxc_uart_device3 = {
.name = "mxcintuart",
.id = 2,
.num_resources = ARRAY_SIZE(mxc_uart_resources3),
.resource = mxc_uart_resources3,
.dev = {
.platform_data = &mxc_ports[2],
},
};
static struct resource mxc_uart_resources4[] = {
{
.start = UART4_BASE_ADDR,
.end = UART4_BASE_ADDR + 0x0B5,
.flags = IORESOURCE_MEM,
},
{
.start = MXC_INT_UART4,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device mxc_uart_device4 = {
.name = "mxcintuart",
.id = 3,
.num_resources = ARRAY_SIZE(mxc_uart_resources4),
.resource = mxc_uart_resources4,
.dev = {
.platform_data = &mxc_ports[3],
},
};
static struct resource mxc_uart_resources5[] = {
{
.start = UART5_BASE_ADDR,
.end = UART5_BASE_ADDR + 0x0B5,
.flags = IORESOURCE_MEM,
},
{
.start = MXC_INT_UART5,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device mxc_uart_device5 = {
.name = "mxcintuart",
.id = 4,
.num_resources = ARRAY_SIZE(mxc_uart_resources5),
.resource = mxc_uart_resources5,
.dev = {
.platform_data = &mxc_ports[4],
},
@@ -244,26 +204,27 @@ static struct platform_device mxc_uart_device5 = {
static int __init mxc_init_uart(void)
{
if (cpu_is_mx53() || cpu_is_mx50()) {
mxc_uart_resources1[0].start -= 0x20000000;
mxc_uart_resources1[0].end -= 0x20000000;
mxc_uart_resources2[0].start -= 0x20000000;
mxc_uart_resources2[0].end -= 0x20000000;
mxc_uart_resources3[0].start -= 0x20000000;
mxc_uart_resources3[0].end -= 0x20000000;
mxc_uart_resources4[0].start -= 0x20000000;
mxc_uart_resources4[0].end -= 0x20000000;
mxc_uart_resources5[0].start -= 0x20000000;
mxc_uart_resources5[0].end -= 0x20000000;
int i;
if (cpu_is_mx53()) {
for (i = 0; i < ARRAY_SIZE(mxc_ports); i++) {
mxc_ports[i].port.mapbase -= 0x20000000;
}
}
/* Register all the MXC UART platform device structures */
platform_device_register(&mxc_uart_device1);
platform_device_register(&mxc_uart_device2);
#if UART3_ENABLED == 1
platform_device_register(&mxc_uart_device3);
#endif /* UART3_ENABLED */
if (cpu_is_mx53()) {
#if UART4_ENABLED == 1
platform_device_register(&mxc_uart_device4);
#endif /* UART4_ENABLED */
#if UART5_ENABLED == 1
platform_device_register(&mxc_uart_device5);
#endif /* UART5_ENABLED */
}
return 0;
}

View File

@@ -14,7 +14,16 @@
#ifndef __ARCH_ARM_MACH_MX51_SERIAL_H__
#define __ARCH_ARM_MACH_MX51_SERIAL_H__
#include <mach/mxc_uart.h>
/* UART 1 configuration */
/*!
* This option allows to choose either an interrupt-driven software controlled
* hardware flow control (set this option to 0) or hardware-driven hardware
* flow control (set this option to 1).
*/
/* UART used as wakeup source */
#define UART1_HW_FLOW 0
/*!
* This specifies the threshold at which the CTS pin is deasserted by the
* RXFIFO. Set this value in Decimal to anything from 0 to 32 for
@@ -23,6 +32,10 @@
* flow control set this option to -1.
*/
#define UART1_UCR4_CTSTL 16
/*!
* This is option to enable (set this option to 1) or disable DMA data transfer
*/
#define UART1_DMA_ENABLE 0
/*!
* Specify the size of the DMA receive buffer. The minimum buffer size is 512
* bytes. The buffer size should be a multiple of 256.
@@ -43,24 +56,88 @@
*/
#define UART1_UFCR_TXTL 16
/* UART 2 configuration */
#define UART2_UCR4_CTSTL -1
#define UART2_HW_FLOW 1 /* 0 */ /*ron: Enable H/W flow control for AGPS */
#define UART2_UCR4_CTSTL 16 /* -1 */
#define UART2_DMA_ENABLE 1 /* 0 */
#define UART2_DMA_RXBUFSIZE 512
#define UART2_UFCR_RXTL 16
#define UART2_UFCR_TXTL 16
/* UART 3 configuration */
#define UART3_HW_FLOW 1
#define UART3_UCR4_CTSTL 16
#define UART3_DMA_ENABLE 1
#define UART3_DMA_RXBUFSIZE 1024
#define UART3_UFCR_RXTL 16
#define UART3_UFCR_TXTL 16
/* UART 4 configuration */
#define UART4_HW_FLOW 0
#define UART4_UCR4_CTSTL -1
#define UART4_DMA_ENABLE 0
#define UART4_DMA_RXBUFSIZE 512
#define UART4_UFCR_RXTL 16
#define UART4_UFCR_TXTL 16
/* UART 5 configuration */
#define UART5_HW_FLOW 0
#define UART5_UCR4_CTSTL -1
#define UART5_DMA_ENABLE 0
#define UART5_DMA_RXBUFSIZE 512
#define UART5_UFCR_RXTL 16
#define UART5_UFCR_TXTL 16
/*
* UART Chip level Configuration that a user may not have to edit. These
* configuration vary depending on how the UART module is integrated with
* the ARM core
*/
/*
* Is the MUXED interrupt output sent to the ARM core
*/
#define INTS_NOTMUXED 0
#define INTS_MUXED 1
/* UART 1 configuration */
/*!
* This define specifies whether the muxed ANDed interrupt line or the
* individual interrupts from the UART port is integrated with the ARM core.
* There exists a define like this for each UART port. Valid values that can
* be used are \b INTS_NOTMUXED or \b INTS_MUXED.
*/
#define UART1_MUX_INTS INTS_MUXED
/*!
* This define specifies the transmitter interrupt number or the interrupt
* number of the ANDed interrupt in case the interrupts are muxed. There exists
* a define like this for each UART port.
*/
#define UART1_INT1 MXC_INT_UART1
/*!
* This define specifies the receiver interrupt number. If the interrupts of
* the UART are muxed, then we specify here a dummy value -1. There exists a
* define like this for each UART port.
*/
#define UART1_INT2 -1
/*!
* This specifies the master interrupt number. If the interrupts of the UART
* are muxed, then we specify here a dummy value of -1. There exists a define
* like this for each UART port.
*/
#define UART1_INT3 -1
/* UART 2 configuration */
#define UART2_MUX_INTS INTS_MUXED
#define UART2_INT1 MXC_INT_UART2
#define UART2_INT2 -1
#define UART2_INT3 -1
/* UART 3 configuration */
#define UART3_MUX_INTS INTS_MUXED
#define UART3_INT1 MXC_INT_UART3
#define UART3_INT2 -1
#define UART3_INT3 -1
/* UART 4 configuration */
#define UART4_MUX_INTS INTS_MUXED
#define UART4_INT1 MXC_INT_UART4
#define UART4_INT2 -1
#define UART4_INT3 -1
/* UART 5 configuration */
#define UART5_MUX_INTS INTS_MUXED
#define UART5_INT1 MXC_INT_UART5
#define UART5_INT2 -1
#define UART5_INT3 -1
#endif /* __ARCH_ARM_MACH_MX51_SERIAL_H__ */

View File

@@ -29,7 +29,8 @@ extern void gpio_usbotg_utmi_inactive(void);
extern void __init mx5_usb_dr_init(void);
extern void __init mx5_usbh1_init(void);
extern void __init mx5_usbh2_init(void);
// micken: name clash with efikamx mainline ?
extern void __init mx51_usbh2_init(void);
typedef void (*driver_vbus_func)(bool);
extern void mx5_set_host1_vbus_func(driver_vbus_func);
extern void mx5_set_otghost_vbus_func(driver_vbus_func);

View File

@@ -209,6 +209,24 @@ struct mxc_w1_config {
* data. It includes the SPI bus number and the maximum number of
* slaves/chips it supports.
*/
/* micken: added battery support */
struct mxc_battery_platform_data {
int batt_in_irq;
int ac_in_irq;
int batt_low_irq;
int (*get_batt_in_status) (void);
int (*get_ac_in_status) (void);
int (*get_batt_low_status) (void);
void (*set_batt_low_led) (int);
};
struct mxc_spi_master {
/*!
* SPI Master's bus number.
@@ -314,6 +332,11 @@ struct mxc_lcd_platform_data {
char *io_reg;
char *core_reg;
char *analog_reg;
// micken: backlight etc for efikasb
void (*power_on_lcd) (int);
void (*power_on_lvds) (int);
void (*turn_on_backlight) (int);
void (*lvds_enable) (int);
void (*reset) (void);
};

View File

@@ -696,6 +696,98 @@ static int dpm_suspend(pm_message_t state)
return error;
}
/* ron: suspend the USB Host if all device are disconnected */
// Inherited Ron's added, Byron 2010.03.10 // micken : added to efikasb kernel 2010.09.03
int suspend_device_by_name(char *dev_name, pm_message_t state)
{
struct list_head list;
int error = 0;
INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx);
while (!list_empty(&dpm_list)) {
struct device *dev = to_device(dpm_list.prev);
get_device(dev);
mutex_unlock(&dpm_list_mtx);
if(strcmp(dev_name, dev->kobj.name) == 0) {
//error = suspend_device(dev, state);
error= device_suspend(dev,state);//2010.03.10 ,Byron
mutex_lock(&dpm_list_mtx);
if (error) {
pm_dev_err(dev, state, "", error);
put_device(dev);
break;
}
dev->power.status = DPM_OFF;
break;
}
if (!list_empty(&dev->power.entry))
list_move(&dev->power.entry, &list);
put_device(dev);
}
list_splice(&list, dpm_list.prev);
mutex_unlock(&dpm_list_mtx);
return error;
}
EXPORT_SYMBOL(suspend_device_by_name);
// inheriated V28 Byron 2010.03.10 // micken: added to efikasb 2010.09.03
int resume_device_by_name(char *dev_name, pm_message_t state)
{
struct list_head list;
int error = 0;
INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx);
transition_started = false;
while (!list_empty(&dpm_list)) {
struct device *dev = to_device(dpm_list.next);
get_device(dev);
if(strcmp(dev_name, dev->kobj.name) == 0) {
if (dev->power.status >= DPM_OFF) {
dev->power.status = DPM_RESUMING;
mutex_unlock(&dpm_list_mtx);
//error = resume_device(dev, state);
error=device_resume(dev,state); //2010.03.10 Byron
mutex_lock(&dpm_list_mtx);
if (error)
pm_dev_err(dev, state, "", error);
} else if (dev->power.status == DPM_SUSPENDING) {
/* Allow new children of the device to be registered */
dev->power.status = DPM_RESUMING;
}
break;
}
if (!list_empty(&dev->power.entry))
list_move_tail(&dev->power.entry, &list);
put_device(dev);
}
list_splice(&list, &dpm_list);
mutex_unlock(&dpm_list_mtx);
return error;
}
EXPORT_SYMBOL(resume_device_by_name);
/**
* device_prepare - Execute the ->prepare() callback(s) for given device.
* @dev: Device.

View File

@@ -51,6 +51,13 @@ MODULE_PARM_DESC(debug, "HID debugging (0=off, 1=probing info, 2=continuous data
EXPORT_SYMBOL_GPL(hid_debug);
#endif
//inherited V28 ,2010.03.10 Byron
#ifdef CONFIG_MACH_MX51_EFIKASB
extern void mxc_reset_idle_timer(void);
#endif
/*
* Register a new report for a device.
*/
@@ -917,6 +924,11 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
continue;
}
/* ron: add kunlun custom Fn + XX key */
//inherited Ron's code, Byron 2010.03.10
if((field->usage[n].hid & HID_USAGE_PAGE) == HID_UP_CUSTOM)
hid_process_event(hid, field, &field->usage[n], value[n], interrupt);
if (field->value[n] >= min && field->value[n] <= max
&& field->usage[field->value[n] - min].hid
&& search(value, field->value[n], count))

View File

@@ -296,6 +296,8 @@
#define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219
//micken: #define USB_DEVICE_ID_LOGITECH_G15_GAMEPANEL 0xc227 define
#define USB_DEVICE_ID_LOGITECH_G15_GAMEPANEL 0xc227
#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283
#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286
#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294

View File

@@ -36,7 +36,7 @@
#include <linux/hid-debug.h>
#include <linux/hidraw.h>
#include "usbhid.h"
#include "../hid-ids.h" // micken: for logitech id
/*
* Version Information
*/
@@ -552,6 +552,12 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
}
EXPORT_SYMBOL_GPL(usbhid_submit_report);
#ifdef CONFIG_MACH_MX51_EFIKASB
extern void mxc_turn_on_caps_led(int on);
#endif
static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct hid_device *hid = input_get_drvdata(dev);
@@ -583,6 +589,14 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un
}
usbhid_submit_report(hid, field->report, USB_DIR_OUT);
#ifdef CONFIG_MACH_MX51_EFIKASB
/* ron: Caps Lock LED */
if(code == 0x01) {
mxc_turn_on_caps_led(value);
}
#endif
return 0;
}
@@ -1130,6 +1144,18 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
hid->bus = BUS_USB;
hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
hid->product = le16_to_cpu(dev->descriptor.idProduct);
#ifdef CONFIG_MACH_MX51_EFIKASB
/* ron: skip the Logitech G15 GamePanel LCD */
// inherited Ron's code , Byron 2010.03.10
if(hid->vendor == USB_VENDOR_ID_LOGITECH &&
hid->product == USB_DEVICE_ID_LOGITECH_G15_GAMEPANEL) {
ret = -ENODEV;
goto err;
}
#endif
hid->name[0] = 0;
if (intf->cur_altsetting->desc.bInterfaceProtocol ==
USB_INTERFACE_PROTOCOL_MOUSE)

View File

@@ -36,6 +36,15 @@ config LEDS_MC13892
tristate "LED Support for mc13892 pmic"
depends on LEDS_CLASS && MXC_MC13892_LIGHT
config LEDS_EFIKASB
tristate "LED Support for Efikasb Platform"
depends on LEDS_CLASS && MACH_MX51_EFIKASB
help
This option enable support for the LEDs on Efikasb Platform.
config LEDS_LOCOMO
tristate "LED Support for Locomo device"
depends on LEDS_CLASS && SHARP_LOCOMO

View File

@@ -8,6 +8,7 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
obj-$(CONFIG_LEDS_MC13892) += leds-mc13892.o
obj-$(CONFIG_LEDS_EFIKASB) += leds-efikasb.o
obj-$(CONFIG_LEDS_STMP378X) += leds-stmp378x-pwm.o
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o

102
drivers/leds/leds-efikasb.c Normal file
View File

@@ -0,0 +1,102 @@
/*
* Copyright 2009 Pegatron Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
extern void mxc_turn_on_alarm_led(int on);
static void msg_alarm_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
mxc_turn_on_alarm_led(value ? 1 : 0);
}
static struct led_classdev msg_alarm_led = {
.name = "msg-alarm-led",
.brightness_set = msg_alarm_led_set,
};
static int efikasb_led_remove(struct platform_device *dev)
{
led_classdev_unregister(&msg_alarm_led);
return 0;
}
static int efikasb_led_probe(struct platform_device *dev)
{
int ret;
ret = led_classdev_register(&dev->dev, &msg_alarm_led);
if (ret < 0) {
dev_err(&dev->dev, "Register Message Alarm LED failed\n");
return -ENODEV;
}
printk(KERN_INFO "Registered Efikasb Message Alarm LED\n");
return 0;
}
#ifdef CONFIG_PM
static int efikasb_led_suspend(struct platform_device *dev, pm_message_t state)
{
led_classdev_suspend(&msg_alarm_led);
return 0;
}
static int efikasb_led_resume(struct platform_device *dev)
{
led_classdev_resume(&msg_alarm_led);
return 0;
}
#else
#define efikasb_led_suspend NULL
#define efikasb_led_resume NULL
#endif
static struct platform_driver efikasb_led_driver = {
.probe = efikasb_led_probe,
.remove = efikasb_led_remove,
.suspend = efikasb_led_suspend,
.resume = efikasb_led_resume,
.driver = {
.name = "efikasb_leds",
.owner = THIS_MODULE,
},
};
static int __init efikasb_led_init(void)
{
return platform_driver_register(&efikasb_led_driver);
}
static void __exit efikasb_led_exit(void)
{
platform_driver_unregister(&efikasb_led_driver);
}
module_init(efikasb_led_init);
module_exit(efikasb_led_exit);
MODULE_DESCRIPTION("Led driver for Efikasb LEDs");
MODULE_AUTHOR("Ron Lee <ron1_lee@pegatroncorp.com> Pegatron Inc.");
MODULE_LICENSE("GPL");

View File

@@ -238,6 +238,15 @@ config MXS_PERSISTENT
depends on ARCH_MXS
default y
config MX51_EFIKASB_DEBUG
tristate "MX51 Efikasb Debug Driver"
depends on MACH_MX51_EFIKASB
default n
---help---
MX51 debug driver of Efikasb board for Engineer.
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"

View File

@@ -21,5 +21,6 @@ obj-$(CONFIG_HP_ILO) += hpilo.o
obj-$(CONFIG_ISL29003) += isl29003.o
obj-$(CONFIG_C2PORT) += c2port/
obj-$(CONFIG_MXS_PERSISTENT) += mxs-persistent.o
obj-$(CONFIG_MX51_EFIKASB_DEBUG) += mx51_efikasb_debug.o
obj-y += eeprom/
obj-y += cb710/

View File

@@ -0,0 +1,657 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <linux/pmic_external.h>
#include <linux/pmic_status.h>
#include <linux/i2c.h>
#include <mach/hardware.h>
static ssize_t reg_dump_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return 0;
}
static ssize_t reg_dump_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *end;
char *token;
char *running;
const char delimiters[] = " ";
u32 addr, value;
int len = 1;
int i;
running = (char *)buf;
token = strsep(&running, delimiters);
addr = simple_strtoul(token, &end, 16);
token = strsep(&running, delimiters);
if(token != NULL)
len = simple_strtoul(token, &end, 10);
if(len <= 1)
len = 1;
for (i = 0; i < len; i ++) {
value = __raw_readl(IO_ADDRESS(addr));
printk("[0x%08x] = 0x%08x\n", addr, value);
addr += 4;
}
return count;
}
static ssize_t reg_write_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return 0;
}
static ssize_t reg_write_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *end;
char *token;
char *running;
const char delimiters[] = "=";
u32 addr, value;
running = (char *)buf;
token = strsep(&running, delimiters);
addr = simple_strtoul(token, &end, 16);
token = strsep(&running, delimiters);
if(token != NULL) {
value = simple_strtoul(token, &end, 16);
} else {
value = __raw_readl(IO_ADDRESS(addr));
printk("[0x%08x] = 0x%08x\n", addr, value);
return -EINVAL;
}
__raw_writel(value, IO_ADDRESS(addr));
value = __raw_readl(IO_ADDRESS(addr));
printk("[0x%08x] = 0x%08x\n", addr, value);
return count;
}
struct pmic_reg_tbl {
char *name;
int reg_addr;
};
struct pmic_reg_tbl pmic_reg_tbl[] = {
{"INT_STATUS0",REG_INT_STATUS0},
{"INT_MASK0",REG_INT_MASK0},
{"INT_SENSE0",REG_INT_SENSE0},
{"INT_STATUS1",REG_INT_STATUS1},
{"INT_MASK1",REG_INT_MASK1},
{"INT_SENSE1",REG_INT_SENSE1},
{"PU_MODE_S",REG_PU_MODE_S},
{"ID",REG_IDENTIFICATION},
{"UNUSED0",REG_UNUSED0},
{"ACC0",REG_ACC0},
{"ACC1",REG_ACC1},
{"UNUSED1",REG_UNUSED1},
{"UNUSED2",REG_UNUSED2},
{"POWER_CTL0",REG_POWER_CTL0},
{"POWER_CTL1",REG_POWER_CTL1},
{"POWER_CTL2",REG_POWER_CTL2},
{"REGEN_ASSIGN",REG_REGEN_ASSIGN},
{"UNUSED3",REG_UNUSED3},
{"MEM_A",REG_MEM_A},
{"MEM_B",REG_MEM_B},
{"RTC_TIME",REG_RTC_TIME},
{"RTC_ALARM",REG_RTC_ALARM},
{"RTC_DAY",REG_RTC_DAY},
{"RTC_DAY_ALARM",REG_RTC_DAY_ALARM},
{"SW0",REG_SW_0},
{"SW1",REG_SW_1},
{"SW2",REG_SW_2},
{"SW3",REG_SW_3},
{"SW4",REG_SW_4},
{"SW5",REG_SW_5},
{"SETTING0",REG_SETTING_0},
{"SETTING1",REG_SETTING_1},
{"MODE_0",REG_MODE_0},
{"MODE_1",REG_MODE_1},
{"POWER_MISC",REG_POWER_MISC},
{"UNUSED4",REG_UNUSED4},
{"UNUSED5",REG_UNUSED5},
{"UNUSED6",REG_UNUSED6},
{"UNUSED7",REG_UNUSED7},
{"UNUSED8",REG_UNUSED8},
{"UNUSED9",REG_UNUSED9},
{"UNUSED10",REG_UNUSED10},
{"UNUSED11",REG_UNUSED11},
{"ADC0",REG_ADC0},
{"ADC1",REG_ADC1},
{"ADC2",REG_ADC2},
{"ADC3",REG_ADC3},
{"ADC4",REG_ADC4},
{"CHARGE",REG_CHARGE},
{"USB0",REG_USB0},
{"USB1",REG_USB1},
{"LED_CTL0",REG_LED_CTL0},
{"LED_CTL1",REG_LED_CTL1},
{"LED_CTL2",REG_LED_CTL2},
{"LED_CTL3",REG_LED_CTL3},
{"UNUSED12",REG_UNUSED12},
{"UNUSED13",REG_UNUSED13},
{"TRIM0",REG_TRIM0},
{"TRIM1",REG_TRIM1},
{"TEST0",REG_TEST0},
{"TEST1",REG_TEST1},
{"TEST2",REG_TEST2},
{"TEST3",REG_TEST3},
{"TEST4",REG_TEST4},
};
static ssize_t pmic_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
u32 value;
int i;
for(i = 0; i < ARRAY_SIZE(pmic_reg_tbl); i++) {
pmic_read(i, &value);
printk("%s: [%d] = 0x%08x\n", pmic_reg_tbl[i].name, i, value);
};
return 0/* strlen(buf) */;
}
static ssize_t pmic_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *end;
char *token;
char *running;
const char delimiters[] = "=";
u32 addr, value;
running = (char *)buf;
token = strsep(&running, delimiters);
addr = simple_strtoul(token, &end, 10);
if(addr > 63) {
return -EINVAL;
}
token = strsep(&running, delimiters);
if(token != NULL) {
value = simple_strtoul(token, &end, 16);
} else {
value = pmic_read(addr, &value);
printk("[%d] = 0x%08x\n", addr, value);
return -EINVAL;
}
pmic_write(addr, value);
pmic_read(addr, &value);
printk("[%d] = 0x%08x\n", addr, value);
return count;
}
static ssize_t edid_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
u8 buf0[2] = {0, 0};
int data = 0;
u16 addr = 0x50;
u8 edid[128];
int i;
struct i2c_adapter *adp;
struct i2c_msg msg[2] = {
{
.addr = addr,
.flags = 0,
.len = 1,
.buf = buf0,
}, {
.addr = addr,
.flags = I2C_M_RD,
.len = 128,
.buf = edid,
},
};
adp = i2c_get_adapter(1);
data = i2c_transfer(adp, msg, 2);
if(data <= 0)
return -ENODEV;
for (i = 0; i < 128; i ++) {
printk("[%x]=%x\n", i, edid[i]);
}
return 0;
}
#define SBS_MFG_ACCESS 0x00
#define SBS_REMAIN_CAPACITY_ALARM 0x01
#define SBS_REMNAIN_TIME_ALARM 0x02
#define SBS_BATTERY_MODE 0x03
#define SBS_AT_RATE 0x04
#define SBS_AT_RATE_TIME_TO_FULL 0x05
#define SBS_AT_RATE_TIME_TO_EMPTY 0x06
#define SBS_AT_RATE_OK 0x07
#define SBS_TEMP 0x08
#define SBS_VOLTAGE 0x09
#define SBS_CURRENT 0x0A
#define SBS_AVG_CURRENT 0x0B
#define SBS_MAX_ERROR 0x0C
#define SBS_REL_STATE_OF_CHARGE 0x0D
#define SBS_ABS_STATE_OF_CHARGE 0x0E
#define SBS_REMAIN_CAPABILITY 0x0F
#define SBS_FULL_CHARGE_CAPACITY 0x10
#define SBS_RUN_TIME_TO_EMPTY 0x11
#define SBS_AVG_TIME_TO_EMPTY 0x12
#define SBS_AVG_TIME_TO_FULL 0x13
#define SBS_CHARGE_CURRENT 0x14
#define SBS_CHARGE_VOLTAGE 0x15
#define SBS_BATTERY_STATUS 0x16
#define SBS_CYCLE_COUNT 0x17
#define SBS_DESIGN_CAPACITY 0x18
#define SBS_DESIGN_VOLTAGE 0x19
#define SBS_SPEC_INFO 0x1A
#define SBS_MFG_DATE 0x1B
#define SBS_SERIAL_NO 0x1C
#define SBS_MFG_NAME 0x20
#define SBS_DEV_NAME 0x21
#define SBS_DEV_CHEMISTRY 0x22
#define SBS_MFG_DATA 0x23
#define SBS_BATTERY_USAGE 0x30
#define SBS_PERMANENT_FAILURE 0x31
#define SBS_BATTERY_LOG1 0x32
#define SBS_BATTERY_LOG2 0x33
#define SBS_FET_TEMP 0x3B
#define SBS_OPTION_MFG_FUNC5 0x2F
#define SBS_OPTION_MFG_FUNC4 0x3C
#define SBS_OPTION_MFG_FUNC3 0x3D
#define SBS_OPTION_MFG_FUNC2 0x3E
#define SBS_OPTION_MFG_FUNC1 0x3F
struct sbs_reg_tbl {
char *name;
unsigned char reg_offset;
};
static struct sbs_reg_tbl reg_tbl[] = {
{"Manufacture Access", SBS_MFG_ACCESS},
{"Remain Capacity Alarm", SBS_REMAIN_CAPACITY_ALARM },
{"Remain Time Alarm", SBS_REMNAIN_TIME_ALARM},
{"Battery Mode", SBS_BATTERY_MODE},
{"AT Rate", SBS_AT_RATE},
{"AT Rate Time To Full", SBS_AT_RATE_TIME_TO_FULL},
{"AT Rate Time To Empty", SBS_AT_RATE_TIME_TO_EMPTY},
{"AT Rate OK", SBS_AT_RATE_OK},
{"Temperature", SBS_TEMP},
{"Voltage", SBS_VOLTAGE},
{"Current", SBS_CURRENT},
{"Average Current", SBS_AVG_CURRENT},
{"Maximum Error", SBS_MAX_ERROR},
{"Relative State of Charge", SBS_REL_STATE_OF_CHARGE},
{"Absolute State of Charge", SBS_ABS_STATE_OF_CHARGE},
{"Remain Capacity", SBS_REMAIN_CAPABILITY},
{"Full Charge Capacity", SBS_FULL_CHARGE_CAPACITY},
{"Run Time To Empty", SBS_RUN_TIME_TO_EMPTY},
{"Average Time To Empty", SBS_AVG_TIME_TO_EMPTY},
{"Average Time To Full", SBS_AVG_TIME_TO_FULL},
{"Charge Current", SBS_CHARGE_CURRENT},
{"Charge Voltage", SBS_CHARGE_VOLTAGE},
{"Battery Status", SBS_BATTERY_STATUS},
{"Cycle Count", SBS_CYCLE_COUNT},
{"Design Capacity", SBS_DESIGN_CAPACITY},
{"Design Voltage", SBS_DESIGN_VOLTAGE},
{"Spec Info", SBS_SPEC_INFO},
{"Manufacture Date", SBS_MFG_DATE},
{"Serial Number", SBS_SERIAL_NO},
{"Manufacture Name", SBS_MFG_NAME},
{"Device Name", SBS_DEV_NAME},
{"Device Chemistry", SBS_DEV_CHEMISTRY},
{"Manufacture Date", SBS_MFG_DATA},
{"Battery Usage", SBS_BATTERY_USAGE},
{"Permanent Failure", SBS_PERMANENT_FAILURE},
{"Battery Log1", SBS_BATTERY_LOG1},
{"Battery Log2", SBS_BATTERY_LOG2},
{"FET Temperature", SBS_FET_TEMP},
{"Optional Mfg Func5", SBS_OPTION_MFG_FUNC5},
{"Optional Mfg Func4", SBS_OPTION_MFG_FUNC4},
{"Optional Mfg Func3", SBS_OPTION_MFG_FUNC3},
{"Optional Mfg Func2", SBS_OPTION_MFG_FUNC2},
{"Optional Mfg Func1", SBS_OPTION_MFG_FUNC1},
};
static ssize_t battery_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
struct i2c_adapter *adp;
union i2c_smbus_data data;
int status;
int i;
adp = i2c_get_adapter(1);
for (i = 0; i < ARRAY_SIZE(reg_tbl); i++) {
status = i2c_smbus_xfer(adp, 0x0b, 0,
I2C_SMBUS_READ, reg_tbl[i].reg_offset,
I2C_SMBUS_WORD_DATA, &data);
if(status < 0)
return -ENODEV;
printk("%s: 0x%04x\n", reg_tbl[i].name, data.word);
}
return 0;
}
static ssize_t mtl017_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
struct i2c_adapter *adp;
union i2c_smbus_data data;
int status;
int i;
adp = i2c_get_adapter(1);
for (i = 0; i < 0xff; i++) {
status = i2c_smbus_xfer(adp, 0x3a, 0,
I2C_SMBUS_READ, i,
I2C_SMBUS_BYTE_DATA, &data);
if(status < 0)
return -ENODEV;
printk("[0x%x]: 0x%02x\n", i, data.byte);
}
return 0;
}
static ssize_t mtl017_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
return count;
}
#define SGTL5000_CHIP_ID 0x0000
#define SGTL5000_CHIP_DIG_POWER 0x0002
#define SGTL5000_CHIP_CLK_CTRL 0x0004
#define SGTL5000_CHIP_I2S_CTRL 0x0006
#define SGTL5000_CHIP_SSS_CTRL 0x000a
#define SGTL5000_CHIP_ADCDAC_CTRL 0x000e
#define SGTL5000_CHIP_DAC_VOL 0x0010
#define SGTL5000_CHIP_PAD_STRENGTH 0x0014
#define SGTL5000_CHIP_ANA_ADC_CTRL 0x0020
#define SGTL5000_CHIP_ANA_HP_CTRL 0x0022
#define SGTL5000_CHIP_ANA_CTRL 0x0024
#define SGTL5000_CHIP_LINREG_CTRL 0x0026
#define SGTL5000_CHIP_REF_CTRL 0x0028
#define SGTL5000_CHIP_MIC_CTRL 0x002a
#define SGTL5000_CHIP_LINE_OUT_CTRL 0x002c
#define SGTL5000_CHIP_LINE_OUT_VOL 0x002e
#define SGTL5000_CHIP_ANA_POWER 0x0030
#define SGTL5000_CHIP_PLL_CTRL 0x0032
#define SGTL5000_CHIP_CLK_TOP_CTRL 0x0034
#define SGTL5000_CHIP_ANA_STATUS 0x0036
#define SGTL5000_CHIP_SHORT_CTRL 0x003c
#define SGTL5000_CHIP_ANA_TEST2 0x003a
#define SGTL5000_DAP_CTRL 0x0100
#define SGTL5000_DAP_PEQ 0x0102
#define SGTL5000_DAP_BASS_ENHANCE 0x0104
#define SGTL5000_DAP_BASS_ENHANCE_CTRL 0x0106
#define SGTL5000_DAP_AUDIO_EQ 0x0108
#define SGTL5000_DAP_SURROUND 0x010a
#define SGTL5000_DAP_FLT_COEF_ACCESS 0x010c
#define SGTL5000_DAP_COEF_WR_B0_MSB 0x010e
#define SGTL5000_DAP_COEF_WR_B0_LSB 0x0110
#define SGTL5000_DAP_EQ_BASS_BAND0 0x0116
#define SGTL5000_DAP_EQ_BASS_BAND1 0x0118
#define SGTL5000_DAP_EQ_BASS_BAND2 0x011a
#define SGTL5000_DAP_EQ_BASS_BAND3 0x011c
#define SGTL5000_DAP_EQ_BASS_BAND4 0x011e
#define SGTL5000_DAP_MAIN_CHAN 0x0120
#define SGTL5000_DAP_MIX_CHAN 0x0122
#define SGTL5000_DAP_AVC_CTRL 0x0124
#define SGTL5000_DAP_AVC_THRESHOLD 0x0126
#define SGTL5000_DAP_AVC_ATTACK 0x0128
#define SGTL5000_DAP_AVC_DECAY 0x012a
#define SGTL5000_DAP_COEF_WR_B1_MSB 0x012c
#define SGTL5000_DAP_COEF_WR_B1_LSB 0x012e
#define SGTL5000_DAP_COEF_WR_B2_MSB 0x0130
#define SGTL5000_DAP_COEF_WR_B2_LSB 0x0132
#define SGTL5000_DAP_COEF_WR_A1_MSB 0x0134
#define SGTL5000_DAP_COEF_WR_A1_LSB 0x0136
#define SGTL5000_DAP_COEF_WR_A2_MSB 0x0138
#define SGTL5000_DAP_COEF_WR_A2_LSB 0x013a
struct sgtl_reg_tbl {
char *name;
unsigned int reg_addr;
};
struct sgtl_reg_tbl sgtl_tbl[] = {
{"ID", SGTL5000_CHIP_ID},
{"POWER", SGTL5000_CHIP_DIG_POWER},
{"CLK_CTRL", SGTL5000_CHIP_CLK_CTRL},
{"I2S_CTRL", SGTL5000_CHIP_I2S_CTRL},
{"SSS_CTRL", SGTL5000_CHIP_SSS_CTRL},
{"ADCDAC_CTRL", SGTL5000_CHIP_ADCDAC_CTRL},
{"DAC_VOL", SGTL5000_CHIP_DAC_VOL},
{"PAD_STRENGTH", SGTL5000_CHIP_PAD_STRENGTH},
{"ANA_ADC_CTRL", SGTL5000_CHIP_ANA_ADC_CTRL},
{"ANA_HP_CTRL", SGTL5000_CHIP_ANA_HP_CTRL},
{"ANA_CTRL", SGTL5000_CHIP_ANA_CTRL},
{"LINREG_CTRL", SGTL5000_CHIP_LINREG_CTRL},
{"REF_CTRL", SGTL5000_CHIP_REF_CTRL},
{"MIC_CTRL", SGTL5000_CHIP_MIC_CTRL},
{"LINE_OUT_CTRL", SGTL5000_CHIP_LINE_OUT_CTRL},
{"LINE_OUT_VOL", SGTL5000_CHIP_LINE_OUT_VOL},
{"ANA_POWER", SGTL5000_CHIP_ANA_POWER},
{"PLL_CTRL", SGTL5000_CHIP_PLL_CTRL},
{"CLK_TOP_CTRL", SGTL5000_CHIP_CLK_TOP_CTRL},
{"ANA_STATUS", SGTL5000_CHIP_ANA_STATUS},
{"SHORT_CTRL", SGTL5000_CHIP_SHORT_CTRL},
{"ANA_TEST2", SGTL5000_CHIP_ANA_TEST2},
{"DAP_CTRL", SGTL5000_DAP_CTRL},
{"DAP_PEQ", SGTL5000_DAP_PEQ},
{"DAP_BASS_ENHANCE", SGTL5000_DAP_BASS_ENHANCE},
{"DAP_BASS_ENHANCE_CTRL", SGTL5000_DAP_BASS_ENHANCE_CTRL},
{"DAP_AUDIO_EQ", SGTL5000_DAP_AUDIO_EQ},
{"DAP_SURROUND", SGTL5000_DAP_SURROUND},
{"DAP_FLT_COEF_ACCESS", SGTL5000_DAP_FLT_COEF_ACCESS},
{"DAP_COEF_WR_B0_MSB", SGTL5000_DAP_COEF_WR_B0_MSB},
{"DAP_COEF_WR_B0_LSB", SGTL5000_DAP_COEF_WR_B0_LSB},
{"DAP_EQ_BASS_BAND0", SGTL5000_DAP_EQ_BASS_BAND0},
{"DAP_EQ_BASS_BAND1", SGTL5000_DAP_EQ_BASS_BAND1},
{"DAP_EQ_BASS_BAND2", SGTL5000_DAP_EQ_BASS_BAND2},
{"DPA_EQ_BASS_BAND3", SGTL5000_DAP_EQ_BASS_BAND3},
{"DAP_EQ_BASS_BAND4", SGTL5000_DAP_EQ_BASS_BAND4},
{"DAP_MAIN_CHAIN", SGTL5000_DAP_MAIN_CHAN},
{"DAP_MIX_CHAN", SGTL5000_DAP_MIX_CHAN},
{"DAP_AVC_CTRL", SGTL5000_DAP_AVC_CTRL},
{"DAP_AVC_THRESHOLD", SGTL5000_DAP_AVC_THRESHOLD},
{"DAP_AVC_ATTACK", SGTL5000_DAP_AVC_ATTACK},
{"DAP_AVC_DECAY", SGTL5000_DAP_AVC_DECAY},
{"DAP_COEF_WR_B1_MSB", SGTL5000_DAP_COEF_WR_B1_MSB},
{"DAP_COEF_WR_B1_LSB", SGTL5000_DAP_COEF_WR_B1_LSB},
{"DAP_COEF_WR_B2_MSB", SGTL5000_DAP_COEF_WR_B2_MSB},
{"DAP_COEF_WR_B2_LSB", SGTL5000_DAP_COEF_WR_B2_LSB},
{"DAP_COEF_WR_A1_MSB", SGTL5000_DAP_COEF_WR_A1_MSB},
{"DAP_COEF_WR_A1_LSB", SGTL5000_DAP_COEF_WR_A1_LSB},
{"DAP_COEF_WR_A2_MSB", SGTL5000_DAP_COEF_WR_A2_MSB},
{"DAP_COEF_WR_A2_LSB", SGTL5000_DAP_COEF_WR_A2_LSB},
};
static unsigned int sgtl5000_hw_read(unsigned int reg)
{
struct i2c_adapter *adp;
u8 buf0[2], buf1[2];
u16 value;
int ret;
struct i2c_msg msg[2] = {
{0x0a, 0, 2, buf0},
{0x0a, I2C_M_RD, 2, buf1},
};
adp = i2c_get_adapter(1);
buf0[0] = (reg & 0xff00) >> 8;
buf0[1] = reg & 0xff;
ret = i2c_transfer(adp, msg, 2);
if (ret < 0) {
return -ENODEV;
}
value = buf1[0] << 8 | buf1[1];
return value;
}
static unsigned int sgtl5000_hw_write(unsigned int reg, unsigned int value)
{
struct i2c_adapter *adp;
u8 buf[4];
int i2c_ret;
struct i2c_msg msg = { 0x0a, 0, 4, buf };
buf[0] = (reg & 0xff00) >> 8;
buf[1] = reg & 0xff;
buf[2] = (value & 0xff00) >> 8;
buf[3] = value & 0xff;
adp = i2c_get_adapter(1);
i2c_ret = i2c_transfer(adp, &msg, 1);
if (i2c_ret < 0) {
return -EIO;
}
return i2c_ret;
}
static ssize_t sgtl5000_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
u32 value;
int i;
for (i = 0; i < ARRAY_SIZE(sgtl_tbl); i++) {
value = sgtl5000_hw_read(sgtl_tbl[i].reg_addr);
if(value < 0)
return -ENODEV;
printk("%s:[0x%04x]=0x%04x\n", sgtl_tbl[i].name, sgtl_tbl[i].reg_addr, value);
}
return 0;
}
static ssize_t sgtl5000_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *end;
char *token;
char *running;
const char delimiters[] = "=";
u32 addr, value;
running = (char *)buf;
token = strsep(&running, delimiters);
addr = simple_strtoul(token, &end, 16);
if(addr > SGTL5000_DAP_COEF_WR_A2_LSB) {
return -EINVAL;
}
token = strsep(&running, delimiters);
if(token != NULL) {
value = simple_strtoul(token, &end, 16);
} else {
value = sgtl5000_hw_read(addr);
printk("[%d] = 0x%04x\n", addr, value);
return -EINVAL;
}
sgtl5000_hw_write(addr, value);
value = sgtl5000_hw_read(addr);
printk("[%d] = 0x%04x\n", addr, value);
return count;
}
static struct kobj_attribute reg_read_attribute =
__ATTR(reg_dump, 0666, reg_dump_show, reg_dump_store);
static struct kobj_attribute reg_write_attribute =
__ATTR(reg_write, 0666, reg_write_show, reg_write_store);
static struct kobj_attribute pmic_attribute =
__ATTR(pmic, 0666, pmic_show, pmic_store);
static struct kobj_attribute edid_attribute =
__ATTR(edid, 0666, edid_show, NULL);
static struct kobj_attribute battery_attribute =
__ATTR(battery, 0666, battery_show, NULL);
static struct kobj_attribute mtl017_attribute =
__ATTR(mtl017, 0666, mtl017_show, mtl017_store);
static struct kobj_attribute sgtl5000_attribute =
__ATTR(sgtl5000, 0666, sgtl5000_show, sgtl5000_store);
static struct attribute *debug_attrs[] = {
&reg_read_attribute.attr,
&reg_write_attribute.attr,
&pmic_attribute.attr,
&edid_attribute.attr,
&battery_attribute.attr,
&mtl017_attribute.attr,
&sgtl5000_attribute.attr,
NULL,
};
static struct attribute_group debug_attr_group = {
.attrs = debug_attrs,
};
static struct platform_device mxc_debug_device = {
.name = "mxc_debug",
};
static struct kobject *debug_kobj;
static int __init mx51_debug_init(void)
{
int retval;
platform_device_register(&mxc_debug_device);
debug_kobj = kobject_create_and_add("debug", &mxc_debug_device.dev.kobj);
if(!debug_kobj)
return -ENOMEM;
retval = sysfs_create_group(debug_kobj, &debug_attr_group);
if(retval) {
kobject_put(debug_kobj);
return retval;
}
return 0;
}
static void __exit mx51_debug_exit(void)
{
sysfs_remove_group(debug_kobj, &debug_attr_group);
kobject_put(debug_kobj);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ron Lee <ron1_lee@pegatroncorp.com>, Pegatron Corp.");
module_init(mx51_debug_init)
module_exit(mx51_debug_exit)

View File

@@ -6,7 +6,7 @@ menu "Broadcom GPS ioctrl support"
config GPS_IOCTRL
tristate "GPS ioctrl support"
depends on MACH_MX31_3DS || MACH_MX35_3DS || MACH_MX37_3DS || MACH_MX51_3DS
depends on MACH_MX31_3DS || MACH_MX35_3DS || MACH_MX37_3DS || MACH_MX51_3DS || MACH_MX51_EFIKASB
---help---
Say Y to enable Broadcom GPS ioctrl on MXC platform.

View File

@@ -124,4 +124,12 @@ config MXS_VBUS_CURRENT_DRAW
Say Y to enable 100mA limitation when USB vbus power on system
before enumeration to match USB2.0 requirement.
config BATTERY_EFIKASB
tristate "Efikasb battery driver"
depends on I2C
help
Say Y here to enable support for Efikasb battery.
endif # POWER_SUPPLY

View File

@@ -30,3 +30,4 @@ obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
obj-$(CONFIG_BATTERY_STMP3XXX) += stmp37xx/
obj-$(CONFIG_BATTERY_MXS) += mxs/
obj-$(CONFIG_BATTERY_EFIKASB) += efikasb_battery.o

View File

@@ -0,0 +1,855 @@
/*
* Efikasb Smart Battery driver
*
* Copyright (C) 2009 Ron Lee <ron1_lee@pegatroncorp.com>
*
*
* This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#include <linux/module.h>
#include <linux/param.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <linux/syscalls.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/idr.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <asm/unaligned.h>
#include <mach/hardware.h>
#define SBS_MFG_ACCESS 0x00
#define SBS_REMAIN_CAPACITY_ALARM 0x01
#define SBS_REMNAIN_TIME_ALARM 0x02
#define SBS_BATTERY_MODE 0x03
#define SBS_AT_RATE 0x04
#define SBS_AT_RATE_TIME_TO_FULL 0x05
#define SBS_AT_RATE_TIME_TO_EMPTY 0x06
#define SBS_AT_RATE_OK 0x07
#define SBS_TEMP 0x08
#define SBS_VOLTAGE 0x09
#define SBS_CURRENT 0x0A
#define SBS_AVG_CURRENT 0x0B
#define SBS_MAX_ERROR 0x0C
#define SBS_REL_STATE_OF_CHARGE 0x0D
#define SBS_ABS_STATE_OF_CHARGE 0x0E
#define SBS_REMAIN_CAPABILITY 0x0F
#define SBS_FULL_CHARGE_CAPACITY 0x10
#define SBS_RUN_TIME_TO_EMPTY 0x11
#define SBS_AVG_TIME_TO_EMPTY 0x12
#define SBS_AVG_TIME_TO_FULL 0x13
#define SBS_CHARGE_CURRENT 0x14
#define SBS_CHARGE_VOLTAGE 0x15
#define SBS_BATTERY_STATUS 0x16
#define SBS_CYCLE_COUNT 0x17
#define SBS_DESIGN_CAPACITY 0x18
#define SBS_DESIGN_VOLTAGE 0x19
#define SBS_SPEC_INFO 0x1A
#define SBS_MFG_DATE 0x1B
#define SBS_SERIAL_NO 0x1C
#define SBS_MFG_NAME 0x20
#define SBS_DEV_NAME 0x21
#define SBS_DEV_CHEMISTRY 0x22
#define SBS_MFG_DATA 0x23
#define SBS_BATTERY_USAGE 0x30
#define SBS_PERMANENT_FAILURE 0x31
#define SBS_BATTERY_LOG1 0x32
#define SBS_BATTERY_LOG2 0x33
#define SBS_FET_TEMP 0x3B
#define SBS_OPTION_MFG_FUNC5 0x2F
#define SBS_OPTION_MFG_FUNC4 0x3C
#define SBS_OPTION_MFG_FUNC3 0x3D
#define SBS_OPTION_MFG_FUNC2 0x3E
#define SBS_OPTION_MFG_FUNC1 0x3F
/* SBS_BATTERY_STATUS Register Bit Mapping */
#define SBS_STATUS_OVER_CHARGE_ALARM 0x8000
#define SBS_STATUS_TERM_CHARGE_ALARM 0x4000
#define SBS_STATUS_OVER_TEMP_ALARM 0x1000
#define SBS_STATUS_TERM_DISCHARGE_ALARM 0x0800
#define SBS_STATUS_REMAIN_CAPACITY_ALARM 0x0200
#define SBS_STATUS_REMAIN_TIME_ALARM 0x0100
#define SBS_STATUS_INITIALIZED 0x0080
#define SBS_STATUS_DISCHARGING 0x0040
#define SBS_STATUS_FULLY_CHARGED 0x0020
#define SBS_STATUS_FULLY_DISCHARGED 0x0010
static void update_status_worker(struct work_struct *work);
static DECLARE_DELAYED_WORK(update_status_work, update_status_worker);
static void batt_capacity_worker(struct work_struct *work);
static DECLARE_WORK(batt_capacity_work, batt_capacity_worker);
static void power_off_worker(struct work_struct *work);
static DECLARE_DELAYED_WORK(power_off_work, power_off_worker);
extern void kernel_power_off(void);
struct sbs_reg_tbl {
char *name;
unsigned char reg_offset;
};
#ifdef EFIKASB_BATTERY_DEBUG
static struct sbs_reg_tbl reg_tbl[] = {
{"Manufacture Access", SBS_MFG_ACCESS},
{"Remain Capacity Alarm", SBS_REMAIN_CAPACITY_ALARM },
{"Remain Time Alarm", SBS_REMNAIN_TIME_ALARM},
{"Battery Mode", SBS_BATTERY_MODE},
{"AT Rate", SBS_AT_RATE},
{"AT Rate Time To Full", SBS_AT_RATE_TIME_TO_FULL},
{"AT Rate Time To Empty", SBS_AT_RATE_TIME_TO_EMPTY},
{"AT Rate OK", SBS_AT_RATE_OK},
{"Temperature", SBS_TEMP},
{"Voltage", SBS_VOLTAGE},
{"Current", SBS_CURRENT},
{"Average Current", SBS_AVG_CURRENT},
{"Maximum Error", SBS_MAX_ERROR},
{"Relative State of Charge", SBS_REL_STATE_OF_CHARGE},
{"Absolute State of Charge", SBS_ABS_STATE_OF_CHARGE},
{"Remain Capacity", SBS_REMAIN_CAPABILITY},
{"Full Charge Capacity", SBS_FULL_CHARGE_CAPACITY},
{"Run Time To Empty", SBS_RUN_TIME_TO_EMPTY},
{"Average Time To Empty", SBS_AVG_TIME_TO_EMPTY},
{"Average Time To Full", SBS_AVG_TIME_TO_FULL},
{"Charge Current", SBS_CHARGE_CURRENT},
{"Charge Voltage", SBS_CHARGE_VOLTAGE},
{"Battery Status", SBS_BATTERY_STATUS},
{"Cycle Count", SBS_CYCLE_COUNT},
{"Design Capacity", SBS_DESIGN_CAPACITY},
{"Design Voltage", SBS_DESIGN_VOLTAGE},
{"Spec Info", SBS_SPEC_INFO},
{"Manufacture Date", SBS_MFG_DATE},
{"Serial Number", SBS_SERIAL_NO},
{"Manufacture Name", SBS_MFG_NAME},
{"Device Name", SBS_DEV_NAME},
{"Device Chemistry", SBS_DEV_CHEMISTRY},
{"Manufacture Date", SBS_MFG_DATA},
{"Battery Usage", SBS_BATTERY_USAGE},
{"Permanent Failure", SBS_PERMANENT_FAILURE},
{"Battery Log1", SBS_BATTERY_LOG1},
{"Battery Log2", SBS_BATTERY_LOG2},
{"FET Temperature", SBS_FET_TEMP},
{"Optional Mfg Func5", SBS_OPTION_MFG_FUNC5},
{"Optional Mfg Func4", SBS_OPTION_MFG_FUNC4},
{"Optional Mfg Func3", SBS_OPTION_MFG_FUNC3},
{"Optional Mfg Func2", SBS_OPTION_MFG_FUNC2},
{"Optional Mfg Func1", SBS_OPTION_MFG_FUNC1},
};
void dump_sbs_reg(struct i2c_client *client)
{
int i;
unsigned int value;
printk("Dump Smart Battery Register\n");
for (i = 0; i < ARRAY_SIZE(reg_tbl); i ++) {
value = i2c_smbus_read_word_data(client, reg_tbl[i].reg_offset);
printk("%s: 0x%04x\n", reg_tbl[i].name, value);
}
}
#else
void dump_sbs_reg(struct i2c_client *client) {}
#endif
struct efikasb_batt_dev_info;
struct efikasb_batt_dev_info {
struct device *dev;
struct i2c_client *client;
struct power_supply *bat;
struct power_supply *ac_charger;
char mfg_name[32];
char model_name[32];
char serial[32];
char chemistry[32];
int batt_in_irq;
int batt_low_irq;
int ac_in_irq;
int batt_in;
int ac_in;
int batt_low;
u32 capacity;
int (*get_batt_in_status) (void);
int (*get_batt_low_status) (void);
int (*get_ac_in_status) (void);
void (*set_batt_low_led) (int);
struct timer_list batt_low_timer;
};
static struct efikasb_batt_dev_info *batt = NULL;
static enum power_supply_property efikasb_batt_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
/* POWER_SUPPLY_PROP_HEALTH, */
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_SERIAL_NUMBER,
};
static enum power_supply_property efikasb_ac_charger_props[] = {
POWER_SUPPLY_PROP_ONLINE,
};
static int efikasb_batt_read(struct i2c_client *client, u8 reg, u32 *value)
{
int ret;
int retry = 5;
retry:
ret = i2c_smbus_read_word_data(client, reg);
if(ret < 0) { /* ron: retry i2c again to avoid conflict with PIC */
if(retry -- > 0)
goto retry;
return ret;
}
*value = ret;
return 0;
}
/* Smart Battery Helper Function */
static int efikasb_batt_get_status(struct efikasb_batt_dev_info *di, u32 *value)
{
return efikasb_batt_read(di->client, SBS_BATTERY_STATUS, value);
}
static int efikasb_batt_get_current(struct efikasb_batt_dev_info *di, u32 *value)
{
return efikasb_batt_read(di->client, SBS_CURRENT, value);
}
static int efikasb_batt_get_voltage(struct efikasb_batt_dev_info *di, u32 *value)
{
return efikasb_batt_read(di->client, SBS_VOLTAGE, value);
}
static int efikasb_batt_get_average_current(struct efikasb_batt_dev_info *di, u32 *value)
{
return efikasb_batt_read(di->client, SBS_AVG_CURRENT, value);
}
static int efikasb_batt_get_capacity(struct efikasb_batt_dev_info *di, u32 *value)
{
return efikasb_batt_read(di->client, SBS_REL_STATE_OF_CHARGE, value);
}
static int efikasb_batt_get_temperature(struct efikasb_batt_dev_info *di, u32 *value)
{
return efikasb_batt_read(di->client, SBS_TEMP, value);
}
static int efikasb_batt_run_time_to_empty(struct efikasb_batt_dev_info *di, u32 *value)
{
return efikasb_batt_read(di->client, SBS_RUN_TIME_TO_EMPTY, value);
}
static int efikasb_batt_avg_time_to_empty(struct efikasb_batt_dev_info *di, u32 *value)
{
return efikasb_batt_read(di->client, SBS_AVG_TIME_TO_EMPTY, value);
}
static int efikasb_batt_avg_time_to_full(struct efikasb_batt_dev_info *di, u32 *value)
{
return efikasb_batt_read(di->client, SBS_AVG_TIME_TO_FULL, value);
}
static int efikasb_batt_get_serial_no(struct efikasb_batt_dev_info *di)
{
int ret;
u32 sn;
ret = efikasb_batt_read(di->client, SBS_SERIAL_NO, &sn);
if(ret != 0)
return ret;
sprintf(di->serial, "%d", sn);
return ret;
}
static int efikasb_batt_get_mfg_name(struct efikasb_batt_dev_info *di)
{
int ret;
char mfg[33];
ret = i2c_smbus_read_i2c_block_data(di->client, SBS_MFG_NAME, 32, mfg);
if(ret < 0)
return ret;
if(mfg[0] > sizeof(di->mfg_name))
return -EINVAL;
strncpy(di->mfg_name, &mfg[1], mfg[0]);
return 0;
}
static int efikasb_batt_get_model_name(struct efikasb_batt_dev_info *di)
{
int ret;
char model[33];
ret = i2c_smbus_read_i2c_block_data(di->client, SBS_DEV_NAME, 32, model);
if(ret < 0)
return ret;
if(model[0] > sizeof(di->model_name))
return -EINVAL;
strncpy(di->model_name, &model[1], model[0]);
return 0;
}
static int efikasb_batt_get_technology(struct efikasb_batt_dev_info *di)
{
int ret;
char chem[33];
ret = i2c_smbus_read_i2c_block_data(di->client, SBS_DEV_CHEMISTRY, 32, chem);
if(ret < 0)
return ret;
if(chem[0] > sizeof(di->chemistry))
return -EINVAL;
strncpy(di->chemistry, &chem[1], chem[0]);
/* printk("Technology: %s\n", di->chemistry); */
if (!strcasecmp("NiCd", di->chemistry))
return POWER_SUPPLY_TECHNOLOGY_NiCd;
if (!strcasecmp("NiMH", di->chemistry))
return POWER_SUPPLY_TECHNOLOGY_NiMH;
if (!strcasecmp("LION", di->chemistry))
return POWER_SUPPLY_TECHNOLOGY_LION;
if (!strncasecmp("LI-ION", di->chemistry, 6))
return POWER_SUPPLY_TECHNOLOGY_LION;
if (!strcasecmp("LiP", di->chemistry))
return POWER_SUPPLY_TECHNOLOGY_LIPO;
return POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
}
#define to_efikasb_batt_device_info(x) container_of((x), \
struct efikasb_batt_dev_info, bat);
static int efikasb_batt_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct efikasb_batt_dev_info *di = batt;
u32 value;
int ret;
int batt_in, ac_in;
batt_in = di->get_batt_in_status();
if (!batt_in) {
val->intval = 0;
return -ENODEV;
}
ac_in = di->get_ac_in_status();
switch(psp) {
case POWER_SUPPLY_PROP_PRESENT:
val->intval = di->get_batt_in_status();
break;
case POWER_SUPPLY_PROP_STATUS:
if (ac_in) {
ret = efikasb_batt_get_status(di, &value);
if (ret != 0) {
val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
return 0;
}
if (value & SBS_STATUS_FULLY_CHARGED)
val->intval = POWER_SUPPLY_STATUS_FULL;
else
val->intval = POWER_SUPPLY_STATUS_CHARGING;
} else {
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
}
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = efikasb_batt_get_technology(di);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW: /* mV */
ret = efikasb_batt_get_voltage(di, &value);
if(ret != 0) {
val->intval = 0;
break;
}
val->intval = value;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW: /* mA */
ret = efikasb_batt_get_current(di, &value);
if(ret != 0) {
val->intval = 0;
return ret;
}
val->intval = value;
break;
case POWER_SUPPLY_PROP_CURRENT_AVG: /* mA in 1 minute rolling avg */
ret = efikasb_batt_get_average_current(di, &value);
if(ret != 0)
return ret;
val->intval = value;
break;
case POWER_SUPPLY_PROP_CAPACITY: /* % percent */
ret = efikasb_batt_get_capacity(di, &value);
if(ret != 0)
return ret;
val->intval = value;
di->capacity = value; /* ron: cache the battery capacity */
break;
case POWER_SUPPLY_PROP_TEMP: /* K degree */
ret = efikasb_batt_get_temperature(di, &value);
if(ret != 0)
return ret;
val->intval = value;
break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: /* minutes */
ret = efikasb_batt_run_time_to_empty(di, &value);
if(ret != 0)
return ret;
val->intval = value;
break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: /* minutes */
ret = efikasb_batt_avg_time_to_empty(di, &value);
if(ret != 0)
return ret;
val->intval = value;
break;
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: /* minutes */
ret = efikasb_batt_avg_time_to_full(di, &value);
if(ret != 0)
return ret;
val->intval = value;
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
ret = efikasb_batt_get_model_name(di);
if(ret < 0)
return ret;
val->strval = di->model_name;
break;
case POWER_SUPPLY_PROP_MANUFACTURER:
ret = efikasb_batt_get_mfg_name(di);
if(ret < 0)
return ret;
val->strval = di->mfg_name;
break;
case POWER_SUPPLY_PROP_SERIAL_NUMBER:
ret = efikasb_batt_get_serial_no(di);
if(ret < 0)
return ret;
val->strval = di->serial;
break;
default:
return -EINVAL;
}
return 0;
}
static int efikasb_ac_charger_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct efikasb_batt_dev_info *di = batt;
switch(psp) {
case POWER_SUPPLY_PROP_ONLINE:
val->intval = di->get_ac_in_status();
break;
default:
return -EINVAL;
break;
}
return 0;
}
static struct power_supply efikasb_batt = {
.name = "efikasb_battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = efikasb_batt_props,
.num_properties = ARRAY_SIZE(efikasb_batt_props),
.get_property = efikasb_batt_get_property,
.external_power_changed = NULL,
};
static struct power_supply efikasb_ac_charger = {
.name = "efikasb_ac_charger",
.type = POWER_SUPPLY_TYPE_MAINS,
.properties = efikasb_ac_charger_props,
.num_properties = ARRAY_SIZE(efikasb_ac_charger_props),
.get_property = efikasb_ac_charger_get_property,
.external_power_changed = NULL,
};
static void update_status_worker(struct work_struct *work)
{
power_supply_changed(batt->ac_charger);
power_supply_changed(batt->bat);
}
static irqreturn_t efikasb_ac_detect_handler(int irq, void *data)
{
struct efikasb_batt_dev_info *di = data;
di->ac_in = di->get_ac_in_status();
printk("efikasb_ac_charger: AC %s\n",
di->ac_in ? "Inserted" : "Removed");
schedule_delayed_work(&update_status_work, msecs_to_jiffies(1500));
if (di->ac_in) {
if(di->set_batt_low_led)
di->set_batt_low_led(0);
set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
mod_timer(&di->batt_low_timer, jiffies + 10);
} else {
mod_timer(&di->batt_low_timer, jiffies + 10);
set_irq_type(irq, IRQ_TYPE_EDGE_FALLING);
}
return IRQ_HANDLED;
}
static irqreturn_t efikasb_batt_detect_handler(int irq, void *data)
{
struct efikasb_batt_dev_info *di = data;
di->batt_in = di->get_batt_in_status();
printk("efikasb_battery: Battery %s\n",
di->batt_in ? "Inserted" : "Removed");
schedule_delayed_work(&update_status_work, msecs_to_jiffies(1500));
if (di->batt_in) {
mod_timer(&di->batt_low_timer, jiffies + 10);
set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
} else {
if(di->set_batt_low_led)
di->set_batt_low_led(0);
set_irq_type(irq, IRQ_TYPE_EDGE_FALLING);
}
return IRQ_HANDLED;
}
static void power_off_worker(struct work_struct *work)
{
if(!batt->ac_in && batt->batt_low && batt->capacity == 0) {
sys_sync();
kernel_power_off();
}
}
static irqreturn_t efikasb_batt_low_handler(int irq, void *data)
{
struct efikasb_batt_dev_info *di = data;
di->batt_low = batt->get_batt_low_status();
printk("efikasb_batter: Battery %s\n",
di->batt_low ? "Low" : "Normal");
if (di->batt_low) {
set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH);
} else {
set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
}
return IRQ_HANDLED;
}
static void batt_capacity_worker(struct work_struct *work)
{
int ret;
u32 capacity;
if(batt->batt_in) {
ret = efikasb_batt_get_capacity(batt, &capacity);
if(ret == 0)
batt->capacity = capacity;
if(!batt->ac_in && batt->capacity == 0) {
sys_sync();
kernel_power_off();
}
}
}
static void batt_low_fn(unsigned long data)
{
struct efikasb_batt_dev_info *di = (struct efikasb_batt_dev_info *)data;
if(!di->batt_in)
return;
if(!di->ac_in && di->batt_in) { /* battery is discharging */
schedule_work(&batt_capacity_work);
if(di->set_batt_low_led) {
if(di->capacity <= 10)
di->set_batt_low_led(1);
else
di->set_batt_low_led(0);
if(di->capacity <= 11 && di->capacity > 9) {
mod_timer(&di->batt_low_timer, jiffies + 4 * HZ);
}
if(di->capacity <= 9 || di->capacity > 11) {
mod_timer(&di->batt_low_timer, jiffies + 60 * HZ);
}
} else {
mod_timer(&di->batt_low_timer, jiffies + 60 * HZ);
}
} else if(di->batt_in) { /* battery is charging */
mod_timer(&di->batt_low_timer, jiffies + 60 * HZ);
}
/* battery is not charging and capacity is critical low, power off immediately */
if(!di->ac_in && di->batt_in && di->capacity == 0)
schedule_delayed_work(&power_off_work, msecs_to_jiffies(1000));
}
static int efikasb_batt_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct efikasb_batt_dev_info *di;
struct mxc_battery_platform_data *platform_data;
int retval = 0;
platform_data = (struct mxc_battery_platform_data *)client->dev.platform_data;
di = kzalloc(sizeof(*di), GFP_KERNEL);
if(!di) {
dev_err(&client->dev, "failed to allocate device info\n");
retval = -ENOMEM;
return retval;
}
batt = di;
i2c_set_clientdata(client, di);
di->dev = &client->dev;
di->client = client;
di->bat = &efikasb_batt;
di->ac_charger = &efikasb_ac_charger;
di->get_batt_in_status = platform_data->get_batt_in_status;
di->get_batt_low_status = platform_data->get_batt_low_status;
di->get_ac_in_status = platform_data->get_ac_in_status;
di->set_batt_low_led = platform_data->set_batt_low_led;
if(di->get_batt_in_status)
di->batt_in = di->get_batt_in_status();
if(di->get_ac_in_status)
di->ac_in = di->get_ac_in_status();
if(di->get_batt_low_status)
di->batt_low = di->get_batt_low_status();
di->batt_in_irq = platform_data->batt_in_irq;
di->batt_low_irq = platform_data->batt_low_irq;
di->ac_in_irq = platform_data->ac_in_irq;
#ifndef CONFIG_EFIKASB_EXPERIMENTAL_OS
if(di->batt_in) {
retval = request_irq(di->batt_in_irq,
efikasb_batt_detect_handler, \
IRQ_TYPE_EDGE_RISING,
"efikasb_battery", di);
} else {
retval = request_irq(di->batt_in_irq,
efikasb_batt_detect_handler,
IRQ_TYPE_EDGE_FALLING,
"efikasb_battery", di);
}
if(retval)
goto batt_irq_failed;
if(di->ac_in) {
retval = request_irq(di->ac_in_irq,
efikasb_ac_detect_handler,
IRQ_TYPE_EDGE_RISING,
"efikasb_ac_charger", di);
} else {
retval = request_irq(di->ac_in_irq,
efikasb_ac_detect_handler,
IRQ_TYPE_EDGE_FALLING,
"efikasb_ac_charger", di);
}
if(retval)
goto ac_irq_failed;
if(di->batt_low) {
retval = request_irq(di->batt_low_irq,
efikasb_batt_low_handler,
IRQ_TYPE_LEVEL_HIGH, "efikasb_batt_low", di);
} else {
retval = request_irq(di->batt_low_irq,
efikasb_batt_low_handler,
IRQ_TYPE_LEVEL_LOW, "efikasb_batt_low", di);
}
if(retval)
goto batt_low_irq_failed;
/* ron: the battery low pin is triggered at 10% battery capacity,
battery critical low won't generate wake up event,
the battery power may be exhauseted */
/* enable_irq_wake(di->batt_low_irq); */
#endif
retval = power_supply_register(&client->dev, &efikasb_batt);
if(retval) {
dev_err(&client->dev, "failed to register battery\n");
goto batt_failed;
}
retval =power_supply_register(&client->dev, &efikasb_ac_charger);
if(retval) {
dev_err(&client->dev, "failed to register ac charger\n");
goto ac_charger_failed;
}
if(di->batt_in) {
efikasb_batt_get_mfg_name(di);
efikasb_batt_get_model_name(di);
efikasb_batt_get_capacity(di, &di->capacity);
}
printk("Probe Efikasb Battery: %s %s %s: %d%%, AC %s\n",
di->mfg_name, di->model_name,
di->batt_in ? "Inserted" : "Removed", di->capacity,
di->ac_in ? "Inserted" : "Removed");
dump_sbs_reg(client);
init_timer(&di->batt_low_timer);
di->batt_low_timer.data = (unsigned long)di;
di->batt_low_timer.function = batt_low_fn;
di->batt_low_timer.expires = jiffies + 10;
if(!di->ac_in && di->batt_in)
add_timer(&di->batt_low_timer);
/* ron: if battery critical low, shutdown immediately */
if(!di->ac_in && di->batt_low && di->capacity == 0) {
printk("Battery critical low, shutdown now....\n");
schedule_delayed_work(&power_off_work, msecs_to_jiffies(1000));
}
return 0;
ac_charger_failed:
power_supply_unregister(&efikasb_batt);
batt_failed:
free_irq(di->batt_low_irq, NULL);
batt_low_irq_failed:
free_irq(di->ac_in_irq, NULL);
ac_irq_failed:
free_irq(di->batt_in_irq, NULL);
batt_irq_failed:
i2c_set_clientdata(client, NULL);
kfree(di);
return retval;
}
static int efikasb_batt_i2c_remove(struct i2c_client *client)
{
struct efikasb_batt_dev_info *di = i2c_get_clientdata(client);
power_supply_unregister(&efikasb_batt);
power_supply_unregister(&efikasb_ac_charger);
free_irq(di->batt_in_irq, NULL);
free_irq(di->ac_in_irq, NULL);
kfree(di);
batt = NULL;
return 0;
}
static const struct i2c_device_id efikasb_batt_id[] = {
{ "efikasb-battery", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, efikasb_batt_id);
static struct i2c_driver efikasb_batt_i2c_driver = {
.driver = {
.name = "efikasb-battery",
},
.probe = efikasb_batt_i2c_probe,
.remove = efikasb_batt_i2c_remove,
.id_table = efikasb_batt_id,
};
static int __init efikasb_batt_init(void)
{
int ret;
ret = i2c_add_driver(&efikasb_batt_i2c_driver);
if(ret)
printk(KERN_ERR "Unable to register Efikasb Battery Driver\n");
return ret;
}
module_init(efikasb_batt_init);
static void __exit efikasb_batt_exit(void)
{
i2c_del_driver(&efikasb_batt_i2c_driver);
}
module_exit(efikasb_batt_exit);
MODULE_AUTHOR("Ron Lee <ron1_lee@pegatroncorp.com>");
MODULE_DESCRIPTION("Efikasb Smart Battery Driver");
MODULE_LICENSE("GPL");

View File

@@ -258,6 +258,12 @@ config BACKLIGHT_MXC_MC13892
depends on BACKLIGHT_MXC && MXC_MC13892_LIGHT
default y
config BACKLIGHT_MXC_EFIKASB
tristate "MXC Efikasb LCD Backlight Driver"
depends on BACKLIGHT_MXC
default y
config BACKLIGHT_STMP37XX
tristate "SigmaTel STMP37xx Backlight Driver"
depends on BACKLIGHT_CLASS_DEVICE && (ARCH_STMP37XX || ARCH_STMP378X)

View File

@@ -30,5 +30,6 @@ obj-$(CONFIG_BACKLIGHT_MXC_IPU) += mxc_ipu_bl.o
obj-$(CONFIG_BACKLIGHT_MXC_PMIC) += mxc_pmic_bl.o
obj-$(CONFIG_BACKLIGHT_WM8350) += wm8350_bl.o
obj-$(CONFIG_BACKLIGHT_MXC_MC13892) += mxc_mc13892_bl.o
obj-$(CONFIG_BACKLIGHT_MXC_EFIKASB) += mxc_efikasb_bl.o
obj-$(CONFIG_BACKLIGHT_STMP37XX) += stmp37xx_bl.o
obj-$(CONFIG_BACKLIGHT_MXS) += mxs_bl.o

View File

@@ -0,0 +1,332 @@
/*
* Copyright 2007 Pegatron Corp, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @defgroup LCDC_BL MXC LCDC Backlight Driver
*/
/*!
* @file mxc_efikasb_bl.c
*
* @brief Backlight Driver for LCDC PWM on Freescale MXC/i.MX Efikasb platforms.
*
* This file contains API defined in include/linux/clk.h for setting up and
* retrieving clocks.
*
* Based on Sharp's Corgi Backlight Driver
*
* @ingroup LCDC_BL
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/clk.h>
#include <mach/hardware.h>
#define MXC_MAX_BL_LEVEL 11
#define MXC_MIN_BL_LEVEL 0
#define MXC_DFL_BL_LEVEL 6
#define MXC_MAX_INTENSITY 255
#define MXC_DEFAULT_INTENSITY 127
#define MXC_INTENSITY_OFF 0
#define MXC_PWM_PERIOD_NS 78770
#define MXC_PWMCR (0x00)
#define MXC_PWMSR (0x04)
#define MXC_PWMIR (0x08)
#define MXC_PWMSAR (0x0C)
#define MXC_PWMPR (0x10)
#define MXC_PWMCNR (0x14)
#define MXC_PWMCR_EN (1)
#define MXC_PWMCR_PRESCALER_MASK (0xFFFF000F)
#define MXC_PWMCR_PRESCALER_OFFSET (4)
#define MXC_PWMCR_CLKSRC_OFF (0)
#define MXC_PWMCR_CLKSRC_IPG (1 << 16)
#define MXC_PWMCR_CLKSRC_IPG_HIGH (2 << 16)
#define MXC_PWMCR_CLKSRC_32K (3 << 16)
#define MXC_PWMCR_STOPEN (1 << 25)
#define MXC_PWMCR_DOZEEN (1 << 24)
#define MXC_PWMCR_WAITEN (1 << 23)
#define MXC_PWMCR_DBGEN (1 << 22)
#define MXC_PWMSAR_SAMPLE_MASK (0xFFFF0000)
#define MXC_PWMPR_PERIOD_MASK (0xFFFF0000)
#define MXC_PWMCR_PRESCALER(x) (((x - 1) & 0xFFF) << 4)
int bl_level_to_pwm_map[MXC_MAX_BL_LEVEL] = {
0, 4, 25, 63, 75, 101, 127, 153, 179, 205, 231, 255,
};
static BLOCKING_NOTIFIER_HEAD(backlight_notifier_list);
int register_backlight_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&backlight_notifier_list, nb);
}
EXPORT_SYMBOL_GPL(register_backlight_notifier);
int unregister_backlight_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&backlight_notifier_list, nb);
}
EXPORT_SYMBOL_GPL(unregister_backlight_notifier);
struct mxcbl_dev_data {
void *base;
struct clk *clk;
int clk_enabled;
int intensity;
int suspend;
};
static int mxc_pwm_enable(struct mxcbl_dev_data *mxc_bl)
{
unsigned long reg;
int rc = 0;
if (!mxc_bl->clk_enabled) {
rc = clk_enable(mxc_bl->clk);
if (!rc)
mxc_bl->clk_enabled = 1;
}
reg = __raw_readl(mxc_bl->base + MXC_PWMCR);
reg |= MXC_PWMCR_EN;
__raw_writel(reg, mxc_bl->base + MXC_PWMCR);
return rc;
}
static void mxc_pwm_disable(struct mxcbl_dev_data *mxc_bl)
{
unsigned long reg;
if (mxc_bl->clk_enabled) {
clk_disable(mxc_bl->clk);
mxc_bl->clk_enabled = 0;
}
reg = __raw_readl(mxc_bl->base + MXC_PWMCR);
reg &= ~MXC_PWMCR_EN;
__raw_writel(reg, mxc_bl->base + MXC_PWMCR);
}
void mxc_set_brightness(struct mxcbl_dev_data *mxc_bl, uint8_t level)
{
unsigned long long c;
unsigned long period_cycles, duty_cycles, prescale;
int duty_ns, period_ns;
//printk("mxc_set_brightness: level=%x\n", level);
#if 0
period_ns = MXC_PWM_PERIOD_NS;
duty_ns = level * period_ns / MXC_MAX_INTENSITY;
c = clk_get_rate(mxc_bl->clk) * 665 / 80;
c = c * period_ns;
do_div(c, 1000000000);
period_cycles = c;
prescale = period_cycles / 0x10000 + 1;
period_cycles /= prescale;
c = (unsigned long long)period_cycles * duty_ns;
do_div(c, period_ns);
duty_cycles = c;
#else
/* ron: hard code the PWM configuration */
prescale = 300;
period_cycles = 0x03f1;
duty_cycles = bl_level_to_pwm_map[level] * period_cycles / MXC_MAX_INTENSITY;
#endif
//printk("duty_cycles %d, period_cycles %d\n", duty_cycles, period_cycles);
__raw_writel(duty_cycles, mxc_bl->base + MXC_PWMSAR);
__raw_writel(period_cycles, mxc_bl->base + MXC_PWMPR);
__raw_writel(MXC_PWMCR_PRESCALER(prescale) |
MXC_PWMCR_CLKSRC_IPG |
MXC_PWMCR_STOPEN | MXC_PWMCR_DOZEEN |
MXC_PWMCR_WAITEN | MXC_PWMCR_DBGEN | MXC_PWMCR_EN,
mxc_bl->base + MXC_PWMCR);
}
#define BL_BRIGHTNESS 0x01
static int mxcbl_set_brightness(struct backlight_device *bd)
{
int level = bd->props.brightness;
struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev);
if (level > MXC_MAX_BL_LEVEL)
return 0;
if (bd->props.power != FB_BLANK_UNBLANK)
level = 0;
if (bd->props.fb_blank != FB_BLANK_UNBLANK)
level = 0;
if (level && !devdata->clk_enabled)
mxc_pwm_enable(devdata);
mxc_set_brightness(devdata, level);
if (level == 0 && devdata->clk_enabled)
mxc_pwm_disable(devdata);
devdata->intensity = bl_level_to_pwm_map[level];
blocking_notifier_call_chain(&backlight_notifier_list, BL_BRIGHTNESS, &level);
return 0;
}
static int mxcbl_get_brightness(struct backlight_device *bd)
{
struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev);
printk("intensity=%d\n", devdata->intensity);
return bd->props.brightness;
}
static int mxcbl_check_fb(struct fb_info *info)
{
if (strcmp(info->fix.id, "DISP0 BG") == 0) {
return 1;
}
return 0;
}
static struct backlight_ops mxcbl_ops = {
.get_brightness = mxcbl_get_brightness,
.update_status = mxcbl_set_brightness,
.check_fb = mxcbl_check_fb,
};
static int __init mxcbl_probe(struct platform_device *pdev)
{
struct backlight_device *bd;
struct mxcbl_dev_data *devdata;
int ret = 0;
devdata = kzalloc(sizeof(struct mxcbl_dev_data), GFP_KERNEL);
if (!devdata)
return -ENOMEM;
;
/* ron: TBD */
devdata->base = IO_ADDRESS(PWM1_BASE_ADDR);
devdata->clk = clk_get(&pdev->dev, "pwm");
devdata->clk_enabled = 0;
mxc_pwm_enable(devdata);
#if 0 //vv, 20100316 dev.bus_id is deprecated, change to use dev_name(&pdev->dev)
bd = backlight_device_register(pdev->dev.bus_id, &pdev->dev, devdata,
&mxcbl_ops);
#else
bd = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, devdata,
&mxcbl_ops);
#endif
if (IS_ERR(bd)) {
ret = PTR_ERR(bd);
goto err0;
}
platform_set_drvdata(pdev, bd);
bd->props.brightness = MXC_DFL_BL_LEVEL;
bd->props.max_brightness = MXC_MAX_BL_LEVEL;
bd->props.power = FB_BLANK_UNBLANK;
bd->props.fb_blank = FB_BLANK_UNBLANK;
mxc_set_brightness(devdata, MXC_DFL_BL_LEVEL);
/*printk("MXC Backlight Device %s Initialized.\n", pdev->dev.bus_id);*/
printk("MXC Backlight Device %s Initialized.\n", dev_name(&pdev->dev));
return 0;
err0:
mxc_pwm_disable(devdata);
kfree(devdata);
return ret;
}
static int mxcbl_remove(struct platform_device *pdev)
{
struct backlight_device *bd = platform_get_drvdata(pdev);
struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev);
bd->props.brightness = MXC_INTENSITY_OFF;
backlight_update_status(bd);
mxc_pwm_disable(devdata);
backlight_device_unregister(bd);
return 0;
}
static int mxcbl_suspend(struct platform_device *pdev, pm_message_t state)
{
struct backlight_device *bd = platform_get_drvdata(pdev);
struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev);
devdata->suspend = 1;
backlight_update_status(bd);
mxc_pwm_disable(devdata);
return 0;
}
static int mxcbl_resume(struct platform_device *pdev)
{
struct backlight_device *bd = platform_get_drvdata(pdev);
struct mxcbl_dev_data *devdata = dev_get_drvdata(&bd->dev);
mxc_pwm_enable(devdata);
devdata->suspend = 0;
backlight_update_status(bd);
return 0;
}
static struct platform_driver mxcbl_driver = {
.probe = mxcbl_probe,
.remove = mxcbl_remove,
.suspend = mxcbl_suspend,
.resume = mxcbl_resume,
.driver = {
.name = "mxc_efikasb_bl",
},
};
static int __init mxcbl_init(void)
{
return platform_driver_register(&mxcbl_driver);
}
static void __exit mxcbl_exit(void)
{
platform_driver_unregister(&mxcbl_driver);
}
module_init(mxcbl_init);
module_exit(mxcbl_exit);
MODULE_DESCRIPTION("Pegatron iMX51 Efikasb LCDC PWM Backlight Driver");
MODULE_AUTHOR("Pegatron Corp.");
MODULE_LICENSE("GPL");

View File

@@ -42,6 +42,20 @@ config FB_MXC_SII9022
depends on FB_MXC_SYNC_PANEL
tristate "SII9022A HDMI Interface Chip"
config FB_MXC_CS8556
depends on FB_MXC_SYNC_PANEL
tristate "Myson CS8556 VGA Interface Chip"
default y
config FB_MXC_MTL017
depends on FB_MXC_SYNC_PANEL
tristate "Myson MTL017 LVDS Controller"
default n
config FB_MXC_CH7026
depends on FB_MXC_SYNC_PANEL
tristate "Chrontel CH7026 VGA Interface Chip"

View File

@@ -21,5 +21,7 @@ obj-$(CONFIG_FB_MXC_LDB) += ldb.o
obj-$(CONFIG_FB_MXC_CH7026) += mxcfb_ch7026.o
#obj-$(CONFIG_FB_MODE_HELPERS) += mxc_edid.o
obj-$(CONFIG_FB_MXC_SII9022) += mxcfb_sii9022.o
obj-$(CONFIG_FB_MXC_CS8556) += mxcfb_cs8556.o
obj-$(CONFIG_FB_MXC_MTL017) += mxcfb_mtl017.o
obj-$(CONFIG_FB_MXC_EINK_PANEL) += mxc_epdc_fb.o
obj-$(CONFIG_FB_MXC_ELCDIF_FB) += mxc_elcdif_fb.o

View File

@@ -0,0 +1,565 @@
/*
* Copyright 2009 Pegatron Corporation. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @defgroup Framebuffer Framebuffer Driver for SDC and ADC.
*/
/*!
* @file mxcfb_cs8556.c
*
* @brief MXC Frame buffer driver for SDC
*
* @ingroup Framebuffer
*/
/*!
* Include files
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/console.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/i2c.h>
#include <linux/mxcfb.h>
#include <linux/ipu.h>
#include <mach/hardware.h>
static struct i2c_client *cs8556_client;
static int lcd_init(void);
static void lcd_poweron(struct fb_info *info);
static void lcd_poweroff(void);
static void (*lcd_reset) (void);
static struct regulator *io_reg;
static struct regulator *core_reg;
static struct regulator *analog_reg;
static struct fb_videomode mode = {
"1024x768", 60, 1024, 768, 16081, 160, 56, 20, 3, 104, 4, 2, FB_VMODE_NONINTERLACED
};
static void lcd_init_fb(struct fb_info *info)
{
struct fb_var_screeninfo var;
memset(&var, 0, sizeof(var));
fb_videomode_to_var(&var, &mode);
var.activate = FB_ACTIVATE_ALL;
acquire_console_sem();
info->flags |= FBINFO_MISC_USEREVENT;
fb_set_var(info, &var);
fb_blank(info, FB_BLANK_UNBLANK);
info->flags &= ~FBINFO_MISC_USEREVENT;
release_console_sem();
}
static int lcd_fb_event(struct notifier_block *nb, unsigned long val, void *v)
{
struct fb_event *event = v;
if (strcmp(event->info->fix.id, "DISP3 BG - DI1"))
return 0;
switch (val) {
case FB_EVENT_FB_REGISTERED:
lcd_init_fb(event->info);
lcd_poweron(event->info);
break;
case FB_EVENT_BLANK:
if (*((int *)event->data) == FB_BLANK_UNBLANK)
lcd_poweron(event->info);
else
lcd_poweroff();
break;
}
return 0;
}
static struct notifier_block nb = {
.notifier_call = lcd_fb_event,
};
/*!
* This function is called whenever the SPI slave device is detected.
*
* @param spi the SPI slave device
*
* @return Returns 0 on SUCCESS and error on FAILURE.
*/
static int __devinit lcd_probe(struct device *dev)
{
int ret = 0;
int i;
struct mxc_lcd_platform_data *plat = dev->platform_data;
if (plat) {
lcd_reset = plat->reset;
if (lcd_reset)
lcd_reset();
}
for (i = 0; i < num_registered_fb; i++) {
if (strcmp(registered_fb[i]->fix.id, "DISP3 BG - DI1") == 0) {
ret = lcd_init();
if (ret < 0)
goto err;
lcd_init_fb(registered_fb[i]);
fb_show_logo(registered_fb[i], 0);
lcd_poweron(registered_fb[i]);
}
}
fb_register_client(&nb);
return 0;
err:
if (io_reg)
regulator_disable(io_reg);
if (core_reg)
regulator_disable(core_reg);
if (analog_reg)
regulator_disable(analog_reg);
return ret;
}
static int __devinit cs8556_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int id_reg;
cs8556_client = client;
return lcd_probe(&client->dev);
}
static int __devexit cs8556_remove(struct i2c_client *client)
{
fb_unregister_client(&nb);
lcd_poweroff();
regulator_put(io_reg);
regulator_put(core_reg);
regulator_put(analog_reg);
return 0;
}
static int cs8556_suspend(struct i2c_client *client, pm_message_t message)
{
return 0;
}
static int cs8556_resume(struct i2c_client *client)
{
return 0;
}
// Hungwen
u8 __devinitdata xga2xga_tbl1[] = {
// 0x00 - 0x0F
0x00,
0x0E, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
// 0x10 - 0x1F
0x10,
//0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //RGB24
0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //RGB565
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x20 - 0x2F
0x20,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x30 - 0x3F
0x30,
0x3F, 0x05, 0x67, 0x00, 0x08, 0x01, 0x08, 0x05,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x40 - 0x4F
0x40,
0x1A, 0x03, 0x03, 0x00, 0x17, 0x00, 0x16, 0x03,
0x00, 0x00, 0x9F, 0x00, 0x15, 0x00, 0x37, 0x00,
// 0x50 - 0x5F
0x50,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x60 - 0x6F
0x60,
0x1F, 0x05, 0x20, 0x00, 0xC0, 0x00, 0xC0, 0x04,
0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x38, 0x03,
// 0x70 - 0x7F
0x70,
0x3D, 0x03, 0x0C, 0x00, 0x16, 0x00, 0x04, 0x03,
0xCC, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x80 - 0x8F
0x80,
0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x90 - 0x9F
0x90,
0x04, 0x01, 0x11, 0x0D, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xA0 - 0xAF
0xA0,
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
// 0xB0 - 0xBF
0xB0,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00,
// 0xC0 - 0xCF
0xC0,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
// 0xD0 - 0xDF
0xD0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xE0 - 0xEF
0xE0,
//0x01, 0x06, 0x13, 0x00, 0x01, 0x02, 0x00, 0x00,
0x01, 0x01, 0x03, 0x00, 0x01, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xF0 - 0xFF
0xF0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
u8 __devinitdata xga2xga_tbl2[] = {
// 0x00 - 0x0F
0x00,
0x80, 0x10, 0x80, 0x00, 0x00, 0x00, 0x99, 0x11,
0x2A, 0x00, 0x70, 0x30, 0x2A, 0x71, 0x9C, 0x00,
// 0x10 - 0x1F
0x10,
0x10, 0x2A, 0x85, 0x82, 0x00, 0x00, 0xFF, 0x00,
0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
// 0x20 - 0x2F
0x20,
0x41, 0x09, 0x98, 0x08, 0xCC, 0x00, 0x4C, 0x08,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00,
// 0x30 - 0x3F
0x30,
0x00, 0x01, 0xEE, 0x02, 0x3A, 0x07, 0x65, 0x04,
0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x40 - 0x4F
0x40,
0x06, 0x40, 0x07, 0x30, 0x15, 0x10, 0x31, 0x02,
0x33, 0x12, 0x34, 0x52, 0x38, 0x42, 0x39, 0x62,
// 0x50 - 0x5F
0x50,
0x48, 0x12, 0x64, 0x04, 0x66, 0x14, 0x00, 0x10,
0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10,
// 0x60 - 0x6F
0x60,
0x7E, 0x10, 0x00, 0xF4, 0x10, 0x76, 0x08, 0x00,
0x8A, 0x00, 0x8F, 0x11, 0x8F, 0x8F, 0x81, 0x00,
// 0x70 - 0x7F
0x70,
0x03, 0x00, 0x89, 0x45, 0x01, 0x45, 0x00, 0x00,
0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
// 0x80 - 0x8F
0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x90 - 0x9F
0x90,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xA0 - 0xAF
0xA0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xB0 - 0xBF
0xB0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xC0 - 0xCF
0xC0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xD0 - 0xDF
0xD0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xE0 - 0xEF
0xE0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xF0 - 0xFF
0xF0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/*******************************************************************/
u8 __devinitdata wsvga2xga_tbl1[] = {
// 0x00 - 0x0F
0x00,
0x0E, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x01,
// 0x10 - 0x0F
0x10,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x20 - 0x0F
0x20,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x30 - 0x0F
0x30,
0x7B, 0x04, 0x10, 0x00, 0x54, 0x00, 0x54, 0x04,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
// 0x40 - 0x0F
0x40,
0x97, 0x02, 0x03, 0x00, 0x12, 0x00, 0x6A, 0x02,
0x00, 0x00, 0x50, 0x00, 0x14, 0x00, 0x17, 0x00,
// 0x50 - 0x0F
0x50,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x60 - 0x0F
0x60,
0x1F, 0x05, 0x20, 0x00, 0xC0, 0x00, 0xC0, 0x04,
0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x38, 0x03,
// 0x70 - 0x0F
0x70,
0x3D, 0x03, 0x0C, 0x00, 0x16, 0x00, 0x04, 0x03,
0xCC, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x80 - 0x0F
0x80,
0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x90 - 0x0F
0x90,
0x04, 0x01, 0x11, 0x0D, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xA0 - 0x0F
0xA0,
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
// 0xB0 - 0x0F
0xB0,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00,
// 0xC0 - 0x0F
0xC0,
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00,
// 0xD0 - 0x0F
0xD0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xE0 - 0x0F
0xE0,
0x01, 0x06, 0x13, 0x00, 0x01, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xF0 - 0x0F
0xF0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
u8 __devinitdata wsvga2xga_tbl2[] = {
// 0x00 - 0x0F
0x00,
0x80, 0x10, 0x80, 0x00, 0x00, 0x00, 0x99, 0x11,
0x2A, 0x00, 0x70, 0x30, 0x2A, 0x71, 0x9C, 0x00,
// 0x10 - 0x0F
0x10,
0x10, 0x2A, 0x85, 0x82, 0x00, 0x00, 0xFF, 0x00,
0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
// 0x20 - 0x0F
0x20,
0x41, 0x01, 0x98, 0x08, 0xCC, 0x00, 0x4C, 0x08,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00,
// 0x30 - 0x0F
0x30,
0x00, 0x01, 0xEE, 0x02, 0x3A, 0x07, 0x65, 0x04,
0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x40 - 0x0F
0x40,
0x06, 0x40, 0x07, 0x30, 0x15, 0x10, 0x31, 0x02,
0x33, 0x12, 0x34, 0x52, 0x38, 0x42, 0x39, 0x62,
// 0x50 - 0x0F
0x50,
0x48, 0x12, 0x64, 0x04, 0x66, 0x14, 0x00, 0x10,
0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10,
// 0x60 - 0x0F
0x60,
0x7E, 0x10, 0x00, 0xF4, 0x10, 0x76, 0x08, 0x00,
0x8A, 0x00, 0x8F, 0x11, 0x8F, 0x8F, 0x81, 0x00,
// 0x70 - 0x0F
0x70,
0x03, 0x00, 0x89, 0x45, 0x01, 0x45, 0x00, 0x00,
0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
// 0x80 - 0x0F
0x80,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x90 - 0x0F
0x90,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xA0 - 0x0F
0xA0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xB0 - 0x0F
0xB0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xC0 - 0x0F
0xC0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xD0 - 0x0F
0xD0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xE0 - 0x0F
0xE0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xF0 - 0x0F
0xF0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#define REGMAP_LENGTH (sizeof(reg_init) / (2*sizeof(u8)))
#define BLOCK_TX_SIZE (16+1) //Must small than 32
/*
* Send init commands to L4F00242T03
*
*/
static int lcd_init(void)
{
int i, j;
int dat;
u8 *tables[] = { xga2xga_tbl1, xga2xga_tbl2 };
int tbl_len[] = {
sizeof(xga2xga_tbl1) / sizeof(u8),
sizeof(xga2xga_tbl2) / sizeof(u8)
};
dev_dbg(&cs8556_client->dev, "initializing CS8556AG\n");
/* read device ID */
msleep(100);
dat = i2c_smbus_read_byte_data(cs8556_client, 0x00);
dev_dbg(&cs8556_client->dev, "read id = 0x%02X\n", dat);
//if (dat != 0x54)
// return -ENODEV;
// Writing configuration table.
for (i = 0; i < 2; i++) {
for (j = 0; j < tbl_len[i]; j += BLOCK_TX_SIZE) {
if (i2c_smbus_write_i2c_block_data(cs8556_client, i,
BLOCK_TX_SIZE, &(tables[i][j]) ) < 0) {
printk("@_@ VGA controller init error!!\n");
return -ENODEV;
}
}
}
return 0;
}
static int lcd_on;
/*
* Send Power On commands to L4F00242T03
*
*/
static void lcd_poweron(struct fb_info *info)
{
u16 data[4];
u32 refresh;
if (lcd_on)
return;
dev_dbg(&cs8556_client->dev, "turning on LCD\n");
data[0] = PICOS2KHZ(info->var.pixclock) / 10;
data[2] = info->var.hsync_len + info->var.left_margin +
info->var.xres + info->var.right_margin;
data[3] = info->var.vsync_len + info->var.upper_margin +
info->var.yres + info->var.lower_margin;
refresh = data[2] * data[3];
refresh = (PICOS2KHZ(info->var.pixclock) * 1000) / refresh;
data[1] = refresh * 100;
lcd_on = 1;
}
/*
* Send Power Off commands to L4F00242T03
*
*/
static void lcd_poweroff(void)
{
if (!lcd_on)
return;
dev_dbg(&cs8556_client->dev, "turning off LCD\n");
lcd_on = 0;
}
static const struct i2c_device_id cs8556_id[] = {
{"cs8556", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, cs8556_id);
static struct i2c_driver cs8556_driver = {
.driver = {
.name = "cs8556",
},
.probe = cs8556_probe,
.remove = cs8556_remove,
.suspend = cs8556_suspend,
.resume = cs8556_resume,
.id_table = cs8556_id,
};
static int __init cs8556_init(void)
{
return i2c_add_driver(&cs8556_driver);
}
static void __exit cs8556_exit(void)
{
i2c_del_driver(&cs8556_driver);
}
module_init(cs8556_init);
module_exit(cs8556_exit);
MODULE_AUTHOR("Pegatron Corporation");
MODULE_DESCRIPTION("cs8556 VGA driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,950 @@
/*
* Copyright 2009 Pegatron, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @defgroup Framebuffer Framebuffer Driver for Efikasb Platform.
*/
/*!
* @file mxcfb_mtl017.c
*
* @brief MXC Frame buffer driver for Efikasb Platform
*
* @ingroup Framebuffer
*/
/*!
* Include files
*/
#define DEBUG
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/console.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/i2c.h>
#include <linux/mxcfb.h>
#include <linux/ipu.h>
#include <linux/spinlock.h>
#include <mach/mxc_edid.h>
#include <mach/hardware.h>
#define EDID_LENGTH 128
extern int register_backlight_notifier(struct notifier_block *nb);
extern int unregister_backlight_notifier(struct notifier_block *nb);
#if 0 //vv
struct mtl017_dev_data {
struct i2c_client *client;
u8 edid[EDID_LENGTH];
struct fb_videomode *mode;
u8 *regs;
int suspending;
int disp_on;
struct semaphore sem;
void (*reset)(void);
void (*power_on_lcd) (int);
void (*power_on_lvds) (int);
void (*turn_on_backlight) (int);
void (*lvds_enable) (int);
};
#else
typedef struct mtl017_dev_data_tag {
struct i2c_client *client;
u8 edid[EDID_LENGTH];
struct fb_videomode *mode;
u8 *regs;
int suspending;
int disp_on;
struct semaphore sem;
void (*reset)(void);
void (*power_on_lcd) (int);
void (*power_on_lvds) (int);
void (*turn_on_backlight) (int);
void (*lvds_enable) (int);
} mtl017_dev_data;
#endif
//vv static struct mtl017_dev_data *mtl017 = NULL;
static mtl017_dev_data * mtl017 = NULL; //vv
static void mtl017_conf(u8 *reg_tbl);
static void disp_power_on(void);
static void disp_power_off(void);
static void disp_on_work_handler(struct work_struct *work);
static void disp_off_work_handler(struct work_struct *work);
static void disp_on_work_handler(struct work_struct *work)
{
disp_power_on();
}
static void disp_off_work_handler(struct work_struct *work)
{
disp_power_off();
}
static DECLARE_WORK(disp_off_work, disp_off_work_handler);
static DECLARE_WORK(disp_on_work, disp_on_work_handler);
#ifdef DEBUG_MTL017
static void dump_edid(unsigned char *id)
{
int i;
printk("EDID Dump:\n");
for (i=0; i < EDID_LENGTH; i++) {
printk("[%x]=%x\n", i, id[i]);
}
printk("\n");
}
static void dump_screeninfo(struct fb_var_screeninfo *einfo)
{
printk("resolution: r(%d, %d), v(%d, %d) o(%d, %d)\n",
einfo->xres,
einfo->yres,
einfo->xres_virtual,
einfo->yres_virtual,
einfo->xoffset,
einfo->yoffset);
printk("bpp=%d, hw(%d, %d), pclk=%d\n",
einfo->bits_per_pixel,
einfo->height,
einfo->width,
einfo->pixclock);
printk("margin(%d, %d, %d, %d)\n",
einfo->left_margin,
einfo->right_margin,
einfo->upper_margin,
einfo->lower_margin);
printk("hv(%d, %d), sync=%x, vmode=%x\n",
einfo->hsync_len,
einfo->vsync_len,
einfo->sync,
einfo->vmode);
}
#else
static void dump_edid(unsigned char *edid) {}
static void dump_screeninfo(struct fb_var_screeninfo *einfo) {}
#endif
static u8 mtl017_auo_tbl[] = {
/* ron: 66M to 66M */
/* 0x00,0x20,0xAF,0x59,0x2B,0xDE,0x51,0x00, */
/* 0x00,0x04,0x17,0x00,0x58,0x02,0x00,0x00, */
/* 0x00,0x21,0x01,0x08,0x00,0x1E,0x01,0x05, */
/* 0x00,0x01,0x7C,0x04,0x32,0x00,0x00,0x04, */
/* 0x00,0x00,0x20,0xA8,0x02,0x12,0x00,0x58, */
/* 0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00, */
/* 0x00,0x02,0x10,0x01,0x68,0x03,0xC2,0x01, */
/* 0x4A,0x03,0x46,0x00,0xF1,0x01,0x5C,0x04, */
/* 0x08,0x00,0x10,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x3A, */
/* 0x18,0x4B,0x29,0x5C,0xDE,0xF6,0xE0,0x1C, */
/* 0x03,0xFC,0xE3,0x1F,0xF3,0x75,0x26,0x45, */
/* 0x4A,0x91,0x8A,0xFF,0x3F,0x83,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x20,0x4E,0x48, */
/* 0x00,0x00,0x06,0x01,0x02,0x00,0x10,0x04, */
/* 0x02,0x1F,0x00,0x00,0x00,0x0A,0x00,0x00, */
/* 0x32,0x00,0x00,0x04,0x12,0x00,0x00,0x03, */
/* 0x02,0x7C,0x04,0x98,0x02,0x11,0x78,0x18, */
/* 0x30,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, */
/* 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, */
/* 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, */
/* ron: 44MHz to 54MHz */
/* 0x00,0x20,0xAF,0x59,0x2B,0xDE,0x51,0x00, */
/* 0x00,0x04,0x17,0x00,0x58,0x02,0x00,0x00, */
/* 0x00,0x3B,0x01,0x08,0x00,0x1E,0x01,0x05, */
/* 0x00,0x01,0x82,0x05,0x32,0x00,0x00,0x04, */
/* 0x00,0x00,0x20,0xA8,0x02,0x12,0x00,0x58, */
/* 0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00, */
/* 0x00,0x02,0x10,0x01,0x68,0x03,0xC2,0x01, */
/* 0x4A,0x03,0x46,0x00,0xF1,0x01,0x5C,0x04, */
/* 0x08,0x00,0x10,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x3A, */
/* 0x18,0x4B,0x29,0x5C,0xDE,0xF6,0xE0,0x1C, */
/* 0x03,0xFC,0xE3,0x1F,0xF3,0x75,0x26,0x45, */
/* 0x4A,0x91,0x8A,0xFF,0x3F,0x83,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x20,0x4E,0x48, */
/* 0x00,0x04,0x2A,0x01,0x02,0x00,0x10,0x04, */
/* 0x02,0x1F,0x00,0x00,0x00,0x0A,0x00,0x00, */
/* 0x32,0x00,0x00,0x04,0x12,0x00,0x58,0x02, */
/* 0x02,0x7C,0x04,0x7E,0x02,0x11,0x78,0x18, */
/* 0x30,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, */
/* 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, */
/* 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, */
/* ron: 44MHz to 48MHz */
/* 0x00,0x20,0xAF,0x59,0x2B,0xDE,0x51,0x00, */
/* 0x00,0x04,0x16,0x00,0x59,0x02,0x00,0x00, */
/* 0x00,0x21,0x00,0x09,0x00,0x1E,0x01,0x05, */
/* 0x00,0x01,0xEA,0x04,0x32,0x00,0x00,0x04, */
/* 0x00,0x00,0x20,0xA8,0x02,0x12,0x00,0x58, */
/* 0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00, */
/* 0x00,0x02,0x10,0x01,0x68,0x03,0xC2,0x01, */
/* 0x4A,0x03,0x46,0x00,0xF1,0x01,0x5C,0x04, */
/* 0x08,0x00,0x10,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x3A, */
/* 0x18,0x4B,0x29,0x5C,0xDE,0xF6,0xE0,0x1C, */
/* 0x03,0xFC,0xE3,0x1F,0xF3,0x75,0x26,0x45, */
/* 0x4A,0x91,0x8A,0xFF,0x3F,0x83,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x20,0x4E,0x48, */
/* 0x00,0x02,0x16,0x01,0x02,0x00,0x10,0x04, */
/* 0x02,0x1F,0x00,0x00,0x00,0x0A,0x00,0x00, */
/* 0x32,0x00,0x00,0x04,0x12,0x00,0x00,0x03, */
/* 0x02,0x7C,0x04,0x7E,0x02,0x11,0x78,0x18, */
/* 0x30,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, */
/* 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, */
/* 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, */
/* ron: 44M to 53.8M */
/* 0x00,0x20,0xAF,0x59,0x2B,0xDE,0x51,0x00, */
/* 0x00,0x04,0x17,0x00,0x58,0x02,0x00,0x00, */
/* 0x00,0x3B,0x01,0x08,0x00,0x1E,0x01,0x05, */
/* 0x00,0x01,0x7E,0x05,0x32,0x00,0x00,0x04, */
/* 0x00,0x00,0x20,0xA8,0x02,0x12,0x00,0x58, */
/* 0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00, */
/* 0x00,0x02,0x10,0x01,0x68,0x03,0xC2,0x01, */
/* 0x4A,0x03,0x46,0x00,0xF1,0x01,0x5C,0x04, */
/* 0x08,0x00,0x10,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x3A, */
/* 0x18,0x4B,0x29,0x5C,0xDE,0xF6,0xE0,0x1C, */
/* 0x03,0xFC,0xE3,0x1F,0xF3,0x75,0x26,0x45, */
/* 0x4A,0x91,0x8A,0xFF,0x3F,0x83,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x20,0x4E,0x48, */
/* 0x00,0x06,0x3B,0x01,0x02,0x00,0x10,0x04, */
/* 0x02,0x1F,0x00,0x00,0x00,0x0A,0x00,0x00, */
/* 0x32,0x00,0x00,0x04,0x12,0x00,0x58,0x02, */
/* 0x02,0x7C,0x04,0x98,0x02,0x11,0x78,0x18, */
/* 0x30,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, */
/* 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, */
/* 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, */
/* ron: 44M to 53.9M */
0x00,0x20,0xAF,0x59,0x2B,0xDE,0x51,0x00,
0x00,0x04,0x17,0x00,0x58,0x02,0x00,0x00,
0x00,0x3B,0x01,0x08,0x00,0x1E,0x01,0x05,
0x00,0x01,0x72,0x05,0x32,0x00,0x00,0x04,
0x00,0x00,0x20,0xA8,0x02,0x12,0x00,0x58,
0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,
0x00,0x02,0x10,0x01,0x68,0x03,0xC2,0x01,
0x4A,0x03,0x46,0x00,0xF1,0x01,0x5C,0x04,
0x08,0x00,0x10,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x3A,
0x18,0x4B,0x29,0x5C,0xDE,0xF6,0xE0,0x1C,
0x03,0xFC,0xE3,0x1F,0xF3,0x75,0x26,0x45,
0x4A,0x91,0x8A,0xFF,0x3F,0x83,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x20,0x4E,0x48,
0x00,0x01,0x10,0x01,0x00,0x00,0x10,0x04,
0x02,0x1F,0x00,0x00,0x00,0x0A,0x00,0x00,
0x32,0x00,0x00,0x04,0x12,0x00,0x58,0x02,
0x02,0x7C,0x04,0x98,0x02,0x11,0x78,0x18,
0x30,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};
static u8 mtl017_cpt_tbl[] = {
/* 0x00,0x20,0xAF,0x59,0x2B,0xDE,0x51,0x00, */
/* 0x00,0x04,0x17,0x00,0x58,0x02,0x00,0x00, */
/* 0x00,0x21,0x01,0x08,0x00,0x1E,0x01,0x05, */
/* 0x00,0x01,0x7C,0x04,0x32,0x00,0x00,0x04, */
/* 0x00,0x00,0x20,0xA8,0x02,0x12,0x00,0x58, */
/* 0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00, */
/* 0x00,0x02,0x10,0x01,0x68,0x03,0xC2,0x01, */
/* 0x4A,0x03,0x46,0x00,0xF1,0x01,0x5C,0x04, */
/* 0x08,0x00,0x10,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x3A, */
/* 0x18,0x4B,0x29,0x5C,0xDE,0xF6,0xE0,0x1C, */
/* 0x03,0xFC,0xE3,0x1F,0xF3,0x75,0x26,0x45, */
/* 0x4A,0x91,0x8A,0xFF,0x3F,0x83,0x00,0x00, */
/* 0x00,0x00,0x00,0x00,0x00,0x20,0x4E,0x48, */
/* 0x00,0x00,0x06,0x01,0x02,0x00,0x10,0x04, */
/* 0x02,0x1F,0x00,0x00,0x00,0x0A,0x00,0x00, */
/* 0x32,0x00,0x00,0x04,0x12,0x00,0x00,0x03, */
/* 0x02,0x7C,0x04,0x98,0x02,0x11,0x78,0x18, */
/* 0x30,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, */
/* 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, */
/* 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, */
/* ron: 44M to 53.9M */
0x00,0x20,0xAF,0x59,0x2B,0xDE,0x51,0x00,
0x00,0x04,0x17,0x00,0x58,0x02,0x00,0x00,
0x00,0x3B,0x01,0x08,0x00,0x1E,0x01,0x05,
0x00,0x01,0x72,0x05,0x32,0x00,0x00,0x04,
0x00,0x00,0x20,0xA8,0x02,0x12,0x00,0x58,
0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,
0x00,0x02,0x10,0x01,0x68,0x03,0xC2,0x01,
0x4A,0x03,0x46,0x00,0xF1,0x01,0x5C,0x04,
0x08,0x00,0x10,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x3A,
0x18,0x4B,0x29,0x5C,0xDE,0xF6,0xE0,0x1C,
0x03,0xFC,0xE3,0x1F,0xF3,0x75,0x26,0x45,
0x4A,0x91,0x8A,0xFF,0x3F,0x83,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x20,0x4E,0x48,
0x00,0x01,0x10,0x01,0x00,0x00,0x10,0x04,
0x02,0x1F,0x00,0x00,0x00,0x0A,0x00,0x00,
0x32,0x00,0x00,0x04,0x12,0x00,0x58,0x02,
0x02,0x7C,0x04,0x98,0x02,0x11,0x78,0x18,
0x30,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};
static u8 mtl017_auo_hires_tbl[] = {
0x00,0x20,0xAF,0x59,0x2B,0xDE,0x3D,0x00,
0x00,0x05,0x0C,0x00,0xD0,0x02,0x00,0x00,
0x00,0x05,0x00,0x02,0x00,0x02,0x00,0x0A,
0x00,0x01,0x70,0x05,0x3D,0x00,0x00,0x05,
0x00,0x00,0x20,0xF0,0x02,0x0C,0x00,0xD0,
0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,
0x00,0x02,0x10,0x01,0x68,0x03,0xC2,0x01,
0x4A,0x03,0x46,0x00,0xF1,0x01,0x5C,0x04,
0x08,0x00,0x10,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x3A,
0x18,0x4B,0x29,0x5C,0xDE,0xF6,0xE0,0x1C,
0x03,0xFC,0xE3,0x1F,0xF3,0x75,0x26,0x45,
0x4A,0x91,0x8A,0xFF,0x3F,0x83,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x20,0x4E,0x48,
0x00,0x00,0x06,0x01,0x02,0x00,0x12,0x04,
0x02,0x1F,0x00,0x00,0x00,0x0A,0x00,0x00,
0x3D,0x00,0x00,0x05,0x0C,0x00,0xD0,0x02,
0x02,0x70,0x05,0xE0,0x02,0x11,0x78,0x18,
0x30,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};
static u8 mtl017_cmo_tbl[] = {
/* ron: 44M to 53.9M */
0x00,0x20,0xAF,0x59,0x2B,0xDE,0x51,0x00,
0x00,0x04,0x17,0x00,0x58,0x02,0x00,0x00,
0x00,0x3B,0x01,0x08,0x00,0x1E,0x01,0x05,
0x00,0x01,0x72,0x05,0x32,0x00,0x00,0x04,
0x00,0x00,0x20,0xA8,0x02,0x12,0x00,0x58,
0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,
0x00,0x02,0x10,0x01,0x68,0x03,0xC2,0x01,
0x4A,0x03,0x46,0x00,0xF1,0x01,0x5C,0x04,
0x08,0x00,0x10,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x3A,
0x18,0x4B,0x29,0x5C,0xDE,0xF6,0xE0,0x1C,
0x03,0xFC,0xE3,0x1F,0xF3,0x75,0x26,0x45,
0x4A,0x91,0x8A,0xFF,0x3F,0x83,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x20,0x4E,0x48,
0x00,0x01,0x10,0x01,0x00,0x00,0x10,0x04,
0x02,0x1F,0x00,0x00,0x00,0x0A,0x00,0x00,
0x32,0x00,0x00,0x04,0x12,0x00,0x58,0x02,
0x02,0x7C,0x04,0x98,0x02,0x11,0x78,0x18,
0x30,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};
#define REGMAP_LENGTH ( sizeof(mtl017_auo_tbl) / sizeof(u8) )
#define BLOCK_TX_SIZE 32
static int mtl017_read_edid(struct i2c_adapter *adp, unsigned char *edid)
{
u8 buf0[2] = {0, 0};
int dat = 0;
u16 addr = 0x50;
struct i2c_msg msg[2] = {
{
.addr = addr,
.flags = 0,
.len = 1,
.buf = buf0,
}, {
.addr = addr,
.flags = I2C_M_RD,
.len = EDID_LENGTH,
.buf = edid,
},
};
if (adp == NULL)
return -EINVAL;
buf0[0] = 0x00;
memset(edid, 0, EDID_LENGTH);
dat = i2c_transfer(adp, msg, 2);
if (dat <= 0)
return -ENODEV;
if (edid[1] == 0x00)
return -ENOENT;
return 0;
}
/* ron: for AUO B101AW02 1024x600 LCD Panel */
static struct fb_videomode auo_mode = {
.name = "AUO B101AW02 1024x600",
.refresh = 60,
.xres = 1024,
.yres = 600,
/* .pixclock = 16666, */ /* ron: 66MHz display clock*/
.pixclock = 22800, /* ron: 44MHz display clock*/
.left_margin = 80,
.right_margin = 40,
.upper_margin = 20,
.lower_margin = 21,
.hsync_len = 4,
.vsync_len = 4,
.sync = FB_SYNC_OE_LOW_ACT,
.vmode = FB_VMODE_NONINTERLACED,
};
/* ron: for AUO B101EW01 1280x720 LCD Panel */
static struct fb_videomode auo_hires_mode = {
.name = "AUO B101EW01 1280x720",
.refresh = 60,
.xres = 1280,
.yres = 720,
.pixclock = 16260,
.left_margin = 32,
.right_margin = 48,
.upper_margin = 7,
.lower_margin = 3,
.hsync_len = 32,
.vsync_len = 6,
.sync = FB_SYNC_OE_LOW_ACT,
.vmode = FB_VMODE_NONINTERLACED,
};
/* ron: for CPT CLAA101NB03A 1024x600 LCD Panel */
static struct fb_videomode cpt_mode = {
.name = "CPT CLAA101 1024x600",
.refresh = 60,
.xres = 1024,
.yres = 600,
.pixclock = 22800, /* ron: 44M display clock */
.left_margin = 80,
.right_margin = 40,
.upper_margin = 20,
.lower_margin = 21,
.hsync_len = 4,
.vsync_len = 4,
.sync = FB_SYNC_OE_LOW_ACT,
.vmode = FB_VMODE_NONINTERLACED,
};
static struct fb_videomode cmo_mode = {
.name = "CMO N101L6-L0D 1024x600",
.refresh = 60,
.xres = 1024,
.yres = 600,
.pixclock = 22800, /* ron: 44MHz display clock*/
.left_margin = 80,
.right_margin = 40,
.upper_margin = 20,
.lower_margin = 21,
.hsync_len = 4,
.vsync_len = 4,
.sync = FB_SYNC_OE_LOW_ACT,
.vmode = FB_VMODE_NONINTERLACED,
};
struct lcd_panel_info {
char manufacture[16];
char product_name[16];
struct fb_videomode *mode;
u8 *regs;
};
static struct lcd_panel_info pinfo[] = {
{
.manufacture = "AUO",
.product_name = "B101AW02 V0",
.mode = &auo_mode,
.regs = mtl017_auo_tbl,
},
{
.manufacture = "CPT",
.product_name = "CLAA101NB03A",
.mode = &cpt_mode,
.regs = mtl017_cpt_tbl,
},
{
.manufacture = "AUO",
.product_name = "B101EW01",
.mode = &auo_hires_mode,
.regs = mtl017_auo_hires_tbl,
},
{
.manufacture = "CMO",
.product_name = "N101L6-L0D",
.mode = &cmo_mode,
.regs = mtl017_cmo_tbl,
}
};
//vv static void mtl017_find_videomode(struct mtl017_dev_data *mtl017)
static void mtl017_find_videomode(mtl017_dev_data *mtl017)
{
int i;
struct i2c_adapter *adp;
struct fb_var_screeninfo var;
int err;
adp = i2c_get_adapter(1);
err = mtl017_read_edid(adp, mtl017->edid);
dump_edid(mtl017->edid);
/* ron: use edid to parse screeninfo,
but these parameters have some problem */
memset(&var, 0, sizeof(var));
fb_parse_edid(mtl017->edid, &var);
dump_screeninfo(&var);
for (i = 0; i < ARRAY_SIZE(pinfo); i ++) {
if(/* (memcmp(mtl017->edid + 0x5f, pinfo[i].manufacture, strlen(pinfo[i].manufacture)) == 0) && */
(memcmp(mtl017->edid + 0x71, pinfo[i].product_name, strlen(pinfo[i].product_name)) == 0)) {
printk("mtl017: Probe LCD Panel: %s %s\n",
pinfo[i].manufacture,
pinfo[i].product_name);
mtl017->mode = pinfo[i].mode;
mtl017->regs = pinfo[i].regs;
return;
}
}
mtl017->regs = NULL;
mtl017->mode = NULL;
}
static void lcd_init_fb(struct fb_info *info)
{
struct fb_var_screeninfo var;
memset(&var, 0, sizeof(var));
fb_videomode_to_var(&var, mtl017->mode);
var.activate = FB_ACTIVATE_ALL;
acquire_console_sem();
info->flags |= FBINFO_MISC_USEREVENT;
fb_set_var(info, &var);
fb_blank(info, FB_BLANK_UNBLANK);
info->flags &= ~FBINFO_MISC_USEREVENT;
release_console_sem();
}
static int lcd_fb_event(struct notifier_block *nb, unsigned long val, void *v)
{
struct fb_event *event = v;
if (strcmp(event->info->fix.id, "DISP3 BG - DI1")) {
return 0;
}
switch (val) {
case FB_EVENT_FB_REGISTERED:
lcd_init_fb(event->info);
break;
case FB_EVENT_BLANK:
if (*((int *)event->data) == FB_BLANK_UNBLANK) {
disp_power_on();
} else {
disp_power_off();
}
break;
}
return 0;
}
#define BL_BRIGHTNESS 0x01
static int lcd_bl_event(struct notifier_block *nb, unsigned long val, void *v)
{
int brightness = v;
switch (val) {
case BL_BRIGHTNESS:
if(brightness == 0) {
disp_power_off();
} else {
disp_power_on();
}
break;
}
return 0;
}
static struct notifier_block fb_nb = {
.notifier_call = lcd_fb_event,
};
static struct notifier_block bl_nb = {
.notifier_call = lcd_bl_event,
};
/*!
* This function is called whenever the SPI slave device is detected.
*
* @param spi the SPI slave device
*
* @return Returns 0 on SUCCESS and error on FAILURE.
*/
static int __devinit mtl017_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct mxc_lcd_platform_data *plat = client->dev.platform_data;
int retval;
int i;
//vv mtl017 = kzalloc(sizeof(*mtl017), GFP_KERNEL);
mtl017 = (mtl017_dev_data*)kzalloc(sizeof(*mtl017), GFP_KERNEL);
if(!mtl017) {
dev_err(&client->dev, "failed to allocate mtl017 device\n");
retval = -ENOMEM;
return retval;
}
mtl017->suspending = 0;
mtl017->client = client;
if(plat) {
mtl017->reset =
plat->reset;
mtl017->power_on_lcd =
plat->power_on_lcd;
mtl017->power_on_lvds =
plat->power_on_lvds;
mtl017->turn_on_backlight =
plat->turn_on_backlight;
mtl017->lvds_enable =
plat->lvds_enable;
}
/* mtl017->lock = SPIN_LOCK_UNLOCKED; */
sema_init(&mtl017->sem, 1);
mtl017_find_videomode(mtl017);
if(mtl017->mode == NULL)
mtl017->mode = &auo_mode;
if(mtl017->regs == NULL)
mtl017->regs = mtl017_auo_tbl;
for (i = 0; i < num_registered_fb; i++) {
if (strcmp(registered_fb[i]->fix.id, "DISP3 BG - DI1") == 0) {
lcd_init_fb(registered_fb[i]);
fb_show_logo(registered_fb[i], 0);
}
}
fb_register_client(&fb_nb);
register_backlight_notifier(&bl_nb);
/* disp_power_on(); */
schedule_work(&disp_on_work);
return 0;
}
static int __devexit mtl017_remove(struct i2c_client *client)
{
unregister_backlight_notifier(&bl_nb);
fb_unregister_client(&fb_nb);
disp_power_off();
kfree(mtl017);
return 0;
}
static int mtl017_suspend(struct i2c_client *client, pm_message_t message)
{
mtl017->suspending = 1;
disp_power_off();
return 0;
}
static int mtl017_resume(struct i2c_client *client)
{
/* ron: because we turn off regulator, */
/* so re-initialize MTL017 controller after suspending */
mtl017->suspending = 0;
disp_power_on();
return 0;
}
static void mtl017_conf(u8 *reg_tbl)
{
int i;
int ret;
int retry = 5;
dev_dbg(&mtl017->client->dev, "Initializing MTL017 LVDS Controller\n");
// Writing configuration table.
for (i = 0; i < REGMAP_LENGTH; i+=BLOCK_TX_SIZE) {
retry:
msleep(1);
ret = i2c_smbus_write_i2c_block_data(mtl017->client, i, BLOCK_TX_SIZE, &(reg_tbl[i]));
if (ret < 0) {
printk(KERN_WARNING "MTL017 LVDS Controller Initialize Fail!\n");
if(retry -- > 0)
goto retry;
return;
}
}
}
static void disp_power_on(void)
{
if(mtl017->disp_on || mtl017->suspending)
return;
down_interruptible(&mtl017->sem);
if(mtl017->mode == &auo_mode) {
if(mtl017->power_on_lcd)
mtl017->power_on_lcd(1);
msleep(10);
if(mtl017->lvds_enable)
mtl017->lvds_enable(1);
msleep(5);
if(mtl017->power_on_lvds)
mtl017->power_on_lvds(1);
msleep(5);
mtl017_conf(mtl017->regs);
msleep(200);
if(mtl017->turn_on_backlight)
mtl017->turn_on_backlight(1);
} else {
if(mtl017->lvds_enable)
mtl017->lvds_enable(-1);
if(mtl017->power_on_lcd)
mtl017->power_on_lcd(1);
if(mtl017->power_on_lvds)
mtl017->power_on_lvds(1);
mtl017_conf(mtl017->regs);
msleep(200);
if(mtl017->turn_on_backlight)
mtl017->turn_on_backlight(1);
}
mtl017->disp_on = 1;
up(&mtl017->sem);
}
static void disp_power_off(void)
{
if(mtl017->disp_on == 0)
return;
down_interruptible(&mtl017->sem);
if(mtl017->turn_on_backlight)
mtl017->turn_on_backlight(0);
msleep(200);
if(mtl017->mode == &auo_mode) {
if(mtl017->lvds_enable)
mtl017->lvds_enable(0);
}
if(mtl017->power_on_lvds)
mtl017->power_on_lvds(0);
msleep(5);
if(mtl017->power_on_lcd)
mtl017->power_on_lcd(0);
mtl017->disp_on = 0;
up(&mtl017->sem);
}
static const struct i2c_device_id mtl017_id[] = {
{ "mtl017", 0 },
{},
};
MODULE_DEVICE_TABLE(i2c, mtl017_id);
static struct i2c_driver mtl017_driver = {
.driver = {
.name = "mtl017",
},
.probe = mtl017_probe,
.remove = mtl017_remove,
.suspend = mtl017_suspend,
.resume = mtl017_resume,
.id_table = mtl017_id,
};
static int __init mtl017_init(void)
{
return i2c_add_driver(&mtl017_driver);
}
static void __exit mtl017_exit(void)
{
i2c_del_driver(&mtl017_driver);
}
module_init(mtl017_init);
module_exit(mtl017_exit);
MODULE_AUTHOR("Ron Lee <ron1_lee@pegatroncorp.com> Pegatron Corp.");
MODULE_DESCRIPTION("MTL017 LVDS Controller driver");
MODULE_LICENSE("GPL");