Add support for the n516

SVN-Revision: 19987
lede-17.01
Lars-Peter Clausen 2010-03-05 04:21:41 +00:00
parent f1afccc2d9
commit 01ed21fc16
11 changed files with 2467 additions and 3 deletions

View File

@ -0,0 +1,39 @@
/*
* linux/include/asm-mips/mach-jz4740/board-n516.h
*
* JZ4730-based N516 board definition.
*
* Copyright (C) 2009, Yauhen Kharuzhy <jekhor@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __ASM_JZ4740_N516_H__
#define __ASM_JZ4740_N516_H__
#include <asm/mach-jz4740/gpio.h>
/*
* GPIO
*/
#define GPIO_SD_VCC_EN_N JZ_GPIO_PORTD(17)
#define GPIO_SD_CD_N JZ_GPIO_PORTD(7)
#define GPIO_SD_WP JZ_GPIO_PORTD(15)
#define GPIO_USB_DETECT JZ_GPIO_PORTD(19)
#define GPIO_CHARG_STAT_N JZ_GPIO_PORTD(16)
#define GPIO_LED_ENABLE JZ_GPIO_PORTD(28)
#define GPIO_LPC_INT JZ_GPIO_PORTD(14)
#define GPIO_HPHONE_DETECT JZ_GPIO_PORTD(20)
#define GPIO_SPEAKER_ENABLE JZ_GPIO_PORTD(21)
/* Display */
#define GPIO_DISPLAY_RST_L JZ_GPIO_PORTB(18)
#define GPIO_DISPLAY_RDY JZ_GPIO_PORTB(17)
#define GPIO_DISPLAY_STBY JZ_GPIO_PORTC(22)
#define GPIO_DISPLAY_ERR JZ_GPIO_PORTC(23)
#define GPIO_DISPLAY_OFF_N JZ_GPIO_PORTD(1)
#endif /* __ASM_JZ4740_N516_H__ */

View File

@ -8,6 +8,11 @@ config JZ4740_QI_LB60
select DMA_NONCOHERENT
select SOC_JZ4740
config JZ4740_N516
bool "Hanvon n516 eBook reader"
select DMA_NONCOHERENT
select SOC_JZ4740
config JZ4740_N526
bool "Hanvon n526 eBook reader"
select DMA_NONCOHERENT

View File

@ -12,9 +12,10 @@ obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o
# board specific support
obj-$(CONFIG_JZ4740_QI_LB60) += board-qi_lb60.o
obj-$(CONFIG_JZ4740_N516) += board-n516.o board-n516-display.o
obj-$(CONFIG_JZ4740_N526) += board-n526.o
# PM support
obj-$(CONFIG_PM) +=pm.o
obj-$(CONFIG_PM) += pm.o

View File

@ -0,0 +1,395 @@
/*
* board-n516-display.c -- Platform device for N516 display
*
* Copyright (C) 2009, Yauhen Kharuzhy <jekhor@gmail.com>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/jz4740_fb.h>
#include <asm/mach-jz4740/platform.h>
#include <asm/mach-jz4740/board-n516.h>
#include <video/metronomefb.h>
#include <linux/console.h>
extern struct platform_device jz_lcd_device;
static struct fb_videomode n516_fb_modes[] = {
[0] = {
.name = "Metronome 800x600",
.refresh = 50,
.xres = 400,
.yres = 624,
.hsync_len = 31,
.vsync_len = 23,
.right_margin = 31,
.left_margin = 5,
.upper_margin = 1,
.lower_margin = 2,
.sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
},
};
static struct jz4740_fb_platform_data n516_fb_pdata = {
.num_modes = ARRAY_SIZE(n516_fb_modes),
.modes = n516_fb_modes,
.bpp = 16,
.lcd_type = JZ_LCD_TYPE_GENERIC_16_BIT,
};
struct n516_board_info {
uint8_t *metromem;
size_t wfm_size;
struct fb_info *host_fbinfo; /* the host LCD controller's fbi */
unsigned int fw;
unsigned int fh;
};
static struct platform_device *n516_device;
static struct n516_board_info n516_board_info;
static int metronome_gpios[] = {
GPIO_DISPLAY_STBY,
GPIO_DISPLAY_RST_L,
GPIO_DISPLAY_RDY,
GPIO_DISPLAY_ERR,
/* GPIO_DISPLAY_OFF,*/
};
static const char *metronome_gpio_names[] = {
"Metronome STDBY",
"Metronome RST",
"Metronome RDY",
"Metronome ERR",
/* "Metronone OFF",*/
};
static int n516_enable_hostfb(bool enable)
{
int ret;
int blank = enable ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
acquire_console_sem();
ret = fb_blank(n516_board_info.host_fbinfo, blank);
release_console_sem();
return ret;
}
static int n516_init_metronome_gpios(struct metronomefb_par *par)
{
int i;
int ret;
for (i = 0; i < ARRAY_SIZE(metronome_gpios); ++i) {
ret = gpio_request(metronome_gpios[i], metronome_gpio_names[i]);
if (ret)
goto err;
}
gpio_direction_output(GPIO_DISPLAY_OFF, 0);
gpio_direction_output(GPIO_DISPLAY_RST_L, 0);
gpio_direction_output(GPIO_DISPLAY_STBY, 0);
gpio_direction_input(GPIO_DISPLAY_RDY);
gpio_direction_input(GPIO_DISPLAY_ERR);
return 0;
err:
for (--i; i >= 0; --i)
gpio_free(metronome_gpios[i]);
return ret;
}
static int n516_share_video_mem(struct fb_info *info)
{
int ret;
dev_dbg(&n516_device->dev, "ENTER %s\n", __func__);
dev_dbg(&n516_device->dev, "%s, info->var.xres = %u, info->var.yres = %u\n", __func__, info->var.xres, info->var.yres);
/* rough check if this is our desired fb and not something else */
if ((info->var.xres != n516_fb_pdata.modes[0].xres)
|| (info->var.yres != n516_fb_pdata.modes[0].yres))
return 0;
/* we've now been notified that we have our new fb */
n516_board_info.metromem = info->screen_base;
n516_board_info.host_fbinfo = info;
n516_enable_hostfb(false);
/* try to refcount host drv since we are the consumer after this */
if (!try_module_get(info->fbops->owner))
return -ENODEV;
/* this _add binds metronomefb to n516. metronomefb refcounts n516 */
ret = platform_device_add(n516_device);
if (ret) {
platform_device_put(n516_device);
return ret;
}
/* request our platform independent driver */
request_module("metronomefb");
return 0;
}
static int n516_unshare_video_mem(struct fb_info *info)
{
dev_dbg(&n516_device->dev, "ENTER %s\n", __func__);
if (info != n516_board_info.host_fbinfo)
return 0;
module_put(n516_board_info.host_fbinfo->fbops->owner);
return 0;
}
static int n516_fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct fb_event *evdata = data;
struct fb_info *info = evdata->info;
dev_dbg(&n516_device->dev, "ENTER %s\n", __func__);
if (event == FB_EVENT_FB_REGISTERED)
return n516_share_video_mem(info);
else if (event == FB_EVENT_FB_UNREGISTERED)
return n516_unshare_video_mem(info);
return 0;
}
static struct notifier_block n516_fb_notif = {
.notifier_call = n516_fb_notifier_callback,
};
/* this gets called as part of our init. these steps must be done now so
* that we can use set_pxa_fb_info */
static void __init n516_presetup_fb(void)
{
int padding_size;
int totalsize;
/* the frame buffer is divided as follows:
command | CRC | padding
16kb waveform data | CRC | padding
image data | CRC
*/
n516_board_info.fw = 800;
n516_board_info.fh = 624;
/* waveform must be 16k + 2 for checksum */
n516_board_info.wfm_size = roundup(16*1024 + 2, n516_board_info.fw);
padding_size = PAGE_SIZE + (4 * n516_board_info.fw);
/* total is 1 cmd , 1 wfm, padding and image */
totalsize = n516_board_info.fw + n516_board_info.wfm_size;
totalsize += padding_size + (n516_board_info.fw*n516_board_info.fh);
/* save this off because we're manipulating fw after this and
* we'll need it when we're ready to setup the framebuffer */
/* the reason we do this adjustment is because we want to acquire
* more framebuffer memory without imposing custom awareness on the
* underlying driver */
n516_fb_pdata.modes[0].yres = DIV_ROUND_UP(totalsize, n516_board_info.fw);
jz4740_framebuffer_device.dev.platform_data = &n516_fb_pdata;
platform_device_register(&jz4740_framebuffer_device);
}
/* this gets called by metronomefb as part of its init, in our case, we
* have already completed initial framebuffer init in presetup_fb so we
* can just setup the fb access pointers */
static int n516_setup_fb(struct metronomefb_par *par)
{
/* metromem was set up by the notifier in share_video_mem so now
* we can use its value to calculate the other entries */
par->metromem_cmd = (struct metromem_cmd *) n516_board_info.metromem;
par->metromem_wfm = n516_board_info.metromem + n516_board_info.fw;
par->metromem_img = par->metromem_wfm + n516_board_info.wfm_size;
par->metromem_img_csum = (u16 *) (par->metromem_img + (n516_board_info.fw * n516_board_info.fh));
par->metromem_dma = n516_board_info.host_fbinfo->fix.smem_start;
return 0;
}
static int n516_get_panel_type(void)
{
return 5;
}
static irqreturn_t n516_handle_irq(int irq, void *dev_id)
{
struct metronomefb_par *par = dev_id;
dev_dbg(&par->pdev->dev, "Metronome IRQ! RDY=%d\n", gpio_get_value(GPIO_DISPLAY_RDY));
wake_up_all(&par->waitq);
return IRQ_HANDLED;
}
static void n516_power_ctl(struct metronomefb_par *par, int cmd)
{
switch (cmd) {
case METRONOME_POWER_OFF:
gpio_set_value(GPIO_DISPLAY_OFF, 1);
n516_enable_hostfb(false);
break;
case METRONOME_POWER_ON:
gpio_set_value(GPIO_DISPLAY_OFF, 0);
n516_enable_hostfb(true);
break;
}
}
static int n516_get_rdy(struct metronomefb_par *par)
{
return gpio_get_value(GPIO_DISPLAY_RDY);
}
static int n516_get_err(struct metronomefb_par *par)
{
return gpio_get_value(GPIO_DISPLAY_ERR);
}
static int n516_setup_irq(struct fb_info *info)
{
int ret;
dev_dbg(&n516_device->dev, "ENTER %s\n", __func__);
ret = request_irq(gpio_to_irq(GPIO_DISPLAY_RDY), n516_handle_irq,
IRQF_TRIGGER_RISING,
"n516", info->par);
if (ret)
dev_err(&n516_device->dev, "request_irq failed: %d\n", ret);
return ret;
}
static void n516_set_rst(struct metronomefb_par *par, int state)
{
dev_dbg(&n516_device->dev, "ENTER %s, RDY=%d\n", __func__, gpio_get_value(GPIO_DISPLAY_RDY));
if (state)
gpio_set_value(GPIO_DISPLAY_RST_L, 1);
else
gpio_set_value(GPIO_DISPLAY_RST_L, 0);
}
static void n516_set_stdby(struct metronomefb_par *par, int state)
{
dev_dbg(&n516_device->dev, "ENTER %s, RDY=%d\n", __func__, gpio_get_value(GPIO_DISPLAY_RDY));
if (state)
gpio_set_value(GPIO_DISPLAY_STBY, 1);
else
gpio_set_value(GPIO_DISPLAY_STBY, 0);
}
static int n516_wait_event(struct metronomefb_par *par)
{
unsigned long timeout = jiffies + HZ / 20;
dev_dbg(&n516_device->dev, "ENTER1 %s, RDY=%d\n",
__func__, gpio_get_value(GPIO_DISPLAY_RDY));
while (n516_get_rdy(par) && time_before(jiffies, timeout))
schedule();
dev_dbg(&n516_device->dev, "ENTER2 %s, RDY=%d\n",
__func__, gpio_get_value(GPIO_DISPLAY_RDY));
return wait_event_timeout(par->waitq,
n516_get_rdy(par), HZ * 2) ? 0 : -EIO;
}
static int n516_wait_event_intr(struct metronomefb_par *par)
{
unsigned long timeout = jiffies + HZ/20;
dev_dbg(&n516_device->dev, "ENTER1 %s, RDY=%d\n",
__func__, gpio_get_value(GPIO_DISPLAY_RDY));
while (n516_get_rdy(par) && time_before(jiffies, timeout))
schedule();
dev_dbg(&n516_device->dev, "ENTER2 %s, RDY=%d\n",
__func__, gpio_get_value(GPIO_DISPLAY_RDY));
return wait_event_interruptible_timeout(par->waitq,
n516_get_rdy(par), HZ * 2) ? 0 : -EIO;
}
static void n516_cleanup(struct metronomefb_par *par)
{
int i;
free_irq(gpio_to_irq(GPIO_DISPLAY_RDY), par);
for (i = 0; i < ARRAY_SIZE(metronome_gpios); ++i)
gpio_free(metronome_gpios[i]);
}
static struct metronome_board n516_board __initdata = {
.owner = THIS_MODULE,
.power_ctl = n516_power_ctl,
.setup_irq = n516_setup_irq,
.setup_io = n516_init_metronome_gpios,
.setup_fb = n516_setup_fb,
.set_rst = n516_set_rst,
.get_err = n516_get_err,
.get_rdy = n516_get_rdy,
.set_stdby = n516_set_stdby,
.met_wait_event = n516_wait_event,
.met_wait_event_intr = n516_wait_event_intr,
.get_panel_type = n516_get_panel_type,
.cleanup = n516_cleanup,
};
static int __init n516_init(void)
{
int ret;
/* Keep the metronome off, until its driver is loaded */
ret = gpio_request(GPIO_DISPLAY_OFF, "Display off");
if (ret)
return ret;
gpio_direction_output(GPIO_DISPLAY_OFF, 1);
/* before anything else, we request notification for any fb
* creation events */
fb_register_client(&n516_fb_notif);
n516_device = platform_device_alloc("metronomefb", -1);
if (!n516_device)
return -ENOMEM;
/* the n516_board that will be seen by metronomefb is a copy */
platform_device_add_data(n516_device, &n516_board,
sizeof(n516_board));
n516_presetup_fb();
return 0;
}
module_init(n516_init);
MODULE_DESCRIPTION("board driver for n516 display");
MODULE_AUTHOR("Yauhen Kharuzhy");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,201 @@
/*
* linux/arch/mips/jz4740/board-516.c
*
* JZ4740 n516 board setup routines.
*
* Copyright (c) 2009, Yauhen Kharuzhy <jekhor@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/console.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mmc/jz4740_mmc.h>
#include <linux/mtd/jz4740_nand.h>
#include <linux/leds.h>
#include <linux/power_supply.h>
#include <linux/power/gpio-charger.h>
#include <linux/i2c.h>
#include <linux/i2c-gpio.h>
#include <asm/mach-jz4740/board-n516.h>
#include <asm/mach-jz4740/platform.h>
#include "clock.h"
static long n516_panic_blink(long time)
{
gpio_set_value(GPIO_LED_ENABLE, 1);
mdelay(200);
gpio_set_value(GPIO_LED_ENABLE, 0);
mdelay(200);
return 400;
}
static void __init board_gpio_setup(void)
{
/* jz_gpio_enable_pullup(JZ_GPIO_PORTD(23));
jz_gpio_enable_pullup(JZ_GPIO_PORTD(24));*/
}
static struct i2c_gpio_platform_data n516_i2c_pdata = {
.sda_pin = JZ_GPIO_PORTD(23),
.scl_pin = JZ_GPIO_PORTD(24),
.udelay = 2,
.timeout = 3 * HZ,
};
static struct platform_device n516_i2c_device = {
.name = "i2c-gpio",
.id = -1,
.dev = {
.platform_data = &n516_i2c_pdata,
},
};
static const struct i2c_board_info n516_i2c_board_info[] = {
{
.type = "LPC524",
.addr = 0x54,
},
{
.type = "lm75a",
.addr = 0x48,
}
};
static struct jz4740_mmc_platform_data n516_mmc_pdata = {
.gpio_card_detect = GPIO_SD_CD_N,
.card_detect_active_low = 1,
.gpio_read_only = -1,
.gpio_power = GPIO_SD_VCC_EN_N,
.power_active_low = 1,
};
static struct gpio_led n516_leds[] = {
{
.name = "n516:blue:power",
.gpio = GPIO_LED_ENABLE,
.default_state = LEDS_GPIO_DEFSTATE_ON,
.default_trigger = "nand-disk",
}
};
static struct gpio_led_platform_data n516_leds_pdata = {
.leds = n516_leds,
.num_leds = ARRAY_SIZE(n516_leds),
};
static struct platform_device n516_leds_device = {
.name = "leds-gpio",
.id = -1,
.dev = {
.platform_data = &n516_leds_pdata,
},
};
static struct mtd_partition n516_partitions[] = {
{ .name = "NAND BOOT partition",
.offset = 0 * 0x100000,
.size = 4 * 0x100000,
},
{ .name = "NAND KERNEL partition",
.offset = 4 * 0x100000,
.size = 4 * 0x100000,
},
{ .name = "NAND ROOTFS partition",
.offset = 8 * 0x100000,
.size = 504 * 0x100000,
},
};
static struct nand_ecclayout n516_ecclayout = {
.eccbytes = 36,
.eccpos = {
6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40, 41,
},
.oobfree = {
{.offset = 2,
.length = 4},
{.offset = 42,
.length = 22}}
};
static struct jz_nand_platform_data n516_nand_pdata = {
.ecc_layout = &n516_ecclayout,
.partitions = n516_partitions,
.num_partitions = ARRAY_SIZE(n516_partitions),
.busy_gpio = 94,
};
static char *n516_batteries[] = {
"n516_battery",
};
static struct gpio_charger_platform_data n516_charger_pdata = {
.name = "usb",
.type = POWER_SUPPLY_TYPE_USB,
.gpio = GPIO_USB_DETECT,
.gpio_active_low = 1,
.batteries = n516_batteries,
.num_batteries = ARRAY_SIZE(n516_batteries),
};
static struct platform_device n516_charger_device = {
.name = "gpio-charger",
.dev = {
.platform_data = &n516_charger_pdata,
},
};
static struct platform_device *n516_devices[] __initdata = {
&jz4740_nand_device,
&n516_leds_device,
&jz4740_mmc_device,
&jz4740_i2s_device,
&jz4740_codec_device,
&jz4740_rtc_device,
&jz4740_usb_gdt_device,
&n516_i2c_device,
&n516_charger_device,
};
struct jz4740_clock_board_data jz4740_clock_bdata = {
.ext_rate = 12000000,
.rtc_rate = 32768,
};
extern int jz_gpiolib_init(void);
static int n516_setup_platform(void)
{
if (jz_gpiolib_init())
panic("Failed to initalize jz gpio\n");
jz4740_clock_init();
board_gpio_setup();
panic_blink = n516_panic_blink;
i2c_register_board_info(0, n516_i2c_board_info, ARRAY_SIZE(n516_i2c_board_info));
jz4740_mmc_device.dev.platform_data = &n516_mmc_pdata;
jz4740_nand_device.dev.platform_data = &n516_nand_pdata;
return platform_add_devices(n516_devices, ARRAY_SIZE(n516_devices));
}
arch_initcall(n516_setup_platform);

View File

@ -0,0 +1,491 @@
/*
* board-n516-display.c -- Platform device for N516 display
*
* Copyright (C) 2009, Yauhen Kharuzhy <jekhor@gmail.com>
* Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/power_supply.h>
#include <linux/suspend.h>
#include <linux/i2c.h>
#include <asm/mach-jz4740/irq.h>
#include <asm/mach-jz4740/gpio.h>
#include <asm/mach-jz4740/board-n516.h>
static int batt_level=0;
module_param(batt_level, int, 0);
struct n516_lpc_chip {
struct i2c_client *i2c_client;
struct input_dev *input;
unsigned int battery_level;
unsigned int suspending:1, can_sleep:1;
};
static struct n516_lpc_chip *the_lpc;
struct i2c_device_id n516_lpc_i2c_ids[] = {
{"LPC524", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, n516_lpc_i2c_ids);
static const unsigned short normal_i2c[] = {0x54, I2C_CLIENT_END};
static const unsigned int n516_lpc_keymap[] = {
[0x01] = KEY_4,
[0x02] = KEY_3,
[0x03] = KEY_2,
[0x04] = KEY_1,
[0x05] = KEY_0,
[0x07] = KEY_9,
[0x08] = KEY_8,
[0x09] = KEY_7,
[0x0a] = KEY_6,
[0x0b] = KEY_5,
[0x0d] = KEY_PLAYPAUSE,
[0x0e] = KEY_MENU,
[0x0f] = KEY_SEARCH,
[0x10] = KEY_DIRECTION,
[0x11] = KEY_SPACE,
[0x13] = KEY_ENTER,
[0x14] = KEY_UP,
[0x15] = KEY_DOWN,
[0x16] = KEY_RIGHT,
[0x17] = KEY_LEFT,
[0x19] = KEY_PAGEDOWN,
[0x1a] = KEY_PAGEUP,
[0x1c] = KEY_POWER,
[0x1d] = KEY_ESC,
[0x1e] = KEY_SLEEP,
/* [0x1f] = KEY_WAKEUP,*/
};
static const unsigned int batt_charge[] = {0, 7, 20, 45, 65, 80, 100};
#define MAX_BAT_LEVEL (ARRAY_SIZE(batt_charge) - 1)
/* Insmod parameters */
I2C_CLIENT_INSMOD_1(n516_lpc);
static inline int n516_bat_usb_connected(void)
{
return !gpio_get_value(GPIO_USB_DETECT);
}
static inline int n516_bat_charging(void)
{
return !gpio_get_value(GPIO_CHARG_STAT_N);
}
static int n516_bat_get_status(struct power_supply *b)
{
if (n516_bat_usb_connected()) {
if (n516_bat_charging())
return POWER_SUPPLY_STATUS_CHARGING;
else
return POWER_SUPPLY_STATUS_FULL;
} else {
return POWER_SUPPLY_STATUS_DISCHARGING;
}
}
static int n516_bat_get_charge(struct power_supply *b)
{
return batt_charge[the_lpc->battery_level];
}
static int n516_bat_get_property(struct power_supply *b,
enum power_supply_property psp,
union power_supply_propval *val)
{
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = n516_bat_get_status(b);
break;
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
val->intval = 100;
break;
case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
val->intval = 0;
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
val->intval = n516_bat_get_charge(b);
break;
default:
return -EINVAL;
}
return 0;
}
static void n516_bat_power_changed(struct power_supply *p)
{
power_supply_changed(p);
}
static enum power_supply_property n516_bat_properties[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
POWER_SUPPLY_PROP_CHARGE_NOW,
};
static struct power_supply n516_battery = {
.name = "n516-battery",
.get_property = n516_bat_get_property,
.properties = n516_bat_properties,
.num_properties = ARRAY_SIZE(n516_bat_properties),
.external_power_changed = n516_bat_power_changed,
};
static irqreturn_t n516_bat_charge_irq(int irq, void *dev)
{
struct power_supply *psy = dev;
dev_dbg(psy->dev, "Battery charging IRQ\n");
if (n516_bat_usb_connected() && !n516_bat_charging())
the_lpc->battery_level = MAX_BAT_LEVEL;
power_supply_changed(psy);
return IRQ_HANDLED;
}
static int n516_lpc_set_normal_mode(struct n516_lpc_chip *chip)
{
struct i2c_client *client = chip->i2c_client;
unsigned char val = 0x02;
struct i2c_msg msg = {client->addr, client->flags, 1, &val};
int ret = 0;
ret = i2c_transfer(client->adapter, &msg, 1);
return ret > 0 ? 0 : ret;
}
static void n516_key_event(struct n516_lpc_chip *chip, unsigned char keycode)
{
struct i2c_client *client = chip->i2c_client;
bool long_press = false;
if (keycode & 0x40) {
keycode &= ~0x40;
long_press = true;
}
dev_dbg(&client->dev, "keycode: 0x%02x, long_press: 0x%02x\n", keycode, (unsigned int)long_press);
if (keycode >= ARRAY_SIZE(n516_lpc_keymap) || n516_lpc_keymap[keycode] == 0)
return;
if (long_press)
input_report_key(chip->input, KEY_LEFTALT, 1);
input_report_key(chip->input, n516_lpc_keymap[keycode], 1);
input_sync(chip->input);
input_report_key(chip->input, n516_lpc_keymap[keycode], 0);
if (long_press)
input_report_key(chip->input, KEY_LEFTALT, 0);
input_sync(chip->input);
}
static void n516_battery_event(struct n516_lpc_chip *chip, unsigned char battery_level)
{
if (battery_level != chip->battery_level) {
chip->battery_level = battery_level;
power_supply_changed(&n516_battery);
}
}
static irqreturn_t n516_lpc_irq(int irq, void *devid)
{
struct n516_lpc_chip *chip = (struct n516_lpc_chip*)devid;
int ret;
unsigned char raw_msg;
struct i2c_client *client = chip->i2c_client;
struct i2c_msg msg = {client->addr, client->flags | I2C_M_RD, 1, &raw_msg};
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret != 1) {
dev_dbg(&client->dev, "I2C error: %d\n", ret);
return IRQ_HANDLED;
}
dev_dbg(&client->dev, "msg: 0x%02x\n", raw_msg);
if ((raw_msg & 0x40) < ARRAY_SIZE(n516_lpc_keymap)) {
n516_key_event(chip, raw_msg);
} else if ((raw_msg >= 0x81) && (raw_msg <= 0x87)) {
n516_battery_event(chip, raw_msg - 0x81);
} else {
n516_lpc_set_normal_mode(chip);
dev_warn(&client->dev, "Unkown message: %x\n", raw_msg);
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret != 1) {
dev_dbg(&client->dev, "I2C error: %d\n", ret);
} else {
dev_warn(&client->dev, "Unkown message part 2: %x\n", raw_msg);
}
}
if (chip->suspending)
chip->can_sleep = 0;
printk("foobar\n");
return IRQ_HANDLED;
}
static void n516_lpc_power_off(void)
{
struct i2c_client *client = the_lpc->i2c_client;
unsigned char val = 0x01;
struct i2c_msg msg = {client->addr, client->flags, 1, &val};
printk("Issue LPC POWEROFF command...\n");
while (1)
i2c_transfer(client->adapter, &msg, 1);
}
static int n516_lpc_detect(struct i2c_client *client, int kind, struct i2c_board_info *info)
{
return 0;
}
static int n516_lpc_suspend_notifier(struct notifier_block *nb,
unsigned long event,
void *dummy)
{
switch(event) {
case PM_SUSPEND_PREPARE:
the_lpc->suspending = 1;
the_lpc->can_sleep = 1;
break;
case PM_POST_SUSPEND:
the_lpc->suspending = 0;
the_lpc->can_sleep = 1;
break;
default:
return NOTIFY_DONE;
}
return NOTIFY_OK;
}
static struct notifier_block n516_lpc_notif_block = {
.notifier_call = n516_lpc_suspend_notifier,
};
static int n516_lpc_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct n516_lpc_chip *chip;
struct input_dev *input;
int ret = 0;
int i;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
the_lpc = chip;
chip->i2c_client = client;
if ((batt_level > 0) && (batt_level < ARRAY_SIZE(batt_charge)))
chip->battery_level = batt_level;
i2c_set_clientdata(client, chip);
ret = gpio_request(GPIO_LPC_INT, "LPC interrupt request");
if (ret) {
dev_err(&client->dev, "Unable to reguest LPC INT GPIO\n");
goto err_gpio_req_lpcint;
}
ret = gpio_request(GPIO_CHARG_STAT_N, "LPC interrupt request");
if (ret) {
dev_err(&client->dev, "Unable to reguest CHARG STAT GPIO\n");
goto err_gpio_req_chargstat;
}
n516_lpc_set_normal_mode(chip);
input = input_allocate_device();
if (!input) {
dev_err(&client->dev, "Unable to allocate input device\n");
ret = -ENOMEM;
goto err_input_alloc;
}
chip->input = input;
__set_bit(EV_KEY, input->evbit);
for (i = 0; i < ARRAY_SIZE(n516_lpc_keymap); i++)
__set_bit(n516_lpc_keymap[i], input->keybit);
__set_bit(KEY_LEFTALT, input->keybit);
input->name = "n516-keys";
input->phys = "n516-keys/input0";
input->dev.parent = &client->dev;
input->id.bustype = BUS_I2C;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
ret = input_register_device(input);
if (ret < 0) {
dev_err(&client->dev, "Unable to register input device\n");
goto err_input_register;
}
ret = power_supply_register(NULL, &n516_battery);
if (ret) {
dev_err(&client->dev, "Unable to register N516 battery\n");
goto err_bat_reg;
}
if (n516_bat_usb_connected() && !n516_bat_charging())
the_lpc->battery_level = MAX_BAT_LEVEL;
ret = request_threaded_irq(gpio_to_irq(GPIO_LPC_INT), NULL, n516_lpc_irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "lpc", chip);
if (ret) {
dev_err(&client->dev, "request_irq failed: %d\n", ret);
goto err_request_lpc_irq;
}
ret = request_irq(gpio_to_irq(GPIO_CHARG_STAT_N), n516_bat_charge_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "battery charging", &n516_battery);
if (ret) {
dev_err(&client->dev, "Unable to claim battery charging IRQ\n");
goto err_request_chrg_irq;
}
pm_power_off = n516_lpc_power_off;
ret = register_pm_notifier(&n516_lpc_notif_block);
if (ret) {
dev_err(&client->dev, "Unable to register PM notify block\n");
goto err_reg_pm_notifier;
}
device_init_wakeup(&client->dev, 1);
return 0;
unregister_pm_notifier(&n516_lpc_notif_block);
err_reg_pm_notifier:
free_irq(gpio_to_irq(GPIO_CHARG_STAT_N), &n516_battery);
err_request_chrg_irq:
free_irq(gpio_to_irq(GPIO_LPC_INT), chip);
err_request_lpc_irq:
power_supply_unregister(&n516_battery);
err_bat_reg:
input_unregister_device(input);
err_input_register:
input_free_device(input);
err_input_alloc:
gpio_free(GPIO_CHARG_STAT_N);
err_gpio_req_chargstat:
gpio_free(GPIO_LPC_INT);
err_gpio_req_lpcint:
i2c_set_clientdata(client, NULL);
kfree(chip);
return ret;
}
static int n516_lpc_remove(struct i2c_client *client)
{
struct n516_lpc_chip *chip = i2c_get_clientdata(client);
unregister_pm_notifier(&n516_lpc_notif_block);
pm_power_off = NULL;
free_irq(gpio_to_irq(GPIO_CHARG_STAT_N), &n516_battery);
free_irq(gpio_to_irq(GPIO_LPC_INT), chip);
power_supply_unregister(&n516_battery);
input_unregister_device(chip->input);
gpio_free(GPIO_CHARG_STAT_N);
gpio_free(GPIO_LPC_INT);
i2c_set_clientdata(client, NULL);
kfree(chip);
return 0;
}
#if CONFIG_PM
static int n516_lpc_suspend(struct i2c_client *client, pm_message_t msg)
{
if (!the_lpc->can_sleep)
return -EBUSY;
if (device_may_wakeup(&client->dev))
enable_irq_wake(gpio_to_irq(GPIO_LPC_INT));
return 0;
}
static int n516_lpc_resume(struct i2c_client *client)
{
if (device_may_wakeup(&client->dev))
disable_irq_wake(gpio_to_irq(GPIO_LPC_INT));
return 0;
}
#else
#define n516_lpc_suspend NULL
#define n516_lpc_resume NULL
#endif
static struct i2c_driver n516_lpc_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "n516-keys",
.owner = THIS_MODULE,
},
.probe = n516_lpc_probe,
.remove = __devexit_p(n516_lpc_remove),
.detect = n516_lpc_detect,
.id_table = n516_lpc_i2c_ids,
.address_data = &addr_data,
.suspend = n516_lpc_suspend,
.resume = n516_lpc_resume,
};
static int n516_lpc_init(void)
{
return i2c_add_driver(&n516_lpc_driver);
}
module_init(n516_lpc_init);
static void n516_lpc_exit(void)
{
i2c_del_driver(&n516_lpc_driver);
}
module_exit(n516_lpc_exit);
MODULE_AUTHOR("Yauhen Kharuzhy");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Keys and power controller driver for N516");
MODULE_ALIAS("platform:n516-keys");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,67 @@
/*
* metronomefb.h - definitions for the metronome framebuffer driver
*
* Copyright (C) 2008 by Jaya Kumar
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*
*/
#ifndef _LINUX_METRONOMEFB_H_
#define _LINUX_METRONOMEFB_H_
/* command structure used by metronome controller */
struct metromem_cmd {
u16 opcode;
u16 args[((64-2)/2)];
u16 csum;
} __attribute__(packed);
/* struct used by metronome. board specific stuff comes from *board */
struct metronomefb_par {
struct metromem_cmd *metromem_cmd;
unsigned char *metromem_wfm;
unsigned char *metromem_img;
u16 *metromem_img_csum;
u16 *csum_table;
dma_addr_t metromem_dma;
const struct firmware *firmware;
struct fb_info *info;
struct metronome_board *board;
struct platform_device *pdev;
wait_queue_head_t waitq;
u8 frame_count;
int extra_size;
int current_wf_mode;
int current_wf_temp;
unsigned int manual_refresh_threshold;
unsigned int partial_autorefresh_interval;
int dt;
u32 *fxbuckets;
u32 *fybuckets;
struct mutex lock;
};
#define METRONOME_POWER_OFF 0
#define METRONOME_POWER_ON 1
/* board specific routines and data */
struct metronome_board {
struct module *owner; /* the platform device */
void (*power_ctl)(struct metronomefb_par *, int);
void (*set_rst)(struct metronomefb_par *, int);
void (*set_stdby)(struct metronomefb_par *, int);
int (*get_err)(struct metronomefb_par *);
int (*get_rdy)(struct metronomefb_par *);
void (*cleanup)(struct metronomefb_par *);
int (*met_wait_event)(struct metronomefb_par *);
int (*met_wait_event_intr)(struct metronomefb_par *);
int (*setup_irq)(struct fb_info *);
int (*setup_fb)(struct metronomefb_par *);
int (*setup_io)(struct metronomefb_par *);
int (*get_panel_type)(void);
};
#endif

View File

@ -0,0 +1,25 @@
CONFIG_FB_DEFERRED_IO=y
CONFIG_FB_JZ4740=y
CONFIG_FB_METRONOME=m
CONFIG_FB_SYS_FOPS=m
# CONFIG_FRAMEBUFFER_CONSOLE is not set
CONFIG_HWMON=y
# CONFIG_HWMON_DEBUG_CHIP is not set
CONFIG_I2C=y
CONFIG_I2C_ALGOBIT=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_GPIO=y
CONFIG_JZ4740_N516=y
# CONFIG_KEYBOARD_GPIO is not set
CONFIG_LEDS_GPIO=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_N516_LPC=y
CONFIG_NEW_LEDS=y
# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
# CONFIG_REGULATOR_LP3971 is not set
# CONFIG_REGULATOR_MAX1586 is not set
# CONFIG_REGULATOR_TPS65023 is not set
# CONFIG_REGULATOR_TPS6507X is not set
CONFIG_SENSORS_LM75=y
# CONFIG_USB_ARCH_HAS_HCD is not set
# CONFIG_USB_ARCH_HAS_OHCI is not set

View File

@ -0,0 +1,2 @@
BOARDNAME:=Hanvon N516 e-book reader
#DEFAULT_PACKAGES += uboot-xburst-n516

View File

@ -10,10 +10,18 @@ Subject: [PATCH] /opt/Projects/openwrt/target/linux/xburst/patches-2.6.31/800-n5
--- a/drivers/i2c/chips/Kconfig
+++ b/drivers/i2c/chips/Kconfig
@@ -26,4 +26,13 @@ config SENSORS_TSL2550
@@ -26,4 +26,21 @@ config SENSORS_TSL2550
This driver can also be built as a module. If so, the module
will be called tsl2550.
+config N516_LPC
+ tristate "N516 keys & power controller"
+ depends on I2C
+ depends on INPUT
+ depends on POWER_SUPPLY
+ help
+ N516 keyboard & power controller driver
+
+config N526_LPC
+ tristate "N526 LPC934 coprocessor"
+ depends on JZ4740_N526
@ -26,10 +34,11 @@ Subject: [PATCH] /opt/Projects/openwrt/target/linux/xburst/patches-2.6.31/800-n5
endmenu
--- a/drivers/i2c/chips/Makefile
+++ b/drivers/i2c/chips/Makefile
@@ -12,6 +12,7 @@
@@ -12,6 +12,8 @@
obj-$(CONFIG_DS1682) += ds1682.o
obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
+obj-$(CONFIG_N516_LPC) += n516-lpc.o
+obj-$(CONFIG_N526_LPC) += n526-lpc.o
ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)